aboutsummaryrefslogtreecommitdiffhomepage
path: root/libs/graphics/sgl
diff options
context:
space:
mode:
Diffstat (limited to 'libs/graphics/sgl')
-rw-r--r--libs/graphics/sgl/SkAlphaRuns.cpp168
-rw-r--r--libs/graphics/sgl/SkAntiRun.h168
-rw-r--r--libs/graphics/sgl/SkBitmap.cpp412
-rw-r--r--libs/graphics/sgl/SkBitmapSampler.cpp281
-rw-r--r--libs/graphics/sgl/SkBitmapSampler.h150
-rw-r--r--libs/graphics/sgl/SkBitmapSamplerTemplate.h99
-rw-r--r--libs/graphics/sgl/SkBitmapShader.cpp476
-rw-r--r--libs/graphics/sgl/SkBitmapShader.h51
-rw-r--r--libs/graphics/sgl/SkBitmapShader16BilerpTemplate.h126
-rw-r--r--libs/graphics/sgl/SkBitmapShaderTemplate.h231
-rw-r--r--libs/graphics/sgl/SkBlitBWMaskTemplate.h120
-rw-r--r--libs/graphics/sgl/SkBlitter.cpp795
-rw-r--r--libs/graphics/sgl/SkBlitter.h102
-rw-r--r--libs/graphics/sgl/SkBlitter_A1.cpp46
-rw-r--r--libs/graphics/sgl/SkBlitter_A8.cpp365
-rw-r--r--libs/graphics/sgl/SkBlitter_ARGB32.cpp465
-rw-r--r--libs/graphics/sgl/SkBlitter_RGB16.cpp674
-rw-r--r--libs/graphics/sgl/SkBlitter_Sprite.cpp73
-rw-r--r--libs/graphics/sgl/SkCanvas.cpp623
-rw-r--r--libs/graphics/sgl/SkColor.cpp118
-rw-r--r--libs/graphics/sgl/SkColorFilter.cpp36
-rw-r--r--libs/graphics/sgl/SkColorTable.cpp99
-rw-r--r--libs/graphics/sgl/SkCoreBlitters.h177
-rw-r--r--libs/graphics/sgl/SkDeque.cpp389
-rw-r--r--libs/graphics/sgl/SkDraw.cpp1139
-rw-r--r--libs/graphics/sgl/SkDraw.h89
-rw-r--r--libs/graphics/sgl/SkEdge.cpp429
-rw-r--r--libs/graphics/sgl/SkEdge.h77
-rw-r--r--libs/graphics/sgl/SkFP.h70
-rw-r--r--libs/graphics/sgl/SkFilterProc.cpp38
-rw-r--r--libs/graphics/sgl/SkFilterProc.h22
-rw-r--r--libs/graphics/sgl/SkGeometry.cpp1013
-rw-r--r--libs/graphics/sgl/SkGeometry.h146
-rw-r--r--libs/graphics/sgl/SkGlobals.cpp75
-rw-r--r--libs/graphics/sgl/SkGlyphCache.cpp234
-rw-r--r--libs/graphics/sgl/SkGlyphCache.h89
-rw-r--r--libs/graphics/sgl/SkGraphics.cpp143
-rw-r--r--libs/graphics/sgl/SkMaskFilter.cpp69
-rw-r--r--libs/graphics/sgl/SkPaint.cpp868
-rw-r--r--libs/graphics/sgl/SkPath.cpp1139
-rw-r--r--libs/graphics/sgl/SkPathEffect.cpp181
-rw-r--r--libs/graphics/sgl/SkPathMeasure.cpp623
-rw-r--r--libs/graphics/sgl/SkProcSpriteBlitter.cpp38
-rw-r--r--libs/graphics/sgl/SkRasterizer.cpp45
-rw-r--r--libs/graphics/sgl/SkRefCnt.cpp36
-rw-r--r--libs/graphics/sgl/SkRegion_path.cpp478
-rw-r--r--libs/graphics/sgl/SkScalerContext.cpp498
-rw-r--r--libs/graphics/sgl/SkScan.cpp32
-rw-r--r--libs/graphics/sgl/SkScan.h30
-rw-r--r--libs/graphics/sgl/SkScanPriv.h25
-rw-r--r--libs/graphics/sgl/SkScan_AntiPath.cpp209
-rw-r--r--libs/graphics/sgl/SkScan_Antihair.cpp397
-rw-r--r--libs/graphics/sgl/SkScan_Hairline.cpp254
-rw-r--r--libs/graphics/sgl/SkScan_Path.cpp430
-rw-r--r--libs/graphics/sgl/SkShader.cpp370
-rw-r--r--libs/graphics/sgl/SkSinTable.h268
-rw-r--r--libs/graphics/sgl/SkSpriteBlitter.h41
-rw-r--r--libs/graphics/sgl/SkSpriteBlitterTemplate.h60
-rw-r--r--libs/graphics/sgl/SkSpriteBlitter_ARGB32.cpp81
-rw-r--r--libs/graphics/sgl/SkSpriteBlitter_RGB16.cpp300
-rw-r--r--libs/graphics/sgl/SkString.cpp602
-rw-r--r--libs/graphics/sgl/SkStroke.cpp586
-rw-r--r--libs/graphics/sgl/SkStrokerPriv.cpp216
-rw-r--r--libs/graphics/sgl/SkStrokerPriv.h33
-rw-r--r--libs/graphics/sgl/SkTSearch.cpp176
-rw-r--r--libs/graphics/sgl/SkTSort.h48
-rw-r--r--libs/graphics/sgl/SkTemplatesPriv.h67
-rw-r--r--libs/graphics/sgl/SkTextLayout.cpp66
-rw-r--r--libs/graphics/sgl/SkUtils.cpp496
-rw-r--r--libs/graphics/sgl/SkXfermode.cpp535
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(&parallel);
+
+ 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;
+}
+