diff options
-rw-r--r-- | dm/DM.cpp | 84 | ||||
-rw-r--r-- | dm/DMSrcSink.cpp | 153 | ||||
-rw-r--r-- | dm/DMSrcSink.h | 22 | ||||
-rw-r--r-- | gyp/ports.gyp | 4 | ||||
-rw-r--r-- | src/ports/SkImageGeneratorCG.cpp | 123 | ||||
-rw-r--r-- | src/ports/SkImageGeneratorCG.h | 43 | ||||
-rw-r--r-- | tools/dm_flags.json | 56 | ||||
-rwxr-xr-x | tools/dm_flags.py | 10 |
8 files changed, 438 insertions, 57 deletions
@@ -343,9 +343,6 @@ static void push_codec_src(Path path, CodecSrc::Mode mode, CodecSrc::DstColorTyp case CodecSrc::kSubset_Mode: folder.append("codec_subset"); break; - case CodecSrc::kGen_Mode: - folder.append("gen"); - break; } switch (dstColorType) { @@ -426,6 +423,40 @@ static void push_android_codec_src(Path path, AndroidCodecSrc::Mode mode, push_src("image", folder, src); } +static void push_image_gen_src(Path path, ImageGenSrc::Mode mode, SkAlphaType alphaType, bool isGpu) +{ + SkString folder; + switch (mode) { + case ImageGenSrc::kCodec_Mode: + folder.append("gen_codec"); + break; + case ImageGenSrc::kPlatform_Mode: + folder.append("gen_platform"); + break; + } + + if (isGpu) { + folder.append("_gpu"); + } else { + switch (alphaType) { + case kOpaque_SkAlphaType: + folder.append("_opaque"); + break; + case kPremul_SkAlphaType: + folder.append("_premul"); + break; + case kUnpremul_SkAlphaType: + folder.append("_unpremul"); + break; + default: + break; + } + } + + ImageGenSrc* src = new ImageGenSrc(path, mode, alphaType, isGpu); + push_src("image", folder, src); +} + static void push_codec_srcs(Path path) { SkAutoTUnref<SkData> encoded(SkData::NewFromFileName(path.c_str())); if (!encoded) { @@ -445,7 +476,6 @@ static void push_codec_srcs(Path path) { SkTArray<CodecSrc::Mode> nativeModes; nativeModes.push_back(CodecSrc::kCodec_Mode); nativeModes.push_back(CodecSrc::kCodecZeroInit_Mode); - nativeModes.push_back(CodecSrc::kGen_Mode); switch (codec->getEncodedFormat()) { case SkEncodedFormat::kJPEG_SkEncodedFormat: nativeModes.push_back(CodecSrc::kScanline_Mode); @@ -487,19 +517,6 @@ static void push_codec_srcs(Path path) { } for (CodecSrc::Mode mode : nativeModes) { - // SkCodecImageGenerator only runs for the default colorType - // recommended by SkCodec. There is no need to generate multiple - // tests for different colorTypes. - // TODO (msarett): Add scaling support to SkCodecImageGenerator. - if (CodecSrc::kGen_Mode == mode) { - // FIXME: The gpu backend does not draw kGray sources correctly. (skbug.com/4822) - if (kGray_8_SkColorType != codec->getInfo().colorType()) { - push_codec_src(path, mode, CodecSrc::kGetFromCanvas_DstColorType, - codec->getInfo().alphaType(), 1.0f); - } - continue; - } - for (float scale : nativeScales) { for (CodecSrc::DstColorType colorType : colorTypes) { for (SkAlphaType alphaType : alphaModes) { @@ -549,6 +566,39 @@ static void push_codec_srcs(Path path) { } } } + + static const char* const rawExts[] = { + "arw", "cr2", "dng", "nef", "nrw", "orf", "raf", "rw2", "pef", "srw", + "ARW", "CR2", "DNG", "NEF", "NRW", "ORF", "RAF", "RW2", "PEF", "SRW", + }; + + // There is not currently a reason to test RAW images on image generator. + // If we want to enable these tests, we will need to fix skbug.com/5079. + for (const char* ext : rawExts) { + if (path.endsWith(ext)) { + return; + } + } + + // Push image generator GPU test. + // FIXME: The gpu backend does not draw kGray sources correctly. (skbug.com/4822) + if (kGray_8_SkColorType != codec->getInfo().colorType()) { + push_image_gen_src(path, ImageGenSrc::kCodec_Mode, codec->getInfo().alphaType(), true); + } + + // Push image generator CPU tests. + for (SkAlphaType alphaType : alphaModes) { + push_image_gen_src(path, ImageGenSrc::kCodec_Mode, alphaType, false); + +#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) + if (kWEBP_SkEncodedFormat != codec->getEncodedFormat() && + kWBMP_SkEncodedFormat != codec->getEncodedFormat() && + kUnpremul_SkAlphaType != alphaType) + { + push_image_gen_src(path, ImageGenSrc::kPlatform_Mode, alphaType, false); + } +#endif + } } static bool brd_color_type_supported(SkBitmapRegionDecoder::Strategy strategy, diff --git a/dm/DMSrcSink.cpp b/dm/DMSrcSink.cpp index 9e45ce9b69..84af854dea 100644 --- a/dm/DMSrcSink.cpp +++ b/dm/DMSrcSink.cpp @@ -14,6 +14,7 @@ #include "SkDocument.h" #include "SkError.h" #include "SkImageGenerator.h" +#include "SkImageGeneratorCG.h" #include "SkMallocPixelRef.h" #include "SkMultiPictureDraw.h" #include "SkNullCanvas.h" @@ -263,15 +264,7 @@ CodecSrc::CodecSrc(Path path, Mode mode, DstColorType dstColorType, SkAlphaType {} bool CodecSrc::veto(SinkFlags flags) const { - // Test CodecImageGenerator on 8888, 565, and gpu - if (kGen_Mode == fMode) { - // For image generator, we want to test kDirect approaches for kRaster and kGPU, - // while skipping everything else. - return (flags.type != SinkFlags::kRaster && flags.type != SinkFlags::kGPU) || - flags.approach != SinkFlags::kDirect; - } - - // Test all other modes to direct raster backends (8888 and 565). + // Test to direct raster backends (8888 and 565). return flags.type != SinkFlags::kRaster || flags.approach != SinkFlags::kDirect; } @@ -333,42 +326,12 @@ bool get_decode_info(SkImageInfo* decodeInfo, SkColorType canvasColorType, return true; } -Error test_gen(SkCanvas* canvas, SkData* data) { - SkAutoTDelete<SkImageGenerator> gen = SkCodecImageGenerator::NewFromEncodedCodec(data); - if (!gen) { - return "Could not create image generator."; - } - - // FIXME: The gpu backend does not draw kGray sources correctly. (skbug.com/4822) - // Currently, we will avoid creating a CodecSrc for this case (see DM.cpp). - SkASSERT(kGray_8_SkColorType != gen->getInfo().colorType()); - - if (kOpaque_SkAlphaType != gen->getInfo().alphaType() && - kRGB_565_SkColorType == canvas->imageInfo().colorType()) { - return Error::Nonfatal("Skip testing non-opaque images to 565."); - } - - SkAutoTDelete<SkImage> image(SkImage::NewFromGenerator(gen.detach(), nullptr)); - if (!image) { - return "Could not create image from codec image generator."; - } - - canvas->drawImage(image, 0, 0); - return ""; -} - Error CodecSrc::draw(SkCanvas* canvas) const { SkAutoTUnref<SkData> encoded(SkData::NewFromFileName(fPath.c_str())); if (!encoded) { return SkStringPrintf("Couldn't read %s.", fPath.c_str()); } - // The CodecImageGenerator test does not share much code with the other tests, - // so we will handle it in its own function. - if (kGen_Mode == fMode) { - return test_gen(canvas, encoded); - } - SkAutoTDelete<SkCodec> codec(SkCodec::NewFromData(encoded)); if (nullptr == codec.get()) { return SkStringPrintf("Couldn't create codec for %s.", fPath.c_str()); @@ -802,6 +765,118 @@ Name AndroidCodecSrc::name() const { /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ +ImageGenSrc::ImageGenSrc(Path path, Mode mode, SkAlphaType alphaType, bool isGpu) + : fPath(path) + , fMode(mode) + , fDstAlphaType(alphaType) + , fIsGpu(isGpu) + , fRunSerially(serial_from_path_name(path)) +{} + +bool ImageGenSrc::veto(SinkFlags flags) const { + if (fIsGpu) { + return flags.type != SinkFlags::kGPU || flags.approach != SinkFlags::kDirect; + } + + return flags.type != SinkFlags::kRaster || flags.approach != SinkFlags::kDirect; +} + +Error ImageGenSrc::draw(SkCanvas* canvas) const { + if (kRGB_565_SkColorType == canvas->imageInfo().colorType()) { + return Error::Nonfatal("Uninteresting to test image generator to 565."); + } + + SkAutoTUnref<SkData> encoded(SkData::NewFromFileName(fPath.c_str())); + if (!encoded) { + return SkStringPrintf("Couldn't read %s.", fPath.c_str()); + } + + SkAutoTDelete<SkImageGenerator> gen(nullptr); + switch (fMode) { + case kCodec_Mode: + gen.reset(SkCodecImageGenerator::NewFromEncodedCodec(encoded)); + if (!gen) { + return "Could not create codec image generator."; + } + break; +#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) + case kPlatform_Mode: + gen.reset(SkImageGeneratorCG::NewFromEncodedCG(encoded)); + if (!gen) { + return "Could not create CG image generator."; + } + break; +#endif + default: + SkASSERT(false); + return "Invalid image generator mode"; + } + + // Test deferred decoding path on GPU + if (fIsGpu) { + // FIXME: The gpu backend does not draw kGray sources correctly. (skbug.com/4822) + // We have disabled these tests in DM.cpp. + SkASSERT(kGray_8_SkColorType != gen->getInfo().colorType()); + + SkAutoTDelete<SkImage> image(SkImage::NewFromGenerator(gen.detach(), nullptr)); + if (!image) { + return "Could not create image from codec image generator."; + } + canvas->drawImage(image, 0, 0); + return ""; + } + + // Test various color and alpha types on CPU + SkImageInfo decodeInfo = gen->getInfo().makeAlphaType(fDstAlphaType); + + if (kGray_8_SkColorType == decodeInfo.colorType() && + kOpaque_SkAlphaType != decodeInfo.alphaType()) { + return Error::Nonfatal("Avoid requesting non-opaque kGray8 decodes."); + } + + SkAutoTUnref<SkColorTable> colorTable(nullptr); + SkPMColor* colorPtr = nullptr; + int* colorCountPtr = nullptr; + int maxColors = 256; + if (kIndex_8_SkColorType == decodeInfo.colorType()) { + SkPMColor colors[256]; + colorTable.reset(new SkColorTable(colors, maxColors)); + colorPtr = const_cast<SkPMColor*>(colorTable->readColors()); + colorCountPtr = &maxColors; + } + + SkBitmap bitmap; + if (!bitmap.tryAllocPixels(decodeInfo, nullptr, colorTable.get())) { + return SkStringPrintf("Image(%s) is too large (%d x %d)", fPath.c_str(), + decodeInfo.width(), decodeInfo.height()); + } + + if (!gen->getPixels(decodeInfo, bitmap.getPixels(), bitmap.rowBytes(), colorPtr, + colorCountPtr)) + { + return SkStringPrintf("Image generator could not getPixels() for %s\n", fPath.c_str()); + } + + premultiply_if_necessary(bitmap); + canvas->drawBitmap(bitmap, 0, 0); + return ""; +} + +SkISize ImageGenSrc::size() const { + SkAutoTUnref<SkData> encoded(SkData::NewFromFileName(fPath.c_str())); + SkAutoTDelete<SkCodec> codec(SkCodec::NewFromData(encoded)); + if (nullptr == codec) { + return SkISize::Make(0, 0); + } + return codec->getInfo().dimensions(); +} + +Name ImageGenSrc::name() const { + return SkOSPath::Basename(fPath.c_str()); +} + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + static const SkRect kSKPViewport = {0,0, 1000,1000}; SKPSrc::SKPSrc(Path path) : fPath(path) {} diff --git a/dm/DMSrcSink.h b/dm/DMSrcSink.h index 638d13824a..b6ee5f09d7 100644 --- a/dm/DMSrcSink.h +++ b/dm/DMSrcSink.h @@ -112,7 +112,6 @@ public: kStripe_Mode, // Tests the skipping of scanlines kCroppedScanline_Mode, // Tests (jpeg) cropped scanline optimization kSubset_Mode, // For codecs that support subsets directly. - kGen_Mode, // Test SkCodecImageGenerator (includes YUV) }; enum DstColorType { kGetFromCanvas_DstColorType, @@ -187,6 +186,27 @@ private: uint32_t fSampleSize; }; +class ImageGenSrc : public Src { +public: + enum Mode { + kCodec_Mode, // Use CodecImageGenerator + kPlatform_Mode, // Uses CG or WIC + }; + ImageGenSrc(Path, Mode, SkAlphaType, bool); + + Error draw(SkCanvas*) const override; + SkISize size() const override; + Name name() const override; + bool veto(SinkFlags) const override; + bool serial() const override { return fRunSerially; } +private: + Path fPath; + Mode fMode; + SkAlphaType fDstAlphaType; + bool fIsGpu; + bool fRunSerially; +}; + class SKPSrc : public Src { public: explicit SKPSrc(Path path); diff --git a/gyp/ports.gyp b/gyp/ports.gyp index 2c41ecfbf1..db72625b94 100644 --- a/gyp/ports.gyp +++ b/gyp/ports.gyp @@ -150,6 +150,8 @@ 'sources': [ '../src/ports/SkFontHost_mac.cpp', '../src/utils/mac/SkStream_mac.cpp', + + '../src/ports/SkImageGeneratorCG.cpp', ], }], [ 'skia_os == "ios"', { @@ -160,6 +162,8 @@ 'sources': [ '../src/ports/SkFontHost_mac.cpp', '../src/utils/mac/SkStream_mac.cpp', + + '../src/ports/SkImageGeneratorCG.cpp', ], }], [ 'skia_os == "win"', { diff --git a/src/ports/SkImageGeneratorCG.cpp b/src/ports/SkImageGeneratorCG.cpp new file mode 100644 index 0000000000..cf00ea703d --- /dev/null +++ b/src/ports/SkImageGeneratorCG.cpp @@ -0,0 +1,123 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkImageGeneratorCG.h" +#include "SkUnpremultiply.h" + +#ifdef SK_BUILD_FOR_MAC +#include <ApplicationServices/ApplicationServices.h> +#endif + +#ifdef SK_BUILD_FOR_IOS +#include <CoreGraphics/CoreGraphics.h> +#include <ImageIO/ImageIO.h> +#include <MobileCoreServices/MobileCoreServices.h> +#endif + +static CGImageSourceRef data_to_CGImageSrc(SkData* data) { + CGDataProviderRef cgData = CGDataProviderCreateWithData(data, data->data(), data->size(), + nullptr); + if (!cgData) { + return nullptr; + } + CGImageSourceRef imageSrc = CGImageSourceCreateWithDataProvider(cgData, 0); + CGDataProviderRelease(cgData); + return imageSrc; +} + +SkImageGenerator* SkImageGeneratorCG::NewFromEncodedCG(SkData* data) { + CGImageSourceRef imageSrc = data_to_CGImageSrc(data); + if (!imageSrc) { + return nullptr; + } + + // Make sure we call CFRelease to free the imageSrc. Since CFRelease actually takes + // a const void*, we must cast the imageSrc to a const void*. + SkAutoTCallVProc<const void, CFRelease> autoImageSrc(imageSrc); + + CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(imageSrc, 0, nullptr); + if (!properties) { + return nullptr; + } + + CFNumberRef widthRef = (CFNumberRef) (CFDictionaryGetValue(properties, + kCGImagePropertyPixelWidth)); + CFNumberRef heightRef = (CFNumberRef) (CFDictionaryGetValue(properties, + kCGImagePropertyPixelHeight)); + if (nullptr == widthRef || nullptr == heightRef) { + return nullptr; + } + bool hasAlpha = (bool) (CFDictionaryGetValue(properties, + kCGImagePropertyHasAlpha)); + + int width, height; + if (!CFNumberGetValue(widthRef, kCFNumberIntType, &width) || + !CFNumberGetValue(heightRef, kCFNumberIntType, &height)) { + return nullptr; + } + + SkAlphaType alphaType = hasAlpha ? kPremul_SkAlphaType : kOpaque_SkAlphaType; + SkImageInfo info = SkImageInfo::Make(width, height, kN32_SkColorType, alphaType); + + // FIXME: We have the opportunity to extract color space information here, + // though I think it makes sense to wait until we understand how + // we want to communicate it to the generator. + + return new SkImageGeneratorCG(info, autoImageSrc.detach(), data); +} + +SkImageGeneratorCG::SkImageGeneratorCG(const SkImageInfo& info, const void* imageSrc, SkData* data) + : INHERITED(info) + , fImageSrc(imageSrc) + , fData(SkRef(data)) +{} + +SkData* SkImageGeneratorCG::onRefEncodedData(SK_REFENCODEDDATA_CTXPARAM) { + return SkRef(fData.get()); +} + +bool SkImageGeneratorCG::onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, + SkPMColor ctable[], int* ctableCount) { + if (kN32_SkColorType != info.colorType()) { + // FIXME: Support other colorTypes. + return false; + } + + switch (info.alphaType()) { + case kOpaque_SkAlphaType: + if (kOpaque_SkAlphaType != this->getInfo().alphaType()) { + return false; + } + break; + case kPremul_SkAlphaType: + break; + default: + return false; + } + + CGImageRef image = CGImageSourceCreateImageAtIndex((CGImageSourceRef) fImageSrc.get(), 0, + nullptr); + if (!image) { + return false; + } + SkAutoTCallVProc<CGImage, CGImageRelease> autoImage(image); + + // FIXME: Using this function (as opposed to swizzling ourselves) greatly + // restricts the color and alpha types that we support. If we + // swizzle ourselves, we can add support for: + // kUnpremul_SkAlphaType + // 16-bit per component RGBA + // kGray_8_SkColorType + // kIndex_8_SkColorType + // Additionally, it would be interesting to compare the performance + // of SkSwizzler with CG's built in swizzler. + if (!SkCopyPixelsFromCGImage(info, rowBytes, pixels, image)) { + return false; + } + + return true; +} diff --git a/src/ports/SkImageGeneratorCG.h b/src/ports/SkImageGeneratorCG.h new file mode 100644 index 0000000000..cdfeae02b6 --- /dev/null +++ b/src/ports/SkImageGeneratorCG.h @@ -0,0 +1,43 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkTypes.h" +#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) + +#include "SkCGUtils.h" +#include "SkData.h" +#include "SkImageGenerator.h" +#include "SkTemplates.h" + +class SkImageGeneratorCG : public SkImageGenerator { +public: + /* + * Refs the data if an image generator can be returned. Otherwise does + * not affect the data. + */ + static SkImageGenerator* NewFromEncodedCG(SkData* data); + +protected: + SkData* onRefEncodedData(SK_REFENCODEDDATA_CTXPARAM) override; + + bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, SkPMColor ctable[], + int* ctableCount) override; + +private: + /* + * Takes ownership of the imageSrc + * Refs the data + */ + SkImageGeneratorCG(const SkImageInfo& info, const void* imageSrc, SkData* data); + + SkAutoTCallVProc<const void, CFRelease> fImageSrc; + SkAutoTUnref<SkData> fData; + + typedef SkImageGenerator INHERITED; +}; + +#endif //defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) diff --git a/tools/dm_flags.json b/tools/dm_flags.json index 5999c1ae3a..4edcff3b3d 100644 --- a/tools/dm_flags.json +++ b/tools/dm_flags.json @@ -38,6 +38,34 @@ "gm", "_", "tilemodesProcess", + "_", + "image", + "gen_platf", + "rgba32abf.bmp", + "_", + "image", + "gen_platf", + "rgb24prof.bmp", + "_", + "image", + "gen_platf", + "rgb24lprof.bmp", + "_", + "image", + "gen_platf", + "8bpp-pixeldata-cropped.bmp", + "_", + "image", + "gen_platf", + "4bpp-pixeldata-cropped.bmp", + "_", + "image", + "gen_platf", + "32bpp-pixeldata-cropped.bmp", + "_", + "image", + "gen_platf", + "24bpp-pixeldata-cropped.bmp", "serialize-8888", "gm", "_", @@ -1955,6 +1983,34 @@ "image", "_", "interlaced3.png", + "_", + "image", + "gen_platf", + "rgba32abf.bmp", + "_", + "image", + "gen_platf", + "rgb24prof.bmp", + "_", + "image", + "gen_platf", + "rgb24lprof.bmp", + "_", + "image", + "gen_platf", + "8bpp-pixeldata-cropped.bmp", + "_", + "image", + "gen_platf", + "4bpp-pixeldata-cropped.bmp", + "_", + "image", + "gen_platf", + "32bpp-pixeldata-cropped.bmp", + "_", + "image", + "gen_platf", + "24bpp-pixeldata-cropped.bmp", "serialize-8888", "gm", "_", diff --git a/tools/dm_flags.py b/tools/dm_flags.py index a790184968..0de32fce81 100755 --- a/tools/dm_flags.py +++ b/tools/dm_flags.py @@ -113,6 +113,16 @@ def get_args(bot): blacklist.extend('_ image _ interlaced2.png'.split(' ')) blacklist.extend('_ image _ interlaced3.png'.split(' ')) + # CG fails on questionable bmps + if 'Mac' in bot or 'iOS' in bot: + blacklist.extend('_ image gen_platf rgba32abf.bmp'.split(' ')) + blacklist.extend('_ image gen_platf rgb24prof.bmp'.split(' ')) + blacklist.extend('_ image gen_platf rgb24lprof.bmp'.split(' ')) + blacklist.extend('_ image gen_platf 8bpp-pixeldata-cropped.bmp'.split(' ')) + blacklist.extend('_ image gen_platf 4bpp-pixeldata-cropped.bmp'.split(' ')) + blacklist.extend('_ image gen_platf 32bpp-pixeldata-cropped.bmp'.split(' ')) + blacklist.extend('_ image gen_platf 24bpp-pixeldata-cropped.bmp'.split(' ')) + # skia:4095 for test in ['not_native32_bitmap_config', 'bleed_image', |