blob: 3bab8de9fd918079579c68d39da70d4f5998884f [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002 * Copyright 2007 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00003 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00004 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00006 */
7
epoger@google.comec3ed6a2011-07-28 14:26:00 +00008
reed@android.com8a1c16f2008-12-17 15:59:43 +00009#include "SkScaledBitmapSampler.h"
10#include "SkBitmap.h"
11#include "SkColorPriv.h"
12#include "SkDither.h"
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000013#include "SkTypes.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000014
15// 8888
16
17static bool Sample_Gray_D8888(void* SK_RESTRICT dstRow,
18 const uint8_t* SK_RESTRICT src,
reed@android.com11344262009-07-08 20:09:23 +000019 int width, int deltaSrc, int, const SkPMColor[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000020 SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
21 for (int x = 0; x < width; x++) {
22 dst[x] = SkPackARGB32(0xFF, src[0], src[0], src[0]);
23 src += deltaSrc;
24 }
25 return false;
26}
27
scroggo@google.com8d239242013-10-01 17:27:15 +000028static SkScaledBitmapSampler::RowProc get_gray_to_8888_proc(const SkImageDecoder& decoder) {
29 // Dither, unpremul, and skipZeroes have no effect
30 return Sample_Gray_D8888;
31}
32
reed@android.com8a1c16f2008-12-17 15:59:43 +000033static bool Sample_RGBx_D8888(void* SK_RESTRICT dstRow,
34 const uint8_t* SK_RESTRICT src,
reed@android.com11344262009-07-08 20:09:23 +000035 int width, int deltaSrc, int, const SkPMColor[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000036 SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
37 for (int x = 0; x < width; x++) {
38 dst[x] = SkPackARGB32(0xFF, src[0], src[1], src[2]);
39 src += deltaSrc;
40 }
41 return false;
42}
43
scroggo@google.com8d239242013-10-01 17:27:15 +000044static SkScaledBitmapSampler::RowProc get_RGBx_to_8888_proc(const SkImageDecoder& decoder) {
45 // Dither, unpremul, and skipZeroes have no effect
46 return Sample_RGBx_D8888;
47}
48
reed@android.com8a1c16f2008-12-17 15:59:43 +000049static bool Sample_RGBA_D8888(void* SK_RESTRICT dstRow,
50 const uint8_t* SK_RESTRICT src,
reed@android.com11344262009-07-08 20:09:23 +000051 int width, int deltaSrc, int, const SkPMColor[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000052 SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
53 unsigned alphaMask = 0xFF;
54 for (int x = 0; x < width; x++) {
55 unsigned alpha = src[3];
56 dst[x] = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]);
57 src += deltaSrc;
58 alphaMask &= alpha;
59 }
60 return alphaMask != 0xFF;
61}
62
scroggo@google.com8d239242013-10-01 17:27:15 +000063static bool Sample_RGBA_D8888_Unpremul(void* SK_RESTRICT dstRow,
64 const uint8_t* SK_RESTRICT src,
65 int width, int deltaSrc, int,
66 const SkPMColor[]) {
67 uint32_t* SK_RESTRICT dst = reinterpret_cast<uint32_t*>(dstRow);
68 unsigned alphaMask = 0xFF;
69 for (int x = 0; x < width; x++) {
70 unsigned alpha = src[3];
71 dst[x] = SkPackARGB32NoCheck(alpha, src[0], src[1], src[2]);
72 src += deltaSrc;
73 alphaMask &= alpha;
74 }
75 return alphaMask != 0xFF;
76}
77
78static bool Sample_RGBA_D8888_SkipZ(void* SK_RESTRICT dstRow,
79 const uint8_t* SK_RESTRICT src,
80 int width, int deltaSrc, int,
81 const SkPMColor[]) {
82 SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
83 unsigned alphaMask = 0xFF;
84 for (int x = 0; x < width; x++) {
85 unsigned alpha = src[3];
86 if (0 != alpha) {
87 dst[x] = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]);
88 }
89 src += deltaSrc;
90 alphaMask &= alpha;
91 }
92 return alphaMask != 0xFF;
93}
94
95static SkScaledBitmapSampler::RowProc get_RGBA_to_8888_proc(const SkImageDecoder& decoder) {
96 // Dither has no effect.
97 if (decoder.getRequireUnpremultipliedColors()) {
98 // We could check each component for a zero, at the expense of extra checks.
99 // For now, just return unpremul.
100 return Sample_RGBA_D8888_Unpremul;
101 }
102 // Supply the versions that premultiply the colors
103 if (decoder.getSkipWritingZeroes()) {
104 return Sample_RGBA_D8888_SkipZ;
105 }
106 return Sample_RGBA_D8888;
107}
108
reed@android.com8a1c16f2008-12-17 15:59:43 +0000109// 565
110
111static bool Sample_Gray_D565(void* SK_RESTRICT dstRow,
112 const uint8_t* SK_RESTRICT src,
reed@android.com11344262009-07-08 20:09:23 +0000113 int width, int deltaSrc, int, const SkPMColor[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000114 uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow;
115 for (int x = 0; x < width; x++) {
116 dst[x] = SkPack888ToRGB16(src[0], src[0], src[0]);
117 src += deltaSrc;
118 }
119 return false;
120}
121
122static bool Sample_Gray_D565_D(void* SK_RESTRICT dstRow,
123 const uint8_t* SK_RESTRICT src,
reed@android.com11344262009-07-08 20:09:23 +0000124 int width, int deltaSrc, int y, const SkPMColor[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000125 uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow;
126 DITHER_565_SCAN(y);
127 for (int x = 0; x < width; x++) {
128 dst[x] = SkDitherRGBTo565(src[0], src[0], src[0], DITHER_VALUE(x));
129 src += deltaSrc;
130 }
131 return false;
132}
133
scroggo@google.com8d239242013-10-01 17:27:15 +0000134static SkScaledBitmapSampler::RowProc get_gray_to_565_proc(const SkImageDecoder& decoder) {
135 // Unpremul and skip zeroes make no difference
136 if (decoder.getDitherImage()) {
137 return Sample_Gray_D565_D;
138 }
139 return Sample_Gray_D565;
140}
141
reed@android.com8a1c16f2008-12-17 15:59:43 +0000142static bool Sample_RGBx_D565(void* SK_RESTRICT dstRow,
143 const uint8_t* SK_RESTRICT src,
reed@android.com11344262009-07-08 20:09:23 +0000144 int width, int deltaSrc, int, const SkPMColor[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000145 uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow;
146 for (int x = 0; x < width; x++) {
147 dst[x] = SkPack888ToRGB16(src[0], src[1], src[2]);
148 src += deltaSrc;
149 }
150 return false;
151}
152
scroggo@google.com8d239242013-10-01 17:27:15 +0000153static bool Sample_RGBx_D565_D(void* SK_RESTRICT dstRow,
154 const uint8_t* SK_RESTRICT src,
155 int width, int deltaSrc, int y,
156 const SkPMColor[]) {
157 uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow;
158 DITHER_565_SCAN(y);
159 for (int x = 0; x < width; x++) {
160 dst[x] = SkDitherRGBTo565(src[0], src[1], src[2], DITHER_VALUE(x));
161 src += deltaSrc;
162 }
163 return false;
164}
165
166static SkScaledBitmapSampler::RowProc get_RGBx_to_565_proc(const SkImageDecoder& decoder) {
167 // Unpremul and skip zeroes make no difference
168 if (decoder.getDitherImage()) {
169 return Sample_RGBx_D565_D;
170 }
171 return Sample_RGBx_D565;
172}
173
174
djsollen@google.com57f49692011-02-23 20:46:31 +0000175static bool Sample_D565_D565(void* SK_RESTRICT dstRow,
176 const uint8_t* SK_RESTRICT src,
177 int width, int deltaSrc, int, const SkPMColor[]) {
178 uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow;
179 uint16_t* SK_RESTRICT castedSrc = (uint16_t*) src;
180 for (int x = 0; x < width; x++) {
181 dst[x] = castedSrc[0];
182 castedSrc += deltaSrc >> 1;
183 }
184 return false;
185}
186
scroggo@google.com8d239242013-10-01 17:27:15 +0000187static SkScaledBitmapSampler::RowProc get_565_to_565_proc(const SkImageDecoder& decoder) {
188 // Unpremul, dither, and skip zeroes have no effect
189 return Sample_D565_D565;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000190}
191
192// 4444
193
194static bool Sample_Gray_D4444(void* SK_RESTRICT dstRow,
195 const uint8_t* SK_RESTRICT src,
reed@android.com11344262009-07-08 20:09:23 +0000196 int width, int deltaSrc, int, const SkPMColor[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000197 SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
198 for (int x = 0; x < width; x++) {
199 unsigned gray = src[0] >> 4;
200 dst[x] = SkPackARGB4444(0xF, gray, gray, gray);
201 src += deltaSrc;
202 }
203 return false;
204}
205
206static bool Sample_Gray_D4444_D(void* SK_RESTRICT dstRow,
207 const uint8_t* SK_RESTRICT src,
reed@android.com11344262009-07-08 20:09:23 +0000208 int width, int deltaSrc, int y, const SkPMColor[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000209 SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
210 DITHER_4444_SCAN(y);
211 for (int x = 0; x < width; x++) {
212 dst[x] = SkDitherARGB32To4444(0xFF, src[0], src[0], src[0],
213 DITHER_VALUE(x));
214 src += deltaSrc;
215 }
216 return false;
217}
218
scroggo@google.com8d239242013-10-01 17:27:15 +0000219static SkScaledBitmapSampler::RowProc get_gray_to_4444_proc(const SkImageDecoder& decoder) {
220 // Skip zeroes and unpremul make no difference
221 if (decoder.getDitherImage()) {
222 return Sample_Gray_D4444_D;
223 }
224 return Sample_Gray_D4444;
225}
226
reed@android.com8a1c16f2008-12-17 15:59:43 +0000227static bool Sample_RGBx_D4444(void* SK_RESTRICT dstRow,
228 const uint8_t* SK_RESTRICT src,
reed@android.com11344262009-07-08 20:09:23 +0000229 int width, int deltaSrc, int, const SkPMColor[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000230 SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
231 for (int x = 0; x < width; x++) {
232 dst[x] = SkPackARGB4444(0xF, src[0] >> 4, src[1] >> 4, src[2] >> 4);
233 src += deltaSrc;
234 }
235 return false;
236}
237
238static bool Sample_RGBx_D4444_D(void* SK_RESTRICT dstRow,
239 const uint8_t* SK_RESTRICT src,
reed@android.com11344262009-07-08 20:09:23 +0000240 int width, int deltaSrc, int y, const SkPMColor[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000241 SkPMColor16* dst = (SkPMColor16*)dstRow;
242 DITHER_4444_SCAN(y);
243
244 for (int x = 0; x < width; x++) {
245 dst[x] = SkDitherARGB32To4444(0xFF, src[0], src[1], src[2],
246 DITHER_VALUE(x));
247 src += deltaSrc;
248 }
249 return false;
250}
251
scroggo@google.com8d239242013-10-01 17:27:15 +0000252static SkScaledBitmapSampler::RowProc get_RGBx_to_4444_proc(const SkImageDecoder& decoder) {
253 // Skip zeroes and unpremul make no difference
254 if (decoder.getDitherImage()) {
255 return Sample_RGBx_D4444_D;
256 }
257 return Sample_RGBx_D4444;
258}
259
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260static bool Sample_RGBA_D4444(void* SK_RESTRICT dstRow,
261 const uint8_t* SK_RESTRICT src,
reed@android.com11344262009-07-08 20:09:23 +0000262 int width, int deltaSrc, int, const SkPMColor[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000263 SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
264 unsigned alphaMask = 0xFF;
265
266 for (int x = 0; x < width; x++) {
267 unsigned alpha = src[3];
268 SkPMColor c = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]);
269 dst[x] = SkPixel32ToPixel4444(c);
270 src += deltaSrc;
271 alphaMask &= alpha;
272 }
273 return alphaMask != 0xFF;
274}
275
scroggo@google.com8d239242013-10-01 17:27:15 +0000276static bool Sample_RGBA_D4444_SkipZ(void* SK_RESTRICT dstRow,
277 const uint8_t* SK_RESTRICT src,
278 int width, int deltaSrc, int,
279 const SkPMColor[]) {
280 SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
281 unsigned alphaMask = 0xFF;
282
283 for (int x = 0; x < width; x++) {
284 unsigned alpha = src[3];
285 if (alpha != 0) {
286 SkPMColor c = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]);
287 dst[x] = SkPixel32ToPixel4444(c);
288 }
289 src += deltaSrc;
290 alphaMask &= alpha;
291 }
292 return alphaMask != 0xFF;
293}
294
295
reed@android.com8a1c16f2008-12-17 15:59:43 +0000296static bool Sample_RGBA_D4444_D(void* SK_RESTRICT dstRow,
297 const uint8_t* SK_RESTRICT src,
scroggo@google.com8d239242013-10-01 17:27:15 +0000298 int width, int deltaSrc, int y,
299 const SkPMColor[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000300 SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
301 unsigned alphaMask = 0xFF;
302 DITHER_4444_SCAN(y);
303
304 for (int x = 0; x < width; x++) {
305 unsigned alpha = src[3];
306 SkPMColor c = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]);
307 dst[x] = SkDitherARGB32To4444(c, DITHER_VALUE(x));
308 src += deltaSrc;
309 alphaMask &= alpha;
310 }
311 return alphaMask != 0xFF;
312}
313
scroggo@google.com8d239242013-10-01 17:27:15 +0000314static bool Sample_RGBA_D4444_D_SkipZ(void* SK_RESTRICT dstRow,
315 const uint8_t* SK_RESTRICT src,
316 int width, int deltaSrc, int y,
317 const SkPMColor[]) {
318 SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
319 unsigned alphaMask = 0xFF;
320 DITHER_4444_SCAN(y);
321
322 for (int x = 0; x < width; x++) {
323 unsigned alpha = src[3];
324 if (alpha != 0) {
325 SkPMColor c = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]);
326 dst[x] = SkDitherARGB32To4444(c, DITHER_VALUE(x));
327 }
328 src += deltaSrc;
329 alphaMask &= alpha;
330 }
331 return alphaMask != 0xFF;
332}
333
334static SkScaledBitmapSampler::RowProc get_RGBA_to_4444_proc(const SkImageDecoder& decoder) {
335 if (decoder.getRequireUnpremultipliedColors()) {
336 // Unpremultiplied is not supported for 4444
337 return NULL;
338 }
339 const bool dither = decoder.getDitherImage();
340 if (decoder.getSkipWritingZeroes()) {
341 if (dither) {
342 return Sample_RGBA_D4444_D_SkipZ;
343 }
344 return Sample_RGBA_D4444_SkipZ;
345 }
346 if (dither) {
347 return Sample_RGBA_D4444_D;
348 }
349 return Sample_RGBA_D4444;
350}
351
reed@android.com8a1c16f2008-12-17 15:59:43 +0000352// Index
353
reed@android.com1cdcb512009-08-24 19:11:00 +0000354#define A32_MASK_IN_PLACE (SkPMColor)(SK_A32_MASK << SK_A32_SHIFT)
reed@android.com11344262009-07-08 20:09:23 +0000355
356static bool Sample_Index_D8888(void* SK_RESTRICT dstRow,
357 const uint8_t* SK_RESTRICT src,
358 int width, int deltaSrc, int, const SkPMColor ctable[]) {
359
360 SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
361 SkPMColor cc = A32_MASK_IN_PLACE;
362 for (int x = 0; x < width; x++) {
363 SkPMColor c = ctable[*src];
364 cc &= c;
365 dst[x] = c;
366 src += deltaSrc;
367 }
368 return cc != A32_MASK_IN_PLACE;
369}
370
scroggo@google.com8d239242013-10-01 17:27:15 +0000371static bool Sample_Index_D8888_SkipZ(void* SK_RESTRICT dstRow,
372 const uint8_t* SK_RESTRICT src,
373 int width, int deltaSrc, int,
374 const SkPMColor ctable[]) {
375
376 SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
377 SkPMColor cc = A32_MASK_IN_PLACE;
378 for (int x = 0; x < width; x++) {
379 SkPMColor c = ctable[*src];
380 cc &= c;
381 if (c != 0) {
382 dst[x] = c;
383 }
384 src += deltaSrc;
385 }
386 return cc != A32_MASK_IN_PLACE;
387}
388
389static SkScaledBitmapSampler::RowProc get_index_to_8888_proc(const SkImageDecoder& decoder) {
390 if (decoder.getRequireUnpremultipliedColors()) {
391 // Unpremultiplied is not supported for an index source.
392 return NULL;
393 }
394 // Dither makes no difference
395 if (decoder.getSkipWritingZeroes()) {
396 return Sample_Index_D8888_SkipZ;
397 }
398 return Sample_Index_D8888;
399}
400
reed@android.com11344262009-07-08 20:09:23 +0000401static bool Sample_Index_D565(void* SK_RESTRICT dstRow,
402 const uint8_t* SK_RESTRICT src,
403 int width, int deltaSrc, int, const SkPMColor ctable[]) {
404
405 uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow;
406 for (int x = 0; x < width; x++) {
407 dst[x] = SkPixel32ToPixel16(ctable[*src]);
408 src += deltaSrc;
409 }
410 return false;
411}
412
413static bool Sample_Index_D565_D(void* SK_RESTRICT dstRow,
414 const uint8_t* SK_RESTRICT src, int width,
415 int deltaSrc, int y, const SkPMColor ctable[]) {
416
417 uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow;
418 DITHER_565_SCAN(y);
419
420 for (int x = 0; x < width; x++) {
421 SkPMColor c = ctable[*src];
422 dst[x] = SkDitherRGBTo565(SkGetPackedR32(c), SkGetPackedG32(c),
423 SkGetPackedB32(c), DITHER_VALUE(x));
424 src += deltaSrc;
425 }
426 return false;
427}
428
scroggo@google.com8d239242013-10-01 17:27:15 +0000429static SkScaledBitmapSampler::RowProc get_index_to_565_proc(const SkImageDecoder& decoder) {
430 // Unpremultiplied and skip zeroes make no difference
431 if (decoder.getDitherImage()) {
432 return Sample_Index_D565_D;
433 }
434 return Sample_Index_D565;
435}
436
reed@android.com11344262009-07-08 20:09:23 +0000437static bool Sample_Index_D4444(void* SK_RESTRICT dstRow,
438 const uint8_t* SK_RESTRICT src, int width,
439 int deltaSrc, int y, const SkPMColor ctable[]) {
440
441 SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
442 SkPMColor cc = A32_MASK_IN_PLACE;
443 for (int x = 0; x < width; x++) {
444 SkPMColor c = ctable[*src];
445 cc &= c;
446 dst[x] = SkPixel32ToPixel4444(c);
447 src += deltaSrc;
448 }
449 return cc != A32_MASK_IN_PLACE;
450}
451
452static bool Sample_Index_D4444_D(void* SK_RESTRICT dstRow,
453 const uint8_t* SK_RESTRICT src, int width,
454 int deltaSrc, int y, const SkPMColor ctable[]) {
455
456 SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
457 SkPMColor cc = A32_MASK_IN_PLACE;
458 DITHER_4444_SCAN(y);
459
460 for (int x = 0; x < width; x++) {
461 SkPMColor c = ctable[*src];
462 cc &= c;
463 dst[x] = SkDitherARGB32To4444(c, DITHER_VALUE(x));
464 src += deltaSrc;
465 }
466 return cc != A32_MASK_IN_PLACE;
467}
468
scroggo@google.com8d239242013-10-01 17:27:15 +0000469static bool Sample_Index_D4444_SkipZ(void* SK_RESTRICT dstRow,
470 const uint8_t* SK_RESTRICT src, int width,
471 int deltaSrc, int y, const SkPMColor ctable[]) {
472
473 SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
474 SkPMColor cc = A32_MASK_IN_PLACE;
475 for (int x = 0; x < width; x++) {
476 SkPMColor c = ctable[*src];
477 cc &= c;
478 if (c != 0) {
479 dst[x] = SkPixel32ToPixel4444(c);
480 }
481 src += deltaSrc;
482 }
483 return cc != A32_MASK_IN_PLACE;
484}
485
486static bool Sample_Index_D4444_D_SkipZ(void* SK_RESTRICT dstRow,
487 const uint8_t* SK_RESTRICT src, int width,
488 int deltaSrc, int y, const SkPMColor ctable[]) {
489
490 SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
491 SkPMColor cc = A32_MASK_IN_PLACE;
492 DITHER_4444_SCAN(y);
493
494 for (int x = 0; x < width; x++) {
495 SkPMColor c = ctable[*src];
496 cc &= c;
497 if (c != 0) {
498 dst[x] = SkDitherARGB32To4444(c, DITHER_VALUE(x));
499 }
500 src += deltaSrc;
501 }
502 return cc != A32_MASK_IN_PLACE;
503}
504
505static SkScaledBitmapSampler::RowProc get_index_to_4444_proc(const SkImageDecoder& decoder) {
506 // Unpremul not allowed
507 if (decoder.getRequireUnpremultipliedColors()) {
508 return NULL;
509 }
510 const bool dither = decoder.getDitherImage();
511 if (decoder.getSkipWritingZeroes()) {
512 if (dither) {
513 return Sample_Index_D4444_D_SkipZ;
514 }
515 return Sample_Index_D4444_SkipZ;
516 }
517 if (dither) {
518 return Sample_Index_D4444_D;
519 }
520 return Sample_Index_D4444;
521}
522
reed@android.com8a1c16f2008-12-17 15:59:43 +0000523static bool Sample_Index_DI(void* SK_RESTRICT dstRow,
524 const uint8_t* SK_RESTRICT src,
reed@android.com11344262009-07-08 20:09:23 +0000525 int width, int deltaSrc, int, const SkPMColor[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000526 if (1 == deltaSrc) {
527 memcpy(dstRow, src, width);
528 } else {
529 uint8_t* SK_RESTRICT dst = (uint8_t*)dstRow;
530 for (int x = 0; x < width; x++) {
531 dst[x] = src[0];
532 src += deltaSrc;
533 }
534 }
535 return false;
536}
537
scroggo@google.com8d239242013-10-01 17:27:15 +0000538static SkScaledBitmapSampler::RowProc get_index_to_index_proc(const SkImageDecoder& decoder) {
539 // Unpremul not allowed
540 if (decoder.getRequireUnpremultipliedColors()) {
541 return NULL;
542 }
543 // Ignore dither and skip zeroes
544 return Sample_Index_DI;
545}
546
scroggo@google.comf698c822013-07-18 19:34:49 +0000547// A8
548static bool Sample_Gray_DA8(void* SK_RESTRICT dstRow,
549 const uint8_t* SK_RESTRICT src,
550 int width, int deltaSrc, int,
551 const SkPMColor[]) {
552 memcpy(dstRow, src, width);
553 return true;
554}
555
scroggo@google.com8d239242013-10-01 17:27:15 +0000556static SkScaledBitmapSampler::RowProc get_gray_to_A8_proc(const SkImageDecoder& decoder) {
557 if (decoder.getRequireUnpremultipliedColors()) {
558 return NULL;
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000559 }
scroggo@google.com8d239242013-10-01 17:27:15 +0000560 // Ignore skip and dither.
561 return Sample_Gray_DA8;
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000562}
563
scroggo@google.com8d239242013-10-01 17:27:15 +0000564typedef SkScaledBitmapSampler::RowProc (*RowProcChooser)(const SkImageDecoder& decoder);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000565///////////////////////////////////////////////////////////////////////////////
566
567#include "SkScaledBitmapSampler.h"
568
569SkScaledBitmapSampler::SkScaledBitmapSampler(int width, int height,
570 int sampleSize) {
vandebo@chromium.orga728e352012-03-28 20:29:38 +0000571 fCTable = NULL;
572 fDstRow = NULL;
573 fRowProc = NULL;
574
reed@android.com8a1c16f2008-12-17 15:59:43 +0000575 if (width <= 0 || height <= 0) {
576 sk_throw();
577 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000578
reed@android.com8a1c16f2008-12-17 15:59:43 +0000579 if (sampleSize <= 1) {
580 fScaledWidth = width;
581 fScaledHeight = height;
582 fX0 = fY0 = 0;
583 fDX = fDY = 1;
584 return;
585 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000586
reed@android.com8a1c16f2008-12-17 15:59:43 +0000587 int dx = SkMin32(sampleSize, width);
588 int dy = SkMin32(sampleSize, height);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000589
reed@android.com8a1c16f2008-12-17 15:59:43 +0000590 fScaledWidth = width / dx;
591 fScaledHeight = height / dy;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000592
reed@android.com8a1c16f2008-12-17 15:59:43 +0000593 SkASSERT(fScaledWidth > 0);
594 SkASSERT(fScaledHeight > 0);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000595
reed@android.com8a1c16f2008-12-17 15:59:43 +0000596 fX0 = dx >> 1;
597 fY0 = dy >> 1;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000598
reed@android.com8a1c16f2008-12-17 15:59:43 +0000599 SkASSERT(fX0 >= 0 && fX0 < width);
600 SkASSERT(fY0 >= 0 && fY0 < height);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000601
reed@android.com8a1c16f2008-12-17 15:59:43 +0000602 fDX = dx;
603 fDY = dy;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000604
reed@android.com8a1c16f2008-12-17 15:59:43 +0000605 SkASSERT(fDX > 0 && (fX0 + fDX * (fScaledWidth - 1)) < width);
606 SkASSERT(fDY > 0 && (fY0 + fDY * (fScaledHeight - 1)) < height);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000607}
608
scroggo@google.com8d239242013-10-01 17:27:15 +0000609bool SkScaledBitmapSampler::begin(SkBitmap* dst, SrcConfig sc,
610 const SkImageDecoder& decoder,
611 const SkPMColor ctable[]) {
612 static const RowProcChooser gProcChoosers[] = {
613 get_gray_to_8888_proc,
614 get_RGBx_to_8888_proc,
615 get_RGBA_to_8888_proc,
616 get_index_to_8888_proc,
617 NULL, // 565 to 8888
618
619 get_gray_to_565_proc,
620 get_RGBx_to_565_proc,
621 get_RGBx_to_565_proc, // The source alpha will be ignored.
622 get_index_to_565_proc,
623 get_565_to_565_proc,
624
625 get_gray_to_4444_proc,
626 get_RGBx_to_4444_proc,
627 get_RGBA_to_4444_proc,
628 get_index_to_4444_proc,
629 NULL, // 565 to 4444
630
631 NULL, // gray to index
632 NULL, // rgbx to index
633 NULL, // rgba to index
634 get_index_to_index_proc,
635 NULL, // 565 to index
636
637 get_gray_to_A8_proc,
638 NULL, // rgbx to a8
639 NULL, // rgba to a8
640 NULL, // index to a8
641 NULL, // 565 to a8
reed@android.com8a1c16f2008-12-17 15:59:43 +0000642 };
scroggo@google.com8d239242013-10-01 17:27:15 +0000643
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000644 // The jump between dst configs in the table
scroggo@google.com8d239242013-10-01 17:27:15 +0000645 static const int gProcDstConfigSpan = 5;
646 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(gProcChoosers) == 5 * gProcDstConfigSpan,
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000647 gProcs_has_the_wrong_number_of_entries);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000648
reed@android.com11344262009-07-08 20:09:23 +0000649 fCTable = ctable;
650
reed@android.com8a1c16f2008-12-17 15:59:43 +0000651 int index = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000652 switch (sc) {
653 case SkScaledBitmapSampler::kGray:
654 fSrcPixelSize = 1;
655 index += 0;
656 break;
657 case SkScaledBitmapSampler::kRGB:
658 fSrcPixelSize = 3;
scroggo@google.com8d239242013-10-01 17:27:15 +0000659 index += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000660 break;
661 case SkScaledBitmapSampler::kRGBX:
662 fSrcPixelSize = 4;
scroggo@google.com8d239242013-10-01 17:27:15 +0000663 index += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000664 break;
665 case SkScaledBitmapSampler::kRGBA:
666 fSrcPixelSize = 4;
scroggo@google.com8d239242013-10-01 17:27:15 +0000667 index += 2;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000668 break;
669 case SkScaledBitmapSampler::kIndex:
670 fSrcPixelSize = 1;
scroggo@google.com8d239242013-10-01 17:27:15 +0000671 index += 3;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000672 break;
djsollen@google.com57f49692011-02-23 20:46:31 +0000673 case SkScaledBitmapSampler::kRGB_565:
674 fSrcPixelSize = 2;
scroggo@google.com8d239242013-10-01 17:27:15 +0000675 index += 4;
djsollen@google.com57f49692011-02-23 20:46:31 +0000676 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000677 default:
678 return false;
679 }
680
681 switch (dst->config()) {
682 case SkBitmap::kARGB_8888_Config:
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000683 index += 0 * gProcDstConfigSpan;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000684 break;
685 case SkBitmap::kRGB_565_Config:
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000686 index += 1 * gProcDstConfigSpan;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000687 break;
688 case SkBitmap::kARGB_4444_Config:
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000689 index += 2 * gProcDstConfigSpan;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000690 break;
691 case SkBitmap::kIndex8_Config:
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000692 index += 3 * gProcDstConfigSpan;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000693 break;
scroggo@google.comf698c822013-07-18 19:34:49 +0000694 case SkBitmap::kA8_Config:
695 index += 4 * gProcDstConfigSpan;
696 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000697 default:
698 return false;
699 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000700
scroggo@google.com8d239242013-10-01 17:27:15 +0000701 RowProcChooser chooser = gProcChoosers[index];
702 if (NULL == chooser) {
703 fRowProc = NULL;
704 } else {
705 fRowProc = chooser(decoder);
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000706 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000707 fDstRow = (char*)dst->getPixels();
708 fDstRowBytes = dst->rowBytes();
709 fCurrY = 0;
710 return fRowProc != NULL;
711}
712
713bool SkScaledBitmapSampler::next(const uint8_t* SK_RESTRICT src) {
714 SkASSERT((unsigned)fCurrY < (unsigned)fScaledHeight);
715
716 bool hadAlpha = fRowProc(fDstRow, src + fX0 * fSrcPixelSize, fScaledWidth,
reed@android.com11344262009-07-08 20:09:23 +0000717 fDX * fSrcPixelSize, fCurrY, fCTable);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000718 fDstRow += fDstRowBytes;
719 fCurrY += 1;
720 return hadAlpha;
721}
scroggo@google.com8d239242013-10-01 17:27:15 +0000722
723#ifdef SK_DEBUG
724// The following code is for a test to ensure that changing the method to get the right row proc
725// did not change the row proc unintentionally. Tested by ImageDecodingTest.cpp
726
727// friend of SkScaledBitmapSampler solely for the purpose of accessing fRowProc.
728class RowProcTester {
729public:
730 static SkScaledBitmapSampler::RowProc getRowProc(const SkScaledBitmapSampler& sampler) {
731 return sampler.fRowProc;
732 }
733};
734
735
736// Table showing the expected RowProc for each combination of inputs.
737// Table formated as follows:
738// Each group of 5 consecutive rows represents sampling from a single
739// SkScaledBitmapSampler::SrcConfig.
740// Within each set, each row represents a different destination SkBitmap::Config
741// Each column represents a different combination of dither and unpremul.
742// D = dither ~D = no dither
743// U = unpremul ~U = no unpremul
744// ~D~U D~U ~DU DU
745SkScaledBitmapSampler::RowProc gTestProcs[] = {
746 // Gray
747 Sample_Gray_DA8, Sample_Gray_DA8, NULL, NULL, // to A8
748 NULL, NULL, NULL, NULL, // to Index8
749 Sample_Gray_D565, Sample_Gray_D565_D, Sample_Gray_D565, Sample_Gray_D565_D, // to 565
750 Sample_Gray_D4444, Sample_Gray_D4444_D, Sample_Gray_D4444, Sample_Gray_D4444_D, // to 4444
751 Sample_Gray_D8888, Sample_Gray_D8888, Sample_Gray_D8888, Sample_Gray_D8888, // to 8888
752 // Index
753 NULL, NULL, NULL, NULL, // to A8
754 Sample_Index_DI, Sample_Index_DI, NULL, NULL, // to Index8
755 Sample_Index_D565, Sample_Index_D565_D, Sample_Index_D565, Sample_Index_D565_D, // to 565
756 Sample_Index_D4444, Sample_Index_D4444_D, NULL, NULL, // to 4444
757 Sample_Index_D8888, Sample_Index_D8888, NULL, NULL, // to 8888
758 // RGB
759 NULL, NULL, NULL, NULL, // to A8
760 NULL, NULL, NULL, NULL, // to Index8
761 Sample_RGBx_D565, Sample_RGBx_D565_D, Sample_RGBx_D565, Sample_RGBx_D565_D, // to 565
762 Sample_RGBx_D4444, Sample_RGBx_D4444_D, Sample_RGBx_D4444, Sample_RGBx_D4444_D, // to 4444
763 Sample_RGBx_D8888, Sample_RGBx_D8888, Sample_RGBx_D8888, Sample_RGBx_D8888, // to 8888
764 // RGBx is the same as RGB
765 NULL, NULL, NULL, NULL, // to A8
766 NULL, NULL, NULL, NULL, // to Index8
767 Sample_RGBx_D565, Sample_RGBx_D565_D, Sample_RGBx_D565, Sample_RGBx_D565_D, // to 565
768 Sample_RGBx_D4444, Sample_RGBx_D4444_D, Sample_RGBx_D4444, Sample_RGBx_D4444_D, // to 4444
769 Sample_RGBx_D8888, Sample_RGBx_D8888, Sample_RGBx_D8888, Sample_RGBx_D8888, // to 8888
770 // RGBA
771 NULL, NULL, NULL, NULL, // to A8
772 NULL, NULL, NULL, NULL, // to Index8
773 Sample_RGBx_D565, Sample_RGBx_D565_D, Sample_RGBx_D565, Sample_RGBx_D565_D, // to 565
774 Sample_RGBA_D4444, Sample_RGBA_D4444_D, NULL, NULL, // to 4444
775 Sample_RGBA_D8888, Sample_RGBA_D8888, Sample_RGBA_D8888_Unpremul, Sample_RGBA_D8888_Unpremul, // to 8888
776 // RGB_565
777 NULL, NULL, NULL, NULL, // to A8
778 NULL, NULL, NULL, NULL, // to Index8
779 Sample_D565_D565, Sample_D565_D565, Sample_D565_D565, Sample_D565_D565, // to 565
780 NULL, NULL, NULL, NULL, // to 4444
781 NULL, NULL, NULL, NULL, // to 8888
782};
783
784// Dummy class that allows instantiation of an ImageDecoder, so begin can query its fields.
785class DummyDecoder : public SkImageDecoder {
786public:
787 DummyDecoder() {}
788protected:
789 virtual bool onDecode(SkStream*, SkBitmap*, SkImageDecoder::Mode) SK_OVERRIDE {
790 return false;
791 }
792};
793
794void test_row_proc_choice() {
795 SkBitmap dummyBitmap;
796 DummyDecoder dummyDecoder;
797 size_t procCounter = 0;
798 for (int sc = SkScaledBitmapSampler::kGray; sc <= SkScaledBitmapSampler::kRGB_565; ++sc) {
799 for (int c = SkBitmap::kA8_Config; c <= SkBitmap::kARGB_8888_Config; ++c) {
800 for (int unpremul = 0; unpremul <= 1; ++unpremul) {
801 for (int dither = 0; dither <= 1; ++dither) {
802 // Arbitrary width/height/sampleSize to allow SkScaledBitmapSampler to
803 // be considered valid.
804 SkScaledBitmapSampler sampler(10, 10, 1);
805 dummyBitmap.setConfig((SkBitmap::Config) c, 10, 10);
806 dummyDecoder.setDitherImage(SkToBool(dither));
807 dummyDecoder.setRequireUnpremultipliedColors(SkToBool(unpremul));
808 sampler.begin(&dummyBitmap, (SkScaledBitmapSampler::SrcConfig) sc,
809 dummyDecoder);
810 SkScaledBitmapSampler::RowProc expected = gTestProcs[procCounter];
811 SkScaledBitmapSampler::RowProc actual = RowProcTester::getRowProc(sampler);
812 SkASSERT(expected == actual);
813 procCounter++;
814 }
815 }
816 }
817 }
818 SkASSERT(SK_ARRAY_COUNT(gTestProcs) == procCounter);
819}
820#endif // SK_DEBUG