/*
* Copyright 2007 The Android Open Source Project
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkScaledBitmapSampler.h"
#include "SkBitmap.h"
#include "SkColorPriv.h"
#include "SkDither.h"
#include "SkTypes.h"
// 8888
static bool Sample_Gray_D8888(void* SK_RESTRICT dstRow,
const uint8_t* SK_RESTRICT src,
int width, int deltaSrc, int, const SkPMColor[]) {
SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
for (int x = 0; x < width; x++) {
dst[x] = SkPackARGB32(0xFF, src[0], src[0], src[0]);
src += deltaSrc;
}
return false;
}
static SkScaledBitmapSampler::RowProc
get_gray_to_8888_proc(const SkScaledBitmapSampler::Options& opts) {
// Dither, unpremul, and skipZeroes have no effect
return Sample_Gray_D8888;
}
static bool Sample_RGBx_D8888(void* SK_RESTRICT dstRow,
const uint8_t* SK_RESTRICT src,
int width, int deltaSrc, int, const SkPMColor[]) {
SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
for (int x = 0; x < width; x++) {
dst[x] = SkPackARGB32(0xFF, src[0], src[1], src[2]);
src += deltaSrc;
}
return false;
}
static SkScaledBitmapSampler::RowProc
get_RGBx_to_8888_proc(const SkScaledBitmapSampler::Options& opts) {
// Dither, unpremul, and skipZeroes have no effect
return Sample_RGBx_D8888;
}
static bool Sample_RGBA_D8888(void* SK_RESTRICT dstRow,
const uint8_t* SK_RESTRICT src,
int width, int deltaSrc, int, const SkPMColor[]) {
SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
unsigned alphaMask = 0xFF;
for (int x = 0; x < width; x++) {
unsigned alpha = src[3];
dst[x] = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]);
src += deltaSrc;
alphaMask &= alpha;
}
return alphaMask != 0xFF;
}
static bool Sample_RGBA_D8888_Unpremul(void* SK_RESTRICT dstRow,
const uint8_t* SK_RESTRICT src,
int width, int deltaSrc, int,
const SkPMColor[]) {
uint32_t* SK_RESTRICT dst = reinterpret_cast<uint32_t*>(dstRow);
unsigned alphaMask = 0xFF;
for (int x = 0; x < width; x++) {
unsigned alpha = src[3];
dst[x] = SkPackARGB32NoCheck(alpha, src[0], src[1], src[2]);
src += deltaSrc;
alphaMask &= alpha;
}
return alphaMask != 0xFF;
}
static bool Sample_RGBA_D8888_SkipZ(void* SK_RESTRICT dstRow,
const uint8_t* SK_RESTRICT src,
int width, int deltaSrc, int,
const SkPMColor[]) {
SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
unsigned alphaMask = 0xFF;
for (int x = 0; x < width; x++) {
unsigned alpha = src[3];
if (0 != alpha) {
dst[x] = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]);
}
src += deltaSrc;
alphaMask &= alpha;
}
return alphaMask != 0xFF;
}
static SkScaledBitmapSampler::RowProc
get_RGBA_to_8888_proc(const SkScaledBitmapSampler::Options& opts) {
// Dither has no effect.
if (!opts.fPremultiplyAlpha) {
// We could check each component for a zero, at the expense of extra checks.
// For now, just return unpremul.
return Sample_RGBA_D8888_Unpremul;
}
// Supply the versions that premultiply the colors
if (opts.fSkipZeros) {
return Sample_RGBA_D8888_SkipZ;
}
return Sample_RGBA_D8888;
}
// 565
static bool Sample_Gray_D565(void* SK_RESTRICT dstRow,
const uint8_t* SK_RESTRICT src,
int width, int deltaSrc, int, const SkPMColor[]) {
uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow;
for (int x = 0; x < width; x++) {
dst[x] = SkPack888ToRGB16(src[0], src[0], src[0]);
src += deltaSrc;
}
return false;
}
static bool Sample_Gray_D565_D(void* SK_RESTRICT dstRow,
const uint8_t* SK_RESTRICT src,
int width, int deltaSrc, int y, const SkPMColor[]) {
uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow;
DITHER_565_SCAN(y);
for (int x = 0; x < width; x++) {
dst[x] = SkDitherRGBTo565(src[0], src[0], src[0], DITHER_VALUE(x));
src += deltaSrc;
}
return false;
}
static SkScaledBitmapSampler::RowProc
get_gray_to_565_proc(const SkScaledBitmapSampler::Options& opts) {
// Unpremul and skip zeroes make no difference
if (opts.fDither) {
return Sample_Gray_D565_D;
}
return Sample_Gray_D565;
}
static bool Sample_RGBx_D565(void* SK_RESTRICT dstRow,
const uint8_t* SK_RESTRICT src,
int width, int deltaSrc, int, const SkPMColor[]) {
uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow;
for (int x = 0; x < width; x++) {
dst[x] = SkPack888ToRGB16(src[0], src[1], src[2]);
src += deltaSrc;
}
return false;
}
static bool Sample_RGBx_D565_D(void* SK_RESTRICT dstRow,
const uint8_t* SK_RESTRICT src,
int width, int deltaSrc, int y,
const SkPMColor[]) {
uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow;
DITHER_565_SCAN(y);
for (int x = 0; x < width; x++) {
dst[x] = SkDitherRGBTo565(src[0], src[1], src[2], DITHER_VALUE(x));
src += deltaSrc;
}
return false;
}
static SkScaledBitmapSampler::RowProc
get_RGBx_to_565_proc(const SkScaledBitmapSampler::Options& opts) {
// Unpremul and skip zeroes make no difference
if (opts.fDither) {
return Sample_RGBx_D565_D;
}
return Sample_RGBx_D565;
}
static bool Sample_D565_D565(void* SK_RESTRICT dstRow,
const uint8_t* SK_RESTRICT src,
int width, int deltaSrc, int, const SkPMColor[]) {
uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow;
uint16_t* SK_RESTRICT castedSrc = (uint16_t*) src;
for (int x = 0; x < width; x++) {
dst[x] = castedSrc[0];
castedSrc += deltaSrc >> 1;
}
return false;
}
static SkScaledBitmapSampler::RowProc
get_565_to_565_proc(const SkScaledBitmapSampler::Options& opts) {
// Unpremul, dither, and skip zeroes have no effect
return Sample_D565_D565;
}
// 4444
static bool Sample_Gray_D4444(void* SK_RESTRICT dstRow,
const uint8_t* SK_RESTRICT src,
int width, int deltaSrc, int, const SkPMColor[]) {
SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
for (int x = 0; x < width; x++) {
unsigned gray = src[0] >> 4;
dst[x] = SkPackARGB4444(0xF, gray, gray, gray);
src += deltaSrc;
}
return false;
}
static bool Sample_Gray_D4444_D(void* SK_RESTRICT dstRow,
const uint8_t* SK_RESTRICT src,
int width, int deltaSrc, int y, const SkPMColor[]) {
SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
DITHER_4444_SCAN(y);
for (int x = 0; x < width; x++) {
dst[x] = SkDitherARGB32To4444(0xFF, src[0], src[0], src[0],
DITHER_VALUE(x));
src += deltaSrc;
}
return false;
}
static SkScaledBitmapSampler::RowProc
get_gray_to_4444_proc(const SkScaledBitmapSampler::Options& opts) {
// Skip zeroes and unpremul make no difference
if (opts.fDither) {
return Sample_Gray_D4444_D;
}
return Sample_Gray_D4444;
}
static bool Sample_RGBx_D4444(void* SK_RESTRICT dstRow,
const uint8_t* SK_RESTRICT src,
int width, int deltaSrc, int, const SkPMColor[]) {
SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
for (int x = 0; x < width; x++) {
dst[x] = SkPackARGB4444(0xF, src[0] >> 4, src[1] >> 4, src[2] >> 4);
src += deltaSrc;
}
return false;
}
static bool Sample_RGBx_D4444_D(void* SK_RESTRICT dstRow,
const uint8_t* SK_RESTRICT src,
int width, int deltaSrc, int y, const SkPMColor[]) {
SkPMColor16* dst = (SkPMColor16*)dstRow;
DITHER_4444_SCAN(y);
for (int x = 0; x < width; x++) {
dst[x] = SkDitherARGB32To4444(0xFF, src[0], src[1], src[2],
DITHER_VALUE(x));
src += deltaSrc;
}
return false;
}
static SkScaledBitmapSampler::RowProc
get_RGBx_to_4444_proc(const SkScaledBitmapSampler::Options& opts) {
// Skip zeroes and unpremul make no difference
if (opts.fDither) {
return Sample_RGBx_D4444_D;
}
return Sample_RGBx_D4444;
}
static bool Sample_RGBA_D4444(void* SK_RESTRICT dstRow,
const uint8_t* SK_RESTRICT src,
int width, int deltaSrc, int, const SkPMColor[]) {
SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
unsigned alphaMask = 0xFF;
for (int x = 0; x < width; x++) {
unsigned alpha = src[3];
SkPMColor c = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]);
dst[x] = SkPixel32ToPixel4444(c);
src += deltaSrc;
alphaMask &= alpha;
}
return alphaMask != 0xFF;
}
static bool Sample_RGBA_D4444_SkipZ(void* SK_RESTRICT dstRow,
const uint8_t* SK_RESTRICT src,
int width, int deltaSrc, int,
const SkPMColor[]) {
SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
unsigned alphaMask = 0xFF;
for (int x = 0; x < width; x++) {
unsigned alpha = src[3];
if (alpha != 0) {
SkPMColor c = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]);
dst[x] = SkPixel32ToPixel4444(c);
}
src += deltaSrc;
alphaMask &= alpha;
}
return alphaMask != 0xFF;
}
static bool Sample_RGBA_D4444_D(void* SK_RESTRICT dstRow,
const uint8_t* SK_RESTRICT src,
int width, int deltaSrc, int y,
const SkPMColor[]) {
SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
unsigned alphaMask = 0xFF;
DITHER_4444_SCAN(y);
for (int x = 0; x < width; x++) {
unsigned alpha = src[3];
SkPMColor c = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]);
dst[x] = SkDitherARGB32To4444(c, DITHER_VALUE(x));
src += deltaSrc;
alphaMask &= alpha;
}
return alphaMask != 0xFF;
}
static bool Sample_RGBA_D4444_D_SkipZ(void* SK_RESTRICT dstRow,
const uint8_t* SK_RESTRICT src,
int width, int deltaSrc, int y,
const SkPMColor[]) {
SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
unsigned alphaMask = 0xFF;
DITHER_4444_SCAN(y);
for (int x = 0; x < width; x++) {
unsigned alpha = src[3];
if (alpha != 0) {
SkPMColor c = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]);
dst[x] = SkDitherARGB32To4444(c, DITHER_VALUE(x));
}
src += deltaSrc;
alphaMask &= alpha;
}
return alphaMask != 0xFF;
}
static SkScaledBitmapSampler::RowProc
get_RGBA_to_4444_proc(const SkScaledBitmapSampler::Options& opts) {
if (!opts.fPremultiplyAlpha) {
// Unpremultiplied is not supported for 4444
return NULL;
}
if (opts.fSkipZeros) {
if (opts.fDither) {
return Sample_RGBA_D4444_D_SkipZ;
}
return Sample_RGBA_D4444_SkipZ;
}
if (opts.fDither) {
return Sample_RGBA_D4444_D;
}
return Sample_RGBA_D4444;
}
// Index
#define A32_MASK_IN_PLACE (SkPMColor)(SK_A32_MASK << SK_A32_SHIFT)
static bool Sample_Index_D8888(void* SK_RESTRICT dstRow,
const uint8_t* SK_RESTRICT src,
int width, int deltaSrc, int, const SkPMColor ctable[]) {
SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
SkPMColor cc = A32_MASK_IN_PLACE;
for (int x = 0; x < width; x++) {
SkPMColor c = ctable[*src];
cc &= c;
dst[x] = c;
src += deltaSrc;
}
return cc != A32_MASK_IN_PLACE;
}
static bool Sample_Index_D8888_SkipZ(void* SK_RESTRICT dstRow,
const uint8_t* SK_RESTRICT src,
int width, int deltaSrc, int,
const SkPMColor ctable[]) {
SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
SkPMColor cc = A32_MASK_IN_PLACE;
for (int x = 0; x < width; x++) {
SkPMColor c = ctable[*src];
cc &= c;
if (c != 0) {
dst[x] = c;
}
src += deltaSrc;
}
return cc != A32_MASK_IN_PLACE;
}
static SkScaledBitmapSampler::RowProc
get_index_to_8888_proc(const SkScaledBitmapSampler::Options& opts) {
if (!opts.fPremultiplyAlpha) {
// Unpremultiplied is not supported for an index source.
return NULL;
}
// Dither makes no difference
if (opts.fSkipZeros) {
return Sample_Index_D8888_SkipZ;
}
return Sample_Index_D8888;
}
static bool Sample_Index_D565(void* SK_RESTRICT dstRow,
const uint8_t* SK_RESTRICT src,
int width, int deltaSrc, int, const SkPMColor ctable[]) {
uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow;
for (int x = 0; x < width; x++) {
dst[x] = SkPixel32ToPixel16(ctable[*src]);
src += deltaSrc;
}
return false;
}
static bool Sample_Index_D565_D(void* SK_RESTRICT dstRow,
const uint8_t* SK_RESTRICT src, int width,
int deltaSrc, int y, const SkPMColor ctable[]) {
uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow;
DITHER_565_SCAN(y);
for (int x = 0; x < width; x++) {
SkPMColor c = ctable[*src];
dst[x] = SkDitherRGBTo565(SkGetPackedR32(c), SkGetPackedG32(c),
SkGetPackedB32(c), DITHER_VALUE(x));
src += deltaSrc;
}
return false;
}
static SkScaledBitmapSampler::RowProc
get_index_to_565_proc(const SkScaledBitmapSampler::Options& opts) {
// Unpremultiplied and skip zeroes make no difference
if (opts.fDither) {
return Sample_Index_D565_D;
}
return Sample_Index_D565;
}
static bool Sample_Index_D4444(void* SK_RESTRICT dstRow,
const uint8_t* SK_RESTRICT src, int width,
int deltaSrc, int y, const SkPMColor ctable[]) {
SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
SkPMColor cc = A32_MASK_IN_PLACE;
for (int x = 0; x < width; x++) {
SkPMColor c = ctable[*src];
cc &= c;
dst[x] = SkPixel32ToPixel4444(c);
src += deltaSrc;
}
return cc != A32_MASK_IN_PLACE;
}
static bool Sample_Index_D4444_D(void* SK_RESTRICT dstRow,
const uint8_t* SK_RESTRICT src, int width,
int deltaSrc, int y, const SkPMColor ctable[]) {
SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
SkPMColor cc = A32_MASK_IN_PLACE;
DITHER_4444_SCAN(y);
for (int x = 0; x < width; x++) {
SkPMColor c = ctable[*src];
cc &= c;
dst[x] = SkDitherARGB32To4444(c, DITHER_VALUE(x));
src += deltaSrc;
}
return cc != A32_MASK_IN_PLACE;
}
static bool Sample_Index_D4444_SkipZ(void* SK_RESTRICT dstRow,
const uint8_t* SK_RESTRICT src, int width,
int deltaSrc, int y, const SkPMColor ctable[]) {
SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
SkPMColor cc = A32_MASK_IN_PLACE;
for (int x = 0; x < width; x++) {
SkPMColor c = ctable[*src];
cc &= c;
if (c != 0) {
dst[x] = SkPixel32ToPixel4444(c);
}
src += deltaSrc;
}
return cc != A32_MASK_IN_PLACE;
}
static bool Sample_Index_D4444_D_SkipZ(void* SK_RESTRICT dstRow,
const uint8_t* SK_RESTRICT src, int width,
int deltaSrc, int y, const SkPMColor ctable[]) {
SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
SkPMColor cc = A32_MASK_IN_PLACE;
DITHER_4444_SCAN(y);
for (int x = 0; x < width; x++) {
SkPMColor c = ctable[*src];
cc &= c;
if (c != 0) {
dst[x] = SkDitherARGB32To4444(c, DITHER_VALUE(x));
}
src += deltaSrc;
}
return cc != A32_MASK_IN_PLACE;
}
static SkScaledBitmapSampler::RowProc
get_index_to_4444_proc(const SkScaledBitmapSampler::Options& opts) {
// Unpremul not allowed
if (!opts.fPremultiplyAlpha) {
return NULL;
}
if (opts.fSkipZeros) {
if (opts.fDither) {
return Sample_Index_D4444_D_SkipZ;
}
return Sample_Index_D4444_SkipZ;
}
if (opts.fDither) {
return Sample_Index_D4444_D;
}
return Sample_Index_D4444;
}
static bool Sample_Index_DI(void* SK_RESTRICT dstRow,
const uint8_t* SK_RESTRICT src,
int width, int deltaSrc, int, const SkPMColor[]) {
if (1 == deltaSrc) {
memcpy(dstRow, src, width);
} else {
uint8_t* SK_RESTRICT dst = (uint8_t*)dstRow;
for (int x = 0; x < width; x++) {
dst[x] = src[0];
src += deltaSrc;
}
}
return false;
}
static SkScaledBitmapSampler::RowProc
get_index_to_index_proc(const SkScaledBitmapSampler::Options& opts) {
// Unpremul not allowed
if (!opts.fPremultiplyAlpha) {
return NULL;
}
// Ignore dither and skip zeroes
return Sample_Index_DI;
}
// A8
static bool Sample_Gray_DA8(void* SK_RESTRICT dstRow,
const uint8_t* SK_RESTRICT src,
int width, int deltaSrc, int,
const SkPMColor[]) {
// Sampling Gray to A8 uses the same function as Index to Index8,
// except we assume that there is alpha for speed, since an A8
// bitmap with no alpha is not interesting.
(void) Sample_Index_DI(dstRow, src, width, deltaSrc, /* y unused */ 0,
/* ctable unused */ NULL);
return true;
}
static SkScaledBitmapSampler::RowProc
get_gray_to_A8_proc(const SkScaledBitmapSampler::Options& opts) {
if (!opts.fPremultiplyAlpha) {
return NULL;
}
// Ignore skip and dither.
return Sample_Gray_DA8;
}
typedef SkScaledBitmapSampler::RowProc (*RowProcChooser)(const SkScaledBitmapSampler::Options&);
///////////////////////////////////////////////////////////////////////////////
#include "SkScaledBitmapSampler.h"
SkScaledBitmapSampler::SkScaledBitmapSampler(int width, int height,
int sampleSize) {
fCTable = NULL;
fDstRow = NULL;
fRowProc = NULL;
if (width <= 0 || height <= 0) {
sk_throw();
}
SkDEBUGCODE(fSampleMode = kUninitialized_SampleMode);
if (sampleSize <= 1) {
fScaledWidth = width;
fScaledHeight = height;
fX0 = fY0 = 0;
fDX = fDY = 1;
return;
}
int dx = SkMin32(sampleSize, width);
int dy = SkMin32(sampleSize, height);
fScaledWidth = width / dx;
fScaledHeight = height / dy;
SkASSERT(fScaledWidth > 0);
SkASSERT(fScaledHeight > 0);
fX0 = dx >> 1;
fY0 = dy >> 1;
SkASSERT(fX0 >= 0 && fX0 < width);
SkASSERT(fY0 >= 0 && fY0 < height);
fDX = dx;
fDY = dy;
SkASSERT(fDX > 0 && (fX0 + fDX * (fScaledWidth - 1)) < width);
SkASSERT(fDY > 0 && (fY0 + fDY * (fScaledHeight - 1)) < height);
}
bool SkScaledBitmapSampler::begin(SkBitmap* dst, SrcConfig sc,
const Options& opts,
const SkPMColor ctable[]) {
static const RowProcChooser gProcChoosers[] = {
get_gray_to_8888_proc,
get_RGBx_to_8888_proc,
get_RGBA_to_8888_proc,
get_index_to_8888_proc,
NULL, // 565 to 8888
get_gray_to_565_proc,
get_RGBx_to_565_proc,
get_RGBx_to_565_proc, // The source alpha will be ignored.
get_index_to_565_proc,
get_565_to_565_proc,
get_gray_to_4444_proc,
get_RGBx_to_4444_proc,
get_RGBA_to_4444_proc,
get_index_to_4444_proc,
NULL, // 565 to 4444
NULL, // gray to index
NULL, // rgbx to index
NULL, // rgba to index
get_index_to_index_proc,
NULL, // 565 to index
get_gray_to_A8_proc,
NULL, // rgbx to a8
NULL, // rgba to a8
NULL, // index to a8
NULL, // 565 to a8
};
// The jump between dst configs in the table
static const int gProcDstConfigSpan = 5;
SK_COMPILE_ASSERT(SK_ARRAY_COUNT(gProcChoosers) == 5 * gProcDstConfigSpan,
gProcs_has_the_wrong_number_of_entries);
fCTable = ctable;
int index = 0;
switch (sc) {
case SkScaledBitmapSampler::kGray:
fSrcPixelSize = 1;
index += 0;
break;
case SkScaledBitmapSampler::kRGB:
fSrcPixelSize = 3;
index += 1;
break;
case SkScaledBitmapSampler::kRGBX:
fSrcPixelSize = 4;
index += 1;
break;
case SkScaledBitmapSampler::kRGBA:
fSrcPixelSize = 4;
index += 2;
break;
case SkScaledBitmapSampler::kIndex:
fSrcPixelSize = 1;
index += 3;
break;
case SkScaledBitmapSampler::kRGB_565:
fSrcPixelSize = 2;
index += 4;
break;
default:
return false;
}
switch (dst->colorType()) {
case kN32_SkColorType:
index += 0 * gProcDstConfigSpan;
break;
case kRGB_565_SkColorType:
index += 1 * gProcDstConfigSpan;
break;
case kARGB_4444_SkColorType:
index += 2 * gProcDstConfigSpan;
break;
case kIndex_8_SkColorType:
index += 3 * gProcDstConfigSpan;
break;
case kAlpha_8_SkColorType:
index += 4 * gProcDstConfigSpan;
break;
default:
return false;
}
RowProcChooser chooser = gProcChoosers[index];
if (NULL == chooser) {
fRowProc = NULL;
} else {
fRowProc = chooser(opts);
}
fDstRow = (char*)dst->getPixels();
fDstRowBytes = dst->rowBytes();
fCurrY = 0;
return fRowProc != NULL;
}
bool SkScaledBitmapSampler::begin(SkBitmap* dst, SrcConfig sc,
const SkImageDecoder& decoder,
const SkPMColor ctable[]) {
return this->begin(dst, sc, Options(decoder), ctable);
}
bool SkScaledBitmapSampler::next(const uint8_t* SK_RESTRICT src) {
SkASSERT(kInterlaced_SampleMode != fSampleMode);
SkDEBUGCODE(fSampleMode = kConsecutive_SampleMode);
SkASSERT((unsigned)fCurrY < (unsigned)fScaledHeight);
bool hadAlpha = fRowProc(fDstRow, src + fX0 * fSrcPixelSize, fScaledWidth,
fDX * fSrcPixelSize, fCurrY, fCTable);
fDstRow += fDstRowBytes;
fCurrY += 1;
return hadAlpha;
}
bool SkScaledBitmapSampler::sampleInterlaced(const uint8_t* SK_RESTRICT src, int srcY) {
SkASSERT(kConsecutive_SampleMode != fSampleMode);
SkDEBUGCODE(fSampleMode = kInterlaced_SampleMode);
// Any line that should be a part of the destination can be created by the formula:
// fY0 + (some multiplier) * fDY
// so if srcY - fY0 is not an integer multiple of fDY that srcY will be skipped.
const int srcYMinusY0 = srcY - fY0;
if (srcYMinusY0 % fDY != 0) {
// This line is not part of the output, so return false for alpha, since we have
// not added an alpha to the output.
return false;
}
// Unlike in next(), where the data is used sequentially, this function skips around,
// so fDstRow and fCurrY are never updated. fDstRow must always be the starting point
// of the destination bitmap's pixels, which is used to calculate the destination row
// each time this function is called.
const int dstY = srcYMinusY0 / fDY;
SkASSERT(dstY < fScaledHeight);
char* dstRow = fDstRow + dstY * fDstRowBytes;
return fRowProc(dstRow, src + fX0 * fSrcPixelSize, fScaledWidth,
fDX * fSrcPixelSize, dstY, fCTable);
}
#ifdef SK_DEBUG
// The following code is for a test to ensure that changing the method to get the right row proc
// did not change the row proc unintentionally. Tested by ImageDecodingTest.cpp
// friend of SkScaledBitmapSampler solely for the purpose of accessing fRowProc.
class RowProcTester {
public:
static SkScaledBitmapSampler::RowProc getRowProc(const SkScaledBitmapSampler& sampler) {
return sampler.fRowProc;
}
};
// Table showing the expected RowProc for each combination of inputs.
// Table formated as follows:
// Each group of 5 consecutive rows represents sampling from a single
// SkScaledBitmapSampler::SrcConfig.
// Within each set, each row represents a different destination SkBitmap::Config
// Each column represents a different combination of dither and unpremul.
// D = dither ~D = no dither
// U = unpremul ~U = no unpremul
// ~D~U D~U ~DU DU
SkScaledBitmapSampler::RowProc gTestProcs[] = {
// Gray
Sample_Gray_DA8, Sample_Gray_DA8, NULL, NULL, // to A8
NULL, NULL, NULL, NULL, // to Index8
Sample_Gray_D565, Sample_Gray_D565_D, Sample_Gray_D565, Sample_Gray_D565_D, // to 565
Sample_Gray_D4444, Sample_Gray_D4444_D, Sample_Gray_D4444, Sample_Gray_D4444_D, // to 4444
Sample_Gray_D8888, Sample_Gray_D8888, Sample_Gray_D8888, Sample_Gray_D8888, // to 8888
// Index
NULL, NULL, NULL, NULL, // to A8
Sample_Index_DI, Sample_Index_DI, NULL, NULL, // to Index8
Sample_Index_D565, Sample_Index_D565_D, Sample_Index_D565, Sample_Index_D565_D, // to 565
Sample_Index_D4444, Sample_Index_D4444_D, NULL, NULL, // to 4444
Sample_Index_D8888, Sample_Index_D8888, NULL, NULL, // to 8888
// RGB
NULL, NULL, NULL, NULL, // to A8
NULL, NULL, NULL, NULL, // to Index8
Sample_RGBx_D565, Sample_RGBx_D565_D, Sample_RGBx_D565, Sample_RGBx_D565_D, // to 565
Sample_RGBx_D4444, Sample_RGBx_D4444_D, Sample_RGBx_D4444, Sample_RGBx_D4444_D, // to 4444
Sample_RGBx_D8888, Sample_RGBx_D8888, Sample_RGBx_D8888, Sample_RGBx_D8888, // to 8888
// RGBx is the same as RGB
NULL, NULL, NULL, NULL, // to A8
NULL, NULL, NULL, NULL, // to Index8
Sample_RGBx_D565, Sample_RGBx_D565_D, Sample_RGBx_D565, Sample_RGBx_D565_D, // to 565
Sample_RGBx_D4444, Sample_RGBx_D4444_D, Sample_RGBx_D4444, Sample_RGBx_D4444_D, // to 4444
Sample_RGBx_D8888, Sample_RGBx_D8888, Sample_RGBx_D8888, Sample_RGBx_D8888, // to 8888
// RGBA
NULL, NULL, NULL, NULL, // to A8
NULL, NULL, NULL, NULL, // to Index8
Sample_RGBx_D565, Sample_RGBx_D565_D, Sample_RGBx_D565, Sample_RGBx_D565_D, // to 565
Sample_RGBA_D4444, Sample_RGBA_D4444_D, NULL, NULL, // to 4444
Sample_RGBA_D8888, Sample_RGBA_D8888, Sample_RGBA_D8888_Unpremul, Sample_RGBA_D8888_Unpremul, // to 8888
// RGB_565
NULL, NULL, NULL, NULL, // to A8
NULL, NULL, NULL, NULL, // to Index8
Sample_D565_D565, Sample_D565_D565, Sample_D565_D565, Sample_D565_D565, // to 565
NULL, NULL, NULL, NULL, // to 4444
NULL, NULL, NULL, NULL, // to 8888
};
// Dummy class that allows instantiation of an ImageDecoder, so begin can query its fields.
class DummyDecoder : public SkImageDecoder {
public:
DummyDecoder() {}
protected:
virtual bool onDecode(SkStream*, SkBitmap*, SkImageDecoder::Mode) SK_OVERRIDE {
return false;
}
};
void test_row_proc_choice();
void test_row_proc_choice() {
const SkColorType colorTypes[] = {
kAlpha_8_SkColorType, kIndex_8_SkColorType, kRGB_565_SkColorType, kARGB_4444_SkColorType,
kN32_SkColorType
};
SkBitmap dummyBitmap;
DummyDecoder dummyDecoder;
size_t procCounter = 0;
for (int sc = SkScaledBitmapSampler::kGray; sc <= SkScaledBitmapSampler::kRGB_565; ++sc) {
for (size_t c = 0; c < SK_ARRAY_COUNT(colorTypes); ++c) {
for (int unpremul = 0; unpremul <= 1; ++unpremul) {
for (int dither = 0; dither <= 1; ++dither) {
// Arbitrary width/height/sampleSize to allow SkScaledBitmapSampler to
// be considered valid.
SkScaledBitmapSampler sampler(10, 10, 1);
dummyBitmap.setInfo(SkImageInfo::Make(10, 10,
colorTypes[c], kPremul_SkAlphaType));
dummyDecoder.setDitherImage(SkToBool(dither));
dummyDecoder.setRequireUnpremultipliedColors(SkToBool(unpremul));
sampler.begin(&dummyBitmap, (SkScaledBitmapSampler::SrcConfig) sc,
dummyDecoder);
SkScaledBitmapSampler::RowProc expected = gTestProcs[procCounter];
SkScaledBitmapSampler::RowProc actual = RowProcTester::getRowProc(sampler);
SkASSERT(expected == actual);
procCounter++;
}
}
}
}
SkASSERT(SK_ARRAY_COUNT(gTestProcs) == procCounter);
}
#endif // SK_DEBUG