1/*
2 * Copyright 2007 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8
9#include "SkScaledBitmapSampler.h"
10#include "SkBitmap.h"
11#include "SkColorPriv.h"
12#include "SkDither.h"
13#include "SkTypes.h"
14
15// 8888
16
17static bool Sample_Gray_D8888(void* SK_RESTRICT dstRow,
18                              const uint8_t* SK_RESTRICT src,
19                              int width, int deltaSrc, int, const SkPMColor[]) {
20    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
28static 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
33static bool Sample_RGBx_D8888(void* SK_RESTRICT dstRow,
34                              const uint8_t* SK_RESTRICT src,
35                              int width, int deltaSrc, int, const SkPMColor[]) {
36    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
44static 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
49static bool Sample_RGBA_D8888(void* SK_RESTRICT dstRow,
50                              const uint8_t* SK_RESTRICT src,
51                              int width, int deltaSrc, int, const SkPMColor[]) {
52    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
63static 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
109// 565
110
111static bool Sample_Gray_D565(void* SK_RESTRICT dstRow,
112                             const uint8_t* SK_RESTRICT src,
113                             int width, int deltaSrc, int, const SkPMColor[]) {
114    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,
124                           int width, int deltaSrc, int y, const SkPMColor[]) {
125    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
134static 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
142static bool Sample_RGBx_D565(void* SK_RESTRICT dstRow,
143                             const uint8_t* SK_RESTRICT src,
144                             int width, int deltaSrc, int, const SkPMColor[]) {
145    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
153static 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
175static 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
187static SkScaledBitmapSampler::RowProc get_565_to_565_proc(const SkImageDecoder& decoder) {
188    // Unpremul, dither, and skip zeroes have no effect
189    return Sample_D565_D565;
190}
191
192// 4444
193
194static bool Sample_Gray_D4444(void* SK_RESTRICT dstRow,
195                              const uint8_t* SK_RESTRICT src,
196                              int width, int deltaSrc, int, const SkPMColor[]) {
197    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,
208                            int width, int deltaSrc, int y, const SkPMColor[]) {
209    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
219static 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
227static bool Sample_RGBx_D4444(void* SK_RESTRICT dstRow,
228                              const uint8_t* SK_RESTRICT src,
229                              int width, int deltaSrc, int, const SkPMColor[]) {
230    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,
240                            int width, int deltaSrc, int y, const SkPMColor[]) {
241    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
252static 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
260static bool Sample_RGBA_D4444(void* SK_RESTRICT dstRow,
261                              const uint8_t* SK_RESTRICT src,
262                              int width, int deltaSrc, int, const SkPMColor[]) {
263    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
276static 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
296static bool Sample_RGBA_D4444_D(void* SK_RESTRICT dstRow,
297                                const uint8_t* SK_RESTRICT src,
298                                int width, int deltaSrc, int y,
299                                const SkPMColor[]) {
300    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
314static 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
352// Index
353
354#define A32_MASK_IN_PLACE   (SkPMColor)(SK_A32_MASK << SK_A32_SHIFT)
355
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
371static 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
401static 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
429static 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
437static 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
469static 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
523static bool Sample_Index_DI(void* SK_RESTRICT dstRow,
524                            const uint8_t* SK_RESTRICT src,
525                            int width, int deltaSrc, int, const SkPMColor[]) {
526    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
538static 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
547// 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
556static SkScaledBitmapSampler::RowProc get_gray_to_A8_proc(const SkImageDecoder& decoder) {
557    if (decoder.getRequireUnpremultipliedColors()) {
558        return NULL;
559    }
560    // Ignore skip and dither.
561    return Sample_Gray_DA8;
562}
563
564typedef SkScaledBitmapSampler::RowProc (*RowProcChooser)(const SkImageDecoder& decoder);
565///////////////////////////////////////////////////////////////////////////////
566
567#include "SkScaledBitmapSampler.h"
568
569SkScaledBitmapSampler::SkScaledBitmapSampler(int width, int height,
570                                             int sampleSize) {
571    fCTable = NULL;
572    fDstRow = NULL;
573    fRowProc = NULL;
574
575    if (width <= 0 || height <= 0) {
576        sk_throw();
577    }
578
579    if (sampleSize <= 1) {
580        fScaledWidth = width;
581        fScaledHeight = height;
582        fX0 = fY0 = 0;
583        fDX = fDY = 1;
584        return;
585    }
586
587    int dx = SkMin32(sampleSize, width);
588    int dy = SkMin32(sampleSize, height);
589
590    fScaledWidth = width / dx;
591    fScaledHeight = height / dy;
592
593    SkASSERT(fScaledWidth > 0);
594    SkASSERT(fScaledHeight > 0);
595
596    fX0 = dx >> 1;
597    fY0 = dy >> 1;
598
599    SkASSERT(fX0 >= 0 && fX0 < width);
600    SkASSERT(fY0 >= 0 && fY0 < height);
601
602    fDX = dx;
603    fDY = dy;
604
605    SkASSERT(fDX > 0 && (fX0 + fDX * (fScaledWidth - 1)) < width);
606    SkASSERT(fDY > 0 && (fY0 + fDY * (fScaledHeight - 1)) < height);
607}
608
609bool SkScaledBitmapSampler::begin(SkBitmap* dst, SrcConfig sc,
610                                  const SkImageDecoder& decoder,
611                                  const SkPMColor ctable[]) {
612    static const RowProcChooser gProcChoosers[] = {
613        get_gray_to_8888_proc,
614        get_RGBx_to_8888_proc,
615        get_RGBA_to_8888_proc,
616        get_index_to_8888_proc,
617        NULL, // 565 to 8888
618
619        get_gray_to_565_proc,
620        get_RGBx_to_565_proc,
621        get_RGBx_to_565_proc, // The source alpha will be ignored.
622        get_index_to_565_proc,
623        get_565_to_565_proc,
624
625        get_gray_to_4444_proc,
626        get_RGBx_to_4444_proc,
627        get_RGBA_to_4444_proc,
628        get_index_to_4444_proc,
629        NULL, // 565 to 4444
630
631        NULL, // gray to index
632        NULL, // rgbx to index
633        NULL, // rgba to index
634        get_index_to_index_proc,
635        NULL, // 565 to index
636
637        get_gray_to_A8_proc,
638        NULL, // rgbx to a8
639        NULL, // rgba to a8
640        NULL, // index to a8
641        NULL, // 565 to a8
642    };
643
644    // The jump between dst configs in the table
645    static const int gProcDstConfigSpan = 5;
646    SK_COMPILE_ASSERT(SK_ARRAY_COUNT(gProcChoosers) == 5 * gProcDstConfigSpan,
647                      gProcs_has_the_wrong_number_of_entries);
648
649    fCTable = ctable;
650
651    int index = 0;
652    switch (sc) {
653        case SkScaledBitmapSampler::kGray:
654            fSrcPixelSize = 1;
655            index += 0;
656            break;
657        case SkScaledBitmapSampler::kRGB:
658            fSrcPixelSize = 3;
659            index += 1;
660            break;
661        case SkScaledBitmapSampler::kRGBX:
662            fSrcPixelSize = 4;
663            index += 1;
664            break;
665        case SkScaledBitmapSampler::kRGBA:
666            fSrcPixelSize = 4;
667            index += 2;
668            break;
669        case SkScaledBitmapSampler::kIndex:
670            fSrcPixelSize = 1;
671            index += 3;
672            break;
673        case SkScaledBitmapSampler::kRGB_565:
674            fSrcPixelSize = 2;
675            index += 4;
676            break;
677        default:
678            return false;
679    }
680
681    switch (dst->config()) {
682        case SkBitmap::kARGB_8888_Config:
683            index += 0 * gProcDstConfigSpan;
684            break;
685        case SkBitmap::kRGB_565_Config:
686            index += 1 * gProcDstConfigSpan;
687            break;
688        case SkBitmap::kARGB_4444_Config:
689            index += 2 * gProcDstConfigSpan;
690            break;
691        case SkBitmap::kIndex8_Config:
692            index += 3 * gProcDstConfigSpan;
693            break;
694        case SkBitmap::kA8_Config:
695            index += 4 * gProcDstConfigSpan;
696            break;
697        default:
698            return false;
699    }
700
701    RowProcChooser chooser = gProcChoosers[index];
702    if (NULL == chooser) {
703        fRowProc = NULL;
704    } else {
705        fRowProc = chooser(decoder);
706    }
707    fDstRow = (char*)dst->getPixels();
708    fDstRowBytes = dst->rowBytes();
709    fCurrY = 0;
710    return fRowProc != NULL;
711}
712
713bool SkScaledBitmapSampler::next(const uint8_t* SK_RESTRICT src) {
714    SkASSERT((unsigned)fCurrY < (unsigned)fScaledHeight);
715
716    bool hadAlpha = fRowProc(fDstRow, src + fX0 * fSrcPixelSize, fScaledWidth,
717                             fDX * fSrcPixelSize, fCurrY, fCTable);
718    fDstRow += fDstRowBytes;
719    fCurrY += 1;
720    return hadAlpha;
721}
722
723#ifdef SK_DEBUG
724// The following code is for a test to ensure that changing the method to get the right row proc
725// did not change the row proc unintentionally. Tested by ImageDecodingTest.cpp
726
727// friend of SkScaledBitmapSampler solely for the purpose of accessing fRowProc.
728class RowProcTester {
729public:
730    static SkScaledBitmapSampler::RowProc getRowProc(const SkScaledBitmapSampler& sampler) {
731        return sampler.fRowProc;
732    }
733};
734
735
736// Table showing the expected RowProc for each combination of inputs.
737// Table formated as follows:
738// Each group of 5 consecutive rows represents sampling from a single
739// SkScaledBitmapSampler::SrcConfig.
740// Within each set, each row represents a different destination SkBitmap::Config
741// Each column represents a different combination of dither and unpremul.
742// D = dither   ~D = no dither
743// U = unpremul ~U = no unpremul
744//  ~D~U                D~U                     ~DU                         DU
745SkScaledBitmapSampler::RowProc gTestProcs[] = {
746    // Gray
747    Sample_Gray_DA8,    Sample_Gray_DA8,        NULL,                       NULL,                       // to A8
748    NULL,               NULL,                   NULL,                       NULL,                       // to Index8
749    Sample_Gray_D565,   Sample_Gray_D565_D,     Sample_Gray_D565,           Sample_Gray_D565_D,         // to 565
750    Sample_Gray_D4444,  Sample_Gray_D4444_D,    Sample_Gray_D4444,          Sample_Gray_D4444_D,        // to 4444
751    Sample_Gray_D8888,  Sample_Gray_D8888,      Sample_Gray_D8888,          Sample_Gray_D8888,          // to 8888
752    // Index
753    NULL,               NULL,                   NULL,                       NULL,                       // to A8
754    Sample_Index_DI,    Sample_Index_DI,        NULL,                       NULL,                       // to Index8
755    Sample_Index_D565,  Sample_Index_D565_D,    Sample_Index_D565,          Sample_Index_D565_D,        // to 565
756    Sample_Index_D4444, Sample_Index_D4444_D,   NULL,                       NULL,                       // to 4444
757    Sample_Index_D8888, Sample_Index_D8888,     NULL,                       NULL,                       // to 8888
758    // RGB
759    NULL,               NULL,                   NULL,                       NULL,                       // to A8
760    NULL,               NULL,                   NULL,                       NULL,                       // to Index8
761    Sample_RGBx_D565,   Sample_RGBx_D565_D,     Sample_RGBx_D565,           Sample_RGBx_D565_D,         // to 565
762    Sample_RGBx_D4444,  Sample_RGBx_D4444_D,    Sample_RGBx_D4444,          Sample_RGBx_D4444_D,        // to 4444
763    Sample_RGBx_D8888,  Sample_RGBx_D8888,      Sample_RGBx_D8888,          Sample_RGBx_D8888,          // to 8888
764    // RGBx is the same as RGB
765    NULL,               NULL,                   NULL,                       NULL,                       // to A8
766    NULL,               NULL,                   NULL,                       NULL,                       // to Index8
767    Sample_RGBx_D565,   Sample_RGBx_D565_D,     Sample_RGBx_D565,           Sample_RGBx_D565_D,         // to 565
768    Sample_RGBx_D4444,  Sample_RGBx_D4444_D,    Sample_RGBx_D4444,          Sample_RGBx_D4444_D,        // to 4444
769    Sample_RGBx_D8888,  Sample_RGBx_D8888,      Sample_RGBx_D8888,          Sample_RGBx_D8888,          // to 8888
770    // RGBA
771    NULL,               NULL,                   NULL,                       NULL,                       // to A8
772    NULL,               NULL,                   NULL,                       NULL,                       // to Index8
773    Sample_RGBx_D565,   Sample_RGBx_D565_D,     Sample_RGBx_D565,           Sample_RGBx_D565_D,         // to 565
774    Sample_RGBA_D4444,  Sample_RGBA_D4444_D,    NULL,                       NULL,                       // to 4444
775    Sample_RGBA_D8888,  Sample_RGBA_D8888,      Sample_RGBA_D8888_Unpremul, Sample_RGBA_D8888_Unpremul, // to 8888
776    // RGB_565
777    NULL,               NULL,                   NULL,                       NULL,                       // to A8
778    NULL,               NULL,                   NULL,                       NULL,                       // to Index8
779    Sample_D565_D565,   Sample_D565_D565,       Sample_D565_D565,           Sample_D565_D565,           // to 565
780    NULL,               NULL,                   NULL,                       NULL,                       // to 4444
781    NULL,               NULL,                   NULL,                       NULL,                       // to 8888
782};
783
784// Dummy class that allows instantiation of an ImageDecoder, so begin can query its fields.
785class DummyDecoder : public SkImageDecoder {
786public:
787    DummyDecoder() {}
788protected:
789    virtual bool onDecode(SkStream*, SkBitmap*, SkImageDecoder::Mode) SK_OVERRIDE {
790        return false;
791    }
792};
793
794void test_row_proc_choice() {
795    SkBitmap dummyBitmap;
796    DummyDecoder dummyDecoder;
797    size_t procCounter = 0;
798    for (int sc = SkScaledBitmapSampler::kGray; sc <= SkScaledBitmapSampler::kRGB_565; ++sc) {
799        for (int c = SkBitmap::kA8_Config; c <= SkBitmap::kARGB_8888_Config; ++c) {
800            for (int unpremul = 0; unpremul <= 1; ++unpremul) {
801                for (int dither = 0; dither <= 1; ++dither) {
802                    // Arbitrary width/height/sampleSize to allow SkScaledBitmapSampler to
803                    // be considered valid.
804                    SkScaledBitmapSampler sampler(10, 10, 1);
805                    dummyBitmap.setConfig((SkBitmap::Config) c, 10, 10);
806                    dummyDecoder.setDitherImage(SkToBool(dither));
807                    dummyDecoder.setRequireUnpremultipliedColors(SkToBool(unpremul));
808                    sampler.begin(&dummyBitmap, (SkScaledBitmapSampler::SrcConfig) sc,
809                                  dummyDecoder);
810                    SkScaledBitmapSampler::RowProc expected = gTestProcs[procCounter];
811                    SkScaledBitmapSampler::RowProc actual = RowProcTester::getRowProc(sampler);
812                    SkASSERT(expected == actual);
813                    procCounter++;
814                }
815            }
816        }
817    }
818    SkASSERT(SK_ARRAY_COUNT(gTestProcs) == procCounter);
819}
820#endif // SK_DEBUG
821