From 4ef01482025e2e629e35458aa214436d3b4138e8 Mon Sep 17 00:00:00 2001 From: Matt Sarett Date: Wed, 22 Mar 2017 12:53:27 -0400 Subject: Add support for writing icc profiles to the jpeg encoder Also, share the impl for skjpeg_error_mgr between the jpeg decoder and encoder. They are already identical anyway. BUG=skia: Change-Id: I4d67f28126388fef3057d62b6e0b203e21ed4afb Reviewed-on: https://skia-review.googlesource.com/10011 Reviewed-by: Leon Scroggins Commit-Queue: Matt Sarett --- src/codec/SkJpegCodec.cpp | 13 +- src/codec/SkJpegPriv.h | 36 +++++ src/codec/SkJpegUtility.h | 8 +- src/images/SkImageEncoderFns.h | 290 ++++++++++++++++++++++++++++++++++++++ src/images/SkJPEGImageEncoder.cpp | 19 ++- src/images/SkJPEGWriteUtility.h | 9 +- src/images/SkPNGImageEncoder.cpp | 20 +-- src/images/SkWEBPImageEncoder.cpp | 2 +- src/images/transform_scanline.h | 276 ------------------------------------ tests/CodecTest.cpp | 28 +++- 10 files changed, 383 insertions(+), 318 deletions(-) create mode 100644 src/codec/SkJpegPriv.h create mode 100644 src/images/SkImageEncoderFns.h delete mode 100644 src/images/transform_scanline.h diff --git a/src/codec/SkJpegCodec.cpp b/src/codec/SkJpegCodec.cpp index 55ee3b5dbc..624bc25845 100644 --- a/src/codec/SkJpegCodec.cpp +++ b/src/codec/SkJpegCodec.cpp @@ -44,9 +44,7 @@ static uint32_t get_endian_int(const uint8_t* data, bool littleEndian) { } const uint32_t kExifHeaderSize = 14; -const uint32_t kICCHeaderSize = 14; const uint32_t kExifMarker = JPEG_APP0 + 1; -const uint32_t kICCMarker = JPEG_APP0 + 2; static bool is_orientation_marker(jpeg_marker_struct* marker, SkCodec::Origin* orientation) { if (kExifMarker != marker->marker || marker->data_length < kExifHeaderSize) { @@ -112,11 +110,10 @@ static SkCodec::Origin get_exif_orientation(jpeg_decompress_struct* dinfo) { } static bool is_icc_marker(jpeg_marker_struct* marker) { - if (kICCMarker != marker->marker || marker->data_length < kICCHeaderSize) { + if (kICCMarker != marker->marker || marker->data_length < kICCMarkerHeaderSize) { return false; } - static const uint8_t kICCSig[] { 'I', 'C', 'C', '_', 'P', 'R', 'O', 'F', 'I', 'L', 'E', '\0' }; return !memcmp(marker->data, kICCSig, sizeof(kICCSig)); } @@ -160,8 +157,8 @@ static sk_sp get_icc_profile(jpeg_decompress_struct* dinfo) { return nullptr; } markerSequence[markerIndex] = marker; - SkASSERT(marker->data_length >= kICCHeaderSize); - totalBytes += marker->data_length - kICCHeaderSize; + SkASSERT(marker->data_length >= kICCMarkerHeaderSize); + totalBytes += marker->data_length - kICCMarkerHeaderSize; } } @@ -180,8 +177,8 @@ static sk_sp get_icc_profile(jpeg_decompress_struct* dinfo) { return nullptr; } - void* src = SkTAddOffset(marker->data, kICCHeaderSize); - size_t bytes = marker->data_length - kICCHeaderSize; + void* src = SkTAddOffset(marker->data, kICCMarkerHeaderSize); + size_t bytes = marker->data_length - kICCMarkerHeaderSize; memcpy(dst, src, bytes); dst = SkTAddOffset(dst, bytes); } diff --git a/src/codec/SkJpegPriv.h b/src/codec/SkJpegPriv.h new file mode 100644 index 0000000000..e4e5b12763 --- /dev/null +++ b/src/codec/SkJpegPriv.h @@ -0,0 +1,36 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + + +#ifndef SkJpegPriv_DEFINED +#define SkJpegPriv_DEFINED + +#include "SkStream.h" + +#include +// stdio is needed for jpeglib +#include + +extern "C" { + #include "jpeglib.h" + #include "jerror.h" +} + +static constexpr uint32_t kICCMarker = JPEG_APP0 + 2; +static constexpr uint32_t kICCMarkerHeaderSize = 14; +static constexpr uint8_t kICCSig[] = { + 'I', 'C', 'C', '_', 'P', 'R', 'O', 'F', 'I', 'L', 'E', '\0', +}; + +/* + * Error handling struct + */ +struct skjpeg_error_mgr : jpeg_error_mgr { + jmp_buf fJmpBuf; +}; + +#endif diff --git a/src/codec/SkJpegUtility.h b/src/codec/SkJpegUtility.h index 43391017b5..33f4fbda8b 100644 --- a/src/codec/SkJpegUtility.h +++ b/src/codec/SkJpegUtility.h @@ -9,6 +9,7 @@ #ifndef SkJpegUtility_codec_DEFINED #define SkJpegUtility_codec_DEFINED +#include "SkJpegPriv.h" #include "SkStream.h" #include @@ -20,13 +21,6 @@ extern "C" { #include "jerror.h" } -/* - * Error handling struct - */ -struct skjpeg_error_mgr : jpeg_error_mgr { - jmp_buf fJmpBuf; -}; - /* * Error handling function */ diff --git a/src/images/SkImageEncoderFns.h b/src/images/SkImageEncoderFns.h new file mode 100644 index 0000000000..5120570c48 --- /dev/null +++ b/src/images/SkImageEncoderFns.h @@ -0,0 +1,290 @@ +/* + * Copyright 2012 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkImageEncoderFns_DEFINED +#define SkImageEncoderFns_DEFINED + +/** + * Functions to transform scanlines between packed-pixel formats. + */ + +#include "SkBitmap.h" +#include "SkColor.h" +#include "SkColorPriv.h" +#include "SkICC.h" +#include "SkPreConfig.h" +#include "SkRasterPipeline.h" +#include "SkUnPreMultiply.h" +#include "SkUnPreMultiplyPriv.h" + +/** + * Function template for transforming scanlines. + * Transform 'width' pixels from 'src' buffer into 'dst' buffer, + * repacking color channel data as appropriate for the given transformation. + * 'bpp' is bytes per pixel in the 'src' buffer. + */ +typedef void (*transform_scanline_proc)(char* SK_RESTRICT dst, const char* SK_RESTRICT src, + int width, int bpp, const SkPMColor* colors); + +/** + * Identity transformation: just copy bytes from src to dst. + */ +static inline void transform_scanline_memcpy(char* SK_RESTRICT dst, const char* SK_RESTRICT src, + int width, int bpp, const SkPMColor*) { + memcpy(dst, src, width * bpp); +} + +static inline void transform_scanline_index8_opaque(char* SK_RESTRICT dst, + const char* SK_RESTRICT src, int width, int, + const SkPMColor* colors) { + for (int i = 0; i < width; i++) { + const uint32_t c = colors[(uint8_t)*src++]; + dst[0] = SkGetPackedR32(c); + dst[1] = SkGetPackedG32(c); + dst[2] = SkGetPackedB32(c); + dst += 3; + } +} + +static inline void transform_scanline_index8_unpremul(char* SK_RESTRICT dst, + const char* SK_RESTRICT src, int width, int, + const SkPMColor* colors) { + uint32_t* SK_RESTRICT dst32 = (uint32_t*) dst; + for (int i = 0; i < width; i++) { + // This function swizzles R and B on platforms where SkPMColor is BGRA. This is + // exactly what we want. + dst32[i] = SkSwizzle_RGBA_to_PMColor(colors[(uint8_t)*src++]); + } +} + +static inline void transform_scanline_gray(char* SK_RESTRICT dst, const char* SK_RESTRICT src, + int width, int, const SkPMColor* colors) { + for (int i = 0; i < width; i++) { + const uint8_t g = (uint8_t) *src++; + dst[0] = g; + dst[1] = g; + dst[2] = g; + dst += 3; + } +} + +/** + * Transform from kRGB_565_Config to 3-bytes-per-pixel RGB. + * Alpha channel data is not present in kRGB_565_Config format, so there is no + * alpha channel data to preserve. + */ +static inline void transform_scanline_565(char* SK_RESTRICT dst, const char* SK_RESTRICT src, + int width, int, const SkPMColor*) { + const uint16_t* srcP = (const uint16_t*)src; + for (int i = 0; i < width; i++) { + unsigned c = *srcP++; + *dst++ = SkPacked16ToR32(c); + *dst++ = SkPacked16ToG32(c); + *dst++ = SkPacked16ToB32(c); + } +} + +/** + * Transform from kRGBA_8888_SkColorType to 3-bytes-per-pixel RGB. + * Alpha channel data is abandoned. + */ +static inline void transform_scanline_RGBX(char* SK_RESTRICT dst, const char* SK_RESTRICT src, + int width, int, const SkPMColor*) { + const uint32_t* srcP = (const SkPMColor*)src; + for (int i = 0; i < width; i++) { + uint32_t c = *srcP++; + *dst++ = (c >> 0) & 0xFF; + *dst++ = (c >> 8) & 0xFF; + *dst++ = (c >> 16) & 0xFF; + } +} + +/** + * Transform from kBGRA_8888_SkColorType to 3-bytes-per-pixel RGB. + * Alpha channel data is abandoned. + */ +static inline void transform_scanline_BGRX(char* SK_RESTRICT dst, const char* SK_RESTRICT src, + int width, int, const SkPMColor*) { + const uint32_t* srcP = (const SkPMColor*)src; + for (int i = 0; i < width; i++) { + uint32_t c = *srcP++; + *dst++ = (c >> 16) & 0xFF; + *dst++ = (c >> 8) & 0xFF; + *dst++ = (c >> 0) & 0xFF; + } +} + +/** + * Transform from kARGB_4444_Config to 3-bytes-per-pixel RGB. + * Alpha channel data, if any, is abandoned. + */ +static inline void transform_scanline_444(char* SK_RESTRICT dst, const char* SK_RESTRICT src, + int width, int, const SkPMColor*) { + const SkPMColor16* srcP = (const SkPMColor16*)src; + for (int i = 0; i < width; i++) { + SkPMColor16 c = *srcP++; + *dst++ = SkPacked4444ToR32(c); + *dst++ = SkPacked4444ToG32(c); + *dst++ = SkPacked4444ToB32(c); + } +} + +/** + * Transform from legacy kPremul, kRGBA_8888_SkColorType to 4-bytes-per-pixel unpremultiplied RGBA. + */ +static inline void transform_scanline_rgbA(char* SK_RESTRICT dst, const char* SK_RESTRICT src, + int width, int, const SkPMColor*) { + SkUnpremultiplyRow((uint32_t*) dst, (const uint32_t*) src, width); +} + +/** + * Transform from legacy kPremul, kBGRA_8888_SkColorType to 4-bytes-per-pixel unpremultiplied RGBA. + */ +static inline void transform_scanline_bgrA(char* SK_RESTRICT dst, const char* SK_RESTRICT src, + int width, int, const SkPMColor*) { + SkUnpremultiplyRow((uint32_t*) dst, (const uint32_t*) src, width); +} + +template +static inline void transform_scanline_unpremultiply_sRGB(void* dst, const void* src, int width) { + SkRasterPipeline p; + p.append(SkRasterPipeline::load_8888, &src); + if (!kIsRGBA) { + p.append(SkRasterPipeline::swap_rb); + } + + p.append_from_srgb(kPremul_SkAlphaType); + p.append(SkRasterPipeline::unpremul); + p.append(SkRasterPipeline::to_srgb); + p.append(SkRasterPipeline::store_8888, &dst); + p.run(0, width); +} + +/** + * Transform from kPremul, kRGBA_8888_SkColorType to 4-bytes-per-pixel unpremultiplied RGBA. + */ +static inline void transform_scanline_srgbA(char* SK_RESTRICT dst, const char* SK_RESTRICT src, + int width, int, const SkPMColor*) { + transform_scanline_unpremultiply_sRGB(dst, src, width); +} + +/** + * Transform from kPremul, kBGRA_8888_SkColorType to 4-bytes-per-pixel unpremultiplied RGBA. + */ +static inline void transform_scanline_sbgrA(char* SK_RESTRICT dst, const char* SK_RESTRICT src, + int width, int, const SkPMColor*) { + transform_scanline_unpremultiply_sRGB(dst, src, width); +} + +/** + * Transform from kUnpremul, kBGRA_8888_SkColorType to 4-bytes-per-pixel unpremultiplied RGBA. + */ +static inline void transform_scanline_BGRA(char* SK_RESTRICT dst, const char* SK_RESTRICT src, + int width, int, const SkPMColor*) { + const uint32_t* srcP = (const SkPMColor*)src; + for (int i = 0; i < width; i++) { + uint32_t c = *srcP++; + *dst++ = (c >> 16) & 0xFF; + *dst++ = (c >> 8) & 0xFF; + *dst++ = (c >> 0) & 0xFF; + *dst++ = (c >> 24) & 0xFF; + } +} + +/** + * Transform from kARGB_8888_Config to 4-bytes-per-pixel RGBA, + * with scaling of RGB based on alpha channel. + */ +static inline void transform_scanline_4444(char* SK_RESTRICT dst, const char* SK_RESTRICT src, + int width, int, const SkPMColor*) { + const SkPMColor16* srcP = (const SkPMColor16*)src; + const SkUnPreMultiply::Scale* table = SkUnPreMultiply::GetScaleTable(); + + for (int i = 0; i < width; i++) { + SkPMColor16 c = *srcP++; + unsigned a = SkPacked4444ToA32(c); + unsigned r = SkPacked4444ToR32(c); + unsigned g = SkPacked4444ToG32(c); + unsigned b = SkPacked4444ToB32(c); + + if (0 != a && 255 != a) { + SkUnPreMultiply::Scale scale = table[a]; + r = SkUnPreMultiply::ApplyScale(scale, r); + g = SkUnPreMultiply::ApplyScale(scale, g); + b = SkUnPreMultiply::ApplyScale(scale, b); + } + *dst++ = r; + *dst++ = g; + *dst++ = b; + *dst++ = a; + } +} + +/** + * Transform from kRGBA_F16 to 8-bytes-per-pixel RGBA. + */ +static inline void transform_scanline_F16(char* SK_RESTRICT dst, const char* SK_RESTRICT src, + int width, int, const SkPMColor*) { + SkRasterPipeline p; + p.append(SkRasterPipeline::load_f16, (const void**) &src); + p.append(SkRasterPipeline::to_srgb); + p.append(SkRasterPipeline::store_u16_be, (void**) &dst); + p.run(0, width); +} + +/** + * Transform from kPremul, kRGBA_F16 to 8-bytes-per-pixel RGBA. + */ +static inline void transform_scanline_F16_premul(char* SK_RESTRICT dst, const char* SK_RESTRICT src, + int width, int, const SkPMColor*) { + SkRasterPipeline p; + p.append(SkRasterPipeline::load_f16, (const void**) &src); + p.append(SkRasterPipeline::unpremul); + p.append(SkRasterPipeline::to_srgb); + p.append(SkRasterPipeline::store_u16_be, (void**) &dst); + p.run(0, width); +} + +/** + * Transform from kRGBA_F16 to 4-bytes-per-pixel RGBA. + */ +static inline void transform_scanline_F16_to_8888(char* SK_RESTRICT dst, + const char* SK_RESTRICT src, int width, int, + const SkPMColor*) { + SkRasterPipeline p; + p.append(SkRasterPipeline::load_f16, (const void**) &src); + p.append(SkRasterPipeline::to_srgb); + p.append(SkRasterPipeline::store_8888, (void**) &dst); + p.run(0, width); +} + +/** + * Transform from kPremul, kRGBA_F16 to 4-bytes-per-pixel RGBA. + */ +static inline void transform_scanline_F16_premul_to_8888(char* SK_RESTRICT dst, + const char* SK_RESTRICT src, int width, + int, const SkPMColor*) { + SkRasterPipeline p; + p.append(SkRasterPipeline::load_f16, (const void**) &src); + p.append(SkRasterPipeline::unpremul); + p.append(SkRasterPipeline::to_srgb); + p.append(SkRasterPipeline::store_8888, (void**) &dst); + p.run(0, width); +} + +static inline sk_sp icc_from_color_space(const SkColorSpace& cs) { + SkColorSpaceTransferFn fn; + SkMatrix44 toXYZD50(SkMatrix44::kUninitialized_Constructor); + if (cs.isNumericalTransferFn(&fn) && cs.toXYZD50(&toXYZD50)) { + return SkICC::WriteToICC(fn, toXYZD50); + } + + // TODO: Should we support writing ICC profiles for additional color spaces? + return nullptr; +} + +#endif // SkImageEncoderFns_DEFINED diff --git a/src/images/SkJPEGImageEncoder.cpp b/src/images/SkJPEGImageEncoder.cpp index 1f8f0d6394..014a0ae160 100644 --- a/src/images/SkJPEGImageEncoder.cpp +++ b/src/images/SkJPEGImageEncoder.cpp @@ -10,10 +10,10 @@ #ifdef SK_HAS_JPEG_LIBRARY #include "SkColorPriv.h" +#include "SkImageEncoderFns.h" #include "SkJPEGWriteUtility.h" #include "SkStream.h" #include "SkTemplates.h" -#include "transform_scanline.h" #include @@ -141,6 +141,23 @@ bool SkEncodeImageAsJPEG(SkWStream* stream, const SkPixmap& pixmap, int quality) jpeg_start_compress(&cinfo, TRUE); + if (pixmap.colorSpace()) { + sk_sp icc = icc_from_color_space(*pixmap.colorSpace()); + if (icc) { + // Create a contiguous block of memory with the icc signature followed by the profile. + sk_sp markerData = + SkData::MakeUninitialized(kICCMarkerHeaderSize + icc->size()); + uint8_t* ptr = (uint8_t*) markerData->writable_data(); + memcpy(ptr, kICCSig, sizeof(kICCSig)); + ptr += sizeof(kICCSig); + *ptr++ = 1; // This is the first marker. + *ptr++ = 1; // Out of one total markers. + memcpy(ptr, icc->data(), icc->size()); + + jpeg_write_marker(&cinfo, kICCMarker, markerData->bytes(), markerData->size()); + } + } + if (proc) { storage.reset(numComponents * pixmap.width()); } diff --git a/src/images/SkJPEGWriteUtility.h b/src/images/SkJPEGWriteUtility.h index 91d07a3616..3765e7e674 100644 --- a/src/images/SkJPEGWriteUtility.h +++ b/src/images/SkJPEGWriteUtility.h @@ -9,6 +9,7 @@ #ifndef SkJpegUtility_DEFINED #define SkJpegUtility_DEFINED +#include "SkJpegPriv.h" #include "SkStream.h" extern "C" { @@ -18,14 +19,6 @@ extern "C" { #include -/* Our error-handling struct. - * -*/ -struct skjpeg_error_mgr : jpeg_error_mgr { - jmp_buf fJmpBuf; -}; - - void skjpeg_error_exit(j_common_ptr cinfo); ///////////////////////////////////////////////////////////////////////////// diff --git a/src/images/SkPNGImageEncoder.cpp b/src/images/SkPNGImageEncoder.cpp index 2eac91d0ca..e28ae12dc0 100644 --- a/src/images/SkPNGImageEncoder.cpp +++ b/src/images/SkPNGImageEncoder.cpp @@ -12,14 +12,13 @@ #include "SkColor.h" #include "SkColorPriv.h" #include "SkDither.h" -#include "SkICC.h" +#include "SkImageEncoderFns.h" #include "SkMath.h" #include "SkStream.h" #include "SkString.h" #include "SkTemplates.h" #include "SkUnPreMultiply.h" #include "SkUtils.h" -#include "transform_scanline.h" #include "png.h" @@ -40,9 +39,7 @@ static void sk_write_fn(png_structp png_ptr, png_bytep data, png_size_t len) { } } -static void set_icc(png_structp png_ptr, png_infop info_ptr, const SkColorSpaceTransferFn& fn, - const SkMatrix44& toXYZD50) { - sk_sp icc = SkICC::WriteToICC(fn, toXYZD50); +static void set_icc(png_structp png_ptr, png_infop info_ptr, sk_sp icc) { #if PNG_LIBPNG_VER_MAJOR > 1 || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 5) const char* name = "Skia"; png_const_bytep iccPtr = icc->bytes(); @@ -351,17 +348,14 @@ static bool do_encode(SkWStream* stream, const SkPixmap& pixmap, } if (pixmap.colorSpace()) { - SkColorSpaceTransferFn fn; - SkMatrix44 toXYZD50(SkMatrix44::kUninitialized_Constructor); if (pixmap.colorSpace()->isSRGB()) { png_set_sRGB(png_ptr, info_ptr, PNG_sRGB_INTENT_PERCEPTUAL); - } else if (pixmap.colorSpace()->isNumericalTransferFn(&fn) && - pixmap.colorSpace()->toXYZD50(&toXYZD50)) - { - set_icc(png_ptr, info_ptr, fn, toXYZD50); + } else { + sk_sp icc = icc_from_color_space(*pixmap.colorSpace()); + if (icc) { + set_icc(png_ptr, info_ptr, std::move(icc)); + } } - - // TODO: Should we support writing ICC profiles for additional color spaces? } png_set_sBIT(png_ptr, info_ptr, &sig_bit); diff --git a/src/images/SkWEBPImageEncoder.cpp b/src/images/SkWEBPImageEncoder.cpp index 8797ff5925..a9fcc31265 100644 --- a/src/images/SkWEBPImageEncoder.cpp +++ b/src/images/SkWEBPImageEncoder.cpp @@ -20,11 +20,11 @@ #include "SkBitmap.h" #include "SkColorPriv.h" +#include "SkImageEncoderFns.h" #include "SkStream.h" #include "SkTemplates.h" #include "SkUnPreMultiply.h" #include "SkUtils.h" -#include "transform_scanline.h" // A WebP decoder only, on top of (subset of) libwebp // For more information on WebP image format, and libwebp library, see: diff --git a/src/images/transform_scanline.h b/src/images/transform_scanline.h deleted file mode 100644 index 3c754275e0..0000000000 --- a/src/images/transform_scanline.h +++ /dev/null @@ -1,276 +0,0 @@ -/* - * Copyright 2012 The Android Open Source Project - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -#ifndef transform_scanline_DEFINED -#define transform_scanline_DEFINED - -/** - * Functions to transform scanlines between packed-pixel formats. - */ - -#include "SkBitmap.h" -#include "SkColor.h" -#include "SkColorPriv.h" -#include "SkPreConfig.h" -#include "SkRasterPipeline.h" -#include "SkUnPreMultiply.h" -#include "SkUnPreMultiplyPriv.h" - -/** - * Function template for transforming scanlines. - * Transform 'width' pixels from 'src' buffer into 'dst' buffer, - * repacking color channel data as appropriate for the given transformation. - * 'bpp' is bytes per pixel in the 'src' buffer. - */ -typedef void (*transform_scanline_proc)(char* SK_RESTRICT dst, const char* SK_RESTRICT src, - int width, int bpp, const SkPMColor* colors); - -/** - * Identity transformation: just copy bytes from src to dst. - */ -static inline void transform_scanline_memcpy(char* SK_RESTRICT dst, const char* SK_RESTRICT src, - int width, int bpp, const SkPMColor*) { - memcpy(dst, src, width * bpp); -} - -static inline void transform_scanline_index8_opaque(char* SK_RESTRICT dst, - const char* SK_RESTRICT src, int width, int, - const SkPMColor* colors) { - for (int i = 0; i < width; i++) { - const uint32_t c = colors[(uint8_t)*src++]; - dst[0] = SkGetPackedR32(c); - dst[1] = SkGetPackedG32(c); - dst[2] = SkGetPackedB32(c); - dst += 3; - } -} - -static inline void transform_scanline_index8_unpremul(char* SK_RESTRICT dst, - const char* SK_RESTRICT src, int width, int, - const SkPMColor* colors) { - uint32_t* SK_RESTRICT dst32 = (uint32_t*) dst; - for (int i = 0; i < width; i++) { - // This function swizzles R and B on platforms where SkPMColor is BGRA. This is - // exactly what we want. - dst32[i] = SkSwizzle_RGBA_to_PMColor(colors[(uint8_t)*src++]); - } -} - -static inline void transform_scanline_gray(char* SK_RESTRICT dst, const char* SK_RESTRICT src, - int width, int, const SkPMColor* colors) { - for (int i = 0; i < width; i++) { - const uint8_t g = (uint8_t) *src++; - dst[0] = g; - dst[1] = g; - dst[2] = g; - dst += 3; - } -} - -/** - * Transform from kRGB_565_Config to 3-bytes-per-pixel RGB. - * Alpha channel data is not present in kRGB_565_Config format, so there is no - * alpha channel data to preserve. - */ -static inline void transform_scanline_565(char* SK_RESTRICT dst, const char* SK_RESTRICT src, - int width, int, const SkPMColor*) { - const uint16_t* srcP = (const uint16_t*)src; - for (int i = 0; i < width; i++) { - unsigned c = *srcP++; - *dst++ = SkPacked16ToR32(c); - *dst++ = SkPacked16ToG32(c); - *dst++ = SkPacked16ToB32(c); - } -} - -/** - * Transform from kRGBA_8888_SkColorType to 3-bytes-per-pixel RGB. - * Alpha channel data is abandoned. - */ -static inline void transform_scanline_RGBX(char* SK_RESTRICT dst, const char* SK_RESTRICT src, - int width, int, const SkPMColor*) { - const uint32_t* srcP = (const SkPMColor*)src; - for (int i = 0; i < width; i++) { - uint32_t c = *srcP++; - *dst++ = (c >> 0) & 0xFF; - *dst++ = (c >> 8) & 0xFF; - *dst++ = (c >> 16) & 0xFF; - } -} - -/** - * Transform from kBGRA_8888_SkColorType to 3-bytes-per-pixel RGB. - * Alpha channel data is abandoned. - */ -static inline void transform_scanline_BGRX(char* SK_RESTRICT dst, const char* SK_RESTRICT src, - int width, int, const SkPMColor*) { - const uint32_t* srcP = (const SkPMColor*)src; - for (int i = 0; i < width; i++) { - uint32_t c = *srcP++; - *dst++ = (c >> 16) & 0xFF; - *dst++ = (c >> 8) & 0xFF; - *dst++ = (c >> 0) & 0xFF; - } -} - -/** - * Transform from kARGB_4444_Config to 3-bytes-per-pixel RGB. - * Alpha channel data, if any, is abandoned. - */ -static inline void transform_scanline_444(char* SK_RESTRICT dst, const char* SK_RESTRICT src, - int width, int, const SkPMColor*) { - const SkPMColor16* srcP = (const SkPMColor16*)src; - for (int i = 0; i < width; i++) { - SkPMColor16 c = *srcP++; - *dst++ = SkPacked4444ToR32(c); - *dst++ = SkPacked4444ToG32(c); - *dst++ = SkPacked4444ToB32(c); - } -} - -/** - * Transform from legacy kPremul, kRGBA_8888_SkColorType to 4-bytes-per-pixel unpremultiplied RGBA. - */ -static inline void transform_scanline_rgbA(char* SK_RESTRICT dst, const char* SK_RESTRICT src, - int width, int, const SkPMColor*) { - SkUnpremultiplyRow((uint32_t*) dst, (const uint32_t*) src, width); -} - -/** - * Transform from legacy kPremul, kBGRA_8888_SkColorType to 4-bytes-per-pixel unpremultiplied RGBA. - */ -static inline void transform_scanline_bgrA(char* SK_RESTRICT dst, const char* SK_RESTRICT src, - int width, int, const SkPMColor*) { - SkUnpremultiplyRow((uint32_t*) dst, (const uint32_t*) src, width); -} - -template -static inline void transform_scanline_unpremultiply_sRGB(void* dst, const void* src, int width) { - SkRasterPipeline p; - p.append(SkRasterPipeline::load_8888, &src); - if (!kIsRGBA) { - p.append(SkRasterPipeline::swap_rb); - } - - p.append_from_srgb(kPremul_SkAlphaType); - p.append(SkRasterPipeline::unpremul); - p.append(SkRasterPipeline::to_srgb); - p.append(SkRasterPipeline::store_8888, &dst); - p.run(0, width); -} - -/** - * Transform from kPremul, kRGBA_8888_SkColorType to 4-bytes-per-pixel unpremultiplied RGBA. - */ -static inline void transform_scanline_srgbA(char* SK_RESTRICT dst, const char* SK_RESTRICT src, - int width, int, const SkPMColor*) { - transform_scanline_unpremultiply_sRGB(dst, src, width); -} - -/** - * Transform from kPremul, kBGRA_8888_SkColorType to 4-bytes-per-pixel unpremultiplied RGBA. - */ -static inline void transform_scanline_sbgrA(char* SK_RESTRICT dst, const char* SK_RESTRICT src, - int width, int, const SkPMColor*) { - transform_scanline_unpremultiply_sRGB(dst, src, width); -} - -/** - * Transform from kUnpremul, kBGRA_8888_SkColorType to 4-bytes-per-pixel unpremultiplied RGBA. - */ -static inline void transform_scanline_BGRA(char* SK_RESTRICT dst, const char* SK_RESTRICT src, - int width, int, const SkPMColor*) { - const uint32_t* srcP = (const SkPMColor*)src; - for (int i = 0; i < width; i++) { - uint32_t c = *srcP++; - *dst++ = (c >> 16) & 0xFF; - *dst++ = (c >> 8) & 0xFF; - *dst++ = (c >> 0) & 0xFF; - *dst++ = (c >> 24) & 0xFF; - } -} - -/** - * Transform from kARGB_8888_Config to 4-bytes-per-pixel RGBA, - * with scaling of RGB based on alpha channel. - */ -static inline void transform_scanline_4444(char* SK_RESTRICT dst, const char* SK_RESTRICT src, - int width, int, const SkPMColor*) { - const SkPMColor16* srcP = (const SkPMColor16*)src; - const SkUnPreMultiply::Scale* table = SkUnPreMultiply::GetScaleTable(); - - for (int i = 0; i < width; i++) { - SkPMColor16 c = *srcP++; - unsigned a = SkPacked4444ToA32(c); - unsigned r = SkPacked4444ToR32(c); - unsigned g = SkPacked4444ToG32(c); - unsigned b = SkPacked4444ToB32(c); - - if (0 != a && 255 != a) { - SkUnPreMultiply::Scale scale = table[a]; - r = SkUnPreMultiply::ApplyScale(scale, r); - g = SkUnPreMultiply::ApplyScale(scale, g); - b = SkUnPreMultiply::ApplyScale(scale, b); - } - *dst++ = r; - *dst++ = g; - *dst++ = b; - *dst++ = a; - } -} - -/** - * Transform from kRGBA_F16 to 8-bytes-per-pixel RGBA. - */ -static inline void transform_scanline_F16(char* SK_RESTRICT dst, const char* SK_RESTRICT src, - int width, int, const SkPMColor*) { - SkRasterPipeline p; - p.append(SkRasterPipeline::load_f16, (const void**) &src); - p.append(SkRasterPipeline::to_srgb); - p.append(SkRasterPipeline::store_u16_be, (void**) &dst); - p.run(0, width); -} - -/** - * Transform from kPremul, kRGBA_F16 to 8-bytes-per-pixel RGBA. - */ -static inline void transform_scanline_F16_premul(char* SK_RESTRICT dst, const char* SK_RESTRICT src, - int width, int, const SkPMColor*) { - SkRasterPipeline p; - p.append(SkRasterPipeline::load_f16, (const void**) &src); - p.append(SkRasterPipeline::unpremul); - p.append(SkRasterPipeline::to_srgb); - p.append(SkRasterPipeline::store_u16_be, (void**) &dst); - p.run(0, width); -} - -/** - * Transform from kRGBA_F16 to 4-bytes-per-pixel RGBA. - */ -static inline void transform_scanline_F16_to_8888(char* SK_RESTRICT dst, - const char* SK_RESTRICT src, int width, int, - const SkPMColor*) { - SkRasterPipeline p; - p.append(SkRasterPipeline::load_f16, (const void**) &src); - p.append(SkRasterPipeline::to_srgb); - p.append(SkRasterPipeline::store_8888, (void**) &dst); - p.run(0, width); -} - -/** - * Transform from kPremul, kRGBA_F16 to 4-bytes-per-pixel RGBA. - */ -static inline void transform_scanline_F16_premul_to_8888(char* SK_RESTRICT dst, - const char* SK_RESTRICT src, int width, - int, const SkPMColor*) { - SkRasterPipeline p; - p.append(SkRasterPipeline::load_f16, (const void**) &src); - p.append(SkRasterPipeline::unpremul); - p.append(SkRasterPipeline::to_srgb); - p.append(SkRasterPipeline::store_8888, (void**) &dst); - p.run(0, width); -} -#endif // transform_scanline_DEFINED diff --git a/tests/CodecTest.cpp b/tests/CodecTest.cpp index 65e5475e54..da75ffb414 100644 --- a/tests/CodecTest.cpp +++ b/tests/CodecTest.cpp @@ -1530,7 +1530,22 @@ DEF_TEST(Codec_InvalidAnimated, r) { } } -DEF_TEST(Codec_EncodeICC, r) { +static void encode_format(SkDynamicMemoryWStream* stream, const SkPixmap& pixmap, + const SkEncodeOptions& opts, SkEncodedImageFormat format) { + switch (format) { + case SkEncodedImageFormat::kPNG: + SkEncodeImageAsPNG(stream, pixmap, opts); + break; + case SkEncodedImageFormat::kJPEG: + SkEncodeImageAsJPEG(stream, pixmap, opts); + break; + default: + SkASSERT(false); + break; + } +} + +static void test_encode_icc(skiatest::Reporter* r, SkEncodedImageFormat format) { // Test with sRGB color space. SkBitmap srgbBitmap; SkImageInfo srgbInfo = SkImageInfo::MakeS32(1, 1, kOpaque_SkAlphaType); @@ -1541,7 +1556,7 @@ DEF_TEST(Codec_EncodeICC, r) { SkDynamicMemoryWStream srgbBuf; SkEncodeOptions opts; opts.fColorBehavior = SkEncodeOptions::ColorBehavior::kCorrect; - SkEncodeImageAsPNG(&srgbBuf, pixmap, opts); + encode_format(&srgbBuf, pixmap, opts, format); sk_sp srgbData = srgbBuf.detachAsData(); std::unique_ptr srgbCodec(SkCodec::NewFromData(srgbData)); REPORTER_ASSERT(r, srgbCodec->getInfo().colorSpace() == SkColorSpace::MakeSRGB().get()); @@ -1551,7 +1566,7 @@ DEF_TEST(Codec_EncodeICC, r) { sk_sp p3 = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, SkColorSpace::kDCIP3_D65_Gamut); pixmap.setColorSpace(p3); - SkEncodeImageAsPNG(&p3Buf, pixmap, opts); + encode_format(&p3Buf, pixmap, opts, format); sk_sp p3Data = p3Buf.detachAsData(); std::unique_ptr p3Codec(SkCodec::NewFromData(p3Data)); REPORTER_ASSERT(r, p3Codec->getInfo().colorSpace()->gammaCloseToSRGB()); @@ -1564,7 +1579,12 @@ DEF_TEST(Codec_EncodeICC, r) { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { - REPORTER_ASSERT(r, color_space_almost_equal(mat0.get(0, 0), mat1.get(0, 0))); + REPORTER_ASSERT(r, color_space_almost_equal(mat0.get(i, j), mat1.get(i, j))); } } } + +DEF_TEST(Codec_EncodeICC, r) { + test_encode_icc(r, SkEncodedImageFormat::kPNG); + test_encode_icc(r, SkEncodedImageFormat::kJPEG); +} -- cgit v1.2.3