blob: 03ee2eed62b85d5a6561264dde5b2dffd9c5a6f8 [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[]) {
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000552 // Sampling Gray to A8 uses the same function as Index to Index8,
553 // except we assume that there is alpha for speed, since an A8
554 // bitmap with no alpha is not interesting.
555 (void) Sample_Index_DI(dstRow, src, width, deltaSrc, /* y unused */ 0,
556 /* ctable unused */ NULL);
scroggo@google.comf698c822013-07-18 19:34:49 +0000557 return true;
558}
559
scroggo@google.com8d239242013-10-01 17:27:15 +0000560static SkScaledBitmapSampler::RowProc get_gray_to_A8_proc(const SkImageDecoder& decoder) {
561 if (decoder.getRequireUnpremultipliedColors()) {
562 return NULL;
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000563 }
scroggo@google.com8d239242013-10-01 17:27:15 +0000564 // Ignore skip and dither.
565 return Sample_Gray_DA8;
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000566}
567
scroggo@google.com8d239242013-10-01 17:27:15 +0000568typedef SkScaledBitmapSampler::RowProc (*RowProcChooser)(const SkImageDecoder& decoder);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000569///////////////////////////////////////////////////////////////////////////////
570
571#include "SkScaledBitmapSampler.h"
572
573SkScaledBitmapSampler::SkScaledBitmapSampler(int width, int height,
574 int sampleSize) {
vandebo@chromium.orga728e352012-03-28 20:29:38 +0000575 fCTable = NULL;
576 fDstRow = NULL;
577 fRowProc = NULL;
578
reed@android.com8a1c16f2008-12-17 15:59:43 +0000579 if (width <= 0 || height <= 0) {
580 sk_throw();
581 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000582
commit-bot@chromium.orgdac4a1d2013-10-08 19:40:18 +0000583 SkDEBUGCODE(fSampleMode = kUninitialized_SampleMode);
584
reed@android.com8a1c16f2008-12-17 15:59:43 +0000585 if (sampleSize <= 1) {
586 fScaledWidth = width;
587 fScaledHeight = height;
588 fX0 = fY0 = 0;
589 fDX = fDY = 1;
590 return;
591 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000592
reed@android.com8a1c16f2008-12-17 15:59:43 +0000593 int dx = SkMin32(sampleSize, width);
594 int dy = SkMin32(sampleSize, height);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000595
reed@android.com8a1c16f2008-12-17 15:59:43 +0000596 fScaledWidth = width / dx;
597 fScaledHeight = height / dy;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000598
reed@android.com8a1c16f2008-12-17 15:59:43 +0000599 SkASSERT(fScaledWidth > 0);
600 SkASSERT(fScaledHeight > 0);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000601
reed@android.com8a1c16f2008-12-17 15:59:43 +0000602 fX0 = dx >> 1;
603 fY0 = dy >> 1;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000604
reed@android.com8a1c16f2008-12-17 15:59:43 +0000605 SkASSERT(fX0 >= 0 && fX0 < width);
606 SkASSERT(fY0 >= 0 && fY0 < height);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000607
reed@android.com8a1c16f2008-12-17 15:59:43 +0000608 fDX = dx;
609 fDY = dy;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000610
reed@android.com8a1c16f2008-12-17 15:59:43 +0000611 SkASSERT(fDX > 0 && (fX0 + fDX * (fScaledWidth - 1)) < width);
612 SkASSERT(fDY > 0 && (fY0 + fDY * (fScaledHeight - 1)) < height);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000613}
614
scroggo@google.com8d239242013-10-01 17:27:15 +0000615bool SkScaledBitmapSampler::begin(SkBitmap* dst, SrcConfig sc,
616 const SkImageDecoder& decoder,
617 const SkPMColor ctable[]) {
618 static const RowProcChooser gProcChoosers[] = {
619 get_gray_to_8888_proc,
620 get_RGBx_to_8888_proc,
621 get_RGBA_to_8888_proc,
622 get_index_to_8888_proc,
623 NULL, // 565 to 8888
624
625 get_gray_to_565_proc,
626 get_RGBx_to_565_proc,
627 get_RGBx_to_565_proc, // The source alpha will be ignored.
628 get_index_to_565_proc,
629 get_565_to_565_proc,
630
631 get_gray_to_4444_proc,
632 get_RGBx_to_4444_proc,
633 get_RGBA_to_4444_proc,
634 get_index_to_4444_proc,
635 NULL, // 565 to 4444
636
637 NULL, // gray to index
638 NULL, // rgbx to index
639 NULL, // rgba to index
640 get_index_to_index_proc,
641 NULL, // 565 to index
642
643 get_gray_to_A8_proc,
644 NULL, // rgbx to a8
645 NULL, // rgba to a8
646 NULL, // index to a8
647 NULL, // 565 to a8
reed@android.com8a1c16f2008-12-17 15:59:43 +0000648 };
scroggo@google.com8d239242013-10-01 17:27:15 +0000649
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000650 // The jump between dst configs in the table
scroggo@google.com8d239242013-10-01 17:27:15 +0000651 static const int gProcDstConfigSpan = 5;
652 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(gProcChoosers) == 5 * gProcDstConfigSpan,
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000653 gProcs_has_the_wrong_number_of_entries);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000654
reed@android.com11344262009-07-08 20:09:23 +0000655 fCTable = ctable;
656
reed@android.com8a1c16f2008-12-17 15:59:43 +0000657 int index = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000658 switch (sc) {
659 case SkScaledBitmapSampler::kGray:
660 fSrcPixelSize = 1;
661 index += 0;
662 break;
663 case SkScaledBitmapSampler::kRGB:
664 fSrcPixelSize = 3;
scroggo@google.com8d239242013-10-01 17:27:15 +0000665 index += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000666 break;
667 case SkScaledBitmapSampler::kRGBX:
668 fSrcPixelSize = 4;
scroggo@google.com8d239242013-10-01 17:27:15 +0000669 index += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000670 break;
671 case SkScaledBitmapSampler::kRGBA:
672 fSrcPixelSize = 4;
scroggo@google.com8d239242013-10-01 17:27:15 +0000673 index += 2;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000674 break;
675 case SkScaledBitmapSampler::kIndex:
676 fSrcPixelSize = 1;
scroggo@google.com8d239242013-10-01 17:27:15 +0000677 index += 3;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000678 break;
djsollen@google.com57f49692011-02-23 20:46:31 +0000679 case SkScaledBitmapSampler::kRGB_565:
680 fSrcPixelSize = 2;
scroggo@google.com8d239242013-10-01 17:27:15 +0000681 index += 4;
djsollen@google.com57f49692011-02-23 20:46:31 +0000682 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000683 default:
684 return false;
685 }
686
687 switch (dst->config()) {
688 case SkBitmap::kARGB_8888_Config:
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000689 index += 0 * gProcDstConfigSpan;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000690 break;
691 case SkBitmap::kRGB_565_Config:
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000692 index += 1 * gProcDstConfigSpan;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000693 break;
694 case SkBitmap::kARGB_4444_Config:
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000695 index += 2 * gProcDstConfigSpan;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000696 break;
697 case SkBitmap::kIndex8_Config:
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000698 index += 3 * gProcDstConfigSpan;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000699 break;
scroggo@google.comf698c822013-07-18 19:34:49 +0000700 case SkBitmap::kA8_Config:
701 index += 4 * gProcDstConfigSpan;
702 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000703 default:
704 return false;
705 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000706
scroggo@google.com8d239242013-10-01 17:27:15 +0000707 RowProcChooser chooser = gProcChoosers[index];
708 if (NULL == chooser) {
709 fRowProc = NULL;
710 } else {
711 fRowProc = chooser(decoder);
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000712 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000713 fDstRow = (char*)dst->getPixels();
714 fDstRowBytes = dst->rowBytes();
715 fCurrY = 0;
716 return fRowProc != NULL;
717}
718
719bool SkScaledBitmapSampler::next(const uint8_t* SK_RESTRICT src) {
commit-bot@chromium.orgdac4a1d2013-10-08 19:40:18 +0000720 SkASSERT(kInterlaced_SampleMode != fSampleMode);
721 SkDEBUGCODE(fSampleMode = kConsecutive_SampleMode);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000722 SkASSERT((unsigned)fCurrY < (unsigned)fScaledHeight);
723
724 bool hadAlpha = fRowProc(fDstRow, src + fX0 * fSrcPixelSize, fScaledWidth,
reed@android.com11344262009-07-08 20:09:23 +0000725 fDX * fSrcPixelSize, fCurrY, fCTable);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000726 fDstRow += fDstRowBytes;
727 fCurrY += 1;
728 return hadAlpha;
729}
scroggo@google.com8d239242013-10-01 17:27:15 +0000730
commit-bot@chromium.orgdac4a1d2013-10-08 19:40:18 +0000731bool SkScaledBitmapSampler::sampleInterlaced(const uint8_t* SK_RESTRICT src, int srcY) {
732 SkASSERT(kConsecutive_SampleMode != fSampleMode);
733 SkDEBUGCODE(fSampleMode = kInterlaced_SampleMode);
734 // Any line that should be a part of the destination can be created by the formula:
735 // fY0 + (some multiplier) * fDY
736 // so if srcY - fY0 is not an integer multiple of fDY that srcY will be skipped.
737 const int srcYMinusY0 = srcY - fY0;
738 if (srcYMinusY0 % fDY != 0) {
739 // This line is not part of the output, so return false for alpha, since we have
740 // not added an alpha to the output.
741 return false;
742 }
743 // Unlike in next(), where the data is used sequentially, this function skips around,
744 // so fDstRow and fCurrY are never updated. fDstRow must always be the starting point
745 // of the destination bitmap's pixels, which is used to calculate the destination row
746 // each time this function is called.
747 const int dstY = srcYMinusY0 / fDY;
748 SkASSERT(dstY < fScaledHeight);
749 char* dstRow = fDstRow + dstY * fDstRowBytes;
750 return fRowProc(dstRow, src + fX0 * fSrcPixelSize, fScaledWidth,
751 fDX * fSrcPixelSize, dstY, fCTable);
752}
753
scroggo@google.com8d239242013-10-01 17:27:15 +0000754#ifdef SK_DEBUG
755// The following code is for a test to ensure that changing the method to get the right row proc
756// did not change the row proc unintentionally. Tested by ImageDecodingTest.cpp
757
758// friend of SkScaledBitmapSampler solely for the purpose of accessing fRowProc.
759class RowProcTester {
760public:
761 static SkScaledBitmapSampler::RowProc getRowProc(const SkScaledBitmapSampler& sampler) {
762 return sampler.fRowProc;
763 }
764};
765
766
767// Table showing the expected RowProc for each combination of inputs.
768// Table formated as follows:
769// Each group of 5 consecutive rows represents sampling from a single
770// SkScaledBitmapSampler::SrcConfig.
771// Within each set, each row represents a different destination SkBitmap::Config
772// Each column represents a different combination of dither and unpremul.
773// D = dither ~D = no dither
774// U = unpremul ~U = no unpremul
775// ~D~U D~U ~DU DU
776SkScaledBitmapSampler::RowProc gTestProcs[] = {
777 // Gray
778 Sample_Gray_DA8, Sample_Gray_DA8, NULL, NULL, // to A8
779 NULL, NULL, NULL, NULL, // to Index8
780 Sample_Gray_D565, Sample_Gray_D565_D, Sample_Gray_D565, Sample_Gray_D565_D, // to 565
781 Sample_Gray_D4444, Sample_Gray_D4444_D, Sample_Gray_D4444, Sample_Gray_D4444_D, // to 4444
782 Sample_Gray_D8888, Sample_Gray_D8888, Sample_Gray_D8888, Sample_Gray_D8888, // to 8888
783 // Index
784 NULL, NULL, NULL, NULL, // to A8
785 Sample_Index_DI, Sample_Index_DI, NULL, NULL, // to Index8
786 Sample_Index_D565, Sample_Index_D565_D, Sample_Index_D565, Sample_Index_D565_D, // to 565
787 Sample_Index_D4444, Sample_Index_D4444_D, NULL, NULL, // to 4444
788 Sample_Index_D8888, Sample_Index_D8888, NULL, NULL, // to 8888
789 // RGB
790 NULL, NULL, NULL, NULL, // to A8
791 NULL, NULL, NULL, NULL, // to Index8
792 Sample_RGBx_D565, Sample_RGBx_D565_D, Sample_RGBx_D565, Sample_RGBx_D565_D, // to 565
793 Sample_RGBx_D4444, Sample_RGBx_D4444_D, Sample_RGBx_D4444, Sample_RGBx_D4444_D, // to 4444
794 Sample_RGBx_D8888, Sample_RGBx_D8888, Sample_RGBx_D8888, Sample_RGBx_D8888, // to 8888
795 // RGBx is the same as RGB
796 NULL, NULL, NULL, NULL, // to A8
797 NULL, NULL, NULL, NULL, // to Index8
798 Sample_RGBx_D565, Sample_RGBx_D565_D, Sample_RGBx_D565, Sample_RGBx_D565_D, // to 565
799 Sample_RGBx_D4444, Sample_RGBx_D4444_D, Sample_RGBx_D4444, Sample_RGBx_D4444_D, // to 4444
800 Sample_RGBx_D8888, Sample_RGBx_D8888, Sample_RGBx_D8888, Sample_RGBx_D8888, // to 8888
801 // RGBA
802 NULL, NULL, NULL, NULL, // to A8
803 NULL, NULL, NULL, NULL, // to Index8
804 Sample_RGBx_D565, Sample_RGBx_D565_D, Sample_RGBx_D565, Sample_RGBx_D565_D, // to 565
805 Sample_RGBA_D4444, Sample_RGBA_D4444_D, NULL, NULL, // to 4444
806 Sample_RGBA_D8888, Sample_RGBA_D8888, Sample_RGBA_D8888_Unpremul, Sample_RGBA_D8888_Unpremul, // to 8888
807 // RGB_565
808 NULL, NULL, NULL, NULL, // to A8
809 NULL, NULL, NULL, NULL, // to Index8
810 Sample_D565_D565, Sample_D565_D565, Sample_D565_D565, Sample_D565_D565, // to 565
811 NULL, NULL, NULL, NULL, // to 4444
812 NULL, NULL, NULL, NULL, // to 8888
813};
814
815// Dummy class that allows instantiation of an ImageDecoder, so begin can query its fields.
816class DummyDecoder : public SkImageDecoder {
817public:
818 DummyDecoder() {}
819protected:
820 virtual bool onDecode(SkStream*, SkBitmap*, SkImageDecoder::Mode) SK_OVERRIDE {
821 return false;
822 }
823};
824
scroggo@google.com1d069992013-10-01 17:46:35 +0000825void test_row_proc_choice();
scroggo@google.com8d239242013-10-01 17:27:15 +0000826void test_row_proc_choice() {
827 SkBitmap dummyBitmap;
828 DummyDecoder dummyDecoder;
829 size_t procCounter = 0;
830 for (int sc = SkScaledBitmapSampler::kGray; sc <= SkScaledBitmapSampler::kRGB_565; ++sc) {
831 for (int c = SkBitmap::kA8_Config; c <= SkBitmap::kARGB_8888_Config; ++c) {
832 for (int unpremul = 0; unpremul <= 1; ++unpremul) {
833 for (int dither = 0; dither <= 1; ++dither) {
834 // Arbitrary width/height/sampleSize to allow SkScaledBitmapSampler to
835 // be considered valid.
836 SkScaledBitmapSampler sampler(10, 10, 1);
837 dummyBitmap.setConfig((SkBitmap::Config) c, 10, 10);
838 dummyDecoder.setDitherImage(SkToBool(dither));
839 dummyDecoder.setRequireUnpremultipliedColors(SkToBool(unpremul));
840 sampler.begin(&dummyBitmap, (SkScaledBitmapSampler::SrcConfig) sc,
841 dummyDecoder);
842 SkScaledBitmapSampler::RowProc expected = gTestProcs[procCounter];
843 SkScaledBitmapSampler::RowProc actual = RowProcTester::getRowProc(sampler);
844 SkASSERT(expected == actual);
845 procCounter++;
846 }
847 }
848 }
849 }
850 SkASSERT(SK_ARRAY_COUNT(gTestProcs) == procCounter);
851}
852#endif // SK_DEBUG