diff options
Diffstat (limited to 'libs/graphics/sgl')
70 files changed, 19105 insertions, 0 deletions
diff --git a/libs/graphics/sgl/SkAlphaRuns.cpp b/libs/graphics/sgl/SkAlphaRuns.cpp new file mode 100644 index 0000000000..891774fff3 --- /dev/null +++ b/libs/graphics/sgl/SkAlphaRuns.cpp @@ -0,0 +1,168 @@ +#include "SkAntiRun.h" + +void SkAlphaRuns::reset(int width) +{ + SkASSERT(width > 0); + + fRuns[0] = SkToS16(width); + fRuns[width] = 0; + fAlpha[0] = 0; + + SkDEBUGCODE(fWidth = width;) + SkDEBUGCODE(this->validate();) +} + +void SkAlphaRuns::Break(S16 runs[], U8 alpha[], int x, int count) +{ + SkASSERT(count > 0 && x >= 0); + +// SkAlphaRuns::BreakAt(runs, alpha, x); +// SkAlphaRuns::BreakAt(&runs[x], &alpha[x], count); + + S16* next_runs = runs + x; + U8* next_alpha = alpha + x; + + while (x > 0) + { + int n = runs[0]; + SkASSERT(n > 0); + + if (x < n) + { + alpha[x] = alpha[0]; + runs[0] = SkToS16(x); + runs[x] = SkToS16(n - x); + break; + } + runs += n; + alpha += n; + x -= n; + } + + runs = next_runs; + alpha = next_alpha; + x = count; + + for (;;) + { + int n = runs[0]; + SkASSERT(n > 0); + + if (x < n) + { + alpha[x] = alpha[0]; + runs[0] = SkToS16(x); + runs[x] = SkToS16(n - x); + break; + } + x -= n; + if (x <= 0) + break; + + runs += n; + alpha += n; + } +} + +void SkAlphaRuns::add(int x, U8CPU startAlpha, int middleCount, U8CPU stopAlpha, U8CPU maxValue) +{ + SkASSERT(middleCount >= 0); + SkASSERT(x >= 0 && x + (startAlpha != 0) + middleCount + (stopAlpha != 0) <= fWidth); + + S16* runs = fRuns; + U8* alpha = fAlpha; + + if (startAlpha) + { + SkAlphaRuns::Break(runs, alpha, x, 1); + /* I should be able to just add alpha[x] + startAlpha. + However, if the trailing edge of the previous span and the leading + edge of the current span round to the same super-sampled x value, + I might overflow to 256 with this add, hence the funny subtract (crud). + */ + unsigned tmp = alpha[x] + startAlpha; + SkASSERT(tmp <= 256); + alpha[x] = SkToU8(tmp - (tmp >> 8)); // was (tmp >> 7), but that seems wrong if we're trying to catch 256 + + runs += x + 1; + alpha += x + 1; + x = 0; + SkDEBUGCODE(this->validate();) + } + if (middleCount) + { + SkAlphaRuns::Break(runs, alpha, x, middleCount); + alpha += x; + runs += x; + x = 0; + do { + alpha[0] = SkToU8(alpha[0] + maxValue); + int n = runs[0]; + SkASSERT(n <= middleCount); + alpha += n; + runs += n; + middleCount -= n; + } while (middleCount > 0); + SkDEBUGCODE(this->validate();) + } + if (stopAlpha) + { + SkAlphaRuns::Break(runs, alpha, x, 1); + alpha[x] = SkToU8(alpha[x] + stopAlpha); + SkDEBUGCODE(this->validate();) + } +} + +#ifdef SK_DEBUG + void SkAlphaRuns::assertValid(int y, int maxStep) const + { + int max = (y + 1) * maxStep - (y == maxStep - 1); + + const S16* runs = fRuns; + const U8* alpha = fAlpha; + + while (*runs) + { + SkASSERT(*alpha <= max); + alpha += *runs; + runs += *runs; + } + } + + void SkAlphaRuns::dump() const + { + const S16* runs = fRuns; + const U8* alpha = fAlpha; + + SkDebugf("Runs"); + while (*runs) + { + int n = *runs; + + SkDebugf(" %02x", *alpha); + if (n > 1) + SkDebugf(",%d", n); + alpha += n; + runs += n; + } + SkDebugf("\n"); + } + + void SkAlphaRuns::validate() const + { + SkASSERT(fWidth > 0); + + int count = 0; + const S16* runs = fRuns; + + while (*runs) + { + SkASSERT(*runs > 0); + count += *runs; + SkASSERT(count <= fWidth); + runs += *runs; + } + SkASSERT(count == fWidth); + } +#endif + diff --git a/libs/graphics/sgl/SkAntiRun.h b/libs/graphics/sgl/SkAntiRun.h new file mode 100644 index 0000000000..43d502fb0a --- /dev/null +++ b/libs/graphics/sgl/SkAntiRun.h @@ -0,0 +1,168 @@ +#ifndef SkAntiRun_DEFINED +#define SkAntiRun_DEFINED + +#include "SkBlitter.h" + +inline int sk_make_nonzero_neg_one(int x) +{ + return (x | -x) >> 31; +} + +#if 0 +template <int kShift> class SkAntiRun { + static U8 coverage_to_alpha(int aa) + { + aa <<= 8 - 2*kShift; + aa -= aa >> (8 - kShift - 1); + return SkToU8(aa); + } +public: + void set(int start, int stop) + { + SkASSERT(start >= 0 && stop > start); + +#if 1 + int fb = start & kMask; + int fe = stop & kMask; + int n = (stop >> kShift) - (start >> kShift) - 1; + + if (n < 0) + { + fb = fe - fb; + n = 0; + fe = 0; + } + else + { + if (fb == 0) + n += 1; + else + fb = (1 << kShift) - fb; + } + + fStartAlpha = coverage_to_alpha(fb); + fMiddleCount = n; + fStopAlpha = coverage_to_alpha(fe); +#else + int x0 = start >> kShift; + int x1 = (stop - 1) >> kShift; + int middle = x1 - x0; + int aa; + + if (middle == 0) + { + aa = stop - start; + aa <<= 8 - 2*kShift; + aa -= aa >> (8 - kShift - 1); + SkASSERT(aa > 0 && aa < kMax); + fStartAlpha = SkToU8(aa); + fMiddleCount = 0; + fStopAlpha = 0; + } + else + { + int aa = start & kMask; + aa <<= 8 - 2*kShift; + aa -= aa >> (8 - kShift - 1); + SkASSERT(aa >= 0 && aa < kMax); + if (aa) + fStartAlpha = SkToU8(kMax - aa); + else + { + fStartAlpha = 0; + middle += 1; + } + aa = stop & kMask; + aa <<= 8 - 2*kShift; + aa -= aa >> (8 - kShift - 1); + SkASSERT(aa >= 0 && aa < kMax); + middle += sk_make_nonzero_neg_one(aa); + + fStopAlpha = SkToU8(aa); + fMiddleCount = middle; + } + SkASSERT(fStartAlpha < kMax); + SkASSERT(fStopAlpha < kMax); +#endif + } + + void blit(int x, int y, SkBlitter* blitter) + { + S16 runs[2]; + runs[0] = 1; + runs[1] = 0; + + if (fStartAlpha) + { + blitter->blitAntiH(x, y, &fStartAlpha, runs); + x += 1; + } + if (fMiddleCount) + { + blitter->blitH(x, y, fMiddleCount); + x += fMiddleCount; + } + if (fStopAlpha) + blitter->blitAntiH(x, y, &fStopAlpha, runs); + } + + U8 getStartAlpha() const { return fStartAlpha; } + int getMiddleCount() const { return fMiddleCount; } + U8 getStopAlpha() const { return fStopAlpha; } + +private: + U8 fStartAlpha, fStopAlpha; + int fMiddleCount; + + enum { + kMask = (1 << kShift) - 1, + kMax = (1 << (8 - kShift)) - 1 + }; +}; +#endif + +//////////////////////////////////////////////////////////////////////////////////// + +class SkAlphaRuns { +public: + S16* fRuns; + U8* fAlpha; + + bool empty() const + { + SkASSERT(fRuns[0] > 0); + return fAlpha[0] == 0 && fRuns[fRuns[0]] == 0; + } + void reset(int width); + void add(int x, U8CPU startAlpha, int middleCount, U8CPU stopAlpha, U8CPU maxValue); + SkDEBUGCODE(void assertValid(int y, int maxStep) const;) + SkDEBUGCODE(void dump() const;) + + static void Break(S16 runs[], U8 alpha[], int x, int count); + static void BreakAt(S16 runs[], U8 alpha[], int x) + { + while (x > 0) + { + int n = runs[0]; + SkASSERT(n > 0); + + if (x < n) + { + alpha[x] = alpha[0]; + runs[0] = SkToS16(x); + runs[x] = SkToS16(n - x); + break; + } + runs += n; + alpha += n; + x -= n; + } + } + +private: + SkDEBUGCODE(int fWidth;) + SkDEBUGCODE(void validate() const;) +}; + +#endif + diff --git a/libs/graphics/sgl/SkBitmap.cpp b/libs/graphics/sgl/SkBitmap.cpp new file mode 100644 index 0000000000..a2ea623e51 --- /dev/null +++ b/libs/graphics/sgl/SkBitmap.cpp @@ -0,0 +1,412 @@ +#include "SkBitmap.h" +#include "SkColorPriv.h" +#include "SkUtils.h" + +SkBitmap::SkBitmap() +{ + memset(this, 0, sizeof(*this)); +} + +SkBitmap::SkBitmap(const SkBitmap& src) +{ + src.fColorTable->safeRef(); + + memcpy(this, &src, sizeof(src)); + fFlags &= ~(kWeOwnThePixels_Flag | kWeOwnTheMipMap_Flag); +} + +SkBitmap::~SkBitmap() +{ + fColorTable->safeUnref(); + this->freePixels(); +} + +SkBitmap& SkBitmap::operator=(const SkBitmap& src) +{ + src.fColorTable->safeRef(); + fColorTable->safeUnref(); + + this->freePixels(); + memcpy(this, &src, sizeof(src)); + fFlags &= ~(kWeOwnThePixels_Flag | kWeOwnTheMipMap_Flag); + + return *this; +} + +void SkBitmap::swap(SkBitmap& other) +{ + SkTSwap<SkColorTable*>(fColorTable, other.fColorTable); + +#ifdef SK_SUPPORT_MIPMAP + SkTSwap<MipMap*>(fMipMap, other.fMipMap); +#endif + + SkTSwap<void*>(fPixels, other.fPixels); + SkTSwap<U16>(fWidth, other.fWidth); + SkTSwap<U16>(fHeight, other.fHeight); + SkTSwap<U16>(fRowBytes, other.fRowBytes); + SkTSwap<U8>(fConfig, other.fConfig); + SkTSwap<U8>(fFlags, other.fFlags); +} + +void SkBitmap::reset() +{ + fColorTable->safeUnref(); + this->freePixels(); + memset(this, 0, sizeof(*this)); +} + +void SkBitmap::setConfig(Config c, U16CPU width, U16CPU height, U16CPU rowBytes) +{ + this->freePixels(); + + if (rowBytes == 0) + { + switch (c) { + case kA1_Config: + rowBytes = (width + 7) >> 3; + break; + case kA8_Config: + case kIndex8_Config: + rowBytes = width; + break; + case kRGB_565_Config: + rowBytes = SkAlign4(width << 1); + break; + case kARGB_8888_Config: + rowBytes = width << 2; + break; + default: + SkASSERT(!"unknown config"); + break; + } + } + + fConfig = SkToU8(c); + fWidth = SkToU16(width); + fHeight = SkToU16(height); + fRowBytes = SkToU16(rowBytes); +} + +void SkBitmap::setPixels(void* p) +{ + this->freePixels(); + + fPixels = p; + fFlags &= ~(kWeOwnThePixels_Flag | kWeOwnTheMipMap_Flag); +} + +void SkBitmap::allocPixels() +{ + this->freePixels(); + + fPixels = (U32*)sk_malloc_throw(fHeight * fRowBytes); + fFlags |= kWeOwnThePixels_Flag; +} + +void SkBitmap::freePixels() +{ + if (fFlags & kWeOwnThePixels_Flag) + { + SkASSERT(fPixels); + sk_free(fPixels); + fPixels = NULL; + fFlags &= ~kWeOwnThePixels_Flag; + } +#ifdef SK_SUPPORT_MIPMAP + if (fFlags & kWeOwnTheMipMap_Flag) + { + sk_free(fMipMap); + fMipMap = NULL; + } +#endif +} + +bool SkBitmap::getOwnsPixels() const +{ + return SkToBool(fFlags & kWeOwnThePixels_Flag); +} + +void SkBitmap::setOwnsPixels(bool ownsPixels) +{ + if (ownsPixels) + fFlags |= kWeOwnThePixels_Flag; + else + fFlags &= ~kWeOwnThePixels_Flag; +} + +SkColorTable* SkBitmap::setColorTable(SkColorTable* ct) +{ + SkRefCnt_SafeAssign(fColorTable, ct); + return ct; +} + +bool SkBitmap::isOpaque() const +{ + switch (fConfig) { + case kA1_Config: + case kA8_Config: + case kARGB_8888_Config: + return (fFlags & kImageIsOpaque_Flag) != 0; + + case kIndex8_Config: + return (fColorTable->getFlags() & SkColorTable::kColorsAreOpaque_Flag) != 0; + + case kRGB_565_Config: + return true; + + default: + SkASSERT(!"unknown bitmap config"); + return false; + } +} + +void SkBitmap::setIsOpaque(bool isOpaque) +{ + /* we record this regardless of fConfig, though it is ignored in isOpaque() for + configs that can't support per-pixel alpha. + */ + if (isOpaque) + fFlags |= kImageIsOpaque_Flag; + else + fFlags &= ~kImageIsOpaque_Flag; +} + +////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////// + +void SkBitmap::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) +{ + if (fPixels == NULL || fConfig == kNo_Config) + return; + + int height = fHeight; + + this->setIsOpaque(a == 255); + + // make rgb premultiplied + if (a != 255) + { + r = SkAlphaMul(r, a); + g = SkAlphaMul(g, a); + b = SkAlphaMul(b, a); + } + + switch (fConfig) { + case kA1_Config: + { + U8* p = (uint8_t*)fPixels; + size_t count = (fWidth + 7) >> 3; + a = (a >> 7) ? 0xFF : 0; + SkASSERT(count <= fRowBytes); + while (--height >= 0) + { + memset(p, a, count); + p += fRowBytes; + } + } + break; + case kA8_Config: + memset(fPixels, a, fRowBytes * fWidth); + break; + case kIndex8_Config: + SkASSERT(!"Don't support writing to Index8 bitmaps"); + break; + case kRGB_565_Config: + // now erase the color-plane + { + U16* p = (uint16_t*)fPixels; + U16 v = SkPackRGB16(r >> (8 - SK_R16_BITS), + g >> (8 - SK_G16_BITS), + b >> (8 - SK_B16_BITS)); + + while (--height >= 0) + { + sk_memset16(p, v, fWidth); + p = (uint16_t*)((char*)p + fRowBytes); + } + } + break; + case kARGB_8888_Config: + { + uint32_t* p = (uint32_t*)fPixels; + uint32_t v = SkPackARGB32(a, r, g, b); + + while (--height >= 0) + { + sk_memset32(p, v, fWidth); + p = (uint32_t*)((char*)p + fRowBytes); + } + } + break; + } +} + +////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_SUPPORT_MIPMAP +static void downsampleby2_proc32(SkBitmap* dst, int x, int y, const SkBitmap& src) +{ + x <<= 1; + y <<= 1; + const U32* p = src.getAddr32(x, y); + U32 c, ag, rb; + + c = *p; ag = (c >> 8) & 0xFF00FF; rb = c & 0xFF00FF; + if (x < (int)src.width() - 1) + p += 1; + c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF; + + if (y < (int)src.height() - 1) + p = src.getAddr32(x, y + 1); + c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF; + if (x < (int)src.width() - 1) + p += 1; + c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF; + + *dst->getAddr32(x >> 1, y >> 1) = ((rb >> 2) & 0xFF00FF) | ((ag << 6) & 0xFF00FF00); +} + +static inline uint32_t expand16(U16CPU c) +{ + return (c & SK_R16B16_MASK_IN_PLACE) | ((c & SK_G16_MASK_IN_PLACE) << 16); +} + +static inline U16CPU pack16(uint32_t c) +{ + return (c & SK_R16B16_MASK_IN_PLACE) | ((c >> 16) & SK_G16_MASK_IN_PLACE); +} + +static void downsampleby2_proc16(SkBitmap* dst, int x, int y, const SkBitmap& src) +{ + x <<= 1; + y <<= 1; + const U16* p = src.getAddr16(x, y); + U32 c; + + c = expand16(*p); + if (x < (int)src.width() - 1) + p += 1; + c += expand16(*p); + + if (y < (int)src.height() - 1) + p = src.getAddr16(x, y + 1); + c += expand16(*p); + if (x < (int)src.width() - 1) + p += 1; + c += expand16(*p); + + *dst->getAddr16(x >> 1, y >> 1) = SkToU16(pack16(c >> 2)); +} + +void SkBitmap::buildMipMap(bool forceRebuild) +{ +#ifdef SK_SUPPORT_MIPMAP + if (!forceRebuild && fMipMap) + return; + + if (fFlags & kWeOwnTheMipMap_Flag) + { + SkASSERT(fMipMap); + sk_free(fMipMap); + fMipMap = NULL; + fFlags &= ~kWeOwnTheMipMap_Flag; + } + + int shift; + void (*proc)(SkBitmap* dst, int x, int y, const SkBitmap& src); + + switch (this->getConfig()) { + case kARGB_8888_Config: + shift = 2; + proc = downsampleby2_proc32; + break; + case kRGB_565_Config: + shift = 1; + proc = downsampleby2_proc16; + break; + case kIndex8_Config: + case kA8_Config: +// shift = 0; break; + default: + return; // don't build mipmaps for these configs + } + + // whip through our loop to compute the exact size needed + size_t size; + { + unsigned width = this->width(); + unsigned height = this->height(); + size = 0; + for (int i = 1; i < kMaxMipLevels; i++) + { + width = (width + 1) >> 1; + height = (height + 1) >> 1; + size += width * height << shift; + } + } + + MipMap* mm = (MipMap*)sk_malloc_throw(sizeof(MipMap) + size); + U8* addr = (U8*)(mm + 1); + + unsigned width = this->width(); + unsigned height = this->height(); + unsigned rowBytes = this->rowBytes(); + SkBitmap srcBM(*this), dstBM; + + mm->fLevel[0].fPixels = this->getPixels(); + mm->fLevel[0].fWidth = SkToU16(width); + mm->fLevel[0].fHeight = SkToU16(height); + mm->fLevel[0].fRowBytes = SkToU16(rowBytes); + mm->fLevel[0].fConfig = SkToU8(this->getConfig()); + mm->fLevel[0].fShift = SkToU8(shift); + + for (int i = 1; i < kMaxMipLevels; i++) + { + width = (width + 1) >> 1; + height = (height + 1) >> 1; + rowBytes = width << shift; + + mm->fLevel[i].fPixels = addr; + mm->fLevel[i].fWidth = SkToU16(width); + mm->fLevel[i].fHeight = SkToU16(height); + mm->fLevel[i].fRowBytes = SkToU16(rowBytes); + mm->fLevel[i].fConfig = SkToU8(this->getConfig()); + mm->fLevel[i].fShift = SkToU8(shift); + + dstBM.setConfig(this->getConfig(), width, height, rowBytes); + dstBM.setPixels(addr); + + for (unsigned y = 0; y < height; y++) + for (unsigned x = 0; x < width; x++) + proc(&dstBM, x, y, srcBM); + + srcBM = dstBM; + addr += height * rowBytes; + } + SkASSERT(addr == (U8*)mm->fLevel[1].fPixels + size); + + fMipMap = mm; + fFlags |= kWeOwnTheMipMap_Flag; +#endif +} + +unsigned SkBitmap::countMipLevels() const +{ +#ifdef SK_SUPPORT_MIPMAP + return fMipMap ? kMaxMipLevels : 0; +#else + return 0; +#endif +} + +const SkBitmap::MipLevel* SkBitmap::getMipLevel(unsigned level) const +{ + SkASSERT(level < this->countMipLevels()); + + return &fMipMap->fLevel[level]; +} +#endif + + diff --git a/libs/graphics/sgl/SkBitmapSampler.cpp b/libs/graphics/sgl/SkBitmapSampler.cpp new file mode 100644 index 0000000000..28b83f0914 --- /dev/null +++ b/libs/graphics/sgl/SkBitmapSampler.cpp @@ -0,0 +1,281 @@ +#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, SkPaint::FilterType ftype, + SkShader::TileMode tmx, SkShader::TileMode tmy) + : fBitmap(bm), fFilterType(ftype), 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); +} + +class SkNullBitmapSampler : public SkBitmapSampler { +public: + SkNullBitmapSampler(const SkBitmap& bm, SkPaint::FilterType ft, + SkShader::TileMode tmx, SkShader::TileMode tmy) + : SkBitmapSampler(bm, ft, 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 + +static void assert_valid_pmcolor(uint32_t c) +{ + SkASSERT(SkGetPackedA32(c) >= SkGetPackedR32(c)); + SkASSERT(SkGetPackedA32(c) >= SkGetPackedG32(c)); + SkASSERT(SkGetPackedA32(c) >= SkGetPackedB32(c)); +} + +#include "SkFilterProc.h" + +class ARGB32_Bilinear_Sampler : public SkBitmapSampler { +public: + ARGB32_Bilinear_Sampler(const SkBitmap& bm, SkShader::TileMode tmx, SkShader::TileMode tmy) + : SkBitmapSampler(bm, SkPaint::kBilinear_FilterType, tmx, tmy) + { + fProcTable = SkGetBilinearFilterProcTable(); + } + + virtual SkPMColor sample(SkFixed x, SkFixed y) const + { + int ix = x >> 16; + int iy = y >> 16; + + ix = fTileProcX(ix, fMaxX); + iy = fTileProcY(iy, fMaxY); + + const uint32_t *p00, *p01, *p10, *p11; + + p00 = p01 = fBitmap.getAddr32(ix, iy); + if (ix < fMaxX) + p01 += 1; + p10 = p00; + p11 = p01; + if (iy < fMaxY) + { + p10 = (const uint32_t*)((const char*)p10 + fBitmap.rowBytes()); + p11 = (const uint32_t*)((const char*)p11 + fBitmap.rowBytes()); + } + + uint32_t c00 = *p00; + uint32_t c01 = *p01; + uint32_t c10 = *p10; + uint32_t c11 = *p11; + + assert_valid_pmcolor(c00); + assert_valid_pmcolor(c01); + assert_valid_pmcolor(c01); + assert_valid_pmcolor(c11); + + SkFilterProc proc = SkGetBilinearFilterProc(fProcTable, x, y); + uint32_t c = SkPackARGB32( proc(SkGetPackedA32(c00), SkGetPackedA32(c01), SkGetPackedA32(c10), SkGetPackedA32(c11)), + proc(SkGetPackedR32(c00), SkGetPackedR32(c01), SkGetPackedR32(c10), SkGetPackedR32(c11)), + proc(SkGetPackedG32(c00), SkGetPackedG32(c01), SkGetPackedG32(c10), SkGetPackedG32(c11)), + proc(SkGetPackedB32(c00), SkGetPackedB32(c01), SkGetPackedB32(c10), SkGetPackedB32(c11))); + + assert_valid_pmcolor(c); + return c; + } + +private: + const SkFilterProc* fProcTable; +}; + +class Index8_Bilinear_Sampler : public SkBitmapSampler { +public: + Index8_Bilinear_Sampler(const SkBitmap& bm, SkShader::TileMode tmx, SkShader::TileMode tmy) + : SkBitmapSampler(bm, SkPaint::kBilinear_FilterType, tmx, tmy) + { + fProcTable = SkGetBilinearFilterProcTable(); + } + + virtual SkPMColor sample(SkFixed x, SkFixed y) const + { + int ix = x >> 16; + int iy = y >> 16; + + ix = fTileProcX(ix, fMaxX); + iy = fTileProcY(iy, fMaxY); + + const U8 *p00, *p01, *p10, *p11; + + p00 = p01 = fBitmap.getAddr8(ix, iy); + if (ix < fMaxX) + p01 += 1; + p10 = p00; + p11 = p01; + if (iy < fMaxY) + { + p10 = (const U8*)((const char*)p10 + fBitmap.rowBytes()); + p11 = (const U8*)((const char*)p11 + fBitmap.rowBytes()); + } + + const SkPMColor* colors = fBitmap.getColorTable()->lockColors(); + + uint32_t c00 = colors[*p00]; + uint32_t c01 = colors[*p01]; + uint32_t c10 = colors[*p10]; + uint32_t c11 = colors[*p11]; + + assert_valid_pmcolor(c00); + assert_valid_pmcolor(c01); + assert_valid_pmcolor(c01); + assert_valid_pmcolor(c11); + + SkFilterProc proc = SkGetBilinearFilterProc(fProcTable, x, y); + uint32_t c = SkPackARGB32( proc(SkGetPackedA32(c00), SkGetPackedA32(c01), SkGetPackedA32(c10), SkGetPackedA32(c11)), + proc(SkGetPackedR32(c00), SkGetPackedR32(c01), SkGetPackedR32(c10), SkGetPackedR32(c11)), + proc(SkGetPackedG32(c00), SkGetPackedG32(c01), SkGetPackedG32(c10), SkGetPackedG32(c11)), + proc(SkGetPackedB32(c00), SkGetPackedB32(c01), SkGetPackedB32(c10), SkGetPackedB32(c11))); + + assert_valid_pmcolor(c); + + fBitmap.getColorTable()->unlockColors(false); + + return c; + } + +private: + const SkFilterProc* fProcTable; +}; + +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// + +SkBitmapSampler* SkBitmapSampler::Create(const SkBitmap& bm, SkPaint::FilterType ftype, + SkShader::TileMode tmx, SkShader::TileMode tmy) +{ + switch (bm.getConfig()) { + case SkBitmap::kARGB_8888_Config: + switch (ftype) { + case SkPaint::kNo_FilterType: + 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 SkPaint::kBilinear_FilterType: + return SkNEW_ARGS(ARGB32_Bilinear_Sampler, (bm, tmx, tmy)); + + default: + SkASSERT(!"unknown filter type"); + } + break; + case SkBitmap::kRGB_565_Config: + // we ignore ftype, since we haven't implemented bilinear for 16bit bitmaps yet + 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: + switch (ftype) { + case SkPaint::kNo_FilterType: + 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 SkPaint::kBilinear_FilterType: + return SkNEW_ARGS(Index8_Bilinear_Sampler, (bm, tmx, tmy)); + default: // to avoid warnings + break; + } + break; + default: + SkASSERT(!"unknown device"); + } + return SkNEW_ARGS(SkNullBitmapSampler, (bm, ftype, tmx, tmy)); +} + diff --git a/libs/graphics/sgl/SkBitmapSampler.h b/libs/graphics/sgl/SkBitmapSampler.h new file mode 100644 index 0000000000..27a18559cd --- /dev/null +++ b/libs/graphics/sgl/SkBitmapSampler.h @@ -0,0 +1,150 @@ +#ifndef SkBitmapSampler_DEFINED +#define SkBitmapSampler_DEFINED + +#include "SkBitmap.h" +#include "SkPaint.h" +#include "SkShader.h" + +typedef int (*SkTileModeProc)(int value, unsigned max); + +class SkBitmapSampler { +public: + SkBitmapSampler(const SkBitmap&, SkPaint::FilterType, SkShader::TileMode tmx, SkShader::TileMode tmy); + virtual ~SkBitmapSampler() {} + + const SkBitmap& getBitmap() const { return fBitmap; } + SkPaint::FilterType getFilterType() const { return fFilterType; } + SkShader::TileMode getTileModeX() const { return fTileModeX; } + SkShader::TileMode getTileModeY() const { return fTileModeY; } + + // override this in your subclass + virtual SkPMColor sample(SkFixed x, SkFixed y) const = 0; + + // This is the factory for finding an optimal subclass + static SkBitmapSampler* Create(const SkBitmap&, SkPaint::FilterType, + SkShader::TileMode tmx, SkShader::TileMode tmy); + +protected: + const SkBitmap& fBitmap; + uint16_t fMaxX, fMaxY; + SkPaint::FilterType fFilterType; + SkShader::TileMode fTileModeX; + SkShader::TileMode fTileModeY; + SkTileModeProc fTileProcX; + SkTileModeProc fTileProcY; + + // illegal + SkBitmapSampler& operator=(const SkBitmapSampler&); +}; + +static inline int fixed_clamp(SkFixed x) +{ +#ifdef SK_CPU_HAS_CONDITIONAL_INSTR + if (x >> 16) + x = 0xFFFF; + if (x < 0) + x = 0; +#else + if (x >> 16) + { + if (x < 0) + x = 0; + else + x = 0xFFFF; + } +#endif + return x; +} + +////////////////////////////////////////////////////////////////////////////////////// + +static inline int fixed_repeat(SkFixed x) +{ + return x & 0xFFFF; +} + +static inline int fixed_mirror(SkFixed x) +{ + SkFixed s = x << 15 >> 31; + // s is FFFFFFFF if we're on an odd interval, or 0 if an even interval + return (x ^ s) & 0xFFFF; +} + +static inline bool is_pow2(int count) +{ + SkASSERT(count > 0); + return (count & (count - 1)) == 0; +} + +static inline int do_clamp(int index, unsigned max) +{ + SkASSERT((int)max >= 0); + +#ifdef SK_CPU_HAS_CONDITIONAL_INSTR + if (index > (int)max) + index = max; + if (index < 0) + index = 0; +#else + if ((unsigned)index > max) + { + if (index < 0) + index = 0; + else + index = max; + } +#endif + return index; +} + +static inline int do_repeat_mod(int index, unsigned max) +{ + SkASSERT((int)max >= 0); + + if ((unsigned)index > max) + { + if (index < 0) + index = max - (~index % (max + 1)); + else + index = index % (max + 1); + } + return index; +} + +static inline int do_repeat_pow2(int index, unsigned max) +{ + SkASSERT((int)max >= 0 && is_pow2(max + 1)); + + return index & max; +} + +static inline int do_mirror_mod(int index, unsigned max) +{ + SkASSERT((int)max >= 0); + + // have to handle negatives so that + // -1 -> 0, -2 -> 1, -3 -> 2, etc. + // so we can't just cal abs + index ^= index >> 31; + + if ((unsigned)index > max) + { + int mod = (max + 1) << 1; + index = index % mod; + if ((unsigned)index > max) + index = mod - index - 1; + } + return index; +} + +static inline int do_mirror_pow2(int index, unsigned max) +{ + SkASSERT((int)max >= 0 && is_pow2(max + 1)); + + int s = (index & (max + 1)) - 1; + s = ~(s >> 31); + // at this stage, s is FFFFFFFF if we're on an odd interval, or 0 if an even interval + return (index ^ s) & max; +} + +#endif diff --git a/libs/graphics/sgl/SkBitmapSamplerTemplate.h b/libs/graphics/sgl/SkBitmapSamplerTemplate.h new file mode 100644 index 0000000000..19530a01cc --- /dev/null +++ b/libs/graphics/sgl/SkBitmapSamplerTemplate.h @@ -0,0 +1,99 @@ +/* this guy is pulled in multiple times, with the following symbols defined each time: + + #define BITMAP_CLASSNAME_PREFIX(name) ARGB32##name + #defube BITMAP_PIXEL_TO_PMCOLOR(bitmap, x, y) *bitmap.getAddr32(x, y) +*/ + +class BITMAP_CLASSNAME_PREFIX(_Point_Sampler) : public SkBitmapSampler { +public: + BITMAP_CLASSNAME_PREFIX(_Point_Sampler)(const SkBitmap& bm, SkShader::TileMode tmx, SkShader::TileMode tmy) + : SkBitmapSampler(bm, SkPaint::kNo_FilterType, tmx, tmy) + { + } + + virtual SkPMColor sample(SkFixed x, SkFixed y) const + { + x = fTileProcX(SkFixedRound(x), fMaxX); + y = fTileProcY(SkFixedRound(y), fMaxY); + return BITMAP_PIXEL_TO_PMCOLOR(fBitmap, x, y); + } +}; + + +class BITMAP_CLASSNAME_PREFIX(_Point_Clamp_Sampler) : public SkBitmapSampler { +public: + BITMAP_CLASSNAME_PREFIX(_Point_Clamp_Sampler)(const SkBitmap& bm) + : SkBitmapSampler(bm, SkPaint::kNo_FilterType, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode) + { + } + + virtual SkPMColor sample(SkFixed x, SkFixed y) const + { + x = do_clamp(SkFixedRound(x), fMaxX); + y = do_clamp(SkFixedRound(y), fMaxY); + return BITMAP_PIXEL_TO_PMCOLOR(fBitmap, x, y); + } +}; + +class BITMAP_CLASSNAME_PREFIX(_Point_Repeat_Pow2_Sampler) : public SkBitmapSampler { +public: + BITMAP_CLASSNAME_PREFIX(_Point_Repeat_Pow2_Sampler)(const SkBitmap& bm) + : SkBitmapSampler(bm, SkPaint::kNo_FilterType, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode) + { + } + + virtual SkPMColor sample(SkFixed x, SkFixed y) const + { + x = do_repeat_pow2(SkFixedRound(x), fMaxX); + y = do_repeat_pow2(SkFixedRound(y), fMaxY); + return BITMAP_PIXEL_TO_PMCOLOR(fBitmap, x, y); + } +}; + +class BITMAP_CLASSNAME_PREFIX(_Point_Repeat_Mod_Sampler) : public SkBitmapSampler { +public: + BITMAP_CLASSNAME_PREFIX(_Point_Repeat_Mod_Sampler)(const SkBitmap& bm) + : SkBitmapSampler(bm, SkPaint::kNo_FilterType, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode) + { + } + + virtual SkPMColor sample(SkFixed x, SkFixed y) const + { + x = do_repeat_mod(SkFixedRound(x), fMaxX); + y = do_repeat_mod(SkFixedRound(y), fMaxY); + return BITMAP_PIXEL_TO_PMCOLOR(fBitmap, x, y); + } +}; + +class BITMAP_CLASSNAME_PREFIX(_Point_Mirror_Pow2_Sampler) : public SkBitmapSampler { +public: + BITMAP_CLASSNAME_PREFIX(_Point_Mirror_Pow2_Sampler)(const SkBitmap& bm) + : SkBitmapSampler(bm, SkPaint::kNo_FilterType, SkShader::kMirror_TileMode, SkShader::kMirror_TileMode) + { + } + + virtual SkPMColor sample(SkFixed x, SkFixed y) const + { + x = do_mirror_pow2(SkFixedRound(x), fMaxX); + y = do_mirror_pow2(SkFixedRound(y), fMaxY); + return BITMAP_PIXEL_TO_PMCOLOR(fBitmap, x, y); + } +}; + +class BITMAP_CLASSNAME_PREFIX(_Point_Mirror_Mod_Sampler) : public SkBitmapSampler { +public: + BITMAP_CLASSNAME_PREFIX(_Point_Mirror_Mod_Sampler)(const SkBitmap& bm) + : SkBitmapSampler(bm, SkPaint::kNo_FilterType, SkShader::kMirror_TileMode, SkShader::kMirror_TileMode) + { + } + + virtual SkPMColor sample(SkFixed x, SkFixed y) const + { + x = do_mirror_mod(SkFixedRound(x), fMaxX); + y = do_mirror_mod(SkFixedRound(y), fMaxY); + return BITMAP_PIXEL_TO_PMCOLOR(fBitmap, x, y); + } +}; + +#undef BITMAP_CLASSNAME_PREFIX +#undef BITMAP_PIXEL_TO_PMCOLOR diff --git a/libs/graphics/sgl/SkBitmapShader.cpp b/libs/graphics/sgl/SkBitmapShader.cpp new file mode 100644 index 0000000000..b70f8bb587 --- /dev/null +++ b/libs/graphics/sgl/SkBitmapShader.cpp @@ -0,0 +1,476 @@ +#include "SkBitmapShader.h" +#include "SkBitmapSampler.h" + +#ifdef SK_SUPPORT_MIPMAP +static SkFixed find_mip_level(SkFixed dx, SkFixed dy) +{ + dx = SkAbs32(dx); + dy = SkAbs32(dy); + if (dx < dy) + dx = dy; + + if (dx < SK_Fixed1) + return 0; + + int clz = SkCLZ(dx); + SkASSERT(clz >= 1 && clz <= 15); + return SkIntToFixed(15 - clz) + ((unsigned)(dx << (clz + 1)) >> 16); +} +#endif + +SkBitmapShader::SkBitmapShader(const SkBitmap& src, + bool transferOwnershipOfPixels, + SkPaint::FilterType filterType, + TileMode tmx, TileMode tmy) + : +#ifdef SK_SUPPORT_MIPMAP + fMipLevel(0), fMipSrcBitmap(src), +#endif + fOrigSrcBitmap(src) + +{ + if (transferOwnershipOfPixels) + { + fOrigSrcBitmap.setOwnsPixels(src.getOwnsPixels()); + ((SkBitmap*)&src)->setOwnsPixels(false); + // do the same for mipmap ownership??? + } + fFilterType = SkToU8(filterType); + fTileModeX = SkToU8(tmx); + fTileModeY = SkToU8(tmy); +} + +bool SkBitmapShader::setContext(const SkBitmap& device, const SkPaint& paint, const SkMatrix& matrix) +{ + // do this first, so we have a correct inverse matrix + if (!this->INHERITED::setContext(device, paint, matrix)) + return false; + + uint32_t flags = fOrigSrcBitmap.isOpaque() ? kOpaqueAlpha_Flag : 0; + + if (flags == kOpaqueAlpha_Flag && paint.getAlpha() != 0xFF) + flags = kConstAlpha_Flag; + + fFlags = SkToU8(flags); + +#ifdef SK_SUPPORT_MIPMAP + if (fOrigSrcBitmap.countMipLevels()) + { + const SkMatrix& inv = this->getTotalInverse(); + + fMipLevel = SkMin32(find_mip_level( SkScalarToFixed(inv.getScaleX()), + SkScalarToFixed(inv.getSkewY())), + SkIntToFixed(fOrigSrcBitmap.countMipLevels() - 1)); + +// SkDEBUGF(("BitmapShader miplevel=%x\n", fMipLevel)); + + const SkBitmap::MipLevel* mm = fOrigSrcBitmap.getMipLevel(fMipLevel >> 16); + + fMipSrcBitmap.setConfig(fOrigSrcBitmap.getConfig(), + mm->fWidth, + mm->fHeight, + mm->fRowBytes); + fMipSrcBitmap.setPixels(mm->fPixels); + } + else + { + fMipLevel = 0; + fMipSrcBitmap = fOrigSrcBitmap; + } +#endif + return true; +} + +/////////////////////////////////////////////////////////////////////////// + +#include "SkColorPriv.h" +#include "SkBitmapSampler.h" + +class Sampler_BitmapShader : public SkBitmapShader { +public: + Sampler_BitmapShader(const SkBitmap& src, + bool transferOwnershipOfPixels, + SkPaint::FilterType ftype, + TileMode tmx, TileMode tmy) + : SkBitmapShader(src, transferOwnershipOfPixels, ftype, tmx, tmy) + { + // make sure to pass our copy of the src bitmap to the sampler, and not the + // original parameter (which might go away). + fSampler = NULL; + } + + virtual ~Sampler_BitmapShader() + { + SkDELETE(fSampler); + } + + virtual bool setContext(const SkBitmap& device, const SkPaint& paint, const SkMatrix& matrix) + { + if (this->INHERITED::setContext(device, paint, matrix)) + { + SkDELETE(fSampler); + fSampler = SkBitmapSampler::Create(this->getSrcBitmap(), this->getFilterType(), + this->getTileModeX(), this->getTileModeY()); + return true; + } + return false; + } + + virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) + { + unsigned scale = SkAlpha255To256(this->getPaintAlpha()); + const SkMatrix& inv = this->getTotalInverse(); + SkMatrix::MapPtProc proc = this->getInverseMapPtProc(); + SkBitmapSampler* sampler = fSampler; + MatrixClass mc = this->getInverseClass(); + + SkPoint srcPt; + + if (mc != kPerspective_MatrixClass) + { + proc(inv, SkIntToScalar(x), SkIntToScalar(y), &srcPt); + + SkFixed fx = SkScalarToFixed(srcPt.fX); + SkFixed fy = SkScalarToFixed(srcPt.fY); + SkFixed dx, dy; + + if (mc == kLinear_MatrixClass) + { + dx = SkScalarToFixed(inv.getScaleX()); + dy = SkScalarToFixed(inv.getSkewY()); + } + else + (void)inv.fixedStepInX(SkIntToScalar(y), &dx, &dy); + +#if defined(SK_SUPPORT_MIPMAP) + { int level = this->getMipLevel() >> 16; + fx >>= level; + fy >>= level; + dx >>= level; + dy >>= level; + } +#endif + if (scale == 256) + { + for (int i = 0; i < count; i++) + { + dstC[i] = sampler->sample(fx, fy); + fx += dx; + fy += dy; + } + } + else + { + for (int i = 0; i < count; i++) + { + uint32_t c = sampler->sample(fx, fy); + dstC[i] = SkAlphaMulQ(c, scale); + fx += dx; + fy += dy; + } + } + } + else + { + SkScalar dstX = SkIntToScalar(x); + SkScalar dstY = SkIntToScalar(y); + + for (int i = 0; i < count; i++) + { + proc(inv, dstX, dstY, &srcPt); + uint32_t c = sampler->sample(SkScalarToFixed(srcPt.fX), SkScalarToFixed(srcPt.fY)); + + if (scale != 256) + c = SkAlphaMulQ(c, scale); + dstC[i] = c; + dstX += SK_Scalar1; + } + } + } + +protected: + + const SkMatrix& getUnitInverse() const { return fUnitInverse; } + SkMatrix::MapPtProc getUnitInverseProc() const { return fUnitInverseProc; } + + /* takes computed inverse (from setContext) and computes fUnitInverse, + taking srcBitmap width/height into account, so that fUnitInverse + walks 0...1, allowing the tile modes to all operate in a fast 16bit + space (no need for mod). The resulting coords need to be scaled by + width/height to get back into src space (coord * width >> 16). + */ + void computeUnitInverse() + { + const SkBitmap& src = getSrcBitmap(); + fUnitInverse = this->getTotalInverse(); + fUnitInverse.postScale(SK_Scalar1 / src.width(), SK_Scalar1 / src.height(), 0, 0); + fUnitInverseProc = fUnitInverse.getMapPtProc(); + } + +private: + SkBitmapSampler* fSampler; + SkMatrix fUnitInverse; + SkMatrix::MapPtProc fUnitInverseProc; + + typedef SkBitmapShader INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////// + +class HasSpan16_Sampler_BitmapShader : public Sampler_BitmapShader { +public: + HasSpan16_Sampler_BitmapShader(const SkBitmap& src, bool transferOwnershipOfPixels, + SkPaint::FilterType ft, TileMode tmx, TileMode tmy) + : Sampler_BitmapShader(src, transferOwnershipOfPixels, ft, tmx, tmy) + { + } + + virtual uint32_t getFlags() + { + uint32_t flags = this->INHERITED::getFlags(); + + if (this->getPaintAlpha() == 0xFF && this->getInverseClass() != kPerspective_MatrixClass) + flags |= SkShader::kHasSpan16_Flag; + else + flags &= ~SkShader::kHasSpan16_Flag; + + return flags; + } +private: + typedef Sampler_BitmapShader INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////// + +#define NOFILTER_BITMAP_SHADER_CLASS Index8_NoFilter_ClampTile_BitmapShader +#define NOFILTER_BITMAP_SHADER_TILEMODE SkShader::kClamp_TileMode +#define NOFILTER_BITMAP_SHADER_TILEPROC(x, max) SkClampMax((x >> 16), max) +#define NOFILTER_BITMAP_SHADER_TYPE uint8_t +#define NOFILTER_BITMAP_SHADER_SAMPLE_X(p, x) colors32[p[x]] +#define NOFILTER_BITMAP_SHADER_SAMPLE_XY(p, x, y, rb) colors32[p[x + y * rb]] +#define NOFILTER_BITMAP_SHADER_PREAMBLE(bitmap, rb) const SkPMColor* colors32 = bitmap.getColorTable()->lockColors() +#define NOFILTER_BITMAP_SHADER_POSTAMBLE(bitmap) bitmap.getColorTable()->unlockColors(false) +#define NOFILTER_BITMAP_SHADER_SAMPLE_X16(p, x) colors16[p[x]] +#define NOFILTER_BITMAP_SHADER_SAMPLE_XY16(p, x, y, rb) colors16[p[x + y * rb]] +#define NOFILTER_BITMAP_SHADER_PREAMBLE16(bitmap, rb) const uint16_t* colors16 = bitmap.getColorTable()->lock16BitCache() +#define NOFILTER_BITMAP_SHADER_POSTAMBLE16(bitmap) bitmap.getColorTable()->unlock16BitCache() +#include "SkBitmapShaderTemplate.h" + +#define NOFILTER_BITMAP_SHADER_CLASS Index8_NoFilter_RepeatTile_BitmapShader +#define NOFILTER_BITMAP_SHADER_TILEMODE SkShader::kRepeat_TileMode +#define NOFILTER_BITMAP_SHADER_TILEPROC(x, max) (fixed_repeat(x) * (max + 1) >> 16) +#define NOFILTER_BITMAP_SHADER_TYPE uint8_t +#define NOFILTER_BITMAP_SHADER_SAMPLE_X(p, x) colors32[p[x]] +#define NOFILTER_BITMAP_SHADER_SAMPLE_XY(p, x, y, rb) colors32[p[x + y * rb]] +#define NOFILTER_BITMAP_SHADER_PREAMBLE(bitmap, rb) const SkPMColor* colors32 = bitmap.getColorTable()->lockColors() +#define NOFILTER_BITMAP_SHADER_POSTAMBLE(bitmap) bitmap.getColorTable()->unlockColors(false) +#define NOFILTER_BITMAP_SHADER_SAMPLE_X16(p, x) colors16[p[x]] +#define NOFILTER_BITMAP_SHADER_SAMPLE_XY16(p, x, y, rb) colors16[p[x + y * rb]] +#define NOFILTER_BITMAP_SHADER_PREAMBLE16(bitmap, rb) const uint16_t* colors16 = bitmap.getColorTable()->lock16BitCache() +#define NOFILTER_BITMAP_SHADER_POSTAMBLE16(bitmap) bitmap.getColorTable()->unlock16BitCache() +#define NOFILTER_BITMAP_SHADER_USE_UNITINVERSE +#include "SkBitmapShaderTemplate.h" + +#define NOFILTER_BITMAP_SHADER_CLASS U16_NoFilter_ClampTile_BitmapShader +#define NOFILTER_BITMAP_SHADER_TILEMODE SkShader::kClamp_TileMode +#define NOFILTER_BITMAP_SHADER_TILEPROC(x, max) SkClampMax((x >> 16), max) +#define NOFILTER_BITMAP_SHADER_TYPE uint16_t +#define NOFILTER_BITMAP_SHADER_SAMPLE_X(p, x) SkPixel16ToPixel32(p[x]) +#define NOFILTER_BITMAP_SHADER_SAMPLE_XY(p, x, y, rb) SkPixel16ToPixel32(*(const uint16_t*)((const char*)p + y * rb + (x << 1))) +#define NOFILTER_BITMAP_SHADER_SAMPLE_X16(p, x) p[x] +#define NOFILTER_BITMAP_SHADER_SAMPLE_XY16(p, x, y, rb) *(const uint16_t*)((const char*)p + y * rb + (x << 1)) +#include "SkBitmapShaderTemplate.h" + +#define NOFILTER_BITMAP_SHADER_CLASS U16_NoFilter_RepeatTile_BitmapShader +#define NOFILTER_BITMAP_SHADER_TILEMODE SkShader::kRepeat_TileMode +#define NOFILTER_BITMAP_SHADER_TILEPROC(x, max) (fixed_repeat(x) * (max + 1) >> 16) +#define NOFILTER_BITMAP_SHADER_TYPE uint16_t +#define NOFILTER_BITMAP_SHADER_SAMPLE_X(p, x) SkPixel16ToPixel32(p[x]) +#define NOFILTER_BITMAP_SHADER_SAMPLE_XY(p, x, y, rb) SkPixel16ToPixel32(*(const uint16_t*)((const char*)p + y * rb + (x << 1))) +#define NOFILTER_BITMAP_SHADER_SAMPLE_X16(p, x) p[x] +#define NOFILTER_BITMAP_SHADER_SAMPLE_XY16(p, x, y, rb) *(const uint16_t*)((const char*)p + y * rb + (x << 1)) +#define NOFILTER_BITMAP_SHADER_USE_UNITINVERSE +#include "SkBitmapShaderTemplate.h" + +#define NOFILTER_BITMAP_SHADER_CLASS U32_NoFilter_ClampTile_BitmapShader +#define NOFILTER_BITMAP_SHADER_TILEMODE SkShader::kClamp_TileMode +#define NOFILTER_BITMAP_SHADER_TILEPROC(x, max) SkClampMax((x >> 16), max) +#define NOFILTER_BITMAP_SHADER_TYPE uint32_t +#define NOFILTER_BITMAP_SHADER_SAMPLE_X(p, x) p[x] +#define NOFILTER_BITMAP_SHADER_SAMPLE_XY(p, x, y, rb) *(const uint32_t*)((const char*)p + y * rb + (x << 2)) +#define NOFILTER_BITMAP_SHADER_SAMPLE_X16(p, x) SkPixel32ToPixel16_ToU16(p[x]) +#define NOFILTER_BITMAP_SHADER_SAMPLE_XY16(p, x, y, rb) SkPixel32ToPixel16_ToU16(*(const uint32_t*)((const char*)p + y * rb + (x << 2))) +#include "SkBitmapShaderTemplate.h" + +#define NOFILTER_BITMAP_SHADER_CLASS U32_NoFilter_RepeatTile_BitmapShader +#define NOFILTER_BITMAP_SHADER_TILEMODE SkShader::kRepeat_TileMode +#define NOFILTER_BITMAP_SHADER_TILEPROC(x, max) (fixed_repeat(x) * (max + 1) >> 16) +#define NOFILTER_BITMAP_SHADER_TYPE uint32_t +#define NOFILTER_BITMAP_SHADER_SAMPLE_X(p, x) p[x] +#define NOFILTER_BITMAP_SHADER_SAMPLE_XY(p, x, y, rb) *(const uint32_t*)((const char*)p + y * rb + (x << 2)) +#define NOFILTER_BITMAP_SHADER_SAMPLE_X16(p, x) SkPixel32ToPixel16_ToU16(p[x]) +#define NOFILTER_BITMAP_SHADER_SAMPLE_XY16(p, x, y, rb) SkPixel32ToPixel16_ToU16(*(const uint32_t*)((const char*)p + y * rb + (x << 2))) +#define NOFILTER_BITMAP_SHADER_USE_UNITINVERSE +#include "SkBitmapShaderTemplate.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////////// + +#define Pack4Bytes(c00, c01, c10, c11) (((c00) << 24) | ((c01) << 16) | ((c10) << 8) | (c11)) +/* Each long stores 4 coefficients, each in a byte. + coeff >> 24 -> [0][0] + coeff >> 16 -> [0][1] + coeff >> 8 -> [1][0] + coeff >> 0 -> [1][1] +*/ +static const uint32_t gBilerpPackedCoeff[] = { + /* y == 0 */ + Pack4Bytes(16, 0, 0, 0), // x == 0 + Pack4Bytes(12, 4, 0, 0), // x == 1/4 + Pack4Bytes( 8, 8, 0, 0), // x == 1/2 + Pack4Bytes( 4, 12, 0, 0), // x == 3/4 + + /* y == 1/4 */ + Pack4Bytes(12, 0, 4, 0), + Pack4Bytes( 9, 3, 3, 1), + Pack4Bytes( 6, 6, 2, 2), + Pack4Bytes( 3, 9, 1, 3), + + /* y == 1/2 */ + Pack4Bytes( 8, 0, 8, 0), + Pack4Bytes( 6, 2, 6, 2), + Pack4Bytes( 4, 4, 4, 4), + Pack4Bytes( 2, 6, 2, 6), + + /* y == 3/4 */ + Pack4Bytes( 4, 0, 12, 0), + Pack4Bytes( 3, 1, 9, 3), + Pack4Bytes( 2, 2, 6, 6), + Pack4Bytes( 1, 3, 3, 9) +}; + +// extract the high two bits in the fractional part of the fixed +#define SK_BILERP_GET_BITS(x) (((x) >> 14) & 3) + +static inline uint32_t sk_find_bilerp_coeff(const uint32_t coeff[], SkFixed fx, SkFixed fy) +{ +#ifdef SK_DEBUG + uint32_t c = coeff[(SK_BILERP_GET_BITS(fy) << 2) | SK_BILERP_GET_BITS(fx)]; + SkASSERT((c >> 24) + ((c >> 16) & 0xFF) + ((c >> 8) & 0xFF) + (c & 0xFF) == 16); +#endif + return coeff[(SK_BILERP_GET_BITS(fy) << 2) | SK_BILERP_GET_BITS(fx)]; +} + +static inline uint32_t expand_rgb_16(U16CPU c, U16CPU rbMask) +{ + return ((c & SK_G16_MASK_IN_PLACE) << 16) | (c & rbMask); +} + +static inline U16CPU compact_rgb_16(uint32_t c, U16CPU rbMask) +{ + return ((c >> 16) & SK_G16_MASK_IN_PLACE) | (c & rbMask); +} + +static inline U16CPU sk_bilerp16(U16CPU c00, U16CPU c01, U16CPU c10, U16CPU c11, uint32_t coeff, U16CPU rbMask) +{ +// U16CPU rbMask = SK_R16B16_MASK_IN_PLACE; + + c00 = expand_rgb_16(c00, rbMask) * (coeff >> 24) + + expand_rgb_16(c01, rbMask) * ((coeff >> 16) & 0xFF) + + expand_rgb_16(c10, rbMask) * ((coeff >> 8) & 0xFF) + + expand_rgb_16(c11, rbMask) * (coeff & 0xFF); + + return compact_rgb_16(c00 >> 4, rbMask); +} + +// this wacky line is to force the compiler to put this contant into a register +// rather than try to construct it each time it is referenced in the inner-loop +extern const uint16_t gRBMask_Bilerp_BitmapShader; + +#define BILERP_BITMAP16_SHADER_CLASS U16_Bilerp_BitmapShader +#define BILERP_BITMAP16_SHADER_TYPE uint16_t +#define BILERP_BITMAP16_SHADER_PREAMBLE(bm) +#define BILERP_BITMAP16_SHADER_PIXEL(c) (c) +#define BILERP_BITMAP16_SHADER_POSTAMBLE(bm) +#include "SkBitmapShader16BilerpTemplate.h" + +#define BILERP_BITMAP16_SHADER_CLASS Index8_Bilerp_BitmapShader +#define BILERP_BITMAP16_SHADER_TYPE uint8_t +#define BILERP_BITMAP16_SHADER_PREAMBLE(bm) SkColorTable* ctable = (bm).getColorTable(); const uint16_t* colors16 = ctable->lock16BitCache() +#define BILERP_BITMAP16_SHADER_PIXEL(c) colors16[c] +#define BILERP_BITMAP16_SHADER_POSTAMBLE(bm) ctable->unlock16BitCache() +#include "SkBitmapShader16BilerpTemplate.h" + +// we define it below all the includes, so they won't try to inline the value +// (which doesn't fit in an immediate register load) +const uint16_t gRBMask_Bilerp_BitmapShader = SK_R16B16_MASK_IN_PLACE; + +/////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////// + +#include "SkTemplatesPriv.h" + +SkShader* SkShader::CreateBitmapShader(const SkBitmap& src, + bool transferOwnershipOfPixels, + SkPaint::FilterType filterType, + TileMode tmx, TileMode tmy, + void* storage, size_t storageSize) +{ + SkShader* shader = NULL; + + if (filterType == SkPaint::kNo_FilterType) + { + switch (src.getConfig()) { + case SkBitmap::kIndex8_Config: + if (kClamp_TileMode == tmx && kClamp_TileMode == tmy) + SK_PLACEMENT_NEW_ARGS(shader, Index8_NoFilter_ClampTile_BitmapShader, storage, storageSize, (src, transferOwnershipOfPixels)); + else if (kRepeat_TileMode == tmx && kRepeat_TileMode == tmy) + SK_PLACEMENT_NEW_ARGS(shader, Index8_NoFilter_RepeatTile_BitmapShader, storage, storageSize, (src, transferOwnershipOfPixels)); + break; + case SkBitmap::kRGB_565_Config: + if (kClamp_TileMode == tmx && kClamp_TileMode == tmy) + SK_PLACEMENT_NEW_ARGS(shader, U16_NoFilter_ClampTile_BitmapShader, storage, storageSize, (src, transferOwnershipOfPixels)); + else if (kRepeat_TileMode == tmx && kRepeat_TileMode == tmy) + SK_PLACEMENT_NEW_ARGS(shader, U16_NoFilter_RepeatTile_BitmapShader, storage, storageSize, (src, transferOwnershipOfPixels)); + break; + case SkBitmap::kARGB_8888_Config: + if (kClamp_TileMode == tmx && kClamp_TileMode == tmy) + SK_PLACEMENT_NEW_ARGS(shader, U32_NoFilter_ClampTile_BitmapShader, storage, storageSize, (src, transferOwnershipOfPixels)); + else if (kRepeat_TileMode == tmx && kRepeat_TileMode == tmy) + SK_PLACEMENT_NEW_ARGS(shader, U32_NoFilter_RepeatTile_BitmapShader, storage, storageSize, (src, transferOwnershipOfPixels)); + break; + default: + break; + } + } + else if (filterType == SkPaint::kBilinear_FilterType + && kClamp_TileMode == tmx + && kClamp_TileMode == tmy) + { + switch (src.getConfig()) { + case SkBitmap::kIndex8_Config: + SK_PLACEMENT_NEW_ARGS(shader, Index8_Bilerp_BitmapShader, storage, storageSize, (src, transferOwnershipOfPixels)); + break; + case SkBitmap::kRGB_565_Config: + SK_PLACEMENT_NEW_ARGS(shader, U16_Bilerp_BitmapShader, storage, storageSize, (src, transferOwnershipOfPixels)); + break; + default: + break; + } + } + + // if shader is null, then none of the special cases could handle the request + // so fall through to our slow-general case + if (shader == NULL) + SK_PLACEMENT_NEW_ARGS(shader, Sampler_BitmapShader, storage, storageSize, + (src, transferOwnershipOfPixels, filterType, tmx, tmy)); + return shader; +} + +SkShader* SkShader::CreateBitmapShader(const SkBitmap& src, + bool transferOwnershipOfPixels, + SkPaint::FilterType filterType, + TileMode tmx, TileMode tmy) +{ + return SkShader::CreateBitmapShader(src, transferOwnershipOfPixels, filterType, tmx, tmy, NULL, 0); +} + diff --git a/libs/graphics/sgl/SkBitmapShader.h b/libs/graphics/sgl/SkBitmapShader.h new file mode 100644 index 0000000000..6727ec4c28 --- /dev/null +++ b/libs/graphics/sgl/SkBitmapShader.h @@ -0,0 +1,51 @@ +#ifndef SkBitmapShader_DEFINED +#define SkBitmapShader_DEFINED + +#include "SkShader.h" +#include "SkBitmap.h" +#include "SkPaint.h" + +class SkBitmapShader : public SkShader { +public: + SkBitmapShader( const SkBitmap& src, bool transferOwnershipOfPixels, + SkPaint::FilterType, TileMode tx, TileMode ty); + + virtual bool setContext(const SkBitmap&, const SkPaint& paint, const SkMatrix&); + virtual uint32_t getFlags() { return fFlags; } + +protected: + const SkBitmap& getSrcBitmap() const + { +#ifdef SK_SUPPORT_MIPMAP + return fMipSrcBitmap; +#else + return fOrigSrcBitmap; +#endif + } + SkPaint::FilterType getFilterType() const { return (SkPaint::FilterType)fFilterType; } + TileMode getTileModeX() const { return (TileMode)fTileModeX; } + TileMode getTileModeY() const { return (TileMode)fTileModeY; } + SkFixed getMipLevel() const + { +#ifdef SK_SUPPORT_MIPMAP + return fMipLevel; +#else + return 0; +#endif + } + +private: +#ifdef SK_SUPPORT_MIPMAP + SkFixed fMipLevel; + SkBitmap fMipSrcBitmap; // the chosen level (in setContext) +#endif + SkBitmap fOrigSrcBitmap; + U8 fFilterType; + U8 fTileModeX; + U8 fTileModeY; + U8 fFlags; + + typedef SkShader INHERITED; +}; + +#endif diff --git a/libs/graphics/sgl/SkBitmapShader16BilerpTemplate.h b/libs/graphics/sgl/SkBitmapShader16BilerpTemplate.h new file mode 100644 index 0000000000..fa64a61bb6 --- /dev/null +++ b/libs/graphics/sgl/SkBitmapShader16BilerpTemplate.h @@ -0,0 +1,126 @@ + + +class BILERP_BITMAP16_SHADER_CLASS : public HasSpan16_Sampler_BitmapShader { +public: + BILERP_BITMAP16_SHADER_CLASS(const SkBitmap& src, bool transferOwnershipOfPixels) + : HasSpan16_Sampler_BitmapShader(src, transferOwnershipOfPixels, SkPaint::kBilinear_FilterType, + SkShader::kClamp_TileMode, SkShader::kClamp_TileMode) + { + } + + virtual void shadeSpanOpaque16(int x, int y, U16 dstC[], int count) + { + SkASSERT(count > 0); + SkASSERT(this->getInverseClass() != kPerspective_MatrixClass); + SkASSERT(this->getPaintAlpha() == 0xFF); + + const SkMatrix& inv = this->getTotalInverse(); + const SkBitmap& srcBitmap = this->getSrcBitmap(); + unsigned srcMaxX = srcBitmap.width() - 1; + unsigned srcMaxY = srcBitmap.height() - 1; + unsigned srcRB = srcBitmap.rowBytes(); + SkFixed fx, fy, dx, dy; + + // now init fx, fy, dx, dy + { + SkPoint srcPt; + this->getInverseMapPtProc()(inv, SkIntToScalar(x), SkIntToScalar(y), &srcPt); + + fx = SkScalarToFixed(srcPt.fX); + fy = SkScalarToFixed(srcPt.fY); + + if (this->getInverseClass() == kFixedStepInX_MatrixClass) + (void)inv.fixedStepInX(SkIntToScalar(y), &dx, &dy); + else + { + dx = SkScalarToFixed(inv.getScaleX()); + dy = SkScalarToFixed(inv.getSkewY()); + } + } + + BILERP_BITMAP16_SHADER_PREAMBLE(srcBitmap); + + const U32* coeff_table = gBilerpPackedCoeff; + const BILERP_BITMAP16_SHADER_TYPE* srcPixels = (const BILERP_BITMAP16_SHADER_TYPE*)srcBitmap.getPixels(); + U16CPU rbMask = gRBMask_Bilerp_BitmapShader; + + if (dy == 0) + { + fy = SkClampMax(fy, srcMaxY << 16); + coeff_table += SK_BILERP_GET_BITS(fy) << 2; // jump the table to the correct section (so we can just use fx to index it) + + unsigned y = fy >> 16; + SkASSERT((int)y >= 0 && y <= srcMaxY); + // pre-bias srcPixels since y won't change + srcPixels = (const BILERP_BITMAP16_SHADER_TYPE*)((const char*)srcPixels + y * srcRB); + // now make y the step from one row to the next + y = srcRB; + if (y == srcMaxY) + y = 0; + + do { + unsigned fx_clamped = SkClampMax(fx, srcMaxX << 16); + unsigned x = fx_clamped >> 16; + SkASSERT((int)x >= 0 && x <= srcMaxX); + + const BILERP_BITMAP16_SHADER_TYPE *p00, *p01, *p10, *p11; + + p00 = p01 = srcPixels + x; + if (x < srcMaxX) + p01 += 1; + p10 = (const BILERP_BITMAP16_SHADER_TYPE*)((const char*)p00 + y); + p11 = (const BILERP_BITMAP16_SHADER_TYPE*)((const char*)p01 + y); + + *dstC++ = SkToU16(sk_bilerp16( BILERP_BITMAP16_SHADER_PIXEL(*p00), + BILERP_BITMAP16_SHADER_PIXEL(*p01), + BILERP_BITMAP16_SHADER_PIXEL(*p10), + BILERP_BITMAP16_SHADER_PIXEL(*p11), + coeff_table[SK_BILERP_GET_BITS(fx_clamped)], + rbMask)); + + fx += dx; + } while (--count != 0); + } + else + { + do { + unsigned x = SkClampMax(fx, srcMaxX << 16) >> 16; + unsigned y = SkClampMax(fy, srcMaxY << 16) >> 16; + + SkASSERT((int)x >= 0 && x <= srcMaxX); + SkASSERT((int)y >= 0 && y <= srcMaxY); + + const BILERP_BITMAP16_SHADER_TYPE *p00, *p01, *p10, *p11; + + p00 = p01 = ((const BILERP_BITMAP16_SHADER_TYPE*)((const char*)srcPixels + y * srcRB)) + x; + if (x < srcMaxX) + p01 += 1; + p10 = p00; + p11 = p01; + if (y < srcMaxY) + { + p10 = (const BILERP_BITMAP16_SHADER_TYPE*)((const char*)p10 + srcRB); + p11 = (const BILERP_BITMAP16_SHADER_TYPE*)((const char*)p11 + srcRB); + } + + *dstC++ = SkToU16(sk_bilerp16( BILERP_BITMAP16_SHADER_PIXEL(*p00), + BILERP_BITMAP16_SHADER_PIXEL(*p01), + BILERP_BITMAP16_SHADER_PIXEL(*p10), + BILERP_BITMAP16_SHADER_PIXEL(*p11), + sk_find_bilerp_coeff(coeff_table, fx, fy), + rbMask)); + + fx += dx; + fy += dy; + } while (--count != 0); + } + + BILERP_BITMAP16_SHADER_POSTAMBLE(srcBitmap); + } +}; + +#undef BILERP_BITMAP16_SHADER_CLASS +#undef BILERP_BITMAP16_SHADER_TYPE +#undef BILERP_BITMAP16_SHADER_PREAMBLE +#undef BILERP_BITMAP16_SHADER_PIXEL +#undef BILERP_BITMAP16_SHADER_POSTAMBLE diff --git a/libs/graphics/sgl/SkBitmapShaderTemplate.h b/libs/graphics/sgl/SkBitmapShaderTemplate.h new file mode 100644 index 0000000000..2e90b84377 --- /dev/null +++ b/libs/graphics/sgl/SkBitmapShaderTemplate.h @@ -0,0 +1,231 @@ + +#ifndef NOFILTER_BITMAP_SHADER_PREAMBLE + #define NOFILTER_BITMAP_SHADER_PREAMBLE(bitmap, rb) +#endif +#ifndef NOFILTER_BITMAP_SHADER_POSTAMBLE + #define NOFILTER_BITMAP_SHADER_POSTAMBLE(bitmap) +#endif +#ifndef NOFILTER_BITMAP_SHADER_PREAMBLE16 + #define NOFILTER_BITMAP_SHADER_PREAMBLE16(bitmap, rb) +#endif +#ifndef NOFILTER_BITMAP_SHADER_POSTAMBLE16 + #define NOFILTER_BITMAP_SHADER_POSTAMBLE16(bitmap) +#endif + +class NOFILTER_BITMAP_SHADER_CLASS : public HasSpan16_Sampler_BitmapShader { +public: + NOFILTER_BITMAP_SHADER_CLASS(const SkBitmap& src, bool transferOwnershipOfPixels) + : HasSpan16_Sampler_BitmapShader(src, transferOwnershipOfPixels, SkPaint::kNo_FilterType, + NOFILTER_BITMAP_SHADER_TILEMODE, NOFILTER_BITMAP_SHADER_TILEMODE) + { + } + +#ifdef NOFILTER_BITMAP_SHADER_USE_UNITINVERSE + virtual bool setContext(const SkBitmap& device, const SkPaint& paint, const SkMatrix& matrix) + { + if (this->INHERITED::setContext(device, paint, matrix)) { + this->computeUnitInverse(); + return true; + } + return false; + } +#endif + + virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) + { + if (this->getInverseClass() == kPerspective_MatrixClass) + { + this->INHERITED::shadeSpan(x, y, dstC, count); + return; + } + + unsigned scale = SkAlpha255To256(this->getPaintAlpha()); +#ifdef NOFILTER_BITMAP_SHADER_USE_UNITINVERSE + const SkMatrix& inv = this->getUnitInverse(); + SkMatrix::MapPtProc invProc = this->getUnitInverseProc(); +#else + const SkMatrix& inv = this->getTotalInverse(); + SkMatrix::MapPtProc invProc = this->getInverseMapPtProc(); +#endif + const SkBitmap& srcBitmap = this->getSrcBitmap(); + unsigned srcMaxX = srcBitmap.width() - 1; + unsigned srcMaxY = srcBitmap.height() - 1; + unsigned srcRB = srcBitmap.rowBytes(); + SkFixed fx, fy, dx, dy; + + // now init fx, fy, dx, dy + { + SkPoint srcPt; + invProc(inv, SkIntToScalar(x), SkIntToScalar(y), &srcPt); + + fx = SkScalarToFixed(srcPt.fX); + fy = SkScalarToFixed(srcPt.fY); +#ifndef NOFILTER_BITMAP_SHADER_USE_UNITINVERSE + fx += SK_Fixed1/2; + fy += SK_Fixed1/2; +#endif + + if (this->getInverseClass() == kFixedStepInX_MatrixClass) + (void)inv.fixedStepInX(SkIntToScalar(y), &dx, &dy); + else + { + dx = SkScalarToFixed(inv.getScaleX()); + dy = SkScalarToFixed(inv.getSkewY()); + } + } + + const NOFILTER_BITMAP_SHADER_TYPE* srcPixels = (const NOFILTER_BITMAP_SHADER_TYPE*)srcBitmap.getPixels(); + NOFILTER_BITMAP_SHADER_PREAMBLE(srcBitmap, srcRB); + +#if defined(SK_SUPPORT_MIPMAP) && !defined(NOFILTER_BITMAP_SHADER_USE_UNITINVERSE) + { int level = this->getMipLevel() >> 16; + fx >>= level; + fy >>= level; + dx >>= level; + dy >>= level; + } +#endif + + if (dy == 0) + { + int y_index = NOFILTER_BITMAP_SHADER_TILEPROC(fy, srcMaxY); +// SkDEBUGF(("fy = %g, srcMaxY = %d, y_index = %d\n", SkFixedToFloat(fy), srcMaxY, y_index)); + srcPixels = (const NOFILTER_BITMAP_SHADER_TYPE*)((const char*)srcPixels + y_index * srcRB); + if (scale == 256) + while (--count >= 0) + { + unsigned x = NOFILTER_BITMAP_SHADER_TILEPROC(fx, srcMaxX); + fx += dx; + *dstC++ = NOFILTER_BITMAP_SHADER_SAMPLE_X(srcPixels, x); + } + else + while (--count >= 0) + { + unsigned x = NOFILTER_BITMAP_SHADER_TILEPROC(fx, srcMaxX); + U32 c = NOFILTER_BITMAP_SHADER_SAMPLE_X(srcPixels, x); + fx += dx; + *dstC++ = SkAlphaMulQ(c, scale); + } + } + else // dy != 0 + { + if (scale == 256) + while (--count >= 0) + { + unsigned x = NOFILTER_BITMAP_SHADER_TILEPROC(fx, srcMaxX); + unsigned y = NOFILTER_BITMAP_SHADER_TILEPROC(fy, srcMaxY); + fx += dx; + fy += dy; + *dstC++ = NOFILTER_BITMAP_SHADER_SAMPLE_XY(srcPixels, x, y, srcRB); + } + else + while (--count >= 0) + { + unsigned x = NOFILTER_BITMAP_SHADER_TILEPROC(fx, srcMaxX); + unsigned y = NOFILTER_BITMAP_SHADER_TILEPROC(fy, srcMaxY); + U32 c = NOFILTER_BITMAP_SHADER_SAMPLE_XY(srcPixels, x, y, srcRB); + fx += dx; + fy += dy; + *dstC++ = SkAlphaMulQ(c, scale); + } + } + + NOFILTER_BITMAP_SHADER_POSTAMBLE(srcBitmap); + } + + virtual void shadeSpanOpaque16(int x, int y, U16 dstC[], int count) + { + SkASSERT(count > 0); + SkASSERT(this->getInverseClass() != kPerspective_MatrixClass); + SkASSERT(this->getFlags() & SkShader::kHasSpan16_Flag); + SkASSERT(this->getFlags() & (SkShader::kOpaqueAlpha_Flag | SkShader::kConstAlpha_Flag)); + +#ifdef NOFILTER_BITMAP_SHADER_USE_UNITINVERSE + const SkMatrix& inv = this->getUnitInverse(); + SkMatrix::MapPtProc invProc = this->getUnitInverseProc(); +#else + const SkMatrix& inv = this->getTotalInverse(); + SkMatrix::MapPtProc invProc = this->getInverseMapPtProc(); +#endif + const SkBitmap& srcBitmap = this->getSrcBitmap(); + unsigned srcMaxX = srcBitmap.width() - 1; + unsigned srcMaxY = srcBitmap.height() - 1; + unsigned srcRB = srcBitmap.rowBytes(); + SkFixed fx, fy, dx, dy; + + // now init fx, fy, dx, dy + { + SkPoint srcPt; + invProc(inv, SkIntToScalar(x), SkIntToScalar(y), &srcPt); + + fx = SkScalarToFixed(srcPt.fX); + fy = SkScalarToFixed(srcPt.fY); +#ifndef NOFILTER_BITMAP_SHADER_USE_UNITINVERSE + fx += SK_Fixed1/2; + fy += SK_Fixed1/2; +#endif + + if (this->getInverseClass() == kFixedStepInX_MatrixClass) + (void)inv.fixedStepInX(SkIntToScalar(y), &dx, &dy); + else + { + dx = SkScalarToFixed(inv.getScaleX()); + dy = SkScalarToFixed(inv.getSkewY()); + } + } + + const NOFILTER_BITMAP_SHADER_TYPE* srcPixels = (const NOFILTER_BITMAP_SHADER_TYPE*)srcBitmap.getPixels(); + NOFILTER_BITMAP_SHADER_PREAMBLE16(srcBitmap, srcRB); + +#if defined(SK_SUPPORT_MIPMAP) && !defined(NOFILTER_BITMAP_SHADER_USE_UNITINVERSE) + { int level = this->getMipLevel() >> 16; + fx >>= level; + fy >>= level; + dx >>= level; + dy >>= level; + } +#endif + + if (dy == 0) + { + srcPixels = (const NOFILTER_BITMAP_SHADER_TYPE*)((const char*)srcPixels + NOFILTER_BITMAP_SHADER_TILEPROC(fy, srcMaxY) * srcRB); + do { + unsigned x = NOFILTER_BITMAP_SHADER_TILEPROC(fx, srcMaxX); + fx += dx; + *dstC++ = NOFILTER_BITMAP_SHADER_SAMPLE_X16(srcPixels, x); + } while (--count != 0); + } + else // dy != 0 + { + do { + int ix = fx >> 16; + unsigned x = NOFILTER_BITMAP_SHADER_TILEPROC(ix, srcMaxX); + ix = fy >> 16; + unsigned y = NOFILTER_BITMAP_SHADER_TILEPROC(ix, srcMaxY); + fx += dx; + fy += dy; + *dstC++ = NOFILTER_BITMAP_SHADER_SAMPLE_XY16(srcPixels, x, y, srcRB); + } while (--count != 0); + } + + NOFILTER_BITMAP_SHADER_POSTAMBLE16(srcBitmap); + } +private: + typedef HasSpan16_Sampler_BitmapShader INHERITED; +}; + +#undef NOFILTER_BITMAP_SHADER_CLASS +#undef NOFILTER_BITMAP_SHADER_TYPE +#undef NOFILTER_BITMAP_SHADER_PREAMBLE +#undef NOFILTER_BITMAP_SHADER_POSTAMBLE +#undef NOFILTER_BITMAP_SHADER_SAMPLE_X //(x) +#undef NOFILTER_BITMAP_SHADER_SAMPLE_XY //(x, y, rowBytes) +#undef NOFILTER_BITMAP_SHADER_TILEMODE +#undef NOFILTER_BITMAP_SHADER_TILEPROC + +#undef NOFILTER_BITMAP_SHADER_PREAMBLE16 +#undef NOFILTER_BITMAP_SHADER_POSTAMBLE16 +#undef NOFILTER_BITMAP_SHADER_SAMPLE_X16 //(x) +#undef NOFILTER_BITMAP_SHADER_SAMPLE_XY16 //(x, y, rowBytes) + +#undef NOFILTER_BITMAP_SHADER_USE_UNITINVERSE diff --git a/libs/graphics/sgl/SkBlitBWMaskTemplate.h b/libs/graphics/sgl/SkBlitBWMaskTemplate.h new file mode 100644 index 0000000000..70530f17fc --- /dev/null +++ b/libs/graphics/sgl/SkBlitBWMaskTemplate.h @@ -0,0 +1,120 @@ +#include "SkBitmap.h" +#include "SkMask.h" + +#ifndef ClearLow3Bits_DEFINED +#define ClearLow3Bits_DEFINED + #define ClearLow3Bits(x) ((unsigned)(x) >> 3 << 3) +#endif + +/* + SK_BLITBWMASK_NAME name of function(const SkBitmap& bitmap, const SkMask& mask, const SkRect16& clip, SK_BLITBWMASK_ARGS) + SK_BLITBWMASK_ARGS list of additional arguments to SK_BLITBWMASK_NAME, beginning with a comma + SK_BLITBWMASK_BLIT8 name of function(U8CPU byteMask, SK_BLITBWMASK_DEVTYPE* dst, int x, int y) + SK_BLITBWMASK_GETADDR either getAddr32 or getAddr16 or getAddr8 + SK_BLITBWMASK_DEVTYPE either U32 or U16 or U8 +*/ + +static void SK_BLITBWMASK_NAME(const SkBitmap& bitmap, const SkMask& srcMask, const SkRect16& clip SK_BLITBWMASK_ARGS) +{ + SkASSERT(clip.fRight <= srcMask.fBounds.fRight); + + int cx = clip.fLeft; + int cy = clip.fTop; + int maskLeft = srcMask.fBounds.fLeft; + unsigned mask_rowBytes = srcMask.fRowBytes; + unsigned bitmap_rowBytes = bitmap.rowBytes(); + unsigned height = clip.height(); + + SkASSERT(mask_rowBytes != 0); + SkASSERT(bitmap_rowBytes != 0); + SkASSERT(height != 0); + + const U8* bits = srcMask.getAddr1(cx, cy); + SK_BLITBWMASK_DEVTYPE* device = bitmap.SK_BLITBWMASK_GETADDR(cx, cy); + + if (cx == maskLeft && clip.fRight == srcMask.fBounds.fRight) + { + do { + SK_BLITBWMASK_DEVTYPE* dst = device; + unsigned rb = mask_rowBytes; + do { + U8CPU mask = *bits++; + SK_BLITBWMASK_BLIT8(mask, dst); + dst += 8; + } while (--rb != 0); + device = (SK_BLITBWMASK_DEVTYPE*)((char*)device + bitmap_rowBytes); + } while (--height != 0); + } + else + { + int left_edge = cx - maskLeft; + SkASSERT(left_edge >= 0); + int rite_edge = clip.fRight - maskLeft; + SkASSERT(rite_edge > left_edge); + + int left_mask = 0xFF >> (left_edge & 7); + int rite_mask = 0xFF << (8 - (rite_edge & 7)); + int full_runs = (rite_edge >> 3) - ((left_edge + 7) >> 3); + + // check for empty right mask, so we don't read off the end (or go slower than we need to) + if (rite_mask == 0) + { + SkASSERT(full_runs >= 0); + full_runs -= 1; + rite_mask = 0xFF; + } + if (left_mask == 0xFF) + full_runs -= 1; + + // back up manually so we can keep in sync with our byte-aligned src + // and not trigger an assert from the getAddr## function + device -= left_edge & 7; + // have cx reflect our actual starting x-coord + cx -= left_edge & 7; + + if (full_runs < 0) + { + left_mask &= rite_mask; + SkASSERT(left_mask != 0); + do { + U8CPU mask = *bits & left_mask; + SK_BLITBWMASK_BLIT8(mask, device); + bits += mask_rowBytes; + device = (SK_BLITBWMASK_DEVTYPE*)((char*)device + bitmap_rowBytes); + } while (--height != 0); + } + else + { + do { + int runs = full_runs; + SK_BLITBWMASK_DEVTYPE* dst = device; + const U8* b = bits; + U8CPU mask; + + mask = *b++ & left_mask; + SK_BLITBWMASK_BLIT8(mask, dst); + dst += 8; + + while (--runs >= 0) + { + mask = *b++; + SK_BLITBWMASK_BLIT8(mask, dst); + dst += 8; + } + + mask = *b & rite_mask; + SK_BLITBWMASK_BLIT8(mask, dst); + + bits += mask_rowBytes; + device = (SK_BLITBWMASK_DEVTYPE*)((char*)device + bitmap_rowBytes); + } while (--height != 0); + } + } +} + +#undef SK_BLITBWMASK_NAME +#undef SK_BLITBWMASK_ARGS +#undef SK_BLITBWMASK_BLIT8 +#undef SK_BLITBWMASK_GETADDR +#undef SK_BLITBWMASK_DEVTYPE +#undef SK_BLITBWMASK_DOROWSETUP diff --git a/libs/graphics/sgl/SkBlitter.cpp b/libs/graphics/sgl/SkBlitter.cpp new file mode 100644 index 0000000000..5dcac42adc --- /dev/null +++ b/libs/graphics/sgl/SkBlitter.cpp @@ -0,0 +1,795 @@ +#include "SkBlitter.h" +#include "SkAntiRun.h" +#include "SkColor.h" +#include "SkColorFilter.h" +#include "SkMask.h" +#include "SkMaskFilter.h" +#include "SkTemplatesPriv.h" +#include "SkUtils.h" +#include "SkXfermode.h" + +SkBlitter::~SkBlitter() +{ +} + +void SkBlitter::blitH(int x, int y, int width) +{ + SkASSERT(!"unimplemented"); +} + +void SkBlitter::blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) +{ + SkASSERT(!"unimplemented"); +} + +void SkBlitter::blitV(int x, int y, int height, SkAlpha alpha) +{ + if (alpha == 255) + this->blitRect(x, y, 1, height); + else + { + int16_t runs[2]; + runs[0] = 1; + runs[1] = 0; + + while (--height >= 0) + this->blitAntiH(x, y++, &alpha, runs); + } +} + +void SkBlitter::blitRect(int x, int y, int width, int height) +{ + while (--height >= 0) + this->blitH(x, y++, width); +} + +////////////////////////////////////////////////////////////////////////////// + +static inline void bits_to_runs(SkBlitter* blitter, int x, int y, const uint8_t bits[], U8CPU left_mask, int rowBytes, U8CPU right_mask) +{ + int inFill = 0; + int pos = 0; + + while (--rowBytes >= 0) + { + unsigned b = *bits++ & left_mask; + if (rowBytes == 0) + b &= right_mask; + + for (unsigned test = 0x80; test != 0; test >>= 1) + { + if (b & test) + { + if (!inFill) + { + pos = x; + inFill = true; + } + } + else + { + if (inFill) + { + blitter->blitH(pos, y, x - pos); + inFill = false; + } + } + x += 1; + } + left_mask = 0xFF; + } + + // final cleanup + if (inFill) + blitter->blitH(pos, y, x - pos); +} + +void SkBlitter::blitMask(const SkMask& mask, const SkRect16& clip) +{ + SkASSERT(mask.fBounds.contains(clip)); + + if (mask.fFormat == SkMask::kBW_Format) + { + int cx = clip.fLeft; + int cy = clip.fTop; + int maskLeft = mask.fBounds.fLeft; + int mask_rowBytes = mask.fRowBytes; + int height = clip.height(); + + const uint8_t* bits = mask.getAddr1(cx, cy); + + if (cx == maskLeft && clip.fRight == mask.fBounds.fRight) + { + while (--height >= 0) + { + bits_to_runs(this, cx, cy, bits, 0xFF, mask_rowBytes, 0xFF); + bits += mask_rowBytes; + cy += 1; + } + } + else + { + int left_edge = cx - maskLeft; + SkASSERT(left_edge >= 0); + int rite_edge = clip.fRight - maskLeft; + SkASSERT(rite_edge > left_edge); + + int left_mask = 0xFF >> (left_edge & 7); + int rite_mask = 0xFF << (8 - (rite_edge & 7)); + int full_runs = (rite_edge >> 3) - ((left_edge + 7) >> 3); + + // check for empty right mask, so we don't read off the end (or go slower than we need to) + if (rite_mask == 0) + { + SkASSERT(full_runs >= 0); + full_runs -= 1; + rite_mask = 0xFF; + } + if (left_mask == 0xFF) + full_runs -= 1; + + // back up manually so we can keep in sync with our byte-aligned src + // have cx reflect our actual starting x-coord + cx -= left_edge & 7; + + if (full_runs < 0) + { + SkASSERT((left_mask & rite_mask) != 0); + while (--height >= 0) + { + bits_to_runs(this, cx, cy, bits, left_mask, 1, rite_mask); + bits += mask_rowBytes; + cy += 1; + } + } + else + { + while (--height >= 0) + { + bits_to_runs(this, cx, cy, bits, left_mask, full_runs + 2, rite_mask); + bits += mask_rowBytes; + cy += 1; + } + } + } + } + else + { + int width = clip.width(); + SkAutoSTMalloc<64, int16_t> runStorage(width + 1); + int16_t* runs = runStorage.get(); + const uint8_t* aa = mask.getAddr(clip.fLeft, clip.fTop); + + sk_memset16((U16*)runs, 1, width); + runs[width] = 0; + + int height = clip.height(); + int y = clip.fTop; + while (--height >= 0) + { + this->blitAntiH(clip.fLeft, y, aa, runs); + aa += mask.fRowBytes; + y += 1; + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////// + +// this guy is not virtual, just a helper +void SkBlitter::blitMaskRegion(const SkMask& mask, const SkRegion& clip) +{ + if (clip.quickReject(mask.fBounds)) + return; + + SkRegion::Cliperator clipper(clip, mask.fBounds); + + if (!clipper.done()) + { + const SkRect16& cr = clipper.rect(); + do { + this->blitMask(mask, cr); + clipper.next(); + } while (!clipper.done()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////// + +static int compute_anti_width(const int16_t runs[]) +{ + int width = 0; + + for (;;) + { + int count = runs[0]; + + SkASSERT(count >= 0); + if (count == 0) + break; + width += count; + runs += count; + + SkASSERT(width < 20000); + } + return width; +} + +/////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////// + +void SkNullBlitter::blitH(int x, int y, int width) +{ +} + +void SkNullBlitter::blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) +{ +} + +void SkNullBlitter::blitV(int x, int y, int height, SkAlpha alpha) +{ +} + +void SkNullBlitter::blitRect(int x, int y, int width, int height) +{ +} + +/////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////// + +static inline bool y_in_rect(int y, const SkRect16& rect) +{ + return (unsigned)(y - rect.fTop) < (unsigned)rect.height(); +} + +static inline bool x_in_rect(int x, const SkRect16& rect) +{ + return (unsigned)(x - rect.fLeft) < (unsigned)rect.width(); +} + +void SkRectClipBlitter::blitH(int left, int y, int width) +{ + SkASSERT(width > 0); + + if (!y_in_rect(y, fClipRect)) + return; + + int right = left + width; + + if (left < fClipRect.fLeft) + left = fClipRect.fLeft; + if (right > fClipRect.fRight) + right = fClipRect.fRight; + + width = right - left; + if (width > 0) + fBlitter->blitH(left, y, width); +} + +void SkRectClipBlitter::blitAntiH(int left, int y, const SkAlpha aa[], const int16_t runs[]) +{ + if (!y_in_rect(y, fClipRect) || left >= fClipRect.fRight) + return; + + int x0 = left; + int x1 = left + compute_anti_width(runs); + + if (x1 <= fClipRect.fLeft) + return; + + SkASSERT(x0 < x1); + if (x0 < fClipRect.fLeft) + { + int dx = fClipRect.fLeft - x0; + SkAlphaRuns::BreakAt((int16_t*)runs, (U8*)aa, dx); + runs += dx; + aa += dx; + x0 = fClipRect.fLeft; + } + + SkASSERT(x0 < x1 && runs[x1 - x0] == 0); + if (x1 > fClipRect.fRight) + { + x1 = fClipRect.fRight; + SkAlphaRuns::BreakAt((int16_t*)runs, (U8*)aa, x1 - x0); + ((int16_t*)runs)[x1 - x0] = 0; + } + + SkASSERT(x0 < x1 && runs[x1 - x0] == 0); + SkASSERT(compute_anti_width(runs) == x1 - x0); + + fBlitter->blitAntiH(x0, y, aa, runs); +} + +void SkRectClipBlitter::blitV(int x, int y, int height, SkAlpha alpha) +{ + SkASSERT(height > 0); + + if (!x_in_rect(x, fClipRect)) + return; + + int y0 = y; + int y1 = y + height; + + if (y0 < fClipRect.fTop) + y0 = fClipRect.fTop; + if (y1 > fClipRect.fBottom) + y1 = fClipRect.fBottom; + + if (y0 < y1) + fBlitter->blitV(x, y0, y1 - y0, alpha); +} + +void SkRectClipBlitter::blitRect(int left, int y, int width, int height) +{ + SkRect16 r; + + r.set(left, y, left + width, y + height); + if (r.intersect(fClipRect)) + fBlitter->blitRect(r.fLeft, r.fTop, r.width(), r.height()); +} + +/////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////// + +void SkRgnClipBlitter::blitH(int x, int y, int width) +{ + SkRegion::Spanerator span(*fRgn, y, x, x + width); + int left, right; + + while (span.next(&left, &right)) + { + SkASSERT(left < right); + fBlitter->blitH(left, y, right - left); + } +} + +void SkRgnClipBlitter::blitAntiH(int x, int y, const SkAlpha aa[], const int16_t runs[]) +{ + int width = compute_anti_width(runs); + SkRegion::Spanerator span(*fRgn, y, x, x + width); + int left, right; + bool firstTime = true; + SkDEBUGCODE(const SkRect16& bounds = fRgn->getBounds();) + +//SkDebugf("rgnClip: x=%d y=%d: ", x, y); + + while (span.next(&left, &right)) + { + SkASSERT(left < right); + SkASSERT(left >= bounds.fLeft && right <= bounds.fRight); + + if (firstTime && x < left) + { +//SkDebugf("zap[%d %d] ", x, left); + SkAlphaRuns::Break((int16_t*)runs, (U8*)aa, 0, left - x); + ((U8*)aa)[0] = 0; // skip runs before the first left + ((int16_t*)runs)[0] = SkToS16(left - x); + } + firstTime = false; + + SkAlphaRuns::Break((int16_t*)runs, (U8*)aa, left - x, right - left); + ((U8*)aa)[right - x] = 0; // skip runs after right + ((int16_t*)runs)[right - x] = SkToS16(right - left); + +//SkDebugf("[%d %d] ", left, right); + } + ((int16_t*)runs)[right - x] = 0; + +//dump_runs(runs, aa); + + fBlitter->blitAntiH(x, y, aa, runs); +} + +void SkRgnClipBlitter::blitV(int x, int y, int height, SkAlpha alpha) +{ + SkRect16 bounds; + bounds.set(x, y, x + 1, y + height); + + SkRegion::Cliperator iter(*fRgn, bounds); + + while (!iter.done()) + { + const SkRect16& r = iter.rect(); + SkASSERT(bounds.contains(r)); + + fBlitter->blitV(x, r.fTop, r.height(), alpha); + iter.next(); + } +} + +void SkRgnClipBlitter::blitRect(int x, int y, int width, int height) +{ + SkRect16 bounds; + bounds.set(x, y, x + width, y + height); + + SkRegion::Cliperator iter(*fRgn, bounds); + + while (!iter.done()) + { + const SkRect16& r = iter.rect(); + SkASSERT(bounds.contains(r)); + + fBlitter->blitRect(r.fLeft, r.fTop, r.width(), r.height()); + iter.next(); + } +} + +/////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////// + +SkBlitter* SkBlitterClipper::apply(SkBlitter* blitter, const SkRegion* clip, const SkRect16* ir) +{ + if (clip) + { + const SkRect16& clipR = clip->getBounds(); + + if (clip->isEmpty() || ir && !SkRect16::Intersects(clipR, *ir)) + blitter = &fNullBlitter; + else if (clip->isRect()) + { + if (ir == nil || !clipR.contains(*ir)) + { + fRectBlitter.init(blitter, clipR); + blitter = &fRectBlitter; + } + } + else + { + fRgnBlitter.init(blitter, clip); + blitter = &fRgnBlitter; + } + } + return blitter; +} + +/////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////// + +#include "SkShader.h" +#include "SkColorPriv.h" + +class SkColorShader : public SkShader { +public: + virtual U32 getFlags() + { + // should I claim hasspan16 if my color isn't opaque? + return (SkGetPackedA32(fPMColor) == 255 ? kOpaqueAlpha_Flag : kConstAlpha_Flag) | kHasSpan16_Flag; + } + virtual bool setContext(const SkBitmap& device, const SkPaint& paint, const SkMatrix& matrix) + { + if (!this->INHERITED::setContext(device, paint, matrix)) + return false; + + SkColor c = paint.getColor(); + unsigned a = SkColorGetA(c); + unsigned r = SkColorGetR(c); + unsigned g = SkColorGetG(c); + unsigned b = SkColorGetB(c); + + if (a != 255) + { + a = SkAlpha255To256(a); + r = SkAlphaMul(r, a); + g = SkAlphaMul(g, a); + b = SkAlphaMul(b, a); + } + fPMColor = SkPackARGB32(a, r, g, b); + fColor16 = SkPixel32ToPixel16_ToU16(fPMColor); // only meaning full if a == 255 + return true; + } + virtual void shadeSpan(int x, int y, SkPMColor span[], int count) + { + sk_memset32(span, fPMColor, count); + } + virtual void shadeSpanOpaque16(int x, int y, U16 span[], int count) + { + SkASSERT(SkGetPackedA32(fPMColor) == 255); + sk_memset16(span, fColor16, count); + } +private: + SkPMColor fPMColor; + U16 fColor16; + + typedef SkShader INHERITED; +}; + +class Sk3DShader : public SkShader { +public: + Sk3DShader(SkShader* proxy) : fProxy(proxy) + { + proxy->safeRef(); + fMask = nil; + } + virtual ~Sk3DShader() + { + fProxy->safeUnref(); + } + void setMask(const SkMask* mask) { fMask = mask; } + + virtual bool setContext(const SkBitmap& device, const SkPaint& paint, const SkMatrix& matrix) + { + if (fProxy) + return fProxy->setContext(device, paint, matrix); + else + { + fPMColor = SkPreMultiplyColor(paint.getColor()); + return this->INHERITED::setContext(device, paint, matrix); + } + } + virtual void shadeSpan(int x, int y, SkPMColor span[], int count) + { + if (fProxy) + fProxy->shadeSpan(x, y, span, count); + + if (fMask == nil) + { + if (fProxy == nil) + sk_memset32(span, fPMColor, count); + return; + } + + SkASSERT(fMask->fBounds.contains(x, y)); + SkASSERT(fMask->fBounds.contains(x + count - 1, y)); + + size_t size = fMask->computeImageSize(); + const U8* alpha = fMask->getAddr(x, y); + const U8* mulp = alpha + size; + const U8* addp = mulp + size; + + if (fProxy) + { + for (int i = 0; i < count; i++) + { + if (alpha[i]) + { + U32 c = span[i]; + if (c) + { + unsigned a = SkGetPackedA32(c); + unsigned r = SkGetPackedR32(c); + unsigned g = SkGetPackedG32(c); + unsigned b = SkGetPackedB32(c); + + unsigned mul = SkAlpha255To256(mulp[i]); + unsigned add = addp[i]; + + r = SkFastMin32(SkAlphaMul(r, mul) + add, a); + g = SkFastMin32(SkAlphaMul(g, mul) + add, a); + b = SkFastMin32(SkAlphaMul(b, mul) + add, a); + + span[i] = SkPackARGB32(a, r, g, b); + } + } + else + span[i] = 0; + } + } + else // color + { + unsigned a = SkGetPackedA32(fPMColor); + unsigned r = SkGetPackedR32(fPMColor); + unsigned g = SkGetPackedG32(fPMColor); + unsigned b = SkGetPackedB32(fPMColor); + for (int i = 0; i < count; i++) + { + if (alpha[i]) + { + unsigned mul = SkAlpha255To256(mulp[i]); + unsigned add = addp[i]; + + span[i] = SkPackARGB32( a, + SkFastMin32(SkAlphaMul(r, mul) + add, a), + SkFastMin32(SkAlphaMul(g, mul) + add, a), + SkFastMin32(SkAlphaMul(b, mul) + add, a)); + } + else + span[i] = 0; + } + } + } +private: + SkShader* fProxy; + SkPMColor fPMColor; + const SkMask* fMask; + + typedef SkShader INHERITED; +}; + +class Sk3DBlitter : public SkBlitter { +public: + Sk3DBlitter(SkBlitter* proxy, Sk3DShader* shader, void (*killProc)(void*)) + : fProxy(proxy), f3DShader(shader), fKillProc(killProc) + { + shader->ref(); + } + virtual ~Sk3DBlitter() + { + f3DShader->unref(); + fKillProc(fProxy); + } + + virtual void blitH(int x, int y, int width) + { + fProxy->blitH(x, y, width); + } + virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) + { + fProxy->blitAntiH(x, y, antialias, runs); + } + virtual void blitV(int x, int y, int height, SkAlpha alpha) + { + fProxy->blitV(x, y, height, alpha); + } + virtual void blitRect(int x, int y, int width, int height) + { + fProxy->blitRect(x, y, width, height); + } + virtual void blitMask(const SkMask& mask, const SkRect16& clip) + { + if (mask.fFormat == SkMask::k3D_Format) + { + f3DShader->setMask(&mask); + + ((SkMask*)&mask)->fFormat = SkMask::kA8_Format; + fProxy->blitMask(mask, clip); + ((SkMask*)&mask)->fFormat = SkMask::k3D_Format; + + f3DShader->setMask(nil); + } + else + fProxy->blitMask(mask, clip); + } +private: + SkBlitter* fProxy; + Sk3DShader* f3DShader; + void (*fKillProc)(void*); +}; + +/////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////// + +#include "SkCoreBlitters.h" + +class SkAutoRestoreShader { +public: + SkAutoRestoreShader(const SkPaint& p) : fPaint((SkPaint*)&p) + { + fShader = fPaint->getShader(); + fShader->safeRef(); + } + ~SkAutoRestoreShader() + { + fPaint->setShader(fShader); + fShader->safeUnref(); + } +private: + SkPaint* fPaint; + SkShader* fShader; +}; + +class SkAutoCallProc { +public: + typedef void (*Proc)(void*); + SkAutoCallProc(void* obj, Proc proc) + : fObj(obj), fProc(proc) + { + } + ~SkAutoCallProc() + { + if (fObj && fProc) + fProc(fObj); + } + void* get() const { return fObj; } + void* detach() + { + void* obj = fObj; + fObj = nil; + return obj; + } +private: + void* fObj; + Proc fProc; +}; + +static void destroy_blitter(void* blitter) +{ + ((SkBlitter*)blitter)->~SkBlitter(); +} + +static void delete_blitter(void* blitter) +{ + SkDELETE((SkBlitter*)blitter); +} + +SkBlitter* SkBlitter::Choose(const SkBitmap& device, + const SkMatrix& matrix, + const SkPaint& paint, + void* storage, size_t storageSize) +{ + SkASSERT(storageSize == 0 || storage != nil); + + SkBlitter* blitter = nil; + SkAutoRestoreShader restore(paint); + SkShader* shader = paint.getShader(); + + Sk3DShader* shader3D = nil; + if (paint.getMaskFilter() != nil && paint.getMaskFilter()->getFormat() == SkMask::k3D_Format) + { + shader3D = SkNEW_ARGS(Sk3DShader, (shader)); + ((SkPaint*)&paint)->setShader(shader3D)->unref(); + shader = shader3D; + } + + SkXfermode* mode = paint.getXfermode(); + if (NULL == shader && (NULL != mode || paint.getColorFilter() != NULL)) + { + // xfermodes require shaders for our current set of blitters + shader = SkNEW(SkColorShader); + ((SkPaint*)&paint)->setShader(shader)->unref(); + } + + if (paint.getColorFilter() != NULL) + { + SkASSERT(shader); + shader = SkNEW_ARGS(SkFilterShader, (shader, paint.getColorFilter())); + ((SkPaint*)&paint)->setShader(shader)->unref(); + } + + if (shader) + { + if (!shader->setContext(device, paint, matrix)) + return SkNEW(SkNullBlitter); + } + + switch (device.getConfig()) { + case SkBitmap::kA1_Config: + SK_PLACEMENT_NEW_ARGS(blitter, SkA1_Blitter, storage, storageSize, (device, paint)); + break; + + case SkBitmap::kA8_Config: + if (shader) + SK_PLACEMENT_NEW_ARGS(blitter, SkA8_Shader_Blitter, storage, storageSize, (device, paint)); + else + SK_PLACEMENT_NEW_ARGS(blitter, SkA8_Blitter, storage, storageSize, (device, paint)); + break; + + case SkBitmap::kRGB_565_Config: + if (shader) + { + if (mode) + SK_PLACEMENT_NEW_ARGS(blitter, SkRGB16_Shader_Xfermode_Blitter, storage, storageSize, (device, paint)); + else + SK_PLACEMENT_NEW_ARGS(blitter, SkRGB16_Shader_Blitter, storage, storageSize, (device, paint)); + } + else if (paint.getColor() == SK_ColorBLACK) + SK_PLACEMENT_NEW_ARGS(blitter, SkRGB16_Black_Blitter, storage, storageSize, (device, paint)); + else + SK_PLACEMENT_NEW_ARGS(blitter, SkRGB16_Blitter, storage, storageSize, (device, paint)); + break; + + case SkBitmap::kARGB_8888_Config: + if (shader) + SK_PLACEMENT_NEW_ARGS(blitter, SkARGB32_Shader_Blitter, storage, storageSize, (device, paint)); + else if (paint.getColor() == SK_ColorBLACK) + SK_PLACEMENT_NEW_ARGS(blitter, SkARGB32_Black_Blitter, storage, storageSize, (device, paint)); + else + SK_PLACEMENT_NEW_ARGS(blitter, SkARGB32_Blitter, storage, storageSize, (device, paint)); + break; + + default: + SkASSERT(!"unsupported device config"); + SK_PLACEMENT_NEW(blitter, SkNullBlitter, storage, storageSize); + } + + if (shader3D) + { + void (*proc)(void*) = ((void*)storage == (void*)blitter) ? destroy_blitter : delete_blitter; + SkAutoCallProc tmp(blitter, proc); + + blitter = SkNEW_ARGS(Sk3DBlitter, (blitter, shader3D, proc)); + (void)tmp.detach(); + } + return blitter; +} + diff --git a/libs/graphics/sgl/SkBlitter.h b/libs/graphics/sgl/SkBlitter.h new file mode 100644 index 0000000000..2031efa093 --- /dev/null +++ b/libs/graphics/sgl/SkBlitter.h @@ -0,0 +1,102 @@ +#ifndef SkBlitter_DEFINED +#define SkBlitter_DEFINED + +#include "SkBitmap.h" +#include "SkMatrix.h" +#include "SkPaint.h" +#include "SkRefCnt.h" +#include "SkRegion.h" +#include "SkMask.h" + +class SkBlitter { +public: + virtual ~SkBlitter(); + virtual void blitH(int x, int y, int width); + virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const S16 runs[]); + virtual void blitV(int x, int y, int height, SkAlpha alpha); + virtual void blitRect(int x, int y, int width, int height); + virtual void blitMask(const SkMask&, const SkRect16& clip); + + // not virtual, just a helper + void blitMaskRegion(const SkMask& mask, const SkRegion& clip); + + static SkBlitter* Choose(const SkBitmap& device, + const SkMatrix& matrix, + const SkPaint& paint) + { + return Choose(device, matrix, paint, nil, 0); + } + + static SkBlitter* Choose(const SkBitmap& device, + const SkMatrix& matrix, + const SkPaint& paint, + void* storage, size_t storageSize); + + static SkBlitter* ChooseSprite(const SkBitmap& device, + const SkPaint&, + const SkBitmap& src, + int left, int top, + void* storage, size_t storageSize); + +private: +}; + +class SkNullBlitter : public SkBlitter { +public: + virtual void blitH(int x, int y, int width); + virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const S16 runs[]); + virtual void blitV(int x, int y, int height, SkAlpha alpha); + virtual void blitRect(int x, int y, int width, int height); +}; + +class SkRectClipBlitter : public SkBlitter { +public: + void init(SkBlitter* blitter, const SkRect16& clipRect) + { + SkASSERT(!clipRect.isEmpty()); + fBlitter = blitter; + fClipRect = clipRect; + } + + // overrides + virtual void blitH(int x, int y, int width); + virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const S16 runs[]); + virtual void blitV(int x, int y, int height, SkAlpha alpha); + virtual void blitRect(int x, int y, int width, int height); + +private: + SkBlitter* fBlitter; + SkRect16 fClipRect; +}; + +class SkRgnClipBlitter : public SkBlitter { +public: + void init(SkBlitter* blitter, const SkRegion* rgn) + { + SkASSERT(rgn && !rgn->isEmpty()); + fBlitter = blitter; + fRgn = rgn; + } + + // overrides + virtual void blitH(int x, int y, int width); + virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const S16 runs[]); + virtual void blitV(int x, int y, int height, SkAlpha alpha); + virtual void blitRect(int x, int y, int width, int height); + +private: + SkBlitter* fBlitter; + const SkRegion* fRgn; +}; + +class SkBlitterClipper { +public: + SkBlitter* apply(SkBlitter* blitter, const SkRegion* clip, const SkRect16* bounds = nil); + +private: + SkNullBlitter fNullBlitter; + SkRectClipBlitter fRectBlitter; + SkRgnClipBlitter fRgnBlitter; +}; + +#endif diff --git a/libs/graphics/sgl/SkBlitter_A1.cpp b/libs/graphics/sgl/SkBlitter_A1.cpp new file mode 100644 index 0000000000..38e0e4f096 --- /dev/null +++ b/libs/graphics/sgl/SkBlitter_A1.cpp @@ -0,0 +1,46 @@ +#include "SkCoreBlitters.h" + +SkA1_Blitter::SkA1_Blitter(const SkBitmap& device, const SkPaint& paint) + : fDevice(device) +{ + fSrcA = SkToU8(SkColorGetA(paint.getColor())); +} + +void SkA1_Blitter::blitH(int x, int y, int width) +{ + SkASSERT(x >= 0 && y >= 0 && (unsigned)(x + width) <= fDevice.width()); + + if (fSrcA <= 0x7F) + return; + + U8* dst = fDevice.getAddr1(x, y); + int right = x + width; + + int left_mask = 0xFF >> (x & 7); + int rite_mask = 0xFF << (8 - (right & 7)); + int full_runs = (right >> 3) - ((x + 7) >> 3); + + // check for empty right mask, so we don't read off the end (or go slower than we need to) + if (rite_mask == 0) + { + SkASSERT(full_runs >= 0); + full_runs -= 1; + rite_mask = 0xFF; + } + if (left_mask == 0xFF) + full_runs -= 1; + + if (full_runs < 0) + { + SkASSERT((left_mask & rite_mask) != 0); + *dst |= (left_mask & rite_mask); + } + else + { + *dst++ |= left_mask; + memset(dst, 0xFF, full_runs); + dst += full_runs; + *dst |= rite_mask; + } +} + diff --git a/libs/graphics/sgl/SkBlitter_A8.cpp b/libs/graphics/sgl/SkBlitter_A8.cpp new file mode 100644 index 0000000000..bb94050125 --- /dev/null +++ b/libs/graphics/sgl/SkBlitter_A8.cpp @@ -0,0 +1,365 @@ +#include "SkCoreBlitters.h" +#include "SkColorPriv.h" +#include "SkShader.h" +#include "SkXfermode.h" + +SkA8_Blitter::SkA8_Blitter(const SkBitmap& device, const SkPaint& paint) : fDevice(device) +{ + fSrcA = SkColorGetA(paint.getColor()); +} + +void SkA8_Blitter::blitH(int x, int y, int width) +{ + SkASSERT(x >= 0 && y >= 0 && (unsigned)(x + width) <= fDevice.width()); + + if (fSrcA == 0) + return; + + uint8_t* device = fDevice.getAddr8(x, y); + + if (fSrcA == 255) + { + memset(device, 0xFF, width); + } + else + { + unsigned scale = 256 - SkAlpha255To256(fSrcA); + unsigned srcA = fSrcA; + + for (int i = 0; i < width; i++) + { + device[i] = SkToU8(srcA + SkAlphaMul(device[i], scale)); + } + } +} + +void SkA8_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[], const S16 runs[]) +{ + if (fSrcA == 0) + return; + + uint8_t* device = fDevice.getAddr8(x, y); + unsigned srcA = fSrcA; + + for (;;) + { + int count = runs[0]; + SkASSERT(count >= 0); + if (count == 0) + return; + unsigned aa = antialias[0]; + + if (aa == 255 && srcA == 255) + memset(device, 0xFF, count); + else + { + unsigned sa = SkAlphaMul(srcA, SkAlpha255To256(aa)); + unsigned scale = 256 - sa; + + for (int i = 0; i < count; i++) + { + device[i] = SkToU8(sa + SkAlphaMul(device[i], scale)); + } + } + runs += count; + antialias += count; + device += count; + } +} + +///////////////////////////////////////////////////////////////////////////////////// + +#define solid_8_pixels(mask, dst) \ + do { \ + if (mask & 0x80) dst[0] = 0xFF; \ + if (mask & 0x40) dst[1] = 0xFF; \ + if (mask & 0x20) dst[2] = 0xFF; \ + if (mask & 0x10) dst[3] = 0xFF; \ + if (mask & 0x08) dst[4] = 0xFF; \ + if (mask & 0x04) dst[5] = 0xFF; \ + if (mask & 0x02) dst[6] = 0xFF; \ + if (mask & 0x01) dst[7] = 0xFF; \ + } while (0) + +#define SK_BLITBWMASK_NAME SkA8_BlitBW +#define SK_BLITBWMASK_ARGS +#define SK_BLITBWMASK_BLIT8(mask, dst) solid_8_pixels(mask, dst) +#define SK_BLITBWMASK_GETADDR getAddr8 +#define SK_BLITBWMASK_DEVTYPE uint8_t +#include "SkBlitBWMaskTemplate.h" + +static inline void blend_8_pixels(U8CPU bw, uint8_t dst[], U8CPU sa, unsigned dst_scale) +{ + if (bw & 0x80) dst[0] = SkToU8(sa + SkAlphaMul(dst[0], dst_scale)); + if (bw & 0x40) dst[1] = SkToU8(sa + SkAlphaMul(dst[1], dst_scale)); + if (bw & 0x20) dst[2] = SkToU8(sa + SkAlphaMul(dst[2], dst_scale)); + if (bw & 0x10) dst[3] = SkToU8(sa + SkAlphaMul(dst[3], dst_scale)); + if (bw & 0x08) dst[4] = SkToU8(sa + SkAlphaMul(dst[4], dst_scale)); + if (bw & 0x04) dst[5] = SkToU8(sa + SkAlphaMul(dst[5], dst_scale)); + if (bw & 0x02) dst[6] = SkToU8(sa + SkAlphaMul(dst[6], dst_scale)); + if (bw & 0x01) dst[7] = SkToU8(sa + SkAlphaMul(dst[7], dst_scale)); +} + +#define SK_BLITBWMASK_NAME SkA8_BlendBW +#define SK_BLITBWMASK_ARGS , U8CPU sa, unsigned dst_scale +#define SK_BLITBWMASK_BLIT8(mask, dst) blend_8_pixels(mask, dst, sa, dst_scale) +#define SK_BLITBWMASK_GETADDR getAddr8 +#define SK_BLITBWMASK_DEVTYPE uint8_t +#include "SkBlitBWMaskTemplate.h" + +void SkA8_Blitter::blitMask(const SkMask& mask, const SkRect16& clip) +{ + if (fSrcA == 0) + return; + + if (mask.fFormat == SkMask::kBW_Format) + { + if (fSrcA == 0xFF) + SkA8_BlitBW(fDevice, mask, clip); + else + SkA8_BlendBW(fDevice, mask, clip, fSrcA, SkAlpha255To256(255 - fSrcA)); + return; + } + + int x = clip.fLeft; + int y = clip.fTop; + int width = clip.width(); + int height = clip.height(); + uint8_t* device = fDevice.getAddr8(x, y); + const uint8_t* alpha = mask.getAddr(x, y); + unsigned srcA = fSrcA; + + while (--height >= 0) + { + for (int i = width - 1; i >= 0; --i) + { + unsigned sa; + // scale our src by the alpha value + { + int aa = alpha[i]; + if (aa == 0) + continue; + + if (aa == 255) + { + if (srcA == 255) + { + device[i] = 0xFF; + continue; + } + sa = srcA; + } + else + sa = SkAlphaMul(srcA, SkAlpha255To256(aa)); + } + + int scale = 256 - SkAlpha255To256(sa); + device[i] = SkToU8(sa + SkAlphaMul(device[i], scale)); + } + device += fDevice.rowBytes(); + alpha += mask.fRowBytes; + } +} + +/////////////////////////////////////////////////////////////////////////////////////// + +void SkA8_Blitter::blitV(int x, int y, int height, SkAlpha alpha) +{ + if (fSrcA == 0) + return; + + unsigned sa = SkAlphaMul(fSrcA, SkAlpha255To256(alpha)); + uint8_t* device = fDevice.getAddr8(x, y); + int rowBytes = fDevice.rowBytes(); + + if (sa == 0xFF) + { + for (int i = 0; i < height; i++) + { + *device = SkToU8(sa); + device += rowBytes; + } + } + else + { + unsigned scale = 256 - SkAlpha255To256(sa); + + for (int i = 0; i < height; i++) + { + *device = SkToU8(sa + SkAlphaMul(*device, scale)); + device += rowBytes; + } + } +} + +void SkA8_Blitter::blitRect(int x, int y, int width, int height) +{ + SkASSERT(x >= 0 && y >= 0 && (unsigned)(x + width) <= fDevice.width() && (unsigned)(y + height) <= fDevice.height()); + + if (fSrcA == 0) + return; + + uint8_t* device = fDevice.getAddr8(x, y); + unsigned srcA = fSrcA; + + if (srcA == 255) + { + while (--height >= 0) + { + memset(device, 0xFF, width); + device += fDevice.rowBytes(); + } + } + else + { + unsigned scale = 256 - SkAlpha255To256(srcA); + + while (--height >= 0) + { + for (int i = 0; i < width; i++) + { + device[i] = SkToU8(srcA + SkAlphaMul(device[i], scale)); + } + device += fDevice.rowBytes(); + } + } +} + +/////////////////////////////////////////////////////////////////////// + +SkA8_Shader_Blitter::SkA8_Shader_Blitter(const SkBitmap& device, const SkPaint& paint) : fDevice(device) +{ + fShader = paint.getShader(); + SkASSERT(fShader); + fShader->ref(); + + if ((fXfermode = paint.getXfermode()) != NULL) + { + fXfermode->ref(); + SkASSERT(fShader); + } + + int width = device.width(); + fBuffer = (SkPMColor*)sk_malloc_throw(sizeof(SkPMColor) * (width + (SkAlign4(width) >> 2))); + fAAExpand = (uint8_t*)(fBuffer + width); +} + +SkA8_Shader_Blitter::~SkA8_Shader_Blitter() +{ + fXfermode->safeUnref(); + fShader->unref(); + sk_free(fBuffer); +} + +void SkA8_Shader_Blitter::blitH(int x, int y, int width) +{ + SkASSERT(x >= 0 && y >= 0 && (unsigned)(x + width) <= fDevice.width()); + + uint8_t* device = fDevice.getAddr8(x, y); + + if ((fShader->getFlags() & SkShader::kOpaqueAlpha_Flag) && fXfermode == NULL) + { + memset(device, 0xFF, width); + } + else + { + SkPMColor* span = fBuffer; + + fShader->shadeSpan(x, y, span, width); + if (fXfermode) + fXfermode->xferA8(device, span, width, NULL); + else + { + for (int i = width - 1; i >= 0; --i) + { + unsigned srcA = SkGetPackedA32(span[i]); + unsigned scale = 256 - SkAlpha255To256(srcA); + + device[i] = SkToU8(srcA + SkAlphaMul(device[i], scale)); + } + } + } +} + +static inline uint8_t aa_blend8(U32 src, U8CPU da, int aa) +{ + SkASSERT((unsigned)aa <= 255); + + int src_scale = SkAlpha255To256(aa); + int sa = SkGetPackedA32(src); + int dst_scale = 256 - SkAlphaMul(sa, src_scale); + + return SkToU8((sa * src_scale + da * dst_scale) >> 8); +} + +void SkA8_Shader_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[], const S16 runs[]) +{ + SkASSERT(x >= 0 && y >= 0 && x < (int)fDevice.width() && y < (int)fDevice.height()); + + SkShader* shader = fShader; + SkXfermode* mode = fXfermode; + uint8_t* aaExpand = fAAExpand; + SkPMColor* span = fBuffer; + uint8_t* device = fDevice.getAddr8(x, y); + int opaque = fShader->getFlags() & SkShader::kOpaqueAlpha_Flag; + + for (;;) + { + int count = *runs; + if (count == 0) + break; + int aa = *antialias; + if (aa) + { + if (opaque && aa == 255 && mode == NULL) + memset(device, 0xFF, count); + else + { + shader->shadeSpan(x, y, span, count); + if (mode) + { + memset(aaExpand, aa, count); + mode->xferA8(device, span, count, aaExpand); + } + else + { + for (int i = count - 1; i >= 0; --i) + device[i] = aa_blend8(span[i], device[i], aa); + } + } + } + device += count; + runs += count; + antialias += count; + x += count; + } +} + +void SkA8_Shader_Blitter::blitMask(const SkMask& mask, const SkRect16& clip) +{ + if (mask.fFormat == SkMask::kBW_Format) + { + this->INHERITED::blitMask(mask, clip); + return; + } + + int x = clip.fLeft; + int y = clip.fTop; + int width = clip.width(); + int height = clip.height(); + uint8_t* device = fDevice.getAddr8(x, y); + const uint8_t* alpha = mask.getAddr(x, y); + + SkPMColor* span = fBuffer; + + while (--height >= 0) + { + fShader->shadeSpan(x, y, span, width); + fXfermode->xferA8(device, span, width, alpha); + + y += 1; + device += fDevice.rowBytes(); + alpha += mask.fRowBytes; + } +} + diff --git a/libs/graphics/sgl/SkBlitter_ARGB32.cpp b/libs/graphics/sgl/SkBlitter_ARGB32.cpp new file mode 100644 index 0000000000..66ff505417 --- /dev/null +++ b/libs/graphics/sgl/SkBlitter_ARGB32.cpp @@ -0,0 +1,465 @@ +#include "SkCoreBlitters.h" +#include "SkColorPriv.h" +#include "SkShader.h" +#include "SkUtils.h" +#include "SkXfermode.h" + +SkARGB32_Blitter::SkARGB32_Blitter(const SkBitmap& device, const SkPaint& paint) : fDevice(device) +{ + uint32_t color = paint.getColor(); + + fSrcA = SkColorGetA(color); + unsigned scale = SkAlpha255To256(fSrcA); + fSrcR = SkAlphaMul(SkColorGetR(color), scale); + fSrcG = SkAlphaMul(SkColorGetG(color), scale); + fSrcB = SkAlphaMul(SkColorGetB(color), scale); + + fPMColor = SkPackARGB32(fSrcA, fSrcR, fSrcG, fSrcB); +} + +#if defined _WIN32 && _MSC_VER >= 1300 // disable warning : local variable used without having been initialized +#pragma warning ( push ) +#pragma warning ( disable : 4701 ) +#endif + +void SkARGB32_Blitter::blitH(int x, int y, int width) +{ + SkASSERT(x >= 0 && y >= 0 && (unsigned)(x + width) <= fDevice.width()); + + if (fSrcA == 0) + return; + + uint32_t* device = fDevice.getAddr32(x, y); + + if (fSrcA == 255) + { + sk_memset32(device, fPMColor, width); + } + else + { + uint32_t color = fPMColor; + unsigned dst_scale = SkAlpha255To256(255 - fSrcA); + uint32_t prevDst = ~device[0]; // so we always fail the test the first time + uint32_t result SK_INIT_TO_AVOID_WARNING; + + for (int i = 0; i < width; i++) + { + uint32_t currDst = device[i]; + if (currDst != prevDst) + { + result = color + SkAlphaMulQ(currDst, dst_scale); + prevDst = currDst; + } + device[i] = result; + } + } +} + +void SkARGB32_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[], const S16 runs[]) +{ + if (fSrcA == 0) + return; + + uint32_t color = fPMColor; + uint32_t* device = fDevice.getAddr32(x, y); + unsigned opaqueMask = fSrcA; // if fSrcA is 0xFF, then we will catch the fast opaque case + + for (;;) + { + int count = runs[0]; + SkASSERT(count >= 0); + if (count == 0) + return; + + unsigned aa = antialias[0]; + if (aa) + { + if ((opaqueMask & aa) == 255) + sk_memset32(device, color, count); + else + { + uint32_t sc = SkAlphaMulQ(color, aa); + unsigned dst_scale = 255 - SkGetPackedA32(sc); + + for (int i = 0; i < count; i++) + device[i] = sc + SkAlphaMulQ(device[i], dst_scale); + } + } + runs += count; + antialias += count; + device += count; + } +} + +////////////////////////////////////////////////////////////////////////////////////// + +#define solid_8_pixels(mask, dst, color) \ + do { \ + if (mask & 0x80) dst[0] = color; \ + if (mask & 0x40) dst[1] = color; \ + if (mask & 0x20) dst[2] = color; \ + if (mask & 0x10) dst[3] = color; \ + if (mask & 0x08) dst[4] = color; \ + if (mask & 0x04) dst[5] = color; \ + if (mask & 0x02) dst[6] = color; \ + if (mask & 0x01) dst[7] = color; \ + } while (0) + +#define SK_BLITBWMASK_NAME SkARGB32_BlitBW +#define SK_BLITBWMASK_ARGS , SkPMColor color +#define SK_BLITBWMASK_BLIT8(mask, dst) solid_8_pixels(mask, dst, color) +#define SK_BLITBWMASK_GETADDR getAddr32 +#define SK_BLITBWMASK_DEVTYPE uint32_t +#include "SkBlitBWMaskTemplate.h" + +#define blend_8_pixels(mask, dst, sc, dst_scale) \ + do { \ + if (mask & 0x80) { dst[0] = sc + SkAlphaMulQ(dst[0], dst_scale); } \ + if (mask & 0x40) { dst[1] = sc + SkAlphaMulQ(dst[1], dst_scale); } \ + if (mask & 0x20) { dst[2] = sc + SkAlphaMulQ(dst[2], dst_scale); } \ + if (mask & 0x10) { dst[3] = sc + SkAlphaMulQ(dst[3], dst_scale); } \ + if (mask & 0x08) { dst[4] = sc + SkAlphaMulQ(dst[4], dst_scale); } \ + if (mask & 0x04) { dst[5] = sc + SkAlphaMulQ(dst[5], dst_scale); } \ + if (mask & 0x02) { dst[6] = sc + SkAlphaMulQ(dst[6], dst_scale); } \ + if (mask & 0x01) { dst[7] = sc + SkAlphaMulQ(dst[7], dst_scale); } \ + } while (0) + +#define SK_BLITBWMASK_NAME SkARGB32_BlendBW +#define SK_BLITBWMASK_ARGS , uint32_t sc, unsigned dst_scale +#define SK_BLITBWMASK_BLIT8(mask, dst) blend_8_pixels(mask, dst, sc, dst_scale) +#define SK_BLITBWMASK_GETADDR getAddr32 +#define SK_BLITBWMASK_DEVTYPE uint32_t +#include "SkBlitBWMaskTemplate.h" + +void SkARGB32_Blitter::blitMask(const SkMask& mask, const SkRect16& clip) +{ + SkASSERT(mask.fBounds.contains(clip)); + + if (fSrcA == 0) + return; + + if (mask.fFormat == SkMask::kBW_Format) + { + if (fSrcA == 0xFF) + SkARGB32_BlitBW(fDevice, mask, clip, fPMColor); + else + SkARGB32_BlendBW(fDevice, mask, clip, fPMColor, SkAlpha255To256(255 - fSrcA)); + return; + } + + int x = clip.fLeft; + int y = clip.fTop; + int width = clip.width(); + int height = clip.height(); + + uint32_t* device = fDevice.getAddr32(x, y); + const uint8_t* alpha = mask.getAddr(x, y); + uint32_t srcColor = fPMColor; + + while (--height >= 0) + { + for (int i = width - 1; i >= 0; --i) + { + uint32_t color = srcColor; + + // scale our src by the alpha value + { + int aa = alpha[i]; + if (aa == 0) + continue; + + if (aa == 255) + { + if (fSrcA == 255) + { + device[i] = color; + continue; + } + } + else + color = SkAlphaMulQ(color, SkAlpha255To256(aa)); + } + device[i] = SkPMSrcOver(color, device[i]); + } + device = (uint32_t*)((char*)device + fDevice.rowBytes()); + alpha += mask.fRowBytes; + } +} + +////////////////////////////////////////////////////////////////////////////////////// + +void SkARGB32_Blitter::blitV(int x, int y, int height, SkAlpha alpha) +{ + if (alpha == 0 || fSrcA == 0) + return; + + uint32_t* device = fDevice.getAddr32(x, y); + uint32_t color = fPMColor; + + if (alpha != 255) + color = SkAlphaMulQ(color, SkAlpha255To256(alpha)); + + unsigned dst_scale = 255 - SkGetPackedA32(color); + uint32_t prevDst = ~device[0]; + uint32_t result SK_INIT_TO_AVOID_WARNING; + uint32_t rowBytes = fDevice.rowBytes(); + + while (--height >= 0) + { + uint32_t dst = device[0]; + if (dst != prevDst) + { + result = color + SkAlphaMulQ(dst, dst_scale); + prevDst = dst; + } + device[0] = result; + device = (uint32_t*)((char*)device + rowBytes); + } +} + +void SkARGB32_Blitter::blitRect(int x, int y, int width, int height) +{ + SkASSERT(x >= 0 && y >= 0 && (unsigned)(x + width) <= fDevice.width() && (unsigned)(y + height) <= fDevice.height()); + + if (fSrcA == 0) + return; + + uint32_t* device = fDevice.getAddr32(x, y); + uint32_t color = fPMColor; + + if (fSrcA == 255) + { + while (--height >= 0) + { + sk_memset32(device, color, width); + device = (uint32_t*)((char*)device + fDevice.rowBytes()); + } + } + else + { + unsigned dst_scale = SkAlpha255To256(255 - fSrcA); + + while (--height >= 0) + { + uint32_t prevDst = ~device[0]; + uint32_t result SK_INIT_TO_AVOID_WARNING; + + for (int i = 0; i < width; i++) + { + uint32_t dst = device[i]; + if (dst != prevDst) + { + result = color + SkAlphaMulQ(dst, dst_scale); + prevDst = dst; + } + device[i] = result; + } + device = (uint32_t*)((char*)device + fDevice.rowBytes()); + } + } +} + +#if defined _WIN32 && _MSC_VER >= 1300 +#pragma warning ( pop ) +#endif + +/////////////////////////////////////////////////////////////////////// + +void SkARGB32_Black_Blitter::blitMask(const SkMask& mask, const SkRect16& clip) +{ + SkASSERT(mask.fBounds.contains(clip)); + + SkPMColor black = (SkPMColor)(SK_A32_MASK << SK_A32_SHIFT); + + if (mask.fFormat == SkMask::kBW_Format) + { + SkARGB32_BlitBW(fDevice, mask, clip, black); + } + else + { + uint32_t* device = fDevice.getAddr32(clip.fLeft, clip.fTop); + const U8* alpha = mask.getAddr(clip.fLeft, clip.fTop); + unsigned width = clip.width(); + unsigned height = clip.height(); + unsigned deviceRB = fDevice.rowBytes() - (width << 2); + unsigned maskRB = mask.fRowBytes - width; + + SkASSERT((int)height > 0); + SkASSERT((int)width > 0); + SkASSERT((int)deviceRB >= 0); + SkASSERT((int)maskRB >= 0); + + do { + unsigned w = width; + do { + unsigned aa = *alpha++; + if (aa) + { + if (aa == 255) + *device = black; + else + *device = (aa << SK_A32_SHIFT) + SkAlphaMulQ(*device, SkAlpha255To256(255 - aa)); + } + device += 1; + } while (--w != 0); + device = (uint32_t*)((char*)device + deviceRB); + alpha += maskRB; + } while (--height != 0); + } +} + +////////////////////////////////////////////////////////////////////////////////////////// + +SkARGB32_Shader_Blitter::SkARGB32_Shader_Blitter(const SkBitmap& device, const SkPaint& paint) : fDevice(device) +{ + fBuffer = (SkPMColor*)sk_malloc_throw(device.width() * (sizeof(SkPMColor))); + + fShader = paint.getShader(); + SkASSERT(fShader); + fShader->ref(); + + (fXfermode = paint.getXfermode())->safeRef(); +} + +SkARGB32_Shader_Blitter::~SkARGB32_Shader_Blitter() +{ + fXfermode->safeUnref(); + fShader->unref(); + sk_free(fBuffer); +} + +void SkARGB32_Shader_Blitter::blitH(int x, int y, int width) +{ + SkASSERT(x >= 0 && y >= 0 && (unsigned)(x + width) <= fDevice.width()); + + uint32_t* device = fDevice.getAddr32(x, y); + + if (fXfermode == NULL && (fShader->getFlags() & SkShader::kOpaqueAlpha_Flag)) + { + fShader->shadeSpan(x, y, device, width); + } + else + { + SkPMColor* span = fBuffer; + fShader->shadeSpan(x, y, span, width); + if (fXfermode) + fXfermode->xfer32(device, span, width, NULL); + else + { + for (int i = 0; i < width; i++) + { + uint32_t src = span[i]; + if (src) + { + unsigned srcA = SkGetPackedA32(src); + if (srcA != 0xFF) + src += SkAlphaMulQ(device[i], SkAlpha255To256(255 - srcA)); + device[i] = src; + } + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +void SkARGB32_Shader_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[], const S16 runs[]) +{ + SkASSERT(x >= 0 && y >= 0 && x < (int)fDevice.width() && y < (int)fDevice.height()); + + SkPMColor* span = fBuffer; + uint32_t* device = fDevice.getAddr32(x, y); + SkShader* shader = fShader; + + if (fXfermode) + { + for (;;) + { + SkXfermode* xfer = fXfermode; + + int count = *runs; + if (count == 0) + break; + int aa = *antialias; + if (aa) + { + shader->shadeSpan(x, y, span, count); + if (aa == 255) + xfer->xfer32(device, span, count, NULL); + else + { + // count is almost always 1 + for (int i = count - 1; i >= 0; --i) + xfer->xfer32(&device[i], &span[i], count, antialias); + } + } + device += count; + runs += count; + antialias += count; + x += count; + } + } + else if (fShader->getFlags() & SkShader::kOpaqueAlpha_Flag) + { + for (;;) + { + int count = *runs; + if (count == 0) + break; + int aa = *antialias; + if (aa) + { + if (aa == 255) // cool, have the shader draw right into the device + shader->shadeSpan(x, y, device, count); + else + { + shader->shadeSpan(x, y, span, count); + for (int i = count - 1; i >= 0; --i) + { + if (span[i]) + device[i] = SkBlendARGB32(span[i], device[i], aa); + } + } + } + device += count; + runs += count; + antialias += count; + x += count; + } + } + else // no xfermode but we are not opaque + { + for (;;) + { + int count = *runs; + if (count == 0) + break; + int aa = *antialias; + if (aa) + { + fShader->shadeSpan(x, y, span, count); + if (aa == 255) + { + for (int i = count - 1; i >= 0; --i) + { + if (span[i]) + device[i] = SkPMSrcOver(span[i], device[i]); + } + } + else + { + for (int i = count - 1; i >= 0; --i) + { + if (span[i]) + device[i] = SkBlendARGB32(span[i], device[i], aa); + } + } + } + device += count; + runs += count; + antialias += count; + x += count; + } + } +} + diff --git a/libs/graphics/sgl/SkBlitter_RGB16.cpp b/libs/graphics/sgl/SkBlitter_RGB16.cpp new file mode 100644 index 0000000000..56b2b419f2 --- /dev/null +++ b/libs/graphics/sgl/SkBlitter_RGB16.cpp @@ -0,0 +1,674 @@ +#include "SkCoreBlitters.h" +#include "SkColorPriv.h" +#include "SkShader.h" +#include "SkUtils.h" +#include "SkXfermode.h" + +#ifdef SK_DEBUG + static unsigned RGB16Add(U16CPU a, U16CPU b) + { + SkASSERT(SkGetPackedR16(a) + SkGetPackedR16(b) <= SK_R16_MASK); + SkASSERT(SkGetPackedG16(a) + SkGetPackedG16(b) <= SK_G16_MASK); + SkASSERT(SkGetPackedB16(a) + SkGetPackedB16(b) <= SK_B16_MASK); + + return a + b; + } +#else + #define RGB16Add(a, b) (a + b) +#endif + +#if 1 +#define black_8_pixels(mask, dst) \ + do { \ + if (mask & 0x80) dst[0] = 0; \ + if (mask & 0x40) dst[1] = 0; \ + if (mask & 0x20) dst[2] = 0; \ + if (mask & 0x10) dst[3] = 0; \ + if (mask & 0x08) dst[4] = 0; \ + if (mask & 0x04) dst[5] = 0; \ + if (mask & 0x02) dst[6] = 0; \ + if (mask & 0x01) dst[7] = 0; \ + } while (0) +#else +static inline black_8_pixels(U8CPU mask, U16 dst[]) +{ + if (mask & 0x80) dst[0] = 0; + if (mask & 0x40) dst[1] = 0; + if (mask & 0x20) dst[2] = 0; + if (mask & 0x10) dst[3] = 0; + if (mask & 0x08) dst[4] = 0; + if (mask & 0x04) dst[5] = 0; + if (mask & 0x02) dst[6] = 0; + if (mask & 0x01) dst[7] = 0; +} +#endif + +#define SK_BLITBWMASK_NAME SkRGB16_Black_BlitBW +#define SK_BLITBWMASK_ARGS +#define SK_BLITBWMASK_BLIT8(mask, dst) black_8_pixels(mask, dst) +#define SK_BLITBWMASK_GETADDR getAddr16 +#define SK_BLITBWMASK_DEVTYPE U16 +#include "SkBlitBWMaskTemplate.h" + +void SkRGB16_Black_Blitter::blitMask(const SkMask& mask, const SkRect16& clip) +{ + if (mask.fFormat == SkMask::kBW_Format) + { + SkRGB16_Black_BlitBW(fDevice, mask, clip); + } + else + { + U16* device = fDevice.getAddr16(clip.fLeft, clip.fTop); + const U8* alpha = mask.getAddr(clip.fLeft, clip.fTop); + unsigned width = clip.width(); + unsigned height = clip.height(); + unsigned deviceRB = fDevice.rowBytes() - (width << 1); + unsigned maskRB = mask.fRowBytes - width; + + SkASSERT((int)height > 0); + SkASSERT((int)width > 0); + SkASSERT((int)deviceRB >= 0); + SkASSERT((int)maskRB >= 0); + + do { + unsigned w = width; + do { + unsigned aa = *alpha++; + if (aa) + { + if (aa == 255) + *device = 0; + else + *device = SkToU16(SkAlphaMulRGB16(*device, SkAlpha255To256(255 - aa))); + } + device += 1; + } while (--w != 0); + device = (U16*)((char*)device + deviceRB); + alpha += maskRB; + } while (--height != 0); + } +} + +////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////// + +SkRGB16_Blitter::SkRGB16_Blitter(const SkBitmap& device, const SkPaint& paint) : fDevice(device) +{ + U32 color = paint.getColor(); + + fScale = SkAlpha255To256(SkColorGetA(color)); + + fRawColor16 = SkPackRGB16( SkColorGetR(color) >> (8 - SK_R16_BITS), + SkColorGetG(color) >> (8 - SK_G16_BITS), + SkColorGetB(color) >> (8 - SK_B16_BITS)); + + fColor16 = SkPackRGB16( SkAlphaMul(SkColorGetR(color), fScale) >> (8 - SK_R16_BITS), + SkAlphaMul(SkColorGetG(color), fScale) >> (8 - SK_G16_BITS), + SkAlphaMul(SkColorGetB(color), fScale) >> (8 - SK_B16_BITS)); +} + +void SkRGB16_Blitter::blitH(int x, int y, int width) +{ + SkASSERT(width > 0); + SkASSERT(x >= 0 && y >= 0 && (unsigned)(x + width) <= fDevice.width()); + + if (fScale == 0) + return; + + U16* device = fDevice.getAddr16(x, y); + unsigned srcColor = fColor16; + + if (fScale == 256) + { + sk_memset16(device, srcColor, width); + } + else + { + unsigned scale = 256 - fScale; + do { + *device = (U16)RGB16Add(srcColor, SkAlphaMulRGB16(*device, scale)); + device += 1; + } while (--width != 0); + } +} + +void SkRGB16_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[], const S16 runs[]) +{ + if (fScale == 0) + return; + + U16* device = fDevice.getAddr16(x, y); + U16 srcColor = fColor16; + unsigned scale = fScale; + + if (scale == 256) + { + for (;;) + { + int count = runs[0]; + SkASSERT(count >= 0); + if (count == 0) + return; + runs += count; + + unsigned aa = antialias[0]; + antialias += count; + if (aa) + { + if (aa == 255) + { + sk_memset16(device, srcColor, count); + } + else + { + unsigned src = SkAlphaMulRGB16(srcColor, SkAlpha255To256(aa)); + unsigned dst_scale = SkAlpha255To256(255 - aa); + do { + *device = (U16)RGB16Add(src, SkAlphaMulRGB16(*device, dst_scale)); + device += 1; + } while (--count != 0); + continue; + } + } + device += count; + } + } + else + { + for (;;) + { + int count = runs[0]; + SkASSERT(count >= 0); + if (count == 0) + return; + runs += count; + + unsigned aa = antialias[0]; + antialias += count; + if (aa) + { + unsigned src = SkAlphaMulRGB16(srcColor, SkAlpha255To256(aa)); + unsigned dst_scale = SkAlpha255To256(255 - SkAlphaMul(aa, scale)); + do { + *device = (U16)RGB16Add(src, SkAlphaMulRGB16(*device, dst_scale)); + device += 1; + } while (--count != 0); + continue; + } + device += count; + } + } +} + +////////////////////////////////////////////////////////////////////////////////////// + +#define solid_8_pixels(mask, dst, color) \ + do { \ + if (mask & 0x80) dst[0] = color; \ + if (mask & 0x40) dst[1] = color; \ + if (mask & 0x20) dst[2] = color; \ + if (mask & 0x10) dst[3] = color; \ + if (mask & 0x08) dst[4] = color; \ + if (mask & 0x04) dst[5] = color; \ + if (mask & 0x02) dst[6] = color; \ + if (mask & 0x01) dst[7] = color; \ + } while (0) + +#define SK_BLITBWMASK_NAME SkRGB16_BlitBW +#define SK_BLITBWMASK_ARGS , U16 color +#define SK_BLITBWMASK_BLIT8(mask, dst) solid_8_pixels(mask, dst, color) +#define SK_BLITBWMASK_GETADDR getAddr16 +#define SK_BLITBWMASK_DEVTYPE U16 +#include "SkBlitBWMaskTemplate.h" + +static inline void blend_8_pixels(U8CPU bw, U16 dst[], unsigned dst_scale, U16CPU srcColor) +{ + if (bw & 0x80) dst[0] = SkToU16(srcColor + SkAlphaMulRGB16(dst[0], dst_scale)); + if (bw & 0x40) dst[1] = SkToU16(srcColor + SkAlphaMulRGB16(dst[1], dst_scale)); + if (bw & 0x20) dst[2] = SkToU16(srcColor + SkAlphaMulRGB16(dst[2], dst_scale)); + if (bw & 0x10) dst[3] = SkToU16(srcColor + SkAlphaMulRGB16(dst[3], dst_scale)); + if (bw & 0x08) dst[4] = SkToU16(srcColor + SkAlphaMulRGB16(dst[4], dst_scale)); + if (bw & 0x04) dst[5] = SkToU16(srcColor + SkAlphaMulRGB16(dst[5], dst_scale)); + if (bw & 0x02) dst[6] = SkToU16(srcColor + SkAlphaMulRGB16(dst[6], dst_scale)); + if (bw & 0x01) dst[7] = SkToU16(srcColor + SkAlphaMulRGB16(dst[7], dst_scale)); +} + +#define SK_BLITBWMASK_NAME SkRGB16_BlendBW +#define SK_BLITBWMASK_ARGS , unsigned dst_scale, U16CPU src_color +#define SK_BLITBWMASK_BLIT8(mask, dst) blend_8_pixels(mask, dst, dst_scale, src_color) +#define SK_BLITBWMASK_GETADDR getAddr16 +#define SK_BLITBWMASK_DEVTYPE U16 +#include "SkBlitBWMaskTemplate.h" + +void SkRGB16_Blitter::blitMask(const SkMask& mask, const SkRect16& clip) +{ + if (fScale == 0) + return; + + if (mask.fFormat == SkMask::kBW_Format) + { + if (fScale == 256) + SkRGB16_BlitBW(fDevice, mask, clip, fColor16); + else + SkRGB16_BlendBW(fDevice, mask, clip, 256 - fScale, fColor16); + return; + } + + U16* device = fDevice.getAddr16(clip.fLeft, clip.fTop); + const U8* alpha = mask.getAddr(clip.fLeft, clip.fTop); + int width = clip.width(); + int height = clip.height(); + unsigned maskRB = mask.fRowBytes; + U16 color16 = fRawColor16; + unsigned scale = fScale; + unsigned deviceRB = fDevice.rowBytes(); + + if (scale == 256) + { + while (--height >= 0) + { + for (int i = width - 1; i >= 0; --i) + { + unsigned aa = alpha[i]; + if (aa) + { + if (aa == 255) + device[i] = color16; + else + { + unsigned src_scale = SkAlpha255To256(aa); + unsigned dst_scale = SkAlpha255To256(255 - aa); + device[i] = (U16)RGB16Add(SkAlphaMulRGB16(color16, src_scale), SkAlphaMulRGB16(device[i], dst_scale)); + } + } + } + device = (U16*)((char*)device + deviceRB); + alpha += maskRB; + } + } + else // scale < 256 + { + while (--height >= 0) + { + for (int i = width - 1; i >= 0; --i) + { + unsigned aa = alpha[i]; + if (aa) + { + aa = SkAlphaMul(aa, scale); + unsigned src_scale = SkAlpha255To256(aa); + unsigned dst_scale = SkAlpha255To256(255 - aa); + device[i] = (U16)RGB16Add(SkAlphaMulRGB16(color16, src_scale), SkAlphaMulRGB16(device[i], dst_scale)); + } + } + device = (U16*)((char*)device + deviceRB); + alpha += maskRB; + } + } +} + +void SkRGB16_Blitter::blitV(int x, int y, int height, SkAlpha alpha) +{ + if (fScale == 0) + return; + + U16* device = fDevice.getAddr16(x, y); + U16 color16 = fColor16; + unsigned deviceRB = fDevice.rowBytes(); + + if (alpha + fScale == (255 + 256)) + { + do { + device[0] = color16; + device = (U16*)((char*)device + deviceRB); + } while (--height != 0); + } + else + { + unsigned scale = fScale; + + if (alpha < 255) + { + scale = SkAlphaMul(alpha, scale); + color16 = SkToU16(SkAlphaMulRGB16(fRawColor16, scale)); + } + scale = 256 - scale; + do { + *device = (U16)RGB16Add(color16, SkAlphaMulRGB16(device[0], scale)); + device = (U16*)((char*)device + deviceRB); + } while (--height != 0); + } +} + +void SkRGB16_Blitter::blitRect(int x, int y, int width, int height) +{ + SkASSERT(x >= 0 && y >= 0 && (unsigned)(x + width) <= fDevice.width() && (unsigned)(y + height) <= fDevice.height()); + + if (fScale == 0) + return; + + U16* device = fDevice.getAddr16(x, y); + unsigned deviceRB = fDevice.rowBytes(); + U16 color16 = fColor16; + + if (fScale == 256) + { + while (--height >= 0) + { + sk_memset16(device, color16, width); + device = (U16*)((char*)device + deviceRB); + } + } + else + { + unsigned dst_scale = 256 - fScale; // apply it to the dst + + while (--height >= 0) + { + for (int i = width - 1; i >= 0; --i) + device[i] = SkToU16(color16 + SkAlphaMulRGB16(device[i], dst_scale)); + device = (U16*)((char*)device + deviceRB); + } + } +} + +/////////////////////////////////////////////////////////////////////// + +#define BLEND_32_TO_16(srcA, src, dst) \ + SkToU16(SkPixel32ToPixel16(src) + SkAlphaMulRGB16(dst, 256 - SkAlpha255To256(srcA))) + +SkRGB16_Shader_Blitter::SkRGB16_Shader_Blitter(const SkBitmap& device, const SkPaint& paint) : fDevice(device) +{ + SkASSERT(paint.getXfermode() == NULL); + + fShader = paint.getShader(); + SkASSERT(fShader); + fShader->ref(); + + SkAutoUnref autoUnref(fShader); + fBuffer = (SkPMColor*)sk_malloc_throw(device.width() * sizeof(SkPMColor)); + (void)autoUnref.detach(); +} + +SkRGB16_Shader_Blitter::~SkRGB16_Shader_Blitter() +{ + fShader->unref(); + sk_free(fBuffer); +} + +void SkRGB16_Shader_Blitter::blitH(int x, int y, int width) +{ + SkASSERT(x >= 0 && y >= 0 && (unsigned)(x + width) <= fDevice.width()); + + U16* device = fDevice.getAddr16(x, y); + U32 flags = fShader->getFlags(); + + if (SkShader::CanCallShadeSpanOpaque16(flags)) + { + fShader->shadeSpanOpaque16(x, y, device, width); + return; + } + + // If we get here, we know we need the 32bit answer from the shader + + SkPMColor* span = fBuffer; + + fShader->shadeSpan(x, y, span, width); + if (flags & SkShader::kOpaqueAlpha_Flag) + { + for (int i = width - 1; i >= 0; --i) + device[i] = SkPixel32ToPixel16_ToU16(span[i]); + } + else + { + for (int i = 0; i < width; i++) + { + U32 src = span[i]; + if (src) + { + unsigned srcA = SkGetPackedA32(src); + if (srcA == 0xFF) + device[i] = SkPixel32ToPixel16_ToU16(src); + else + device[i] = BLEND_32_TO_16(srcA, src, device[i]); + } + } + } +} + +static inline U16 aa_blendS32D16(U32 src, U16CPU dst, int aa) +{ + SkASSERT((unsigned)aa <= 255); + + int src_scale = SkAlpha255To256(aa); + int sa = SkGetPackedA32(src); + int dst_scale = SkAlpha255To256(255 - SkAlphaMul(sa, src_scale)); + + int dr = (SkPacked32ToR16(src) * src_scale + SkGetPackedR16(dst) * dst_scale) >> 8; + int dg = (SkPacked32ToG16(src) * src_scale + SkGetPackedG16(dst) * dst_scale) >> 8; + int db = (SkPacked32ToB16(src) * src_scale + SkGetPackedB16(dst) * dst_scale) >> 8; + + return SkPackRGB16(dr, dg, db); +} + +static inline int count_nonzero_span(const S16 runs[], const SkAlpha aa[]) +{ + int count = 0; + for (;;) + { + int n = *runs; + if (n == 0 || *aa == 0) + break; + runs += n; + aa += n; + count += n; + } + return count; +} + +void SkRGB16_Shader_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[], const S16 runs[]) +{ +// aa_blendS32D16(0xd4d4d49c, 65526, 238); + + SkASSERT(x >= 0 && y >= 0 && x < (int)fDevice.width() && y < (int)fDevice.height()); + + SkShader* shader = fShader; + SkPMColor* span = fBuffer; + U16* device = fDevice.getAddr16(x, y); + U32 flags = fShader->getFlags(); + + if (SkShader::CanCallShadeSpanOpaque16(flags)) + { + U16* span16 = (U16*)span; + for (;;) + { + int count = *runs; + if (count == 0) + break; + + int aa = *antialias; + if (aa == 255) + shader->shadeSpanOpaque16(x, y, device, count); + else if (aa) + { + unsigned scale = SkAlpha255To256(aa); + shader->shadeSpanOpaque16(x, y, span16, count); + for (int i = 0; i < count; i++) + device[i] = SkToU16(SkBlendRGB16(span16[i], device[i], scale)); + } + device += count; + runs += count; + antialias += count; + x += count; + } + return; + } + + // If we get here, take the 32bit shadeSpan case + + int opaque = flags & SkShader::kOpaqueAlpha_Flag; + + for (;;) + { + int count = *runs; + if (count == 0) + break; + + int aa = *antialias; + if (aa == 0) + { + device += count; + runs += count; + antialias += count; + x += count; + continue; + } + + int nonZeroCount = count + count_nonzero_span(runs + count, antialias + count); + + shader->shadeSpan(x, y, span, nonZeroCount); + x += nonZeroCount; + SkPMColor* localSpan = span; + for (;;) + { + if (aa == 255) // no antialiasing + { + if (opaque) + { + for (int i = 0; i < count; i++) + device[i] = SkPixel32ToPixel16_ToU16(localSpan[i]); + } + else + { + for (int i = 0; i < count; i++) + { + U32 src = localSpan[i]; + if (src) + { + unsigned srcA = SkGetPackedA32(src); + if (srcA == 0xFF) + device[i] = SkPixel32ToPixel16_ToU16(src); + else + device[i] = BLEND_32_TO_16(srcA, src, device[i]); + } + } + } + } + else + { + for (int i = 0; i < count; i++) + { + if (localSpan[i]) + device[i] = aa_blendS32D16(localSpan[i], device[i], aa); + } + } + + device += count; + runs += count; + antialias += count; + nonZeroCount -= count; + if (nonZeroCount == 0) + break; + + localSpan += count; + SkASSERT(nonZeroCount > 0); + count = *runs; + SkASSERT(count > 0); + aa = *antialias; + } + } +} + +/////////////////////////////////////////////////////////////////////// + +SkRGB16_Shader_Xfermode_Blitter::SkRGB16_Shader_Xfermode_Blitter(const SkBitmap& device, const SkPaint& paint) : fDevice(device) +{ + fShader = paint.getShader(); + SkASSERT(fShader); + fShader->ref(); + + fXfermode = paint.getXfermode(); + SkASSERT(fXfermode); + fXfermode->ref(); + + int width = device.width(); + fBuffer = (SkPMColor*)sk_malloc_throw((width + (SkAlign4(width) >> 2)) * sizeof(SkPMColor)); + fAAExpand = (U8*)(fBuffer + width); +} + +SkRGB16_Shader_Xfermode_Blitter::~SkRGB16_Shader_Xfermode_Blitter() +{ + fXfermode->unref(); + fShader->unref(); + sk_free(fBuffer); +} + +void SkRGB16_Shader_Xfermode_Blitter::blitH(int x, int y, int width) +{ + SkASSERT(x >= 0 && y >= 0 && (unsigned)(x + width) <= fDevice.width()); + + U16* device = fDevice.getAddr16(x, y); + SkPMColor* span = fBuffer; + + fShader->shadeSpan(x, y, span, width); + fXfermode->xfer16(device, span, width, NULL); +} + +void SkRGB16_Shader_Xfermode_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[], const S16 runs[]) +{ + SkASSERT(x >= 0 && y >= 0 && x < (int)fDevice.width() && y < (int)fDevice.height()); + + SkShader* shader = fShader; + SkXfermode* mode = fXfermode; + SkPMColor* span = fBuffer; + U8* aaExpand = fAAExpand; + U16* device = fDevice.getAddr16(x, y); + + for (;;) + { + int count = *runs; + if (count == 0) + break; + + int aa = *antialias; + if (aa == 0) + { + device += count; + runs += count; + antialias += count; + x += count; + continue; + } + + int nonZeroCount = count + count_nonzero_span(runs + count, antialias + count); + + shader->shadeSpan(x, y, span, nonZeroCount); + x += nonZeroCount; + SkPMColor* localSpan = span; + for (;;) + { + if (aa == 0xFF) + mode->xfer16(device, localSpan, count, NULL); + else + { + SkASSERT(aa); + memset(aaExpand, aa, count); + mode->xfer16(device, localSpan, count, aaExpand); + } + device += count; + runs += count; + antialias += count; + nonZeroCount -= count; + if (nonZeroCount == 0) + break; + + localSpan += count; + SkASSERT(nonZeroCount > 0); + count = *runs; + SkASSERT(count > 0); + aa = *antialias; + } + } +} + + diff --git a/libs/graphics/sgl/SkBlitter_Sprite.cpp b/libs/graphics/sgl/SkBlitter_Sprite.cpp new file mode 100644 index 0000000000..a740e356d9 --- /dev/null +++ b/libs/graphics/sgl/SkBlitter_Sprite.cpp @@ -0,0 +1,73 @@ +#include "SkSpriteBlitter.h" + +SkSpriteBlitter::SkSpriteBlitter(const SkBitmap& source) + : fSource(&source) +{ +} + +SkSpriteBlitter::~SkSpriteBlitter() +{ +} + +#ifdef SK_DEBUG +void SkSpriteBlitter::blitH(int x, int y, int width) +{ + SkASSERT(!"how did we get here?"); +} + +void SkSpriteBlitter::blitAntiH(int x, int y, const SkAlpha antialias[], const S16 runs[]) +{ + SkASSERT(!"how did we get here?"); +} + +void SkSpriteBlitter::blitV(int x, int y, int height, SkAlpha alpha) +{ + SkASSERT(!"how did we get here?"); +} + +void SkSpriteBlitter::blitMask(const SkMask&, const SkRect16& clip) +{ + SkASSERT(!"how did we get here?"); +} +#endif + +////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////// + +// returning nil means the caller will call SkBlitter::Choose() and +// have wrapped the source bitmap inside a shader +SkBlitter* SkBlitter::ChooseSprite( const SkBitmap& device, + const SkPaint& paint, + const SkBitmap& source, + int left, int top, + void* storage, size_t storageSize) +{ + /* We currently ignore antialiasing and filtertype, meaning we will take our + special blitters regardless of these settings. Ignoring filtertype seems fine + since by definition there is no scale in the matrix. Ignoring antialiasing is + a bit of a hack, since we "could" pass in the fractional left/top for the bitmap, + and respect that by blending the edges of the bitmap against the device. To support + this we could either add more special blitters here, or detect antialiasing in the + paint and return nil if it is set, forcing the client to take the slow shader case + (which does respect soft edges). + */ + + SkSpriteBlitter* blitter = nil; + SkXfermode* mode = paint.getXfermode(); + U8 alpha = paint.getAlpha(); + + switch (device.getConfig()) { + case SkBitmap::kRGB_565_Config: + blitter = SkSpriteBlitter::ChooseD16(source, mode, alpha, storage, storageSize); + break; + case SkBitmap::kARGB_8888_Config: + blitter = SkSpriteBlitter::ChooseD32(source, mode, alpha, storage, storageSize); + default: + break; + } + + if (blitter) + blitter->setup(device, left, top); + return blitter; +} + diff --git a/libs/graphics/sgl/SkCanvas.cpp b/libs/graphics/sgl/SkCanvas.cpp new file mode 100644 index 0000000000..75cb7bd1b0 --- /dev/null +++ b/libs/graphics/sgl/SkCanvas.cpp @@ -0,0 +1,623 @@ +#include "SkCanvas.h" +#include "SkDraw.h" +#include "SkBounder.h" +#include "SkUtils.h" + +struct MCRecLayer { + SkBitmap fBitmap; + int fX, fY; + SkPaint fPaint; + + MCRecLayer(const SkPaint& paint) : fPaint(paint) {} +}; + +struct SkCanvas::MCRec { + MCRec* fNext; + + SkMatrix fMatrix; + SkMatrix::MapPtProc fMapPtProc; + SkRegion fRegion; + + MCRecLayer* fLayer; // may be NULL + const SkBitmap* fCurrBitmap; // points to layer or prevLayer or pixels + + uint8_t fSetPaintBits; + uint8_t fClearPaintBits; + + MCRec() : fLayer(NULL) + { + } + MCRec(const MCRec& other) + : fMatrix(other.fMatrix), fRegion(other.fRegion), fLayer(NULL) + { + // don't bother initializing fNext + fMapPtProc = other.fMapPtProc; + fCurrBitmap = other.fCurrBitmap; + fSetPaintBits = other.fSetPaintBits; + fClearPaintBits = other.fClearPaintBits; + } + ~MCRec() + { + SkDELETE(fLayer); + } +}; + +class AutoPaintSetClear { +public: + AutoPaintSetClear(const SkPaint& paint, U32 setBits, U32 clearBits) : fPaint(paint) + { + fFlags = paint.getFlags(); + ((SkPaint*)&paint)->setFlags((fFlags | setBits) & ~clearBits); + } + ~AutoPaintSetClear() + { + ((SkPaint*)&fPaint)->setFlags(fFlags); + } +private: + const SkPaint& fPaint; + U32 fFlags; + + // illegal + AutoPaintSetClear(const AutoPaintSetClear&); + AutoPaintSetClear& operator=(const AutoPaintSetClear&); +}; + +class SkAutoBounderCommit { +public: + SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {} + ~SkAutoBounderCommit() { if (fBounder) fBounder->commit(); } +private: + SkBounder* fBounder; +}; + +//////////////////////////////////////////////////////////////////////////// + +SkCanvas::SkCanvas() + : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)), fBounder(NULL) +{ + fMCRec = (MCRec*)fMCStack.push_back(); + new (fMCRec) MCRec; + + fMCRec->fNext = NULL; + fMCRec->fMatrix.reset(); + fMCRec->fSetPaintBits = 0; + fMCRec->fClearPaintBits = 0; + fMCRec->fMapPtProc = NULL; // mark as dirty/unknown + + fMCRec->fLayer = NULL; + fMCRec->fCurrBitmap = &fBitmap; +} + +SkCanvas::SkCanvas(const SkBitmap& bitmap) + : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)), fBitmap(bitmap), fBounder(NULL) +{ + fMCRec = (MCRec*)fMCStack.push_back(); + new (fMCRec) MCRec; + + fMCRec->fNext = NULL; + fMCRec->fMatrix.reset(); + fMCRec->fSetPaintBits = 0; + fMCRec->fClearPaintBits = 0; + fMCRec->fMapPtProc = NULL; // mark as dirty/unknown + + fMCRec->fRegion.setRect(0, 0, bitmap.width(), bitmap.height()); + + fMCRec->fLayer = NULL; + fMCRec->fCurrBitmap = &fBitmap; +} + +SkCanvas::~SkCanvas() +{ +} + +SkBounder* SkCanvas::setBounder(SkBounder* bounder) +{ + SkRefCnt_SafeAssign(fBounder, bounder); + return bounder; +} + +void SkCanvas::getPixels(SkBitmap* bitmap) const +{ + if (bitmap) + *bitmap = fBitmap; +} + +void SkCanvas::setPixels(const SkBitmap& bitmap) +{ + unsigned prevWidth = fBitmap.width(); + unsigned prevHeight = fBitmap.height(); + + fBitmap = bitmap; + + /* Now we update our initial region to have the bounds of the new bitmap, + and then intersect all of the clips in our stack with these bounds, + to ensure that we can't draw outside of the bitmap's bounds (and trash + memory). + + NOTE: this is only a partial-fix, since if the new bitmap is larger than + the previous one, we don't know how to "enlarge" the clips in our stack, + so drawing may be artificially restricted. Without keeping a history of + all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly + reconstruct the correct clips, so this approximation will have to do. + The caller really needs to restore() back to the base if they want to + accurately take advantage of the new bitmap bounds. + */ + + if (prevWidth != bitmap.width() || prevHeight != bitmap.height()) + { + SkRect16 r; + r.set(0, 0, bitmap.width(), bitmap.height()); + + SkDeque::Iter iter(fMCStack); + MCRec* rec = (MCRec*)iter.next(); + + SkASSERT(rec); + rec->fRegion.setRect(r); + + while ((rec = (MCRec*)iter.next()) != NULL) + (void)rec->fRegion.op(r, SkRegion::kIntersect_Op); + } +} + +bool SkCanvas::isBitmapOpaque() const +{ + SkBitmap::Config c = fBitmap.getConfig(); + + return c != SkBitmap::kA8_Config && c != SkBitmap::kARGB_8888_Config; +} + +/////////////////////////////////////////////////////////////////////////////////////////// + +U32 SkCanvas::getPaintSetBits() const +{ + return fMCRec->fSetPaintBits; +} + +U32 SkCanvas::getPaintClearBits() const +{ + return fMCRec->fClearPaintBits; +} + +void SkCanvas::setPaintSetClearBits(U32 setBits, U32 clearBits) +{ + fMCRec->fSetPaintBits = SkToU8(setBits & SkPaint::kAllFlagMasks); + fMCRec->fClearPaintBits = SkToU8(clearBits & SkPaint::kAllFlagMasks); +} + +void SkCanvas::orPaintSetClearBits(U32 setBits, U32 clearBits) +{ + fMCRec->fSetPaintBits |= setBits; + fMCRec->fClearPaintBits |= clearBits; +} + +///////////////////////////////////////////////////////////////////////////// + +int SkCanvas::save() +{ + int saveCount = this->getSaveCount(); // record this before the actual save + + MCRec* newTop = (MCRec*)fMCStack.push_back(); + new (newTop) MCRec(*fMCRec); + + newTop->fNext = fMCRec; + fMCRec = newTop; + + return saveCount; +} + +int SkCanvas::saveLayer(const SkRect& bounds, const SkPaint& paint) +{ + // do this before we create the layer + int count = this->save(); + + SkRect r; + SkRect16 ir; + + fMCRec->fMatrix.mapRect(&r, bounds); + r.roundOut(&ir); + + if (ir.intersect(fMCRec->fRegion.getBounds())) + { + MCRecLayer* layer = SkNEW_ARGS(MCRecLayer, (paint)); + + layer->fBitmap.setConfig(SkBitmap::kARGB_8888_Config, ir.width(), ir.height()); + layer->fBitmap.allocPixels(); + layer->fBitmap.eraseARGB(0, 0, 0, 0); + layer->fX = ir.fLeft; + layer->fY = ir.fTop; + + fMCRec->fLayer = layer; + fMCRec->fCurrBitmap = &layer->fBitmap; + + fMCRec->fMatrix.postTranslate(-SkIntToScalar(ir.fLeft), -SkIntToScalar(ir.fTop)); + fMCRec->fMapPtProc = NULL; + + fMCRec->fRegion.op(ir, SkRegion::kIntersect_Op); + fMCRec->fRegion.translate(-ir.fLeft, -ir.fTop); + } + return count; +} + +#include "SkTemplates.h" + +void SkCanvas::restore() +{ + SkASSERT(!fMCStack.empty()); + + MCRecLayer* layer = fMCRec->fLayer; + SkAutoTDelete<MCRecLayer> ad(layer); + // now detach it from fMCRec + fMCRec->fLayer = NULL; + + // now do the normal restore() + fMCStack.pop_back(); + fMCRec = (MCRec*)fMCStack.back(); + + // now handle the layer if needed + if (layer) + this->drawSprite(layer->fBitmap, layer->fX, layer->fY, layer->fPaint); +} + +int SkCanvas::getSaveCount() const +{ + return fMCStack.count(); +} + +void SkCanvas::restoreToCount(int count) +{ + SkASSERT(fMCStack.count() >= count); + + while (fMCStack.count() > count) + this->restore(); +} + +void SkCanvas::clipDeviceRgn(const SkRegion& rgn) +{ + fMCRec->fRegion.op(rgn, SkRegion::kIntersect_Op); +} + +///////////////////////////////////////////////////////////////////////////// + +bool SkCanvas::translate(SkScalar dx, SkScalar dy) +{ + fMCRec->fMapPtProc = NULL; // mark as dirty/unknown + return fMCRec->fMatrix.preTranslate(dx, dy); +} + +bool SkCanvas::scale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) +{ + fMCRec->fMapPtProc = NULL; // mark as dirty/unknown + return fMCRec->fMatrix.preScale(sx, sy, px, py); +} + +bool SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) +{ + fMCRec->fMapPtProc = NULL; // mark as dirty/unknown + return fMCRec->fMatrix.preRotate(degrees, px, py); +} + +bool SkCanvas::skew(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) +{ + fMCRec->fMapPtProc = NULL; // mark as dirty/unknown + return fMCRec->fMatrix.preSkew(sx, sy, px, py); +} + +bool SkCanvas::concat(const SkMatrix& matrix) +{ + fMCRec->fMapPtProc = NULL; // mark as dirty/unknown + return fMCRec->fMatrix.preConcat(matrix); +} + +////////////////////////////////////////////////////////////////////////////// + +void SkCanvas::clipRect(const SkRect& rect) +{ + if (fMCRec->fMatrix.rectStaysRect()) + { + SkRect r; + SkRect16 ir; + + fMCRec->fMatrix.mapRect(&r, rect); + r.round(&ir); + fMCRec->fRegion.op(ir, SkRegion::kIntersect_Op); + } + else + { + SkPath path; + + path.addRect(rect); + this->clipPath(path); + } +} + +void SkCanvas::clipRect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) +{ + SkRect r; + + r.set(left, top, right, bottom); + this->clipRect(r); +} + +void SkCanvas::clipPath(const SkPath& path) +{ + SkPath devPath; + + path.transform(fMCRec->fMatrix, &devPath); + fMCRec->fRegion.setPath(devPath, &fMCRec->fRegion); +} + +bool SkCanvas::quickReject(const SkRect& rect, bool antialiased) const +{ + if (fMCRec->fRegion.isEmpty() || rect.isEmpty()) + return true; + + SkRect r; + SkRect16 ir; + + fMCRec->fMatrix.mapRect(&r, rect); + if (antialiased) + r.roundOut(&ir); + else + r.round(&ir); + + return fMCRec->fRegion.quickReject(ir); +} + +bool SkCanvas::quickReject(const SkPath& path, bool antialiased) const +{ + if (fMCRec->fRegion.isEmpty() || path.isEmpty()) + return true; + + if (fMCRec->fMatrix.rectStaysRect()) + { + SkRect r; + path.computeBounds(&r, SkPath::kExact_BoundsType); + return this->quickReject(r, antialiased); + } + + SkPath dstPath; + SkRect r; + SkRect16 ir; + + path.transform(fMCRec->fMatrix, &dstPath); + dstPath.computeBounds(&r, SkPath::kExact_BoundsType); + if (antialiased) + r.roundOut(&ir); + else + r.round(&ir); + + return fMCRec->fRegion.quickReject(ir); +} + +////////////////////////////////////////////////////////////////////////////// + +void SkCanvas::drawRGB(U8CPU r, U8CPU g, U8CPU b) +{ + SkPaint paint; + + paint.setARGB(0xFF, r, g, b); + this->drawPaint(paint); +} + +void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) +{ + SkPaint paint; + + paint.setARGB(a, r, g, b); + this->drawPaint(paint); +} + +void SkCanvas::drawColor(SkColor c, SkPorterDuff::Mode mode) +{ + SkPaint paint; + + paint.setColor(c); + paint.setPorterDuffXfermode(mode); + this->drawPaint(paint); +} + +void SkCanvas::drawPaint(const SkPaint& paint) +{ + SkAutoBounderCommit ac(fBounder); + SkDraw draw(*this); + + draw.drawPaint(paint); +} + +void SkCanvas::drawLine(const SkPoint& start, const SkPoint& stop, const SkPaint& paint) +{ + AutoPaintSetClear force(paint, fMCRec->fSetPaintBits, fMCRec->fClearPaintBits); + SkAutoBounderCommit ac(fBounder); + SkDraw draw(*this); + + draw.drawLine(start, stop, paint); +} + +void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const SkPaint& paint) +{ + AutoPaintSetClear force(paint, fMCRec->fSetPaintBits, fMCRec->fClearPaintBits); + SkAutoBounderCommit ac(fBounder); + SkDraw draw(*this); + + SkPoint pts[2]; + pts[0].set(x0, y0); + pts[1].set(x1, y1); + draw.drawLine(pts[0], pts[1], paint); +} + +//#include <stdio.h> + +void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) +{ + AutoPaintSetClear force(paint, fMCRec->fSetPaintBits, fMCRec->fClearPaintBits); + SkAutoBounderCommit ac(fBounder); + SkDraw draw(*this); + +#if 0 + const SkScalar* m = (const SkScalar*)draw.fMatrix; +printf("drawRect(%g %g %g %g) matrix(%g %g %g %g %g %g)\n", + SkScalarToFloat(r.fLeft), SkScalarToFloat(r.fTop), SkScalarToFloat(r.fRight), SkScalarToFloat(r.fBottom), + SkScalarToFloat(m[0]), SkScalarToFloat(m[1]), SkScalarToFloat(m[2]), + SkScalarToFloat(m[3]), SkScalarToFloat(m[4]), SkScalarToFloat(m[5]), + SkScalarToFloat(m[6]), SkScalarToFloat(m[7]), SkScalarToFloat(m[8])); +#endif + + draw.drawRect(r, paint); +} + +void SkCanvas::drawRect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom, const SkPaint& paint) +{ + SkRect r; + + r.set(left, top, right, bottom); + this->drawRect(r, paint); +} + +void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) +{ + AutoPaintSetClear force(paint, fMCRec->fSetPaintBits, fMCRec->fClearPaintBits); + SkAutoBounderCommit ac(fBounder); + SkDraw draw(*this); + + SkPath path; + + path.addOval(oval); + draw.drawPath(path, paint, NULL, true); +} + +void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, const SkPaint& paint) +{ + AutoPaintSetClear force(paint, fMCRec->fSetPaintBits, fMCRec->fClearPaintBits); + SkAutoBounderCommit ac(fBounder); + SkDraw draw(*this); + + SkPath path; + + path.addCircle(cx, cy, radius); + draw.drawPath(path, paint, NULL, true); +} + +void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry, const SkPaint& paint) +{ + AutoPaintSetClear force(paint, fMCRec->fSetPaintBits, fMCRec->fClearPaintBits); + SkAutoBounderCommit ac(fBounder); + SkDraw draw(*this); + + SkPath path; + + path.addRoundRect(r, rx, ry, SkPath::kCW_Direction); + draw.drawPath(path, paint, NULL, true); +} + +void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) +{ + AutoPaintSetClear force(paint, fMCRec->fSetPaintBits, fMCRec->fClearPaintBits); + SkAutoBounderCommit ac(fBounder); + SkDraw draw(*this); + + draw.drawPath(path, paint, NULL, false); +} + +void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint& paint) +{ + AutoPaintSetClear force(paint, fMCRec->fSetPaintBits, fMCRec->fClearPaintBits); + SkAutoBounderCommit ac(fBounder); + SkDraw draw(*this); + + draw.drawBitmap(bitmap, x, y, paint); +} + +void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y) +{ + SkPaint paint; + this->drawBitmap(bitmap, x, y, paint); +} + +void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint& paint) +{ + AutoPaintSetClear force(paint, fMCRec->fSetPaintBits, fMCRec->fClearPaintBits); + SkAutoBounderCommit ac(fBounder); + SkDraw draw(*this); + + draw.drawSprite(bitmap, x, y, paint); +} + +void SkCanvas::drawText(const char text[], size_t byteLength, SkScalar x, SkScalar y, const SkPaint& paint) +{ + AutoPaintSetClear force(paint, fMCRec->fSetPaintBits, fMCRec->fClearPaintBits); + SkAutoBounderCommit ac(fBounder); + SkDraw draw(*this); + + draw.drawText((SkUnicodeWalkerProc)SkUTF8_NextUnichar, text, byteLength, x, y, paint); +} + +void SkCanvas::drawText16(const U16 text[], size_t numberOf16BitValues, SkScalar x, SkScalar y, const SkPaint& paint) +{ + AutoPaintSetClear force(paint, fMCRec->fSetPaintBits, fMCRec->fClearPaintBits); + SkAutoBounderCommit ac(fBounder); + SkDraw draw(*this); + + draw.drawText((SkUnicodeWalkerProc)SkUTF16_NextUnichar, (const char*)text, numberOf16BitValues << 1, x, y, paint); +} + +void SkCanvas::drawPosText(const char text[], size_t byteLength, const SkPoint pos[], const SkPaint& paint) +{ + AutoPaintSetClear force(paint, fMCRec->fSetPaintBits, fMCRec->fClearPaintBits); + SkAutoBounderCommit ac(fBounder); + SkDraw draw(*this); + + draw.drawPosText((SkUnicodeWalkerProc)SkUTF8_NextUnichar, text, byteLength, pos, paint); +} + +void SkCanvas::drawPosText16(const U16 text[], size_t numberOf16BitValues, const SkPoint pos[], const SkPaint& paint) +{ + AutoPaintSetClear force(paint, fMCRec->fSetPaintBits, fMCRec->fClearPaintBits); + SkAutoBounderCommit ac(fBounder); + SkDraw draw(*this); + + draw.drawPosText((SkUnicodeWalkerProc)SkUTF16_NextUnichar, (const char*)text, numberOf16BitValues << 1, pos, paint); +} + +void SkCanvas::drawTextOnPath(const char text[], size_t byteLength, const SkPath& path, SkScalar offset, const SkPaint& paint) +{ + AutoPaintSetClear force(paint, fMCRec->fSetPaintBits, fMCRec->fClearPaintBits); + SkAutoBounderCommit ac(fBounder); + SkDraw draw(*this); + + draw.drawTextOnPath((SkUnicodeWalkerProc)SkUTF8_NextUnichar, text, byteLength, path, offset, paint); +} + +void SkCanvas::drawText16OnPath(const U16 text[], size_t numberOf16BitValues, const SkPath& path, SkScalar offset, const SkPaint& paint) +{ + AutoPaintSetClear force(paint, fMCRec->fSetPaintBits, fMCRec->fClearPaintBits); + SkAutoBounderCommit ac(fBounder); + SkDraw draw(*this); + + draw.drawTextOnPath((SkUnicodeWalkerProc)SkUTF16_NextUnichar, (const char*)text, numberOf16BitValues << 1, path, offset, paint); +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////// + +const SkBitmap& SkCanvas::getCurrBitmap() const +{ + return *fMCRec->fCurrBitmap; +} + +SkMatrix::MapPtProc SkCanvas::getCurrMapPtProc() const +{ + if (fMCRec->fMapPtProc == NULL) + fMCRec->fMapPtProc = fMCRec->fMatrix.getMapPtProc(); + + return fMCRec->fMapPtProc; +} + +const SkMatrix& SkCanvas::getTotalMatrix() const +{ + return fMCRec->fMatrix; +} + +const SkRegion& SkCanvas::getTotalClip() const +{ + return fMCRec->fRegion; +} + diff --git a/libs/graphics/sgl/SkColor.cpp b/libs/graphics/sgl/SkColor.cpp new file mode 100644 index 0000000000..3831c575b0 --- /dev/null +++ b/libs/graphics/sgl/SkColor.cpp @@ -0,0 +1,118 @@ +#include "SkColor.h" +#include "SkColorPriv.h" + +SkPMColor SkPreMultiplyARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) +{ + if (a != 255) + { + unsigned scale = SkAlpha255To256(a); + r = SkAlphaMul(r, scale); + g = SkAlphaMul(g, scale); + b = SkAlphaMul(b, scale); + } + return SkPackARGB32(a, r, g, b); +} + +SkPMColor SkPreMultiplyColor(SkColor c) +{ + unsigned a = SkColorGetA(c); + unsigned r = SkColorGetR(c); + unsigned g = SkColorGetG(c); + unsigned b = SkColorGetB(c); + + return SkPreMultiplyARGB(a, r, g, b); +} + +////////////////////////////////////////////////////////////////////////////////////////////// + +static inline SkScalar ByteToScalar(U8CPU x) +{ + SkASSERT(x <= 255); + return SkIntToScalar(x) / 255; +} + +static inline SkScalar ByteDivToScalar(int numer, U8CPU denom) +{ + // cast to keep the answer signed + return SkIntToScalar(numer) / (int)denom; +} + +void SkRGBToHSV(U8CPU r, U8CPU g, U8CPU b, SkScalar hsv[3]) +{ + SkASSERT(hsv); + + unsigned min = SkMin32(r, SkMin32(g, b)); + unsigned max = SkMax32(r, SkMax32(g, b)); + unsigned delta = max - min; + + SkScalar v = ByteToScalar(max); + SkASSERT(v >= 0 && v <= SK_Scalar1); + + if (0 == delta) // we're a shade of gray + { + hsv[0] = hsv[1] = hsv[2] = v; + return; + } + + SkScalar s = ByteDivToScalar(delta, max); + SkASSERT(s >= 0 && s <= SK_Scalar1); + + SkScalar h; + if (r == max) + h = ByteDivToScalar(g - b, delta); + else if (g == max) + h = SkIntToScalar(2) + ByteDivToScalar(b - r, delta); + else // b == max + h = SkIntToScalar(4) + ByteDivToScalar(r - g, delta); + + h *= 60; + if (h < 0) + h += SkIntToScalar(360); + SkASSERT(h >= 0 && h < SkIntToScalar(360)); + + hsv[0] = h; + hsv[1] = s; + hsv[2] = v; +} + +static inline U8CPU UnitScalarToByte(SkScalar x) +{ + if (x < 0) + return 0; + if (x >= SK_Scalar1) + return 255; + return SkScalarToFixed(x) >> 8; +} + +SkColor SkHSVToColor(U8CPU a, const SkScalar hsv[3]) +{ + SkASSERT(hsv); + + U8CPU s = UnitScalarToByte(hsv[1]); + U8CPU v = UnitScalarToByte(hsv[2]); + + if (0 == s) // shade of gray + return SkColorSetARGB(a, v, v, v); + + SkFixed hx = (hsv[0] < 0 || hsv[0] >= SkIntToScalar(360)) ? 0 : SkScalarToFixed(hsv[0]/60); + SkFixed f = hx & 0xFFFF; + + unsigned v_scale = SkAlpha255To256(v); + unsigned p = SkAlphaMul(255 - s, v_scale); + unsigned q = SkAlphaMul(255 - (s * f >> 16), v_scale); + unsigned t = SkAlphaMul(255 - (s * (SK_Fixed1 - f) >> 16), v_scale); + + unsigned r, g, b; + + SkASSERT((unsigned)(hx >> 16) < 6); + switch (hx >> 16) { + case 0: r = v; g = t; b = p; break; + case 1: r = q; g = v; b = p; break; + case 2: r = p; g = v; b = t; break; + case 3: r = p; g = q; b = v; break; + case 4: r = t; g = p; b = v; break; + default: r = v; g = p; b = q; break; + } + return SkColorSetARGB(a, r, g, b); +} + diff --git a/libs/graphics/sgl/SkColorFilter.cpp b/libs/graphics/sgl/SkColorFilter.cpp new file mode 100644 index 0000000000..dc89eb4e33 --- /dev/null +++ b/libs/graphics/sgl/SkColorFilter.cpp @@ -0,0 +1,36 @@ +#include "SkColorFilter.h" +#include "SkShader.h" + +void SkColorFilter::filterSpan(const SkPMColor src[], int count, SkPMColor result[]) +{ + memcpy(result, src, count * sizeof(SkPMColor)); +} + +////////////////////////////////////////////////////////////////////////////////////// + +SkFilterShader::SkFilterShader(SkShader* shader, SkColorFilter* filter) +{ + fShader = shader; shader->ref(); + fFilter = filter; filter->ref(); +} + +SkFilterShader::~SkFilterShader() +{ + fFilter->unref(); + fShader->unref(); +} + +bool SkFilterShader::setContext(const SkBitmap& device, + const SkPaint& paint, + const SkMatrix& matrix) +{ + return this->INHERITED::setContext(device, paint, matrix) && + fShader->setContext(device, paint, matrix); +} + +void SkFilterShader::shadeSpan(int x, int y, SkPMColor result[], int count) +{ + fShader->shadeSpan(x, y, result, count); + fFilter->filterSpan(result, count, result); +} + diff --git a/libs/graphics/sgl/SkColorTable.cpp b/libs/graphics/sgl/SkColorTable.cpp new file mode 100644 index 0000000000..2904a8feba --- /dev/null +++ b/libs/graphics/sgl/SkColorTable.cpp @@ -0,0 +1,99 @@ +#include "SkBitmap.h" +#include "SkTemplates.h" + +SkColorTable::SkColorTable() : fColors(nil), f16BitCache(nil), fCount(0), fFlags(0) +{ + SkDEBUGCODE(fColorLockCount = 0;) + SkDEBUGCODE(f16BitCacheLockCount = 0;) +} + +SkColorTable::~SkColorTable() +{ + SkASSERT(fColorLockCount == 0); + SkASSERT(f16BitCacheLockCount == 0); + + sk_free(fColors); + sk_free(f16BitCache); +} + +void SkColorTable::setFlags(unsigned flags) +{ + fFlags = SkToU8(flags); +} + +void SkColorTable::setColors(const SkPMColor src[], int count) +{ + SkASSERT(fColorLockCount == 0); + SkASSERT((unsigned)count <= 256); + + if (fCount != count) + { + if (count == 0) + { + sk_free(fColors); + fColors = nil; + } + else + { + // allocate new array before freeing old, in case the alloc fails (throws) + SkPMColor* table = (SkPMColor*)sk_malloc_throw(count * sizeof(SkPMColor)); + sk_free(fColors); + fColors = table; + + if (src) + memcpy(fColors, src, count * sizeof(SkPMColor)); + } + fCount = SkToU16(count); + } + else + { + if (src) + memcpy(fColors, src, count * sizeof(SkPMColor)); + } + + this->inval16BitCache(); +} + +void SkColorTable::inval16BitCache() +{ + SkASSERT(f16BitCacheLockCount == 0); + if (f16BitCache) + { + sk_free(f16BitCache); + f16BitCache = nil; + } +} + +#include "SkColorPriv.h" + +static inline void build_16bitcache(U16 dst[], const SkPMColor src[], int count) +{ + while (--count >= 0) + *dst++ = SkPixel32ToPixel16_ToU16(*src++); +} + +const U16* SkColorTable::lock16BitCache() +{ + if (fFlags & kColorsAreOpaque_Flag) + { + if (f16BitCache == nil) // build the cache + { + f16BitCache = (U16*)sk_malloc_throw(fCount * sizeof(U16)); + build_16bitcache(f16BitCache, fColors, fCount); + } + } + else // our colors have alpha, so no cache + { + this->inval16BitCache(); + if (f16BitCache) + { + sk_free(f16BitCache); + f16BitCache = nil; + } + } + + SkDEBUGCODE(f16BitCacheLockCount += 1); + return f16BitCache; +} + + diff --git a/libs/graphics/sgl/SkCoreBlitters.h b/libs/graphics/sgl/SkCoreBlitters.h new file mode 100644 index 0000000000..ebf339a50b --- /dev/null +++ b/libs/graphics/sgl/SkCoreBlitters.h @@ -0,0 +1,177 @@ +#ifndef SkCoreBlitters_DEFINED +#define SkCoreBlitters_DEFINED + +#include "SkBlitter.h" + +class SkA8_Blitter : public SkBlitter { +public: + SkA8_Blitter(const SkBitmap& device, const SkPaint& paint); + virtual void blitH(int x, int y, int width); + virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const S16 runs[]); + virtual void blitV(int x, int y, int height, SkAlpha alpha); + virtual void blitRect(int x, int y, int width, int height); + virtual void blitMask(const SkMask&, const SkRect16&); + +private: + const SkBitmap& fDevice; + unsigned fSrcA; + + // illegal + SkA8_Blitter& operator=(const SkA8_Blitter&); +}; + +class SkA8_Shader_Blitter : public SkBlitter { +public: + SkA8_Shader_Blitter(const SkBitmap& device, const SkPaint& paint); + virtual ~SkA8_Shader_Blitter(); + virtual void blitH(int x, int y, int width); + virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const S16 runs[]); + virtual void blitMask(const SkMask&, const SkRect16&); + +private: + const SkBitmap& fDevice; + SkShader* fShader; + SkXfermode* fXfermode; + SkPMColor* fBuffer; + U8* fAAExpand; + + typedef SkBlitter INHERITED; + + // illegal + SkA8_Shader_Blitter& operator=(const SkA8_Shader_Blitter&); +}; + +//////////////////////////////////////////////////////////////// + +class SkARGB32_Blitter : public SkBlitter { +public: + SkARGB32_Blitter(const SkBitmap& device, const SkPaint& paint); + virtual void blitH(int x, int y, int width); + virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const S16 runs[]); + virtual void blitV(int x, int y, int height, SkAlpha alpha); + virtual void blitRect(int x, int y, int width, int height); + virtual void blitMask(const SkMask&, const SkRect16&); + +protected: + const SkBitmap& fDevice; + +private: + SkColor fPMColor; + unsigned fSrcA, fSrcR, fSrcG, fSrcB; + + // illegal + SkARGB32_Blitter& operator=(const SkARGB32_Blitter&); +}; + +class SkARGB32_Black_Blitter : public SkARGB32_Blitter { +public: + SkARGB32_Black_Blitter(const SkBitmap& device, const SkPaint& paint) + : SkARGB32_Blitter(device, paint) {} + virtual void blitMask(const SkMask&, const SkRect16&); +}; + +class SkARGB32_Shader_Blitter : public SkBlitter { +public: + SkARGB32_Shader_Blitter(const SkBitmap& device, const SkPaint& paint); + virtual ~SkARGB32_Shader_Blitter(); + virtual void blitH(int x, int y, int width); + virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const S16 runs[]); + +private: + const SkBitmap& fDevice; + SkShader* fShader; + SkXfermode* fXfermode; + SkPMColor* fBuffer; + + typedef SkBlitter INHERITED; + + // illegal + SkARGB32_Shader_Blitter& operator=(const SkARGB32_Shader_Blitter&); +}; + +//////////////////////////////////////////////////////////////// + +class SkRGB16_Blitter : public SkBlitter { +public: + SkRGB16_Blitter(const SkBitmap& device, const SkPaint& paint); + virtual void blitH(int x, int y, int width); + virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const S16 runs[]); + virtual void blitV(int x, int y, int height, SkAlpha alpha); + virtual void blitRect(int x, int y, int width, int height); + virtual void blitMask(const SkMask&, const SkRect16&); + +protected: + const SkBitmap& fDevice; + +private: + unsigned fScale; + U16 fColor16; + U16 fRawColor16; + + // illegal + SkRGB16_Blitter& operator=(const SkRGB16_Blitter&); +}; + +class SkRGB16_Black_Blitter : public SkRGB16_Blitter { +public: + SkRGB16_Black_Blitter(const SkBitmap& device, const SkPaint& paint) + : SkRGB16_Blitter(device, paint) {} + virtual void blitMask(const SkMask&, const SkRect16&); +}; + +class SkRGB16_Shader_Blitter : public SkBlitter { +public: + SkRGB16_Shader_Blitter(const SkBitmap& device, const SkPaint& paint); + virtual ~SkRGB16_Shader_Blitter(); + virtual void blitH(int x, int y, int width); + virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const S16 runs[]); + +private: + const SkBitmap& fDevice; + SkShader* fShader; + SkPMColor* fBuffer; + + typedef SkBlitter INHERITED; + + // illegal + SkRGB16_Shader_Blitter& operator=(const SkRGB16_Shader_Blitter&); +}; + +class SkRGB16_Shader_Xfermode_Blitter : public SkBlitter { +public: + SkRGB16_Shader_Xfermode_Blitter(const SkBitmap& device, const SkPaint& paint); + virtual ~SkRGB16_Shader_Xfermode_Blitter(); + virtual void blitH(int x, int y, int width); + virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const S16 runs[]); + +private: + const SkBitmap& fDevice; + SkShader* fShader; + SkXfermode* fXfermode; + SkPMColor* fBuffer; + U8* fAAExpand; + + typedef SkBlitter INHERITED; + + // illegal + SkRGB16_Shader_Xfermode_Blitter& operator=(const SkRGB16_Shader_Xfermode_Blitter&); +}; + +///////////////////////////////////////////////////////////////////////////// + +class SkA1_Blitter : public SkBlitter { +public: + SkA1_Blitter(const SkBitmap& device, const SkPaint& paint); + virtual void blitH(int x, int y, int width); + +private: + const SkBitmap& fDevice; + U8 fSrcA; + + // illegal + SkA1_Blitter& operator=(const SkA1_Blitter&); +}; + + +#endif + diff --git a/libs/graphics/sgl/SkDeque.cpp b/libs/graphics/sgl/SkDeque.cpp new file mode 100644 index 0000000000..d2ccfeb992 --- /dev/null +++ b/libs/graphics/sgl/SkDeque.cpp @@ -0,0 +1,389 @@ +#include "SkDeque.h" + +#define INIT_ELEM_COUNT 1 // should we let the caller set this in the constructor? + +struct SkDeque::Head { + Head* fNext; + Head* fPrev; + char* fBegin; // start of used section in this chunk + char* fEnd; // end of used section in this chunk + char* fStop; // end of the allocated chunk + + char* start() { return (char*)(this + 1); } + const char* start() const { return (const char*)(this + 1); } + + void init(size_t size) + { + fNext = fPrev = nil; + fBegin = fEnd = nil; + fStop = (char*)this + size; + } +}; + +SkDeque::SkDeque(size_t elemSize) : fElemSize(elemSize), fInitialStorage(nil), fCount(0) +{ + fFront = fBack = nil; +} + +SkDeque::SkDeque(size_t elemSize, void* storage, size_t storageSize) + : fElemSize(elemSize), fInitialStorage(storage), fCount(0) +{ + SkASSERT(storageSize == 0 || storage != nil); + + if (storageSize >= sizeof(Head) + elemSize) + { + fFront = (Head*)storage; + fFront->init(storageSize); + } + else + { + fFront = nil; + } + fBack = fFront; +} + +SkDeque::~SkDeque() +{ + Head* head = fFront; + Head* initialHead = (Head*)fInitialStorage; + + while (head) + { + Head* next = head->fNext; + if (head != initialHead) + sk_free(head); + head = next; + } +} + +const void* SkDeque::front() const +{ + Head* front = fFront; + + if (front == nil) + return nil; + + if (front->fBegin == nil) + { + front = front->fNext; + if (front == nil) + return nil; + } + SkASSERT(front->fBegin); + return front->fBegin; +} + +const void* SkDeque::back() const +{ + Head* back = fBack; + + if (back == nil) + return nil; + + if (back->fEnd == nil) // marked as deleted + { + back = back->fPrev; + if (back == nil) + return nil; + } + SkASSERT(back->fEnd); + return back->fEnd - fElemSize; +} + +void* SkDeque::push_front() +{ + fCount += 1; + + if (fFront == nil) + { + fFront = (Head*)sk_malloc_throw(sizeof(Head) + INIT_ELEM_COUNT * fElemSize); + fFront->init(sizeof(Head) + INIT_ELEM_COUNT * fElemSize); + fBack = fFront; // update our linklist + } + + Head* first = fFront; + char* begin; + + if (first->fBegin == nil) + { + INIT_CHUNK: + first->fEnd = first->fStop; + begin = first->fStop - fElemSize; + } + else + { + begin = first->fBegin - fElemSize; + if (begin < first->start()) // no more room in this chunk + { + // should we alloc more as we accumulate more elements? + size_t size = sizeof(Head) + INIT_ELEM_COUNT * fElemSize; + + first = (Head*)sk_malloc_throw(size); + first->init(size); + first->fNext = fFront; + fFront->fPrev = first; + fFront = first; + goto INIT_CHUNK; + } + } + + first->fBegin = begin; + return begin; +} + +void* SkDeque::push_back() +{ + fCount += 1; + + if (fBack == nil) + { + fBack = (Head*)sk_malloc_throw(sizeof(Head) + INIT_ELEM_COUNT * fElemSize); + fBack->init(sizeof(Head) + INIT_ELEM_COUNT * fElemSize); + fFront = fBack; // update our linklist + } + + Head* last = fBack; + char* end; + + if (last->fBegin == nil) + { + INIT_CHUNK: + last->fBegin = last->start(); + end = last->fBegin + fElemSize; + } + else + { + end = last->fEnd + fElemSize; + if (end > last->fStop) // no more room in this chunk + { + // should we alloc more as we accumulate more elements? + size_t size = sizeof(Head) + INIT_ELEM_COUNT * fElemSize; + + last = (Head*)sk_malloc_throw(size); + last->init(size); + last->fPrev = fBack; + fBack->fNext = last; + fBack = last; + goto INIT_CHUNK; + } + } + + last->fEnd = end; + return end - fElemSize; +} + +void SkDeque::pop_front() +{ + SkASSERT(fCount > 0); + fCount -= 1; + + Head* first = fFront; + + SkASSERT(first != nil); + + if (first->fBegin == nil) // we were marked empty from before + { + first = first->fNext; + first->fPrev = nil; + sk_free(fFront); + fFront = first; + SkASSERT(first != nil); // else we popped too far + } + + char* begin = first->fBegin + fElemSize; + SkASSERT(begin <= first->fEnd); + + if (begin < fFront->fEnd) + first->fBegin = begin; + else + first->fBegin = first->fEnd = nil; // mark as empty +} + +void SkDeque::pop_back() +{ + SkASSERT(fCount > 0); + fCount -= 1; + + Head* last = fBack; + + SkASSERT(last != nil); + + if (last->fEnd == nil) // we were marked empty from before + { + last = last->fPrev; + last->fNext = nil; + sk_free(fBack); + fBack = last; + SkASSERT(last != nil); // else we popped too far + } + + char* end = last->fEnd - fElemSize; + SkASSERT(end >= last->fBegin); + + if (end > last->fBegin) + last->fEnd = end; + else + last->fBegin = last->fEnd = nil; // mark as empty +} + +/////////////////////////////////////////////////////////////////////////////// + +SkDeque::Iter::Iter(const SkDeque& d) : fElemSize(d.fElemSize) +{ + fHead = d.fFront; + while (fHead != nil && fHead->fBegin == nil) + fHead = fHead->fNext; + fPos = fHead ? fHead->fBegin : nil; +} + +void* SkDeque::Iter::next() +{ + char* pos = fPos; + + if (pos) // if we were valid, try to move to the next setting + { + char* next = pos + fElemSize; + SkASSERT(next <= fHead->fEnd); + if (next == fHead->fEnd) // exhausted this chunk, move to next + { + do { + fHead = fHead->fNext; + } while (fHead != nil && fHead->fBegin == nil); + next = fHead ? fHead->fBegin : nil; + } + fPos = next; + } + return pos; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + +SK_SET_BASIC_TRAITS(void); +SK_SET_BASIC_TRAITS(bool); +SK_SET_BASIC_TRAITS(char); +SK_SET_BASIC_TRAITS(unsigned char); +SK_SET_BASIC_TRAITS(short); +SK_SET_BASIC_TRAITS(unsigned short); +SK_SET_BASIC_TRAITS(int); +SK_SET_BASIC_TRAITS(unsigned int); +SK_SET_BASIC_TRAITS(long); +SK_SET_BASIC_TRAITS(unsigned long); +SK_SET_BASIC_TRAITS(float); +SK_SET_BASIC_TRAITS(double); + +#include <new> + +static size_t gTestClassCount; + +//#define SPEW_LIFETIME + +class TestClass { +public: + TestClass() + { + ++gTestClassCount; +#ifdef SPEW_LIFETIME + SkDebugf("gTestClassCount=%d\n", gTestClassCount); +#endif + } + TestClass(int value) : fValue(value) + { + ++gTestClassCount; +#ifdef SPEW_LIFETIME + SkDebugf("gTestClassCount=%d\n", gTestClassCount); +#endif + } + TestClass(const TestClass& src) : fValue(src.fValue) + { + ++gTestClassCount; +#ifdef SPEW_LIFETIME + SkDebugf("gTestClassCount=%d\n", gTestClassCount); +#endif + } + ~TestClass() + { + --gTestClassCount; +#ifdef SPEW_LIFETIME + SkDebugf("~gTestClassCount=%d\n", gTestClassCount); +#endif + } + int fValue; +}; + +SK_SET_TYPE_TRAITS(TestClass, false, false, false, true); + +void SkDeque::UnitTest() +{ + { + SkTDeque<int> d1; + SkTDeque<int> d2; + SkTDeque<TestClass> d3; + SkSTDeque<5, TestClass> d4; + + int i; + + SkASSERT(d1.empty()); + SkASSERT(d2.empty()); + SkASSERT(d3.empty()); + SkASSERT(d4.empty()); + + for (i = 0; i < 100; i++) + { + d1.push_front(i); + SkASSERT(*d1.front() == i); + SkASSERT(*d1.back() == 0); + + d2.push_back(i); + SkASSERT(*d2.back() == i); + SkASSERT(*d2.front() == 0); + + d3.push_front(TestClass(i)); + d3.push_back(TestClass(-i)); + SkASSERT(d3.front()->fValue == i); + SkASSERT(d3.back()->fValue == -i); + + d4.push_front()->fValue = i; + d4.push_back()->fValue = -i; + SkASSERT(d4.front()->fValue == i); + SkASSERT(d4.back()->fValue == -i); + } + + SkASSERT(d1.count() == 100); + SkASSERT(d2.count() == 100); + SkASSERT(d3.count() == 200); + SkASSERT(d4.count() == 200); + + { + SkTDeque<int>::Iter iter(d2); + int* curr; + + i = 0; + while ((curr = iter.next()) != nil) { + SkASSERT(*curr == i); + i += 1; + } + SkASSERT(i == 100); + } + + for (i = 0; i < 50; i++) + { + d1.pop_front(); d1.pop_back(); + d2.pop_front(); d2.pop_back(); + d3.pop_front(); d3.pop_back(); + d4.pop_front(); d4.pop_back(); + } + + SkASSERT(d1.count() == 0); + SkASSERT(d2.count() == 0); + SkASSERT(d3.count() == 100); + SkASSERT(d4.count() == 100); + } + int counter = gTestClassCount; + SkASSERT(counter == 0); +} + +#endif + diff --git a/libs/graphics/sgl/SkDraw.cpp b/libs/graphics/sgl/SkDraw.cpp new file mode 100644 index 0000000000..9b1ed985ef --- /dev/null +++ b/libs/graphics/sgl/SkDraw.cpp @@ -0,0 +1,1139 @@ +#include "SkDraw.h" +#include "SkBlitter.h" +#include "SkBounder.h" +#include "SkCanvas.h" +#include "SkMaskFilter.h" +#include "SkPaint.h" +#include "SkPathEffect.h" +#include "SkRasterizer.h" +#include "SkScan.h" +#include "SkShader.h" +#include "SkStroke.h" +#include "SkTemplatesPriv.h" +#include "SkTextLayout.h" + +/** Helper for allocating small blitters on the stack. +*/ + +#define kBlitterStorageLongCount 40 + +class SkAutoBlitterChoose { +public: + SkAutoBlitterChoose(const SkBitmap& device, const SkMatrix& matrix, const SkPaint& paint) + { + fBlitter = SkBlitter::Choose(device, matrix, paint, fStorage, sizeof(fStorage)); + } + ~SkAutoBlitterChoose(); + + SkBlitter* operator->() { return fBlitter; } + SkBlitter* get() const { return fBlitter; } + +private: + SkBlitter* fBlitter; + uint32_t fStorage[kBlitterStorageLongCount]; +}; + +SkAutoBlitterChoose::~SkAutoBlitterChoose() +{ + if ((void*)fBlitter == (void*)fStorage) + fBlitter->~SkBlitter(); + else + SkDELETE(fBlitter); +} + +class SkAutoBitmapShaderInstall { +public: + SkAutoBitmapShaderInstall(const SkBitmap& src, const SkPaint* paint) : fPaint((SkPaint*)paint) + { + fPrevShader = paint->getShader(); + fPrevShader->safeRef(); + fPaint->setShader(SkShader::CreateBitmapShader( src, false, paint->getFilterType(), + SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, + fStorage, sizeof(fStorage))); + } + ~SkAutoBitmapShaderInstall() + { + SkShader* shader = fPaint->getShader(); + + fPaint->setShader(fPrevShader); + fPrevShader->safeUnref(); + + if ((void*)shader == (void*)fStorage) + shader->~SkShader(); + else + SkDELETE(shader); + } +private: + SkPaint* fPaint; + SkShader* fPrevShader; + uint32_t fStorage[kBlitterStorageLongCount]; +}; + +class SkAutoPaintStyleRestore { +public: + SkAutoPaintStyleRestore(const SkPaint& paint, SkPaint::Style style) + : fPaint((SkPaint&)paint) + { + fStyle = paint.getStyle(); // record the old + fPaint.setStyle(style); // change it to the specified style + } + ~SkAutoPaintStyleRestore() + { + fPaint.setStyle(fStyle); // restore the old + } +private: + SkPaint& fPaint; + SkPaint::Style fStyle; + + // illegal + SkAutoPaintStyleRestore(const SkAutoPaintStyleRestore&); + SkAutoPaintStyleRestore& operator=(const SkAutoPaintStyleRestore&); +}; + +/////////////////////////////////////////////////////////////////////////////// + +SkDraw::SkDraw(const SkCanvas& canvas) +{ + fDevice = &canvas.getCurrBitmap(); + fMatrix = &canvas.getTotalMatrix(); + fClip = &canvas.getTotalClip(); + fBounder = canvas.getBounder(); + fMapPtProc = canvas.getCurrMapPtProc(); + + SkDEBUGCODE(this->validate();) +} + +void SkDraw::drawPaint(const SkPaint& paint) +{ + SkDEBUGCODE(this->validate();) + + if (fClip->isEmpty()) + return; + + SkRect16 devRect; + devRect.set(0, 0, fDevice->width(), fDevice->height()); + + if (fBounder && !fBounder->doIRect(devRect, *fClip)) + return; + + SkAutoBlitterChoose blitter(*fDevice, *fMatrix, paint); + SkScan::FillDevRect(devRect, fClip, blitter.get()); +} + +void SkDraw::drawLine(const SkPoint& start, const SkPoint& stop, const SkPaint& paint) +{ + SkDEBUGCODE(this->validate();) + + if (fClip->isEmpty() || + (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) // nothing to draw + return; + + if (!paint.getPathEffect() && !paint.getMaskFilter() && + !paint.getRasterizer() && paint.getStrokeWidth() == 0) // hairline + { + SkPoint pts[2]; + fMapPtProc(*fMatrix, start.fX, start.fY, &pts[0]); + fMapPtProc(*fMatrix, stop.fX, stop.fY, &pts[1]); + + if (fBounder && !fBounder->doHairline(pts[0], pts[1], paint, *fClip)) + return; + + SkAutoBlitterChoose blitter(*fDevice, *fMatrix, paint); + + if (paint.isAntiAliasOn()) + SkScan::AntiHairLine(pts[0], pts[1], fClip, blitter.get()); + else + SkScan::HairLine(pts[0], pts[1], fClip, blitter.get()); + } + else + { + SkPath path; + // temporarily mark the paint as framing + SkAutoPaintStyleRestore restore(paint, SkPaint::kStroke_Style); + + path.moveTo(start.fX, start.fY); + path.lineTo(stop.fX, stop.fY); + this->drawPath(path, paint); + } +} + +void SkDraw::drawRect(const SkRect& rect, const SkPaint& paint) +{ + SkDEBUGCODE(this->validate();) + + if (fClip->isEmpty() || + (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) // nothing to draw + return; + + if (paint.getPathEffect() || paint.getMaskFilter() || paint.getRasterizer() || + !fMatrix->rectStaysRect() || (paint.getStyle() != SkPaint::kFill_Style && (paint.getStrokeWidth() / 2) > 0)) + { + SkPath tmp; + tmp.addRect(rect); + tmp.setFillType(SkPath::kWinding_FillType); + this->drawPath(tmp, paint); + return; + } + + SkRect devRect; + fMapPtProc(*fMatrix, rect.fLeft, rect.fTop, (SkPoint*)&devRect.fLeft); + fMapPtProc(*fMatrix, rect.fRight, rect.fBottom, (SkPoint*)&devRect.fRight); + devRect.sort(); + + if (fBounder && !fBounder->doRect(devRect, paint, *fClip)) + return; + + SkAutoBlitterChoose blitter(*fDevice, *fMatrix, paint); + + if (paint.getStyle() == SkPaint::kFill_Style) + SkScan::FillRect(devRect, fClip, blitter.get()); + else + { + if (paint.isAntiAliasOn()) + SkScan::AntiHairRect(devRect, fClip, blitter.get()); + else + SkScan::HairRect(devRect, fClip, blitter.get()); + } +} + +void SkDraw::drawDevMask(const SkMask& srcM, const SkPaint& paint) +{ + if (srcM.fBounds.isEmpty()) + return; + + SkMask dstM; + const SkMask* mask = &srcM; + + dstM.fImage = NULL; + SkAutoMaskImage ami(&dstM, false); + + if (paint.getMaskFilter() && + paint.getMaskFilter()->filterMask(&dstM, srcM, *fMatrix, NULL)) + { + mask = &dstM; + } + + if (fBounder && !fBounder->doIRect(mask->fBounds, *fClip)) + return; + + SkAutoBlitterChoose blitter(*fDevice, *fMatrix, paint); + + blitter->blitMaskRegion(*mask, *fClip); +} + +void SkDraw::drawPath(const SkPath& origSrcPath, const SkPaint& paint, const SkMatrix* prePathMatrix, bool srcPathIsMutable) +{ + SkDEBUGCODE(this->validate();) + + if (fClip->isEmpty() || + (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) // nothing to draw + return; + + SkPath* pathPtr = (SkPath*)&origSrcPath; + bool doFill = true; + SkPath tmpPath; + SkMatrix tmpMatrix; + const SkMatrix* matrix = fMatrix; + + if (prePathMatrix) + { + if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style || paint.getRasterizer()) + { + SkPath* result = pathPtr; + + if (!srcPathIsMutable) + { + result = &tmpPath; + srcPathIsMutable = true; + } + pathPtr->transform(*prePathMatrix, result); + pathPtr = result; + } + else + { + tmpMatrix.setConcat(*matrix, *prePathMatrix); + matrix = &tmpMatrix; + } + } + // at this point we're done with prePathMatrix + SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;) + + if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style) + { + doFill = paint.getFillPath(*pathPtr, &tmpPath); + pathPtr = &tmpPath; + } + + if (paint.getRasterizer()) + { + SkMask mask; + if (paint.getRasterizer()->rasterize(*pathPtr, *matrix, &fClip->getBounds(), + paint.getMaskFilter(), &mask, + SkMask::kComputeBoundsAndRenderImage_CreateMode)) + { + this->drawDevMask(mask, paint); + SkMask::FreeImage(mask.fImage); + } + return; + } + + // avoid possibly allocating a new path in transform if we can + SkPath* devPathPtr = srcPathIsMutable ? pathPtr : &tmpPath; + + // transform the path into device space + if (!pathPtr->transform(*matrix, devPathPtr)) + return; + + SkAutoBlitterChoose blitter(*fDevice, *fMatrix, paint); + + // how does filterPath() know to fill or hairline the path??? <mrr> + if (paint.getMaskFilter() && + paint.getMaskFilter()->filterPath(*devPathPtr, *fMatrix, *fClip, fBounder, blitter.get())) + { + return; // filterPath() called the blitter, so we're done + } + + if (fBounder && !fBounder->doPath(*devPathPtr, paint, *fClip, doFill)) + return; + + if (doFill) + { + if (paint.isAntiAliasOn()) + SkScan::AntiFillPath(*devPathPtr, fClip, blitter.get()); + else + SkScan::FillPath(*devPathPtr, fClip, blitter.get()); + } + else // hairline + { + if (paint.isAntiAliasOn()) + SkScan::AntiHairPath(*devPathPtr, fClip, blitter.get()); + else + SkScan::HairPath(*devPathPtr, fClip, blitter.get()); + } +} + +static inline bool just_translate(const SkMatrix& m) +{ + return (m.getType() & ~SkMatrix::kTranslate_Mask) == 0; +} + +void SkDraw::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint& paint) +{ + SkDEBUGCODE(this->validate();) + + if (fClip->isEmpty() || + bitmap.width() == 0 || bitmap.height() == 0 || bitmap.getConfig() == SkBitmap::kNo_Config || + (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) // nothing to draw + return; + + SkAutoPaintStyleRestore restore(paint, SkPaint::kFill_Style); + + SkMatrix matrix = *fMatrix; + matrix.preTranslate(x, y); + + if (NULL == paint.getColorFilter() && just_translate(matrix)) + { + int ix = SkScalarRound(matrix.getTranslateX()); + int iy = SkScalarRound(matrix.getTranslateY()); + U32 storage[kBlitterStorageLongCount]; + SkBlitter* blitter = SkBlitter::ChooseSprite(*fDevice, paint, bitmap, ix, iy, storage, sizeof(storage)); + if (blitter) + { + SkAutoTPlacementDelete<SkBlitter> ad(blitter, storage); + + SkRect16 ir; + ir.set(ix, iy, ix + bitmap.width(), iy + bitmap.height()); + + if (fBounder && !fBounder->doIRect(ir, *fClip)) + return; + + SkRegion::Cliperator iter(*fClip, ir); + const SkRect16& cr = iter.rect(); + + for (; !iter.done(); iter.next()) + { + SkASSERT(!cr.isEmpty()); + #if 0 + LOGI("blitRect(%d %d %d %d) [%d %d %p]\n", cr.fLeft, cr.fTop, cr.width(), cr.height(), + bitmap.width(), bitmap.height(), bitmap.getPixels()); + #endif + blitter->blitRect(cr.fLeft, cr.fTop, cr.width(), cr.height()); + } + return; + } + } + + SkAutoBitmapShaderInstall install(bitmap, &paint); + + // save our state + const SkMatrix* saveMatrix = fMatrix; + SkMatrix::MapPtProc saveProc = fMapPtProc; + + // jam in the new temp state + fMatrix = &matrix; + fMapPtProc = matrix.getMapPtProc(); + + // call ourself with a rect + { + SkRect r; + r.set(0, 0, SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height())); + // is this ok if paint has a rasterizer? <reed> + this->drawRect(r, paint); + } + + // restore our state + fMapPtProc = saveProc; + fMatrix = saveMatrix; +} + +void SkDraw::drawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint& paint) +{ + SkDEBUGCODE(this->validate();) + + SkRect16 bounds; + bounds.set(x, y, x + bitmap.width(), y + bitmap.height()); + + if (fClip->quickReject(bounds) || + bitmap.getConfig() == SkBitmap::kNo_Config || + (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) + { + return; // nothing to draw + } + + SkAutoPaintStyleRestore restore(paint, SkPaint::kFill_Style); + + if (NULL == paint.getColorFilter()) + { + uint32_t storage[kBlitterStorageLongCount]; + SkBlitter* blitter = SkBlitter::ChooseSprite(*fDevice, paint, bitmap, x, y, storage, sizeof(storage)); + + if (blitter) + { + SkAutoTPlacementDelete<SkBlitter> ad(blitter, storage); + + if (fBounder && !fBounder->doIRect(bounds, *fClip)) + return; + + SkRegion::Cliperator iter(*fClip, bounds); + const SkRect16& cr = iter.rect(); + + for (; !iter.done(); iter.next()) + { + SkASSERT(!cr.isEmpty()); + blitter->blitRect(cr.fLeft, cr.fTop, cr.width(), cr.height()); + } + return; + } + } + + SkAutoBitmapShaderInstall install(bitmap, &paint); + + // save our state + const SkMatrix* saveMatrix = fMatrix; + SkMatrix::MapPtProc saveProc = fMapPtProc; + SkMatrix matrix; + SkRect r; + + // get a scalar version of our rect + r.set(bounds); + + // tell the shader our offset + matrix.setTranslate(r.fLeft, r.fTop); + paint.getShader()->setLocalMatrix(matrix); + + // jam in the new temp state + matrix.reset(); + fMatrix = &matrix; + fMapPtProc = matrix.getMapPtProc(); + + // call ourself with a rect + { + // is this OK if paint has a rasterizer? <reed> + this->drawRect(r, paint); + } + + // restore our state + fMapPtProc = saveProc; + fMatrix = saveMatrix; +} + +/////////////////////////////////////////////////////////////////////////////////// + +#include "SkScalerContext.h" +#include "SkGlyphCache.h" +#include "SkUtils.h" + +static void measure_text(SkGlyphCache* cache, SkUnicodeWalkerProc textProc, const char text[], + size_t byteLength, SkVector* stopVector) +{ + SkFixed x = 0, y = 0; + const char* stop = text + byteLength; + + while (text < stop) + { + const SkGlyph& glyph = cache->getMetrics(textProc(&text)); + x += glyph.fAdvanceX; + y += glyph.fAdvanceY; + } + stopVector->set(SkFixedToScalar(x), SkFixedToScalar(y)); + + SkASSERT(text == stop); +} + +static void measure_layout(SkGlyphCache* cache, const SkTextLayout::Rec rec[], int count, + const SkMatrix& matrix, SkVector* stopVector) +{ + SkFixed x = 0, y = 0; + + for (int i = 0; i < count; i++) + { + // should pass glyphID to the cache, when we have that + const SkGlyph& glyph = cache->getMetrics(rec[i].charCode()); + SkVector adv; + adv.set(rec[i].fDeltaAdvance, 0); + matrix.mapVectors(&adv, 1); + x += glyph.fAdvanceX + SkScalarToFixed(adv.fX); + y += glyph.fAdvanceY + SkScalarToFixed(adv.fY); + } + stopVector->set(SkFixedToScalar(x), SkFixedToScalar(y)); +} + +void SkDraw::drawText_asPaths(SkUnicodeWalkerProc textProc, const char text[], size_t byteLength, SkScalar x, SkScalar y, const SkPaint& paint) +{ + SkDEBUGCODE(this->validate();) + + SkTextToPathIter iter(textProc, text, byteLength, paint, true, true); + + SkMatrix matrix; + matrix.setScale(iter.getPathScale(), iter.getPathScale(), 0, 0); + matrix.postTranslate(x, y); + + const SkPath* iterPath; + SkScalar xpos, prevXPos = 0; + + while ((iterPath = iter.next(&xpos)) != NULL) + { + matrix.postTranslate(xpos - prevXPos, 0); + this->drawPath(*iterPath, iter.getPaint(), &matrix, false); + prevXPos = xpos; + } +} + +#define kStdStrikeThru_Offset (-SK_Scalar1 * 6 / 21) +#define kStdUnderline_Offset (SK_Scalar1 / 9) +#define kStdUnderline_Thickness (SK_Scalar1 / 18) + +static void draw_paint_rect(SkDraw* draw, const SkPaint& paint, const SkRect& r, SkScalar textSize) +{ + if (paint.getStyle() == SkPaint::kFill_Style) + draw->drawRect(r, paint); + else + { + SkPaint p(paint); + p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth())); + draw->drawRect(r, p); + } +} + +static void handle_aftertext(SkDraw* draw, const SkPaint& paint, SkScalar width, const SkPoint& start) +{ + U32 flags = paint.getFlags(); + + if (flags & (SkPaint::kUnderlineText_Mask | SkPaint::kStrikeThruText_Mask)) + { + SkScalar textSize = paint.getTextSize(); + SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness); + SkRect r; + + r.fLeft = start.fX; + r.fRight = start.fX + width; + + if (flags & SkPaint::kUnderlineText_Mask) + { + SkScalar offset = start.fY + SkScalarMul(textSize, kStdUnderline_Offset); + r.fTop = offset; + r.fBottom = offset + height; + draw_paint_rect(draw, paint, r, textSize); + } + if (flags & SkPaint::kStrikeThruText_Mask) + { + SkScalar offset = start.fY + SkScalarMul(textSize, kStdStrikeThru_Offset); + r.fTop = offset; + r.fBottom = offset + height; + draw_paint_rect(draw, paint, r, textSize); + } + } +} + +#if defined _WIN32 && _MSC_VER >= 1300 // disable warning : local variable used without having been initialized +#pragma warning ( push ) +#pragma warning ( disable : 4701 ) +#endif + +static inline void draw_one_glyph(const SkGlyph& glyph, int left, int top, SkBounder* bounder, + const SkRegion& clip, SkBlitter* blitter, SkGlyphCache* cache) +{ + SkMask mask; + + int right = left + glyph.fWidth; + int bottom = top + glyph.fHeight; + + mask.fBounds.set(left, top, right, bottom); + + if (bounder == NULL && clip.quickContains(left, top, right, bottom)) + { + uint8_t* aa = (uint8_t*)glyph.fImage; + if (aa == NULL) + aa = (uint8_t*)cache->findImage(glyph.fCharCode); + + if (aa) + { + mask.fRowBytes = glyph.fRowBytes; + mask.fFormat = glyph.fMaskFormat; + mask.fImage = aa; + blitter->blitMask(mask, mask.fBounds); + } + } + else + { + SkRegion::Cliperator clipper(clip, mask.fBounds); + if (!clipper.done()) + { + const SkRect16& cr = clipper.rect(); + const uint8_t* aa = (const uint8_t*)glyph.fImage; + if (NULL == aa) + aa = (const uint8_t*)cache->findImage(glyph.fCharCode); + + if (aa && (bounder == NULL || bounder->doIRect(cr, clip))) + { + mask.fRowBytes = glyph.fRowBytes; + mask.fFormat = glyph.fMaskFormat; + mask.fImage = (uint8_t*)aa; + do { + blitter->blitMask(mask, cr); + clipper.next(); + } while (!clipper.done()); + } + } + } +} + +void SkDraw::drawText(SkUnicodeWalkerProc textProc, const char text[], size_t byteLength, SkScalar x, SkScalar y, const SkPaint& paint) +{ + SkASSERT(byteLength == 0 || text != NULL); + + SkDEBUGCODE(this->validate();) + + if (text == NULL || byteLength == 0 || + fClip->isEmpty() || + (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) // nothing to draw + return; + + SkASSERT(textProc); + + SkScalar underlineWidth = 0; + SkPoint underlineStart; + + if (paint.getFlags() & (SkPaint::kUnderlineText_Mask | SkPaint::kStrikeThruText_Mask)) + { + underlineWidth = paint.privateMeasureText(textProc, text, byteLength, NULL, NULL); + + SkScalar offsetX = 0; + if (paint.getTextAlign() == SkPaint::kCenter_Align) + offsetX = SkScalarHalf(underlineWidth); + else if (paint.getTextAlign() == SkPaint::kRight_Align) + offsetX = underlineWidth; + + underlineStart.set(x - offsetX, y); + } + + if (paint.isLinearTextOn() || + (fMatrix->getType() & SkMatrix::kPerspective_Mask)) + { + this->drawText_asPaths(textProc, text, byteLength, x, y, paint); + handle_aftertext(this, paint, underlineWidth, underlineStart); + return; + } + + SkAutoGlyphCache autoCache(paint, fMatrix); + SkGlyphCache* cache = autoCache.getCache(); + SkAutoBlitterChoose blitter(*fDevice, *fMatrix, paint); + SkTextLayout* layout = paint.getTextLayout(); + + // transform our starting point + { + SkPoint loc; + fMapPtProc(*fMatrix, x, y, &loc); + x = loc.fX; + y = loc.fY; + } + + if (paint.getTextAlign() != SkPaint::kLeft_Align) // need to measure first + { + SkVector stop; + + if (layout) + { + SkAutoSTMalloc<32, SkTextLayout::Rec> storage(byteLength); + SkTextLayout::Rec* rec = storage.get(); + + int count = layout->layout(paint, text, byteLength, textProc, rec); + measure_layout(cache, rec, count, *fMatrix, &stop); + } + else + measure_text(cache, textProc, text, byteLength, &stop); + + SkScalar stopX = stop.fX; + SkScalar stopY = stop.fY; + + if (paint.getTextAlign() == SkPaint::kCenter_Align) + { + stopX = SkScalarHalf(stopX); + stopY = SkScalarHalf(stopY); + } + x -= stopX; + y -= stopY; + } + + // add a half now so we can trunc rather than round in the loop + SkFixed fx = SkScalarToFixed(x) + SK_FixedHalf; + SkFixed fy = SkScalarToFixed(y) + SK_FixedHalf; + const char* stop = text + byteLength; + const SkRegion& clip = *fClip; + SkBounder* bounder = fBounder; + SkBlitter* blit = blitter.get(); + + if (layout) + { + SkAutoSTMalloc<32, SkTextLayout::Rec> storage(byteLength); + SkTextLayout::Rec* rec = storage.get(); + + int count = layout->layout(paint, text, byteLength, textProc, rec); + for (int i = 0; i < count; i++) + { + // should pass rec[i].glyphID when we have it + const SkGlyph& glyph = cache->getMetrics(rec[i].charCode()); + SkVector advance; + + advance.set(rec[i].fDeltaAdvance, 0); + fMatrix->mapVectors(&advance, 1); + SkFixed dx = SkScalarToFixed(advance.fX); + SkFixed dy = SkScalarToFixed(advance.fY); + + if (glyph.fWidth) { + draw_one_glyph( glyph, + SkFixedFloor(fx + (dx >> 1)) + glyph.fLeft, + SkFixedFloor(fy + (dy >> 1)) + glyph.fTop, + fBounder, *fClip, blit, cache); + } + fx += glyph.fAdvanceX + dx; + fy += glyph.fAdvanceY + dy; + } + } + else // no layout object + { + while (text < stop) + { + const SkGlyph& glyph = cache->getMetrics(textProc(&text)); + + if (glyph.fWidth) { + draw_one_glyph( glyph, + SkFixedFloor(fx) + glyph.fLeft, SkFixedFloor(fy) + glyph.fTop, + bounder, clip, blit, cache); + } + fx += glyph.fAdvanceX; + fy += glyph.fAdvanceY; + } + } + + if (underlineWidth) + { + autoCache.release(); // release this now to free up the RAM + handle_aftertext(this, paint, underlineWidth, underlineStart); + } +} + +typedef void (*AlignProc)(const SkPoint&, const SkGlyph&, SkPoint16*); + +static void leftAlignProc(const SkPoint& loc, const SkGlyph& glyph, SkPoint16* dst) +{ + dst->set(SkScalarRound(loc.fX) + glyph.fLeft, + SkScalarRound(loc.fY) + glyph.fTop); +} + +static void centerAlignProc(const SkPoint& loc, const SkGlyph& glyph, SkPoint16* dst) +{ + dst->set(SkFixedRound(SkScalarToFixed(loc.fX) - (glyph.fAdvanceX >> 1)) + glyph.fLeft, + SkFixedRound(SkScalarToFixed(loc.fY) - (glyph.fAdvanceY >> 1)) + glyph.fTop); +} + +static void rightAlignProc(const SkPoint& loc, const SkGlyph& glyph, SkPoint16* dst) +{ + dst->set(SkFixedRound(SkScalarToFixed(loc.fX) - glyph.fAdvanceX) + glyph.fLeft, + SkFixedRound(SkScalarToFixed(loc.fY) - glyph.fAdvanceY) + glyph.fTop); +} + +static AlignProc pick_align_proc(SkPaint::Align align) +{ + static const AlignProc gProcs[] = { leftAlignProc, centerAlignProc, rightAlignProc }; + + SkASSERT((unsigned)align < SK_ARRAY_COUNT(gProcs)); + + return gProcs[align]; +} + +void SkDraw::drawPosText(SkUnicodeWalkerProc textProc, const char text[], size_t byteLength, + const SkPoint pos[], const SkPaint& paint) +{ + SkASSERT(byteLength == 0 || text != NULL); + + SkDEBUGCODE(this->validate();) + + if (text == NULL || byteLength == 0 || + fClip->isEmpty() || + (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) // nothing to draw + return; + + SkASSERT(textProc); + + if (paint.isLinearTextOn() || + (fMatrix->getType() & SkMatrix::kPerspective_Mask)) + { +// this->drawText_asPaths(textProc, text, byteLength, x, y, paint); + return; + } + + SkAutoGlyphCache autoCache(paint, fMatrix); + SkGlyphCache* cache = autoCache.getCache(); + SkAutoBlitterChoose blitter(*fDevice, *fMatrix, paint); + + const char* stop = text + byteLength; + const SkRegion& clip = *fClip; + SkBounder* bounder = fBounder; + SkBlitter* blit = blitter.get(); + SkMatrix::MapPtProc mapPtProc = fMapPtProc; + const SkMatrix& matrix = *fMatrix; + AlignProc alignProc = pick_align_proc(paint.getTextAlign()); + + while (text < stop) + { + const SkGlyph& glyph = cache->getMetrics(textProc(&text)); + + if (glyph.fWidth) + { + SkPoint loc; + mapPtProc(matrix, pos->fX, pos->fY, &loc); + + SkPoint16 devLoc; + alignProc(loc, glyph, &devLoc); + + draw_one_glyph( glyph, devLoc.fX, devLoc.fY, + bounder, clip, blit, cache); + } + pos += 1; + } +} + +#if defined _WIN32 && _MSC_VER >= 1300 +#pragma warning ( pop ) +#endif + +//////////////////////////////////////////////////////////////////////////////////////////// + +#include "SkPathMeasure.h" + +static void morphpoints(SkPoint dst[], const SkPoint src[], int count, + SkPathMeasure& meas, SkScalar offset, SkScalar scale) +{ + for (int i = 0; i < count; i++) + { + SkPoint pos; + SkVector tangent; + + SkScalar sx = SkScalarMul(src[i].fX, scale) + offset; + SkScalar sy = SkScalarMul(src[i].fY, scale); + + meas.getPosTan(sx, &pos, &tangent); + + SkMatrix matrix; + SkPoint pt; + + pt.set(sx, sy); + matrix.setSinCos(tangent.fY, tangent.fX, 0, 0); + matrix.preTranslate(-sx, 0); + matrix.postTranslate(pos.fX, pos.fY); + matrix.mapPoints(&dst[i], &pt, 1); + } +} + +/* TODO + + Need differentially more subdivisions when the follow-path is curvy. Not sure how to + determine that, but we need it. I guess a cheap answer is let the caller tell us, + but that seems like a cop-out. Another answer is to get Rob Johnson to figure it out. +*/ +static void morphpath(SkPath* dst, const SkPath& src, SkPathMeasure& meas, + SkScalar offset, SkScalar scale) +{ + SkPath::Iter iter(src, false); + SkPoint srcP[4], dstP[3]; + SkPath::Verb verb; + + while ((verb = iter.next(srcP)) != SkPath::kDone_Verb) + { + switch (verb) { + case SkPath::kMove_Verb: + morphpoints(dstP, srcP, 1, meas, offset, scale); + dst->moveTo(dstP[0]); + break; + case SkPath::kLine_Verb: + srcP[2] = srcP[1]; + srcP[1].set(SkScalarAve(srcP[0].fX, srcP[2].fX), + SkScalarAve(srcP[0].fY, srcP[2].fY)); + // fall through to quad + case SkPath::kQuad_Verb: + morphpoints(dstP, &srcP[1], 2, meas, offset, scale); + dst->quadTo(dstP[0], dstP[1]); + break; + case SkPath::kCubic_Verb: + morphpoints(dstP, &srcP[1], 3, meas, offset, scale); + dst->cubicTo(dstP[0], dstP[1], dstP[2]); + break; + case SkPath::kClose_Verb: + dst->close(); + break; + default: + SkASSERT(!"unknown verb"); + break; + } + } +} + +void SkDraw::drawTextOnPath(SkUnicodeWalkerProc textProc, const char text[], size_t byteLength, + const SkPath& follow, SkScalar offset, const SkPaint& paint) +{ + SkASSERT(byteLength == 0 || text != NULL); + + if (text == NULL || byteLength == 0 || + fClip->getBounds().isEmpty() || + (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) // nothing to draw + return; + + SkTextToPathIter iter(textProc, text, byteLength, paint, true, true); + SkPathMeasure meas(follow, false); + + if (paint.getTextAlign() != SkPaint::kLeft_Align) // need to measure first + { + SkScalar pathLen = meas.getLength(); + if (paint.getTextAlign() == SkPaint::kCenter_Align) + pathLen = SkScalarHalf(pathLen); + offset += pathLen; + } + + const SkPath* iterPath; + SkScalar xpos; + while ((iterPath = iter.next(&xpos)) != NULL) + { + SkPath tmp; + morphpath(&tmp, *iterPath, meas, offset + xpos, iter.getPathScale()); + this->drawPath(tmp, iter.getPaint()); + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + +void SkDraw::validate() const +{ + SkASSERT(fDevice != NULL); + SkASSERT(fMatrix != NULL); + SkASSERT(fClip != NULL); + + const SkRect16& cr = fClip->getBounds(); + SkRect16 br; + + br.set(0, 0, fDevice->width(), fDevice->height()); + SkASSERT(br.contains(cr)); +} + +#endif + +////////////////////////////////////////////////////////////////////////////////////////// + +bool SkBounder::doIRect(const SkRect16& r, const SkRegion& clip) +{ + SkRect16 rr; + return rr.intersect(clip.getBounds(), r) && this->onIRect(rr); +} + +bool SkBounder::doHairline(const SkPoint& pt0, const SkPoint& pt1, const SkPaint& paint, const SkRegion& clip) +{ + SkRect16 r; + SkScalar v0, v1; + + v0 = pt0.fX; + v1 = pt1.fX; + if (v0 > v1) + SkTSwap<SkScalar>(v0, v1); + r.fLeft = SkToS16(SkScalarFloor(v0)); + r.fRight = SkToS16(SkScalarCeil(v1)); + + v0 = pt0.fY; + v1 = pt1.fY; + if (v0 > v1) + SkTSwap<SkScalar>(v0, v1); + r.fTop = SkToS16(SkScalarFloor(v0)); + r.fBottom = SkToS16(SkScalarCeil(v1)); + + if (paint.isAntiAliasOn()) + r.inset(-1, -1); + + return this->doIRect(r, clip); +} + +bool SkBounder::doRect(const SkRect& rect, const SkPaint& paint, const SkRegion& clip) +{ + SkRect16 r; + + if (paint.getStyle() == SkPaint::kFill_Style) + rect.round(&r); + else + { + int rad = -1; + rect.roundOut(&r); + if (paint.isAntiAliasOn()) + rad = -2; + r.inset(rad, rad); + } + return this->doIRect(r, clip); +} + +bool SkBounder::doPath(const SkPath& path, const SkPaint& paint, const SkRegion& clip, bool doFill) +{ + SkRect bounds; + SkRect16 r; + + path.computeBounds(&bounds, SkPath::kFast_BoundsType); + + if (doFill) + bounds.round(&r); + else // hairline + bounds.roundOut(&r); + + if (paint.isAntiAliasOn()) + r.inset(-1, -1); + + return this->doIRect(r, clip); +} + +void SkBounder::commit() +{ + // override in subclass +} + +//////////////////////////////////////////////////////////////////////////////////////////////// + +#include "SkPath.h" +#include "SkDraw.h" +#include "SkRegion.h" +#include "SkBlitter.h" + +static bool compute_bounds(const SkPath& devPath, const SkRect16* clipBounds, + SkMaskFilter* filter, const SkMatrix* filterMatrix, + SkRect16* bounds) +{ + if (devPath.isEmpty()) + return false; + + SkPoint16 margin; + margin.set(0, 0); + + // init our bounds from the path + { + SkRect pathBounds; + devPath.computeBounds(&pathBounds, SkPath::kExact_BoundsType); + pathBounds.inset(-SK_ScalarHalf, -SK_ScalarHalf); + pathBounds.roundOut(bounds); + } + + if (filter) + { + SkASSERT(filterMatrix); + + SkMask srcM, dstM; + + srcM.fBounds = *bounds; + srcM.fFormat = SkMask::kA8_Format; + srcM.fImage = NULL; + if (!filter->filterMask(&dstM, srcM, *filterMatrix, &margin)) + return false; + + *bounds = dstM.fBounds; + } + + if (clipBounds && !SkRect16::Intersects(*clipBounds, *bounds)) + return false; + + // (possibly) trim the srcM bounds to reflect the clip + // (plus whatever slop the filter needs) + if (clipBounds && !clipBounds->contains(*bounds)) + { + SkRect16 tmp = *bounds; + (void)tmp.intersect(*clipBounds); + tmp.inset(-margin.fX, -margin.fY); + (void)bounds->intersect(tmp); + } + + return true; +} + +static void draw_into_mask(const SkMask& mask, const SkPath& devPath) +{ + SkBitmap bm; + SkDraw draw; + SkRegion clipRgn; + SkMatrix matrix; + SkPaint paint; + + bm.setConfig(SkBitmap::kA8_Config, mask.fBounds.width(), mask.fBounds.height(), mask.fRowBytes); + bm.setPixels(mask.fImage); + + clipRgn.setRect(0, 0, mask.fBounds.width(), mask.fBounds.height()); + matrix.setTranslate(-SkIntToScalar(mask.fBounds.fLeft), + -SkIntToScalar(mask.fBounds.fTop)); + + draw.fDevice = &bm; + draw.fClip = &clipRgn; + draw.fMatrix = &matrix; + draw.fMapPtProc = matrix.getMapPtProc(); + draw.fBounder = NULL; + paint.setAntiAliasOn(true); + draw.drawPath(devPath, paint); +} + +bool SkDraw::DrawToMask(const SkPath& devPath, const SkRect16* clipBounds, + SkMaskFilter* filter, const SkMatrix* filterMatrix, + SkMask* mask, SkMask::CreateMode mode) +{ + if (SkMask::kJustRenderImage_CreateMode != mode) + { + if (!compute_bounds(devPath, clipBounds, filter, filterMatrix, &mask->fBounds)) + return false; + } + + if (SkMask::kComputeBoundsAndRenderImage_CreateMode == mode) + { + mask->fFormat = SkMask::kA8_Format; + mask->fRowBytes = mask->fBounds.width(); + mask->fImage = SkMask::AllocImage(mask->computeImageSize()); + memset(mask->fImage, 0, mask->computeImageSize()); + } + + if (SkMask::kJustComputeBounds_CreateMode != mode) + draw_into_mask(*mask, devPath); + + return true; +} + diff --git a/libs/graphics/sgl/SkDraw.h b/libs/graphics/sgl/SkDraw.h new file mode 100644 index 0000000000..488fa793f0 --- /dev/null +++ b/libs/graphics/sgl/SkDraw.h @@ -0,0 +1,89 @@ +#ifndef SkDraw_DEFINED +#define SkDraw_DEFINED + +#include "SkBitmap.h" +#include "SkMask.h" +#include "SkMatrix.h" +#include "SkPaint.h" +#include "SkRect.h" + +class SkBounder; +class SkCanvas; +class SkPath; +class SkRegion; + +class SkDraw { +public: + SkDraw() {} + SkDraw(const SkCanvas&); + + void drawPaint(const SkPaint&); + void drawLine(const SkPoint& start, const SkPoint& stop, const SkPaint&); + void drawRect(const SkRect&, const SkPaint&); + /* To save on mallocs, we allow a flag that tells us that srcPath is mutable, so that we don't have to + make copies of it as we transform it. + */ + void drawPath(const SkPath& srcPath, const SkPaint&, const SkMatrix* prePathMatrix, bool srcPathIsMutable); + void drawBitmap(const SkBitmap&, SkScalar x, SkScalar y, const SkPaint&); + void drawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint& paint); + void drawText(SkUnicodeWalkerProc, const char text[], size_t byteLength, SkScalar x, SkScalar y, const SkPaint& paint); + void drawPosText(SkUnicodeWalkerProc, const char text[], size_t byteLength, const SkPoint pos[], const SkPaint& paint); + void drawTextOnPath(SkUnicodeWalkerProc, const char text[], size_t byteLength, const SkPath& follow, + SkScalar offset, const SkPaint& paint); + + void drawPath(const SkPath& src, const SkPaint& paint) + { + this->drawPath(src, paint, NULL, false); + } + + /** Helper function that creates a mask from a path and an optional maskfilter. + Note however, that the resulting mask will not have been actually filtered, + that must be done afterwards (by calling filterMask). The maskfilter is provided + solely to assist in computing the mask's bounds (if the mode requests that). + */ + static bool DrawToMask(const SkPath& devPath, const SkRect16* clipBounds, + SkMaskFilter* filter, const SkMatrix* filterMatrix, + SkMask* mask, SkMask::CreateMode mode); + +private: + void drawText_asPaths(SkUnicodeWalkerProc, const char text[], size_t byteLength, SkScalar x, SkScalar y, const SkPaint&); + void drawDevMask(const SkMask& mask, const SkPaint&); + +#ifdef SK_DEBUG + void validate() const; +#endif + +public: + const SkBitmap* fDevice; // required + const SkMatrix* fMatrix; // required + const SkRegion* fClip; // required + SkMatrix::MapPtProc fMapPtProc; // required + SkBounder* fBounder; // optional +}; + +class SkTextToPathIter { +public: + SkTextToPathIter(SkUnicodeWalkerProc, const char text[], size_t length, const SkPaint&, + bool applyStrokeAndPathEffects, bool forceLinearTextOn); + ~SkTextToPathIter(); + + const SkPaint& getPaint() const { return fPaint; } + SkScalar getPathScale() const { return fScale; } + + const SkPath* next(SkScalar* xpos); //!< returns nil when there are no more paths + +private: + SkGlyphCache* fCache; + SkPaint fPaint; + SkScalar fScale, fPrevAdvance; + const char* fText; + const char* fStop; + SkUnicodeWalkerProc fTextProc; + + const SkPath* fPath; // returned in next + SkScalar fXPos; // accumulated xpos, returned in next +}; + +#endif + + diff --git a/libs/graphics/sgl/SkEdge.cpp b/libs/graphics/sgl/SkEdge.cpp new file mode 100644 index 0000000000..d5e5590635 --- /dev/null +++ b/libs/graphics/sgl/SkEdge.cpp @@ -0,0 +1,429 @@ +#include "SkEdge.h" +#include "SkFDot6.h" + +/* + In setLine, setQuadratic, setCubic, the first thing we do is to convert + the points into FDot6. This is modulated by the shift parameter, which + will either be 0, or something like 2 for antialiasing. + + In the float case, we want to turn the float into .6 by saying pt * 64, + or pt * 256 for antialiasing. This is implemented as 1 << (shift + 6). + + In the fixed case, we want to turn the fixed into .6 by saying pt >> 10, + or pt >> 8 for antialiasing. This is implemented as pt >> (10 - shift). +*/ + +///////////////////////////////////////////////////////////////////////// + +int SkEdge::setLine(const SkPoint pts[2], const SkRect16* clip, int shift) +{ + SkFDot6 x0, y0, x1, y1; + + { +#ifdef SK_SCALAR_IS_FLOAT + float scale = float(1 << (shift + 6)); + x0 = int(pts[0].fX * scale); + y0 = int(pts[0].fY * scale); + x1 = int(pts[1].fX * scale); + y1 = int(pts[1].fY * scale); +#else + shift = 10 - shift; + x0 = pts[0].fX >> shift; + y0 = pts[0].fY >> shift; + x1 = pts[1].fX >> shift; + y1 = pts[1].fY >> shift; +#endif + } + + int winding = 1; + + if (y0 > y1) + { + SkTSwap(x0, x1); + SkTSwap(y0, y1); + winding = -1; + } + + int top = SkFDot6Round(y0); + int bot = SkFDot6Round(y1); + + // are we a zero-height line? + if (top == bot) + return 0; + + // are we completely above or below the clip? + if (clip && (top >= clip->fBottom || bot <= clip->fTop)) + return 0; + + SkFixed slope = SkFDot6Div(x1 - x0, y1 - y0); + + fX = SkFDot6ToFixed(x0 + SkFixedMul(slope, (32 - y0) & 63)); // + SK_Fixed1/2 + fDX = slope; + fFirstY = SkToS16(top); + fLastY = SkToS16(bot - 1); + fCurveCount = 0; + fWinding = SkToS8(winding); + fCurveShift = 0; + + if (clip) + this->chopLineWithClip(*clip); + return 1; +} + +// called from a curve subclass +int SkEdge::updateLine(SkFixed x0, SkFixed y0, SkFixed x1, SkFixed y1) +{ + SkASSERT(fWinding == 1 || fWinding == -1); + SkASSERT(fCurveCount != 0); + SkASSERT(fCurveShift != 0); + + y0 >>= 10; + y1 >>= 10; + + SkASSERT(y0 <= y1); + + int top = SkFDot6Round(y0); + int bot = SkFDot6Round(y1); + +// SkASSERT(top >= fFirstY); + + // are we a zero-height line? + if (top == bot) + return 0; + + x0 >>= 10; + x1 >>= 10; + + SkFixed slope = SkFDot6Div(x1 - x0, y1 - y0); + + fX = SkFDot6ToFixed(x0 + SkFixedMul(slope, (32 - y0) & 63)); // + SK_Fixed1/2 + fDX = slope; + fFirstY = SkToS16(top); + fLastY = SkToS16(bot - 1); + + return 1; +} + +void SkEdge::chopLineWithClip(const SkRect16& clip) +{ + int top = fFirstY; + + SkASSERT(top < clip.fBottom); + + // clip the line to the top + if (top < clip.fTop) + { + SkASSERT(fLastY >= clip.fTop); + fX += fDX * (clip.fTop - top); + fFirstY = clip.fTop; + } +} + +///////////////////////////////////////////////////////////////////////// + +static inline SkFDot6 cheap_distance(SkFDot6 dx, SkFDot6 dy) +{ + dx = SkAbs32(dx); + dy = SkAbs32(dy); + // return max + min/2 + if (dx > dy) + dx += dy >> 1; + else + dx = dy + (dx >> 1); + return dx; +} + +static inline int diff_to_shift(SkFDot6 dx, SkFDot6 dy) +{ + // cheap calc of distance from center of p0-p2 to the center of the curve + SkFDot6 dist = cheap_distance(dx, dy); + + // shift down dist (it is currently in dot6) + // down by 5 should give us 1/2 pixel accuracy (assuming our dist is accurate...) + // this is chosen by heuristic: make it as big as possible (to minimize segments) + // ... but small enough so that our curves still look smooth + dist >>= 5; + + // each subdivision (shift value) cuts this dist (error) by 1/4 + return (32 - SkCLZ(dist)) >> 1; +} + +int SkQuadraticEdge::setQuadratic(const SkPoint pts[3], const SkRect16* clip, int shift) +{ + SkFDot6 x0, y0, x1, y1, x2, y2; + + { +#ifdef SK_SCALAR_IS_FLOAT + float scale = float(1 << (shift + 6)); + x0 = int(pts[0].fX * scale); + y0 = int(pts[0].fY * scale); + x1 = int(pts[1].fX * scale); + y1 = int(pts[1].fY * scale); + x2 = int(pts[2].fX * scale); + y2 = int(pts[2].fY * scale); +#else + shift = 10 - shift; + x0 = pts[0].fX >> shift; + y0 = pts[0].fY >> shift; + x1 = pts[1].fX >> shift; + y1 = pts[1].fY >> shift; + x2 = pts[2].fX >> shift; + y2 = pts[2].fY >> shift; +#endif + } + + int winding = 1; + if (y0 > y2) + { + SkTSwap(x0, x2); + SkTSwap(y0, y2); + winding = -1; + } + SkASSERT(y0 <= y1 && y1 <= y2); + + int top = SkFDot6Round(y0); + int bot = SkFDot6Round(y2); + + // are we a zero-height quad (line)? + if (top == bot) + return 0; + // are we completely above or below the clip? + if (clip && (top >= clip->fBottom || bot <= clip->fTop)) + return 0; + + // compute number of steps needed (1 << shift) + { + SkFDot6 dx = ((x1 << 1) - x0 - x2) >> 2; + SkFDot6 dy = ((y1 << 1) - y0 - y2) >> 2; + shift = diff_to_shift(dx, dy); + } + // need at least 1 subdivision for our bias trick + if (shift == 0) + shift = 1; + + fWinding = SkToS8(winding); + fCurveShift = SkToU8(shift); + fCurveCount = SkToS16(1 << shift); + + SkFixed A = SkFDot6ToFixed(x0 - x1 - x1 + x2); + SkFixed B = SkFDot6ToFixed(x1 - x0 + x1 - x0); + + fQx = SkFDot6ToFixed(x0); + fQDx = B + (A >> shift); // biased by shift + fQDDx = A >> (shift - 1); // biased by shift + + A = SkFDot6ToFixed(y0 - y1 - y1 + y2); + B = SkFDot6ToFixed(y1 - y0 + y1 - y0); + + fQy = SkFDot6ToFixed(y0); + fQDy = B + (A >> shift); // biased by shift + fQDDy = A >> (shift - 1); // biased by shift + + fQLastX = SkFDot6ToFixed(x2); + fQLastY = SkFDot6ToFixed(y2); + + if (clip) + { + do { + for (;!this->updateQuadratic();) + ; + } while (!this->intersectsClip(*clip)); + this->chopLineWithClip(*clip); + return 1; + } + return this->updateQuadratic(); +} + +int SkQuadraticEdge::updateQuadratic() +{ + int success; + int count = fCurveCount; + SkFixed oldx = fQx; + SkFixed oldy = fQy; + SkFixed newx, newy; + int shift = fCurveShift; + + SkASSERT(count > 0); + + do { + if (--count > 0) + { + newx = oldx + (fQDx >> shift); + fQDx += fQDDx; + newy = oldy + (fQDy >> shift); + fQDy += fQDDy; + } + else // last segment + { + newx = fQLastX; + newy = fQLastY; + } + success = this->updateLine(oldx, oldy, newx, newy); + oldx = newx; + oldy = newy; + } while (count > 0 && !success); + + fQx = newx; + fQy = newy; + fCurveCount = SkToS16(count); + return success; +} + +///////////////////////////////////////////////////////////////////////// + +/* f(1/3) = (8a + 12b + 6c + d) / 27 + f(2/3) = (a + 6b + 12c + 8d) / 27 + + f(1/3)-b = (8a - 15b + 6c + d) / 27 + f(2/3)-c = (a + 6b - 15c + 8d) / 27 + + use 16/512 to approximate 1/27 +*/ +static SkFDot6 cubic_delta_from_line(SkFDot6 a, SkFDot6 b, SkFDot6 c, SkFDot6 d) +{ + SkFDot6 oneThird = ((a << 3) - ((b << 4) - b) + 6*c + d) * 19 >> 9; + SkFDot6 twoThird = (a + 6*b - ((c << 4) - c) + (d << 3)) * 19 >> 9; + + return SkMax32(SkAbs32(oneThird), SkAbs32(twoThird)); +} + +int SkCubicEdge::setCubic(const SkPoint pts[4], const SkRect16* clip, int shift) +{ + SkFDot6 x0, y0, x1, y1, x2, y2, x3, y3; + + { +#ifdef SK_SCALAR_IS_FLOAT + float scale = float(1 << (shift + 6)); + x0 = int(pts[0].fX * scale); + y0 = int(pts[0].fY * scale); + x1 = int(pts[1].fX * scale); + y1 = int(pts[1].fY * scale); + x2 = int(pts[2].fX * scale); + y2 = int(pts[2].fY * scale); + x3 = int(pts[3].fX * scale); + y3 = int(pts[3].fY * scale); +#else + shift = 10 - shift; + x0 = pts[0].fX >> shift; + y0 = pts[0].fY >> shift; + x1 = pts[1].fX >> shift; + y1 = pts[1].fY >> shift; + x2 = pts[2].fX >> shift; + y2 = pts[2].fY >> shift; + x3 = pts[3].fX >> shift; + y3 = pts[3].fY >> shift; +#endif + } + + int winding = 1; + if (y0 > y3) + { + SkTSwap(x0, x3); + SkTSwap(x1, x2); + SkTSwap(y0, y3); + SkTSwap(y1, y2); + winding = -1; + } + + int top = SkFDot6Round(y0); + int bot = SkFDot6Round(y3); + + // are we a zero-height cubic (line)? + if (top == bot) + return 0; + + // are we completely above or below the clip? + if (clip && (top >= clip->fBottom || bot <= clip->fTop)) + return 0; + + // compute number of steps needed (1 << shift) + { + // Can't use (center of curve - center of baseline), since center-of-curve + // need not be the max delta from the baseline (it could even be coincident) + // so we try just looking at the two off-curve points + SkFDot6 dx = cubic_delta_from_line(x0, x1, x2, x3); + SkFDot6 dy = cubic_delta_from_line(y0, y1, y2, y3); + // add 1 (by observation) + shift = diff_to_shift(dx, dy) + 1; + } + // need at least 1 subdivision for our bias trick + SkASSERT(shift > 0); + + fWinding = SkToS8(winding); + fCurveShift = SkToU8(shift); + fCurveCount = SkToS16(-1 << shift); + + SkFixed B = SkFDot6ToFixed(3 * (x1 - x0)); + SkFixed C = SkFDot6ToFixed(3 * (x0 - x1 - x1 + x2)); + SkFixed D = SkFDot6ToFixed(x3 + 3 * (x1 - x2) - x0); + + fCx = SkFDot6ToFixed(x0); + fCDx = B + (C >> shift) + (D >> 2*shift); // biased by shift + fCDDx = 2*C + (3*D >> (shift - 1)); // biased by 2*shift + fCDDDx = 3*D >> (shift - 1); // biased by 2*shift + + B = SkFDot6ToFixed(3 * (y1 - y0)); + C = SkFDot6ToFixed(3 * (y0 - y1 - y1 + y2)); + D = SkFDot6ToFixed(y3 + 3 * (y1 - y2) - y0); + + fCy = SkFDot6ToFixed(y0); + fCDy = B + (C >> shift) + (D >> 2*shift); // biased by shift + fCDDy = 2*C + (3*D >> (shift - 1)); // biased by 2*shift + fCDDDy = 3*D >> (shift - 1); // biased by 2*shift + + fCLastX = SkFDot6ToFixed(x3); + fCLastY = SkFDot6ToFixed(y3); + + if (clip) + { + do { + for (;!this->updateCubic();) + ; + } while (!this->intersectsClip(*clip)); + this->chopLineWithClip(*clip); + return 1; + } + return this->updateCubic(); +} + +int SkCubicEdge::updateCubic() +{ + int success; + int count = fCurveCount; + SkFixed oldx = fCx; + SkFixed oldy = fCy; + SkFixed newx, newy; + int shift = fCurveShift; + + SkASSERT(count < 0); + + do { + if (++count < 0) + { + newx = oldx + (fCDx >> shift); + fCDx += fCDDx >> shift; + fCDDx += fCDDDx; + + newy = oldy + (fCDy >> shift); + fCDy += fCDDy >> shift; + fCDDy += fCDDDy; + } + else // last segment + { + // SkDebugf("LastX err=%d, LastY err=%d\n", (oldx + (fCDx >> shift) - fLastX), (oldy + (fCDy >> shift) - fLastY)); + newx = fCLastX; + newy = fCLastY; + } + success = this->updateLine(oldx, oldy, newx, newy); + oldx = newx; + oldy = newy; + } while (count < 0 && !success); + + fCx = newx; + fCy = newy; + fCurveCount = SkToS16(count); + return success; +} + + + diff --git a/libs/graphics/sgl/SkEdge.h b/libs/graphics/sgl/SkEdge.h new file mode 100644 index 0000000000..73add2a141 --- /dev/null +++ b/libs/graphics/sgl/SkEdge.h @@ -0,0 +1,77 @@ +#ifndef SkEdge_DEFINED +#define SkEdge_DEFINED + +#include "SkRect.h" + +struct SkEdge { + enum Type { + kLine_Type, + kQuad_Type, + kCubic_Type + }; + + SkEdge* fNext; + SkEdge* fPrev; + + SkFixed fX; + SkFixed fDX; + S16 fFirstY; + S16 fLastY; + S16 fCurveCount; // only used by kQuad(+) and kCubic(-) + U8 fCurveShift; + S8 fWinding; // 1 or -1 + + int setLine(const SkPoint pts[2], const SkRect16* clip, int shiftUp); + inline int updateLine(SkFixed ax, SkFixed ay, SkFixed bx, SkFixed by); + void chopLineWithClip(const SkRect16& clip); + + inline bool intersectsClip(const SkRect16& clip) const + { + SkASSERT(fFirstY < clip.fBottom); + return fLastY >= clip.fTop; + } + +#ifdef SK_DEBUG + void dump() const + { + #ifdef SK_CAN_USE_FLOAT + SkDebugf("edge: firstY:%d lastY:%d x:%g dx:%g w:%d\n", fFirstY, fLastY, SkFixedToFloat(fX), SkFixedToFloat(fDX), fWinding); + #else + SkDebugf("edge: firstY:%d lastY:%d x:%x dx:%x w:%d\n", fFirstY, fLastY, fX, fDX, fWinding); + #endif + } + + void validate() const + { + SkASSERT(fPrev && fNext); + SkASSERT(fPrev->fNext == this); + SkASSERT(fNext->fPrev == this); + + SkASSERT(fFirstY <= fLastY); + SkASSERT(SkAbs32(fWinding) == 1); + } +#endif +}; + +struct SkQuadraticEdge : public SkEdge { + SkFixed fQx, fQy; + SkFixed fQDx, fQDy; + SkFixed fQDDx, fQDDy; + SkFixed fQLastX, fQLastY; + + int setQuadratic(const SkPoint pts[3], const SkRect16* clip, int shiftUp); + int updateQuadratic(); +}; + +struct SkCubicEdge : public SkEdge { + SkFixed fCx, fCy; + SkFixed fCDx, fCDy; + SkFixed fCDDx, fCDDy; + SkFixed fCDDDx, fCDDDy; + SkFixed fCLastX, fCLastY; + + int setCubic(const SkPoint pts[4], const SkRect16* clip, int shiftUp); + int updateCubic(); +}; + +#endif diff --git a/libs/graphics/sgl/SkFP.h b/libs/graphics/sgl/SkFP.h new file mode 100644 index 0000000000..eb311b8e58 --- /dev/null +++ b/libs/graphics/sgl/SkFP.h @@ -0,0 +1,70 @@ +#ifndef SkFP_DEFINED +#define SkFP_DEFINED + +#include "SkMath.h" + +#ifdef SK_SCALAR_IS_FLOAT + + typedef float SkFP; + + #define SkScalarToFP(n) (n) + #define SkFPToScalar(n) (n) + #define SkIntToFP(n) SkIntToScalar(n) + #define SkFPRound(x) SkScalarRound(n) + #define SkFPCeil(x) SkScalarCeil(n) + #define SkFPFloor(x) SkScalarFloor(n) + + #define SkFPNeg(x) (-(x)) + #define SkFPAbs(x) SkScalarAbs(x) + #define SkFPAdd(a, b) ((a) + (b)) + #define SkFPSub(a, b) ((a) - (b)) + #define SkFPMul(a, b) ((a) * (b)) + #define SkFPMulInt(a, n) ((a) * (n)) + #define SkFPDiv(a, b) ((a) / (b)) + #define SkFPDivInt(a, n) ((a) / (n)) + #define SkFPInvert(x) SkScalarInvert(x) + #define SkFPSqrt(x) SkScalarSqrt(x) + #define SkFPCubeRoot(x) pow(x, 1.0f/3) + + #define SkFPLT(a, b) ((a) < (b)) + #define SkFPLE(a, b) ((a) <= (b)) + #define SkFPGT(a, b) ((a) > (b)) + #define SkFPGE(a, b) ((a) >= (b)) + +#else // scalar is fixed + + #include "SkFloat.h" + + typedef S32 SkFP; + + #define SkScalarToFP(n) SkFloat::SetShift(n, -16) + #define SkFPToScalar(n) SkFloat::GetShift(n, -16) + #define SkIntToFP(n) SkFloat::SetShift(n, 0) + #define SkFPRound(x) SkFloat::Round(x); + #define SkFPCeil(x) SkFloat::Ceil(); + #define SkFPFloor(x) SkFloat::Floor(); + + #define SkFPNeg(x) SkFloat::Neg(x) + #define SkFPAbs(x) SkFloat::Abs(x) + #define SkFPAdd(a, b) SkFloat::Add(a, b) + #define SkFPSub(a, b) SkFloat::Add(a, SkFloat::Neg(b)) + #define SkFPMul(a, b) SkFloat::Mul(a, b) + #define SkFPMulInt(a, n) SkFloat::MulInt(a, n) + #define SkFPDiv(a, b) SkFloat::Div(a, b) + #define SkFPDivInt(a, n) SkFloat::DivInt(a, n) + #define SkFPInvert(x) SkFloat::Invert(x) + #define SkFPSqrt(x) SkFloat::Sqrt(x) + #define SkFPCubeRoot(x) SkFloat::CubeRoot(x) + + #define SkFPLT(a, b) (SkFloat::Cmp(a, b) < 0) + #define SkFPLE(a, b) (SkFloat::Cmp(a, b) <= 0) + #define SkFPGT(a, b) (SkFloat::Cmp(a, b) > 0) + #define SkFPGE(a, b) (SkFloat::Cmp(a, b) >= 0) + +#endif + +#ifdef SK_DEBUG + void SkFP_UnitTest(); +#endif + +#endif diff --git a/libs/graphics/sgl/SkFilterProc.cpp b/libs/graphics/sgl/SkFilterProc.cpp new file mode 100644 index 0000000000..b51fa931bd --- /dev/null +++ b/libs/graphics/sgl/SkFilterProc.cpp @@ -0,0 +1,38 @@ +#include "SkFilterProc.h" + +/* [1-x 1-y] [x 1-y] + [1-x y] [x y] +*/ + +static unsigned bilerp00(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return a00; } +static unsigned bilerp01(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (3 * a00 + a01) >> 2; } +static unsigned bilerp02(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (a00 + a01) >> 1; } +static unsigned bilerp03(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (a00 + 3 * a01) >> 2; } + +static unsigned bilerp10(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (3 * a00 + a10) >> 2; } +static unsigned bilerp11(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (9 * a00 + 3 * (a01 + a10) + a11) >> 4; } +static unsigned bilerp12(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (3 * (a00 + a01) + a10 + a11) >> 3; } +static unsigned bilerp13(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (9 * a01 + 3 * (a00 + a11) + a10) >> 4; } + +static unsigned bilerp20(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (a00 + a10) >> 1; } +static unsigned bilerp21(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (3 * (a00 + a10) + a01 + a11) >> 3; } +static unsigned bilerp22(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (a00 + a01 + a10 + a11) >> 2; } +static unsigned bilerp23(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (3 * (a01 + a11) + a00 + a10) >> 3; } + +static unsigned bilerp30(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (a00 + 3 * a10) >> 2; } +static unsigned bilerp31(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (9 * a10 + 3 * (a00 + a11) + a01) >> 4; } +static unsigned bilerp32(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (3 * (a10 + a11) + a00 + a01) >> 3; } +static unsigned bilerp33(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (9 * a11 + 3 * (a01 + a10) + a00) >> 4; } + +static const SkFilterProc gBilerpProcs[4 * 4] = { + bilerp00, bilerp01, bilerp02, bilerp03, + bilerp10, bilerp11, bilerp12, bilerp13, + bilerp20, bilerp21, bilerp22, bilerp23, + bilerp30, bilerp31, bilerp32, bilerp33 +}; + +const SkFilterProc* SkGetBilinearFilterProcTable() +{ + return gBilerpProcs; +} + diff --git a/libs/graphics/sgl/SkFilterProc.h b/libs/graphics/sgl/SkFilterProc.h new file mode 100644 index 0000000000..a9c7223c3e --- /dev/null +++ b/libs/graphics/sgl/SkFilterProc.h @@ -0,0 +1,22 @@ +#ifndef SkFilter_DEFINED +#define SkFilter_DEFINED + +#include "SkMath.h" + +typedef unsigned (*SkFilterProc)(unsigned x00, unsigned x01, unsigned x10, unsigned x11); + +const SkFilterProc* SkGetBilinearFilterProcTable(); + +inline SkFilterProc SkGetBilinearFilterProc(const SkFilterProc* table, SkFixed x, SkFixed y) +{ + SkASSERT(table); + + // convert to dot 2 + x = (unsigned)(x << 16) >> 30; + y = (unsigned)(y << 16) >> 30; + return table[(y << 2) | x]; +} + +#endif + + diff --git a/libs/graphics/sgl/SkGeometry.cpp b/libs/graphics/sgl/SkGeometry.cpp new file mode 100644 index 0000000000..fe47cff526 --- /dev/null +++ b/libs/graphics/sgl/SkGeometry.cpp @@ -0,0 +1,1013 @@ +#include "SkGeometry.h" +#include "Sk64.h" +#include "SkMatrix.h" + +/** If defined, this makes eval_quad and eval_cubic do more setup (sometimes + involving integer multiplies by 2 or 3, but fewer calls to SkScalarMul. + May also introduce overflow of fixed when we compute our setup. +*/ +#ifdef SK_SCALAR_IS_FIXED + #define DIRECT_EVAL_OF_POLYNOMIALS +#endif + +//////////////////////////////////////////////////////////////////////// + +#if defined(SK_SCALAR_IS_FIXED) && !defined(SK_CPU_HAS_CONDITIONAL_INSTR) + static int is_not_monotonic(int a, int b, int c, int d) + { + return (((a - b) | (b - c) | (c - d)) & ((b - a) | (c - b) | (d - c))) >> 31; + } + static int is_not_monotonic(int a, int b, int c) + { + return (((a - b) | (b - c)) & ((b - a) | (c - b))) >> 31; + } +#else // scalar-is-float or we have fast if/then instructions + static int is_not_monotonic(SkScalar a, SkScalar b, SkScalar c, SkScalar d) + { + int neg = 0, pos = 0; + + if (a < b) neg = 1; + if (a > b) pos = 1; + if (b < c) neg = 1; + if (b > c) pos = 1; + if (c < d) neg = 1; + if (c > d) pos = 1; + + return neg & pos; + } + static int is_not_monotonic(SkScalar a, SkScalar b, SkScalar c) + { + int neg = 0, pos = 0; + + if (a < b) neg = 1; + if (a > b) pos = 1; + if (b < c) neg = 1; + if (b > c) pos = 1; + + return neg & pos; + } +#endif + +//////////////////////////////////////////////////////////////////////// + +static bool is_unit_interval(SkScalar x) +{ + return x > 0 && x < SK_Scalar1; +} + +static int valid_unit_divide(SkScalar numer, SkScalar denom, SkScalar* ratio) +{ + if (numer < 0) + { + numer = -numer; + denom = -denom; + } + + if (denom == 0 || numer == 0 || numer >= denom) + return 0; + + if (ratio) + { + SkScalar r = SkScalarDiv(numer, denom); + SkASSERT(r >= 0 && r < SK_Scalar1); + if (r == 0) // catch underflow if numer <<<< denom + return 0; + *ratio = r; + } + return 1; +} + +/** From Numerical Recipes in C. + + Q = -1/2 (B + sign(B) sqrt[B*B - 4*A*C]) + x1 = Q / A + x2 = C / Q +*/ +int SkFindUnitQuadRoots(SkScalar A, SkScalar B, SkScalar C, SkScalar roots[2]) +{ + SkScalar* r = roots; + + if (A == 0) + return valid_unit_divide(-C, B, roots); + +#ifdef SK_SCALAR_IS_FLOAT + float R = B*B - 4*A*C; + if (R < 0) // complex roots + return 0; + R = sk_float_sqrt(R); +#else + Sk64 RR, tmp; + + RR.setMul(B,B); + tmp.setMul(A,C); + tmp.shiftLeft(2); + RR.sub(tmp); + if (RR.isNeg()) + return 0; + SkFixed R = RR.getSqrt(); +#endif + + SkScalar Q = (B < 0) ? -(B-R)/2 : -(B+R)/2; + r += valid_unit_divide(Q, A, r); + r += valid_unit_divide(C, Q, r); + if (r - roots == 2) + { + if (roots[0] > roots[1]) + SkTSwap<SkScalar>(roots[0], roots[1]); + else if (roots[0] == roots[1]) // nearly-equal? + r -= 1; // skip the double root + } + return (int)(r - roots); +} + +#ifdef SK_SCALAR_IS_FIXED +/** Trim A/B/C down so that they are all <= 32bits + and then call SkFindUnitQuadRoots() +*/ +static int Sk64FindFixedQuadRoots(const Sk64& A, const Sk64& B, const Sk64& C, SkFixed roots[2]) +{ + int na = A.shiftToMake32(); + int nb = B.shiftToMake32(); + int nc = C.shiftToMake32(); + + int shift = SkMax32(na, SkMax32(nb, nc)); + SkASSERT(shift >= 0); + + return SkFindUnitQuadRoots(A.getShiftRight(shift), B.getShiftRight(shift), C.getShiftRight(shift), roots); +} +#endif + +///////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// + +static SkScalar eval_quad(const SkScalar src[], SkScalar t) +{ + SkASSERT(src); + SkASSERT(t >= 0 && t <= SK_Scalar1); + +#ifdef DIRECT_EVAL_OF_POLYNOMIALS + SkScalar C = src[0]; + SkScalar A = src[4] - 2 * src[2] + C; + SkScalar B = 2 * (src[2] - C); + return SkScalarMul(SkScalarMul(A, t) + B, t) + C; +#else + SkScalar ab = SkScalarInterp(src[0], src[2], t); + SkScalar bc = SkScalarInterp(src[2], src[4], t); + return SkScalarInterp(ab, bc, t); +#endif +} + +static SkScalar eval_quad_derivative(const SkScalar src[], SkScalar t) +{ + SkScalar A = src[4] - 2 * src[2] + src[0]; + SkScalar B = src[2] - src[0]; + + return 2 * (SkScalarMul(A, t) + B); +} + +static SkScalar eval_quad_derivative_at_half(const SkScalar src[]) +{ + SkScalar A = src[4] - 2 * src[2] + src[0]; + SkScalar B = src[2] - src[0]; + return A + 2 * B; +} + +void SkEvalQuadAt(const SkPoint src[3], SkScalar t, SkPoint* pt, SkVector* tangent) +{ + SkASSERT(src); + SkASSERT(t >= 0 && t <= SK_Scalar1); + + if (pt) + pt->set(eval_quad(&src[0].fX, t), eval_quad(&src[0].fY, t)); + if (tangent) + tangent->set(eval_quad_derivative(&src[0].fX, t), + eval_quad_derivative(&src[0].fY, t)); +} + +void SkEvalQuadAtHalf(const SkPoint src[3], SkPoint* pt, SkVector* tangent) +{ + SkASSERT(src); + + if (pt) + { + SkScalar x01 = SkScalarAve(src[0].fX, src[1].fX); + SkScalar y01 = SkScalarAve(src[0].fY, src[1].fY); + SkScalar x12 = SkScalarAve(src[1].fX, src[2].fX); + SkScalar y12 = SkScalarAve(src[1].fY, src[2].fY); + pt->set(SkScalarAve(x01, x12), SkScalarAve(y01, y12)); + } + if (tangent) + tangent->set(eval_quad_derivative_at_half(&src[0].fX), + eval_quad_derivative_at_half(&src[0].fY)); +} + +static void interp_quad_coords(const SkScalar* src, SkScalar* dst, SkScalar t) +{ + SkScalar ab = SkScalarInterp(src[0], src[2], t); + SkScalar bc = SkScalarInterp(src[2], src[4], t); + + dst[0] = src[0]; + dst[2] = ab; + dst[4] = SkScalarInterp(ab, bc, t); + dst[6] = bc; + dst[8] = src[4]; +} + +void SkChopQuadAt(const SkPoint src[3], SkPoint dst[5], SkScalar t) +{ + SkASSERT(t > 0 && t < SK_Scalar1); + + interp_quad_coords(&src[0].fX, &dst[0].fX, t); + interp_quad_coords(&src[0].fY, &dst[0].fY, t); +} + +void SkChopQuadAtHalf(const SkPoint src[3], SkPoint dst[5]) +{ + SkScalar x01 = SkScalarAve(src[0].fX, src[1].fX); + SkScalar y01 = SkScalarAve(src[0].fY, src[1].fY); + SkScalar x12 = SkScalarAve(src[1].fX, src[2].fX); + SkScalar y12 = SkScalarAve(src[1].fY, src[2].fY); + + dst[0] = src[0]; + dst[1].set(x01, y01); + dst[2].set(SkScalarAve(x01, x12), SkScalarAve(y01, y12)); + dst[3].set(x12, y12); + dst[4] = src[2]; +} + +/** Quad'(t) = At + B, where + A = 2(a - 2b + c) + B = 2(b - a) + Solve for t, only if it fits between 0 < t < 1 +*/ +int SkFindQuadExtrema(SkScalar a, SkScalar b, SkScalar c, SkScalar tValue[1]) +{ + /* At + B == 0 + t = -B / A + */ +#ifdef SK_SCALAR_IS_FIXED + return is_not_monotonic(a, b, c) && valid_unit_divide(a - b, a - b - b + c, tValue); +#else + return valid_unit_divide(a - b, a - b - b + c, tValue); +#endif +} + +static void flatten_double_quad_extrema(SkScalar coords[14]) +{ + coords[2] = coords[6] = coords[4]; +} + +static void force_quad_monotonic_in_y(SkPoint pts[3]) +{ + // zap pts[1].fY to the nearest value + SkScalar ab = SkScalarAbs(pts[0].fY - pts[1].fY); + SkScalar bc = SkScalarAbs(pts[1].fY - pts[2].fY); + pts[1].fY = ab < bc ? pts[0].fY : pts[2].fY; +} + +/* Returns 0 for 1 quad, and 1 for two quads, either way the answer is + stored in dst[]. Guarantees that the 1/2 quads will be monotonic. +*/ +int SkChopQuadAtYExtrema(const SkPoint src[3], SkPoint dst[5]) +{ +#if 0 + static bool once = true; + if (once) + { + once = false; + SkPoint s[3] = { 0, 26398, 0, 26331, 0, 20621428 }; + SkPoint d[6]; + + int n = SkChopQuadAtYExtrema(s, d); + SkDebugf("chop=%d, Y=[%x %x %x %x %x %x]\n", n, d[0].fY, d[1].fY, d[2].fY, d[3].fY, d[4].fY, d[5].fY); + } +#endif + + SkScalar tValue; + int roots = SkFindQuadExtrema(src[0].fY, src[1].fY, src[2].fY, &tValue); + + if (dst) + { + if (roots == 0) // nothing to chop + { + memcpy(dst, src, 3*sizeof(SkPoint)); + // check if valid_unit_divide gave up but we're still not monotonic + // can happen if valid_unit_divide can't see the t-value (underflow) + // e.g. SkPoint s[3] = { 0, 26398, 0, 26331, 0, 20621428 }; + if (is_not_monotonic(src[0].fY, src[1].fY, src[2].fY)) + force_quad_monotonic_in_y(dst); + } + else + { + SkChopQuadAt(src, dst, tValue); + flatten_double_quad_extrema(&dst[0].fY); + } + } + return roots; +} + +// F(t) = a (1 - t) ^ 2 + 2 b t (1 - t) + c t ^ 2 +// F'(t) = 2 (b - a) + 2 (a - 2b + c) t +// F''(t) = 2 (a - 2b + c) +// +// A = 2 (b - a) +// B = 2 (a - 2b + c) +// +// Maximum curvature for a quadratic means solving +// Fx' Fx'' + Fy' Fy'' = 0 +// +// t = - (Ax Bx + Ay By) / (Bx ^ 2 + By ^ 2) +// +int SkChopQuadAtMaxCurvature(const SkPoint src[3], SkPoint dst[5]) +{ + SkScalar Ax = src[1].fX - src[0].fX; + SkScalar Ay = src[1].fY - src[0].fY; + SkScalar Bx = src[0].fX - src[1].fX - src[1].fX + src[2].fX; + SkScalar By = src[0].fY - src[1].fY - src[1].fY + src[2].fY; + SkScalar t = 0; // 0 means don't chop + +#ifdef SK_SCALAR_IS_FLOAT + (void)valid_unit_divide(-(Ax * Bx + Ay * By), Bx * Bx + By * By, &t); +#else + // !!! should I use SkFloat here? seems like it + Sk64 numer, denom, tmp; + + numer.setMul(Ax, -Bx); + tmp.setMul(Ay, -By); + numer.add(tmp); + + if (numer.isPos()) // do nothing if numer <= 0 + { + denom.setMul(Bx, Bx); + tmp.setMul(By, By); + denom.add(tmp); + SkASSERT(!denom.isNeg()); + if (numer < denom) + { + t = numer.getFixedDiv(denom); + SkASSERT(t >= 0 && t <= SK_Fixed1); // assert that we're numerically stable (ha!) + if ((unsigned)t >= SK_Fixed1) // runtime check for numerical stability + t = 0; // ignore the chop + } + } +#endif + + if (t == 0) + { + memcpy(dst, src, 3 * sizeof(SkPoint)); + return 1; + } + else + { + SkChopQuadAt(src, dst, t); + return 2; + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +///// CUBICS // CUBICS // CUBICS // CUBICS // CUBICS // CUBICS // CUBICS // CUBICS ///// +//////////////////////////////////////////////////////////////////////////////////////// + +static void get_cubic_coeff(const SkScalar pt[], SkScalar coeff[4]) +{ + coeff[0] = pt[6] + 3*(pt[2] - pt[4]) - pt[0]; + coeff[1] = 3*(pt[4] - pt[2] - pt[2] + pt[0]); + coeff[2] = 3*(pt[2] - pt[0]); + coeff[3] = pt[0]; +} + +void SkGetCubicCoeff(const SkPoint pts[4], SkScalar cx[4], SkScalar cy[4]) +{ + SkASSERT(pts); + + if (cx) + get_cubic_coeff(&pts[0].fX, cx); + if (cy) + get_cubic_coeff(&pts[0].fY, cy); +} + +static SkScalar eval_cubic(const SkScalar src[], SkScalar t) +{ + SkASSERT(src); + SkASSERT(t >= 0 && t <= SK_Scalar1); + + if (t == 0) + return src[0]; + +#ifdef DIRECT_EVAL_OF_POLYNOMIALS + SkScalar D = src[0]; + SkScalar A = src[6] + 3*(src[2] - src[4]) - D; + SkScalar B = 3*(src[4] - src[2] - src[2] + D); + SkScalar C = 3*(src[2] - D); + + return SkScalarMul(SkScalarMul(SkScalarMul(A, t) + B, t) + C, t) + D; +#else + SkScalar ab = SkScalarInterp(src[0], src[2], t); + SkScalar bc = SkScalarInterp(src[2], src[4], t); + SkScalar cd = SkScalarInterp(src[4], src[6], t); + SkScalar abc = SkScalarInterp(ab, bc, t); + SkScalar bcd = SkScalarInterp(bc, cd, t); + return SkScalarInterp(abc, bcd, t); +#endif +} + +/** return At^2 + Bt + C +*/ +static SkScalar eval_quadratic(SkScalar A, SkScalar B, SkScalar C, SkScalar t) +{ + SkASSERT(t >= 0 && t <= SK_Scalar1); + + return SkScalarMul(SkScalarMul(A, t) + B, t) + C; +} + +static SkScalar eval_cubic_derivative(const SkScalar src[], SkScalar t) +{ + SkScalar A = src[6] + 3*(src[2] - src[4]) - src[0]; + SkScalar B = 2*(src[4] - 2 * src[2] + src[0]); + SkScalar C = src[2] - src[0]; + + return eval_quadratic(A, B, C, t); +} + +static SkScalar eval_cubic_2ndDerivative(const SkScalar src[], SkScalar t) +{ + SkScalar A = src[6] + 3*(src[2] - src[4]) - src[0]; + SkScalar B = src[4] - 2 * src[2] + src[0]; + + return SkScalarMul(A, t) + B; +} + +void SkEvalCubicAt(const SkPoint src[4], SkScalar t, SkPoint* loc, SkVector* tangent, SkVector* curvature) +{ + SkASSERT(src); + SkASSERT(t >= 0 && t <= SK_Scalar1); + + if (loc) + loc->set(eval_cubic(&src[0].fX, t), eval_cubic(&src[0].fY, t)); + if (tangent) + tangent->set(eval_cubic_derivative(&src[0].fX, t), + eval_cubic_derivative(&src[0].fY, t)); + if (curvature) + curvature->set(eval_cubic_2ndDerivative(&src[0].fX, t), + eval_cubic_2ndDerivative(&src[0].fY, t)); +} + +/** Cubic'(t) = At^2 + Bt + C, where + A = 3(-a + 3(b - c) + d) + B = 6(a - 2b + c) + C = 3(b - a) + Solve for t, keeping only those that fit betwee 0 < t < 1 +*/ +int SkFindCubicExtrema(SkScalar a, SkScalar b, SkScalar c, SkScalar d, SkScalar tValues[2]) +{ +#ifdef SK_SCALAR_IS_FIXED + if (!is_not_monotonic(a, b, c, d)) + return 0; +#endif + + // we divide A,B,C by 3 to simplify + SkScalar A = d - a + 3*(b - c); + SkScalar B = 2*(a - b - b + c); + SkScalar C = b - a; + + return SkFindUnitQuadRoots(A, B, C, tValues); +} + +static void interp_cubic_coords(const SkScalar* src, SkScalar* dst, SkScalar t) +{ + SkScalar ab = SkScalarInterp(src[0], src[2], t); + SkScalar bc = SkScalarInterp(src[2], src[4], t); + SkScalar cd = SkScalarInterp(src[4], src[6], t); + SkScalar abc = SkScalarInterp(ab, bc, t); + SkScalar bcd = SkScalarInterp(bc, cd, t); + SkScalar abcd = SkScalarInterp(abc, bcd, t); + + dst[0] = src[0]; + dst[2] = ab; + dst[4] = abc; + dst[6] = abcd; + dst[8] = bcd; + dst[10] = cd; + dst[12] = src[6]; +} + +void SkChopCubicAt(const SkPoint src[4], SkPoint dst[7], SkScalar t) +{ + SkASSERT(t > 0 && t < SK_Scalar1); + + interp_cubic_coords(&src[0].fX, &dst[0].fX, t); + interp_cubic_coords(&src[0].fY, &dst[0].fY, t); +} + +void SkChopCubicAt(const SkPoint src[4], SkPoint dst[], const SkScalar tValues[], int roots) +{ +#ifdef SK_DEBUG + { + for (int i = 0; i < roots - 1; i++) + { + SkASSERT(is_unit_interval(tValues[i])); + SkASSERT(is_unit_interval(tValues[i+1])); + SkASSERT(tValues[i] < tValues[i+1]); + } + } +#endif + + if (dst) + { + if (roots == 0) // nothing to chop + memcpy(dst, src, 4*sizeof(SkPoint)); + else + { + SkScalar t = tValues[0]; + SkPoint tmp[4]; + + for (int i = 0; i < roots; i++) + { + SkChopCubicAt(src, dst, t); + if (i == roots - 1) + break; + + SkDEBUGCODE(int valid =) valid_unit_divide(tValues[i+1] - tValues[i], SK_Scalar1 - tValues[i], &t); + SkASSERT(valid); + + dst += 3; + memcpy(tmp, dst, 4 * sizeof(SkPoint)); + src = tmp; + } + } + } +} + +void SkChopCubicAtHalf(const SkPoint src[4], SkPoint dst[7]) +{ + SkScalar x01 = SkScalarAve(src[0].fX, src[1].fX); + SkScalar y01 = SkScalarAve(src[0].fY, src[1].fY); + SkScalar x12 = SkScalarAve(src[1].fX, src[2].fX); + SkScalar y12 = SkScalarAve(src[1].fY, src[2].fY); + SkScalar x23 = SkScalarAve(src[2].fX, src[3].fX); + SkScalar y23 = SkScalarAve(src[2].fY, src[3].fY); + + SkScalar x012 = SkScalarAve(x01, x12); + SkScalar y012 = SkScalarAve(y01, y12); + SkScalar x123 = SkScalarAve(x12, x23); + SkScalar y123 = SkScalarAve(y12, y23); + + dst[0] = src[0]; + dst[1].set(x01, y01); + dst[2].set(x012, y012); + dst[3].set(SkScalarAve(x012, x123), SkScalarAve(y012, y123)); + dst[4].set(x123, y123); + dst[5].set(x23, y23); + dst[6] = src[3]; +} + +static void flatten_double_cubic_extrema(SkScalar coords[14]) +{ + coords[4] = coords[8] = coords[6]; +} + +/** Given 4 points on a cubic bezier, chop it into 1, 2, 3 beziers such that + the resulting beziers are monotonic in Y. This is called by the scan converter. + Depending on what is returned, dst[] is treated as follows + 0 dst[0..3] is the original cubic + 1 dst[0..3] and dst[3..6] are the two new cubics + 2 dst[0..3], dst[3..6], dst[6..9] are the three new cubics + If dst == nil, it is ignored and only the count is returned. +*/ +int SkChopCubicAtYExtrema(const SkPoint src[4], SkPoint dst[10]) +{ + SkScalar tValues[2]; + int roots = SkFindCubicExtrema(src[0].fY, src[1].fY, src[2].fY, src[3].fY, tValues); + + SkChopCubicAt(src, dst, tValues, roots); + if (dst && roots > 0) + { + // we do some cleanup to ensure our Y extrema are flat + flatten_double_cubic_extrema(&dst[0].fY); + if (roots == 2) + flatten_double_cubic_extrema(&dst[3].fY); + } + return roots; +} + +/** http://www.faculty.idc.ac.il/arik/quality/appendixA.html + + Inflection means that curvature is zero. + Curvature is [F' x F''] / [F'^3] + So we solve F'x X F''y - F'y X F''y == 0 + After some canceling of the cubic term, we get + A = b - a + B = c - 2b + a + C = d - 3c + 3b - a + (BxCy - ByCx)t^2 + (AxCy - AyCx)t + AxBy - AyBx == 0 +*/ +int SkFindCubicInflections(const SkPoint src[4], SkScalar tValues[]) +{ + SkScalar Ax = src[1].fX - src[0].fX; + SkScalar Ay = src[1].fY - src[0].fY; + SkScalar Bx = src[2].fX - 2 * src[1].fX + src[0].fX; + SkScalar By = src[2].fY - 2 * src[1].fY + src[0].fY; + SkScalar Cx = src[3].fX + 3 * (src[1].fX - src[2].fX) - src[0].fX; + SkScalar Cy = src[3].fY + 3 * (src[1].fY - src[2].fY) - src[0].fY; + int count; + +#ifdef SK_SCALAR_IS_FLOAT + count = SkFindUnitQuadRoots(Bx*Cy - By*Cx, Ax*Cy - Ay*Cx, Ax*By - Ay*Bx, tValues); +#else + Sk64 A, B, C, tmp; + + A.setMul(Bx, Cy); + tmp.setMul(By, Cx); + A.sub(tmp); + + B.setMul(Ax, Cy); + tmp.setMul(Ay, Cx); + B.sub(tmp); + + C.setMul(Ax, By); + tmp.setMul(Ay, Bx); + C.sub(tmp); + + count = Sk64FindFixedQuadRoots(A, B, C, tValues); +#endif + + return count; +} + +int SkChopCubicAtInflections(const SkPoint src[], SkPoint dst[10]) +{ + SkScalar tValues[2]; + int count = SkFindCubicInflections(src, tValues); + + if (dst) + { + if (count == 0) + memcpy(dst, src, 4 * sizeof(SkPoint)); + else + SkChopCubicAt(src, dst, tValues, count); + } + return count + 1; +} + +template <typename T> void bubble_sort(T array[], int count) +{ + for (int i = count - 1; i > 0; --i) + for (int j = i; j > 0; --j) + if (array[j] < array[j-1]) + { + T tmp(array[j]); + array[j] = array[j-1]; + array[j-1] = tmp; + } +} + +#include "SkFP.h" + +// newton refinement +#if 0 +static SkScalar refine_cubic_root(const SkFP coeff[4], SkScalar root) +{ + // x1 = x0 - f(t) / f'(t) + + SkFP T = SkScalarToFloat(root); + SkFP N, D; + + // f' = 3*coeff[0]*T^2 + 2*coeff[1]*T + coeff[2] + D = SkFPMul(SkFPMul(coeff[0], SkFPMul(T,T)), 3); + D = SkFPAdd(D, SkFPMulInt(SkFPMul(coeff[1], T), 2)); + D = SkFPAdd(D, coeff[2]); + + if (D == 0) + return root; + + // f = coeff[0]*T^3 + coeff[1]*T^2 + coeff[2]*T + coeff[3] + N = SkFPMul(SkFPMul(SkFPMul(T, T), T), coeff[0]); + N = SkFPAdd(N, SkFPMul(SkFPMul(T, T), coeff[1])); + N = SkFPAdd(N, SkFPMul(T, coeff[2])); + N = SkFPAdd(N, coeff[3]); + + if (N) + { + SkScalar delta = SkFPToScalar(SkFPDiv(N, D)); + + if (delta) + root -= delta; + } + return root; +} +#endif + +#if defined _WIN32 && _MSC_VER >= 1300 && defined SK_SCALAR_IS_FIXED // disable warning : unreachable code if building fixed point for windows desktop +#pragma warning ( disable : 4702 ) +#endif + +/* Solve coeff(t) == 0, returning the number of roots that + lie withing 0 < t < 1. + coeff[0]t^3 + coeff[1]t^2 + coeff[2]t + coeff[3] +*/ +static int solve_cubic_polynomial(const SkFP coeff[4], SkScalar tValues[3]) +{ +#ifndef SK_SCALAR_IS_FLOAT + return 0; // this is not yet implemented for software float +#endif + + if (SkScalarNearlyZero(coeff[0])) // we're just a quadratic + { + return SkFindUnitQuadRoots(coeff[1], coeff[2], coeff[3], tValues); + } + + SkFP a, b, c, Q, R; + + { + SkASSERT(coeff[0] != 0); + + SkFP inva = SkFPInvert(coeff[0]); + a = SkFPMul(coeff[1], inva); + b = SkFPMul(coeff[2], inva); + c = SkFPMul(coeff[3], inva); + } + Q = SkFPDivInt(SkFPSub(SkFPMul(a,a), SkFPMulInt(b, 3)), 9); +// R = (2*a*a*a - 9*a*b + 27*c) / 54; + R = SkFPMulInt(SkFPMul(SkFPMul(a, a), a), 2); + R = SkFPSub(R, SkFPMulInt(SkFPMul(a, b), 9)); + R = SkFPAdd(R, SkFPMulInt(c, 27)); + R = SkFPDivInt(R, 54); + + SkFP Q3 = SkFPMul(SkFPMul(Q, Q), Q); + SkFP R2MinusQ3 = SkFPSub(SkFPMul(R,R), Q3); + SkFP adiv3 = SkFPDivInt(a, 3); + + SkScalar* roots = tValues; + SkScalar r; + + if (SkFPLT(R2MinusQ3, 0)) // we have 3 real roots + { +#ifdef SK_SCALAR_IS_FLOAT + float theta = sk_float_acos(R / sk_float_sqrt(Q3)); + float neg2RootQ = -2 * sk_float_sqrt(Q); + + r = neg2RootQ * sk_float_cos(theta/3) - adiv3; + if (is_unit_interval(r)) + *roots++ = r; + + r = neg2RootQ * sk_float_cos((theta + 2*SK_ScalarPI)/3) - adiv3; + if (is_unit_interval(r)) + *roots++ = r; + + r = neg2RootQ * sk_float_cos((theta - 2*SK_ScalarPI)/3) - adiv3; + if (is_unit_interval(r)) + *roots++ = r; + + // now sort the roots + bubble_sort(tValues, (int)(roots - tValues)); +#endif + } + else // we have 1 real root + { + SkFP A = SkFPAdd(SkFPAbs(R), SkFPSqrt(R2MinusQ3)); + A = SkFPCubeRoot(A); + if (SkFPGT(R, 0)) + A = SkFPNeg(A); + + if (A != 0) + A = SkFPAdd(A, SkFPDiv(Q, A)); + r = SkFPToScalar(SkFPSub(A, adiv3)); + if (is_unit_interval(r)) + *roots++ = r; + } + + return (int)(roots - tValues); +} + +/* Looking for F' dot F'' == 0 + + A = b - a + B = c - 2b + a + C = d - 3c + 3b - a + + F' = 3Ct^2 + 6Bt + 3A + F'' = 6Ct + 6B + + F' dot F'' -> CCt^3 + 3BCt^2 + (2BB + CA)t + AB +*/ +static void formulate_F1DotF2(const SkScalar src[], SkFP coeff[4]) +{ + SkScalar a = src[2] - src[0]; + SkScalar b = src[4] - 2 * src[2] + src[0]; + SkScalar c = src[6] + 3 * (src[2] - src[4]) - src[0]; + + SkFP A = SkScalarToFP(a); + SkFP B = SkScalarToFP(b); + SkFP C = SkScalarToFP(c); + + coeff[0] = SkFPMul(C, C); + coeff[1] = SkFPMulInt(SkFPMul(B, C), 3); + coeff[2] = SkFPMulInt(SkFPMul(B, B), 2); + coeff[2] = SkFPAdd(coeff[2], SkFPMul(C, A)); + coeff[3] = SkFPMul(A, B); +} + +// EXPERIMENTAL: can set this to zero to accept all t-values 0 < t < 1 +//#define kMinTValueForChopping (SK_Scalar1 / 256) +#define kMinTValueForChopping 0 + +/* Looking for F' dot F'' == 0 + + A = b - a + B = c - 2b + a + C = d - 3c + 3b - a + + F' = 3Ct^2 + 6Bt + 3A + F'' = 6Ct + 6B + + F' dot F'' -> CCt^3 + 3BCt^2 + (2BB + CA)t + AB +*/ +int SkFindCubicMaxCurvature(const SkPoint src[4], SkScalar tValues[3]) +{ + SkFP coeffX[4], coeffY[4]; + int i; + + formulate_F1DotF2(&src[0].fX, coeffX); + formulate_F1DotF2(&src[0].fY, coeffY); + + for (i = 0; i < 4; i++) + coeffX[i] = SkFPAdd(coeffX[i],coeffY[i]); + + SkScalar t[3]; + int count = solve_cubic_polynomial(coeffX, t); + int maxCount = 0; + + // now remove extrema where the curvature is zero (mins) + // !!!! need a test for this !!!! + for (i = 0; i < count; i++) + { + // if (not_min_curvature()) + if (t[i] > kMinTValueForChopping && t[i] < SK_Scalar1 - kMinTValueForChopping) + tValues[maxCount++] = t[i]; + } + return maxCount; +} + +int SkChopCubicAtMaxCurvature(const SkPoint src[4], SkPoint dst[13], SkScalar tValues[3]) +{ + SkScalar t_storage[3]; + + if (tValues == nil) + tValues = t_storage; + + int count = SkFindCubicMaxCurvature(src, tValues); + + if (dst) + { + if (count == 0) + memcpy(dst, src, 4 * sizeof(SkPoint)); + else + SkChopCubicAt(src, dst, tValues, count); + } + return count + 1; +} + +//////////////////////////////////////////////////////////////////////////////// + +/* Find t value for quadratic [a, b, c] = d. + Return 0 if there is no solution +*/ +static SkScalar quad_solve(SkScalar a, SkScalar b, SkScalar c, SkScalar d) +{ + // At^2 + Bt + C = d + SkScalar A = a - 2 * b + c; + SkScalar B = 2 * (b - a); + SkScalar C = a - d; + + SkScalar roots[2]; + int count = SkFindUnitQuadRoots(A, B, C, roots); + + SkASSERT(count <= 1); + return count == 1 ? roots[0] : 0; +} + +/* given a quad-curve and a point (x,y), chop the quad at that point and return + the new quad's offCurve point. +*/ +static bool quad_pt2OffCurve(const SkPoint quad[3], SkScalar x, SkScalar y, SkPoint* offCurve) +{ + SkScalar t; + SkPoint tmp[5]; + + if (SkScalarAbs(x) < SkScalarAbs(y)) + t = quad_solve(quad[0].fX, quad[1].fX, quad[2].fX, x); + else + t = quad_solve(quad[0].fY, quad[1].fY, quad[2].fY, y); + + if (t > 0) + { + SkChopQuadAt(quad, tmp, t); + *offCurve = tmp[1]; + return true; + } + return false; +} + +static const SkPoint gQuadCirclePts[kSkBuildQuadArcStorage] = { + { SK_Scalar1, 0 }, + { SK_Scalar1, SK_ScalarTanPIOver8 }, + { SK_ScalarRoot2Over2, SK_ScalarRoot2Over2 }, + { SK_ScalarTanPIOver8, SK_Scalar1 }, + + { 0, SK_Scalar1 }, + { -SK_ScalarTanPIOver8, SK_Scalar1 }, + { -SK_ScalarRoot2Over2, SK_ScalarRoot2Over2 }, + { -SK_Scalar1, SK_ScalarTanPIOver8 }, + + { -SK_Scalar1, 0 }, + { -SK_Scalar1, -SK_ScalarTanPIOver8 }, + { -SK_ScalarRoot2Over2, -SK_ScalarRoot2Over2 }, + { -SK_ScalarTanPIOver8, -SK_Scalar1 }, + + { 0, -SK_Scalar1 }, + { SK_ScalarTanPIOver8, -SK_Scalar1 }, + { SK_ScalarRoot2Over2, -SK_ScalarRoot2Over2 }, + { SK_Scalar1, -SK_ScalarTanPIOver8 }, + + { SK_Scalar1, 0 } +}; + +int SkBuildQuadArc(const SkVector& uStart, const SkVector& uStop, + SkRotationDirection dir, const SkMatrix* userMatrix, + SkPoint quadPoints[]) +{ + // check for (effectively) coincident vectors + { + SkScalar dot = SkScalarMul(uStart.fX, uStop.fX) + SkScalarMul(uStart.fY, uStop.fY); + if (SkScalarAbs(dot - SK_Scalar1) <= SK_ScalarNearlyZero) + return 0; + } + // rotate unitStop so that unitStart is at (1,0) + SkScalar x = SkScalarMul(uStop.fX, uStart.fX) + SkScalarMul(uStop.fY, uStart.fY); + SkScalar y = SkScalarMul(uStop.fY, uStart.fX) - SkScalarMul(uStop.fX, uStart.fY); + + if (dir == kCCW_SkRotationDirection) + y = -y; + + // what octant (quadratic curve) is [xy] in? + int oct = 0; + bool sameSign = true; + + if (y < 0) + oct += 4; + if ((x < 0) != (y < 0)) + { + oct += 2; + sameSign = false; + } + if ((SkScalarAbs(x) < SkScalarAbs(y)) == sameSign) + oct += 1; + + if (SkScalarAbs(y) >= SK_Scalar1 || x <= -SK_Scalar1) + oct += 1; + + int wholeCount = oct << 1; + memcpy(quadPoints, gQuadCirclePts, (wholeCount + 1) * sizeof(SkPoint)); + + const SkPoint* arc = &gQuadCirclePts[wholeCount]; + if (quad_pt2OffCurve(arc, x, y, &quadPoints[wholeCount + 1])) + { + quadPoints[wholeCount + 2].set(x, y); + wholeCount += 2; + } + wholeCount += 1; + + // now handle counter-clockwise and the initial unitStart rotation + SkMatrix matrix; + matrix.setSinCos(uStart.fY, uStart.fX, 0, 0); + if (dir == kCCW_SkRotationDirection) + matrix.preScale(SK_Scalar1, -SK_Scalar1, 0, 0); + if (userMatrix) + matrix.postConcat(*userMatrix); + matrix.mapPoints(quadPoints, wholeCount); + return wholeCount; +} + + +///////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + +void SkGeometry::UnitTest() +{ +#ifdef SK_SUPPORT_UNITTEST + SkPoint pts[3], dst[5]; + + pts[0].set(0, 0); + pts[1].set(100, 50); + pts[2].set(0, 100); + + int count = SkChopQuadAtMaxCurvature(pts, dst); + SkASSERT(count == 1 || count == 2); +#endif +} + +#endif + + diff --git a/libs/graphics/sgl/SkGeometry.h b/libs/graphics/sgl/SkGeometry.h new file mode 100644 index 0000000000..a76dfc2b8e --- /dev/null +++ b/libs/graphics/sgl/SkGeometry.h @@ -0,0 +1,146 @@ +#ifndef SkGeometry_DEFINED +#define SkGeometry_DEFINED + +#include "SkMatrix.h" + +/** Given a quadratic equation Ax^2 + Bx + C = 0, return 0, 1, 2 roots for the + equation. +*/ +int SkFindUnitQuadRoots(SkScalar A, SkScalar B, SkScalar C, SkScalar roots[2]); + +/////////////////////////////////////////////////////////////////////////////// + +/** Set pt to the point on the src quadratic specified by t. t must be + 0 <= t <= 1.0 +*/ +void SkEvalQuadAt(const SkPoint src[3], SkScalar t, SkPoint* pt, SkVector* tangent = nil); +void SkEvalQuadAtHalf(const SkPoint src[3], SkPoint* pt, SkVector* tangent = nil); + +/** Given a src quadratic bezier, chop it at the specified t value, + where 0 < t < 1, and return the two new quadratics in dst: + dst[0..2] and dst[2..4] +*/ +void SkChopQuadAt(const SkPoint src[3], SkPoint dst[5], SkScalar t); + +/** Given a src quadratic bezier, chop it at the specified t == 1/2, + The new quads are returned in dst[0..2] and dst[2..4] +*/ +void SkChopQuadAtHalf(const SkPoint src[3], SkPoint dst[5]); + +/** Given the 3 coefficients for a quadratic bezier (either X or Y values), look + for extrema, and return the number of t-values that are found that represent + these extrema. If the quadratic has no extrema betwee (0..1) exclusive, the + function returns 0. + Returned count tValues[] + 0 ignored + 1 0 < tValues[0] < 1 +*/ +int SkFindQuadExtrema(SkScalar a, SkScalar b, SkScalar c, SkScalar tValues[1]); + +/** Given 3 points on a quadratic bezier, chop it into 1, 2 beziers such that + the resulting beziers are monotonic in Y. This is called by the scan converter. + Depending on what is returned, dst[] is treated as follows + 1 dst[0..2] is the original quad + 2 dst[0..2] and dst[2..4] are the two new quads + If dst == nil, it is ignored and only the count is returned. +*/ +int SkChopQuadAtYExtrema(const SkPoint src[3], SkPoint dst[5]); + +/** Given 3 points on a quadratic bezier, divide it into 2 quadratics + if the point of maximum curvature exists on the quad segment. + Depending on what is returned, dst[] is treated as follows + 1 dst[0..2] is the original quad + 2 dst[0..2] and dst[2..4] are the two new quads + If dst == nil, it is ignored and only the count is returned. +*/ +int SkChopQuadAtMaxCurvature(const SkPoint src[3], SkPoint dst[5]); + +//////////////////////////////////////////////////////////////////////////////////////// + +/** Convert from parametric from (pts) to polynomial coefficients + coeff[0]*T^3 + coeff[1]*T^2 + coeff[2]*T + coeff[3] +*/ +void SkGetCubicCoeff(const SkPoint pts[4], SkScalar cx[4], SkScalar cy[4]); + +/** Set pt to the point on the src cubic specified by t. t must be + 0 <= t <= 1.0 +*/ +void SkEvalCubicAt(const SkPoint src[4], SkScalar t, SkPoint* locOrNil, SkVector* tangentOrNil, SkVector* curvatureOrNil); + +/** Given a src cubic bezier, chop it at the specified t value, + where 0 < t < 1, and return the two new cubics in dst: + dst[0..3] and dst[3..6] +*/ +void SkChopCubicAt(const SkPoint src[4], SkPoint dst[7], SkScalar t); +void SkChopCubicAt(const SkPoint src[4], SkPoint dst[7], const SkScalar t[], int t_count); + +/** Given a src cubic bezier, chop it at the specified t == 1/2, + The new cubics are returned in dst[0..3] and dst[3..6] +*/ +void SkChopCubicAtHalf(const SkPoint src[4], SkPoint dst[7]); + +/** Given the 4 coefficients for a cubic bezier (either X or Y values), look + for extrema, and return the number of t-values that are found that represent + these extrema. If the cubic has no extrema betwee (0..1) exclusive, the + function returns 0. + Returned count tValues[] + 0 ignored + 1 0 < tValues[0] < 1 + 2 0 < tValues[0] < tValues[1] < 1 +*/ +int SkFindCubicExtrema(SkScalar a, SkScalar b, SkScalar c, SkScalar d, SkScalar tValues[2]); + +/** Given 4 points on a cubic bezier, chop it into 1, 2, 3 beziers such that + the resulting beziers are monotonic in Y. This is called by the scan converter. + Depending on what is returned, dst[] is treated as follows + 1 dst[0..3] is the original cubic + 2 dst[0..3] and dst[3..6] are the two new cubics + 3 dst[0..3], dst[3..6], dst[6..9] are the three new cubics + If dst == nil, it is ignored and only the count is returned. +*/ +int SkChopCubicAtYExtrema(const SkPoint src[4], SkPoint dst[10]); + +/** Given a cubic bezier, return 0, 1, or 2 t-values that represent the + inflection points. +*/ +int SkFindCubicInflections(const SkPoint src[4], SkScalar tValues[2]); + +/** Return 1 for no chop, or 2 for having chopped the cubic at its + inflection point. +*/ +int SkChopCubicAtInflections(const SkPoint src[4], SkPoint dst[10]); + +int SkFindCubicMaxCurvature(const SkPoint src[4], SkScalar tValues[3]); +int SkChopCubicAtMaxCurvature(const SkPoint src[4], SkPoint dst[13], SkScalar tValues[3] = nil); + +/////////////////////////////////////////////////////////////////////////////////////////// + +enum SkRotationDirection { + kCW_SkRotationDirection, + kCCW_SkRotationDirection +}; + +/** Maximum number of points needed in the quadPoints[] parameter for + SkBuildQuadArc() +*/ +#define kSkBuildQuadArcStorage 17 + +/** Given 2 unit vectors and a rotation direction, fill out the specified + array of points with quadratic segments. Return is the number of points + written to, which will be { 0, 3, 5, 7, ... kSkBuildQuadArcStorage } + + matrix, if not nil, is appled to the points before they are returned. +*/ +int SkBuildQuadArc(const SkVector& unitStart, const SkVector& unitStop, SkRotationDirection, + const SkMatrix* matrix, SkPoint quadPoints[]); + +////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + class SkGeometry { + public: + static void UnitTest(); + }; +#endif + +#endif diff --git a/libs/graphics/sgl/SkGlobals.cpp b/libs/graphics/sgl/SkGlobals.cpp new file mode 100644 index 0000000000..a53cc1d88b --- /dev/null +++ b/libs/graphics/sgl/SkGlobals.cpp @@ -0,0 +1,75 @@ +#include "SkGlobals.h" +#include "SkThread.h" + +SkGlobals::Rec::~Rec() +{ +} + +SkGlobals::Rec* SkGlobals::Find(U32 tag, Rec* (*create_proc)()) +{ + SkGlobals::BootStrap& bootstrap = SkGlobals::GetBootStrap(); + + Rec* rec = bootstrap.fHead; + while (rec) + { + if (rec->fTag == tag) + return rec; + rec = rec->fNext; + } + + if (create_proc == nil) // no create proc, just return not found + return nil; + + // if we get here, we may need to create one. First grab the mutex, and + // search again, creating one if its not found the 2nd time. + + bootstrap.fMutex.acquire(); + + // search again, now that we have the mutex. Odds are it won't be there, but we check again + // just in case it was added by another thread before we grabbed the mutex + + Rec*& head = bootstrap.fHead; + rec = head; + while (rec) + { + if (rec->fTag == tag) + break; + rec = rec->fNext; + } + + if (rec == nil && (rec = create_proc()) != nil) + { + rec->fTag = tag; + rec->fNext = head; + bootstrap.fHead = rec; + } + + bootstrap.fMutex.release(); + return rec; +} + +void SkGlobals::Init() +{ +} + +void SkGlobals::Term() +{ + SkGlobals::BootStrap& bootstrap = SkGlobals::GetBootStrap(); + + bootstrap.fMutex.acquire(); + + Rec*& head = bootstrap.fHead; + Rec* rec = head; + + while (rec) + { + Rec* next = rec->fNext; + SkDELETE(rec); + rec = next; + } + + bootstrap.fHead = nil; + bootstrap.fMutex.release(); +} + + diff --git a/libs/graphics/sgl/SkGlyphCache.cpp b/libs/graphics/sgl/SkGlyphCache.cpp new file mode 100644 index 0000000000..60d0ba3ee7 --- /dev/null +++ b/libs/graphics/sgl/SkGlyphCache.cpp @@ -0,0 +1,234 @@ +#include "SkGlyphCache.h" +#include "SkPaint.h" +#include "SkTemplates.h" + +////////////////////////////////////////////////////////////////////// + +#define kMinGlphAlloc (sizeof(SkGlyph) * 64) +#define kMinImageAlloc (24 * 64) // this guy should be pointsize-dependent IMHO + +/* We use this local function instead of the static class method to work around + a bug in gcc98 +*/ +void SkDescriptor_Free(SkDescriptor* desc); +void SkDescriptor_Free(SkDescriptor* desc) +{ + SkDescriptor::Free(desc); +} + +SkGlyphCache::SkGlyphCache(const SkDescriptor* desc) + : fGlyphAlloc(kMinGlphAlloc), fImageAlloc(kMinImageAlloc) +{ + fNext = NULL; + fDesc = desc->copy(); + SkAutoTCallProc<SkDescriptor, SkDescriptor_Free> autoFree(fDesc); + fScalerContext = SkScalerContext::Create(desc); + memset(fHash, 0, sizeof(fHash)); + + fScalerContext->getLineHeight(&fAbove, &fBelow); + (void)autoFree.detach(); +} + +SkGlyphCache::~SkGlyphCache() +{ + SkGlyph** gptr = fGlyphArray.begin(); + SkGlyph** stop = fGlyphArray.end(); + while (gptr < stop) + { + SkPath* path = (*gptr)->fPath; + if (path) + SkDELETE(path); + gptr += 1; + } + SkDescriptor::Free(fDesc); + SkDELETE(fScalerContext); +} + +const SkGlyph& SkGlyphCache::lookupMetrics(SkUnichar charCode) +{ + SkGlyph* glyph; + + int hi = 0; + int count = fGlyphArray.count(); + + if (count) + { + SkGlyph** gptr = fGlyphArray.begin(); + int lo = 0; + + hi = count - 1; + while (lo < hi) + { + int mid = (hi + lo) >> 1; + if (gptr[mid]->fCharCode < charCode) + lo = mid + 1; + else + hi = mid; + } + glyph = gptr[hi]; + if (glyph->fCharCode == charCode) + goto DONE; + + // check if we need to bump hi before falling though to the allocator + if (glyph->fCharCode < charCode) + hi += 1; + } + + // not found, but hi tells us where to inser the new glyph + + glyph = (SkGlyph*)fGlyphAlloc.alloc(sizeof(SkGlyph), SkChunkAlloc::kThrow_AllocFailType); + glyph->fCharCode = SkToU16(charCode); + glyph->fImage = NULL; + glyph->fPath = NULL; + fScalerContext->getMetrics(glyph); + *fGlyphArray.insert(hi) = glyph; + +DONE: + fHash[charCode & kHashMask] = glyph; + return *glyph; +} + +const void* SkGlyphCache::findImage(SkUnichar uni) +{ + // cast away the constness, so we can update fImage if needed + SkGlyph* glyph = (SkGlyph*)&this->getMetrics(uni); + + if (glyph->fWidth) + { + if (glyph->fImage == NULL) + { + size_t size = glyph->computeImageSize(); + glyph->fImage = fImageAlloc.alloc(size, SkChunkAlloc::kReturnNil_AllocFailType); + fScalerContext->getImage(*glyph); + } + } + return glyph->fImage; +} + +const SkPath* SkGlyphCache::findPath(SkUnichar uni) +{ + // cast away the constness, so we can update fImage if needed + SkGlyph* glyph = (SkGlyph*)&this->getMetrics(uni); + + if (glyph->fWidth) + { + if (glyph->fPath == NULL) + { + glyph->fPath = SkNEW(SkPath); + fScalerContext->getPath(*glyph, glyph->fPath); + } + } + return glyph->fPath; +} + +void SkGlyphCache::getLineHeight(SkPoint* above, SkPoint* below) +{ + if (above) + *above = fAbove; + if (below) + *below = fBelow; +} + +///////////////////////////////////////////////////////////////// + +SkGlyphCache* SkGlyphCache::DetachCache(const SkPaint& paint, const SkMatrix* matrix) +{ + return paint.detachCache(matrix); +} + +#include "SkGlobals.h" +#include "SkThread.h" + +#define SkGlyphCache_GlobalsTag SkSetFourByteTag('g', 'l', 'f', 'c') + +class SkGlyphCache_Globals : public SkGlobals::Rec { +public: + SkMutex fMutex; + SkGlyphCache* fHead; +}; + +#ifdef SK_USE_RUNTIME_GLOBALS + static SkGlobals::Rec* create_globals() + { + SkGlyphCache_Globals* rec = SkNEW(SkGlyphCache_Globals); + rec->fHead = NULL; + return rec; + } + + #define FIND_GC_GLOBALS() *(SkGlyphCache_Globals*)SkGlobals::Find(SkGlyphCache_GlobalsTag, create_globals) + #define GET_GC_GLOBALS() *(SkGlyphCache_Globals*)SkGlobals::Get(SkGlyphCache_GlobalsTag) +#else + static SkGlyphCache_Globals gGCGlobals; + #define FIND_GC_GLOBALS() gGCGlobals + #define GET_GC_GLOBALS() gGCGlobals +#endif + +SkGlyphCache* SkGlyphCache::DetachCache(const SkDescriptor* desc) +{ + SkASSERT(desc); + + SkGlyphCache_Globals& globals = FIND_GC_GLOBALS(); + + globals.fMutex.acquire(); + SkGlyphCache* cache = globals.fHead; + SkGlyphCache* prev = NULL; + + while (cache) + { + SkGlyphCache* next = cache->fNext; + + if (*cache->fDesc == *desc) + { + if (prev) + prev->fNext = next; + else + globals.fHead = next; + cache->fNext = NULL; + break; + } + prev = cache; + cache = next; + } + globals.fMutex.release(); + + if (cache == NULL) + cache = SkNEW_ARGS(SkGlyphCache, (desc)); + return cache; +} + +void SkGlyphCache::AttachCache(SkGlyphCache* cache) +{ + SkASSERT(cache); + SkASSERT(cache->fNext == NULL); + + SkGlyphCache_Globals& globals = GET_GC_GLOBALS(); + + globals.fMutex.acquire(); + + cache->fNext = globals.fHead; + globals.fHead = cache; + + globals.fMutex.release(); +} + +bool SkGlyphCache::FreeCache(size_t bytesNeeded) +{ + SkGlyphCache_Globals& globals = FIND_GC_GLOBALS(); + + globals.fMutex.acquire(); + SkGlyphCache* cache = globals.fHead; + bool didSomething = (cache != NULL); + + while (cache) + { + SkGlyphCache* next = cache->fNext; + SkDELETE(cache); + cache = next; + } + + globals.fHead = NULL; + globals.fMutex.release(); + + return didSomething; +} + diff --git a/libs/graphics/sgl/SkGlyphCache.h b/libs/graphics/sgl/SkGlyphCache.h new file mode 100644 index 0000000000..20a7aced1d --- /dev/null +++ b/libs/graphics/sgl/SkGlyphCache.h @@ -0,0 +1,89 @@ +#ifndef SkGlyphCache_DEFINED +#define SkGlyphCache_DEFINED + +#include "SkBitmap.h" +#include "SkChunkAlloc.h" +#include "SkDescriptor.h" +#include "SkScalerContext.h" +#include "SkTemplates.h" + +class SkPaint; + +class SkGlyphCache { +public: + const SkGlyph& getMetrics(SkUnichar charCode) + { + int hash = charCode & kHashMask; + SkGlyph* glyph = fHash[hash]; + + if (glyph != nil && glyph->fCharCode == charCode) + return *glyph; + return this->lookupMetrics(charCode); + } + + + const void* findImage(SkUnichar); + const SkPath* findPath(SkUnichar); + void getLineHeight(SkPoint* above, SkPoint* below); + + static SkGlyphCache* DetachCache(const SkPaint&, const SkMatrix* matrix); + static SkGlyphCache* DetachCache(const SkDescriptor*); + static void AttachCache(SkGlyphCache*); + static bool FreeCache(size_t bytesNeeded); + +private: + SkGlyphCache(const SkDescriptor*); + ~SkGlyphCache(); + + const SkGlyph& lookupMetrics(SkUnichar charCode); + + SkGlyphCache* fNext; + SkDescriptor* fDesc; + SkScalerContext* fScalerContext; + + enum { + kHashBits = 6, + kHashCount = 1 << kHashBits, + kHashMask = kHashCount - 1 + }; + SkGlyph* fHash[kHashCount]; + SkTDArray<SkGlyph*> fGlyphArray; + SkChunkAlloc fGlyphAlloc; + SkChunkAlloc fImageAlloc; + + SkPoint fAbove, fBelow; +}; + +class SkAutoGlyphCache { +public: + SkAutoGlyphCache(SkGlyphCache* cache) : fCache(cache) {} + SkAutoGlyphCache(const SkDescriptor* desc) + { + fCache = SkGlyphCache::DetachCache(desc); + } + SkAutoGlyphCache(const SkPaint& paint, const SkMatrix* matrix) + { + fCache = SkGlyphCache::DetachCache(paint, matrix); + } + ~SkAutoGlyphCache() + { + if (fCache) + SkGlyphCache::AttachCache(fCache); + } + + SkGlyphCache* getCache() const { return fCache; } + + void release() + { + if (fCache) + { + SkGlyphCache::AttachCache(fCache); + fCache = nil; + } + } +private: + SkGlyphCache* fCache; +}; + +#endif + diff --git a/libs/graphics/sgl/SkGraphics.cpp b/libs/graphics/sgl/SkGraphics.cpp new file mode 100644 index 0000000000..82c9a6864d --- /dev/null +++ b/libs/graphics/sgl/SkGraphics.cpp @@ -0,0 +1,143 @@ +#include "SkGraphics.h" + +#include "Sk64.h" +#include "SkBlitter.h" +#include "SkCanvas.h" +#include "SkDeque.h" +#include "SkDOM.h" +#include "SkFloat.h" +#include "SkGeometry.h" +#include "SkGlobals.h" +#include "SkMath.h" +#include "SkMatrix.h" +#include "SkPath.h" +#include "SkPathEffect.h" +#include "SkPathMeasure.h" +#include "SkRefCnt.h" +#include "SkShader.h" +#include "SkStream.h" +#include "SkTSearch.h" +#include "SkUtils.h" +#include "SkXfermode.h" + +#define typesizeline(type) { #type , sizeof(type) } +#define unittestline(type) { #type , type::UnitTest } + + +#ifdef BUILD_EMBOSS_TABLE + extern void SkEmbossMask_BuildTable(); +#endif + +#ifdef BUILD_RADIALGRADIENT_TABLE + extern void SkRadialGradient_BuildTable(); +#endif + +void SkGraphics::Init(bool runUnitTests) +{ + SkGlobals::Init(); + +#ifdef BUILD_EMBOSS_TABLE + SkEmbossMask_BuildTable(); +#endif +#ifdef BUILD_RADIALGRADIENT_TABLE + SkRadialGradient_BuildTable(); +#endif + +#ifdef SK_SUPPORT_UNITTEST + if (runUnitTests == false) + return; + int i; + + static const struct { + const char* fTypeName; + size_t fSizeOf; + } gTypeSize[] = { + typesizeline(char), + typesizeline(short), + typesizeline(int), + typesizeline(long), + typesizeline(size_t), + typesizeline(void*), + + typesizeline(S8), + typesizeline(U8), + typesizeline(S16), + typesizeline(U16), + typesizeline(S32), + typesizeline(U32), + typesizeline(S8CPU), + typesizeline(U8CPU), + typesizeline(S16CPU), + typesizeline(U16CPU), + + typesizeline(SkPoint), + typesizeline(SkRect), + typesizeline(SkMatrix), + typesizeline(SkPath), + typesizeline(SkRefCnt), + + typesizeline(SkPaint), + typesizeline(SkCanvas), + typesizeline(SkBlitter), + typesizeline(SkShader), + typesizeline(SkXfermode), + typesizeline(SkPathEffect) + }; + + { + char test = (char)(0-1); // use this subtract to avoid truncation warnings (in VC7 at least) + if (test < 0) + SkDebugf("SkGraphics: char is signed\n"); + else + SkDebugf("SkGraphics: char is unsigned\n"); + } + for (i = 0; i < (int)SK_ARRAY_COUNT(gTypeSize); i++) + SkDebugf("SkGraphics: sizeof(%s) = %d\n", gTypeSize[i].fTypeName, gTypeSize[i].fSizeOf); + + static const struct { + const char* fTypeName; + void (*fUnitTest)(); + } gUnitTests[] = { + unittestline(Sk64), + unittestline(SkMath), + unittestline(SkUtils), + unittestline(SkString), + unittestline(SkFloat), + unittestline(SkMatrix), + unittestline(SkGeometry), + unittestline(SkDeque), + unittestline(SkPath), + unittestline(SkPathMeasure) + }; + + for (i = 0; i < (int)SK_ARRAY_COUNT(gUnitTests); i++) + { + SkDebugf("SkGraphics: Running UnitTest for %s\n", gUnitTests[i].fTypeName); + gUnitTests[i].fUnitTest(); + SkDebugf("SkGraphics: End UnitTest for %s\n", gUnitTests[i].fTypeName); + } + SkQSort_UnitTest(); + +#endif +} + +//////////////////////////////////////////////////////////////////////////// + +#include "SkGlyphCache.h" +#include "SkImageDecoder.h" + +void SkGraphics::Term() +{ + SkBitmapRef::PurgeCacheAll(); + SkGraphics::FreeCaches(SK_MaxS32); + SkGlobals::Term(); +} + +bool SkGraphics::FreeCaches(size_t bytesNeeded) +{ + bool didSomething = SkBitmapRef::PurgeCacheOne(); + + return SkGlyphCache::FreeCache(bytesNeeded) || didSomething; +} + + diff --git a/libs/graphics/sgl/SkMaskFilter.cpp b/libs/graphics/sgl/SkMaskFilter.cpp new file mode 100644 index 0000000000..a23b44e45f --- /dev/null +++ b/libs/graphics/sgl/SkMaskFilter.cpp @@ -0,0 +1,69 @@ +#include "SkMaskFilter.h" +#include "SkBlitter.h" +#include "SkBounder.h" +#include "SkBuffer.h" +#include "SkDraw.h" +#include "SkRegion.h" + +size_t SkMask::computeImageSize() const +{ + return fBounds.height() * fRowBytes; +} + +size_t SkMask::computeTotalImageSize() const +{ + size_t size = this->computeImageSize(); + + if (fFormat == SkMask::k3D_Format) + size *= 3; + return size; +} + +uint8_t* SkMask::AllocImage(size_t size) +{ + return (uint8_t*)sk_malloc_throw(SkAlign4(size)); +} + +void SkMask::FreeImage(uint8_t* image) +{ + sk_free(image); +} + +bool SkMaskFilter::filterMask(SkMask*, const SkMask&, const SkMatrix&, SkPoint16*) +{ + return false; +} + +bool SkMaskFilter::filterPath(const SkPath& devPath, const SkMatrix& matrix, + const SkRegion& clip, SkBounder* bounder, + SkBlitter* blitter) +{ + SkMask srcM, dstM; + + if (!SkDraw::DrawToMask(devPath, &clip.getBounds(), this, &matrix, &srcM, + SkMask::kComputeBoundsAndRenderImage_CreateMode)) + { + return false; + } + + SkAutoMaskImage autoSrc(&srcM, false); + + if (!this->filterMask(&dstM, srcM, matrix, NULL)) + return false; + + SkAutoMaskImage autoDst(&dstM, false); + SkRegion::Cliperator clipper(clip, dstM.fBounds); + + if (!clipper.done() && (bounder == NULL || bounder->doIRect(dstM.fBounds, clip))) + { + const SkRect16& cr = clipper.rect(); + do { + blitter->blitMask(dstM, cr); + clipper.next(); + } while (!clipper.done()); + } + + return true; +} + + diff --git a/libs/graphics/sgl/SkPaint.cpp b/libs/graphics/sgl/SkPaint.cpp new file mode 100644 index 0000000000..23216845fa --- /dev/null +++ b/libs/graphics/sgl/SkPaint.cpp @@ -0,0 +1,868 @@ +#include "SkPaint.h" +#include "SkColorFilter.h" +#include "SkFontHost.h" +#include "SkMaskFilter.h" +#include "SkPathEffect.h" +#include "SkRasterizer.h" +#include "SkShader.h" +#include "SkScalerContext.h" +#include "SkStroke.h" +#include "SkTextLayout.h" +#include "SkTypeface.h" +#include "SkXfermode.h" + +#define SK_DefaultTextSize SkIntToScalar(12) + +SkPaint::SkPaint() +{ + fTypeface = NULL; + fTextSize = SK_DefaultTextSize; + fTextScaleX = SK_Scalar1; + fTextSkewX = 0; + + fPathEffect = NULL; + fShader = NULL; + fXfermode = NULL; + fMaskFilter = NULL; + fColorFilter = NULL; + fTextLayout = NULL; + fRasterizer = NULL; + + fColor = SK_ColorBLACK; + fWidth = 0; + fMiterLimit = SK_DefaultMiterLimit; + fFlags = 0; + fCapType = kDefault_Cap; + fJoinType = kDefault_Join; + fFilterType = kNo_FilterType; + fTextAlign = kLeft_Align; + fStyle = kFill_Style; +} + +SkPaint::SkPaint(const SkPaint& src) +{ + memcpy(this, &src, sizeof(src)); + + fTypeface->safeRef(); + fPathEffect->safeRef(); + fShader->safeRef(); + fXfermode->safeRef(); + fMaskFilter->safeRef(); + fColorFilter->safeRef(); + fTextLayout->safeRef(); + fRasterizer->safeRef(); +} + +SkPaint::~SkPaint() +{ + fTypeface->safeUnref(); + fPathEffect->safeUnref(); + fShader->safeUnref(); + fXfermode->safeUnref(); + fMaskFilter->safeUnref(); + fColorFilter->safeUnref(); + fTextLayout->safeUnref(); + fRasterizer->safeUnref(); +} + +SkPaint& SkPaint::operator=(const SkPaint& src) +{ + SkASSERT(&src); + + src.fTypeface->safeRef(); + src.fPathEffect->safeRef(); + src.fShader->safeRef(); + src.fXfermode->safeRef(); + src.fMaskFilter->safeRef(); + src.fColorFilter->safeRef(); + src.fTextLayout->safeRef(); + src.fRasterizer->safeRef(); + + fTypeface->safeUnref(); + fPathEffect->safeUnref(); + fShader->safeUnref(); + fXfermode->safeUnref(); + fMaskFilter->safeUnref(); + fColorFilter->safeUnref(); + fTextLayout->safeUnref(); + fRasterizer->safeUnref(); + + memcpy(this, &src, sizeof(src)); + + return *this; +} + +int operator==(const SkPaint& a, const SkPaint& b) +{ + return memcmp(&a, &b, sizeof(a)) == 0; +} + +void SkPaint::reset() +{ + SkPaint init; + + *this = init; +} + +void SkPaint::setFlags(U32 flags) +{ + fFlags = SkToU8(flags); +} + +void SkPaint::setAntiAliasOn(bool doAA) +{ + this->setFlags(SkSetClear32(fFlags, doAA, kAntiAlias_Shift)); +} + +void SkPaint::setLinearTextOn(bool doLinearText) +{ + this->setFlags(SkSetClear32(fFlags, doLinearText, kLinearText_Shift)); +} + +void SkPaint::setUnderlineTextOn(bool doUnderline) +{ + this->setFlags(SkSetClear32(fFlags, doUnderline, kUnderlineText_Shift)); +} + +void SkPaint::setStrikeThruTextOn(bool doStrikeThru) +{ + this->setFlags(SkSetClear32(fFlags, doStrikeThru, kStrikeThruText_Shift)); +} + +void SkPaint::setFakeBoldTextOn(bool doFakeBold) +{ + this->setFlags(SkSetClear32(fFlags, doFakeBold, kFakeBoldText_Shift)); +} + +void SkPaint::setStyle(Style style) +{ + SkASSERT((unsigned)style < kStyleCount); + fStyle = style; +} + +void SkPaint::setColor(SkColor color) +{ + fColor = color; +} + +void SkPaint::setAlpha(U8CPU a) +{ + fColor = SkColorSetARGB(a, SkColorGetR(fColor), SkColorGetG(fColor), SkColorGetB(fColor)); +} + +void SkPaint::setARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) +{ + fColor = SkColorSetARGB(a, r, g, b); +} + +void SkPaint::setStrokeWidth(SkScalar width) +{ + SkASSERT(width >= 0); + fWidth = width; +} + +void SkPaint::setStrokeMiter(SkScalar limit) +{ + SkASSERT(limit >= 0); + fMiterLimit = limit; +} + +void SkPaint::setStrokeCap(Cap ct) +{ + SkASSERT((unsigned)ct < kCapCount); + fCapType = SkToU8(ct); +} + +void SkPaint::setStrokeJoin(Join jt) +{ + SkASSERT((unsigned)jt < kJoinCount); + fJoinType = SkToU8(jt); +} + +void SkPaint::setFilterType(FilterType ft) +{ + SkASSERT((unsigned)ft < kFilterTypeCount); + fFilterType = SkToU8(ft); +} + +////////////////////////////////////////////////////////////////// + +void SkPaint::setTextAlign(Align align) +{ + SkASSERT((unsigned)align < kAlignCount); + fTextAlign = SkToU8(align); +} + +void SkPaint::setTextSize(SkScalar ts) +{ + SkASSERT(ts > 0); + fTextSize = ts; +} + +void SkPaint::setTextScaleX(SkScalar scaleX) +{ + fTextScaleX = scaleX; +} + +void SkPaint::setTextSkewX(SkScalar skewX) +{ + fTextSkewX = skewX; +} + +/////////////////////////////////////////////////////////////////////////////////////// + +SkTextLayout* SkPaint::setTextLayout(SkTextLayout* layout) +{ + SkRefCnt_SafeAssign(fTextLayout, layout); + return layout; +} + +SkTypeface* SkPaint::setTypeface(SkTypeface* font) +{ + SkRefCnt_SafeAssign(fTypeface, font); + return font; +} + +SkRasterizer* SkPaint::setRasterizer(SkRasterizer* r) +{ + SkRefCnt_SafeAssign(fRasterizer, r); + return r; +} + +/////////////////////////////////////////////////////////////////////////////////////// + +#include "SkGlyphCache.h" +#include "SkUtils.h" + +class SkAutoRestorePaintTextSizeAndFrame { +public: + SkAutoRestorePaintTextSizeAndFrame(const SkPaint* paint) : fPaint((SkPaint*)paint) + { + fTextSize = paint->getTextSize(); + fStyle = paint->getStyle(); + fPaint->setStyle(SkPaint::kFill_Style); + } + ~SkAutoRestorePaintTextSizeAndFrame() + { + fPaint->setStyle(fStyle); + fPaint->setTextSize(fTextSize); + } + +private: + SkPaint* fPaint; + SkScalar fTextSize; + SkPaint::Style fStyle; +}; + +static SkScalar measure_text(const SkPaint& paint, SkGlyphCache* cache, + SkUnicodeWalkerProc proc, const char* text, size_t byteLength, + int* count) +{ + SkASSERT(count); + + SkFixed x = 0; + int n; + SkTextLayout* layout = paint.getTextLayout(); + + if (layout) + { + SkAutoSTMalloc<32, SkTextLayout::Rec> storage(byteLength); + SkTextLayout::Rec* rec = storage.get(); + + n = layout->layout(paint, text, byteLength, proc, rec); + for (int i = 0; i < n; i++) + { + // should pass rec[i].glyphID when we have it + x += cache->getMetrics(rec[i].charCode()).fAdvanceX + SkScalarToFixed(rec[i].fDeltaAdvance); + } + } + else + { + const char* stop = (const char*)text + byteLength; + for (n = 0; text < stop; n++) + { + x += cache->getMetrics(proc(&text)).fAdvanceX; + } + SkASSERT(text == stop); + } + *count = n; + return SkFixedToScalar(x); +} + +SkScalar SkPaint::privateMeasureText(SkUnicodeWalkerProc textProc, + const char* text, size_t byteLength, + SkScalar* above, SkScalar* below) const +{ + SkASSERT(text != NULL || byteLength == 0); + + SkScalar scale = 0; + SkAutoRestorePaintTextSizeAndFrame restore(this); + + if (this->isLinearTextOn()) + { + scale = fTextSize / kCanonicalTextSizeForPaths; + // this gets restored by restore + ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths)); + } + + SkAutoGlyphCache autoCache(*this, NULL); + SkGlyphCache* cache = autoCache.getCache(); + + if (above || below) + { + SkPoint abovePt, belowPt; + cache->getLineHeight(&abovePt, &belowPt); + if (scale) + { + abovePt.fY = SkScalarMul(abovePt.fY, scale); + belowPt.fY = SkScalarMul(belowPt.fY, scale); + } + if (above) + *above = abovePt.fY; + if (below) + *below = belowPt.fY; + } + + SkScalar width = 0; + + if (byteLength) + { + int count; + width = measure_text(*this, cache, textProc, text, byteLength, &count); + + if (scale) + width = SkScalarMul(width, scale); + } + return width; +} + +SkScalar SkPaint::measureText(const char utf8[], size_t length, + SkScalar* above, SkScalar* below) const +{ + return this->privateMeasureText((SkUnicodeWalkerProc)SkUTF8_NextUnichar, utf8, length, above, below); +} + +SkScalar SkPaint::measureText16(const U16 utf16[], size_t numberOf16BitValues, + SkScalar* above, SkScalar* below) const +{ + return this->privateMeasureText((SkUnicodeWalkerProc)SkUTF16_NextUnichar, (const char*)utf16, numberOf16BitValues << 1, above, below); +} + +//////////////////////////////////////////////////////////////////////////////////////////// + +int SkPaint::privateGetTextWidths(const char text[], size_t byteLength, + SkScalar widths[], SkUnicodeWalkerProc proc) const +{ + SkASSERT(text != NULL && byteLength > 0); + + SkAutoRestorePaintTextSizeAndFrame restore(this); + SkScalar scale = 0; + + if (this->isLinearTextOn()) + { + scale = fTextSize / kCanonicalTextSizeForPaths; + // this gets restored by restore + ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths)); + } + + SkAutoGlyphCache autoCache(*this, NULL); + SkGlyphCache* cache = autoCache.getCache(); + + SkScalar* w = widths; + const char* stop = (const char*)text + byteLength; + if (scale) { + while (text < stop) + *w++ = SkScalarMul(SkFixedToScalar(cache->getMetrics(proc(&text)).fAdvanceX), scale); + } + else { + while (text < stop) + *w++ = SkFixedToScalar(cache->getMetrics(proc(&text)).fAdvanceX); + } + return w - widths; // count +} + +int SkPaint::getTextWidths(const char text[], size_t byteLength, SkScalar widths[]) const +{ + if (0 == byteLength) + return 0; + + if (NULL == widths) + return SkUTF8_CountUnichars(text, byteLength); + + int count = this->privateGetTextWidths(text, byteLength, widths, (SkUnicodeWalkerProc)SkUTF8_NextUnichar); + SkASSERT(SkUTF8_CountUnichars(text, byteLength) == count); + return count; +} + +int SkPaint::getTextWidths16(const uint16_t text[], size_t numberOf16BitValues, SkScalar widths[]) const +{ + if (0 == numberOf16BitValues) + return 0; + + if (NULL == widths) + return SkUTF16_CountUnichars(text, numberOf16BitValues); + + int count = this->privateGetTextWidths((const char*)text, numberOf16BitValues << 1, widths, (SkUnicodeWalkerProc)SkUTF16_NextUnichar); + SkASSERT(SkUTF16_CountUnichars(text, numberOf16BitValues) == count); + return count; +} + +//////////////////////////////////////////////////////////////////////////////////////////// + +SkScalar SkPaint::ascent() const +{ + SkScalar above; + (void)this->measureText(NULL, 0, &above, NULL); + return above; +} + +SkScalar SkPaint::descent() const +{ + SkScalar below; + (void)this->measureText(NULL, 0, NULL, &below); + return below; +} + +#include "SkDraw.h" + +void SkPaint::privateGetTextPath(SkUnicodeWalkerProc textProc, const char text[], size_t length, SkScalar x, SkScalar y, SkPath* path) const +{ + SkASSERT(length == 0 || text != NULL); + if (text == NULL || length == 0 || path == NULL) + return; + SkASSERT(textProc); + + SkTextToPathIter iter(textProc, text, length, *this, false, true); + SkMatrix matrix; + SkScalar prevXPos = 0; + + matrix.setScale(iter.getPathScale(), iter.getPathScale(), 0, 0); + matrix.postTranslate(x, y); + path->reset(); + + SkScalar xpos; + const SkPath* iterPath; + while ((iterPath = iter.next(&xpos)) != NULL) + { + matrix.postTranslate(xpos - prevXPos, 0); + path->addPath(*iterPath, matrix); + prevXPos = xpos; + } +} + +void SkPaint::getTextPath(const char text[], size_t length, SkScalar x, SkScalar y, SkPath* path) const +{ + this->privateGetTextPath((SkUnicodeWalkerProc)SkUTF8_NextUnichar, text, length, x, y, path); +} + +void SkPaint::getText16Path(const U16 text[], size_t numberOf16BitValues, SkScalar x, SkScalar y, SkPath* path) const +{ + this->privateGetTextPath((SkUnicodeWalkerProc)SkUTF16_NextUnichar, (const char*)text, numberOf16BitValues << 1, x, y, path); +} + +static void add_flattenable(SkDescriptor* desc, U32 tag, U32 len, SkFlattenable* obj) +{ + SkFlattenable::Factory fact = obj->getFactory(); + SkASSERT(fact); + + SkWBuffer buffer(desc->addEntry(tag, sizeof(void*) + len, NULL), sizeof(void*) + len); + + buffer.writePtr((const void*)fact); + obj->flatten(buffer); + SkASSERT(buffer.pos() == buffer.size()); +} + +/* + * interpolates to find the right value for key, in the function represented by the 'length' number of pairs: (keys[i], values[i]) + inspired by a desire to change the multiplier for thickness in fakebold + therefore, i assumed number of pairs (length) will be small, so a linear search is sufficient + repeated keys are allowed for discontinuous functions (so long as keys is monotonically increasing), and if + key is the value of a repeated scalar in keys, the first one will be used + - this may change if a binary search is used + - also, this ensures that there is no divide by zero (an assert also checks for that) +*/ +static SkScalar interpolate(SkScalar key, const SkScalar keys[], const SkScalar values[], int length) +{ + + SkASSERT(length > 0); + SkASSERT(keys != NULL); + SkASSERT(values != NULL); +#ifdef SK_DEBUG + for (int i = 1; i < length; i++) + SkASSERT(keys[i] >= keys[i-1]); +#endif + int right = 0; + while (right < length && key > keys[right]) + right++; + //could use sentinal values to eliminate conditionals + //i assume i am not in control of input values, so i want to make it simple + if (length == right) + return values[length-1]; + if (0 == right) + return values[0]; + //otherwise, we interpolate between right-1 and right + SkScalar rVal = values[right]; + SkScalar lVal = values[right-1]; + SkScalar rightKey = keys[right]; + SkScalar leftKey = keys[right-1]; + SkASSERT(rightKey != leftKey); + //fractional amount which we will multiply by the difference in the left value and right value + SkScalar fract = SkScalarDiv(key-leftKey,rightKey-leftKey); + return lVal + SkScalarMul(fract, rVal-lVal); +} + +//used for interpolating in fakeBold +static const SkScalar pointSizes[] = { SkIntToScalar(9), SkIntToScalar(36) }; +static const SkScalar multipliers[] = { SK_Scalar1/24, SK_Scalar1/32 }; + +void SkScalerContext::MakeRec(const SkPaint& paint, const SkMatrix* deviceMatrix, Rec* rec) +{ + SkASSERT(deviceMatrix == NULL || (deviceMatrix->getType() & SkMatrix::kPerspective_Mask) == 0); + + rec->fTextSize = paint.getTextSize(); + rec->fPreScaleX = paint.getTextScaleX(); + rec->fPreSkewX = paint.getTextSkewX(); + + if (deviceMatrix) + { + rec->fPost2x2[0][0] = deviceMatrix->getScaleX(); + rec->fPost2x2[0][1] = deviceMatrix->getSkewX(); + rec->fPost2x2[1][0] = deviceMatrix->getSkewY(); + rec->fPost2x2[1][1] = deviceMatrix->getScaleY(); + } + else + { + rec->fPost2x2[0][0] = rec->fPost2x2[1][1] = SK_Scalar1; + rec->fPost2x2[0][1] = rec->fPost2x2[1][0] = 0; + } + + SkPaint::Style style = paint.getStyle(); + SkScalar strokeWidth = paint.getStrokeWidth(); + + if (paint.isFakeBoldTextOn()) + { + SkScalar fakeBoldScale = interpolate(paint.getTextSize(), pointSizes, multipliers, 2); + SkScalar extra = SkScalarMul(paint.getTextSize(), fakeBoldScale); + + if (style == SkPaint::kFill_Style) + { + style = SkPaint::kStrokeAndFill_Style; + strokeWidth = extra; // ignore paint's strokeWidth if it was "fill" + } + else + strokeWidth += extra; + } + + if (style != SkPaint::kFill_Style && strokeWidth > 0) + { + rec->fFrameWidth = strokeWidth; + rec->fMiterLimit = paint.getStrokeMiter(); + rec->fFrameAndFill = SkToU8(style == SkPaint::kStrokeAndFill_Style); + rec->fStrokeJoin = SkToU8(paint.getStrokeJoin()); + } + else + { + rec->fFrameWidth = 0; + rec->fMiterLimit = 0; + rec->fFrameAndFill = false; + rec->fStrokeJoin = 0; + } + + rec->fUseHints = SkToU8(!paint.isLinearTextOn()); + rec->fDoAA = SkToU8(paint.isAntiAliasOn()); +} + +SkGlyphCache* SkPaint::detachCache(const SkMatrix* deviceMatrix) const +{ + SkScalerContext::Rec rec; + + SkScalerContext::MakeRec(*this, deviceMatrix, &rec); + + size_t descSize = sizeof(rec); + int entryCount = 2; // scalerrec + typeface + SkTypeface* tf = this->getTypeface(); + size_t tfSize = 0; + SkPathEffect* pe = this->getPathEffect(); + size_t peLen = 0; + SkMaskFilter* mf = this->getMaskFilter(); + size_t mfLen = 0; + SkRasterizer* ra = this->getRasterizer(); + size_t raLen = 0; + + // we always do this, even if tf is NULL + tfSize = SkFontHost::FlattenTypeface(tf, NULL); + descSize += tfSize; + + if (pe) + { + if (pe->getFactory()) + { + SkWBuffer buffer; + pe->flatten(buffer); + peLen = buffer.pos(); + descSize += sizeof(SkFlattenable::Factory) + peLen; + entryCount += 1; + rec.fDoAA = true; // force antialiasing when we do the scan conversion + } + else + pe = NULL; + } + if (mf) + { + if (mf->getFactory()) + { + SkWBuffer buffer; + mf->flatten(buffer); + mfLen = buffer.pos(); + descSize += sizeof(SkFlattenable::Factory) + mfLen; + entryCount += 1; + rec.fDoAA = true; // force antialiasing with maskfilters + } + else + mf = NULL; + } + if (ra) + { + if (ra->getFactory()) + { + SkWBuffer buffer; + ra->flatten(buffer); + raLen = buffer.pos(); + descSize += sizeof(SkFlattenable::Factory) + raLen; + entryCount += 1; + rec.fDoAA = true; // force antialiasing when we do the scan conversion + } + else + ra = NULL; + } + descSize += SkDescriptor::ComputeOverhead(entryCount); + + SkAutoDescriptor ad(descSize); + SkDescriptor* desc = ad.getDesc(); + + desc->init(); + desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec); + + // we always do this, even if tf is NULL + { + SkDEBUGCODE(size_t tfSize2 = ) SkFontHost::FlattenTypeface(tf, desc->addEntry(kTypeface_SkDescriptorTag, tfSize, NULL)); + SkASSERT(tfSize2 == tfSize); + } + + if (pe) + add_flattenable(desc, kPathEffect_SkDescriptorTag, peLen, pe); + if (mf) + add_flattenable(desc, kMaskFilter_SkDescriptorTag, mfLen, mf); + if (ra) + add_flattenable(desc, kRasterizer_SkDescriptorTag, raLen, ra); + + SkASSERT(descSize == desc->getLength()); + desc->computeChecksum(); + + return SkGlyphCache::DetachCache(desc); +} + +////////////////////////////////////////////////////////////////// + +SkShader* SkPaint::setShader(SkShader* shader) +{ + SkRefCnt_SafeAssign(fShader, shader); + return shader; +} + +SkColorFilter* SkPaint::setColorFilter(SkColorFilter* filter) +{ + SkRefCnt_SafeAssign(fColorFilter, filter); + return filter; +} + +SkXfermode* SkPaint::setXfermode(SkXfermode* mode) +{ + SkRefCnt_SafeAssign(fXfermode, mode); + return mode; +} + +SkXfermode* SkPaint::setPorterDuffXfermode(SkPorterDuff::Mode mode) +{ + fXfermode->safeUnref(); + fXfermode = SkPorterDuff::CreateXfermode(mode); + return fXfermode; +} + +SkPathEffect* SkPaint::setPathEffect(SkPathEffect* effect) +{ + SkRefCnt_SafeAssign(fPathEffect, effect); + return effect; +} + +SkMaskFilter* SkPaint::setMaskFilter(SkMaskFilter* filter) +{ + SkRefCnt_SafeAssign(fMaskFilter, filter); + return filter; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +bool SkPaint::getFillPath(const SkPath& src, SkPath* dst) const +{ + SkPath effectPath, strokePath; + const SkPath* path = &src; + + SkScalar width = this->getStrokeWidth(); + + switch (this->getStyle()) { + case SkPaint::kFill_Style: + width = -1; // mark it as no-stroke + break; + case SkPaint::kStrokeAndFill_Style: + if (width == 0) + width = -1; // mark it as no-stroke + break; + case SkPaint::kStroke_Style: + break; + default: + SkASSERT(!"unknown paint style"); + } + + if (this->getPathEffect()) + { + // lie to the pathEffect if our style is strokeandfill, so that it treats us as just fill + if (this->getStyle() == SkPaint::kStrokeAndFill_Style) + width = -1; // mark it as no-stroke + + if (this->getPathEffect()->filterPath(&effectPath, src, &width)) + path = &effectPath; + + // restore the width if we earlier had to lie, and if we're still set to no-stroke + // note: if we're now stroke (width >= 0), then the pathEffect asked for that change + // and we want to respect that (i.e. don't overwrite their setting for width) + if (this->getStyle() == SkPaint::kStrokeAndFill_Style && width < 0) + { + width = this->getStrokeWidth(); + if (width == 0) + width = -1; + } + } + + if (width > 0 && !path->isEmpty()) + { + SkStroke stroker(*this, width); + stroker.strokePath(*path, &strokePath); + path = &strokePath; + } + + if (path == &src) + *dst = src; + else + { + SkASSERT(path == &effectPath || path == &strokePath); + dst->swap(*(SkPath*)path); + } + + return width != 0; // return true if we're filled, or false if we're hairline (width == 0) +} + +//////////////////////////////////////////////////////////////////////////////////////// + +static bool has_thick_frame(const SkPaint& paint) +{ + return paint.getStrokeWidth() > 0 && paint.getStyle() != SkPaint::kFill_Style; +} + +SkTextToPathIter::SkTextToPathIter( SkUnicodeWalkerProc textProc, + const char text[], size_t length, + const SkPaint& paint, + bool applyStrokeAndPathEffects, + bool forceLinearTextOn) + : fPaint(paint), fTextProc(textProc) +{ + if (forceLinearTextOn) + fPaint.setLinearTextOn(true); + fPaint.setMaskFilter(NULL); // don't want this affecting our path-cache lookup + + if (fPaint.getPathEffect() == NULL && !has_thick_frame(fPaint)) + applyStrokeAndPathEffects = false; + + // can't use our canonical size if we need to apply patheffects/strokes + if (fPaint.isLinearTextOn() && !applyStrokeAndPathEffects) + { + fPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths)); + fScale = paint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths; + } + else + fScale = SK_Scalar1; + + if (!applyStrokeAndPathEffects) + { + fPaint.setStyle(SkPaint::kFill_Style); + fPaint.setPathEffect(NULL); + } + + fCache = SkGlyphCache::DetachCache(fPaint, NULL); + + SkPaint::Style style = SkPaint::kFill_Style; + SkPathEffect* pe = NULL; + + if (!applyStrokeAndPathEffects) + { + style = paint.getStyle(); // restore + pe = paint.getPathEffect(); // restore + } + fPaint.setStyle(style); + fPaint.setPathEffect(pe); + fPaint.setMaskFilter(paint.getMaskFilter()); // restore + + // now compute fXOffset if needed + + SkScalar xOffset = 0; + if (paint.getTextAlign() != SkPaint::kLeft_Align) // need to measure first + { + int count; + SkScalar width = SkScalarMul(measure_text(paint, fCache, textProc, text, length, &count), fScale); + if (paint.getTextAlign() == SkPaint::kCenter_Align) + width = SkScalarHalf(width); + xOffset = -width; + } + fXPos = xOffset; // + SkScalarHalf(paint.getTextTracking()); do we need to return the textlayout's first deltaAdvance? + fPrevAdvance = 0; + + fText = text; + fStop = text + length; +} + +SkTextToPathIter::~SkTextToPathIter() +{ + SkGlyphCache::AttachCache(fCache); +} + +const SkPath* SkTextToPathIter::next(SkScalar* xpos) +{ + while (fText < fStop) + { + const SkGlyph& glyph = fCache->getMetrics(fTextProc(&fText)); + + fXPos += fPrevAdvance; + fPrevAdvance = SkScalarMul(SkFixedToScalar(glyph.fAdvanceX), fScale); // + fPaint.getTextTracking(); + + if (glyph.fWidth) + { + if (xpos) + *xpos = fXPos; + return fCache->findPath(glyph.fCharCode); + } + } + return NULL; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////// + +SkTypeface* SkTypeface::Create(const char name[], Style style) +{ + return SkFontHost::CreateTypeface(NULL, name, style); +} + +SkTypeface* SkTypeface::CreateFromTypeface(const SkTypeface* family, Style style) +{ + return SkFontHost::CreateTypeface(family, NULL, style); +} + diff --git a/libs/graphics/sgl/SkPath.cpp b/libs/graphics/sgl/SkPath.cpp new file mode 100644 index 0000000000..18e4fb80a3 --- /dev/null +++ b/libs/graphics/sgl/SkPath.cpp @@ -0,0 +1,1139 @@ +#include "SkPath.h" +#include "SkMath.h" + +/* + Stores the verbs and points as they are given to us, with exceptions: + - we only record "Close" if it was immediately preceeded by Line | Quad | Cubic + - we insert a Move(0,0) if Line | Quad | Cubic is our first command + + The iterator does more cleanup, especially if forceClose == true + 1. if we encounter Close, return a cons'd up Line() first (if the curr-pt != start-pt) + 2. if we encounter Move without a preceeding Close, and forceClose is true, goto #1 + 3. if we encounter Line | Quad | Cubic after Close, cons up a Move +*/ + +//////////////////////////////////////////////////////////////////////////// + +SkPath::SkPath() : fFillType(kWinding_FillType) +{ +} + +SkPath::SkPath(const SkPath& src) +{ + *this = src; +} + +SkPath::~SkPath() +{ +} + +SkPath& SkPath::operator=(const SkPath& src) +{ + if (this != &src) + { + fPts = src.fPts; + fVerbs = src.fVerbs; + fFillType = src.fFillType; + } + return *this; +} + +void SkPath::swap(SkPath& other) +{ + SkASSERT(&other != nil); + + if (this != &other) + { + fPts.swap(other.fPts); + fVerbs.swap(other.fVerbs); + SkTSwap<U8>(fFillType, other.fFillType); + } +} + +void SkPath::reset() +{ + fPts.reset(); + fVerbs.reset(); +} + +bool SkPath::isEmpty() const +{ + int count = fVerbs.count(); + return count == 0 || (count == 1 && fVerbs[0] == kMove_Verb); +} + +bool SkPath::isRect(SkRect*) const +{ + SkASSERT(!"unimplemented"); + return false; +} + +int SkPath::getPoints(SkPoint copy[], int max) const +{ + SkASSERT(max >= 0); + int count = fPts.count(); + if (copy && max > 0 && count > 0) + memcpy(copy, fPts.begin(), sizeof(SkPoint) * SkMin32(max, count)); + return count; +} + +void SkPath::getLastPt(SkPoint* lastPt) const +{ + if (lastPt) + { + int count = fPts.count(); + if (count == 0) + lastPt->set(0, 0); + else + *lastPt = fPts[count - 1]; + } +} + +void SkPath::setLastPt(SkScalar x, SkScalar y) +{ + int count = fPts.count(); + if (count == 0) + this->moveTo(x, y); + else + fPts[count - 1].set(x, y); +} + +void SkPath::computeBounds(SkRect* bounds, BoundsType bt) const +{ + SkASSERT(bounds); + + if (fPts.count() <= 1) + bounds->set(0, 0, 0, 0); + else if (true || bt == kFast_BoundsType) + bounds->set(fPts.begin(), fPts.count()); + else + { + SkASSERT(!"unimplemented"); + Iter iter(*this, false); + SkPoint pts[4]; + Verb verb; + + while ((verb = iter.next(pts)) != kDone_Verb) + { + switch (verb) { + case kLine_Verb: + case kQuad_Verb: + case kCubic_Verb: + break; + default: + break; + } + } + } +} + +////////////////////////////////////////////////////////////////////////////// +// Construction methods + +void SkPath::incReserve(U16CPU inc) +{ + fVerbs.setReserve(fVerbs.count() + inc); + fPts.setReserve(fPts.count() + inc); +} + +void SkPath::moveTo(SkScalar x, SkScalar y) +{ + int vc = fVerbs.count(); + SkPoint* pt; + + if (vc > 0 && fVerbs[vc - 1] == kMove_Verb) + { + pt = &fPts[fPts.count() - 1]; + } + else + { + pt = fPts.append(); + *fVerbs.append() = kMove_Verb; + } + pt->set(x, y); +} + +void SkPath::rMoveTo(SkScalar x, SkScalar y) +{ + SkPoint pt; + this->getLastPt(&pt); + this->moveTo(pt.fX + x, pt.fY + y); +} + +void SkPath::lineTo(SkScalar x, SkScalar y) +{ + if (fVerbs.count() == 0) + { + fPts.append()->set(0, 0); + *fVerbs.append() = kMove_Verb; + } + fPts.append()->set(x, y); + *fVerbs.append() = kLine_Verb; +} + +void SkPath::rLineTo(SkScalar x, SkScalar y) +{ + SkPoint pt; + this->getLastPt(&pt); + this->lineTo(pt.fX + x, pt.fY + y); +} + +void SkPath::quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) +{ + if (fVerbs.count() == 0) + { + fPts.append()->set(0, 0); + *fVerbs.append() = kMove_Verb; + } + + SkPoint* pts = fPts.append(2); + pts[0].set(x1, y1); + pts[1].set(x2, y2); + *fVerbs.append() = kQuad_Verb; +} + +void SkPath::rQuadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) +{ + SkPoint pt; + this->getLastPt(&pt); + this->quadTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2); +} + +void SkPath::cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar x3, SkScalar y3) +{ + if (fVerbs.count() == 0) + { + fPts.append()->set(0, 0); + *fVerbs.append() = kMove_Verb; + } + SkPoint* pts = fPts.append(3); + pts[0].set(x1, y1); + pts[1].set(x2, y2); + pts[2].set(x3, y3); + *fVerbs.append() = kCubic_Verb; +} + +void SkPath::rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar x3, SkScalar y3) +{ + SkPoint pt; + this->getLastPt(&pt); + this->cubicTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2, pt.fX + x3, pt.fY + y3); +} + +void SkPath::close() +{ + int count = fVerbs.count(); + if (count > 0) + { + switch (fVerbs[count - 1]) { + case kLine_Verb: + case kQuad_Verb: + case kCubic_Verb: + *fVerbs.append() = kClose_Verb; + break; + default: + // don't add a close if the prev wasn't a primitive + break; + } + } +} + +//////////////////////////////////////////////////////////////////////////////////// + +void SkPath::addRect(const SkRect& rect, Direction dir) +{ + this->addRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, dir); +} + +void SkPath::addRect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom, Direction dir) +{ + this->moveTo(left, top); + if (dir == kCCW_Direction) + { + this->lineTo(left, bottom); + this->lineTo(right, bottom); + this->lineTo(right, top); + } + else + { + this->lineTo(right, top); + this->lineTo(right, bottom); + this->lineTo(left, bottom); + } + this->close(); +} + +#define CUBIC_ARC_FACTOR ((SK_ScalarSqrt2 - SK_Scalar1) * 4 / 3) + +void SkPath::addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry, Direction dir) +{ + SkScalar w = rect.width(); + SkScalar halfW = SkScalarHalf(w); + SkScalar h = rect.height(); + SkScalar halfH = SkScalarHalf(h); + + if (halfW <= 0 || halfH <= 0) + return; + + bool skip_hori = rx >= halfW; + bool skip_vert = ry >= halfH; + + if (skip_hori && skip_vert) + { + this->addOval(rect, dir); + return; + } + if (skip_hori) + rx = halfW; + else if (skip_vert) + ry = halfH; + + SkScalar sx = SkScalarMul(rx, CUBIC_ARC_FACTOR); + SkScalar sy = SkScalarMul(ry, CUBIC_ARC_FACTOR); + + this->incReserve(17); + this->moveTo(rect.fRight - rx, rect.fTop); + if (dir == kCCW_Direction) + { + if (!skip_hori) + this->lineTo(rect.fLeft + rx, rect.fTop); // top + this->cubicTo(rect.fLeft + rx - sx, rect.fTop, + rect.fLeft, rect.fTop + ry - sy, + rect.fLeft, rect.fTop + ry); // top-left + if (!skip_vert) + this->lineTo(rect.fLeft, rect.fBottom - ry); // left + this->cubicTo(rect.fLeft, rect.fBottom - ry + sy, + rect.fLeft + rx - sx, rect.fBottom, + rect.fLeft + rx, rect.fBottom); // bot-left + if (!skip_hori) + this->lineTo(rect.fRight - rx, rect.fBottom); // bottom + this->cubicTo(rect.fRight - rx + sx, rect.fBottom, + rect.fRight, rect.fBottom - ry + sy, + rect.fRight, rect.fBottom - ry); // bot-right + if (!skip_vert) + this->lineTo(rect.fRight, rect.fTop + ry); + this->cubicTo(rect.fRight, rect.fTop + ry - sy, + rect.fRight - rx + sx, rect.fTop, + rect.fRight - rx, rect.fTop); // top-right + } + else + { + this->cubicTo(rect.fRight - rx + sx, rect.fTop, + rect.fRight, rect.fTop + ry - sy, + rect.fRight, rect.fTop + ry); // top-right + if (!skip_vert) + this->lineTo(rect.fRight, rect.fBottom - ry); + this->cubicTo(rect.fRight, rect.fBottom - ry + sy, + rect.fRight - rx + sx, rect.fBottom, + rect.fRight - rx, rect.fBottom); // bot-right + if (!skip_hori) + this->lineTo(rect.fLeft + rx, rect.fBottom); // bottom + this->cubicTo(rect.fLeft + rx - sx, rect.fBottom, + rect.fLeft, rect.fBottom - ry + sy, + rect.fLeft, rect.fBottom - ry); // bot-left + if (!skip_vert) + this->lineTo(rect.fLeft, rect.fTop + ry); // left + this->cubicTo(rect.fLeft, rect.fTop + ry - sy, + rect.fLeft + rx - sx, rect.fTop, + rect.fLeft + rx, rect.fTop); // top-left + if (!skip_hori) + this->lineTo(rect.fRight - rx, rect.fTop); // top + } + this->close(); +} + +void SkPath::addOval(const SkRect& oval, Direction dir) +{ + SkScalar cx = oval.centerX(); + SkScalar cy = oval.centerY(); + SkScalar rx = SkScalarHalf(oval.width()); + SkScalar ry = SkScalarHalf(oval.height()); +#if 1 // these seem faster than using quads (1/2 the number of edges to process) + SkScalar sx = SkScalarMul(rx, CUBIC_ARC_FACTOR); + SkScalar sy = SkScalarMul(ry, CUBIC_ARC_FACTOR); + + this->incReserve(13); + this->moveTo(cx + rx, cy); + if (dir == kCCW_Direction) + { + this->cubicTo(cx + rx, cy - sy, cx + sx, cy - ry, cx, cy - ry); + this->cubicTo(cx - sx, cy - ry, cx - rx, cy - sy, cx - rx, cy); + this->cubicTo(cx - rx, cy + sy, cx - sx, cy + ry, cx, cy + ry); + this->cubicTo(cx + sx, cy + ry, cx + rx, cy + sy, cx + rx, cy); + } + else + { + this->cubicTo(cx + rx, cy + sy, cx + sx, cy + ry, cx, cy + ry); + this->cubicTo(cx - sx, cy + ry, cx - rx, cy + sy, cx - rx, cy); + this->cubicTo(cx - rx, cy - sy, cx - sx, cy - ry, cx, cy - ry); + this->cubicTo(cx + sx, cy - ry, cx + rx, cy - sy, cx + rx, cy); + } +#else + SkScalar sx = SkScalarMul(rx, SK_ScalarTanPIOver8); + SkScalar sy = SkScalarMul(ry, SK_ScalarTanPIOver8); + SkScalar mx = SkScalarMul(rx, SK_ScalarRoot2Over2); + SkScalar my = SkScalarMul(ry, SK_ScalarRoot2Over2); + + this->incReserve(16); + this->moveTo(cx + rx, cy); + if (dir == kCCW_Direction) + { + this->quadTo(cx + rx, cy - sy, cx + mx, cy - my); + this->quadTo(cx + sx, cy - ry, cx + 0, cy - ry); + this->quadTo(cx - sx, cy - ry, cx - mx, cy - my); + this->quadTo(cx - rx, cy - sy, cx - rx, cy - 0); + this->quadTo(cx - rx, cy + sy, cx - mx, cy + my); + this->quadTo(cx - sx, cy + ry, cx - 0, cy + ry); + this->quadTo(cx + sx, cy + ry, cx + mx, cy + my); + this->quadTo(cx + rx, cy + sy, cx + rx, cy + 0); + } + else + { + this->quadTo(cx + rx, cy + sy, cx + mx, cy + my); + this->quadTo(cx + sx, cy + ry, cx - 0, cy + ry); + this->quadTo(cx - sx, cy + ry, cx - mx, cy + my); + this->quadTo(cx - rx, cy + sy, cx - rx, cy - 0); + this->quadTo(cx - rx, cy - sy, cx - mx, cy - my); + this->quadTo(cx - sx, cy - ry, cx + 0, cy - ry); + this->quadTo(cx + sx, cy - ry, cx + mx, cy - my); + this->quadTo(cx + rx, cy - sy, cx + rx, cy + 0); + } +#endif + this->close(); +} + +void SkPath::addCircle(SkScalar x, SkScalar y, SkScalar r, Direction dir) +{ + if (r > 0) + { + SkRect rect; + rect.set(x - r, y - r, x + r, y + r); + this->addOval(rect, dir); + } +} + +void SkPath::addPath(const SkPath& path, SkScalar dx, SkScalar dy) +{ + SkMatrix matrix; + + matrix.setTranslate(dx, dy); + this->addPath(path, matrix); +} + +void SkPath::addPath(const SkPath& path, const SkMatrix& matrix) +{ + this->incReserve(path.fPts.count()); + + Iter iter(path, false); + SkPoint pts[4]; + Verb verb; + + SkMatrix::TypeMask mask = matrix.getType(); + + while ((verb = iter.next(pts)) != kDone_Verb) + { + switch (verb) { + case kMove_Verb: + matrix.mapPoints(&pts[0], &pts[0], 1, mask); + this->moveTo(pts[0]); + break; + case kLine_Verb: + matrix.mapPoints(&pts[1], &pts[1], 1, mask); + this->lineTo(pts[1]); + break; + case kQuad_Verb: + matrix.mapPoints(&pts[1], &pts[1], 2, mask); + this->quadTo(pts[1], pts[2]); + break; + case kCubic_Verb: + matrix.mapPoints(&pts[1], &pts[1], 3, mask); + this->cubicTo(pts[1], pts[2], pts[3]); + break; + case kClose_Verb: + this->close(); + break; + default: + SkASSERT(!"unknown verb"); + } + } +} + +//////////////////////////////////////////////////////////////////////////////////// + +static const U8 gPtsInVerb[] = { + 1, // kMove + 1, // kLine + 2, // kQuad + 3, // kCubic + 0, // kClose + 0 // kDone +}; + +// ignore the initial moveto, and stop when the 1st contour ends +void SkPath::pathTo(const SkPath& path) +{ + int i, vcount = path.fVerbs.count(); + if (vcount == 0) + return; + + const U8* verbs = path.fVerbs.begin(); + const SkPoint* pts = path.fPts.begin() + 1; // 1 for the initial moveTo + + SkASSERT(verbs[0] == kMove_Verb); + for (i = 1; i < vcount; i++) + { + switch (verbs[i]) { + case kLine_Verb: + this->lineTo(pts[0].fX, pts[0].fY); + break; + case kQuad_Verb: + this->quadTo(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY); + break; + case kCubic_Verb: + this->cubicTo(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY); + break; + case kClose_Verb: + return; + } + pts += gPtsInVerb[verbs[i]]; + } +} + +// ignore the last point of the 1st contour +void SkPath::reversePathTo(const SkPath& path) +{ + int i, vcount = path.fVerbs.count(); + if (vcount == 0) + return; + + const U8* verbs = path.fVerbs.begin(); + const SkPoint* pts = path.fPts.begin(); + + SkASSERT(verbs[0] == kMove_Verb); + for (i = 1; i < vcount; i++) + { + int n = gPtsInVerb[verbs[i]]; + if (n == 0) + break; + pts += n; + } + + while (--i > 0) + { + switch (verbs[i]) { + case kLine_Verb: + this->lineTo(pts[-1].fX, pts[-1].fY); + break; + case kQuad_Verb: + this->quadTo(pts[-1].fX, pts[-1].fY, pts[-2].fX, pts[-2].fY); + break; + case kCubic_Verb: + this->cubicTo(pts[-1].fX, pts[-1].fY, pts[-2].fX, pts[-2].fY, pts[-3].fX, pts[-3].fY); + break; + default: + SkASSERT(!"bad verb"); + break; + } + pts -= gPtsInVerb[verbs[i]]; + } +} + +//////////////////////////////////////////////////////////////////////////////////// + +bool SkPath::offset(SkScalar dx, SkScalar dy, SkPath* dst) const +{ + SkMatrix matrix; + + matrix.setTranslate(dx, dy); + return this->transform(matrix, dst); +} + +#include "SkGeometry.h" + +static void subdivide_quad_to(SkPath* path, const SkPoint pts[3], int level = 2) +{ + if (--level >= 0) + { + SkPoint tmp[5]; + + SkChopQuadAtHalf(pts, tmp); + subdivide_quad_to(path, &tmp[0], level); + subdivide_quad_to(path, &tmp[2], level); + } + else + path->quadTo(pts[1], pts[2]); +} + +static void subdivide_cubic_to(SkPath* path, const SkPoint pts[4], int level = 2) +{ + if (--level >= 0) + { + SkPoint tmp[7]; + + SkChopCubicAtHalf(pts, tmp); + subdivide_cubic_to(path, &tmp[0], level); + subdivide_cubic_to(path, &tmp[3], level); + } + else + path->cubicTo(pts[1], pts[2], pts[3]); +} + +bool SkPath::transform(const SkMatrix& matrix, SkPath* dst) const +{ + if (dst == nil) + dst = (SkPath*)this; + + if (matrix.getType() & SkMatrix::kPerspective_Mask) + { + SkPath tmp; + tmp.fFillType = fFillType; + + SkPath::Iter iter(*this, false); + SkPoint pts[4]; + SkPath::Verb verb; + + while ((verb = iter.next(pts)) != kDone_Verb) + { + switch (verb) { + case kMove_Verb: + tmp.moveTo(pts[0]); + break; + case kLine_Verb: + tmp.lineTo(pts[1]); + break; + case kQuad_Verb: + subdivide_quad_to(&tmp, pts); + break; + case kCubic_Verb: + subdivide_cubic_to(&tmp, pts); + break; + case kClose_Verb: + tmp.close(); + break; + default: + SkASSERT(!"unknown verb"); + break; + } + } + + dst->swap(tmp); + return matrix.mapPoints(dst->fPts.begin(), dst->fPts.count()); + } + + else + { + if (this != dst) + { + dst->fVerbs = fVerbs; + dst->fPts.setCount(fPts.count()); + dst->fFillType = fFillType; + } + return matrix.mapPoints(dst->fPts.begin(), fPts.begin(), fPts.count()); + } +} + +//////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// + +enum NeedMoveToState { + kAfterClose_NeedMoveToState, + kAfterCons_NeedMoveToState, + kAfterPrefix_NeedMoveToState +}; + +SkPath::Iter::Iter() +{ +#ifdef SK_DEBUG + fPts = nil; + fMoveTo.fX = fMoveTo.fY = fLastPt.fX = fLastPt.fY = 0; + fForceClose = fNeedMoveTo = fCloseLine = false; +#endif + // need to init enough to make next() harmlessly return kDone_Verb + fVerbs = nil; + fVerbStop = nil; + fNeedClose = false; +} + +SkPath::Iter::Iter(const SkPath& path, bool forceClose) +{ + this->setPath(path, forceClose); +} + +void SkPath::Iter::setPath(const SkPath& path, bool forceClose) +{ + fPts = path.fPts.begin(); + fVerbs = path.fVerbs.begin(); + fVerbStop = path.fVerbs.end(); + fForceClose = SkToU8(forceClose); + fNeedClose = false; + fNeedMoveTo = kAfterPrefix_NeedMoveToState; +} + +bool SkPath::Iter::isClosedContour() const +{ + if (fVerbs == nil || fVerbs == fVerbStop) + return false; + if (fForceClose) + return true; + + const uint8_t* verbs = fVerbs; + const uint8_t* stop = fVerbStop; + + if (kMove_Verb == *verbs) + verbs += 1; // skip the initial moveto + + while (verbs < stop) + { + unsigned v = *verbs++; + if (kMove_Verb == v) + break; + if (kClose_Verb == v) + return true; + } + return false; +} + +SkPath::Verb SkPath::Iter::autoClose(SkPoint pts[2]) +{ + if (fLastPt != fMoveTo) + { + if (pts) + { + pts[0] = fLastPt; + pts[1] = fMoveTo; + } + fLastPt = fMoveTo; + fCloseLine = true; + return kLine_Verb; + } + return kClose_Verb; +} + +bool SkPath::Iter::cons_moveTo(SkPoint pts[1]) +{ + if (fNeedMoveTo == kAfterClose_NeedMoveToState) + { + if (pts) + *pts = fMoveTo; + fNeedClose = fForceClose; + fNeedMoveTo = kAfterCons_NeedMoveToState; + fVerbs -= 1; + return true; + } + + if (fNeedMoveTo == kAfterCons_NeedMoveToState) + { + if (pts) + *pts = fMoveTo; + fNeedMoveTo = kAfterPrefix_NeedMoveToState; + } + else + { + SkASSERT(fNeedMoveTo == kAfterPrefix_NeedMoveToState); + if (pts) + *pts = fPts[-1]; + } + return false; +} + +SkPath::Verb SkPath::Iter::next(SkPoint pts[4]) +{ + if (fVerbs == fVerbStop) + { + if (fNeedClose) + { + if (kLine_Verb == this->autoClose(pts)) + return kLine_Verb; + fNeedClose = false; + return kClose_Verb; + } + return kDone_Verb; + } + + unsigned verb = *fVerbs++; + const SkPoint* srcPts = fPts; + + switch (verb) { + case kMove_Verb: + if (fNeedClose) + { + fVerbs -= 1; + verb = this->autoClose(pts); + if (verb == kClose_Verb) + fNeedClose = false; + return (Verb)verb; + } + if (fVerbs == fVerbStop) // might be a trailing moveto + return kDone_Verb; + fMoveTo = *srcPts; + if (pts) + pts[0] = *srcPts; + srcPts += 1; + fNeedMoveTo = kAfterCons_NeedMoveToState; + fNeedClose = fForceClose; + break; + case kLine_Verb: + if (this->cons_moveTo(pts)) + return kMove_Verb; + if (pts) + pts[1] = srcPts[0]; + fLastPt = srcPts[0]; + fCloseLine = false; + srcPts += 1; + break; + case kQuad_Verb: + if (this->cons_moveTo(pts)) + return kMove_Verb; + if (pts) + memcpy(&pts[1], srcPts, 2 * sizeof(SkPoint)); + fLastPt = srcPts[1]; + srcPts += 2; + break; + case kCubic_Verb: + if (this->cons_moveTo(pts)) + return kMove_Verb; + if (pts) + memcpy(&pts[1], srcPts, 3 * sizeof(SkPoint)); + fLastPt = srcPts[2]; + srcPts += 3; + break; + case kClose_Verb: + verb = this->autoClose(pts); + if (verb == kLine_Verb) + fVerbs -= 1; + else + fNeedClose = false; + fNeedMoveTo = kAfterClose_NeedMoveToState; + break; + } + fPts = srcPts; + return (Verb)verb; +} + +/////////////////////////////////////////////////////////////////////// + +static bool exceeds_dist(const SkScalar p[], const SkScalar q[], SkScalar dist, int count) +{ + SkASSERT(dist > 0); + + count *= 2; + for (int i = 0; i < count; i++) + if (SkScalarAbs(p[i] - q[i]) > dist) + return true; + return false; +} + +static void subdivide_quad(SkPath* dst, const SkPoint pts[3], SkScalar dist, int subLevel = 4) +{ + if (--subLevel >= 0 && exceeds_dist(&pts[0].fX, &pts[1].fX, dist, 4)) + { + SkPoint tmp[5]; + SkChopQuadAtHalf(pts, tmp); + + subdivide_quad(dst, &tmp[0], dist, subLevel); + subdivide_quad(dst, &tmp[2], dist, subLevel); + } + else + dst->quadTo(pts[1], pts[2]); +} + +static void subdivide_cubic(SkPath* dst, const SkPoint pts[4], SkScalar dist, int subLevel = 4) +{ + if (--subLevel >= 0 && exceeds_dist(&pts[0].fX, &pts[1].fX, dist, 6)) + { + SkPoint tmp[7]; + SkChopCubicAtHalf(pts, tmp); + + subdivide_cubic(dst, &tmp[0], dist, subLevel); + subdivide_cubic(dst, &tmp[3], dist, subLevel); + } + else + dst->cubicTo(pts[1], pts[2], pts[3]); +} + +void SkPath::subdivide(SkScalar dist, bool bendLines, SkPath* dst) const +{ + SkPath tmpPath; + if (nil == dst || this == dst) + dst = &tmpPath; + + SkPath::Iter iter(*this, false); + SkPoint pts[4]; + + for (;;) + { + switch (iter.next(pts)) { + case SkPath::kMove_Verb: + dst->moveTo(pts[0]); + break; + case SkPath::kLine_Verb: + if (!bendLines) + { + dst->lineTo(pts[1]); + break; + } + // construct a quad from the line + pts[2] = pts[1]; + pts[1].set(SkScalarAve(pts[0].fX, pts[2].fX), SkScalarAve(pts[0].fY, pts[2].fY)); + // fall through to the quad case + case SkPath::kQuad_Verb: + subdivide_quad(dst, pts, dist); + break; + case SkPath::kCubic_Verb: + subdivide_cubic(dst, pts, dist); + break; + case SkPath::kClose_Verb: + dst->close(); + break; + case SkPath::kDone_Verb: + goto DONE; + } + } +DONE: + if (&tmpPath == dst) // i.e. the dst should be us + dst->swap(*(SkPath*)this); +} + +/////////////////////////////////////////////////////////////////////// +/* + Format in flattened buffer: [ptCount, verbCount, pts[], verbs[]] +*/ + +#include "SkBuffer.h" + +U32 SkPath::flatten(void* storage) const +{ + if (storage) + { + SkWBuffer buffer(storage); + + buffer.write32(fPts.count()); + buffer.write32(fVerbs.count()); + buffer.write32(fFillType); + buffer.write(fPts.begin(), sizeof(SkPoint) * fPts.count()); + buffer.write(fVerbs.begin(), fVerbs.count()); + buffer.padToAlign4(); + } + return 3 * sizeof(int32_t) + sizeof(SkPoint) * fPts.count() + SkAlign4(fVerbs.count()); +} + +void SkPath::unflatten(const void* storage) +{ + SkRBuffer buffer(storage); + + fPts.setCount(buffer.readS32()); + fVerbs.setCount(buffer.readS32()); + fFillType = buffer.readS32(); + buffer.read(fPts.begin(), sizeof(SkPoint) * fPts.count()); + buffer.read(fVerbs.begin(), fVerbs.count()); + buffer.skipToAlign4(); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "SkString.h" +#include "SkStream.h" + +static void write_scalar(SkWStream* stream, SkScalar value) +{ + char buffer[SkStrAppendScalar_MaxSize]; + char* stop = SkStrAppendScalar(buffer, value); + stream->write(buffer, stop - buffer); +} + +static void append_scalars(SkWStream* stream, char verb, const SkScalar data[], int count) +{ + stream->write(&verb, 1); + write_scalar(stream, data[0]); + for (int i = 1; i < count; i++) { + if (data[i] >= 0) + stream->write(" ", 1); // can skip the separater if data[i] is negative + write_scalar(stream, data[i]); + } +} + +void SkPath::toString(SkString* str) const +{ + SkDynamicMemoryWStream stream; + + SkPath::Iter iter(*this, false); + SkPoint pts[4]; + + for (;;) { + switch (iter.next(pts)) { + case SkPath::kMove_Verb: + append_scalars(&stream, 'M', &pts[0].fX, 2); + break; + case SkPath::kLine_Verb: + append_scalars(&stream, 'L', &pts[1].fX, 2); + break; + case SkPath::kQuad_Verb: + append_scalars(&stream, 'Q', &pts[1].fX, 4); + break; + case SkPath::kCubic_Verb: + append_scalars(&stream, 'C', &pts[1].fX, 6); + break; + case SkPath::kClose_Verb: + stream.write("Z", 1); + break; + case SkPath::kDone_Verb: + str->resize(stream.getOffset()); + stream.copyTo(str->writable_str()); + return; + } + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + +#if 0 // test to ensure that the iterator returns the same data as the path +void SkPath::test() const +{ + Iter iter(*this, false); + SkPoint pts[4]; + Verb verb; + + const U8* verbs = fVerbs.begin(); + const SkPoint* points = fPts.begin(); + + while ((verb = iter.next(pts)) != kDone_Verb) + { + SkASSERT(*verbs == verb); + verbs += 1; + + int count; + switch (verb) { + case kMove_Verb: + count = 1; + break; + case kLine_Verb: + count = 2; + break; + case kQuad_Verb: + count = 3; + break; + case kCubic_Verb: + count = 4; + break; + case kClose_Verb: + default: + count = 0; + break; + } + if (count > 1) + points -= 1; + SkASSERT(memcmp(pts, points, count * sizeof(SkPoint)) == 0); + points += count; + } + + int vc = fVerbs.count(), pc = fPts.count(); + if (vc && fVerbs.begin()[vc-1] == kMove_Verb) + { + vc -= 1; + pc -= 1; + } + SkASSERT(verbs - fVerbs.begin() == vc); + SkASSERT(points - fPts.begin() == pc); +} +#endif + +void SkPath::dump(bool forceClose, const char title[]) const +{ + Iter iter(*this, forceClose); + SkPoint pts[4]; + Verb verb; + + SkDebugf("path: forceClose=%s %s\n", forceClose ? "true" : "false", title ? title : ""); + + while ((verb = iter.next(pts)) != kDone_Verb) + { + switch (verb) { + case kMove_Verb: +#ifdef SK_CAN_USE_FLOAT + SkDebugf(" path: moveTo [%g %g]\n", + SkScalarToFloat(pts[0].fX), SkScalarToFloat(pts[0].fY)); +#else + SkDebugf(" path: moveTo [%x %x]\n", pts[0].fX, pts[0].fY); +#endif + break; + case kLine_Verb: +#ifdef SK_CAN_USE_FLOAT + SkDebugf(" path: lineTo [%g %g]\n", + SkScalarToFloat(pts[1].fX), SkScalarToFloat(pts[1].fY)); +#else + SkDebugf(" path: lineTo [%x %x]\n", pts[1].fX, pts[1].fY); +#endif + break; + case kQuad_Verb: +#ifdef SK_CAN_USE_FLOAT + SkDebugf(" path: quadTo [%g %g] [%g %g]\n", + SkScalarToFloat(pts[1].fX), SkScalarToFloat(pts[1].fY), + SkScalarToFloat(pts[2].fX), SkScalarToFloat(pts[2].fY)); +#else + SkDebugf(" path: quadTo [%x %x] [%x %x]\n", pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY); +#endif + break; + case kCubic_Verb: +#ifdef SK_CAN_USE_FLOAT + SkDebugf(" path: cubeTo [%g %g] [%g %g] [%g %g]\n", + SkScalarToFloat(pts[1].fX), SkScalarToFloat(pts[1].fY), + SkScalarToFloat(pts[2].fX), SkScalarToFloat(pts[2].fY), + SkScalarToFloat(pts[3].fX), SkScalarToFloat(pts[3].fY)); +#else + SkDebugf(" path: cubeTo [%x %x] [%x %x] [%x %x]\n", + pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY, pts[3].fX, pts[3].fY); +#endif + break; + case kClose_Verb: + SkDebugf(" path: close\n"); + break; + default: + SkDebugf(" path: UNKNOWN VERB %d, aborting dump...\n", verb); + verb = kDone_Verb; // stop the loop + break; + } + } + SkDebugf("path: done %s\n", title ? title : ""); +} + +#include "SkTSort.h" + +void SkPath::UnitTest() +{ +#ifdef SK_SUPPORT_UNITTEST + SkPath p; + SkRect r; + + r.set(0, 0, 10, 20); + p.addRect(r); + p.dump(false); + p.dump(true); + + { + int array[] = { 5, 3, 7, 2, 6, 1, 2, 9, 5, 0 }; + int i; + + for (i = 0; i < (int)SK_ARRAY_COUNT(array); i++) + SkDebugf(" %d", array[i]); + SkDebugf("\n"); + SkTHeapSort<int>(array, SK_ARRAY_COUNT(array)); + for (i = 0; i < (int)SK_ARRAY_COUNT(array); i++) + SkDebugf(" %d", array[i]); + SkDebugf("\n"); + } + + { + SkPath p; + SkPoint pt; + + p.moveTo(SK_Scalar1, 0); + p.getLastPt(&pt); + SkASSERT(pt.fX == SK_Scalar1); + } +#endif +} + +#endif diff --git a/libs/graphics/sgl/SkPathEffect.cpp b/libs/graphics/sgl/SkPathEffect.cpp new file mode 100644 index 0000000000..cb09db13b3 --- /dev/null +++ b/libs/graphics/sgl/SkPathEffect.cpp @@ -0,0 +1,181 @@ +#include "SkPathEffect.h" +#include "SkPath.h" +#include "SkBuffer.h" + +SkFlattenable::Factory SkFlattenable::getFactory() +{ + return NULL; +} + +void SkFlattenable::flatten(SkWBuffer&) +{ +} + +////////////////////////////////////////////////////////////////////////////////// + +bool SkPathEffect::filterPath(SkPath*, const SkPath&, SkScalar*) +{ + return false; +} + +static SkFlattenable* create_null_patheffect(SkRBuffer&) +{ + return SkNEW(SkPathEffect); +} + +SkFlattenable::Factory SkPathEffect::getFactory() +{ + return create_null_patheffect; +} + +////////////////////////////////////////////////////////////////////////////////// + +SkPairPathEffect::SkPairPathEffect(SkPathEffect* pe0, SkPathEffect* pe1) + : fPE0(pe0), fPE1(pe1) +{ + SkASSERT(pe0); + SkASSERT(pe1); + fPE0->ref(); + fPE1->ref(); +} + +SkPairPathEffect::~SkPairPathEffect() +{ + fPE0->unref(); + fPE1->unref(); +} + +/* + Format: [oe0-factory][pe1-factory][pe0-size][pe0-data][pe1-data] +*/ +void SkPairPathEffect::flatten(SkWBuffer& buffer) +{ + buffer.writePtr((void*)fPE0->getFactory()); + buffer.writePtr((void*)fPE1->getFactory()); + fPE0->flatten(buffer); + fPE1->flatten(buffer); +} + +SkPairPathEffect::SkPairPathEffect(SkRBuffer& buffer) +{ + Factory factory0 = (Factory)buffer.readPtr(); + Factory factory1 = (Factory)buffer.readPtr(); + + fPE0 = (SkPathEffect*)factory0(buffer); + fPE1 = (SkPathEffect*)factory1(buffer); +} + +////////////////////////////////////////////////////////////////////////////////// + +bool SkComposePathEffect::filterPath(SkPath* dst, const SkPath& src, SkScalar* width) +{ + SkPath tmp; + const SkPath* ptr = &src; + + if (fPE1->filterPath(&tmp, src, width)) + ptr = &tmp; + return fPE0->filterPath(dst, *ptr, width); +} + +SkFlattenable* SkComposePathEffect::CreateProc(SkRBuffer& buffer) +{ + return SkNEW_ARGS(SkComposePathEffect, (buffer)); +} + +SkFlattenable::Factory SkComposePathEffect::getFactory() +{ + return SkComposePathEffect::CreateProc; +} + +////////////////////////////////////////////////////////////////////////////////// + +bool SkSumPathEffect::filterPath(SkPath* dst, const SkPath& src, SkScalar* width) +{ + // use bit-or so that we always call both, even if the first one succeeds + return fPE0->filterPath(dst, src, width) | fPE1->filterPath(dst, src, width); +} + +SkFlattenable* SkSumPathEffect::CreateProc(SkRBuffer& buffer) +{ + return SkNEW_ARGS(SkSumPathEffect, (buffer)); +} + +SkFlattenable::Factory SkSumPathEffect::getFactory() +{ + return SkSumPathEffect::CreateProc; +} + +///////////////////////////////////////////////////////////////////////////////// + +#include "SkStroke.h" + +SkStrokePathEffect::SkStrokePathEffect(const SkPaint& paint) + : fWidth(paint.getStrokeWidth()), fMiter(paint.getStrokeMiter()), + fStyle(SkToU8(paint.getStyle())), fJoin(SkToU8(paint.getStrokeJoin())), fCap(SkToU8(paint.getStrokeCap())) +{ +} + +SkStrokePathEffect::SkStrokePathEffect(SkScalar width, SkPaint::Style style, SkPaint::Join join, SkPaint::Cap cap, SkScalar miter) + : fWidth(width), fMiter(miter), fStyle(SkToU8(style)), fJoin(SkToU8(join)), fCap(SkToU8(cap)) +{ + if (miter < 0) // signal they want the default + fMiter = SK_DefaultMiterLimit; +} + +bool SkStrokePathEffect::filterPath(SkPath* dst, const SkPath& src, SkScalar* width) +{ + if (fWidth < 0 || fStyle == SkPaint::kFill_Style) + return false; + + if (fStyle == SkPaint::kStroke_Style && fWidth == 0) // hairline + { + *width = 0; + return true; + } + + SkStroke stroke; + + stroke.setWidth(fWidth); + stroke.setMiterLimit(fMiter); + stroke.setJoin((SkPaint::Join)fJoin); + stroke.setCap((SkPaint::Cap)fCap); + stroke.setDoFill(fStyle == SkPaint::kStrokeAndFill_Style); + + stroke.strokePath(src, dst); + return true; +} + +SkFlattenable::Factory SkStrokePathEffect::getFactory() +{ + return CreateProc; +} + +SkFlattenable* SkStrokePathEffect::CreateProc(SkRBuffer& buffer) +{ + return SkNEW_ARGS(SkStrokePathEffect, (buffer)); +} + +void SkStrokePathEffect::flatten(SkWBuffer& buffer) +{ + this->INHERITED::flatten(buffer); + + buffer.writeScalar(fWidth); + buffer.writeScalar(fMiter); + buffer.write8(fStyle); + buffer.write8(fJoin); + buffer.write8(fCap); + buffer.padToAlign4(); +} + +SkStrokePathEffect::SkStrokePathEffect(SkRBuffer& buffer) + : SkPathEffect(buffer) +{ + fWidth = buffer.readScalar(); + fMiter = buffer.readScalar(); + fStyle = buffer.readU8(); + fJoin = buffer.readU8(); + fCap = buffer.readU8(); + buffer.skipToAlign4(); +} + + diff --git a/libs/graphics/sgl/SkPathMeasure.cpp b/libs/graphics/sgl/SkPathMeasure.cpp new file mode 100644 index 0000000000..6191724ee2 --- /dev/null +++ b/libs/graphics/sgl/SkPathMeasure.cpp @@ -0,0 +1,623 @@ +#include "SkPathMeasure.h" +#include "SkGeometry.h" +#include "SkPath.h" +#include "SkTSearch.h" + +// these must be 0,1,2 since they are in our 2-bit field +enum { + kLine_SegType, + kCloseLine_SegType, + kQuad_SegType, + kCubic_SegType +}; + +#define kMaxTValue 32767 + +static inline SkScalar tValue2Scalar(int t) +{ + SkASSERT((unsigned)t <= kMaxTValue); + +#ifdef SK_SCALAR_IS_FLOAT + return t * 3.05185e-5f; // t / 32767 +#else + return (t + (t >> 14)) << 1; +#endif +} + +SkScalar SkPathMeasure::Segment::getScalarT() const +{ + return tValue2Scalar(fTValue); +} + +const SkPathMeasure::Segment* SkPathMeasure::NextSegment(const Segment* seg) +{ + unsigned ptIndex = seg->fPtIndex; + + do { + ++seg; + } while (seg->fPtIndex == ptIndex); + return seg; +} + +///////////////////////////////////////////////////////////////////////////////// + +static inline int tspan_big_enough(int tspan) +{ + SkASSERT((unsigned)tspan <= kMaxTValue); + return tspan >> 10; +} + +#if 0 +static inline bool tangents_too_curvy(const SkVector& tan0, SkVector& tan1) +{ + static const SkScalar kFlatEnoughTangentDotProd = SK_Scalar1 * 99 / 100; + + SkASSERT(kFlatEnoughTangentDotProd > 0 && kFlatEnoughTangentDotProd < SK_Scalar1); + + return SkPoint::DotProduct(tan0, tan1) < kFlatEnoughTangentDotProd; +} +#endif + +// can't use tangents, since we need [0..1..................2] to be seen +// as definitely not a line (it is when drawn, but not parametrically) +// so we compare midpoints +#define CHEAP_DIST_LIMIT (SK_Scalar1/2) // just made this value up + +static bool cheap_dist_exceeds_limit(const SkPoint& pt, SkScalar x, SkScalar y) +{ + SkScalar dist = SkMaxScalar(SkScalarAbs(x - pt.fX), SkScalarAbs(y - pt.fY)); + // just made up the 1/2 + return dist > CHEAP_DIST_LIMIT; +} + +static bool quad_too_curvy(const SkPoint pts[3]) +{ +#if 0 + SkPoint mid; + SkEvalQuadAtHalf(pts, &mid); + return cheap_dist_exceeds_limit(mid, + SkScalarAve(pts[0].fX, pts[2].fX), + SkScalarAve(pts[0].fY, pts[2].fY)); +#else + // diff = (a/4 + b/2 + c/4) - (a/2 + c/2) + // diff = -a/4 + b/2 - c/4 + SkScalar dx = SkScalarHalf(pts[1].fX) - SkScalarHalf(SkScalarHalf(pts[0].fX + pts[2].fX)); + SkScalar dy = SkScalarHalf(pts[1].fY) - SkScalarHalf(SkScalarHalf(pts[0].fY + pts[2].fY)); + + SkScalar dist = SkMaxScalar(SkScalarAbs(dx), SkScalarAbs(dy)); + return dist > CHEAP_DIST_LIMIT; +#endif +} + +static bool cubic_too_curvy(const SkPoint pts[4]) +{ + SkPoint third; + + // test 1/3 + SkEvalCubicAt(pts, SK_Scalar1/3, &third, nil, nil); + if (cheap_dist_exceeds_limit(third, + SkScalarInterp(pts[0].fX, pts[3].fX, SK_Scalar1/3), + SkScalarInterp(pts[0].fY, pts[2].fY, SK_Scalar1/3))) + return true; + + // test 2/3 + SkEvalCubicAt(pts, SK_Scalar1*2/3, &third, nil, nil); + return cheap_dist_exceeds_limit(third, + SkScalarInterp(pts[0].fX, pts[3].fX, SK_Scalar1*2/3), + SkScalarInterp(pts[0].fY, pts[2].fY, SK_Scalar1*2/3)); +} + +SkScalar SkPathMeasure::compute_quad_segs(const SkPoint pts[3], SkScalar distance, + int mint, int maxt, int ptIndex) +{ + if (tspan_big_enough(maxt - mint) && quad_too_curvy(pts)) + { + SkPoint tmp[5]; + int halft = (mint + maxt) >> 1; + + SkChopQuadAtHalf(pts, tmp); + distance = this->compute_quad_segs(tmp, distance, mint, halft, ptIndex); + distance = this->compute_quad_segs(&tmp[2], distance, halft, maxt, ptIndex); + } + else + { + SkScalar d = SkPoint::Distance(pts[0], pts[2]); + SkASSERT(d >= 0); + if (!SkScalarNearlyZero(d)) + { + distance += d; + Segment* seg = fSegments.append(); + seg->fDistance = distance; + seg->fPtIndex = ptIndex; + seg->fType = kQuad_SegType; + seg->fTValue = maxt; + } + } + return distance; +} + +SkScalar SkPathMeasure::compute_cubic_segs(const SkPoint pts[4], SkScalar distance, + int mint, int maxt, int ptIndex) +{ + if (tspan_big_enough(maxt - mint) && cubic_too_curvy(pts)) + { + SkPoint tmp[7]; + int halft = (mint + maxt) >> 1; + + SkChopCubicAtHalf(pts, tmp); + distance = this->compute_cubic_segs(tmp, distance, mint, halft, ptIndex); + distance = this->compute_cubic_segs(&tmp[3], distance, halft, maxt, ptIndex); + } + else + { + SkScalar d = SkPoint::Distance(pts[0], pts[3]); + SkASSERT(d >= 0); + if (!SkScalarNearlyZero(d)) + { + distance += d; + Segment* seg = fSegments.append(); + seg->fDistance = distance; + seg->fPtIndex = ptIndex; + seg->fType = kCubic_SegType; + seg->fTValue = maxt; + } + } + return distance; +} + +void SkPathMeasure::buildSegments() +{ + SkPoint pts[4]; + int ptIndex = fFirstPtIndex; + SkScalar d, distance = 0; + bool isClosed = fForceClosed; + bool firstMoveTo = ptIndex < 0; + Segment* seg; + + fSegments.reset(); + for (;;) + { + switch (fIter.next(pts)) { + case SkPath::kMove_Verb: + if (!firstMoveTo) + goto DONE; + ptIndex += 1; + firstMoveTo = false; + break; + + case SkPath::kLine_Verb: + d = SkPoint::Distance(pts[0], pts[1]); + SkASSERT(d >= 0); + if (!SkScalarNearlyZero(d)) + { + distance += d; + seg = fSegments.append(); + seg->fDistance = distance; + seg->fPtIndex = ptIndex; + seg->fType = fIter.isCloseLine() ? kCloseLine_SegType : kLine_SegType; + seg->fTValue = kMaxTValue; + } + ptIndex += !fIter.isCloseLine(); + break; + + case SkPath::kQuad_Verb: + distance = this->compute_quad_segs(pts, distance, 0, kMaxTValue, ptIndex); + ptIndex += 2; + break; + + case SkPath::kCubic_Verb: + distance = this->compute_cubic_segs(pts, distance, 0, kMaxTValue, ptIndex); + ptIndex += 3; + break; + + case SkPath::kClose_Verb: + isClosed = true; + break; + + case SkPath::kDone_Verb: + goto DONE; + } + } +DONE: + fLength = distance; + fIsClosed = isClosed; + fFirstPtIndex = ptIndex + 1; + +#ifdef SK_DEBUG + { + const Segment* seg = fSegments.begin(); + const Segment* stop = fSegments.end(); + unsigned ptIndex = 0; + SkScalar distance = 0; + + while (seg < stop) + { + // SkDebugf("seg dist=%g t=%d p=%d\n", seg->fDistance, seg->fTValue, seg->fPtIndex); + + SkASSERT(seg->fDistance > distance); + SkASSERT(seg->fPtIndex >= ptIndex); + SkASSERT(seg->fTValue > 0); + + const Segment* s = seg; + while (s < stop - 1 && s[0].fPtIndex == s[1].fPtIndex) + { + SkASSERT(s[0].fType == s[1].fType); + SkASSERT(s[0].fTValue < s[1].fTValue); + s += 1; + } + + distance = seg->fDistance; + ptIndex = seg->fPtIndex; + seg += 1; + } + // SkDebugf("\n"); + } +#endif +} + +// marked as a friend in SkPath.h +const SkPoint* sk_get_path_points(const SkPath& path, int index) +{ + return &path.fPts[index]; +} + +static void compute_pos_tan(const SkPath& path, int firstPtIndex, int ptIndex, int segType, + SkScalar t, SkPoint* pos, SkVector* tangent) +{ + const SkPoint* pts = sk_get_path_points(path, ptIndex); + + switch (segType) { + case kLine_SegType: + case kCloseLine_SegType: + { + const SkPoint* endp = (segType == kLine_SegType) ? + &pts[1] : + sk_get_path_points(path, firstPtIndex); + + if (pos) + pos->set(SkScalarInterp(pts[0].fX, endp->fX, t), + SkScalarInterp(pts[0].fY, endp->fY, t)); + if (tangent) + tangent->setUnit(endp->fX - pts[0].fX, endp->fY - pts[0].fY); + } + break; + case kQuad_SegType: + SkEvalQuadAt(pts, t, pos, tangent); + if (tangent) + tangent->normalize(); + break; + case kCubic_SegType: + SkEvalCubicAt(pts, t, pos, tangent, nil); + if (tangent) + tangent->normalize(); + break; + default: + SkASSERT(!"unknown segType"); + } +} + +static void seg_to(const SkPath& src, int firstPtIndex, int ptIndex, int segType, SkScalar startT, SkScalar stopT, SkPath* dst) +{ + SkASSERT(startT >= 0 && startT <= SK_Scalar1); + SkASSERT(stopT >= 0 && stopT <= SK_Scalar1); + SkASSERT(startT <= stopT); + + if (SkScalarNearlyZero(stopT - startT)) + return; + + const SkPoint* pts = sk_get_path_points(src, ptIndex); + SkPoint tmp0[7], tmp1[7]; + + switch (segType) { + case kLine_SegType: + case kCloseLine_SegType: + { + const SkPoint* endp = (segType == kLine_SegType) ? + &pts[1] : + sk_get_path_points(src, firstPtIndex); + + if (stopT == kMaxTValue) + dst->lineTo(*endp); + else + dst->lineTo(SkScalarInterp(pts[0].fX, endp->fX, stopT), + SkScalarInterp(pts[0].fY, endp->fY, stopT)); + } + break; + case kQuad_SegType: + if (startT == 0) + { + if (stopT == SK_Scalar1) + dst->quadTo(pts[1], pts[2]); + else + { + SkChopQuadAt(pts, tmp0, stopT); + dst->quadTo(tmp0[1], tmp0[2]); + } + } + else + { + SkChopQuadAt(pts, tmp0, startT); + if (stopT == SK_Scalar1) + dst->quadTo(tmp0[3], tmp0[4]); + else + { + SkChopQuadAt(&tmp0[2], tmp1, SkScalarDiv(stopT - startT, SK_Scalar1 - startT)); + dst->quadTo(tmp1[1], tmp1[2]); + } + } + break; + case kCubic_SegType: + if (startT == 0) + { + if (stopT == SK_Scalar1) + dst->cubicTo(pts[1], pts[2], pts[3]); + else + { + SkChopCubicAt(pts, tmp0, stopT); + dst->cubicTo(tmp0[1], tmp0[2], tmp0[3]); + } + } + else + { + SkChopCubicAt(pts, tmp0, startT); + if (stopT == SK_Scalar1) + dst->cubicTo(tmp0[4], tmp0[5], tmp0[6]); + else + { + SkChopCubicAt(&tmp0[3], tmp1, SkScalarDiv(stopT - startT, SK_Scalar1 - startT)); + dst->cubicTo(tmp1[1], tmp1[2], tmp1[3]); + } + } + break; + default: + SkASSERT(!"unknown segType"); + sk_throw(); + } +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +SkPathMeasure::SkPathMeasure() +{ + fPath = nil; + fLength = -1; // signal we need to compute it + fForceClosed = false; + fFirstPtIndex = -1; +} + +SkPathMeasure::SkPathMeasure(const SkPath& path, bool forceClosed) +{ + fPath = &path; + fLength = -1; // signal we need to compute it + fForceClosed = forceClosed; + fFirstPtIndex = -1; + + fIter.setPath(path, forceClosed); +} + +SkPathMeasure::~SkPathMeasure() +{ +} + +/** Assign a new path, or nil to have none. +*/ +void SkPathMeasure::setPath(const SkPath* path, bool forceClosed) +{ + fPath = path; + fLength = -1; // signal we need to compute it + fForceClosed = forceClosed; + fFirstPtIndex = -1; + + if (path) + fIter.setPath(*path, forceClosed); + fSegments.reset(); +} + +SkScalar SkPathMeasure::getLength() +{ + if (fPath == nil) + return 0; + + if (fLength < 0) + this->buildSegments(); + + SkASSERT(fLength >= 0); + return fLength; +} + +const SkPathMeasure::Segment* SkPathMeasure::distanceToSegment(SkScalar distance, SkScalar* t) +{ + SkDEBUGCODE(SkScalar length = ) this->getLength(); + SkASSERT(distance >= 0 && distance <= length); + + const Segment* seg = fSegments.begin(); + int count = fSegments.count(); + + int index = SkTSearch<SkScalar>(&seg->fDistance, count, distance, sizeof(Segment)); + // don't care if we hit an exact match or not, so we xor index if it is negative + index ^= (index >> 31); + seg = &seg[index]; + + // now interpolate t-values with the prev segment (if possible) + SkScalar startT = 0, startD = 0; + // check if the prev segment is legal, and references the same set of points + if (index > 0) + { + startD = seg[-1].fDistance; + if (seg[-1].fPtIndex == seg->fPtIndex) + { + SkASSERT(seg[-1].fType == seg->fType); + startT = seg[-1].getScalarT(); + } + } + + SkASSERT(seg->getScalarT() > startT); + SkASSERT(distance >= startD); + SkASSERT(seg->fDistance > startD); + + *t = startT + SkScalarMulDiv(seg->getScalarT() - startT, + distance - startD, + seg->fDistance - startD); + return seg; +} + +bool SkPathMeasure::getPosTan(SkScalar distance, SkPoint* pos, SkVector* tangent) +{ + SkASSERT(fPath); + if (fPath == nil) + { + EMPTY: + return false; + } + + SkScalar length = this->getLength(); // call this to force computing it + int count = fSegments.count(); + + if (count == 0 || length == 0) + goto EMPTY; + + // pin the distance to a legal range + if (distance < 0) + distance = 0; + else if (distance > length) + distance = length; + + SkScalar t; + const Segment* seg = this->distanceToSegment(distance, &t); + + compute_pos_tan(*fPath, fSegments[0].fPtIndex, seg->fPtIndex, seg->fType, t, pos, tangent); + return true; +} + +bool SkPathMeasure::getMatrix(SkScalar distance, SkMatrix* matrix, MatrixFlags flags) +{ + SkPoint position; + SkVector tangent; + + if (this->getPosTan(distance, &position, &tangent)) + { + if (matrix) + { + if (flags & kGetTangent_MatrixFlag) + matrix->setSinCos(tangent.fY, tangent.fX, 0, 0); + else + matrix->reset(); + if (flags & kGetPosition_MatrixFlag) + matrix->postTranslate(position.fX, position.fY); + } + return true; + } + return false; +} + +bool SkPathMeasure::getSegment(SkScalar startD, SkScalar stopD, SkPath* dst, bool startWithMoveTo) +{ + SkASSERT(dst); + + SkScalar length = this->getLength(); // ensure we have built our segments + + if (startD < 0) + startD = 0; + if (stopD > length) + stopD = length; + if (startD >= stopD) + return false; + + SkPoint p; + SkScalar startT, stopT; + const Segment* seg = this->distanceToSegment(startD, &startT); + const Segment* stopSeg = this->distanceToSegment(stopD, &stopT); + SkASSERT(seg <= stopSeg); + + if (startWithMoveTo) + { + compute_pos_tan(*fPath, fSegments[0].fPtIndex, seg->fPtIndex, seg->fType, startT, &p, nil); + dst->moveTo(p); + } + + if (seg->fPtIndex == stopSeg->fPtIndex) + seg_to(*fPath, fSegments[0].fPtIndex, seg->fPtIndex, seg->fType, startT, stopT, dst); + else + { + do { + seg_to(*fPath, fSegments[0].fPtIndex, seg->fPtIndex, seg->fType, startT, SK_Scalar1, dst); + seg = SkPathMeasure::NextSegment(seg); + startT = 0; + } while (seg->fPtIndex < stopSeg->fPtIndex); + seg_to(*fPath, fSegments[0].fPtIndex, seg->fPtIndex, seg->fType, 0, stopT, dst); + } + return true; +} + +bool SkPathMeasure::isClosed() +{ + (void)this->getLength(); + return fIsClosed; +} + +/** Move to the next contour in the path. Return true if one exists, or false if + we're done with the path. +*/ +bool SkPathMeasure::nextContour() +{ + fLength = -1; + return this->getLength() > 0; +} + +//////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + +void SkPathMeasure::dump() +{ + SkDebugf("pathmeas: length=%g, segs=%d\n", fLength, fSegments.count()); + + for (int i = 0; i < fSegments.count(); i++) + { + const Segment* seg = &fSegments[i]; + SkDebugf("pathmeas: seg[%d] distance=%g, point=%d, t=%g, type=%d\n", + i, seg->fDistance, seg->fPtIndex, seg->getScalarT(), seg->fType); + } +} + +void SkPathMeasure::UnitTest() +{ +#ifdef SK_SUPPORT_UNITTEST + SkPath path; + + path.moveTo(0, 0); + path.lineTo(SK_Scalar1, 0); + path.lineTo(SK_Scalar1, SK_Scalar1); + path.lineTo(0, SK_Scalar1); + + SkPathMeasure meas(path, true); + SkScalar length = meas.getLength(); + SkASSERT(length == SK_Scalar1*4); + + path.reset(); + path.moveTo(0, 0); + path.lineTo(SK_Scalar1*3, SK_Scalar1*4); + meas.setPath(&path, false); + length = meas.getLength(); + SkASSERT(length == SK_Scalar1*5); + + path.reset(); + path.addCircle(0, 0, SK_Scalar1); + meas.setPath(&path, true); + length = meas.getLength(); + SkDebugf("circle arc-length = %g\n", length); + + for (int i = 0; i < 8; i++) + { + SkScalar d = length * i / 8; + SkPoint p; + SkVector v; + meas.getPosTan(d, &p, &v); + SkDebugf("circle arc-length=%g, pos[%g %g] tan[%g %g]\n", d, p.fX, p.fY, v.fX, v.fY); + } +#endif +} + +#endif diff --git a/libs/graphics/sgl/SkProcSpriteBlitter.cpp b/libs/graphics/sgl/SkProcSpriteBlitter.cpp new file mode 100644 index 0000000000..9f0592feaf --- /dev/null +++ b/libs/graphics/sgl/SkProcSpriteBlitter.cpp @@ -0,0 +1,38 @@ +#if 0 // experimental + +class SkProcSpriteBlitter : public SkSpriteBlitter { +public: + typedef void (*Proc)(void* dst, const void* src, int count, const U32 ctable[]); + + SkProcSpriteBlitter(const SkBitmap& source, Proc proc, unsigned srcShift, unsigned dstShift) + : SkSpriteBlitter(source), fProc(proc), fSrcShift(SkToU8(srcShift)), fDstShift(SkToU8(dstShift)) {} + + virtual void blitRect(int x, int y, int width, int height) + { + size_t dstRB = fDevice.rowBytes(); + size_t srcRB = fSource.rowBytes(); + char* dst = (char*)fDevice.getPixels() + y * dstRB + (x << fDstShift); + const char* src = (const char*)fSource.getPixels() + (y - fTop) * srcRB + ((x - fLeft) << fSrcShift); + Proc proc = fProc; + const U32* ctable = nil; + + if fSource.getColorTable()) + ctable = fSource.getColorTable()->lockColors(); + + while (--height >= 0) + { + proc(dst, src, width, ctable); + dst += dstRB; + src += srcRB; + } + + if fSource.getColorTable()) + fSource.getColorTable()->unlockColors(false); + } + +private: + Proc fProc; + U8 fSrcShift, fDstShift; +}; + +#endif diff --git a/libs/graphics/sgl/SkRasterizer.cpp b/libs/graphics/sgl/SkRasterizer.cpp new file mode 100644 index 0000000000..5e3dc995ae --- /dev/null +++ b/libs/graphics/sgl/SkRasterizer.cpp @@ -0,0 +1,45 @@ +#include "SkRasterizer.h" +#include "SkDraw.h" +#include "SkMaskFilter.h" +#include "SkPath.h" + +// do nothing for now, since we don't store anything at flatten time +SkRasterizer::SkRasterizer(SkRBuffer&) {} + +bool SkRasterizer::rasterize(const SkPath& fillPath, const SkMatrix& matrix, + const SkRect16* clipBounds, SkMaskFilter* filter, + SkMask* mask, SkMask::CreateMode mode) +{ + SkRect16 storage; + + if (clipBounds && filter && SkMask::kJustRenderImage_CreateMode != mode) + { + SkPoint16 margin; + SkMask srcM, dstM; + + srcM.fFormat = SkMask::kA8_Format; + srcM.fBounds.set(0, 0, 1, 1); + srcM.fImage = NULL; + if (!filter->filterMask(&dstM, srcM, matrix, &margin)) + return false; + + storage = *clipBounds; + storage.inset(-margin.fX, -margin.fY); + clipBounds = &storage; + } + + return this->onRasterize(fillPath, matrix, clipBounds, mask, mode); +} + +/* Our default implementation of the virtual method just scan converts +*/ +bool SkRasterizer::onRasterize(const SkPath& fillPath, const SkMatrix& matrix, + const SkRect16* clipBounds, + SkMask* mask, SkMask::CreateMode mode) +{ + SkPath devPath; + + fillPath.transform(matrix, &devPath); + return SkDraw::DrawToMask(devPath, clipBounds, NULL, NULL, mask, mode); +} + diff --git a/libs/graphics/sgl/SkRefCnt.cpp b/libs/graphics/sgl/SkRefCnt.cpp new file mode 100644 index 0000000000..7c3d59c430 --- /dev/null +++ b/libs/graphics/sgl/SkRefCnt.cpp @@ -0,0 +1,36 @@ +#include "SkRefCnt.h" + +SkAutoUnref::~SkAutoUnref() +{ + if (fObj) + fObj->unref(); +} + +bool SkAutoUnref::ref() +{ + if (fObj) + { + fObj->ref(); + return true; + } + return false; +} + +bool SkAutoUnref::unref() +{ + if (fObj) + { + fObj->unref(); + fObj = nil; + return true; + } + return false; +} + +SkRefCnt* SkAutoUnref::detach() +{ + SkRefCnt* obj = fObj; + + fObj = nil; + return obj; +} diff --git a/libs/graphics/sgl/SkRegion_path.cpp b/libs/graphics/sgl/SkRegion_path.cpp new file mode 100644 index 0000000000..ab45c8f493 --- /dev/null +++ b/libs/graphics/sgl/SkRegion_path.cpp @@ -0,0 +1,478 @@ +#include "SkRegionPriv.h" +#include "SkBlitter.h" +#include "SkScan.h" +#include "SkTDArray.h" +#include "SkPath.h" + +class SkRgnBuilder : public SkBlitter { +public: + virtual ~SkRgnBuilder(); + + void init(int maxHeight, int maxTransitions); + void done() { (void)this->collapsWithPrev(); } + + int computeRunCount() const; + void copyToRect(SkRect16*) const; + void copyToRgn(S16 runs[]) const; + + virtual void blitH(int x, int y, int width); + +#ifdef SK_DEBUG + void dump() const + { + SkDebugf("SkRgnBuilder: Top = %d\n", fTop); + const Scanline* line = (Scanline*)fStorage; + while (line < fCurrScanline) + { + SkDebugf("SkRgnBuilder::Scanline: LastY=%d, fXCount=%d", line->fLastY, line->fXCount); + for (int i = 0; i < line->fXCount; i++) + SkDebugf(" %d", line->firstX()[i]); + SkDebugf("\n"); + + line = line->nextScanline(); + } + } +#endif +private: + struct Scanline { + S16 fLastY; + S16 fXCount; + + S16* firstX() const { return (S16*)(this + 1); } + Scanline* nextScanline() const { return (Scanline*)((S16*)(this + 1) + fXCount); } + }; + S16* fStorage; + Scanline* fCurrScanline; + Scanline* fPrevScanline; + S16* fCurrXPtr; // points at next avialable x[] in fCurrScanline + S16 fTop; // first Y value + + bool collapsWithPrev() + { + if (fPrevScanline != nil && + fPrevScanline->fLastY + 1 == fCurrScanline->fLastY && + fPrevScanline->fXCount == fCurrScanline->fXCount && + !memcmp(fPrevScanline->firstX(), + fCurrScanline->firstX(), + fCurrScanline->fXCount * sizeof(S16))) + { + // update the height of fPrevScanline + fPrevScanline->fLastY = fCurrScanline->fLastY; + return true; + } + return false; + } +}; + +SkRgnBuilder::~SkRgnBuilder() +{ + sk_free(fStorage); +} + +void SkRgnBuilder::init(int maxHeight, int maxTransitions) +{ + int count = maxHeight * (3 + maxTransitions); + + // add maxTransitions to have slop for working buffer + fStorage = (S16*)sk_malloc_throw((count + 3 + maxTransitions) * sizeof(S16)); + + fCurrScanline = nil; // signal empty collection + fPrevScanline = nil; // signal first scanline +} + +void SkRgnBuilder::blitH(int x, int y, int width) +{ + if (fCurrScanline == nil) // first time + { + fTop = SkToS16(y); + fCurrScanline = (Scanline*)fStorage; + fCurrScanline->fLastY = SkToS16(y); + fCurrXPtr = fCurrScanline->firstX(); + } + else + { + SkASSERT(y >= fCurrScanline->fLastY); + + if (y > fCurrScanline->fLastY) + { + // if we get here, we're done with fCurrScanline + fCurrScanline->fXCount = SkToS16((int)(fCurrXPtr - fCurrScanline->firstX())); + + int prevLastY = fCurrScanline->fLastY; + if (!this->collapsWithPrev()) + { + fPrevScanline = fCurrScanline; + fCurrScanline = fCurrScanline->nextScanline(); + + } + if (y - 1 > prevLastY) // insert empty run + { + fCurrScanline->fLastY = SkToS16(y - 1); + fCurrScanline->fXCount = 0; + fCurrScanline = fCurrScanline->nextScanline(); + } + // setup for the new curr line + fCurrScanline->fLastY = SkToS16(y); + fCurrXPtr = fCurrScanline->firstX(); + } + } + // check if we should extend the current run, or add a new one + if (fCurrXPtr > fCurrScanline->firstX() && fCurrXPtr[-1] == x) + fCurrXPtr[-1] = SkToS16(x + width); + else + { + fCurrXPtr[0] = SkToS16(x); + fCurrXPtr[1] = SkToS16(x + width); + fCurrXPtr += 2; + } +} + +int SkRgnBuilder::computeRunCount() const +{ + if (fCurrScanline == nil) + return 0; + + const S16* line = fStorage; + const S16* stop = (const S16*)fCurrScanline; + + return 2 + (int)(stop - line); +} + +void SkRgnBuilder::copyToRect(SkRect16* r) const +{ + SkASSERT(fCurrScanline != nil); + SkASSERT((const S16*)fCurrScanline - fStorage == 4); + + const Scanline* line = (const Scanline*)fStorage; + SkASSERT(line->fXCount == 2); + + r->set(line->firstX()[0], fTop, line->firstX()[1], line->fLastY + 1); +} + +void SkRgnBuilder::copyToRgn(S16 runs[]) const +{ + SkASSERT(fCurrScanline != nil); + SkASSERT((const S16*)fCurrScanline - fStorage > 4); + + const Scanline* line = (const Scanline*)fStorage; + const Scanline* stop = fCurrScanline; + + *runs++ = fTop; + do { + *runs++ = SkToS16(line->fLastY + 1); + int count = line->fXCount; + if (count) + { + memcpy(runs, line->firstX(), count * sizeof(S16)); + runs += count; + } + *runs++ = kRunTypeSentinel; + line = line->nextScanline(); + } while (line < stop); + SkASSERT(line == stop); + *runs = kRunTypeSentinel; +} + +static int count_path_runtype_values(const SkPath& path, int* itop, int* ibot) +{ + static const U8 gPathVerbToInitialPointCount[] = { + 0, // kMove_Verb + 1, // kLine_Verb + 2, // kQuad_Verb + 3, // kCubic_Verb + 0, // kClose_Verb + 0 // kDone_Verb + }; + + static const U8 gPathVerbToMaxEdges[] = { + 0, // kMove_Verb + 1, // kLine_Verb + 2, // kQuad_VerbB + 3, // kCubic_Verb + 0, // kClose_Verb + 0 // kDone_Verb + }; + + SkPath::Iter iter(path, true); + SkPoint pts[4]; + SkPath::Verb verb; + + int maxEdges = 0; + SkScalar top = SkIntToScalar(SK_MaxS16); + SkScalar bot = SkIntToScalar(SK_MinS16); + + while ((verb = iter.next(pts)) != SkPath::kDone_Verb) + { + int ptCount = gPathVerbToInitialPointCount[verb]; + if (ptCount) + { + maxEdges += gPathVerbToMaxEdges[verb]; + for (int i = ptCount - 1; i >= 0; --i) + { + if (top > pts[i].fY) + top = pts[i].fY; + else if (bot < pts[i].fY) + bot = pts[i].fY; + } + } + } + SkASSERT(top <= bot); + + *itop = SkScalarRound(top); + *ibot = SkScalarRound(bot); + return maxEdges; +} + +bool SkRegion::setPath(const SkPath& path, const SkRegion* clip) +{ + SkDEBUGCODE(this->validate();) + + if (path.isEmpty() || clip && clip->isEmpty()) + return this->setEmpty(); + + // compute worst-case rgn-size for the path + int pathTop, pathBot; + int pathTransitions = count_path_runtype_values(path, &pathTop, &pathBot); + int clipTop, clipBot; + int clipTransitions = clip->count_runtype_values(&clipTop, &clipBot); + + int top = SkMax32(pathTop, clipTop); + int bot = SkMin32(pathBot, clipBot); + + if (top >= bot) + return this->setEmpty(); + + SkRgnBuilder builder; + + builder.init(bot - top, SkMax32(pathTransitions, clipTransitions)); + SkScan::FillPath(path, clip, &builder); + builder.done(); + + int count = builder.computeRunCount(); + if (count == 0) + { + return this->setEmpty(); + } + else if (count == kRectRegionRuns) + { + builder.copyToRect(&fBounds); + this->setRect(fBounds); + } + else + { + SkRegion tmp; + + tmp.fRunHead = RunHead::Alloc(count); + builder.copyToRgn(tmp.fRunHead->runs()); + compute_run_bounds(tmp.fRunHead->runs(), count, &tmp.fBounds); + this->swap(tmp); + } + SkDEBUGCODE(this->validate();) + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////// + +struct SkPrivPoint { + int fX, fY; + + friend int operator==(const SkPrivPoint& a, const SkPrivPoint& b) + { + return a.fX == b.fX && a.fY == b.fY; + } + friend int operator!=(const SkPrivPoint& a, const SkPrivPoint& b) + { + return a.fX != b.fX || a.fY != b.fY; + } +}; + +struct SkPrivLine { + SkPrivPoint fP0, fP1; + int fWinding; + SkPrivLine* fNext, *fPrev; + + void set(int x, int y0, int y1) + { + fP0.fX = x; + fP0.fY = y0; + fP1.fX = x; + fP1.fY = y1; + fWinding = y1 > y0; // 1: up, 0: down + fNext = fPrev = nil; + } + + void detach() + { + fNext->fPrev = fPrev; + fPrev->fNext = fNext; + } + + void attachNext(SkPrivLine* next) + { + next->fNext = fNext; + next->fPrev = this; + fNext->fPrev = next; + fNext = next; + } +}; + +static SkPrivLine* find_match(SkPrivLine* ctr, SkPrivLine* skip) +{ + const SkPrivPoint pt = skip->fP1; + int winding = skip->fWinding; + + SkPrivLine* start = ctr; + + SkPrivLine* closest_pos = nil; + int dist_pos = 0x7FFFFFFF; + SkPrivLine* closest_neg = nil; + int dist_neg = -0x7FFFFFFF; + + do { + // find a Line one the same scan as pt + if (ctr != skip && (ctr->fP0.fY == pt.fY || ctr->fP1.fY == pt.fY)) + { + int dist = ctr->fP0.fX - pt.fX; // keep the sign + + if (dist == 0) + { + if (winding == ctr->fWinding) // quick accept + goto FOUND; + else + goto NEXT; // quick reject + } + + if (dist < 0) + { + if (dist > dist_neg) + { + dist_neg = dist; + closest_neg = ctr; + } + } + else + { + if (dist < dist_pos) + { + dist_pos = dist; + closest_pos = ctr; + } + } + } + NEXT: + ctr = ctr->fNext; + } while (ctr != start); + + SkASSERT(closest_pos != nil || closest_neg != nil); + + if (closest_pos == nil) + ctr = closest_neg; + else if (closest_neg == nil) + ctr = closest_pos; + else + { + if (closest_neg->fP0.fY != pt.fY) + ctr = closest_pos; + else if (closest_pos->fP0.fY != pt.fY) + ctr = closest_neg; + else + { + if (closest_pos->fWinding != closest_neg->fWinding) + { + if (closest_pos->fWinding == winding) + ctr = closest_pos; + else + ctr = closest_neg; + } + else + { + if (winding == 0) + ctr = closest_pos; + else + ctr = closest_neg; + } + } + } + +FOUND: + SkASSERT(ctr && ctr->fP0.fY == pt.fY); + return ctr; +} + +static void LinesToPath(SkPrivLine lines[], int count, SkPath* path) +{ + SkASSERT(count > 1); + + // turn the array into a linked list + lines[0].fNext = &lines[1]; + lines[0].fPrev = &lines[count - 1]; + for (int i = 1; i < count - 1; i++) + { + lines[i].fNext = &lines[i+1]; + lines[i].fPrev = &lines[i-1]; + } + lines[count - 1].fNext = &lines[0]; + lines[count - 1].fPrev = &lines[count - 2]; + + SkPrivLine* head = lines; + + // loop through looking for contours + while (count > 0) + { + SkPrivLine* ctr = head; + SkPrivLine* first = ctr; + head = head->fNext; + + path->moveTo(SkIntToScalar(ctr->fP0.fX), SkIntToScalar(ctr->fP0.fY)); + do { + SkPrivLine* next = find_match(head, ctr); + + if (ctr->fP1 != next->fP0) + { + path->lineTo(SkIntToScalar(ctr->fP1.fX), SkIntToScalar(ctr->fP1.fY)); // Vertical + path->lineTo(SkIntToScalar(next->fP0.fX), SkIntToScalar(next->fP0.fY)); // Horzontal + } + if (head == next) + head = head->fNext; + next->detach(); + count -= 1; + ctr = next; + } while (ctr != first); + path->close(); + ctr->detach(); + count -= 1; + } +// SkASSERT(count == 0); +} + +bool SkRegion::getBoundaryPath(SkPath* path) const +{ + if (this->isEmpty()) + return false; + + const SkRect16& bounds = this->getBounds(); + + if (this->isRect()) + { + SkRect r; + r.set(bounds); // this converts the ints to scalars + path->addRect(r); + return true; + } + + SkRegion::Iterator iter(*this); + SkTDArray<SkPrivLine> lines; + + for (const SkRect16& r = iter.rect(); !iter.done(); iter.next()) + { + SkPrivLine* line = lines.append(2); + line[0].set(r.fLeft, r.fBottom, r.fTop); + line[1].set(r.fRight, r.fTop, r.fBottom); + } + + LinesToPath(lines.begin(), lines.count(), path); + return true; +} + + diff --git a/libs/graphics/sgl/SkScalerContext.cpp b/libs/graphics/sgl/SkScalerContext.cpp new file mode 100644 index 0000000000..9e44bdcfcd --- /dev/null +++ b/libs/graphics/sgl/SkScalerContext.cpp @@ -0,0 +1,498 @@ +#include "SkScalerContext.h" +#include "SkDescriptor.h" +#include "SkDraw.h" +#include "SkFontHost.h" +#include "SkMaskFilter.h" +#include "SkPathEffect.h" +#include "SkRasterizer.h" +#include "SkRegion.h" +#include "SkStroke.h" +#include "SkThread.h" + +#define ComputeBWRowBytes(width) (((unsigned)(width) + 7) >> 3) + +static uint16_t compute_rowbytes(unsigned format, unsigned width) +{ + if (format == SkMask::kBW_Format) + width = ComputeBWRowBytes(width); + return SkToU16(width); +} + +size_t SkGlyph::computeImageSize() const +{ + size_t size = fRowBytes * fHeight; + if (fMaskFormat == SkMask::k3D_Format) + size *= 3; + return size; +} + +#ifdef SK_DEBUG + #define DUMP_RECx +#endif + +static SkFlattenable* load_flattenable(const SkDescriptor* desc, uint32_t tag) +{ + SkFlattenable* obj = NULL; + uint32_t len; + const void* data = desc->findEntry(tag, &len); + + if (data) + { + SkRBuffer buffer(data, len); + SkFlattenable::Factory fact = (SkFlattenable::Factory)buffer.readPtr(); + SkASSERT(fact); + obj = fact(buffer); + SkASSERT(buffer.pos() == buffer.size()); + } + return obj; +} + +SkScalerContext::SkScalerContext(const SkDescriptor* desc) + : fPathEffect(NULL), fMaskFilter(NULL) +{ + memset(fAuxContext, 0, sizeof(fAuxContext)); + + const Rec* rec = (const Rec*)desc->findEntry(kRec_SkDescriptorTag, NULL); + SkASSERT(rec); + + fRec = *rec; + +#ifdef DUMP_REC + desc->assertChecksum(); + SkDebugf("SkScalarContext checksum %x count %d length %d\n", desc->getChecksum(), desc->getCount(), desc->getLength()); + SkDebugf(" textsize %g prescale %g preskew %g post [%g %g %g %g]\n", + rec->fTextSize, rec->fPreScaleX, rec->fPreSkewX, rec->fPost2x2[0][0], + rec->fPost2x2[0][1], rec->fPost2x2[1][0], rec->fPost2x2[1][1]); + SkDebugf(" frame %g miter %g hints %d framefill %d aa %d join %d\n", + rec->fFrameWidth, rec->fMiterLimit, rec->fUseHints, rec->fFrameAndFill, + rec->fDoAA, rec->fStrokeJoin); + SkDebugf(" pathEffect %x maskFilter %x\n", desc->findEntry(kPathEffect_SkDescriptorTag, NULL), + desc->findEntry(kMaskFilter_SkDescriptorTag, NULL)); +#endif + + fPathEffect = (SkPathEffect*)load_flattenable(desc, kPathEffect_SkDescriptorTag); + fMaskFilter = (SkMaskFilter*)load_flattenable(desc, kMaskFilter_SkDescriptorTag); + fRasterizer = (SkRasterizer*)load_flattenable(desc, kRasterizer_SkDescriptorTag); +} + +SkScalerContext::~SkScalerContext() +{ + fPathEffect->safeUnref(); + fMaskFilter->safeUnref(); + fRasterizer->safeUnref(); + + for (unsigned i = 0; i < SK_ARRAY_COUNT(fAuxContext); i++) + delete fAuxContext[i]; +} + +static void glyph2mask(const SkGlyph& glyph, SkMask* mask) +{ + SkASSERT(&glyph && mask); + + mask->fImage = (uint8_t*)glyph.fImage; + mask->fBounds.set(glyph.fLeft, glyph.fTop, + glyph.fLeft + glyph.fWidth, + glyph.fTop + glyph.fHeight); + mask->fRowBytes = glyph.fRowBytes; + mask->fFormat = glyph.fMaskFormat; +} + +void SkScalerContext::getMetrics(SkGlyph* glyph) +{ + this->generateMetrics(glyph); + // assume that we're handling this glyph + glyph->fUseAuxContext = false; + + if (0 == glyph->fGlyphID) + { + SkFontHost::ScalerContextID id = SkFontHost::FindScalerContextIDForUnichar(glyph->fCharCode); + if (SK_UnknownAuxScalerContextID != id) + { + SkASSERT(id > 0 && (unsigned)id <= SK_ARRAY_COUNT(fAuxContext)); + SkScalerContext* ctx = fAuxContext[id - 1]; + if (NULL == ctx) + { + ctx = SkFontHost::CreateScalerContextFromID(id, fRec); + SkASSERT(ctx); + fAuxContext[id - 1] = ctx; + } + ctx->generateMetrics(glyph); + glyph->fUseAuxContext = true; + } + } + + if (0 == glyph->fWidth) + return; + + if (fRec.fFrameWidth > 0 || fPathEffect != NULL || fRasterizer != NULL) + { + SkPath devPath, fillPath; + SkMatrix fillToDevMatrix; + + this->internalGetPath(*glyph, &fillPath, &devPath, &fillToDevMatrix); + + if (fRasterizer) + { + SkMask mask; + + if (fRasterizer->rasterize(fillPath, fillToDevMatrix, NULL, + fMaskFilter, &mask, + SkMask::kJustComputeBounds_CreateMode)) + { + glyph->fLeft = mask.fBounds.fLeft; + glyph->fTop = mask.fBounds.fTop; + glyph->fWidth = SkToU16(mask.fBounds.width()); + glyph->fHeight = SkToU16(mask.fBounds.height()); + } + else // draw nothing 'cause we failed + { + glyph->fLeft = 0; + glyph->fTop = 0; + glyph->fWidth = 0; + glyph->fHeight = 0; + return; + } + } + else // just use devPath + { + SkRect r; + SkRect16 ir; + + devPath.computeBounds(&r, SkPath::kExact_BoundsType); + r.roundOut(&ir); + + glyph->fLeft = ir.fLeft; + glyph->fTop = ir.fTop; + glyph->fWidth = SkToU16(ir.width()); + glyph->fHeight = SkToU16(ir.height()); + } + } + + glyph->fMaskFormat = SkToU8(fRec.fDoAA ? SkMask::kA8_Format : SkMask::kBW_Format); + + if (fMaskFilter) + { + SkMask src, dst; + SkMatrix matrix; + + glyph2mask(*glyph, &src); + fRec.getMatrixFrom2x2(&matrix); + + src.fImage = NULL; // only want the bounds from the filter + if (fMaskFilter->filterMask(&dst, src, matrix, NULL)) + { + SkASSERT(dst.fImage == NULL); + glyph->fLeft = dst.fBounds.fLeft; + glyph->fTop = dst.fBounds.fTop; + glyph->fWidth = SkToU16(dst.fBounds.width()); + glyph->fHeight = SkToU16(dst.fBounds.height()); + glyph->fMaskFormat = dst.fFormat; + } + } + + glyph->fRowBytes = compute_rowbytes(glyph->fMaskFormat, glyph->fWidth); +} + +//#define PLAY_WITH_GAMMA + +#ifdef PLAY_WITH_GAMMA +static SkFixed interp(SkFixed a, SkFixed b, int scale) // scale is [0..255] +{ + return a + ((b - a) * scale >> 8); +} + +static void filter_image(uint8_t image[], size_t size) +{ + static uint8_t gGammaTable[256]; + static bool gInit; + + if (!gInit) + { + for (int i = 0; i < 256; i++) + { + SkFixed n = i * 257; + n += n >> 15; + SkASSERT(n >= 0 && n <= SK_Fixed1); + + // n = SkFixedSqrt(n); + n = interp(SkFixedMul(n, n), n, 0xDD); + + n = n * 255 >> 16; + // SkDebugf("morph %d -> %d\n", i, n); + gGammaTable[i] = SkToU8(n); + } + gInit = true; + } + + const uint8_t* table = gGammaTable; + uint8_t* stop = image + size; + while (image < stop) + { + *image = table[*image]; + image += 1; + } +} +#endif + +void SkScalerContext::getImage(const SkGlyph& origGlyph) +{ + const SkGlyph* glyph = &origGlyph; + + SkGlyph tmpGlyph; + if (fMaskFilter) // restore the prefilter bounds + { + tmpGlyph.fCharCode = origGlyph.fCharCode; + + // need the original bounds, sans our maskfilter + SkMaskFilter* mf = fMaskFilter; + fMaskFilter = NULL; // temp disable + this->getMetrics(&tmpGlyph); + fMaskFilter = mf; // restore + + tmpGlyph.fImage = origGlyph.fImage; + + // we need the prefilter bounds to be <= filter bounds + SkASSERT(tmpGlyph.fWidth <= origGlyph.fWidth); + SkASSERT(tmpGlyph.fHeight <= origGlyph.fHeight); + glyph = &tmpGlyph; + } + + if (fRec.fFrameWidth > 0 || fPathEffect != NULL || fRasterizer != NULL) + { + SkPath devPath, fillPath; + SkMatrix fillToDevMatrix; + + this->internalGetPath(*glyph, &fillPath, &devPath, &fillToDevMatrix); + + if (fRasterizer) + { + SkMask mask; + + mask.fFormat = SkMask::kA8_Format; + mask.fRowBytes = glyph->fRowBytes; + mask.fBounds.set(glyph->fLeft, + glyph->fTop, + glyph->fLeft + glyph->fWidth, + glyph->fTop + glyph->fHeight); + mask.fImage = (uint8_t*)glyph->fImage; + memset(glyph->fImage, 0, glyph->fRowBytes * glyph->fHeight); + + if (!fRasterizer->rasterize(fillPath, fillToDevMatrix, NULL, + fMaskFilter, &mask, SkMask::kJustRenderImage_CreateMode)) + { + return; + } + } + else + { + SkBitmap bm; + SkBitmap::Config config; + SkMatrix matrix; + SkRegion clip; + SkPaint paint; + SkDraw draw; + + if (fRec.fDoAA) + { + config = SkBitmap::kA8_Config; + paint.setAntiAliasOn(true); + } + else + { + config = SkBitmap::kA1_Config; + paint.setAntiAliasOn(false); + } + + clip.setRect(0, 0, glyph->fWidth, glyph->fHeight); + matrix.setTranslate(-SkIntToScalar(glyph->fLeft), -SkIntToScalar(glyph->fTop)); + bm.setConfig(config, glyph->fWidth, glyph->fHeight); + bm.setPixels(glyph->fImage); + memset(glyph->fImage, 0, bm.height() * bm.rowBytes()); + + draw.fClip = &clip; + draw.fMatrix = &matrix; + draw.fDevice = &bm; + draw.fBounder = NULL; + draw.drawPath(devPath, paint); + } + } + else + { + SkScalerContext* ctx = this; + if (glyph->fUseAuxContext) + { + SkFontHost::ScalerContextID id = SkFontHost::FindScalerContextIDForUnichar(glyph->fCharCode); + SkASSERT(id > 0 && (unsigned)id <= SK_ARRAY_COUNT(fAuxContext)); + ctx = fAuxContext[id - 1]; + SkASSERT(ctx); + } + ctx->generateImage(*glyph); + } + + if (fMaskFilter) + { + SkMask srcM, dstM; + SkMatrix matrix; + + SkASSERT(SkMask::k3D_Format != glyph->fMaskFormat); // the src glyph image shouldn't be 3D + glyph2mask(*glyph, &srcM); + fRec.getMatrixFrom2x2(&matrix); + + if (fMaskFilter->filterMask(&dstM, srcM, matrix, NULL)) + { + if (true) // hack until I can figure out why the assert below sometimes fails + { + int width = SkFastMin32(origGlyph.fWidth, dstM.fBounds.width()); + int height = SkFastMin32(origGlyph.fHeight, dstM.fBounds.height()); + int srcRB = origGlyph.fRowBytes; + int dstRB = dstM.fRowBytes; + + const uint8_t* src = (const uint8_t*)dstM.fImage; + uint8_t* dst = (uint8_t*)origGlyph.fImage; + + if (SkMask::k3D_Format == dstM.fFormat) // we have to copy 3 times as much + height *= 3; + + while (--height >= 0) + { + memcpy(dst, src, width); + src += srcRB; + dst += dstRB; + } + } + else + { + SkASSERT(origGlyph.fWidth == dstM.fBounds.width()); + SkASSERT(origGlyph.fTop == dstM.fBounds.fTop); + SkASSERT(origGlyph.fLeft == dstM.fBounds.fLeft); + SkASSERT(origGlyph.fHeight == dstM.fBounds.height()); + SkASSERT(origGlyph.computeImageSize() == dstM.computeTotalImageSize()); + + memcpy(glyph->fImage, dstM.fImage, dstM.computeTotalImageSize()); + } + SkMask::FreeImage(dstM.fImage); + } + } +} + +void SkScalerContext::getPath(const SkGlyph& glyph, SkPath* path) +{ + this->internalGetPath(glyph, NULL, path, NULL); +} + +void SkScalerContext::getLineHeight(SkPoint* above, SkPoint* below) +{ + this->generateLineHeight(above, below); + + // apply any mods due to effects (e.g. stroking, etc.)... +} + +/////////////////////////////////////////////////////////////////////// + +void SkScalerContext::internalGetPath(const SkGlyph& glyph, SkPath* fillPath, SkPath* devPath, SkMatrix* fillToDevMatrix) +{ + SkPath path; + + this->generatePath(glyph, &path); + + if (fRec.fFrameWidth > 0 || fPathEffect != NULL) + { + // need the path in user-space, with only the point-size applied + // so that our stroking and effects will operate the same way they + // would if the user had extracted the path themself, and then + // called drawPath + SkPath localPath; + SkMatrix matrix, inverse; + + fRec.getMatrixFrom2x2(&matrix); + matrix.invert(&inverse); + path.transform(inverse, &localPath); + // now localPath is only affected by the paint settings, and not the canvas matrix + + SkScalar width = fRec.fFrameWidth; + + if (fPathEffect) + { + SkPath effectPath; + + if (fPathEffect->filterPath(&effectPath, localPath, &width)) + localPath.swap(effectPath); + } + + if (width > 0) + { + SkStroke stroker; + SkPath outline; + + stroker.setWidth(width); + stroker.setMiterLimit(fRec.fMiterLimit); + stroker.setJoin((SkPaint::Join)fRec.fStrokeJoin); + stroker.setDoFill(fRec.fFrameAndFill != 0); + stroker.strokePath(localPath, &outline); + localPath.swap(outline); + } + + // now return stuff to the caller + if (fillToDevMatrix) + *fillToDevMatrix = matrix; + + if (devPath) + localPath.transform(matrix, devPath); + + if (fillPath) + fillPath->swap(localPath); + } + else // nothing tricky to do + { + if (fillToDevMatrix) + fillToDevMatrix->reset(); + + if (devPath) + { + if (fillPath == NULL) + devPath->swap(path); + else + *devPath = path; + } + + if (fillPath) + fillPath->swap(path); + } +} + + +void SkScalerContext::Rec::getMatrixFrom2x2(SkMatrix* dst) const +{ + dst->reset(); + dst->setScaleX(fPost2x2[0][0]); + dst->setSkewX( fPost2x2[0][1]); + dst->setSkewY( fPost2x2[1][0]); + dst->setScaleY(fPost2x2[1][1]); +} + +void SkScalerContext::Rec::getLocalMatrix(SkMatrix* m) const +{ + m->setScale(SkScalarMul(fTextSize, fPreScaleX), fTextSize, 0, 0); + if (fPreSkewX) + m->postSkew(fPreSkewX, 0, 0, 0); +} + +void SkScalerContext::Rec::getSingleMatrix(SkMatrix* m) const +{ + this->getLocalMatrix(m); + + // now concat the device matrix + { + SkMatrix deviceMatrix; + this->getMatrixFrom2x2(&deviceMatrix); + m->postConcat(deviceMatrix); + } +} + +#include "SkFontHost.h" + +SkScalerContext* SkScalerContext::Create(const SkDescriptor* desc) +{ + return SkFontHost::CreateScalerContext(desc); +} + diff --git a/libs/graphics/sgl/SkScan.cpp b/libs/graphics/sgl/SkScan.cpp new file mode 100644 index 0000000000..b8534e6342 --- /dev/null +++ b/libs/graphics/sgl/SkScan.cpp @@ -0,0 +1,32 @@ +#include "SkScan.h" +#include "SkBlitter.h" +#include "SkRegion.h" + +void SkScan::FillRect(const SkRect& rect, const SkRegion* clip, SkBlitter* blitter) +{ + SkRect16 r; + + rect.round(&r); + SkScan::FillDevRect(r, clip, blitter); +} + +void SkScan::FillDevRect(const SkRect16& r, const SkRegion* clip, SkBlitter* blitter) +{ + if (!r.isEmpty()) + { + if (clip) + { + SkRegion::Cliperator cliper(*clip, r); + const SkRect16& rr = cliper.rect(); + + while (!cliper.done()) + { + blitter->blitRect(rr.fLeft, rr.fTop, rr.width(), rr.height()); + cliper.next(); + } + } + else + blitter->blitRect(r.fLeft, r.fTop, r.width(), r.height()); + } +} + diff --git a/libs/graphics/sgl/SkScan.h b/libs/graphics/sgl/SkScan.h new file mode 100644 index 0000000000..c8b62c8d71 --- /dev/null +++ b/libs/graphics/sgl/SkScan.h @@ -0,0 +1,30 @@ +#ifndef SkScan_DEFINED +#define SkScan_DEFINED + +#include "SkRect.h" + +class SkRegion; +class SkBlitter; +class SkPath; + +class SkScan { +public: + static void FillDevRect(const SkRect16&, const SkRegion* clip, SkBlitter*); + static void FillRect(const SkRect&, const SkRegion* clip, SkBlitter*); + static void FillPath(const SkPath&, const SkRegion* clip, SkBlitter*); + + static void HairLine(const SkPoint&, const SkPoint&, const SkRegion* clip, SkBlitter*); + static void HairRect(const SkRect&, const SkRegion* clip, SkBlitter*); + static void HairPath(const SkPath&, const SkRegion* clip, SkBlitter*); + + static void FrameRect(const SkRect&, SkScalar width, const SkRegion* clip, SkBlitter*); + + static void AntiFillRect(const SkRect&, const SkRegion* clip, SkBlitter*); + static void AntiFillPath(const SkPath&, const SkRegion* clip, SkBlitter*); + + static void AntiHairLine(const SkPoint&, const SkPoint&, const SkRegion* clip, SkBlitter*); + static void AntiHairRect(const SkRect&, const SkRegion* clip, SkBlitter*); + static void AntiHairPath(const SkPath&, const SkRegion* clip, SkBlitter*); +}; + +#endif diff --git a/libs/graphics/sgl/SkScanPriv.h b/libs/graphics/sgl/SkScanPriv.h new file mode 100644 index 0000000000..2fce606165 --- /dev/null +++ b/libs/graphics/sgl/SkScanPriv.h @@ -0,0 +1,25 @@ +#ifndef SkScanPriv_DEFINED +#define SkScanPriv_DEFINED + +#include "SkScan.h" +#include "SkBlitter.h" + +class SkScanClipper { +public: + SkScanClipper(SkBlitter* blitter, const SkRegion* clip, const SkRect16& bounds); + + SkBlitter* getBlitter() const { return fBlitter; } + const SkRect16* getClipRect() const { return fClipRect; } + +private: + SkRectClipBlitter fRectBlitter; + SkRgnClipBlitter fRgnBlitter; + SkBlitter* fBlitter; + const SkRect16* fClipRect; +}; + +void sk_fill_path(const SkPath& path, const SkRect16* clipRect, SkBlitter* blitter, + const SkRect16& ir, int shiftEdgesUp); + +#endif + diff --git a/libs/graphics/sgl/SkScan_AntiPath.cpp b/libs/graphics/sgl/SkScan_AntiPath.cpp new file mode 100644 index 0000000000..592edff8d1 --- /dev/null +++ b/libs/graphics/sgl/SkScan_AntiPath.cpp @@ -0,0 +1,209 @@ +#include "SkScanPriv.h" +#include "SkPath.h" +#include "SkMatrix.h" +#include "SkBlitter.h" +#include "SkRegion.h" +#include "SkAntiRun.h" + +#define SHIFT 2 +#define SCALE (1 << SHIFT) +#define MASK (SCALE - 1) + +/////////////////////////////////////////////////////////////////////////////////////////// + +class SuperBlitter : public SkBlitter { +public: + SuperBlitter(SkBlitter* realBlitter, const SkRegion* clip, const SkRect16& ir); + virtual ~SuperBlitter() + { + sk_free(fRuns.fRuns); + } + void flush(); + + virtual void blitH(int x, int y, int width); + virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const S16 runs[]) + { + SkASSERT(!"How did I get here?"); + } + virtual void blitV(int x, int y, int height, SkAlpha alpha) + { + SkASSERT(!"How did I get here?"); + } + virtual void blitRect(int x, int y, int width, int height) + { + SkASSERT(!"How did I get here?"); + } + +private: + SkBlitter* fRealBlitter; + int fCurrIY; + int fWidth, fLeft, fSuperLeft; + SkAlphaRuns fRuns; + + SkDEBUGCODE(int fCurrX;) + SkDEBUGCODE(int fCurrY;) +}; + +SuperBlitter::SuperBlitter(SkBlitter* realBlitter, const SkRegion* clip, const SkRect16& ir) +{ + fRealBlitter = realBlitter; + + int width = ir.width(); + + // extra one to store the zero at the end + fRuns.fRuns = (S16*)sk_malloc_throw((width + 1 + (width + 2)/2) * sizeof(S16)); + fRuns.fAlpha = (U8*)(fRuns.fRuns + width + 1); + fRuns.reset(width); + + fLeft = ir.fLeft; + fSuperLeft = ir.fLeft << SHIFT; + fWidth = ir.width(); + fCurrIY = -1; + SkDEBUGCODE(fCurrX = -1; fCurrY = -1;) +} + +void SuperBlitter::flush() +{ + if (fCurrIY >= 0) + { + if (!fRuns.empty()) + { + // SkDEBUGCODE(fRuns.dump();) + fRealBlitter->blitAntiH(fLeft, fCurrIY, fRuns.fAlpha, fRuns.fRuns); + fRuns.reset(fWidth); + } + fCurrIY = -1; + SkDEBUGCODE(fCurrX = -1;) + } +} + +static inline int coverage_to_alpha(int aa) +{ + aa <<= 8 - 2*SHIFT; + aa -= aa >> (8 - SHIFT - 1); + return aa; +} + +#define SUPER_Mask ((1 << SHIFT) - 1) + +void SuperBlitter::blitH(int x, int y, int width) +{ + int iy = y >> SHIFT; + SkASSERT(iy >= fCurrIY); + + x -= fSuperLeft; +#if 0 // I should just need to assert + SkASSERT(x >= 0); +#else + // hack, until I figure out why my cubics (I think) go beyond the bounds + if (x < 0) + { + width += x; + x = 0; + } +#endif + +#ifdef SK_DEBUG + SkASSERT(y >= fCurrY); + SkASSERT(y != fCurrY || x >= fCurrX); + fCurrY = y; +#endif + + if (iy != fCurrIY) // new scanline + { + this->flush(); + fCurrIY = iy; + } + + // we sub 1 from maxValue 1 time for each block, so that we don't + // hit 256 as a summed max, but 255. +// int maxValue = (1 << (8 - SHIFT)) - (((y & MASK) + 1) >> SHIFT); + +#if 0 + SkAntiRun<SHIFT> arun; + arun.set(x, x + width); + fRuns.add(x >> SHIFT, arun.getStartAlpha(), arun.getMiddleCount(), arun.getStopAlpha(), maxValue); +#else + { + int start = x; + int stop = x + width; + + SkASSERT(start >= 0 && stop > start); + int fb = start & SUPER_Mask; + int fe = stop & SUPER_Mask; + int n = (stop >> SHIFT) - (start >> SHIFT) - 1; + + if (n < 0) + { + fb = fe - fb; + n = 0; + fe = 0; + } + else + { + if (fb == 0) + n += 1; + else + fb = (1 << SHIFT) - fb; + } + fRuns.add(x >> SHIFT, coverage_to_alpha(fb), n, coverage_to_alpha(fe), (1 << (8 - SHIFT)) - (((y & MASK) + 1) >> SHIFT)); + } +#endif + +#ifdef SK_DEBUG + fRuns.assertValid(y & MASK, (1 << (8 - SHIFT))); + fCurrX = x + width; +#endif +} + +////////////////////////////////////////////////////////////////////////////////////////// + +static int overflows_short(int value) +{ + return value - (short)value; +} + +void SkScan::AntiFillPath(const SkPath& path, const SkRegion* clip, SkBlitter* blitter) +{ + if (clip && clip->isEmpty()) + return; + + SkRect r; + SkRect16 ir; + + path.computeBounds(&r, SkPath::kFast_BoundsType); + r.roundOut(&ir); + if (ir.isEmpty()) + return; + + if (overflows_short(ir.fLeft << SHIFT) || + overflows_short(ir.fRight << SHIFT) || + overflows_short(ir.width() << SHIFT) || + overflows_short(ir.fTop << SHIFT) || + overflows_short(ir.fBottom << SHIFT) || + overflows_short(ir.height() << SHIFT)) + return; + + SkScanClipper clipper(blitter, clip, ir); + const SkRect16* clipRect = clipper.getClipRect(); + + blitter = clipper.getBlitter(); + if (blitter == nil) // clipped out + return; + + SuperBlitter superBlit(blitter, clip, ir); + SkRect16 superIR, superRect, *superClipRect = nil; + + superIR.set(ir.fLeft << SHIFT, ir.fTop << SHIFT, + ir.fRight << SHIFT, ir.fBottom << SHIFT); + + if (clipRect) + { + superRect.set( clipRect->fLeft << SHIFT, clipRect->fTop << SHIFT, + clipRect->fRight << SHIFT, clipRect->fBottom << SHIFT); + superClipRect = &superRect; + } + + sk_fill_path(path, superClipRect, &superBlit, superIR, SHIFT); + superBlit.flush(); +} diff --git a/libs/graphics/sgl/SkScan_Antihair.cpp b/libs/graphics/sgl/SkScan_Antihair.cpp new file mode 100644 index 0000000000..81f503bece --- /dev/null +++ b/libs/graphics/sgl/SkScan_Antihair.cpp @@ -0,0 +1,397 @@ +#include "SkScan.h" +#include "SkBlitter.h" +#include "SkRegion.h" +#include "SkFDot6.h" + +#define HLINE_STACK_BUFFER 100 + +//#define TEST_GAMMA + +#ifdef TEST_GAMMA + static U8 gGammaTable[256]; + #define ApplyGamma(table, alpha) (table)[alpha] + + static void build_gamma_table() + { + static bool gInit = false; + + if (gInit == false) + { + for (int i = 0; i < 256; i++) + { + SkFixed n = i * 257; + n += n >> 15; + SkASSERT(n >= 0 && n <= SK_Fixed1); + n = SkFixedSqrt(n); + n = n * 255 >> 16; + // SkDebugf("morph %d -> %d\n", i, n); + gGammaTable[i] = SkToU8(n); + } + gInit = true; + } + } +#else + #define ApplyGamma(table, alpha) SkToU8(alpha) +#endif + + +static void call_hline_blitter(SkBlitter* blitter, int x, int y, int count, U8 alpha) +{ + SkASSERT(count > 0); + + S16 runs[HLINE_STACK_BUFFER + 1]; + U8 aa[HLINE_STACK_BUFFER]; + + aa[0] = ApplyGamma(gGammaTable, alpha); + do { + int n = count; + if (n > HLINE_STACK_BUFFER) + n = HLINE_STACK_BUFFER; + + runs[0] = SkToS16(n); + runs[n] = 0; + blitter->blitAntiH(x, y, aa, runs); + x += n; + count -= n; + } while (count > 0); +} + +static void hline(int x, int stopx, SkFixed fy, SkFixed /*slope*/, SkBlitter* blitter) +{ + SkASSERT(x < stopx); + int count = stopx - x; + fy += SK_Fixed1/2; + + int y = fy >> 16; + U8 a = (U8)(fy >> 8); + + // lower line + if (a) + call_hline_blitter(blitter, x, y, count, a); + + // upper line + a = (U8)(255 - a); + if (a) + call_hline_blitter(blitter, x, y - 1, count, a); +} + +static void horish(int x, int stopx, SkFixed fy, SkFixed dy, SkBlitter* blitter) +{ + SkASSERT(x < stopx); + +#ifdef TEST_GAMMA + const U8* gamma = gGammaTable; +#endif + S16 runs[2]; + U8 aa[1]; + + runs[0] = 1; + runs[1] = 0; + + fy += SK_Fixed1/2; + do { + int lower_y = fy >> 16; + U8 a = (U8)(fy >> 8); + + if (a) + { + aa[0] = ApplyGamma(gamma, a); + blitter->blitAntiH(x, lower_y, aa, runs); + // the clipping blitters might edit runs, but should not affect us + SkASSERT(runs[0] == 1); + SkASSERT(runs[1] == 0); + } + a = (U8)(255 - a); + if (a) + { + aa[0] = ApplyGamma(gamma, a); + blitter->blitAntiH(x, lower_y - 1, aa, runs); + // the clipping blitters might edit runs, but should not affect us + SkASSERT(runs[0] == 1); + SkASSERT(runs[1] == 0); + } + fy += dy; + } while (++x < stopx); +} + +static void vline(int y, int stopy, SkFixed fx, SkFixed /*slope*/, SkBlitter* blitter) +{ + SkASSERT(y < stopy); + fx += SK_Fixed1/2; + + int x = fx >> 16; + int a = (U8)(fx >> 8); + + if (a) + blitter->blitV(x, y, stopy - y, ApplyGamma(gGammaTable, a)); + a = 255 - a; + if (a) + blitter->blitV(x - 1, y, stopy - y, ApplyGamma(gGammaTable, a)); +} + +static void vertish(int y, int stopy, SkFixed fx, SkFixed dx, SkBlitter* blitter) +{ + SkASSERT(y < stopy); +#ifdef TEST_GAMMA + const U8* gamma = gGammaTable; +#endif + S16 runs[3]; + U8 aa[2]; + + runs[0] = 1; + runs[2] = 0; + + fx += SK_Fixed1/2; + do { + int x = fx >> 16; + U8 a = (U8)(fx >> 8); + + aa[0] = ApplyGamma(gamma, 255 - a); + aa[1] = ApplyGamma(gamma, a); + // the clippng blitters might overwrite this guy, so we have to reset it each time + runs[1] = 1; + blitter->blitAntiH(x - 1, y, aa, runs); + // the clipping blitters might edit runs, but should not affect us + SkASSERT(runs[0] == 1); + SkASSERT(runs[2] == 0); + fx += dx; + } while (++y < stopy); +} + +typedef void (*LineProc)(int istart, int istop, SkFixed fstart, SkFixed slope, SkBlitter*); + +static inline SkFixed fastfixdiv(SkFDot6 a, SkFDot6 b) +{ + SkASSERT((a << 16 >> 16) == a); + SkASSERT(b != 0); + return (a << 16) / b; +} +static inline SkFDot6 fastfixmul(SkFixed fixed, SkFDot6 b) +{ + SkASSERT(SkAbs32(fixed) <= SK_Fixed1 && SkAbs32(b) <= SkIntToFDot6(511)); + return (fixed * b + 0x8000) >> 16; +} + +static void do_anti_hairline(SkFDot6 x0, SkFDot6 y0, SkFDot6 x1, SkFDot6 y1, + const SkRect16* clip, SkBlitter* blitter) +{ + // check that we're no larger than 511 pixels (so we can do a faster div). + // if we are, subdivide and call again + + if (SkAbs32(x1 - x0) > SkIntToFDot6(511) || SkAbs32(y1 - y0) > SkIntToFDot6(511)) + { + int hx = (x0 + x1) >> 1; + int hy = (y0 + y1) >> 1; + do_anti_hairline(x0, y0, hx, hy, clip, blitter); + do_anti_hairline(hx, hy, x1, y1, clip, blitter); + return; + } + + int istart, istop; + SkFixed fstart, slope; + LineProc proc; + + if (SkAbs32(x1 - x0) > SkAbs32(y1 - y0)) // mostly horizontal + { + if (x0 > x1) // we want to go left-to-right + { + SkTSwap<SkFDot6>(x0, x1); + SkTSwap<SkFDot6>(y0, y1); + } + istart = SkFDot6Round(x0); + istop = SkFDot6Round(x1); + if (istart == istop) // too short to draw + return; + + if (y0 == y1) // completely horizontal, take fast case + { + slope = 0; + fstart = SkFDot6ToFixed(y0); + proc = hline; + } + else + { + slope = fastfixdiv(y1 - y0, x1 - x0); + SkASSERT(slope >= -SK_Fixed1 && slope <= SK_Fixed1); + fstart = SkFDot6ToFixed(y0 + fastfixmul(slope, (32 - x0) & 63)); + proc = horish; + } + + if (clip) + { + if (istart >= clip->fRight || istop <= clip->fLeft) + return; + if (istart < clip->fLeft) + { + fstart += slope * (clip->fLeft - istart); + istart = clip->fLeft; + } + if (istop > clip->fRight) + istop = clip->fRight; + SkASSERT(istart <= istop); + if (istart == istop) + return; + // now test if our Y values are completely inside the clip + int top, bottom; + if (slope >= 0) // T2B + { + top = SkFixedFloor(fstart - SK_FixedHalf); + bottom = SkFixedCeil(fstart + (istop - istart - 1) * slope + SK_FixedHalf); + } + else // B2T + { + bottom = SkFixedCeil(fstart + SK_FixedHalf); + top = SkFixedFloor(fstart + (istop - istart - 1) * slope - SK_FixedHalf); + } + if (top >= clip->fBottom || bottom <= clip->fTop) + return; + if (clip->fTop <= top && clip->fBottom >= bottom) + clip = nil; + } + } + else // mostly vertical + { + if (y0 > y1) // we want to go top-to-bottom + { + SkTSwap<SkFDot6>(x0, x1); + SkTSwap<SkFDot6>(y0, y1); + } + istart = SkFDot6Round(y0); + istop = SkFDot6Round(y1); + if (istart == istop) // too short to draw + return; + + if (x0 == x1) + { + slope = 0; + fstart = SkFDot6ToFixed(x0); + proc = vline; + } + else + { + slope = fastfixdiv(x1 - x0, y1 - y0); + SkASSERT(slope <= SK_Fixed1 && slope >= -SK_Fixed1); + fstart = SkFDot6ToFixed(x0 + fastfixmul(slope, (32 - y0) & 63)); + proc = vertish; + } + + if (clip) + { + if (istart >= clip->fBottom || istop <= clip->fTop) + return; + if (istart < clip->fTop) + { + fstart += slope * (clip->fTop - istart); + istart = clip->fTop; + } + if (istop > clip->fBottom) + istop = clip->fBottom; + SkASSERT(istart <= istop); + if (istart == istop) + return; + // now test if our X values are completely inside the clip + int left, right; + if (slope >= 0) // L2R + { + left = SkFixedFloor(fstart - SK_FixedHalf); + right = SkFixedCeil(fstart + (istop - istart - 1) * slope + SK_FixedHalf); + } + else // R2L + { + right = SkFixedCeil(fstart + SK_FixedHalf); + left = SkFixedFloor(fstart + (istop - istart - 1) * slope - SK_FixedHalf); + } + if (left >= clip->fRight || right <= clip->fLeft) + return; + if (clip->fLeft <= left && clip->fRight >= right) + clip = nil; + } + } + + SkRectClipBlitter rectClipper; + if (clip) + { + rectClipper.init(blitter, *clip); + blitter = &rectClipper; + } + proc(istart, istop, fstart, slope, blitter); +} + +void SkScan::AntiHairLine(const SkPoint& pt0, const SkPoint& pt1, + const SkRegion* clip, SkBlitter* blitter) +{ + if (clip && clip->isEmpty()) + return; + + SkASSERT(clip == nil || !clip->getBounds().isEmpty()); + +#ifdef TEST_GAMMA + build_gamma_table(); +#endif + + SkFDot6 x0 = SkScalarToFDot6(pt0.fX); + SkFDot6 y0 = SkScalarToFDot6(pt0.fY); + SkFDot6 x1 = SkScalarToFDot6(pt1.fX); + SkFDot6 y1 = SkScalarToFDot6(pt1.fY); + + if (clip) + { + SkFDot6 left = SkMin32(x0, x1); + SkFDot6 top = SkMin32(y0, y1); + SkFDot6 right = SkMax32(x0, x1); + SkFDot6 bottom = SkMax32(y0, y1); + SkRect16 ir; + + ir.set( SkFDot6Round(left) - 1, + SkFDot6Round(top) - 1, + SkFDot6Round(right) + 1, + SkFDot6Round(bottom) + 1); + + if (clip->quickReject(ir)) + return; + if (!clip->quickContains(ir)) + { + SkRegion::Cliperator iter(*clip, ir); + const SkRect16* r = &iter.rect(); + + while (!iter.done()) + { + do_anti_hairline(x0, y0, x1, y1, r, blitter); + iter.next(); + } + return; + } + // fall through to no-clip case + } + do_anti_hairline(x0, y0, x1, y1, nil, blitter); +} + +void SkScan::AntiHairRect(const SkRect& rect, const SkRegion* clip, SkBlitter* blitter) +{ + if (clip) + { + SkRect16 ir; + SkRect r = rect; + + r.inset(-SK_Scalar1/2, -SK_Scalar1/2); + r.roundOut(&ir); + if (clip->quickReject(ir)) + return; + if (clip->quickContains(ir)) + clip = nil; + } + + SkPoint p0, p1; + + p0.set(rect.fLeft, rect.fTop); + p1.set(rect.fRight, rect.fTop); + SkScan::AntiHairLine(p0, p1, clip, blitter); + p0.set(rect.fRight, rect.fBottom); + SkScan::AntiHairLine(p0, p1, clip, blitter); + p1.set(rect.fLeft, rect.fBottom); + SkScan::AntiHairLine(p0, p1, clip, blitter); + p0.set(rect.fLeft, rect.fTop); + SkScan::AntiHairLine(p0, p1, clip, blitter); +} + + diff --git a/libs/graphics/sgl/SkScan_Hairline.cpp b/libs/graphics/sgl/SkScan_Hairline.cpp new file mode 100644 index 0000000000..2af9e12163 --- /dev/null +++ b/libs/graphics/sgl/SkScan_Hairline.cpp @@ -0,0 +1,254 @@ +#include "SkScan.h" +#include "SkBlitter.h" +#include "SkRegion.h" +#include "SkFDot6.h" + +static void horiline(int x, int stopx, SkFixed fy, SkFixed dy, SkBlitter* blitter) +{ + SkASSERT(x < stopx); + + do { + blitter->blitH(x, fy >> 16, 1); + fy += dy; + } while (++x < stopx); +} + +static void vertline(int y, int stopy, SkFixed fx, SkFixed dx, SkBlitter* blitter) +{ + SkASSERT(y < stopy); + + do { + blitter->blitH(fx >> 16, y, 1); + fx += dx; + } while (++y < stopy); +} + +void SkScan::HairLine(const SkPoint& pt0, const SkPoint& pt1, const SkRegion* clip, SkBlitter* blitter) +{ + SkBlitterClipper clipper; + + SkFDot6 x0 = SkScalarToFDot6(pt0.fX); + SkFDot6 y0 = SkScalarToFDot6(pt0.fY); + SkFDot6 x1 = SkScalarToFDot6(pt1.fX); + SkFDot6 y1 = SkScalarToFDot6(pt1.fY); + + if (clip) + { + SkRect r; + SkRect16 ir; + SkPoint pts[2]; + + pts[0] = pt0; + pts[1] = pt1; + r.set(pts, 2); + r.roundOut(&ir); + + if (clip->quickReject(ir)) + return; + if (clip->quickContains(ir)) + clip = nil; + else + { + blitter = clipper.apply(blitter, clip); + } + } + + SkFDot6 dx = x1 - x0; + SkFDot6 dy = y1 - y0; + + if (SkAbs32(dx) > SkAbs32(dy)) // mostly horizontal + { + if (x0 > x1) // we want to go left-to-right + { + SkTSwap<SkFDot6>(x0, x1); + SkTSwap<SkFDot6>(y0, y1); + } + int ix0 = SkFDot6Round(x0); + int ix1 = SkFDot6Round(x1); + if (ix0 == ix1) // too short to draw + return; + + SkFixed slope = SkFixedDiv(dy, dx); + SkFixed startY = SkFDot6ToFixed(y0 + SkFixedMul(slope, (32 - x0) & 63)); + + horiline(ix0, ix1, startY, slope, blitter); + } + else // mostly vertical + { + if (y0 > y1) // we want to go top-to-bottom + { + SkTSwap<SkFDot6>(x0, x1); + SkTSwap<SkFDot6>(y0, y1); + } + int iy0 = SkFDot6Round(y0); + int iy1 = SkFDot6Round(y1); + if (iy0 == iy1) // too short to draw + return; + + SkFixed slope = SkFixedDiv(dx, dy); + SkFixed startX = SkFDot6ToFixed(x0 + SkFixedMul(slope, (32 - y0) & 63)); + + vertline(iy0, iy1, startX, slope, blitter); + } +} + +void SkScan::HairRect(const SkRect& rect, const SkRegion* clip, SkBlitter* blitter) +{ + SkPoint p0, p1; + + p0.set(rect.fLeft, rect.fTop); + p1.set(rect.fRight, rect.fTop); + SkScan::HairLine(p0, p1, clip, blitter); + p0.set(rect.fRight, rect.fBottom); + SkScan::HairLine(p0, p1, clip, blitter); + p1.set(rect.fLeft, rect.fBottom); + SkScan::HairLine(p0, p1, clip, blitter); + p0.set(rect.fLeft, rect.fTop); + SkScan::HairLine(p0, p1, clip, blitter); +} + +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "SkPath.h" +#include "SkGeometry.h" + +static bool quad_too_curvy(const SkPoint pts[3]) +{ + return true; +} + +static void hairquad(const SkPoint pts[3], const SkRegion* clip, SkBlitter* blitter, int level, + void (*lineproc)(const SkPoint&, const SkPoint&, const SkRegion* clip, SkBlitter*)) +{ +#if 1 + if (level > 0 && quad_too_curvy(pts)) + { + SkPoint tmp[5]; + + SkChopQuadAtHalf(pts, tmp); + hairquad(tmp, clip, blitter, level - 1, lineproc); + hairquad(&tmp[2], clip, blitter, level - 1, lineproc); + } + else + lineproc(pts[0], pts[2], clip, blitter); +#else + lineproc(pts[0], pts[1], clip, blitter); + lineproc(pts[1], pts[2], clip, blitter); +#endif +} + +static bool cubic_too_curvy(const SkPoint pts[4]) +{ + return true; +} + +static void haircubic(const SkPoint pts[4], const SkRegion* clip, SkBlitter* blitter, int level, + void (*lineproc)(const SkPoint&, const SkPoint&, const SkRegion*, SkBlitter*)) +{ + if (level > 0 && cubic_too_curvy(pts)) + { + SkPoint tmp[7]; + + SkChopCubicAt(pts, tmp, SK_Scalar1/2); + haircubic(tmp, clip, blitter, level - 1, lineproc); + haircubic(&tmp[3], clip, blitter, level - 1, lineproc); + } + else + lineproc(pts[0], pts[3], clip, blitter); +} + +#define kMaxCubicSubdivideLevel 6 +#define kMaxQuadSubdivideLevel 5 + +static void hair_path(const SkPath& path, const SkRegion* clip, SkBlitter* blitter, + void (*lineproc)(const SkPoint&, const SkPoint&, const SkRegion*, SkBlitter*)) +{ + if (path.isEmpty()) + return; + + const SkRect16* clipR = nil; + + if (clip) + { + SkRect bounds; + SkRect16 ibounds; + + path.computeBounds(&bounds, SkPath::kFast_BoundsType); + bounds.roundOut(&ibounds); + ibounds.inset(-1, -1); + + if (clip->quickReject(ibounds)) + return; + + if (clip->quickContains(ibounds)) + clip = nil; + else + clipR = &clip->getBounds(); + } + + SkPath::Iter iter(path, false); + SkPoint pts[4]; + SkPath::Verb verb; + + while ((verb = iter.next(pts)) != SkPath::kDone_Verb) + { + switch (verb) { + case SkPath::kLine_Verb: + lineproc(pts[0], pts[1], clip, blitter); + break; + case SkPath::kQuad_Verb: + hairquad(pts, clip, blitter, kMaxQuadSubdivideLevel, lineproc); + break; + case SkPath::kCubic_Verb: + haircubic(pts, clip, blitter, kMaxCubicSubdivideLevel, lineproc); + break; + default: + break; + } + } +} + +void SkScan::HairPath(const SkPath& path, const SkRegion* clip, SkBlitter* blitter) +{ + hair_path(path, clip, blitter, SkScan::HairLine); +} + +void SkScan::AntiHairPath(const SkPath& path, const SkRegion* clip, SkBlitter* blitter) +{ + hair_path(path, clip, blitter, SkScan::AntiHairLine); +} + +//////////////////////////////////////////////////////////////////////////////// + +void SkScan::FrameRect(const SkRect& r, SkScalar diameter, const SkRegion* clip, SkBlitter* blitter) +{ + SkASSERT(diameter > 0); + + if (r.isEmpty()) + return; + + SkScalar radius = diameter / 2; + SkRect outer, tmp; + + outer.set( r.fLeft - radius, r.fTop - radius, + r.fRight + radius, r.fBottom + radius); + + if (r.width() <= diameter || r.height() <= diameter) + { + SkScan::FillRect(outer, clip, blitter); + return; + } + + tmp.set(outer.fLeft, outer.fTop, outer.fRight, outer.fTop + diameter); + SkScan::FillRect(tmp, clip, blitter); + tmp.fTop = outer.fBottom - diameter; + tmp.fBottom = outer.fBottom; + SkScan::FillRect(tmp, clip, blitter); + + tmp.set(outer.fLeft, outer.fTop + diameter, outer.fLeft + diameter, outer.fBottom - diameter); + SkScan::FillRect(tmp, clip, blitter); + tmp.fLeft = outer.fRight - diameter; + tmp.fRight = outer.fRight; + SkScan::FillRect(tmp, clip, blitter); +} + diff --git a/libs/graphics/sgl/SkScan_Path.cpp b/libs/graphics/sgl/SkScan_Path.cpp new file mode 100644 index 0000000000..2518bb5e87 --- /dev/null +++ b/libs/graphics/sgl/SkScan_Path.cpp @@ -0,0 +1,430 @@ +#include "SkScanPriv.h" +#include "SkBlitter.h" +#include "SkEdge.h" +#include "SkGeometry.h" +#include "SkPath.h" +#include "SkRegion.h" +#include "SkTemplates.h" + +#define kEDGE_HEAD_Y SK_MinS16 +#define kEDGE_TAIL_Y SK_MaxS16 + +#ifdef SK_DEBUG + static void validate_sort(const SkEdge* edge) + { + int y = kEDGE_HEAD_Y; + + while (edge->fFirstY != SK_MaxS16) + { + edge->validate(); + SkASSERT(y <= edge->fFirstY); + + y = edge->fFirstY; + edge = edge->fNext; + } + } +#else + #define validate_sort(edge) +#endif + +static inline void remove_edge(SkEdge* edge) +{ + edge->fPrev->fNext = edge->fNext; + edge->fNext->fPrev = edge->fPrev; +} + +static inline void swap_edges(SkEdge* prev, SkEdge* next) +{ + SkASSERT(prev->fNext == next && next->fPrev == prev); + + // remove prev from the list + prev->fPrev->fNext = next; + next->fPrev = prev->fPrev; + + // insert prev after next + prev->fNext = next->fNext; + next->fNext->fPrev = prev; + next->fNext = prev; + prev->fPrev = next; +} + +static void backward_insert_edge_based_on_x(SkEdge* edge SkDECLAREPARAM(int, curr_y)) +{ + SkFixed x = edge->fX; + + for (;;) + { + SkEdge* prev = edge->fPrev; + + // add 1 to curr_y since we may have added new edges (built from curves) + // that start on the next scanline + SkASSERT(prev && prev->fFirstY <= curr_y + 1); + + if (prev->fX <= x) + break; + + swap_edges(prev, edge); + } +} + +static void insert_new_edges(SkEdge* newEdge, int curr_y) +{ + SkASSERT(newEdge->fFirstY >= curr_y); + + while (newEdge->fFirstY == curr_y) + { + SkEdge* next = newEdge->fNext; + backward_insert_edge_based_on_x(newEdge SkPARAM(curr_y)); + newEdge = next; + } +} + +#ifdef SK_DEBUG +static void validate_edges_for_y(const SkEdge* edge, int curr_y) +{ + while (edge->fFirstY <= curr_y) + { + SkASSERT(edge->fPrev && edge->fNext); + SkASSERT(edge->fPrev->fNext == edge); + SkASSERT(edge->fNext->fPrev == edge); + SkASSERT(edge->fFirstY <= edge->fLastY); + + SkASSERT(edge->fPrev->fX <= edge->fX); + edge = edge->fNext; + } +} +#else + #define validate_edges_for_y(edge, curr_y) +#endif + +#if defined _WIN32 && _MSC_VER >= 1300 // disable warning : local variable used without having been initialized +#pragma warning ( push ) +#pragma warning ( disable : 4701 ) +#endif + +static void walk_edges(SkEdge* prevHead, SkPath::FillType fillType, SkBlitter* blitter, + int stop_y) +{ + validate_sort(prevHead->fNext); + + int curr_y = prevHead->fNext->fFirstY; + int windingMask = (fillType == SkPath::kWinding_FillType) ? -1 : 1; + + for (;;) + { + int w = 0; + int left SK_INIT_TO_AVOID_WARNING; + bool in_interval = false; + SkEdge* currE = prevHead->fNext; + SkFixed prevX = prevHead->fX; + + validate_edges_for_y(currE, curr_y); + + while (currE->fFirstY <= curr_y) + { + SkASSERT(currE->fLastY >= curr_y); + + int x = (currE->fX + SK_Fixed1/2) >> 16; + w += currE->fWinding; + if ((w & windingMask) == 0) // we finished an interval + { + SkASSERT(in_interval); + int width = x - left; + SkASSERT(width >= 0); + if (width) + blitter->blitH(left, curr_y, width); + in_interval = false; + } + else if (!in_interval) + { + left = x; + in_interval = true; + } + + SkEdge* next = currE->fNext; + SkFixed newX; + + if (currE->fLastY == curr_y) // are we done with this edge? + { + if (currE->fCurveCount < 0) + { + if (((SkCubicEdge*)currE)->updateCubic()) + { + SkASSERT(currE->fFirstY == curr_y + 1); + + newX = currE->fX; + goto NEXT_X; + } + } + else if (currE->fCurveCount > 0) + { + if (((SkQuadraticEdge*)currE)->updateQuadratic()) + { + newX = currE->fX; + goto NEXT_X; + } + } + remove_edge(currE); + } + else + { + SkASSERT(currE->fLastY > curr_y); + newX = currE->fX + currE->fDX; + currE->fX = newX; + NEXT_X: + if (newX < prevX) // ripple currE backwards until it is x-sorted + backward_insert_edge_based_on_x(currE SkPARAM(curr_y)); + else + prevX = newX; + } + currE = next; + SkASSERT(currE); + } + + curr_y += 1; + if (curr_y >= stop_y) + break; + + // now currE points to the first edge with a Yint larger than curr_y + insert_new_edges(currE, curr_y); + } +} + +#if defined _WIN32 && _MSC_VER >= 1300 +#pragma warning ( pop ) +#endif + +/* Our line edge relies on the maximum span being <= 512, so that it can + use FDot6 and keep the dx,dy in 16bits (for much faster slope divide). + This function returns true if the specified line is too big. +*/ +static inline bool line_too_big(const SkPoint pts[2]) +{ + SkScalar dx = pts[1].fX - pts[0].fX; + SkScalar dy = pts[1].fY - pts[0].fY; + + return SkScalarAbs(dx) > SkIntToScalar(511) || + SkScalarAbs(dy) > SkIntToScalar(511); +} + +static int build_edges(SkEdge edge[], const SkPath& path, const SkRect16* clipRect, SkEdge* list[], int shiftUp) +{ + SkEdge** start = list; + SkPath::Iter iter(path, true); + SkPoint pts[4]; + SkPath::Verb verb; + + while ((verb = iter.next(pts)) != SkPath::kDone_Verb) + { + switch (verb) { + case SkPath::kLine_Verb: + if (edge->setLine(pts, clipRect, shiftUp)) + { + *list++ = edge; + edge = (SkEdge*)((char*)edge + sizeof(SkEdge)); + } + break; + case SkPath::kQuad_Verb: + { + SkPoint tmp[5]; + SkPoint* p = tmp; + int count = SkChopQuadAtYExtrema(pts, tmp); + + do { + if (((SkQuadraticEdge*)edge)->setQuadratic(p, clipRect, shiftUp)) + { + *list++ = edge; + edge = (SkEdge*)((char*)edge + sizeof(SkQuadraticEdge)); + } + p += 2; + } while (--count >= 0); + } + break; + case SkPath::kCubic_Verb: + { + SkPoint tmp[10]; + SkPoint* p = tmp; + int count = SkChopCubicAtYExtrema(pts, tmp); + SkASSERT(count >= 0 && count <= 2); + + do { + if (((SkCubicEdge*)edge)->setCubic(p, clipRect, shiftUp)) + { + *list++ = edge; + edge = (SkEdge*)((char*)edge + sizeof(SkCubicEdge)); + } + p += 3; + } while (--count >= 0); + } + break; + default: + break; + } + } + return (int)(list - start); +} + +extern "C" { + static int edge_compare(const void* a, const void* b) + { + const SkEdge* edgea = *(const SkEdge**)a; + const SkEdge* edgeb = *(const SkEdge**)b; + + int valuea = edgea->fFirstY; + int valueb = edgeb->fFirstY; + + if (valuea == valueb) + { + valuea = edgea->fX; + valueb = edgeb->fX; + } + return valuea - valueb; + } +} + +static SkEdge* sort_edges(SkEdge* list[], int count, SkEdge** last) +{ + qsort(list, count, sizeof(SkEdge*), edge_compare); + + // now make the edges linked in sorted order + for (int i = 1; i < count; i++) + { + list[i - 1]->fNext = list[i]; + list[i]->fPrev = list[i - 1]; + } + + *last = list[count - 1]; + return list[0]; +} + +static int worst_case_edge_count(const SkPath& path, size_t* storage) +{ + size_t size = 0; + int edgeCount = 0; + + SkPath::Iter iter(path, true); + SkPath::Verb verb; + + while ((verb = iter.next(nil)) != SkPath::kDone_Verb) + { + switch (verb) { + case SkPath::kLine_Verb: + edgeCount += 1; + size += sizeof(SkQuadraticEdge); // treat line like Quad (in case its > 512) + break; + case SkPath::kQuad_Verb: + edgeCount += 2; // might need 2 edges when we chop on Y extrema + size += 2 * sizeof(SkQuadraticEdge); + break; + case SkPath::kCubic_Verb: + edgeCount += 3; // might need 3 edges when we chop on Y extrema + size += 3 * sizeof(SkCubicEdge); + break; + default: + break; + } + } + + SkASSERT(storage); + *storage = size; + return edgeCount; +} + +void sk_fill_path(const SkPath& path, const SkRect16* clipRect, SkBlitter* blitter, + const SkRect16& ir, int shiftEdgesUp) +{ + SkASSERT(&path && blitter); + + size_t size; + int maxCount = worst_case_edge_count(path, &size); + + SkAutoMalloc memory(maxCount * sizeof(SkEdge*) + size); + SkEdge** list = (SkEdge**)memory.get(); + SkEdge* edge = (SkEdge*)(list + maxCount); + int count = build_edges(edge, path, clipRect, list, shiftEdgesUp); + SkEdge headEdge, tailEdge, *last; + + SkASSERT(count <= maxCount); + if (count == 0) + return; + SkASSERT(count > 1); + + // this returns the first and last edge after they're sorted into a dlink list + edge = sort_edges(list, count, &last); + + headEdge.fPrev = nil; + headEdge.fNext = edge; + headEdge.fFirstY = kEDGE_HEAD_Y; + headEdge.fX = SK_MinS32; + edge->fPrev = &headEdge; + + tailEdge.fPrev = last; + tailEdge.fNext = nil; + tailEdge.fFirstY = kEDGE_TAIL_Y; + last->fNext = &tailEdge; + + // now edge is the head of the sorted linklist + int stop_y = ir.fBottom; + if (clipRect && stop_y > clipRect->fBottom) + stop_y = clipRect->fBottom; + walk_edges(&headEdge, path.getFillType(), blitter, stop_y); +} + +///////////////////////////////////////////////////////////////////////////////////// + +SkScanClipper::SkScanClipper(SkBlitter* blitter, const SkRegion* clip, const SkRect16& ir) +{ + fBlitter = nil; // nil means blit nothing + fClipRect = nil; + + if (clip) + { + fClipRect = &clip->getBounds(); + if (!SkRect16::Intersects(*fClipRect, ir)) // completely clipped out + return; + + if (clip->isRect()) + { + if (fClipRect->contains(ir)) + fClipRect = nil; + else + { + // only need a wrapper blitter if we're horizontally clipped + if (fClipRect->fLeft > ir.fLeft || fClipRect->fRight < ir.fRight) + { + fRectBlitter.init(blitter, *fClipRect); + blitter = &fRectBlitter; + } + } + } + else + { + fRgnBlitter.init(blitter, clip); + blitter = &fRgnBlitter; + } + } + fBlitter = blitter; +} + +///////////////////////////////////////////////////////////////////////////////////// + +void SkScan::FillPath(const SkPath& path, const SkRegion* clip, SkBlitter* blitter) +{ + if (clip && clip->isEmpty()) + return; + + SkRect r; + SkRect16 ir; + + path.computeBounds(&r, SkPath::kFast_BoundsType); + r.round(&ir); + if (ir.isEmpty()) + return; + + SkScanClipper clipper(blitter, clip, ir); + + blitter = clipper.getBlitter(); + if (blitter) + sk_fill_path(path, clipper.getClipRect(), blitter, ir, 0); +} + diff --git a/libs/graphics/sgl/SkShader.cpp b/libs/graphics/sgl/SkShader.cpp new file mode 100644 index 0000000000..6d1ac3efac --- /dev/null +++ b/libs/graphics/sgl/SkShader.cpp @@ -0,0 +1,370 @@ +#include "SkShader.h" +#include "SkPaint.h" + +SkShader::SkShader() : fLocalMatrix(nil) +{ +} + +SkShader::~SkShader() +{ + sk_free(fLocalMatrix); +} + +void SkShader::setLocalMatrix(const SkMatrix& matrix) +{ + if (matrix.isIdentity()) + { + if (fLocalMatrix) + { + sk_free(fLocalMatrix); + fLocalMatrix = nil; + } + } + else + { + if (fLocalMatrix == nil) + fLocalMatrix = (SkMatrix*)sk_malloc_throw(sizeof(SkMatrix)); + *fLocalMatrix = matrix; + } +} + +bool SkShader::setContext(const SkBitmap& device, + const SkPaint& paint, + const SkMatrix& matrix) +{ + const SkMatrix* m = &matrix; + SkMatrix total; + + fDeviceConfig = SkToU8(device.getConfig()); + fPaintAlpha = paint.getAlpha(); + if (fLocalMatrix) + { + total.setConcat(matrix, *fLocalMatrix); + m = &total; + } + if (m->invert(&fTotalInverse)) + { + fInverseMapPtProc = fTotalInverse.getMapPtProc(); + fTotalInverseClass = (U8)SkShader::ComputeMatrixClass(fTotalInverse); + return true; + } + return false; +} + +U32 SkShader::getFlags() +{ + return 0; +} + +#include "SkColorPriv.h" + +void SkShader::shadeSpanOpaque16(int x, int y, U16 span16[], int count) +{ + SkASSERT(span16); + SkASSERT(count > 0); + SkASSERT(this->canCallShadeSpanOpaque16()); + + // basically, if we get here, the subclass screwed up + SkASSERT(!"kHasSpan16 flag is set, but shadeSpanOpaque16() not implemented"); +} + +#define kTempColorQuadCount 6 // balance between speed (larger) and saving stack-space +#define kTempColorCount (kTempColorQuadCount << 2) + +#ifdef SK_CPU_BENDIAN + #define SkU32BitShiftToByteOffset(shift) (3 - ((shift) >> 3)) +#else + #define SkU32BitShiftToByteOffset(shift) ((shift) >> 3) +#endif + +void SkShader::shadeSpanAlpha(int x, int y, U8 alpha[], int count) +{ + SkASSERT(count > 0); + + SkPMColor colors[kTempColorCount]; + + while ((count -= kTempColorCount) >= 0) + { + this->shadeSpan(x, y, colors, kTempColorCount); + x += kTempColorCount; + + const U8* srcA = (const U8*)colors + SkU32BitShiftToByteOffset(SK_A32_SHIFT); + int quads = kTempColorQuadCount; + do { + U8CPU a0 = srcA[0]; + U8CPU a1 = srcA[4]; + U8CPU a2 = srcA[8]; + U8CPU a3 = srcA[12]; + srcA += 4*4; + *alpha++ = SkToU8(a0); + *alpha++ = SkToU8(a1); + *alpha++ = SkToU8(a2); + *alpha++ = SkToU8(a3); + } while (--quads != 0); + } + SkASSERT(count < 0); + SkASSERT(count + kTempColorCount >= 0); + if (count += kTempColorCount) + { + this->shadeSpan(x, y, colors, count); + + const U8* srcA = (const U8*)colors + SkU32BitShiftToByteOffset(SK_A32_SHIFT); + do { + *alpha++ = *srcA; + srcA += 4; + } while (--count != 0); + } +#if 0 + do { + int n = count; + if (n > kTempColorCount) + n = kTempColorCount; + SkASSERT(n > 0); + + this->shadeSpan(x, y, colors, n); + x += n; + count -= n; + + const U8* srcA = (const U8*)colors + SkU32BitShiftToByteOffset(SK_A32_SHIFT); + do { + *alpha++ = *srcA; + srcA += 4; + } while (--n != 0); + } while (count > 0); +#endif +} + +SkShader::MatrixClass SkShader::ComputeMatrixClass(const SkMatrix& mat) +{ + MatrixClass mc = kLinear_MatrixClass; + + if (mat.getType() & SkMatrix::kPerspective_Mask) + { + if (mat.fixedStepInX(0, nil, nil)) + mc = kFixedStepInX_MatrixClass; + else + mc = kPerspective_MatrixClass; + } + return mc; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +#if 0 +SkPairShader::SkPairShader(SkShader* s0, SkShader* s1) + : fShader0(s0), fShader1(s1) +{ + s0->safeRef(); + s1->safeRef(); +} + +SkPairShader::~SkPairShader() +{ + fShader1->safeUnref(); + fShader0->safeUnref(); +} + +U32 SkPairShader::getFlags() +{ + SkASSERT(fShader0 || fShader1); + + U32 flags = 0-1U; + + if (fShader0) + flags &= fShader0->getFlags(); + if (fShader1) + flags &= fShader1->getFlags(); + return flags; +} + +bool SkPairShader::setContext( const SkBitmap& device, + const SkPaint& paint, + const SkMatrix& matrix) +{ + if (fShader0 == nil && fShader1 == nil) + return false; + + const SkMatrix* localM = this->getLocalMatrix(); + SkMatrix tmp; + + if (localM) + { + tmp.setConcat(matrix, *localM); + localM = &tmp; + } + else + localM = &matrix; + + // wonder if some subclasses will want OR instead of AND? + + if (fShader0 && !fShader0->setContext(device, paint, *localM)) + return false; + if (fShader1 && !fShader1->setContext(device, paint, *localM)) + return false; + return true; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +void SkComposeShader::shadeSpan(int x, int y, SkPMColor span[], int count) +{ + SkShader* s0 = this->getShader0(); + SkShader* s1 = this->getShader1(); + + if (s1) + s1->shadeSpan(x, y, span, count); + if (s0) + s0->shadeSpan(x, y, span, count); +} + +void SkComposeShader::shadeSpanOpaque16(int x, int y, U16 span[], int count) +{ + SkShader* s0 = this->getShader0(); + SkShader* s1 = this->getShader1(); + + if (s1) + s1->shadeSpanOpaque16(x, y, span, count); + if (s0) + s0->shadeSpanOpaque16(x, y, span, count); +} + +void SkComposeShader::shadeSpanAlpha(int x, int y, U8 span[], int count) +{ + SkShader* s0 = this->getShader0(); + SkShader* s1 = this->getShader1(); + + if (s1) + s1->shadeSpanAlpha(x, y, span, count); + if (s0) + s0->shadeSpanAlpha(x, y, span, count); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +#include "SkXfermode.h" +#include "SkColorPriv.h" +#include "SkBitmap.h" + +SkSumShader::SkSumShader(SkShader* s0, SkShader* s1, U8CPU weight) + : SkPairShader(s0, s1), fBuffer(nil), fMode(nil), fWeight(SkToU8(weight)) +{ +} + +SkSumShader::SkSumShader(SkShader* s0, SkShader* s1, SkXfermode* mode) + : SkPairShader(s0, s1), fBuffer(nil), fMode(mode), fWeight(0xFF) +{ + mode->safeRef(); +} + +SkSumShader::~SkSumShader() +{ + fMode->safeUnref(); + sk_free(fBuffer); +} + +bool SkSumShader::setContext(const SkBitmap& device, + const SkPaint& paint, + const SkMatrix& matrix) +{ + if (!this->INHERITED::setContext(device, paint, matrix)) + return false; + + if (fBuffer == nil) + fBuffer = (SkPMColor*)sk_malloc_throw(device.width() * sizeof(SkPMColor)); + else + fBuffer = (SkPMColor*)sk_realloc_throw(fBuffer, device.width() * sizeof(SkPMColor)); + return true; +} + +void SkSumShader::shadeSpan(int x, int y, SkPMColor dst[], int count) +{ + SkShader* s0 = this->getShader0(); + SkShader* s1 = this->getShader1(); + SkASSERT(s0 || s1); + + if (s0 == nil) + s1->shadeSpan(x, y, dst, count); + else + { + s0->shadeSpan(x, y, dst, count); + if (s1) + { + SkPMColor* src = fBuffer; + s1->shadeSpan(x, y, src, count); + + if (fMode) + fMode->xfer32(dst, src, count, nil); + else + { + unsigned weight = fWeight; + for (int i = 0; i < count; i++) + dst[i] = SkBlendARGB32(src[i], dst[i], weight); + } + } + } +} + +void SkSumShader::shadeSpanOpaque16(int x, int y, U16 dst[], int count) +{ + SkShader* s0 = this->getShader0(); + SkShader* s1 = this->getShader1(); + SkASSERT(s0 || s1); + + if (s0 == nil) + s1->shadeSpanOpaque16(x, y, dst, count); + else + { + s0->shadeSpanOpaque16(x, y, dst, count); + if (s1) + { + if (fMode) + { + SkPMColor* src = fBuffer; + s1->shadeSpan(x, y, src, count); + fMode->xfer16(dst, src, count, nil); + } + else + { + U16* src = (U16*)fBuffer; + s1->shadeSpanOpaque16(x, y, src, count); + + unsigned scale = SkAlpha255To256(fWeight); + for (int i = 0; i < count; i++) + dst[i] = (U16)SkBlendRGB16(src[i], dst[i], scale); + } + } + } +} + +void SkSumShader::shadeSpanAlpha(int x, int y, U8 dst[], int count) +{ + SkShader* s0 = this->getShader0(); + SkShader* s1 = this->getShader1(); + SkASSERT(s0 || s1); + + if (s0 == nil) + s1->shadeSpanAlpha(x, y, dst, count); + else + { + s0->shadeSpanAlpha(x, y, dst, count); + if (s1) + { + if (fMode) + { + SkPMColor* src = fBuffer; + s1->shadeSpan(x, y, src, count); + fMode->xferA8(dst, src, count, nil); + } + else + { + U8* src = (U8*)fBuffer; + s1->shadeSpanAlpha(x, y, src, count); + + unsigned scale = SkAlpha255To256(fWeight); + for (int i = 0; i < count; i++) + dst[i] = (U8)SkAlphaBlend(src[i], dst[i], scale); + } + } + } +} +#endif diff --git a/libs/graphics/sgl/SkSinTable.h b/libs/graphics/sgl/SkSinTable.h new file mode 100644 index 0000000000..2c4c11d488 --- /dev/null +++ b/libs/graphics/sgl/SkSinTable.h @@ -0,0 +1,268 @@ +#ifndef SkSinTable_DEFINED +#define SkSinTable_DEFINED + +#include "SkTypes.h" + +/* Fixed point values (low 16 bits) of sin(radians) for + radians in [0...PI/2) +*/ +static const U16 gSkSinTable[256] = { + 0x0000, + 0x0192, + 0x0324, + 0x04B6, + 0x0648, + 0x07DA, + 0x096C, + 0x0AFE, + 0x0C8F, + 0x0E21, + 0x0FB2, + 0x1144, + 0x12D5, + 0x1466, + 0x15F6, + 0x1787, + 0x1917, + 0x1AA7, + 0x1C37, + 0x1DC7, + 0x1F56, + 0x20E5, + 0x2273, + 0x2402, + 0x2590, + 0x271D, + 0x28AA, + 0x2A37, + 0x2BC4, + 0x2D50, + 0x2EDB, + 0x3066, + 0x31F1, + 0x337B, + 0x3505, + 0x368E, + 0x3817, + 0x399F, + 0x3B26, + 0x3CAD, + 0x3E33, + 0x3FB9, + 0x413E, + 0x42C3, + 0x4447, + 0x45CA, + 0x474D, + 0x48CE, + 0x4A50, + 0x4BD0, + 0x4D50, + 0x4ECF, + 0x504D, + 0x51CA, + 0x5347, + 0x54C3, + 0x563E, + 0x57B8, + 0x5931, + 0x5AAA, + 0x5C22, + 0x5D98, + 0x5F0E, + 0x6083, + 0x61F7, + 0x636A, + 0x64DC, + 0x664D, + 0x67BD, + 0x692D, + 0x6A9B, + 0x6C08, + 0x6D74, + 0x6EDF, + 0x7049, + 0x71B1, + 0x7319, + 0x7480, + 0x75E5, + 0x774A, + 0x78AD, + 0x7A0F, + 0x7B70, + 0x7CD0, + 0x7E2E, + 0x7F8B, + 0x80E7, + 0x8242, + 0x839C, + 0x84F4, + 0x864B, + 0x87A1, + 0x88F5, + 0x8A48, + 0x8B9A, + 0x8CEA, + 0x8E39, + 0x8F87, + 0x90D3, + 0x921E, + 0x9368, + 0x94B0, + 0x95F6, + 0x973C, + 0x987F, + 0x99C2, + 0x9B02, + 0x9C42, + 0x9D7F, + 0x9EBC, + 0x9FF6, + 0xA12F, + 0xA267, + 0xA39D, + 0xA4D2, + 0xA605, + 0xA736, + 0xA866, + 0xA994, + 0xAAC0, + 0xABEB, + 0xAD14, + 0xAE3B, + 0xAF61, + 0xB085, + 0xB1A8, + 0xB2C8, + 0xB3E7, + 0xB504, + 0xB620, + 0xB73A, + 0xB852, + 0xB968, + 0xBA7C, + 0xBB8F, + 0xBCA0, + 0xBDAE, + 0xBEBC, + 0xBFC7, + 0xC0D0, + 0xC1D8, + 0xC2DE, + 0xC3E2, + 0xC4E3, + 0xC5E4, + 0xC6E2, + 0xC7DE, + 0xC8D8, + 0xC9D1, + 0xCAC7, + 0xCBBB, + 0xCCAE, + 0xCD9F, + 0xCE8D, + 0xCF7A, + 0xD064, + 0xD14D, + 0xD233, + 0xD318, + 0xD3FA, + 0xD4DB, + 0xD5B9, + 0xD695, + 0xD770, + 0xD848, + 0xD91E, + 0xD9F2, + 0xDAC4, + 0xDB94, + 0xDC61, + 0xDD2D, + 0xDDF6, + 0xDEBE, + 0xDF83, + 0xE046, + 0xE106, + 0xE1C5, + 0xE282, + 0xE33C, + 0xE3F4, + 0xE4AA, + 0xE55E, + 0xE60F, + 0xE6BE, + 0xE76B, + 0xE816, + 0xE8BF, + 0xE965, + 0xEA09, + 0xEAAB, + 0xEB4B, + 0xEBE8, + 0xEC83, + 0xED1C, + 0xEDB2, + 0xEE46, + 0xEED8, + 0xEF68, + 0xEFF5, + 0xF080, + 0xF109, + 0xF18F, + 0xF213, + 0xF294, + 0xF314, + 0xF391, + 0xF40B, + 0xF484, + 0xF4FA, + 0xF56D, + 0xF5DE, + 0xF64D, + 0xF6BA, + 0xF724, + 0xF78B, + 0xF7F1, + 0xF853, + 0xF8B4, + 0xF912, + 0xF96E, + 0xF9C7, + 0xFA1E, + 0xFA73, + 0xFAC5, + 0xFB14, + 0xFB61, + 0xFBAC, + 0xFBF5, + 0xFC3B, + 0xFC7E, + 0xFCBF, + 0xFCFE, + 0xFD3A, + 0xFD74, + 0xFDAB, + 0xFDE0, + 0xFE13, + 0xFE43, + 0xFE70, + 0xFE9B, + 0xFEC4, + 0xFEEA, + 0xFF0E, + 0xFF2F, + 0xFF4E, + 0xFF6A, + 0xFF84, + 0xFF9C, + 0xFFB1, + 0xFFC3, + 0xFFD3, + 0xFFE1, + 0xFFEC, + 0xFFF4, + 0xFFFB, + 0xFFFE +}; + +#endif diff --git a/libs/graphics/sgl/SkSpriteBlitter.h b/libs/graphics/sgl/SkSpriteBlitter.h new file mode 100644 index 0000000000..b85690e29f --- /dev/null +++ b/libs/graphics/sgl/SkSpriteBlitter.h @@ -0,0 +1,41 @@ +#ifndef SkSpriteBlitter_DEFINED +#define SkSpriteBlitter_DEFINED + +#include "SkBlitter.h" +#include "SkBitmap.h" + +class SkXfermode; + +class SkSpriteBlitter : public SkBlitter { +public: + SkSpriteBlitter(const SkBitmap& source); + virtual ~SkSpriteBlitter(); + + void setup(const SkBitmap& device, int left, int top) + { + fDevice = &device; + fLeft = left; + fTop = top; + } + + // overrides +#ifdef SK_DEBUG + virtual void blitH(int x, int y, int width); + virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const S16 runs[]); + virtual void blitV(int x, int y, int height, SkAlpha alpha); + virtual void blitMask(const SkMask&, const SkRect16& clip); +#endif + + static SkSpriteBlitter* ChooseD16(const SkBitmap& source, SkXfermode* mode, U8 alpha, + void* storage, size_t storageSize); + static SkSpriteBlitter* ChooseD32(const SkBitmap& source, SkXfermode* mode, U8 alpha, + void* storage, size_t storageSize); + +protected: + const SkBitmap* fDevice; + const SkBitmap* fSource; + int fLeft, fTop; +}; + +#endif + diff --git a/libs/graphics/sgl/SkSpriteBlitterTemplate.h b/libs/graphics/sgl/SkSpriteBlitterTemplate.h new file mode 100644 index 0000000000..23d2fbf872 --- /dev/null +++ b/libs/graphics/sgl/SkSpriteBlitterTemplate.h @@ -0,0 +1,60 @@ + +class SkSPRITE_CLASSNAME : public SkSpriteBlitter { +public: + SkSPRITE_CLASSNAME(const SkBitmap& source SkSPRITE_ARGS) + : SkSpriteBlitter(source) + { + SkSPRITE_INIT + } + virtual void blitRect(int x, int y, int width, int height) + { + SkASSERT(width > 0 && height > 0); + int srcX = x - fLeft; + int srcY = y - fTop; + SkSPRITE_DST_TYPE* dst = fDevice->SkSPRITE_DST_GETADDR(x, y); + const SkSPRITE_SRC_TYPE* src = fSource->SkSPRITE_SRC_GETADDR(srcX, srcY); + unsigned dstRB = fDevice->rowBytes(); + unsigned srcRB = fSource->rowBytes(); + + SkDEBUGCODE((void)fDevice->SkSPRITE_DST_GETADDR(x + width - 1, y + height - 1);) + SkDEBUGCODE((void)fSource->SkSPRITE_SRC_GETADDR(srcX + width - 1, srcY + height - 1);) + + SkSPRITE_PREAMBLE((*fSource), srcX, srcY); + + do { + SkSPRITE_DST_TYPE* d = dst; + const SkSPRITE_SRC_TYPE* s = src; +#ifdef SkSPRITE_BEGIN_ROW + SkSPRITE_BEGIN_ROW +#endif + int w = width; + do { + SkSPRITE_SRC_TYPE sc = *s++; + SkSPRITE_BLIT_PIXEL(d, sc); + d += 1; + } while (--w != 0); + dst = (SkSPRITE_DST_TYPE*)((char*)dst + dstRB); + src = (const SkSPRITE_SRC_TYPE*)((const char*)src + srcRB); + SkSPRITE_NEXT_ROW + } while (--height != 0); + + SkSPRITE_POSTAMBLE((*fSource)); + } +private: + SkSPRITE_FIELDS +}; + +#undef SkSPRITE_BLIT_PIXEL +#undef SkSPRITE_CLASSNAME +#undef SkSPRITE_DST_TYPE +#undef SkSPRITE_SRC_TYPE +#undef SkSPRITE_DST_GETADDR +#undef SkSPRITE_SRC_GETADDR +#undef SkSPRITE_PREAMBLE +#undef SkSPRITE_POSTAMBLE +#undef SkSPRITE_ARGS +#undef SkSPRITE_FIELDS +#undef SkSPRITE_INIT +#undef SkSPRITE_NEXT_ROW +#undef SkSPRITE_BEGIN_ROW + diff --git a/libs/graphics/sgl/SkSpriteBlitter_ARGB32.cpp b/libs/graphics/sgl/SkSpriteBlitter_ARGB32.cpp new file mode 100644 index 0000000000..80ab5a5ce5 --- /dev/null +++ b/libs/graphics/sgl/SkSpriteBlitter_ARGB32.cpp @@ -0,0 +1,81 @@ +#include "SkSpriteBlitter.h" +#include "SkTemplates.h" +#include "SkUtils.h" +#include "SkColorPriv.h" + +#define D32_S32A_Opaque_Pixel(dst, sc) \ +do { \ + if (sc) \ + { \ + unsigned srcA = SkGetPackedA32(sc); \ + U32 result = sc; \ + if (srcA != 0xFF) \ + result += SkAlphaMulQ(*dst, SkAlpha255To256(255 - srcA)); \ + *dst = result; \ + } \ +} while (0) + +#define SkSPRITE_CLASSNAME Sprite_D32_S32A_Opaque +#define SkSPRITE_ARGS +#define SkSPRITE_FIELDS +#define SkSPRITE_INIT +#define SkSPRITE_DST_TYPE uint32_t +#define SkSPRITE_SRC_TYPE uint32_t +#define SkSPRITE_DST_GETADDR getAddr32 +#define SkSPRITE_SRC_GETADDR getAddr32 +#define SkSPRITE_PREAMBLE(srcBM, x, y) +#define SkSPRITE_BLIT_PIXEL(dst, src) D32_S32A_Opaque_Pixel(dst, src) +#define SkSPRITE_NEXT_ROW +#define SkSPRITE_POSTAMBLE(srcBM) +#include "SkSpriteBlitterTemplate.h" + +class Sprite_D32_S32_Opaque : public SkSpriteBlitter { +public: + Sprite_D32_S32_Opaque(const SkBitmap& source) : SkSpriteBlitter(source) {} + + virtual void blitRect(int x, int y, int width, int height) + { + SkASSERT(width > 0 && height > 0); + uint32_t* dst = fDevice->getAddr32(x, y); + const uint32_t* src = fSource->getAddr32(x - fLeft, y - fTop); + unsigned dstRB = fDevice->rowBytes(); + unsigned srcRB = fSource->rowBytes(); + size_t size = width * sizeof(uint32_t); + + do { + memcpy(dst, src, size); + dst = (uint32_t*)((char*)dst + dstRB); + src = (const uint32_t*)((const char*)src + srcRB); + } while (--height != 0); + } +}; + +////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////// + +#include "SkTemplatesPriv.h" + +SkSpriteBlitter* SkSpriteBlitter::ChooseD32(const SkBitmap& source, SkXfermode* mode, U8 alpha, + void* storage, size_t storageSize) +{ + SkSpriteBlitter* blitter = nil; + + switch (source.getConfig()) { + case SkBitmap::kARGB_8888_Config: + if (mode == nil) + { + if (alpha == 255) + { + if (source.isOpaque()) + SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D32_S32_Opaque, storage, storageSize, (source)); + else + SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D32_S32A_Opaque, storage, storageSize, (source)); + } + } + break; + default: + break; + } + return blitter; +} + diff --git a/libs/graphics/sgl/SkSpriteBlitter_RGB16.cpp b/libs/graphics/sgl/SkSpriteBlitter_RGB16.cpp new file mode 100644 index 0000000000..ab65bae777 --- /dev/null +++ b/libs/graphics/sgl/SkSpriteBlitter_RGB16.cpp @@ -0,0 +1,300 @@ +#include "SkSpriteBlitter.h" +#include "SkTemplates.h" +#include "SkUtils.h" +#include "SkColorPriv.h" + +#define D16_S32A_Opaque_Pixel(dst, sc) \ +do { \ + if (sc) \ + { \ + unsigned srcA = SkGetPackedA32(sc); \ + unsigned result = SkPixel32ToPixel16(sc); \ + if (srcA != 0xFF) \ + result += SkAlphaMulRGB16(*dst, SkAlpha255To256(255 - srcA)); \ + *dst = SkToU16(result); \ + } \ +} while (0) + +#define SkSPRITE_CLASSNAME Sprite_D16_S32A_Opaque +#define SkSPRITE_ARGS +#define SkSPRITE_FIELDS +#define SkSPRITE_INIT +#define SkSPRITE_DST_TYPE uint16_t +#define SkSPRITE_SRC_TYPE uint32_t +#define SkSPRITE_DST_GETADDR getAddr16 +#define SkSPRITE_SRC_GETADDR getAddr32 +#define SkSPRITE_PREAMBLE(srcBM, x, y) +#define SkSPRITE_BLIT_PIXEL(dst, src) D16_S32A_Opaque_Pixel(dst, src) +#define SkSPRITE_NEXT_ROW +#define SkSPRITE_POSTAMBLE(srcBM) +#include "SkSpriteBlitterTemplate.h" + +static inline void D16_S32A_Blend_Pixel_helper(U16* dst, U32 sc, unsigned src_scale) +{ + uint16_t dc = *dst; + unsigned sa = SkGetPackedA32(sc); + unsigned dr, dg, db; + + if (sa == 255) + { + dr = SkAlphaBlend(SkPacked32ToR16(sc), SkGetPackedR16(dc), src_scale); + dg = SkAlphaBlend(SkPacked32ToG16(sc), SkGetPackedG16(dc), src_scale); + db = SkAlphaBlend(SkPacked32ToB16(sc), SkGetPackedB16(dc), src_scale); + } + else + { + unsigned dst_scale = 255 - SkAlphaMul(sa, src_scale); + dr = (SkPacked32ToR16(sc) * src_scale + SkGetPackedR16(dc) * dst_scale) >> 8; + dg = (SkPacked32ToG16(sc) * src_scale + SkGetPackedG16(dc) * dst_scale) >> 8; + db = (SkPacked32ToB16(sc) * src_scale + SkGetPackedB16(dc) * dst_scale) >> 8; + } + *dst = SkPackRGB16(dr, dg, db); +} + +#define D16_S32A_Blend_Pixel(dst, sc, src_scale) do { if (sc) D16_S32A_Blend_Pixel_helper(dst, sc, src_scale); } while (0) + + +#define SkSPRITE_CLASSNAME Sprite_D16_S32A_Blend +#define SkSPRITE_ARGS , U8 alpha +#define SkSPRITE_FIELDS U8 fSrcAlpha; +#define SkSPRITE_INIT fSrcAlpha = alpha; +#define SkSPRITE_DST_TYPE uint16_t +#define SkSPRITE_SRC_TYPE uint32_t +#define SkSPRITE_DST_GETADDR getAddr16 +#define SkSPRITE_SRC_GETADDR getAddr32 +#define SkSPRITE_PREAMBLE(srcBM, x, y) unsigned src_scale = SkAlpha255To256(fSrcAlpha); +#define SkSPRITE_BLIT_PIXEL(dst, src) D16_S32A_Blend_Pixel(dst, src, src_scale) +#define SkSPRITE_NEXT_ROW +#define SkSPRITE_POSTAMBLE(srcBM) +#include "SkSpriteBlitterTemplate.h" + +////////////////////////////////////////////////////////////////////////////////////////////////// + +#define SkSPRITE_CLASSNAME Sprite_D16_S32_Opaque +#define SkSPRITE_ARGS +#define SkSPRITE_FIELDS +#define SkSPRITE_INIT +#define SkSPRITE_DST_TYPE uint16_t +#define SkSPRITE_SRC_TYPE uint32_t +#define SkSPRITE_DST_GETADDR getAddr16 +#define SkSPRITE_SRC_GETADDR getAddr32 +#define SkSPRITE_PREAMBLE(srcBM, x, y) +#define SkSPRITE_BLIT_PIXEL(dst, src) *dst = SkPixel32ToPixel16_ToU16(src) +#define SkSPRITE_NEXT_ROW +#define SkSPRITE_POSTAMBLE(srcBM) +#include "SkSpriteBlitterTemplate.h" + +#if 1 +#define D16_S32_Blend_Pixel(dst, sc, scale) \ +do { \ + U16 dc = *dst; \ + *dst = SkPackRGB16( SkAlphaBlend(SkPacked32ToR16(sc), SkGetPackedR16(dc), scale), \ + SkAlphaBlend(SkPacked32ToG16(sc), SkGetPackedG16(dc), scale), \ + SkAlphaBlend(SkPacked32ToB16(sc), SkGetPackedB16(dc), scale)); \ +} while (0) +#else +static inline void D16_S32_Blend_Pixel(uint16_t* dst, uint32_t sc, int scale) +{ + U16 dc = *dst; + *dst = SkPackRGB16( SkAlphaBlend(SkPacked32ToR16(sc), SkGetPackedR16(dc), scale), + SkAlphaBlend(SkPacked32ToG16(sc), SkGetPackedG16(dc), scale), + SkAlphaBlend(SkPacked32ToB16(sc), SkGetPackedB16(dc), scale)); +} +#endif + +#define SkSPRITE_CLASSNAME Sprite_D16_S32_Blend +#define SkSPRITE_ARGS , uint8_t alpha +#define SkSPRITE_FIELDS uint8_t fSrcAlpha; +#define SkSPRITE_INIT fSrcAlpha = alpha; +#define SkSPRITE_DST_TYPE uint16_t +#define SkSPRITE_SRC_TYPE uint32_t +#define SkSPRITE_DST_GETADDR getAddr16 +#define SkSPRITE_SRC_GETADDR getAddr32 +#define SkSPRITE_PREAMBLE(srcBM, x, y) int src_scale = SkAlpha255To256(fSrcAlpha); +#define SkSPRITE_BLIT_PIXEL(dst, src) D16_S32_Blend_Pixel(dst, src, src_scale) +#define SkSPRITE_NEXT_ROW +#define SkSPRITE_POSTAMBLE(srcBM) +#include "SkSpriteBlitterTemplate.h" + +////////////////////////////////////////////////////////////////////////////////////////////////// + +class Sprite_D16_S16_Opaque : public SkSpriteBlitter { +public: + Sprite_D16_S16_Opaque(const SkBitmap& source) + : SkSpriteBlitter(source) {} + + // overrides + virtual void blitRect(int x, int y, int width, int height) + { + uint16_t* dst = fDevice->getAddr16(x, y); + const uint16_t* src = fSource->getAddr16(x - fLeft, y - fTop); + unsigned dstRB = fDevice->rowBytes(); + unsigned srcRB = fSource->rowBytes(); + + while (--height >= 0) + { + memcpy(dst, src, width << 1); + dst = (uint16_t*)((char*)dst + dstRB); + src = (const uint16_t*)((const char*)src + srcRB); + } + } +}; + +#define D16_S16_Blend_Pixel(dst, sc, scale) \ + do { \ + uint16_t dc = *dst; \ + *dst = SkToU16(SkBlendRGB16(sc, dc, scale)); \ + } while (0) + +#define SkSPRITE_CLASSNAME Sprite_D16_S16_Blend +#define SkSPRITE_ARGS , U8 alpha +#define SkSPRITE_FIELDS U8 fSrcAlpha; +#define SkSPRITE_INIT fSrcAlpha = alpha; +#define SkSPRITE_DST_TYPE uint16_t +#define SkSPRITE_SRC_TYPE uint16_t +#define SkSPRITE_DST_GETADDR getAddr16 +#define SkSPRITE_SRC_GETADDR getAddr16 +#define SkSPRITE_PREAMBLE(srcBM, x, y) int scale = SkAlpha255To256(fSrcAlpha); +#define SkSPRITE_BLIT_PIXEL(dst, src) D16_S16_Blend_Pixel(dst, src, scale) +#define SkSPRITE_NEXT_ROW +#define SkSPRITE_POSTAMBLE(srcBM) +#include "SkSpriteBlitterTemplate.h" + +////////////////////////////////////////////////////////////////////////////////////////////////// + +#define SkSPRITE_CLASSNAME Sprite_D16_SIndex8A_Opaque +#define SkSPRITE_ARGS +#define SkSPRITE_FIELDS +#define SkSPRITE_INIT +#define SkSPRITE_DST_TYPE uint16_t +#define SkSPRITE_SRC_TYPE uint8_t +#define SkSPRITE_DST_GETADDR getAddr16 +#define SkSPRITE_SRC_GETADDR getAddr8 +#define SkSPRITE_PREAMBLE(srcBM, x, y) const SkPMColor* ctable = srcBM.getColorTable()->lockColors() +#define SkSPRITE_BLIT_PIXEL(dst, src) D16_S32A_Opaque_Pixel(dst, ctable[src]) +#define SkSPRITE_NEXT_ROW +#define SkSPRITE_POSTAMBLE(srcBM) srcBM.getColorTable()->unlockColors(false) +#include "SkSpriteBlitterTemplate.h" + +#define SkSPRITE_CLASSNAME Sprite_D16_SIndex8A_Blend +#define SkSPRITE_ARGS , uint8_t alpha +#define SkSPRITE_FIELDS uint8_t fSrcAlpha; +#define SkSPRITE_INIT fSrcAlpha = alpha; +#define SkSPRITE_DST_TYPE uint16_t +#define SkSPRITE_SRC_TYPE uint8_t +#define SkSPRITE_DST_GETADDR getAddr16 +#define SkSPRITE_SRC_GETADDR getAddr8 +#define SkSPRITE_PREAMBLE(srcBM, x, y) const SkPMColor* ctable = srcBM.getColorTable()->lockColors(); unsigned src_scale = SkAlpha255To256(fSrcAlpha); +#define SkSPRITE_BLIT_PIXEL(dst, src) D16_S32A_Blend_Pixel(dst, ctable[src], src_scale) +#define SkSPRITE_NEXT_ROW +#define SkSPRITE_POSTAMBLE(srcBM) srcBM.getColorTable()->unlockColors(false); +#include "SkSpriteBlitterTemplate.h" + +////////////////////////////////////////////////////////////////////////////////////////////////// + +#define SkSPRITE_CLASSNAME Sprite_D16_SIndex8_Opaque +#define SkSPRITE_ARGS +#define SkSPRITE_FIELDS +#define SkSPRITE_INIT +#define SkSPRITE_DST_TYPE uint16_t +#define SkSPRITE_SRC_TYPE uint8_t +#define SkSPRITE_DST_GETADDR getAddr16 +#define SkSPRITE_SRC_GETADDR getAddr8 +#define SkSPRITE_PREAMBLE(srcBM, x, y) const uint16_t* ctable = srcBM.getColorTable()->lock16BitCache() +#define SkSPRITE_BLIT_PIXEL(dst, src) *dst = ctable[src] +#define SkSPRITE_NEXT_ROW +#define SkSPRITE_POSTAMBLE(srcBM) srcBM.getColorTable()->unlock16BitCache() +#include "SkSpriteBlitterTemplate.h" + +#define SkSPRITE_CLASSNAME Sprite_D16_SIndex8_Blend +#define SkSPRITE_ARGS , uint8_t alpha +#define SkSPRITE_FIELDS uint8_t fSrcAlpha; +#define SkSPRITE_INIT fSrcAlpha = alpha; +#define SkSPRITE_DST_TYPE uint16_t +#define SkSPRITE_SRC_TYPE uint8_t +#define SkSPRITE_DST_GETADDR getAddr16 +#define SkSPRITE_SRC_GETADDR getAddr8 +#define SkSPRITE_PREAMBLE(srcBM, x, y) const uint16_t* ctable = srcBM.getColorTable()->lock16BitCache(); unsigned src_scale = SkAlpha255To256(fSrcAlpha); +#define SkSPRITE_BLIT_PIXEL(dst, src) D16_S16_Blend_Pixel(dst, ctable[src], src_scale) +#define SkSPRITE_NEXT_ROW +#define SkSPRITE_POSTAMBLE(srcBM) srcBM.getColorTable()->unlock16BitCache(); +#include "SkSpriteBlitterTemplate.h" + +////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////// + +#include "SkTemplatesPriv.h" + +SkSpriteBlitter* SkSpriteBlitter::ChooseD16(const SkBitmap& source, SkXfermode* mode, U8 alpha, + void* storage, size_t storageSize) +{ + SkSpriteBlitter* blitter = nil; + + switch (source.getConfig()) { + case SkBitmap::kARGB_8888_Config: + if (mode == nil) + { + bool srcIsOpaque = source.isOpaque(); + if (alpha == 255) + { + if (srcIsOpaque) + SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_S32_Opaque, storage, storageSize, (source)); + else + SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_S32A_Opaque, storage, storageSize, (source)); + } + else + { + if (srcIsOpaque) + SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_S32_Blend, storage, storageSize, (source, alpha)); + else + SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_S32A_Blend, storage, storageSize, (source, alpha)); + } + } + break; + case SkBitmap::kRGB_565_Config: +#ifdef SK_SUPPORT_16_8_BITMAP + if (source.getA8Plane()) + { + if (mode == nil) + { + if (alpha == 255) + SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_S816_Opaque, storage, storageSize, (source)); + else + SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_S816_Blend, storage, storageSize, (source, alpha)); + } + } + else +#endif + if (mode == nil) + { + if (alpha == 255) + SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_S16_Opaque, storage, storageSize, (source)); + else + SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_S16_Blend, storage, storageSize, (source, alpha)); + } + break; + case SkBitmap::kIndex8_Config: + if (mode == nil) + { + if (source.getColorTable()->getFlags() & SkColorTable::kColorsAreOpaque_Flag) + { + if (alpha == 255) + SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_SIndex8_Opaque, storage, storageSize, (source)); + else + SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_SIndex8_Blend, storage, storageSize, (source, alpha)); + } + else + { + if (alpha == 255) + SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_SIndex8A_Opaque, storage, storageSize, (source)); + else + SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_SIndex8A_Blend, storage, storageSize, (source, alpha)); + } + } + break; + default: + break; + } + return blitter; +} + diff --git a/libs/graphics/sgl/SkString.cpp b/libs/graphics/sgl/SkString.cpp new file mode 100644 index 0000000000..1859f98154 --- /dev/null +++ b/libs/graphics/sgl/SkString.cpp @@ -0,0 +1,602 @@ +#include "SkString.h" +#include "SkFixed.h" +#include "SkUtils.h" +#include <stdarg.h> + +bool SkStrStartsWith(const char string[], const char prefix[]) +{ + SkASSERT(string); + SkASSERT(prefix); + return !strncmp(string, prefix, strlen(prefix)); +} + +bool SkStrEndsWith(const char string[], const char suffix[]) +{ + SkASSERT(string); + SkASSERT(suffix); + size_t strLen = strlen(string); + size_t suffixLen = strlen(suffix); + return strLen >= suffixLen && + !strncmp(string + strLen - suffixLen, suffix, suffixLen); +} + +int SkStrStartsWithOneOf(const char string[], const char prefixes[]) +{ + int index = 0; + do { + const char* limit = strchr(prefixes, '\0'); + if (!strncmp(string, prefixes, limit - prefixes)) + return index; + prefixes = limit + 1; + index++; + } while (prefixes[0]); + return -1; +} + +char* SkStrAppendS32(char string[], int32_t dec) +{ + SkDEBUGCODE(char* start = string;) + + char buffer[SkStrAppendS32_MaxSize]; + char* p = buffer + sizeof(buffer); + bool neg = false; + + if (dec < 0) + { + neg = true; + dec = -dec; + } + do { + *--p = SkToU8('0' + dec % 10); + dec /= 10; + } while (dec != 0); + if (neg) + *--p = '-'; + + SkASSERT(p >= buffer); + char* stop = buffer + sizeof(buffer); + while (p < stop) + *string++ = *p++; + + SkASSERT(string - start <= SkStrAppendS32_MaxSize); + return string; +} + +char* SkStrAppendScalar(char string[], SkScalar value) +{ + SkDEBUGCODE(char* start = string;) + + SkFixed x = SkScalarToFixed(value); + + if (x < 0) + { + *string++ = '-'; + x = -x; + } + + unsigned frac = x & 0xFFFF; + x >>= 16; + if (frac == 0xFFFF) // need to do this to "round up", since 65535/65536 is closer to 1 than to .9999 + { + x += 1; + frac = 0; + } + string = SkStrAppendS32(string, x); + + // now handle the fractional part (if any) + if (frac) + { + static const uint16_t gTens[] = { 1000, 100, 10, 1 }; + const uint16_t* tens = gTens; + + x = SkFixedRound(frac * 10000); + SkASSERT(x < 10000); + *string++ = '.'; + do { + unsigned powerOfTen = *tens++; + *string++ = SkToU8('0' + x / powerOfTen); + x %= powerOfTen; + } while (x != 0); + } + + SkASSERT(string - start <= SkStrAppendScalar_MaxSize); + return string; +} + +//////////////////////////////////////////////////////////////////////////////////// + +#define kMaxRefCnt_SkString SK_MaxU16 + +// the 3 values are [length] [refcnt] [terminating zero data] +static const U16 gEmptyRec[3] = { 0, 0, 0 }; + +SkString::Rec* SkString::AllocRec(const char text[], U16CPU len) +{ + Rec* rec; + + if (len == 0) + rec = (Rec*)gEmptyRec; + else + { + // add 1 for terminating 0, then align4 so we can have some slop when growing the string + rec = (Rec*)sk_malloc_throw(sizeof(Rec) + SkAlign4(len + 1)); + rec->fLength = SkToU16(len); + rec->fRefCnt = 1; + if (text) + memcpy(rec->data(), text, len); + rec->data()[len] = 0; + } + return rec; +} + +SkString::Rec* SkString::RefRec(Rec* src) +{ + if (src != (Rec*)gEmptyRec) + { + if (src->fRefCnt == kMaxRefCnt_SkString) { + src = AllocRec(src->data(), src->fLength); + } else + src->fRefCnt += 1; + } + return src; +} + +#ifdef SK_DEBUG +void SkString::validate() const +{ + // make sure know one has written over our global + SkASSERT(((Rec*)gEmptyRec)->fLength == 0); + SkASSERT(((Rec*)gEmptyRec)->fRefCnt == 0); + SkASSERT(((Rec*)gEmptyRec)->data()[0] == 0); + + if (fRec != (Rec*)gEmptyRec) + { + SkASSERT(fRec->fLength > 0); + SkASSERT(fRec->fRefCnt > 0); + SkASSERT(fRec->data()[fRec->fLength] == 0); + } + SkASSERT(fStr == c_str()); +} +#endif + +/////////////////////////////////////////////////////////////////////// + +SkString::SkString() : fRec((Rec*)gEmptyRec) { +#ifdef SK_DEBUG + fStr = fRec->data(); +#endif +} + +SkString::SkString(size_t len) +{ + SkASSERT(SkToU16(len) == len); // can't handle larger than 64K + + fRec = AllocRec(nil, (U16CPU)len); +#ifdef SK_DEBUG + fStr = fRec->data(); +#endif +} + +SkString::SkString(const char text[]) +{ + size_t len = text ? strlen(text) : 0; + + fRec = AllocRec(text, (U16CPU)len); +#ifdef SK_DEBUG + fStr = fRec->data(); +#endif +} + +SkString::SkString(const char text[], size_t len) +{ + fRec = AllocRec(text, (U16CPU)len); +#ifdef SK_DEBUG + fStr = fRec->data(); +#endif +} + +SkString::SkString(const SkString& src) +{ + src.validate(); + + fRec = RefRec(src.fRec); +#ifdef SK_DEBUG + fStr = fRec->data(); +#endif +} + +SkString::~SkString() +{ + this->validate(); + + if (fRec->fLength) + { + SkASSERT(fRec->fRefCnt > 0); + if (--fRec->fRefCnt == 0) + sk_free(fRec); + } +} + +bool SkString::equals(const SkString& src) const +{ + return fRec == src.fRec || this->equals(src.c_str(), src.size()); +} + +bool SkString::equals(const char text[]) const +{ + return this->equals(text, text ? strlen(text) : 0); +} + +bool SkString::equals(const char text[], size_t len) const +{ + SkASSERT(len == 0 || text != nil); + + return fRec->fLength == len && !memcmp(fRec->data(), text, len); +} + +SkString& SkString::operator=(const SkString& src) +{ + this->validate(); + + if (fRec != src.fRec) + { + SkString tmp(src); + this->swap(tmp); + } + return *this; +} + +void SkString::reset() +{ + this->validate(); + + if (fRec->fLength) + { + SkASSERT(fRec->fRefCnt > 0); + if (--fRec->fRefCnt == 0) + sk_free(fRec); + } + + fRec = (Rec*)gEmptyRec; +#ifdef SK_DEBUG + fStr = fRec->data(); +#endif +} + +char* SkString::writable_str() +{ + this->validate(); + + if (fRec->fLength) + { + if (fRec->fRefCnt > 1) + { + fRec->fRefCnt -= 1; + fRec = AllocRec(fRec->data(), fRec->fLength); + #ifdef SK_DEBUG + fStr = fRec->data(); + #endif + } + } + return fRec->data(); +} + +void SkString::set(const char text[]) +{ + this->set(text, text ? strlen(text) : 0); +} + +void SkString::set(const char text[], size_t len) +{ + if (len == 0) + this->reset(); + else if (fRec->fRefCnt == 1 && len <= fRec->fLength) // should we resize if len <<<< fLength, to save RAM? (e.g. len < (fLength>>1)) + { + // just use less of the buffer without allocating a smaller one + char* p = this->writable_str(); + if (text) + memcpy(p, text, len); + p[len] = 0; + fRec->fLength = SkToU16(len); + } + else if (fRec->fRefCnt == 1 && ((unsigned)fRec->fLength >> 2) == (len >> 2)) + { + // we have spare room in the current allocation, so don't alloc a larger one + char* p = this->writable_str(); + if (text) + memcpy(p, text, len); + p[len] = 0; + fRec->fLength = SkToU16(len); + } + else + { + SkString tmp(text, len); + this->swap(tmp); + } +} + +void SkString::setUTF16(const U16 src[]) +{ + int count = 0; + + while (src[count]) + count += 1; + + if (count == 0) + this->reset(); + else if (count <= fRec->fLength) // should we resize if len <<<< fLength, to save RAM? (e.g. len < (fLength>>1)) + { + if (count < fRec->fLength) + this->resize(count); + char* p = this->writable_str(); + for (int i = 0; i < count; i++) + p[i] = SkToU8(src[i]); + p[count] = 0; + } + else + { + SkString tmp(count); + char* p = tmp.writable_str(); + + for (int i = 0; i < count; i++) + p[i] = SkToU8(src[i]); + + this->swap(tmp); + } +} + +void SkString::insert(size_t offset, const char text[]) +{ + this->insert(offset, text, text ? strlen(text) : 0); +} + +void SkString::insert(size_t offset, const char text[], size_t len) +{ + if (len) + { + size_t length = fRec->fLength; + if (offset > length) + offset = length; + + /* If we're the only owner, and we have room in our allocation for the insert, + do it in place, rather than allocating a new buffer. + + To know we have room, compare the allocated sizes + beforeAlloc = SkAlign4(length + 1) + afterAlloc = SkAligh4(length + 1 + len) + but SkAlign4(x) is (x + 3) >> 2 << 2 + which is equivalent for testing to (length + 1 + 3) >> 2 == (length + 1 + 3 + len) >> 2 + and we can then eliminate the +1+3 since that doesn't affec the answer + */ + if (fRec->fRefCnt == 1 && (length >> 2) == ((length + len) >> 2)) + { + char* dst = this->writable_str(); + + if (offset < length) + memmove(dst + offset + len, dst + offset, length - offset); + memcpy(dst + offset, text, len); + + dst[length + len] = 0; + fRec->fLength = SkToU16(length + len); + } + else + { + /* Seems we should use realloc here, since that is safe if it fails + (we have the original data), and might be faster than alloc/copy/free. + */ + SkString tmp(fRec->fLength + len); + char* dst = tmp.writable_str(); + + if (offset > 0) + memcpy(dst, fRec->data(), offset); + memcpy(dst + offset, text, len); + if (offset < fRec->fLength) + memcpy(dst + offset + len, fRec->data() + offset, fRec->fLength - offset); + + this->swap(tmp); + } + } +} + +void SkString::insertUnichar(size_t offset, SkUnichar uni) +{ + char buffer[kMaxBytesInUTF8Sequence]; + size_t len = SkUTF8_FromUnichar(uni, buffer); + + if (len) + this->insert(offset, buffer, len); +} + +void SkString::insertS32(size_t offset, S32 dec) +{ + char buffer[SkStrAppendS32_MaxSize]; + char* stop = SkStrAppendS32(buffer, dec); + this->insert(offset, buffer, stop - buffer); +} + +void SkString::insertHex(size_t offset, U32 hex, int minDigits) +{ + minDigits = SkPin32(minDigits, 0, 8); + + static const char gHex[] = "0123456789ABCDEF"; + + char buffer[8]; + char* p = buffer + sizeof(buffer); + + do { + *--p = gHex[hex & 0xF]; + hex >>= 4; + minDigits -= 1; + } while (hex != 0); + while (--minDigits >= 0) + *--p = '0'; + + SkASSERT(p >= buffer); + this->insert(offset, p, buffer + sizeof(buffer) - p); +} + +void SkString::insertScalar(size_t offset, SkScalar value) +{ + char buffer[SkStrAppendScalar_MaxSize]; + char* stop = SkStrAppendScalar(buffer, value); + this->insert(offset, buffer, stop - buffer); +} + +/////////////////////////////////////////////////////////////////////////// + +//#include <stdarg.h> +#if defined(SK_BUILD_FOR_WIN) || defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_UNIX) + #include <stdio.h> +#endif + +void SkString::printf(const char format[], ...) +{ + static const size_t kBufferSize = 100; + + char buffer[kBufferSize + 1]; + +#ifdef SK_BUILD_FOR_WIN + va_list args; + va_start(args, format); + _vsnprintf(buffer, kBufferSize, format, args); + va_end(args); +#elif defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_UNIX) + va_list args; + va_start(args, format); + vsnprintf(buffer, kBufferSize, format, args); + va_end(args); +#else + buffer[0] = 0; +#endif + + this->set(buffer, strlen(buffer)); +} + +/////////////////////////////////////////////////////////////////////////// + +void SkString::remove(size_t offset, size_t length) +{ + size_t size = this->size(); + + if (offset < size) + { + if (offset + length > size) + length = size - offset; + if (length > 0) + { + SkASSERT(size > length); + SkString tmp(size - length); + char* dst = tmp.writable_str(); + const char* src = this->c_str(); + + if (offset) + { + SkASSERT(offset <= tmp.size()); + memcpy(dst, src, offset); + } + size_t tail = size - offset - length; + SkASSERT((S32)tail >= 0); + if (tail) + { + // SkASSERT(offset + length <= tmp.size()); + memcpy(dst + offset, src + offset + length, tail); + } + SkASSERT(dst[tmp.size()] == 0); + this->swap(tmp); + } + } +} + +void SkString::swap(SkString& other) +{ + this->validate(); + other.validate(); + + SkTSwap<Rec*>(fRec, other.fRec); +#ifdef SK_DEBUG + SkTSwap<const char*>(fStr, other.fStr); +#endif +} + +///////////////////////////////////////////////////////////////////////////////// + +SkAutoUCS2::SkAutoUCS2(const char utf8[]) +{ + size_t len = strlen(utf8); + fUCS2 = (U16*)sk_malloc_throw((len + 1) * sizeof(U16)); + + U16* dst = fUCS2; + for (;;) + { + SkUnichar uni = SkUTF8_NextUnichar(&utf8); + *dst++ = SkToU16(uni); + if (uni == 0) + break; + } + fCount = (int)(dst - fUCS2); +} + +SkAutoUCS2::~SkAutoUCS2() +{ + delete[] fUCS2; +} + +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + +void SkString::UnitTest() +{ +#ifdef SK_SUPPORT_UNITTEST + SkString a; + SkString b((size_t)0); + SkString c(""); + SkString d(nil, 0); + + SkASSERT(a.isEmpty()); + SkASSERT(a == b && a == c && a == d); + + a.set("hello"); + b.set("hellox", 5); + c.set(a); + d.resize(5); + memcpy(d.writable_str(), "helloz", 5); + + SkASSERT(!a.isEmpty()); + SkASSERT(a.size() == 5); + SkASSERT(a == b && a == c && a == d); + SkASSERT(a.equals("hello", 5)); + SkASSERT(a.equals("hello")); + SkASSERT(!a.equals("help")); + + SkString e(a); + SkString f("hello"); + SkString g("helloz", 5); + + SkASSERT(a == e && a == f && a == g); + + b.set("world"); + c = b; + SkASSERT(a != b && a != c && b == c); + + a.append(" world"); + e.append("worldz", 5); + e.insert(5, " "); + f.set("world"); + f.prepend("hello "); + SkASSERT(a.equals("hello world") && a == e && a == f); + + a.reset(); + b.resize(0); + SkASSERT(a.isEmpty() && b.isEmpty() && a == b); + + a.set("a"); + a.set("ab"); + a.set("abc"); + a.set("abcd"); +#endif +} + +#endif + diff --git a/libs/graphics/sgl/SkStroke.cpp b/libs/graphics/sgl/SkStroke.cpp new file mode 100644 index 0000000000..f0c66546b0 --- /dev/null +++ b/libs/graphics/sgl/SkStroke.cpp @@ -0,0 +1,586 @@ +#include "SkStrokerPriv.h" +#include "SkGeometry.h" +#include "SkPath.h" + +#define kMaxQuadSubdivide 5 +#define kMaxCubicSubdivide 4 + +static inline bool degenerate_vector(const SkVector& v) +{ + return SkScalarNearlyZero(v.fX) && SkScalarNearlyZero(v.fY); +} + +static inline bool degenerate_line(const SkPoint& a, const SkPoint& b, SkScalar tolerance = SK_ScalarNearlyZero) +{ + return SkScalarNearlyZero(a.fX - b.fX, tolerance) && SkScalarNearlyZero(a.fY - b.fY, tolerance); +} + +static inline bool normals_too_curvy(const SkVector& norm0, SkVector& norm1) +{ + /* root2/2 is a 45-degree angle + make this constant bigger for more subdivisions (but not >= 1) + */ + static const SkScalar kFlatEnoughNormalDotProd = SK_ScalarSqrt2/2 + SK_Scalar1/10; + + SkASSERT(kFlatEnoughNormalDotProd > 0 && kFlatEnoughNormalDotProd < SK_Scalar1); + + return SkPoint::DotProduct(norm0, norm1) <= kFlatEnoughNormalDotProd; +} + +static inline bool normals_too_pinchy(const SkVector& norm0, SkVector& norm1) +{ + static const SkScalar kTooPinchyNormalDotProd = -SK_Scalar1 * 999 / 1000; + + return SkPoint::DotProduct(norm0, norm1) <= kTooPinchyNormalDotProd; +} + +static bool set_normal_unitnormal(const SkPoint& before, const SkPoint& after, + SkScalar radius, + SkVector* normal, SkVector* unitNormal) +{ + if (!unitNormal->setUnit(after.fX - before.fX, after.fY - before.fY)) + return false; + + unitNormal->rotateCCW(); + unitNormal->scale(radius, normal); + return true; +} + +static bool set_normal_unitnormal(const SkVector& vec, + SkScalar radius, + SkVector* normal, SkVector* unitNormal) +{ + if (!unitNormal->setUnit(vec.fX, vec.fY)) + return false; + + unitNormal->rotateCCW(); + unitNormal->scale(radius, normal); + return true; +} + +////////////////////////////////////////////////////////////////////////////////////////// + +class SkPathStroker { +public: + SkPathStroker(SkScalar radius, SkScalar miterLimit, SkPaint::Cap cap, SkPaint::Join join); + + void moveTo(const SkPoint&); + void lineTo(const SkPoint&); + void quadTo(const SkPoint&, const SkPoint&); + void cubicTo(const SkPoint&, const SkPoint&, const SkPoint&); + void close(bool isLine) { this->finishContour(true, isLine); } + + void done(SkPath* dst, bool isLine) + { + this->finishContour(false, isLine); + fOuter.addPath(fExtra); + dst->swap(fOuter); + } + +private: + SkScalar fRadius; + SkScalar fInvMiterLimit; + + SkVector fFirstNormal, fPrevNormal, fFirstUnitNormal, fPrevUnitNormal; + SkPoint fFirstPt, fPrevPt; // on original path + SkPoint fFirstOuterPt; + int fSegmentCount; + bool fPrevIsLine; + + SkStrokerPriv::CapProc fCapper; + SkStrokerPriv::JoinProc fJoiner; + + SkPath fInner, fOuter; // outer is our working answer, inner is temp + SkPath fExtra; // added as extra complete contours + + void finishContour(bool close, bool isLine); + void preJoinTo(const SkPoint&, SkVector* normal, SkVector* unitNormal, bool isLine); + void postJoinTo(const SkPoint&, const SkVector& normal, const SkVector& unitNormal); + + void line_to(const SkPoint& currPt, const SkVector& normal); + void quad_to(const SkPoint pts[3], + const SkVector& normalAB, const SkVector& unitNormalAB, + SkVector* normalBC, SkVector* unitNormalBC, + int subDivide); + void cubic_to(const SkPoint pts[4], + const SkVector& normalAB, const SkVector& unitNormalAB, + SkVector* normalCD, SkVector* unitNormalCD, + int subDivide); +}; + +//////////////////////////////////////////////////////////////////////////// + +void SkPathStroker::preJoinTo(const SkPoint& currPt, SkVector* normal, SkVector* unitNormal, bool currIsLine) +{ + SkASSERT(fSegmentCount >= 0); + + SkScalar prevX = fPrevPt.fX; + SkScalar prevY = fPrevPt.fY; + + SkAssertResult(set_normal_unitnormal(fPrevPt, currPt, fRadius, normal, unitNormal)); + + if (fSegmentCount == 0) + { + fFirstNormal = *normal; + fFirstUnitNormal = *unitNormal; + fFirstOuterPt.set(prevX + normal->fX, prevY + normal->fY); + + fOuter.moveTo(fFirstOuterPt.fX, fFirstOuterPt.fY); + fInner.moveTo(prevX - normal->fX, prevY - normal->fY); + } + else // we have a previous segment + { + fJoiner(&fOuter, &fInner, fPrevUnitNormal, fPrevPt, *unitNormal, fRadius, fInvMiterLimit, + fPrevIsLine, currIsLine); + } + fPrevIsLine = currIsLine; +} + +void SkPathStroker::postJoinTo(const SkPoint& currPt, const SkVector& normal, const SkVector& unitNormal) +{ + fPrevPt = currPt; + fPrevUnitNormal = unitNormal; + fPrevNormal = normal; + fSegmentCount += 1; +} + +void SkPathStroker::finishContour(bool close, bool currIsLine) +{ + if (fSegmentCount > 0) + { + SkPoint pt; + + if (close) + { + fJoiner(&fOuter, &fInner, fPrevUnitNormal, fPrevPt, fFirstUnitNormal, + fRadius, fInvMiterLimit, fPrevIsLine, currIsLine); + fOuter.close(); + // now add fInner as its own contour + fInner.getLastPt(&pt); + fOuter.moveTo(pt.fX, pt.fY); + fOuter.reversePathTo(fInner); + fOuter.close(); + } + else // add caps to start and end + { + // cap the end + fInner.getLastPt(&pt); + fCapper(&fOuter, fPrevPt, fPrevNormal, pt, currIsLine ? &fInner : nil); + fOuter.reversePathTo(fInner); + // cap the start + fCapper(&fOuter, fFirstPt, -fFirstNormal, fFirstOuterPt, fPrevIsLine ? &fInner : nil); + fOuter.close(); + } + } + fInner.reset(); + fSegmentCount = -1; +} + +//////////////////////////////////////////////////////////////////////////// + +SkPathStroker::SkPathStroker(SkScalar radius, SkScalar miterLimit, SkPaint::Cap cap, SkPaint::Join join) + : fRadius(radius) +{ + if (join == SkPaint::kMiter_Join) + { + if (miterLimit <= SK_Scalar1) + join = SkPaint::kBevel_Join; + else + fInvMiterLimit = SkScalarInvert(miterLimit); + } + fCapper = SkStrokerPriv::CapFactory(cap); + fJoiner = SkStrokerPriv::JoinFactory(join); + fSegmentCount = -1; + fPrevIsLine = false; +} + +void SkPathStroker::moveTo(const SkPoint& pt) +{ + if (fSegmentCount > 0) + this->finishContour(false, false); + + fSegmentCount = 0; + fFirstPt = fPrevPt = pt; +} + +void SkPathStroker::line_to(const SkPoint& currPt, const SkVector& normal) +{ + fOuter.lineTo(currPt.fX + normal.fX, currPt.fY + normal.fY); + fInner.lineTo(currPt.fX - normal.fX, currPt.fY - normal.fY); +} + +void SkPathStroker::lineTo(const SkPoint& currPt) +{ + if (degenerate_line(fPrevPt, currPt)) + return; + + SkVector normal, unitNormal; + + this->preJoinTo(currPt, &normal, &unitNormal, true); + this->line_to(currPt, normal); + this->postJoinTo(currPt, normal, unitNormal); +} + +void SkPathStroker::quad_to(const SkPoint pts[3], + const SkVector& normalAB, const SkVector& unitNormalAB, + SkVector* normalBC, SkVector* unitNormalBC, + int subDivide) +{ + if (!set_normal_unitnormal(pts[1], pts[2], fRadius, normalBC, unitNormalBC)) + { + // pts[1] nearly equals pts[2], so just draw a line to pts[2] + this->line_to(pts[2], normalAB); + *normalBC = normalAB; + *unitNormalBC = unitNormalAB; + return; + } + + if (--subDivide >= 0 && normals_too_curvy(unitNormalAB, *unitNormalBC)) + { + SkPoint tmp[5]; + SkVector norm, unit; + + SkChopQuadAtHalf(pts, tmp); + this->quad_to(&tmp[0], normalAB, unitNormalAB, &norm, &unit, subDivide); + this->quad_to(&tmp[2], norm, unit, normalBC, unitNormalBC, subDivide); + } + else + { + SkVector normalB, unitB; + SkAssertResult(set_normal_unitnormal(pts[0], pts[2], fRadius, &normalB, &unitB)); + + fOuter.quadTo( pts[1].fX + normalB.fX, pts[1].fY + normalB.fY, + pts[2].fX + normalBC->fX, pts[2].fY + normalBC->fY); + fInner.quadTo( pts[1].fX - normalB.fX, pts[1].fY - normalB.fY, + pts[2].fX - normalBC->fX, pts[2].fY - normalBC->fY); + } +} + +void SkPathStroker::cubic_to(const SkPoint pts[4], + const SkVector& normalAB, const SkVector& unitNormalAB, + SkVector* normalCD, SkVector* unitNormalCD, + int subDivide) +{ + SkVector ab = pts[1] - pts[0]; + SkVector cd = pts[3] - pts[2]; + SkVector normalBC, unitNormalBC; + + bool degenerateAB = degenerate_vector(ab); + bool degenerateCD = degenerate_vector(cd); + + if (degenerateAB && degenerateCD) + { +DRAW_LINE: + this->line_to(pts[3], normalAB); + *normalCD = normalAB; + *unitNormalCD = unitNormalAB; + return; + } + + if (degenerateAB) + { + ab = pts[2] - pts[0]; + degenerateAB = degenerate_vector(ab); + } + if (degenerateCD) + { + cd = pts[3] - pts[1]; + degenerateCD = degenerate_vector(cd); + } + if (degenerateAB || degenerateCD) + goto DRAW_LINE; + + SkAssertResult(set_normal_unitnormal(cd, fRadius, normalCD, unitNormalCD)); + bool degenerateBC = !set_normal_unitnormal(pts[1], pts[2], fRadius, &normalBC, &unitNormalBC); + + if (--subDivide >= 0 && + (degenerateBC || normals_too_curvy(unitNormalAB, unitNormalBC) || normals_too_curvy(unitNormalBC, *unitNormalCD))) + { + SkPoint tmp[7]; + SkVector norm, unit, dummy, unitDummy; + + SkChopCubicAtHalf(pts, tmp); + this->cubic_to(&tmp[0], normalAB, unitNormalAB, &norm, &unit, subDivide); + // we use dummys since we already have a valid (and more accurate) normals for CD + this->cubic_to(&tmp[3], norm, unit, &dummy, &unitDummy, subDivide); + } + else + { + SkVector normalB, normalC; + + // need normals to inset/outset the off-curve pts B and C + + if (0)// this is normal to the line between our adjacent pts + { + normalB = pts[2] - pts[0]; + normalB.rotateCCW(); + SkAssertResult(normalB.setLength(fRadius)); + + normalC = pts[3] - pts[1]; + normalC.rotateCCW(); + SkAssertResult(normalC.setLength(fRadius)); + } + else // miter-join + { + SkVector unitBC = pts[2] - pts[1]; + unitBC.normalize(); + unitBC.rotateCCW(); + + normalB = unitNormalAB + unitBC; + normalC = *unitNormalCD + unitBC; + + SkScalar dot = SkPoint::DotProduct(unitNormalAB, unitBC); + SkAssertResult(normalB.setLength(SkScalarDiv(fRadius, SkScalarSqrt((SK_Scalar1 + dot)/2)))); + dot = SkPoint::DotProduct(*unitNormalCD, unitBC); + SkAssertResult(normalC.setLength(SkScalarDiv(fRadius, SkScalarSqrt((SK_Scalar1 + dot)/2)))); + } + + fOuter.cubicTo( pts[1].fX + normalB.fX, pts[1].fY + normalB.fY, + pts[2].fX + normalC.fX, pts[2].fY + normalC.fY, + pts[3].fX + normalCD->fX, pts[3].fY + normalCD->fY); + + fInner.cubicTo( pts[1].fX - normalB.fX, pts[1].fY - normalB.fY, + pts[2].fX - normalC.fX, pts[2].fY - normalC.fY, + pts[3].fX - normalCD->fX, pts[3].fY - normalCD->fY); + } +} + +void SkPathStroker::quadTo(const SkPoint& pt1, const SkPoint& pt2) +{ + bool degenerateAB = degenerate_line(fPrevPt, pt1); + bool degenerateBC = degenerate_line(pt1, pt2); + + if (degenerateAB | degenerateBC) + { + if (degenerateAB ^ degenerateBC) + this->lineTo(pt2); + return; + } + + SkVector normalAB, unitAB, normalBC, unitBC; + + this->preJoinTo(pt1, &normalAB, &unitAB, false); + + { + SkPoint pts[3], tmp[5]; + pts[0] = fPrevPt; + pts[1] = pt1; + pts[2] = pt2; + + if (SkChopQuadAtMaxCurvature(pts, tmp) == 2) + { + unitBC.setUnit(pts[2].fX - pts[1].fX, pts[2].fY - pts[1].fY); + unitBC.rotateCCW(); + if (normals_too_pinchy(unitAB, unitBC)) + { + normalBC = unitBC; + normalBC.scale(fRadius); + + fOuter.lineTo(tmp[2].fX + normalAB.fX, tmp[2].fY + normalAB.fY); + fOuter.lineTo(tmp[2].fX + normalBC.fX, tmp[2].fY + normalBC.fY); + fOuter.lineTo(tmp[4].fX + normalBC.fX, tmp[4].fY + normalBC.fY); + + fInner.lineTo(tmp[2].fX - normalAB.fX, tmp[2].fY - normalAB.fY); + fInner.lineTo(tmp[2].fX - normalBC.fX, tmp[2].fY - normalBC.fY); + fInner.lineTo(tmp[4].fX - normalBC.fX, tmp[4].fY - normalBC.fY); + + fExtra.addCircle(tmp[2].fX, tmp[2].fY, fRadius, SkPath::kCW_Direction); + } + else + { + this->quad_to(&tmp[0], normalAB, unitAB, &normalBC, &unitBC, kMaxQuadSubdivide); + SkVector n = normalBC; + SkVector u = unitBC; + this->quad_to(&tmp[2], n, u, &normalBC, &unitBC, kMaxQuadSubdivide); + } + } + else + this->quad_to(pts, normalAB, unitAB, &normalBC, &unitBC, kMaxQuadSubdivide); + } + + this->postJoinTo(pt2, normalBC, unitBC); +} + +void SkPathStroker::cubicTo(const SkPoint& pt1, const SkPoint& pt2, const SkPoint& pt3) +{ + bool degenerateAB = degenerate_line(fPrevPt, pt1); + bool degenerateBC = degenerate_line(pt1, pt2); + bool degenerateCD = degenerate_line(pt2, pt3); + + if (degenerateAB + degenerateBC + degenerateCD >= 2) + { + this->lineTo(pt3); + return; + } + + SkVector normalAB, unitAB, normalCD, unitCD; + + // find the first tangent (which might be pt1 or pt2 + { + const SkPoint* nextPt = &pt1; + if (degenerateAB) + nextPt = &pt2; + this->preJoinTo(*nextPt, &normalAB, &unitAB, false); + } + + { + SkPoint pts[4], tmp[13]; + int i, count; + SkVector n, u; + SkScalar tValues[3]; + + pts[0] = fPrevPt; + pts[1] = pt1; + pts[2] = pt2; + pts[3] = pt3; + +#if 1 + count = SkChopCubicAtMaxCurvature(pts, tmp, tValues); +#else + count = 1; + memcpy(tmp, pts, 4 * sizeof(SkPoint)); +#endif + n = normalAB; + u = unitAB; + for (i = 0; i < count; i++) + { + this->cubic_to(&tmp[i * 3], n, u, &normalCD, &unitCD, kMaxCubicSubdivide); + if (i == count - 1) + break; + n = normalCD; + u = unitCD; + + } + + // check for too pinchy + for (i = 1; i < count; i++) + { + SkPoint p; + SkVector v, c; + + SkEvalCubicAt(pts, tValues[i - 1], &p, &v, &c); + + SkScalar dot = SkPoint::DotProduct(c, c); + v.scale(SkScalarInvert(dot)); + + if (SkScalarNearlyZero(v.fX) && SkScalarNearlyZero(v.fY)) + { + fExtra.addCircle(p.fX, p.fY, fRadius, SkPath::kCW_Direction); + } + } + + } + + this->postJoinTo(pt3, normalCD, unitCD); +} + +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// + +#include "SkPaint.h" + +SkStroke::SkStroke() +{ + fWidth = SK_DefaultStrokeWidth; + fMiterLimit = SK_DefaultMiterLimit; + fCap = SkPaint::kDefault_Cap; + fJoin = SkPaint::kDefault_Join; + fDoFill = false; +} + +SkStroke::SkStroke(const SkPaint& p) +{ + fWidth = p.getStrokeWidth(); + fMiterLimit = p.getStrokeMiter(); + fCap = (U8)p.getStrokeCap(); + fJoin = (U8)p.getStrokeJoin(); + fDoFill = SkToU8(p.getStyle() == SkPaint::kStrokeAndFill_Style); +} + +SkStroke::SkStroke(const SkPaint& p, SkScalar width) +{ + fWidth = width; + fMiterLimit = p.getStrokeMiter(); + fCap = (U8)p.getStrokeCap(); + fJoin = (U8)p.getStrokeJoin(); + fDoFill = SkToU8(p.getStyle() == SkPaint::kStrokeAndFill_Style); +} + +void SkStroke::setWidth(SkScalar width) +{ + SkASSERT(width >= 0); + fWidth = width; +} + +void SkStroke::setMiterLimit(SkScalar miterLimit) +{ + SkASSERT(miterLimit >= 0); + fMiterLimit = miterLimit; +} + +void SkStroke::setCap(SkPaint::Cap cap) +{ + SkASSERT((unsigned)cap < SkPaint::kCapCount); + fCap = SkToU8(cap); +} + +void SkStroke::setJoin(SkPaint::Join join) +{ + SkASSERT((unsigned)join < SkPaint::kJoinCount); + fJoin = SkToU8(join); +} + +void SkStroke::strokePath(const SkPath& src, SkPath* dst) const +{ + SkASSERT(&src != nil && dst != nil); + + dst->reset(); + if (SkScalarHalf(fWidth) <= 0) + return; + + SkPathStroker stroker(SkScalarHalf(fWidth), fMiterLimit, this->getCap(), this->getJoin()); + + SkPath::Iter iter(src, false); + SkPoint pts[4]; + SkPath::Verb verb, lastSegment = SkPath::kMove_Verb; + + while ((verb = iter.next(pts)) != SkPath::kDone_Verb) + { + switch (verb) { + case SkPath::kMove_Verb: + stroker.moveTo(pts[0]); + break; + case SkPath::kLine_Verb: + stroker.lineTo(pts[1]); + lastSegment = verb; + break; + case SkPath::kQuad_Verb: + stroker.quadTo(pts[1], pts[2]); + lastSegment = verb; + break; + case SkPath::kCubic_Verb: + stroker.cubicTo(pts[1], pts[2], pts[3]); + lastSegment = verb; + break; + case SkPath::kClose_Verb: + stroker.close(lastSegment == SkPath::kLine_Verb); + break; + default: + break; + } + } + stroker.done(dst, lastSegment == SkPath::kLine_Verb); + + if (fDoFill) + dst->addPath(src); +} + +void SkStroke::strokeLine(const SkPoint& p0, const SkPoint& p1, SkPath* dst) const +{ + SkPath tmp; + + tmp.moveTo(p0); + tmp.lineTo(p1); + this->strokePath(tmp, dst); +} + diff --git a/libs/graphics/sgl/SkStrokerPriv.cpp b/libs/graphics/sgl/SkStrokerPriv.cpp new file mode 100644 index 0000000000..4daf9326b0 --- /dev/null +++ b/libs/graphics/sgl/SkStrokerPriv.cpp @@ -0,0 +1,216 @@ +#include "SkStrokerPriv.h" +#include "SkGeometry.h" +#include "SkPath.h" + +static void ButtCapper(SkPath* path, const SkPoint& pivot, + const SkVector& normal, const SkPoint& stop, + SkPath*) +{ + path->lineTo(stop.fX, stop.fY); +} + +static void RoundCapper(SkPath* path, const SkPoint& pivot, + const SkVector& normal, const SkPoint& stop, + SkPath*) +{ + SkScalar px = pivot.fX; + SkScalar py = pivot.fY; + SkScalar nx = normal.fX; + SkScalar ny = normal.fY; + SkScalar sx = SkScalarMul(nx, CUBIC_ARC_FACTOR); + SkScalar sy = SkScalarMul(ny, CUBIC_ARC_FACTOR); + + path->cubicTo(px + nx + CWX(sx, sy), py + ny + CWY(sx, sy), + px + CWX(nx, ny) + sx, py + CWY(nx, ny) + sy, + px + CWX(nx, ny), py + CWY(nx, ny)); + path->cubicTo(px + CWX(nx, ny) - sx, py + CWY(nx, ny) - sy, + px - nx + CWX(sx, sy), py - ny + CWY(sx, sy), + stop.fX, stop.fY); +} + +static void SquareCapper(SkPath* path, const SkPoint& pivot, + const SkVector& normal, const SkPoint& stop, + SkPath* otherPath) +{ + SkVector parallel; + normal.rotateCW(¶llel); + + if (otherPath) + { + path->setLastPt(pivot.fX + normal.fX + parallel.fX, pivot.fY + normal.fY + parallel.fY); + path->lineTo(pivot.fX - normal.fX + parallel.fX, pivot.fY - normal.fY + parallel.fY); + } + else + { + path->lineTo(pivot.fX + normal.fX + parallel.fX, pivot.fY + normal.fY + parallel.fY); + path->lineTo(pivot.fX - normal.fX + parallel.fX, pivot.fY - normal.fY + parallel.fY); + path->lineTo(stop.fX, stop.fY); + } +} + +///////////////////////////////////////////////////////////////////////////// + +static bool is_clockwise(const SkVector& before, const SkVector& after) +{ + return SkScalarMul(before.fX, after.fY) - SkScalarMul(before.fY, after.fX) > 0; +} + +enum AngleType { + kNearly180_AngleType, + kSharp_AngleType, + kShallow_AngleType, + kNearlyLine_AngleType +}; + +static AngleType Dot2AngleType(SkScalar dot) +{ +// need more precise fixed normalization +// SkASSERT(SkScalarAbs(dot) <= SK_Scalar1 + SK_ScalarNearlyZero); + + if (dot >= 0) // shallow or line + return SkScalarNearlyZero(SK_Scalar1 - dot) ? kNearlyLine_AngleType : kShallow_AngleType; + else // sharp or 180 + return SkScalarNearlyZero(SK_Scalar1 + dot) ? kNearly180_AngleType : kSharp_AngleType; +} + +static void BluntJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal, + const SkPoint& pivot, const SkVector& afterUnitNormal, + SkScalar radius, SkScalar invMiterLimit, bool, bool) +{ + SkVector after; + afterUnitNormal.scale(radius, &after); + + outer->lineTo(pivot.fX + after.fX, pivot.fY + after.fY); + inner->lineTo(pivot.fX - after.fX, pivot.fY - after.fY); +} + +static void RoundJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal, + const SkPoint& pivot, const SkVector& afterUnitNormal, + SkScalar radius, SkScalar invMiterLimit, bool, bool) +{ + SkScalar dotProd = SkPoint::DotProduct(beforeUnitNormal, afterUnitNormal); + AngleType angleType = Dot2AngleType(dotProd); + + if (angleType == kNearlyLine_AngleType) + return; + + SkVector before = beforeUnitNormal; + SkVector after = afterUnitNormal; + SkRotationDirection dir = kCW_SkRotationDirection; + + if (!is_clockwise(before, after)) + { + SkTSwap<SkPath*>(outer, inner); + before.negate(); + after.negate(); + dir = kCCW_SkRotationDirection; + } + + SkPoint pts[kSkBuildQuadArcStorage]; + SkMatrix matrix; + matrix.setScale(radius, radius, 0, 0); + matrix.postTranslate(pivot.fX, pivot.fY); + int count = SkBuildQuadArc(before, after, dir, &matrix, pts); + + if (count > 0) + { + for (int i = 1; i < count; i += 2) + outer->quadTo(pts[i].fX, pts[i].fY, pts[i+1].fX, pts[i+1].fY); + + after.scale(radius); + inner->lineTo(pivot.fX - after.fX, pivot.fY - after.fY); + } +} + +static void MiterJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal, + const SkPoint& pivot, const SkVector& afterUnitNormal, + SkScalar radius, SkScalar invMiterLimit, + bool prevIsLine, bool currIsLine) +{ + // negate the dot since we're using normals instead of tangents + SkScalar dotProd = SkPoint::DotProduct(beforeUnitNormal, afterUnitNormal); + AngleType angleType = Dot2AngleType(dotProd); + SkVector before = beforeUnitNormal; + SkVector after = afterUnitNormal; + SkVector mid; + SkScalar sinHalfAngle; + bool ccw; + + if (angleType == kNearlyLine_AngleType) + return; + if (angleType == kNearly180_AngleType) + { + currIsLine = false; + goto DO_BLUNT; + } + + /* midLength = radius / sinHalfAngle + if (midLength > miterLimit * radius) abort + if (radius / sinHalf > miterLimit * radius) abort + if (1 / sinHalf > miterLimit) abort + if (1 / miterLimit > sinHalf) abort + My dotProd is opposite sign, since it is built from normals and not tangents + hence 1 + dot instead of 1 - dot in the formula + */ + sinHalfAngle = SkScalarSqrt(SkScalarHalf(SK_Scalar1 + dotProd)); + if (sinHalfAngle < invMiterLimit) + { + currIsLine = false; + goto DO_BLUNT; + } + + ccw = !is_clockwise(before, after); + if (ccw) + { + SkTSwap<SkPath*>(outer, inner); + before.negate(); + after.negate(); + } + + // choose the most accurate way to form the initial mid-vector + if (angleType == kSharp_AngleType) + { + mid.set(after.fY - before.fY, before.fX - after.fX); + if (ccw) + mid.negate(); + } + else + mid.set(before.fX + after.fX, before.fY + after.fY); + + mid.setLength(SkScalarDiv(radius, sinHalfAngle)); + if (prevIsLine) + outer->setLastPt(pivot.fX + mid.fX, pivot.fY + mid.fY); + else + outer->lineTo(pivot.fX + mid.fX, pivot.fY + mid.fY); + +DO_BLUNT: + after.scale(radius); + if (!currIsLine) + outer->lineTo(pivot.fX + after.fX, pivot.fY + after.fY); + inner->lineTo(pivot.fX - after.fX, pivot.fY - after.fY); +} + +///////////////////////////////////////////////////////////////////////////// + +SkStrokerPriv::CapProc SkStrokerPriv::CapFactory(SkPaint::Cap cap) +{ + static const SkStrokerPriv::CapProc gCappers[] = { + ButtCapper, RoundCapper, SquareCapper + }; + + SkASSERT((unsigned)cap < SkPaint::kCapCount); + return gCappers[cap]; +} + +SkStrokerPriv::JoinProc SkStrokerPriv::JoinFactory(SkPaint::Join join) +{ + static const SkStrokerPriv::JoinProc gJoiners[] = { + MiterJoiner, RoundJoiner, BluntJoiner + }; + + SkASSERT((unsigned)join < SkPaint::kJoinCount); + return gJoiners[join]; +} + + + diff --git a/libs/graphics/sgl/SkStrokerPriv.h b/libs/graphics/sgl/SkStrokerPriv.h new file mode 100644 index 0000000000..60a2a1f392 --- /dev/null +++ b/libs/graphics/sgl/SkStrokerPriv.h @@ -0,0 +1,33 @@ +#ifndef SkStrokerPriv_DEFINED +#define SkStrokerPriv_DEFINED + +#include "SkStroke.h" + +#define CWX(x, y) (-y) +#define CWY(x, y) (x) +#define CCWX(x, y) (y) +#define CCWY(x, y) (-x) + +#define CUBIC_ARC_FACTOR ((SK_ScalarSqrt2 - SK_Scalar1) * 4 / 3) + +class SkStrokerPriv { +public: + typedef void (*CapProc)(SkPath* path, + const SkPoint& pivot, + const SkVector& normal, + const SkPoint& stop, + SkPath* otherPath); + + typedef void (*JoinProc)(SkPath* outer, SkPath* inner, + const SkVector& beforeUnitNormal, + const SkPoint& pivot, + const SkVector& afterUnitNormal, + SkScalar radius, SkScalar invMiterLimit, + bool prevIsLine, bool currIsLine); + + static CapProc CapFactory(SkPaint::Cap); + static JoinProc JoinFactory(SkPaint::Join); +}; + +#endif + diff --git a/libs/graphics/sgl/SkTSearch.cpp b/libs/graphics/sgl/SkTSearch.cpp new file mode 100644 index 0000000000..41118bb5f8 --- /dev/null +++ b/libs/graphics/sgl/SkTSearch.cpp @@ -0,0 +1,176 @@ +#include "SkTSearch.h" +#include <ctype.h> + +static inline const char* index_into_base(const char*const* base, int index, size_t elemSize) +{ + return *(const char*const*)((const char*)base + index * elemSize); +} + +int SkStrSearch(const char*const* base, int count, const char target[], size_t target_len, size_t elemSize) +{ + SkASSERT(base != nil); + SkASSERT(count >= 0); + + if (count <= 0) + return ~0; + + int lo = 0; + int hi = count - 1; + + while (lo < hi) + { + int mid = (hi + lo) >> 1; + const char* elem = index_into_base(base, mid, elemSize); + + int cmp = strncmp(elem, target, target_len); + if (cmp < 0) + lo = mid + 1; + else if (cmp > 0 || strlen(elem) > target_len) + hi = mid; + else + return mid; + } + + const char* elem = index_into_base(base, hi, elemSize); + int cmp = strncmp(elem, target, target_len); + if (cmp || strlen(elem) > target_len) + { + if (cmp < 0) + hi += 1; + hi = ~hi; + } + return hi; +} + +int SkStrSearch(const char*const* base, int count, const char target[], size_t elemSize) { + return SkStrSearch(base, count, target, strlen(target), elemSize); +} + +#define kLCBufferSize 32 + +int SkStrLCSearch(const char*const* base, int count, const char target[], size_t len, size_t elemSize) +{ + SkASSERT(target); + + char lcBuffer[kLCBufferSize + 1]; + char* lc; + + if (len <= kLCBufferSize) + lc = lcBuffer; + else + lc = (char*)sk_malloc_throw(len + 1); + + for (int i = (int)(len - 1); i >= 0; --i) + { + SkASSERT((target[i] & 0x80) == 0); // only works for ascii + lc[i] = (char)tolower(target[i]); + } + lc[len] = 0; + + int index = SkStrSearch(base, count, lc, len, elemSize); + + if (lc != lcBuffer) + sk_free(lc); + return index; +} + +int SkStrLCSearch(const char*const* base, int count, const char target[], size_t elemSize) { + return SkStrLCSearch(base, count, target, strlen(target), elemSize); +} + +////////////////////////////////////////////////////////////////////////////// + +#define SK_QSortTempSize 16 + +static inline void sk_qsort_swap(char a[], char b[], size_t elemSize) +{ + char tmp[SK_QSortTempSize]; + + while (elemSize > 0) + { + size_t size = elemSize; + if (size > SK_QSortTempSize) + size = SK_QSortTempSize; + elemSize -= size; + + memcpy(tmp, a, size); + memcpy(a, b, size); + memcpy(b, tmp, size); + a += size; + b += size; + } +} + +static void SkQSort_Partition(char* first, char* last, size_t elemSize, SkQSortCompareProc compare) +{ + char* left = first; + char* rite = last; + char* pivot = left; + + while (left <= rite) + { + while (left < last && compare(left, pivot) < 0) + left += elemSize; + while (first < rite && compare(rite, pivot) > 0) + rite -= elemSize; + if (left <= rite) + { + if (left < rite) + { + SkASSERT(compare(left, rite) >= 0); + sk_qsort_swap(left, rite, elemSize); + } + left += elemSize; + rite -= elemSize; + } + } + if (first < rite) + SkQSort_Partition(first, rite, elemSize, compare); + if (left < last) + SkQSort_Partition(left, last, elemSize, compare); +} + +void SkQSort(void* base, size_t count, size_t elemSize, SkQSortCompareProc compare) +{ + SkASSERT(base); + SkASSERT(compare); + SkASSERT(elemSize > 0); + + if (count <= 1) + return; + + SkQSort_Partition((char*)base, (char*)base + (count - 1) * elemSize, elemSize, compare); +} + +#ifdef SK_DEBUG + +#include "SkRandom.h" + +#ifdef SK_SUPPORT_UNITTEST +extern "C" { + int compare_int(const void* a, const void* b) + { + return *(const int*)a - *(const int*)b; + } +} +#endif + +void SkQSort_UnitTest() +{ +#ifdef SK_SUPPORT_UNITTEST + int array[100]; + SkRandom rand; + + for (int i = 0; i < 1000; i++) + { + int j, count = rand.nextRangeU(1, SK_ARRAY_COUNT(array)); + for (j = 0; j < count; j++) + array[j] = rand.nextS() & 0xFF; + SkQSort(array, count, sizeof(int), compare_int); + for (j = 1; j < count; j++) + SkASSERT(array[j-1] <= array[j]); + } +#endif +} + +#endif diff --git a/libs/graphics/sgl/SkTSort.h b/libs/graphics/sgl/SkTSort.h new file mode 100644 index 0000000000..bdfbf6daf4 --- /dev/null +++ b/libs/graphics/sgl/SkTSort.h @@ -0,0 +1,48 @@ +#ifndef SkTSort_DEFINED +#define SkTSort_DEFINED + +#include "SkTypes.h" + +template <typename T> +void SkTHeapSort_SiftDown(T array[], int root, int bottom) +{ + int root2 = root << 1; + + while (root2 <= bottom) + { + int maxChild; + + if (root2 == bottom) + maxChild = root2; + else if (array[root2] > array[root2 + 1]) + maxChild = root2; + else + maxChild = root2 + 1; + + if (array[root] < array[maxChild]) + { + SkTSwap<T>(array[root], array[maxChild]); + root = maxChild; + root2 = root << 1; + } + else + break; + } +} + +template <typename T> +void SkTHeapSort(T array[], int count) +{ + int i; + + for (i = count/2 - 1; i >= 0; --i) + SkTHeapSort_SiftDown<T>(array, i, count); + + for (i = count - 2; i >= 0; --i) + { + SkTSwap<T>(array[0], array[i + 1]); + SkTHeapSort_SiftDown<T>(array, 0, i); + } +} + +#endif diff --git a/libs/graphics/sgl/SkTemplatesPriv.h b/libs/graphics/sgl/SkTemplatesPriv.h new file mode 100644 index 0000000000..7c2e915421 --- /dev/null +++ b/libs/graphics/sgl/SkTemplatesPriv.h @@ -0,0 +1,67 @@ +#ifndef SkTemplatesPriv_DEFINED +#define SkTemplatesPriv_DEFINED + +#include "SkTemplates.h" + +//////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_BUILD_FOR_WIN32 + #define SK_PLACEMENT_NEW(result, classname, storage, storageSize) \ + result = SkNEW(classname) + + #define SK_PLACEMENT_NEW_ARGS(result, classname, storage, storageSize, args) \ + result = SkNEW_ARGS(classname, args) +#else + #include <new> + #define SK_PLACEMENT_NEW(result, classname, storage, storagesize) \ + do { \ + if (storagesize) \ + { \ + SkASSERT(storageSize >= sizeof(classname)); \ + result = new(storage) classname; \ + } \ + else \ + result = SkNEW(classname); \ + } while (0) + + #define SK_PLACEMENT_NEW_ARGS(result, classname, storage, storagesize, args) \ + do { \ + if (storagesize) \ + { \ + SkASSERT(storageSize >= sizeof(classname)); \ + result = new(storage) classname args; \ + } \ + else \ + result = SkNEW_ARGS(classname, args); \ + } while (0) +#endif + +//////////////////////////////////////////////////////////////////////////////// + +template <class T> class SkAutoTPlacementDelete { +public: + SkAutoTPlacementDelete(T* obj, void* storage) : fObj(obj), fStorage(storage) + { + } + ~SkAutoTPlacementDelete() + { + if (fObj) + { + if (fObj == fStorage) + fObj->~T(); + else + delete fObj; + } + } + T* detach() + { + T* obj = fObj; + fObj = nil; + return obj; + } +private: + T* fObj; + void* fStorage; +}; + +#endif diff --git a/libs/graphics/sgl/SkTextLayout.cpp b/libs/graphics/sgl/SkTextLayout.cpp new file mode 100644 index 0000000000..1ff426426d --- /dev/null +++ b/libs/graphics/sgl/SkTextLayout.cpp @@ -0,0 +1,66 @@ +#include "SkTextLayout.h" +#include "SkPaint.h" + +int SkTextLayout::layout(const SkPaint& paint, + const char* text, size_t byteLength, SkUnicodeWalkerProc proc, + Rec rec[]) +{ + const char* stop = text + byteLength; + Rec* recStart = rec; + + while (text < stop) + { + rec->fCharCode = proc(&text); + rec += 1; + // set private fields of Rec (when we use them) + } + + int count = rec - recStart; + if (count > 0) + this->onLayout(paint, recStart, count); + return count; +} + +///////////////////////////////////////////////////////////////////////////////////////////// + +class SkTrackingTextLayout : public SkTextLayout { +public: + SkTrackingTextLayout(SkScalar charExtra, SkScalar spaceExtra) + : fCharExtra(charExtra), fSpaceExtra(spaceExtra) {} + +protected: + // override + virtual void onLayout(const SkPaint& paint, Rec rec[], int count) + { + SkScalar ce = fCharExtra; + SkScalar se = fSpaceExtra; + + if (0 == se) // special case no space-extra (so we don't have to read charCode() + { + for (int i = 0; i < count; i++) + rec[i].fDeltaAdvance = ce; + } + else + { + for (int i = 0; i < count; i++) + { + SkScalar delta = ce; + if (32 == rec[i].charCode()) // do I need a fancier test? + delta += se; + rec[i].fDeltaAdvance = delta; + } + } + } + +private: + SkScalar fCharExtra, fSpaceExtra; +}; + +///////////////////////////////////////////////////////////////////////////////////////////// + +SkTextLayout* SkTextLayout::CreateTrackingLayout(SkScalar charExtra, SkScalar spaceExtra) +{ + return SkNEW_ARGS(SkTrackingTextLayout, (charExtra, spaceExtra)); +} + + diff --git a/libs/graphics/sgl/SkUtils.cpp b/libs/graphics/sgl/SkUtils.cpp new file mode 100644 index 0000000000..9cb7c65d0f --- /dev/null +++ b/libs/graphics/sgl/SkUtils.cpp @@ -0,0 +1,496 @@ +#include "SkUtils.h" + +#if 0 +#define assign_16_longs(dst, value) \ + do { \ + (dst)[0] = value; (dst)[1] = value; \ + (dst)[2] = value; (dst)[3] = value; \ + (dst)[4] = value; (dst)[5] = value; \ + (dst)[6] = value; (dst)[7] = value; \ + (dst)[8] = value; (dst)[9] = value; \ + (dst)[10] = value; (dst)[11] = value; \ + (dst)[12] = value; (dst)[13] = value; \ + (dst)[14] = value; (dst)[15] = value; \ + } while (0) +#else +#define assign_16_longs(dst, value) \ + do { \ + *(dst)++ = value; *(dst)++ = value; \ + *(dst)++ = value; *(dst)++ = value; \ + *(dst)++ = value; *(dst)++ = value; \ + *(dst)++ = value; *(dst)++ = value; \ + *(dst)++ = value; *(dst)++ = value; \ + *(dst)++ = value; *(dst)++ = value; \ + *(dst)++ = value; *(dst)++ = value; \ + *(dst)++ = value; *(dst)++ = value; \ + } while (0) +#endif + +/////////////////////////////////////////////////////////////////////////// + +#ifndef SK_MEMSET16_REDIRECT +void sk_memset16(uint16_t dst[], U16CPU value, int count) +{ + SkASSERT(dst != NULL && count >= 0); + + if (count <= 0) + return; + + // not sure if this helps to short-circuit on small values of count + if (count < 8) + { + do { + *dst++ = (uint16_t)value; + } while (--count != 0); + return; + } + + // ensure we're on a long boundary + if ((size_t)dst & 2) + { + *dst++ = (uint16_t)value; + count -= 1; + } + + uint32_t value32 = ((uint32_t)value << 16) | value; + + // handle the bulk with our unrolled macro + { + int sixteenlongs = count >> 5; + if (sixteenlongs) + { + U32* dst32 = (U32*)dst; + do { + assign_16_longs(dst32, value32); + } while (--sixteenlongs != 0); + dst = (uint16_t*)dst32; + count &= 31; + } + } + + // handle (most) of the rest + { + int longs = count >> 1; + if (longs) + { + do { + *(uint32_t*)dst = value32; + dst += 2; + } while (--longs != 0); + } + } + + // cleanup a possible trailing short + if (count & 1) + *dst = (uint16_t)value; +} +#endif + +#ifndef SK_MEMSET32_REDIRECT +void sk_memset32(uint32_t dst[], uint32_t value, int count) +{ + SkASSERT(dst != NULL && count >= 0); + + { + int sixteenlongs = count >> 4; + if (sixteenlongs) + { + do { + assign_16_longs(dst, value); + } while (--sixteenlongs != 0); + count &= 15; + } + } + + if (count) + { + do { + *dst++ = value; + } while (--count != 0); + } +} +#endif + +////////////////////////////////////////////////////////////////////////////// + +/* 0xxxxxxx 1 total + 10xxxxxx // never a leading byte + 110xxxxx 2 total + 1110xxxx 3 total + 11110xxx 4 total + + 11 10 01 01 xx xx xx xx 0... + 0xE5XX0000 + 0xE5 << 24 +*/ + +#ifdef SK_DEBUG + static void assert_utf8_leadingbyte(unsigned c) + { + SkASSERT(c <= 0xF7); // otherwise leading byte is too big (more than 4 bytes) + SkASSERT((c & 0xC0) != 0x80); // can't begin with a middle char + } + + int SkUTF8_LeadByteToCount(unsigned c) + { + assert_utf8_leadingbyte(c); + return (((0xE5 << 24) >> (c >> 4 << 1)) & 3) + 1; + } +#else + #define assert_utf8_leadingbyte(c) +#endif + +int SkUTF8_CountUnichars(const char utf8[]) +{ + SkASSERT(utf8); + + int count = 0; + + for (;;) + { + int c = *(const U8*)utf8; + if (c == 0) + break; + + utf8 += SkUTF8_LeadByteToCount(c); + count += 1; + } + return count; +} + +int SkUTF8_CountUnichars(const char utf8[], size_t byteLength) +{ + SkASSERT(NULL != utf8 || 0 == byteLength); + + int count = 0; + const char* stop = utf8 + byteLength; + + while (utf8 < stop) + { + utf8 += SkUTF8_LeadByteToCount(*(const uint8_t*)utf8); + count += 1; + } + return count; +} + +SkUnichar SkUTF8_ToUnichar(const char utf8[]) +{ + SkASSERT(NULL != utf8); + + const U8* p = (const U8*)utf8; + int c = *p; + int hic = c << 24; + + assert_utf8_leadingbyte(c); + + if (hic < 0) + { + U32 mask = (U32)~0x3F; + hic <<= 1; + do { + c = (c << 6) | (*++p & 0x3F); + mask <<= 5; + } while ((hic <<= 1) < 0); + c &= ~mask; + } + return c; +} + +SkUnichar SkUTF8_NextUnichar(const char** ptr) +{ + SkASSERT(NULL != ptr && NULL != *ptr); + + const U8* p = (const U8*)*ptr; + int c = *p; + int hic = c << 24; + + assert_utf8_leadingbyte(c); + + if (hic < 0) + { + U32 mask = (U32)~0x3F; + hic <<= 1; + do { + c = (c << 6) | (*++p & 0x3F); + mask <<= 5; + } while ((hic <<= 1) < 0); + c &= ~mask; + } + *ptr = (char*)p + 1; + return c; +} + +size_t SkUTF8_FromUnichar(SkUnichar uni, char utf8[]) +{ + if ((uint32_t)uni > 0x10FFFF) + { + SkASSERT(!"bad unichar"); + return 0; + } + + if (uni <= 127) + { + if (utf8) + *utf8 = (char)uni; + return 1; + } + + char tmp[4]; + char* p = tmp; + size_t count = 1; + + SkDEBUGCODE(SkUnichar orig = uni;) + + while (uni > 0x3F) + { + *p++ = (char)(0x80 | (uni & 0x3F)); + uni >>= 6; + count += 1; + } + + if (utf8) + { + p = tmp; + utf8 += count; + while (p < tmp + count - 1) + *--utf8 = *p++; + *--utf8 = (char)(~(0xFF >> count) | uni); + } + + SkASSERT(utf8 == NULL || orig == SkUTF8_ToUnichar(utf8)); + return count; +} + +//////////////////////////////////////////////////////////////////////////////////// + +int SkUTF16_CountUnichars(const uint16_t src[]) +{ + SkASSERT(src); + + int count = 0; + unsigned c; + while ((c = *src++) != 0) + { + SkASSERT(!SkUTF16_IsLowSurrogate(c)); + if (SkUTF16_IsHighSurrogate(c)) + { + c = *src++; + SkASSERT(SkUTF16_IsLowSurrogate(c)); + } + count += 1; + } + return count; +} + +int SkUTF16_CountUnichars(const uint16_t src[], int numberOf16BitValues) +{ + SkASSERT(src); + + const uint16_t* stop = src + numberOf16BitValues; + int count = 0; + while (src < stop) + { + unsigned c = *src++; + SkASSERT(!SkUTF16_IsLowSurrogate(c)); + if (SkUTF16_IsHighSurrogate(c)) + { + SkASSERT(src < stop); + c = *src++; + SkASSERT(SkUTF16_IsLowSurrogate(c)); + } + count += 1; + } + return count; +} + +SkUnichar SkUTF16_NextUnichar(const uint16_t** srcPtr) +{ + SkASSERT(srcPtr && *srcPtr); + + const uint16_t* src = *srcPtr; + SkUnichar c = *src++; + + SkASSERT(!SkUTF16_IsLowSurrogate(c)); + if (SkUTF16_IsHighSurrogate(c)) + { + unsigned c2 = *src++; + SkASSERT(SkUTF16_IsLowSurrogate(c2)); + + // c = ((c & 0x3FF) << 10) + (c2 & 0x3FF) + 0x10000 + // c = (((c & 0x3FF) + 64) << 10) + (c2 & 0x3FF) + c = (c << 10) + c2 + (0x10000 - (0xD800 << 10) - 0xDC00); + } + *srcPtr = src; + return c; +} + +size_t SkUTF16_FromUnichar(SkUnichar uni, uint16_t dst[]) +{ + SkASSERT((unsigned)uni <= 0x10FFFF); + + int extra = (uni > 0xFFFF); + + if (dst) + { + if (extra) + { + // dst[0] = SkToU16(0xD800 | ((uni - 0x10000) >> 10)); + // dst[0] = SkToU16(0xD800 | ((uni >> 10) - 64)); + dst[0] = SkToU16((0xD800 - 64) + (uni >> 10)); + dst[1] = SkToU16(0xDC00 | (uni & 0x3FF)); + + SkASSERT(SkUTF16_IsHighSurrogate(dst[0])); + SkASSERT(SkUTF16_IsLowSurrogate(dst[1])); + } + else + { + dst[0] = SkToU16(uni); + SkASSERT(!SkUTF16_IsHighSurrogate(dst[0])); + SkASSERT(!SkUTF16_IsLowSurrogate(dst[0])); + } + } + return 1 + extra; +} + +size_t SkUTF16_ToUTF8(const uint16_t utf16[], int numberOf16BitValues, char utf8[]) +{ + SkASSERT(numberOf16BitValues >= 0); + if (numberOf16BitValues <= 0) + return 0; + + SkASSERT(utf16 != NULL); + + const uint16_t* stop = utf16 + numberOf16BitValues; + size_t size = 0; + + if (utf8 == NULL) // just count + { + while (utf16 < stop) + size += SkUTF8_FromUnichar(SkUTF16_NextUnichar(&utf16), NULL); + } + else + { + char* start = utf8; + while (utf16 < stop) + utf8 += SkUTF8_FromUnichar(SkUTF16_NextUnichar(&utf16), utf8); + size = utf8 - start; + } + return size; +} + +//////////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + +#include "SkRandom.h" +#include "SkTSearch.h" +#include "SkTSort.h" + +#define kSEARCH_COUNT 91 + +#ifdef SK_SUPPORT_UNITTEST +static void test_search() +{ + int i, array[kSEARCH_COUNT]; + SkRandom rand; + + for (i = 0; i < kSEARCH_COUNT; i++) + array[i] = rand.nextS(); + + SkTHeapSort<int>(array, kSEARCH_COUNT); + // make sure we got sorted properly + for (i = 1; i < kSEARCH_COUNT; i++) + SkASSERT(array[i-1] <= array[i]); + + // make sure we can find all of our values + for (i = 0; i < kSEARCH_COUNT; i++) + { + int index = SkTSearch<int>(array, kSEARCH_COUNT, array[i], sizeof(int)); + SkASSERT(index == i); + } + + // make sure that random values are either found, or the correct + // insertion index is returned + for (i = 0; i < 10000; i++) + { + int value = rand.nextS(); + int index = SkTSearch<int>(array, kSEARCH_COUNT, value, sizeof(int)); + + if (index >= 0) + SkASSERT(index < kSEARCH_COUNT && array[index] == value); + else + { + index = ~index; + SkASSERT(index <= kSEARCH_COUNT); + if (index < kSEARCH_COUNT) + { + SkASSERT(value < array[index]); + if (index > 0) + SkASSERT(value > array[index - 1]); + } + else // we should append the new value + { + SkASSERT(value > array[kSEARCH_COUNT - 1]); + } + } + } +} + +static void test_utf16() +{ + static const SkUnichar gUni[] = { + 0x10000, 0x18080, 0x20202, 0xFFFFF, 0x101234 + }; + + uint16_t buf[2]; + + for (unsigned i = 0; i < SK_ARRAY_COUNT(gUni); i++) + { + size_t count = SkUTF16_FromUnichar(gUni[i], buf); + SkASSERT(count == 2); + size_t count2 = SkUTF16_CountUnichars(buf, 2); + SkASSERT(count2 == 1); + const uint16_t* ptr = buf; + SkUnichar c = SkUTF16_NextUnichar(&ptr); + SkASSERT(c == gUni[i]); + SkASSERT(ptr - buf == 2); + } +} + +#endif + +void SkUtils::UnitTest() +{ +#ifdef SK_SUPPORT_UNITTEST + static const struct { + const char* fUtf8; + SkUnichar fUni; + } gTest[] = { + { "a", 'a' }, + { "\xC3\x83", (3 << 6) | 3 }, + { "\xE3\x83\x83", (3 << 12) | (3 << 6) | 3 }, + { "\xF3\x83\x83\x83", (3 << 18) | (3 << 12) | (3 << 6) | 3 } + }; + + for (unsigned i = 0; i < SK_ARRAY_COUNT(gTest); i++) + { + const char* p = gTest[i].fUtf8; + int n = SkUTF8_CountUnichars(p); + SkUnichar u0 = SkUTF8_ToUnichar(gTest[i].fUtf8); + SkUnichar u1 = SkUTF8_NextUnichar(&p); + + SkASSERT(n == 1); + SkASSERT(u0 == u1); + SkASSERT(u0 == gTest[i].fUni); + SkASSERT(p - gTest[i].fUtf8 == (int)strlen(gTest[i].fUtf8)); + } + + test_utf16(); + + test_search(); +#endif +} + +#endif + + diff --git a/libs/graphics/sgl/SkXfermode.cpp b/libs/graphics/sgl/SkXfermode.cpp new file mode 100644 index 0000000000..6f5a9a89f8 --- /dev/null +++ b/libs/graphics/sgl/SkXfermode.cpp @@ -0,0 +1,535 @@ +#include "SkXfermode.h" +#include "SkColorPriv.h" + +static inline U8CPU SkAlphaMulAlpha(U8CPU a, U8CPU b) +{ + unsigned ab = a * b; +#if 0 + return ab / 255; +#else + return ((ab << 8) + ab + 257) >> 16; +#endif +} + +void SkXfermode::xfer32(SkPMColor dst[], const SkPMColor src[], int count, const SkAlpha aa[]) +{ + // override in subclass +} + +void SkXfermode::xfer16(uint16_t dst[], const SkPMColor src[], int count, const SkAlpha aa[]) +{ + // override in subclass +} + +void SkXfermode::xferA8(SkAlpha dst[], const SkPMColor src[], int count, const SkAlpha aa[]) +{ + // override in subclass +} + +////////////////////////////////////////////////////////////////////////////////// + +static SkPMColor SkFourByteInterp(SkPMColor src, SkPMColor dst, U8CPU alpha) +{ + unsigned scale = SkAlpha255To256(alpha); + + unsigned a = SkAlphaBlend(SkGetPackedA32(src), SkGetPackedA32(dst), scale); + unsigned r = SkAlphaBlend(SkGetPackedR32(src), SkGetPackedR32(dst), scale); + unsigned g = SkAlphaBlend(SkGetPackedG32(src), SkGetPackedG32(dst), scale); + unsigned b = SkAlphaBlend(SkGetPackedB32(src), SkGetPackedB32(dst), scale); + + return SkPackARGB32(a, r, g, b); +} + +void SkProcXfermode::xfer32(SkPMColor dst[], const SkPMColor src[], int count, const SkAlpha aa[]) +{ + SkASSERT(dst && src && count >= 0); + + SkXfermodeProc proc = fProc; + if (proc) + { + if (NULL == aa) + { + for (int i = count - 1; i >= 0; --i) + dst[i] = proc(src[i], dst[i]); + } + else + { + for (int i = count - 1; i >= 0; --i) + { + unsigned a = aa[i]; + if (a != 0) + { + SkPMColor dstC = dst[i]; + SkPMColor C = proc(src[i], dstC); + if (a != 0xFF) + C = SkFourByteInterp(C, dstC, a); + dst[i] = C; + } + } + } + } +} + +void SkProcXfermode::xfer16(uint16_t dst[], const SkPMColor src[], int count, const SkAlpha aa[]) +{ + SkASSERT(dst && src && count >= 0); + + SkXfermodeProc proc = fProc; + if (proc) + { + if (NULL == aa) + { + for (int i = count - 1; i >= 0; --i) + { + SkPMColor dstC = SkPixel16ToPixel32(dst[i]); + dst[i] = SkPixel32ToPixel16_ToU16(proc(src[i], dstC)); + } + } + else + { + for (int i = count - 1; i >= 0; --i) + { + unsigned a = aa[i]; + if (a != 0) + { + SkPMColor dstC = SkPixel16ToPixel32(dst[i]); + SkPMColor C = proc(src[i], dstC); + if (a != 0xFF) + C = SkFourByteInterp(C, dstC, a); + dst[i] = SkPixel32ToPixel16_ToU16(C); + } + } + } + } +} + +void SkProcXfermode::xferA8(SkAlpha dst[], const SkPMColor src[], int count, const SkAlpha aa[]) +{ + SkASSERT(dst && src && count >= 0); + + SkXfermodeProc proc = fProc; + if (proc) + { + if (NULL == aa) + { + for (int i = count - 1; i >= 0; --i) + dst[i] = SkToU8(SkGetPackedA32(proc(src[i], (SkPMColor)(dst[i] << SK_A32_SHIFT)))); + } + else + { + for (int i = count - 1; i >= 0; --i) + { + unsigned a = aa[i]; + if (a != 0) + { + SkAlpha dstA = dst[i]; + unsigned A = SkGetPackedA32(proc(src[i], (SkPMColor)(dstA << SK_A32_SHIFT))); + if (a != 0xFF) + A = SkAlphaBlend(A, dstA, SkAlpha255To256(a)); + dst[i] = SkToU8(A); + } + } + } + } +} + +SkProcXfermode::SkProcXfermode(SkRBuffer& buffer) : SkXfermode(buffer) +{ + fProc = (SkXfermodeProc)buffer.readPtr(); +} + +void SkProcXfermode::flatten(SkWBuffer& buffer) +{ + this->INHERITED::flatten(buffer); + + buffer.writePtr((void*)fProc); +} + +SkFlattenable::Factory SkProcXfermode::getFactory() +{ + return CreateProc; +} + +SkFlattenable* SkProcXfermode::CreateProc(SkRBuffer& buffer) +{ + return SkNEW_ARGS(SkProcXfermode, (buffer)); +} + +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// + +// kClear_Mode, //!< [0, 0] +static SkPMColor clear_modeproc(SkPMColor src, SkPMColor dst) +{ + return 0; +} + +// kSrc_Mode, //!< [Sa, Sc] +static SkPMColor src_modeproc(SkPMColor src, SkPMColor dst) +{ + return src; +} + +// kDst_Mode, //!< [Da, Dc] +static SkPMColor dst_modeproc(SkPMColor src, SkPMColor dst) +{ + return dst; +} + +// kSrcOver_Mode, //!< [Sa + (1 - Sa)*Da, Sc + (1 - Sa)*Dc] this is the default mode +static SkPMColor srcover_modeproc(SkPMColor src, SkPMColor dst) +{ + return src + SkAlphaMulQ(dst, SkAlpha255To256(255 - SkGetPackedA32(src))); +} + +// kDstOver_Mode, //!< [Sa + (1 - Sa)*Da, Dc + (1 - Da)*Sc] +static SkPMColor dstover_modeproc(SkPMColor src, SkPMColor dst) +{ + unsigned sa = SkGetPackedA32(src); + unsigned da = SkGetPackedA32(dst); + + return SkPackARGB32(sa + da - SkAlphaMulAlpha(sa, da), + SkGetPackedR32(dst) + SkAlphaMulAlpha(255 - da, SkGetPackedR32(src)), + SkGetPackedG32(dst) + SkAlphaMulAlpha(255 - da, SkGetPackedG32(src)), + SkGetPackedB32(dst) + SkAlphaMulAlpha(255 - da, SkGetPackedB32(src))); +} + +// kSrcIn_Mode, //!< [Sa * Da, Sc * Da] +static SkPMColor srcin_modeproc(SkPMColor src, SkPMColor dst) +{ + return SkAlphaMulQ(src, SkAlpha255To256(SkGetPackedA32(dst))); +} + +// kDstIn_Mode, //!< [Sa * Da, Sa * Dc] +static SkPMColor dstin_modeproc(SkPMColor src, SkPMColor dst) +{ + return SkAlphaMulQ(dst, SkAlpha255To256(SkGetPackedA32(src))); +} + +// kSrcOut_Mode, //!< [Sa * (1 - Da), Sc * (1 - Da)] +static SkPMColor srcout_modeproc(SkPMColor src, SkPMColor dst) +{ + return SkAlphaMulQ(src, SkAlpha255To256(255 - SkGetPackedA32(dst))); +} + +// kDstOut_Mode, //!< [Da * (1 - Sa), Dc * (1 - Sa)] +static SkPMColor dstout_modeproc(SkPMColor src, SkPMColor dst) +{ + return SkAlphaMulQ(dst, SkAlpha255To256(255 - SkGetPackedA32(src))); +} + +// kSrcATop_Mode, //!< [Da, Sc * Da + (1 - Sa) * Dc] +static SkPMColor srcatop_modeproc(SkPMColor src, SkPMColor dst) +{ + unsigned sa = SkGetPackedA32(src); + unsigned da = SkGetPackedA32(dst); + unsigned src_scale = SkAlpha255To256(255 - sa); + unsigned dst_scale = SkAlpha255To256(da); + + return SkPackARGB32(da, + (dst_scale * SkGetPackedR32(src) + src_scale * SkGetPackedR32(dst)) >> 8, + (dst_scale * SkGetPackedG32(src) + src_scale * SkGetPackedG32(dst)) >> 8, + (dst_scale * SkGetPackedB32(src) + src_scale * SkGetPackedB32(dst)) >> 8); +} + +// kDstATop_Mode, //!< [Sa, Sa * Dc + Sc * (1 - Da)] +static SkPMColor dstatop_modeproc(SkPMColor src, SkPMColor dst) +{ + unsigned sa = SkGetPackedA32(src); + unsigned da = SkGetPackedA32(dst); + unsigned src_scale = SkAlpha255To256(sa); + unsigned dst_scale = SkAlpha255To256(255 - da); + + return SkPackARGB32(sa, + (dst_scale * SkGetPackedR32(src) + src_scale * SkGetPackedR32(dst)) >> 8, + (dst_scale * SkGetPackedG32(src) + src_scale * SkGetPackedG32(dst)) >> 8, + (dst_scale * SkGetPackedB32(src) + src_scale * SkGetPackedB32(dst)) >> 8); +} + +// kXor_Mode, //!< [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc] +static SkPMColor xor_modeproc(SkPMColor src, SkPMColor dst) +{ + unsigned sa = SkGetPackedA32(src); + unsigned da = SkGetPackedA32(dst); + unsigned src_scale = SkAlpha255To256(255 - sa); + unsigned dst_scale = SkAlpha255To256(255 - da); + + return SkPackARGB32(sa + da - (SkAlphaMulAlpha(sa, da) << 1), + (dst_scale * SkGetPackedR32(src) + src_scale * SkGetPackedR32(dst)) >> 8, + (dst_scale * SkGetPackedG32(src) + src_scale * SkGetPackedG32(dst)) >> 8, + (dst_scale * SkGetPackedB32(src) + src_scale * SkGetPackedB32(dst)) >> 8); +} + + +// kDarken_Mode, [Sa + Da - SaáDa, Scá(1 - Da) + Dcá(1 - Sa) + min(Sc, Dc)] + +static inline unsigned darken_p(unsigned src, unsigned dst, unsigned src_mul, unsigned dst_mul) +{ + return (dst_mul * src + src_mul * dst >> 8) + SkMin32(src, dst); +} + +static SkPMColor darken_modeproc(SkPMColor src, SkPMColor dst) +{ + unsigned sa = SkGetPackedA32(src); + unsigned da = SkGetPackedA32(dst); + unsigned src_scale = SkAlpha255To256(255 - sa); + unsigned dst_scale = SkAlpha255To256(255 - da); + + unsigned ra = sa + da - SkAlphaMulAlpha(sa, da); + unsigned rr = darken_p(SkGetPackedR32(src), SkGetPackedR32(dst), src_scale, dst_scale); + unsigned rg = darken_p(SkGetPackedG32(src), SkGetPackedG32(dst), src_scale, dst_scale); + unsigned rb = darken_p(SkGetPackedB32(src), SkGetPackedB32(dst), src_scale, dst_scale); + + return SkPackARGB32(ra, SkFastMin32(rr, ra), SkFastMin32(rg, ra), SkFastMin32(rb, ra)); +} + +// kLighten_Mode, [Sa + Da - SaáDa, Scá(1 - Da) + Dcá(1 - Sa) + max(Sc, Dc)] +static inline unsigned lighten_p(unsigned src, unsigned dst, unsigned src_mul, unsigned dst_mul) +{ + return (dst_mul * src + src_mul * dst >> 8) + SkMax32(src, dst); +} + +static SkPMColor lighten_modeproc(SkPMColor src, SkPMColor dst) +{ + unsigned sa = SkGetPackedA32(src); + unsigned da = SkGetPackedA32(dst); + unsigned src_scale = SkAlpha255To256(255 - sa); + unsigned dst_scale = SkAlpha255To256(255 - da); + + unsigned ra = sa + da - SkAlphaMulAlpha(sa, da); + unsigned rr = lighten_p(SkGetPackedR32(src), SkGetPackedR32(dst), src_scale, dst_scale); + unsigned rg = lighten_p(SkGetPackedG32(src), SkGetPackedG32(dst), src_scale, dst_scale); + unsigned rb = lighten_p(SkGetPackedB32(src), SkGetPackedB32(dst), src_scale, dst_scale); + + return SkPackARGB32(ra, SkFastMin32(rr, ra), SkFastMin32(rg, ra), SkFastMin32(rb, ra)); +} + +////////////////////////////////////////////////////////////////////////////////// + +#if 0 // maybe do these later + +#define SkPinToU8(value) SkFastMin32(value, 0xFF) + +// kAdd_Mode, //!< clamp [Sa + Da, Sc + Dc] +static SkPMColor add_modeproc(SkPMColor src, SkPMColor dst) +{ + return SkPackARGB32(SkPinToU8(SkGetPackedA32(src) + SkGetPackedA32(dst)), + SkPinToU8(SkGetPackedR32(src) + SkGetPackedR32(dst)), + SkPinToU8(SkGetPackedG32(src) + SkGetPackedG32(dst)), + SkPinToU8(SkGetPackedB32(src) + SkGetPackedB32(dst))); +} + +static U8CPU do_mul(U8CPU src, U8CPU dst, unsigned src_scale, unsigned dst_scale) +{ + return (src * dst_scale + dst * (SkAlpha255To256(src) + src_scale)) >> 8; +} + +// kMul_Mode, //!< clamp [Sa + Da - Sa * Da, Sc * Dc + Sc * (1 - Da) + (1 - Sa) * Dc] +static SkPMColor mul_modeproc(SkPMColor src, SkPMColor dst) +{ + unsigned sa = SkGetPackedA32(src); + unsigned src_scale = SkAlpha255To256(255 - sa); + + unsigned da = SkGetPackedA32(dst); + unsigned dst_scale = SkAlpha255To256(255 - da); + + return SkPackARGB32(sa + da - SkAlphaMul(SkAlpha255To256(sa), da), + do_mul(SkGetPackedR32(src), SkGetPackedR32(dst), src_scale, dst_scale), + do_mul(SkGetPackedG32(src), SkGetPackedG32(dst), src_scale, dst_scale), + do_mul(SkGetPackedB32(src), SkGetPackedB32(dst), src_scale, dst_scale)); +} +#endif + +////////////////////////////////////////////////////////////////////////////////// + +class SkClearXfermode : public SkProcXfermode { +public: + SkClearXfermode() : SkProcXfermode(clear_modeproc) {} + + virtual void xfer32(SkPMColor dst[], const SkPMColor[], int count, const SkAlpha aa[]) + { + SkASSERT(dst && count >= 0); + + if (NULL == aa) + memset(dst, 0, count << 2); + else + { + for (int i = count - 1; i >= 0; --i) + { + unsigned a = aa[i]; + if (a == 0xFF) + dst[i] = 0; + else if (a != 0) + dst[i] = SkAlphaMulQ(dst[i], SkAlpha255To256(255 - a)); + } + } + } + virtual void xferA8(SkAlpha dst[], const SkPMColor[], int count, const SkAlpha aa[]) + { + SkASSERT(dst && count >= 0); + + if (NULL == aa) + memset(dst, 0, count); + else + { + for (int i = count - 1; i >= 0; --i) + { + unsigned a = aa[i]; + if (a == 0xFF) + dst[i] = 0; + else if (a != 0) + dst[i] = SkToU8(SkAlphaMul(dst[i], SkAlpha255To256(255 - a))); + } + } + } + + virtual Factory getFactory() { return CreateProc; } + // we have nothing to flatten(), so don't need to override it + +private: + SkClearXfermode(SkRBuffer& buffer) : SkProcXfermode(buffer) {} + + static SkFlattenable* CreateProc(SkRBuffer& buffer) + { + return SkNEW_ARGS(SkClearXfermode, (buffer)); + } +}; + +////////////////////////////////////////////////////////////////////////////////// + +class SkSrcXfermode : public SkProcXfermode { +public: + SkSrcXfermode() : SkProcXfermode(src_modeproc) {} + + virtual void xfer32(SkPMColor dst[], const SkPMColor src[], int count, const SkAlpha aa[]) + { + SkASSERT(dst && src && count >= 0); + + if (NULL == aa) + memcpy(dst, src, count << 2); + else + { + for (int i = count - 1; i >= 0; --i) + { + unsigned a = aa[i]; + if (a == 0xFF) + dst[i] = src[i]; + else if (a != 0) + dst[i] = SkFourByteInterp(src[i], dst[i], a); + } + } + } + virtual void xferA8(SkAlpha dst[], const SkPMColor src[], int count, const SkAlpha aa[]) + { + SkASSERT(dst && src && count >= 0); + + if (NULL == aa) + { + for (int i = count - 1; i >= 0; --i) + dst[i] = SkToU8(SkGetPackedA32(src[i])); + } + else + { + for (int i = count - 1; i >= 0; --i) + { + unsigned a = aa[i]; + if (a != 0) + { + unsigned srcA = SkGetPackedA32(src[i]); + if (a == 0xFF) + dst[i] = SkToU8(srcA); + else + dst[i] = SkToU8(SkAlphaBlend(srcA, dst[i], a)); + } + } + } + } + + virtual Factory getFactory() { return CreateProc; } + // we have nothing to flatten(), so don't need to override it + +private: + SkSrcXfermode(SkRBuffer& buffer) : SkProcXfermode(buffer) {} + + static SkFlattenable* CreateProc(SkRBuffer& buffer) + { + return SkNEW_ARGS(SkSrcXfermode, (buffer)); + } +}; + +////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "SkPorterDuff.h" + +static const SkXfermodeProc gPorterDuffModeProcs[] = { + clear_modeproc, + src_modeproc, + dst_modeproc, + srcover_modeproc, + dstover_modeproc, + srcin_modeproc, + dstin_modeproc, + srcout_modeproc, + dstout_modeproc, + srcatop_modeproc, + dstatop_modeproc, + xor_modeproc, + darken_modeproc, + lighten_modeproc +}; + +SkXfermode* SkPorterDuff::CreateXfermode(SkPorterDuff::Mode mode) +{ + SkASSERT(SK_ARRAY_COUNT(gPorterDuffModeProcs) == SkPorterDuff::kModeCount); + SkASSERT((unsigned)mode < SkPorterDuff::kModeCount); + + switch (mode) { + case kClear_Mode: + return SkNEW(SkClearXfermode); + case kSrc_Mode: + return SkNEW(SkSrcXfermode); + case kSrcOver_Mode: + return NULL; + default: + return SkNEW_ARGS(SkProcXfermode, (gPorterDuffModeProcs[mode])); + } +} + +#ifdef SK_DEBUG +static void unit_test() +{ + for (unsigned a = 0; a <= 255; a++) { + for (unsigned c = 0; c <= a; c++) { + SkPMColor pm = SkPackARGB32(a, c, c, c); + for (unsigned aa = 0; aa <= 255; aa++) { + for (unsigned cc = 0; cc <= aa; cc++) { + SkPMColor pm2 = SkPackARGB32(aa, cc, cc, cc); + + for (unsigned i = 0; i < SK_ARRAY_COUNT(gPorterDuffModeProcs); i++) { + gPorterDuffModeProcs[i](pm, pm2); + } + } + } + } + } +} +#endif + +SkXfermodeProc SkPorterDuff::GetXfermodeProc(Mode mode) +{ +#ifdef SK_DEBUGx + static bool gUnitTest; + if (!gUnitTest) { + gUnitTest = true; + unit_test(); + } +#endif + + SkXfermodeProc proc = NULL; + + if ((unsigned)mode < SkPorterDuff::kModeCount) + proc = gPorterDuffModeProcs[mode]; + + return proc; +} + |