/* * 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 "SkColorData.h" #include "SkICC.h" #include "SkOpts.h" #include "SkPreConfig.h" #include "SkRasterPipeline.h" #include "SkUnPreMultiply.h" #include "SkUnPreMultiplyPriv.h" #include "../jumper/SkJumper.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 kAlpha_8_Config to 2-bytes-per-pixel GrayAlpha. */ static inline void transform_scanline_A8_to_GrayAlpha(char* SK_RESTRICT dst, const char* SK_RESTRICT src, int width, int, const SkPMColor*) { for (int i = 0; i < width; i++) { *dst++ = 0; // gray (ignored) *dst++ = *src++; // alpha } } /** * 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<false>((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<true>((uint32_t*) dst, (const uint32_t*) src, width); } template <bool kIsRGBA> static inline void transform_scanline_unpremultiply_sRGB(void* dst, const void* src, int width) { SkJumper_MemoryCtx src_ctx = { (void*)src, 0 }, dst_ctx = { (void*)dst, 0 }; SkRasterPipeline_<256> p; if (kIsRGBA) { p.append(SkRasterPipeline::load_8888, &src_ctx); } else { p.append(SkRasterPipeline::load_bgra, &src_ctx); } p.append(SkRasterPipeline::from_srgb); p.append(SkRasterPipeline::unpremul); p.append(SkRasterPipeline::to_srgb); p.append(SkRasterPipeline::store_8888, &dst_ctx); p.run(0,0, width,1); } /** * Premultiply RGBA to rgbA. */ static inline void transform_scanline_to_premul_legacy(char* SK_RESTRICT dst, const char* SK_RESTRICT src, int width, int, const SkPMColor*) { SkOpts::RGBA_to_rgbA((uint32_t*)dst, (const uint32_t*)src, width); } /** * Premultiply RGBA to rgbA linearly. */ static inline void transform_scanline_to_premul_linear(char* SK_RESTRICT dst, const char* SK_RESTRICT src, int width, int, const SkPMColor*) { SkJumper_MemoryCtx src_ctx = { (void*)src, 0 }, dst_ctx = { (void*)dst, 0 }; SkRasterPipeline_<256> p; p.append(SkRasterPipeline::load_8888, &src_ctx); p.append(SkRasterPipeline::from_srgb); p.append(SkRasterPipeline::premul); p.append(SkRasterPipeline::to_srgb); p.append(SkRasterPipeline::store_8888, &dst_ctx); p.run(0,0, width,1); } /** * 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<true>(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<false>(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; } } // 888x is opaque RGB in four bytes, with 8 junk bits. We convert that to 3 byte RGB. static inline void transform_scanline_888x(char* dst, const char* src, int width, int, const SkPMColor*) { while (width --> 0) { dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; dst += 3; src += 4; } } // 101010x is opaque RGB in four bytes, with 2 bits junk. We convert to 6 byte RGB (big endian). static inline void transform_scanline_101010x(char* dst, const char* src, int width, int, const SkPMColor*) { auto d = ( uint16_t*)dst; auto s = (const uint32_t*)src; while (width --> 0) { uint32_t r = (*s >> 0) & 1023, g = (*s >> 10) & 1023, b = (*s >> 20) & 1023; // Scale 10-bit unorms to 16-bit by replicating the most significant bits. r = (r << 6) | (r >> 4); g = (g << 6) | (g >> 4); b = (b << 6) | (b >> 4); // Store big-endian. d[0] = (r >> 8) | (r << 8); d[1] = (g >> 8) | (g << 8); d[2] = (b >> 8) | (b << 8); d += 3; // 3 channels s += 1; // 1 whole pixel } } static inline void transform_scanline_1010102(char* dst, const char* src, int width, int, const SkPMColor*) { SkJumper_MemoryCtx src_ctx = { (void*)src, 0 }, dst_ctx = { (void*)dst, 0 }; SkRasterPipeline_<256> p; p.append(SkRasterPipeline::load_1010102, &src_ctx); p.append(SkRasterPipeline::store_u16_be, &dst_ctx); p.run(0,0, width,1); } static inline void transform_scanline_1010102_premul(char* dst, const char* src, int width, int, const SkPMColor*) { SkJumper_MemoryCtx src_ctx = { (void*)src, 0 }, dst_ctx = { (void*)dst, 0 }; SkRasterPipeline_<256> p; p.append(SkRasterPipeline::load_1010102, &src_ctx); p.append(SkRasterPipeline::unpremul); p.append(SkRasterPipeline::store_u16_be, &dst_ctx); p.run(0,0, width,1); } /** * 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*) { SkJumper_MemoryCtx src_ctx = { (void*)src, 0 }, dst_ctx = { (void*)dst, 0 }; SkRasterPipeline_<256> p; p.append(SkRasterPipeline::load_f16, &src_ctx); p.append(SkRasterPipeline::clamp_0); // F16 values may be out of [0,1] range, so clamp. p.append(SkRasterPipeline::clamp_1); p.append(SkRasterPipeline::to_srgb); p.append(SkRasterPipeline::store_u16_be, &dst_ctx); p.run(0,0, width,1); } /** * 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*) { SkJumper_MemoryCtx src_ctx = { (void*)src, 0 }, dst_ctx = { (void*)dst, 0 }; SkRasterPipeline_<256> p; p.append(SkRasterPipeline::load_f16, &src_ctx); p.append(SkRasterPipeline::unpremul); p.append(SkRasterPipeline::clamp_0); // F16 values may be out of [0,1] range, so clamp. p.append(SkRasterPipeline::clamp_1); p.append(SkRasterPipeline::to_srgb); p.append(SkRasterPipeline::store_u16_be, &dst_ctx); p.run(0,0, width,1); } /** * 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*) { SkJumper_MemoryCtx src_ctx = { (void*)src, 0 }, dst_ctx = { (void*)dst, 0 }; SkRasterPipeline_<256> p; p.append(SkRasterPipeline::load_f16, &src_ctx); p.append(SkRasterPipeline::clamp_0); // F16 values may be out of [0,1] range, so clamp. p.append(SkRasterPipeline::clamp_1); p.append(SkRasterPipeline::to_srgb); p.append(SkRasterPipeline::store_8888, &dst_ctx); p.run(0,0, width,1); } /** * 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*) { SkJumper_MemoryCtx src_ctx = { (void*)src, 0 }, dst_ctx = { (void*)dst, 0 }; SkRasterPipeline_<256> p; p.append(SkRasterPipeline::load_f16, &src_ctx); p.append(SkRasterPipeline::unpremul); p.append(SkRasterPipeline::clamp_0); // F16 values may be out of [0,1] range, so clamp. p.append(SkRasterPipeline::clamp_1); p.append(SkRasterPipeline::to_srgb); p.append(SkRasterPipeline::store_8888, &dst_ctx); p.run(0,0, width,1); } /** * Transform from kUnpremul, kRGBA_F16 to premultiplied rgbA 8888. */ static inline void transform_scanline_F16_to_premul_8888(char* SK_RESTRICT dst, const char* SK_RESTRICT src, int width, int, const SkPMColor*) { SkJumper_MemoryCtx src_ctx = { (void*)src, 0 }, dst_ctx = { (void*)dst, 0 }; SkRasterPipeline_<256> p; p.append(SkRasterPipeline::load_f16, &src_ctx); p.append(SkRasterPipeline::clamp_0); // F16 values may be out of [0,1] range, so clamp. p.append(SkRasterPipeline::clamp_1); p.append(SkRasterPipeline::premul); p.append(SkRasterPipeline::to_srgb); p.append(SkRasterPipeline::store_8888, &dst_ctx); p.run(0,0, width,1); } static inline sk_sp<SkData> icc_from_color_space(const SkImageInfo& info) { SkColorSpace* cs = info.colorSpace(); if (!cs) { return nullptr; } sk_sp<SkColorSpace> owned; if (kRGBA_F16_SkColorType == info.colorType()) { owned = cs->makeSRGBGamma(); cs = owned.get(); } 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