blob: fe425d58b1cab09ac744114a18b8d0184049391a [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
krajcevski407d7c92014-06-09 14:29:11 -070028static SkScaledBitmapSampler::RowProc
29get_gray_to_8888_proc(const SkScaledBitmapSampler::Options& opts) {
scroggo@google.com8d239242013-10-01 17:27:15 +000030 // Dither, unpremul, and skipZeroes have no effect
31 return Sample_Gray_D8888;
32}
33
reed@android.com8a1c16f2008-12-17 15:59:43 +000034static bool Sample_RGBx_D8888(void* SK_RESTRICT dstRow,
35 const uint8_t* SK_RESTRICT src,
reed@android.com11344262009-07-08 20:09:23 +000036 int width, int deltaSrc, int, const SkPMColor[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000037 SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
38 for (int x = 0; x < width; x++) {
39 dst[x] = SkPackARGB32(0xFF, src[0], src[1], src[2]);
40 src += deltaSrc;
41 }
42 return false;
43}
44
krajcevski407d7c92014-06-09 14:29:11 -070045static SkScaledBitmapSampler::RowProc
46get_RGBx_to_8888_proc(const SkScaledBitmapSampler::Options& opts) {
scroggo@google.com8d239242013-10-01 17:27:15 +000047 // Dither, unpremul, and skipZeroes have no effect
48 return Sample_RGBx_D8888;
49}
50
reed@android.com8a1c16f2008-12-17 15:59:43 +000051static bool Sample_RGBA_D8888(void* SK_RESTRICT dstRow,
52 const uint8_t* SK_RESTRICT src,
reed@android.com11344262009-07-08 20:09:23 +000053 int width, int deltaSrc, int, const SkPMColor[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000054 SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
55 unsigned alphaMask = 0xFF;
56 for (int x = 0; x < width; x++) {
57 unsigned alpha = src[3];
58 dst[x] = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]);
59 src += deltaSrc;
60 alphaMask &= alpha;
61 }
62 return alphaMask != 0xFF;
63}
64
scroggo@google.com8d239242013-10-01 17:27:15 +000065static bool Sample_RGBA_D8888_Unpremul(void* SK_RESTRICT dstRow,
66 const uint8_t* SK_RESTRICT src,
67 int width, int deltaSrc, int,
68 const SkPMColor[]) {
69 uint32_t* SK_RESTRICT dst = reinterpret_cast<uint32_t*>(dstRow);
70 unsigned alphaMask = 0xFF;
71 for (int x = 0; x < width; x++) {
72 unsigned alpha = src[3];
73 dst[x] = SkPackARGB32NoCheck(alpha, src[0], src[1], src[2]);
74 src += deltaSrc;
75 alphaMask &= alpha;
76 }
77 return alphaMask != 0xFF;
78}
79
80static bool Sample_RGBA_D8888_SkipZ(void* SK_RESTRICT dstRow,
81 const uint8_t* SK_RESTRICT src,
82 int width, int deltaSrc, int,
83 const SkPMColor[]) {
84 SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
85 unsigned alphaMask = 0xFF;
86 for (int x = 0; x < width; x++) {
87 unsigned alpha = src[3];
88 if (0 != alpha) {
89 dst[x] = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]);
90 }
91 src += deltaSrc;
92 alphaMask &= alpha;
93 }
94 return alphaMask != 0xFF;
95}
96
krajcevski407d7c92014-06-09 14:29:11 -070097static SkScaledBitmapSampler::RowProc
98get_RGBA_to_8888_proc(const SkScaledBitmapSampler::Options& opts) {
scroggo@google.com8d239242013-10-01 17:27:15 +000099 // Dither has no effect.
krajcevski407d7c92014-06-09 14:29:11 -0700100 if (!opts.fPremultiplyAlpha) {
scroggo@google.com8d239242013-10-01 17:27:15 +0000101 // We could check each component for a zero, at the expense of extra checks.
102 // For now, just return unpremul.
103 return Sample_RGBA_D8888_Unpremul;
104 }
105 // Supply the versions that premultiply the colors
krajcevski407d7c92014-06-09 14:29:11 -0700106 if (opts.fSkipZeros) {
scroggo@google.com8d239242013-10-01 17:27:15 +0000107 return Sample_RGBA_D8888_SkipZ;
108 }
109 return Sample_RGBA_D8888;
110}
111
reed@android.com8a1c16f2008-12-17 15:59:43 +0000112// 565
113
114static bool Sample_Gray_D565(void* SK_RESTRICT dstRow,
115 const uint8_t* SK_RESTRICT src,
reed@android.com11344262009-07-08 20:09:23 +0000116 int width, int deltaSrc, int, const SkPMColor[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000117 uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow;
118 for (int x = 0; x < width; x++) {
119 dst[x] = SkPack888ToRGB16(src[0], src[0], src[0]);
120 src += deltaSrc;
121 }
122 return false;
123}
124
125static bool Sample_Gray_D565_D(void* SK_RESTRICT dstRow,
126 const uint8_t* SK_RESTRICT src,
reed@android.com11344262009-07-08 20:09:23 +0000127 int width, int deltaSrc, int y, const SkPMColor[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000128 uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow;
129 DITHER_565_SCAN(y);
130 for (int x = 0; x < width; x++) {
131 dst[x] = SkDitherRGBTo565(src[0], src[0], src[0], DITHER_VALUE(x));
132 src += deltaSrc;
133 }
134 return false;
135}
136
krajcevski407d7c92014-06-09 14:29:11 -0700137static SkScaledBitmapSampler::RowProc
138get_gray_to_565_proc(const SkScaledBitmapSampler::Options& opts) {
scroggo@google.com8d239242013-10-01 17:27:15 +0000139 // Unpremul and skip zeroes make no difference
krajcevski407d7c92014-06-09 14:29:11 -0700140 if (opts.fDither) {
scroggo@google.com8d239242013-10-01 17:27:15 +0000141 return Sample_Gray_D565_D;
142 }
143 return Sample_Gray_D565;
144}
145
reed@android.com8a1c16f2008-12-17 15:59:43 +0000146static bool Sample_RGBx_D565(void* SK_RESTRICT dstRow,
147 const uint8_t* SK_RESTRICT src,
reed@android.com11344262009-07-08 20:09:23 +0000148 int width, int deltaSrc, int, const SkPMColor[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000149 uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow;
150 for (int x = 0; x < width; x++) {
151 dst[x] = SkPack888ToRGB16(src[0], src[1], src[2]);
152 src += deltaSrc;
153 }
154 return false;
155}
156
scroggo@google.com8d239242013-10-01 17:27:15 +0000157static bool Sample_RGBx_D565_D(void* SK_RESTRICT dstRow,
158 const uint8_t* SK_RESTRICT src,
159 int width, int deltaSrc, int y,
160 const SkPMColor[]) {
161 uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow;
162 DITHER_565_SCAN(y);
163 for (int x = 0; x < width; x++) {
164 dst[x] = SkDitherRGBTo565(src[0], src[1], src[2], DITHER_VALUE(x));
165 src += deltaSrc;
166 }
167 return false;
168}
169
krajcevski407d7c92014-06-09 14:29:11 -0700170static SkScaledBitmapSampler::RowProc
171get_RGBx_to_565_proc(const SkScaledBitmapSampler::Options& opts) {
scroggo@google.com8d239242013-10-01 17:27:15 +0000172 // Unpremul and skip zeroes make no difference
krajcevski407d7c92014-06-09 14:29:11 -0700173 if (opts.fDither) {
scroggo@google.com8d239242013-10-01 17:27:15 +0000174 return Sample_RGBx_D565_D;
175 }
176 return Sample_RGBx_D565;
177}
178
179
djsollen@google.com57f49692011-02-23 20:46:31 +0000180static bool Sample_D565_D565(void* SK_RESTRICT dstRow,
181 const uint8_t* SK_RESTRICT src,
182 int width, int deltaSrc, int, const SkPMColor[]) {
183 uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow;
184 uint16_t* SK_RESTRICT castedSrc = (uint16_t*) src;
185 for (int x = 0; x < width; x++) {
186 dst[x] = castedSrc[0];
187 castedSrc += deltaSrc >> 1;
188 }
189 return false;
190}
191
krajcevski407d7c92014-06-09 14:29:11 -0700192static SkScaledBitmapSampler::RowProc
193get_565_to_565_proc(const SkScaledBitmapSampler::Options& opts) {
scroggo@google.com8d239242013-10-01 17:27:15 +0000194 // Unpremul, dither, and skip zeroes have no effect
195 return Sample_D565_D565;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000196}
197
198// 4444
199
200static bool Sample_Gray_D4444(void* SK_RESTRICT dstRow,
201 const uint8_t* SK_RESTRICT src,
reed@android.com11344262009-07-08 20:09:23 +0000202 int width, int deltaSrc, int, const SkPMColor[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000203 SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
204 for (int x = 0; x < width; x++) {
205 unsigned gray = src[0] >> 4;
206 dst[x] = SkPackARGB4444(0xF, gray, gray, gray);
207 src += deltaSrc;
208 }
209 return false;
210}
211
212static bool Sample_Gray_D4444_D(void* SK_RESTRICT dstRow,
213 const uint8_t* SK_RESTRICT src,
reed@android.com11344262009-07-08 20:09:23 +0000214 int width, int deltaSrc, int y, const SkPMColor[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000215 SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
216 DITHER_4444_SCAN(y);
217 for (int x = 0; x < width; x++) {
218 dst[x] = SkDitherARGB32To4444(0xFF, src[0], src[0], src[0],
219 DITHER_VALUE(x));
220 src += deltaSrc;
221 }
222 return false;
223}
224
krajcevski407d7c92014-06-09 14:29:11 -0700225static SkScaledBitmapSampler::RowProc
226get_gray_to_4444_proc(const SkScaledBitmapSampler::Options& opts) {
scroggo@google.com8d239242013-10-01 17:27:15 +0000227 // Skip zeroes and unpremul make no difference
krajcevski407d7c92014-06-09 14:29:11 -0700228 if (opts.fDither) {
scroggo@google.com8d239242013-10-01 17:27:15 +0000229 return Sample_Gray_D4444_D;
230 }
231 return Sample_Gray_D4444;
232}
233
reed@android.com8a1c16f2008-12-17 15:59:43 +0000234static bool Sample_RGBx_D4444(void* SK_RESTRICT dstRow,
235 const uint8_t* SK_RESTRICT src,
reed@android.com11344262009-07-08 20:09:23 +0000236 int width, int deltaSrc, int, const SkPMColor[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000237 SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
238 for (int x = 0; x < width; x++) {
239 dst[x] = SkPackARGB4444(0xF, src[0] >> 4, src[1] >> 4, src[2] >> 4);
240 src += deltaSrc;
241 }
242 return false;
243}
244
245static bool Sample_RGBx_D4444_D(void* SK_RESTRICT dstRow,
246 const uint8_t* SK_RESTRICT src,
reed@android.com11344262009-07-08 20:09:23 +0000247 int width, int deltaSrc, int y, const SkPMColor[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000248 SkPMColor16* dst = (SkPMColor16*)dstRow;
249 DITHER_4444_SCAN(y);
250
251 for (int x = 0; x < width; x++) {
252 dst[x] = SkDitherARGB32To4444(0xFF, src[0], src[1], src[2],
253 DITHER_VALUE(x));
254 src += deltaSrc;
255 }
256 return false;
257}
258
krajcevski407d7c92014-06-09 14:29:11 -0700259static SkScaledBitmapSampler::RowProc
260get_RGBx_to_4444_proc(const SkScaledBitmapSampler::Options& opts) {
scroggo@google.com8d239242013-10-01 17:27:15 +0000261 // Skip zeroes and unpremul make no difference
krajcevski407d7c92014-06-09 14:29:11 -0700262 if (opts.fDither) {
scroggo@google.com8d239242013-10-01 17:27:15 +0000263 return Sample_RGBx_D4444_D;
264 }
265 return Sample_RGBx_D4444;
266}
267
reed@android.com8a1c16f2008-12-17 15:59:43 +0000268static bool Sample_RGBA_D4444(void* SK_RESTRICT dstRow,
269 const uint8_t* SK_RESTRICT src,
reed@android.com11344262009-07-08 20:09:23 +0000270 int width, int deltaSrc, int, const SkPMColor[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000271 SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
272 unsigned alphaMask = 0xFF;
273
274 for (int x = 0; x < width; x++) {
275 unsigned alpha = src[3];
276 SkPMColor c = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]);
277 dst[x] = SkPixel32ToPixel4444(c);
278 src += deltaSrc;
279 alphaMask &= alpha;
280 }
281 return alphaMask != 0xFF;
282}
283
scroggo@google.com8d239242013-10-01 17:27:15 +0000284static bool Sample_RGBA_D4444_SkipZ(void* SK_RESTRICT dstRow,
285 const uint8_t* SK_RESTRICT src,
286 int width, int deltaSrc, int,
287 const SkPMColor[]) {
288 SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
289 unsigned alphaMask = 0xFF;
290
291 for (int x = 0; x < width; x++) {
292 unsigned alpha = src[3];
293 if (alpha != 0) {
294 SkPMColor c = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]);
295 dst[x] = SkPixel32ToPixel4444(c);
296 }
297 src += deltaSrc;
298 alphaMask &= alpha;
299 }
300 return alphaMask != 0xFF;
301}
302
303
reed@android.com8a1c16f2008-12-17 15:59:43 +0000304static bool Sample_RGBA_D4444_D(void* SK_RESTRICT dstRow,
305 const uint8_t* SK_RESTRICT src,
scroggo@google.com8d239242013-10-01 17:27:15 +0000306 int width, int deltaSrc, int y,
307 const SkPMColor[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000308 SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
309 unsigned alphaMask = 0xFF;
310 DITHER_4444_SCAN(y);
311
312 for (int x = 0; x < width; x++) {
313 unsigned alpha = src[3];
314 SkPMColor c = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]);
315 dst[x] = SkDitherARGB32To4444(c, DITHER_VALUE(x));
316 src += deltaSrc;
317 alphaMask &= alpha;
318 }
319 return alphaMask != 0xFF;
320}
321
scroggo@google.com8d239242013-10-01 17:27:15 +0000322static bool Sample_RGBA_D4444_D_SkipZ(void* SK_RESTRICT dstRow,
323 const uint8_t* SK_RESTRICT src,
324 int width, int deltaSrc, int y,
325 const SkPMColor[]) {
326 SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
327 unsigned alphaMask = 0xFF;
328 DITHER_4444_SCAN(y);
329
330 for (int x = 0; x < width; x++) {
331 unsigned alpha = src[3];
332 if (alpha != 0) {
333 SkPMColor c = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]);
334 dst[x] = SkDitherARGB32To4444(c, DITHER_VALUE(x));
335 }
336 src += deltaSrc;
337 alphaMask &= alpha;
338 }
339 return alphaMask != 0xFF;
340}
341
krajcevski407d7c92014-06-09 14:29:11 -0700342static SkScaledBitmapSampler::RowProc
343get_RGBA_to_4444_proc(const SkScaledBitmapSampler::Options& opts) {
344 if (!opts.fPremultiplyAlpha) {
scroggo@google.com8d239242013-10-01 17:27:15 +0000345 // Unpremultiplied is not supported for 4444
346 return NULL;
347 }
krajcevski407d7c92014-06-09 14:29:11 -0700348 if (opts.fSkipZeros) {
349 if (opts.fDither) {
scroggo@google.com8d239242013-10-01 17:27:15 +0000350 return Sample_RGBA_D4444_D_SkipZ;
351 }
352 return Sample_RGBA_D4444_SkipZ;
353 }
krajcevski407d7c92014-06-09 14:29:11 -0700354 if (opts.fDither) {
scroggo@google.com8d239242013-10-01 17:27:15 +0000355 return Sample_RGBA_D4444_D;
356 }
357 return Sample_RGBA_D4444;
358}
359
reed@android.com8a1c16f2008-12-17 15:59:43 +0000360// Index
361
reed@android.com1cdcb512009-08-24 19:11:00 +0000362#define A32_MASK_IN_PLACE (SkPMColor)(SK_A32_MASK << SK_A32_SHIFT)
reed@android.com11344262009-07-08 20:09:23 +0000363
364static bool Sample_Index_D8888(void* SK_RESTRICT dstRow,
365 const uint8_t* SK_RESTRICT src,
366 int width, int deltaSrc, int, const SkPMColor ctable[]) {
367
368 SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
369 SkPMColor cc = A32_MASK_IN_PLACE;
370 for (int x = 0; x < width; x++) {
371 SkPMColor c = ctable[*src];
372 cc &= c;
373 dst[x] = c;
374 src += deltaSrc;
375 }
376 return cc != A32_MASK_IN_PLACE;
377}
378
scroggo@google.com8d239242013-10-01 17:27:15 +0000379static bool Sample_Index_D8888_SkipZ(void* SK_RESTRICT dstRow,
380 const uint8_t* SK_RESTRICT src,
381 int width, int deltaSrc, int,
382 const SkPMColor ctable[]) {
383
384 SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
385 SkPMColor cc = A32_MASK_IN_PLACE;
386 for (int x = 0; x < width; x++) {
387 SkPMColor c = ctable[*src];
388 cc &= c;
389 if (c != 0) {
390 dst[x] = c;
391 }
392 src += deltaSrc;
393 }
394 return cc != A32_MASK_IN_PLACE;
395}
396
krajcevski407d7c92014-06-09 14:29:11 -0700397static SkScaledBitmapSampler::RowProc
398get_index_to_8888_proc(const SkScaledBitmapSampler::Options& opts) {
399 if (!opts.fPremultiplyAlpha) {
scroggo@google.com8d239242013-10-01 17:27:15 +0000400 // Unpremultiplied is not supported for an index source.
401 return NULL;
402 }
403 // Dither makes no difference
krajcevski407d7c92014-06-09 14:29:11 -0700404 if (opts.fSkipZeros) {
scroggo@google.com8d239242013-10-01 17:27:15 +0000405 return Sample_Index_D8888_SkipZ;
406 }
407 return Sample_Index_D8888;
408}
409
reed@android.com11344262009-07-08 20:09:23 +0000410static bool Sample_Index_D565(void* SK_RESTRICT dstRow,
411 const uint8_t* SK_RESTRICT src,
412 int width, int deltaSrc, int, const SkPMColor ctable[]) {
413
414 uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow;
415 for (int x = 0; x < width; x++) {
416 dst[x] = SkPixel32ToPixel16(ctable[*src]);
417 src += deltaSrc;
418 }
419 return false;
420}
421
422static bool Sample_Index_D565_D(void* SK_RESTRICT dstRow,
423 const uint8_t* SK_RESTRICT src, int width,
424 int deltaSrc, int y, const SkPMColor ctable[]) {
425
426 uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow;
427 DITHER_565_SCAN(y);
428
429 for (int x = 0; x < width; x++) {
430 SkPMColor c = ctable[*src];
431 dst[x] = SkDitherRGBTo565(SkGetPackedR32(c), SkGetPackedG32(c),
432 SkGetPackedB32(c), DITHER_VALUE(x));
433 src += deltaSrc;
434 }
435 return false;
436}
437
krajcevski407d7c92014-06-09 14:29:11 -0700438static SkScaledBitmapSampler::RowProc
439get_index_to_565_proc(const SkScaledBitmapSampler::Options& opts) {
scroggo@google.com8d239242013-10-01 17:27:15 +0000440 // Unpremultiplied and skip zeroes make no difference
krajcevski407d7c92014-06-09 14:29:11 -0700441 if (opts.fDither) {
scroggo@google.com8d239242013-10-01 17:27:15 +0000442 return Sample_Index_D565_D;
443 }
444 return Sample_Index_D565;
445}
446
reed@android.com11344262009-07-08 20:09:23 +0000447static bool Sample_Index_D4444(void* SK_RESTRICT dstRow,
448 const uint8_t* SK_RESTRICT src, int width,
449 int deltaSrc, int y, const SkPMColor ctable[]) {
450
451 SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
452 SkPMColor cc = A32_MASK_IN_PLACE;
453 for (int x = 0; x < width; x++) {
454 SkPMColor c = ctable[*src];
455 cc &= c;
456 dst[x] = SkPixel32ToPixel4444(c);
457 src += deltaSrc;
458 }
459 return cc != A32_MASK_IN_PLACE;
460}
461
462static bool Sample_Index_D4444_D(void* SK_RESTRICT dstRow,
463 const uint8_t* SK_RESTRICT src, int width,
464 int deltaSrc, int y, const SkPMColor ctable[]) {
465
466 SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
467 SkPMColor cc = A32_MASK_IN_PLACE;
468 DITHER_4444_SCAN(y);
469
470 for (int x = 0; x < width; x++) {
471 SkPMColor c = ctable[*src];
472 cc &= c;
473 dst[x] = SkDitherARGB32To4444(c, DITHER_VALUE(x));
474 src += deltaSrc;
475 }
476 return cc != A32_MASK_IN_PLACE;
477}
478
scroggo@google.com8d239242013-10-01 17:27:15 +0000479static bool Sample_Index_D4444_SkipZ(void* SK_RESTRICT dstRow,
480 const uint8_t* SK_RESTRICT src, int width,
481 int deltaSrc, int y, const SkPMColor ctable[]) {
482
483 SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
484 SkPMColor cc = A32_MASK_IN_PLACE;
485 for (int x = 0; x < width; x++) {
486 SkPMColor c = ctable[*src];
487 cc &= c;
488 if (c != 0) {
489 dst[x] = SkPixel32ToPixel4444(c);
490 }
491 src += deltaSrc;
492 }
493 return cc != A32_MASK_IN_PLACE;
494}
495
496static bool Sample_Index_D4444_D_SkipZ(void* SK_RESTRICT dstRow,
497 const uint8_t* SK_RESTRICT src, int width,
498 int deltaSrc, int y, const SkPMColor ctable[]) {
499
500 SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
501 SkPMColor cc = A32_MASK_IN_PLACE;
502 DITHER_4444_SCAN(y);
503
504 for (int x = 0; x < width; x++) {
505 SkPMColor c = ctable[*src];
506 cc &= c;
507 if (c != 0) {
508 dst[x] = SkDitherARGB32To4444(c, DITHER_VALUE(x));
509 }
510 src += deltaSrc;
511 }
512 return cc != A32_MASK_IN_PLACE;
513}
514
krajcevski407d7c92014-06-09 14:29:11 -0700515static SkScaledBitmapSampler::RowProc
516get_index_to_4444_proc(const SkScaledBitmapSampler::Options& opts) {
scroggo@google.com8d239242013-10-01 17:27:15 +0000517 // Unpremul not allowed
krajcevski407d7c92014-06-09 14:29:11 -0700518 if (!opts.fPremultiplyAlpha) {
scroggo@google.com8d239242013-10-01 17:27:15 +0000519 return NULL;
520 }
krajcevski407d7c92014-06-09 14:29:11 -0700521 if (opts.fSkipZeros) {
522 if (opts.fDither) {
scroggo@google.com8d239242013-10-01 17:27:15 +0000523 return Sample_Index_D4444_D_SkipZ;
524 }
525 return Sample_Index_D4444_SkipZ;
526 }
krajcevski407d7c92014-06-09 14:29:11 -0700527 if (opts.fDither) {
scroggo@google.com8d239242013-10-01 17:27:15 +0000528 return Sample_Index_D4444_D;
529 }
530 return Sample_Index_D4444;
531}
532
reed@android.com8a1c16f2008-12-17 15:59:43 +0000533static bool Sample_Index_DI(void* SK_RESTRICT dstRow,
534 const uint8_t* SK_RESTRICT src,
reed@android.com11344262009-07-08 20:09:23 +0000535 int width, int deltaSrc, int, const SkPMColor[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000536 if (1 == deltaSrc) {
537 memcpy(dstRow, src, width);
538 } else {
539 uint8_t* SK_RESTRICT dst = (uint8_t*)dstRow;
540 for (int x = 0; x < width; x++) {
541 dst[x] = src[0];
542 src += deltaSrc;
543 }
544 }
545 return false;
546}
547
krajcevski407d7c92014-06-09 14:29:11 -0700548static SkScaledBitmapSampler::RowProc
549get_index_to_index_proc(const SkScaledBitmapSampler::Options& opts) {
scroggo@google.com8d239242013-10-01 17:27:15 +0000550 // Unpremul not allowed
krajcevski407d7c92014-06-09 14:29:11 -0700551 if (!opts.fPremultiplyAlpha) {
scroggo@google.com8d239242013-10-01 17:27:15 +0000552 return NULL;
553 }
554 // Ignore dither and skip zeroes
555 return Sample_Index_DI;
556}
557
scroggo@google.comf698c822013-07-18 19:34:49 +0000558// A8
559static bool Sample_Gray_DA8(void* SK_RESTRICT dstRow,
560 const uint8_t* SK_RESTRICT src,
561 int width, int deltaSrc, int,
562 const SkPMColor[]) {
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000563 // Sampling Gray to A8 uses the same function as Index to Index8,
564 // except we assume that there is alpha for speed, since an A8
565 // bitmap with no alpha is not interesting.
566 (void) Sample_Index_DI(dstRow, src, width, deltaSrc, /* y unused */ 0,
567 /* ctable unused */ NULL);
scroggo@google.comf698c822013-07-18 19:34:49 +0000568 return true;
569}
570
krajcevski407d7c92014-06-09 14:29:11 -0700571static SkScaledBitmapSampler::RowProc
572get_gray_to_A8_proc(const SkScaledBitmapSampler::Options& opts) {
573 if (!opts.fPremultiplyAlpha) {
scroggo@google.com8d239242013-10-01 17:27:15 +0000574 return NULL;
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000575 }
scroggo@google.com8d239242013-10-01 17:27:15 +0000576 // Ignore skip and dither.
577 return Sample_Gray_DA8;
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000578}
579
krajcevski407d7c92014-06-09 14:29:11 -0700580typedef SkScaledBitmapSampler::RowProc (*RowProcChooser)(const SkScaledBitmapSampler::Options&);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000581///////////////////////////////////////////////////////////////////////////////
582
583#include "SkScaledBitmapSampler.h"
584
585SkScaledBitmapSampler::SkScaledBitmapSampler(int width, int height,
586 int sampleSize) {
vandebo@chromium.orga728e352012-03-28 20:29:38 +0000587 fCTable = NULL;
588 fDstRow = NULL;
589 fRowProc = NULL;
590
reed@android.com8a1c16f2008-12-17 15:59:43 +0000591 if (width <= 0 || height <= 0) {
592 sk_throw();
593 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000594
commit-bot@chromium.orgdac4a1d2013-10-08 19:40:18 +0000595 SkDEBUGCODE(fSampleMode = kUninitialized_SampleMode);
596
reed@android.com8a1c16f2008-12-17 15:59:43 +0000597 if (sampleSize <= 1) {
598 fScaledWidth = width;
599 fScaledHeight = height;
600 fX0 = fY0 = 0;
601 fDX = fDY = 1;
602 return;
603 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000604
reed@android.com8a1c16f2008-12-17 15:59:43 +0000605 int dx = SkMin32(sampleSize, width);
606 int dy = SkMin32(sampleSize, height);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000607
reed@android.com8a1c16f2008-12-17 15:59:43 +0000608 fScaledWidth = width / dx;
609 fScaledHeight = height / dy;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000610
reed@android.com8a1c16f2008-12-17 15:59:43 +0000611 SkASSERT(fScaledWidth > 0);
612 SkASSERT(fScaledHeight > 0);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000613
reed@android.com8a1c16f2008-12-17 15:59:43 +0000614 fX0 = dx >> 1;
615 fY0 = dy >> 1;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000616
reed@android.com8a1c16f2008-12-17 15:59:43 +0000617 SkASSERT(fX0 >= 0 && fX0 < width);
618 SkASSERT(fY0 >= 0 && fY0 < height);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000619
reed@android.com8a1c16f2008-12-17 15:59:43 +0000620 fDX = dx;
621 fDY = dy;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000622
reed@android.com8a1c16f2008-12-17 15:59:43 +0000623 SkASSERT(fDX > 0 && (fX0 + fDX * (fScaledWidth - 1)) < width);
624 SkASSERT(fDY > 0 && (fY0 + fDY * (fScaledHeight - 1)) < height);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000625}
626
scroggo@google.com8d239242013-10-01 17:27:15 +0000627bool SkScaledBitmapSampler::begin(SkBitmap* dst, SrcConfig sc,
krajcevski407d7c92014-06-09 14:29:11 -0700628 const Options& opts,
scroggo@google.com8d239242013-10-01 17:27:15 +0000629 const SkPMColor ctable[]) {
630 static const RowProcChooser gProcChoosers[] = {
631 get_gray_to_8888_proc,
632 get_RGBx_to_8888_proc,
633 get_RGBA_to_8888_proc,
634 get_index_to_8888_proc,
635 NULL, // 565 to 8888
636
637 get_gray_to_565_proc,
638 get_RGBx_to_565_proc,
639 get_RGBx_to_565_proc, // The source alpha will be ignored.
640 get_index_to_565_proc,
641 get_565_to_565_proc,
642
643 get_gray_to_4444_proc,
644 get_RGBx_to_4444_proc,
645 get_RGBA_to_4444_proc,
646 get_index_to_4444_proc,
647 NULL, // 565 to 4444
648
649 NULL, // gray to index
650 NULL, // rgbx to index
651 NULL, // rgba to index
652 get_index_to_index_proc,
653 NULL, // 565 to index
654
655 get_gray_to_A8_proc,
656 NULL, // rgbx to a8
657 NULL, // rgba to a8
658 NULL, // index to a8
659 NULL, // 565 to a8
reed@android.com8a1c16f2008-12-17 15:59:43 +0000660 };
scroggo@google.com8d239242013-10-01 17:27:15 +0000661
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000662 // The jump between dst configs in the table
scroggo@google.com8d239242013-10-01 17:27:15 +0000663 static const int gProcDstConfigSpan = 5;
664 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(gProcChoosers) == 5 * gProcDstConfigSpan,
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000665 gProcs_has_the_wrong_number_of_entries);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000666
reed@android.com11344262009-07-08 20:09:23 +0000667 fCTable = ctable;
668
reed@android.com8a1c16f2008-12-17 15:59:43 +0000669 int index = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000670 switch (sc) {
671 case SkScaledBitmapSampler::kGray:
672 fSrcPixelSize = 1;
673 index += 0;
674 break;
675 case SkScaledBitmapSampler::kRGB:
676 fSrcPixelSize = 3;
scroggo@google.com8d239242013-10-01 17:27:15 +0000677 index += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000678 break;
679 case SkScaledBitmapSampler::kRGBX:
680 fSrcPixelSize = 4;
scroggo@google.com8d239242013-10-01 17:27:15 +0000681 index += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000682 break;
683 case SkScaledBitmapSampler::kRGBA:
684 fSrcPixelSize = 4;
scroggo@google.com8d239242013-10-01 17:27:15 +0000685 index += 2;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000686 break;
687 case SkScaledBitmapSampler::kIndex:
688 fSrcPixelSize = 1;
scroggo@google.com8d239242013-10-01 17:27:15 +0000689 index += 3;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000690 break;
djsollen@google.com57f49692011-02-23 20:46:31 +0000691 case SkScaledBitmapSampler::kRGB_565:
692 fSrcPixelSize = 2;
scroggo@google.com8d239242013-10-01 17:27:15 +0000693 index += 4;
djsollen@google.com57f49692011-02-23 20:46:31 +0000694 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000695 default:
696 return false;
697 }
698
reed0689d7b2014-06-14 05:30:20 -0700699 switch (dst->colorType()) {
700 case kN32_SkColorType:
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000701 index += 0 * gProcDstConfigSpan;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000702 break;
reed0689d7b2014-06-14 05:30:20 -0700703 case kRGB_565_SkColorType:
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000704 index += 1 * gProcDstConfigSpan;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000705 break;
reed0689d7b2014-06-14 05:30:20 -0700706 case kARGB_4444_SkColorType:
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000707 index += 2 * gProcDstConfigSpan;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000708 break;
reed0689d7b2014-06-14 05:30:20 -0700709 case kIndex_8_SkColorType:
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000710 index += 3 * gProcDstConfigSpan;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000711 break;
reed0689d7b2014-06-14 05:30:20 -0700712 case kAlpha_8_SkColorType:
scroggo@google.comf698c822013-07-18 19:34:49 +0000713 index += 4 * gProcDstConfigSpan;
714 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000715 default:
716 return false;
717 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000718
scroggo@google.com8d239242013-10-01 17:27:15 +0000719 RowProcChooser chooser = gProcChoosers[index];
720 if (NULL == chooser) {
721 fRowProc = NULL;
722 } else {
krajcevski407d7c92014-06-09 14:29:11 -0700723 fRowProc = chooser(opts);
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000724 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000725 fDstRow = (char*)dst->getPixels();
726 fDstRowBytes = dst->rowBytes();
727 fCurrY = 0;
728 return fRowProc != NULL;
729}
730
krajcevski407d7c92014-06-09 14:29:11 -0700731bool SkScaledBitmapSampler::begin(SkBitmap* dst, SrcConfig sc,
732 const SkImageDecoder& decoder,
733 const SkPMColor ctable[]) {
734 return this->begin(dst, sc, Options(decoder), ctable);
735}
736
reed@android.com8a1c16f2008-12-17 15:59:43 +0000737bool SkScaledBitmapSampler::next(const uint8_t* SK_RESTRICT src) {
commit-bot@chromium.orgdac4a1d2013-10-08 19:40:18 +0000738 SkASSERT(kInterlaced_SampleMode != fSampleMode);
739 SkDEBUGCODE(fSampleMode = kConsecutive_SampleMode);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000740 SkASSERT((unsigned)fCurrY < (unsigned)fScaledHeight);
741
742 bool hadAlpha = fRowProc(fDstRow, src + fX0 * fSrcPixelSize, fScaledWidth,
reed@android.com11344262009-07-08 20:09:23 +0000743 fDX * fSrcPixelSize, fCurrY, fCTable);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000744 fDstRow += fDstRowBytes;
745 fCurrY += 1;
746 return hadAlpha;
747}
scroggo@google.com8d239242013-10-01 17:27:15 +0000748
commit-bot@chromium.orgdac4a1d2013-10-08 19:40:18 +0000749bool SkScaledBitmapSampler::sampleInterlaced(const uint8_t* SK_RESTRICT src, int srcY) {
750 SkASSERT(kConsecutive_SampleMode != fSampleMode);
751 SkDEBUGCODE(fSampleMode = kInterlaced_SampleMode);
752 // Any line that should be a part of the destination can be created by the formula:
753 // fY0 + (some multiplier) * fDY
754 // so if srcY - fY0 is not an integer multiple of fDY that srcY will be skipped.
755 const int srcYMinusY0 = srcY - fY0;
756 if (srcYMinusY0 % fDY != 0) {
757 // This line is not part of the output, so return false for alpha, since we have
758 // not added an alpha to the output.
759 return false;
760 }
761 // Unlike in next(), where the data is used sequentially, this function skips around,
762 // so fDstRow and fCurrY are never updated. fDstRow must always be the starting point
763 // of the destination bitmap's pixels, which is used to calculate the destination row
764 // each time this function is called.
765 const int dstY = srcYMinusY0 / fDY;
766 SkASSERT(dstY < fScaledHeight);
767 char* dstRow = fDstRow + dstY * fDstRowBytes;
768 return fRowProc(dstRow, src + fX0 * fSrcPixelSize, fScaledWidth,
769 fDX * fSrcPixelSize, dstY, fCTable);
770}
771
scroggo@google.com8d239242013-10-01 17:27:15 +0000772#ifdef SK_DEBUG
773// The following code is for a test to ensure that changing the method to get the right row proc
774// did not change the row proc unintentionally. Tested by ImageDecodingTest.cpp
775
776// friend of SkScaledBitmapSampler solely for the purpose of accessing fRowProc.
777class RowProcTester {
778public:
779 static SkScaledBitmapSampler::RowProc getRowProc(const SkScaledBitmapSampler& sampler) {
780 return sampler.fRowProc;
781 }
782};
783
784
785// Table showing the expected RowProc for each combination of inputs.
786// Table formated as follows:
787// Each group of 5 consecutive rows represents sampling from a single
788// SkScaledBitmapSampler::SrcConfig.
789// Within each set, each row represents a different destination SkBitmap::Config
790// Each column represents a different combination of dither and unpremul.
791// D = dither ~D = no dither
792// U = unpremul ~U = no unpremul
793// ~D~U D~U ~DU DU
794SkScaledBitmapSampler::RowProc gTestProcs[] = {
795 // Gray
796 Sample_Gray_DA8, Sample_Gray_DA8, NULL, NULL, // to A8
797 NULL, NULL, NULL, NULL, // to Index8
798 Sample_Gray_D565, Sample_Gray_D565_D, Sample_Gray_D565, Sample_Gray_D565_D, // to 565
799 Sample_Gray_D4444, Sample_Gray_D4444_D, Sample_Gray_D4444, Sample_Gray_D4444_D, // to 4444
800 Sample_Gray_D8888, Sample_Gray_D8888, Sample_Gray_D8888, Sample_Gray_D8888, // to 8888
801 // Index
802 NULL, NULL, NULL, NULL, // to A8
803 Sample_Index_DI, Sample_Index_DI, NULL, NULL, // to Index8
804 Sample_Index_D565, Sample_Index_D565_D, Sample_Index_D565, Sample_Index_D565_D, // to 565
805 Sample_Index_D4444, Sample_Index_D4444_D, NULL, NULL, // to 4444
806 Sample_Index_D8888, Sample_Index_D8888, NULL, NULL, // to 8888
807 // RGB
808 NULL, NULL, NULL, NULL, // to A8
809 NULL, NULL, NULL, NULL, // to Index8
810 Sample_RGBx_D565, Sample_RGBx_D565_D, Sample_RGBx_D565, Sample_RGBx_D565_D, // to 565
811 Sample_RGBx_D4444, Sample_RGBx_D4444_D, Sample_RGBx_D4444, Sample_RGBx_D4444_D, // to 4444
812 Sample_RGBx_D8888, Sample_RGBx_D8888, Sample_RGBx_D8888, Sample_RGBx_D8888, // to 8888
813 // RGBx is the same as RGB
814 NULL, NULL, NULL, NULL, // to A8
815 NULL, NULL, NULL, NULL, // to Index8
816 Sample_RGBx_D565, Sample_RGBx_D565_D, Sample_RGBx_D565, Sample_RGBx_D565_D, // to 565
817 Sample_RGBx_D4444, Sample_RGBx_D4444_D, Sample_RGBx_D4444, Sample_RGBx_D4444_D, // to 4444
818 Sample_RGBx_D8888, Sample_RGBx_D8888, Sample_RGBx_D8888, Sample_RGBx_D8888, // to 8888
819 // RGBA
820 NULL, NULL, NULL, NULL, // to A8
821 NULL, NULL, NULL, NULL, // to Index8
822 Sample_RGBx_D565, Sample_RGBx_D565_D, Sample_RGBx_D565, Sample_RGBx_D565_D, // to 565
823 Sample_RGBA_D4444, Sample_RGBA_D4444_D, NULL, NULL, // to 4444
824 Sample_RGBA_D8888, Sample_RGBA_D8888, Sample_RGBA_D8888_Unpremul, Sample_RGBA_D8888_Unpremul, // to 8888
825 // RGB_565
826 NULL, NULL, NULL, NULL, // to A8
827 NULL, NULL, NULL, NULL, // to Index8
828 Sample_D565_D565, Sample_D565_D565, Sample_D565_D565, Sample_D565_D565, // to 565
829 NULL, NULL, NULL, NULL, // to 4444
830 NULL, NULL, NULL, NULL, // to 8888
831};
832
833// Dummy class that allows instantiation of an ImageDecoder, so begin can query its fields.
834class DummyDecoder : public SkImageDecoder {
835public:
836 DummyDecoder() {}
837protected:
scroggo2a120802014-10-22 12:07:00 -0700838 virtual Result onDecode(SkStream*, SkBitmap*, SkImageDecoder::Mode) SK_OVERRIDE {
839 return kFailure;
scroggo@google.com8d239242013-10-01 17:27:15 +0000840 }
841};
842
scroggo@google.com1d069992013-10-01 17:46:35 +0000843void test_row_proc_choice();
scroggo@google.com8d239242013-10-01 17:27:15 +0000844void test_row_proc_choice() {
reed6c225732014-06-09 19:52:07 -0700845 const SkColorType colorTypes[] = {
846 kAlpha_8_SkColorType, kIndex_8_SkColorType, kRGB_565_SkColorType, kARGB_4444_SkColorType,
847 kN32_SkColorType
848 };
849
scroggo@google.com8d239242013-10-01 17:27:15 +0000850 SkBitmap dummyBitmap;
851 DummyDecoder dummyDecoder;
852 size_t procCounter = 0;
853 for (int sc = SkScaledBitmapSampler::kGray; sc <= SkScaledBitmapSampler::kRGB_565; ++sc) {
reed6c225732014-06-09 19:52:07 -0700854 for (size_t c = 0; c < SK_ARRAY_COUNT(colorTypes); ++c) {
scroggo@google.com8d239242013-10-01 17:27:15 +0000855 for (int unpremul = 0; unpremul <= 1; ++unpremul) {
856 for (int dither = 0; dither <= 1; ++dither) {
857 // Arbitrary width/height/sampleSize to allow SkScaledBitmapSampler to
858 // be considered valid.
859 SkScaledBitmapSampler sampler(10, 10, 1);
reed6c225732014-06-09 19:52:07 -0700860 dummyBitmap.setInfo(SkImageInfo::Make(10, 10,
861 colorTypes[c], kPremul_SkAlphaType));
scroggo@google.com8d239242013-10-01 17:27:15 +0000862 dummyDecoder.setDitherImage(SkToBool(dither));
863 dummyDecoder.setRequireUnpremultipliedColors(SkToBool(unpremul));
864 sampler.begin(&dummyBitmap, (SkScaledBitmapSampler::SrcConfig) sc,
865 dummyDecoder);
866 SkScaledBitmapSampler::RowProc expected = gTestProcs[procCounter];
867 SkScaledBitmapSampler::RowProc actual = RowProcTester::getRowProc(sampler);
868 SkASSERT(expected == actual);
869 procCounter++;
870 }
871 }
872 }
873 }
874 SkASSERT(SK_ARRAY_COUNT(gTestProcs) == procCounter);
875}
876#endif // SK_DEBUG