/* libs/graphics/sgl/SkBitmapSampler.cpp ** ** Copyright 2006, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. ** You may obtain a copy of the License at ** ** http://www.apache.org/licenses/LICENSE-2.0 ** ** Unless required by applicable law or agreed to in writing, software ** distributed under the License is distributed on an "AS IS" BASIS, ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** See the License for the specific language governing permissions and ** limitations under the License. */ #include "SkBitmapSampler.h" static SkTileModeProc get_tilemode_proc(SkShader::TileMode mode) { switch (mode) { case SkShader::kClamp_TileMode: return do_clamp; case SkShader::kRepeat_TileMode: return do_repeat_mod; case SkShader::kMirror_TileMode: return do_mirror_mod; default: SkASSERT(!"unknown mode"); return NULL; } } SkBitmapSampler::SkBitmapSampler(const SkBitmap& bm, bool filter, SkShader::TileMode tmx, SkShader::TileMode tmy) : fBitmap(bm), fFilterBitmap(filter), fTileModeX(tmx), fTileModeY(tmy) { SkASSERT(bm.width() > 0 && bm.height() > 0); fMaxX = SkToU16(bm.width() - 1); fMaxY = SkToU16(bm.height() - 1); fTileProcX = get_tilemode_proc(tmx); fTileProcY = get_tilemode_proc(tmy); } void SkBitmapSampler::setPaint(const SkPaint& paint) { } class SkNullBitmapSampler : public SkBitmapSampler { public: SkNullBitmapSampler(const SkBitmap& bm, bool filter, SkShader::TileMode tmx, SkShader::TileMode tmy) : SkBitmapSampler(bm, filter, tmx, tmy) {} virtual SkPMColor sample(SkFixed x, SkFixed y) const { return 0; } }; ///////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////// #define BITMAP_CLASSNAME_PREFIX(name) ARGB32##name #define BITMAP_PIXEL_TO_PMCOLOR(bitmap, x, y) *bitmap.getAddr32(x, y) #include "SkBitmapSamplerTemplate.h" #include "SkColorPriv.h" #define BITMAP_CLASSNAME_PREFIX(name) RGB16##name #define BITMAP_PIXEL_TO_PMCOLOR(bitmap, x, y) SkPixel16ToPixel32(*bitmap.getAddr16(x, y)) #include "SkBitmapSamplerTemplate.h" #define BITMAP_CLASSNAME_PREFIX(name) Index8##name #define BITMAP_PIXEL_TO_PMCOLOR(bitmap, x, y) bitmap.getIndex8Color(x, y) #include "SkBitmapSamplerTemplate.h" ///////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////// ///////////////// The Bilinear versions #include "SkFilterProc.h" class ARGB32_Bilinear_Sampler : public SkBitmapSampler { public: ARGB32_Bilinear_Sampler(const SkBitmap& bm, SkShader::TileMode tmx, SkShader::TileMode tmy) : SkBitmapSampler(bm, true, tmx, tmy) { fPtrProcTable = SkGetBilinearFilterPtrProcTable(); } virtual SkPMColor sample(SkFixed x, SkFixed y) const { const uint32_t *p00, *p01, *p10, *p11; // turn pixel centers into the top-left of our filter-box x -= SK_FixedHalf; y -= SK_FixedHalf; // compute our pointers { const SkBitmap* bitmap = &fBitmap; int ix = x >> 16; int iy = y >> 16; int maxX = fMaxX; SkTileModeProc procX = fTileProcX; int maxY = fMaxY; SkTileModeProc procY = fTileProcY; int tmpx = procX(ix, maxX); int tmpy = procY(iy, maxY); p00 = bitmap->getAddr32(tmpx, tmpy); int tmpx1 = procX(ix + 1, maxX); p01 = bitmap->getAddr32(tmpx1, tmpy); int tmpy1 = procY(iy + 1, maxY); p10 = bitmap->getAddr32(tmpx, tmpy1); p11 = bitmap->getAddr32(tmpx1, tmpy1); } SkFilterPtrProc proc = SkGetBilinearFilterPtrProc(fPtrProcTable, x, y); return proc(p00, p01, p10, p11); } private: const SkFilterPtrProc* fPtrProcTable; }; class RGB16_Bilinear_Sampler : public SkBitmapSampler { public: RGB16_Bilinear_Sampler(const SkBitmap& bm, SkShader::TileMode tmx, SkShader::TileMode tmy) : SkBitmapSampler(bm, true, tmx, tmy) { fProcTable = SkGetBilinearFilterProcTable(); } virtual SkPMColor sample(SkFixed x, SkFixed y) const { const uint16_t *p00, *p01, *p10, *p11; // turn pixel centers into the top-left of our filter-box x -= SK_FixedHalf; y -= SK_FixedHalf; // compute our pointers { const SkBitmap* bitmap = &fBitmap; int ix = x >> 16; int iy = y >> 16; int maxX = fMaxX; SkTileModeProc procX = fTileProcX; int maxY = fMaxY; SkTileModeProc procY = fTileProcY; int tmpx = procX(ix, maxX); int tmpy = procY(iy, maxY); p00 = bitmap->getAddr16(tmpx, tmpy); int tmpx1 = procX(ix + 1, maxX); p01 = bitmap->getAddr16(tmpx1, tmpy); int tmpy1 = procY(iy + 1, maxY); p10 = bitmap->getAddr16(tmpx, tmpy1); p11 = bitmap->getAddr16(tmpx1, tmpy1); } SkFilterProc proc = SkGetBilinearFilterProc(fProcTable, x, y); uint32_t c = proc(SkExpand_rgb_16(*p00), SkExpand_rgb_16(*p01), SkExpand_rgb_16(*p10), SkExpand_rgb_16(*p11)); return SkPixel16ToPixel32((uint16_t)SkCompact_rgb_16(c)); } private: const SkFilterProc* fProcTable; }; // If we had a init/term method on sampler, we could avoid the per-pixel // call to lockColors/unlockColors class Index8_Bilinear_Sampler : public SkBitmapSampler { public: Index8_Bilinear_Sampler(const SkBitmap& bm, SkShader::TileMode tmx, SkShader::TileMode tmy) : SkBitmapSampler(bm, true, tmx, tmy) { fPtrProcTable = SkGetBilinearFilterPtrProcTable(); } virtual SkPMColor sample(SkFixed x, SkFixed y) const { const SkBitmap* bitmap = &fBitmap; const uint8_t *p00, *p01, *p10, *p11; // turn pixel centers into the top-left of our filter-box x -= SK_FixedHalf; y -= SK_FixedHalf; // compute our pointers { int ix = x >> 16; int iy = y >> 16; int maxX = fMaxX; SkTileModeProc procX = fTileProcX; int maxY = fMaxY; SkTileModeProc procY = fTileProcY; int tmpx = procX(ix, maxX); int tmpy = procY(iy, maxY); p00 = bitmap->getAddr8(tmpx, tmpy); int tmpx1 = procX(ix + 1, maxX); p01 = bitmap->getAddr8(tmpx1, tmpy); int tmpy1 = procY(iy + 1, maxY); p10 = bitmap->getAddr8(tmpx, tmpy1); p11 = bitmap->getAddr8(tmpx1, tmpy1); } const SkPMColor* colors = bitmap->getColorTable()->lockColors(); SkFilterPtrProc proc = SkGetBilinearFilterPtrProc(fPtrProcTable, x, y); uint32_t c = proc(&colors[*p00], &colors[*p01], &colors[*p10], &colors[*p11]); bitmap->getColorTable()->unlockColors(false); return c; } private: const SkFilterPtrProc* fPtrProcTable; }; class A8_Bilinear_Sampler : public SkBitmapSampler { public: A8_Bilinear_Sampler(const SkBitmap& bm, SkShader::TileMode tmx, SkShader::TileMode tmy) : SkBitmapSampler(bm, true, tmx, tmy) { fProcTable = SkGetBilinearFilterProcTable(); } virtual void setPaint(const SkPaint& paint) { fColor = SkPreMultiplyColor(paint.getColor()); } virtual SkPMColor sample(SkFixed x, SkFixed y) const { const uint8_t *p00, *p01, *p10, *p11; // turn pixel centers into the top-left of our filter-box x -= SK_FixedHalf; y -= SK_FixedHalf; // compute our pointers { const SkBitmap* bitmap = &fBitmap; int ix = x >> 16; int iy = y >> 16; int maxX = fMaxX; SkTileModeProc procX = fTileProcX; int maxY = fMaxY; SkTileModeProc procY = fTileProcY; int tmpx = procX(ix, maxX); int tmpy = procY(iy, maxY); p00 = bitmap->getAddr8(tmpx, tmpy); int tmpx1 = procX(ix + 1, maxX); p01 = bitmap->getAddr8(tmpx1, tmpy); int tmpy1 = procY(iy + 1, maxY); p10 = bitmap->getAddr8(tmpx, tmpy1); p11 = bitmap->getAddr8(tmpx1, tmpy1); } SkFilterProc proc = SkGetBilinearFilterProc(fProcTable, x, y); int alpha = proc(*p00, *p01, *p10, *p11); return SkAlphaMulQ(fColor, SkAlpha255To256(alpha)); } private: const SkFilterProc* fProcTable; SkPMColor fColor; }; class A8_NoFilter_Sampler : public SkBitmapSampler { public: A8_NoFilter_Sampler(const SkBitmap& bm, SkShader::TileMode tmx, SkShader::TileMode tmy) : SkBitmapSampler(bm, false, tmx, tmy) { } virtual void setPaint(const SkPaint& paint) { fColor = SkPreMultiplyColor(paint.getColor()); } virtual SkPMColor sample(SkFixed x, SkFixed y) const { int ix = SkFixedFloor(x); int iy = SkFixedFloor(y); int alpha = *fBitmap.getAddr8(fTileProcX(ix, fMaxX), fTileProcY(iy, fMaxY)); return SkAlphaMulQ(fColor, SkAlpha255To256(alpha)); } private: const SkFilterProc* fProcTable; SkPMColor fColor; }; /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// SkBitmapSampler* SkBitmapSampler::Create(const SkBitmap& bm, bool doFilter, SkShader::TileMode tmx, SkShader::TileMode tmy) { switch (bm.getConfig()) { case SkBitmap::kARGB_8888_Config: if (doFilter) return SkNEW_ARGS(ARGB32_Bilinear_Sampler, (bm, tmx, tmy)); if (tmx == tmy) { switch (tmx) { case SkShader::kClamp_TileMode: return SkNEW_ARGS(ARGB32_Point_Clamp_Sampler, (bm)); case SkShader::kRepeat_TileMode: if (is_pow2(bm.width()) && is_pow2(bm.height())) return SkNEW_ARGS(ARGB32_Point_Repeat_Pow2_Sampler, (bm)); else return SkNEW_ARGS(ARGB32_Point_Repeat_Mod_Sampler, (bm)); case SkShader::kMirror_TileMode: if (is_pow2(bm.width()) && is_pow2(bm.height())) return SkNEW_ARGS(ARGB32_Point_Mirror_Pow2_Sampler, (bm)); else return SkNEW_ARGS(ARGB32_Point_Mirror_Mod_Sampler, (bm)); default: SkASSERT(!"unknown mode"); } } else { // tmx != tmy return SkNEW_ARGS(ARGB32_Point_Sampler, (bm, tmx, tmy)); } break; case SkBitmap::kRGB_565_Config: if (doFilter) return SkNEW_ARGS(RGB16_Bilinear_Sampler, (bm, tmx, tmy)); if (tmx == tmy) { switch (tmx) { case SkShader::kClamp_TileMode: return SkNEW_ARGS(RGB16_Point_Clamp_Sampler, (bm)); case SkShader::kRepeat_TileMode: if (is_pow2(bm.width()) && is_pow2(bm.height())) return SkNEW_ARGS(RGB16_Point_Repeat_Pow2_Sampler, (bm)); else return SkNEW_ARGS(RGB16_Point_Repeat_Mod_Sampler, (bm)); case SkShader::kMirror_TileMode: if (is_pow2(bm.width()) && is_pow2(bm.height())) return SkNEW_ARGS(RGB16_Point_Mirror_Pow2_Sampler, (bm)); else return SkNEW_ARGS(RGB16_Point_Mirror_Mod_Sampler, (bm)); default: SkASSERT(!"unknown mode"); } } else { // tmx != tmy return SkNEW_ARGS(RGB16_Point_Sampler, (bm, tmx, tmy)); } break; case SkBitmap::kIndex8_Config: if (doFilter) return SkNEW_ARGS(Index8_Bilinear_Sampler, (bm, tmx, tmy)); if (tmx == tmy) { switch (tmx) { case SkShader::kClamp_TileMode: return SkNEW_ARGS(Index8_Point_Clamp_Sampler, (bm)); case SkShader::kRepeat_TileMode: if (is_pow2(bm.width()) && is_pow2(bm.height())) return SkNEW_ARGS(Index8_Point_Repeat_Pow2_Sampler, (bm)); else return SkNEW_ARGS(Index8_Point_Repeat_Mod_Sampler, (bm)); case SkShader::kMirror_TileMode: if (is_pow2(bm.width()) && is_pow2(bm.height())) return SkNEW_ARGS(Index8_Point_Mirror_Pow2_Sampler, (bm)); else return SkNEW_ARGS(Index8_Point_Mirror_Mod_Sampler, (bm)); default: SkASSERT(!"unknown mode"); } } else { // tmx != tmy return SkNEW_ARGS(Index8_Point_Sampler, (bm, tmx, tmy)); } break; case SkBitmap::kA8_Config: if (doFilter) return SkNEW_ARGS(A8_Bilinear_Sampler, (bm, tmx, tmy)); else return SkNEW_ARGS(A8_NoFilter_Sampler, (bm, tmx, tmy)); break; default: SkASSERT(!"unknown device"); } return SkNEW_ARGS(SkNullBitmapSampler, (bm, doFilter, tmx, tmy)); }