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 "SkColorSpaceXform_Base.h"
9 #include "SkColorSpaceXformPriv.h"
10 #include "SkColorTable.h"
11 #include "SkConvertPixels.h"
12 #include "SkHalf.h"
13 #include "SkImageInfoPriv.h"
14 #include "SkOpts.h"
15 #include "SkPM4fPriv.h"
16 #include "SkRasterPipeline.h"
17 #include "SkUnPreMultiply.h"
18 #include "SkUnPreMultiplyPriv.h"
19
20 // Fast Path 1: The memcpy() case.
can_memcpy(const SkImageInfo & dstInfo,const SkImageInfo & srcInfo)21 static inline bool can_memcpy(const SkImageInfo& dstInfo, const SkImageInfo& srcInfo) {
22 if (dstInfo.colorType() != srcInfo.colorType()) {
23 return false;
24 }
25
26 if (kAlpha_8_SkColorType == dstInfo.colorType()) {
27 return true;
28 }
29
30 if (dstInfo.alphaType() != srcInfo.alphaType() &&
31 kOpaque_SkAlphaType != dstInfo.alphaType() &&
32 kOpaque_SkAlphaType != srcInfo.alphaType())
33 {
34 // We need to premultiply or unpremultiply.
35 return false;
36 }
37
38 return !dstInfo.colorSpace() ||
39 SkColorSpace::Equals(dstInfo.colorSpace(), srcInfo.colorSpace());
40 }
41
42 // Fast Path 2: Simple swizzles and premuls.
43 enum AlphaVerb {
44 kNothing_AlphaVerb,
45 kPremul_AlphaVerb,
46 kUnpremul_AlphaVerb,
47 };
48
49 template <bool kSwapRB>
wrap_unpremultiply(uint32_t * dst,const void * src,int count)50 static void wrap_unpremultiply(uint32_t* dst, const void* src, int count) {
51 SkUnpremultiplyRow<kSwapRB>(dst, (const uint32_t*) src, count);
52 }
53
swizzle_and_multiply(const SkImageInfo & dstInfo,void * dstPixels,size_t dstRB,const SkImageInfo & srcInfo,const void * srcPixels,size_t srcRB)54 void swizzle_and_multiply(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB,
55 const SkImageInfo& srcInfo, const void* srcPixels, size_t srcRB) {
56 void (*proc)(uint32_t* dst, const void* src, int count);
57 const bool swapRB = dstInfo.colorType() != srcInfo.colorType();
58 AlphaVerb alphaVerb = kNothing_AlphaVerb;
59 if (kPremul_SkAlphaType == dstInfo.alphaType() &&
60 kUnpremul_SkAlphaType == srcInfo.alphaType())
61 {
62 alphaVerb = kPremul_AlphaVerb;
63 } else if (kUnpremul_SkAlphaType == dstInfo.alphaType() &&
64 kPremul_SkAlphaType == srcInfo.alphaType()) {
65 alphaVerb = kUnpremul_AlphaVerb;
66 }
67
68 switch (alphaVerb) {
69 case kNothing_AlphaVerb:
70 // If we do not need to swap or multiply, we should hit the memcpy case.
71 SkASSERT(swapRB);
72 proc = SkOpts::RGBA_to_BGRA;
73 break;
74 case kPremul_AlphaVerb:
75 proc = swapRB ? SkOpts::RGBA_to_bgrA : SkOpts::RGBA_to_rgbA;
76 break;
77 case kUnpremul_AlphaVerb:
78 proc = swapRB ? wrap_unpremultiply<true> : wrap_unpremultiply<false>;
79 break;
80 }
81
82 for (int y = 0; y < dstInfo.height(); y++) {
83 proc((uint32_t*) dstPixels, srcPixels, dstInfo.width());
84 dstPixels = SkTAddOffset<void>(dstPixels, dstRB);
85 srcPixels = SkTAddOffset<const void>(srcPixels, srcRB);
86 }
87 }
88
89 // Fast Path 3: Color space xform.
optimized_color_xform(const SkImageInfo & dstInfo,const SkImageInfo & srcInfo,SkTransferFunctionBehavior behavior)90 static inline bool optimized_color_xform(const SkImageInfo& dstInfo, const SkImageInfo& srcInfo,
91 SkTransferFunctionBehavior behavior) {
92 // Unpremultiplication is unsupported by SkColorSpaceXform. Note that if |src| is non-linearly
93 // premultiplied, we're always going to have to unpremultiply before doing anything.
94 if (kPremul_SkAlphaType == srcInfo.alphaType() &&
95 (kUnpremul_SkAlphaType == dstInfo.alphaType() ||
96 SkTransferFunctionBehavior::kIgnore == behavior)) {
97 return false;
98 }
99
100 switch (dstInfo.colorType()) {
101 case kRGBA_8888_SkColorType:
102 case kBGRA_8888_SkColorType:
103 case kRGBA_F16_SkColorType:
104 break;
105 default:
106 return false;
107 }
108
109 switch (srcInfo.colorType()) {
110 case kRGBA_8888_SkColorType:
111 case kBGRA_8888_SkColorType:
112 break;
113 default:
114 return false;
115 }
116
117 return true;
118 }
119
apply_color_xform(const SkImageInfo & dstInfo,void * dstPixels,size_t dstRB,const SkImageInfo & srcInfo,const void * srcPixels,size_t srcRB,SkTransferFunctionBehavior behavior)120 static inline void apply_color_xform(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB,
121 const SkImageInfo& srcInfo, const void* srcPixels,
122 size_t srcRB, SkTransferFunctionBehavior behavior) {
123 SkColorSpaceXform::ColorFormat dstFormat = select_xform_format(dstInfo.colorType());
124 SkColorSpaceXform::ColorFormat srcFormat = select_xform_format(srcInfo.colorType());
125 SkAlphaType xformAlpha;
126 switch (srcInfo.alphaType()) {
127 case kOpaque_SkAlphaType:
128 xformAlpha = kOpaque_SkAlphaType;
129 break;
130 case kPremul_SkAlphaType:
131 SkASSERT(kPremul_SkAlphaType == dstInfo.alphaType());
132
133 // This signal means: copy the src alpha to the dst, do not premultiply (in this
134 // case because the pixels are already premultiplied).
135 xformAlpha = kUnpremul_SkAlphaType;
136 break;
137 case kUnpremul_SkAlphaType:
138 SkASSERT(kPremul_SkAlphaType == dstInfo.alphaType() ||
139 kUnpremul_SkAlphaType == dstInfo.alphaType());
140
141 xformAlpha = dstInfo.alphaType();
142 break;
143 default:
144 SkASSERT(false);
145 xformAlpha = kUnpremul_SkAlphaType;
146 break;
147 }
148
149 std::unique_ptr<SkColorSpaceXform> xform =
150 SkColorSpaceXform_Base::New(srcInfo.colorSpace(), dstInfo.colorSpace(), behavior);
151 SkASSERT(xform);
152
153 for (int y = 0; y < dstInfo.height(); y++) {
154 SkAssertResult(xform->apply(dstFormat, dstPixels, srcFormat, srcPixels, dstInfo.width(),
155 xformAlpha));
156 dstPixels = SkTAddOffset<void>(dstPixels, dstRB);
157 srcPixels = SkTAddOffset<const void>(srcPixels, srcRB);
158 }
159 }
160
161 // Fast Path 4: Index 8 sources.
162 template <typename T>
do_index8(const SkImageInfo & dstInfo,T * dstPixels,size_t dstRB,const SkImageInfo & srcInfo,const uint8_t * srcPixels,size_t srcRB,SkColorTable * ctable,SkTransferFunctionBehavior behavior)163 void do_index8(const SkImageInfo& dstInfo, T* dstPixels, size_t dstRB,
164 const SkImageInfo& srcInfo, const uint8_t* srcPixels, size_t srcRB,
165 SkColorTable* ctable, SkTransferFunctionBehavior behavior) {
166 T dstCTable[256];
167 int count = ctable->count();
168 SkImageInfo srcInfo8888 = srcInfo.makeColorType(kN32_SkColorType).makeWH(count, 1);
169 SkImageInfo dstInfoCT = dstInfo.makeWH(count, 1);
170 size_t rowBytes = count * sizeof(T);
171 SkConvertPixels(dstInfoCT, dstCTable, rowBytes, srcInfo8888, ctable->readColors(), rowBytes,
172 nullptr, behavior);
173
174 for (int y = 0; y < dstInfo.height(); y++) {
175 for (int x = 0; x < dstInfo.width(); x++) {
176 dstPixels[x] = dstCTable[srcPixels[x]];
177 }
178 dstPixels = SkTAddOffset<T>(dstPixels, dstRB);
179 srcPixels = SkTAddOffset<const uint8_t>(srcPixels, srcRB);
180 }
181 }
182
convert_from_index8(const SkImageInfo & dstInfo,void * dstPixels,size_t dstRB,const SkImageInfo & srcInfo,const uint8_t * srcPixels,size_t srcRB,SkColorTable * ctable,SkTransferFunctionBehavior behavior)183 void convert_from_index8(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB,
184 const SkImageInfo& srcInfo, const uint8_t* srcPixels, size_t srcRB,
185 SkColorTable* ctable, SkTransferFunctionBehavior behavior) {
186 switch (dstInfo.colorType()) {
187 case kAlpha_8_SkColorType:
188 do_index8(dstInfo, (uint8_t*) dstPixels, dstRB, srcInfo, srcPixels, srcRB, ctable,
189 behavior);
190 break;
191 case kRGB_565_SkColorType:
192 case kARGB_4444_SkColorType:
193 do_index8(dstInfo, (uint16_t*) dstPixels, dstRB, srcInfo, srcPixels, srcRB, ctable,
194 behavior);
195 break;
196 case kRGBA_8888_SkColorType:
197 case kBGRA_8888_SkColorType:
198 do_index8(dstInfo, (uint32_t*) dstPixels, dstRB, srcInfo, srcPixels, srcRB, ctable,
199 behavior);
200 break;
201 case kRGBA_F16_SkColorType:
202 do_index8(dstInfo, (uint64_t*) dstPixels, dstRB, srcInfo, srcPixels, srcRB, ctable,
203 behavior);
204 break;
205 default:
206 SkASSERT(false);
207 }
208 }
209
210 // Fast Path 5: Alpha 8 dsts.
convert_to_alpha8(uint8_t * dst,size_t dstRB,const SkImageInfo & srcInfo,const void * src,size_t srcRB,SkColorTable * ctable)211 static void convert_to_alpha8(uint8_t* dst, size_t dstRB, const SkImageInfo& srcInfo,
212 const void* src, size_t srcRB, SkColorTable* ctable) {
213 if (srcInfo.isOpaque()) {
214 for (int y = 0; y < srcInfo.height(); ++y) {
215 memset(dst, 0xFF, srcInfo.width());
216 dst = SkTAddOffset<uint8_t>(dst, dstRB);
217 }
218 return;
219 }
220
221 switch (srcInfo.colorType()) {
222 case kBGRA_8888_SkColorType:
223 case kRGBA_8888_SkColorType: {
224 auto src32 = (const uint32_t*) src;
225 for (int y = 0; y < srcInfo.height(); y++) {
226 for (int x = 0; x < srcInfo.width(); x++) {
227 dst[x] = src32[x] >> 24;
228 }
229 dst = SkTAddOffset<uint8_t>(dst, dstRB);
230 src32 = SkTAddOffset<const uint32_t>(src32, srcRB);
231 }
232 break;
233 }
234 case kARGB_4444_SkColorType: {
235 auto src16 = (const uint16_t*) src;
236 for (int y = 0; y < srcInfo.height(); y++) {
237 for (int x = 0; x < srcInfo.width(); x++) {
238 dst[x] = SkPacked4444ToA32(src16[x]);
239 }
240 dst = SkTAddOffset<uint8_t>(dst, dstRB);
241 src16 = SkTAddOffset<const uint16_t>(src16, srcRB);
242 }
243 break;
244 }
245 case kIndex_8_SkColorType: {
246 SkASSERT(ctable);
247 const uint32_t* table = ctable->readColors();
248 auto src8 = (const uint8_t*)src;
249 for (int y = 0; y < srcInfo.height(); y++) {
250 for (int x = 0; x < srcInfo.width(); x++) {
251 dst[x] = table[src8[x]] >> 24;
252 }
253 dst = SkTAddOffset<uint8_t>(dst, dstRB);
254 src8 = SkTAddOffset<const uint8_t>(src8, srcRB);
255 }
256 break;
257 }
258 case kRGBA_F16_SkColorType: {
259 auto src64 = (const uint64_t*) src;
260 for (int y = 0; y < srcInfo.height(); y++) {
261 for (int x = 0; x < srcInfo.width(); x++) {
262 dst[x] = (uint8_t) (255.0f * SkHalfToFloat(src64[x] >> 48));
263 }
264 dst = SkTAddOffset<uint8_t>(dst, dstRB);
265 src64 = SkTAddOffset<const uint64_t>(src64, srcRB);
266 }
267 break;
268 }
269 default:
270 SkASSERT(false);
271 break;
272 }
273 }
274
275 // Default: Use the pipeline.
convert_with_pipeline(const SkImageInfo & dstInfo,void * dstRow,size_t dstRB,const SkImageInfo & srcInfo,const void * srcRow,size_t srcRB,bool isColorAware,SkTransferFunctionBehavior behavior)276 static void convert_with_pipeline(const SkImageInfo& dstInfo, void* dstRow, size_t dstRB,
277 const SkImageInfo& srcInfo, const void* srcRow, size_t srcRB,
278 bool isColorAware, SkTransferFunctionBehavior behavior) {
279 SkRasterPipeline pipeline;
280 switch (srcInfo.colorType()) {
281 case kRGBA_8888_SkColorType:
282 pipeline.append(SkRasterPipeline::load_8888, &srcRow);
283 break;
284 case kBGRA_8888_SkColorType:
285 pipeline.append(SkRasterPipeline::load_8888, &srcRow);
286 pipeline.append(SkRasterPipeline::swap_rb);
287 break;
288 case kRGB_565_SkColorType:
289 pipeline.append(SkRasterPipeline::load_565, &srcRow);
290 break;
291 case kRGBA_F16_SkColorType:
292 pipeline.append(SkRasterPipeline::load_f16, &srcRow);
293 break;
294 case kGray_8_SkColorType:
295 pipeline.append(SkRasterPipeline::load_g8, &srcRow);
296 break;
297 case kARGB_4444_SkColorType:
298 pipeline.append(SkRasterPipeline::load_4444, &srcRow);
299 break;
300 default:
301 SkASSERT(false);
302 break;
303 }
304
305 SkAlphaType premulState = srcInfo.alphaType();
306 if (kPremul_SkAlphaType == premulState && SkTransferFunctionBehavior::kIgnore == behavior) {
307 pipeline.append(SkRasterPipeline::unpremul);
308 premulState = kUnpremul_SkAlphaType;
309 }
310
311 if (isColorAware && srcInfo.gammaCloseToSRGB()) {
312 pipeline.append_from_srgb(srcInfo.alphaType());
313 }
314
315 float matrix[12];
316 if (isColorAware) {
317 SkAssertResult(append_gamut_transform(&pipeline, matrix, srcInfo.colorSpace(),
318 dstInfo.colorSpace()));
319 }
320
321 SkAlphaType dat = dstInfo.alphaType();
322 if (SkTransferFunctionBehavior::kRespect == behavior) {
323 if (kPremul_SkAlphaType == premulState && kUnpremul_SkAlphaType == dat) {
324 pipeline.append(SkRasterPipeline::unpremul);
325 premulState = kUnpremul_SkAlphaType;
326 } else if (kUnpremul_SkAlphaType == premulState && kPremul_SkAlphaType == dat) {
327 pipeline.append(SkRasterPipeline::premul);
328 premulState = kPremul_SkAlphaType;
329 }
330 }
331
332 if (isColorAware && dstInfo.gammaCloseToSRGB()) {
333 pipeline.append(SkRasterPipeline::to_srgb);
334 }
335
336 if (kUnpremul_SkAlphaType == premulState && kPremul_SkAlphaType == dat &&
337 SkTransferFunctionBehavior::kIgnore == behavior)
338 {
339 pipeline.append(SkRasterPipeline::premul);
340 premulState = kPremul_SkAlphaType;
341 }
342
343 // The final premul state must equal the dst alpha type. Note that if we are "converting"
344 // opaque to another alpha type, there's no need to worry about multiplication.
345 SkASSERT(premulState == dat || kOpaque_SkAlphaType == srcInfo.alphaType());
346
347 switch (dstInfo.colorType()) {
348 case kRGBA_8888_SkColorType:
349 pipeline.append(SkRasterPipeline::store_8888, &dstRow);
350 break;
351 case kBGRA_8888_SkColorType:
352 pipeline.append(SkRasterPipeline::swap_rb);
353 pipeline.append(SkRasterPipeline::store_8888, &dstRow);
354 break;
355 case kRGB_565_SkColorType:
356 pipeline.append(SkRasterPipeline::store_565, &dstRow);
357 break;
358 case kRGBA_F16_SkColorType:
359 pipeline.append(SkRasterPipeline::store_f16, &dstRow);
360 break;
361 case kARGB_4444_SkColorType:
362 pipeline.append(SkRasterPipeline::store_4444, &dstRow);
363 break;
364 default:
365 SkASSERT(false);
366 break;
367 }
368
369 for (int y = 0; y < srcInfo.height(); ++y) {
370 pipeline.run(0,srcInfo.width());
371 // The pipeline has pointers to srcRow and dstRow, so we just need to update them in the
372 // loop to move between rows of src/dst.
373 dstRow = SkTAddOffset<void>(dstRow, dstRB);
374 srcRow = SkTAddOffset<const void>(srcRow, srcRB);
375 }
376 }
377
SkConvertPixels(const SkImageInfo & dstInfo,void * dstPixels,size_t dstRB,const SkImageInfo & srcInfo,const void * srcPixels,size_t srcRB,SkColorTable * ctable,SkTransferFunctionBehavior behavior)378 void SkConvertPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB,
379 const SkImageInfo& srcInfo, const void* srcPixels, size_t srcRB,
380 SkColorTable* ctable, SkTransferFunctionBehavior behavior) {
381 SkASSERT(dstInfo.dimensions() == srcInfo.dimensions());
382 SkASSERT(SkImageInfoValidConversion(dstInfo, srcInfo));
383
384 // Fast Path 1: The memcpy() case.
385 if (can_memcpy(dstInfo, srcInfo)) {
386 SkRectMemcpy(dstPixels, dstRB, srcPixels, srcRB, dstInfo.minRowBytes(), dstInfo.height());
387 return;
388 }
389
390 const bool isColorAware = dstInfo.colorSpace();
391 SkASSERT(srcInfo.colorSpace() || !isColorAware);
392
393 // Fast Path 2: Simple swizzles and premuls.
394 if (4 == srcInfo.bytesPerPixel() && 4 == dstInfo.bytesPerPixel() && !isColorAware) {
395 swizzle_and_multiply(dstInfo, dstPixels, dstRB, srcInfo, srcPixels, srcRB);
396 return;
397 }
398
399 // Fast Path 3: Color space xform.
400 if (isColorAware && optimized_color_xform(dstInfo, srcInfo, behavior)) {
401 apply_color_xform(dstInfo, dstPixels, dstRB, srcInfo, srcPixels, srcRB, behavior);
402 return;
403 }
404
405 // Fast Path 4: Index 8 sources.
406 if (kIndex_8_SkColorType == srcInfo.colorType()) {
407 SkASSERT(ctable);
408 convert_from_index8(dstInfo, dstPixels, dstRB, srcInfo, (const uint8_t*) srcPixels, srcRB,
409 ctable, behavior);
410 return;
411 }
412
413 // Fast Path 5: Alpha 8 dsts.
414 if (kAlpha_8_SkColorType == dstInfo.colorType()) {
415 convert_to_alpha8((uint8_t*) dstPixels, dstRB, srcInfo, srcPixels, srcRB, ctable);
416 return;
417 }
418
419 // Default: Use the pipeline.
420 convert_with_pipeline(dstInfo, dstPixels, dstRB, srcInfo, srcPixels, srcRB, isColorAware,
421 behavior);
422 }
423