|  | /* | 
|  | * Copyright 2023 Google LLC | 
|  | * | 
|  | * Use of this source code is governed by a BSD-style license that can be | 
|  | * found in the LICENSE file. | 
|  | */ | 
|  |  | 
|  | #include "gm/gm.h" | 
|  | #include "include/codec/SkCodec.h" | 
|  | #include "include/codec/SkEncodedImageFormat.h" | 
|  | #include "include/codec/SkPngDecoder.h" | 
|  | #include "include/core/SkAlphaType.h" | 
|  | #include "include/core/SkBitmap.h" | 
|  | #include "include/core/SkCanvas.h" | 
|  | #include "include/core/SkImage.h" | 
|  | #include "include/core/SkRect.h" | 
|  | #include "include/core/SkSize.h" | 
|  | #include "include/core/SkStream.h" | 
|  | #include "include/core/SkString.h" | 
|  | #include "include/private/base/SkTArray.h" | 
|  | #include "include/private/base/SkTemplates.h" | 
|  | #include "src/base/SkAutoMalloc.h" | 
|  | #include "src/core/SkSwizzlePriv.h" | 
|  | #include "src/utils/SkOSPath.h" | 
|  | #include "tools/flags/CommandLineFlags.h" | 
|  | #include "tools/flags/CommonFlags.h" | 
|  |  | 
|  | #include <map> | 
|  | #include <memory> | 
|  | #include <string> | 
|  | #include <vector> | 
|  |  | 
|  | DEFINE_string(pngCodecGMImages, | 
|  | "", | 
|  | "Zero or more images or directories where to find PNG images to test with " | 
|  | "PNGCodecGM. Directories are scanned non-recursively. All files are assumed to be " | 
|  | "PNG images."); | 
|  | DEFINE_string(pngCodecDecodeMode, | 
|  | "", | 
|  | "One of \"get-all-pixels\", \"incremental\" or \"zero-init\"."); | 
|  | DEFINE_string(pngCodecDstColorType, | 
|  | "", | 
|  | "One of \"force-grayscale\", " | 
|  | "\"force-nonnative-premul-color\" or \"get-from-canvas\"."); | 
|  | DEFINE_string(pngCodecDstAlphaType, "", "One of \"premul\" or \"unpremul\"."); | 
|  |  | 
|  | static constexpr const char* sk_color_type_to_str(SkColorType colorType) { | 
|  | switch (colorType) { | 
|  | case kUnknown_SkColorType: | 
|  | return "kUnknown_SkColorType"; | 
|  | case kAlpha_8_SkColorType: | 
|  | return "kAlpha_8_SkColorType"; | 
|  | case kRGB_565_SkColorType: | 
|  | return "kRGB_565_SkColorType"; | 
|  | case kARGB_4444_SkColorType: | 
|  | return "kARGB_4444_SkColorType"; | 
|  | case kRGBA_8888_SkColorType: | 
|  | return "kRGBA_8888_SkColorType"; | 
|  | case kRGB_888x_SkColorType: | 
|  | return "kRGB_888x_SkColorType"; | 
|  | case kBGRA_8888_SkColorType: | 
|  | return "kBGRA_8888_SkColorType"; | 
|  | case kRGBA_1010102_SkColorType: | 
|  | return "kRGBA_1010102_SkColorType"; | 
|  | case kBGRA_1010102_SkColorType: | 
|  | return "kBGRA_1010102_SkColorType"; | 
|  | case kRGB_101010x_SkColorType: | 
|  | return "kRGB_101010x_SkColorType"; | 
|  | case kBGR_101010x_SkColorType: | 
|  | return "kBGR_101010x_SkColorType"; | 
|  | case kBGR_101010x_XR_SkColorType: | 
|  | return "kBGR_101010x_XR_SkColorType"; | 
|  | case kGray_8_SkColorType: | 
|  | return "kGray_8_SkColorType"; | 
|  | case kRGBA_F16Norm_SkColorType: | 
|  | return "kRGBA_F16Norm_SkColorType"; | 
|  | case kRGBA_F16_SkColorType: | 
|  | return "kRGBA_F16_SkColorType"; | 
|  | case kRGB_F16F16F16x_SkColorType: | 
|  | return "kRGB_F16F16F16x_SkColorType"; | 
|  | case kRGBA_F32_SkColorType: | 
|  | return "kRGBA_F32_SkColorType"; | 
|  | case kR8G8_unorm_SkColorType: | 
|  | return "kR8G8_unorm_SkColorType"; | 
|  | case kA16_float_SkColorType: | 
|  | return "kA16_float_SkColorType"; | 
|  | case kR16G16_float_SkColorType: | 
|  | return "kR16G16_float_SkColorType"; | 
|  | case kA16_unorm_SkColorType: | 
|  | return "kA16_unorm_SkColorType"; | 
|  | case kR16G16_unorm_SkColorType: | 
|  | return "kR16G16_unorm_SkColorType"; | 
|  | case kR16G16B16A16_unorm_SkColorType: | 
|  | return "kR16G16B16A16_unorm_SkColorType"; | 
|  | case kSRGBA_8888_SkColorType: | 
|  | return "kSRGBA_8888_SkColorType"; | 
|  | case kR8_unorm_SkColorType: | 
|  | return "kR8_unorm_SkColorType"; | 
|  | case kRGBA_10x6_SkColorType: | 
|  | return "kRGBA_10x6_SkColorType"; | 
|  | case kBGRA_10101010_XR_SkColorType: | 
|  | return "kBGRA_10101010_XR_SkColorType"; | 
|  | } | 
|  | SkUNREACHABLE; | 
|  | } | 
|  |  | 
|  | static constexpr const char* sk_alpha_type_to_str(SkAlphaType alphaType) { | 
|  | switch (alphaType) { | 
|  | case kUnknown_SkAlphaType: | 
|  | return "kUnknown_SkAlphaType"; | 
|  | case kOpaque_SkAlphaType: | 
|  | return "kOpaque_SkAlphaType"; | 
|  | case kPremul_SkAlphaType: | 
|  | return "kPremul_SkAlphaType"; | 
|  | case kUnpremul_SkAlphaType: | 
|  | return "kUnpremul_SkAlphaType"; | 
|  | } | 
|  | SkUNREACHABLE; | 
|  | } | 
|  |  | 
|  | struct DecodeResult { | 
|  | std::unique_ptr<SkCodec> codec; | 
|  | std::string errorMsg; | 
|  | }; | 
|  |  | 
|  | static DecodeResult decode(std::string path) { | 
|  | sk_sp<SkData> encoded(SkData::MakeFromFileName(path.c_str())); | 
|  | if (!encoded) { | 
|  | return {.errorMsg = SkStringPrintf("Could not read \"%s\".", path.c_str()).c_str()}; | 
|  | } | 
|  | SkCodec::Result result; | 
|  | std::unique_ptr<SkCodec> codec = SkPngDecoder::Decode(SkMemoryStream::Make(encoded), &result); | 
|  | if (result != SkCodec::Result::kSuccess) { | 
|  | return {.errorMsg = SkStringPrintf("Could not create codec for \"%s\": %s.", | 
|  | path.c_str(), | 
|  | SkCodec::ResultToString(result)) | 
|  | .c_str()}; | 
|  | } | 
|  | return {.codec = std::move(codec)}; | 
|  | } | 
|  |  | 
|  | // This GM implements the PNG-related behaviors found in DM's CodecSrc class. It takes a single | 
|  | // image as an argument and applies the same logic as CodecSrc. | 
|  | // | 
|  | // See the CodecSrc class here: | 
|  | // https://skia.googlesource.com/skia/+/ce49fc71bc7cc25244020cd3e64764a6d08e54fb/dm/DMSrcSink.h#158. | 
|  | class PNGCodecGM : public skiagm::GM { | 
|  | public: | 
|  | // Based on CodecSrc::Mode. | 
|  | // https://skia.googlesource.com/skia/+/ce49fc71bc7cc25244020cd3e64764a6d08e54fb/dm/DMSrcSink.h#160 | 
|  | enum class DecodeMode { | 
|  | kGetAllPixels, | 
|  | kIncremental, | 
|  | kZeroInit, | 
|  | }; | 
|  |  | 
|  | // Based on CodecSrc::DstColorType. | 
|  | // https://skia.googlesource.com/skia/+/ce49fc71bc7cc25244020cd3e64764a6d08e54fb/dm/DMSrcSink.h#172 | 
|  | enum class DstColorType { | 
|  | kForceGrayscale, | 
|  | kForceNonNativePremulColor, | 
|  | kGetFromCanvas, | 
|  | }; | 
|  |  | 
|  | static constexpr const char* DecodeModeToString(DecodeMode decodeMode) { | 
|  | switch (decodeMode) { | 
|  | case DecodeMode::kGetAllPixels: | 
|  | return "kGetAllPixels"; | 
|  | case DecodeMode::kIncremental: | 
|  | return "kIncremental"; | 
|  | case DecodeMode::kZeroInit: | 
|  | return "kZeroInit"; | 
|  | } | 
|  | SkUNREACHABLE; | 
|  | } | 
|  |  | 
|  | static constexpr const char* DstColorTypeToString(DstColorType dstColorType) { | 
|  | switch (dstColorType) { | 
|  | case DstColorType::kForceGrayscale: | 
|  | return "kForceGrayscale"; | 
|  | case DstColorType::kForceNonNativePremulColor: | 
|  | return "kForceNonNativePremulColor"; | 
|  | case DstColorType::kGetFromCanvas: | 
|  | return "kGetFromCanvas"; | 
|  | } | 
|  | SkUNREACHABLE; | 
|  | } | 
|  |  | 
|  | // Based on DM's CodecSrc::CodecSrc(). | 
|  | // https://skia.googlesource.com/skia/+/ce49fc71bc7cc25244020cd3e64764a6d08e54fb/dm/DMSrcSink.cpp#371 | 
|  | PNGCodecGM(std::string path, | 
|  | DecodeMode decodeMode, | 
|  | DstColorType dstColorType, | 
|  | SkAlphaType dstAlphaType) | 
|  | : skiagm::GM() | 
|  | , fPath(path) | 
|  | , fDecodeMode(decodeMode) | 
|  | , fDstColorType(dstColorType) | 
|  | , fDstAlphaType(dstAlphaType) {} | 
|  |  | 
|  | bool isBazelOnly() const override { | 
|  | // This GM class overlaps with DM's CodecSrc and related sources. | 
|  | return true; | 
|  | } | 
|  |  | 
|  | std::map<std::string, std::string> getGoldKeys() const override { | 
|  | return std::map<std::string, std::string>{ | 
|  | {"name", getName().c_str()}, | 
|  | {"source_type", "image"}, | 
|  | {"decode_mode", DecodeModeToString(fDecodeMode)}, | 
|  | {"dst_color_type", DstColorTypeToString(fDstColorType)}, | 
|  | {"dst_alpha_type", sk_alpha_type_to_str(fDstAlphaType)}, | 
|  | }; | 
|  | } | 
|  |  | 
|  | protected: | 
|  | // Based on CodecSrc::name(). | 
|  | // https://skia.googlesource.com/skia/+/ce49fc71bc7cc25244020cd3e64764a6d08e54fb/dm/DMSrcSink.cpp#828 | 
|  | SkString getName() const override { | 
|  | SkString name = SkOSPath::Basename(fPath.c_str()); | 
|  | return name; | 
|  | } | 
|  |  | 
|  | // Based on CodecSrc::size(). | 
|  | // https://skia.googlesource.com/skia/+/ce49fc71bc7cc25244020cd3e64764a6d08e54fb/dm/DMSrcSink.cpp#803 | 
|  | SkISize getISize() override { | 
|  | DecodeResult decodeResult = decode(fPath); | 
|  | if (decodeResult.errorMsg != "") { | 
|  | return {0, 0}; | 
|  | } | 
|  | return decodeResult.codec->dimensions(); | 
|  | } | 
|  |  | 
|  | // Based on CodecSrc::draw(). | 
|  | // https://skia.googlesource.com/skia/+/ce49fc71bc7cc25244020cd3e64764a6d08e54fb/dm/DMSrcSink.cpp#450 | 
|  | DrawResult onDraw(SkCanvas* canvas, SkString* errorMsg) override { | 
|  | DecodeResult decodeResult = decode(fPath); | 
|  | if (decodeResult.errorMsg != "") { | 
|  | *errorMsg = decodeResult.errorMsg.c_str(); | 
|  | return DrawResult::kFail; | 
|  | } | 
|  | std::unique_ptr<SkCodec> codec = std::move(decodeResult.codec); | 
|  |  | 
|  | SkImageInfo decodeInfo = codec->getInfo(); | 
|  | if (*errorMsg = validateCanvasColorTypeAndGetDecodeInfo(&decodeInfo, | 
|  | canvas->imageInfo().colorType()); | 
|  | *errorMsg != SkString()) { | 
|  | return DrawResult::kFail; | 
|  | } | 
|  |  | 
|  | SkISize size = codec->dimensions(); | 
|  | decodeInfo = decodeInfo.makeDimensions(size); | 
|  |  | 
|  | const int bpp = decodeInfo.bytesPerPixel(); | 
|  | const size_t rowBytes = size.width() * bpp; | 
|  | const size_t safeSize = decodeInfo.computeByteSize(rowBytes); | 
|  | SkAutoMalloc pixels(safeSize); | 
|  |  | 
|  | SkCodec::Options options; | 
|  | if (DecodeMode::kZeroInit == fDecodeMode) { | 
|  | memset(pixels.get(), 0, size.height() * rowBytes); | 
|  | options.fZeroInitialized = SkCodec::kYes_ZeroInitialized; | 
|  | } | 
|  |  | 
|  | // For codec srcs, we want the "draw" step to be a memcpy.  Any interesting color space or | 
|  | // color format conversions should be performed by the codec.  Sometimes the output of the | 
|  | // decode will be in an interesting color space.  On our srgb and f16 backends, we need to | 
|  | // "pretend" that the color space is standard sRGB to avoid triggering color conversion | 
|  | // at draw time. | 
|  | SkImageInfo bitmapInfo = decodeInfo.makeColorSpace(SkColorSpace::MakeSRGB()); | 
|  |  | 
|  | if (kRGBA_8888_SkColorType == decodeInfo.colorType() || | 
|  | kBGRA_8888_SkColorType == decodeInfo.colorType()) { | 
|  | bitmapInfo = bitmapInfo.makeColorType(kN32_SkColorType); | 
|  | } | 
|  |  | 
|  | switch (fDecodeMode) { | 
|  | case DecodeMode::kZeroInit: | 
|  | case DecodeMode::kGetAllPixels: { | 
|  | switch (codec->getPixels(decodeInfo, pixels.get(), rowBytes, &options)) { | 
|  | case SkCodec::kSuccess: | 
|  | // We consider these to be valid, since we should still decode what is | 
|  | // available. | 
|  | case SkCodec::kErrorInInput: | 
|  | case SkCodec::kIncompleteInput: | 
|  | break; | 
|  | default: | 
|  | // Everything else is considered a failure. | 
|  | *errorMsg = SkStringPrintf("Couldn't getPixels %s.", fPath.c_str()); | 
|  | return DrawResult::kFail; | 
|  | } | 
|  |  | 
|  | drawToCanvas(canvas, bitmapInfo, pixels.get(), rowBytes); | 
|  | break; | 
|  | } | 
|  | case DecodeMode::kIncremental: { | 
|  | void* dst = pixels.get(); | 
|  | uint32_t height = decodeInfo.height(); | 
|  | if (SkCodec::kSuccess == | 
|  | codec->startIncrementalDecode(decodeInfo, dst, rowBytes, &options)) { | 
|  | int rowsDecoded; | 
|  | auto result = codec->incrementalDecode(&rowsDecoded); | 
|  | if (SkCodec::kIncompleteInput == result || SkCodec::kErrorInInput == result) { | 
|  | codec->fillIncompleteImage(decodeInfo, | 
|  | dst, | 
|  | rowBytes, | 
|  | SkCodec::kNo_ZeroInitialized, | 
|  | height, | 
|  | rowsDecoded); | 
|  | } | 
|  | } else { | 
|  | *errorMsg = "Could not start incremental decode"; | 
|  | return DrawResult::kFail; | 
|  | } | 
|  | drawToCanvas(canvas, bitmapInfo, dst, rowBytes); | 
|  | break; | 
|  | } | 
|  | default: | 
|  | SkASSERT(false); | 
|  | *errorMsg = "Invalid fDecodeMode"; | 
|  | return DrawResult::kFail; | 
|  | } | 
|  | return DrawResult::kOk; | 
|  | } | 
|  |  | 
|  | private: | 
|  | // Checks that the canvas color type, destination color and alpha types and input image | 
|  | // constitute an interesting test case, and constructs the SkImageInfo to use when decoding the | 
|  | // image. | 
|  | // | 
|  | // Based on DM's get_decode_info() function. | 
|  | // https://skia.googlesource.com/skia/+/ce49fc71bc7cc25244020cd3e64764a6d08e54fb/dm/DMSrcSink.cpp#398 | 
|  | SkString validateCanvasColorTypeAndGetDecodeInfo(SkImageInfo* decodeInfo, | 
|  | SkColorType canvasColorType) { | 
|  | switch (fDstColorType) { | 
|  | case DstColorType::kForceGrayscale: | 
|  | if (kRGB_565_SkColorType == canvasColorType) { | 
|  | return SkStringPrintf( | 
|  | "canvas color type %s and destination color type %s are redundant", | 
|  | sk_color_type_to_str(canvasColorType), | 
|  | DstColorTypeToString(fDstColorType)); | 
|  | } | 
|  | *decodeInfo = decodeInfo->makeColorType(kGray_8_SkColorType); | 
|  | break; | 
|  |  | 
|  | case DstColorType::kForceNonNativePremulColor: | 
|  | if (kRGB_565_SkColorType == canvasColorType || | 
|  | kRGBA_F16_SkColorType == canvasColorType) { | 
|  | return SkStringPrintf( | 
|  | "canvas color type %s and destination color type %s are redundant", | 
|  | sk_color_type_to_str(canvasColorType), | 
|  | DstColorTypeToString(fDstColorType)); | 
|  | } | 
|  | #ifdef SK_PMCOLOR_IS_RGBA | 
|  | *decodeInfo = decodeInfo->makeColorType(kBGRA_8888_SkColorType); | 
|  | #else | 
|  | *decodeInfo = decodeInfo->makeColorType(kRGBA_8888_SkColorType); | 
|  | #endif | 
|  | break; | 
|  |  | 
|  | case DstColorType::kGetFromCanvas: | 
|  | if (kRGB_565_SkColorType == canvasColorType && | 
|  | kOpaque_SkAlphaType != decodeInfo->alphaType()) { | 
|  | return SkStringPrintf( | 
|  | "image \"%s\" has alpha type %s; this is incompatible with with " | 
|  | "canvas color type %s and destination color type %s", | 
|  | fPath.c_str(), | 
|  | sk_alpha_type_to_str(decodeInfo->alphaType()), | 
|  | sk_color_type_to_str(canvasColorType), | 
|  | DstColorTypeToString(fDstColorType)); | 
|  | } | 
|  | *decodeInfo = decodeInfo->makeColorType(canvasColorType); | 
|  | break; | 
|  |  | 
|  | default: | 
|  | SkUNREACHABLE; | 
|  | } | 
|  |  | 
|  | *decodeInfo = decodeInfo->makeAlphaType(fDstAlphaType); | 
|  | return SkString(); | 
|  | } | 
|  |  | 
|  | // Based on DM's draw_to_canvas() function. | 
|  | // https://skia.googlesource.com/skia/+/ce49fc71bc7cc25244020cd3e64764a6d08e54fb/dm/DMSrcSink.cpp#432 | 
|  | void drawToCanvas(SkCanvas* canvas, | 
|  | const SkImageInfo& info, | 
|  | void* pixels, | 
|  | size_t rowBytes, | 
|  | SkScalar left = 0, | 
|  | SkScalar top = 0) { | 
|  | SkBitmap bitmap; | 
|  | bitmap.installPixels(info, pixels, rowBytes); | 
|  | swapRbIfNecessary(bitmap); | 
|  | canvas->drawImage(bitmap.asImage(), left, top); | 
|  | } | 
|  |  | 
|  | // Allows us to test decodes to non-native 8888. | 
|  | // | 
|  | // Based on DM's swap_rb_if_necessary function. | 
|  | // https://skia.googlesource.com/skia/+/ce49fc71bc7cc25244020cd3e64764a6d08e54fb/dm/DMSrcSink.cpp#387 | 
|  | void swapRbIfNecessary(SkBitmap& bitmap) { | 
|  | if (DstColorType::kForceNonNativePremulColor != fDstColorType) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | for (int y = 0; y < bitmap.height(); y++) { | 
|  | uint32_t* row = (uint32_t*)bitmap.getAddr(0, y); | 
|  | SkOpts::RGBA_to_BGRA(row, row, bitmap.width()); | 
|  | } | 
|  | } | 
|  |  | 
|  | std::string fPath; | 
|  | DecodeMode fDecodeMode; | 
|  | DstColorType fDstColorType; | 
|  | SkAlphaType fDstAlphaType; | 
|  | }; | 
|  |  | 
|  | // Registers GMs with zero or more PNGCodecGM instances for the given image. Returns a non-empty, | 
|  | // human-friendly error message in the case of errors. | 
|  | // | 
|  | // Based on DM's push_codec_srcs() function. It only covers "simple" codecs (lines 740-834). | 
|  | // https://skia.googlesource.com/skia/+/ce49fc71bc7cc25244020cd3e64764a6d08e54fb/dm/DM.cpp#740 | 
|  | // | 
|  | // Specifically, this function does not capture any behaviors found in the following DM classes: | 
|  | // | 
|  | //  - AndroidCodecSrc | 
|  | //  - BRDSrc | 
|  | //  - ImageGenSrc | 
|  | // | 
|  | // TODO(lovisolo): Implement the above sources as GMs (if necessary). | 
|  | static std::string registerGMsForImage(std::string path, | 
|  | PNGCodecGM::DecodeMode decodeMode, | 
|  | PNGCodecGM::DstColorType dstColorType, | 
|  | SkAlphaType dstAlphaType) { | 
|  | DecodeResult decodeResult = decode(path); | 
|  | if (decodeResult.errorMsg != "") { | 
|  | return decodeResult.errorMsg; | 
|  | } | 
|  |  | 
|  | if (dstColorType == PNGCodecGM::DstColorType::kForceGrayscale && | 
|  | decodeResult.codec->getInfo().colorType() != kGray_8_SkColorType) { | 
|  | return SkStringPrintf( | 
|  | "image \"%s\" has color type %s; this is incompatible with the given " | 
|  | "dstColorType argument: %s (expected image color type: %s)", | 
|  | path.c_str(), | 
|  | sk_color_type_to_str(decodeResult.codec->getInfo().colorType()), | 
|  | PNGCodecGM::DstColorTypeToString(PNGCodecGM::DstColorType::kForceGrayscale), | 
|  | sk_color_type_to_str(kGray_8_SkColorType)) | 
|  | .c_str(); | 
|  | } | 
|  |  | 
|  | if (dstAlphaType == kUnpremul_SkAlphaType && | 
|  | decodeResult.codec->getInfo().alphaType() == kOpaque_SkAlphaType) { | 
|  | return SkStringPrintf( | 
|  | "image \"%s\" has alpha type %s; this is incompatible with the given " | 
|  | "dstAlphaType argument: %s", | 
|  | path.c_str(), | 
|  | sk_alpha_type_to_str(kOpaque_SkAlphaType), | 
|  | sk_alpha_type_to_str(kUnpremul_SkAlphaType)) | 
|  | .c_str(); | 
|  | } | 
|  |  | 
|  | skiagm::Register(new PNGCodecGM(path, decodeMode, dstColorType, dstAlphaType)); | 
|  | return ""; | 
|  | } | 
|  |  | 
|  | // Returns a non-empty message in the case of errors. | 
|  | static std::string parse_and_validate_flags(PNGCodecGM::DecodeMode* decodeMode, | 
|  | PNGCodecGM::DstColorType* dstColorType, | 
|  | SkAlphaType* dstAlphaType) { | 
|  | skia_private::THashMap<SkString, PNGCodecGM::DecodeMode> decodeModeValues = { | 
|  | {SkString("get-all-pixels"), PNGCodecGM::DecodeMode::kGetAllPixels}, | 
|  | {SkString("incremental"), PNGCodecGM::DecodeMode::kIncremental}, | 
|  | {SkString("zero-init"), PNGCodecGM::DecodeMode::kZeroInit}, | 
|  | }; | 
|  | if (SkString errorMsg = FLAGS_pngCodecDecodeMode.parseAndValidate( | 
|  | "--pngCodecDecodeMode", decodeModeValues, decodeMode); | 
|  | errorMsg != SkString()) { | 
|  | return errorMsg.c_str(); | 
|  | } | 
|  |  | 
|  | skia_private::THashMap<SkString, PNGCodecGM::DstColorType> dstColorTypeValues = { | 
|  | {SkString("get-from-canvas"), PNGCodecGM::DstColorType::kGetFromCanvas}, | 
|  | {SkString("force-grayscale"), PNGCodecGM::DstColorType::kForceGrayscale}, | 
|  | {SkString("force-nonnative-premul-color"), | 
|  | PNGCodecGM::DstColorType::kForceNonNativePremulColor}, | 
|  | }; | 
|  | if (SkString errorMsg = FLAGS_pngCodecDstColorType.parseAndValidate( | 
|  | "--pngCodecDstColorType", dstColorTypeValues, dstColorType); | 
|  | errorMsg != SkString()) { | 
|  | return errorMsg.c_str(); | 
|  | } | 
|  |  | 
|  | skia_private::THashMap<SkString, SkAlphaType> dstAlphaTypeValues = { | 
|  | {SkString("premul"), kPremul_SkAlphaType}, | 
|  | {SkString("unpremul"), kUnpremul_SkAlphaType}, | 
|  | }; | 
|  | if (SkString errorMsg = FLAGS_pngCodecDstAlphaType.parseAndValidate( | 
|  | "--pngCodecDstAlphaType", dstAlphaTypeValues, dstAlphaType); | 
|  | errorMsg != SkString()) { | 
|  | return errorMsg.c_str(); | 
|  | } | 
|  |  | 
|  | return ""; | 
|  | } | 
|  |  | 
|  | // Registers one PNGCodecGM instance for each image passed via the --pngCodecGMImages flag, which | 
|  | // can take files and directories. Directories are scanned non-recursively. | 
|  | // | 
|  | // Based on DM's gather_srcs() function. | 
|  | // https://skia.googlesource.com/skia/+/ce49fc71bc7cc25244020cd3e64764a6d08e54fb/dm/DM.cpp#953 | 
|  | DEF_GM_REGISTERER_FN([]() -> std::string { | 
|  | // Parse flags. | 
|  | PNGCodecGM::DecodeMode decodeMode; | 
|  | PNGCodecGM::DstColorType dstColorType; | 
|  | SkAlphaType dstAlphaType; | 
|  | if (std::string errorMsg = parse_and_validate_flags(&decodeMode, &dstColorType, &dstAlphaType); | 
|  | errorMsg != "") { | 
|  | return errorMsg; | 
|  | } | 
|  |  | 
|  | // Collect images. | 
|  | skia_private::TArray<SkString> images; | 
|  | if (!CommonFlags::CollectImages(FLAGS_pngCodecGMImages, &images)) { | 
|  | return "Failed to collect images."; | 
|  | } | 
|  |  | 
|  | // Register one GM per image. | 
|  | for (const SkString& image : images) { | 
|  | if (std::string errorMsg = | 
|  | registerGMsForImage(image.c_str(), decodeMode, dstColorType, dstAlphaType); | 
|  | errorMsg != "") { | 
|  | return errorMsg; | 
|  | } | 
|  | } | 
|  |  | 
|  | return ""; | 
|  | }); |