blob: 825f9d5268fb64485abd5219d86903ebc1ba3b15 [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
commit-bot@chromium.orgdac4a1d2013-10-08 19:40:18 +0000579 SkDEBUGCODE(fSampleMode = kUninitialized_SampleMode);
580
reed@android.com8a1c16f2008-12-17 15:59:43 +0000581 if (sampleSize <= 1) {
582 fScaledWidth = width;
583 fScaledHeight = height;
584 fX0 = fY0 = 0;
585 fDX = fDY = 1;
586 return;
587 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000588
reed@android.com8a1c16f2008-12-17 15:59:43 +0000589 int dx = SkMin32(sampleSize, width);
590 int dy = SkMin32(sampleSize, height);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000591
reed@android.com8a1c16f2008-12-17 15:59:43 +0000592 fScaledWidth = width / dx;
593 fScaledHeight = height / dy;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000594
reed@android.com8a1c16f2008-12-17 15:59:43 +0000595 SkASSERT(fScaledWidth > 0);
596 SkASSERT(fScaledHeight > 0);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000597
reed@android.com8a1c16f2008-12-17 15:59:43 +0000598 fX0 = dx >> 1;
599 fY0 = dy >> 1;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000600
reed@android.com8a1c16f2008-12-17 15:59:43 +0000601 SkASSERT(fX0 >= 0 && fX0 < width);
602 SkASSERT(fY0 >= 0 && fY0 < height);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000603
reed@android.com8a1c16f2008-12-17 15:59:43 +0000604 fDX = dx;
605 fDY = dy;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000606
reed@android.com8a1c16f2008-12-17 15:59:43 +0000607 SkASSERT(fDX > 0 && (fX0 + fDX * (fScaledWidth - 1)) < width);
608 SkASSERT(fDY > 0 && (fY0 + fDY * (fScaledHeight - 1)) < height);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000609}
610
scroggo@google.com8d239242013-10-01 17:27:15 +0000611bool SkScaledBitmapSampler::begin(SkBitmap* dst, SrcConfig sc,
612 const SkImageDecoder& decoder,
613 const SkPMColor ctable[]) {
614 static const RowProcChooser gProcChoosers[] = {
615 get_gray_to_8888_proc,
616 get_RGBx_to_8888_proc,
617 get_RGBA_to_8888_proc,
618 get_index_to_8888_proc,
619 NULL, // 565 to 8888
620
621 get_gray_to_565_proc,
622 get_RGBx_to_565_proc,
623 get_RGBx_to_565_proc, // The source alpha will be ignored.
624 get_index_to_565_proc,
625 get_565_to_565_proc,
626
627 get_gray_to_4444_proc,
628 get_RGBx_to_4444_proc,
629 get_RGBA_to_4444_proc,
630 get_index_to_4444_proc,
631 NULL, // 565 to 4444
632
633 NULL, // gray to index
634 NULL, // rgbx to index
635 NULL, // rgba to index
636 get_index_to_index_proc,
637 NULL, // 565 to index
638
639 get_gray_to_A8_proc,
640 NULL, // rgbx to a8
641 NULL, // rgba to a8
642 NULL, // index to a8
643 NULL, // 565 to a8
reed@android.com8a1c16f2008-12-17 15:59:43 +0000644 };
scroggo@google.com8d239242013-10-01 17:27:15 +0000645
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000646 // The jump between dst configs in the table
scroggo@google.com8d239242013-10-01 17:27:15 +0000647 static const int gProcDstConfigSpan = 5;
648 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(gProcChoosers) == 5 * gProcDstConfigSpan,
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000649 gProcs_has_the_wrong_number_of_entries);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000650
reed@android.com11344262009-07-08 20:09:23 +0000651 fCTable = ctable;
652
reed@android.com8a1c16f2008-12-17 15:59:43 +0000653 int index = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000654 switch (sc) {
655 case SkScaledBitmapSampler::kGray:
656 fSrcPixelSize = 1;
657 index += 0;
658 break;
659 case SkScaledBitmapSampler::kRGB:
660 fSrcPixelSize = 3;
scroggo@google.com8d239242013-10-01 17:27:15 +0000661 index += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000662 break;
663 case SkScaledBitmapSampler::kRGBX:
664 fSrcPixelSize = 4;
scroggo@google.com8d239242013-10-01 17:27:15 +0000665 index += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000666 break;
667 case SkScaledBitmapSampler::kRGBA:
668 fSrcPixelSize = 4;
scroggo@google.com8d239242013-10-01 17:27:15 +0000669 index += 2;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000670 break;
671 case SkScaledBitmapSampler::kIndex:
672 fSrcPixelSize = 1;
scroggo@google.com8d239242013-10-01 17:27:15 +0000673 index += 3;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000674 break;
djsollen@google.com57f49692011-02-23 20:46:31 +0000675 case SkScaledBitmapSampler::kRGB_565:
676 fSrcPixelSize = 2;
scroggo@google.com8d239242013-10-01 17:27:15 +0000677 index += 4;
djsollen@google.com57f49692011-02-23 20:46:31 +0000678 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000679 default:
680 return false;
681 }
682
683 switch (dst->config()) {
684 case SkBitmap::kARGB_8888_Config:
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000685 index += 0 * gProcDstConfigSpan;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000686 break;
687 case SkBitmap::kRGB_565_Config:
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000688 index += 1 * gProcDstConfigSpan;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000689 break;
690 case SkBitmap::kARGB_4444_Config:
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000691 index += 2 * gProcDstConfigSpan;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000692 break;
693 case SkBitmap::kIndex8_Config:
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000694 index += 3 * gProcDstConfigSpan;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000695 break;
scroggo@google.comf698c822013-07-18 19:34:49 +0000696 case SkBitmap::kA8_Config:
697 index += 4 * gProcDstConfigSpan;
698 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000699 default:
700 return false;
701 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000702
scroggo@google.com8d239242013-10-01 17:27:15 +0000703 RowProcChooser chooser = gProcChoosers[index];
704 if (NULL == chooser) {
705 fRowProc = NULL;
706 } else {
707 fRowProc = chooser(decoder);
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000708 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000709 fDstRow = (char*)dst->getPixels();
710 fDstRowBytes = dst->rowBytes();
711 fCurrY = 0;
712 return fRowProc != NULL;
713}
714
715bool SkScaledBitmapSampler::next(const uint8_t* SK_RESTRICT src) {
commit-bot@chromium.orgdac4a1d2013-10-08 19:40:18 +0000716 SkASSERT(kInterlaced_SampleMode != fSampleMode);
717 SkDEBUGCODE(fSampleMode = kConsecutive_SampleMode);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000718 SkASSERT((unsigned)fCurrY < (unsigned)fScaledHeight);
719
720 bool hadAlpha = fRowProc(fDstRow, src + fX0 * fSrcPixelSize, fScaledWidth,
reed@android.com11344262009-07-08 20:09:23 +0000721 fDX * fSrcPixelSize, fCurrY, fCTable);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000722 fDstRow += fDstRowBytes;
723 fCurrY += 1;
724 return hadAlpha;
725}
scroggo@google.com8d239242013-10-01 17:27:15 +0000726
commit-bot@chromium.orgdac4a1d2013-10-08 19:40:18 +0000727bool SkScaledBitmapSampler::sampleInterlaced(const uint8_t* SK_RESTRICT src, int srcY) {
728 SkASSERT(kConsecutive_SampleMode != fSampleMode);
729 SkDEBUGCODE(fSampleMode = kInterlaced_SampleMode);
730 // Any line that should be a part of the destination can be created by the formula:
731 // fY0 + (some multiplier) * fDY
732 // so if srcY - fY0 is not an integer multiple of fDY that srcY will be skipped.
733 const int srcYMinusY0 = srcY - fY0;
734 if (srcYMinusY0 % fDY != 0) {
735 // This line is not part of the output, so return false for alpha, since we have
736 // not added an alpha to the output.
737 return false;
738 }
739 // Unlike in next(), where the data is used sequentially, this function skips around,
740 // so fDstRow and fCurrY are never updated. fDstRow must always be the starting point
741 // of the destination bitmap's pixels, which is used to calculate the destination row
742 // each time this function is called.
743 const int dstY = srcYMinusY0 / fDY;
744 SkASSERT(dstY < fScaledHeight);
745 char* dstRow = fDstRow + dstY * fDstRowBytes;
746 return fRowProc(dstRow, src + fX0 * fSrcPixelSize, fScaledWidth,
747 fDX * fSrcPixelSize, dstY, fCTable);
748}
749
scroggo@google.com8d239242013-10-01 17:27:15 +0000750#ifdef SK_DEBUG
751// The following code is for a test to ensure that changing the method to get the right row proc
752// did not change the row proc unintentionally. Tested by ImageDecodingTest.cpp
753
754// friend of SkScaledBitmapSampler solely for the purpose of accessing fRowProc.
755class RowProcTester {
756public:
757 static SkScaledBitmapSampler::RowProc getRowProc(const SkScaledBitmapSampler& sampler) {
758 return sampler.fRowProc;
759 }
760};
761
762
763// Table showing the expected RowProc for each combination of inputs.
764// Table formated as follows:
765// Each group of 5 consecutive rows represents sampling from a single
766// SkScaledBitmapSampler::SrcConfig.
767// Within each set, each row represents a different destination SkBitmap::Config
768// Each column represents a different combination of dither and unpremul.
769// D = dither ~D = no dither
770// U = unpremul ~U = no unpremul
771// ~D~U D~U ~DU DU
772SkScaledBitmapSampler::RowProc gTestProcs[] = {
773 // Gray
774 Sample_Gray_DA8, Sample_Gray_DA8, NULL, NULL, // to A8
775 NULL, NULL, NULL, NULL, // to Index8
776 Sample_Gray_D565, Sample_Gray_D565_D, Sample_Gray_D565, Sample_Gray_D565_D, // to 565
777 Sample_Gray_D4444, Sample_Gray_D4444_D, Sample_Gray_D4444, Sample_Gray_D4444_D, // to 4444
778 Sample_Gray_D8888, Sample_Gray_D8888, Sample_Gray_D8888, Sample_Gray_D8888, // to 8888
779 // Index
780 NULL, NULL, NULL, NULL, // to A8
781 Sample_Index_DI, Sample_Index_DI, NULL, NULL, // to Index8
782 Sample_Index_D565, Sample_Index_D565_D, Sample_Index_D565, Sample_Index_D565_D, // to 565
783 Sample_Index_D4444, Sample_Index_D4444_D, NULL, NULL, // to 4444
784 Sample_Index_D8888, Sample_Index_D8888, NULL, NULL, // to 8888
785 // RGB
786 NULL, NULL, NULL, NULL, // to A8
787 NULL, NULL, NULL, NULL, // to Index8
788 Sample_RGBx_D565, Sample_RGBx_D565_D, Sample_RGBx_D565, Sample_RGBx_D565_D, // to 565
789 Sample_RGBx_D4444, Sample_RGBx_D4444_D, Sample_RGBx_D4444, Sample_RGBx_D4444_D, // to 4444
790 Sample_RGBx_D8888, Sample_RGBx_D8888, Sample_RGBx_D8888, Sample_RGBx_D8888, // to 8888
791 // RGBx is the same as RGB
792 NULL, NULL, NULL, NULL, // to A8
793 NULL, NULL, NULL, NULL, // to Index8
794 Sample_RGBx_D565, Sample_RGBx_D565_D, Sample_RGBx_D565, Sample_RGBx_D565_D, // to 565
795 Sample_RGBx_D4444, Sample_RGBx_D4444_D, Sample_RGBx_D4444, Sample_RGBx_D4444_D, // to 4444
796 Sample_RGBx_D8888, Sample_RGBx_D8888, Sample_RGBx_D8888, Sample_RGBx_D8888, // to 8888
797 // RGBA
798 NULL, NULL, NULL, NULL, // to A8
799 NULL, NULL, NULL, NULL, // to Index8
800 Sample_RGBx_D565, Sample_RGBx_D565_D, Sample_RGBx_D565, Sample_RGBx_D565_D, // to 565
801 Sample_RGBA_D4444, Sample_RGBA_D4444_D, NULL, NULL, // to 4444
802 Sample_RGBA_D8888, Sample_RGBA_D8888, Sample_RGBA_D8888_Unpremul, Sample_RGBA_D8888_Unpremul, // to 8888
803 // RGB_565
804 NULL, NULL, NULL, NULL, // to A8
805 NULL, NULL, NULL, NULL, // to Index8
806 Sample_D565_D565, Sample_D565_D565, Sample_D565_D565, Sample_D565_D565, // to 565
807 NULL, NULL, NULL, NULL, // to 4444
808 NULL, NULL, NULL, NULL, // to 8888
809};
810
811// Dummy class that allows instantiation of an ImageDecoder, so begin can query its fields.
812class DummyDecoder : public SkImageDecoder {
813public:
814 DummyDecoder() {}
815protected:
816 virtual bool onDecode(SkStream*, SkBitmap*, SkImageDecoder::Mode) SK_OVERRIDE {
817 return false;
818 }
819};
820
scroggo@google.com1d069992013-10-01 17:46:35 +0000821void test_row_proc_choice();
scroggo@google.com8d239242013-10-01 17:27:15 +0000822void test_row_proc_choice() {
823 SkBitmap dummyBitmap;
824 DummyDecoder dummyDecoder;
825 size_t procCounter = 0;
826 for (int sc = SkScaledBitmapSampler::kGray; sc <= SkScaledBitmapSampler::kRGB_565; ++sc) {
827 for (int c = SkBitmap::kA8_Config; c <= SkBitmap::kARGB_8888_Config; ++c) {
828 for (int unpremul = 0; unpremul <= 1; ++unpremul) {
829 for (int dither = 0; dither <= 1; ++dither) {
830 // Arbitrary width/height/sampleSize to allow SkScaledBitmapSampler to
831 // be considered valid.
832 SkScaledBitmapSampler sampler(10, 10, 1);
833 dummyBitmap.setConfig((SkBitmap::Config) c, 10, 10);
834 dummyDecoder.setDitherImage(SkToBool(dither));
835 dummyDecoder.setRequireUnpremultipliedColors(SkToBool(unpremul));
836 sampler.begin(&dummyBitmap, (SkScaledBitmapSampler::SrcConfig) sc,
837 dummyDecoder);
838 SkScaledBitmapSampler::RowProc expected = gTestProcs[procCounter];
839 SkScaledBitmapSampler::RowProc actual = RowProcTester::getRowProc(sampler);
840 SkASSERT(expected == actual);
841 procCounter++;
842 }
843 }
844 }
845 }
846 SkASSERT(SK_ARRAY_COUNT(gTestProcs) == procCounter);
847}
848#endif // SK_DEBUG