| 1 | /* |
| 2 | * Copyright 2014 Google Inc. |
| 3 | * |
| 4 | * Use of this source code is governed by a BSD-style license that can be |
| 5 | * found in the LICENSE file. |
| 6 | */ |
| 7 | |
| 8 | #include "include/private/SkColorData.h" |
| 9 | #include "include/private/SkHalf.h" |
| 10 | #include "include/private/SkImageInfoPriv.h" |
| 11 | #include "src/core/SkColorSpacePriv.h" |
| 12 | #include "src/core/SkColorSpaceXformSteps.h" |
| 13 | #include "src/core/SkConvertPixels.h" |
| 14 | #include "src/core/SkOpts.h" |
| 15 | #include "src/core/SkRasterPipeline.h" |
| 16 | |
| 17 | static bool rect_memcpy(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB, |
| 18 | const SkImageInfo& srcInfo, const void* srcPixels, size_t srcRB, |
| 19 | const SkColorSpaceXformSteps& steps) { |
| 20 | // We can copy the pixels when no color type, alpha type, or color space changes. |
| 21 | if (dstInfo.colorType() != srcInfo.colorType()) { |
| 22 | return false; |
| 23 | } |
| 24 | if (dstInfo.colorType() != kAlpha_8_SkColorType |
| 25 | && steps.flags.mask() != 0b00000) { |
| 26 | return false; |
| 27 | } |
| 28 | |
| 29 | SkRectMemcpy(dstPixels, dstRB, |
| 30 | srcPixels, srcRB, dstInfo.minRowBytes(), dstInfo.height()); |
| 31 | return true; |
| 32 | } |
| 33 | |
| 34 | static bool swizzle_or_premul(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB, |
| 35 | const SkImageInfo& srcInfo, const void* srcPixels, size_t srcRB, |
| 36 | const SkColorSpaceXformSteps& steps) { |
| 37 | auto is_8888 = [](SkColorType ct) { |
| 38 | return ct == kRGBA_8888_SkColorType || ct == kBGRA_8888_SkColorType; |
| 39 | }; |
| 40 | if (!is_8888(dstInfo.colorType()) || |
| 41 | !is_8888(srcInfo.colorType()) || |
| 42 | steps.flags.linearize || |
| 43 | steps.flags.gamut_transform || |
| 44 | steps.flags.unpremul || |
| 45 | steps.flags.encode) { |
| 46 | return false; |
| 47 | } |
| 48 | |
| 49 | const bool swapRB = dstInfo.colorType() != srcInfo.colorType(); |
| 50 | |
| 51 | void (*fn)(uint32_t*, const uint32_t*, int) = nullptr; |
| 52 | |
| 53 | if (steps.flags.premul) { |
| 54 | fn = swapRB ? SkOpts::RGBA_to_bgrA |
| 55 | : SkOpts::RGBA_to_rgbA; |
| 56 | } else { |
| 57 | // If we're not swizzling, we ought to have used rect_memcpy(). |
| 58 | SkASSERT(swapRB); |
| 59 | fn = SkOpts::RGBA_to_BGRA; |
| 60 | } |
| 61 | |
| 62 | for (int y = 0; y < dstInfo.height(); y++) { |
| 63 | fn((uint32_t*)dstPixels, (const uint32_t*)srcPixels, dstInfo.width()); |
| 64 | dstPixels = SkTAddOffset<void>(dstPixels, dstRB); |
| 65 | srcPixels = SkTAddOffset<const void>(srcPixels, srcRB); |
| 66 | } |
| 67 | return true; |
| 68 | } |
| 69 | |
| 70 | static bool convert_to_alpha8(const SkImageInfo& dstInfo, void* vdst, size_t dstRB, |
| 71 | const SkImageInfo& srcInfo, const void* src, size_t srcRB, |
| 72 | const SkColorSpaceXformSteps&) { |
| 73 | if (dstInfo.colorType() != kAlpha_8_SkColorType) { |
| 74 | return false; |
| 75 | } |
| 76 | auto dst = (uint8_t*)vdst; |
| 77 | |
| 78 | switch (srcInfo.colorType()) { |
| 79 | case kUnknown_SkColorType: |
| 80 | case kAlpha_8_SkColorType: { |
| 81 | // Unknown should never happen. |
| 82 | // Alpha8 should have been handled by rect_memcpy(). |
| 83 | SkASSERT(false); |
| 84 | return false; |
| 85 | } |
| 86 | |
| 87 | case kA16_unorm_SkColorType: { |
| 88 | auto src16 = (const uint16_t*) src; |
| 89 | for (int y = 0; y < srcInfo.height(); y++) { |
| 90 | for (int x = 0; x < srcInfo.width(); x++) { |
| 91 | dst[x] = src16[x] >> 8; |
| 92 | } |
| 93 | dst = SkTAddOffset<uint8_t>(dst, dstRB); |
| 94 | src16 = SkTAddOffset<const uint16_t>(src16, srcRB); |
| 95 | } |
| 96 | return true; |
| 97 | } |
| 98 | |
| 99 | case kGray_8_SkColorType: |
| 100 | case kRGB_565_SkColorType: |
| 101 | case kR8G8_unorm_SkColorType: |
| 102 | case kR16G16_unorm_SkColorType: |
| 103 | case kR16G16_float_SkColorType: |
| 104 | case kRGB_888x_SkColorType: |
| 105 | case kRGB_101010x_SkColorType: |
| 106 | case kBGR_101010x_SkColorType: { |
| 107 | for (int y = 0; y < srcInfo.height(); ++y) { |
| 108 | memset(dst, 0xFF, srcInfo.width()); |
| 109 | dst = SkTAddOffset<uint8_t>(dst, dstRB); |
| 110 | } |
| 111 | return true; |
| 112 | } |
| 113 | |
| 114 | case kARGB_4444_SkColorType: { |
| 115 | auto src16 = (const uint16_t*) src; |
| 116 | for (int y = 0; y < srcInfo.height(); y++) { |
| 117 | for (int x = 0; x < srcInfo.width(); x++) { |
| 118 | dst[x] = SkPacked4444ToA32(src16[x]); |
| 119 | } |
| 120 | dst = SkTAddOffset<uint8_t>(dst, dstRB); |
| 121 | src16 = SkTAddOffset<const uint16_t>(src16, srcRB); |
| 122 | } |
| 123 | return true; |
| 124 | } |
| 125 | |
| 126 | case kBGRA_8888_SkColorType: |
| 127 | case kRGBA_8888_SkColorType: { |
| 128 | auto src32 = (const uint32_t*) src; |
| 129 | for (int y = 0; y < srcInfo.height(); y++) { |
| 130 | for (int x = 0; x < srcInfo.width(); x++) { |
| 131 | dst[x] = src32[x] >> 24; |
| 132 | } |
| 133 | dst = SkTAddOffset<uint8_t>(dst, dstRB); |
| 134 | src32 = SkTAddOffset<const uint32_t>(src32, srcRB); |
| 135 | } |
| 136 | return true; |
| 137 | } |
| 138 | |
| 139 | case kRGBA_1010102_SkColorType: |
| 140 | case kBGRA_1010102_SkColorType: { |
| 141 | auto src32 = (const uint32_t*) src; |
| 142 | for (int y = 0; y < srcInfo.height(); y++) { |
| 143 | for (int x = 0; x < srcInfo.width(); x++) { |
| 144 | dst[x] = (src32[x] >> 30) * 0x55; |
| 145 | } |
| 146 | dst = SkTAddOffset<uint8_t>(dst, dstRB); |
| 147 | src32 = SkTAddOffset<const uint32_t>(src32, srcRB); |
| 148 | } |
| 149 | return true; |
| 150 | } |
| 151 | |
| 152 | case kRGBA_F16Norm_SkColorType: |
| 153 | case kRGBA_F16_SkColorType: { |
| 154 | auto src64 = (const uint64_t*) src; |
| 155 | for (int y = 0; y < srcInfo.height(); y++) { |
| 156 | for (int x = 0; x < srcInfo.width(); x++) { |
| 157 | dst[x] = (uint8_t) (255.0f * SkHalfToFloat(src64[x] >> 48)); |
| 158 | } |
| 159 | dst = SkTAddOffset<uint8_t>(dst, dstRB); |
| 160 | src64 = SkTAddOffset<const uint64_t>(src64, srcRB); |
| 161 | } |
| 162 | return true; |
| 163 | } |
| 164 | |
| 165 | case kRGBA_F32_SkColorType: { |
| 166 | auto rgba = (const float*)src; |
| 167 | for (int y = 0; y < srcInfo.height(); y++) { |
| 168 | for (int x = 0; x < srcInfo.width(); x++) { |
| 169 | dst[x] = (uint8_t)(255.0f * rgba[4*x+3]); |
| 170 | } |
| 171 | dst = SkTAddOffset<uint8_t>(dst, dstRB); |
| 172 | rgba = SkTAddOffset<const float>(rgba, srcRB); |
| 173 | } |
| 174 | return true; |
| 175 | } |
| 176 | |
| 177 | case kA16_float_SkColorType: { |
| 178 | auto srcF16 = (const uint16_t*) src; |
| 179 | for (int y = 0; y < srcInfo.height(); y++) { |
| 180 | for (int x = 0; x < srcInfo.width(); x++) { |
| 181 | dst[x] = (uint8_t) (255.0f * SkHalfToFloat(srcF16[x])); |
| 182 | } |
| 183 | dst = SkTAddOffset<uint8_t>(dst, dstRB); |
| 184 | srcF16 = SkTAddOffset<const uint16_t>(srcF16, srcRB); |
| 185 | } |
| 186 | return true; |
| 187 | } |
| 188 | |
| 189 | case kR16G16B16A16_unorm_SkColorType: { |
| 190 | auto src64 = (const uint64_t*) src; |
| 191 | for (int y = 0; y < srcInfo.height(); y++) { |
| 192 | for (int x = 0; x < srcInfo.width(); x++) { |
| 193 | dst[x] = (src64[x] >> 48) >> 8; |
| 194 | } |
| 195 | dst = SkTAddOffset<uint8_t>(dst, dstRB); |
| 196 | src64 = SkTAddOffset<const uint64_t>(src64, srcRB); |
| 197 | } |
| 198 | return true; |
| 199 | } |
| 200 | } |
| 201 | return false; |
| 202 | } |
| 203 | |
| 204 | // Default: Use the pipeline. |
| 205 | static void convert_with_pipeline(const SkImageInfo& dstInfo, void* dstRow, size_t dstRB, |
| 206 | const SkImageInfo& srcInfo, const void* srcRow, size_t srcRB, |
| 207 | const SkColorSpaceXformSteps& steps) { |
| 208 | |
| 209 | SkRasterPipeline_MemoryCtx src = { (void*)srcRow, (int)(srcRB / srcInfo.bytesPerPixel()) }, |
| 210 | dst = { (void*)dstRow, (int)(dstRB / dstInfo.bytesPerPixel()) }; |
| 211 | |
| 212 | SkRasterPipeline_<256> pipeline; |
| 213 | pipeline.append_load(srcInfo.colorType(), &src); |
| 214 | steps.apply(&pipeline); |
| 215 | |
| 216 | pipeline.append_gamut_clamp_if_normalized(dstInfo); |
| 217 | |
| 218 | pipeline.append_store(dstInfo.colorType(), &dst); |
| 219 | pipeline.run(0,0, srcInfo.width(), srcInfo.height()); |
| 220 | } |
| 221 | |
| 222 | void SkConvertPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB, |
| 223 | const SkImageInfo& srcInfo, const void* srcPixels, size_t srcRB) { |
| 224 | SkASSERT(dstInfo.dimensions() == srcInfo.dimensions()); |
| 225 | SkASSERT(SkImageInfoValidConversion(dstInfo, srcInfo)); |
| 226 | |
| 227 | SkColorSpaceXformSteps steps{srcInfo.colorSpace(), srcInfo.alphaType(), |
| 228 | dstInfo.colorSpace(), dstInfo.alphaType()}; |
| 229 | |
| 230 | for (auto fn : {rect_memcpy, swizzle_or_premul, convert_to_alpha8}) { |
| 231 | if (fn(dstInfo, dstPixels, dstRB, srcInfo, srcPixels, srcRB, steps)) { |
| 232 | return; |
| 233 | } |
| 234 | } |
| 235 | convert_with_pipeline(dstInfo, dstPixels, dstRB, srcInfo, srcPixels, srcRB, steps); |
| 236 | } |
| 237 | |