aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/ARGB32_Clamp_Bilinear_BitmapShader.h171
-rw-r--r--src/core/Sk64.cpp575
-rw-r--r--src/core/SkAlphaRuns.cpp185
-rw-r--r--src/core/SkAntiRun.h185
-rw-r--r--src/core/SkAutoKern.h62
-rw-r--r--src/core/SkBitmap.cpp1255
-rw-r--r--src/core/SkBitmapProcShader.cpp231
-rw-r--r--src/core/SkBitmapProcShader.h59
-rw-r--r--src/core/SkBitmapProcState.cpp476
-rw-r--r--src/core/SkBitmapProcState.h78
-rw-r--r--src/core/SkBitmapProcState_matrix.h271
-rw-r--r--src/core/SkBitmapProcState_matrixProcs.cpp199
-rw-r--r--src/core/SkBitmapProcState_sample.h207
-rw-r--r--src/core/SkBitmapSampler.cpp423
-rw-r--r--src/core/SkBitmapSampler.h170
-rw-r--r--src/core/SkBitmapSamplerTemplate.h116
-rw-r--r--src/core/SkBitmapShader.cpp822
-rw-r--r--src/core/SkBitmapShader.h73
-rw-r--r--src/core/SkBitmapShader16BilerpTemplate.h253
-rw-r--r--src/core/SkBitmapShaderTemplate.h314
-rw-r--r--src/core/SkBitmap_scroll.cpp107
-rw-r--r--src/core/SkBlitBWMaskTemplate.h137
-rw-r--r--src/core/SkBlitRow.h22
-rw-r--r--src/core/SkBlitRow_D16.cpp258
-rw-r--r--src/core/SkBlitRow_D4444.cpp214
-rw-r--r--src/core/SkBlitter.cpp923
-rw-r--r--src/core/SkBlitter.h143
-rw-r--r--src/core/SkBlitter_4444.cpp501
-rw-r--r--src/core/SkBlitter_A1.cpp63
-rw-r--r--src/core/SkBlitter_A8.cpp387
-rw-r--r--src/core/SkBlitter_ARGB32.cpp485
-rw-r--r--src/core/SkBlitter_RGB16.cpp782
-rw-r--r--src/core/SkBlitter_Sprite.cpp101
-rw-r--r--src/core/SkBuffer.cpp137
-rw-r--r--src/core/SkCanvas.cpp1420
-rw-r--r--src/core/SkChunkAlloc.cpp120
-rw-r--r--src/core/SkColor.cpp138
-rw-r--r--src/core/SkColorFilter.cpp108
-rw-r--r--src/core/SkColorTable.cpp143
-rw-r--r--src/core/SkComposeShader.cpp174
-rw-r--r--src/core/SkCordic.cpp301
-rw-r--r--src/core/SkCordic.h37
-rw-r--r--src/core/SkCoreBlitters.h259
-rw-r--r--src/core/SkDebug.cpp59
-rw-r--r--src/core/SkDebug_stdio.cpp61
-rw-r--r--src/core/SkDeque.cpp252
-rw-r--r--src/core/SkDevice.cpp110
-rw-r--r--src/core/SkDither.cpp49
-rw-r--r--src/core/SkDraw.cpp2348
-rw-r--r--src/core/SkDrawProcs.h26
-rw-r--r--src/core/SkEdge.cpp484
-rw-r--r--src/core/SkEdge.h93
-rw-r--r--src/core/SkFP.h87
-rw-r--r--src/core/SkFilterProc.cpp303
-rw-r--r--src/core/SkFilterProc.h144
-rw-r--r--src/core/SkFlattenable.cpp263
-rw-r--r--src/core/SkFloat.cpp404
-rw-r--r--src/core/SkFloat.h118
-rw-r--r--src/core/SkFloatBits.cpp205
-rw-r--r--src/core/SkGeometry.cpp1072
-rw-r--r--src/core/SkGeometry.h163
-rw-r--r--src/core/SkGlobals.cpp92
-rw-r--r--src/core/SkGlyphCache.cpp662
-rw-r--r--src/core/SkGlyphCache.h274
-rw-r--r--src/core/SkGraphics.cpp526
-rw-r--r--src/core/SkMask.cpp49
-rw-r--r--src/core/SkMaskFilter.cpp62
-rw-r--r--src/core/SkMath.cpp932
-rw-r--r--src/core/SkMatrix.cpp1690
-rw-r--r--src/core/SkMemory_stdlib.cpp287
-rw-r--r--src/core/SkPackBits.cpp407
-rw-r--r--src/core/SkPaint.cpp1571
-rw-r--r--src/core/SkPath.cpp1438
-rw-r--r--src/core/SkPathEffect.cpp142
-rw-r--r--src/core/SkPathHeap.cpp55
-rw-r--r--src/core/SkPathHeap.h37
-rw-r--r--src/core/SkPathMeasure.cpp598
-rw-r--r--src/core/SkPicture.cpp246
-rw-r--r--src/core/SkPictureFlat.cpp257
-rw-r--r--src/core/SkPictureFlat.h206
-rw-r--r--src/core/SkPicturePlayback.cpp1338
-rw-r--r--src/core/SkPicturePlayback.h168
-rw-r--r--src/core/SkPictureRecord.cpp698
-rw-r--r--src/core/SkPictureRecord.h178
-rw-r--r--src/core/SkPixelRef.cpp129
-rw-r--r--src/core/SkPoint.cpp334
-rw-r--r--src/core/SkProcSpriteBlitter.cpp55
-rw-r--r--src/core/SkPtrRecorder.cpp53
-rw-r--r--src/core/SkRasterizer.cpp62
-rw-r--r--src/core/SkRect.cpp145
-rw-r--r--src/core/SkRefCnt.cpp48
-rw-r--r--src/core/SkRegion.cpp1341
-rw-r--r--src/core/SkRegionPriv.h89
-rw-r--r--src/core/SkRegion_path.cpp480
-rw-r--r--src/core/SkScalerContext.cpp541
-rw-r--r--src/core/SkScan.cpp75
-rw-r--r--src/core/SkScan.h123
-rw-r--r--src/core/SkScanPriv.h48
-rw-r--r--src/core/SkScan_AntiPath.cpp406
-rw-r--r--src/core/SkScan_Antihair.cpp645
-rw-r--r--src/core/SkScan_Hairline.cpp345
-rw-r--r--src/core/SkScan_Path.cpp671
-rw-r--r--src/core/SkShader.cpp284
-rw-r--r--src/core/SkSinTable.h285
-rw-r--r--src/core/SkSpriteBlitter.h55
-rw-r--r--src/core/SkSpriteBlitterTemplate.h91
-rw-r--r--src/core/SkSpriteBlitter_ARGB32.cpp314
-rw-r--r--src/core/SkSpriteBlitter_RGB16.cpp320
-rw-r--r--src/core/SkString.cpp642
-rw-r--r--src/core/SkStroke.cpp645
-rw-r--r--src/core/SkStrokerPriv.cpp275
-rw-r--r--src/core/SkStrokerPriv.h50
-rw-r--r--src/core/SkTSearch.cpp219
-rw-r--r--src/core/SkTSort.h65
-rw-r--r--src/core/SkTemplatesPriv.h84
-rw-r--r--src/core/SkTypeface.cpp64
-rw-r--r--src/core/SkUnPreMultiply.cpp73
-rw-r--r--src/core/SkUtils.cpp574
-rw-r--r--src/core/SkWriter32.cpp170
-rw-r--r--src/core/SkXfermode.cpp978
120 files changed, 42017 insertions, 0 deletions
diff --git a/src/core/ARGB32_Clamp_Bilinear_BitmapShader.h b/src/core/ARGB32_Clamp_Bilinear_BitmapShader.h
new file mode 100644
index 0000000000..c7e23af3cc
--- /dev/null
+++ b/src/core/ARGB32_Clamp_Bilinear_BitmapShader.h
@@ -0,0 +1,171 @@
+
+class ARGB32_Clamp_Bilinear_BitmapShader : public SkBitmapShader {
+public:
+ ARGB32_Clamp_Bilinear_BitmapShader(const SkBitmap& src)
+ : SkBitmapShader(src, true,
+ SkShader::kClamp_TileMode, SkShader::kClamp_TileMode)
+ {}
+
+ virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
+};
+
+SkPMColor sample_bilerp(SkFixed fx, SkFixed fy, unsigned srcMaxX, unsigned srcMaxY,
+ const SkPMColor* srcPixels, int srcRB, const SkFilterPtrProc* proc_table);
+SkPMColor sample_bilerp(SkFixed fx, SkFixed fy, unsigned srcMaxX, unsigned srcMaxY,
+ const SkPMColor* srcPixels, int srcRB, const SkFilterPtrProc* proc_table)
+{
+ int ix = fx >> 16;
+ int iy = fy >> 16;
+
+ const SkPMColor *p00, *p01, *p10, *p11;
+
+ p00 = p01 = ((const SkPMColor*)((const char*)srcPixels
+ + SkClampMax(iy, srcMaxY) * srcRB))
+ + SkClampMax(ix, srcMaxX);
+
+ if ((unsigned)ix < srcMaxX)
+ p01 += 1;
+ p10 = p00;
+ p11 = p01;
+ if ((unsigned)iy < srcMaxY)
+ {
+ p10 = (const SkPMColor*)((const char*)p10 + srcRB);
+ p11 = (const SkPMColor*)((const char*)p11 + srcRB);
+ }
+
+ SkFilterPtrProc proc = SkGetBilinearFilterPtrProc(proc_table, fx, fy);
+ return proc(p00, p01, p10, p11);
+}
+
+static inline SkPMColor sample_bilerpx(SkFixed fx, unsigned srcMaxX, const SkPMColor* srcPixels,
+ int srcRB, const SkFilterPtrProc* proc_table)
+{
+ int ix = fx >> 16;
+
+ const SkPMColor *p00, *p01, *p10, *p11;
+
+ p00 = p01 = srcPixels + SkClampMax(ix, srcMaxX);
+ if ((unsigned)ix < srcMaxX)
+ p01 += 1;
+
+ p10 = (const SkPMColor*)((const char*)p00 + srcRB);
+ p11 = (const SkPMColor*)((const char*)p01 + srcRB);
+
+ SkFilterPtrProc proc = SkGetBilinearFilterPtrXProc(proc_table, fx);
+ return proc(p00, p01, p10, p11);
+}
+
+void ARGB32_Clamp_Bilinear_BitmapShader::shadeSpan(int x, int y, SkPMColor dstC[], int count)
+{
+ SkASSERT(count > 0);
+
+ unsigned srcScale = SkAlpha255To256(this->getPaintAlpha());
+
+ const SkMatrix& inv = this->getTotalInverse();
+ const SkBitmap& srcBitmap = this->getSrcBitmap();
+ unsigned srcMaxX = srcBitmap.width() - 1;
+ unsigned srcMaxY = srcBitmap.height() - 1;
+ unsigned srcRB = srcBitmap.rowBytes();
+
+ const SkFilterPtrProc* proc_table = SkGetBilinearFilterPtrProcTable();
+ const SkPMColor* srcPixels = (const SkPMColor*)srcBitmap.getPixels();
+
+ if (this->getInverseClass() == kPerspective_MatrixClass)
+ {
+ SkPerspIter iter(inv, SkIntToScalar(x) + SK_ScalarHalf,
+ SkIntToScalar(y) + SK_ScalarHalf, count);
+
+ if (256 == srcScale)
+ {
+ while ((count = iter.next()) != 0)
+ {
+ const SkFixed* srcXY = iter.getXY();
+ while (--count >= 0)
+ {
+ SkFixed fx = *srcXY++ - SK_FixedHalf;
+ SkFixed fy = *srcXY++ - SK_FixedHalf;
+ *dstC++ = sample_bilerp(fx, fy, srcMaxX, srcMaxY, srcPixels, srcRB, proc_table);
+ }
+ }
+ }
+ else // scale by srcScale
+ {
+ while ((count = iter.next()) != 0)
+ {
+ const SkFixed* srcXY = iter.getXY();
+ while (--count >= 0)
+ {
+ SkFixed fx = *srcXY++ - SK_FixedHalf;
+ SkFixed fy = *srcXY++ - SK_FixedHalf;
+ SkPMColor c = sample_bilerp(fx, fy, srcMaxX, srcMaxY, srcPixels, srcRB, proc_table);
+ *dstC++ = SkAlphaMulQ(c, srcScale);
+ }
+ }
+ }
+ }
+ else // linear case
+ {
+ SkFixed fx, fy, dx, dy;
+
+ // now init fx, fy, dx, dy
+ {
+ SkPoint srcPt;
+ this->getInverseMapPtProc()(inv, SkIntToScalar(x) + SK_ScalarHalf,
+ SkIntToScalar(y) + SK_ScalarHalf,
+ &srcPt);
+
+ fx = SkScalarToFixed(srcPt.fX) - SK_FixedHalf;
+ fy = SkScalarToFixed(srcPt.fY) - SK_FixedHalf;
+
+ if (this->getInverseClass() == kFixedStepInX_MatrixClass)
+ (void)inv.fixedStepInX(SkIntToScalar(y), &dx, &dy);
+ else
+ {
+ dx = SkScalarToFixed(inv.getScaleX());
+ dy = SkScalarToFixed(inv.getSkewY());
+ }
+ }
+
+ if (dy == 0 && (unsigned)(fy >> 16) < srcMaxY)
+ {
+ srcPixels = (const SkPMColor*)((const char*)srcPixels + (fy >> 16) * srcRB);
+ proc_table = SkGetBilinearFilterPtrProcYTable(proc_table, fy);
+ if (256 == srcScale)
+ {
+ do {
+ *dstC++ = sample_bilerpx(fx, srcMaxX, srcPixels, srcRB, proc_table);
+ fx += dx;
+ } while (--count != 0);
+ }
+ else
+ {
+ do {
+ SkPMColor c = sample_bilerpx(fx, srcMaxX, srcPixels, srcRB, proc_table);
+ *dstC++ = SkAlphaMulQ(c, srcScale);
+ fx += dx;
+ } while (--count != 0);
+ }
+ }
+ else // dy is != 0
+ {
+ if (256 == srcScale)
+ {
+ do {
+ *dstC++ = sample_bilerp(fx, fy, srcMaxX, srcMaxY, srcPixels, srcRB, proc_table);
+ fx += dx;
+ fy += dy;
+ } while (--count != 0);
+ }
+ else
+ {
+ do {
+ SkPMColor c = sample_bilerp(fx, fy, srcMaxX, srcMaxY, srcPixels, srcRB, proc_table);
+ *dstC++ = SkAlphaMulQ(c, srcScale);
+ fx += dx;
+ fy += dy;
+ } while (--count != 0);
+ }
+ }
+ }
+}
+
diff --git a/src/core/Sk64.cpp b/src/core/Sk64.cpp
new file mode 100644
index 0000000000..6013bd7411
--- /dev/null
+++ b/src/core/Sk64.cpp
@@ -0,0 +1,575 @@
+/* libs/corecg/Sk64.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "Sk64.h"
+
+#define shift_left(hi, lo) \
+ hi = (hi << 1) | (lo >> 31); \
+ lo <<= 1
+
+#define shift_left_bits(hi, lo, bits) \
+ SkASSERT((unsigned)(bits) < 31); \
+ hi = (hi << (bits)) | (lo >> (32 - (bits))); \
+ lo <<= (bits)
+
+//////////////////////////////////////////////////////////////////////
+
+int Sk64::getClzAbs() const
+{
+ int32_t hi = fHi;
+ uint32_t lo = fLo;
+
+ // get abs
+ if (hi < 0)
+ {
+ hi = -hi - Sk32ToBool(lo);
+ lo = 0 - lo;
+ }
+ return hi ? SkCLZ(hi) : SkCLZ(lo) + 32;
+}
+
+void Sk64::shiftLeft(unsigned bits)
+{
+ SkASSERT(bits <= 63);
+ if (bits == 0)
+ return;
+
+ if (bits >= 32)
+ {
+ fHi = fLo << (bits - 32);
+ fLo = 0;
+ }
+ else
+ {
+ fHi = (fHi << bits) | (fLo >> (32 - bits));
+ fLo <<= bits;
+ }
+}
+
+int32_t Sk64::getShiftRight(unsigned bits) const
+{
+ SkASSERT(bits <= 63);
+
+ if (bits == 0)
+ return fLo;
+
+ if (bits >= 32)
+ return fHi >> (bits - 32);
+ else
+ {
+#ifdef SK_DEBUG
+ int32_t tmp = fHi >> bits;
+ SkASSERT(tmp == 0 || tmp == -1);
+#endif
+ return (fHi << (32 - bits)) | (fLo >> bits);
+ }
+}
+
+void Sk64::shiftRight(unsigned bits)
+{
+ SkASSERT(bits <= 63);
+ if (bits == 0)
+ return;
+
+ if (bits >= 32)
+ {
+ fLo = fHi >> (bits - 32);
+ fHi >>= 31;
+ }
+ else
+ {
+ fLo = (fHi << (32 - bits)) | (fLo >> bits);
+ fHi >>= bits;
+ }
+}
+
+void Sk64::roundRight(unsigned bits)
+{
+ SkASSERT(bits <= 63);
+ if (bits)
+ {
+ Sk64 one;
+ one.set(1);
+ one.shiftLeft(bits - 1);
+ this->add(one);
+ this->shiftRight(bits);
+ }
+}
+
+int Sk64::shiftToMake32() const
+{
+ int32_t hi = fHi;
+ uint32_t lo = fLo;
+
+ if (hi < 0) // make it positive
+ {
+ hi = -hi - Sk32ToBool(lo);
+ lo = 0 - lo;
+ }
+
+ if (hi == 0)
+ return lo >> 31;
+ else
+ return 33 - SkCLZ(hi);
+}
+
+void Sk64::negate()
+{
+ fHi = -fHi - Sk32ToBool(fLo);
+ fLo = 0 - fLo;
+}
+
+void Sk64::abs()
+{
+ if (fHi < 0)
+ {
+ fHi = -fHi - Sk32ToBool(fLo);
+ fLo = 0 - fLo;
+ }
+}
+
+////////////////////////////////////////////////////////////////
+
+static inline int32_t round_right_16(int32_t hi, uint32_t lo)
+{
+ uint32_t sum = lo + (1 << 15);
+ hi += (sum < lo);
+ return (hi << 16) | (sum >> 16);
+}
+
+SkBool Sk64::isFixed() const
+{
+ Sk64 tmp = *this;
+ tmp.roundRight(16);
+ return tmp.is32();
+}
+
+SkFract Sk64::getFract() const
+{
+ Sk64 tmp = *this;
+ tmp.roundRight(30);
+ return tmp.get32();
+}
+
+void Sk64::sub(const Sk64& a)
+{
+ fHi = fHi - a.fHi - (fLo < a.fLo);
+ fLo = fLo - a.fLo;
+}
+
+void Sk64::rsub(const Sk64& a)
+{
+ fHi = a.fHi - fHi - (a.fLo < fLo);
+ fLo = a.fLo - fLo;
+}
+
+void Sk64::setMul(int32_t a, int32_t b)
+{
+ int sa = a >> 31;
+ int sb = b >> 31;
+ // now make them positive
+ a = (a ^ sa) - sa;
+ b = (b ^ sb) - sb;
+
+ uint32_t ah = a >> 16;
+ uint32_t al = a & 0xFFFF;
+ uint32_t bh = b >> 16;
+ uint32_t bl = b & 0xFFFF;
+
+ uint32_t A = ah * bh;
+ uint32_t B = ah * bl + al * bh;
+ uint32_t C = al * bl;
+
+ /* [ A ]
+ [ B ]
+ [ C ]
+ */
+ fLo = C + (B << 16);
+ fHi = A + (B >>16) + (fLo < C);
+
+ if (sa != sb)
+ this->negate();
+}
+
+void Sk64::div(int32_t denom, DivOptions option)
+{
+ SkASSERT(denom);
+
+ int32_t hi = fHi;
+ uint32_t lo = fLo;
+ int sign = denom ^ hi;
+
+ denom = SkAbs32(denom);
+ if (hi < 0)
+ {
+ hi = -hi - Sk32ToBool(lo);
+ lo = 0 - lo;
+ }
+
+ if (option == kRound_DivOption) // add denom/2
+ {
+ uint32_t newLo = lo + (denom >> 1);
+ hi += (newLo < lo);
+ lo = newLo;
+ }
+
+ if (hi == 0) // fast-case
+ {
+ if (lo < (uint32_t)denom)
+ this->set(0, 0);
+ else
+ {
+ this->set(0, lo / denom);
+ if (sign < 0)
+ this->negate();
+ }
+ return;
+ }
+
+ int bits;
+
+ {
+ int dbits = SkCLZ(denom);
+ int nbits = SkCLZ(hi);
+
+ bits = 32 + dbits - nbits;
+ SkASSERT(bits <= 63);
+ if (bits <= 0)
+ {
+ this->set(0, 0);
+ return;
+ }
+ denom <<= (dbits - 1);
+ shift_left_bits(hi, lo, nbits - 1);
+ }
+
+ int32_t rhi = 0;
+ uint32_t rlo = 0;
+
+ do {
+ shift_left(rhi, rlo);
+#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
+ if ((uint32_t)denom <= (uint32_t)hi)
+ {
+ hi -= denom;
+ rlo |= 1;
+ }
+#else
+ int32_t diff = (denom - hi - 1) >> 31;
+ hi -= denom & diff;
+ rlo -= diff;
+#endif
+ shift_left(hi, lo);
+ } while (--bits >= 0);
+ SkASSERT(rhi >= 0);
+
+ fHi = rhi;
+ fLo = rlo;
+ if (sign < 0)
+ this->negate();
+}
+
+#define shift_left_2(a, b, c) \
+ a = (a << 2) | (b >> 30); \
+ b = (b << 2) | (c >> 30); \
+ c <<= 2
+
+int32_t Sk64::getSqrt() const
+{
+ SkASSERT(!this->isNeg());
+
+ uint32_t hi = fHi;
+ uint32_t lo = fLo;
+ uint32_t sqr = 0;
+ uint32_t root = 0;
+ int count = 31;
+
+ do {
+ root <<= 1;
+ shift_left_2(sqr, hi, lo);
+
+ uint32_t testDiv = (root << 1) + 1;
+ if (sqr >= testDiv)
+ {
+ sqr -= testDiv;
+ root++;
+ }
+ } while (--count >= 0);
+ SkASSERT((int32_t)root >= 0);
+
+ return root;
+}
+
+#ifdef SkLONGLONG
+ SkLONGLONG Sk64::getLongLong() const
+ {
+ SkLONGLONG value = fHi;
+ value <<= 32;
+ return value | fLo;
+ }
+#endif
+
+SkFixed Sk64::getFixedDiv(const Sk64& denom) const
+{
+ Sk64 N = *this;
+ Sk64 D = denom;
+ int32_t sign = SkExtractSign(N.fHi ^ D.fHi);
+ SkFixed result;
+
+ N.abs();
+ D.abs();
+
+ // need to knock D down to just 31 bits
+ // either by rounding it to the right, or shifting N to the left
+ // then we can just call 64/32 div
+
+ int nclz = N.fHi ? SkCLZ(N.fHi) : 32;
+ int dclz = D.fHi ? SkCLZ(D.fHi) : (33 - (D.fLo >> 31));
+
+ int shiftN = nclz - 1;
+ SkASSERT(shiftN >= 0);
+ int shiftD = 33 - dclz;
+ SkASSERT(shiftD >= 0);
+
+ if (shiftD + shiftN < 16)
+ shiftD = 16 - shiftN;
+ else
+ shiftN = 16 - shiftD;
+
+ D.roundRight(shiftD);
+ if (D.isZero())
+ result = SK_MaxS32;
+ else
+ {
+ if (shiftN >= 0)
+ N.shiftLeft(shiftN);
+ else
+ N.roundRight(-shiftN);
+ N.div(D.get32(), Sk64::kTrunc_DivOption);
+ if (N.is32())
+ result = N.get32();
+ else
+ result = SK_MaxS32;
+ }
+ return SkApplySign(result, sign);
+}
+
+///////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+#include "SkRandom.h"
+#include <math.h>
+
+#ifdef SK_SUPPORT_UNITTEST
+struct BoolTable {
+ int8_t zero, pos, neg, toBool, sign;
+};
+
+static void bool_table_test(const Sk64& a, const BoolTable& table)
+{
+ SkASSERT(a.isZero() != a.nonZero());
+
+ SkASSERT(!a.isZero() == !table.zero);
+ SkASSERT(!a.isPos() == !table.pos);
+ SkASSERT(!a.isNeg() == !table.neg);
+ SkASSERT(a.sign() == table.sign);
+}
+
+#ifdef SkLONGLONG
+ static SkLONGLONG asLL(const Sk64& a)
+ {
+ return ((SkLONGLONG)a.fHi << 32) | a.fLo;
+ }
+#endif
+#endif
+
+void Sk64::UnitTest()
+{
+#ifdef SK_SUPPORT_UNITTEST
+ enum BoolTests {
+ kZero_BoolTest,
+ kPos_BoolTest,
+ kNeg_BoolTest
+ };
+ static const BoolTable gBoolTable[] = {
+ { 1, 0, 0, 0, 0 },
+ { 0, 1, 0, 1, 1 },
+ { 0, 0, 1, 1, -1 }
+ };
+
+ Sk64 a, b, c;
+
+ a.fHi = a.fLo = 0;
+ b.set(0);
+ c.setZero();
+ SkASSERT(a == b);
+ SkASSERT(a == c);
+ bool_table_test(a, gBoolTable[kZero_BoolTest]);
+
+ a.fHi = 0; a.fLo = 5;
+ b.set(5);
+ SkASSERT(a == b);
+ SkASSERT(a.is32() && a.get32() == 5 && !a.is64());
+ bool_table_test(a, gBoolTable[kPos_BoolTest]);
+
+ a.fHi = -1; a.fLo = (uint32_t)-5;
+ b.set(-5);
+ SkASSERT(a == b);
+ SkASSERT(a.is32() && a.get32() == -5 && !a.is64());
+ bool_table_test(a, gBoolTable[kNeg_BoolTest]);
+
+ a.setZero();
+ b.set(6);
+ c.set(-6);
+ SkASSERT(a != b && b != c && a != c);
+ SkASSERT(!(a == b) && !(a == b) && !(a == b));
+ SkASSERT(a < b && b > a && a <= b && b >= a);
+ SkASSERT(c < a && a > c && c <= a && a >= c);
+ SkASSERT(c < b && b > c && c <= b && b >= c);
+
+ // Now test add/sub
+
+ SkRandom rand;
+ int i;
+
+ for (i = 0; i < 1000; i++)
+ {
+ int aa = rand.nextS() >> 1;
+ int bb = rand.nextS() >> 1;
+ a.set(aa);
+ b.set(bb);
+ SkASSERT(a.get32() == aa && b.get32() == bb);
+ c = a; c.add(bb);
+ SkASSERT(c.get32() == aa + bb);
+ c = a; c.add(-bb);
+ SkASSERT(c.get32() == aa - bb);
+ c = a; c.add(b);
+ SkASSERT(c.get32() == aa + bb);
+ c = a; c.sub(b);
+ SkASSERT(c.get32() == aa - bb);
+ }
+
+#ifdef SkLONGLONG
+ for (i = 0; i < 1000; i++)
+ {
+ rand.next64(&a); //a.fHi >>= 1; // avoid overflow
+ rand.next64(&b); //b.fHi >>= 1; // avoid overflow
+
+ if (!(i & 3)) // want to explicitly test these cases
+ {
+ a.fLo = 0;
+ b.fLo = 0;
+ }
+ else if (!(i & 7)) // want to explicitly test these cases
+ {
+ a.fHi = 0;
+ b.fHi = 0;
+ }
+
+ SkLONGLONG aa = asLL(a);
+ SkLONGLONG bb = asLL(b);
+
+ SkASSERT((a < b) == (aa < bb));
+ SkASSERT((a <= b) == (aa <= bb));
+ SkASSERT((a > b) == (aa > bb));
+ SkASSERT((a >= b) == (aa >= bb));
+ SkASSERT((a == b) == (aa == bb));
+ SkASSERT((a != b) == (aa != bb));
+
+ c = a; c.add(b);
+ SkASSERT(asLL(c) == aa + bb);
+ c = a; c.sub(b);
+ SkASSERT(asLL(c) == aa - bb);
+ c = a; c.rsub(b);
+ SkASSERT(asLL(c) == bb - aa);
+ c = a; c.negate();
+ SkASSERT(asLL(c) == -aa);
+
+ int bits = rand.nextU() & 63;
+ c = a; c.shiftLeft(bits);
+ SkASSERT(asLL(c) == (aa << bits));
+ c = a; c.shiftRight(bits);
+ SkASSERT(asLL(c) == (aa >> bits));
+ c = a; c.roundRight(bits);
+
+ SkLONGLONG tmp;
+
+ tmp = aa;
+ if (bits > 0)
+ tmp += (SkLONGLONG)1 << (bits - 1);
+ SkASSERT(asLL(c) == (tmp >> bits));
+
+ c.setMul(a.fHi, b.fHi);
+ tmp = (SkLONGLONG)a.fHi * b.fHi;
+ SkASSERT(asLL(c) == tmp);
+ }
+
+
+ for (i = 0; i < 100000; i++)
+ {
+ Sk64 wide;
+ int32_t denom = rand.nextS();
+
+ while (denom == 0)
+ denom = rand.nextS();
+ wide.setMul(rand.nextS(), rand.nextS());
+ SkLONGLONG check = wide.getLongLong();
+
+ wide.div(denom, Sk64::kTrunc_DivOption);
+ check /= denom;
+ SkLONGLONG w = wide.getLongLong();
+
+ SkASSERT(check == w);
+
+#ifdef SK_CAN_USE_FLOATx
+ wide.setMul(rand.nextS(), rand.nextS());
+ wide.abs();
+ denom = wide.getSqrt();
+ int32_t ck = (int32_t)sqrt((double)wide.getLongLong());
+ int diff = denom - ck;
+ SkASSERT(SkAbs32(diff) <= 1);
+
+ wide.setMul(rand.nextS(), rand.nextS());
+ Sk64 dwide;
+ dwide.setMul(rand.nextS(), rand.nextS());
+ SkFixed fixdiv = wide.getFixedDiv(dwide);
+ double dnumer = (double)wide.getLongLong();
+ double ddenom = (double)dwide.getLongLong();
+ double ddiv = dnumer / ddenom;
+ SkFixed dfixdiv;
+ if (ddiv >= (double)SK_MaxS32 / (double)SK_Fixed1)
+ dfixdiv = SK_MaxS32;
+ else if (ddiv <= -(double)SK_MaxS32 / (double)SK_Fixed1)
+ dfixdiv = SK_MinS32;
+ else
+ dfixdiv = SkFloatToFixed(dnumer / ddenom);
+ diff = fixdiv - dfixdiv;
+
+ if (SkAbs32(diff) > 1) {
+ SkDebugf(" %d === numer %g denom %g div %g xdiv %x fxdiv %x\n",
+ i, dnumer, ddenom, ddiv, dfixdiv, fixdiv);
+ }
+// SkASSERT(SkAbs32(diff) <= 1);
+#endif
+ }
+#endif
+#endif
+}
+
+#endif
+
diff --git a/src/core/SkAlphaRuns.cpp b/src/core/SkAlphaRuns.cpp
new file mode 100644
index 0000000000..46b0206ee8
--- /dev/null
+++ b/src/core/SkAlphaRuns.cpp
@@ -0,0 +1,185 @@
+/* libs/graphics/sgl/SkAlphaRuns.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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(int16_t runs[], uint8_t alpha[], int x, int count)
+{
+ SkASSERT(count > 0 && x >= 0);
+
+// SkAlphaRuns::BreakAt(runs, alpha, x);
+// SkAlphaRuns::BreakAt(&runs[x], &alpha[x], count);
+
+ int16_t* next_runs = runs + x;
+ uint8_t* 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);
+
+ int16_t* runs = fRuns;
+ uint8_t* 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 int16_t* runs = fRuns;
+ const uint8_t* alpha = fAlpha;
+
+ while (*runs)
+ {
+ SkASSERT(*alpha <= max);
+ alpha += *runs;
+ runs += *runs;
+ }
+ }
+
+ void SkAlphaRuns::dump() const
+ {
+ const int16_t* runs = fRuns;
+ const uint8_t* 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 int16_t* runs = fRuns;
+
+ while (*runs)
+ {
+ SkASSERT(*runs > 0);
+ count += *runs;
+ SkASSERT(count <= fWidth);
+ runs += *runs;
+ }
+ SkASSERT(count == fWidth);
+ }
+#endif
+
diff --git a/src/core/SkAntiRun.h b/src/core/SkAntiRun.h
new file mode 100644
index 0000000000..12930e6675
--- /dev/null
+++ b/src/core/SkAntiRun.h
@@ -0,0 +1,185 @@
+/* libs/graphics/sgl/SkAntiRun.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#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 uint8_t 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)
+ {
+ int16_t 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);
+ }
+
+ uint8_t getStartAlpha() const { return fStartAlpha; }
+ int getMiddleCount() const { return fMiddleCount; }
+ uint8_t getStopAlpha() const { return fStopAlpha; }
+
+private:
+ uint8_t fStartAlpha, fStopAlpha;
+ int fMiddleCount;
+
+ enum {
+ kMask = (1 << kShift) - 1,
+ kMax = (1 << (8 - kShift)) - 1
+ };
+};
+#endif
+
+////////////////////////////////////////////////////////////////////////////////////
+
+class SkAlphaRuns {
+public:
+ int16_t* fRuns;
+ uint8_t* 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(int16_t runs[], uint8_t alpha[], int x, int count);
+ static void BreakAt(int16_t runs[], uint8_t 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/src/core/SkAutoKern.h b/src/core/SkAutoKern.h
new file mode 100644
index 0000000000..023cb6b2fe
--- /dev/null
+++ b/src/core/SkAutoKern.h
@@ -0,0 +1,62 @@
+/* libs/graphics/sgl/SkAutoKern.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef SkAutoKern_DEFINED
+#define SkAutoKern_DEFINED
+
+#include "SkScalerContext.h"
+
+#define SkAutoKern_AdjustF(prev, next) (((next) - (prev) + 32) >> 6 << 16)
+#define SkAutoKern_AdjustS(prev, next) SkIntToScalar(((next) - (prev) + 32) >> 6)
+
+/* this is a helper class to perform auto-kerning
+ * the adjust() method returns a SkFixed corresponding
+ * to a +1/0/-1 pixel adjustment
+ */
+
+class SkAutoKern {
+public:
+ SkAutoKern() : fPrevRsbDelta(0) {}
+
+ SkFixed adjust(const SkGlyph& glyph)
+ {
+// if (SkAbs32(glyph.fLsbDelta) > 47 || SkAbs32(glyph.fRsbDelta) > 47)
+// printf("------- %d> L %d R %d\n", glyph.f_GlyphID, glyph.fLsbDelta, glyph.fRsbDelta);
+
+#if 0
+ int distort = fPrevRsbDelta - glyph.fLsbDelta;
+
+ fPrevRsbDelta = glyph.fRsbDelta;
+
+ if (distort >= 32)
+ return -SK_Fixed1;
+ else if (distort < -32)
+ return +SK_Fixed1;
+ else
+ return 0;
+#else
+ SkFixed adjust = SkAutoKern_AdjustF(fPrevRsbDelta, glyph.fLsbDelta);
+ fPrevRsbDelta = glyph.fRsbDelta;
+ return adjust;
+#endif
+ }
+private:
+ int fPrevRsbDelta;
+};
+
+#endif
+
diff --git a/src/core/SkBitmap.cpp b/src/core/SkBitmap.cpp
new file mode 100644
index 0000000000..5ca3601a42
--- /dev/null
+++ b/src/core/SkBitmap.cpp
@@ -0,0 +1,1255 @@
+/*
+ * Copyright (C) 2006-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkBitmap.h"
+#include "SkColorPriv.h"
+#include "SkDither.h"
+#include "SkFlattenable.h"
+#include "SkMallocPixelRef.h"
+#include "SkMask.h"
+#include "SkPixelRef.h"
+#include "SkThread.h"
+#include "SkUtils.h"
+#include "SkPackBits.h"
+#include <new>
+
+#ifdef SK_SUPPORT_MIPMAP
+struct MipLevel {
+ void* fPixels;
+ uint32_t fRowBytes;
+ uint16_t fWidth, fHeight;
+};
+
+struct SkBitmap::MipMap : SkNoncopyable {
+ int32_t fRefCnt;
+ int fLevelCount;
+// MipLevel fLevel[fLevelCount];
+// Pixels[]
+
+ static MipMap* Alloc(int levelCount, size_t pixelSize) {
+ MipMap* mm = (MipMap*)sk_malloc_throw(sizeof(MipMap) +
+ levelCount * sizeof(MipLevel) +
+ pixelSize);
+ mm->fRefCnt = 1;
+ mm->fLevelCount = levelCount;
+ return mm;
+ }
+
+ const MipLevel* levels() const { return (const MipLevel*)(this + 1); }
+ MipLevel* levels() { return (MipLevel*)(this + 1); }
+
+ const void* pixels() const { return levels() + fLevelCount; }
+ void* pixels() { return levels() + fLevelCount; }
+
+ void safeRef() {
+ if (this) {
+ SkASSERT(fRefCnt > 0);
+ sk_atomic_inc(&fRefCnt);
+ }
+ }
+ void safeUnref() {
+ if (this) {
+ SkASSERT(fRefCnt > 0);
+ if (sk_atomic_dec(&fRefCnt) == 1) {
+ sk_free(this);
+ }
+ }
+ }
+};
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+SkBitmap::SkBitmap() {
+ bzero(this, sizeof(*this));
+}
+
+SkBitmap::SkBitmap(const SkBitmap& src) {
+ SkDEBUGCODE(src.validate();)
+ bzero(this, sizeof(*this));
+ *this = src;
+ SkDEBUGCODE(this->validate();)
+}
+
+SkBitmap::~SkBitmap() {
+ SkDEBUGCODE(this->validate();)
+ this->freePixels();
+}
+
+SkBitmap& SkBitmap::operator=(const SkBitmap& src) {
+ if (this != &src) {
+ this->freePixels();
+ memcpy(this, &src, sizeof(src));
+
+ // inc src reference counts
+ src.fPixelRef->safeRef();
+#ifdef SK_SUPPORT_MIPMAP
+ src.fMipMap->safeRef();
+#endif
+
+ // we reset our locks if we get blown away
+ fPixelLockCount = 0;
+
+ /* The src could be in 3 states
+ 1. no pixelref, in which case we just copy/ref the pixels/ctable
+ 2. unlocked pixelref, pixels/ctable should be null
+ 3. locked pixelref, we should lock the ref again ourselves
+ */
+ if (NULL == fPixelRef) {
+ // leave fPixels as it is
+ fColorTable->safeRef(); // ref the user's ctable if present
+ } else { // we have a pixelref, so pixels/ctable reflect it
+ // ignore the values from the memcpy
+ fPixels = NULL;
+ fColorTable = NULL;
+ }
+ }
+
+ SkDEBUGCODE(this->validate();)
+ return *this;
+}
+
+void SkBitmap::swap(SkBitmap& other) {
+ SkTSwap<SkColorTable*>(fColorTable, other.fColorTable);
+ SkTSwap<SkPixelRef*>(fPixelRef, other.fPixelRef);
+ SkTSwap<size_t>(fPixelRefOffset, other.fPixelRefOffset);
+ SkTSwap<int>(fPixelLockCount, other.fPixelLockCount);
+#ifdef SK_SUPPORT_MIPMAP
+ SkTSwap<MipMap*>(fMipMap, other.fMipMap);
+#endif
+ SkTSwap<void*>(fPixels, other.fPixels);
+ SkTSwap<uint16_t>(fWidth, other.fWidth);
+ SkTSwap<uint16_t>(fHeight, other.fHeight);
+ SkTSwap<uint32_t>(fRowBytes, other.fRowBytes);
+ SkTSwap<uint8_t>(fConfig, other.fConfig);
+ SkTSwap<uint8_t>(fFlags, other.fFlags);
+ SkTSwap<uint8_t>(fBytesPerPixel, other.fBytesPerPixel);
+
+ SkDEBUGCODE(this->validate();)
+}
+
+void SkBitmap::reset() {
+ this->freePixels();
+ bzero(this, sizeof(*this));
+}
+
+int SkBitmap::ComputeBytesPerPixel(SkBitmap::Config config) {
+ int bpp;
+ switch (config) {
+ case kNo_Config:
+ case kA1_Config:
+ bpp = 0; // not applicable
+ break;
+ case kRLE_Index8_Config:
+ case kA8_Config:
+ case kIndex8_Config:
+ bpp = 1;
+ break;
+ case kRGB_565_Config:
+ case kARGB_4444_Config:
+ bpp = 2;
+ break;
+ case kARGB_8888_Config:
+ bpp = 4;
+ break;
+ default:
+ SkASSERT(!"unknown config");
+ bpp = 0; // error
+ break;
+ }
+ return bpp;
+}
+
+int SkBitmap::ComputeRowBytes(Config c, int width) {
+ int rowBytes = 0;
+
+ switch (c) {
+ case kNo_Config:
+ case kRLE_Index8_Config:
+ // assume that the bitmap has no pixels to draw to
+ rowBytes = 0;
+ break;
+ case kA1_Config:
+ rowBytes = (width + 7) >> 3;
+ break;
+ case kA8_Config:
+ case kIndex8_Config:
+ rowBytes = width;
+ break;
+ case kRGB_565_Config:
+ case kARGB_4444_Config:
+ rowBytes = width << 1;
+ break;
+ case kARGB_8888_Config:
+ rowBytes = width << 2;
+ break;
+ default:
+ SkASSERT(!"unknown config");
+ break;
+ }
+ return rowBytes;
+}
+
+Sk64 SkBitmap::ComputeSize64(Config c, int width, int height) {
+ Sk64 size;
+ size.setMul(SkBitmap::ComputeRowBytes(c, width), height);
+ return size;
+}
+
+size_t SkBitmap::ComputeSize(Config c, int width, int height) {
+ Sk64 size = SkBitmap::ComputeSize64(c, width, height);
+ if (size.isNeg() || !size.is32()) {
+ return 0;
+ }
+ return size.get32();
+}
+
+void SkBitmap::setConfig(Config c, int width, int height, int rowBytes) {
+ this->freePixels();
+
+ if (rowBytes == 0) {
+ rowBytes = SkBitmap::ComputeRowBytes(c, width);
+ }
+ fConfig = SkToU8(c);
+ fWidth = SkToU16(width);
+ fHeight = SkToU16(height);
+ fRowBytes = rowBytes;
+
+ fBytesPerPixel = (uint8_t)ComputeBytesPerPixel(c);
+
+ SkDEBUGCODE(this->validate();)
+}
+
+void SkBitmap::updatePixelsFromRef() const {
+ if (NULL != fPixelRef) {
+ if (fPixelLockCount > 0) {
+ SkASSERT(fPixelRef->getLockCount() > 0);
+
+ void* p = fPixelRef->pixels();
+ if (NULL != p) {
+ p = (char*)p + fPixelRefOffset;
+ }
+ fPixels = p;
+ SkRefCnt_SafeAssign(fColorTable, fPixelRef->colorTable());
+ } else {
+ SkASSERT(0 == fPixelLockCount);
+ fPixels = NULL;
+ fColorTable->safeUnref();
+ fColorTable = NULL;
+ }
+ }
+}
+
+SkPixelRef* SkBitmap::setPixelRef(SkPixelRef* pr, size_t offset) {
+ // do this first, we that we never have a non-zero offset with a null ref
+ if (NULL == pr) {
+ offset = 0;
+ }
+
+ if (fPixelRef != pr || fPixelRefOffset != offset) {
+ if (fPixelRef != pr) {
+ this->freePixels();
+ SkASSERT(NULL == fPixelRef);
+
+ pr->safeRef();
+ fPixelRef = pr;
+ }
+ fPixelRefOffset = offset;
+ this->updatePixelsFromRef();
+ }
+
+ SkDEBUGCODE(this->validate();)
+ return pr;
+}
+
+void SkBitmap::lockPixels() const {
+ if (NULL != fPixelRef && 1 == ++fPixelLockCount) {
+ fPixelRef->lockPixels();
+ this->updatePixelsFromRef();
+ }
+ SkDEBUGCODE(this->validate();)
+}
+
+void SkBitmap::unlockPixels() const {
+ SkASSERT(NULL == fPixelRef || fPixelLockCount > 0);
+
+ if (NULL != fPixelRef && 0 == --fPixelLockCount) {
+ fPixelRef->unlockPixels();
+ this->updatePixelsFromRef();
+ }
+ SkDEBUGCODE(this->validate();)
+}
+
+void SkBitmap::setPixels(void* p, SkColorTable* ctable) {
+ this->freePixels();
+ fPixels = p;
+ SkRefCnt_SafeAssign(fColorTable, ctable);
+
+ SkDEBUGCODE(this->validate();)
+}
+
+bool SkBitmap::allocPixels(Allocator* allocator, SkColorTable* ctable) {
+ HeapAllocator stdalloc;
+
+ if (NULL == allocator) {
+ allocator = &stdalloc;
+ }
+ return allocator->allocPixelRef(this, ctable);
+}
+
+void SkBitmap::freePixels() {
+ // if we're gonna free the pixels, we certainly need to free the mipmap
+ this->freeMipMap();
+
+ fColorTable->safeUnref();
+ fColorTable = NULL;
+
+ if (NULL != fPixelRef) {
+ if (fPixelLockCount > 0) {
+ fPixelRef->unlockPixels();
+ }
+ fPixelRef->unref();
+ fPixelRef = NULL;
+ fPixelRefOffset = 0;
+ }
+ fPixelLockCount = 0;
+ fPixels = NULL;
+}
+
+void SkBitmap::freeMipMap() {
+#ifdef SK_SUPPORT_MIPMAP
+ fMipMap->safeUnref();
+ fMipMap = NULL;
+#endif
+}
+
+uint32_t SkBitmap::getGenerationID() const {
+ return fPixelRef ? fPixelRef->getGenerationID() : 0;
+}
+
+void SkBitmap::notifyPixelsChanged() const {
+ if (fPixelRef) {
+ fPixelRef->notifyPixelsChanged();
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkMallocPixelRef::SkMallocPixelRef(void* storage, size_t size,
+ SkColorTable* ctable) {
+ SkASSERT(storage);
+ fStorage = storage;
+ fSize = size;
+ fCTable = ctable;
+ ctable->safeRef();
+}
+
+SkMallocPixelRef::~SkMallocPixelRef() {
+ fCTable->safeUnref();
+ sk_free(fStorage);
+}
+
+void* SkMallocPixelRef::onLockPixels(SkColorTable** ct) {
+ *ct = fCTable;
+ return fStorage;
+}
+
+void SkMallocPixelRef::onUnlockPixels() {
+ // nothing to do
+}
+
+void SkMallocPixelRef::flatten(SkFlattenableWriteBuffer& buffer) const {
+ this->INHERITED::flatten(buffer);
+
+ buffer.write32(fSize);
+ buffer.writePad(fStorage, fSize);
+ if (fCTable) {
+ buffer.writeBool(true);
+ fCTable->flatten(buffer);
+ } else {
+ buffer.writeBool(false);
+ }
+}
+
+SkMallocPixelRef::SkMallocPixelRef(SkFlattenableReadBuffer& buffer) : INHERITED(buffer, NULL) {
+ fSize = buffer.readU32();
+ fStorage = sk_malloc_throw(fSize);
+ buffer.read(fStorage, fSize);
+ if (buffer.readBool()) {
+ fCTable = SkNEW_ARGS(SkColorTable, (buffer));
+ } else {
+ fCTable = NULL;
+ }
+}
+
+static SkPixelRef::Registrar reg("SkMallocPixelRef",
+ SkMallocPixelRef::Create);
+
+/** We explicitly use the same allocator for our pixels that SkMask does,
+ so that we can freely assign memory allocated by one class to the other.
+ */
+bool SkBitmap::HeapAllocator::allocPixelRef(SkBitmap* dst,
+ SkColorTable* ctable) {
+ Sk64 size = dst->getSize64();
+ if (size.isNeg() || !size.is32()) {
+ return false;
+ }
+
+ void* addr = sk_malloc_flags(size.get32(), 0); // returns NULL on failure
+ if (NULL == addr) {
+ return false;
+ }
+
+ dst->setPixelRef(new SkMallocPixelRef(addr, size.get32(), ctable))->unref();
+ // since we're already allocated, we lockPixels right away
+ dst->lockPixels();
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkBitmap::isOpaque() const {
+ switch (fConfig) {
+ case kNo_Config:
+ return true;
+
+ case kA1_Config:
+ case kA8_Config:
+ case kARGB_4444_Config:
+ case kARGB_8888_Config:
+ return (fFlags & kImageIsOpaque_Flag) != 0;
+
+ case kIndex8_Config:
+ case kRLE_Index8_Config: {
+ uint32_t flags = 0;
+
+ this->lockPixels();
+ // if lockPixels failed, we may not have a ctable ptr
+ if (fColorTable) {
+ flags = fColorTable->getFlags();
+ }
+ this->unlockPixels();
+
+ return (flags & SkColorTable::kColorsAreOpaque_Flag) != 0;
+ }
+
+ case kRGB_565_Config:
+ return true;
+
+ default:
+ SkASSERT(!"unknown bitmap config pased to isOpaque");
+ 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::getAddr(int x, int y) const {
+ SkASSERT((unsigned)x < (unsigned)this->width());
+ SkASSERT((unsigned)y < (unsigned)this->height());
+
+ char* base = (char*)this->getPixels();
+ if (base) {
+ base += y * this->rowBytes();
+ switch (this->config()) {
+ case SkBitmap::kARGB_8888_Config:
+ base += x << 2;
+ break;
+ case SkBitmap::kARGB_4444_Config:
+ case SkBitmap::kRGB_565_Config:
+ base += x << 1;
+ break;
+ case SkBitmap::kA8_Config:
+ case SkBitmap::kIndex8_Config:
+ base += x;
+ break;
+ case SkBitmap::kA1_Config:
+ base += x >> 3;
+ break;
+ case kRLE_Index8_Config:
+ SkASSERT(!"Can't return addr for kRLE_Index8_Config");
+ base = NULL;
+ break;
+ default:
+ SkASSERT(!"Can't return addr for config");
+ base = NULL;
+ break;
+ }
+ }
+ return base;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+void SkBitmap::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) const {
+ SkDEBUGCODE(this->validate();)
+
+ if (0 == fWidth || 0 == fHeight ||
+ kNo_Config == fConfig || kIndex8_Config == fConfig) {
+ return;
+ }
+
+ SkAutoLockPixels alp(*this);
+ // perform this check after the lock call
+ if (!this->readyToDraw()) {
+ return;
+ }
+
+ int height = fHeight;
+ const int width = fWidth;
+ const int rowBytes = fRowBytes;
+
+ // make rgb premultiplied
+ if (255 != a) {
+ r = SkAlphaMul(r, a);
+ g = SkAlphaMul(g, a);
+ b = SkAlphaMul(b, a);
+ }
+
+ switch (fConfig) {
+ case kA1_Config: {
+ uint8_t* p = (uint8_t*)fPixels;
+ const int count = (width + 7) >> 3;
+ a = (a >> 7) ? 0xFF : 0;
+ SkASSERT(count <= rowBytes);
+ while (--height >= 0) {
+ memset(p, a, count);
+ p += rowBytes;
+ }
+ break;
+ }
+ case kA8_Config: {
+ uint8_t* p = (uint8_t*)fPixels;
+ while (--height >= 0) {
+ memset(p, a, width);
+ p += rowBytes;
+ }
+ break;
+ }
+ case kARGB_4444_Config:
+ case kRGB_565_Config: {
+ uint16_t* p = (uint16_t*)fPixels;
+ uint16_t v;
+
+ if (kARGB_4444_Config == fConfig) {
+ v = SkPackARGB4444(a >> 4, r >> 4, g >> 4, b >> 4);
+ } else { // kRGB_565_Config
+ v = SkPackRGB16(r >> (8 - SK_R16_BITS), g >> (8 - SK_G16_BITS),
+ b >> (8 - SK_B16_BITS));
+ }
+ while (--height >= 0) {
+ sk_memset16(p, v, width);
+ p = (uint16_t*)((char*)p + rowBytes);
+ }
+ 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, width);
+ p = (uint32_t*)((char*)p + rowBytes);
+ }
+ break;
+ }
+ }
+
+ this->notifyPixelsChanged();
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////
+
+#define SUB_OFFSET_FAILURE ((size_t)-1)
+
+static size_t getSubOffset(const SkBitmap& bm, int x, int y) {
+ SkASSERT((unsigned)x < (unsigned)bm.width());
+ SkASSERT((unsigned)y < (unsigned)bm.height());
+
+ switch (bm.getConfig()) {
+ case SkBitmap::kA8_Config:
+ case SkBitmap:: kIndex8_Config:
+ // x is fine as is for the calculation
+ break;
+
+ case SkBitmap::kRGB_565_Config:
+ case SkBitmap::kARGB_4444_Config:
+ x <<= 1;
+ break;
+
+ case SkBitmap::kARGB_8888_Config:
+ x <<= 2;
+ break;
+
+ case SkBitmap::kNo_Config:
+ case SkBitmap::kA1_Config:
+ default:
+ return SUB_OFFSET_FAILURE;
+ }
+ return y * bm.rowBytes() + x;
+}
+
+bool SkBitmap::extractSubset(SkBitmap* result, const SkIRect& subset) const {
+ SkDEBUGCODE(this->validate();)
+
+ if (NULL == result || (NULL == fPixelRef && NULL == fPixels)) {
+ return false; // no src pixels
+ }
+
+ SkIRect srcRect, r;
+ srcRect.set(0, 0, this->width(), this->height());
+ if (!r.intersect(srcRect, subset)) {
+ return false; // r is empty (i.e. no intersection)
+ }
+
+ if (kRLE_Index8_Config == fConfig) {
+ SkAutoLockPixels alp(*this);
+ // don't call readyToDraw(), since we can operate w/o a colortable
+ // at this stage
+ if (this->getPixels() == NULL) {
+ return false;
+ }
+ SkBitmap bm;
+
+ bm.setConfig(kIndex8_Config, r.width(), r.height());
+ bm.allocPixels(this->getColorTable());
+ if (NULL == bm.getPixels()) {
+ return false;
+ }
+
+ const RLEPixels* rle = (const RLEPixels*)this->getPixels();
+ uint8_t* dst = bm.getAddr8(0, 0);
+ const int width = bm.width();
+ const int rowBytes = bm.rowBytes();
+
+ for (int y = r.fTop; y < r.fBottom; y++) {
+ SkPackBits::Unpack8(dst, r.fLeft, width, rle->packedAtY(y));
+ dst += rowBytes;
+ }
+ result->swap(bm);
+ return true;
+ }
+
+ size_t offset = getSubOffset(*this, r.fLeft, r.fTop);
+ if (SUB_OFFSET_FAILURE == offset) {
+ return false; // config not supported
+ }
+
+ SkBitmap dst;
+ dst.setConfig(this->config(), r.width(), r.height(), this->rowBytes());
+
+ if (fPixelRef) {
+ // share the pixelref with a custom offset
+ dst.setPixelRef(fPixelRef, fPixelRefOffset + offset);
+ } else {
+ // share the pixels (owned by the caller)
+ dst.setPixels((char*)fPixels + offset, this->getColorTable());
+ }
+ SkDEBUGCODE(dst.validate();)
+
+ // we know we're good, so commit to result
+ result->swap(dst);
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkCanvas.h"
+#include "SkPaint.h"
+
+bool SkBitmap::copyTo(SkBitmap* dst, Config dstConfig, Allocator* alloc) const {
+ if (NULL == dst || this->width() == 0 || this->height() == 0) {
+ return false;
+ }
+
+ switch (dstConfig) {
+ case kA8_Config:
+ case kARGB_4444_Config:
+ case kRGB_565_Config:
+ case kARGB_8888_Config:
+ break;
+ default:
+ return false;
+ }
+
+ SkBitmap tmp;
+
+ tmp.setConfig(dstConfig, this->width(), this->height());
+ // pass null for colortable, since we don't support Index8 config for dst
+ if (!tmp.allocPixels(alloc, NULL)) {
+ return false;
+ }
+
+ SkAutoLockPixels srclock(*this);
+ SkAutoLockPixels dstlock(tmp);
+
+ if (!this->readyToDraw() || !tmp.readyToDraw()) {
+ // allocator/lock failed
+ return false;
+ }
+
+ // if the src has alpha, we have to clear the dst first
+ if (!this->isOpaque()) {
+ tmp.eraseColor(0);
+ }
+
+ SkCanvas canvas(tmp);
+ SkPaint paint;
+
+ paint.setDither(true);
+ canvas.drawBitmap(*this, 0, 0, &paint);
+
+ dst->swap(tmp);
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static void downsampleby2_proc32(SkBitmap* dst, int x, int y,
+ const SkBitmap& src) {
+ x <<= 1;
+ y <<= 1;
+ const SkPMColor* p = src.getAddr32(x, y);
+ SkPMColor c, ag, rb;
+
+ c = *p; ag = (c >> 8) & 0xFF00FF; rb = c & 0xFF00FF;
+ if (x < src.width() - 1) {
+ p += 1;
+ }
+ c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
+
+ if (y < src.height() - 1) {
+ p = src.getAddr32(x, y + 1);
+ }
+ c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
+ if (x < 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_G16_MASK_IN_PLACE) | ((c & SK_G16_MASK_IN_PLACE) << 16);
+}
+
+// returns dirt in the top 16bits, but we don't care, since we only
+// store the low 16bits.
+static inline U16CPU pack16(uint32_t c) {
+ return (c & ~SK_G16_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 uint16_t* p = src.getAddr16(x, y);
+ SkPMColor 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) = (uint16_t)pack16(c >> 2);
+}
+
+static uint32_t expand4444(U16CPU c) {
+ return (c & 0xF0F) | ((c & ~0xF0F) << 12);
+}
+
+static U16CPU collaps4444(uint32_t c) {
+ return (c & 0xF0F) | ((c >> 12) & ~0xF0F);
+}
+
+static void downsampleby2_proc4444(SkBitmap* dst, int x, int y,
+ const SkBitmap& src) {
+ x <<= 1;
+ y <<= 1;
+ const uint16_t* p = src.getAddr16(x, y);
+ uint32_t c;
+
+ c = expand4444(*p);
+ if (x < src.width() - 1) {
+ p += 1;
+ }
+ c += expand4444(*p);
+
+ if (y < src.height() - 1) {
+ p = src.getAddr16(x, y + 1);
+ }
+ c += expand4444(*p);
+ if (x < src.width() - 1) {
+ p += 1;
+ }
+ c += expand4444(*p);
+
+ *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)collaps4444(c >> 2);
+}
+
+void SkBitmap::buildMipMap(bool forceRebuild) {
+#ifdef SK_SUPPORT_MIPMAP
+ if (forceRebuild)
+ this->freeMipMap();
+ else if (fMipMap)
+ return; // we're already built
+
+ SkASSERT(NULL == fMipMap);
+
+ void (*proc)(SkBitmap* dst, int x, int y, const SkBitmap& src);
+
+ const SkBitmap::Config config = this->getConfig();
+
+ switch (config) {
+ case kARGB_8888_Config:
+ proc = downsampleby2_proc32;
+ break;
+ case kRGB_565_Config:
+ proc = downsampleby2_proc16;
+ break;
+ case kARGB_4444_Config:
+ proc = downsampleby2_proc4444;
+ break;
+ case kIndex8_Config:
+ case kA8_Config:
+ default:
+ return; // don't build mipmaps for these configs
+ }
+
+ // whip through our loop to compute the exact size needed
+ size_t size = 0;
+ int maxLevels = 0;
+ {
+ unsigned width = this->width();
+ unsigned height = this->height();
+ for (;;) {
+ width >>= 1;
+ height >>= 1;
+ if (0 == width || 0 == height) {
+ break;
+ }
+ size += ComputeRowBytes(config, width) * height;
+ maxLevels += 1;
+ }
+ }
+ if (0 == maxLevels) {
+ return;
+ }
+
+ MipMap* mm = MipMap::Alloc(maxLevels, size);
+ MipLevel* level = mm->levels();
+ uint8_t* addr = (uint8_t*)mm->pixels();
+
+ unsigned width = this->width();
+ unsigned height = this->height();
+ unsigned rowBytes = this->rowBytes();
+ SkBitmap srcBM(*this), dstBM;
+
+ srcBM.lockPixels();
+
+ for (int i = 0; i < maxLevels; i++) {
+ width >>= 1;
+ height >>= 1;
+ rowBytes = ComputeRowBytes(config, width);
+
+ level[i].fPixels = addr;
+ level[i].fWidth = SkToU16(width);
+ level[i].fHeight = SkToU16(height);
+ level[i].fRowBytes = SkToU16(rowBytes);
+
+ dstBM.setConfig(config, 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 == (uint8_t*)mm->pixels() + size);
+ fMipMap = mm;
+#endif
+}
+
+bool SkBitmap::hasMipMap() const {
+#ifdef SK_SUPPORT_MIPMAP
+ return fMipMap != NULL;
+#else
+ return false;
+#endif
+}
+
+int SkBitmap::extractMipLevel(SkBitmap* dst, SkFixed sx, SkFixed sy) {
+#ifdef SK_SUPPORT_MIPMAP
+ if (NULL == fMipMap)
+ return 0;
+
+ int level = ComputeMipLevel(sx, sy) >> 16;
+ SkASSERT(level >= 0);
+ if (level <= 0) {
+ return 0;
+ }
+
+ if (level >= fMipMap->fLevelCount) {
+ level = fMipMap->fLevelCount - 1;
+ }
+ if (dst) {
+ const MipLevel& mip = fMipMap->levels()[level - 1];
+ dst->setConfig((SkBitmap::Config)this->config(),
+ mip.fWidth, mip.fHeight, mip.fRowBytes);
+ dst->setPixels(mip.fPixels);
+ }
+ return level;
+#else
+ return 0;
+#endif
+}
+
+SkFixed SkBitmap::ComputeMipLevel(SkFixed sx, SkFixed sy) {
+#ifdef SK_SUPPORT_MIPMAP
+ sx = SkAbs32(sx);
+ sy = SkAbs32(sy);
+ if (sx < sy) {
+ sx = sy;
+ }
+ if (sx < SK_Fixed1) {
+ return 0;
+ }
+ int clz = SkCLZ(sx);
+ SkASSERT(clz >= 1 && clz <= 15);
+ return SkIntToFixed(15 - clz) + ((unsigned)(sx << (clz + 1)) >> 16);
+#else
+ return 0;
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void GetBitmapAlpha(const SkBitmap& src, uint8_t SK_RESTRICT alpha[],
+ int alphaRowBytes) {
+ SkASSERT(alpha != NULL);
+ SkASSERT(alphaRowBytes >= src.width());
+
+ SkBitmap::Config config = src.getConfig();
+ int w = src.width();
+ int h = src.height();
+ int rb = src.rowBytes();
+
+ if (SkBitmap::kA8_Config == config && !src.isOpaque()) {
+ const uint8_t* s = src.getAddr8(0, 0);
+ while (--h >= 0) {
+ memcpy(alpha, s, w);
+ s += rb;
+ alpha += alphaRowBytes;
+ }
+ } else if (SkBitmap::kARGB_8888_Config == config && !src.isOpaque()) {
+ const SkPMColor* SK_RESTRICT s = src.getAddr32(0, 0);
+ while (--h >= 0) {
+ for (int x = 0; x < w; x++) {
+ alpha[x] = SkGetPackedA32(s[x]);
+ }
+ s = (const SkPMColor*)((const char*)s + rb);
+ alpha += alphaRowBytes;
+ }
+ } else if (SkBitmap::kARGB_4444_Config == config && !src.isOpaque()) {
+ const SkPMColor16* SK_RESTRICT s = src.getAddr16(0, 0);
+ while (--h >= 0) {
+ for (int x = 0; x < w; x++) {
+ alpha[x] = SkPacked4444ToA32(s[x]);
+ }
+ s = (const SkPMColor16*)((const char*)s + rb);
+ alpha += alphaRowBytes;
+ }
+ } else if (SkBitmap::kIndex8_Config == config && !src.isOpaque()) {
+ SkColorTable* ct = src.getColorTable();
+ if (ct) {
+ const SkPMColor* SK_RESTRICT table = ct->lockColors();
+ const uint8_t* SK_RESTRICT s = src.getAddr8(0, 0);
+ while (--h >= 0) {
+ for (int x = 0; x < w; x++) {
+ alpha[x] = SkGetPackedA32(table[s[x]]);
+ }
+ s += rb;
+ alpha += alphaRowBytes;
+ }
+ ct->unlockColors(false);
+ }
+ } else { // src is opaque, so just fill alpha[] with 0xFF
+ memset(alpha, 0xFF, h * alphaRowBytes);
+ }
+}
+
+#include "SkPaint.h"
+#include "SkMaskFilter.h"
+#include "SkMatrix.h"
+
+void SkBitmap::extractAlpha(SkBitmap* dst, const SkPaint* paint,
+ SkIPoint* offset) const {
+ SkDEBUGCODE(this->validate();)
+
+ SkMatrix identity;
+ SkMask srcM, dstM;
+
+ srcM.fBounds.set(0, 0, this->width(), this->height());
+ srcM.fRowBytes = SkAlign4(this->width());
+ srcM.fFormat = SkMask::kA8_Format;
+
+ SkMaskFilter* filter = paint ? paint->getMaskFilter() : NULL;
+
+ // compute our (larger?) dst bounds if we have a filter
+ if (NULL != filter) {
+ identity.reset();
+ srcM.fImage = NULL;
+ if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
+ goto NO_FILTER_CASE;
+ }
+ dstM.fRowBytes = SkAlign4(dstM.fBounds.width());
+ } else {
+ NO_FILTER_CASE:
+ dst->setConfig(SkBitmap::kA8_Config, this->width(), this->height(),
+ srcM.fRowBytes);
+ dst->allocPixels();
+ GetBitmapAlpha(*this, dst->getAddr8(0, 0), srcM.fRowBytes);
+ if (offset) {
+ offset->set(0, 0);
+ }
+ return;
+ }
+
+ SkAutoMaskImage srcCleanup(&srcM, true);
+
+ GetBitmapAlpha(*this, srcM.fImage, srcM.fRowBytes);
+ if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
+ goto NO_FILTER_CASE;
+ }
+
+ SkAutoMaskImage dstCleanup(&dstM, false);
+
+ dst->setConfig(SkBitmap::kA8_Config, dstM.fBounds.width(),
+ dstM.fBounds.height(), dstM.fRowBytes);
+ dst->allocPixels();
+ memcpy(dst->getPixels(), dstM.fImage, dstM.computeImageSize());
+ if (offset) {
+ offset->set(dstM.fBounds.fLeft, dstM.fBounds.fTop);
+ }
+ SkDEBUGCODE(dst->validate();)
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+enum {
+ SERIALIZE_PIXELTYPE_NONE,
+ SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE,
+ SERIALIZE_PIXELTYPE_RAW_NO_CTABLE,
+ SERIALIZE_PIXELTYPE_REF_DATA,
+ SERIALIZE_PIXELTYPE_REF_PTR,
+};
+
+static void writeString(SkFlattenableWriteBuffer& buffer, const char str[]) {
+ size_t len = strlen(str);
+ buffer.write32(len);
+ buffer.writePad(str, len);
+}
+
+static SkPixelRef::Factory deserialize_factory(SkFlattenableReadBuffer& buffer) {
+ size_t len = buffer.readInt();
+ SkAutoSMalloc<256> storage(len + 1);
+ char* str = (char*)storage.get();
+ buffer.read(str, len);
+ str[len] = 0;
+ return SkPixelRef::NameToFactory(str);
+}
+
+/*
+ It is tricky to know how much to flatten. If we don't have a pixelref (i.e.
+ we just have pixels, then we can only flatten the pixels, or write out an
+ empty bitmap.
+
+ With a pixelref, we still have the question of recognizing when two sitings
+ of the same pixelref are the same, and when they are different. Perhaps we
+ should look at the generationID and keep a record of that in some dictionary
+ associated with the buffer. SkGLTextureCache does this sort of thing to know
+ when to create a new texture.
+*/
+void SkBitmap::flatten(SkFlattenableWriteBuffer& buffer) const {
+ buffer.write32(fWidth);
+ buffer.write32(fHeight);
+ buffer.write32(fRowBytes);
+ buffer.write8(fConfig);
+ buffer.writeBool(this->isOpaque());
+
+ /* If we are called in this mode, then it is up to the caller to manage
+ the owner-counts on the pixelref, as we just record the ptr itself.
+ */
+ if (!buffer.persistBitmapPixels()) {
+ if (fPixelRef) {
+ buffer.write8(SERIALIZE_PIXELTYPE_REF_PTR);
+ buffer.write32(fPixelRefOffset);
+ buffer.writeRefCnt(fPixelRef);
+ return;
+ } else {
+ // we ignore the non-persist request, since we don't have a ref
+ // ... or we could just write an empty bitmap...
+ // (true) will write an empty bitmap, (false) will flatten the pix
+ if (true) {
+ buffer.write8(SERIALIZE_PIXELTYPE_NONE);
+ return;
+ }
+ }
+ }
+
+ if (fPixelRef) {
+ SkPixelRef::Factory fact = fPixelRef->getFactory();
+ if (fact) {
+ const char* name = SkPixelRef::FactoryToName(fact);
+ if (name && *name) {
+ buffer.write8(SERIALIZE_PIXELTYPE_REF_DATA);
+ buffer.write32(fPixelRefOffset);
+ writeString(buffer, name);
+ fPixelRef->flatten(buffer);
+ return;
+ }
+ }
+ // if we get here, we can't record the pixels
+ buffer.write8(SERIALIZE_PIXELTYPE_NONE);
+ } else if (fPixels) {
+ if (fColorTable) {
+ buffer.write8(SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE);
+ fColorTable->flatten(buffer);
+ } else {
+ buffer.write8(SERIALIZE_PIXELTYPE_RAW_NO_CTABLE);
+ }
+ buffer.writePad(fPixels, this->getSize());
+ } else {
+ buffer.write8(SERIALIZE_PIXELTYPE_NONE);
+ }
+}
+
+void SkBitmap::unflatten(SkFlattenableReadBuffer& buffer) {
+ this->reset();
+
+ int width = buffer.readInt();
+ int height = buffer.readInt();
+ int rowBytes = buffer.readInt();
+ int config = buffer.readU8();
+
+ this->setConfig((Config)config, width, height, rowBytes);
+ this->setIsOpaque(buffer.readBool());
+
+ size_t size = this->getSize();
+ int reftype = buffer.readU8();
+ switch (reftype) {
+ case SERIALIZE_PIXELTYPE_REF_PTR: {
+ size_t offset = buffer.readU32();
+ SkPixelRef* pr = (SkPixelRef*)buffer.readRefCnt();
+ this->setPixelRef(pr, offset);
+ break;
+ }
+ case SERIALIZE_PIXELTYPE_REF_DATA: {
+ size_t offset = buffer.readU32();
+ SkPixelRef::Factory fact = deserialize_factory(buffer);
+ SkPixelRef* pr = fact(buffer);
+ this->setPixelRef(pr, offset)->safeUnref();
+ break;
+ }
+ case SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE:
+ case SERIALIZE_PIXELTYPE_RAW_NO_CTABLE: {
+ SkColorTable* ctable = NULL;
+ if (SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE == reftype) {
+ ctable = SkNEW_ARGS(SkColorTable, (buffer));
+ }
+ if (this->allocPixels(ctable)) {
+ this->lockPixels();
+ buffer.read(this->getPixels(), size);
+ this->unlockPixels();
+ } else {
+ buffer.skip(size);
+ }
+ ctable->safeUnref();
+ break;
+ }
+ case SERIALIZE_PIXELTYPE_NONE:
+ break;
+ default:
+ SkASSERT(!"unrecognized pixeltype in serialized data");
+ sk_throw();
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkBitmap::RLEPixels::RLEPixels(int width, int height) {
+ fHeight = height;
+ fYPtrs = (uint8_t**)sk_malloc_throw(height * sizeof(uint8_t*));
+ bzero(fYPtrs, height * sizeof(uint8_t*));
+}
+
+SkBitmap::RLEPixels::~RLEPixels() {
+ sk_free(fYPtrs);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+void SkBitmap::validate() const {
+ SkASSERT(fConfig < kConfigCount);
+ SkASSERT(fRowBytes >= (unsigned)ComputeRowBytes((Config)fConfig, fWidth));
+ SkASSERT(fFlags <= kImageIsOpaque_Flag);
+ SkASSERT(fPixelLockCount >= 0);
+ SkASSERT(NULL == fColorTable || (unsigned)fColorTable->getRefCnt() < 10000);
+ SkASSERT((uint8_t)ComputeBytesPerPixel((Config)fConfig) == fBytesPerPixel);
+
+#if 0 // these asserts are not thread-correct, so disable for now
+ if (fPixelRef) {
+ if (fPixelLockCount > 0) {
+ SkASSERT(fPixelRef->getLockCount() > 0);
+ } else {
+ SkASSERT(NULL == fPixels);
+ SkASSERT(NULL == fColorTable);
+ }
+ }
+#endif
+}
+#endif
+
diff --git a/src/core/SkBitmapProcShader.cpp b/src/core/SkBitmapProcShader.cpp
new file mode 100644
index 0000000000..6d7d581604
--- /dev/null
+++ b/src/core/SkBitmapProcShader.cpp
@@ -0,0 +1,231 @@
+#include "SkBitmapProcShader.h"
+#include "SkColorPriv.h"
+#include "SkPixelRef.h"
+
+bool SkBitmapProcShader::CanDo(const SkBitmap& bm, TileMode tx, TileMode ty) {
+ switch (bm.config()) {
+ case SkBitmap::kA8_Config:
+ case SkBitmap::kRGB_565_Config:
+ case SkBitmap::kIndex8_Config:
+ case SkBitmap::kARGB_8888_Config:
+ // if (tx == ty && (kClamp_TileMode == tx || kRepeat_TileMode == tx))
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+SkBitmapProcShader::SkBitmapProcShader(const SkBitmap& src,
+ TileMode tmx, TileMode tmy) {
+ fRawBitmap = src;
+ fState.fTileModeX = (uint8_t)tmx;
+ fState.fTileModeY = (uint8_t)tmy;
+}
+
+SkBitmapProcShader::SkBitmapProcShader(SkFlattenableReadBuffer& buffer)
+ : INHERITED(buffer) {
+ fRawBitmap.unflatten(buffer);
+ fState.fTileModeX = buffer.readU8();
+ fState.fTileModeY = buffer.readU8();
+}
+
+void SkBitmapProcShader::beginSession() {
+ this->INHERITED::beginSession();
+
+ fRawBitmap.lockPixels();
+}
+
+void SkBitmapProcShader::endSession() {
+ fRawBitmap.unlockPixels();
+
+ this->INHERITED::endSession();
+}
+
+bool SkBitmapProcShader::asABitmap(SkBitmap* texture, SkMatrix* texM,
+ TileMode xy[]) {
+ if (texture) {
+ *texture = fRawBitmap;
+ }
+ if (texM) {
+ texM->reset();
+ }
+ if (xy) {
+ xy[0] = (TileMode)fState.fTileModeX;
+ xy[1] = (TileMode)fState.fTileModeY;
+ }
+ return true;
+}
+
+void SkBitmapProcShader::flatten(SkFlattenableWriteBuffer& buffer) {
+ this->INHERITED::flatten(buffer);
+
+ fRawBitmap.flatten(buffer);
+ buffer.write8(fState.fTileModeX);
+ buffer.write8(fState.fTileModeY);
+}
+
+bool SkBitmapProcShader::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;
+ }
+
+ fState.fOrigBitmap = fRawBitmap;
+ fState.fOrigBitmap.lockPixels();
+ if (fState.fOrigBitmap.getPixels() == NULL) {
+ fState.fOrigBitmap.unlockPixels();
+ return false;
+ }
+
+ if (!fState.chooseProcs(this->getTotalInverse(), paint)) {
+ return false;
+ }
+
+ bool bitmapIsOpaque = fState.fBitmap->isOpaque();
+
+ // filtering doesn't guarantee that opaque stays opaque (finite precision)
+ // so pretend we're not opaque if we're being asked to filter. If we had
+ // more blit-procs, we could specialize on opaque src, and just OR in 0xFF
+ // after the filter to be sure...
+ if (paint.isFilterBitmap()) {
+ bitmapIsOpaque = false;
+ }
+
+ // update fFlags
+ fFlags = 0; // this should happen in SkShader.cpp
+
+ if (bitmapIsOpaque && (255 == this->getPaintAlpha())) {
+ fFlags |= kOpaqueAlpha_Flag;
+ }
+
+ switch (fState.fBitmap->config()) {
+ case SkBitmap::kRGB_565_Config:
+ fFlags |= (kHasSpan16_Flag | kIntrinsicly16_Flag);
+ break;
+ case SkBitmap::kIndex8_Config:
+ case SkBitmap::kARGB_8888_Config:
+ if (bitmapIsOpaque) {
+ fFlags |= kHasSpan16_Flag;
+ }
+ break;
+ case SkBitmap::kA8_Config:
+ break; // never set kHasSpan16_Flag
+ default:
+ break;
+ }
+ return true;
+}
+
+#define BUF_MAX 128
+
+void SkBitmapProcShader::shadeSpan(int x, int y, SkPMColor dstC[], int count) {
+ uint32_t buffer[BUF_MAX];
+
+ const SkBitmapProcState& state = fState;
+ SkBitmapProcState::MatrixProc mproc = state.fMatrixProc;
+ SkBitmapProcState::SampleProc32 sproc = state.fSampleProc32;
+ int max = fState.fDoFilter ? (BUF_MAX >> 1) : BUF_MAX;
+
+ SkASSERT(state.fBitmap->getPixels());
+ SkASSERT(state.fBitmap->pixelRef() == NULL ||
+ state.fBitmap->pixelRef()->getLockCount());
+
+ for (;;) {
+ int n = count;
+ if (n > max) {
+ n = max;
+ }
+ mproc(state, buffer, n, x, y);
+ sproc(state, buffer, n, dstC);
+
+ if ((count -= n) == 0) {
+ break;
+ }
+ x += n;
+ dstC += n;
+ }
+}
+
+void SkBitmapProcShader::shadeSpan16(int x, int y, uint16_t dstC[], int count) {
+ uint32_t buffer[BUF_MAX];
+
+ const SkBitmapProcState& state = fState;
+ SkBitmapProcState::MatrixProc mproc = state.fMatrixProc;
+ SkBitmapProcState::SampleProc16 sproc = state.fSampleProc16;
+ int max = fState.fDoFilter ? (BUF_MAX >> 1) : BUF_MAX;
+
+ SkASSERT(state.fBitmap->getPixels());
+ SkASSERT(state.fBitmap->pixelRef() == NULL ||
+ state.fBitmap->pixelRef()->getLockCount());
+
+ for (;;) {
+ int n = count;
+ if (n > max) {
+ n = max;
+ }
+ mproc(state, buffer, n, x, y);
+ sproc(state, buffer, n, dstC);
+
+ if ((count -= n) == 0) {
+ break;
+ }
+ x += n;
+ dstC += n;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkTemplatesPriv.h"
+
+SkShader* SkShader::CreateBitmapShader(const SkBitmap& src,
+ TileMode tmx, TileMode tmy,
+ void* storage, size_t storageSize) {
+ SkShader* shader;
+ SK_PLACEMENT_NEW_ARGS(shader, SkBitmapProcShader, storage,
+ storageSize, (src, tmx, tmy));
+ return shader;
+}
+
+static SkFlattenable::Registrar gBitmapProcShaderReg("SkBitmapProcShader",
+ SkBitmapProcShader::CreateProc);
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const char* gTileModeName[] = {
+ "clamp", "repeat", "mirror"
+};
+
+bool SkBitmapProcShader::toDumpString(SkString* str) const {
+ str->printf("BitmapShader: [%d %d %d",
+ fRawBitmap.width(), fRawBitmap.height(),
+ fRawBitmap.bytesPerPixel());
+
+ // add the pixelref
+ SkPixelRef* pr = fRawBitmap.pixelRef();
+ if (pr) {
+ const char* uri = pr->getURI();
+ if (uri) {
+ str->appendf(" \"%s\"", uri);
+ }
+ }
+
+ // add the (optional) matrix
+ {
+ SkMatrix m;
+ if (this->getLocalMatrix(&m)) {
+ SkString info;
+ m.toDumpString(&info);
+ str->appendf(" %s", info.c_str());
+ }
+ }
+
+ str->appendf(" [%s %s]]",
+ gTileModeName[fState.fTileModeX],
+ gTileModeName[fState.fTileModeY]);
+ return true;
+}
+
diff --git a/src/core/SkBitmapProcShader.h b/src/core/SkBitmapProcShader.h
new file mode 100644
index 0000000000..09d53afe1d
--- /dev/null
+++ b/src/core/SkBitmapProcShader.h
@@ -0,0 +1,59 @@
+/* libs/graphics/sgl/SkBitmapShader.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef SkBitmapProcShader_DEFINED
+#define SkBitmapProcShader_DEFINED
+
+#include "SkShader.h"
+#include "SkBitmapProcState.h"
+
+class SkBitmapProcShader : public SkShader {
+public:
+ SkBitmapProcShader(const SkBitmap& src, TileMode tx, TileMode ty);
+
+ // overrides from SkShader
+ virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&);
+ virtual uint32_t getFlags() { return fFlags; }
+ virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
+ virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count);
+ virtual void beginSession();
+ virtual void endSession();
+ virtual bool asABitmap(SkBitmap*, SkMatrix*, TileMode*);
+
+ static bool CanDo(const SkBitmap&, TileMode tx, TileMode ty);
+
+ static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+ return SkNEW_ARGS(SkBitmapProcShader, (buffer));
+ }
+
+ // override from flattenable
+ virtual bool toDumpString(SkString* str) const;
+
+protected:
+ SkBitmapProcShader(SkFlattenableReadBuffer& );
+ virtual void flatten(SkFlattenableWriteBuffer& );
+ virtual Factory getFactory() { return CreateProc; }
+
+ SkBitmap fRawBitmap; // experimental for RLE encoding
+ SkBitmapProcState fState;
+ uint32_t fFlags;
+
+private:
+ typedef SkShader INHERITED;
+};
+
+#endif
diff --git a/src/core/SkBitmapProcState.cpp b/src/core/SkBitmapProcState.cpp
new file mode 100644
index 0000000000..428921d20e
--- /dev/null
+++ b/src/core/SkBitmapProcState.cpp
@@ -0,0 +1,476 @@
+#include "SkBitmapProcState.h"
+#include "SkColorPriv.h"
+#include "SkFilterProc.h"
+#include "SkPaint.h"
+#include "SkShader.h" // for tilemodes
+
+#ifdef SK_CPU_BENDIAN
+ #define UNPACK_PRIMARY_SHORT(packed) ((uint32_t)(packed) >> 16)
+ #define UNPACK_SECONDARY_SHORT(packed) ((packed) & 0xFFFF)
+#else
+ #define UNPACK_PRIMARY_SHORT(packed) ((packed) & 0xFFFF)
+ #define UNPACK_SECONDARY_SHORT(packed) ((uint32_t)(packed) >> 16)
+#endif
+
+static inline SkPMColor Filter_32(unsigned x, unsigned y,
+ SkPMColor a00, SkPMColor a01,
+ SkPMColor a10, SkPMColor a11) {
+ SkASSERT((unsigned)x <= 0xF);
+ SkASSERT((unsigned)y <= 0xF);
+
+ int xy = x * y;
+ uint32_t mask = gMask_00FF00FF; //0xFF00FF;
+
+ int scale = 256 - 16*y - 16*x + xy;
+ uint32_t lo = (a00 & mask) * scale;
+ uint32_t hi = ((a00 >> 8) & mask) * scale;
+
+ scale = 16*x - xy;
+ lo += (a01 & mask) * scale;
+ hi += ((a01 >> 8) & mask) * scale;
+
+ scale = 16*y - xy;
+ lo += (a10 & mask) * scale;
+ hi += ((a10 >> 8) & mask) * scale;
+
+ lo += (a11 & mask) * xy;
+ hi += ((a11 >> 8) & mask) * xy;
+
+ return ((lo >> 8) & mask) | (hi & ~mask);
+}
+
+// returns expanded * 5bits
+static inline uint32_t Filter_565_Expanded(unsigned x, unsigned y,
+ uint32_t a00, uint32_t a01,
+ uint32_t a10, uint32_t a11) {
+ SkASSERT((unsigned)x <= 0xF);
+ SkASSERT((unsigned)y <= 0xF);
+
+ a00 = SkExpand_rgb_16(a00);
+ a01 = SkExpand_rgb_16(a01);
+ a10 = SkExpand_rgb_16(a10);
+ a11 = SkExpand_rgb_16(a11);
+
+ int xy = x * y >> 3;
+ return a00 * (32 - 2*y - 2*x + xy) +
+ a01 * (2*x - xy) +
+ a10 * (2*y - xy) +
+ a11 * xy;
+}
+
+// turn an expanded 565 * 5bits into SkPMColor
+// g:11 | r:10 | x:1 | b:10
+static inline SkPMColor SkExpanded_565_To_PMColor(uint32_t c) {
+ unsigned r = (c >> 13) & 0xFF;
+ unsigned g = (c >> 24);
+ unsigned b = (c >> 2) & 0xFF;
+ return SkPackARGB32(0xFF, r, g, b);
+}
+
+// returns answer in SkPMColor format
+static inline SkPMColor Filter_4444_D32(unsigned x, unsigned y,
+ uint32_t a00, uint32_t a01,
+ uint32_t a10, uint32_t a11) {
+ SkASSERT((unsigned)x <= 0xF);
+ SkASSERT((unsigned)y <= 0xF);
+
+ a00 = SkExpand_4444(a00);
+ a01 = SkExpand_4444(a01);
+ a10 = SkExpand_4444(a10);
+ a11 = SkExpand_4444(a11);
+
+ int xy = x * y >> 4;
+ uint32_t result = a00 * (16 - y - x + xy) +
+ a01 * (x - xy) +
+ a10 * (y - xy) +
+ a11 * xy;
+
+ return SkCompact_8888(result);
+}
+
+static inline U8CPU Filter_8(unsigned x, unsigned y,
+ U8CPU a00, U8CPU a01,
+ U8CPU a10, U8CPU a11) {
+ SkASSERT((unsigned)x <= 0xF);
+ SkASSERT((unsigned)y <= 0xF);
+
+ int xy = x * y;
+ unsigned result = a00 * (256 - 16*y - 16*x + xy) +
+ a01 * (16*x - xy) +
+ a10 * (16*y - xy) +
+ a11 * xy;
+
+ return result >> 8;
+}
+
+/*****************************************************************************
+ *
+ * D32 functions
+ *
+ */
+
+// SRC == 8888
+
+#define FILTER_PROC(x, y, a, b, c, d) Filter_32(x, y, a, b, c, d)
+
+#define MAKENAME(suffix) S32_opaque_D32 ## suffix
+#define DSTSIZE 32
+#define SRCTYPE SkPMColor
+#define CHECKSTATE(state) SkASSERT(state.fBitmap->config() == SkBitmap::kARGB_8888_Config); \
+ SkASSERT(state.fAlphaScale == 256)
+#define RETURNDST(src) src
+#define SRC_TO_FILTER(src) src
+#define FILTER_TO_DST(c) c
+#include "SkBitmapProcState_sample.h"
+
+#define MAKENAME(suffix) S32_alpha_D32 ## suffix
+#define DSTSIZE 32
+#define SRCTYPE SkPMColor
+#define CHECKSTATE(state) SkASSERT(state.fBitmap->config() == SkBitmap::kARGB_8888_Config); \
+ SkASSERT(state.fAlphaScale < 256)
+#define PREAMBLE(state) unsigned scale = state.fAlphaScale
+#define RETURNDST(src) SkAlphaMulQ(src, scale)
+#define SRC_TO_FILTER(src) src
+#define FILTER_TO_DST(c) SkAlphaMulQ(c, scale)
+#include "SkBitmapProcState_sample.h"
+
+// SRC == 565
+
+#undef FILTER_PROC
+#define FILTER_PROC(x, y, a, b, c, d) Filter_565_Expanded(x, y, a, b, c, d)
+
+#define MAKENAME(suffix) S16_opaque_D32 ## suffix
+#define DSTSIZE 32
+#define SRCTYPE uint16_t
+#define CHECKSTATE(state) SkASSERT(state.fBitmap->config() == SkBitmap::kRGB_565_Config); \
+ SkASSERT(state.fAlphaScale == 256)
+#define RETURNDST(src) SkPixel16ToPixel32(src)
+#define SRC_TO_FILTER(src) src
+#define FILTER_TO_DST(c) SkExpanded_565_To_PMColor(c)
+#include "SkBitmapProcState_sample.h"
+
+#define MAKENAME(suffix) S16_alpha_D32 ## suffix
+#define DSTSIZE 32
+#define SRCTYPE uint16_t
+#define CHECKSTATE(state) SkASSERT(state.fBitmap->config() == SkBitmap::kRGB_565_Config); \
+ SkASSERT(state.fAlphaScale < 256)
+#define PREAMBLE(state) unsigned scale = state.fAlphaScale
+#define RETURNDST(src) SkAlphaMulQ(SkPixel16ToPixel32(src), scale)
+#define SRC_TO_FILTER(src) src
+#define FILTER_TO_DST(c) SkAlphaMulQ(SkExpanded_565_To_PMColor(c), scale)
+#include "SkBitmapProcState_sample.h"
+
+// SRC == Index8
+
+#undef FILTER_PROC
+#define FILTER_PROC(x, y, a, b, c, d) Filter_32(x, y, a, b, c, d)
+
+#define MAKENAME(suffix) SI8_opaque_D32 ## suffix
+#define DSTSIZE 32
+#define SRCTYPE uint8_t
+#define CHECKSTATE(state) SkASSERT(state.fBitmap->config() == SkBitmap::kIndex8_Config); \
+ SkASSERT(state.fAlphaScale == 256)
+#define PREAMBLE(state) const SkPMColor* SK_RESTRICT table = state.fBitmap->getColorTable()->lockColors()
+#define RETURNDST(src) table[src]
+#define SRC_TO_FILTER(src) table[src]
+#define FILTER_TO_DST(c) c
+#define POSTAMBLE(state) state.fBitmap->getColorTable()->unlockColors(false)
+#include "SkBitmapProcState_sample.h"
+
+#define MAKENAME(suffix) SI8_alpha_D32 ## suffix
+#define DSTSIZE 32
+#define SRCTYPE uint8_t
+#define CHECKSTATE(state) SkASSERT(state.fBitmap->config() == SkBitmap::kIndex8_Config); \
+ SkASSERT(state.fAlphaScale < 256)
+#define PREAMBLE(state) unsigned scale = state.fAlphaScale; \
+ const SkPMColor* SK_RESTRICT table = state.fBitmap->getColorTable()->lockColors()
+#define RETURNDST(src) SkAlphaMulQ(table[src], scale)
+#define SRC_TO_FILTER(src) table[src]
+#define FILTER_TO_DST(c) SkAlphaMulQ(c, scale)
+#define POSTAMBLE(state) state.fBitmap->getColorTable()->unlockColors(false)
+#include "SkBitmapProcState_sample.h"
+
+// SRC == 4444
+
+#undef FILTER_PROC
+#define FILTER_PROC(x, y, a, b, c, d) Filter_4444_D32(x, y, a, b, c, d)
+
+#define MAKENAME(suffix) S4444_opaque_D32 ## suffix
+#define DSTSIZE 32
+#define SRCTYPE SkPMColor16
+#define CHECKSTATE(state) SkASSERT(state.fBitmap->config() == SkBitmap::kARGB_4444_Config); \
+SkASSERT(state.fAlphaScale == 256)
+#define RETURNDST(src) SkPixel4444ToPixel32(src)
+#define SRC_TO_FILTER(src) src
+#define FILTER_TO_DST(c) c
+#include "SkBitmapProcState_sample.h"
+
+#define MAKENAME(suffix) S4444_alpha_D32 ## suffix
+#define DSTSIZE 32
+#define SRCTYPE SkPMColor16
+#define CHECKSTATE(state) SkASSERT(state.fBitmap->config() == SkBitmap::kARGB_4444_Config); \
+SkASSERT(state.fAlphaScale < 256)
+#define PREAMBLE(state) unsigned scale = state.fAlphaScale
+#define RETURNDST(src) SkAlphaMulQ(SkPixel4444ToPixel32(src), scale)
+#define SRC_TO_FILTER(src) src
+#define FILTER_TO_DST(c) SkAlphaMulQ(c, scale)
+#include "SkBitmapProcState_sample.h"
+
+// SRC == A8
+
+#undef FILTER_PROC
+#define FILTER_PROC(x, y, a, b, c, d) Filter_8(x, y, a, b, c, d)
+
+#define MAKENAME(suffix) SA8_alpha_D32 ## suffix
+#define DSTSIZE 32
+#define SRCTYPE uint8_t
+#define CHECKSTATE(state) SkASSERT(state.fBitmap->config() == SkBitmap::kA8_Config); \
+ SkASSERT(state.fAlphaScale == 256)
+#define PREAMBLE(state) const SkPMColor pmColor = state.fPaintPMColor;
+#define RETURNDST(src) SkAlphaMulQ(pmColor, SkAlpha255To256(src))
+#define SRC_TO_FILTER(src) src
+#define FILTER_TO_DST(c) SkAlphaMulQ(pmColor, SkAlpha255To256(c))
+#include "SkBitmapProcState_sample.h"
+
+/*****************************************************************************
+ *
+ * D16 functions
+ *
+ */
+
+// SRC == 8888
+
+#undef FILTER_PROC
+#define FILTER_PROC(x, y, a, b, c, d) Filter_32(x, y, a, b, c, d)
+
+#define MAKENAME(suffix) S32_D16 ## suffix
+#define DSTSIZE 16
+#define SRCTYPE SkPMColor
+#define CHECKSTATE(state) SkASSERT(state.fBitmap->config() == SkBitmap::kARGB_8888_Config); \
+ SkASSERT(state.fBitmap->isOpaque())
+#define RETURNDST(src) SkPixel32ToPixel16(src)
+#define SRC_TO_FILTER(src) src
+#define FILTER_TO_DST(c) SkPixel32ToPixel16(c)
+#include "SkBitmapProcState_sample.h"
+
+// SRC == 565
+
+#undef FILTER_PROC
+#define FILTER_PROC(x, y, a, b, c, d) Filter_565_Expanded(x, y, a, b, c, d)
+
+#define MAKENAME(suffix) S16_D16 ## suffix
+#define DSTSIZE 16
+#define SRCTYPE uint16_t
+#define CHECKSTATE(state) SkASSERT(state.fBitmap->config() == SkBitmap::kRGB_565_Config)
+#define RETURNDST(src) src
+#define SRC_TO_FILTER(src) src
+#define FILTER_TO_DST(c) SkCompact_rgb_16((c) >> 5)
+#include "SkBitmapProcState_sample.h"
+
+// SRC == Index8
+
+#undef FILTER_PROC
+#define FILTER_PROC(x, y, a, b, c, d) Filter_565_Expanded(x, y, a, b, c, d)
+
+#define MAKENAME(suffix) SI8_D16 ## suffix
+#define DSTSIZE 16
+#define SRCTYPE uint8_t
+#define CHECKSTATE(state) SkASSERT(state.fBitmap->config() == SkBitmap::kIndex8_Config); \
+ SkASSERT(state.fBitmap->isOpaque())
+#define PREAMBLE(state) const uint16_t* SK_RESTRICT table = state.fBitmap->getColorTable()->lock16BitCache()
+#define RETURNDST(src) table[src]
+#define SRC_TO_FILTER(src) table[src]
+#define FILTER_TO_DST(c) SkCompact_rgb_16(c >> 5)
+#define POSTAMBLE(state) state.fBitmap->getColorTable()->unlock16BitCache()
+#include "SkBitmapProcState_sample.h"
+
+static bool valid_for_filtering(unsigned dimension) {
+ // for filtering, width and height must fit in 14bits, since we use steal
+ // 2 bits from each to store our 4bit subpixel data
+ return (dimension & ~0x3FFF) == 0;
+}
+
+bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) {
+ if (fOrigBitmap.width() == 0 || fOrigBitmap.height() == 0) {
+ return false;
+ }
+ const SkMatrix* m;
+
+ if (SkShader::kClamp_TileMode == fTileModeX &&
+ SkShader::kClamp_TileMode == fTileModeY) {
+ m = &inv;
+ } else {
+ fUnitInvMatrix = inv;
+ fUnitInvMatrix.postIDiv(fOrigBitmap.width(), fOrigBitmap.height());
+ m = &fUnitInvMatrix;
+ }
+
+ fBitmap = &fOrigBitmap;
+#ifdef SK_SUPPORT_MIPMAP
+ if (fOrigBitmap.hasMipMap()) {
+ int shift = fOrigBitmap.extractMipLevel(&fMipBitmap,
+ SkScalarToFixed(m->getScaleX()),
+ SkScalarToFixed(m->getSkewY()));
+
+ if (shift > 0) {
+ if (m != &fUnitInvMatrix) {
+ fUnitInvMatrix = *m;
+ m = &fUnitInvMatrix;
+ }
+
+ SkScalar scale = SkFixedToScalar(SK_Fixed1 >> shift);
+ fUnitInvMatrix.postScale(scale, scale);
+
+ // now point here instead of fOrigBitmap
+ fBitmap = &fMipBitmap;
+ }
+ }
+#endif
+
+ fInvMatrix = m;
+ fInvProc = m->getMapXYProc();
+ fInvType = m->getType();
+ fInvSx = SkScalarToFixed(m->getScaleX());
+ fInvSy = SkScalarToFixed(m->getScaleY());
+ fInvKy = SkScalarToFixed(m->getSkewY());
+ fInvTxPlusHalf = SkScalarToFixed(m->getTranslateX()) + (fInvSx >> 1);
+ fInvTyPlusHalf = SkScalarToFixed(m->getTranslateY()) + (fInvSy >> 1);
+
+ /* the -1 keeps us symetric with general policy for rounding, which is
+ (x + 1/2) >> 16. This sends exact halves to the next large pixel
+ e.g. x==3.5, round(x) == 4. However, our state is working with the
+ inverse matrix, and so to match the result of "normal" rounding, we
+ subtract 1 so that we in effect behave the same at the half-way point.
+ To compare, try drawing a bitmap with y == exact-half using the sprite
+ blitters and with us. Without the -1, we will draw the colors a whole
+ pixel shifted up (yikes).
+ */
+ fInvTxPlusHalf -= 1;
+ fInvTyPlusHalf -= 1;
+
+ fAlphaScale = SkAlpha255To256(paint.getAlpha());
+
+ // pick-up filtering from the paint, but only if the matrix is
+ // more complex than identity/translate (i.e. no need to pay the cost
+ // of filtering if we're not scaled etc.).
+ // note: we explicitly check inv, since m might be scaled due to unitinv
+ // trickery, but we don't want to see that for this test
+ fDoFilter = paint.isFilterBitmap() &&
+ (inv.getType() > SkMatrix::kTranslate_Mask &&
+ valid_for_filtering(fBitmap->width() | fBitmap->height()));
+
+ fMatrixProc = this->chooseMatrixProc();
+ if (NULL == fMatrixProc) {
+ return false;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ int index = 0;
+ if (fAlphaScale < 256) { // note: this distinction is not used for D16
+ index |= 1;
+ }
+ if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
+ index |= 2;
+ }
+ if (fDoFilter) {
+ index |= 4;
+ }
+ // bits 3,4,5 encoding the source bitmap format
+ switch (fBitmap->config()) {
+ case SkBitmap::kARGB_8888_Config:
+ index |= 0;
+ break;
+ case SkBitmap::kRGB_565_Config:
+ index |= 8;
+ break;
+ case SkBitmap::kIndex8_Config:
+ index |= 16;
+ break;
+ case SkBitmap::kARGB_4444_Config:
+ index |= 24;
+ break;
+ case SkBitmap::kA8_Config:
+ index |= 32;
+ fPaintPMColor = SkPreMultiplyColor(paint.getColor());
+ default:
+ return false;
+ }
+
+ static const SampleProc32 gSample32[] = {
+ S32_opaque_D32_nofilter_DXDY,
+ S32_alpha_D32_nofilter_DXDY,
+ S32_opaque_D32_nofilter_DX,
+ S32_alpha_D32_nofilter_DX,
+ S32_opaque_D32_filter_DXDY,
+ S32_alpha_D32_filter_DXDY,
+ S32_opaque_D32_filter_DX,
+ S32_alpha_D32_filter_DX,
+
+ S16_opaque_D32_nofilter_DXDY,
+ S16_alpha_D32_nofilter_DXDY,
+ S16_opaque_D32_nofilter_DX,
+ S16_alpha_D32_nofilter_DX,
+ S16_opaque_D32_filter_DXDY,
+ S16_alpha_D32_filter_DXDY,
+ S16_opaque_D32_filter_DX,
+ S16_alpha_D32_filter_DX,
+
+ SI8_opaque_D32_nofilter_DXDY,
+ SI8_alpha_D32_nofilter_DXDY,
+ SI8_opaque_D32_nofilter_DX,
+ SI8_alpha_D32_nofilter_DX,
+ SI8_opaque_D32_filter_DXDY,
+ SI8_alpha_D32_filter_DXDY,
+ SI8_opaque_D32_filter_DX,
+ SI8_alpha_D32_filter_DX,
+
+ S4444_opaque_D32_nofilter_DXDY,
+ S4444_alpha_D32_nofilter_DXDY,
+ S4444_opaque_D32_nofilter_DX,
+ S4444_alpha_D32_nofilter_DX,
+ S4444_opaque_D32_filter_DXDY,
+ S4444_alpha_D32_filter_DXDY,
+ S4444_opaque_D32_filter_DX,
+ S4444_alpha_D32_filter_DX,
+
+ // A8 treats alpha/opauqe the same (equally efficient)
+ SA8_alpha_D32_nofilter_DXDY,
+ SA8_alpha_D32_nofilter_DXDY,
+ SA8_alpha_D32_nofilter_DX,
+ SA8_alpha_D32_nofilter_DX,
+ SA8_alpha_D32_filter_DXDY,
+ SA8_alpha_D32_filter_DXDY,
+ SA8_alpha_D32_filter_DX,
+ SA8_alpha_D32_filter_DX
+ };
+
+ static const SampleProc16 gSample16[] = {
+ S32_D16_nofilter_DXDY,
+ S32_D16_nofilter_DX,
+ S32_D16_filter_DXDY,
+ S32_D16_filter_DX,
+
+ S16_D16_nofilter_DXDY,
+ S16_D16_nofilter_DX,
+ S16_D16_filter_DXDY,
+ S16_D16_filter_DX,
+
+ SI8_D16_nofilter_DXDY,
+ SI8_D16_nofilter_DX,
+ SI8_D16_filter_DXDY,
+ SI8_D16_filter_DX,
+
+ // Don't support 4444 -> 565
+ NULL, NULL, NULL, NULL,
+ // Don't support A8 -> 565
+ NULL, NULL, NULL, NULL
+ };
+
+ fSampleProc32 = gSample32[index];
+ index >>= 1; // shift away any opaque/alpha distinction
+ fSampleProc16 = gSample16[index];
+
+ return true;
+}
+
diff --git a/src/core/SkBitmapProcState.h b/src/core/SkBitmapProcState.h
new file mode 100644
index 0000000000..1366d3bccc
--- /dev/null
+++ b/src/core/SkBitmapProcState.h
@@ -0,0 +1,78 @@
+/*
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef SkBitmapProcState_DEFINED
+#define SkBitmapProcState_DEFINED
+
+#include "SkBitmap.h"
+#include "SkMatrix.h"
+
+class SkPaint;
+
+struct SkBitmapProcState {
+
+ typedef void (*MatrixProc)(const SkBitmapProcState&,
+ uint32_t bitmapXY[],
+ int count,
+ int x, int y);
+
+ typedef void (*SampleProc32)(const SkBitmapProcState&,
+ const uint32_t[],
+ int count,
+ SkPMColor colors[]);
+
+ typedef void (*SampleProc16)(const SkBitmapProcState&,
+ const uint32_t[],
+ int count,
+ uint16_t colors[]);
+
+ typedef U16CPU (*FixedTileProc)(SkFixed); // returns 0..0xFFFF
+
+ MatrixProc fMatrixProc; // chooseProcs
+ SampleProc32 fSampleProc32; // chooseProcs
+ SampleProc16 fSampleProc16; // chooseProcs
+
+ SkMatrix fUnitInvMatrix; // chooseProcs
+ FixedTileProc fTileProcX; // chooseProcs
+ FixedTileProc fTileProcY; // chooseProcs
+ SkFixed fFilterOneX;
+ SkFixed fFilterOneY;
+
+ const SkBitmap* fBitmap; // chooseProcs - orig or mip
+ SkBitmap fOrigBitmap; // CONSTRUCTOR
+#ifdef SK_SUPPORT_MIPMAP
+ SkBitmap fMipBitmap;
+#endif
+ SkPMColor fPaintPMColor; // chooseProcs - A8 config
+ const SkMatrix* fInvMatrix; // chooseProcs
+ SkMatrix::MapXYProc fInvProc; // chooseProcs
+ SkFixed fInvSx, fInvSy; // chooseProcs
+ SkFixed fInvKy; // chooseProcs
+ SkFixed fInvTxPlusHalf; // chooseProcs
+ SkFixed fInvTyPlusHalf; // chooseProcs
+ uint16_t fAlphaScale; // chooseProcs
+ uint8_t fInvType; // chooseProcs
+ uint8_t fTileModeX; // CONSTRUCTOR
+ uint8_t fTileModeY; // CONSTRUCTOR
+ SkBool8 fDoFilter; // chooseProcs
+
+ bool chooseProcs(const SkMatrix& inv, const SkPaint&);
+
+private:
+ MatrixProc chooseMatrixProc();
+};
+
+#endif
diff --git a/src/core/SkBitmapProcState_matrix.h b/src/core/SkBitmapProcState_matrix.h
new file mode 100644
index 0000000000..f54f8b10fc
--- /dev/null
+++ b/src/core/SkBitmapProcState_matrix.h
@@ -0,0 +1,271 @@
+
+#define SCALE_NOFILTER_NAME MAKENAME(_nofilter_scale)
+#define SCALE_FILTER_NAME MAKENAME(_filter_scale)
+#define AFFINE_NOFILTER_NAME MAKENAME(_nofilter_affine)
+#define AFFINE_FILTER_NAME MAKENAME(_filter_affine)
+#define PERSP_NOFILTER_NAME MAKENAME(_nofilter_persp)
+#define PERSP_FILTER_NAME MAKENAME(_filter_persp)
+
+#define PACK_FILTER_X_NAME MAKENAME(_pack_filter_x)
+#define PACK_FILTER_Y_NAME MAKENAME(_pack_filter_y)
+
+#ifndef PREAMBLE
+ #define PREAMBLE(state)
+ #define PREAMBLE_PARAM_X
+ #define PREAMBLE_PARAM_Y
+ #define PREAMBLE_ARG_X
+ #define PREAMBLE_ARG_Y
+#endif
+
+static void SCALE_NOFILTER_NAME(const SkBitmapProcState& s,
+ uint32_t xy[], int count, int x, int y) {
+ SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
+ SkMatrix::kScale_Mask)) == 0);
+
+ PREAMBLE(s);
+ // we store y, x, x, x, x, x
+
+ const unsigned maxX = s.fBitmap->width() - 1;
+ const SkFixed dx = s.fInvSx;
+ SkFixed fx;
+ {
+ SkPoint pt;
+ s.fInvProc(*s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
+ SkIntToScalar(y) + SK_ScalarHalf, &pt);
+ fx = SkScalarToFixed(pt.fY);
+ const unsigned maxY = s.fBitmap->height() - 1;
+ *xy++ = TILEY_PROCF(fx, maxY);
+ fx = SkScalarToFixed(pt.fX);
+ }
+
+#ifdef CHECK_FOR_DECAL
+ // test if we don't need to apply the tile proc
+ if ((unsigned)(fx >> 16) <= maxX &&
+ (unsigned)((fx + dx * (count - 1)) >> 16) <= maxX) {
+ decal_nofilter_scale(xy, fx, dx, count);
+ } else
+#endif
+ {
+ int i;
+ for (i = (count >> 2); i > 0; --i) {
+ unsigned a, b;
+ a = TILEX_PROCF(fx, maxX); fx += dx;
+ b = TILEX_PROCF(fx, maxX); fx += dx;
+#ifdef SK_CPU_BENDIAN
+ *xy++ = (a << 16) | b;
+#else
+ *xy++ = (b << 16) | a;
+#endif
+ a = TILEX_PROCF(fx, maxX); fx += dx;
+ b = TILEX_PROCF(fx, maxX); fx += dx;
+#ifdef SK_CPU_BENDIAN
+ *xy++ = (a << 16) | b;
+#else
+ *xy++ = (b << 16) | a;
+#endif
+ }
+ uint16_t* xx = (uint16_t*)xy;
+ for (i = (count & 3); i > 0; --i) {
+ *xx++ = TILEX_PROCF(fx, maxX); fx += dx;
+ }
+ }
+}
+
+// note: we could special-case on a matrix which is skewed in X but not Y.
+// this would require a more general setup thatn SCALE does, but could use
+// SCALE's inner loop that only looks at dx
+
+static void AFFINE_NOFILTER_NAME(const SkBitmapProcState& s,
+ uint32_t xy[], int count, int x, int y) {
+ SkASSERT(s.fInvType & SkMatrix::kAffine_Mask);
+ SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
+ SkMatrix::kScale_Mask |
+ SkMatrix::kAffine_Mask)) == 0);
+
+ PREAMBLE(s);
+ SkPoint srcPt;
+ s.fInvProc(*s.fInvMatrix,
+ SkIntToScalar(x) + SK_ScalarHalf,
+ SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
+
+ SkFixed fx = SkScalarToFixed(srcPt.fX);
+ SkFixed fy = SkScalarToFixed(srcPt.fY);
+ SkFixed dx = s.fInvSx;
+ SkFixed dy = s.fInvKy;
+ int maxX = s.fBitmap->width() - 1;
+ int maxY = s.fBitmap->height() - 1;
+
+ for (int i = count; i > 0; --i) {
+ *xy++ = (TILEY_PROCF(fy, maxY) << 16) | TILEX_PROCF(fx, maxX);
+ fx += dx; fy += dy;
+ }
+}
+
+static void PERSP_NOFILTER_NAME(const SkBitmapProcState& s,
+ uint32_t* SK_RESTRICT xy,
+ int count, int x, int y) {
+ SkASSERT(s.fInvType & SkMatrix::kPerspective_Mask);
+
+ PREAMBLE(s);
+ int maxX = s.fBitmap->width() - 1;
+ int maxY = s.fBitmap->height() - 1;
+
+ SkPerspIter iter(*s.fInvMatrix,
+ SkIntToScalar(x) + SK_ScalarHalf,
+ SkIntToScalar(y) + SK_ScalarHalf, count);
+
+ while ((count = iter.next()) != 0) {
+ const SkFixed* SK_RESTRICT srcXY = iter.getXY();
+ while (--count >= 0) {
+ *xy++ = (TILEY_PROCF(srcXY[1], maxY) << 16) |
+ TILEX_PROCF(srcXY[0], maxX);
+ srcXY += 2;
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+static inline uint32_t PACK_FILTER_Y_NAME(SkFixed f, unsigned max,
+ SkFixed one PREAMBLE_PARAM_Y) {
+ unsigned i = TILEY_PROCF(f, max);
+ i = (i << 4) | TILEY_LOW_BITS(f, max);
+ return (i << 14) | (TILEY_PROCF((f + one), max));
+}
+
+static inline uint32_t PACK_FILTER_X_NAME(SkFixed f, unsigned max,
+ SkFixed one PREAMBLE_PARAM_X) {
+ unsigned i = TILEX_PROCF(f, max);
+ i = (i << 4) | TILEX_LOW_BITS(f, max);
+ return (i << 14) | (TILEX_PROCF((f + one), max));
+}
+
+static void SCALE_FILTER_NAME(const SkBitmapProcState& s,
+ uint32_t xy[], int count, int x, int y) {
+ SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
+ SkMatrix::kScale_Mask)) == 0);
+ SkASSERT(s.fInvKy == 0);
+
+ PREAMBLE(s);
+
+ const unsigned maxX = s.fBitmap->width() - 1;
+ const SkFixed one = s.fFilterOneX;
+ const SkFixed dx = s.fInvSx;
+ SkFixed fx;
+
+ {
+ SkPoint pt;
+ s.fInvProc(*s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
+ SkIntToScalar(y) + SK_ScalarHalf, &pt);
+ const SkFixed fy = SkScalarToFixed(pt.fY) - (s.fFilterOneY >> 1);
+ const unsigned maxY = s.fBitmap->height() - 1;
+ // compute our two Y values up front
+ *xy++ = PACK_FILTER_Y_NAME(fy, maxY, s.fFilterOneY PREAMBLE_ARG_Y);
+ // now initialize fx
+ fx = SkScalarToFixed(pt.fX) - (one >> 1);
+ }
+
+#ifdef CHECK_FOR_DECAL
+ // test if we don't need to apply the tile proc
+ if (dx > 0 &&
+ (unsigned)(fx >> 16) <= maxX &&
+ (unsigned)((fx + dx * (count - 1)) >> 16) < maxX) {
+ decal_filter_scale(xy, fx, dx, count);
+ } else
+#endif
+ {
+ do {
+ *xy++ = PACK_FILTER_X_NAME(fx, maxX, one PREAMBLE_ARG_X);
+ fx += dx;
+ } while (--count != 0);
+ }
+}
+
+static void AFFINE_FILTER_NAME(const SkBitmapProcState& s,
+ uint32_t xy[], int count, int x, int y) {
+ SkASSERT(s.fInvType & SkMatrix::kAffine_Mask);
+ SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
+ SkMatrix::kScale_Mask |
+ SkMatrix::kAffine_Mask)) == 0);
+
+ PREAMBLE(s);
+ SkPoint srcPt;
+ s.fInvProc(*s.fInvMatrix,
+ SkIntToScalar(x) + SK_ScalarHalf,
+ SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
+
+ SkFixed oneX = s.fFilterOneX;
+ SkFixed oneY = s.fFilterOneY;
+ SkFixed fx = SkScalarToFixed(srcPt.fX) - (oneX >> 1);
+ SkFixed fy = SkScalarToFixed(srcPt.fY) - (oneY >> 1);
+ SkFixed dx = s.fInvSx;
+ SkFixed dy = s.fInvKy;
+ unsigned maxX = s.fBitmap->width() - 1;
+ unsigned maxY = s.fBitmap->height() - 1;
+
+ do {
+ *xy++ = PACK_FILTER_Y_NAME(fy, maxY, oneY PREAMBLE_ARG_Y);
+ fy += dy;
+ *xy++ = PACK_FILTER_X_NAME(fx, maxX, oneX PREAMBLE_ARG_X);
+ fx += dx;
+ } while (--count != 0);
+}
+
+static void PERSP_FILTER_NAME(const SkBitmapProcState& s,
+ uint32_t* SK_RESTRICT xy, int count,
+ int x, int y) {
+ SkASSERT(s.fInvType & SkMatrix::kPerspective_Mask);
+
+ PREAMBLE(s);
+ unsigned maxX = s.fBitmap->width() - 1;
+ unsigned maxY = s.fBitmap->height() - 1;
+ SkFixed oneX = s.fFilterOneX;
+ SkFixed oneY = s.fFilterOneY;
+
+ SkPerspIter iter(*s.fInvMatrix,
+ SkIntToScalar(x) + SK_ScalarHalf,
+ SkIntToScalar(y) + SK_ScalarHalf, count);
+
+ while ((count = iter.next()) != 0) {
+ const SkFixed* SK_RESTRICT srcXY = iter.getXY();
+ do {
+ *xy++ = PACK_FILTER_Y_NAME(srcXY[1] - (oneY >> 1), maxY,
+ oneY PREAMBLE_ARG_Y);
+ *xy++ = PACK_FILTER_X_NAME(srcXY[0] - (oneX >> 1), maxX,
+ oneX PREAMBLE_ARG_X);
+ srcXY += 2;
+ } while (--count != 0);
+ }
+}
+
+static SkBitmapProcState::MatrixProc MAKENAME(_Procs)[] = {
+ SCALE_NOFILTER_NAME,
+ SCALE_FILTER_NAME,
+ AFFINE_NOFILTER_NAME,
+ AFFINE_FILTER_NAME,
+ PERSP_NOFILTER_NAME,
+ PERSP_FILTER_NAME
+};
+
+#undef MAKENAME
+#undef TILEX_PROCF
+#undef TILEY_PROCF
+#ifdef CHECK_FOR_DECAL
+ #undef CHECK_FOR_DECAL
+#endif
+
+#undef SCALE_NOFILTER_NAME
+#undef SCALE_FILTER_NAME
+#undef AFFINE_NOFILTER_NAME
+#undef AFFINE_FILTER_NAME
+#undef PERSP_NOFILTER_NAME
+#undef PERSP_FILTER_NAME
+
+#undef PREAMBLE
+#undef PREAMBLE_PARAM_X
+#undef PREAMBLE_PARAM_Y
+#undef PREAMBLE_ARG_X
+#undef PREAMBLE_ARG_Y
+
+#undef TILEX_LOW_BITS
+#undef TILEY_LOW_BITS
diff --git a/src/core/SkBitmapProcState_matrixProcs.cpp b/src/core/SkBitmapProcState_matrixProcs.cpp
new file mode 100644
index 0000000000..beb21c8ef7
--- /dev/null
+++ b/src/core/SkBitmapProcState_matrixProcs.cpp
@@ -0,0 +1,199 @@
+#include "SkBitmapProcState.h"
+#include "SkPerspIter.h"
+#include "SkShader.h"
+
+void decal_nofilter_scale(uint32_t dst[], SkFixed fx, SkFixed dx, int count);
+void decal_filter_scale(uint32_t dst[], SkFixed fx, SkFixed dx, int count);
+
+#ifdef SK_CPU_BENDIAN
+ #define PACK_TWO_SHORTS(pri, sec) ((pri) << 16 | (sec))
+#else
+ #define PACK_TWO_SHORTS(pri, sec) ((pri) | ((sec) << 16))
+#endif
+
+#ifdef SK_DEBUG
+ static uint32_t pack_two_shorts(U16CPU pri, U16CPU sec)
+ {
+ SkASSERT((uint16_t)pri == pri);
+ SkASSERT((uint16_t)sec == sec);
+ return PACK_TWO_SHORTS(pri, sec);
+ }
+#else
+ #define pack_two_shorts(pri, sec) PACK_TWO_SHORTS(pri, sec)
+#endif
+
+#define MAKENAME(suffix) ClampX_ClampY ## suffix
+#define TILEX_PROCF(fx, max) SkClampMax((fx) >> 16, max)
+#define TILEY_PROCF(fy, max) SkClampMax((fy) >> 16, max)
+#define TILEX_LOW_BITS(fx, max) (((fx) >> 12) & 0xF)
+#define TILEY_LOW_BITS(fy, max) (((fy) >> 12) & 0xF)
+#define CHECK_FOR_DECAL
+#include "SkBitmapProcState_matrix.h"
+
+#define MAKENAME(suffix) RepeatX_RepeatY ## suffix
+#define TILEX_PROCF(fx, max) (((fx) & 0xFFFF) * ((max) + 1) >> 16)
+#define TILEY_PROCF(fy, max) (((fy) & 0xFFFF) * ((max) + 1) >> 16)
+#define TILEX_LOW_BITS(fx, max) ((((fx) & 0xFFFF) * ((max) + 1) >> 12) & 0xF)
+#define TILEY_LOW_BITS(fy, max) ((((fy) & 0xFFFF) * ((max) + 1) >> 12) & 0xF)
+#include "SkBitmapProcState_matrix.h"
+
+#define MAKENAME(suffix) GeneralXY ## suffix
+#define PREAMBLE(state) SkBitmapProcState::FixedTileProc tileProcX = (state).fTileProcX; \
+ SkBitmapProcState::FixedTileProc tileProcY = (state).fTileProcY
+#define PREAMBLE_PARAM_X , SkBitmapProcState::FixedTileProc tileProcX
+#define PREAMBLE_PARAM_Y , SkBitmapProcState::FixedTileProc tileProcY
+#define PREAMBLE_ARG_X , tileProcX
+#define PREAMBLE_ARG_Y , tileProcY
+#define TILEX_PROCF(fx, max) (tileProcX(fx) * ((max) + 1) >> 16)
+#define TILEY_PROCF(fy, max) (tileProcY(fy) * ((max) + 1) >> 16)
+#define TILEX_LOW_BITS(fx, max) ((tileProcX(fx) * ((max) + 1) >> 12) & 0xF)
+#define TILEY_LOW_BITS(fy, max) ((tileProcY(fy) * ((max) + 1) >> 12) & 0xF)
+#include "SkBitmapProcState_matrix.h"
+
+static inline U16CPU 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 U16CPU fixed_repeat(SkFixed x)
+{
+ return x & 0xFFFF;
+}
+
+static inline U16CPU 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 SkBitmapProcState::FixedTileProc choose_tile_proc(unsigned m)
+{
+ if (SkShader::kClamp_TileMode == m)
+ return fixed_clamp;
+ if (SkShader::kRepeat_TileMode == m)
+ return fixed_repeat;
+ SkASSERT(SkShader::kMirror_TileMode == m);
+ return fixed_mirror;
+}
+
+SkBitmapProcState::MatrixProc SkBitmapProcState::chooseMatrixProc()
+{
+ int index = 0;
+ if (fDoFilter)
+ index = 1;
+ if (fInvType & SkMatrix::kPerspective_Mask)
+ index |= 4;
+ else if (fInvType & SkMatrix::kAffine_Mask)
+ index |= 2;
+
+ if (SkShader::kClamp_TileMode == fTileModeX &&
+ SkShader::kClamp_TileMode == fTileModeY)
+ {
+ // clamp gets special version of filterOne
+ fFilterOneX = SK_Fixed1;
+ fFilterOneY = SK_Fixed1;
+ return ClampX_ClampY_Procs[index];
+ }
+
+ // all remaining procs use this form for filterOne
+ fFilterOneX = SK_Fixed1 / fBitmap->width();
+ fFilterOneY = SK_Fixed1 / fBitmap->height();
+
+ if (SkShader::kRepeat_TileMode == fTileModeX &&
+ SkShader::kRepeat_TileMode == fTileModeY)
+ {
+ return RepeatX_RepeatY_Procs[index];
+ }
+
+ // only general needs these procs
+ fTileProcX = choose_tile_proc(fTileModeX);
+ fTileProcY = choose_tile_proc(fTileModeY);
+ return GeneralXY_Procs[index];
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void decal_nofilter_scale(uint32_t dst[], SkFixed fx, SkFixed dx, int count)
+{
+ int i;
+
+ for (i = (count >> 2); i > 0; --i)
+ {
+ *dst++ = pack_two_shorts(fx >> 16, (fx + dx) >> 16);
+ fx += dx+dx;
+ *dst++ = pack_two_shorts(fx >> 16, (fx + dx) >> 16);
+ fx += dx+dx;
+ }
+ uint16_t* xx = (uint16_t*)dst;
+
+ for (i = (count & 3); i > 0; --i)
+ {
+ *xx++ = SkToU16(fx >> 16); fx += dx;
+ }
+}
+
+void decal_filter_scale(uint32_t dst[], SkFixed fx, SkFixed dx, int count)
+{
+ if (count & 1)
+ {
+ SkASSERT((fx >> (16 + 14)) == 0);
+ *dst++ = (fx >> 12 << 14) | ((fx >> 16) + 1);
+ fx += dx;
+ }
+ while ((count -= 2) >= 0)
+ {
+ SkASSERT((fx >> (16 + 14)) == 0);
+ *dst++ = (fx >> 12 << 14) | ((fx >> 16) + 1);
+ fx += dx;
+
+ *dst++ = (fx >> 12 << 14) | ((fx >> 16) + 1);
+ fx += dx;
+ }
+}
+
+///////////////////////////////////
+
+void repeat_nofilter_identity(uint32_t dst[], int x, int width, int count)
+{
+ if (x >= width)
+ x %= width;
+
+ int i;
+ uint16_t* xx = (uint16_t*)dst;
+
+ // do the first partial run
+ int n = width - x;
+ if (n > count)
+ n = count;
+
+ count -= n;
+ n += x;
+ for (i = x; i < n; i++)
+ *xx++ = SkToU16(i);
+
+ // do all the full-width runs
+ while ((count -= width) >= 0)
+ for (i = 0; i < width; i++)
+ *xx++ = SkToU16(i);
+
+ // final cleanup run
+ count += width;
+ for (i = 0; i < count; i++)
+ *xx++ = SkToU16(i);
+}
+
diff --git a/src/core/SkBitmapProcState_sample.h b/src/core/SkBitmapProcState_sample.h
new file mode 100644
index 0000000000..122ccf8822
--- /dev/null
+++ b/src/core/SkBitmapProcState_sample.h
@@ -0,0 +1,207 @@
+
+#if DSTSIZE==32
+ #define DSTTYPE SkPMColor
+#elif DSTSIZE==16
+ #define DSTTYPE uint16_t
+#else
+ #error "need DSTSIZE to be 32 or 16"
+#endif
+
+static void MAKENAME(_nofilter_DXDY)(const SkBitmapProcState& s,
+ const uint32_t* SK_RESTRICT xy,
+ int count, DSTTYPE* SK_RESTRICT colors) {
+ SkASSERT(count > 0 && colors != NULL);
+ SkASSERT(s.fDoFilter == false);
+ SkDEBUGCODE(CHECKSTATE(s);)
+
+#ifdef PREAMBLE
+ PREAMBLE(s);
+#endif
+ const char* SK_RESTRICT srcAddr = (const char*)s.fBitmap->getPixels();
+ int i, rb = s.fBitmap->rowBytes();
+
+ uint32_t XY;
+ SRCTYPE src;
+
+ for (i = (count >> 1); i > 0; --i) {
+ XY = *xy++;
+ SkASSERT((XY >> 16) < (unsigned)s.fBitmap->height() &&
+ (XY & 0xFFFF) < (unsigned)s.fBitmap->width());
+ src = ((const SRCTYPE*)(srcAddr + (XY >> 16) * rb))[XY & 0xFFFF];
+ *colors++ = RETURNDST(src);
+
+ XY = *xy++;
+ SkASSERT((XY >> 16) < (unsigned)s.fBitmap->height() &&
+ (XY & 0xFFFF) < (unsigned)s.fBitmap->width());
+ src = ((const SRCTYPE*)(srcAddr + (XY >> 16) * rb))[XY & 0xFFFF];
+ *colors++ = RETURNDST(src);
+ }
+ if (count & 1) {
+ XY = *xy++;
+ SkASSERT((XY >> 16) < (unsigned)s.fBitmap->height() &&
+ (XY & 0xFFFF) < (unsigned)s.fBitmap->width());
+ src = ((const SRCTYPE*)(srcAddr + (XY >> 16) * rb))[XY & 0xFFFF];
+ *colors++ = RETURNDST(src);
+ }
+
+#ifdef POSTAMBLE
+ POSTAMBLE(s);
+#endif
+}
+
+static void MAKENAME(_nofilter_DX)(const SkBitmapProcState& s,
+ const uint32_t* SK_RESTRICT xy,
+ int count, DSTTYPE* SK_RESTRICT colors) {
+ SkASSERT(count > 0 && colors != NULL);
+ SkASSERT(s.fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask));
+ SkASSERT(s.fDoFilter == false);
+ SkDEBUGCODE(CHECKSTATE(s);)
+
+#ifdef PREAMBLE
+ PREAMBLE(s);
+#endif
+ const SRCTYPE* SK_RESTRICT srcAddr = (const SRCTYPE*)s.fBitmap->getPixels();
+ int i;
+
+ // bump srcAddr to the proper row, since we're told Y never changes
+ SkASSERT((unsigned)xy[0] < (unsigned)s.fBitmap->height());
+ srcAddr = (const SRCTYPE*)((const char*)srcAddr +
+ xy[0] * s.fBitmap->rowBytes());
+ // buffer is y32, x16, x16, x16, x16, x16
+ const uint16_t* SK_RESTRICT xx = (const uint16_t*)(xy + 1);
+
+ SRCTYPE src;
+
+ for (i = (count >> 2); i > 0; --i) {
+ SkASSERT(*xx < (unsigned)s.fBitmap->width());
+ src = srcAddr[*xx++]; *colors++ = RETURNDST(src);
+
+ SkASSERT(*xx < (unsigned)s.fBitmap->width());
+ src = srcAddr[*xx++]; *colors++ = RETURNDST(src);
+
+ SkASSERT(*xx < (unsigned)s.fBitmap->width());
+ src = srcAddr[*xx++]; *colors++ = RETURNDST(src);
+
+ SkASSERT(*xx < (unsigned)s.fBitmap->width());
+ src = srcAddr[*xx++]; *colors++ = RETURNDST(src);
+ }
+ for (i = (count & 3); i > 0; --i) {
+ SkASSERT(*xx < (unsigned)s.fBitmap->width());
+ src = srcAddr[*xx++]; *colors++ = RETURNDST(src);
+ }
+
+#ifdef POSTAMBLE
+ POSTAMBLE(s);
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void MAKENAME(_filter_DX)(const SkBitmapProcState& s,
+ const uint32_t* SK_RESTRICT xy,
+ int count, DSTTYPE* SK_RESTRICT colors) {
+ SkASSERT(count > 0 && colors != NULL);
+ SkASSERT(s.fDoFilter);
+ SkDEBUGCODE(CHECKSTATE(s);)
+
+#ifdef PREAMBLE
+ PREAMBLE(s);
+#endif
+ const char* SK_RESTRICT srcAddr = (const char*)s.fBitmap->getPixels();
+ unsigned rb = s.fBitmap->rowBytes();
+ unsigned subY;
+ const SRCTYPE* SK_RESTRICT row0;
+ const SRCTYPE* SK_RESTRICT row1;
+
+ // setup row ptrs and update proc_table
+ {
+ uint32_t XY = *xy++;
+ unsigned y0 = XY >> 14;
+ row0 = (const SRCTYPE*)(srcAddr + (y0 >> 4) * rb);
+ row1 = (const SRCTYPE*)(srcAddr + (XY & 0x3FFF) * rb);
+ subY = y0 & 0xF;
+ }
+
+ do {
+ uint32_t XX = *xy++; // x0:14 | 4 | x1:14
+ unsigned x0 = XX >> 14;
+ unsigned x1 = XX & 0x3FFF;
+ unsigned subX = x0 & 0xF;
+ x0 >>= 4;
+
+ uint32_t c = FILTER_PROC(subX, subY,
+ SRC_TO_FILTER(row0[x0]),
+ SRC_TO_FILTER(row0[x1]),
+ SRC_TO_FILTER(row1[x0]),
+ SRC_TO_FILTER(row1[x1]));
+ *colors++ = FILTER_TO_DST(c);
+
+ } while (--count != 0);
+
+#ifdef POSTAMBLE
+ POSTAMBLE(s);
+#endif
+}
+static void MAKENAME(_filter_DXDY)(const SkBitmapProcState& s,
+ const uint32_t* SK_RESTRICT xy,
+ int count, DSTTYPE* SK_RESTRICT colors) {
+ SkASSERT(count > 0 && colors != NULL);
+ SkASSERT(s.fDoFilter);
+ SkDEBUGCODE(CHECKSTATE(s);)
+
+#ifdef PREAMBLE
+ PREAMBLE(s);
+#endif
+ const char* SK_RESTRICT srcAddr = (const char*)s.fBitmap->getPixels();
+ int rb = s.fBitmap->rowBytes();
+
+ do {
+ uint32_t data = *xy++;
+ unsigned y0 = data >> 14;
+ unsigned y1 = data & 0x3FFF;
+ unsigned subY = y0 & 0xF;
+ y0 >>= 4;
+
+ data = *xy++;
+ unsigned x0 = data >> 14;
+ unsigned x1 = data & 0x3FFF;
+ unsigned subX = x0 & 0xF;
+ x0 >>= 4;
+
+ const SRCTYPE* SK_RESTRICT row0 = (const SRCTYPE*)(srcAddr + y0 * rb);
+ const SRCTYPE* SK_RESTRICT row1 = (const SRCTYPE*)(srcAddr + y1 * rb);
+
+ uint32_t c = FILTER_PROC(subX, subY,
+ SRC_TO_FILTER(row0[x0]),
+ SRC_TO_FILTER(row0[x1]),
+ SRC_TO_FILTER(row1[x0]),
+ SRC_TO_FILTER(row1[x1]));
+ *colors++ = FILTER_TO_DST(c);
+ } while (--count != 0);
+
+#ifdef POSTAMBLE
+ POSTAMBLE(s);
+#endif
+}
+
+#undef MAKENAME
+#undef DSTSIZE
+#undef DSTTYPE
+#undef SRCTYPE
+#undef CHECKSTATE
+#undef RETURNDST
+#undef SRC_TO_FILTER
+#undef FILTER_TO_DST
+
+#ifdef PREAMBLE
+ #undef PREAMBLE
+#endif
+#ifdef POSTAMBLE
+ #undef POSTAMBLE
+#endif
+
+#undef FILTER_PROC_TYPE
+#undef GET_FILTER_TABLE
+#undef GET_FILTER_ROW
+#undef GET_FILTER_ROW_PROC
+#undef GET_FILTER_PROC
diff --git a/src/core/SkBitmapSampler.cpp b/src/core/SkBitmapSampler.cpp
new file mode 100644
index 0000000000..045efd1f3c
--- /dev/null
+++ b/src/core/SkBitmapSampler.cpp
@@ -0,0 +1,423 @@
+/* libs/graphics/sgl/SkBitmapSampler.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkBitmapSampler.h"
+
+static SkTileModeProc get_tilemode_proc(SkShader::TileMode mode)
+{
+ switch (mode) {
+ case SkShader::kClamp_TileMode:
+ return do_clamp;
+ case SkShader::kRepeat_TileMode:
+ return do_repeat_mod;
+ case SkShader::kMirror_TileMode:
+ return do_mirror_mod;
+ default:
+ SkASSERT(!"unknown mode");
+ return NULL;
+ }
+}
+
+SkBitmapSampler::SkBitmapSampler(const SkBitmap& bm, bool filter,
+ SkShader::TileMode tmx, SkShader::TileMode tmy)
+ : fBitmap(bm), fFilterBitmap(filter), fTileModeX(tmx), fTileModeY(tmy)
+{
+ SkASSERT(bm.width() > 0 && bm.height() > 0);
+
+ fMaxX = SkToU16(bm.width() - 1);
+ fMaxY = SkToU16(bm.height() - 1);
+
+ fTileProcX = get_tilemode_proc(tmx);
+ fTileProcY = get_tilemode_proc(tmy);
+}
+
+void SkBitmapSampler::setPaint(const SkPaint& paint)
+{
+}
+
+class SkNullBitmapSampler : public SkBitmapSampler {
+public:
+ SkNullBitmapSampler(const SkBitmap& bm, bool filter,
+ SkShader::TileMode tmx, SkShader::TileMode tmy)
+ : SkBitmapSampler(bm, filter, tmx, tmy) {}
+
+ virtual SkPMColor sample(SkFixed x, SkFixed y) const { return 0; }
+};
+
+/////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////
+
+#define BITMAP_CLASSNAME_PREFIX(name) ARGB32##name
+#define BITMAP_PIXEL_TO_PMCOLOR(bitmap, x, y) *bitmap.getAddr32(x, y)
+#include "SkBitmapSamplerTemplate.h"
+
+#include "SkColorPriv.h"
+
+#define BITMAP_CLASSNAME_PREFIX(name) RGB16##name
+#define BITMAP_PIXEL_TO_PMCOLOR(bitmap, x, y) SkPixel16ToPixel32(*bitmap.getAddr16(x, y))
+#include "SkBitmapSamplerTemplate.h"
+
+#define BITMAP_CLASSNAME_PREFIX(name) Index8##name
+#define BITMAP_PIXEL_TO_PMCOLOR(bitmap, x, y) bitmap.getIndex8Color(x, y)
+#include "SkBitmapSamplerTemplate.h"
+
+/////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////
+///////////////// The Bilinear versions
+
+#include "SkFilterProc.h"
+
+class ARGB32_Bilinear_Sampler : public SkBitmapSampler {
+public:
+ ARGB32_Bilinear_Sampler(const SkBitmap& bm, SkShader::TileMode tmx, SkShader::TileMode tmy)
+ : SkBitmapSampler(bm, true, tmx, tmy)
+ {
+ fPtrProcTable = SkGetBilinearFilterPtrProcTable();
+ }
+
+ virtual SkPMColor sample(SkFixed x, SkFixed y) const
+ {
+ const uint32_t *p00, *p01, *p10, *p11;
+
+ // turn pixel centers into the top-left of our filter-box
+ x -= SK_FixedHalf;
+ y -= SK_FixedHalf;
+
+ // compute our pointers
+ {
+ const SkBitmap* bitmap = &fBitmap;
+ int ix = x >> 16;
+ int iy = y >> 16;
+
+ int maxX = fMaxX;
+ SkTileModeProc procX = fTileProcX;
+ int maxY = fMaxY;
+ SkTileModeProc procY = fTileProcY;
+
+ int tmpx = procX(ix, maxX);
+ int tmpy = procY(iy, maxY);
+ p00 = bitmap->getAddr32(tmpx, tmpy);
+
+ int tmpx1 = procX(ix + 1, maxX);
+ p01 = bitmap->getAddr32(tmpx1, tmpy);
+
+ int tmpy1 = procY(iy + 1, maxY);
+ p10 = bitmap->getAddr32(tmpx, tmpy1);
+
+ p11 = bitmap->getAddr32(tmpx1, tmpy1);
+ }
+
+ SkFilterPtrProc proc = SkGetBilinearFilterPtrProc(fPtrProcTable, x, y);
+ return proc(p00, p01, p10, p11);
+ }
+
+private:
+ const SkFilterPtrProc* fPtrProcTable;
+};
+
+class RGB16_Bilinear_Sampler : public SkBitmapSampler {
+public:
+ RGB16_Bilinear_Sampler(const SkBitmap& bm, SkShader::TileMode tmx, SkShader::TileMode tmy)
+ : SkBitmapSampler(bm, true, tmx, tmy)
+ {
+ fProcTable = SkGetBilinearFilterProcTable();
+ }
+
+ virtual SkPMColor sample(SkFixed x, SkFixed y) const
+ {
+ const uint16_t *p00, *p01, *p10, *p11;
+
+ // turn pixel centers into the top-left of our filter-box
+ x -= SK_FixedHalf;
+ y -= SK_FixedHalf;
+
+ // compute our pointers
+ {
+ const SkBitmap* bitmap = &fBitmap;
+ int ix = x >> 16;
+ int iy = y >> 16;
+
+ int maxX = fMaxX;
+ SkTileModeProc procX = fTileProcX;
+ int maxY = fMaxY;
+ SkTileModeProc procY = fTileProcY;
+
+ int tmpx = procX(ix, maxX);
+ int tmpy = procY(iy, maxY);
+ p00 = bitmap->getAddr16(tmpx, tmpy);
+
+ int tmpx1 = procX(ix + 1, maxX);
+ p01 = bitmap->getAddr16(tmpx1, tmpy);
+
+ int tmpy1 = procY(iy + 1, maxY);
+ p10 = bitmap->getAddr16(tmpx, tmpy1);
+
+ p11 = bitmap->getAddr16(tmpx1, tmpy1);
+ }
+
+ SkFilterProc proc = SkGetBilinearFilterProc(fProcTable, x, y);
+ uint32_t c = proc(SkExpand_rgb_16(*p00), SkExpand_rgb_16(*p01),
+ SkExpand_rgb_16(*p10), SkExpand_rgb_16(*p11));
+
+ return SkPixel16ToPixel32((uint16_t)SkCompact_rgb_16(c));
+ }
+
+private:
+ const SkFilterProc* fProcTable;
+};
+
+// If we had a init/term method on sampler, we could avoid the per-pixel
+// call to lockColors/unlockColors
+
+class Index8_Bilinear_Sampler : public SkBitmapSampler {
+public:
+ Index8_Bilinear_Sampler(const SkBitmap& bm, SkShader::TileMode tmx, SkShader::TileMode tmy)
+ : SkBitmapSampler(bm, true, tmx, tmy)
+ {
+ fPtrProcTable = SkGetBilinearFilterPtrProcTable();
+ }
+
+ virtual SkPMColor sample(SkFixed x, SkFixed y) const
+ {
+ const SkBitmap* bitmap = &fBitmap;
+
+ const uint8_t *p00, *p01, *p10, *p11;
+
+ // turn pixel centers into the top-left of our filter-box
+ x -= SK_FixedHalf;
+ y -= SK_FixedHalf;
+
+ // compute our pointers
+ {
+ int ix = x >> 16;
+ int iy = y >> 16;
+
+ int maxX = fMaxX;
+ SkTileModeProc procX = fTileProcX;
+ int maxY = fMaxY;
+ SkTileModeProc procY = fTileProcY;
+
+ int tmpx = procX(ix, maxX);
+ int tmpy = procY(iy, maxY);
+ p00 = bitmap->getAddr8(tmpx, tmpy);
+
+ int tmpx1 = procX(ix + 1, maxX);
+ p01 = bitmap->getAddr8(tmpx1, tmpy);
+
+ int tmpy1 = procY(iy + 1, maxY);
+ p10 = bitmap->getAddr8(tmpx, tmpy1);
+
+ p11 = bitmap->getAddr8(tmpx1, tmpy1);
+ }
+
+ const SkPMColor* colors = bitmap->getColorTable()->lockColors();
+
+ SkFilterPtrProc proc = SkGetBilinearFilterPtrProc(fPtrProcTable, x, y);
+ uint32_t c = proc(&colors[*p00], &colors[*p01], &colors[*p10], &colors[*p11]);
+
+ bitmap->getColorTable()->unlockColors(false);
+
+ return c;
+ }
+
+private:
+ const SkFilterPtrProc* fPtrProcTable;
+};
+
+class A8_Bilinear_Sampler : public SkBitmapSampler {
+public:
+ A8_Bilinear_Sampler(const SkBitmap& bm, SkShader::TileMode tmx, SkShader::TileMode tmy)
+ : SkBitmapSampler(bm, true, tmx, tmy)
+ {
+ fProcTable = SkGetBilinearFilterProcTable();
+ }
+
+ virtual void setPaint(const SkPaint& paint)
+ {
+ fColor = SkPreMultiplyColor(paint.getColor());
+ }
+
+ virtual SkPMColor sample(SkFixed x, SkFixed y) const
+ {
+ const uint8_t *p00, *p01, *p10, *p11;
+
+ // turn pixel centers into the top-left of our filter-box
+ x -= SK_FixedHalf;
+ y -= SK_FixedHalf;
+
+ // compute our pointers
+ {
+ const SkBitmap* bitmap = &fBitmap;
+ int ix = x >> 16;
+ int iy = y >> 16;
+
+ int maxX = fMaxX;
+ SkTileModeProc procX = fTileProcX;
+ int maxY = fMaxY;
+ SkTileModeProc procY = fTileProcY;
+
+ int tmpx = procX(ix, maxX);
+ int tmpy = procY(iy, maxY);
+ p00 = bitmap->getAddr8(tmpx, tmpy);
+
+ int tmpx1 = procX(ix + 1, maxX);
+ p01 = bitmap->getAddr8(tmpx1, tmpy);
+
+ int tmpy1 = procY(iy + 1, maxY);
+ p10 = bitmap->getAddr8(tmpx, tmpy1);
+
+ p11 = bitmap->getAddr8(tmpx1, tmpy1);
+ }
+
+ SkFilterProc proc = SkGetBilinearFilterProc(fProcTable, x, y);
+ int alpha = proc(*p00, *p01, *p10, *p11);
+ return SkAlphaMulQ(fColor, SkAlpha255To256(alpha));
+ }
+
+private:
+ const SkFilterProc* fProcTable;
+ SkPMColor fColor;
+};
+
+class A8_NoFilter_Sampler : public SkBitmapSampler {
+public:
+ A8_NoFilter_Sampler(const SkBitmap& bm, SkShader::TileMode tmx, SkShader::TileMode tmy)
+ : SkBitmapSampler(bm, false, tmx, tmy)
+ {
+ }
+
+ virtual void setPaint(const SkPaint& paint)
+ {
+ fColor = SkPreMultiplyColor(paint.getColor());
+ }
+
+ virtual SkPMColor sample(SkFixed x, SkFixed y) const
+ {
+ int ix = SkFixedFloor(x);
+ int iy = SkFixedFloor(y);
+
+ int alpha = *fBitmap.getAddr8(fTileProcX(ix, fMaxX), fTileProcY(iy, fMaxY));
+ return SkAlphaMulQ(fColor, SkAlpha255To256(alpha));
+ }
+
+private:
+ const SkFilterProc* fProcTable;
+ SkPMColor fColor;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+SkBitmapSampler* SkBitmapSampler::Create(const SkBitmap& bm, bool doFilter,
+ SkShader::TileMode tmx,
+ SkShader::TileMode tmy)
+{
+ switch (bm.getConfig()) {
+ case SkBitmap::kARGB_8888_Config:
+ if (doFilter)
+ return SkNEW_ARGS(ARGB32_Bilinear_Sampler, (bm, tmx, tmy));
+
+ if (tmx == tmy) {
+ switch (tmx) {
+ case SkShader::kClamp_TileMode:
+ return SkNEW_ARGS(ARGB32_Point_Clamp_Sampler, (bm));
+ case SkShader::kRepeat_TileMode:
+ if (is_pow2(bm.width()) && is_pow2(bm.height()))
+ return SkNEW_ARGS(ARGB32_Point_Repeat_Pow2_Sampler, (bm));
+ else
+ return SkNEW_ARGS(ARGB32_Point_Repeat_Mod_Sampler, (bm));
+ case SkShader::kMirror_TileMode:
+ if (is_pow2(bm.width()) && is_pow2(bm.height()))
+ return SkNEW_ARGS(ARGB32_Point_Mirror_Pow2_Sampler, (bm));
+ else
+ return SkNEW_ARGS(ARGB32_Point_Mirror_Mod_Sampler, (bm));
+ default:
+ SkASSERT(!"unknown mode");
+ }
+ }
+ else { // tmx != tmy
+ return SkNEW_ARGS(ARGB32_Point_Sampler, (bm, tmx, tmy));
+ }
+ break;
+
+ case SkBitmap::kRGB_565_Config:
+ if (doFilter)
+ return SkNEW_ARGS(RGB16_Bilinear_Sampler, (bm, tmx, tmy));
+
+ if (tmx == tmy) {
+ switch (tmx) {
+ case SkShader::kClamp_TileMode:
+ return SkNEW_ARGS(RGB16_Point_Clamp_Sampler, (bm));
+ case SkShader::kRepeat_TileMode:
+ if (is_pow2(bm.width()) && is_pow2(bm.height()))
+ return SkNEW_ARGS(RGB16_Point_Repeat_Pow2_Sampler, (bm));
+ else
+ return SkNEW_ARGS(RGB16_Point_Repeat_Mod_Sampler, (bm));
+ case SkShader::kMirror_TileMode:
+ if (is_pow2(bm.width()) && is_pow2(bm.height()))
+ return SkNEW_ARGS(RGB16_Point_Mirror_Pow2_Sampler, (bm));
+ else
+ return SkNEW_ARGS(RGB16_Point_Mirror_Mod_Sampler, (bm));
+ default:
+ SkASSERT(!"unknown mode");
+ }
+ }
+ else { // tmx != tmy
+ return SkNEW_ARGS(RGB16_Point_Sampler, (bm, tmx, tmy));
+ }
+ break;
+
+ case SkBitmap::kIndex8_Config:
+ if (doFilter)
+ return SkNEW_ARGS(Index8_Bilinear_Sampler, (bm, tmx, tmy));
+
+ if (tmx == tmy) {
+ switch (tmx) {
+ case SkShader::kClamp_TileMode:
+ return SkNEW_ARGS(Index8_Point_Clamp_Sampler, (bm));
+ case SkShader::kRepeat_TileMode:
+ if (is_pow2(bm.width()) && is_pow2(bm.height()))
+ return SkNEW_ARGS(Index8_Point_Repeat_Pow2_Sampler, (bm));
+ else
+ return SkNEW_ARGS(Index8_Point_Repeat_Mod_Sampler, (bm));
+ case SkShader::kMirror_TileMode:
+ if (is_pow2(bm.width()) && is_pow2(bm.height()))
+ return SkNEW_ARGS(Index8_Point_Mirror_Pow2_Sampler, (bm));
+ else
+ return SkNEW_ARGS(Index8_Point_Mirror_Mod_Sampler, (bm));
+ default:
+ SkASSERT(!"unknown mode");
+ }
+ }
+ else { // tmx != tmy
+ return SkNEW_ARGS(Index8_Point_Sampler, (bm, tmx, tmy));
+ }
+ break;
+
+ case SkBitmap::kA8_Config:
+ if (doFilter)
+ return SkNEW_ARGS(A8_Bilinear_Sampler, (bm, tmx, tmy));
+ else
+ return SkNEW_ARGS(A8_NoFilter_Sampler, (bm, tmx, tmy));
+ break;
+
+ default:
+ SkASSERT(!"unknown device");
+ }
+ return SkNEW_ARGS(SkNullBitmapSampler, (bm, doFilter, tmx, tmy));
+}
+
diff --git a/src/core/SkBitmapSampler.h b/src/core/SkBitmapSampler.h
new file mode 100644
index 0000000000..eeef3b3828
--- /dev/null
+++ b/src/core/SkBitmapSampler.h
@@ -0,0 +1,170 @@
+/* libs/graphics/sgl/SkBitmapSampler.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#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&, bool filter, SkShader::TileMode tmx, SkShader::TileMode tmy);
+ virtual ~SkBitmapSampler() {}
+
+ const SkBitmap& getBitmap() const { return fBitmap; }
+ bool getFilterBitmap() const { return fFilterBitmap; }
+ SkShader::TileMode getTileModeX() const { return fTileModeX; }
+ SkShader::TileMode getTileModeY() const { return fTileModeY; }
+
+ /** Given a pixel center at [x,y], return the color sample
+ */
+ virtual SkPMColor sample(SkFixed x, SkFixed y) const = 0;
+
+ virtual void setPaint(const SkPaint& paint);
+
+ // This is the factory for finding an optimal subclass
+ static SkBitmapSampler* Create(const SkBitmap&, bool filter,
+ SkShader::TileMode tmx, SkShader::TileMode tmy);
+
+protected:
+ const SkBitmap& fBitmap;
+ uint16_t fMaxX, fMaxY;
+ bool fFilterBitmap;
+ 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/src/core/SkBitmapSamplerTemplate.h b/src/core/SkBitmapSamplerTemplate.h
new file mode 100644
index 0000000000..00df10ce32
--- /dev/null
+++ b/src/core/SkBitmapSamplerTemplate.h
@@ -0,0 +1,116 @@
+/* libs/graphics/sgl/SkBitmapSamplerTemplate.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+/* 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, false, tmx, tmy)
+ {
+ }
+
+ virtual SkPMColor sample(SkFixed x, SkFixed y) const
+ {
+ x = fTileProcX(SkFixedFloor(x), fMaxX);
+ y = fTileProcY(SkFixedFloor(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, false, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode)
+ {
+ }
+
+ virtual SkPMColor sample(SkFixed x, SkFixed y) const
+ {
+ x = do_clamp(SkFixedFloor(x), fMaxX);
+ y = do_clamp(SkFixedFloor(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, false, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode)
+ {
+ }
+
+ virtual SkPMColor sample(SkFixed x, SkFixed y) const
+ {
+ x = do_repeat_pow2(SkFixedFloor(x), fMaxX);
+ y = do_repeat_pow2(SkFixedFloor(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, false, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode)
+ {
+ }
+
+ virtual SkPMColor sample(SkFixed x, SkFixed y) const
+ {
+ x = do_repeat_mod(SkFixedFloor(x), fMaxX);
+ y = do_repeat_mod(SkFixedFloor(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, false, SkShader::kMirror_TileMode, SkShader::kMirror_TileMode)
+ {
+ }
+
+ virtual SkPMColor sample(SkFixed x, SkFixed y) const
+ {
+ x = do_mirror_pow2(SkFixedFloor(x), fMaxX);
+ y = do_mirror_pow2(SkFixedFloor(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, false, SkShader::kMirror_TileMode, SkShader::kMirror_TileMode)
+ {
+ }
+
+ virtual SkPMColor sample(SkFixed x, SkFixed y) const
+ {
+ x = do_mirror_mod(SkFixedFloor(x), fMaxX);
+ y = do_mirror_mod(SkFixedFloor(y), fMaxY);
+ return BITMAP_PIXEL_TO_PMCOLOR(fBitmap, x, y);
+ }
+};
+
+#undef BITMAP_CLASSNAME_PREFIX
+#undef BITMAP_PIXEL_TO_PMCOLOR
diff --git a/src/core/SkBitmapShader.cpp b/src/core/SkBitmapShader.cpp
new file mode 100644
index 0000000000..5d70dd38c2
--- /dev/null
+++ b/src/core/SkBitmapShader.cpp
@@ -0,0 +1,822 @@
+/* Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#if 0
+
+#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 doFilter,
+ TileMode tmx, TileMode tmy)
+ :
+#ifdef SK_SUPPORT_MIPMAP
+ fMipLevel(0), fMipSrcBitmap(src),
+#endif
+ fOrigSrcBitmap(src)
+
+{
+ fFilterBitmap = doFilter;
+ fTileModeX = SkToU8(tmx);
+ fTileModeY = SkToU8(tmy);
+}
+
+SkBitmapShader::SkBitmapShader(SkFlattenableReadBuffer& buffer) :
+ INHERITED(buffer)
+{
+ Bitmap src;
+ buffer.readBitmap(&src);
+#ifdef SK_SUPPORT_MIPMAP
+ fMipLevel = 0;
+ fMipSrcBitmap = src;
+#endif
+ fOrigSrcBitmap = src;
+ fFilterBitmap = buffer.readU8();
+ fTileModeX = buffer.readU8();
+ fTileModeY = buffer.readU8();
+}
+
+void SkBitmapShader::flatten(SkFlattenableWriteBuffer& buffer)
+{
+ this->INHERITED::flatten(buffer);
+ buffer.writeBitmap(&fOrigSrcBitmap);
+ buffer.write8(fFilterBitmap);
+ buffer.write8(fTileModeX);
+ buffer.write8(fTileModeY);
+}
+
+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;
+
+ if (fOrigSrcBitmap.getConfig() == SkBitmap::kNo_Config ||
+ fOrigSrcBitmap.width() == 0 ||
+ fOrigSrcBitmap.height() == 0)
+ return false;
+
+ SkBitmap& bm = fOrigSrcBitmap;
+
+#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);
+ bm = fMipSrcBitmap;
+ }
+ else
+ {
+ fMipLevel = 0;
+ fMipSrcBitmap = fOrigSrcBitmap;
+ }
+#endif
+
+ fFlags = 0;
+ if (paint.getAlpha() == 255 && bm.isOpaque())
+ fFlags |= kOpaqueAlpha_Flag;
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+#include "SkColorPriv.h"
+#include "SkBitmapSampler.h"
+#include "SkPerspIter.h"
+
+class Sampler_BitmapShader : public SkBitmapShader {
+public:
+ Sampler_BitmapShader(const SkBitmap& src, bool doFilter,
+ TileMode tmx, TileMode tmy)
+ : SkBitmapShader(src, doFilter, 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->getFilterBitmap(),
+ this->getTileModeX(), this->getTileModeY());
+ fSampler->setPaint(paint);
+ return true;
+ }
+ return false;
+ }
+
+ enum {
+ kMaxPointStorageCount = 32
+ };
+
+ 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) + SK_ScalarHalf,
+ SkIntToScalar(y) + SK_ScalarHalf, &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
+ {
+ SkPerspIter iter(inv, SkIntToScalar(x) + SK_ScalarHalf,
+ SkIntToScalar(y) + SK_ScalarHalf, count);
+ if (scale == 256)
+ {
+ while ((count = iter.next()) != 0)
+ {
+ const SkFixed* src = iter.getXY();
+ for (int i = 0; i < count; i++)
+ {
+ *dstC++ = sampler->sample(src[0], src[1]);
+ src += 2;
+ }
+ }
+ }
+ else
+ {
+ while ((count = iter.next()) != 0)
+ {
+ const SkFixed* src = iter.getXY();
+ for (int i = 0; i < count; i++)
+ {
+ uint32_t c = sampler->sample(src[0] - SK_FixedHalf, src[1] - SK_FixedHalf);
+ *dstC++ = SkAlphaMulQ(c, scale);
+ src += 2;
+ }
+ }
+ }
+ }
+ }
+
+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.postIDiv(src.width(), src.height());
+ 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 doFilter,
+ TileMode tmx, TileMode tmy)
+ : Sampler_BitmapShader(src, doFilter, tmx, tmy)
+ {
+ }
+
+ virtual uint32_t getFlags()
+ {
+ uint32_t flags = this->INHERITED::getFlags();
+
+ switch (this->getSrcBitmap().getConfig()) {
+ case SkBitmap::kRGB_565_Config:
+ flags |= kHasSpan16_Flag;
+ break;
+ case SkBitmap::kIndex8_Config:
+ case SkBitmap::kARGB_8888_Config:
+ if (this->getSrcBitmap().isOpaque())
+ flags |= kHasSpan16_Flag;
+ break;
+ default:
+ break;
+ }
+ return flags;
+ }
+
+ const SkBitmap& revealSrcBitmap() const { return this->getSrcBitmap(); }
+ uint8_t revealPaintAlpha() const { return this->getPaintAlpha(); }
+ const SkMatrix& revealTotalInverse() const { return this->getTotalInverse(); }
+
+private:
+ typedef Sampler_BitmapShader INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////
+
+static void Index8_RepeatTile_Sprite16(HasSpan16_Sampler_BitmapShader* shader,
+ int x, int y, uint16_t dstC[], int count)
+{
+ const SkMatrix& inv = shader->revealTotalInverse();
+ const SkBitmap& srcBitmap = shader->revealSrcBitmap();
+ int width = srcBitmap.width();
+ int height = srcBitmap.height();
+
+ SkColorTable* ctable = srcBitmap.getColorTable();
+ const uint16_t* colors = ctable->lock16BitCache();
+
+ x += SkScalarRound(inv[SkMatrix::kMTransX]);
+ y += SkScalarRound(inv[SkMatrix::kMTransY]);
+
+ x = do_repeat_mod(x, width - 1);
+ y = do_repeat_mod(y, height - 1);
+ const uint8_t* row = srcBitmap.getAddr8(0, y);
+ const uint8_t* src = row + x;
+
+ // do the first partial run
+ int n = width - x;
+ if (n > count) n = count;
+ count -= n;
+ SkASSERT(n > 0);
+ do {
+ *dstC++ = colors[*src++];
+ } while (--n > 0);
+
+ // do 1 complete run
+ if (count >= width)
+ {
+ uint16_t* baseDstC = dstC; // remember the first complete run start
+ n = width;
+ count -= width;
+ src = row;
+ do {
+ *dstC++ = colors[*src++];
+ } while (--n > 0);
+
+ // do the rest of the complete runs
+ while (count >= width)
+ {
+ count -= width;
+ memcpy(dstC, baseDstC, width << 1);
+ dstC += width;
+ }
+ // do final partial run
+ if (count > 0)
+ memcpy(dstC, baseDstC, count << 1);
+ }
+ else // do final partial
+ {
+ if (count > 0)
+ {
+ src = row;
+ do {
+ *dstC++ = colors[*src++];
+ } while (--count > 0);
+ }
+ }
+
+ ctable->unlock16BitCache();
+}
+
+static void Index8_RepeatTile_Sprite32(HasSpan16_Sampler_BitmapShader* shader,
+ int x, int y, SkPMColor dstC[], int count)
+{
+ const SkMatrix& inv = shader->revealTotalInverse();
+ const SkBitmap& srcBitmap = shader->revealSrcBitmap();
+ int width = srcBitmap.width();
+ int height = srcBitmap.height();
+
+ SkColorTable* ctable = srcBitmap.getColorTable();
+ const SkPMColor* colors = ctable->lockColors();
+
+ x += SkScalarRound(inv[SkMatrix::kMTransX]);
+ y += SkScalarRound(inv[SkMatrix::kMTransY]);
+
+ x = do_repeat_mod(x, width - 1);
+ y = do_repeat_mod(y, height - 1);
+
+ const uint8_t* row = srcBitmap.getAddr8(0, y);
+ const uint8_t* src = row + x;
+
+ // do the first partial run
+ int n = width - x;
+ if (n > count) n = count;
+ count -= n;
+ SkASSERT(n > 0);
+ do {
+ *dstC++ = colors[*src++];
+ } while (--n > 0);
+
+ // do 1 complete run
+ if (count >= width)
+ {
+ SkPMColor* baseDstC = dstC; // remember the first complete run start
+ n = width;
+ count -= width;
+ src = row;
+ do {
+ *dstC++ = colors[*src++];
+ } while (--n > 0);
+
+ // do the rest of the complete runs
+ while (count >= width)
+ {
+ count -= width;
+ memcpy(dstC, baseDstC, width << 2);
+ dstC += width;
+ }
+ // do final partial run
+ if (count > 0)
+ memcpy(dstC, baseDstC, count << 2);
+ }
+ else // do final partial
+ {
+ if (count > 0)
+ {
+ src = row;
+ do {
+ *dstC++ = colors[*src++];
+ } while (--count > 0);
+ }
+ }
+
+ ctable->unlockColors(false);
+}
+
+static void RGB16_RepeatTile_Sprite16(HasSpan16_Sampler_BitmapShader* shader,
+ int x, int y, uint16_t dstC[], int count)
+{
+ SkASSERT(count > 0);
+
+ const SkMatrix& inv = shader->revealTotalInverse();
+ const SkBitmap& srcBitmap = shader->revealSrcBitmap();
+ int width = srcBitmap.width();
+ int height = srcBitmap.height();
+
+ SkASSERT(width > 0 && height > 0);
+
+ x += SkScalarRound(inv[SkMatrix::kMTransX]);
+ y += SkScalarRound(inv[SkMatrix::kMTransY]);
+
+ x = do_repeat_mod(x, width - 1);
+ y = do_repeat_mod(y, height - 1);
+
+ const uint16_t* row = srcBitmap.getAddr16(0, y);
+ const uint16_t* src = row + x;
+
+ int n = SkMin32(width - x, count);
+
+ for (;;)
+ {
+ SkASSERT(n > 0 && count >= n);
+ memcpy(dstC, src, n << 1);
+ count -= n;
+ if (count == 0)
+ break;
+ dstC += n;
+ src = row;
+ n = SkMin32(width, count);
+ }
+}
+
+static void RGB16_RepeatTile_Sprite32(HasSpan16_Sampler_BitmapShader* shader,
+ int x, int y, SkPMColor dstC[], int count)
+{
+ SkASSERT(count > 0);
+
+ const SkMatrix& inv = shader->revealTotalInverse();
+ const SkBitmap& srcBitmap = shader->revealSrcBitmap();
+ int width = srcBitmap.width();
+ int height = srcBitmap.height();
+
+ SkASSERT(width > 0 && height > 0);
+
+ x += SkScalarRound(inv[SkMatrix::kMTransX]);
+ y += SkScalarRound(inv[SkMatrix::kMTransY]);
+
+ x = do_repeat_mod(x, width - 1);
+ y = do_repeat_mod(y, height - 1);
+
+ const uint16_t* row = srcBitmap.getAddr16(0, y);
+ const uint16_t* src = row + x;
+
+ int n = SkMin32(width - x, count);
+
+ // do the first partial run
+ count -= n;
+ SkASSERT(n > 0);
+ do {
+ *dstC++ = SkPixel16ToPixel32(*src++);
+ } while (--n > 0);
+
+ // do 1 complete run
+ if (count >= width)
+ {
+ SkPMColor* baseDstC = dstC; // remember the first complete run start
+ n = width;
+ count -= width;
+ src = row;
+ do {
+ *dstC++ = SkPixel16ToPixel32(*src++);
+ } while (--n > 0);
+
+ // do the rest of the complete runs
+ while (count >= width)
+ {
+ count -= width;
+ memcpy(dstC, baseDstC, width << 2);
+ dstC += width;
+ }
+ // do final partial run
+ if (count > 0)
+ memcpy(dstC, baseDstC, count << 2);
+ }
+ else // do final partial
+ {
+ if (count > 0)
+ {
+ src = row;
+ do {
+ *dstC++ = SkPixel16ToPixel32(*src++);;
+ } while (--count > 0);
+ }
+ }
+}
+
+static void ARGB32_RepeatTile_Sprite16(HasSpan16_Sampler_BitmapShader* shader,
+ int x, int y, uint16_t dstC[], int count)
+{
+ SkASSERT(count > 0);
+
+ const SkMatrix& inv = shader->revealTotalInverse();
+ const SkBitmap& srcBitmap = shader->revealSrcBitmap();
+ int width = srcBitmap.width();
+ int height = srcBitmap.height();
+
+ SkASSERT(width > 0 && height > 0);
+
+ x += SkScalarRound(inv[SkMatrix::kMTransX]);
+ y += SkScalarRound(inv[SkMatrix::kMTransY]);
+
+ x = do_repeat_mod(x, width - 1);
+ y = do_repeat_mod(y, height - 1);
+
+ const SkPMColor* row = srcBitmap.getAddr32(0, y);
+ const SkPMColor* src = row + x;
+
+ int n = SkMin32(width - x, count);
+
+ // do the first partial run
+ count -= n;
+ SkASSERT(n > 0);
+ do {
+ *dstC++ = SkPixel32ToPixel16(*src++);
+ } while (--n > 0);
+
+ // do 1 complete run
+ if (count >= width)
+ {
+ uint16_t* baseDstC = dstC; // remember the first complete run start
+ n = width;
+ count -= width;
+ src = row;
+ do {
+ *dstC++ = SkPixel32ToPixel16(*src++);
+ } while (--n > 0);
+
+ // do the rest of the complete runs
+ while (count >= width)
+ {
+ count -= width;
+ memcpy(dstC, baseDstC, width << 1);
+ dstC += width;
+ }
+ // do final partial run
+ if (count > 0)
+ memcpy(dstC, baseDstC, count << 1);
+ }
+ else // do final partial
+ {
+ if (count > 0)
+ {
+ src = row;
+ do {
+ *dstC++ = SkPixel32ToPixel16(*src++);;
+ } while (--count > 0);
+ }
+ }
+}
+
+static void ARGB32_RepeatTile_Sprite32(HasSpan16_Sampler_BitmapShader* shader,
+ int x, int y, SkPMColor dstC[], int count)
+{
+ SkASSERT(count > 0);
+
+ const SkMatrix& inv = shader->revealTotalInverse();
+ const SkBitmap& srcBitmap = shader->revealSrcBitmap();
+ int width = srcBitmap.width();
+ int height = srcBitmap.height();
+
+ SkASSERT(width > 0 && height > 0);
+
+ x += SkScalarRound(inv[SkMatrix::kMTransX]);
+ y += SkScalarRound(inv[SkMatrix::kMTransY]);
+
+ x = do_repeat_mod(x, width - 1);
+ y = do_repeat_mod(y, height - 1);
+
+ const SkPMColor* row = srcBitmap.getAddr32(0, y);
+ const SkPMColor* src = row + x;
+
+ int n = SkMin32(width - x, count);
+
+ for (;;)
+ {
+ SkASSERT(n > 0 && count >= n);
+ memcpy(dstC, src, n << 2);
+ count -= n;
+ if (count == 0)
+ break;
+ dstC += n;
+ src = row;
+ n = SkMin32(width, count);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+#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
+#define NOFILTER_BITMAP_SHADER_SPRITEPROC16 Index8_RepeatTile_Sprite16
+#define NOFILTER_BITMAP_SHADER_SPRITEPROC32 Index8_RepeatTile_Sprite32
+#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
+#define NOFILTER_BITMAP_SHADER_SPRITEPROC16 RGB16_RepeatTile_Sprite16
+#define NOFILTER_BITMAP_SHADER_SPRITEPROC32 RGB16_RepeatTile_Sprite32
+#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
+#define NOFILTER_BITMAP_SHADER_SPRITEPROC16 ARGB32_RepeatTile_Sprite16
+#define NOFILTER_BITMAP_SHADER_SPRITEPROC32 ARGB32_RepeatTile_Sprite32
+#include "SkBitmapShaderTemplate.h"
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static inline SkPMColor expanded_rgb16_to_8888(uint32_t c, U8CPU alpha)
+{
+// GGGG Gggg gggR RRRR rrrr r|BB BBBb bbbb
+ SkASSERT(alpha <= 255);
+
+#if 1
+ int scale = SkAlpha255To256(alpha);
+ int r = (c & 0xF800) * scale >> 16;
+ int g = ((c >> 21) & 0x3F) * scale >> 6;
+ int b = (c & 0x1F) * scale >> 5;
+ return SkPackARGB32(alpha, r, g, b);
+#else
+ int scale = SkAlpha255To256(alpha) >> 3;
+ c &= 0x07E0F81F;
+ c = c * scale;
+ int r = (c >> 13) & 0xFF;
+ int g = (c >> 24) & 0xFF;
+ int b = (c >> 2) & 0xFF;
+ return SkPackARGB32(alpha, r, g, b);
+#endif
+}
+
+#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"
+
+#include "ARGB32_Clamp_Bilinear_BitmapShader.h"
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+
+#include "SkBitmapProcShader.h"
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+
+#include "SkTemplatesPriv.h"
+
+SkShader* SkShader::CreateBitmapShader(const SkBitmap& src,
+ bool doFilter,
+ TileMode tmx, TileMode tmy,
+ void* storage, size_t storageSize)
+{
+#if 1
+
+ SkShader* shader;
+ SK_PLACEMENT_NEW_ARGS(shader, SkBitmapProcShader, storage,
+ storageSize, (src, doFilter, tmx, tmy));
+ return shader;
+#else
+
+ if (!doFilter)
+ {
+ if (kClamp_TileMode == tmx && kClamp_TileMode == tmy)
+ {
+ SK_PLACEMENT_NEW_ARGS(shader, SkBitmapProcShader, storage,
+ storageSize, (src, doFilter, tmx, tmy));
+ }
+ else if (kRepeat_TileMode == tmx && kRepeat_TileMode == tmy)
+ {
+ #if 1
+ SK_PLACEMENT_NEW_ARGS(shader, SkBitmapProcShader, storage,
+ storageSize, (src, doFilter, tmx, tmy));
+ #else
+ switch (src.getConfig()) {
+ case SkBitmap::kIndex8_Config:
+ SK_PLACEMENT_NEW_ARGS(shader, Index8_NoFilter_RepeatTile_BitmapShader, storage, storageSize, (src));
+ break;
+ case SkBitmap::kRGB_565_Config:
+ SK_PLACEMENT_NEW_ARGS(shader, U16_NoFilter_RepeatTile_BitmapShader, storage, storageSize, (src));
+ break;
+ case SkBitmap::kARGB_8888_Config:
+ SK_PLACEMENT_NEW_ARGS(shader, U32_NoFilter_RepeatTile_BitmapShader, storage, storageSize, (src));
+ break;
+ default:
+ break;
+ }
+ #endif
+ }
+ }
+ else if (kClamp_TileMode == tmx && kClamp_TileMode == tmy)
+ {
+#if 1
+ if (SkBitmapProcShader::CanDo(src, tmx, tmy))
+ {
+ SK_PLACEMENT_NEW_ARGS(shader, SkBitmapProcShader, storage,
+ storageSize, (src, doFilter, tmx, tmy));
+ }
+#else
+ switch (src.getConfig()) {
+ case SkBitmap::kIndex8_Config:
+ if (src.isOpaque())
+ SK_PLACEMENT_NEW_ARGS(shader, Index8_Bilerp_BitmapShader, storage, storageSize, (src));
+ break;
+ case SkBitmap::kRGB_565_Config:
+ SK_PLACEMENT_NEW_ARGS(shader, U16_Bilerp_BitmapShader, storage, storageSize, (src));
+ break;
+ case SkBitmap::kARGB_8888_Config:
+ SK_PLACEMENT_NEW_ARGS(shader, ARGB32_Clamp_Bilinear_BitmapShader, storage, storageSize, (src));
+ break;
+ default:
+ break;
+ }
+#endif
+ }
+
+ // 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, doFilter, tmx, tmy));
+ return shader;
+#endif
+}
+
+SkShader* SkShader::CreateBitmapShader(const SkBitmap& src, bool doFilter,
+ TileMode tmx, TileMode tmy)
+{
+ return SkShader::CreateBitmapShader(src, doFilter, tmx, tmy, NULL, 0);
+}
+
+#endif
diff --git a/src/core/SkBitmapShader.h b/src/core/SkBitmapShader.h
new file mode 100644
index 0000000000..8d40a4b041
--- /dev/null
+++ b/src/core/SkBitmapShader.h
@@ -0,0 +1,73 @@
+/* libs/graphics/sgl/SkBitmapShader.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#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 doFilter, TileMode tx, TileMode ty);
+
+ virtual bool setContext(const SkBitmap&, const SkPaint& paint, const SkMatrix&);
+ virtual uint32_t getFlags() { return fFlags; }
+
+protected:
+ SkBitmapShader(SkFlattenableReadBuffer& );
+ virtual void flatten(SkFlattenableWriteBuffer& );
+ virtual Factory getFactory() { return CreateProc; }
+ const SkBitmap& getSrcBitmap() const
+ {
+#ifdef SK_SUPPORT_MIPMAP
+ return fMipSrcBitmap;
+#else
+ return fOrigSrcBitmap;
+#endif
+ }
+ bool getFilterBitmap() const { return fFilterBitmap != 0; }
+ 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:
+ static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+ return SkNEW_ARGS(SkBitmapShader, (buffer)); }
+#ifdef SK_SUPPORT_MIPMAP
+ SkFixed fMipLevel;
+ SkBitmap fMipSrcBitmap; // the chosen level (in setContext)
+#endif
+ SkBitmap fOrigSrcBitmap;
+ uint8_t fFilterBitmap;
+ uint8_t fTileModeX;
+ uint8_t fTileModeY;
+ uint8_t fFlags;
+
+ typedef SkShader INHERITED;
+};
+
+#endif
diff --git a/src/core/SkBitmapShader16BilerpTemplate.h b/src/core/SkBitmapShader16BilerpTemplate.h
new file mode 100644
index 0000000000..b70801ea4a
--- /dev/null
+++ b/src/core/SkBitmapShader16BilerpTemplate.h
@@ -0,0 +1,253 @@
+/* libs/graphics/sgl/SkBitmapShader16BilerpTemplate.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkFilterProc.h"
+
+class BILERP_BITMAP16_SHADER_CLASS : public HasSpan16_Sampler_BitmapShader {
+public:
+ BILERP_BITMAP16_SHADER_CLASS(const SkBitmap& src)
+ : HasSpan16_Sampler_BitmapShader(src, true,
+ SkShader::kClamp_TileMode,
+ SkShader::kClamp_TileMode)
+ {
+ }
+
+ virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count)
+ {
+ SkASSERT(count > 0);
+
+ U8CPU alpha = this->getPaintAlpha();
+
+ const SkMatrix& inv = this->getTotalInverse();
+ const SkBitmap& srcBitmap = this->getSrcBitmap();
+ unsigned srcMaxX = srcBitmap.width() - 1;
+ unsigned srcMaxY = srcBitmap.height() - 1;
+ unsigned srcRB = srcBitmap.rowBytes();
+
+ BILERP_BITMAP16_SHADER_PREAMBLE(srcBitmap);
+
+ const SkFilterProc* proc_table = SkGetBilinearFilterProcTable();
+ const BILERP_BITMAP16_SHADER_TYPE* srcPixels = (const BILERP_BITMAP16_SHADER_TYPE*)srcBitmap.getPixels();
+
+ if (this->getInverseClass() == kPerspective_MatrixClass)
+ {
+ SkPerspIter iter(inv, SkIntToScalar(x) + SK_ScalarHalf,
+ SkIntToScalar(y) + SK_ScalarHalf, count);
+ while ((count = iter.next()) != 0)
+ {
+ const SkFixed* srcXY = iter.getXY();
+ while (--count >= 0)
+ {
+ SkFixed fx = *srcXY++ - SK_FixedHalf;
+ SkFixed fy = *srcXY++ - SK_FixedHalf;
+ int ix = fx >> 16;
+ int iy = fy >> 16;
+ int x = SkClampMax(ix, srcMaxX);
+ int y = SkClampMax(iy, srcMaxY);
+
+ const BILERP_BITMAP16_SHADER_TYPE *p00, *p01, *p10, *p11;
+
+ p00 = p01 = ((const BILERP_BITMAP16_SHADER_TYPE*)((const char*)srcPixels + y * srcRB)) + x;
+ if ((unsigned)ix < srcMaxX)
+ p01 += 1;
+ p10 = p00;
+ p11 = p01;
+ if ((unsigned)iy < srcMaxY)
+ {
+ p10 = (const BILERP_BITMAP16_SHADER_TYPE*)((const char*)p10 + srcRB);
+ p11 = (const BILERP_BITMAP16_SHADER_TYPE*)((const char*)p11 + srcRB);
+ }
+
+ SkFilterProc proc = SkGetBilinearFilterProc(proc_table, fx, fy);
+ uint32_t c = proc(SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p00)),
+ SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p01)),
+ SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p10)),
+ SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p11)));
+
+ *dstC++ = expanded_rgb16_to_8888(c, alpha);
+ }
+ }
+ }
+ else // linear case
+ {
+ SkFixed fx, fy, dx, dy;
+
+ // now init fx, fy, dx, dy
+ {
+ SkPoint srcPt;
+ this->getInverseMapPtProc()(inv, SkIntToScalar(x) + SK_ScalarHalf,
+ SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
+
+ fx = SkScalarToFixed(srcPt.fX) - SK_FixedHalf;
+ fy = SkScalarToFixed(srcPt.fY) - SK_FixedHalf;
+
+ if (this->getInverseClass() == kFixedStepInX_MatrixClass)
+ (void)inv.fixedStepInX(SkIntToScalar(y), &dx, &dy);
+ else
+ {
+ dx = SkScalarToFixed(inv.getScaleX());
+ dy = SkScalarToFixed(inv.getSkewY());
+ }
+ }
+
+ do {
+ int ix = fx >> 16;
+ int iy = fy >> 16;
+
+ const BILERP_BITMAP16_SHADER_TYPE *p00, *p01, *p10, *p11;
+
+ p00 = p01 = ((const BILERP_BITMAP16_SHADER_TYPE*)((const char*)srcPixels +
+ SkClampMax(iy, srcMaxY) * srcRB)) +
+ SkClampMax(ix, srcMaxX);
+ if ((unsigned)ix < srcMaxX)
+ p01 += 1;
+ p10 = p00;
+ p11 = p01;
+ if ((unsigned)iy < srcMaxY)
+ {
+ p10 = (const BILERP_BITMAP16_SHADER_TYPE*)((const char*)p10 + srcRB);
+ p11 = (const BILERP_BITMAP16_SHADER_TYPE*)((const char*)p11 + srcRB);
+ }
+
+ SkFilterProc proc = SkGetBilinearFilterProc(proc_table, fx, fy);
+ uint32_t c = proc(SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p00)),
+ SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p01)),
+ SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p10)),
+ SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p11)));
+ *dstC++ = expanded_rgb16_to_8888(c, alpha);
+
+ fx += dx;
+ fy += dy;
+ } while (--count != 0);
+ }
+ BILERP_BITMAP16_SHADER_POSTAMBLE(srcBitmap);
+ }
+
+ virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count)
+ {
+ SkASSERT(count > 0);
+
+ const SkMatrix& inv = this->getTotalInverse();
+ const SkBitmap& srcBitmap = this->getSrcBitmap();
+ unsigned srcMaxX = srcBitmap.width() - 1;
+ unsigned srcMaxY = srcBitmap.height() - 1;
+ unsigned srcRB = srcBitmap.rowBytes();
+
+ BILERP_BITMAP16_SHADER_PREAMBLE(srcBitmap);
+
+ const SkFilterProc* proc_table = SkGetBilinearFilterProcTable();
+ const BILERP_BITMAP16_SHADER_TYPE* srcPixels = (const BILERP_BITMAP16_SHADER_TYPE*)srcBitmap.getPixels();
+
+ if (this->getInverseClass() == kPerspective_MatrixClass)
+ {
+ SkPerspIter iter(inv, SkIntToScalar(x) + SK_ScalarHalf,
+ SkIntToScalar(y) + SK_ScalarHalf, count);
+ while ((count = iter.next()) != 0)
+ {
+ const SkFixed* srcXY = iter.getXY();
+ while (--count >= 0)
+ {
+ SkFixed fx = *srcXY++ - SK_FixedHalf;
+ SkFixed fy = *srcXY++ - SK_FixedHalf;
+ int ix = fx >> 16;
+ int iy = fy >> 16;
+
+ const BILERP_BITMAP16_SHADER_TYPE *p00, *p01, *p10, *p11;
+
+ p00 = p01 = ((const BILERP_BITMAP16_SHADER_TYPE*)((const char*)srcPixels +
+ SkClampMax(iy, srcMaxY) * srcRB)) +
+ SkClampMax(ix, srcMaxX);
+ if ((unsigned)ix < srcMaxX)
+ p01 += 1;
+ p10 = p00;
+ p11 = p01;
+ if ((unsigned)iy < srcMaxY)
+ {
+ p10 = (const BILERP_BITMAP16_SHADER_TYPE*)((const char*)p10 + srcRB);
+ p11 = (const BILERP_BITMAP16_SHADER_TYPE*)((const char*)p11 + srcRB);
+ }
+
+ SkFilterProc proc = SkGetBilinearFilterProc(proc_table, fx, fy);
+ uint32_t c = proc(SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p00)),
+ SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p01)),
+ SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p10)),
+ SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p11)));
+ *dstC++ = SkCompact_rgb_16(c);
+ }
+ }
+ }
+ else // linear case
+ {
+ SkFixed fx, fy, dx, dy;
+
+ // now init fx, fy, dx, dy
+ {
+ SkPoint srcPt;
+ this->getInverseMapPtProc()(inv, SkIntToScalar(x) + SK_ScalarHalf,
+ SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
+
+ fx = SkScalarToFixed(srcPt.fX) - SK_FixedHalf;
+ fy = SkScalarToFixed(srcPt.fY) - SK_FixedHalf;
+
+ if (this->getInverseClass() == kFixedStepInX_MatrixClass)
+ (void)inv.fixedStepInX(SkIntToScalar(y), &dx, &dy);
+ else
+ {
+ dx = SkScalarToFixed(inv.getScaleX());
+ dy = SkScalarToFixed(inv.getSkewY());
+ }
+ }
+
+ do {
+ int ix = fx >> 16;
+ int iy = fy >> 16;
+
+ const BILERP_BITMAP16_SHADER_TYPE *p00, *p01, *p10, *p11;
+
+ p00 = p01 = ((const BILERP_BITMAP16_SHADER_TYPE*)((const char*)srcPixels +
+ SkClampMax(iy, srcMaxY) * srcRB)) +
+ SkClampMax(ix, srcMaxX);
+ if ((unsigned)ix < srcMaxX)
+ p01 += 1;
+ p10 = p00;
+ p11 = p01;
+ if ((unsigned)iy < srcMaxY)
+ {
+ p10 = (const BILERP_BITMAP16_SHADER_TYPE*)((const char*)p10 + srcRB);
+ p11 = (const BILERP_BITMAP16_SHADER_TYPE*)((const char*)p11 + srcRB);
+ }
+
+ SkFilterProc proc = SkGetBilinearFilterProc(proc_table, fx, fy);
+ uint32_t c = proc(SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p00)),
+ SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p01)),
+ SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p10)),
+ SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p11)));
+ *dstC++ = SkCompact_rgb_16(c);
+
+ 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/src/core/SkBitmapShaderTemplate.h b/src/core/SkBitmapShaderTemplate.h
new file mode 100644
index 0000000000..01741386f5
--- /dev/null
+++ b/src/core/SkBitmapShaderTemplate.h
@@ -0,0 +1,314 @@
+/* libs/graphics/sgl/SkBitmapShaderTemplate.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+
+#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)
+ : HasSpan16_Sampler_BitmapShader(src, false,
+ NOFILTER_BITMAP_SHADER_TILEMODE,
+ NOFILTER_BITMAP_SHADER_TILEMODE)
+ {
+ }
+
+ virtual bool setContext(const SkBitmap& device, const SkPaint& paint, const SkMatrix& matrix)
+ {
+ if (!this->INHERITED::setContext(device, paint, matrix))
+ return false;
+
+#ifdef NOFILTER_BITMAP_SHADER_USE_UNITINVERSE
+ this->computeUnitInverse();
+#endif
+ return true;
+ }
+
+ virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count)
+ {
+ SkASSERT(count > 0);
+
+#ifdef NOFILTER_BITMAP_SHADER_SPRITEPROC32
+ if ((this->getTotalInverse().getType() & ~SkMatrix::kTranslate_Mask) == 0)
+ {
+ NOFILTER_BITMAP_SHADER_SPRITEPROC32(this, x, y, dstC, count);
+ return;
+ }
+#endif
+
+ 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;
+
+ const NOFILTER_BITMAP_SHADER_TYPE* srcPixels = (const NOFILTER_BITMAP_SHADER_TYPE*)srcBitmap.getPixels();
+ NOFILTER_BITMAP_SHADER_PREAMBLE(srcBitmap, srcRB);
+
+ if (this->getInverseClass() == kPerspective_MatrixClass)
+ {
+ SkPerspIter iter(inv, SkIntToScalar(x) + SK_ScalarHalf,
+ SkIntToScalar(y) + SK_ScalarHalf, count);
+ while ((count = iter.next()) != 0)
+ {
+ const SkFixed* srcXY = iter.getXY();
+
+/* Do I need this?
+#ifndef NOFILTER_BITMAP_SHADER_USE_UNITINVERSE
+ fx >>= level;
+ fy >>= level;
+#endif
+*/
+ if (256 == scale)
+ {
+ while (--count >= 0)
+ {
+ fx = *srcXY++;
+ fy = *srcXY++;
+ unsigned x = NOFILTER_BITMAP_SHADER_TILEPROC(fx, srcMaxX);
+ unsigned y = NOFILTER_BITMAP_SHADER_TILEPROC(fy, srcMaxY);
+ *dstC++ = NOFILTER_BITMAP_SHADER_SAMPLE_XY(srcPixels, x, y, srcRB);
+ }
+ }
+ else
+ {
+ while (--count >= 0)
+ {
+ fx = *srcXY++;
+ fy = *srcXY++;
+ unsigned x = NOFILTER_BITMAP_SHADER_TILEPROC(fx, srcMaxX);
+ unsigned y = NOFILTER_BITMAP_SHADER_TILEPROC(fy, srcMaxY);
+ uint32_t c = NOFILTER_BITMAP_SHADER_SAMPLE_XY(srcPixels, x, y, srcRB);
+ *dstC++ = SkAlphaMulQ(c, scale);
+ }
+ }
+ }
+ return;
+ }
+
+ // now init fx, fy, dx, dy
+ {
+ SkPoint srcPt;
+ invProc(inv, SkIntToScalar(x) + SK_ScalarHalf,
+ SkIntToScalar(y) + SK_ScalarHalf, &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());
+ }
+ }
+
+#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);
+ SkPMColor 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);
+ SkPMColor 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 shadeSpan16(int x, int y, uint16_t dstC[], int count)
+ {
+ SkASSERT(count > 0);
+ SkASSERT(this->getFlags() & SkShader::kHasSpan16_Flag);
+
+#ifdef NOFILTER_BITMAP_SHADER_SPRITEPROC16
+ if ((this->getTotalInverse().getType() & ~SkMatrix::kTranslate_Mask) == 0)
+ {
+ NOFILTER_BITMAP_SHADER_SPRITEPROC16(this, x, y, dstC, count);
+ return;
+ }
+#endif
+
+#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;
+
+ const NOFILTER_BITMAP_SHADER_TYPE* srcPixels = (const NOFILTER_BITMAP_SHADER_TYPE*)srcBitmap.getPixels();
+ NOFILTER_BITMAP_SHADER_PREAMBLE16(srcBitmap, srcRB);
+
+ if (this->getInverseClass() == kPerspective_MatrixClass)
+ {
+ SkPerspIter iter(inv, SkIntToScalar(x) + SK_ScalarHalf,
+ SkIntToScalar(y) + SK_ScalarHalf, count);
+ while ((count = iter.next()) != 0)
+ {
+ const SkFixed* srcXY = iter.getXY();
+
+ while (--count >= 0)
+ {
+ fx = *srcXY++;
+ fy = *srcXY++;
+ unsigned x = NOFILTER_BITMAP_SHADER_TILEPROC(fx, srcMaxX);
+ unsigned y = NOFILTER_BITMAP_SHADER_TILEPROC(fy, srcMaxY);
+ *dstC++ = NOFILTER_BITMAP_SHADER_SAMPLE_XY16(srcPixels, x, y, srcRB);
+ }
+ }
+ return;
+ }
+
+ // now init fx, fy, dx, dy
+ {
+ SkPoint srcPt;
+ invProc(inv, SkIntToScalar(x) + SK_ScalarHalf,
+ SkIntToScalar(y) + SK_ScalarHalf, &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());
+ }
+ }
+
+#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 {
+ 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_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
+#undef NOFILTER_BITMAP_SHADER_SPRITEPROC16
+#undef NOFILTER_BITMAP_SHADER_SPRITEPROC32
diff --git a/src/core/SkBitmap_scroll.cpp b/src/core/SkBitmap_scroll.cpp
new file mode 100644
index 0000000000..f9f197d581
--- /dev/null
+++ b/src/core/SkBitmap_scroll.cpp
@@ -0,0 +1,107 @@
+#include "SkBitmap.h"
+#include "SkRegion.h"
+
+bool SkBitmap::scrollRect(const SkIRect* subset, int dx, int dy,
+ SkRegion* inval) const
+{
+ if (NULL != subset) {
+ SkBitmap tmp;
+
+ return this->extractSubset(&tmp, *subset) &&
+ // now call again with no rectangle
+ tmp.scrollRect(NULL, dx, dy, inval);
+ }
+
+ int shift;
+
+ switch (this->config()) {
+ case kIndex8_Config:
+ case kA8_Config:
+ shift = 0;
+ break;
+ case kARGB_4444_Config:
+ case kRGB_565_Config:
+ shift = 1;
+ break;
+ case kARGB_8888_Config:
+ shift = 2;
+ break;
+ default:
+ // can't scroll this config
+ return false;
+ }
+
+ int width = this->width();
+ int height = this->height();
+
+ // check if there's nothing to do
+ if ((dx | dy) == 0 || width <= 0 || height <= 0) {
+ if (NULL != inval) {
+ inval->setEmpty();
+ }
+ return true;
+ }
+
+ // compute the inval region now, before we see if there are any pixels
+ if (NULL != inval) {
+ SkIRect r;
+
+ r.set(0, 0, width, height);
+ // initial the region with the entire bounds
+ inval->setRect(r);
+ // do the "scroll"
+ r.offset(dx, dy);
+
+ // check if we scrolled completely away
+ if (!SkIRect::Intersects(r, inval->getBounds())) {
+ // inval has already been updated...
+ return true;
+ }
+
+ // compute the dirty area
+ inval->op(r, SkRegion::kDifference_Op);
+ }
+
+ SkAutoLockPixels alp(*this);
+ // if we have no pixels, just return (inval is already updated)
+ // don't call readyToDraw(), since we don't require a colortable per se
+ if (this->getPixels() == NULL) {
+ return true;
+ }
+
+ // if we get this far, then we need to shift the pixels
+
+ char* dst = (char*)this->getPixels();
+ const char* src = dst;
+ int rowBytes = this->rowBytes(); // need rowBytes to be signed
+
+ if (dy <= 0) {
+ src -= dy * rowBytes;
+ height += dy;
+ } else {
+ dst += dy * rowBytes;
+ height -= dy;
+ // now jump src/dst to the last scanline
+ src += (height - 1) * rowBytes;
+ dst += (height - 1) * rowBytes;
+ // now invert rowbytes so we copy backwards in the loop
+ rowBytes = -rowBytes;
+ }
+
+ if (dx <= 0) {
+ src -= dx << shift;
+ width += dx;
+ } else {
+ dst += dx << shift;
+ width -= dx;
+ }
+
+ width <<= shift; // now width is the number of bytes to move per line
+ while (--height >= 0) {
+ memmove(dst, src, width);
+ dst += rowBytes;
+ src += rowBytes;
+ }
+ return true;
+}
+
diff --git a/src/core/SkBlitBWMaskTemplate.h b/src/core/SkBlitBWMaskTemplate.h
new file mode 100644
index 0000000000..e433d360f3
--- /dev/null
+++ b/src/core/SkBlitBWMaskTemplate.h
@@ -0,0 +1,137 @@
+/* libs/graphics/sgl/SkBlitBWMaskTemplate.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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 SkIRect& 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 SkIRect& 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 uint8_t* 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 uint8_t* 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/src/core/SkBlitRow.h b/src/core/SkBlitRow.h
new file mode 100644
index 0000000000..bb6a29b829
--- /dev/null
+++ b/src/core/SkBlitRow.h
@@ -0,0 +1,22 @@
+#ifndef SkBlitRow_DEFINED
+#define SkBlitRow_DEFINED
+
+#include "SkBitmap.h"
+#include "SkColor.h"
+
+class SkBlitRow {
+public:
+ enum {
+ kGlobalAlpha_Flag = 0x01,
+ kSrcPixelAlpha_Flag = 0x02,
+ kDither_Flag = 0x04
+ };
+
+ typedef void (*Proc)(uint16_t* SK_RESTRICT dst,
+ const SkPMColor* SK_RESTRICT src,
+ int count, U8CPU alpha, int x, int y);
+
+ static Proc Factory(unsigned flags, SkBitmap::Config);
+};
+
+#endif
diff --git a/src/core/SkBlitRow_D16.cpp b/src/core/SkBlitRow_D16.cpp
new file mode 100644
index 0000000000..f40df36da3
--- /dev/null
+++ b/src/core/SkBlitRow_D16.cpp
@@ -0,0 +1,258 @@
+#include "SkBlitRow.h"
+#include "SkColorPriv.h"
+#include "SkDither.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void S32_D565_Opaque(uint16_t* SK_RESTRICT dst,
+ const SkPMColor* SK_RESTRICT src, int count,
+ U8CPU alpha, int /*x*/, int /*y*/) {
+ SkASSERT(255 == alpha);
+
+ if (count > 0) {
+ do {
+ SkPMColor c = *src++;
+ SkPMColorAssert(c);
+ SkASSERT(SkGetPackedA32(c) == 255);
+ *dst++ = SkPixel32ToPixel16_ToU16(c);
+ } while (--count != 0);
+ }
+}
+
+static void S32_D565_Blend(uint16_t* SK_RESTRICT dst,
+ const SkPMColor* SK_RESTRICT src, int count,
+ U8CPU alpha, int /*x*/, int /*y*/) {
+ SkASSERT(255 > alpha);
+
+ if (count > 0) {
+ int scale = SkAlpha255To256(alpha);
+ do {
+ SkPMColor c = *src++;
+ SkPMColorAssert(c);
+ SkASSERT(SkGetPackedA32(c) == 255);
+ uint16_t d = *dst;
+ *dst++ = SkPackRGB16(
+ SkAlphaBlend(SkPacked32ToR16(c), SkGetPackedR16(d), scale),
+ SkAlphaBlend(SkPacked32ToG16(c), SkGetPackedG16(d), scale),
+ SkAlphaBlend(SkPacked32ToB16(c), SkGetPackedB16(d), scale));
+ } while (--count != 0);
+ }
+}
+
+static void S32A_D565_Opaque(uint16_t* SK_RESTRICT dst,
+ const SkPMColor* SK_RESTRICT src, int count,
+ U8CPU alpha, int /*x*/, int /*y*/) {
+ SkASSERT(255 == alpha);
+
+ if (count > 0) {
+ do {
+ SkPMColor c = *src++;
+ SkPMColorAssert(c);
+// if (__builtin_expect(c!=0, 1))
+ if (c) {
+ *dst = SkSrcOver32To16(c, *dst);
+ }
+ dst += 1;
+ } while (--count != 0);
+ }
+}
+
+static void S32A_D565_Blend(uint16_t* SK_RESTRICT dst,
+ const SkPMColor* SK_RESTRICT src, int count,
+ U8CPU alpha, int /*x*/, int /*y*/) {
+ SkASSERT(255 > alpha);
+
+ if (count > 0) {
+ int src_scale = SkAlpha255To256(alpha);
+ do {
+ SkPMColor sc = *src++;
+ SkPMColorAssert(sc);
+ if (sc)
+ {
+ 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);
+ }
+ dst += 1;
+ } while (--count != 0);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+static void S32_D565_Opaque_Dither(uint16_t* SK_RESTRICT dst,
+ const SkPMColor* SK_RESTRICT src,
+ int count, U8CPU alpha, int x, int y) {
+ SkASSERT(255 == alpha);
+
+ if (count > 0) {
+ DITHER_565_SCAN(y);
+ do {
+ SkPMColor c = *src++;
+ SkPMColorAssert(c);
+ SkASSERT(SkGetPackedA32(c) == 255);
+
+ unsigned dither = DITHER_VALUE(x);
+ *dst++ = SkDitherRGB32To565(c, dither);
+ DITHER_INC_X(x);
+ } while (--count != 0);
+ }
+}
+
+static void S32_D565_Blend_Dither(uint16_t* SK_RESTRICT dst,
+ const SkPMColor* SK_RESTRICT src,
+ int count, U8CPU alpha, int x, int y) {
+ SkASSERT(255 > alpha);
+
+ if (count > 0) {
+ int scale = SkAlpha255To256(alpha);
+ DITHER_565_SCAN(y);
+ do {
+ SkPMColor c = *src++;
+ SkPMColorAssert(c);
+ SkASSERT(SkGetPackedA32(c) == 255);
+
+ int dither = DITHER_VALUE(x);
+ int sr = SkGetPackedR32(c);
+ int sg = SkGetPackedG32(c);
+ int sb = SkGetPackedB32(c);
+ sr = SkDITHER_R32To565(sr, dither);
+ sg = SkDITHER_G32To565(sg, dither);
+ sb = SkDITHER_B32To565(sb, dither);
+
+ uint16_t d = *dst;
+ *dst++ = SkPackRGB16(SkAlphaBlend(sr, SkGetPackedR16(d), scale),
+ SkAlphaBlend(sg, SkGetPackedG16(d), scale),
+ SkAlphaBlend(sb, SkGetPackedB16(d), scale));
+ DITHER_INC_X(x);
+ } while (--count != 0);
+ }
+}
+
+static void S32A_D565_Opaque_Dither(uint16_t* SK_RESTRICT dst,
+ const SkPMColor* SK_RESTRICT src,
+ int count, U8CPU alpha, int x, int y) {
+ SkASSERT(255 == alpha);
+
+ if (count > 0) {
+ DITHER_565_SCAN(y);
+ do {
+ SkPMColor c = *src++;
+ SkPMColorAssert(c);
+ if (c) {
+ unsigned a = SkGetPackedA32(c);
+
+ int d = SkAlphaMul(DITHER_VALUE(x), SkAlpha255To256(a));
+
+ unsigned sr = SkGetPackedR32(c);
+ unsigned sg = SkGetPackedG32(c);
+ unsigned sb = SkGetPackedB32(c);
+ sr = SkDITHER_R32_FOR_565(sr, d);
+ sg = SkDITHER_G32_FOR_565(sg, d);
+ sb = SkDITHER_B32_FOR_565(sb, d);
+
+ uint32_t src_expanded = (sg << 24) | (sr << 13) | (sb << 2);
+ uint32_t dst_expanded = SkExpand_rgb_16(*dst);
+ dst_expanded = dst_expanded * (SkAlpha255To256(255 - a) >> 3);
+ // now src and dst expanded are in g:11 r:10 x:1 b:10
+ *dst = SkCompact_rgb_16((src_expanded + dst_expanded) >> 5);
+ }
+ dst += 1;
+ DITHER_INC_X(x);
+ } while (--count != 0);
+ }
+}
+
+static void S32A_D565_Blend_Dither(uint16_t* SK_RESTRICT dst,
+ const SkPMColor* SK_RESTRICT src,
+ int count, U8CPU alpha, int x, int y) {
+ SkASSERT(255 > alpha);
+
+ if (count > 0) {
+ int src_scale = SkAlpha255To256(alpha);
+ DITHER_565_SCAN(y);
+ do {
+ SkPMColor c = *src++;
+ SkPMColorAssert(c);
+ if (c)
+ {
+ unsigned d = *dst;
+ int sa = SkGetPackedA32(c);
+ int dst_scale = SkAlpha255To256(255 - SkAlphaMul(sa, src_scale));
+ int dither = DITHER_VALUE(x);
+
+ int sr = SkGetPackedR32(c);
+ int sg = SkGetPackedG32(c);
+ int sb = SkGetPackedB32(c);
+ sr = SkDITHER_R32To565(sr, dither);
+ sg = SkDITHER_G32To565(sg, dither);
+ sb = SkDITHER_B32To565(sb, dither);
+
+ int dr = (sr * src_scale + SkGetPackedR16(d) * dst_scale) >> 8;
+ int dg = (sg * src_scale + SkGetPackedG16(d) * dst_scale) >> 8;
+ int db = (sb * src_scale + SkGetPackedB16(d) * dst_scale) >> 8;
+
+ *dst = SkPackRGB16(dr, dg, db);
+ }
+ dst += 1;
+ DITHER_INC_X(x);
+ } while (--count != 0);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef USE_T32CB16BLEND_ASM
+ extern "C" void scanline_t32cb16blend_arm(uint16_t*, uint32_t*, size_t);
+#endif
+
+static const SkBlitRow::Proc gProcs16[] = {
+ // no dither
+ S32_D565_Opaque,
+ S32_D565_Blend,
+
+#ifdef USE_T32CB16BLEND_ASM
+ (SkBlitRow::Proc)scanline_t32cb16blend_arm,
+#else
+ S32A_D565_Opaque,
+#endif
+
+ S32A_D565_Blend,
+
+ // dither
+ S32_D565_Opaque_Dither,
+ S32_D565_Blend_Dither,
+
+ S32A_D565_Opaque_Dither,
+ S32A_D565_Blend_Dither
+};
+
+extern SkBlitRow::Proc SkBlitRow_Factory_4444(unsigned flags);
+
+SkBlitRow::Proc SkBlitRow::Factory(unsigned flags, SkBitmap::Config config) {
+ SkASSERT(flags < SK_ARRAY_COUNT(gProcs16));
+
+ switch (config) {
+ case SkBitmap::kRGB_565_Config:
+ return gProcs16[flags];
+ case SkBitmap::kARGB_4444_Config:
+ return SkBlitRow_Factory_4444(flags);
+ default:
+ break;
+ }
+ return NULL;
+}
+
diff --git a/src/core/SkBlitRow_D4444.cpp b/src/core/SkBlitRow_D4444.cpp
new file mode 100644
index 0000000000..e60c721c5f
--- /dev/null
+++ b/src/core/SkBlitRow_D4444.cpp
@@ -0,0 +1,214 @@
+#include "SkBlitRow.h"
+#include "SkColorPriv.h"
+#include "SkDither.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void S32_D4444_Opaque(uint16_t* SK_RESTRICT dst,
+ const SkPMColor* SK_RESTRICT src, int count,
+ U8CPU alpha, int /*x*/, int /*y*/) {
+ SkASSERT(255 == alpha);
+
+ if (count > 0) {
+ do {
+ SkPMColor c = *src++;
+ SkPMColorAssert(c);
+ SkASSERT(SkGetPackedA32(c) == 255);
+ *dst++ = SkPixel32ToPixel4444(c);
+ } while (--count != 0);
+ }
+}
+
+static void S32_D4444_Blend(uint16_t* SK_RESTRICT dst,
+ const SkPMColor* SK_RESTRICT src, int count,
+ U8CPU alpha, int /*x*/, int /*y*/) {
+ SkASSERT(255 > alpha);
+
+ if (count > 0) {
+ unsigned scale16 = SkAlpha255To256(alpha) >> 4;
+ do {
+ SkPMColor c = *src++;
+ SkPMColorAssert(c);
+ SkASSERT(SkGetPackedA32(c) == 255);
+
+ uint32_t src_expand = SkExpand32_4444(c);
+ uint32_t dst_expand = SkExpand_4444(*dst);
+ dst_expand += (src_expand - dst_expand) * scale16 >> 4;
+ *dst++ = SkCompact_4444(dst_expand);
+ } while (--count != 0);
+ }
+}
+
+static void S32A_D4444_Opaque(uint16_t* SK_RESTRICT dst,
+ const SkPMColor* SK_RESTRICT src, int count,
+ U8CPU alpha, int /*x*/, int /*y*/) {
+ SkASSERT(255 == alpha);
+
+ if (count > 0) {
+ do {
+ SkPMColor c = *src++;
+ SkPMColorAssert(c);
+// if (__builtin_expect(c!=0, 1))
+ if (c)
+ {
+ unsigned scale16 = SkAlpha255To256(255 - SkGetPackedA32(c)) >> 4;
+ uint32_t src_expand = SkExpand_8888(c);
+ uint32_t dst_expand = SkExpand_4444(*dst) * scale16;
+ *dst = SkCompact_4444((src_expand + dst_expand) >> 4);
+ }
+ dst += 1;
+ } while (--count != 0);
+ }
+}
+
+static void S32A_D4444_Blend(uint16_t* SK_RESTRICT dst,
+ const SkPMColor* SK_RESTRICT src, int count,
+ U8CPU alpha, int /*x*/, int /*y*/) {
+ SkASSERT(255 > alpha);
+
+ if (count > 0) {
+ int src_scale = SkAlpha255To256(alpha) >> 4;
+ do {
+ SkPMColor sc = *src++;
+ SkPMColorAssert(sc);
+
+ if (sc) {
+ unsigned dst_scale = 16 - (SkGetPackedA32(sc) * src_scale >> 8);
+ uint32_t src_expand = SkExpand32_4444(sc) * src_scale;
+ uint32_t dst_expand = SkExpand_4444(*dst) * dst_scale;
+ *dst = SkCompact_4444((src_expand + dst_expand) >> 4);
+ }
+ dst += 1;
+ } while (--count != 0);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+static void S32_D4444_Opaque_Dither(uint16_t* SK_RESTRICT dst,
+ const SkPMColor* SK_RESTRICT src,
+ int count, U8CPU alpha, int x, int y) {
+ SkASSERT(255 == alpha);
+
+ if (count > 0) {
+ DITHER_4444_SCAN(y);
+ do {
+ SkPMColor c = *src++;
+ SkPMColorAssert(c);
+ SkASSERT(SkGetPackedA32(c) == 255);
+
+ unsigned dither = DITHER_VALUE(x);
+ *dst++ = SkDitherARGB32To4444(c, dither);
+ DITHER_INC_X(x);
+ } while (--count != 0);
+ }
+}
+
+static void S32_D4444_Blend_Dither(uint16_t* SK_RESTRICT dst,
+ const SkPMColor* SK_RESTRICT src,
+ int count, U8CPU alpha, int x, int y) {
+ SkASSERT(255 > alpha);
+
+ if (count > 0) {
+ int scale16 = SkAlpha255To256(alpha) >> 4;
+ DITHER_4444_SCAN(y);
+ do {
+ SkPMColor c = *src++;
+ SkPMColorAssert(c);
+ SkASSERT(SkGetPackedA32(c) == 255);
+
+ uint32_t src_expand = SkExpand32_4444(c) * scale16;
+ uint32_t dst_expand = SkExpand_4444(*dst) * (16 - scale16);
+
+ c = SkCompact_8888(src_expand + dst_expand); // convert back to SkPMColor
+ *dst++ = SkDitherARGB32To4444(c, DITHER_VALUE(x));
+ DITHER_INC_X(x);
+ } while (--count != 0);
+ }
+}
+
+static void S32A_D4444_Opaque_Dither(uint16_t* SK_RESTRICT dst,
+ const SkPMColor* SK_RESTRICT src,
+ int count, U8CPU alpha, int x, int y) {
+ SkASSERT(255 == alpha);
+
+ if (count > 0) {
+ DITHER_4444_SCAN(y);
+ do {
+ SkPMColor c = *src++;
+ SkPMColorAssert(c);
+ if (c) {
+ unsigned a = SkGetPackedA32(c);
+ int d = SkAlphaMul(DITHER_VALUE(x), SkAlpha255To256(a));
+
+ unsigned scale16 = SkAlpha255To256(255 - a) >> 4;
+ uint32_t src_expand = SkExpand_8888(c);
+ uint32_t dst_expand = SkExpand_4444(*dst) * scale16;
+ // convert back to SkPMColor
+ c = SkCompact_8888(src_expand + dst_expand);
+ *dst = SkDitherARGB32To4444(c, d);
+ }
+ dst += 1;
+ DITHER_INC_X(x);
+ } while (--count != 0);
+ }
+}
+
+// need DitherExpand888To4444(expand, dither)
+
+static void S32A_D4444_Blend_Dither(uint16_t* SK_RESTRICT dst,
+ const SkPMColor* SK_RESTRICT src,
+ int count, U8CPU alpha, int x, int y) {
+ SkASSERT(255 > alpha);
+
+ if (count > 0) {
+ int src_scale = SkAlpha255To256(alpha) >> 4;
+ DITHER_4444_SCAN(y);
+ do {
+ SkPMColor c = *src++;
+ SkPMColorAssert(c);
+ if (c) {
+ unsigned a = SkAlpha255To256(SkGetPackedA32(c));
+ int d = SkAlphaMul(DITHER_VALUE(x), a);
+
+ unsigned dst_scale = 16 - SkAlphaMul(src_scale, a);
+ uint32_t src_expand = SkExpand32_4444(c) * src_scale;
+ uint32_t dst_expand = SkExpand_4444(*dst) * dst_scale;
+ // convert back to SkPMColor
+ c = SkCompact_8888(src_expand + dst_expand);
+ *dst = SkDitherARGB32To4444(c, d);
+ }
+ dst += 1;
+ DITHER_INC_X(x);
+ } while (--count != 0);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static const SkBlitRow::Proc gProcs4444[] = {
+ // no dither
+ S32_D4444_Opaque,
+ S32_D4444_Blend,
+
+ S32A_D4444_Opaque,
+ S32A_D4444_Blend,
+
+ // dither
+ S32_D4444_Opaque_Dither,
+ S32_D4444_Blend_Dither,
+
+ S32A_D4444_Opaque_Dither,
+ S32A_D4444_Blend_Dither
+};
+
+SkBlitRow::Proc SkBlitRow_Factory_4444(unsigned flags);
+SkBlitRow::Proc SkBlitRow_Factory_4444(unsigned flags)
+{
+ SkASSERT(flags < SK_ARRAY_COUNT(gProcs4444));
+
+ return gProcs4444[flags];
+}
+
+
diff --git a/src/core/SkBlitter.cpp b/src/core/SkBlitter.cpp
new file mode 100644
index 0000000000..920842937c
--- /dev/null
+++ b/src/core/SkBlitter.cpp
@@ -0,0 +1,923 @@
+/* libs/graphics/sgl/SkBlitter.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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()
+{
+}
+
+const SkBitmap* SkBlitter::justAnOpaqueColor(uint32_t* value)
+{
+ return NULL;
+}
+
+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 SkIRect& 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((uint16_t*)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;
+ }
+ }
+}
+
+/////////////////////// these guys are not virtual, just a helpers
+
+void SkBlitter::blitMaskRegion(const SkMask& mask, const SkRegion& clip) {
+ if (clip.quickReject(mask.fBounds)) {
+ return;
+ }
+
+ SkRegion::Cliperator clipper(clip, mask.fBounds);
+
+ while (!clipper.done()) {
+ const SkIRect& cr = clipper.rect();
+ this->blitMask(mask, cr);
+ clipper.next();
+ }
+}
+
+void SkBlitter::blitRectRegion(const SkIRect& rect, const SkRegion& clip) {
+ SkRegion::Cliperator clipper(clip, rect);
+
+ while (!clipper.done()) {
+ const SkIRect& cr = clipper.rect();
+ this->blitRect(cr.fLeft, cr.fTop, cr.width(), cr.height());
+ clipper.next();
+ }
+}
+
+void SkBlitter::blitRegion(const SkRegion& clip) {
+ SkRegion::Iterator iter(clip);
+
+ while (!iter.done()) {
+ const SkIRect& cr = iter.rect();
+ this->blitRect(cr.fLeft, cr.fTop, cr.width(), cr.height());
+ iter.next();
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////
+
+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)
+{
+}
+
+void SkNullBlitter::blitMask(const SkMask& mask, const SkIRect& clip)
+{
+}
+
+const SkBitmap* SkNullBlitter::justAnOpaqueColor(uint32_t* value)
+{
+ return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////
+
+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;
+}
+
+static inline bool y_in_rect(int y, const SkIRect& rect)
+{
+ return (unsigned)(y - rect.fTop) < (unsigned)rect.height();
+}
+
+static inline bool x_in_rect(int x, const SkIRect& 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, (uint8_t*)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, (uint8_t*)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)
+{
+ SkIRect r;
+
+ r.set(left, y, left + width, y + height);
+ if (r.intersect(fClipRect))
+ fBlitter->blitRect(r.fLeft, r.fTop, r.width(), r.height());
+}
+
+void SkRectClipBlitter::blitMask(const SkMask& mask, const SkIRect& clip)
+{
+ SkASSERT(mask.fBounds.contains(clip));
+
+ SkIRect r = clip;
+
+ if (r.intersect(fClipRect))
+ fBlitter->blitMask(mask, r);
+}
+
+const SkBitmap* SkRectClipBlitter::justAnOpaqueColor(uint32_t* value)
+{
+ return fBlitter->justAnOpaqueColor(value);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////
+
+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;
+ SkDEBUGCODE(const SkIRect& bounds = fRgn->getBounds();)
+
+ int prevRite = x;
+ while (span.next(&left, &right))
+ {
+ SkASSERT(x <= left);
+ SkASSERT(left < right);
+ SkASSERT(left >= bounds.fLeft && right <= bounds.fRight);
+
+ SkAlphaRuns::Break((int16_t*)runs, (uint8_t*)aa, left - x, right - left);
+
+ // now zero before left
+ if (left > prevRite)
+ {
+ int index = prevRite - x;
+ ((uint8_t*)aa)[index] = 0; // skip runs after right
+ ((int16_t*)runs)[index] = SkToS16(left - prevRite);
+ }
+
+ prevRite = right;
+ }
+
+ if (prevRite > x)
+ {
+ ((int16_t*)runs)[prevRite - x] = 0;
+
+ if (x < 0) {
+ int skip = runs[0];
+ SkASSERT(skip >= -x);
+ aa += skip;
+ runs += skip;
+ x += skip;
+ }
+ fBlitter->blitAntiH(x, y, aa, runs);
+ }
+}
+
+void SkRgnClipBlitter::blitV(int x, int y, int height, SkAlpha alpha)
+{
+ SkIRect bounds;
+ bounds.set(x, y, x + 1, y + height);
+
+ SkRegion::Cliperator iter(*fRgn, bounds);
+
+ while (!iter.done())
+ {
+ const SkIRect& 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)
+{
+ SkIRect bounds;
+ bounds.set(x, y, x + width, y + height);
+
+ SkRegion::Cliperator iter(*fRgn, bounds);
+
+ while (!iter.done())
+ {
+ const SkIRect& r = iter.rect();
+ SkASSERT(bounds.contains(r));
+
+ fBlitter->blitRect(r.fLeft, r.fTop, r.width(), r.height());
+ iter.next();
+ }
+}
+
+void SkRgnClipBlitter::blitMask(const SkMask& mask, const SkIRect& clip)
+{
+ SkASSERT(mask.fBounds.contains(clip));
+
+ SkRegion::Cliperator iter(*fRgn, clip);
+ const SkIRect& r = iter.rect();
+ SkBlitter* blitter = fBlitter;
+
+ while (!iter.done())
+ {
+ blitter->blitMask(mask, r);
+ iter.next();
+ }
+}
+
+const SkBitmap* SkRgnClipBlitter::justAnOpaqueColor(uint32_t* value)
+{
+ return fBlitter->justAnOpaqueColor(value);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////
+
+SkBlitter* SkBlitterClipper::apply(SkBlitter* blitter, const SkRegion* clip, const SkIRect* ir)
+{
+ if (clip)
+ {
+ const SkIRect& clipR = clip->getBounds();
+
+ if (clip->isEmpty() || (ir && !SkIRect::Intersects(clipR, *ir)))
+ blitter = &fNullBlitter;
+ else if (clip->isRect())
+ {
+ if (ir == NULL || !clipR.contains(*ir))
+ {
+ fRectBlitter.init(blitter, clipR);
+ blitter = &fRectBlitter;
+ }
+ }
+ else
+ {
+ fRgnBlitter.init(blitter, clip);
+ blitter = &fRgnBlitter;
+ }
+ }
+ return blitter;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkColorShader.h"
+#include "SkColorPriv.h"
+
+class Sk3DShader : public SkShader {
+public:
+ Sk3DShader(SkShader* proxy) : fProxy(proxy)
+ {
+ proxy->safeRef();
+ fMask = NULL;
+ }
+ 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 == NULL)
+ {
+ if (fProxy == NULL)
+ 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 uint8_t* alpha = fMask->getAddr(x, y);
+ const uint8_t* mulp = alpha + size;
+ const uint8_t* addp = mulp + size;
+
+ if (fProxy)
+ {
+ for (int i = 0; i < count; i++)
+ {
+ if (alpha[i])
+ {
+ SkPMColor 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;
+ }
+ }
+ }
+
+ virtual void beginSession()
+ {
+ this->INHERITED::beginSession();
+ if (fProxy)
+ fProxy->beginSession();
+ }
+
+ virtual void endSession()
+ {
+ if (fProxy)
+ fProxy->endSession();
+ this->INHERITED::endSession();
+ }
+
+protected:
+ Sk3DShader(SkFlattenableReadBuffer& buffer) :
+ INHERITED(buffer)
+ {
+ fProxy = static_cast<SkShader*>(buffer.readFlattenable());
+ fPMColor = buffer.readU32();
+ fMask = NULL;
+ }
+
+ virtual void flatten(SkFlattenableWriteBuffer& buffer)
+ {
+ this->INHERITED::flatten(buffer);
+ buffer.writeFlattenable(fProxy);
+ buffer.write32(fPMColor);
+ }
+
+ virtual Factory getFactory()
+ {
+ return CreateProc;
+ }
+
+private:
+ static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer)
+ {
+ return SkNEW_ARGS(Sk3DShader, (buffer));
+ }
+
+ 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 SkIRect& 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(NULL);
+ }
+ 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 = NULL;
+ 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 != NULL);
+
+ SkBlitter* blitter = NULL;
+
+ // which check, in case we're being called by a client with a dummy device
+ // (e.g. they have a bounder that always aborts the draw)
+ if (SkBitmap::kNo_Config == device.getConfig())
+ {
+ SK_PLACEMENT_NEW(blitter, SkNullBlitter, storage, storageSize);
+ return blitter;
+ }
+
+ SkAutoRestoreShader restore(paint);
+ SkShader* shader = paint.getShader();
+
+ Sk3DShader* shader3D = NULL;
+ if (paint.getMaskFilter() != NULL && 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();
+ }
+
+ bool doDither = paint.isDither();
+
+ if (shader)
+ {
+ if (!shader->setContext(device, paint, matrix))
+ return SkNEW(SkNullBlitter);
+
+ // disable dither if our shader is natively 16bit (no need to upsample)
+ if (shader->getFlags() & SkShader::kIntrinsicly16_Flag)
+ doDither = false;
+ }
+
+ 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::kARGB_4444_Config:
+ blitter = SkBlitter_ChooseD4444(device, paint, storage, storageSize);
+ break;
+
+ case SkBitmap::kRGB_565_Config:
+ if (shader)
+ {
+ if (mode)
+ SK_PLACEMENT_NEW_ARGS(blitter, SkRGB16_Shader_Xfermode_Blitter, storage, storageSize, (device, paint));
+ else if (SkShader::CanCallShadeSpan16(shader->getFlags()) && !doDither)
+ SK_PLACEMENT_NEW_ARGS(blitter, SkRGB16_Shader16_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 if (paint.getAlpha() == 0xFF)
+ SK_PLACEMENT_NEW_ARGS(blitter, SkARGB32_Opaque_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;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+
+const uint16_t gMask_0F0F = 0xF0F;
+const uint32_t gMask_00FF00FF = 0xFF00FF;
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+
+SkShaderBlitter::SkShaderBlitter(const SkBitmap& device, const SkPaint& paint)
+ : INHERITED(device)
+{
+ fShader = paint.getShader();
+ SkASSERT(fShader);
+
+ fShader->ref();
+ fShader->beginSession();
+}
+
+SkShaderBlitter::~SkShaderBlitter()
+{
+ fShader->endSession();
+ fShader->unref();
+}
+
diff --git a/src/core/SkBlitter.h b/src/core/SkBlitter.h
new file mode 100644
index 0000000000..11b84fdb37
--- /dev/null
+++ b/src/core/SkBlitter.h
@@ -0,0 +1,143 @@
+/* libs/graphics/sgl/SkBlitter.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#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[], const int16_t 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 SkIRect& clip);
+
+ /* If the blitter just sets a single value for each pixel, return the
+ bitmap it draws into, and assign value. If not, return NULL and ignore
+ the value parameter.
+ */
+ virtual const SkBitmap* justAnOpaqueColor(uint32_t* value);
+
+ // not virtual, just helpers
+ void blitMaskRegion(const SkMask& mask, const SkRegion& clip);
+ void blitRectRegion(const SkIRect& rect, const SkRegion& clip);
+ void blitRegion(const SkRegion& clip);
+
+ // factories
+ static SkBlitter* Choose(const SkBitmap& device,
+ const SkMatrix& matrix,
+ const SkPaint& paint) {
+ return Choose(device, matrix, paint, NULL, 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:
+};
+
+/** This blitter silently never draws anything.
+*/
+class SkNullBlitter : public SkBlitter {
+public:
+ virtual void blitH(int x, int y, int width);
+ virtual void blitAntiH(int x, int y, const SkAlpha[], const int16_t 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 SkIRect& clip);
+ virtual const SkBitmap* justAnOpaqueColor(uint32_t* value);
+};
+
+/** Wraps another (real) blitter, and ensures that the real blitter is only
+ called with coordinates that have been clipped by the specified clipRect.
+ This means the caller need not perform the clipping ahead of time.
+*/
+class SkRectClipBlitter : public SkBlitter {
+public:
+ void init(SkBlitter* blitter, const SkIRect& 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[], const int16_t 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 SkIRect& clip);
+ virtual const SkBitmap* justAnOpaqueColor(uint32_t* value);
+
+private:
+ SkBlitter* fBlitter;
+ SkIRect fClipRect;
+};
+
+/** Wraps another (real) blitter, and ensures that the real blitter is only
+called with coordinates that have been clipped by the specified clipRgn.
+This means the caller need not perform the clipping ahead of time.
+*/
+class SkRgnClipBlitter : public SkBlitter {
+public:
+ void init(SkBlitter* blitter, const SkRegion* clipRgn) {
+ SkASSERT(clipRgn && !clipRgn->isEmpty());
+ fBlitter = blitter;
+ fRgn = clipRgn;
+ }
+
+ // overrides
+ virtual void blitH(int x, int y, int width);
+ virtual void blitAntiH(int x, int y, const SkAlpha[], const int16_t 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 SkIRect& clip);
+ virtual const SkBitmap* justAnOpaqueColor(uint32_t* value);
+
+private:
+ SkBlitter* fBlitter;
+ const SkRegion* fRgn;
+};
+
+class SkBlitterClipper {
+public:
+ SkBlitter* apply(SkBlitter* blitter, const SkRegion* clip,
+ const SkIRect* bounds = NULL);
+
+private:
+ SkNullBlitter fNullBlitter;
+ SkRectClipBlitter fRectBlitter;
+ SkRgnClipBlitter fRgnBlitter;
+};
+
+#endif
diff --git a/src/core/SkBlitter_4444.cpp b/src/core/SkBlitter_4444.cpp
new file mode 100644
index 0000000000..cce94c5156
--- /dev/null
+++ b/src/core/SkBlitter_4444.cpp
@@ -0,0 +1,501 @@
+/* libs/graphics/sgl/SkBlitter_ARGB32.cpp
+ **
+ ** Copyright 2006, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#include "SkCoreBlitters.h"
+#include "SkColorPriv.h"
+#include "SkDither.h"
+#include "SkShader.h"
+#include "SkTemplatesPriv.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+
+inline SkPMColor SkBlendARGB4444(SkPMColor16 src, SkPMColor16 dst, U8CPU aa)
+{
+ SkASSERT((unsigned)aa <= 255);
+
+ unsigned src_scale = SkAlpha255To256(aa) >> 4;
+ unsigned dst_scale = SkAlpha15To16(15 - SkAlphaMul4(SkGetPackedA4444(src), src_scale));
+
+ uint32_t src32 = SkExpand_4444(src) * src_scale;
+ uint32_t dst32 = SkExpand_4444(dst) * dst_scale;
+ return SkCompact_4444((src32 + dst32) >> 4);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkARGB4444_Blitter : public SkRasterBlitter {
+public:
+ SkARGB4444_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 int16_t 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 SkIRect&);
+ virtual const SkBitmap* justAnOpaqueColor(uint32_t*);
+
+protected:
+ SkPMColor16 fPMColor16, fPMColor16Other;
+ SkPMColor16 fRawColor16, fRawColor16Other;
+ uint8_t fScale16;
+
+private:
+ // illegal
+ SkARGB4444_Blitter& operator=(const SkARGB4444_Blitter&);
+
+ typedef SkRasterBlitter INHERITED;
+};
+
+
+SkARGB4444_Blitter::SkARGB4444_Blitter(const SkBitmap& device, const SkPaint& paint)
+ : INHERITED(device)
+{
+ // cache premultiplied versions in 4444
+ SkPMColor c = SkPreMultiplyColor(paint.getColor());
+ fPMColor16 = SkPixel32ToPixel4444(c);
+ if (paint.isDither()) {
+ fPMColor16Other = SkDitherPixel32To4444(c);
+ } else {
+ fPMColor16Other = fPMColor16;
+ }
+
+ // cache raw versions in 4444
+ fRawColor16 = SkPackARGB4444(0xFF >> 4, SkColorGetR(c) >> 4,
+ SkColorGetG(c) >> 4, SkColorGetB(c) >> 4);
+ if (paint.isDither()) {
+ fRawColor16Other = SkDitherARGB32To4444(0xFF, SkColorGetR(c),
+ SkColorGetG(c), SkColorGetB(c));
+ } else {
+ fRawColor16Other = fRawColor16;
+ }
+
+ // our dithered color will be the same or more opaque than the original
+ // so use dithered to compute our scale
+ SkASSERT(SkGetPackedA4444(fPMColor16Other) >= SkGetPackedA4444(fPMColor16));
+
+ fScale16 = SkAlpha15To16(SkGetPackedA4444(fPMColor16Other));
+ if (16 == fScale16) {
+ // force the original to also be opaque
+ fPMColor16 |= (0xF << SK_A4444_SHIFT);
+ }
+}
+
+const SkBitmap* SkARGB4444_Blitter::justAnOpaqueColor(uint32_t* value)
+{
+ if (16 == fScale16) {
+ *value = fPMColor16;
+ return &fDevice;
+ }
+ return NULL;
+}
+
+static void src_over_4444(SkPMColor16 dst[], SkPMColor16 color,
+ SkPMColor16 other, unsigned invScale, int count)
+{
+ int twice = count >> 1;
+ while (--twice >= 0) {
+ *dst = color + SkAlphaMulQ4(*dst, invScale);
+ dst++;
+ *dst = other + SkAlphaMulQ4(*dst, invScale);
+ dst++;
+ }
+ if (color & 1) {
+ *dst = color + SkAlphaMulQ4(*dst, invScale);
+ }
+}
+
+static inline uint32_t SkExpand_4444_Replicate(SkPMColor16 c)
+{
+ uint32_t c32 = SkExpand_4444(c);
+ return c32 | (c32 << 4);
+}
+
+static void src_over_4444x(SkPMColor16 dst[], uint32_t color,
+ uint32_t other, unsigned invScale, int count)
+{
+ int twice = count >> 1;
+ uint32_t tmp;
+ while (--twice >= 0) {
+ tmp = SkExpand_4444(*dst) * invScale;
+ *dst++ = SkCompact_4444((color + tmp) >> 4);
+ tmp = SkExpand_4444(*dst) * invScale;
+ *dst++ = SkCompact_4444((other + tmp) >> 4);
+ }
+ if (color & 1) {
+ tmp = SkExpand_4444(*dst) * invScale;
+ *dst = SkCompact_4444((color + tmp) >> 4);
+ }
+}
+
+void SkARGB4444_Blitter::blitH(int x, int y, int width)
+{
+ SkASSERT(x >= 0 && y >= 0 && x + width <= fDevice.width());
+
+ if (0 == fScale16) {
+ return;
+ }
+
+ SkPMColor16* device = fDevice.getAddr16(x, y);
+ SkPMColor16 color = fPMColor16;
+ SkPMColor16 other = fPMColor16Other;
+
+ if ((x ^ y) & 1) {
+ SkTSwap<SkPMColor16>(color, other);
+ }
+
+ if (16 == fScale16) {
+ sk_dither_memset16(device, color, other, width);
+ }
+ else {
+ src_over_4444x(device, SkExpand_4444_Replicate(color),
+ SkExpand_4444_Replicate(other),
+ 16 - fScale16, width);
+ }
+}
+
+void SkARGB4444_Blitter::blitV(int x, int y, int height, SkAlpha alpha)
+{
+ if (0 == alpha || 0 == fScale16) {
+ return;
+ }
+
+ SkPMColor16* device = fDevice.getAddr16(x, y);
+ SkPMColor16 color = fPMColor16;
+ SkPMColor16 other = fPMColor16Other;
+ unsigned rb = fDevice.rowBytes();
+
+ if ((x ^ y) & 1) {
+ SkTSwap<SkPMColor16>(color, other);
+ }
+
+ if (16 == fScale16 && 255 == alpha) {
+ while (--height >= 0) {
+ *device = color;
+ device = (SkPMColor16*)((char*)device + rb);
+ SkTSwap<SkPMColor16>(color, other);
+ }
+ } else {
+ unsigned alphaScale = SkAlpha255To256(alpha);
+ uint32_t c32 = SkExpand_4444(color) * (alphaScale >> 4);
+ // need to normalize the low nibble of each expanded component
+ // so we don't overflow the add with d32
+ c32 = SkCompact_4444(c32 >> 4);
+ unsigned invScale = 16 - SkAlpha15To16(SkGetPackedA4444(c32));
+ // now re-expand and replicate
+ c32 = SkExpand_4444_Replicate(c32);
+
+ while (--height >= 0) {
+ uint32_t d32 = SkExpand_4444(*device) * invScale;
+ *device = SkCompact_4444((c32 + d32) >> 4);
+ device = (SkPMColor16*)((char*)device + rb);
+ }
+ }
+}
+
+void SkARGB4444_Blitter::blitRect(int x, int y, int width, int height)
+{
+ SkASSERT(x >= 0 && y >= 0 && x + width <= fDevice.width() && y + height <= fDevice.height());
+
+ if (0 == fScale16) {
+ return;
+ }
+
+ SkPMColor16* device = fDevice.getAddr16(x, y);
+ SkPMColor16 color = fPMColor16;
+ SkPMColor16 other = fPMColor16Other;
+
+ if ((x ^ y) & 1) {
+ SkTSwap<SkPMColor16>(color, other);
+ }
+
+ if (16 == fScale16) {
+ while (--height >= 0) {
+ sk_dither_memset16(device, color, other, width);
+ device = (SkPMColor16*)((char*)device + fDevice.rowBytes());
+ SkTSwap<SkPMColor16>(color, other);
+ }
+ } else {
+ unsigned invScale = 16 - fScale16;
+
+ uint32_t c32 = SkExpand_4444_Replicate(color);
+ uint32_t o32 = SkExpand_4444_Replicate(other);
+ while (--height >= 0) {
+ src_over_4444x(device, c32, o32, invScale, width);
+ device = (SkPMColor16*)((char*)device + fDevice.rowBytes());
+ SkTSwap<uint32_t>(c32, o32);
+ }
+ }
+}
+
+void SkARGB4444_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[])
+{
+ if (0 == fScale16) {
+ return;
+ }
+
+ SkPMColor16* device = fDevice.getAddr16(x, y);
+ SkPMColor16 color = fPMColor16;
+ SkPMColor16 other = fPMColor16Other;
+
+ if ((x ^ y) & 1) {
+ SkTSwap<SkPMColor16>(color, other);
+ }
+
+ for (;;) {
+ int count = runs[0];
+ SkASSERT(count >= 0);
+ if (count <= 0) {
+ return;
+ }
+
+ unsigned aa = antialias[0];
+ if (aa) {
+ if (0xFF == aa) {
+ if (16 == fScale16) {
+ sk_dither_memset16(device, color, other, count);
+ } else {
+ src_over_4444(device, color, other, 16 - fScale16, count);
+ }
+ } else {
+ // todo: respect dithering
+ aa = SkAlpha255To256(aa); // FIX
+ SkPMColor16 src = SkAlphaMulQ4(color, aa >> 4);
+ unsigned dst_scale = SkAlpha15To16(15 - SkGetPackedA4444(src)); // FIX
+ int n = count;
+ do {
+ --n;
+ device[n] = src + SkAlphaMulQ4(device[n], dst_scale);
+ } while (n > 0);
+ }
+ }
+
+ runs += count;
+ antialias += count;
+ device += count;
+
+ if (count & 1) {
+ SkTSwap<SkPMColor16>(color, other);
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+
+#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 SkARGB4444_BlitBW
+#define SK_BLITBWMASK_ARGS , SkPMColor16 color
+#define SK_BLITBWMASK_BLIT8(mask, dst) solid_8_pixels(mask, dst, color)
+#define SK_BLITBWMASK_GETADDR getAddr16
+#define SK_BLITBWMASK_DEVTYPE uint16_t
+#include "SkBlitBWMaskTemplate.h"
+
+#define blend_8_pixels(mask, dst, sc, dst_scale) \
+do { \
+if (mask & 0x80) { dst[0] = sc + SkAlphaMulQ4(dst[0], dst_scale); } \
+if (mask & 0x40) { dst[1] = sc + SkAlphaMulQ4(dst[1], dst_scale); } \
+if (mask & 0x20) { dst[2] = sc + SkAlphaMulQ4(dst[2], dst_scale); } \
+if (mask & 0x10) { dst[3] = sc + SkAlphaMulQ4(dst[3], dst_scale); } \
+if (mask & 0x08) { dst[4] = sc + SkAlphaMulQ4(dst[4], dst_scale); } \
+if (mask & 0x04) { dst[5] = sc + SkAlphaMulQ4(dst[5], dst_scale); } \
+if (mask & 0x02) { dst[6] = sc + SkAlphaMulQ4(dst[6], dst_scale); } \
+if (mask & 0x01) { dst[7] = sc + SkAlphaMulQ4(dst[7], dst_scale); } \
+} while (0)
+
+#define SK_BLITBWMASK_NAME SkARGB4444_BlendBW
+#define SK_BLITBWMASK_ARGS , uint16_t sc, unsigned dst_scale
+#define SK_BLITBWMASK_BLIT8(mask, dst) blend_8_pixels(mask, dst, sc, dst_scale)
+#define SK_BLITBWMASK_GETADDR getAddr16
+#define SK_BLITBWMASK_DEVTYPE uint16_t
+#include "SkBlitBWMaskTemplate.h"
+
+void SkARGB4444_Blitter::blitMask(const SkMask& mask, const SkIRect& clip)
+{
+ SkASSERT(mask.fBounds.contains(clip));
+
+ if (0 == fScale16) {
+ return;
+ }
+
+ if (mask.fFormat == SkMask::kBW_Format) {
+ if (16 == fScale16) {
+ SkARGB4444_BlitBW(fDevice, mask, clip, fPMColor16);
+ } else {
+ SkARGB4444_BlendBW(fDevice, mask, clip, fPMColor16, 16 - fScale16);
+ }
+ return;
+ }
+
+ int x = clip.fLeft;
+ int y = clip.fTop;
+ int width = clip.width();
+ int height = clip.height();
+
+ SkPMColor16* device = fDevice.getAddr16(x, y);
+ const uint8_t* alpha = mask.getAddr(x, y);
+ SkPMColor16 srcColor = fPMColor16;
+ unsigned devRB = fDevice.rowBytes() - (width << 1);
+ unsigned maskRB = mask.fRowBytes - width;
+
+ do {
+ int w = width;
+ do {
+ unsigned aa = *alpha++;
+ *device = SkBlendARGB4444(srcColor, *device, aa);
+ device += 1;
+ } while (--w != 0);
+ device = (SkPMColor16*)((char*)device + devRB);
+ alpha += maskRB;
+ } while (--height != 0);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+
+class SkARGB4444_Shader_Blitter : public SkShaderBlitter {
+ SkXfermode* fXfermode;
+ SkBlitRow::Proc fOpaqueProc;
+ SkBlitRow::Proc fAlphaProc;
+ SkPMColor* fBuffer;
+ uint8_t* fAAExpand;
+public:
+SkARGB4444_Shader_Blitter(const SkBitmap& device, const SkPaint& paint)
+ : INHERITED(device, paint)
+{
+ const int width = device.width();
+ fBuffer = (SkPMColor*)sk_malloc_throw(width * sizeof(SkPMColor) + width);
+ fAAExpand = (uint8_t*)(fBuffer + width);
+
+ (fXfermode = paint.getXfermode())->safeRef();
+
+ unsigned flags = 0;
+ if (!(fShader->getFlags() & SkShader::kOpaqueAlpha_Flag)) {
+ flags |= SkBlitRow::kSrcPixelAlpha_Flag;
+ }
+ if (paint.isDither()) {
+ flags |= SkBlitRow::kDither_Flag;
+ }
+ fOpaqueProc = SkBlitRow::Factory(flags, SkBitmap::kARGB_4444_Config);
+ fAlphaProc = SkBlitRow::Factory(flags | SkBlitRow::kGlobalAlpha_Flag,
+ SkBitmap::kARGB_4444_Config);
+}
+
+virtual ~SkARGB4444_Shader_Blitter()
+{
+ fXfermode->safeUnref();
+ sk_free(fBuffer);
+}
+
+virtual void blitH(int x, int y, int width)
+{
+ SkASSERT(x >= 0 && y >= 0 && x + width <= fDevice.width());
+
+ SkPMColor16* device = fDevice.getAddr16(x, y);
+ SkPMColor* span = fBuffer;
+
+ fShader->shadeSpan(x, y, span, width);
+ if (fXfermode) {
+ fXfermode->xfer4444(device, span, width, NULL);
+ }
+ else {
+ fOpaqueProc(device, span, width, 0xFF, x, y);
+ }
+}
+
+virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[])
+{
+ SkPMColor* SK_RESTRICT span = fBuffer;
+ uint8_t* SK_RESTRICT aaExpand = fAAExpand;
+ SkPMColor16* device = fDevice.getAddr16(x, y);
+ SkShader* shader = fShader;
+ SkXfermode* xfer = fXfermode;
+
+ if (NULL != xfer) {
+ for (;;) {
+ int count = *runs;
+ if (count <= 0)
+ break;
+ int aa = *antialias;
+ if (aa) {
+ shader->shadeSpan(x, y, span, count);
+ if (255 == aa) {
+ xfer->xfer4444(device, span, count, NULL);
+ } else {
+ const uint8_t* aaBuffer = antialias;
+ if (count > 1) {
+ memset(aaExpand, aa, count);
+ aaBuffer = aaExpand;
+ }
+ xfer->xfer4444(device, span, count, aaBuffer);
+ }
+ }
+ device += count;
+ runs += count;
+ antialias += count;
+ x += count;
+ }
+ } else { // no xfermode
+ for (;;) {
+ int count = *runs;
+ if (count <= 0)
+ break;
+ int aa = *antialias;
+ if (aa) {
+ fShader->shadeSpan(x, y, span, count);
+ if (255 == aa) {
+ fOpaqueProc(device, span, count, aa, x, y);
+ } else {
+ fAlphaProc(device, span, count, aa, x, y);
+ }
+ }
+ device += count;
+ runs += count;
+ antialias += count;
+ x += count;
+ }
+ }
+}
+
+private:
+ typedef SkShaderBlitter INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+SkBlitter* SkBlitter_ChooseD4444(const SkBitmap& device,
+ const SkPaint& paint,
+ void* storage, size_t storageSize)
+{
+ SkBlitter* blitter;
+
+ if (paint.getShader()) {
+ SK_PLACEMENT_NEW_ARGS(blitter, SkARGB4444_Shader_Blitter, storage, storageSize, (device, paint));
+ } else {
+ SK_PLACEMENT_NEW_ARGS(blitter, SkARGB4444_Blitter, storage, storageSize, (device, paint));
+ }
+ return blitter;
+}
+
diff --git a/src/core/SkBlitter_A1.cpp b/src/core/SkBlitter_A1.cpp
new file mode 100644
index 0000000000..1a91a263dd
--- /dev/null
+++ b/src/core/SkBlitter_A1.cpp
@@ -0,0 +1,63 @@
+/* libs/graphics/sgl/SkBlitter_A1.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkCoreBlitters.h"
+
+SkA1_Blitter::SkA1_Blitter(const SkBitmap& device, const SkPaint& paint)
+ : INHERITED(device)
+{
+ fSrcA = SkToU8(SkColorGetA(paint.getColor()));
+}
+
+void SkA1_Blitter::blitH(int x, int y, int width)
+{
+ SkASSERT(x >= 0 && y >= 0 && (unsigned)(x + width) <= (unsigned)fDevice.width());
+
+ if (fSrcA <= 0x7F)
+ return;
+
+ uint8_t* 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/src/core/SkBlitter_A8.cpp b/src/core/SkBlitter_A8.cpp
new file mode 100644
index 0000000000..18b088160e
--- /dev/null
+++ b/src/core/SkBlitter_A8.cpp
@@ -0,0 +1,387 @@
+/* libs/graphics/sgl/SkBlitter_A8.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkCoreBlitters.h"
+#include "SkColorPriv.h"
+#include "SkShader.h"
+#include "SkXfermode.h"
+
+SkA8_Blitter::SkA8_Blitter(const SkBitmap& device, const SkPaint& paint)
+ : INHERITED(device)
+{
+ fSrcA = SkColorGetA(paint.getColor());
+}
+
+const SkBitmap* SkA8_Blitter::justAnOpaqueColor(uint32_t* value)
+{
+ if (255 == fSrcA)
+ {
+ *value = 255;
+ return &fDevice;
+ }
+ return NULL;
+}
+
+void SkA8_Blitter::blitH(int x, int y, int width)
+{
+ SkASSERT(x >= 0 && y >= 0 && (unsigned)(x + width) <= (unsigned)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 int16_t 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 SkIRect& 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) <= (unsigned)fDevice.width() && (unsigned)(y + height) <= (unsigned)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)
+ : INHERITED(device, paint)
+{
+ 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();
+ sk_free(fBuffer);
+}
+
+void SkA8_Shader_Blitter::blitH(int x, int y, int width)
+{
+ SkASSERT(x >= 0 && y >= 0 && (unsigned)(x + width) <= (unsigned)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(SkPMColor 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 int16_t runs[])
+{
+ 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 SkIRect& 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/src/core/SkBlitter_ARGB32.cpp b/src/core/SkBlitter_ARGB32.cpp
new file mode 100644
index 0000000000..ed2fc0ec9a
--- /dev/null
+++ b/src/core/SkBlitter_ARGB32.cpp
@@ -0,0 +1,485 @@
+/* libs/graphics/sgl/SkBlitter_ARGB32.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkCoreBlitters.h"
+#include "SkColorPriv.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+
+SkARGB32_Blitter::SkARGB32_Blitter(const SkBitmap& device, const SkPaint& paint)
+ : INHERITED(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);
+}
+
+const SkBitmap* SkARGB32_Blitter::justAnOpaqueColor(uint32_t* value) {
+ if (255 == fSrcA) {
+ *value = fPMColor;
+ return &fDevice;
+ }
+ return NULL;
+}
+
+#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 && 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 int16_t 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);
+ int n = count;
+ do {
+ --n;
+ device[n] = sc + SkAlphaMulQ(device[n], dst_scale);
+ } while (n > 0);
+ }
+ }
+ 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 SkIRect& clip) {
+ SkASSERT(mask.fBounds.contains(clip));
+ SkASSERT(fSrcA != 0xFF);
+
+ if (fSrcA == 0) {
+ return;
+ }
+
+ if (mask.fFormat == SkMask::kBW_Format) {
+ 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;
+ unsigned devRB = fDevice.rowBytes() - (width << 2);
+ unsigned maskRB = mask.fRowBytes - width;
+
+ do {
+ int w = width;
+ do {
+ unsigned aa = *alpha++;
+ *device = SkBlendARGB32(srcColor, *device, aa);
+ device += 1;
+ } while (--w != 0);
+ device = (uint32_t*)((char*)device + devRB);
+ alpha += maskRB;
+ } while (--height != 0);
+}
+
+void SkARGB32_Opaque_Blitter::blitMask(const SkMask& mask,
+ const SkIRect& clip) {
+ SkASSERT(mask.fBounds.contains(clip));
+
+ if (mask.fFormat == SkMask::kBW_Format) {
+ SkARGB32_BlitBW(fDevice, mask, clip, fPMColor);
+ 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;
+ unsigned devRB = fDevice.rowBytes() - (width << 2);
+ unsigned maskRB = mask.fRowBytes - width;
+
+ do {
+ int w = width;
+ do {
+ unsigned aa = *alpha++;
+ *device = SkAlphaMulQ(srcColor, SkAlpha255To256(aa)) + SkAlphaMulQ(*device, SkAlpha255To256(255 - aa));
+ device += 1;
+ } while (--w != 0);
+ device = (uint32_t*)((char*)device + devRB);
+ alpha += maskRB;
+ } while (--height != 0);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+
+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 && x + width <= fDevice.width() && 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 SkIRect& clip) {
+ SkASSERT(mask.fBounds.contains(clip));
+
+ if (mask.fFormat == SkMask::kBW_Format) {
+ SkPMColor black = (SkPMColor)(SK_A32_MASK << SK_A32_SHIFT);
+
+ SkARGB32_BlitBW(fDevice, mask, clip, black);
+ } else {
+ uint32_t* device = fDevice.getAddr32(clip.fLeft, clip.fTop);
+ const uint8_t* 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++;
+ *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);
+ }
+}
+
+void SkARGB32_Black_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[],
+ const int16_t runs[]) {
+ uint32_t* device = fDevice.getAddr32(x, y);
+ SkPMColor black = (SkPMColor)(SK_A32_MASK << SK_A32_SHIFT);
+
+ for (;;) {
+ int count = runs[0];
+ SkASSERT(count >= 0);
+ if (count <= 0) {
+ return;
+ }
+ unsigned aa = antialias[0];
+ if (aa) {
+ if (aa == 255) {
+ sk_memset32(device, black, count);
+ } else {
+ SkPMColor src = aa << SK_A32_SHIFT;
+ unsigned dst_scale = 256 - aa;
+ int n = count;
+ do {
+ --n;
+ device[n] = src + SkAlphaMulQ(device[n], dst_scale);
+ } while (n > 0);
+ }
+ }
+ runs += count;
+ antialias += count;
+ device += count;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+
+SkARGB32_Shader_Blitter::SkARGB32_Shader_Blitter(const SkBitmap& device,
+ const SkPaint& paint)
+ : INHERITED(device, paint) {
+ fBuffer = (SkPMColor*)sk_malloc_throw(device.width() * (sizeof(SkPMColor)));
+
+ (fXfermode = paint.getXfermode())->safeRef();
+}
+
+SkARGB32_Shader_Blitter::~SkARGB32_Shader_Blitter() {
+ fXfermode->safeUnref();
+ sk_free(fBuffer);
+}
+
+void SkARGB32_Shader_Blitter::blitH(int x, int y, int width) {
+ SkASSERT(x >= 0 && y >= 0 && 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 int16_t runs[]) {
+ 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], 1, 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/src/core/SkBlitter_RGB16.cpp b/src/core/SkBlitter_RGB16.cpp
new file mode 100644
index 0000000000..b253662da3
--- /dev/null
+++ b/src/core/SkBlitter_RGB16.cpp
@@ -0,0 +1,782 @@
+/* libs/graphics/sgl/SkBlitter_RGB16.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkBlitRow.h"
+#include "SkCoreBlitters.h"
+#include "SkColorPriv.h"
+#include "SkDither.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+
+void sk_dither_memset16(uint16_t dst[], uint16_t value, uint16_t other,
+ int count) {
+ if (count > 0) {
+ // see if we need to write one short before we can cast to an 4byte ptr
+ // (we do this subtract rather than (unsigned)dst so we don't get warnings
+ // on 64bit machines)
+ if (((char*)dst - (char*)0) & 2) {
+ *dst++ = value;
+ count -= 1;
+ SkTSwap(value, other);
+ }
+
+ // fast way to set [value,other] pairs
+#ifdef SK_CPU_BENDIAN
+ sk_memset32((uint32_t*)dst, (value << 16) | other, count >> 1);
+#else
+ sk_memset32((uint32_t*)dst, (other << 16) | value, count >> 1);
+#endif
+
+ if (count & 1) {
+ dst[count - 1] = value;
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkRGB16_Black_Blitter::SkRGB16_Black_Blitter(const SkBitmap& device, const SkPaint& paint)
+ : SkRGB16_Blitter(device, paint) {
+ SkASSERT(paint.getShader() == NULL);
+ SkASSERT(paint.getColorFilter() == NULL);
+ SkASSERT(paint.getXfermode() == NULL);
+ SkASSERT(paint.getColor() == SK_ColorBLACK);
+}
+
+#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, uint16_t 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 uint16_t
+#include "SkBlitBWMaskTemplate.h"
+
+void SkRGB16_Black_Blitter::blitMask(const SkMask& SK_RESTRICT mask,
+ const SkIRect& SK_RESTRICT clip)
+ SK_RESTRICT {
+ if (mask.fFormat == SkMask::kBW_Format) {
+ SkRGB16_Black_BlitBW(fDevice, mask, clip);
+ } else {
+ uint16_t* SK_RESTRICT device = fDevice.getAddr16(clip.fLeft, clip.fTop);
+ const uint8_t* SK_RESTRICT 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++;
+ *device = SkAlphaMulRGB16(*device, SkAlpha255To256(255 - aa));
+ device += 1;
+ } while (--w != 0);
+ device = (uint16_t*)((char*)device + deviceRB);
+ alpha += maskRB;
+ } while (--height != 0);
+ }
+}
+
+void SkRGB16_Black_Blitter::blitAntiH(int x, int y,
+ const SkAlpha* SK_RESTRICT antialias,
+ const int16_t* SK_RESTRICT runs)
+ SK_RESTRICT {
+ uint16_t* SK_RESTRICT device = fDevice.getAddr16(x, y);
+
+ 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) {
+ memset(device, 0, count << 1);
+ } else {
+ aa = SkAlpha255To256(255 - aa);
+ do {
+ *device = SkAlphaMulRGB16(*device, aa);
+ device += 1;
+ } while (--count != 0);
+ continue;
+ }
+ }
+ device += count;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+SkRGB16_Blitter::SkRGB16_Blitter(const SkBitmap& device, const SkPaint& paint)
+ : INHERITED(device) {
+ SkColor color = paint.getColor();
+
+ fSrcColor32 = SkPreMultiplyColor(color);
+ fScale = SkAlpha255To256(SkColorGetA(color));
+
+ int r = SkColorGetR(color);
+ int g = SkColorGetG(color);
+ int b = SkColorGetB(color);
+
+ fRawColor16 = fRawDither16 = SkPack888ToRGB16(r, g, b);
+ // if we're dithered, use fRawDither16 to hold that.
+ if ((fDoDither = paint.isDither()) != false) {
+ fRawDither16 = SkDitherPack888ToRGB16(r, g, b);
+ }
+
+ fColor16 = SkPackRGB16( SkAlphaMul(r, fScale) >> (8 - SK_R16_BITS),
+ SkAlphaMul(g, fScale) >> (8 - SK_G16_BITS),
+ SkAlphaMul(b, fScale) >> (8 - SK_B16_BITS));
+}
+
+const SkBitmap* SkRGB16_Blitter::justAnOpaqueColor(uint32_t* value) {
+ if (!fDoDither && 256 == fScale) {
+ *value = fRawColor16;
+ return &fDevice;
+ }
+ return NULL;
+}
+
+void SkRGB16_Blitter::blitH(int x, int y, int width) SK_RESTRICT {
+ SkASSERT(width > 0);
+ SkASSERT(x + width <= fDevice.width());
+
+ if (fScale == 0) {
+ return;
+ }
+
+ uint16_t* SK_RESTRICT device = fDevice.getAddr16(x, y);
+ uint16_t srcColor = fColor16;
+
+ if (256 == fScale) {
+ SkASSERT(fRawColor16 == srcColor);
+ if (fDoDither) {
+ uint16_t ditherColor = fRawDither16;
+ if ((x ^ y) & 1) {
+ SkTSwap(ditherColor, srcColor);
+ }
+ sk_dither_memset16(device, srcColor, ditherColor, width);
+ } else {
+ sk_memset16(device, srcColor, width);
+ }
+ } else {
+ // TODO: respect fDoDither
+ SkPMColor src32 = fSrcColor32;
+ do {
+ *device = SkSrcOver32To16(src32, *device);
+ device += 1;
+ } while (--width != 0);
+ }
+}
+
+// return 1 or 0 from a bool
+static int Bool2Int(bool value) {
+ return !!value;
+}
+
+void SkRGB16_Blitter::blitAntiH(int x, int y,
+ const SkAlpha* SK_RESTRICT antialias,
+ const int16_t* SK_RESTRICT runs) SK_RESTRICT {
+ if (fScale == 0) {
+ return;
+ }
+
+ uint16_t* SK_RESTRICT device = fDevice.getAddr16(x, y);
+ uint16_t srcColor = fRawColor16;
+ unsigned scale = fScale;
+ int ditherInt = Bool2Int(fDoDither);
+
+ if (256 == scale) {
+ uint16_t ditherColor = fRawDither16;
+ // if we have no dithering, this will always fail
+ if ((x ^ y) & ditherInt) {
+ SkTSwap(ditherColor, srcColor);
+ }
+ 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) {
+ if (ditherInt) {
+ sk_dither_memset16(device, srcColor,
+ ditherColor, count);
+ } else {
+ sk_memset16(device, srcColor, count);
+ }
+ } else {
+ // TODO: respect fDoDither
+ unsigned scale5 = SkAlpha255To256(aa) >> 3;
+ uint32_t src32 = SkExpand_rgb_16(srcColor) * scale5;
+ scale5 = 32 - scale5; // now we can use it on the device
+ int n = count;
+ do {
+ uint32_t dst32 = SkExpand_rgb_16(*device) * scale5;
+ *device++ = SkCompact_rgb_16((src32 + dst32) >> 5);
+ } while (--n != 0);
+ goto DONE;
+ }
+ }
+ device += count;
+
+ DONE:
+ // if we have no dithering, this will always fail
+ if (count & ditherInt) {
+ SkTSwap(ditherColor, srcColor);
+ }
+ }
+ } else {
+ // TODO: respect fDoDither
+ for (;;) {
+ int count = runs[0];
+ SkASSERT(count >= 0);
+ if (count <= 0) {
+ return;
+ }
+ runs += count;
+
+ unsigned aa = antialias[0];
+ antialias += count;
+ if (aa) {
+ unsigned scale5 = SkAlpha255To256(aa) * scale >> (8 + 3);
+ uint32_t src32 = SkExpand_rgb_16(srcColor) * scale5;
+ scale5 = 32 - scale5;
+ do {
+ uint32_t dst32 = SkExpand_rgb_16(*device) * scale5;
+ *device++ = SkCompact_rgb_16((src32 + dst32) >> 5);
+ } 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 , uint16_t color
+#define SK_BLITBWMASK_BLIT8(mask, dst) solid_8_pixels(mask, dst, color)
+#define SK_BLITBWMASK_GETADDR getAddr16
+#define SK_BLITBWMASK_DEVTYPE uint16_t
+#include "SkBlitBWMaskTemplate.h"
+
+static inline void blend_8_pixels(U8CPU bw, uint16_t dst[], unsigned dst_scale,
+ U16CPU srcColor) {
+ if (bw & 0x80) dst[0] = srcColor + SkAlphaMulRGB16(dst[0], dst_scale);
+ if (bw & 0x40) dst[1] = srcColor + SkAlphaMulRGB16(dst[1], dst_scale);
+ if (bw & 0x20) dst[2] = srcColor + SkAlphaMulRGB16(dst[2], dst_scale);
+ if (bw & 0x10) dst[3] = srcColor + SkAlphaMulRGB16(dst[3], dst_scale);
+ if (bw & 0x08) dst[4] = srcColor + SkAlphaMulRGB16(dst[4], dst_scale);
+ if (bw & 0x04) dst[5] = srcColor + SkAlphaMulRGB16(dst[5], dst_scale);
+ if (bw & 0x02) dst[6] = srcColor + SkAlphaMulRGB16(dst[6], dst_scale);
+ if (bw & 0x01) dst[7] = 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 uint16_t
+#include "SkBlitBWMaskTemplate.h"
+
+static U16CPU blend_compact(uint32_t src32, uint32_t dst32, unsigned scale5) {
+ return SkCompact_rgb_16(dst32 + ((src32 - dst32) * scale5 >> 5));
+}
+
+void SkRGB16_Blitter::blitMask(const SkMask& SK_RESTRICT mask,
+ const SkIRect& SK_RESTRICT clip) SK_RESTRICT {
+ 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;
+ }
+
+ uint16_t* SK_RESTRICT device = fDevice.getAddr16(clip.fLeft, clip.fTop);
+ const uint8_t* SK_RESTRICT alpha = mask.getAddr(clip.fLeft, clip.fTop);
+ int width = clip.width();
+ int height = clip.height();
+ unsigned deviceRB = fDevice.rowBytes() - (width << 1);
+ unsigned maskRB = mask.fRowBytes - width;
+ uint32_t color32 = SkExpand_rgb_16(fRawColor16);
+
+ if (256 == fScale) {
+ do {
+ int w = width;
+ do {
+ *device = blend_compact(color32, SkExpand_rgb_16(*device),
+ SkAlpha255To256(*alpha++) >> 3);
+ device += 1;
+ } while (--w != 0);
+ device = (uint16_t*)((char*)device + deviceRB);
+ alpha += maskRB;
+ } while (--height != 0);
+ } else { // scale < 256
+ unsigned scale256 = fScale;
+ do {
+ int w = width;
+ do {
+ unsigned aa = *alpha++;
+ unsigned scale = SkAlpha255To256(aa) * scale256 >> (8 + 3);
+ uint32_t src32 = color32 * scale;
+ uint32_t dst32 = SkExpand_rgb_16(*device) * (32 - scale);
+ *device++ = SkCompact_rgb_16((src32 + dst32) >> 5);
+ } while (--w != 0);
+ device = (uint16_t*)((char*)device + deviceRB);
+ alpha += maskRB;
+ } while (--height != 0);
+ }
+}
+
+void SkRGB16_Blitter::blitV(int x, int y, int height, SkAlpha alpha) {
+ if (fScale == 0) {
+ return;
+ }
+ uint16_t* SK_RESTRICT device = fDevice.getAddr16(x, y);
+ uint16_t color16 = fRawColor16;
+ unsigned deviceRB = fDevice.rowBytes();
+
+ if (alpha + fScale == (255 + 256)) {
+ if (fDoDither) {
+ uint16_t ditherColor = fRawDither16;
+ if ((x ^ y) & 1) {
+ SkTSwap(ditherColor, color16);
+ }
+ do {
+ device[0] = color16;
+ device = (uint16_t*)((char*)device + deviceRB);
+ SkTSwap(ditherColor, color16);
+ } while (--height != 0);
+ } else {
+ do {
+ device[0] = color16;
+ device = (uint16_t*)((char*)device + deviceRB);
+ } while (--height != 0);
+ }
+ } else {
+ // TODO: respect fDoDither
+ unsigned scale5 = SkAlpha255To256(alpha) * fScale >> (8 + 3);
+ uint32_t src32 = SkExpand_rgb_16(color16) * scale5;
+ scale5 = 32 - scale5;
+ do {
+ uint32_t dst32 = SkExpand_rgb_16(*device) * scale5;
+ *device = SkCompact_rgb_16((src32 + dst32) >> 5);
+ device = (uint16_t*)((char*)device + deviceRB);
+ } while (--height != 0);
+ }
+}
+
+void SkRGB16_Blitter::blitRect(int x, int y, int width, int height) {
+ SkASSERT(x + width <= fDevice.width() && y + height <= fDevice.height());
+
+ if (fScale == 0) {
+ return;
+ }
+ uint16_t* SK_RESTRICT device = fDevice.getAddr16(x, y);
+ unsigned deviceRB = fDevice.rowBytes();
+ uint16_t color16 = fColor16;
+
+ if (256 == fScale) {
+ if (fDoDither) {
+ uint16_t ditherColor = fRawDither16;
+ if ((x ^ y) & 1) {
+ SkTSwap(ditherColor, color16);
+ }
+ while (--height >= 0) {
+ sk_dither_memset16(device, color16, ditherColor, width);
+ SkTSwap(ditherColor, color16);
+ device = (uint16_t*)((char*)device + deviceRB);
+ }
+ } else { // no dither
+ while (--height >= 0) {
+ sk_memset16(device, color16, width);
+ device = (uint16_t*)((char*)device + deviceRB);
+ }
+ }
+ } else {
+ SkPMColor src32 = fSrcColor32;
+ while (--height >= 0) {
+ for (int i = width - 1; i >= 0; --i) {
+ device[i] = SkSrcOver32To16(src32, device[i]);
+ }
+ device = (uint16_t*)((char*)device + deviceRB);
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkRGB16_Shader16_Blitter::SkRGB16_Shader16_Blitter(const SkBitmap& device,
+ const SkPaint& paint)
+ : SkRGB16_Shader_Blitter(device, paint) {
+ SkASSERT(SkShader::CanCallShadeSpan16(fShader->getFlags()));
+}
+
+void SkRGB16_Shader16_Blitter::blitH(int x, int y, int width) SK_RESTRICT {
+ SkASSERT(x + width <= fDevice.width());
+
+ uint16_t* SK_RESTRICT device = fDevice.getAddr16(x, y);
+ SkShader* shader = fShader;
+
+ int alpha = shader->getSpan16Alpha();
+ if (0xFF == alpha) {
+ shader->shadeSpan16(x, y, device, width);
+ } else {
+ uint16_t* span16 = (uint16_t*)fBuffer;
+ shader->shadeSpan16(x, y, span16, width);
+ SkBlendRGB16(span16, device, SkAlpha255To256(alpha), width);
+ }
+}
+
+void SkRGB16_Shader16_Blitter::blitAntiH(int x, int y,
+ const SkAlpha* SK_RESTRICT antialias,
+ const int16_t* SK_RESTRICT runs)
+ SK_RESTRICT {
+ SkShader* shader = fShader;
+ SkPMColor* SK_RESTRICT span = fBuffer;
+ uint16_t* SK_RESTRICT device = fDevice.getAddr16(x, y);
+
+ int alpha = shader->getSpan16Alpha();
+ uint16_t* span16 = (uint16_t*)span;
+
+ if (0xFF == alpha) {
+ for (;;) {
+ int count = *runs;
+ if (count <= 0) {
+ break;
+ }
+ SkASSERT(count <= fDevice.width()); // don't overrun fBuffer
+
+ int aa = *antialias;
+ if (aa == 255) {
+ // go direct to the device!
+ shader->shadeSpan16(x, y, device, count);
+ } else if (aa) {
+ shader->shadeSpan16(x, y, span16, count);
+ SkBlendRGB16(span16, device, SkAlpha255To256(aa), count);
+ }
+ device += count;
+ runs += count;
+ antialias += count;
+ x += count;
+ }
+ } else { // span alpha is < 255
+ alpha = SkAlpha255To256(alpha);
+ for (;;) {
+ int count = *runs;
+ if (count <= 0) {
+ break;
+ }
+ SkASSERT(count <= fDevice.width()); // don't overrun fBuffer
+
+ int aa = SkAlphaMul(*antialias, alpha);
+ if (aa) {
+ shader->shadeSpan16(x, y, span16, count);
+ SkBlendRGB16(span16, device, SkAlpha255To256(aa), count);
+ }
+
+ device += count;
+ runs += count;
+ antialias += count;
+ x += count;
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkRGB16_Shader_Blitter::SkRGB16_Shader_Blitter(const SkBitmap& device,
+ const SkPaint& paint)
+: INHERITED(device, paint) {
+ SkASSERT(paint.getXfermode() == NULL);
+
+ fBuffer = (SkPMColor*)sk_malloc_throw(device.width() * sizeof(SkPMColor));
+
+ // compute SkBlitRow::Procs
+ unsigned flags = 0;
+
+ uint32_t shaderFlags = fShader->getFlags();
+ // shaders take care of global alpha, so we never set it in SkBlitRow
+ if (!(shaderFlags & SkShader::kOpaqueAlpha_Flag)) {
+ flags |= SkBlitRow::kSrcPixelAlpha_Flag;
+ }
+ // don't dither if the shader is really 16bit
+ if (paint.isDither() && !(shaderFlags & SkShader::kIntrinsicly16_Flag)) {
+ flags |= SkBlitRow::kDither_Flag;
+ }
+ // used when we know our global alpha is 0xFF
+ fOpaqueProc = SkBlitRow::Factory(flags, SkBitmap::kRGB_565_Config);
+ // used when we know our global alpha is < 0xFF
+ fAlphaProc = SkBlitRow::Factory(flags | SkBlitRow::kGlobalAlpha_Flag,
+ SkBitmap::kRGB_565_Config);
+}
+
+SkRGB16_Shader_Blitter::~SkRGB16_Shader_Blitter() {
+ sk_free(fBuffer);
+}
+
+void SkRGB16_Shader_Blitter::blitH(int x, int y, int width) {
+ SkASSERT(x + width <= fDevice.width());
+
+ fShader->shadeSpan(x, y, fBuffer, width);
+ // shaders take care of global alpha, so we pass 0xFF (should be ignored)
+ fOpaqueProc(fDevice.getAddr16(x, y), fBuffer, width, 0xFF, x, y);
+}
+
+static inline int count_nonzero_span(const int16_t 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* SK_RESTRICT antialias,
+ const int16_t* SK_RESTRICT runs)
+ SK_RESTRICT {
+ SkShader* shader = fShader;
+ SkPMColor* SK_RESTRICT span = fBuffer;
+ uint16_t* SK_RESTRICT device = fDevice.getAddr16(x, y);
+
+ for (;;) {
+ int count = *runs;
+ if (count <= 0) {
+ break;
+ }
+ int aa = *antialias;
+ if (0 == aa) {
+ device += count;
+ runs += count;
+ antialias += count;
+ x += count;
+ continue;
+ }
+
+ int nonZeroCount = count + count_nonzero_span(runs + count, antialias + count);
+
+ SkASSERT(nonZeroCount <= fDevice.width()); // don't overrun fBuffer
+ shader->shadeSpan(x, y, span, nonZeroCount);
+
+ SkPMColor* localSpan = span;
+ for (;;) {
+ SkBlitRow::Proc proc = (aa == 0xFF) ? fOpaqueProc : fAlphaProc;
+ proc(device, localSpan, count, aa, x, y);
+
+ x += count;
+ 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)
+: INHERITED(device, paint) {
+ fXfermode = paint.getXfermode();
+ SkASSERT(fXfermode);
+ fXfermode->ref();
+
+ int width = device.width();
+ fBuffer = (SkPMColor*)sk_malloc_throw((width + (SkAlign4(width) >> 2)) * sizeof(SkPMColor));
+ fAAExpand = (uint8_t*)(fBuffer + width);
+}
+
+SkRGB16_Shader_Xfermode_Blitter::~SkRGB16_Shader_Xfermode_Blitter() {
+ fXfermode->unref();
+ sk_free(fBuffer);
+}
+
+void SkRGB16_Shader_Xfermode_Blitter::blitH(int x, int y, int width) {
+ SkASSERT(x + width <= fDevice.width());
+
+ uint16_t* 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* SK_RESTRICT antialias,
+ const int16_t* SK_RESTRICT runs) SK_RESTRICT {
+ SkShader* shader = fShader;
+ SkXfermode* mode = fXfermode;
+ SkPMColor* SK_RESTRICT span = fBuffer;
+ uint8_t* SK_RESTRICT aaExpand = fAAExpand;
+ uint16_t* SK_RESTRICT device = fDevice.getAddr16(x, y);
+
+ for (;;) {
+ int count = *runs;
+ if (count <= 0) {
+ break;
+ }
+ int aa = *antialias;
+ if (0 == aa) {
+ device += count;
+ runs += count;
+ antialias += count;
+ x += count;
+ continue;
+ }
+
+ int nonZeroCount = count + count_nonzero_span(runs + count,
+ antialias + count);
+
+ SkASSERT(nonZeroCount <= fDevice.width()); // don't overrun fBuffer
+ 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;
+ }
+ }
+}
+
+////////////////////////
+
+#if 0
+static inline uint16_t aa_blendS32D16(SkPMColor src, U16CPU dst, int aa
+#ifdef DITHER_SHADER
+ , int dither
+#endif
+ )
+{
+ SkASSERT((unsigned)aa <= 255);
+
+ int src_scale = SkAlpha255To256(aa);
+ int sa = SkGetPackedA32(src);
+ int dst_scale = SkAlpha255To256(255 - SkAlphaMul(sa, src_scale));
+
+#ifdef DITHER_SHADER
+ int sr = SkGetPackedR32(src);
+ int sg = SkGetPackedG32(src);
+ int sb = SkGetPackedB32(src);
+ sr = SkDITHER_R32To16(sr, dither);
+ sg = SkDITHER_G32To16(sg, dither);
+ sb = SkDITHER_B32To16(sb, dither);
+#else
+ int sr = SkPacked32ToR16(src);
+ int sg = SkPacked32ToG16(src);
+ int sb = SkPacked32ToB16(src);
+#endif
+
+ int dr = (sr * src_scale + SkGetPackedR16(dst) * dst_scale) >> 8;
+ int dg = (sg * src_scale + SkGetPackedG16(dst) * dst_scale) >> 8;
+ int db = (sb * src_scale + SkGetPackedB16(dst) * dst_scale) >> 8;
+
+ return SkPackRGB16(dr, dg, db);
+}
+#endif
+
diff --git a/src/core/SkBlitter_Sprite.cpp b/src/core/SkBlitter_Sprite.cpp
new file mode 100644
index 0000000000..f0da166ca8
--- /dev/null
+++ b/src/core/SkBlitter_Sprite.cpp
@@ -0,0 +1,101 @@
+/* libs/graphics/sgl/SkBlitter_Sprite.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkSpriteBlitter.h"
+
+SkSpriteBlitter::SkSpriteBlitter(const SkBitmap& source)
+ : fSource(&source)
+{
+ fSource->lockPixels();
+}
+
+SkSpriteBlitter::~SkSpriteBlitter()
+{
+ fSource->unlockPixels();
+}
+
+void SkSpriteBlitter::setup(const SkBitmap& device, int left, int top,
+ const SkPaint& paint)
+{
+ fDevice = &device;
+ fLeft = left;
+ fTop = top;
+ fPaint = &paint;
+}
+
+#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 int16_t 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 SkIRect& clip)
+{
+ SkASSERT(!"how did we get here?");
+}
+#endif
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////////
+
+// returning null 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 null if it is set, forcing the client to take the slow shader case
+ (which does respect soft edges).
+ */
+
+ SkSpriteBlitter* blitter;
+
+ switch (device.getConfig()) {
+ case SkBitmap::kRGB_565_Config:
+ blitter = SkSpriteBlitter::ChooseD16(source, paint, storage, storageSize);
+ break;
+ case SkBitmap::kARGB_8888_Config:
+ blitter = SkSpriteBlitter::ChooseD32(source, paint, storage, storageSize);
+ break;
+ default:
+ blitter = NULL;
+ break;
+ }
+
+ if (blitter)
+ blitter->setup(device, left, top, paint);
+ return blitter;
+}
+
diff --git a/src/core/SkBuffer.cpp b/src/core/SkBuffer.cpp
new file mode 100644
index 0000000000..5768ca4e29
--- /dev/null
+++ b/src/core/SkBuffer.cpp
@@ -0,0 +1,137 @@
+/* libs/corecg/SkBuffer.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkBuffer.h"
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+void SkRBuffer::readNoSizeCheck(void* buffer, size_t size)
+{
+ SkASSERT((fData != 0 && fStop == 0) || fPos + size <= fStop);
+ if (buffer)
+ memcpy(buffer, fPos, size);
+ fPos += size;
+}
+
+const void* SkRBuffer::skip(size_t size)
+{
+ const void* result = fPos;
+ readNoSizeCheck(NULL, size);
+ return result;
+}
+
+size_t SkRBuffer::skipToAlign4()
+{
+ size_t pos = this->pos();
+ size_t n = SkAlign4(pos) - pos;
+ fPos += n;
+ return n;
+}
+
+void* SkWBuffer::skip(size_t size)
+{
+ void* result = fPos;
+ writeNoSizeCheck(NULL, size);
+ return fData == NULL ? NULL : result;
+}
+
+void SkWBuffer::writeNoSizeCheck(const void* buffer, size_t size)
+{
+ SkASSERT(fData == 0 || fStop == 0 || fPos + size <= fStop);
+ if (fData && buffer)
+ memcpy(fPos, buffer, size);
+ fPos += size;
+}
+
+size_t SkWBuffer::padToAlign4()
+{
+ size_t pos = this->pos();
+ size_t n = SkAlign4(pos) - pos;
+
+ if (n && fData)
+ {
+ char* p = fPos;
+ char* stop = p + n;
+ do {
+ *p++ = 0;
+ } while (p < stop);
+ }
+ fPos += n;
+ return n;
+}
+
+#if 0
+#ifdef SK_DEBUG
+ static void AssertBuffer32(const void* buffer)
+ {
+ SkASSERT(buffer);
+ SkASSERT(((size_t)buffer & 3) == 0);
+ }
+#else
+ #define AssertBuffer32(buffer)
+#endif
+
+void* sk_buffer_write_int32(void* buffer, int32_t value)
+{
+ AssertBuffer32(buffer);
+ *(int32_t*)buffer = value;
+ return (char*)buffer + sizeof(int32_t);
+}
+
+void* sk_buffer_write_int32(void* buffer, const int32_t values[], int count)
+{
+ AssertBuffer32(buffer);
+ SkASSERT(count >= 0);
+
+ memcpy((int32_t*)buffer, values, count * sizeof(int32_t));
+ return (char*)buffer + count * sizeof(int32_t);
+}
+
+const void* sk_buffer_read_int32(const void* buffer, int32_t* value)
+{
+ AssertBuffer32(buffer);
+ if (value)
+ *value = *(const int32_t*)buffer;
+ return (const char*)buffer + sizeof(int32_t);
+}
+
+const void* sk_buffer_read_int32(const void* buffer, int32_t values[], int count)
+{
+ AssertBuffer32(buffer);
+ SkASSERT(count >= 0);
+
+ if (values)
+ memcpy(values, (const int32_t*)buffer, count * sizeof(int32_t));
+ return (const char*)buffer + count * sizeof(int32_t);
+}
+
+void* sk_buffer_write_ptr(void* buffer, void* ptr)
+{
+ AssertBuffer32(buffer);
+ *(void**)buffer = ptr;
+ return (char*)buffer + sizeof(void*);
+}
+
+const void* sk_buffer_read_ptr(const void* buffer, void** ptr)
+{
+ AssertBuffer32(buffer);
+ if (ptr)
+ *ptr = *(void**)buffer;
+ return (const char*)buffer + sizeof(void*);
+}
+
+#endif
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
new file mode 100644
index 0000000000..40884164d3
--- /dev/null
+++ b/src/core/SkCanvas.cpp
@@ -0,0 +1,1420 @@
+/*
+ * Copyright (C) 2006-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkCanvas.h"
+#include "SkBounder.h"
+#include "SkDevice.h"
+#include "SkDraw.h"
+#include "SkDrawFilter.h"
+#include "SkDrawLooper.h"
+#include "SkPicture.h"
+#include "SkScalarCompare.h"
+#include "SkTemplates.h"
+#include "SkUtils.h"
+#include <new>
+
+//#define SK_TRACE_SAVERESTORE
+
+#ifdef SK_TRACE_SAVERESTORE
+ static int gLayerCounter;
+ static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
+ static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
+
+ static int gRecCounter;
+ static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
+ static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
+
+ static int gCanvasCounter;
+ static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
+ static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
+#else
+ #define inc_layer()
+ #define dec_layer()
+ #define inc_rec()
+ #define dec_rec()
+ #define inc_canvas()
+ #define dec_canvas()
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// Helpers for computing fast bounds for quickReject tests
+
+static SkCanvas::EdgeType paint2EdgeType(const SkPaint* paint) {
+ return paint != NULL && paint->isAntiAlias() ?
+ SkCanvas::kAA_EdgeType : SkCanvas::kBW_EdgeType;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/* This is the record we keep for each SkDevice that the user installs.
+ The clip/matrix/proc are fields that reflect the top of the save/restore
+ stack. Whenever the canvas changes, it marks a dirty flag, and then before
+ these are used (assuming we're not on a layer) we rebuild these cache
+ values: they reflect the top of the save stack, but translated and clipped
+ by the device's XY offset and bitmap-bounds.
+*/
+struct DeviceCM {
+ DeviceCM* fNext;
+ SkDevice* fDevice;
+ SkRegion fClip;
+ const SkMatrix* fMatrix;
+ SkPaint* fPaint; // may be null (in the future)
+ int16_t fX, fY; // relative to base matrix/clip
+
+ DeviceCM(SkDevice* device, int x, int y, const SkPaint* paint)
+ : fNext(NULL) {
+ if (NULL != device) {
+ device->ref();
+ device->lockPixels();
+ }
+ fDevice = device;
+ fX = SkToS16(x);
+ fY = SkToS16(y);
+ fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
+ }
+
+ ~DeviceCM() {
+ if (NULL != fDevice) {
+ fDevice->unlockPixels();
+ fDevice->unref();
+ }
+ SkDELETE(fPaint);
+ }
+
+ void updateMC(const SkMatrix& totalMatrix, const SkRegion& totalClip,
+ SkRegion* updateClip) {
+ int x = fX;
+ int y = fY;
+ int width = fDevice->width();
+ int height = fDevice->height();
+
+ if ((x | y) == 0) {
+ fMatrix = &totalMatrix;
+ fClip = totalClip;
+ } else {
+ fMatrixStorage = totalMatrix;
+ fMatrixStorage.postTranslate(SkIntToScalar(-x),
+ SkIntToScalar(-y));
+ fMatrix = &fMatrixStorage;
+
+ totalClip.translate(-x, -y, &fClip);
+ }
+
+ fClip.op(0, 0, width, height, SkRegion::kIntersect_Op);
+
+ // intersect clip, but don't translate it (yet)
+
+ if (updateClip) {
+ updateClip->op(x, y, x + width, y + height,
+ SkRegion::kDifference_Op);
+ }
+
+ fDevice->setMatrixClip(*fMatrix, fClip);
+
+#ifdef SK_DEBUG
+ if (!fClip.isEmpty()) {
+ SkIRect deviceR;
+ deviceR.set(0, 0, width, height);
+ SkASSERT(deviceR.contains(fClip.getBounds()));
+ }
+#endif
+ }
+
+ void translateClip() {
+ if (fX | fY) {
+ fClip.translate(fX, fY);
+ }
+ }
+
+private:
+ SkMatrix fMatrixStorage;
+};
+
+/* This is the record we keep for each save/restore level in the stack.
+ Since a level optionally copies the matrix and/or stack, we have pointers
+ for these fields. If the value is copied for this level, the copy is
+ stored in the ...Storage field, and the pointer points to that. If the
+ value is not copied for this level, we ignore ...Storage, and just point
+ at the corresponding value in the previous level in the stack.
+*/
+class SkCanvas::MCRec {
+public:
+ MCRec* fNext;
+ SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec
+ SkRegion* fRegion; // points to either fRegionStorage or prev MCRec
+ SkDrawFilter* fFilter; // the current filter (or null)
+
+ DeviceCM* fLayer;
+ /* If there are any layers in the stack, this points to the top-most
+ one that is at or below this level in the stack (so we know what
+ bitmap/device to draw into from this level. This value is NOT
+ reference counted, since the real owner is either our fLayer field,
+ or a previous one in a lower level.)
+ */
+ DeviceCM* fTopLayer;
+
+ MCRec(const MCRec* prev, int flags) {
+ if (NULL != prev) {
+ if (flags & SkCanvas::kMatrix_SaveFlag) {
+ fMatrixStorage = *prev->fMatrix;
+ fMatrix = &fMatrixStorage;
+ } else {
+ fMatrix = prev->fMatrix;
+ }
+
+ if (flags & SkCanvas::kClip_SaveFlag) {
+ fRegionStorage = *prev->fRegion;
+ fRegion = &fRegionStorage;
+ } else {
+ fRegion = prev->fRegion;
+ }
+
+ fFilter = prev->fFilter;
+ fFilter->safeRef();
+
+ fTopLayer = prev->fTopLayer;
+ } else { // no prev
+ fMatrixStorage.reset();
+
+ fMatrix = &fMatrixStorage;
+ fRegion = &fRegionStorage;
+ fFilter = NULL;
+ fTopLayer = NULL;
+ }
+ fLayer = NULL;
+
+ // don't bother initializing fNext
+ inc_rec();
+ }
+ ~MCRec() {
+ fFilter->safeUnref();
+ SkDELETE(fLayer);
+ dec_rec();
+ }
+
+private:
+ SkMatrix fMatrixStorage;
+ SkRegion fRegionStorage;
+};
+
+class SkDrawIter : public SkDraw {
+public:
+ SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
+ fCanvas = canvas;
+ canvas->updateDeviceCMCache();
+
+ fBounder = canvas->getBounder();
+ fCurrLayer = canvas->fMCRec->fTopLayer;
+ fSkipEmptyClips = skipEmptyClips;
+ }
+
+ bool next() {
+ // skip over recs with empty clips
+ if (fSkipEmptyClips) {
+ while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
+ fCurrLayer = fCurrLayer->fNext;
+ }
+ }
+
+ if (NULL != fCurrLayer) {
+ const DeviceCM* rec = fCurrLayer;
+
+ fMatrix = rec->fMatrix;
+ fClip = &rec->fClip;
+ fDevice = rec->fDevice;
+ fBitmap = &fDevice->accessBitmap(true);
+ fLayerX = rec->fX;
+ fLayerY = rec->fY;
+ fPaint = rec->fPaint;
+ SkDEBUGCODE(this->validate();)
+
+ fCurrLayer = rec->fNext;
+ if (fBounder) {
+ fBounder->setClip(fClip);
+ }
+
+ // fCurrLayer may be NULL now
+
+ fCanvas->prepareForDeviceDraw(fDevice);
+ return true;
+ }
+ return false;
+ }
+
+ int getX() const { return fLayerX; }
+ int getY() const { return fLayerY; }
+ SkDevice* getDevice() const { return fDevice; }
+ const SkMatrix& getMatrix() const { return *fMatrix; }
+ const SkRegion& getClip() const { return *fClip; }
+ const SkPaint* getPaint() const { return fPaint; }
+private:
+ SkCanvas* fCanvas;
+ const DeviceCM* fCurrLayer;
+ const SkPaint* fPaint; // May be null.
+ int fLayerX;
+ int fLayerY;
+ SkBool8 fSkipEmptyClips;
+
+ typedef SkDraw INHERITED;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+class AutoDrawLooper {
+public:
+ AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint, SkDrawFilter::Type t)
+ : fCanvas(canvas), fPaint((SkPaint*)&paint), fType(t) {
+ if ((fLooper = paint.getLooper()) != NULL) {
+ fLooper->init(canvas, (SkPaint*)&paint);
+ } else {
+ fOnce = true;
+ }
+ fFilter = canvas->getDrawFilter();
+ fNeedFilterRestore = false;
+ }
+
+ ~AutoDrawLooper() {
+ if (fNeedFilterRestore) {
+ SkASSERT(fFilter);
+ fFilter->restore(fCanvas, fPaint, fType);
+ }
+ if (NULL != fLooper) {
+ fLooper->restore();
+ }
+ }
+
+ bool next() {
+ SkDrawFilter* filter = fFilter;
+
+ // if we drew earlier with a filter, then we need to restore first
+ if (fNeedFilterRestore) {
+ SkASSERT(filter);
+ filter->restore(fCanvas, fPaint, fType);
+ fNeedFilterRestore = false;
+ }
+
+ bool result;
+
+ if (NULL != fLooper) {
+ result = fLooper->next();
+ } else {
+ result = fOnce;
+ fOnce = false;
+ }
+
+ // if we're gonna draw, give the filter a chance to do its work
+ if (result && NULL != filter) {
+ fNeedFilterRestore = result = filter->filter(fCanvas, fPaint,
+ fType);
+ }
+ return result;
+ }
+
+private:
+ SkDrawLooper* fLooper;
+ SkDrawFilter* fFilter;
+ SkCanvas* fCanvas;
+ SkPaint* fPaint;
+ SkDrawFilter::Type fType;
+ bool fOnce;
+ bool fNeedFilterRestore;
+
+};
+
+/* Stack helper for managing a SkBounder. In the destructor, if we were
+ given a bounder, we call its commit() method, signifying that we are
+ done accumulating bounds for that draw.
+*/
+class SkAutoBounderCommit {
+public:
+ SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {}
+ ~SkAutoBounderCommit() {
+ if (NULL != fBounder) {
+ fBounder->commit();
+ }
+ }
+private:
+ SkBounder* fBounder;
+};
+
+#include "SkColorPriv.h"
+
+class AutoValidator {
+public:
+ AutoValidator(SkDevice* device) : fDevice(device) {}
+ ~AutoValidator() {
+#ifdef SK_DEBUG
+ const SkBitmap& bm = fDevice->accessBitmap(false);
+ if (bm.config() == SkBitmap::kARGB_4444_Config) {
+ for (int y = 0; y < bm.height(); y++) {
+ const SkPMColor16* p = bm.getAddr16(0, y);
+ for (int x = 0; x < bm.width(); x++) {
+ SkPMColor16 c = p[x];
+ SkPMColor16Assert(c);
+ }
+ }
+ }
+#endif
+ }
+private:
+ SkDevice* fDevice;
+};
+
+////////// macros to place around the internal draw calls //////////////////
+
+#define ITER_BEGIN(paint, type) \
+/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \
+ AutoDrawLooper looper(this, paint, type); \
+ while (looper.next()) { \
+ SkAutoBounderCommit ac(fBounder); \
+ SkDrawIter iter(this);
+
+#define ITER_END }
+
+////////////////////////////////////////////////////////////////////////////
+
+SkDevice* SkCanvas::init(SkDevice* device) {
+ fBounder = NULL;
+ fLocalBoundsCompareTypeDirty = true;
+
+ fMCRec = (MCRec*)fMCStack.push_back();
+ new (fMCRec) MCRec(NULL, 0);
+
+ fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL));
+ fMCRec->fTopLayer = fMCRec->fLayer;
+ fMCRec->fNext = NULL;
+
+ return this->setDevice(device);
+}
+
+SkCanvas::SkCanvas(SkDevice* device)
+ : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
+ inc_canvas();
+
+ this->init(device);
+}
+
+SkCanvas::SkCanvas(const SkBitmap& bitmap)
+ : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
+ inc_canvas();
+
+ this->init(SkNEW_ARGS(SkDevice, (bitmap)))->unref();
+}
+
+SkCanvas::~SkCanvas() {
+ // free up the contents of our deque
+ this->restoreToCount(1); // restore everything but the last
+ this->internalRestore(); // restore the last, since we're going away
+
+ fBounder->safeUnref();
+
+ dec_canvas();
+}
+
+SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
+ SkRefCnt_SafeAssign(fBounder, bounder);
+ return bounder;
+}
+
+SkDrawFilter* SkCanvas::getDrawFilter() const {
+ return fMCRec->fFilter;
+}
+
+SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
+ SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
+ return filter;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkDevice* SkCanvas::getDevice() const {
+ // return root device
+ SkDeque::Iter iter(fMCStack);
+ MCRec* rec = (MCRec*)iter.next();
+ SkASSERT(rec && rec->fLayer);
+ return rec->fLayer->fDevice;
+}
+
+SkDevice* SkCanvas::setDevice(SkDevice* device) {
+ // return root device
+ SkDeque::Iter iter(fMCStack);
+ MCRec* rec = (MCRec*)iter.next();
+ SkASSERT(rec && rec->fLayer);
+ SkDevice* rootDevice = rec->fLayer->fDevice;
+
+ if (rootDevice == device) {
+ return device;
+ }
+
+ /* Notify the devices that they are going in/out of scope, so they can do
+ things like lock/unlock their pixels, etc.
+ */
+ if (device) {
+ device->lockPixels();
+ }
+ if (rootDevice) {
+ rootDevice->unlockPixels();
+ }
+
+ SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
+ rootDevice = device;
+
+ fDeviceCMDirty = true;
+
+ /* Now we update our initial region to have the bounds of the new device,
+ and then intersect all of the clips in our stack with these bounds,
+ to ensure that we can't draw outside of the device's bounds (and trash
+ memory).
+
+ NOTE: this is only a partial-fix, since if the new device 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 device bounds.
+ */
+
+ if (NULL == device) {
+ rec->fRegion->setEmpty();
+ while ((rec = (MCRec*)iter.next()) != NULL) {
+ (void)rec->fRegion->setEmpty();
+ }
+ } else {
+ // compute our total bounds for all devices
+ SkIRect bounds;
+
+ bounds.set(0, 0, device->width(), device->height());
+
+ // now jam our 1st clip to be bounds, and intersect the rest with that
+ rec->fRegion->setRect(bounds);
+ while ((rec = (MCRec*)iter.next()) != NULL) {
+ (void)rec->fRegion->op(bounds, SkRegion::kIntersect_Op);
+ }
+ }
+ return device;
+}
+
+SkDevice* SkCanvas::setBitmapDevice(const SkBitmap& bitmap) {
+ SkDevice* device = this->setDevice(SkNEW_ARGS(SkDevice, (bitmap)));
+ device->unref();
+ return device;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+bool SkCanvas::getViewport(SkIPoint* size) const {
+ return false;
+}
+
+bool SkCanvas::setViewport(int width, int height) {
+ return false;
+}
+
+void SkCanvas::updateDeviceCMCache() {
+ if (fDeviceCMDirty) {
+ const SkMatrix& totalMatrix = this->getTotalMatrix();
+ const SkRegion& totalClip = this->getTotalClip();
+ DeviceCM* layer = fMCRec->fTopLayer;
+
+ if (NULL == layer->fNext) { // only one layer
+ layer->updateMC(totalMatrix, totalClip, NULL);
+ } else {
+ SkRegion clip;
+ clip = totalClip; // make a copy
+ do {
+ layer->updateMC(totalMatrix, clip, &clip);
+ } while ((layer = layer->fNext) != NULL);
+ }
+ fDeviceCMDirty = false;
+ }
+}
+
+void SkCanvas::prepareForDeviceDraw(SkDevice* device) {
+ SkASSERT(device);
+ device->gainFocus(this);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int SkCanvas::internalSave(SaveFlags flags) {
+ int saveCount = this->getSaveCount(); // record this before the actual save
+
+ MCRec* newTop = (MCRec*)fMCStack.push_back();
+ new (newTop) MCRec(fMCRec, flags); // balanced in restore()
+
+ newTop->fNext = fMCRec;
+ fMCRec = newTop;
+
+ return saveCount;
+}
+
+int SkCanvas::save(SaveFlags flags) {
+ // call shared impl
+ return this->internalSave(flags);
+}
+
+#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
+#define C16MASK (1 << SkBitmap::kRGB_565_Config)
+#define C8MASK (1 << SkBitmap::kA8_Config)
+
+static SkBitmap::Config resolve_config(SkCanvas* canvas,
+ const SkIRect& bounds,
+ SkCanvas::SaveFlags flags,
+ bool* isOpaque) {
+ *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
+
+#if 0
+ // loop through and union all the configs we may draw into
+ uint32_t configMask = 0;
+ for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
+ {
+ SkDevice* device = canvas->getLayerDevice(i);
+ if (device->intersects(bounds))
+ configMask |= 1 << device->config();
+ }
+
+ // if the caller wants alpha or fullcolor, we can't return 565
+ if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
+ SkCanvas::kHasAlphaLayer_SaveFlag))
+ configMask &= ~C16MASK;
+
+ switch (configMask) {
+ case C8MASK: // if we only have A8, return that
+ return SkBitmap::kA8_Config;
+
+ case C16MASK: // if we only have 565, return that
+ return SkBitmap::kRGB_565_Config;
+
+ default:
+ return SkBitmap::kARGB_8888_Config; // default answer
+ }
+#else
+ return SkBitmap::kARGB_8888_Config; // default answer
+#endif
+}
+
+static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
+ return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
+}
+
+int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
+ SaveFlags flags) {
+ // do this before we create the layer. We don't call the public save() since
+ // that would invoke a possibly overridden virtual
+ int count = this->internalSave(flags);
+
+ fDeviceCMDirty = true;
+
+ SkIRect ir;
+ const SkIRect& clipBounds = this->getTotalClip().getBounds();
+
+ if (NULL != bounds) {
+ SkRect r;
+
+ this->getTotalMatrix().mapRect(&r, *bounds);
+ r.roundOut(&ir);
+ // early exit if the layer's bounds are clipped out
+ if (!ir.intersect(clipBounds)) {
+ if (bounds_affects_clip(flags))
+ fMCRec->fRegion->setEmpty();
+ return count;
+ }
+ } else { // no user bounds, so just use the clip
+ ir = clipBounds;
+ }
+
+ // early exit if the clip is now empty
+ if (bounds_affects_clip(flags) &&
+ !fMCRec->fRegion->op(ir, SkRegion::kIntersect_Op)) {
+ return count;
+ }
+
+ bool isOpaque;
+ SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
+
+ SkDevice* device = this->createDevice(config, ir.width(), ir.height(),
+ isOpaque, true);
+ DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint));
+ device->unref();
+
+ layer->fNext = fMCRec->fTopLayer;
+ fMCRec->fLayer = layer;
+ fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
+
+ return count;
+}
+
+int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
+ SaveFlags flags) {
+ if (0xFF == alpha) {
+ return this->saveLayer(bounds, NULL, flags);
+ } else {
+ SkPaint tmpPaint;
+ tmpPaint.setAlpha(alpha);
+ return this->saveLayer(bounds, &tmpPaint, flags);
+ }
+}
+
+void SkCanvas::restore() {
+ // check for underflow
+ if (fMCStack.count() > 1) {
+ this->internalRestore();
+ }
+}
+
+void SkCanvas::internalRestore() {
+ SkASSERT(fMCStack.count() != 0);
+
+ fDeviceCMDirty = true;
+ fLocalBoundsCompareTypeDirty = true;
+
+ // reserve our layer (if any)
+ DeviceCM* layer = fMCRec->fLayer; // may be null
+ // now detach it from fMCRec so we can pop(). Gets freed after its drawn
+ fMCRec->fLayer = NULL;
+
+ // now do the normal restore()
+ fMCRec->~MCRec(); // balanced in save()
+ fMCStack.pop_back();
+ fMCRec = (MCRec*)fMCStack.back();
+
+ /* Time to draw the layer's offscreen. We can't call the public drawSprite,
+ since if we're being recorded, we don't want to record this (the
+ recorder will have already recorded the restore).
+ */
+ if (NULL != layer) {
+ if (layer->fNext) {
+ this->drawDevice(layer->fDevice, layer->fX, layer->fY,
+ layer->fPaint);
+ // reset this, since drawDevice will have set it to true
+ fDeviceCMDirty = true;
+ }
+ SkDELETE(layer);
+ }
+}
+
+int SkCanvas::getSaveCount() const {
+ return fMCStack.count();
+}
+
+void SkCanvas::restoreToCount(int count) {
+ // sanity check
+ if (count < 1) {
+ count = 1;
+ }
+ while (fMCStack.count() > count) {
+ this->restore();
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+// can't draw it if its empty, or its too big for a fixed-point width or height
+static bool reject_bitmap(const SkBitmap& bitmap) {
+ return bitmap.width() <= 0 || bitmap.height() <= 0 ||
+ bitmap.width() > 32767 || bitmap.height() > 32767;
+}
+
+void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
+ const SkMatrix& matrix, const SkPaint* paint) {
+ if (reject_bitmap(bitmap)) {
+ return;
+ }
+
+ if (NULL == paint) {
+ SkPaint tmpPaint;
+ this->commonDrawBitmap(bitmap, matrix, tmpPaint);
+ } else {
+ this->commonDrawBitmap(bitmap, matrix, *paint);
+ }
+}
+
+void SkCanvas::drawDevice(SkDevice* device, int x, int y,
+ const SkPaint* paint) {
+ SkPaint tmp;
+ if (NULL == paint) {
+ tmp.setDither(true);
+ paint = &tmp;
+ }
+
+ ITER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
+ while (iter.next()) {
+ iter.fDevice->drawDevice(iter, device, x - iter.getX(), y - iter.getY(),
+ *paint);
+ }
+ ITER_END
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
+ fDeviceCMDirty = true;
+ fLocalBoundsCompareTypeDirty = true;
+ return fMCRec->fMatrix->preTranslate(dx, dy);
+}
+
+bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
+ fDeviceCMDirty = true;
+ fLocalBoundsCompareTypeDirty = true;
+ return fMCRec->fMatrix->preScale(sx, sy);
+}
+
+bool SkCanvas::rotate(SkScalar degrees) {
+ fDeviceCMDirty = true;
+ fLocalBoundsCompareTypeDirty = true;
+ return fMCRec->fMatrix->preRotate(degrees);
+}
+
+bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
+ fDeviceCMDirty = true;
+ fLocalBoundsCompareTypeDirty = true;
+ return fMCRec->fMatrix->preSkew(sx, sy);
+}
+
+bool SkCanvas::concat(const SkMatrix& matrix) {
+ fDeviceCMDirty = true;
+ fLocalBoundsCompareTypeDirty = true;
+ return fMCRec->fMatrix->preConcat(matrix);
+}
+
+void SkCanvas::setMatrix(const SkMatrix& matrix) {
+ fDeviceCMDirty = true;
+ fLocalBoundsCompareTypeDirty = true;
+ *fMCRec->fMatrix = matrix;
+}
+
+// this is not virtual, so it must call a virtual method so that subclasses
+// will see its action
+void SkCanvas::resetMatrix() {
+ SkMatrix matrix;
+
+ matrix.reset();
+ this->setMatrix(matrix);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op) {
+ fDeviceCMDirty = true;
+ fLocalBoundsCompareTypeDirty = true;
+
+ if (fMCRec->fMatrix->rectStaysRect()) {
+ SkRect r;
+ SkIRect ir;
+
+ fMCRec->fMatrix->mapRect(&r, rect);
+ r.round(&ir);
+ return fMCRec->fRegion->op(ir, op);
+ } else {
+ SkPath path;
+
+ path.addRect(rect);
+ return this->clipPath(path, op);
+ }
+}
+
+bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op) {
+ fDeviceCMDirty = true;
+ fLocalBoundsCompareTypeDirty = true;
+
+ SkPath devPath;
+ path.transform(*fMCRec->fMatrix, &devPath);
+
+ if (SkRegion::kIntersect_Op == op) {
+ return fMCRec->fRegion->setPath(devPath, *fMCRec->fRegion);
+ } else {
+ SkRegion base;
+ const SkBitmap& bm = this->getDevice()->accessBitmap(false);
+ base.setRect(0, 0, bm.width(), bm.height());
+
+ if (SkRegion::kReplace_Op == op) {
+ return fMCRec->fRegion->setPath(devPath, base);
+ } else {
+ SkRegion rgn;
+ rgn.setPath(devPath, base);
+ return fMCRec->fRegion->op(rgn, op);
+ }
+ }
+}
+
+bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
+ fDeviceCMDirty = true;
+ fLocalBoundsCompareTypeDirty = true;
+
+ return fMCRec->fRegion->op(rgn, op);
+}
+
+void SkCanvas::computeLocalClipBoundsCompareType() const {
+ SkRect r;
+
+ if (!this->getClipBounds(&r, kAA_EdgeType)) {
+ fLocalBoundsCompareType.setEmpty();
+ } else {
+ fLocalBoundsCompareType.set(SkScalarToCompareType(r.fLeft),
+ SkScalarToCompareType(r.fTop),
+ SkScalarToCompareType(r.fRight),
+ SkScalarToCompareType(r.fBottom));
+ }
+}
+
+bool SkCanvas::quickReject(const SkRect& rect, EdgeType) const {
+ /* current impl ignores edgetype, and relies on
+ getLocalClipBoundsCompareType(), which always returns a value assuming
+ antialiasing (worst case)
+ */
+
+ if (fMCRec->fRegion->isEmpty()) {
+ return true;
+ }
+
+ // check for empty user rect (horizontal)
+ SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
+ SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
+ if (userL >= userR) {
+ return true;
+ }
+
+ // check for empty user rect (vertical)
+ SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
+ SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
+ if (userT >= userB) {
+ return true;
+ }
+
+ // check if we are completely outside of the local clip bounds
+ const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
+ return userL >= clipR.fRight || userT >= clipR.fBottom ||
+ userR <= clipR.fLeft || userB <= clipR.fTop;
+}
+
+bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const {
+ if (fMCRec->fRegion->isEmpty() || path.isEmpty()) {
+ return true;
+ }
+
+ if (fMCRec->fMatrix->rectStaysRect()) {
+ SkRect r;
+ path.computeBounds(&r, SkPath::kFast_BoundsType);
+ return this->quickReject(r, et);
+ }
+
+ SkPath dstPath;
+ SkRect r;
+ SkIRect ir;
+
+ path.transform(*fMCRec->fMatrix, &dstPath);
+ dstPath.computeBounds(&r, SkPath::kFast_BoundsType);
+ r.round(&ir);
+ if (kAA_EdgeType == et) {
+ ir.inset(-1, -1);
+ }
+ return fMCRec->fRegion->quickReject(ir);
+}
+
+bool SkCanvas::quickRejectY(SkScalar top, SkScalar bottom, EdgeType et) const {
+ /* current impl ignores edgetype, and relies on
+ getLocalClipBoundsCompareType(), which always returns a value assuming
+ antialiasing (worst case)
+ */
+
+ if (fMCRec->fRegion->isEmpty()) {
+ return true;
+ }
+
+ SkScalarCompareType userT = SkScalarAs2sCompliment(top);
+ SkScalarCompareType userB = SkScalarAs2sCompliment(bottom);
+
+ // check for invalid user Y coordinates (i.e. empty)
+ if (userT >= userB) {
+ return true;
+ }
+
+ // check if we are above or below the local clip bounds
+ const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
+ return userT >= clipR.fBottom || userB <= clipR.fTop;
+}
+
+bool SkCanvas::getClipBounds(SkRect* bounds, EdgeType et) const {
+ const SkRegion& clip = *fMCRec->fRegion;
+ if (clip.isEmpty()) {
+ if (bounds) {
+ bounds->setEmpty();
+ }
+ return false;
+ }
+
+ if (NULL != bounds) {
+ SkMatrix inverse;
+ SkRect r;
+
+ fMCRec->fMatrix->invert(&inverse);
+
+ // get the clip's bounds
+ const SkIRect& ibounds = clip.getBounds();
+ // adjust it outwards if we are antialiasing
+ int inset = (kAA_EdgeType == et);
+ r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
+ ibounds.fRight + inset, ibounds.fBottom + inset);
+
+ // invert into local coordinates
+ inverse.mapRect(bounds, r);
+ }
+ return true;
+}
+
+const SkMatrix& SkCanvas::getTotalMatrix() const {
+ return *fMCRec->fMatrix;
+}
+
+const SkRegion& SkCanvas::getTotalClip() const {
+ return *fMCRec->fRegion;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkDevice* SkCanvas::createDevice(SkBitmap::Config config, int width,
+ int height, bool isOpaque, bool isForLayer) {
+ SkBitmap bitmap;
+
+ bitmap.setConfig(config, width, height);
+ bitmap.setIsOpaque(isOpaque);
+
+ // should this happen in the device subclass?
+ bitmap.allocPixels();
+ if (!bitmap.isOpaque()) {
+ bitmap.eraseARGB(0, 0, 0, 0);
+ }
+
+ return SkNEW_ARGS(SkDevice, (bitmap));
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// These are the virtual drawing methods
+//////////////////////////////////////////////////////////////////////////////
+
+void SkCanvas::drawPaint(const SkPaint& paint) {
+ ITER_BEGIN(paint, SkDrawFilter::kPaint_Type)
+
+ while (iter.next()) {
+ iter.fDevice->drawPaint(iter, paint);
+ }
+
+ ITER_END
+}
+
+void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
+ const SkPaint& paint) {
+ if ((long)count <= 0) {
+ return;
+ }
+
+ SkASSERT(pts != NULL);
+
+ ITER_BEGIN(paint, SkDrawFilter::kPoint_Type)
+
+ while (iter.next()) {
+ iter.fDevice->drawPoints(iter, mode, count, pts, paint);
+ }
+
+ ITER_END
+}
+
+void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
+ if (paint.canComputeFastBounds()) {
+ SkRect storage;
+ if (this->quickReject(paint.computeFastBounds(r, &storage),
+ paint2EdgeType(&paint))) {
+ return;
+ }
+ }
+
+ ITER_BEGIN(paint, SkDrawFilter::kRect_Type)
+
+ while (iter.next()) {
+ iter.fDevice->drawRect(iter, r, paint);
+ }
+
+ ITER_END
+}
+
+void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
+ if (paint.canComputeFastBounds()) {
+ SkRect r;
+ path.computeBounds(&r, SkPath::kFast_BoundsType);
+ if (this->quickReject(paint.computeFastBounds(r, &r),
+ paint2EdgeType(&paint))) {
+ return;
+ }
+ }
+
+ ITER_BEGIN(paint, SkDrawFilter::kPath_Type)
+
+ while (iter.next()) {
+ iter.fDevice->drawPath(iter, path, paint);
+ }
+
+ ITER_END
+}
+
+void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
+ const SkPaint* paint) {
+ SkDEBUGCODE(bitmap.validate();)
+
+ if (NULL == paint || (paint->getMaskFilter() == NULL)) {
+ SkRect fastBounds;
+ fastBounds.set(x, y,
+ x + SkIntToScalar(bitmap.width()),
+ y + SkIntToScalar(bitmap.height()));
+ if (this->quickReject(fastBounds, paint2EdgeType(paint))) {
+ return;
+ }
+ }
+
+ SkMatrix matrix;
+ matrix.setTranslate(x, y);
+ this->internalDrawBitmap(bitmap, matrix, paint);
+}
+
+void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
+ const SkRect& dst, const SkPaint* paint) {
+ if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
+ return;
+ }
+
+ // do this now, to avoid the cost of calling extract for RLE bitmaps
+ if (this->quickReject(dst, paint2EdgeType(paint))) {
+ return;
+ }
+
+ SkBitmap tmp; // storage if we need a subset of bitmap
+ const SkBitmap* bitmapPtr = &bitmap;
+
+ if (NULL != src) {
+ if (!bitmap.extractSubset(&tmp, *src)) {
+ return; // extraction failed
+ }
+ bitmapPtr = &tmp;
+ }
+
+ SkScalar width = SkIntToScalar(bitmapPtr->width());
+ SkScalar height = SkIntToScalar(bitmapPtr->height());
+ SkMatrix matrix;
+
+ if (dst.width() == width && dst.height() == height) {
+ matrix.setTranslate(dst.fLeft, dst.fTop);
+ } else {
+ SkRect tmpSrc;
+ tmpSrc.set(0, 0, width, height);
+ matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
+ }
+ this->internalDrawBitmap(*bitmapPtr, matrix, paint);
+}
+
+void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
+ const SkPaint* paint) {
+ SkDEBUGCODE(bitmap.validate();)
+ this->internalDrawBitmap(bitmap, matrix, paint);
+}
+
+void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
+ const SkPaint& paint) {
+ SkDEBUGCODE(bitmap.validate();)
+
+ ITER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
+
+ while (iter.next()) {
+ iter.fDevice->drawBitmap(iter, bitmap, matrix, paint);
+ }
+
+ ITER_END
+}
+
+void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
+ const SkPaint* paint) {
+ SkDEBUGCODE(bitmap.validate();)
+
+ if (reject_bitmap(bitmap)) {
+ return;
+ }
+
+ SkPaint tmp;
+ if (NULL == paint) {
+ paint = &tmp;
+ }
+
+ ITER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
+
+ while (iter.next()) {
+ iter.fDevice->drawSprite(iter, bitmap, x - iter.getX(), y - iter.getY(),
+ *paint);
+ }
+ ITER_END
+}
+
+void SkCanvas::drawText(const void* text, size_t byteLength,
+ SkScalar x, SkScalar y, const SkPaint& paint) {
+ ITER_BEGIN(paint, SkDrawFilter::kText_Type)
+
+ while (iter.next()) {
+ iter.fDevice->drawText(iter, text, byteLength, x, y, paint);
+ }
+
+ ITER_END
+}
+
+void SkCanvas::drawPosText(const void* text, size_t byteLength,
+ const SkPoint pos[], const SkPaint& paint) {
+ ITER_BEGIN(paint, SkDrawFilter::kText_Type)
+
+ while (iter.next()) {
+ iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
+ paint);
+ }
+
+ ITER_END
+}
+
+void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
+ const SkScalar xpos[], SkScalar constY,
+ const SkPaint& paint) {
+ ITER_BEGIN(paint, SkDrawFilter::kText_Type)
+
+ while (iter.next()) {
+ iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
+ paint);
+ }
+
+ ITER_END
+}
+
+void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
+ const SkPath& path, const SkMatrix* matrix,
+ const SkPaint& paint) {
+ ITER_BEGIN(paint, SkDrawFilter::kText_Type)
+
+ while (iter.next()) {
+ iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
+ matrix, paint);
+ }
+
+ ITER_END
+}
+
+void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
+ const SkPoint verts[], const SkPoint texs[],
+ const SkColor colors[], SkXfermode* xmode,
+ const uint16_t indices[], int indexCount,
+ const SkPaint& paint) {
+ ITER_BEGIN(paint, SkDrawFilter::kPath_Type)
+
+ while (iter.next()) {
+ iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
+ colors, xmode, indices, indexCount, paint);
+ }
+
+ ITER_END
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// These methods are NOT virtual, and therefore must call back into virtual
+// methods, rather than actually drawing themselves.
+//////////////////////////////////////////////////////////////////////////////
+
+void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
+ SkPorterDuff::Mode mode) {
+ SkPaint paint;
+
+ paint.setARGB(a, r, g, b);
+ if (SkPorterDuff::kSrcOver_Mode != mode) {
+ paint.setPorterDuffXfermode(mode);
+ }
+ this->drawPaint(paint);
+}
+
+void SkCanvas::drawColor(SkColor c, SkPorterDuff::Mode mode) {
+ SkPaint paint;
+
+ paint.setColor(c);
+ if (SkPorterDuff::kSrcOver_Mode != mode) {
+ paint.setPorterDuffXfermode(mode);
+ }
+ this->drawPaint(paint);
+}
+
+void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
+ SkPoint pt;
+
+ pt.set(x, y);
+ this->drawPoints(kPoints_PointMode, 1, &pt, paint);
+}
+
+void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
+ SkPoint pt;
+ SkPaint paint;
+
+ pt.set(x, y);
+ paint.setColor(color);
+ this->drawPoints(kPoints_PointMode, 1, &pt, paint);
+}
+
+void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
+ const SkPaint& paint) {
+ SkPoint pts[2];
+
+ pts[0].set(x0, y0);
+ pts[1].set(x1, y1);
+ this->drawPoints(kLines_PointMode, 2, pts, paint);
+}
+
+void SkCanvas::drawRectCoords(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::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
+ const SkPaint& paint) {
+ if (radius < 0) {
+ radius = 0;
+ }
+
+ SkRect r;
+ r.set(cx - radius, cy - radius, cx + radius, cy + radius);
+
+ if (paint.canComputeFastBounds()) {
+ SkRect storage;
+ if (this->quickReject(paint.computeFastBounds(r, &storage),
+ paint2EdgeType(&paint))) {
+ return;
+ }
+ }
+
+ SkPath path;
+ path.addOval(r);
+ this->drawPath(path, paint);
+}
+
+void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
+ const SkPaint& paint) {
+ if (rx > 0 && ry > 0) {
+ if (paint.canComputeFastBounds()) {
+ SkRect storage;
+ if (this->quickReject(paint.computeFastBounds(r, &storage),
+ paint2EdgeType(&paint))) {
+ return;
+ }
+ }
+
+ SkPath path;
+ path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
+ this->drawPath(path, paint);
+ } else {
+ this->drawRect(r, paint);
+ }
+}
+
+void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
+ if (paint.canComputeFastBounds()) {
+ SkRect storage;
+ if (this->quickReject(paint.computeFastBounds(oval, &storage),
+ paint2EdgeType(&paint))) {
+ return;
+ }
+ }
+
+ SkPath path;
+ path.addOval(oval);
+ this->drawPath(path, paint);
+}
+
+void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
+ SkScalar sweepAngle, bool useCenter,
+ const SkPaint& paint) {
+ if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
+ this->drawOval(oval, paint);
+ } else {
+ SkPath path;
+ if (useCenter) {
+ path.moveTo(oval.centerX(), oval.centerY());
+ }
+ path.arcTo(oval, startAngle, sweepAngle, !useCenter);
+ if (useCenter) {
+ path.close();
+ }
+ this->drawPath(path, paint);
+ }
+}
+
+void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
+ const SkPath& path, SkScalar hOffset,
+ SkScalar vOffset, const SkPaint& paint) {
+ SkMatrix matrix;
+
+ matrix.setTranslate(hOffset, vOffset);
+ this->drawTextOnPath(text, byteLength, path, &matrix, paint);
+}
+
+void SkCanvas::drawPicture(SkPicture& picture) {
+ int saveCount = save();
+ picture.draw(this);
+ restoreToCount(saveCount);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
+ // need COMPILE_TIME_ASSERT
+ SkASSERT(sizeof(fStorage) >= sizeof(SkDrawIter));
+
+ SkASSERT(canvas);
+
+ fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
+ fDone = !fImpl->next();
+}
+
+SkCanvas::LayerIter::~LayerIter() {
+ fImpl->~SkDrawIter();
+}
+
+void SkCanvas::LayerIter::next() {
+ fDone = !fImpl->next();
+}
+
+SkDevice* SkCanvas::LayerIter::device() const {
+ return fImpl->getDevice();
+}
+
+const SkMatrix& SkCanvas::LayerIter::matrix() const {
+ return fImpl->getMatrix();
+}
+
+const SkPaint& SkCanvas::LayerIter::paint() const {
+ const SkPaint* paint = fImpl->getPaint();
+ if (NULL == paint) {
+ paint = &fDefaultPaint;
+ }
+ return *paint;
+}
+
+const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
+int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
+int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
+
diff --git a/src/core/SkChunkAlloc.cpp b/src/core/SkChunkAlloc.cpp
new file mode 100644
index 0000000000..ae37ec0839
--- /dev/null
+++ b/src/core/SkChunkAlloc.cpp
@@ -0,0 +1,120 @@
+/* libs/corecg/SkChunkAlloc.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkChunkAlloc.h"
+
+struct SkChunkAlloc::Block {
+ Block* fNext;
+ size_t fFreeSize;
+ char* fFreePtr;
+ // data[] follows
+
+ void freeChain() { // this can be null
+ Block* block = this;
+ while (block) {
+ Block* next = block->fNext;
+ sk_free(block);
+ block = next;
+ }
+ };
+
+ Block* tail() {
+ Block* block = this;
+ if (block) {
+ for (;;) {
+ Block* next = block->fNext;
+ if (NULL == next) {
+ break;
+ }
+ block = next;
+ }
+ }
+ return block;
+ }
+};
+
+SkChunkAlloc::SkChunkAlloc(size_t minSize)
+ : fBlock(NULL), fMinSize(SkAlign4(minSize)), fPool(NULL), fTotalCapacity(0)
+{
+}
+
+SkChunkAlloc::~SkChunkAlloc() {
+ this->reset();
+}
+
+void SkChunkAlloc::reset() {
+ fBlock->freeChain();
+ fBlock = NULL;
+ fPool->freeChain();
+ fPool = NULL;
+ fTotalCapacity = 0;
+}
+
+void SkChunkAlloc::reuse() {
+ if (fPool && fBlock) {
+ fPool->tail()->fNext = fBlock;
+ }
+ fPool = fBlock;
+ fBlock = NULL;
+ fTotalCapacity = 0;
+}
+
+SkChunkAlloc::Block* SkChunkAlloc::newBlock(size_t bytes, AllocFailType ftype) {
+ Block* block = fPool;
+
+ if (block && bytes <= block->fFreeSize) {
+ fPool = block->fNext;
+ return block;
+ }
+
+ size_t size = SkMax32((int32_t)bytes, (int32_t)fMinSize);
+
+ block = (Block*)sk_malloc_flags(sizeof(Block) + size,
+ ftype == kThrow_AllocFailType ? SK_MALLOC_THROW : 0);
+
+ if (block) {
+ // block->fNext = fBlock;
+ block->fFreeSize = size;
+ block->fFreePtr = (char*)block + sizeof(Block);
+
+ fTotalCapacity += size;
+ }
+ return block;
+}
+
+void* SkChunkAlloc::alloc(size_t bytes, AllocFailType ftype) {
+ bytes = SkAlign4(bytes);
+
+ Block* block = fBlock;
+
+ if (block == NULL || bytes > block->fFreeSize) {
+ block = this->newBlock(bytes, ftype);
+ if (NULL == block) {
+ return NULL;
+ }
+ block->fNext = fBlock;
+ fBlock = block;
+ }
+
+ SkASSERT(block && bytes <= block->fFreeSize);
+ void* ptr = block->fFreePtr;
+
+ block->fFreeSize -= bytes;
+ block->fFreePtr += bytes;
+ return ptr;
+}
+
diff --git a/src/core/SkColor.cpp b/src/core/SkColor.cpp
new file mode 100644
index 0000000000..4256179eff
--- /dev/null
+++ b/src/core/SkColor.cpp
@@ -0,0 +1,138 @@
+/* libs/graphics/sgl/SkColor.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkColor.h"
+#include "SkColorPriv.h"
+
+SkPMColor SkPreMultiplyARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) {
+ if (a != 255) {
+#if 0
+ unsigned scale = SkAlpha255To256(a);
+ r = SkAlphaMul(r, scale);
+ g = SkAlphaMul(g, scale);
+ b = SkAlphaMul(b, scale);
+#else
+ r = SkMulDiv255Round(r, a);
+ g = SkMulDiv255Round(g, a);
+ b = SkMulDiv255Round(b, a);
+#endif
+ }
+ 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] = 0;
+ hsv[1] = 0;
+ 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/src/core/SkColorFilter.cpp b/src/core/SkColorFilter.cpp
new file mode 100644
index 0000000000..bb4be485d6
--- /dev/null
+++ b/src/core/SkColorFilter.cpp
@@ -0,0 +1,108 @@
+/* libs/graphics/sgl/SkColorFilter.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkColorFilter.h"
+#include "SkShader.h"
+
+void SkColorFilter::filterSpan16(const uint16_t s[], int count, uint16_t d[])
+{
+ SkASSERT(this->getFlags() & SkColorFilter::kHasFilter16_Flag);
+ SkASSERT(!"missing implementation of SkColorFilter::filterSpan16");
+
+ if (d != s)
+ memcpy(d, s, count * sizeof(uint16_t));
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+SkFilterShader::SkFilterShader(SkShader* shader, SkColorFilter* filter)
+{
+ fShader = shader; shader->ref();
+ fFilter = filter; filter->ref();
+}
+
+SkFilterShader::SkFilterShader(SkFlattenableReadBuffer& buffer) :
+ INHERITED(buffer)
+{
+ fShader = static_cast<SkShader*>(buffer.readFlattenable());
+ fFilter = static_cast<SkColorFilter*>(buffer.readFlattenable());
+}
+
+SkFilterShader::~SkFilterShader()
+{
+ fFilter->unref();
+ fShader->unref();
+}
+
+void SkFilterShader::beginSession()
+{
+ this->INHERITED::beginSession();
+ fShader->beginSession();
+}
+
+void SkFilterShader::endSession()
+{
+ fShader->endSession();
+ this->INHERITED::endSession();
+}
+
+void SkFilterShader::flatten(SkFlattenableWriteBuffer& buffer)
+{
+ this->INHERITED::flatten(buffer);
+ buffer.writeFlattenable(fShader);
+ buffer.writeFlattenable(fFilter);
+}
+
+uint32_t SkFilterShader::getFlags()
+{
+ uint32_t shaderF = fShader->getFlags();
+ uint32_t filterF = fFilter->getFlags();
+
+ // if the filter doesn't support 16bit, clear the matching bit in the shader
+ if (!(filterF & SkColorFilter::kHasFilter16_Flag))
+ shaderF &= ~SkShader::kHasSpan16_Flag;
+
+ // if the filter might change alpha, clear the opaque flag in the shader
+ if (!(filterF & SkColorFilter::kAlphaUnchanged_Flag))
+ shaderF &= ~(SkShader::kOpaqueAlpha_Flag | SkShader::kHasSpan16_Flag);
+
+ return shaderF;
+}
+
+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);
+}
+
+void SkFilterShader::shadeSpan16(int x, int y, uint16_t result[], int count)
+{
+ SkASSERT(fShader->getFlags() & SkShader::kHasSpan16_Flag);
+ SkASSERT(fFilter->getFlags() & SkColorFilter::kHasFilter16_Flag);
+
+ fShader->shadeSpan16(x, y, result, count);
+ fFilter->filterSpan16(result, count, result);
+}
+
diff --git a/src/core/SkColorTable.cpp b/src/core/SkColorTable.cpp
new file mode 100644
index 0000000000..f991da3bc0
--- /dev/null
+++ b/src/core/SkColorTable.cpp
@@ -0,0 +1,143 @@
+/* libs/graphics/sgl/SkColorTable.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkBitmap.h"
+#include "SkFlattenable.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+
+SkColorTable::SkColorTable(int count)
+ : f16BitCache(NULL), fFlags(0)
+{
+ if (count < 0)
+ count = 0;
+ else if (count > 256)
+ count = 256;
+
+ fCount = SkToU16(count);
+ fColors = (SkPMColor*)sk_malloc_throw(count * sizeof(SkPMColor));
+ memset(fColors, 0, count * sizeof(SkPMColor));
+
+ SkDEBUGCODE(fColorLockCount = 0;)
+ SkDEBUGCODE(f16BitCacheLockCount = 0;)
+}
+
+SkColorTable::SkColorTable(const SkPMColor colors[], int count)
+ : f16BitCache(NULL), fFlags(0)
+{
+ if (count < 0)
+ count = 0;
+ else if (count > 256)
+ count = 256;
+
+ fCount = SkToU16(count);
+ fColors = (SkPMColor*)sk_malloc_throw(count * sizeof(SkPMColor));
+
+ if (colors)
+ memcpy(fColors, colors, count * sizeof(SkPMColor));
+
+ 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::unlockColors(bool changed)
+{
+ SkASSERT(fColorLockCount != 0);
+ SkDEBUGCODE(fColorLockCount -= 1;)
+ if (changed)
+ this->inval16BitCache();
+}
+
+void SkColorTable::inval16BitCache()
+{
+ SkASSERT(f16BitCacheLockCount == 0);
+ if (f16BitCache)
+ {
+ sk_free(f16BitCache);
+ f16BitCache = NULL;
+ }
+}
+
+#include "SkColorPriv.h"
+
+static inline void build_16bitcache(uint16_t dst[], const SkPMColor src[], int count)
+{
+ while (--count >= 0)
+ *dst++ = SkPixel32ToPixel16_ToU16(*src++);
+}
+
+const uint16_t* SkColorTable::lock16BitCache()
+{
+ if (fFlags & kColorsAreOpaque_Flag)
+ {
+ if (f16BitCache == NULL) // build the cache
+ {
+ f16BitCache = (uint16_t*)sk_malloc_throw(fCount * sizeof(uint16_t));
+ build_16bitcache(f16BitCache, fColors, fCount);
+ }
+ }
+ else // our colors have alpha, so no cache
+ {
+ this->inval16BitCache();
+ if (f16BitCache)
+ {
+ sk_free(f16BitCache);
+ f16BitCache = NULL;
+ }
+ }
+
+ SkDEBUGCODE(f16BitCacheLockCount += 1);
+ return f16BitCache;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkColorTable::SkColorTable(SkFlattenableReadBuffer& buffer) {
+ f16BitCache = NULL;
+ SkDEBUGCODE(fColorLockCount = 0;)
+ SkDEBUGCODE(f16BitCacheLockCount = 0;)
+
+ fCount = buffer.readU16();
+ SkASSERT((unsigned)fCount <= 256);
+
+ fFlags = buffer.readU8();
+
+ fColors = (SkPMColor*)sk_malloc_throw(fCount * sizeof(SkPMColor));
+ buffer.read(fColors, fCount * sizeof(SkPMColor));
+}
+
+void SkColorTable::flatten(SkFlattenableWriteBuffer& buffer) const {
+ int count = this->count();
+ buffer.write16(count);
+ buffer.write8(this->getFlags());
+ buffer.writeMul4(fColors, count * sizeof(SkPMColor));
+}
+
diff --git a/src/core/SkComposeShader.cpp b/src/core/SkComposeShader.cpp
new file mode 100644
index 0000000000..1e5e202b8e
--- /dev/null
+++ b/src/core/SkComposeShader.cpp
@@ -0,0 +1,174 @@
+/* libs/graphics/effects/SkShaderExtras.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkComposeShader.h"
+#include "SkColorFilter.h"
+#include "SkColorPriv.h"
+#include "SkXfermode.h"
+
+//////////////////////////////////////////////////////////////////////////////////////
+
+SkComposeShader::SkComposeShader(SkShader* sA, SkShader* sB, SkXfermode* mode)
+{
+ fShaderA = sA; sA->ref();
+ fShaderB = sB; sB->ref();
+ // mode may be null
+ fMode = mode; mode->safeRef();
+}
+
+SkComposeShader::SkComposeShader(SkFlattenableReadBuffer& buffer) :
+ INHERITED(buffer)
+{
+ fShaderA = static_cast<SkShader*>(buffer.readFlattenable());
+ fShaderB = static_cast<SkShader*>(buffer.readFlattenable());
+ fMode = static_cast<SkXfermode*>(buffer.readFlattenable());
+}
+
+SkComposeShader::~SkComposeShader()
+{
+ fMode->safeUnref(); // may be null
+ fShaderB->unref();
+ fShaderA->unref();
+}
+
+void SkComposeShader::beginSession()
+{
+ this->INHERITED::beginSession();
+ fShaderA->beginSession();
+ fShaderB->beginSession();
+}
+
+void SkComposeShader::endSession()
+{
+ fShaderA->endSession();
+ fShaderB->endSession();
+ this->INHERITED::endSession();
+}
+
+class SkAutoAlphaRestore {
+public:
+ SkAutoAlphaRestore(SkPaint* paint, uint8_t newAlpha)
+ {
+ fAlpha = paint->getAlpha();
+ fPaint = paint;
+ paint->setAlpha(newAlpha);
+ }
+ ~SkAutoAlphaRestore()
+ {
+ fPaint->setAlpha(fAlpha);
+ }
+private:
+ SkPaint* fPaint;
+ uint8_t fAlpha;
+};
+
+void SkComposeShader::flatten(SkFlattenableWriteBuffer& buffer)
+{
+ this->INHERITED::flatten(buffer);
+ buffer.writeFlattenable(fShaderA);
+ buffer.writeFlattenable(fShaderB);
+ buffer.writeFlattenable(fMode);
+}
+
+/* We call setContext on our two worker shaders. However, we
+ always let them see opaque alpha, and if the paint really
+ is translucent, then we apply that after the fact.
+*/
+bool SkComposeShader::setContext(const SkBitmap& device,
+ const SkPaint& paint,
+ const SkMatrix& matrix)
+{
+ if (!this->INHERITED::setContext(device, paint, matrix))
+ return false;
+
+ // we preconcat our localMatrix (if any) with the device matrix
+ // before calling our sub-shaders
+
+ SkMatrix tmpM;
+
+ (void)this->getLocalMatrix(&tmpM);
+ tmpM.setConcat(matrix, tmpM);
+
+ SkAutoAlphaRestore restore(const_cast<SkPaint*>(&paint), 0xFF);
+
+ return fShaderA->setContext(device, paint, tmpM) &&
+ fShaderB->setContext(device, paint, tmpM);
+}
+
+// larger is better (fewer times we have to loop), but we shouldn't
+// take up too much stack-space (each element is 4 bytes)
+#define TMP_COLOR_COUNT 64
+
+void SkComposeShader::shadeSpan(int x, int y, SkPMColor result[], int count)
+{
+ SkShader* shaderA = fShaderA;
+ SkShader* shaderB = fShaderB;
+ SkXfermode* mode = fMode;
+ unsigned scale = SkAlpha255To256(this->getPaintAlpha());
+
+ SkPMColor tmp[TMP_COLOR_COUNT];
+
+ if (NULL == mode) // implied SRC_OVER
+ {
+ do {
+ int n = count;
+ if (n > TMP_COLOR_COUNT)
+ n = TMP_COLOR_COUNT;
+
+ shaderA->shadeSpan(x, y, result, n);
+ shaderB->shadeSpan(x, y, tmp, n);
+
+ if (256 == scale)
+ {
+ for (int i = 0; i < n; i++)
+ result[i] = SkPMSrcOver(tmp[i], result[i]);
+ }
+ else
+ {
+ for (int i = 0; i < n; i++)
+ result[i] = SkAlphaMulQ(SkPMSrcOver(tmp[i], result[i]), scale);
+ }
+
+ result += n;
+ x += n;
+ count -= n;
+ } while (count > 0);
+ }
+ else // use mode for the composition
+ {
+ do {
+ int n = count;
+ if (n > TMP_COLOR_COUNT)
+ n = TMP_COLOR_COUNT;
+
+ shaderA->shadeSpan(x, y, result, n);
+ shaderB->shadeSpan(x, y, tmp, n);
+ mode->xfer32(result, tmp, n, NULL);
+
+ if (256 == scale)
+ {
+ for (int i = 0; i < n; i++)
+ result[i] = SkAlphaMulQ(result[i], scale);
+ }
+
+ result += n;
+ x += n;
+ count -= n;
+ } while (count > 0);
+ }
+}
+
diff --git a/src/core/SkCordic.cpp b/src/core/SkCordic.cpp
new file mode 100644
index 0000000000..539bc9b6d7
--- /dev/null
+++ b/src/core/SkCordic.cpp
@@ -0,0 +1,301 @@
+/* libs/corecg/SkCordic.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkCordic.h"
+#include "SkMath.h"
+#include "Sk64.h"
+
+// 0x20000000 equals pi / 4
+const int32_t kATanDegrees[] = { 0x20000000,
+ 0x12E4051D, 0x9FB385B, 0x51111D4, 0x28B0D43, 0x145D7E1, 0xA2F61E, 0x517C55,
+ 0x28BE53, 0x145F2E, 0xA2F98, 0x517CC, 0x28BE6, 0x145F3, 0xA2F9, 0x517C,
+ 0x28BE, 0x145F, 0xA2F, 0x517, 0x28B, 0x145, 0xA2, 0x51, 0x28, 0x14,
+ 0xA, 0x5, 0x2, 0x1 };
+
+const int32_t kFixedInvGain1 = 0x18bde0bb; // 0.607252935
+
+static void SkCircularRotation(int32_t* x0, int32_t* y0, int32_t* z0)
+{
+ int32_t t = 0;
+ int32_t x = *x0;
+ int32_t y = *y0;
+ int32_t z = *z0;
+ const int32_t* tanPtr = kATanDegrees;
+ do {
+ int32_t x1 = y >> t;
+ int32_t y1 = x >> t;
+ int32_t tan = *tanPtr++;
+ if (z >= 0) {
+ x -= x1;
+ y += y1;
+ z -= tan;
+ } else {
+ x += x1;
+ y -= y1;
+ z += tan;
+ }
+ } while (++t < 16); // 30);
+ *x0 = x;
+ *y0 = y;
+ *z0 = z;
+}
+
+SkFixed SkCordicSinCos(SkFixed radians, SkFixed* cosp)
+{
+ int32_t scaledRadians = radians * 0x28be; // scale radians to 65536 / PI()
+ int quadrant = scaledRadians >> 30;
+ quadrant += 1;
+ if (quadrant & 2)
+ scaledRadians = -scaledRadians + 0x80000000;
+ /* |a| <= 90 degrees as a 1.31 number */
+ SkFixed sin = 0;
+ SkFixed cos = kFixedInvGain1;
+ SkCircularRotation(&cos, &sin, &scaledRadians);
+ Sk64 scaled;
+ scaled.setMul(sin, 0x6488d);
+ sin = scaled.fHi;
+ scaled.setMul(cos, 0x6488d);
+ if (quadrant & 2)
+ scaled.fHi = - scaled.fHi;
+ *cosp = scaled.fHi;
+ return sin;
+}
+
+SkFixed SkCordicTan(SkFixed a)
+{
+ int32_t cos;
+ int32_t sin = SkCordicSinCos(a, &cos);
+ return SkFixedDiv(sin, cos);
+}
+
+static int32_t SkCircularVector(int32_t* y0, int32_t* x0, int32_t vecMode)
+{
+ int32_t x = *x0;
+ int32_t y = *y0;
+ int32_t z = 0;
+ int32_t t = 0;
+ const int32_t* tanPtr = kATanDegrees;
+ do {
+ int32_t x1 = y >> t;
+ int32_t y1 = x >> t;
+ int32_t tan = *tanPtr++;
+ if (y < vecMode) {
+ x -= x1;
+ y += y1;
+ z -= tan;
+ } else {
+ x += x1;
+ y -= y1;
+ z += tan;
+ }
+ } while (++t < 16); // 30
+ Sk64 scaled;
+ scaled.setMul(z, 0x6488d); // scale back into the SkScalar space (0x100000000/0x28be)
+ return scaled.fHi;
+}
+
+SkFixed SkCordicASin(SkFixed a) {
+ int32_t sign = SkExtractSign(a);
+ int32_t z = SkFixedAbs(a);
+ if (z >= SK_Fixed1)
+ return SkApplySign(SK_FixedPI>>1, sign);
+ int32_t x = kFixedInvGain1;
+ int32_t y = 0;
+ z *= 0x28be;
+ z = SkCircularVector(&y, &x, z);
+ z = SkApplySign(z, ~sign);
+ return z;
+}
+
+SkFixed SkCordicACos(SkFixed a) {
+ int32_t z = SkCordicASin(a);
+ z = (SK_FixedPI>>1) - z;
+ return z;
+}
+
+SkFixed SkCordicATan2(SkFixed y, SkFixed x) {
+ if ((x | y) == 0)
+ return 0;
+ int32_t xsign = SkExtractSign(x);
+ x = SkFixedAbs(x);
+ int32_t result = SkCircularVector(&y, &x, 0);
+ if (xsign) {
+ int32_t rsign = SkExtractSign(result);
+ if (y == 0)
+ rsign = 0;
+ SkFixed pi = SkApplySign(SK_FixedPI, rsign);
+ result = pi - result;
+ }
+ return result;
+}
+
+const int32_t kATanHDegrees[] = {
+ 0x1661788D, 0xA680D61, 0x51EA6FC, 0x28CBFDD, 0x1460E34,
+ 0xA2FCE8, 0x517D2E, 0x28BE6E, 0x145F32,
+ 0xA2F98, 0x517CC, 0x28BE6, 0x145F3, 0xA2F9, 0x517C,
+ 0x28BE, 0x145F, 0xA2F, 0x517, 0x28B, 0x145, 0xA2, 0x51, 0x28, 0x14,
+ 0xA, 0x5, 0x2, 0x1 };
+
+const int32_t kFixedInvGain2 = 0x31330AAA; // 1.207534495
+
+static void SkHyperbolic(int32_t* x0, int32_t* y0, int32_t* z0, int mode)
+{
+ int32_t t = 1;
+ int32_t x = *x0;
+ int32_t y = *y0;
+ int32_t z = *z0;
+ const int32_t* tanPtr = kATanHDegrees;
+ int k = -3;
+ do {
+ int32_t x1 = y >> t;
+ int32_t y1 = x >> t;
+ int32_t tan = *tanPtr++;
+ int count = 2 + (k >> 31);
+ if (++k == 1)
+ k = -2;
+ do {
+ if (((y >> 31) & mode) | ~((z >> 31) | mode)) {
+ x += x1;
+ y += y1;
+ z -= tan;
+ } else {
+ x -= x1;
+ y -= y1;
+ z += tan;
+ }
+ } while (--count);
+ } while (++t < 30);
+ *x0 = x;
+ *y0 = y;
+ *z0 = z;
+}
+
+SkFixed SkCordicLog(SkFixed a) {
+ a *= 0x28be;
+ int32_t x = a + 0x28BE60DB; // 1.0
+ int32_t y = a - 0x28BE60DB;
+ int32_t z = 0;
+ SkHyperbolic(&x, &y, &z, -1);
+ Sk64 scaled;
+ scaled.setMul(z, 0x6488d);
+ z = scaled.fHi;
+ return z << 1;
+}
+
+SkFixed SkCordicExp(SkFixed a) {
+ int32_t cosh = kFixedInvGain2;
+ int32_t sinh = 0;
+ SkHyperbolic(&cosh, &sinh, &a, 0);
+ return cosh + sinh;
+}
+
+#ifdef SK_DEBUG
+
+#ifdef SK_CAN_USE_FLOAT
+ #include "SkFloatingPoint.h"
+#endif
+
+void SkCordic_UnitTest()
+{
+#if defined(SK_SUPPORT_UNITTEST) && defined(SK_CAN_USE_FLOAT)
+ float val;
+ for (float angle = -720; angle < 720; angle += 30) {
+ float radian = angle * 3.1415925358f / 180.0f;
+ SkFixed f_angle = (int) (radian * 65536.0f);
+ // sincos
+ float sine = sinf(radian);
+ float cosine = cosf(radian);
+ SkFixed f_cosine;
+ SkFixed f_sine = SkCordicSinCos(f_angle, &f_cosine);
+ float sine2 = (float) f_sine / 65536.0f;
+ float cosine2 = (float) f_cosine / 65536.0f;
+ float error = fabsf(sine - sine2);
+ if (error > 0.001)
+ SkDebugf("sin error : angle = %g ; sin = %g ; cordic = %g\n", angle, sine, sine2);
+ error = fabsf(cosine - cosine2);
+ if (error > 0.001)
+ SkDebugf("cos error : angle = %g ; cos = %g ; cordic = %g\n", angle, cosine, cosine2);
+ // tan
+ float _tan = tanf(radian);
+ SkFixed f_tan = SkCordicTan(f_angle);
+ float tan2 = (float) f_tan / 65536.0f;
+ error = fabsf(_tan - tan2);
+ if (error > 0.05 && fabsf(_tan) < 1e6)
+ SkDebugf("tan error : angle = %g ; tan = %g ; cordic = %g\n", angle, _tan, tan2);
+ }
+ for (val = -1; val <= 1; val += .1f) {
+ SkFixed f_val = (int) (val * 65536.0f);
+ // asin
+ float arcsine = asinf(val);
+ SkFixed f_arcsine = SkCordicASin(f_val);
+ float arcsine2 = (float) f_arcsine / 65536.0f;
+ float error = fabsf(arcsine - arcsine2);
+ if (error > 0.001)
+ SkDebugf("asin error : val = %g ; asin = %g ; cordic = %g\n", val, arcsine, arcsine2);
+ }
+#if 1
+ for (val = -1; val <= 1; val += .1f) {
+#else
+ val = .5; {
+#endif
+ SkFixed f_val = (int) (val * 65536.0f);
+ // acos
+ float arccos = acosf(val);
+ SkFixed f_arccos = SkCordicACos(f_val);
+ float arccos2 = (float) f_arccos / 65536.0f;
+ float error = fabsf(arccos - arccos2);
+ if (error > 0.001)
+ SkDebugf("acos error : val = %g ; acos = %g ; cordic = %g\n", val, arccos, arccos2);
+ }
+ // atan2
+#if 1
+ for (val = -1000; val <= 1000; val += 500.f) {
+ for (float val2 = -1000; val2 <= 1000; val2 += 500.f) {
+#else
+ val = 0; {
+ float val2 = -1000; {
+#endif
+ SkFixed f_val = (int) (val * 65536.0f);
+ SkFixed f_val2 = (int) (val2 * 65536.0f);
+ float arctan = atan2f(val, val2);
+ SkFixed f_arctan = SkCordicATan2(f_val, f_val2);
+ float arctan2 = (float) f_arctan / 65536.0f;
+ float error = fabsf(arctan - arctan2);
+ if (error > 0.001)
+ SkDebugf("atan2 error : val = %g ; val2 = %g ; atan2 = %g ; cordic = %g\n", val, val2, arctan, arctan2);
+ }
+ }
+ // log
+#if 1
+ for (val = 0.125f; val <= 8.f; val *= 2.0f) {
+#else
+ val = .5; {
+#endif
+ SkFixed f_val = (int) (val * 65536.0f);
+ // acos
+ float log = logf(val);
+ SkFixed f_log = SkCordicLog(f_val);
+ float log2 = (float) f_log / 65536.0f;
+ float error = fabsf(log - log2);
+ if (error > 0.001)
+ SkDebugf("log error : val = %g ; log = %g ; cordic = %g\n", val, log, log2);
+ }
+ // exp
+#endif
+}
+
+#endif
diff --git a/src/core/SkCordic.h b/src/core/SkCordic.h
new file mode 100644
index 0000000000..9f45a811be
--- /dev/null
+++ b/src/core/SkCordic.h
@@ -0,0 +1,37 @@
+/* libs/corecg/SkCordic.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef SkCordic_DEFINED
+#define SkCordic_DEFINED
+
+#include "SkTypes.h"
+#include "SkFixed.h"
+
+SkFixed SkCordicACos(SkFixed a);
+SkFixed SkCordicASin(SkFixed a);
+SkFixed SkCordicATan2(SkFixed y, SkFixed x);
+SkFixed SkCordicExp(SkFixed a);
+SkFixed SkCordicLog(SkFixed a);
+SkFixed SkCordicSinCos(SkFixed radians, SkFixed* cosp);
+SkFixed SkCordicTan(SkFixed a);
+
+#ifdef SK_DEBUG
+ void SkCordic_UnitTest();
+#endif
+
+#endif // SkCordic
+
diff --git a/src/core/SkCoreBlitters.h b/src/core/SkCoreBlitters.h
new file mode 100644
index 0000000000..5b3497e089
--- /dev/null
+++ b/src/core/SkCoreBlitters.h
@@ -0,0 +1,259 @@
+/* libs/graphics/sgl/SkCoreBlitters.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef SkCoreBlitters_DEFINED
+#define SkCoreBlitters_DEFINED
+
+#include "SkBlitter.h"
+#include "SkBlitRow.h"
+
+class SkRasterBlitter : public SkBlitter {
+public:
+ SkRasterBlitter(const SkBitmap& device) : fDevice(device) {}
+
+protected:
+ const SkBitmap& fDevice;
+
+private:
+ typedef SkBlitter INHERITED;
+};
+
+class SkShaderBlitter : public SkRasterBlitter {
+public:
+ SkShaderBlitter(const SkBitmap& device, const SkPaint& paint);
+ virtual ~SkShaderBlitter();
+
+protected:
+ SkShader* fShader;
+
+private:
+ // illegal
+ SkShaderBlitter& operator=(const SkShaderBlitter&);
+
+ typedef SkRasterBlitter INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkA8_Blitter : public SkRasterBlitter {
+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 int16_t 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 SkIRect&);
+ virtual const SkBitmap* justAnOpaqueColor(uint32_t*);
+
+private:
+ unsigned fSrcA;
+
+ // illegal
+ SkA8_Blitter& operator=(const SkA8_Blitter&);
+
+ typedef SkRasterBlitter INHERITED;
+};
+
+class SkA8_Shader_Blitter : public SkShaderBlitter {
+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 int16_t runs[]);
+ virtual void blitMask(const SkMask&, const SkIRect&);
+
+private:
+ SkXfermode* fXfermode;
+ SkPMColor* fBuffer;
+ uint8_t* fAAExpand;
+
+ // illegal
+ SkA8_Shader_Blitter& operator=(const SkA8_Shader_Blitter&);
+
+ typedef SkShaderBlitter INHERITED;
+};
+
+////////////////////////////////////////////////////////////////
+
+class SkARGB32_Blitter : public SkRasterBlitter {
+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 int16_t 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 SkIRect&);
+ virtual const SkBitmap* justAnOpaqueColor(uint32_t*);
+
+protected:
+ SkColor fPMColor;
+
+private:
+ unsigned fSrcA, fSrcR, fSrcG, fSrcB;
+
+ // illegal
+ SkARGB32_Blitter& operator=(const SkARGB32_Blitter&);
+
+ typedef SkRasterBlitter INHERITED;
+};
+
+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 SkIRect&);
+ virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]);
+
+private:
+ typedef SkARGB32_Blitter INHERITED;
+};
+
+class SkARGB32_Opaque_Blitter : public SkARGB32_Blitter {
+public:
+ SkARGB32_Opaque_Blitter(const SkBitmap& device, const SkPaint& paint)
+ : SkARGB32_Blitter(device, paint) { SkASSERT(paint.getAlpha() == 0xFF); }
+ virtual void blitMask(const SkMask&, const SkIRect&);
+
+private:
+ typedef SkARGB32_Blitter INHERITED;
+};
+
+class SkARGB32_Shader_Blitter : public SkShaderBlitter {
+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 int16_t runs[]);
+
+private:
+ SkXfermode* fXfermode;
+ SkPMColor* fBuffer;
+
+ // illegal
+ SkARGB32_Shader_Blitter& operator=(const SkARGB32_Shader_Blitter&);
+
+ typedef SkShaderBlitter INHERITED;
+};
+
+////////////////////////////////////////////////////////////////
+
+class SkRGB16_Blitter : public SkRasterBlitter {
+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 int16_t 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 SkIRect&);
+ virtual const SkBitmap* justAnOpaqueColor(uint32_t*);
+
+private:
+ SkPMColor fSrcColor32;
+ unsigned fScale;
+ uint16_t fColor16; // already scaled by fScale
+ uint16_t fRawColor16; // unscaled
+ uint16_t fRawDither16; // unscaled
+ SkBool8 fDoDither;
+
+ // illegal
+ SkRGB16_Blitter& operator=(const SkRGB16_Blitter&);
+
+ typedef SkRasterBlitter INHERITED;
+};
+
+class SkRGB16_Black_Blitter : public SkRGB16_Blitter {
+public:
+ SkRGB16_Black_Blitter(const SkBitmap& device, const SkPaint& paint);
+
+ virtual void blitMask(const SkMask&, const SkIRect&);
+ virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]);
+
+private:
+ typedef SkRGB16_Blitter INHERITED;
+};
+
+class SkRGB16_Shader_Blitter : public SkShaderBlitter {
+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 int16_t runs[]);
+
+protected:
+ SkPMColor* fBuffer;
+ SkBlitRow::Proc fOpaqueProc;
+ SkBlitRow::Proc fAlphaProc;
+
+private:
+ // illegal
+ SkRGB16_Shader_Blitter& operator=(const SkRGB16_Shader_Blitter&);
+
+ typedef SkShaderBlitter INHERITED;
+};
+
+// used only if the shader can perform shadSpan16
+class SkRGB16_Shader16_Blitter : public SkRGB16_Shader_Blitter {
+public:
+ SkRGB16_Shader16_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 int16_t runs[]);
+
+private:
+ typedef SkRGB16_Shader_Blitter INHERITED;
+};
+
+class SkRGB16_Shader_Xfermode_Blitter : public SkShaderBlitter {
+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 int16_t runs[]);
+
+private:
+ SkXfermode* fXfermode;
+ SkPMColor* fBuffer;
+ uint8_t* fAAExpand;
+
+ // illegal
+ SkRGB16_Shader_Xfermode_Blitter& operator=(const SkRGB16_Shader_Xfermode_Blitter&);
+
+ typedef SkShaderBlitter INHERITED;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+class SkA1_Blitter : public SkRasterBlitter {
+public:
+ SkA1_Blitter(const SkBitmap& device, const SkPaint& paint);
+ virtual void blitH(int x, int y, int width);
+
+private:
+ uint8_t fSrcA;
+
+ // illegal
+ SkA1_Blitter& operator=(const SkA1_Blitter&);
+
+ typedef SkRasterBlitter INHERITED;
+};
+
+
+extern SkBlitter* SkBlitter_ChooseD4444(const SkBitmap& device,
+ const SkPaint& paint,
+ void* storage, size_t storageSize);
+
+#endif
+
diff --git a/src/core/SkDebug.cpp b/src/core/SkDebug.cpp
new file mode 100644
index 0000000000..64ea8b428f
--- /dev/null
+++ b/src/core/SkDebug.cpp
@@ -0,0 +1,59 @@
+/* libs/corecg/SkDebug.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkTypes.h"
+
+#ifdef SK_DEBUG
+
+int8_t SkToS8(long x)
+{
+ SkASSERT((int8_t)x == x);
+ return (int8_t)x;
+}
+
+uint8_t SkToU8(size_t x)
+{
+ SkASSERT((uint8_t)x == x);
+ return (uint8_t)x;
+}
+
+int16_t SkToS16(long x)
+{
+ SkASSERT((int16_t)x == x);
+ return (int16_t)x;
+}
+
+uint16_t SkToU16(size_t x)
+{
+ SkASSERT((uint16_t)x == x);
+ return (uint16_t)x;
+}
+
+int32_t SkToS32(long x)
+{
+ SkASSERT((int32_t)x == x);
+ return (int32_t)x;
+}
+
+uint32_t SkToU32(size_t x)
+{
+ SkASSERT((uint32_t)x == x);
+ return (uint32_t)x;
+}
+
+#endif
+
diff --git a/src/core/SkDebug_stdio.cpp b/src/core/SkDebug_stdio.cpp
new file mode 100644
index 0000000000..c8a0d8123e
--- /dev/null
+++ b/src/core/SkDebug_stdio.cpp
@@ -0,0 +1,61 @@
+/* libs/corecg/SkDebug_stdio.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkTypes.h"
+
+static const size_t kBufferSize = 256;
+
+#ifdef ANDROID
+
+#define LOG_TAG "skia"
+#include <utils/Log.h>
+
+void Android_SkDebugf(const char* file, int line, const char* function,
+ const char* format, ...)
+{
+ if (format[0] == '\n' && format[1] == '\0')
+ return;
+ va_list args;
+ va_start(args, format);
+#ifdef HAVE_ANDROID_OS
+ char buffer[kBufferSize + 1];
+ vsnprintf(buffer, kBufferSize, format, args);
+ if (buffer[0] != 0)
+ __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "%s", buffer);
+#else
+ android_vprintLog(ANDROID_LOG_DEBUG, NULL, LOG_TAG, format, args);
+#endif
+ va_end(args);
+}
+
+#else
+
+#include <stdarg.h>
+#include <stdio.h>
+
+void SkDebugf(const char format[], ...)
+{
+ char buffer[kBufferSize + 1];
+ va_list args;
+ va_start(args, format);
+ vsnprintf(buffer, kBufferSize, format, args);
+ va_end(args);
+ fprintf(stderr, buffer);
+}
+
+#endif
+
diff --git a/src/core/SkDeque.cpp b/src/core/SkDeque.cpp
new file mode 100644
index 0000000000..4f15051012
--- /dev/null
+++ b/src/core/SkDeque.cpp
@@ -0,0 +1,252 @@
+/* libs/graphics/sgl/SkDeque.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkDeque.h"
+
+#define INIT_ELEM_COUNT 1 // should we let the caller set this?
+
+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 = NULL;
+ fBegin = fEnd = NULL;
+ fStop = (char*)this + size;
+ }
+};
+
+SkDeque::SkDeque(size_t elemSize)
+ : fElemSize(elemSize), fInitialStorage(NULL), fCount(0) {
+ fFront = fBack = NULL;
+}
+
+SkDeque::SkDeque(size_t elemSize, void* storage, size_t storageSize)
+ : fElemSize(elemSize), fInitialStorage(storage), fCount(0) {
+ SkASSERT(storageSize == 0 || storage != NULL);
+
+ if (storageSize >= sizeof(Head) + elemSize) {
+ fFront = (Head*)storage;
+ fFront->init(storageSize);
+ } else {
+ fFront = NULL;
+ }
+ 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 (NULL == front) {
+ return NULL;
+ }
+ if (NULL == front->fBegin) {
+ front = front->fNext;
+ if (NULL == front) {
+ return NULL;
+ }
+ }
+ SkASSERT(front->fBegin);
+ return front->fBegin;
+}
+
+const void* SkDeque::back() const {
+ Head* back = fBack;
+
+ if (NULL == back) {
+ return NULL;
+ }
+ if (NULL == back->fEnd) { // marked as deleted
+ back = back->fPrev;
+ if (NULL == back) {
+ return NULL;
+ }
+ }
+ SkASSERT(back->fEnd);
+ return back->fEnd - fElemSize;
+}
+
+void* SkDeque::push_front() {
+ fCount += 1;
+
+ if (NULL == fFront) {
+ 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 (NULL == first->fBegin) {
+ 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 (NULL == fBack) {
+ 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 (NULL == last->fBegin) {
+ 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 != NULL);
+
+ if (first->fBegin == NULL) { // we were marked empty from before
+ first = first->fNext;
+ first->fPrev = NULL;
+ sk_free(fFront);
+ fFront = first;
+ SkASSERT(first != NULL); // 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 = NULL; // mark as empty
+ }
+}
+
+void SkDeque::pop_back() {
+ SkASSERT(fCount > 0);
+ fCount -= 1;
+
+ Head* last = fBack;
+
+ SkASSERT(last != NULL);
+
+ if (last->fEnd == NULL) { // we were marked empty from before
+ last = last->fPrev;
+ last->fNext = NULL;
+ sk_free(fBack);
+ fBack = last;
+ SkASSERT(last != NULL); // 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 = NULL; // mark as empty
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkDeque::Iter::Iter(const SkDeque& d) : fElemSize(d.fElemSize) {
+ fHead = d.fFront;
+ while (fHead != NULL && fHead->fBegin == NULL) {
+ fHead = fHead->fNext;
+ }
+ fPos = fHead ? fHead->fBegin : NULL;
+}
+
+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 != NULL && fHead->fBegin == NULL);
+ next = fHead ? fHead->fBegin : NULL;
+ }
+ fPos = next;
+ }
+ return pos;
+}
+
diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp
new file mode 100644
index 0000000000..139174da30
--- /dev/null
+++ b/src/core/SkDevice.cpp
@@ -0,0 +1,110 @@
+#include "SkDevice.h"
+#include "SkDraw.h"
+#include "SkRect.h"
+
+SkDevice::SkDevice() {}
+
+SkDevice::SkDevice(const SkBitmap& bitmap) : fBitmap(bitmap) {}
+
+void SkDevice::lockPixels() {
+ fBitmap.lockPixels();
+}
+
+void SkDevice::unlockPixels() {
+ fBitmap.unlockPixels();
+}
+
+const SkBitmap& SkDevice::accessBitmap(bool changePixels) {
+ this->onAccessBitmap(&fBitmap);
+ if (changePixels) {
+ fBitmap.notifyPixelsChanged();
+ }
+ return fBitmap;
+}
+
+void SkDevice::getBounds(SkIRect* bounds) const {
+ if (bounds) {
+ bounds->set(0, 0, fBitmap.width(), fBitmap.height());
+ }
+}
+
+bool SkDevice::intersects(const SkIRect& r, SkIRect* sect) const {
+ SkIRect bounds;
+
+ this->getBounds(&bounds);
+ return sect ? sect->intersect(r, bounds) : SkIRect::Intersects(r, bounds);
+}
+
+void SkDevice::eraseColor(SkColor eraseColor) {
+ fBitmap.eraseColor(eraseColor);
+}
+
+void SkDevice::onAccessBitmap(SkBitmap* bitmap) {}
+
+void SkDevice::setMatrixClip(const SkMatrix&, const SkRegion&) {}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) {
+ draw.drawPaint(paint);
+}
+
+void SkDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, size_t count,
+ const SkPoint pts[], const SkPaint& paint) {
+ draw.drawPoints(mode, count, pts, paint);
+}
+
+void SkDevice::drawRect(const SkDraw& draw, const SkRect& r,
+ const SkPaint& paint) {
+ draw.drawRect(r, paint);
+}
+
+void SkDevice::drawPath(const SkDraw& draw, const SkPath& path,
+ const SkPaint& paint) {
+ draw.drawPath(path, paint);
+}
+
+void SkDevice::drawBitmap(const SkDraw& draw, const SkBitmap& bitmap,
+ const SkMatrix& matrix, const SkPaint& paint) {
+ draw.drawBitmap(bitmap, matrix, paint);
+}
+
+void SkDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
+ int x, int y, const SkPaint& paint) {
+ draw.drawSprite(bitmap, x, y, paint);
+}
+
+void SkDevice::drawText(const SkDraw& draw, const void* text, size_t len,
+ SkScalar x, SkScalar y, const SkPaint& paint) {
+ draw.drawText((const char*)text, len, x, y, paint);
+}
+
+void SkDevice::drawPosText(const SkDraw& draw, const void* text, size_t len,
+ const SkScalar xpos[], SkScalar y,
+ int scalarsPerPos, const SkPaint& paint) {
+ draw.drawPosText((const char*)text, len, xpos, y, scalarsPerPos, paint);
+}
+
+void SkDevice::drawTextOnPath(const SkDraw& draw, const void* text,
+ size_t len, const SkPath& path,
+ const SkMatrix* matrix,
+ const SkPaint& paint) {
+ draw.drawTextOnPath((const char*)text, len, path, matrix, paint);
+}
+
+void SkDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode,
+ int vertexCount,
+ const SkPoint verts[], const SkPoint textures[],
+ const SkColor colors[], SkXfermode* xmode,
+ const uint16_t indices[], int indexCount,
+ const SkPaint& paint) {
+ draw.drawVertices(vmode, vertexCount, verts, textures, colors, xmode,
+ indices, indexCount, paint);
+}
+
+void SkDevice::drawDevice(const SkDraw& draw, SkDevice* device,
+ int x, int y, const SkPaint& paint) {
+ draw.drawSprite(device->accessBitmap(false), x, y, paint);
+}
+
+
diff --git a/src/core/SkDither.cpp b/src/core/SkDither.cpp
new file mode 100644
index 0000000000..53a8573b21
--- /dev/null
+++ b/src/core/SkDither.cpp
@@ -0,0 +1,49 @@
+#include "SkDither.h"
+
+/* The base dither matrix we use to derive optimized ones for 565 and 4444
+
+ { 0, 32, 8, 40, 2, 34, 10, 42 },
+ { 48, 16, 56, 24, 50, 18, 58, 26 },
+ { 12, 44, 4, 36, 14, 46, 6, 38 },
+ { 60, 28, 52, 20, 62, 30, 54, 22 },
+ { 3, 35, 11, 43, 1, 33, 9, 41 },
+ { 51, 19, 59, 27, 49, 17, 57, 25 },
+ { 15, 47, 7, 39, 13, 45, 5, 37 },
+ { 63, 31, 55, 23, 61, 29, 53, 21 }
+
+ The 4444 version only needs 4 bits, and given that we can reduce its size
+ since the other 4x4 sub pieces all look the same once we truncate the bits.
+
+ The 565 version only needs 3 bits for red/blue, and only 2 bits for green.
+ For simplicity, we store 3 bits, and have the dither macros for green know
+ this, and they shift the dither value down by 1 to make it 2 bits.
+ */
+
+#ifdef ENABLE_DITHER_MATRIX_4X4
+
+const uint8_t gDitherMatrix_4Bit_4X4[4][4] = {
+ { 0, 8, 2, 10 },
+ { 12, 4, 14, 6 },
+ { 3, 11, 1, 9 },
+ { 15, 7, 13, 5 }
+};
+
+const uint8_t gDitherMatrix_3Bit_4X4[4][4] = {
+ { 0, 4, 1, 5 },
+ { 6, 2, 7, 3 },
+ { 1, 5, 0, 4 },
+ { 7, 3, 6, 2 }
+};
+
+#else // used packed shorts for a scanlines worth of dither values
+
+const uint16_t gDitherMatrix_4Bit_16[4] = {
+ 0xA280, 0x6E4C, 0x91B3, 0x5D7F
+};
+
+const uint16_t gDitherMatrix_3Bit_16[4] = {
+ 0x5140, 0x3726, 0x4051, 0x2637
+};
+
+#endif
+
diff --git a/src/core/SkDraw.cpp b/src/core/SkDraw.cpp
new file mode 100644
index 0000000000..f85c5ae085
--- /dev/null
+++ b/src/core/SkDraw.cpp
@@ -0,0 +1,2348 @@
+/* libs/graphics/sgl/SkDraw.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkDraw.h"
+#include "SkBlitter.h"
+#include "SkBounder.h"
+#include "SkCanvas.h"
+#include "SkColorPriv.h"
+#include "SkDevice.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 "SkUtils.h"
+
+#include "SkAutoKern.h"
+#include "SkBitmapProcShader.h"
+#include "SkDrawProcs.h"
+
+//#define TRACE_BITMAP_DRAWS
+
+class SkAutoRestoreBounder : SkNoncopyable {
+public:
+ // note: initializing fBounder is done only to fix a warning
+ SkAutoRestoreBounder() : fDraw(NULL), fBounder(NULL) {}
+ ~SkAutoRestoreBounder() {
+ if (fDraw) {
+ fDraw->fBounder = fBounder;
+ }
+ }
+
+ void clearBounder(const SkDraw* draw) {
+ fDraw = const_cast<SkDraw*>(draw);
+ fBounder = draw->fBounder;
+ fDraw->fBounder = NULL;
+ }
+
+private:
+ SkDraw* fDraw;
+ SkBounder* fBounder;
+};
+
+static SkPoint* rect_points(SkRect& r, int index) {
+ SkASSERT((unsigned)index < 2);
+ return &((SkPoint*)(void*)&r)[index];
+}
+
+/** Helper for allocating small blitters on the stack.
+*/
+
+#define kBlitterStorageLongCount (sizeof(SkBitmapProcShader) >> 2)
+
+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,
+ 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 SkDraw& src) {
+ memcpy(this, &src, sizeof(*this));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+typedef void (*BitmapXferProc)(void* pixels, size_t bytes, uint32_t data);
+
+static void D_Clear_BitmapXferProc(void* pixels, size_t bytes, uint32_t) {
+ bzero(pixels, bytes);
+}
+
+static void D_Dst_BitmapXferProc(void*, size_t, uint32_t data) {}
+
+static void D32_Src_BitmapXferProc(void* pixels, size_t bytes, uint32_t data) {
+ sk_memset32((uint32_t*)pixels, data, bytes >> 2);
+}
+
+static void D16_Src_BitmapXferProc(void* pixels, size_t bytes, uint32_t data) {
+ sk_memset16((uint16_t*)pixels, data, bytes >> 1);
+}
+
+static void DA8_Src_BitmapXferProc(void* pixels, size_t bytes, uint32_t data) {
+ memset(pixels, data, bytes);
+}
+
+static BitmapXferProc ChooseBitmapXferProc(const SkBitmap& bitmap,
+ const SkPaint& paint,
+ uint32_t* data) {
+ // todo: we can apply colorfilter up front if no shader, so we wouldn't
+ // need to abort this fastpath
+ if (paint.getShader() || paint.getColorFilter()) {
+ return NULL;
+ }
+
+ SkPorterDuff::Mode mode;
+ if (!SkPorterDuff::IsMode(paint.getXfermode(), &mode)) {
+ return NULL;
+ }
+
+ SkColor color = paint.getColor();
+
+ // collaps modes based on color...
+ if (SkPorterDuff::kSrcOver_Mode == mode) {
+ unsigned alpha = SkColorGetA(color);
+ if (0 == alpha) {
+ mode = SkPorterDuff::kDst_Mode;
+ } else if (0xFF == alpha) {
+ mode = SkPorterDuff::kSrc_Mode;
+ }
+ }
+
+ switch (mode) {
+ case SkPorterDuff::kClear_Mode:
+// SkDebugf("--- D_Clear_BitmapXferProc\n");
+ return D_Clear_BitmapXferProc; // ignore data
+ case SkPorterDuff::kDst_Mode:
+// SkDebugf("--- D_Dst_BitmapXferProc\n");
+ return D_Dst_BitmapXferProc; // ignore data
+ case SkPorterDuff::kSrc_Mode: {
+ /*
+ should I worry about dithering for the lower depths?
+ */
+ SkPMColor pmc = SkPreMultiplyColor(color);
+ switch (bitmap.config()) {
+ case SkBitmap::kARGB_8888_Config:
+ if (data) {
+ *data = pmc;
+ }
+// SkDebugf("--- D32_Src_BitmapXferProc\n");
+ return D32_Src_BitmapXferProc;
+ case SkBitmap::kARGB_4444_Config:
+ if (data) {
+ *data = SkPixel32ToPixel4444(pmc);
+ }
+// SkDebugf("--- D16_Src_BitmapXferProc\n");
+ return D16_Src_BitmapXferProc;
+ case SkBitmap::kRGB_565_Config:
+ if (data) {
+ *data = SkPixel32ToPixel16(pmc);
+ }
+// SkDebugf("--- D16_Src_BitmapXferProc\n");
+ return D16_Src_BitmapXferProc;
+ case SkBitmap::kA8_Config:
+ if (data) {
+ *data = SkGetPackedA32(pmc);
+ }
+// SkDebugf("--- DA8_Src_BitmapXferProc\n");
+ return DA8_Src_BitmapXferProc;
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return NULL;
+}
+
+static void CallBitmapXferProc(const SkBitmap& bitmap, const SkIRect& rect,
+ BitmapXferProc proc, uint32_t procData) {
+ int shiftPerPixel;
+ switch (bitmap.config()) {
+ case SkBitmap::kARGB_8888_Config:
+ shiftPerPixel = 2;
+ break;
+ case SkBitmap::kARGB_4444_Config:
+ case SkBitmap::kRGB_565_Config:
+ shiftPerPixel = 1;
+ break;
+ case SkBitmap::kA8_Config:
+ shiftPerPixel = 0;
+ break;
+ default:
+ SkASSERT(!"Can't use xferproc on this config");
+ return;
+ }
+
+ uint8_t* pixels = (uint8_t*)bitmap.getPixels();
+ SkASSERT(pixels);
+ const size_t rowBytes = bitmap.rowBytes();
+ const int widthBytes = rect.width() << shiftPerPixel;
+
+ // skip down to the first scanline and X position
+ pixels += rect.fTop * rowBytes + (rect.fLeft << shiftPerPixel);
+ for (int scans = rect.height() - 1; scans >= 0; --scans) {
+ proc(pixels, widthBytes, procData);
+ pixels += rowBytes;
+ }
+}
+
+void SkDraw::drawPaint(const SkPaint& paint) const {
+ SkDEBUGCODE(this->validate();)
+
+ if (fClip->isEmpty()) {
+ return;
+ }
+
+ SkIRect devRect;
+ devRect.set(0, 0, fBitmap->width(), fBitmap->height());
+ if (fBounder && !fBounder->doIRect(devRect)) {
+ return;
+ }
+
+ /* If we don't have a shader (i.e. we're just a solid color) we may
+ be faster to operate directly on the device bitmap, rather than invoking
+ a blitter. Esp. true for xfermodes, which require a colorshader to be
+ present, which is just redundant work. Since we're drawing everywhere
+ in the clip, we don't have to worry about antialiasing.
+ */
+ uint32_t procData = 0; // to avoid the warning
+ BitmapXferProc proc = ChooseBitmapXferProc(*fBitmap, paint, &procData);
+ if (proc) {
+ if (D_Dst_BitmapXferProc == proc) { // nothing to do
+ return;
+ }
+
+ SkRegion::Iterator iter(*fClip);
+ while (!iter.done()) {
+ CallBitmapXferProc(*fBitmap, iter.rect(), proc, procData);
+ iter.next();
+ }
+ } else {
+ // normal case: use a blitter
+ SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, paint);
+ SkScan::FillIRect(devRect, fClip, blitter.get());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct PtProcRec {
+ SkCanvas::PointMode fMode;
+ const SkPaint* fPaint;
+ const SkRegion* fClip;
+
+ // computed values
+ SkFixed fRadius;
+
+ typedef void (*Proc)(const PtProcRec&, const SkPoint devPts[], int count,
+ SkBlitter*);
+
+ bool init(SkCanvas::PointMode, const SkPaint&, const SkMatrix* matrix,
+ const SkRegion* clip);
+ Proc chooseProc(SkBlitter* blitter);
+};
+
+static void bw_pt_rect_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
+ int count, SkBlitter* blitter) {
+ SkASSERT(rec.fClip->isRect());
+ const SkIRect& r = rec.fClip->getBounds();
+
+ for (int i = 0; i < count; i++) {
+ int x = SkScalarFloor(devPts[i].fX);
+ int y = SkScalarFloor(devPts[i].fY);
+ if (r.contains(x, y)) {
+ blitter->blitH(x, y, 1);
+ }
+ }
+}
+
+static void bw_pt_rect_16_hair_proc(const PtProcRec& rec,
+ const SkPoint devPts[], int count,
+ SkBlitter* blitter) {
+ SkASSERT(rec.fClip->isRect());
+ const SkIRect& r = rec.fClip->getBounds();
+ uint32_t value;
+ const SkBitmap* bitmap = blitter->justAnOpaqueColor(&value);
+ SkASSERT(bitmap);
+
+ uint16_t* addr = bitmap->getAddr16(0, 0);
+ int rb = bitmap->rowBytes();
+
+ for (int i = 0; i < count; i++) {
+ int x = SkScalarFloor(devPts[i].fX);
+ int y = SkScalarFloor(devPts[i].fY);
+ if (r.contains(x, y)) {
+// *bitmap->getAddr16(x, y) = SkToU16(value);
+ ((uint16_t*)((char*)addr + y * rb))[x] = SkToU16(value);
+ }
+ }
+}
+
+static void bw_pt_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
+ int count, SkBlitter* blitter) {
+ for (int i = 0; i < count; i++) {
+ int x = SkScalarFloor(devPts[i].fX);
+ int y = SkScalarFloor(devPts[i].fY);
+ if (rec.fClip->contains(x, y)) {
+ blitter->blitH(x, y, 1);
+ }
+ }
+}
+
+static void bw_line_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
+ int count, SkBlitter* blitter) {
+ for (int i = 0; i < count; i += 2) {
+ SkScan::HairLine(devPts[i], devPts[i+1], rec.fClip, blitter);
+ }
+}
+
+static void bw_poly_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
+ int count, SkBlitter* blitter) {
+ for (int i = 0; i < count - 1; i++) {
+ SkScan::HairLine(devPts[i], devPts[i+1], rec.fClip, blitter);
+ }
+}
+
+// aa versions
+
+static void aa_line_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
+ int count, SkBlitter* blitter) {
+ for (int i = 0; i < count; i += 2) {
+ SkScan::AntiHairLine(devPts[i], devPts[i+1], rec.fClip, blitter);
+ }
+}
+
+static void aa_poly_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
+ int count, SkBlitter* blitter) {
+ for (int i = 0; i < count - 1; i++) {
+ SkScan::AntiHairLine(devPts[i], devPts[i+1], rec.fClip, blitter);
+ }
+}
+
+// square procs (strokeWidth > 0 but matrix is square-scale (sx == sy)
+
+static void bw_square_proc(const PtProcRec& rec, const SkPoint devPts[],
+ int count, SkBlitter* blitter) {
+ const SkFixed radius = rec.fRadius;
+ for (int i = 0; i < count; i++) {
+ SkFixed x = SkScalarToFixed(devPts[i].fX);
+ SkFixed y = SkScalarToFixed(devPts[i].fY);
+
+ SkXRect r;
+ r.fLeft = x - radius;
+ r.fTop = y - radius;
+ r.fRight = x + radius;
+ r.fBottom = y + radius;
+
+ SkScan::FillXRect(r, rec.fClip, blitter);
+ }
+}
+
+static void aa_square_proc(const PtProcRec& rec, const SkPoint devPts[],
+ int count, SkBlitter* blitter) {
+ const SkFixed radius = rec.fRadius;
+ for (int i = 0; i < count; i++) {
+ SkFixed x = SkScalarToFixed(devPts[i].fX);
+ SkFixed y = SkScalarToFixed(devPts[i].fY);
+
+ SkXRect r;
+ r.fLeft = x - radius;
+ r.fTop = y - radius;
+ r.fRight = x + radius;
+ r.fBottom = y + radius;
+
+ SkScan::AntiFillXRect(r, rec.fClip, blitter);
+ }
+}
+
+bool PtProcRec::init(SkCanvas::PointMode mode, const SkPaint& paint,
+ const SkMatrix* matrix, const SkRegion* clip) {
+ if (paint.getPathEffect()) {
+ return false;
+ }
+ SkScalar width = paint.getStrokeWidth();
+ if (0 == width) {
+ fMode = mode;
+ fPaint = &paint;
+ fClip = clip;
+ fRadius = SK_Fixed1 >> 1;
+ return true;
+ }
+ if (matrix->rectStaysRect() && SkCanvas::kPoints_PointMode == mode) {
+ SkScalar sx = matrix->get(SkMatrix::kMScaleX);
+ SkScalar sy = matrix->get(SkMatrix::kMScaleY);
+ if (SkScalarNearlyZero(sx - sy)) {
+ if (sx < 0) {
+ sx = -sx;
+ }
+
+ fMode = mode;
+ fPaint = &paint;
+ fClip = clip;
+ fRadius = SkScalarToFixed(SkScalarMul(width, sx)) >> 1;
+ return true;
+ }
+ }
+ return false;
+}
+
+PtProcRec::Proc PtProcRec::chooseProc(SkBlitter* blitter) {
+ Proc proc;
+
+ // for our arrays
+ SkASSERT(0 == SkCanvas::kPoints_PointMode);
+ SkASSERT(1 == SkCanvas::kLines_PointMode);
+ SkASSERT(2 == SkCanvas::kPolygon_PointMode);
+ SkASSERT((unsigned)fMode <= (unsigned)SkCanvas::kPolygon_PointMode);
+
+ // first check for hairlines
+ if (0 == fPaint->getStrokeWidth()) {
+ if (fPaint->isAntiAlias()) {
+ static const Proc gAAProcs[] = {
+ aa_square_proc, aa_line_hair_proc, aa_poly_hair_proc
+ };
+ proc = gAAProcs[fMode];
+ } else {
+ if (SkCanvas::kPoints_PointMode == fMode && fClip->isRect()) {
+ uint32_t value;
+ const SkBitmap* bm = blitter->justAnOpaqueColor(&value);
+ if (bm && bm->config() == SkBitmap::kRGB_565_Config) {
+ proc = bw_pt_rect_16_hair_proc;
+ } else {
+ proc = bw_pt_rect_hair_proc;
+ }
+ } else {
+ static Proc gBWProcs[] = {
+ bw_pt_hair_proc, bw_line_hair_proc, bw_poly_hair_proc
+ };
+ proc = gBWProcs[fMode];
+ }
+ }
+ } else {
+ SkASSERT(SkCanvas::kPoints_PointMode == fMode);
+ if (fPaint->isAntiAlias()) {
+ proc = aa_square_proc;
+ } else {
+ proc = bw_square_proc;
+ }
+ }
+ return proc;
+}
+
+static bool bounder_points(SkBounder* bounder, SkCanvas::PointMode mode,
+ size_t count, const SkPoint pts[],
+ const SkPaint& paint, const SkMatrix& matrix) {
+ SkIRect ibounds;
+ SkRect bounds;
+ SkScalar inset = paint.getStrokeWidth();
+
+ bounds.set(pts, count);
+ bounds.inset(-inset, -inset);
+ matrix.mapRect(&bounds);
+
+ bounds.roundOut(&ibounds);
+ return bounder->doIRect(ibounds);
+}
+
+// each of these costs 8-bytes of stack space, so don't make it too large
+// must be even for lines/polygon to work
+#define MAX_DEV_PTS 32
+
+void SkDraw::drawPoints(SkCanvas::PointMode mode, size_t count,
+ const SkPoint pts[], const SkPaint& paint) const {
+ // if we're in lines mode, force count to be even
+ if (SkCanvas::kLines_PointMode == mode) {
+ count &= ~(size_t)1;
+ }
+
+ if ((long)count <= 0) {
+ return;
+ }
+
+ SkAutoRestoreBounder arb;
+
+ if (fBounder) {
+ if (!bounder_points(fBounder, mode, count, pts, paint, *fMatrix)) {
+ return;
+ }
+ // clear the bounder for the rest of this function, so we don't call it
+ // again later if we happen to call ourselves for drawRect, drawPath,
+ // etc.
+ arb.clearBounder(this);
+ }
+
+ SkASSERT(pts != NULL);
+ SkDEBUGCODE(this->validate();)
+
+ // nothing to draw
+ if (fClip->isEmpty() ||
+ (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
+ return;
+ }
+
+ PtProcRec rec;
+ if (rec.init(mode, paint, fMatrix, fClip)) {
+ SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, paint);
+
+ SkPoint devPts[MAX_DEV_PTS];
+ const SkMatrix* matrix = fMatrix;
+ SkBlitter* bltr = blitter.get();
+ PtProcRec::Proc proc = rec.chooseProc(bltr);
+ // we have to back up subsequent passes if we're in polygon mode
+ const size_t backup = (SkCanvas::kPolygon_PointMode == mode);
+
+ do {
+ size_t n = count;
+ if (n > MAX_DEV_PTS) {
+ n = MAX_DEV_PTS;
+ }
+ matrix->mapPoints(devPts, pts, n);
+ proc(rec, devPts, n, bltr);
+ pts += n - backup;
+ SkASSERT(count >= n);
+ count -= n;
+ if (count > 0) {
+ count += backup;
+ }
+ } while (count != 0);
+ } else {
+ switch (mode) {
+ case SkCanvas::kPoints_PointMode: {
+ // temporarily mark the paint as filling.
+ SkAutoPaintStyleRestore restore(paint, SkPaint::kFill_Style);
+
+ SkScalar width = paint.getStrokeWidth();
+ SkScalar radius = SkScalarHalf(width);
+
+ if (paint.getStrokeCap() == SkPaint::kRound_Cap) {
+ SkPath path;
+ SkMatrix preMatrix;
+
+ path.addCircle(0, 0, radius);
+ for (size_t i = 0; i < count; i++) {
+ preMatrix.setTranslate(pts[i].fX, pts[i].fY);
+ // pass true for the last point, since we can modify
+ // then path then
+ this->drawPath(path, paint, &preMatrix, (count-1) == i);
+ }
+ } else {
+ SkRect r;
+
+ for (size_t i = 0; i < count; i++) {
+ r.fLeft = pts[i].fX - radius;
+ r.fTop = pts[i].fY - radius;
+ r.fRight = r.fLeft + width;
+ r.fBottom = r.fTop + width;
+ this->drawRect(r, paint);
+ }
+ }
+ break;
+ }
+ case SkCanvas::kLines_PointMode:
+ case SkCanvas::kPolygon_PointMode: {
+ count -= 1;
+ SkPath path;
+ SkPaint p(paint);
+ p.setStyle(SkPaint::kStroke_Style);
+ size_t inc = (SkCanvas::kLines_PointMode == mode) ? 2 : 1;
+ for (size_t i = 0; i < count; i += inc) {
+ path.moveTo(pts[i]);
+ path.lineTo(pts[i+1]);
+ this->drawPath(path, p, NULL, true);
+ path.rewind();
+ }
+ break;
+ }
+ }
+ }
+}
+
+static inline SkPoint* as_lefttop(SkRect* r) {
+ return (SkPoint*)(void*)r;
+}
+
+static inline SkPoint* as_rightbottom(SkRect* r) {
+ return ((SkPoint*)(void*)r) + 1;
+}
+
+void SkDraw::drawRect(const SkRect& rect, const SkPaint& paint) const {
+ SkDEBUGCODE(this->validate();)
+
+ // nothing to draw
+ if (fClip->isEmpty() ||
+ (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
+ return;
+ }
+
+ // complex enough to draw as a path
+ if (paint.getPathEffect() || paint.getMaskFilter() ||
+ paint.getRasterizer() || !fMatrix->rectStaysRect() ||
+ (paint.getStyle() != SkPaint::kFill_Style &&
+ SkScalarHalf(paint.getStrokeWidth()) > 0)) {
+ SkPath tmp;
+ tmp.addRect(rect);
+ tmp.setFillType(SkPath::kWinding_FillType);
+ this->drawPath(tmp, paint);
+ return;
+ }
+
+ const SkMatrix& matrix = *fMatrix;
+ SkRect devRect;
+
+ // transform rect into devRect
+ {
+ matrix.mapXY(rect.fLeft, rect.fTop, rect_points(devRect, 0));
+ matrix.mapXY(rect.fRight, rect.fBottom, rect_points(devRect, 1));
+ devRect.sort();
+ }
+
+ if (fBounder && !fBounder->doRect(devRect, paint)) {
+ return;
+ }
+
+ // look for the quick exit, before we build a blitter
+ {
+ SkIRect ir;
+ devRect.roundOut(&ir);
+ if (fClip->quickReject(ir))
+ return;
+ }
+
+ SkAutoBlitterChoose blitterStorage(*fBitmap, matrix, paint);
+ SkBlitter* blitter = blitterStorage.get();
+ const SkRegion* clip = fClip;
+
+ if (paint.getStyle() == SkPaint::kFill_Style) {
+ if (paint.isAntiAlias()) {
+ SkScan::AntiFillRect(devRect, clip, blitter);
+ } else {
+ SkScan::FillRect(devRect, clip, blitter);
+ }
+ } else {
+ if (paint.isAntiAlias()) {
+ SkScan::AntiHairRect(devRect, clip, blitter);
+ } else {
+ SkScan::HairRect(devRect, clip, blitter);
+ }
+ }
+}
+
+void SkDraw::drawDevMask(const SkMask& srcM, const SkPaint& paint) const {
+ 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)) {
+ return;
+ }
+
+ SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, paint);
+
+ blitter->blitMaskRegion(*mask, *fClip);
+}
+
+class SkAutoPaintRestoreColorStrokeWidth {
+public:
+ SkAutoPaintRestoreColorStrokeWidth(const SkPaint& paint) {
+ fPaint = (SkPaint*)&paint;
+ fColor = paint.getColor();
+ fWidth = paint.getStrokeWidth();
+ }
+ ~SkAutoPaintRestoreColorStrokeWidth() {
+ fPaint->setColor(fColor);
+ fPaint->setStrokeWidth(fWidth);
+ }
+
+private:
+ SkPaint* fPaint;
+ SkColor fColor;
+ SkScalar fWidth;
+};
+
+void SkDraw::drawPath(const SkPath& origSrcPath, const SkPaint& paint,
+ const SkMatrix* prePathMatrix, bool pathIsMutable) const {
+ SkDEBUGCODE(this->validate();)
+
+ // nothing to draw
+ if (fClip->isEmpty() ||
+ (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
+ 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 (!pathIsMutable) {
+ result = &tmpPath;
+ pathIsMutable = true;
+ }
+ pathPtr->transform(*prePathMatrix, result);
+ pathPtr = result;
+ } else {
+ if (!tmpMatrix.setConcat(*matrix, *prePathMatrix)) {
+ // overflow
+ return;
+ }
+ matrix = &tmpMatrix;
+ }
+ }
+ // at this point we're done with prePathMatrix
+ SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;)
+
+ /*
+ If the device thickness < 1.0, then make it a hairline, and
+ modulate alpha if the thickness is even smaller (e.g. thickness == 0.5
+ should modulate the alpha by 1/2)
+ */
+
+ SkAutoPaintRestoreColorStrokeWidth aprc(paint);
+
+ if (paint.getStyle() == SkPaint::kStroke_Style &&
+ paint.getXfermode() == NULL &&
+ (matrix->getType() & SkMatrix::kPerspective_Mask) == 0) {
+ SkScalar width = paint.getStrokeWidth();
+ if (width > 0) {
+ width = matrix->mapRadius(paint.getStrokeWidth());
+ if (width < SK_Scalar1) {
+ int scale = (int)SkScalarMul(width, 256);
+ int alpha = paint.getAlpha() * scale >> 8;
+
+ // pretend to be a hairline, with a modulated alpha
+ ((SkPaint*)&paint)->setAlpha(alpha);
+ ((SkPaint*)&paint)->setStrokeWidth(0);
+
+// SkDebugf("------ convert to hairline %d\n", scale);
+ }
+ }
+ }
+
+ 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 = pathIsMutable ? pathPtr : &tmpPath;
+
+ // transform the path into device space
+ pathPtr->transform(*matrix, devPathPtr);
+
+ SkAutoBlitterChoose blitter(*fBitmap, *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, doFill)) {
+ return;
+ }
+
+ if (doFill) {
+ if (paint.isAntiAlias()) {
+ SkScan::AntiFillPath(*devPathPtr, *fClip, blitter.get());
+ } else {
+ SkScan::FillPath(*devPathPtr, *fClip, blitter.get());
+ }
+ } else { // hairline
+ if (paint.isAntiAlias()) {
+ 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::drawBitmapAsMask(const SkBitmap& bitmap,
+ const SkPaint& paint) const {
+ SkASSERT(bitmap.getConfig() == SkBitmap::kA8_Config);
+
+ if (just_translate(*fMatrix)) {
+ int ix = SkScalarRound(fMatrix->getTranslateX());
+ int iy = SkScalarRound(fMatrix->getTranslateY());
+
+ SkMask mask;
+ mask.fBounds.set(ix, iy, ix + bitmap.width(), iy + bitmap.height());
+ mask.fFormat = SkMask::kA8_Format;
+ mask.fRowBytes = bitmap.rowBytes();
+ mask.fImage = bitmap.getAddr8(0, 0);
+
+ this->drawDevMask(mask, paint);
+ } else { // need to xform the bitmap first
+ SkRect r;
+ SkMask mask;
+
+ r.set(0, 0,
+ SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height()));
+ fMatrix->mapRect(&r);
+ r.round(&mask.fBounds);
+
+ // set the mask's bounds to the transformed bitmap-bounds,
+ // clipped to the actual device
+ {
+ SkIRect devBounds;
+ devBounds.set(0, 0, fBitmap->width(), fBitmap->height());
+ // need intersect(l, t, r, b) on irect
+ if (!mask.fBounds.intersect(devBounds)) {
+ return;
+ }
+ }
+ mask.fFormat = SkMask::kA8_Format;
+ mask.fRowBytes = SkAlign4(mask.fBounds.width());
+
+ // allocate (and clear) our temp buffer to hold the transformed bitmap
+ size_t size = mask.computeImageSize();
+ SkAutoMalloc storage(size);
+ mask.fImage = (uint8_t*)storage.get();
+ memset(mask.fImage, 0, size);
+
+ // now draw our bitmap(src) into mask(dst), transformed by the matrix
+ {
+ SkBitmap device;
+ device.setConfig(SkBitmap::kA8_Config, mask.fBounds.width(),
+ mask.fBounds.height(), mask.fRowBytes);
+ device.setPixels(mask.fImage);
+
+ SkCanvas c(device);
+ // need the unclipped top/left for the translate
+ c.translate(-SkIntToScalar(mask.fBounds.fLeft),
+ -SkIntToScalar(mask.fBounds.fTop));
+ c.concat(*fMatrix);
+ c.drawBitmap(bitmap, 0, 0, NULL);
+ }
+ this->drawDevMask(mask, paint);
+ }
+}
+
+static bool clipped_out(const SkMatrix& m, const SkRegion& c,
+ const SkRect& srcR) {
+ SkRect dstR;
+ SkIRect devIR;
+
+ m.mapRect(&dstR, srcR);
+ dstR.roundOut(&devIR);
+ return c.quickReject(devIR);
+}
+
+static bool clipped_out(const SkMatrix& matrix, const SkRegion& clip,
+ int width, int height) {
+ SkRect r;
+ r.set(0, 0, SkIntToScalar(width), SkIntToScalar(height));
+ return clipped_out(matrix, clip, r);
+}
+
+void SkDraw::drawBitmap(const SkBitmap& bitmap, const SkMatrix& prematrix,
+ const SkPaint& paint) const {
+ SkDEBUGCODE(this->validate();)
+
+ // nothing to draw
+ if (fClip->isEmpty() ||
+ bitmap.width() == 0 || bitmap.height() == 0 ||
+ bitmap.getConfig() == SkBitmap::kNo_Config ||
+ (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
+ return;
+ }
+
+ // run away on too-big bitmaps for now (exceed 16.16)
+ if (bitmap.width() > 32767 || bitmap.height() > 32767) {
+ return;
+ }
+
+ SkAutoPaintStyleRestore restore(paint, SkPaint::kFill_Style);
+
+ SkMatrix matrix;
+ if (!matrix.setConcat(*fMatrix, prematrix)) {
+ return;
+ }
+
+ // do I need to call the bounder first???
+ if (clipped_out(matrix, *fClip, bitmap.width(), bitmap.height())) {
+ return;
+ }
+
+ // only lock the pixels if we passed the clip test
+ SkAutoLockPixels alp(bitmap);
+ // after the lock, check if we are valid
+ if (!bitmap.readyToDraw()) {
+ return;
+ }
+
+ if (bitmap.getConfig() != SkBitmap::kA8_Config && just_translate(matrix)) {
+ int ix = SkScalarRound(matrix.getTranslateX());
+ int iy = SkScalarRound(matrix.getTranslateY());
+ uint32_t storage[kBlitterStorageLongCount];
+ SkBlitter* blitter = SkBlitter::ChooseSprite(*fBitmap, paint, bitmap,
+ ix, iy, storage, sizeof(storage));
+ if (blitter) {
+ SkAutoTPlacementDelete<SkBlitter> ad(blitter, storage);
+
+ SkIRect ir;
+ ir.set(ix, iy, ix + bitmap.width(), iy + bitmap.height());
+
+ if (fBounder && !fBounder->doIRect(ir)) {
+ return;
+ }
+
+ SkRegion::Cliperator iter(*fClip, ir);
+ const SkIRect& cr = iter.rect();
+
+ for (; !iter.done(); iter.next()) {
+ SkASSERT(!cr.isEmpty());
+ blitter->blitRect(cr.fLeft, cr.fTop, cr.width(), cr.height());
+ }
+ return;
+ }
+#if 0
+ SkDebugf("---- MISSING sprite case: config=%d [%d %d], device=%d, xfer=%p, alpha=0x%X colorFilter=%p\n",
+ bitmap.config(), bitmap.width(), bitmap.height(), fBitmap->config(),
+ paint.getXfermode(), paint.getAlpha(), paint.getColorFilter());
+#endif
+ }
+
+ // now make a temp draw on the stack, and use it
+ //
+ SkDraw draw(*this);
+ draw.fMatrix = &matrix;
+
+ if (bitmap.getConfig() == SkBitmap::kA8_Config) {
+ draw.drawBitmapAsMask(bitmap, paint);
+ } else {
+ SkAutoBitmapShaderInstall install(bitmap, &paint);
+
+ SkRect r;
+ r.set(0, 0, SkIntToScalar(bitmap.width()),
+ SkIntToScalar(bitmap.height()));
+ // is this ok if paint has a rasterizer?
+ draw.drawRect(r, paint);
+ }
+}
+
+void SkDraw::drawSprite(const SkBitmap& bitmap, int x, int y,
+ const SkPaint& paint) const {
+ SkDEBUGCODE(this->validate();)
+
+ // nothing to draw
+ if (fClip->isEmpty() ||
+ bitmap.width() == 0 || bitmap.height() == 0 ||
+ bitmap.getConfig() == SkBitmap::kNo_Config ||
+ (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
+ return;
+ }
+
+ SkIRect bounds;
+ bounds.set(x, y, x + bitmap.width(), y + bitmap.height());
+
+ if (fClip->quickReject(bounds)) {
+ return; // nothing to draw
+ }
+
+ SkAutoPaintStyleRestore restore(paint, SkPaint::kFill_Style);
+
+ if (NULL == paint.getColorFilter()) {
+ uint32_t storage[kBlitterStorageLongCount];
+ SkBlitter* blitter = SkBlitter::ChooseSprite(*fBitmap, paint, bitmap,
+ x, y, storage, sizeof(storage));
+
+ if (blitter) {
+ SkAutoTPlacementDelete<SkBlitter> ad(blitter, storage);
+
+ if (fBounder && !fBounder->doIRect(bounds)) {
+ return;
+ }
+
+ SkRegion::Cliperator iter(*fClip, bounds);
+ const SkIRect& 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);
+
+ 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);
+
+ SkDraw draw(*this);
+ matrix.reset();
+ draw.fMatrix = &matrix;
+ // call ourself with a rect
+ // is this OK if paint has a rasterizer?
+ draw.drawRect(r, paint);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkScalerContext.h"
+#include "SkGlyphCache.h"
+#include "SkUtils.h"
+
+static void measure_text(SkGlyphCache* cache, SkDrawCacheProc glyphCacheProc,
+ const char text[], size_t byteLength, SkVector* stopVector) {
+ SkFixed x = 0, y = 0;
+ const char* stop = text + byteLength;
+
+ SkAutoKern autokern;
+
+ while (text < stop) {
+ // don't need x, y here, since all subpixel variants will have the
+ // same advance
+ const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
+
+ x += autokern.adjust(glyph) + glyph.fAdvanceX;
+ y += glyph.fAdvanceY;
+ }
+ stopVector->set(SkFixedToScalar(x), SkFixedToScalar(y));
+
+ SkASSERT(text == stop);
+}
+
+void SkDraw::drawText_asPaths(const char text[], size_t byteLength,
+ SkScalar x, SkScalar y,
+ const SkPaint& paint) const {
+ SkDEBUGCODE(this->validate();)
+
+ SkTextToPathIter iter(text, byteLength, paint, true, true);
+
+ SkMatrix matrix;
+ matrix.setScale(iter.getPathScale(), iter.getPathScale());
+ 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(const 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(const SkDraw* draw, const SkPaint& paint,
+ SkScalar width, const SkPoint& start) {
+ uint32_t flags = paint.getFlags();
+
+ if (flags & (SkPaint::kUnderlineText_Flag |
+ SkPaint::kStrikeThruText_Flag)) {
+ 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_Flag) {
+ SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
+ start.fY);
+ r.fTop = offset;
+ r.fBottom = offset + height;
+ draw_paint_rect(draw, paint, r, textSize);
+ }
+ if (flags & SkPaint::kStrikeThruText_Flag) {
+ SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
+ start.fY);
+ r.fTop = offset;
+ r.fBottom = offset + height;
+ draw_paint_rect(draw, paint, r, textSize);
+ }
+ }
+}
+
+// disable warning : local variable used without having been initialized
+#if defined _WIN32 && _MSC_VER >= 1300
+#pragma warning ( push )
+#pragma warning ( disable : 4701 )
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+
+static void D1G_NoBounder_RectClip(const SkDraw1Glyph& state,
+ const SkGlyph& glyph, int left, int top) {
+ SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0);
+ SkASSERT(state.fClip->isRect());
+ SkASSERT(NULL == state.fBounder);
+ SkASSERT(state.fClipBounds == state.fClip->getBounds());
+
+ left += glyph.fLeft;
+ top += glyph.fTop;
+
+ int right = left + glyph.fWidth;
+ int bottom = top + glyph.fHeight;
+
+ SkMask mask;
+ SkIRect storage;
+ SkIRect* bounds = &mask.fBounds;
+
+ mask.fBounds.set(left, top, right, bottom);
+
+ // this extra test is worth it, assuming that most of the time it succeeds
+ // since we can avoid writing to storage
+ if (!state.fClipBounds.containsNoEmptyCheck(left, top, right, bottom)) {
+ if (!storage.intersectNoEmptyCheck(mask.fBounds, state.fClipBounds))
+ return;
+ bounds = &storage;
+ }
+
+ uint8_t* aa = (uint8_t*)glyph.fImage;
+ if (NULL == aa) {
+ aa = (uint8_t*)state.fCache->findImage(glyph);
+ if (NULL == aa) {
+ return; // can't rasterize glyph
+ }
+ }
+
+ mask.fRowBytes = glyph.rowBytes();
+ mask.fFormat = glyph.fMaskFormat;
+ mask.fImage = aa;
+ state.fBlitter->blitMask(mask, *bounds);
+}
+
+static void D1G_NoBounder_RgnClip(const SkDraw1Glyph& state,
+ const SkGlyph& glyph, int left, int top) {
+ SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0);
+ SkASSERT(!state.fClip->isRect());
+ SkASSERT(NULL == state.fBounder);
+
+ SkMask mask;
+
+ left += glyph.fLeft;
+ top += glyph.fTop;
+
+ mask.fBounds.set(left, top, left + glyph.fWidth, top + glyph.fHeight);
+ SkRegion::Cliperator clipper(*state.fClip, mask.fBounds);
+
+ if (!clipper.done()) {
+ const SkIRect& cr = clipper.rect();
+ const uint8_t* aa = (const uint8_t*)glyph.fImage;
+ if (NULL == aa) {
+ aa = (uint8_t*)state.fCache->findImage(glyph);
+ if (NULL == aa) {
+ return;
+ }
+ }
+
+ mask.fRowBytes = glyph.rowBytes();
+ mask.fFormat = glyph.fMaskFormat;
+ mask.fImage = (uint8_t*)aa;
+ do {
+ state.fBlitter->blitMask(mask, cr);
+ clipper.next();
+ } while (!clipper.done());
+ }
+}
+
+static void D1G_Bounder(const SkDraw1Glyph& state,
+ const SkGlyph& glyph, int left, int top) {
+ SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0);
+
+ SkMask mask;
+
+ left += glyph.fLeft;
+ top += glyph.fTop;
+
+ mask.fBounds.set(left, top, left + glyph.fWidth, top + glyph.fHeight);
+ SkRegion::Cliperator clipper(*state.fClip, mask.fBounds);
+
+ if (!clipper.done()) {
+ const SkIRect& cr = clipper.rect();
+ const uint8_t* aa = (const uint8_t*)glyph.fImage;
+ if (NULL == aa) {
+ aa = (uint8_t*)state.fCache->findImage(glyph);
+ if (NULL == aa) {
+ return;
+ }
+ }
+
+ if (state.fBounder->doIRect(cr)) {
+ mask.fRowBytes = glyph.rowBytes();
+ mask.fFormat = glyph.fMaskFormat;
+ mask.fImage = (uint8_t*)aa;
+ do {
+ state.fBlitter->blitMask(mask, cr);
+ clipper.next();
+ } while (!clipper.done());
+ }
+ }
+}
+
+SkDraw1Glyph::Proc SkDraw1Glyph::init(const SkDraw* draw, SkBlitter* blitter,
+ SkGlyphCache* cache) {
+ fDraw = draw;
+ fBounder = draw->fBounder;
+ fClip = draw->fClip;
+ fClipBounds = fClip->getBounds();
+ fBlitter = blitter;
+ fCache = cache;
+
+ if (draw->fProcs && draw->fProcs->fD1GProc) {
+ return draw->fProcs->fD1GProc;
+ }
+
+ if (NULL == fBounder) {
+ if (fClip->isRect()) {
+ return D1G_NoBounder_RectClip;
+ } else {
+ return D1G_NoBounder_RgnClip;
+ }
+ } else {
+ return D1G_Bounder;
+ }
+}
+
+enum RoundBaseline {
+ kDont_Round_Baseline,
+ kRound_X_Baseline,
+ kRound_Y_Baseline
+};
+
+static RoundBaseline computeRoundBaseline(const SkMatrix& mat) {
+ if (mat[1] == 0 && mat[3] == 0) {
+ // we're 0 or 180 degrees, round the y coordinate of the baseline
+ return kRound_Y_Baseline;
+ } else if (mat[0] == 0 && mat[4] == 0) {
+ // we're 90 or 270 degrees, round the x coordinate of the baseline
+ return kRound_X_Baseline;
+ } else {
+ return kDont_Round_Baseline;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkDraw::drawText(const char text[], size_t byteLength,
+ SkScalar x, SkScalar y, const SkPaint& paint) const {
+ SkASSERT(byteLength == 0 || text != NULL);
+
+ SkDEBUGCODE(this->validate();)
+
+ // nothing to draw
+ if (text == NULL || byteLength == 0 ||
+ fClip->isEmpty() ||
+ (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
+ return;
+ }
+
+ SkScalar underlineWidth = 0;
+ SkPoint underlineStart;
+
+ underlineStart.set(0, 0); // to avoid warning
+ if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
+ SkPaint::kStrikeThruText_Flag)) {
+ underlineWidth = paint.measureText(text, byteLength);
+
+ 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.isLinearText() ||*/
+ (fMatrix->getType() & SkMatrix::kPerspective_Mask)) {
+ this->drawText_asPaths(text, byteLength, x, y, paint);
+ handle_aftertext(this, paint, underlineWidth, underlineStart);
+ return;
+ }
+
+ SkDrawCacheProc glyphCacheProc = paint.getDrawCacheProc();
+
+ SkAutoGlyphCache autoCache(paint, fMatrix);
+ SkGlyphCache* cache = autoCache.getCache();
+ SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, paint);
+
+ // transform our starting point
+ {
+ SkPoint loc;
+ fMatrix->mapXY(x, y, &loc);
+ x = loc.fX;
+ y = loc.fY;
+ }
+
+ // need to measure first
+ if (paint.getTextAlign() != SkPaint::kLeft_Align) {
+ SkVector stop;
+
+ measure_text(cache, glyphCacheProc, 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;
+ }
+
+ SkFixed fx = SkScalarToFixed(x);
+ SkFixed fy = SkScalarToFixed(y);
+ const char* stop = text + byteLength;
+
+ if (paint.isSubpixelText()) {
+ RoundBaseline roundBaseline = computeRoundBaseline(*fMatrix);
+ if (kRound_Y_Baseline == roundBaseline) {
+ fy = (fy + 0x8000) & ~0xFFFF;
+ } else if (kRound_X_Baseline == roundBaseline) {
+ fx = (fx + 0x8000) & ~0xFFFF;
+ }
+ } else {
+ // apply the bias here, so we don't have to add 1/2 in the loop
+ fx += SK_Fixed1/2;
+ fy += SK_Fixed1/2;
+ }
+
+ SkAutoKern autokern;
+ SkDraw1Glyph d1g;
+ SkDraw1Glyph::Proc proc = d1g.init(this, blitter.get(), cache);
+
+ while (text < stop) {
+ const SkGlyph& glyph = glyphCacheProc(cache, &text, fx, fy);
+
+ fx += autokern.adjust(glyph);
+
+ if (glyph.fWidth) {
+ proc(d1g, glyph, SkFixedFloor(fx), SkFixedFloor(fy));
+ }
+ fx += glyph.fAdvanceX;
+ fy += glyph.fAdvanceY;
+ }
+
+ if (underlineWidth) {
+ autoCache.release(); // release this now to free up the RAM
+ handle_aftertext(this, paint, underlineWidth, underlineStart);
+ }
+}
+
+// last parameter is interpreted as SkFixed [x, y]
+// return the fixed position, which may be rounded or not by the caller
+// e.g. subpixel doesn't round
+typedef void (*AlignProc)(const SkPoint&, const SkGlyph&, SkIPoint*);
+
+static void leftAlignProc(const SkPoint& loc, const SkGlyph& glyph,
+ SkIPoint* dst) {
+ dst->set(SkScalarToFixed(loc.fX), SkScalarToFixed(loc.fY));
+}
+
+static void centerAlignProc(const SkPoint& loc, const SkGlyph& glyph,
+ SkIPoint* dst) {
+ dst->set(SkScalarToFixed(loc.fX) - (glyph.fAdvanceX >> 1),
+ SkScalarToFixed(loc.fY) - (glyph.fAdvanceY >> 1));
+}
+
+static void rightAlignProc(const SkPoint& loc, const SkGlyph& glyph,
+ SkIPoint* dst) {
+ dst->set(SkScalarToFixed(loc.fX) - glyph.fAdvanceX,
+ SkScalarToFixed(loc.fY) - glyph.fAdvanceY);
+}
+
+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];
+}
+
+class TextMapState {
+public:
+ mutable SkPoint fLoc;
+
+ TextMapState(const SkMatrix& matrix, SkScalar y)
+ : fMatrix(matrix), fProc(matrix.getMapXYProc()), fY(y) {}
+
+ typedef void (*Proc)(const TextMapState&, const SkScalar pos[]);
+
+ Proc pickProc(int scalarsPerPosition);
+
+private:
+ const SkMatrix& fMatrix;
+ SkMatrix::MapXYProc fProc;
+ SkScalar fY; // ignored by MapXYProc
+ // these are only used by Only... procs
+ SkScalar fScaleX, fTransX, fTransformedY;
+
+ static void MapXProc(const TextMapState& state, const SkScalar pos[]) {
+ state.fProc(state.fMatrix, *pos, state.fY, &state.fLoc);
+ }
+
+ static void MapXYProc(const TextMapState& state, const SkScalar pos[]) {
+ state.fProc(state.fMatrix, pos[0], pos[1], &state.fLoc);
+ }
+
+ static void MapOnlyScaleXProc(const TextMapState& state,
+ const SkScalar pos[]) {
+ state.fLoc.set(SkScalarMul(state.fScaleX, *pos) + state.fTransX,
+ state.fTransformedY);
+ }
+
+ static void MapOnlyTransXProc(const TextMapState& state,
+ const SkScalar pos[]) {
+ state.fLoc.set(*pos + state.fTransX, state.fTransformedY);
+ }
+};
+
+TextMapState::Proc TextMapState::pickProc(int scalarsPerPosition) {
+ SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
+
+ if (1 == scalarsPerPosition) {
+ unsigned mtype = fMatrix.getType();
+ if (mtype & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)) {
+ return MapXProc;
+ } else {
+ fScaleX = fMatrix.getScaleX();
+ fTransX = fMatrix.getTranslateX();
+ fTransformedY = SkScalarMul(fY, fMatrix.getScaleY()) +
+ fMatrix.getTranslateY();
+ return (mtype & SkMatrix::kScale_Mask) ?
+ MapOnlyScaleXProc : MapOnlyTransXProc;
+ }
+ } else {
+ return MapXYProc;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void SkDraw::drawPosText(const char text[], size_t byteLength,
+ const SkScalar pos[], SkScalar constY,
+ int scalarsPerPosition, const SkPaint& paint) const {
+ SkASSERT(byteLength == 0 || text != NULL);
+ SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
+
+ SkDEBUGCODE(this->validate();)
+
+ // nothing to draw
+ if (text == NULL || byteLength == 0 ||
+ fClip->isEmpty() ||
+ (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
+ return;
+ }
+
+ if (/*paint.isLinearText() ||*/
+ (fMatrix->getType() & SkMatrix::kPerspective_Mask)) {
+ // TODO !!!!
+// this->drawText_asPaths(text, byteLength, x, y, paint);
+ return;
+ }
+
+ SkDrawCacheProc glyphCacheProc = paint.getDrawCacheProc();
+ SkAutoGlyphCache autoCache(paint, fMatrix);
+ SkGlyphCache* cache = autoCache.getCache();
+ SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, paint);
+
+ const char* stop = text + byteLength;
+ AlignProc alignProc = pick_align_proc(paint.getTextAlign());
+ SkDraw1Glyph d1g;
+ SkDraw1Glyph::Proc proc = d1g.init(this, blitter.get(), cache);
+ TextMapState tms(*fMatrix, constY);
+ TextMapState::Proc tmsProc = tms.pickProc(scalarsPerPosition);
+
+ if (paint.isSubpixelText()) {
+ // maybe we should skip the rounding if linearText is set
+ RoundBaseline roundBaseline = computeRoundBaseline(*fMatrix);
+
+ if (SkPaint::kLeft_Align == paint.getTextAlign()) {
+ while (text < stop) {
+ tmsProc(tms, pos);
+
+ SkFixed fx = SkScalarToFixed(tms.fLoc.fX);
+ SkFixed fy = SkScalarToFixed(tms.fLoc.fY);
+
+ if (kRound_Y_Baseline == roundBaseline) {
+ fy = (fy + 0x8000) & ~0xFFFF;
+ } else if (kRound_X_Baseline == roundBaseline) {
+ fx = (fx + 0x8000) & ~0xFFFF;
+ }
+
+ const SkGlyph& glyph = glyphCacheProc(cache, &text, fx, fy);
+
+ if (glyph.fWidth) {
+ proc(d1g, glyph, SkFixedFloor(fx), SkFixedFloor(fy));
+ }
+ pos += scalarsPerPosition;
+ }
+ } else {
+ while (text < stop) {
+ const SkGlyph* glyph = &glyphCacheProc(cache, &text, 0, 0);
+
+ if (glyph->fWidth) {
+ SkDEBUGCODE(SkFixed prevAdvX = glyph->fAdvanceX;)
+ SkDEBUGCODE(SkFixed prevAdvY = glyph->fAdvanceY;)
+
+ SkFixed fx, fy;
+ tmsProc(tms, pos);
+
+ {
+ SkIPoint fixedLoc;
+ alignProc(tms.fLoc, *glyph, &fixedLoc);
+ fx = fixedLoc.fX;
+ fy = fixedLoc.fY;
+
+ if (kRound_Y_Baseline == roundBaseline) {
+ fy = (fy + 0x8000) & ~0xFFFF;
+ } else if (kRound_X_Baseline == roundBaseline) {
+ fx = (fx + 0x8000) & ~0xFFFF;
+ }
+ }
+
+ // have to call again, now that we've been "aligned"
+ glyph = &glyphCacheProc(cache, &text, fx, fy);
+ // the assumption is that the advance hasn't changed
+ SkASSERT(prevAdvX == glyph->fAdvanceX);
+ SkASSERT(prevAdvY == glyph->fAdvanceY);
+
+ proc(d1g, *glyph, SkFixedFloor(fx), SkFixedFloor(fy));
+ }
+ pos += scalarsPerPosition;
+ }
+ }
+ } else { // not subpixel
+ while (text < stop) {
+ // the last 2 parameters are ignored
+ const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
+
+ if (glyph.fWidth) {
+ tmsProc(tms, pos);
+
+ SkIPoint fixedLoc;
+ alignProc(tms.fLoc, glyph, &fixedLoc);
+
+ proc(d1g, glyph,
+ SkFixedRound(fixedLoc.fX), SkFixedRound(fixedLoc.fY));
+ }
+ pos += scalarsPerPosition;
+ }
+ }
+}
+
+#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, const SkMatrix& matrix) {
+ SkMatrix::MapXYProc proc = matrix.getMapXYProc();
+
+ for (int i = 0; i < count; i++) {
+ SkPoint pos;
+ SkVector tangent;
+
+ proc(matrix, src[i].fX, src[i].fY, &pos);
+ SkScalar sx = pos.fX;
+ SkScalar sy = pos.fY;
+
+ meas.getPosTan(sx, &pos, &tangent);
+
+ /* This is the old way (that explains our approach but is way too slow
+ SkMatrix matrix;
+ SkPoint pt;
+
+ pt.set(sx, sy);
+ matrix.setSinCos(tangent.fY, tangent.fX);
+ matrix.preTranslate(-sx, 0);
+ matrix.postTranslate(pos.fX, pos.fY);
+ matrix.mapPoints(&dst[i], &pt, 1);
+ */
+ dst[i].set(pos.fX - SkScalarMul(tangent.fY, sy),
+ pos.fY + SkScalarMul(tangent.fX, sy));
+ }
+}
+
+/* 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,
+ const SkMatrix& matrix) {
+ 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, matrix);
+ dst->moveTo(dstP[0]);
+ break;
+ case SkPath::kLine_Verb:
+ // turn lines into quads to look bendy
+ srcP[0].fX = SkScalarAve(srcP[0].fX, srcP[1].fX);
+ srcP[0].fY = SkScalarAve(srcP[0].fY, srcP[1].fY);
+ morphpoints(dstP, srcP, 2, meas, matrix);
+ dst->quadTo(dstP[0], dstP[1]);
+ break;
+ case SkPath::kQuad_Verb:
+ morphpoints(dstP, &srcP[1], 2, meas, matrix);
+ dst->quadTo(dstP[0], dstP[1]);
+ break;
+ case SkPath::kCubic_Verb:
+ morphpoints(dstP, &srcP[1], 3, meas, matrix);
+ dst->cubicTo(dstP[0], dstP[1], dstP[2]);
+ break;
+ case SkPath::kClose_Verb:
+ dst->close();
+ break;
+ default:
+ SkASSERT(!"unknown verb");
+ break;
+ }
+ }
+}
+
+void SkDraw::drawTextOnPath(const char text[], size_t byteLength,
+ const SkPath& follow, const SkMatrix* matrix,
+ const SkPaint& paint) const {
+ SkASSERT(byteLength == 0 || text != NULL);
+
+ // nothing to draw
+ if (text == NULL || byteLength == 0 ||
+ fClip->isEmpty() ||
+ (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
+ return;
+ }
+
+ SkTextToPathIter iter(text, byteLength, paint, true, true);
+ SkPathMeasure meas(follow, false);
+ SkScalar hOffset = 0;
+
+ // need to measure first
+ if (paint.getTextAlign() != SkPaint::kLeft_Align) {
+ SkScalar pathLen = meas.getLength();
+ if (paint.getTextAlign() == SkPaint::kCenter_Align) {
+ pathLen = SkScalarHalf(pathLen);
+ }
+ hOffset += pathLen;
+ }
+
+ const SkPath* iterPath;
+ SkScalar xpos;
+ SkMatrix scaledMatrix;
+ SkScalar scale = iter.getPathScale();
+
+ scaledMatrix.setScale(scale, scale);
+
+ while ((iterPath = iter.next(&xpos)) != NULL) {
+ SkPath tmp;
+ SkMatrix m(scaledMatrix);
+
+ m.postTranslate(xpos + hOffset, 0);
+ if (matrix) {
+ m.postConcat(*matrix);
+ }
+ morphpath(&tmp, *iterPath, meas, m);
+ this->drawPath(tmp, iter.getPaint());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct VertState {
+ int f0, f1, f2;
+
+ VertState(int vCount, const uint16_t indices[], int indexCount)
+ : fIndices(indices) {
+ fCurrIndex = 0;
+ if (indices) {
+ fCount = indexCount;
+ } else {
+ fCount = vCount;
+ }
+ }
+
+ typedef bool (*Proc)(VertState*);
+ Proc chooseProc(SkCanvas::VertexMode mode);
+
+private:
+ int fCount;
+ int fCurrIndex;
+ const uint16_t* fIndices;
+
+ static bool Triangles(VertState*);
+ static bool TrianglesX(VertState*);
+ static bool TriangleStrip(VertState*);
+ static bool TriangleStripX(VertState*);
+ static bool TriangleFan(VertState*);
+ static bool TriangleFanX(VertState*);
+};
+
+bool VertState::Triangles(VertState* state) {
+ int index = state->fCurrIndex;
+ if (index + 3 > state->fCount) {
+ return false;
+ }
+ state->f0 = index + 0;
+ state->f1 = index + 1;
+ state->f2 = index + 2;
+ state->fCurrIndex = index + 3;
+ return true;
+}
+
+bool VertState::TrianglesX(VertState* state) {
+ const uint16_t* indices = state->fIndices;
+ int index = state->fCurrIndex;
+ if (index + 3 > state->fCount) {
+ return false;
+ }
+ state->f0 = indices[index + 0];
+ state->f1 = indices[index + 1];
+ state->f2 = indices[index + 2];
+ state->fCurrIndex = index + 3;
+ return true;
+}
+
+bool VertState::TriangleStrip(VertState* state) {
+ int index = state->fCurrIndex;
+ if (index + 3 > state->fCount) {
+ return false;
+ }
+ state->f2 = index + 2;
+ if (index & 1) {
+ state->f0 = index + 1;
+ state->f1 = index + 0;
+ } else {
+ state->f0 = index + 0;
+ state->f1 = index + 1;
+ }
+ state->fCurrIndex = index + 1;
+ return true;
+}
+
+bool VertState::TriangleStripX(VertState* state) {
+ const uint16_t* indices = state->fIndices;
+ int index = state->fCurrIndex;
+ if (index + 3 > state->fCount) {
+ return false;
+ }
+ state->f2 = indices[index + 2];
+ if (index & 1) {
+ state->f0 = indices[index + 1];
+ state->f1 = indices[index + 0];
+ } else {
+ state->f0 = indices[index + 0];
+ state->f1 = indices[index + 1];
+ }
+ state->fCurrIndex = index + 1;
+ return true;
+}
+
+bool VertState::TriangleFan(VertState* state) {
+ int index = state->fCurrIndex;
+ if (index + 3 > state->fCount) {
+ return false;
+ }
+ state->f0 = 0;
+ state->f1 = index + 1;
+ state->f2 = index + 2;
+ state->fCurrIndex = index + 1;
+ return true;
+}
+
+bool VertState::TriangleFanX(VertState* state) {
+ const uint16_t* indices = state->fIndices;
+ int index = state->fCurrIndex;
+ if (index + 3 > state->fCount) {
+ return false;
+ }
+ state->f0 = indices[0];
+ state->f1 = indices[index + 1];
+ state->f2 = indices[index + 2];
+ state->fCurrIndex = index + 1;
+ return true;
+}
+
+VertState::Proc VertState::chooseProc(SkCanvas::VertexMode mode) {
+ switch (mode) {
+ case SkCanvas::kTriangles_VertexMode:
+ return fIndices ? TrianglesX : Triangles;
+ case SkCanvas::kTriangleStrip_VertexMode:
+ return fIndices ? TriangleStripX : TriangleStrip;
+ case SkCanvas::kTriangleFan_VertexMode:
+ return fIndices ? TriangleFanX : TriangleFan;
+ default:
+ return NULL;
+ }
+}
+
+typedef void (*HairProc)(const SkPoint&, const SkPoint&, const SkRegion*,
+ SkBlitter*);
+
+static HairProc ChooseHairProc(bool doAntiAlias) {
+ return doAntiAlias ? SkScan::AntiHairLine : SkScan::HairLine;
+}
+
+static bool texture_to_matrix(const VertState& state, const SkPoint verts[],
+ const SkPoint texs[], SkMatrix* matrix) {
+ SkPoint src[3], dst[3];
+
+ src[0] = texs[state.f0];
+ src[1] = texs[state.f1];
+ src[2] = texs[state.f2];
+ dst[0] = verts[state.f0];
+ dst[1] = verts[state.f1];
+ dst[2] = verts[state.f2];
+ return matrix->setPolyToPoly(src, dst, 3);
+}
+
+class SkTriColorShader : public SkShader {
+public:
+ SkTriColorShader() {}
+
+ bool setup(const SkPoint pts[], const SkColor colors[], int, int, int);
+
+ virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
+
+protected:
+ SkTriColorShader(SkFlattenableReadBuffer& buffer) : SkShader(buffer) {}
+
+ virtual Factory getFactory() { return CreateProc; }
+
+private:
+ SkMatrix fDstToUnit;
+ SkPMColor fColors[3];
+
+ static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+ return SkNEW_ARGS(SkTriColorShader, (buffer));
+ }
+ typedef SkShader INHERITED;
+};
+
+bool SkTriColorShader::setup(const SkPoint pts[], const SkColor colors[],
+ int index0, int index1, int index2) {
+
+ fColors[0] = SkPreMultiplyColor(colors[index0]);
+ fColors[1] = SkPreMultiplyColor(colors[index1]);
+ fColors[2] = SkPreMultiplyColor(colors[index2]);
+
+ SkMatrix m, im;
+ m.reset();
+ m.set(0, pts[index1].fX - pts[index0].fX);
+ m.set(1, pts[index2].fX - pts[index0].fX);
+ m.set(2, pts[index0].fX);
+ m.set(3, pts[index1].fY - pts[index0].fY);
+ m.set(4, pts[index2].fY - pts[index0].fY);
+ m.set(5, pts[index0].fY);
+ if (!m.invert(&im)) {
+ return false;
+ }
+ return fDstToUnit.setConcat(im, this->getTotalInverse());
+}
+
+#include "SkColorPriv.h"
+#include "SkPorterDuff.h"
+#include "SkComposShader.h"
+#include "SkXfermode.h"
+
+static int ScalarTo256(SkScalar v) {
+ int scale = SkScalarToFixed(v) >> 8;
+ if (scale < 0) {
+ scale = 0;
+ }
+ if (scale > 255) {
+ scale = 255;
+ }
+ return SkAlpha255To256(scale);
+}
+
+void SkTriColorShader::shadeSpan(int x, int y, SkPMColor dstC[], int count) {
+ SkPoint src;
+
+ for (int i = 0; i < count; i++) {
+ fDstToUnit.mapXY(SkIntToScalar(x), SkIntToScalar(y), &src);
+ x += 1;
+
+ int scale1 = ScalarTo256(src.fX);
+ int scale2 = ScalarTo256(src.fY);
+ int scale0 = 256 - scale1 - scale2;
+ if (scale0 < 0) {
+ if (scale1 > scale2) {
+ scale2 = 256 - scale1;
+ } else {
+ scale1 = 256 - scale2;
+ }
+ scale0 = 0;
+ }
+
+ dstC[i] = SkAlphaMulQ(fColors[0], scale0) +
+ SkAlphaMulQ(fColors[1], scale1) +
+ SkAlphaMulQ(fColors[2], scale2);
+ }
+}
+
+void SkDraw::drawVertices(SkCanvas::VertexMode vmode, int count,
+ const SkPoint vertices[], const SkPoint textures[],
+ const SkColor colors[], SkXfermode* xmode,
+ const uint16_t indices[], int indexCount,
+ const SkPaint& paint) const {
+ SkASSERT(0 == count || NULL != vertices);
+
+ // abort early if there is nothing to draw
+ if (count < 3 || (indices && indexCount < 3) || fClip->isEmpty() ||
+ (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
+ return;
+ }
+
+ // transform out vertices into device coordinates
+ SkAutoSTMalloc<16, SkPoint> storage(count);
+ SkPoint* devVerts = storage.get();
+ fMatrix->mapPoints(devVerts, vertices, count);
+
+ if (fBounder) {
+ SkRect bounds;
+ bounds.set(devVerts, count);
+ if (!fBounder->doRect(bounds, paint)) {
+ return;
+ }
+ }
+
+ /*
+ We can draw the vertices in 1 of 4 ways:
+
+ - solid color (no shader/texture[], no colors[])
+ - just colors (no shader/texture[], has colors[])
+ - just texture (has shader/texture[], no colors[])
+ - colors * texture (has shader/texture[], has colors[])
+
+ Thus for texture drawing, we need both texture[] and a shader.
+ */
+
+ SkTriColorShader triShader; // must be above declaration of p
+ SkPaint p(paint);
+
+ SkShader* shader = p.getShader();
+ if (NULL == shader) {
+ // if we have no shader, we ignore the texture coordinates
+ textures = NULL;
+ } else if (NULL == textures) {
+ // if we don't have texture coordinates, ignore the shader
+ p.setShader(NULL);
+ shader = NULL;
+ }
+
+ // setup the custom shader (if needed)
+ if (NULL != colors) {
+ if (NULL == textures) {
+ // just colors (no texture)
+ p.setShader(&triShader);
+ } else {
+ // colors * texture
+ SkASSERT(shader);
+ bool releaseMode = false;
+ if (NULL == xmode) {
+ xmode = SkPorterDuff::CreateXfermode(
+ SkPorterDuff::kMultiply_Mode);
+ releaseMode = true;
+ }
+ SkShader* compose = SkNEW_ARGS(SkComposeShader,
+ (&triShader, shader, xmode));
+ p.setShader(compose)->unref();
+ if (releaseMode) {
+ xmode->unref();
+ }
+ }
+ }
+
+ SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, p);
+ // setup our state and function pointer for iterating triangles
+ VertState state(count, indices, indexCount);
+ VertState::Proc vertProc = state.chooseProc(vmode);
+
+ if (NULL != textures || NULL != colors) {
+ SkMatrix localM, tempM;
+ bool hasLocalM = shader && shader->getLocalMatrix(&localM);
+
+ if (NULL != colors) {
+ if (!triShader.setContext(*fBitmap, p, *fMatrix)) {
+ colors = NULL;
+ }
+ }
+
+ while (vertProc(&state)) {
+ if (NULL != textures) {
+ if (texture_to_matrix(state, vertices, textures, &tempM)) {
+ if (hasLocalM) {
+ tempM.postConcat(localM);
+ }
+ shader->setLocalMatrix(tempM);
+ // need to recal setContext since we changed the local matrix
+ if (!shader->setContext(*fBitmap, p, *fMatrix)) {
+ continue;
+ }
+ }
+ }
+ if (NULL != colors) {
+ if (!triShader.setup(vertices, colors,
+ state.f0, state.f1, state.f2)) {
+ continue;
+ }
+ }
+ SkScan::FillTriangle(devVerts[state.f0], devVerts[state.f1],
+ devVerts[state.f2], fClip, blitter.get());
+ }
+ // now restore the shader's original local matrix
+ if (NULL != shader) {
+ if (hasLocalM) {
+ shader->setLocalMatrix(localM);
+ } else {
+ shader->resetLocalMatrix();
+ }
+ }
+ } else {
+ // no colors[] and no texture
+ HairProc hairProc = ChooseHairProc(paint.isAntiAlias());
+ while (vertProc(&state)) {
+ hairProc(devVerts[state.f0], devVerts[state.f1], fClip, blitter.get());
+ hairProc(devVerts[state.f1], devVerts[state.f2], fClip, blitter.get());
+ hairProc(devVerts[state.f2], devVerts[state.f0], fClip, blitter.get());
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+void SkDraw::validate() const {
+ SkASSERT(fBitmap != NULL);
+ SkASSERT(fMatrix != NULL);
+ SkASSERT(fClip != NULL);
+
+ const SkIRect& cr = fClip->getBounds();
+ SkIRect br;
+
+ br.set(0, 0, fBitmap->width(), fBitmap->height());
+ SkASSERT(cr.isEmpty() || br.contains(cr));
+}
+
+#endif
+
+//////////////////////////////////////////////////////////////////////////////////////////
+
+bool SkBounder::doIRect(const SkIRect& r) {
+ SkIRect rr;
+ return rr.intersect(fClip->getBounds(), r) && this->onIRect(rr);
+}
+
+bool SkBounder::doHairline(const SkPoint& pt0, const SkPoint& pt1,
+ const SkPaint& paint) {
+ SkIRect r;
+ SkScalar v0, v1;
+
+ v0 = pt0.fX;
+ v1 = pt1.fX;
+ if (v0 > v1) {
+ SkTSwap<SkScalar>(v0, v1);
+ }
+ r.fLeft = SkScalarFloor(v0);
+ r.fRight = SkScalarCeil(v1);
+
+ v0 = pt0.fY;
+ v1 = pt1.fY;
+ if (v0 > v1) {
+ SkTSwap<SkScalar>(v0, v1);
+ }
+ r.fTop = SkScalarFloor(v0);
+ r.fBottom = SkScalarCeil(v1);
+
+ if (paint.isAntiAlias()) {
+ r.inset(-1, -1);
+ }
+ return this->doIRect(r);
+}
+
+bool SkBounder::doRect(const SkRect& rect, const SkPaint& paint) {
+ SkIRect r;
+
+ if (paint.getStyle() == SkPaint::kFill_Style) {
+ rect.round(&r);
+ } else {
+ int rad = -1;
+ rect.roundOut(&r);
+ if (paint.isAntiAlias()) {
+ rad = -2;
+ }
+ r.inset(rad, rad);
+ }
+ return this->doIRect(r);
+}
+
+bool SkBounder::doPath(const SkPath& path, const SkPaint& paint, bool doFill) {
+ SkRect bounds;
+ SkIRect r;
+
+ path.computeBounds(&bounds, SkPath::kFast_BoundsType);
+
+ if (doFill) {
+ bounds.round(&r);
+ } else { // hairline
+ bounds.roundOut(&r);
+ }
+
+ if (paint.isAntiAlias()) {
+ r.inset(-1, -1);
+ }
+ return this->doIRect(r);
+}
+
+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 SkIRect* clipBounds,
+ SkMaskFilter* filter, const SkMatrix* filterMatrix,
+ SkIRect* bounds) {
+ if (devPath.isEmpty()) {
+ return false;
+ }
+
+ SkIPoint 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 && !SkIRect::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)) {
+ SkIRect 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.fBitmap = &bm;
+ draw.fClip = &clipRgn;
+ draw.fMatrix = &matrix;
+ draw.fBounder = NULL;
+ paint.setAntiAlias(true);
+ draw.drawPath(devPath, paint);
+}
+
+bool SkDraw::DrawToMask(const SkPath& devPath, const SkIRect* 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/src/core/SkDrawProcs.h b/src/core/SkDrawProcs.h
new file mode 100644
index 0000000000..d64c088a30
--- /dev/null
+++ b/src/core/SkDrawProcs.h
@@ -0,0 +1,26 @@
+#ifndef SkDrawProcs_DEFINED
+#define SkDrawProcs_DEFINED
+
+#include "SkDraw.h"
+
+class SkBlitter;
+
+struct SkDraw1Glyph {
+ const SkDraw* fDraw;
+ SkBounder* fBounder;
+ const SkRegion* fClip;
+ SkBlitter* fBlitter;
+ SkGlyphCache* fCache;
+ SkIRect fClipBounds;
+
+ typedef void (*Proc)(const SkDraw1Glyph&, const SkGlyph&, int x, int y);
+
+ Proc init(const SkDraw* draw, SkBlitter* blitter, SkGlyphCache* cache);
+};
+
+struct SkDrawProcs {
+ SkDraw1Glyph::Proc fD1GProc;
+};
+
+#endif
+
diff --git a/src/core/SkEdge.cpp b/src/core/SkEdge.cpp
new file mode 100644
index 0000000000..6efe1bad39
--- /dev/null
+++ b/src/core/SkEdge.cpp
@@ -0,0 +1,484 @@
+/* libs/graphics/sgl/SkEdge.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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& p0, const SkPoint& p1, const SkIRect* clip,
+ int shift) {
+ SkFDot6 x0, y0, x1, y1;
+
+ {
+#ifdef SK_SCALAR_IS_FLOAT
+ float scale = float(1 << (shift + 6));
+ x0 = int(p0.fX * scale);
+ y0 = int(p0.fY * scale);
+ x1 = int(p1.fX * scale);
+ y1 = int(p1.fY * scale);
+#else
+ shift = 10 - shift;
+ x0 = p0.fX >> shift;
+ y0 = p0.fY >> shift;
+ x1 = p1.fX >> shift;
+ y1 = p1.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 (NULL != 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 = top;
+ fLastY = 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 = top;
+ fLastY = bot - 1;
+
+ return 1;
+}
+
+void SkEdge::chopLineWithClip(const SkIRect& 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;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/* We store 1<<shift in a (signed) byte, so its maximum value is 1<<6 == 64.
+ Note that this limits the number of lines we use to approximate a curve.
+ If we need to increase this, we need to store fCurveCount in something
+ larger than int8_t.
+*/
+#define MAX_COEFF_SHIFT 6
+
+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 = (dist + (1 << 4)) >> 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 SkIRect* 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);
+ SkASSERT(shift >= 0);
+ }
+ // need at least 1 subdivision for our bias trick
+ if (shift == 0) {
+ shift = 1;
+ } else if (shift > MAX_COEFF_SHIFT) {
+ shift = MAX_COEFF_SHIFT;
+ }
+
+ fWinding = SkToS8(winding);
+ fCurveShift = SkToU8(shift);
+ //fCubicDShift only set for cubics
+ fCurveCount = SkToS8(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 dx = fQDx;
+ SkFixed dy = fQDy;
+ SkFixed newx, newy;
+ int shift = fCurveShift;
+
+ SkASSERT(count > 0);
+
+ do {
+ if (--count > 0)
+ {
+ newx = oldx + (dx >> shift);
+ dx += fQDDx;
+ newy = oldy + (dy >> shift);
+ dy += 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;
+ fQDx = dx;
+ fQDy = dy;
+ fCurveCount = SkToS16(count);
+ return success;
+}
+
+/////////////////////////////////////////////////////////////////////////
+
+static inline int SkFDot6UpShift(SkFDot6 x, int upShift) {
+ SkASSERT((x << upShift >> upShift) == x);
+ return x << upShift;
+}
+
+/* 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 SkIRect* 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);
+ if (shift > MAX_COEFF_SHIFT) {
+ shift = MAX_COEFF_SHIFT;
+ }
+
+ /* Since our in coming data is initially shifted down by 10 (or 8 in
+ antialias). That means the most we can shift up is 8. However, we
+ compute coefficients with a 3*, so the safest upshift is really 6
+ */
+ int upShift = 6; // largest safe value
+ int downShift = shift + upShift - 10;
+ if (downShift < 0) {
+ downShift = 0;
+ upShift = 10 - shift;
+ }
+
+ fWinding = SkToS8(winding);
+ fCurveCount = SkToS8(-1 << shift);
+ fCurveShift = SkToU8(shift);
+ fCubicDShift = SkToU8(downShift);
+
+ SkFixed B = SkFDot6UpShift(3 * (x1 - x0), upShift);
+ SkFixed C = SkFDot6UpShift(3 * (x0 - x1 - x1 + x2), upShift);
+ SkFixed D = SkFDot6UpShift(x3 + 3 * (x1 - x2) - x0, upShift);
+
+ 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 = SkFDot6UpShift(3 * (y1 - y0), upShift);
+ C = SkFDot6UpShift(3 * (y0 - y1 - y1 + y2), upShift);
+ D = SkFDot6UpShift(y3 + 3 * (y1 - y2) - y0, upShift);
+
+ 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;
+ const int ddshift = fCurveShift;
+ const int dshift = fCubicDShift;
+
+ SkASSERT(count < 0);
+
+ do {
+ if (++count < 0)
+ {
+ newx = oldx + (fCDx >> dshift);
+ fCDx += fCDDx >> ddshift;
+ fCDDx += fCDDDx;
+
+ newy = oldy + (fCDy >> dshift);
+ fCDy += fCDDy >> ddshift;
+ 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/src/core/SkEdge.h b/src/core/SkEdge.h
new file mode 100644
index 0000000000..5b0cc75d4c
--- /dev/null
+++ b/src/core/SkEdge.h
@@ -0,0 +1,93 @@
+/* libs/graphics/sgl/SkEdge.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#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;
+ int32_t fFirstY;
+ int32_t fLastY;
+ int8_t fCurveCount; // only used by kQuad(+) and kCubic(-)
+ uint8_t fCurveShift; // appled to all Dx/DDx/DDDx except for fCubicDShift exception
+ uint8_t fCubicDShift; // applied to fCDx and fCDy only in cubic
+ int8_t fWinding; // 1 or -1
+
+ int setLine(const SkPoint& p0, const SkPoint& p1, const SkIRect* clip,
+ int shiftUp);
+ inline int updateLine(SkFixed ax, SkFixed ay, SkFixed bx, SkFixed by);
+ void chopLineWithClip(const SkIRect& clip);
+
+ inline bool intersectsClip(const SkIRect& 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 SkIRect* 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 SkIRect* clip, int shiftUp);
+ int updateCubic();
+};
+
+#endif
diff --git a/src/core/SkFP.h b/src/core/SkFP.h
new file mode 100644
index 0000000000..6c0c52634e
--- /dev/null
+++ b/src/core/SkFP.h
@@ -0,0 +1,87 @@
+/* libs/graphics/sgl/SkFP.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#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 int32_t 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/src/core/SkFilterProc.cpp b/src/core/SkFilterProc.cpp
new file mode 100644
index 0000000000..814bafe490
--- /dev/null
+++ b/src/core/SkFilterProc.cpp
@@ -0,0 +1,303 @@
+/* libs/graphics/sgl/SkFilterProc.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+#define MASK 0xFF00FF
+#define LO_PAIR(x) ((x) & MASK)
+#define HI_PAIR(x) (((x) >> 8) & MASK)
+#define COMBINE(lo, hi) (((lo) & ~0xFF00) | (((hi) & ~0xFF00) << 8))
+
+///////////////////////////////////////////////////////////////////////////////
+
+static unsigned bilerp4_00(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+ return c00;
+}
+static unsigned bilerp4_01(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+ uint32_t lo = (3 * LO_PAIR(c00) + LO_PAIR(c01)) >> 2;
+ uint32_t hi = (3 * HI_PAIR(c00) + HI_PAIR(c01)) >> 2;
+ return COMBINE(lo, hi);
+}
+static unsigned bilerp4_02(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+ uint32_t lo = (LO_PAIR(c00) + LO_PAIR(c01)) >> 1;
+ uint32_t hi = (HI_PAIR(c00) + HI_PAIR(c01)) >> 1;
+ return COMBINE(lo, hi);
+}
+static unsigned bilerp4_03(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+ uint32_t lo = (LO_PAIR(c00) + 3 * LO_PAIR(c01)) >> 2;
+ uint32_t hi = (HI_PAIR(c00) + 3 * HI_PAIR(c01)) >> 2;
+ return COMBINE(lo, hi);
+}
+
+static unsigned bilerp4_10(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+ uint32_t lo = (3 * LO_PAIR(c00) + LO_PAIR(c10)) >> 2;
+ uint32_t hi = (3 * HI_PAIR(c00) + HI_PAIR(c10)) >> 2;
+ return COMBINE(lo, hi);
+}
+static unsigned bilerp4_11(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+ uint32_t lo = (9 * LO_PAIR(c00) + 3 * (LO_PAIR(c01) + LO_PAIR(c10)) + LO_PAIR(c11)) >> 4;
+ uint32_t hi = (9 * HI_PAIR(c00) + 3 * (HI_PAIR(c01) + HI_PAIR(c10)) + HI_PAIR(c11)) >> 4;
+ return COMBINE(lo, hi);
+}
+static unsigned bilerp4_12(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+ uint32_t lo = (3 * (LO_PAIR(c00) + LO_PAIR(c01)) + LO_PAIR(c10) + LO_PAIR(c11)) >> 3;
+ uint32_t hi = (3 * (HI_PAIR(c00) + HI_PAIR(c01)) + HI_PAIR(c10) + HI_PAIR(c11)) >> 3;
+ return COMBINE(lo, hi);
+}
+static unsigned bilerp4_13(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+ uint32_t lo = (9 * LO_PAIR(c01) + 3 * (LO_PAIR(c00) + LO_PAIR(c11)) + LO_PAIR(c10)) >> 4;
+ uint32_t hi = (9 * HI_PAIR(c01) + 3 * (HI_PAIR(c00) + HI_PAIR(c11)) + HI_PAIR(c10)) >> 4;
+ return COMBINE(lo, hi);
+}
+
+static unsigned bilerp4_20(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+ uint32_t lo = (LO_PAIR(c00) + LO_PAIR(c10)) >> 1;
+ uint32_t hi = (HI_PAIR(c00) + HI_PAIR(c10)) >> 1;
+ return COMBINE(lo, hi);
+}
+static unsigned bilerp4_21(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+ uint32_t lo = (3 * (LO_PAIR(c00) + LO_PAIR(c10)) + LO_PAIR(c01) + LO_PAIR(c11)) >> 3;
+ uint32_t hi = (3 * (HI_PAIR(c00) + HI_PAIR(c10)) + HI_PAIR(c01) + HI_PAIR(c11)) >> 3;
+ return COMBINE(lo, hi);
+}
+static unsigned bilerp4_22(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+ uint32_t lo = (LO_PAIR(c00) + LO_PAIR(c01) + LO_PAIR(c10) + LO_PAIR(c11)) >> 2;
+ uint32_t hi = (HI_PAIR(c00) + HI_PAIR(c01) + HI_PAIR(c10) + HI_PAIR(c11)) >> 2;
+ return COMBINE(lo, hi);
+}
+static unsigned bilerp4_23(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+ uint32_t lo = (3 * (LO_PAIR(c01) + LO_PAIR(c11)) + LO_PAIR(c00) + LO_PAIR(c10)) >> 3;
+ uint32_t hi = (3 * (HI_PAIR(c01) + HI_PAIR(c11)) + HI_PAIR(c00) + HI_PAIR(c10)) >> 3;
+ return COMBINE(lo, hi);
+}
+
+static unsigned bilerp4_30(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+ uint32_t lo = (LO_PAIR(c00) + 3 * LO_PAIR(c10)) >> 2;
+ uint32_t hi = (HI_PAIR(c00) + 3 * HI_PAIR(c10)) >> 2;
+ return COMBINE(lo, hi);
+}
+static unsigned bilerp4_31(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+ uint32_t lo = (9 * LO_PAIR(c10) + 3 * (LO_PAIR(c00) + LO_PAIR(c11)) + LO_PAIR(c01)) >> 4;
+ uint32_t hi = (9 * HI_PAIR(c10) + 3 * (HI_PAIR(c00) + HI_PAIR(c11)) + HI_PAIR(c01)) >> 4;
+ return COMBINE(lo, hi);
+}
+static unsigned bilerp4_32(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+ uint32_t lo = (3 * (LO_PAIR(c10) + LO_PAIR(c11)) + LO_PAIR(c00) + LO_PAIR(c01)) >> 3;
+ uint32_t hi = (3 * (HI_PAIR(c10) + HI_PAIR(c11)) + HI_PAIR(c00) + HI_PAIR(c01)) >> 3;
+ return COMBINE(lo, hi);
+}
+static unsigned bilerp4_33(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+ uint32_t lo = (9 * LO_PAIR(c11) + 3 * (LO_PAIR(c01) + LO_PAIR(c10)) + LO_PAIR(c00)) >> 4;
+ uint32_t hi = (9 * HI_PAIR(c11) + 3 * (HI_PAIR(c01) + HI_PAIR(c10)) + HI_PAIR(c00)) >> 4;
+ return COMBINE(lo, hi);
+}
+
+static const SkFilter32Proc gBilerp32Procs[4 * 4] = {
+ bilerp4_00, bilerp4_01, bilerp4_02, bilerp4_03,
+ bilerp4_10, bilerp4_11, bilerp4_12, bilerp4_13,
+ bilerp4_20, bilerp4_21, bilerp4_22, bilerp4_23,
+ bilerp4_30, bilerp4_31, bilerp4_32, bilerp4_33
+};
+
+const SkFilter32Proc* SkGetFilter32ProcTable()
+{
+ return gBilerp32Procs;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static unsigned bilerptr00(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) {
+ return *a00;
+}
+static unsigned bilerptr01(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) {
+ uint32_t c00 = *a00;
+ uint32_t c01 = *a01;
+ uint32_t lo = (3 * LO_PAIR(c00) + LO_PAIR(c01)) >> 2;
+ uint32_t hi = (3 * HI_PAIR(c00) + HI_PAIR(c01)) >> 2;
+ return COMBINE(lo, hi);
+}
+static unsigned bilerptr02(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) {
+ uint32_t c00 = *a00;
+ uint32_t c01 = *a01;
+ uint32_t lo = (LO_PAIR(c00) + LO_PAIR(c01)) >> 1;
+ uint32_t hi = (HI_PAIR(c00) + HI_PAIR(c01)) >> 1;
+ return COMBINE(lo, hi);
+}
+static unsigned bilerptr03(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) {
+ uint32_t c00 = *a00;
+ uint32_t c01 = *a01;
+ uint32_t lo = (LO_PAIR(c00) + 3 * LO_PAIR(c01)) >> 2;
+ uint32_t hi = (HI_PAIR(c00) + 3 * HI_PAIR(c01)) >> 2;
+ return COMBINE(lo, hi);
+}
+
+static unsigned bilerptr10(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) {
+ uint32_t c00 = *a00;
+ uint32_t c10 = *a10;
+ uint32_t lo = (3 * LO_PAIR(c00) + LO_PAIR(c10)) >> 2;
+ uint32_t hi = (3 * HI_PAIR(c00) + HI_PAIR(c10)) >> 2;
+ return COMBINE(lo, hi);
+}
+static unsigned bilerptr11(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) {
+ uint32_t c00 = *a00;
+ uint32_t c01 = *a01;
+ uint32_t c10 = *a10;
+ uint32_t c11 = *a11;
+ uint32_t lo = (9 * LO_PAIR(c00) + 3 * (LO_PAIR(c01) + LO_PAIR(c10)) + LO_PAIR(c11)) >> 4;
+ uint32_t hi = (9 * HI_PAIR(c00) + 3 * (HI_PAIR(c01) + HI_PAIR(c10)) + HI_PAIR(c11)) >> 4;
+ return COMBINE(lo, hi);
+}
+static unsigned bilerptr12(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) {
+ uint32_t c00 = *a00;
+ uint32_t c01 = *a01;
+ uint32_t c10 = *a10;
+ uint32_t c11 = *a11;
+ uint32_t lo = (3 * (LO_PAIR(c00) + LO_PAIR(c01)) + LO_PAIR(c10) + LO_PAIR(c11)) >> 3;
+ uint32_t hi = (3 * (HI_PAIR(c00) + HI_PAIR(c01)) + HI_PAIR(c10) + HI_PAIR(c11)) >> 3;
+ return COMBINE(lo, hi);
+}
+static unsigned bilerptr13(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) {
+ uint32_t c00 = *a00;
+ uint32_t c01 = *a01;
+ uint32_t c10 = *a10;
+ uint32_t c11 = *a11;
+ uint32_t lo = (9 * LO_PAIR(c01) + 3 * (LO_PAIR(c00) + LO_PAIR(c11)) + LO_PAIR(c10)) >> 4;
+ uint32_t hi = (9 * HI_PAIR(c01) + 3 * (HI_PAIR(c00) + HI_PAIR(c11)) + HI_PAIR(c10)) >> 4;
+ return COMBINE(lo, hi);
+}
+
+static unsigned bilerptr20(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) {
+ uint32_t c00 = *a00;
+ uint32_t c10 = *a10;
+ uint32_t lo = (LO_PAIR(c00) + LO_PAIR(c10)) >> 1;
+ uint32_t hi = (HI_PAIR(c00) + HI_PAIR(c10)) >> 1;
+ return COMBINE(lo, hi);
+}
+static unsigned bilerptr21(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) {
+ uint32_t c00 = *a00;
+ uint32_t c01 = *a01;
+ uint32_t c10 = *a10;
+ uint32_t c11 = *a11;
+ uint32_t lo = (3 * (LO_PAIR(c00) + LO_PAIR(c10)) + LO_PAIR(c01) + LO_PAIR(c11)) >> 3;
+ uint32_t hi = (3 * (HI_PAIR(c00) + HI_PAIR(c10)) + HI_PAIR(c01) + HI_PAIR(c11)) >> 3;
+ return COMBINE(lo, hi);
+}
+static unsigned bilerptr22(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) {
+ uint32_t c00 = *a00;
+ uint32_t c01 = *a01;
+ uint32_t c10 = *a10;
+ uint32_t c11 = *a11;
+ uint32_t lo = (LO_PAIR(c00) + LO_PAIR(c01) + LO_PAIR(c10) + LO_PAIR(c11)) >> 2;
+ uint32_t hi = (HI_PAIR(c00) + HI_PAIR(c01) + HI_PAIR(c10) + HI_PAIR(c11)) >> 2;
+ return COMBINE(lo, hi);
+}
+static unsigned bilerptr23(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) {
+ uint32_t c00 = *a00;
+ uint32_t c01 = *a01;
+ uint32_t c10 = *a10;
+ uint32_t c11 = *a11;
+ uint32_t lo = (3 * (LO_PAIR(c01) + LO_PAIR(c11)) + LO_PAIR(c00) + LO_PAIR(c10)) >> 3;
+ uint32_t hi = (3 * (HI_PAIR(c01) + HI_PAIR(c11)) + HI_PAIR(c00) + HI_PAIR(c10)) >> 3;
+ return COMBINE(lo, hi);
+}
+
+static unsigned bilerptr30(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) {
+ uint32_t c00 = *a00;
+ uint32_t c10 = *a10;
+ uint32_t lo = (LO_PAIR(c00) + 3 * LO_PAIR(c10)) >> 2;
+ uint32_t hi = (HI_PAIR(c00) + 3 * HI_PAIR(c10)) >> 2;
+ return COMBINE(lo, hi);
+}
+static unsigned bilerptr31(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) {
+ uint32_t c00 = *a00;
+ uint32_t c01 = *a01;
+ uint32_t c10 = *a10;
+ uint32_t c11 = *a11;
+ uint32_t lo = (9 * LO_PAIR(c10) + 3 * (LO_PAIR(c00) + LO_PAIR(c11)) + LO_PAIR(c01)) >> 4;
+ uint32_t hi = (9 * HI_PAIR(c10) + 3 * (HI_PAIR(c00) + HI_PAIR(c11)) + HI_PAIR(c01)) >> 4;
+ return COMBINE(lo, hi);
+}
+static unsigned bilerptr32(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) {
+ uint32_t c00 = *a00;
+ uint32_t c01 = *a01;
+ uint32_t c10 = *a10;
+ uint32_t c11 = *a11;
+ uint32_t lo = (3 * (LO_PAIR(c10) + LO_PAIR(c11)) + LO_PAIR(c00) + LO_PAIR(c01)) >> 3;
+ uint32_t hi = (3 * (HI_PAIR(c10) + HI_PAIR(c11)) + HI_PAIR(c00) + HI_PAIR(c01)) >> 3;
+ return COMBINE(lo, hi);
+}
+static unsigned bilerptr33(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) {
+ uint32_t c00 = *a00;
+ uint32_t c01 = *a01;
+ uint32_t c10 = *a10;
+ uint32_t c11 = *a11;
+ uint32_t lo = (9 * LO_PAIR(c11) + 3 * (LO_PAIR(c01) + LO_PAIR(c10)) + LO_PAIR(c00)) >> 4;
+ uint32_t hi = (9 * HI_PAIR(c11) + 3 * (HI_PAIR(c01) + HI_PAIR(c10)) + HI_PAIR(c00)) >> 4;
+ return COMBINE(lo, hi);
+}
+
+static const SkFilterPtrProc gBilerpPtrProcs[4 * 4] = {
+ bilerptr00, bilerptr01, bilerptr02, bilerptr03,
+ bilerptr10, bilerptr11, bilerptr12, bilerptr13,
+ bilerptr20, bilerptr21, bilerptr22, bilerptr23,
+ bilerptr30, bilerptr31, bilerptr32, bilerptr33
+};
+
+const SkFilterPtrProc* SkGetBilinearFilterPtrProcTable()
+{
+ return gBilerpPtrProcs;
+}
+
diff --git a/src/core/SkFilterProc.h b/src/core/SkFilterProc.h
new file mode 100644
index 0000000000..9af4ed52ff
--- /dev/null
+++ b/src/core/SkFilterProc.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2006-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkFilter_DEFINED
+#define SkFilter_DEFINED
+
+#include "SkMath.h"
+#include "SkFixed.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];
+}
+
+inline SkFilterProc SkGetBilinearFilterProc22(const SkFilterProc* table,
+ unsigned x, unsigned y)
+{
+ SkASSERT(table);
+
+ // extract low 2 bits
+ x = x << 30 >> 30;
+ y = y << 30 >> 30;
+ return table[(y << 2) | x];
+}
+
+inline const SkFilterProc* SkGetBilinearFilterProc22Row(const SkFilterProc* table,
+ unsigned y)
+{
+ SkASSERT(table);
+ // extract low 2 bits and shift up 2
+ return &table[y << 30 >> 28];
+}
+
+inline SkFilterProc SkGetBilinearFilterProc22RowProc(const SkFilterProc* row,
+ unsigned x)
+{
+ SkASSERT(row);
+ // extract low 2 bits
+ return row[x << 30 >> 30];
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+typedef unsigned (*SkFilter32Proc)(uint32_t x00, uint32_t x01,
+ uint32_t x10, uint32_t x11);
+
+const SkFilter32Proc* SkGetFilter32ProcTable();
+
+inline SkFilter32Proc SkGetFilter32Proc22(const SkFilter32Proc* table,
+ unsigned x, unsigned y)
+{
+ SkASSERT(table);
+
+ // extract low 2 bits
+ x = x << 30 >> 30;
+ y = y << 30 >> 30;
+ return table[(y << 2) | x];
+}
+
+inline const SkFilter32Proc* SkGetFilter32Proc22Row(const SkFilter32Proc* table,
+ unsigned y)
+{
+ SkASSERT(table);
+ // extract low 2 bits and shift up 2
+ return &table[y << 30 >> 28];
+}
+
+inline SkFilter32Proc SkGetFilter32Proc22RowProc(const SkFilter32Proc* row,
+ unsigned x)
+{
+ SkASSERT(row);
+ // extract low 2 bits
+ return row[x << 30 >> 30];
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/** Special version of SkFilterProc. This takes the address of 4 ints, and combines them a byte at a
+ time. AABBCCDD.
+*/
+typedef uint32_t (*SkFilterPtrProc)(const uint32_t*, const uint32_t*, const uint32_t*, const uint32_t*);
+
+const SkFilterPtrProc* SkGetBilinearFilterPtrProcTable();
+inline SkFilterPtrProc SkGetBilinearFilterPtrProc(const SkFilterPtrProc* 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];
+}
+
+/** Given a Y value, return a subset of the proc table for that value.
+ Pass this to SkGetBilinearFilterPtrXProc with the corresponding X value to get the
+ correct proc.
+*/
+inline const SkFilterPtrProc* SkGetBilinearFilterPtrProcYTable(const SkFilterPtrProc* table, SkFixed y)
+{
+ SkASSERT(table);
+
+ y = (unsigned)(y << 16) >> 30;
+ return table + (y << 2);
+}
+
+/** Given a subtable returned by SkGetBilinearFilterPtrProcYTable(), return the proc for the
+ specified X value.
+*/
+inline SkFilterPtrProc SkGetBilinearFilterPtrXProc(const SkFilterPtrProc* table, SkFixed x)
+{
+ SkASSERT(table);
+
+ // convert to dot 2
+ x = (unsigned)(x << 16) >> 30;
+ return table[x];
+}
+
+#endif
+
+
diff --git a/src/core/SkFlattenable.cpp b/src/core/SkFlattenable.cpp
new file mode 100644
index 0000000000..355851924b
--- /dev/null
+++ b/src/core/SkFlattenable.cpp
@@ -0,0 +1,263 @@
+#include "SkFlattenable.h"
+#include "SkTypeface.h"
+
+void SkFlattenable::flatten(SkFlattenableWriteBuffer&)
+{
+ /* we don't write anything at the moment, but this allows our subclasses
+ to not know that, since we want them to always call INHERITED::flatten()
+ in their code.
+ */
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+SkFlattenableReadBuffer::SkFlattenableReadBuffer() {
+ fRCArray = NULL;
+ fRCCount = 0;
+
+ fTFArray = NULL;
+ fTFCount = 0;
+
+ fFactoryArray = NULL;
+ fFactoryCount = 0;
+}
+
+SkFlattenableReadBuffer::SkFlattenableReadBuffer(const void* data) :
+ INHERITED(data, 1024 * 1024) {
+ fRCArray = NULL;
+ fRCCount = 0;
+
+ fTFArray = NULL;
+ fTFCount = 0;
+
+ fFactoryArray = NULL;
+ fFactoryCount = 0;
+}
+
+SkFlattenableReadBuffer::SkFlattenableReadBuffer(const void* data, size_t size)
+ : INHERITED(data, size) {
+ fRCArray = NULL;
+ fRCCount = 0;
+
+ fTFArray = NULL;
+ fTFCount = 0;
+
+ fFactoryArray = NULL;
+ fFactoryCount = 0;
+}
+
+SkTypeface* SkFlattenableReadBuffer::readTypeface() {
+ uint32_t index = this->readU32();
+ if (0 == index || index > (unsigned)fTFCount) {
+ if (index) {
+ SkDebugf("====== typeface index %d\n", index);
+ }
+ return NULL;
+ } else {
+ SkASSERT(fTFArray);
+ return fTFArray[index - 1];
+ }
+}
+
+SkRefCnt* SkFlattenableReadBuffer::readRefCnt() {
+ uint32_t index = this->readU32();
+ if (0 == index || index > (unsigned)fRCCount) {
+ return NULL;
+ } else {
+ SkASSERT(fRCArray);
+ return fRCArray[index - 1];
+ }
+}
+
+SkFlattenable* SkFlattenableReadBuffer::readFlattenable() {
+ SkFlattenable::Factory factory = NULL;
+
+ if (fFactoryCount > 0) {
+ uint32_t index = this->readU32();
+ if (index > 0) {
+ index -= 1;
+ SkASSERT(index < (unsigned)fFactoryCount);
+ factory = fFactoryArray[index];
+ // if we recorded an index, but failed to get a factory, we need
+ // to skip the flattened data in the buffer
+ if (NULL == factory) {
+ uint32_t size = this->readU32();
+ this->skip(size);
+ // fall through and return NULL for the object
+ }
+ }
+ } else {
+ factory = (SkFlattenable::Factory)readFunctionPtr();
+ }
+
+ SkFlattenable* obj = NULL;
+ if (factory) {
+ uint32_t sizeRecorded = this->readU32();
+ uint32_t offset = this->offset();
+ obj = (*factory)(*this);
+ // check that we read the amount we expected
+ uint32_t sizeRead = this->offset() - offset;
+ if (sizeRecorded != sizeRead) {
+ // we could try to fix up the offset...
+ sk_throw();
+ }
+ }
+ return obj;
+}
+
+void* SkFlattenableReadBuffer::readFunctionPtr() {
+ void* proc;
+ this->read(&proc, sizeof(proc));
+ return proc;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkFlattenableWriteBuffer::SkFlattenableWriteBuffer(size_t minSize) :
+ INHERITED(minSize) {
+ fFlags = (Flags)0;
+ fRCRecorder = NULL;
+ fTFRecorder = NULL;
+ fFactoryRecorder = NULL;
+}
+
+SkFlattenableWriteBuffer::~SkFlattenableWriteBuffer() {
+ fRCRecorder->safeUnref();
+ fTFRecorder->safeUnref();
+ fFactoryRecorder->safeUnref();
+}
+
+SkRefCntRecorder* SkFlattenableWriteBuffer::setRefCntRecorder(
+ SkRefCntRecorder* rec) {
+ SkRefCnt_SafeAssign(fRCRecorder, rec);
+ return rec;
+}
+
+SkRefCntRecorder* SkFlattenableWriteBuffer::setTypefaceRecorder(
+ SkRefCntRecorder* rec) {
+ SkRefCnt_SafeAssign(fTFRecorder, rec);
+ return rec;
+}
+
+SkFactoryRecorder* SkFlattenableWriteBuffer::setFactoryRecorder(
+ SkFactoryRecorder* rec) {
+ SkRefCnt_SafeAssign(fFactoryRecorder, rec);
+ return rec;
+}
+
+void SkFlattenableWriteBuffer::writeTypeface(SkTypeface* obj) {
+ if (NULL == obj || NULL == fTFRecorder) {
+ this->write32(0);
+ } else {
+ this->write32(fTFRecorder->record(obj));
+ }
+}
+
+void SkFlattenableWriteBuffer::writeRefCnt(SkRefCnt* obj) {
+ if (NULL == obj || NULL == fRCRecorder) {
+ this->write32(0);
+ } else {
+ this->write32(fRCRecorder->record(obj));
+ }
+}
+
+void SkFlattenableWriteBuffer::writeFlattenable(SkFlattenable* flattenable) {
+ SkFlattenable::Factory factory = NULL;
+ if (flattenable) {
+ factory = flattenable->getFactory();
+ }
+
+ if (fFactoryRecorder) {
+ this->write32(fFactoryRecorder->record(factory));
+ } else {
+ this->writeFunctionPtr((void*)factory);
+ }
+
+ if (factory) {
+ // make room for the size of the flatttened object
+ (void)this->reserve(sizeof(uint32_t));
+ // record the current size, so we can subtract after the object writes.
+ uint32_t offset = this->size();
+ // now flatten the object
+ flattenable->flatten(*this);
+ uint32_t objSize = this->size() - offset;
+ // record the obj's size
+ *this->peek32(offset - sizeof(uint32_t)) = objSize;
+ }
+}
+
+void SkFlattenableWriteBuffer::writeFunctionPtr(void* proc) {
+ *(void**)this->reserve(sizeof(void*)) = proc;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkRefCntRecorder::~SkRefCntRecorder() {
+ // call this now, while our decPtr() is sill in scope
+ this->reset();
+}
+
+void SkRefCntRecorder::incPtr(void* ptr) {
+ ((SkRefCnt*)ptr)->ref();
+}
+
+void SkRefCntRecorder::decPtr(void* ptr) {
+ ((SkRefCnt*)ptr)->unref();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+#define MAX_PAIR_COUNT 64
+
+struct Pair {
+ const char* fName;
+ SkFlattenable::Factory fFactory;
+};
+
+static int gCount;
+static Pair gPairs[MAX_PAIR_COUNT];
+
+void SkFlattenable::Register(const char name[], Factory factory) {
+ SkASSERT(name);
+ SkASSERT(factory);
+
+ static bool gOnce;
+ if (!gOnce) {
+ gCount = 0;
+ gOnce = true;
+ }
+
+ SkASSERT(gCount < MAX_PAIR_COUNT);
+
+ gPairs[gCount].fName = name;
+ gPairs[gCount].fFactory = factory;
+ gCount += 1;
+}
+
+SkFlattenable::Factory SkFlattenable::NameToFactory(const char name[]) {
+ const Pair* pairs = gPairs;
+ for (int i = gCount - 1; i >= 0; --i) {
+ if (strcmp(pairs[i].fName, name) == 0) {
+ return pairs[i].fFactory;
+ }
+ }
+ return NULL;
+}
+
+const char* SkFlattenable::FactoryToName(Factory fact) {
+ const Pair* pairs = gPairs;
+ for (int i = gCount - 1; i >= 0; --i) {
+ if (pairs[i].fFactory == fact) {
+ return pairs[i].fName;
+ }
+ }
+ return NULL;
+}
+
+bool SkFlattenable::toDumpString(SkString* str) const {
+ return false;
+}
+
diff --git a/src/core/SkFloat.cpp b/src/core/SkFloat.cpp
new file mode 100644
index 0000000000..504c1d3f3e
--- /dev/null
+++ b/src/core/SkFloat.cpp
@@ -0,0 +1,404 @@
+/*
+ * Copyright (C) 2006-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkFloat.h"
+#include "SkMath.h"
+
+#define EXP_BIAS (127+23)
+
+static int get_unsigned_exp(uint32_t packed)
+{
+ return (packed << 1 >> 24);
+}
+
+static unsigned get_unsigned_value(uint32_t packed)
+{
+ return (packed << 9 >> 9) | (1 << 23);
+}
+
+static int get_signed_value(int32_t packed)
+{
+ return SkApplySign(get_unsigned_value(packed), SkExtractSign(packed));
+}
+
+/////////////////////////////////////////////////////////////////////////
+
+int SkFloat::GetShift(int32_t packed, int shift)
+{
+ if (packed == 0)
+ return 0;
+
+ int exp = get_unsigned_exp(packed) - EXP_BIAS - shift;
+ int value = get_unsigned_value(packed);
+
+ if (exp >= 0)
+ {
+ if (exp > 8) // overflow
+ value = SK_MaxS32;
+ else
+ value <<= exp;
+ }
+ else
+ {
+ exp = -exp;
+ if (exp > 23) // underflow
+ value = 0;
+ else
+ value >>= exp;
+ }
+ return SkApplySign(value, SkExtractSign(packed));
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+int32_t SkFloat::SetShift(int value, int shift)
+{
+ if (value == 0)
+ return 0;
+
+ // record the sign and make value positive
+ int sign = SkExtractSign(value);
+ value = SkApplySign(value, sign);
+
+ if (value >> 24) // value is too big (has more than 24 bits set)
+ {
+ int bias = 8 - SkCLZ(value);
+ SkASSERT(bias > 0 && bias < 8);
+ value >>= bias;
+ shift += bias;
+ }
+ else
+ {
+ int zeros = SkCLZ(value << 8);
+ SkASSERT(zeros >= 0 && zeros <= 23);
+ value <<= zeros;
+ shift -= zeros;
+ }
+ // now value is left-aligned to 24 bits
+ SkASSERT((value >> 23) == 1);
+
+ shift += EXP_BIAS;
+ if (shift < 0) // underflow
+ return 0;
+ else
+ {
+ if (shift > 255) // overflow
+ {
+ shift = 255;
+ value = 0x00FFFFFF;
+ }
+ int32_t packed = sign << 31; // set the sign-bit
+ packed |= shift << 23; // store the packed exponent
+ packed |= ((unsigned)(value << 9) >> 9); // clear 24th bit of value (its implied)
+
+#ifdef SK_DEBUG
+ {
+ int n;
+
+ n = SkExtractSign(packed);
+ SkASSERT(n == sign);
+ n = get_unsigned_exp(packed);
+ SkASSERT(n == shift);
+ n = get_unsigned_value(packed);
+ SkASSERT(n == value);
+ }
+#endif
+ return packed;
+ }
+}
+
+int32_t SkFloat::Neg(int32_t packed)
+{
+ if (packed)
+ packed = packed ^ (1 << 31);
+ return packed;
+}
+
+int32_t SkFloat::Add(int32_t packed_a, int32_t packed_b)
+{
+ if (packed_a == 0)
+ return packed_b;
+ if (packed_b == 0)
+ return packed_a;
+
+ int exp_a = get_unsigned_exp(packed_a);
+ int exp_b = get_unsigned_exp(packed_b);
+ int exp_diff = exp_a - exp_b;
+
+ int shift_a = 0, shift_b = 0;
+ int exp;
+
+ if (exp_diff >= 0)
+ {
+ if (exp_diff > 24) // B is too small to contribute
+ return packed_a;
+ shift_b = exp_diff;
+ exp = exp_a;
+ }
+ else
+ {
+ exp_diff = -exp_diff;
+ if (exp_diff > 24) // A is too small to contribute
+ return packed_b;
+ shift_a = exp_diff;
+ exp = exp_b;
+ }
+
+ int value_a = get_signed_value(packed_a) >> shift_a;
+ int value_b = get_signed_value(packed_b) >> shift_b;
+
+ return SkFloat::SetShift(value_a + value_b, exp - EXP_BIAS);
+}
+
+#include "Sk64.h"
+
+static inline int32_t mul24(int32_t a, int32_t b)
+{
+ Sk64 tmp;
+
+ tmp.setMul(a, b);
+ tmp.roundRight(24);
+ return tmp.get32();
+}
+
+int32_t SkFloat::Mul(int32_t packed_a, int32_t packed_b)
+{
+ if (packed_a == 0 || packed_b == 0)
+ return 0;
+
+ int exp_a = get_unsigned_exp(packed_a);
+ int exp_b = get_unsigned_exp(packed_b);
+
+ int value_a = get_signed_value(packed_a);
+ int value_b = get_signed_value(packed_b);
+
+ return SkFloat::SetShift(mul24(value_a, value_b), exp_a + exp_b - 2*EXP_BIAS + 24);
+}
+
+int32_t SkFloat::MulInt(int32_t packed, int n)
+{
+ return Mul(packed, SetShift(n, 0));
+}
+
+int32_t SkFloat::Div(int32_t packed_n, int32_t packed_d)
+{
+ SkASSERT(packed_d != 0);
+
+ if (packed_n == 0)
+ return 0;
+
+ int exp_n = get_unsigned_exp(packed_n);
+ int exp_d = get_unsigned_exp(packed_d);
+
+ int value_n = get_signed_value(packed_n);
+ int value_d = get_signed_value(packed_d);
+
+ return SkFloat::SetShift(SkDivBits(value_n, value_d, 24), exp_n - exp_d - 24);
+}
+
+int32_t SkFloat::DivInt(int32_t packed, int n)
+{
+ return Div(packed, SetShift(n, 0));
+}
+
+int32_t SkFloat::Invert(int32_t packed)
+{
+ return Div(packed, SetShift(1, 0));
+}
+
+int32_t SkFloat::Sqrt(int32_t packed)
+{
+ if (packed < 0)
+ {
+ SkASSERT(!"can't sqrt a negative number");
+ return 0;
+ }
+
+ int exp = get_unsigned_exp(packed);
+ int value = get_unsigned_value(packed);
+
+ int nexp = exp - EXP_BIAS;
+ int root = SkSqrtBits(value << (nexp & 1), 26);
+ nexp >>= 1;
+ return SkFloat::SetShift(root, nexp - 11);
+}
+
+#if defined _WIN32 && _MSC_VER >= 1300 // disable warning : unreachable code
+#pragma warning ( push )
+#pragma warning ( disable : 4702 )
+#endif
+
+int32_t SkFloat::CubeRoot(int32_t packed)
+{
+ sk_throw();
+ return 0;
+}
+
+#if defined _WIN32 && _MSC_VER >= 1300
+#pragma warning ( pop )
+#endif
+
+static inline int32_t clear_high_bit(int32_t n)
+{
+ return ((uint32_t)(n << 1)) >> 1;
+}
+
+static inline int int_sign(int32_t a, int32_t b)
+{
+ return a > b ? 1 : (a < b ? -1 : 0);
+}
+
+int SkFloat::Cmp(int32_t packed_a, int32_t packed_b)
+{
+ packed_a = SkApplySign(clear_high_bit(packed_a), SkExtractSign(packed_a));
+ packed_b = SkApplySign(clear_high_bit(packed_b), SkExtractSign(packed_b));
+
+ return int_sign(packed_a, packed_b);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+#include "SkRandom.h"
+#ifdef SK_CAN_USE_FLOAT
+ #include "SkFloatingPoint.h"
+#endif
+
+void SkFloat::UnitTest()
+{
+#ifdef SK_SUPPORT_UNITTEST
+ SkFloat a, b, c, d;
+ int n;
+
+ a.setZero();
+ n = a.getInt();
+ SkASSERT(n == 0);
+
+ b.setInt(5);
+ n = b.getInt();
+ SkASSERT(n == 5);
+
+ c.setInt(-3);
+ n = c.getInt();
+ SkASSERT(n == -3);
+
+ d.setAdd(c, b);
+ SkDebugf("SkFloat: %d + %d = %d\n", c.getInt(), b.getInt(), d.getInt());
+
+ SkRandom rand;
+
+#ifdef SK_CAN_USE_FLOAT
+ int i;
+ for (i = 0; i < 1000; i++)
+ {
+ float fa, fb;
+ int aa = rand.nextS() >> 14;
+ int bb = rand.nextS() >> 14;
+ a.setInt(aa);
+ b.setInt(bb);
+ SkASSERT(a.getInt() == aa);
+ SkASSERT(b.getInt() == bb);
+
+ c.setAdd(a, b);
+ int cc = c.getInt();
+ SkASSERT(cc == aa + bb);
+
+ c.setSub(a, b);
+ cc = c.getInt();
+ SkASSERT(cc == aa - bb);
+
+ aa >>= 5;
+ bb >>= 5;
+ a.setInt(aa);
+ b.setInt(bb);
+ c.setMul(a, b);
+ cc = c.getInt();
+ SkASSERT(cc == aa * bb);
+ /////////////////////////////////////
+
+ aa = rand.nextS() >> 11;
+ a.setFixed(aa);
+ cc = a.getFixed();
+ SkASSERT(aa == cc);
+
+ bb = rand.nextS() >> 11;
+ b.setFixed(bb);
+ cc = b.getFixed();
+ SkASSERT(bb == cc);
+
+ cc = SkFixedMul(aa, bb);
+ c.setMul(a, b);
+ SkFixed dd = c.getFixed();
+ int diff = cc - dd;
+ SkASSERT(SkAbs32(diff) <= 1);
+
+ fa = (float)aa / 65536.0f;
+ fb = (float)bb / 65536.0f;
+ a.assertEquals(fa);
+ b.assertEquals(fb);
+ fa = a.getFloat();
+ fb = b.getFloat();
+
+ c.assertEquals(fa * fb, 1);
+
+ c.setDiv(a, b);
+ cc = SkFixedDiv(aa, bb);
+ dd = c.getFixed();
+ diff = cc - dd;
+ SkASSERT(SkAbs32(diff) <= 3);
+
+ c.assertEquals(fa / fb, 1);
+
+ SkASSERT((aa == bb) == (a == b));
+ SkASSERT((aa != bb) == (a != b));
+ SkASSERT((aa < bb) == (a < b));
+ SkASSERT((aa <= bb) == (a <= b));
+ SkASSERT((aa > bb) == (a > b));
+ SkASSERT((aa >= bb) == (a >= b));
+
+ if (aa < 0)
+ {
+ aa = -aa;
+ fa = -fa;
+ }
+ a.setFixed(aa);
+ c.setSqrt(a);
+ cc = SkFixedSqrt(aa);
+ dd = c.getFixed();
+ SkASSERT(dd == cc);
+
+ c.assertEquals(sk_float_sqrt(fa), 2);
+
+ // cuberoot
+#if 0
+ a.setInt(1);
+ a.cubeRoot();
+ a.assertEquals(1.0f, 0);
+ a.setInt(8);
+ a.cubeRoot();
+ a.assertEquals(2.0f, 0);
+ a.setInt(27);
+ a.cubeRoot();
+ a.assertEquals(3.0f, 0);
+#endif
+ }
+#endif
+#endif
+}
+
+#endif
diff --git a/src/core/SkFloat.h b/src/core/SkFloat.h
new file mode 100644
index 0000000000..31aaeedee2
--- /dev/null
+++ b/src/core/SkFloat.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2006-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkFloat_DEFINED
+#define SkFloat_DEFINED
+
+#include "SkFixed.h"
+
+class SkFloat {
+public:
+ SkFloat() {}
+
+ void setZero() { fPacked = 0; }
+// void setShift(int value, int shift) { fPacked = SetShift(value, shift); }
+ void setInt(int value) { fPacked = SetShift(value, 0); }
+ void setFixed(SkFixed value) { fPacked = SetShift(value, -16); }
+ void setFract(SkFract value) { fPacked = SetShift(value, -30); }
+
+// int getShift(int shift) const { return GetShift(fPacked, shift); }
+ int getInt() const { return GetShift(fPacked, 0); }
+ SkFixed getFixed() const { return GetShift(fPacked, -16); }
+ SkFract getFract() const { return GetShift(fPacked, -30); }
+
+ void abs() { fPacked = Abs(fPacked); }
+ void negate() { fPacked = Neg(fPacked); }
+
+ void shiftLeft(int bits) { fPacked = Shift(fPacked, bits); }
+ void setShiftLeft(const SkFloat& a, int bits) { fPacked = Shift(a.fPacked, bits); }
+
+ void shiftRight(int bits) { fPacked = Shift(fPacked, -bits); }
+ void setShiftRight(const SkFloat& a, int bits) { fPacked = Shift(a.fPacked, -bits); }
+
+ void add(const SkFloat& a) { fPacked = Add(fPacked, a.fPacked); }
+ void setAdd(const SkFloat& a, const SkFloat& b) { fPacked = Add(a.fPacked, b.fPacked); }
+
+ void sub(const SkFloat& a) { fPacked = Add(fPacked, Neg(a.fPacked)); }
+ void setSub(const SkFloat& a, const SkFloat& b) { fPacked = Add(a.fPacked, Neg(b.fPacked)); }
+
+ void mul(const SkFloat& a) { fPacked = Mul(fPacked, a.fPacked); }
+ void setMul(const SkFloat& a, const SkFloat& b) { fPacked = Mul(a.fPacked, b.fPacked); }
+
+ void div(const SkFloat& a) { fPacked = Div(fPacked, a.fPacked); }
+ void setDiv(const SkFloat& a, const SkFloat& b) { fPacked = Div(a.fPacked, b.fPacked); }
+
+ void sqrt() { fPacked = Sqrt(fPacked); }
+ void setSqrt(const SkFloat& a) { fPacked = Sqrt(a.fPacked); }
+ void cubeRoot() { fPacked = CubeRoot(fPacked); }
+ void setCubeRoot(const SkFloat& a) { fPacked = CubeRoot(a.fPacked); }
+
+ friend bool operator==(const SkFloat& a, const SkFloat& b) { return a.fPacked == b.fPacked; }
+ friend bool operator!=(const SkFloat& a, const SkFloat& b) { return a.fPacked != b.fPacked; }
+ friend bool operator<(const SkFloat& a, const SkFloat& b) { return Cmp(a.fPacked, b.fPacked) < 0; }
+ friend bool operator<=(const SkFloat& a, const SkFloat& b) { return Cmp(a.fPacked, b.fPacked) <= 0; }
+ friend bool operator>(const SkFloat& a, const SkFloat& b) { return Cmp(a.fPacked, b.fPacked) > 0; }
+ friend bool operator>=(const SkFloat& a, const SkFloat& b) { return Cmp(a.fPacked, b.fPacked) >= 0; }
+
+#ifdef SK_DEBUG
+ static void UnitTest();
+
+ void assertEquals(float f, int tolerance = 0)
+ {
+ union {
+ float fFloat;
+ int32_t fPacked;
+ } tmp;
+
+ tmp.fFloat = f;
+ int d = tmp.fPacked - fPacked;
+ SkASSERT(SkAbs32(d) <= tolerance);
+ }
+ float getFloat() const
+ {
+ union {
+ float fFloat;
+ int32_t fPacked;
+ } tmp;
+
+ tmp.fPacked = fPacked;
+ return tmp.fFloat;
+ }
+#endif
+
+private:
+ int32_t fPacked;
+
+ SkFloat(int32_t packed) : fPacked(fPacked) {}
+
+public:
+ static int GetShift(int32_t packed, int shift);
+ static int32_t SetShift(int value, int shift);
+ static int32_t Neg(int32_t);
+ static int32_t Abs(int32_t packed) { return (uint32_t)(packed << 1) >> 1; }
+ static int32_t Shift(int32_t, int bits);
+ static int32_t Add(int32_t, int32_t);
+ static int32_t Mul(int32_t, int32_t);
+ static int32_t MulInt(int32_t, int);
+ static int32_t Div(int32_t, int32_t);
+ static int32_t DivInt(int32_t, int);
+ static int32_t Invert(int32_t);
+ static int32_t Sqrt(int32_t);
+ static int32_t CubeRoot(int32_t);
+ static int Cmp(int32_t, int32_t);
+};
+
+#endif
diff --git a/src/core/SkFloatBits.cpp b/src/core/SkFloatBits.cpp
new file mode 100644
index 0000000000..1f15df2c03
--- /dev/null
+++ b/src/core/SkFloatBits.cpp
@@ -0,0 +1,205 @@
+#include "SkFloatBits.h"
+#include "SkMath.h"
+
+/******************************************************************************
+ SkFloatBits_toInt[Floor, Round, Ceil] are identical except for what they
+ do right before they return ... >> exp;
+ Floor - adds nothing
+ Round - adds 1 << (exp - 1)
+ Ceil - adds (1 << exp) - 1
+
+ Floor and Cast are very similar, but Cast applies its sign after all other
+ computations on value. Also, Cast does not need to check for negative zero,
+ as that value (0x80000000) "does the right thing" for Ceil. Note that it
+ doesn't for Floor/Round/Ceil, hence the explicit check.
+******************************************************************************/
+
+#define EXP_BIAS (127+23)
+#define MATISSA_MAGIC_BIG (1 << 23)
+
+static inline int unpack_exp(uint32_t packed) {
+ return (packed << 1 >> 24);
+}
+
+#if 0
+// the ARM compiler generates an extra BIC, so I use the dirty version instead
+static inline int unpack_matissa(uint32_t packed) {
+ // we could mask with 0x7FFFFF, but that is harder for ARM to encode
+ return (packed & ~0xFF000000) | MATISSA_MAGIC_BIG;
+}
+#endif
+
+// returns the low 24-bits, so we need to OR in the magic_bit afterwards
+static inline int unpack_matissa_dirty(uint32_t packed) {
+ return packed & ~0xFF000000;
+}
+
+// same as (int)float
+int32_t SkFloatBits_toIntCast(int32_t packed) {
+ int exp = unpack_exp(packed) - EXP_BIAS;
+ int value = unpack_matissa_dirty(packed) | MATISSA_MAGIC_BIG;
+
+ if (exp >= 0) {
+ if (exp > 7) { // overflow
+ value = SK_MaxS32;
+ } else {
+ value <<= exp;
+ }
+ } else {
+ exp = -exp;
+ if (exp > 25) { // underflow
+ exp = 25;
+ }
+ value >>= exp;
+ }
+ return SkApplySign(value, SkExtractSign(packed));
+}
+
+// same as (int)floor(float)
+int32_t SkFloatBits_toIntFloor(int32_t packed) {
+ // curse you negative 0
+ if ((packed << 1) == 0) {
+ return 0;
+ }
+
+ int exp = unpack_exp(packed) - EXP_BIAS;
+ int value = unpack_matissa_dirty(packed) | MATISSA_MAGIC_BIG;
+
+ if (exp >= 0) {
+ if (exp > 7) { // overflow
+ value = SK_MaxS32;
+ } else {
+ value <<= exp;
+ }
+ // apply the sign after we check for overflow
+ return SkApplySign(value, SkExtractSign(packed));
+ } else {
+ // apply the sign before we right-shift
+ value = SkApplySign(value, SkExtractSign(packed));
+ exp = -exp;
+ if (exp > 25) { // underflow
+ exp = 25;
+ }
+ // int add = 0;
+ return value >> exp;
+ }
+}
+
+// same as (int)floor(float + 0.5)
+int32_t SkFloatBits_toIntRound(int32_t packed) {
+ // curse you negative 0
+ if ((packed << 1) == 0) {
+ return 0;
+ }
+
+ int exp = unpack_exp(packed) - EXP_BIAS;
+ int value = unpack_matissa_dirty(packed) | MATISSA_MAGIC_BIG;
+
+ if (exp >= 0) {
+ if (exp > 7) { // overflow
+ value = SK_MaxS32;
+ } else {
+ value <<= exp;
+ }
+ // apply the sign after we check for overflow
+ return SkApplySign(value, SkExtractSign(packed));
+ } else {
+ // apply the sign before we right-shift
+ value = SkApplySign(value, SkExtractSign(packed));
+ exp = -exp;
+ if (exp > 25) { // underflow
+ exp = 25;
+ }
+ int add = 1 << (exp - 1);
+ return (value + add) >> exp;
+ }
+}
+
+// same as (int)ceil(float)
+int32_t SkFloatBits_toIntCeil(int32_t packed) {
+ // curse you negative 0
+ if ((packed << 1) == 0) {
+ return 0;
+ }
+
+ int exp = unpack_exp(packed) - EXP_BIAS;
+ int value = unpack_matissa_dirty(packed) | MATISSA_MAGIC_BIG;
+
+ if (exp >= 0) {
+ if (exp > 7) { // overflow
+ value = SK_MaxS32;
+ } else {
+ value <<= exp;
+ }
+ // apply the sign after we check for overflow
+ return SkApplySign(value, SkExtractSign(packed));
+ } else {
+ // apply the sign before we right-shift
+ value = SkApplySign(value, SkExtractSign(packed));
+ exp = -exp;
+ if (exp > 25) { // underflow
+ exp = 25;
+ }
+ int add = (1 << exp) - 1;
+ return (value + add) >> exp;
+ }
+}
+
+float SkFloatBits_intToFloatNative(int x);
+float SkFloatBits_intToFloatNative(int x) {
+ return x;
+}
+
+float SkIntToFloatCast(int32_t value) {
+ if (0 == value) {
+ return 0;
+ }
+
+ int shift = EXP_BIAS;
+
+ // record the sign and make value positive
+ int sign = SkExtractSign(value);
+ value = SkApplySign(value, sign);
+
+ if (value >> 24) { // value is too big (has more than 24 bits set)
+ int bias = 8 - SkCLZ(value);
+ SkDebugf("value = %d, bias = %d\n", value, bias);
+ SkASSERT(bias > 0 && bias < 8);
+ value >>= bias; // need to round?
+ shift += bias;
+ } else {
+ int zeros = SkCLZ(value << 8);
+ SkASSERT(zeros >= 0 && zeros <= 23);
+ value <<= zeros;
+ shift -= zeros;
+ }
+
+ // now value is left-aligned to 24 bits
+ SkASSERT((value >> 23) == 1);
+ SkASSERT(shift >= 0 && shift <= 255);
+
+ SkFloatIntUnion data;
+ data.fSignBitInt = (sign << 31) | (shift << 23) | (value & ~MATISSA_MAGIC_BIG);
+ return data.fFloat;
+}
+
+float SkIntToFloatCast_NoOverflowCheck(int32_t value) {
+ if (0 == value) {
+ return 0;
+ }
+
+ int shift = EXP_BIAS;
+
+ // record the sign and make value positive
+ int sign = SkExtractSign(value);
+ value = SkApplySign(value, sign);
+
+ int zeros = SkCLZ(value << 8);
+ value <<= zeros;
+ shift -= zeros;
+
+ SkFloatIntUnion data;
+ data.fSignBitInt = (sign << 31) | (shift << 23) | (value & ~MATISSA_MAGIC_BIG);
+ return data.fFloat;
+}
+
diff --git a/src/core/SkGeometry.cpp b/src/core/SkGeometry.cpp
new file mode 100644
index 0000000000..4f22e92b88
--- /dev/null
+++ b/src/core/SkGeometry.cpp
@@ -0,0 +1,1072 @@
+/* libs/graphics/sgl/SkGeometry.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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
+
+////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_SCALAR_IS_FIXED
+ 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
+ static int is_not_monotonic(float a, float b, float c)
+ {
+ float ab = a - b;
+ float bc = b - c;
+ if (ab < 0)
+ bc = -bc;
+ return ab == 0 || bc < 0;
+ }
+#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)
+{
+ SkASSERT(ratio);
+
+ if (numer < 0)
+ {
+ numer = -numer;
+ denom = -denom;
+ }
+
+ if (denom == 0 || numer == 0 || numer >= denom)
+ return 0;
+
+ 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])
+{
+ SkASSERT(roots);
+
+ if (A == 0)
+ return valid_unit_divide(-C, B, roots);
+
+ SkScalar* r = 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 SkScalarMulAdd(SkScalarMulAdd(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 * SkScalarMulAdd(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])
+{
+ SkASSERT(src);
+ SkASSERT(dst);
+
+#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 a = src[0].fY;
+ SkScalar b = src[1].fY;
+ SkScalar c = src[2].fY;
+
+ if (is_not_monotonic(a, b, c))
+ {
+ SkScalar tValue;
+ if (valid_unit_divide(a - b, a - b - b + c, &tValue))
+ {
+ SkChopQuadAt(src, dst, tValue);
+ flatten_double_quad_extrema(&dst[0].fY);
+ return 1;
+ }
+ // if we get here, we need to force dst to be monotonic, even though
+ // we couldn't compute a unit_divide value (probably underflow).
+ b = SkScalarAbs(a - b) < SkScalarAbs(b - c) ? a : c;
+ }
+ dst[0].set(src[0].fX, a);
+ dst[1].set(src[1].fX, b);
+ dst[2].set(src[2].fX, c);
+ return 0;
+}
+
+// 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 SkScalarMulAdd(SkScalarMulAdd(SkScalarMulAdd(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 SkScalarMulAdd(SkScalarMulAdd(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 SkScalarMulAdd(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 == null, 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 == NULL)
+ 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 within [0, 1)
+*/
+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. Should only return false if the computed pos
+ is the start of the curve (i.e. root == 0)
+*/
+static bool quad_pt2OffCurve(const SkPoint quad[3], SkScalar x, SkScalar y, SkPoint* offCurve)
+{
+ const SkScalar* base;
+ SkScalar value;
+
+ if (SkScalarAbs(x) < SkScalarAbs(y)) {
+ base = &quad[0].fX;
+ value = x;
+ } else {
+ base = &quad[0].fY;
+ value = y;
+ }
+
+ // note: this returns 0 if it thinks value is out of range, meaning the
+ // root might return something outside of [0, 1)
+ SkScalar t = quad_solve(base[0], base[2], base[4], value);
+
+ if (t > 0)
+ {
+ SkPoint tmp[5];
+ SkChopQuadAt(quad, tmp, t);
+ *offCurve = tmp[1];
+ return true;
+ } else {
+ /* t == 0 means either the value triggered a root outside of [0, 1)
+ For our purposes, we can ignore the <= 0 roots, but we want to
+ catch the >= 1 roots (which given our caller, will basically mean
+ a root of 1, give-or-take numerical instability). If we are in the
+ >= 1 case, return the existing offCurve point.
+
+ The test below checks to see if we are close to the "end" of the
+ curve (near base[4]). Rather than specifying a tolerance, I just
+ check to see if value is on to the right/left of the middle point
+ (depending on the direction/sign of the end points).
+ */
+ if ((base[0] < base[4] && value > base[2]) ||
+ (base[0] > base[4] && value < base[2])) // should root have been 1
+ {
+ *offCurve = quad[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[])
+{
+ // rotate by x,y so that uStart is (1.0)
+ SkScalar x = SkPoint::DotProduct(uStart, uStop);
+ SkScalar y = SkPoint::CrossProduct(uStart, uStop);
+
+ SkScalar absX = SkScalarAbs(x);
+ SkScalar absY = SkScalarAbs(y);
+
+ int pointCount;
+
+ // check for (effectively) coincident vectors
+ // this can happen if our angle is nearly 0 or nearly 180 (y == 0)
+ // ... we use the dot-prod to distinguish between 0 and 180 (x > 0)
+ if (absY <= SK_ScalarNearlyZero && x > 0 &&
+ ((y >= 0 && kCW_SkRotationDirection == dir) ||
+ (y <= 0 && kCCW_SkRotationDirection == dir))) {
+
+ // just return the start-point
+ quadPoints[0].set(SK_Scalar1, 0);
+ pointCount = 1;
+ } else {
+ if (dir == kCCW_SkRotationDirection)
+ y = -y;
+
+ // what octant (quadratic curve) is [xy] in?
+ int oct = 0;
+ bool sameSign = true;
+
+ if (0 == y)
+ {
+ oct = 4; // 180
+ SkASSERT(SkScalarAbs(x + SK_Scalar1) <= SK_ScalarNearlyZero);
+ }
+ else if (0 == x)
+ {
+ SkASSERT(absY - SK_Scalar1 <= SK_ScalarNearlyZero);
+ if (y > 0)
+ oct = 2; // 90
+ else
+ oct = 6; // 270
+ }
+ else
+ {
+ if (y < 0)
+ oct += 4;
+ if ((x < 0) != (y < 0))
+ {
+ oct += 2;
+ sameSign = false;
+ }
+ if ((absX < absY) == sameSign)
+ 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;
+ }
+ pointCount = wholeCount + 1;
+ }
+
+ // now handle counter-clockwise and the initial unitStart rotation
+ SkMatrix matrix;
+ matrix.setSinCos(uStart.fY, uStart.fX);
+ if (dir == kCCW_SkRotationDirection) {
+ matrix.preScale(SK_Scalar1, -SK_Scalar1);
+ }
+ if (userMatrix) {
+ matrix.postConcat(*userMatrix);
+ }
+ matrix.mapPoints(quadPoints, pointCount);
+ return pointCount;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////
+
+#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/src/core/SkGeometry.h b/src/core/SkGeometry.h
new file mode 100644
index 0000000000..571159fd07
--- /dev/null
+++ b/src/core/SkGeometry.h
@@ -0,0 +1,163 @@
+/* libs/graphics/sgl/SkGeometry.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#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 = NULL);
+void SkEvalQuadAtHalf(const SkPoint src[3], SkPoint* pt, SkVector* tangent = NULL);
+
+/** 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 == null, 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 == null, 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* locOrNull, SkVector* tangentOrNull, SkVector* curvatureOrNull);
+
+/** 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 == null, 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] = NULL);
+
+///////////////////////////////////////////////////////////////////////////////////////////
+
+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 null, 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/src/core/SkGlobals.cpp b/src/core/SkGlobals.cpp
new file mode 100644
index 0000000000..bc72b976a7
--- /dev/null
+++ b/src/core/SkGlobals.cpp
@@ -0,0 +1,92 @@
+/* libs/graphics/sgl/SkGlobals.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkGlobals.h"
+#include "SkThread.h"
+
+SkGlobals::Rec::~Rec()
+{
+}
+
+SkGlobals::Rec* SkGlobals::Find(uint32_t 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 == NULL) // no create proc, just return not found
+ return NULL;
+
+ // 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 == NULL && (rec = create_proc()) != NULL)
+ {
+ 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 = NULL;
+ bootstrap.fMutex.release();
+}
+
+
diff --git a/src/core/SkGlyphCache.cpp b/src/core/SkGlyphCache.cpp
new file mode 100644
index 0000000000..6b214dfc65
--- /dev/null
+++ b/src/core/SkGlyphCache.cpp
@@ -0,0 +1,662 @@
+/* libs/graphics/sgl/SkGlyphCache.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkGlyphCache.h"
+#include "SkFontHost.h"
+#include "SkPaint.h"
+#include "SkTemplates.h"
+
+#define SPEW_PURGE_STATUS
+//#define USE_CACHE_HASH
+//#define RECORD_HASH_EFFICIENCY
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef RECORD_HASH_EFFICIENCY
+ static uint32_t gHashSuccess;
+ static uint32_t gHashCollision;
+
+ static void RecordHashSuccess() {
+ gHashSuccess += 1;
+ }
+
+ static void RecordHashCollisionIf(bool pred) {
+ if (pred) {
+ gHashCollision += 1;
+
+ uint32_t total = gHashSuccess + gHashCollision;
+ SkDebugf("Font Cache Hash success rate: %d%%\n",
+ 100 * gHashSuccess / total);
+ }
+ }
+#else
+ #define RecordHashSuccess() (void)0
+ #define RecordHashCollisionIf(pred) (void)0
+#endif
+#define RecordHashCollision() RecordHashCollisionIf(true)
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define kMinGlphAlloc (sizeof(SkGlyph) * 64)
+#define kMinImageAlloc (24 * 64) // should be pointsize-dependent
+
+#define METRICS_RESERVE_COUNT 128 // so we don't grow this array a lot
+
+SkGlyphCache::SkGlyphCache(const SkDescriptor* desc)
+ : fGlyphAlloc(kMinGlphAlloc), fImageAlloc(kMinImageAlloc) {
+ fPrev = fNext = NULL;
+
+ fDesc = desc->copy();
+ fScalerContext = SkScalerContext::Create(desc);
+ fScalerContext->getFontMetrics(NULL, &fFontMetricsY);
+
+ // init to 0 so that all of the pointers will be null
+ memset(fGlyphHash, 0, sizeof(fGlyphHash));
+ // init with 0xFF so that the charCode field will be -1, which is invalid
+ memset(fCharToGlyphHash, 0xFF, sizeof(fCharToGlyphHash));
+
+ fMemoryUsed = sizeof(*this) + kMinGlphAlloc + kMinImageAlloc;
+
+ fGlyphArray.setReserve(METRICS_RESERVE_COUNT);
+
+ fMetricsCount = 0;
+ fAdvanceCount = 0;
+ fAuxProcList = NULL;
+}
+
+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);
+ this->invokeAndRemoveAuxProcs();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+class AutoCheckForNull {
+public:
+ AutoCheckForNull(const SkTDArray<SkGlyph*>& array) : fArray(array) {
+ for (int i = 0; i < array.count(); i++)
+ SkASSERT(array[i]);
+ }
+ ~AutoCheckForNull() {
+ const SkTDArray<SkGlyph*>& array = fArray;
+ for (int i = 0; i < array.count(); i++) {
+ SkASSERT(array[i]);
+ }
+ }
+private:
+ const SkTDArray<SkGlyph*>& fArray;
+};
+#define VALIDATE() AutoCheckForNull acfn(fGlyphArray)
+#else
+#define VALIDATE()
+#endif
+
+uint16_t SkGlyphCache::unicharToGlyph(SkUnichar charCode) {
+ VALIDATE();
+ uint32_t id = SkGlyph::MakeID(charCode);
+ const CharGlyphRec& rec = fCharToGlyphHash[ID2HashIndex(id)];
+
+ if (rec.fID == id) {
+ return rec.fGlyph->getGlyphID();
+ } else {
+ return fScalerContext->charToGlyphID(charCode);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+const SkGlyph& SkGlyphCache::getUnicharAdvance(SkUnichar charCode) {
+ VALIDATE();
+ uint32_t id = SkGlyph::MakeID(charCode);
+ CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)];
+
+ if (rec->fID != id) {
+ // this ID is based on the UniChar
+ rec->fID = id;
+ // this ID is based on the glyph index
+ id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode));
+ rec->fGlyph = this->lookupMetrics(id, kJustAdvance_MetricsType);
+ }
+ return *rec->fGlyph;
+}
+
+const SkGlyph& SkGlyphCache::getGlyphIDAdvance(uint16_t glyphID) {
+ VALIDATE();
+ uint32_t id = SkGlyph::MakeID(glyphID);
+ unsigned index = ID2HashIndex(id);
+ SkGlyph* glyph = fGlyphHash[index];
+
+ if (NULL == glyph || glyph->fID != id) {
+ glyph = this->lookupMetrics(glyphID, kJustAdvance_MetricsType);
+ fGlyphHash[index] = glyph;
+ }
+ return *glyph;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode) {
+ VALIDATE();
+ uint32_t id = SkGlyph::MakeID(charCode);
+ CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)];
+
+ if (rec->fID != id) {
+ RecordHashCollisionIf(rec->fGlyph != NULL);
+ // this ID is based on the UniChar
+ rec->fID = id;
+ // this ID is based on the glyph index
+ id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode));
+ rec->fGlyph = this->lookupMetrics(id, kFull_MetricsType);
+ } else {
+ RecordHashSuccess();
+ if (rec->fGlyph->isJustAdvance()) {
+ fScalerContext->getMetrics(rec->fGlyph);
+ }
+ }
+ SkASSERT(rec->fGlyph->isFullMetrics());
+ return *rec->fGlyph;
+}
+
+const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode,
+ SkFixed x, SkFixed y) {
+ VALIDATE();
+ uint32_t id = SkGlyph::MakeID(charCode, x, y);
+ CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)];
+
+ if (rec->fID != id) {
+ RecordHashCollisionIf(rec->fGlyph != NULL);
+ // this ID is based on the UniChar
+ rec->fID = id;
+ // this ID is based on the glyph index
+ id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode), x, y);
+ rec->fGlyph = this->lookupMetrics(id, kFull_MetricsType);
+ } else {
+ RecordHashSuccess();
+ if (rec->fGlyph->isJustAdvance()) {
+ fScalerContext->getMetrics(rec->fGlyph);
+ }
+ }
+ SkASSERT(rec->fGlyph->isFullMetrics());
+ return *rec->fGlyph;
+}
+
+const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID) {
+ VALIDATE();
+ uint32_t id = SkGlyph::MakeID(glyphID);
+ unsigned index = ID2HashIndex(id);
+ SkGlyph* glyph = fGlyphHash[index];
+
+ if (NULL == glyph || glyph->fID != id) {
+ RecordHashCollisionIf(glyph != NULL);
+ glyph = this->lookupMetrics(glyphID, kFull_MetricsType);
+ fGlyphHash[index] = glyph;
+ } else {
+ RecordHashSuccess();
+ if (glyph->isJustAdvance()) {
+ fScalerContext->getMetrics(glyph);
+ }
+ }
+ SkASSERT(glyph->isFullMetrics());
+ return *glyph;
+}
+
+const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID,
+ SkFixed x, SkFixed y) {
+ VALIDATE();
+ uint32_t id = SkGlyph::MakeID(glyphID, x, y);
+ unsigned index = ID2HashIndex(id);
+ SkGlyph* glyph = fGlyphHash[index];
+
+ if (NULL == glyph || glyph->fID != id) {
+ RecordHashCollisionIf(glyph != NULL);
+ glyph = this->lookupMetrics(id, kFull_MetricsType);
+ fGlyphHash[index] = glyph;
+ } else {
+ RecordHashSuccess();
+ if (glyph->isJustAdvance()) {
+ fScalerContext->getMetrics(glyph);
+ }
+ }
+ SkASSERT(glyph->isFullMetrics());
+ return *glyph;
+}
+
+SkGlyph* SkGlyphCache::lookupMetrics(uint32_t id, MetricsType mtype) {
+ 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]->fID < id) {
+ lo = mid + 1;
+ } else {
+ hi = mid;
+ }
+ }
+ glyph = gptr[hi];
+ if (glyph->fID == id) {
+ if (kFull_MetricsType == mtype && glyph->isJustAdvance()) {
+ fScalerContext->getMetrics(glyph);
+ }
+ return glyph;
+ }
+
+ // check if we need to bump hi before falling though to the allocator
+ if (glyph->fID < id) {
+ hi += 1;
+ }
+ }
+
+ // not found, but hi tells us where to inser the new glyph
+ fMemoryUsed += sizeof(SkGlyph);
+
+ glyph = (SkGlyph*)fGlyphAlloc.alloc(sizeof(SkGlyph),
+ SkChunkAlloc::kThrow_AllocFailType);
+ glyph->fID = id;
+ glyph->fImage = NULL;
+ glyph->fPath = NULL;
+ *fGlyphArray.insert(hi) = glyph;
+
+ if (kJustAdvance_MetricsType == mtype) {
+ fScalerContext->getAdvance(glyph);
+ fAdvanceCount += 1;
+ } else {
+ SkASSERT(kFull_MetricsType == mtype);
+ fScalerContext->getMetrics(glyph);
+ fMetricsCount += 1;
+ }
+
+ return glyph;
+}
+
+const void* SkGlyphCache::findImage(const SkGlyph& glyph) {
+ if (glyph.fWidth) {
+ if (glyph.fImage == NULL) {
+ size_t size = glyph.computeImageSize();
+ const_cast<SkGlyph&>(glyph).fImage = fImageAlloc.alloc(size,
+ SkChunkAlloc::kReturnNil_AllocFailType);
+ fScalerContext->getImage(glyph);
+ fMemoryUsed += size;
+ }
+ }
+ return glyph.fImage;
+}
+
+const SkPath* SkGlyphCache::findPath(const SkGlyph& glyph) {
+ if (glyph.fWidth) {
+ if (glyph.fPath == NULL) {
+ const_cast<SkGlyph&>(glyph).fPath = SkNEW(SkPath);
+ fScalerContext->getPath(glyph, glyph.fPath);
+ fMemoryUsed += sizeof(SkPath) +
+ glyph.fPath->getPoints(NULL, 0x7FFFFFFF) * sizeof(SkPoint);
+ }
+ }
+ return glyph.fPath;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkGlyphCache::getAuxProcData(void (*proc)(void*), void** dataPtr) const {
+ const AuxProcRec* rec = fAuxProcList;
+ while (rec) {
+ if (rec->fProc == proc) {
+ if (dataPtr) {
+ *dataPtr = rec->fData;
+ }
+ return true;
+ }
+ rec = rec->fNext;
+ }
+ return false;
+}
+
+void SkGlyphCache::setAuxProc(void (*proc)(void*), void* data) {
+ if (proc == NULL) {
+ return;
+ }
+
+ AuxProcRec* rec = fAuxProcList;
+ while (rec) {
+ if (rec->fProc == proc) {
+ rec->fData = data;
+ return;
+ }
+ rec = rec->fNext;
+ }
+ // not found, create a new rec
+ rec = SkNEW(AuxProcRec);
+ rec->fProc = proc;
+ rec->fData = data;
+ rec->fNext = fAuxProcList;
+ fAuxProcList = rec;
+}
+
+void SkGlyphCache::removeAuxProc(void (*proc)(void*)) {
+ AuxProcRec* rec = fAuxProcList;
+ AuxProcRec* prev = NULL;
+ while (rec) {
+ AuxProcRec* next = rec->fNext;
+ if (rec->fProc == proc) {
+ if (prev) {
+ prev->fNext = next;
+ } else {
+ fAuxProcList = next;
+ }
+ SkDELETE(rec);
+ return;
+ }
+ prev = rec;
+ rec = next;
+ }
+}
+
+void SkGlyphCache::invokeAndRemoveAuxProcs() {
+ AuxProcRec* rec = fAuxProcList;
+ while (rec) {
+ rec->fProc(rec->fData);
+ AuxProcRec* next = rec->fNext;
+ SkDELETE(rec);
+ rec = next;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkGlobals.h"
+#include "SkThread.h"
+
+#define SkGlyphCache_GlobalsTag SkSetFourByteTag('g', 'l', 'f', 'c')
+
+#ifdef USE_CACHE_HASH
+ #define HASH_BITCOUNT 6
+ #define HASH_COUNT (1 << HASH_BITCOUNT)
+ #define HASH_MASK (HASH_COUNT - 1)
+
+ static unsigned desc_to_hashindex(const SkDescriptor* desc)
+ {
+ SkASSERT(HASH_MASK < 256); // since our munging reduces to 8 bits
+
+ uint32_t n = *(const uint32_t*)desc; //desc->getChecksum();
+ SkASSERT(n == desc->getChecksum());
+
+ // don't trust that the low bits of checksum vary enough, so...
+ n ^= (n >> 24) ^ (n >> 16) ^ (n >> 8) ^ (n >> 30);
+
+ return n & HASH_MASK;
+ }
+#endif
+
+class SkGlyphCache_Globals : public SkGlobals::Rec {
+public:
+ SkMutex fMutex;
+ SkGlyphCache* fHead;
+ size_t fTotalMemoryUsed;
+#ifdef USE_CACHE_HASH
+ SkGlyphCache* fHash[HASH_COUNT];
+#endif
+
+#ifdef SK_DEBUG
+ void validate() const;
+#else
+ void validate() const {}
+#endif
+};
+
+#ifdef SK_USE_RUNTIME_GLOBALS
+ static SkGlobals::Rec* create_globals() {
+ SkGlyphCache_Globals* rec = SkNEW(SkGlyphCache_Globals);
+ rec->fHead = NULL;
+ rec->fTotalMemoryUsed = 0;
+#ifdef USE_CACHE_HASH
+ memset(rec->fHash, 0, sizeof(rec->fHash));
+#endif
+ 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
+
+void SkGlyphCache::VisitAllCaches(bool (*proc)(SkGlyphCache*, void*),
+ void* context) {
+ SkGlyphCache_Globals& globals = FIND_GC_GLOBALS();
+ SkAutoMutexAcquire ac(globals.fMutex);
+ SkGlyphCache* cache;
+
+ globals.validate();
+
+ for (cache = globals.fHead; cache != NULL; cache = cache->fNext) {
+ if (proc(cache, context)) {
+ break;
+ }
+ }
+
+ globals.validate();
+}
+
+/* This guy calls the visitor from within the mutext lock, so the visitor
+ cannot:
+ - take too much time
+ - try to acquire the mutext again
+ - call a fontscaler (which might call into the cache)
+*/
+SkGlyphCache* SkGlyphCache::VisitCache(const SkDescriptor* desc,
+ bool (*proc)(const SkGlyphCache*, void*),
+ void* context) {
+ SkASSERT(desc);
+
+ SkGlyphCache_Globals& globals = FIND_GC_GLOBALS();
+ SkAutoMutexAcquire ac(globals.fMutex);
+ SkGlyphCache* cache;
+ bool insideMutex = true;
+
+ globals.validate();
+
+#ifdef USE_CACHE_HASH
+ SkGlyphCache** hash = globals.fHash;
+ unsigned index = desc_to_hashindex(desc);
+ cache = hash[index];
+ if (cache && *cache->fDesc == *desc) {
+ cache->detach(&globals.fHead);
+ goto FOUND_IT;
+ }
+#endif
+
+ for (cache = globals.fHead; cache != NULL; cache = cache->fNext) {
+ if (cache->fDesc->equals(*desc)) {
+ cache->detach(&globals.fHead);
+ goto FOUND_IT;
+ }
+ }
+
+ /* Release the mutex now, before we create a new entry (which might have
+ side-effects like trying to access the cache/mutex (yikes!)
+ */
+ ac.release(); // release the mutex now
+ insideMutex = false; // can't use globals anymore
+
+ cache = SkNEW_ARGS(SkGlyphCache, (desc));
+
+FOUND_IT:
+ if (proc(cache, context)) { // stay detached
+ if (insideMutex) {
+ SkASSERT(globals.fTotalMemoryUsed >= cache->fMemoryUsed);
+ globals.fTotalMemoryUsed -= cache->fMemoryUsed;
+#ifdef USE_CACHE_HASH
+ hash[index] = NULL;
+#endif
+ }
+ } else { // reattach
+ if (insideMutex) {
+ cache->attachToHead(&globals.fHead);
+#ifdef USE_CACHE_HASH
+ hash[index] = cache;
+#endif
+ } else {
+ AttachCache(cache);
+ }
+ cache = NULL;
+ }
+ return cache;
+}
+
+void SkGlyphCache::AttachCache(SkGlyphCache* cache) {
+ SkASSERT(cache);
+ SkASSERT(cache->fNext == NULL);
+
+ SkGlyphCache_Globals& globals = GET_GC_GLOBALS();
+ SkAutoMutexAcquire ac(globals.fMutex);
+
+ globals.validate();
+
+ // if we have a fixed budget for our cache, do a purge here
+ {
+ size_t allocated = globals.fTotalMemoryUsed + cache->fMemoryUsed;
+ size_t amountToFree = SkFontHost::ShouldPurgeFontCache(allocated);
+ if (amountToFree)
+ (void)InternalFreeCache(&globals, amountToFree);
+ }
+
+ cache->attachToHead(&globals.fHead);
+ globals.fTotalMemoryUsed += cache->fMemoryUsed;
+
+#ifdef USE_CACHE_HASH
+ unsigned index = desc_to_hashindex(cache->fDesc);
+ SkASSERT(globals.fHash[index] != cache);
+ globals.fHash[index] = cache;
+#endif
+
+ globals.validate();
+}
+
+size_t SkGlyphCache::GetCacheUsed() {
+ SkGlyphCache_Globals& globals = FIND_GC_GLOBALS();
+ SkAutoMutexAcquire ac(globals.fMutex);
+
+ return SkGlyphCache::ComputeMemoryUsed(globals.fHead);
+}
+
+bool SkGlyphCache::SetCacheUsed(size_t bytesUsed) {
+ size_t curr = SkGlyphCache::GetCacheUsed();
+
+ if (curr > bytesUsed) {
+ SkGlyphCache_Globals& globals = FIND_GC_GLOBALS();
+ SkAutoMutexAcquire ac(globals.fMutex);
+
+ return InternalFreeCache(&globals, curr - bytesUsed) > 0;
+ }
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkGlyphCache* SkGlyphCache::FindTail(SkGlyphCache* cache) {
+ if (cache) {
+ while (cache->fNext) {
+ cache = cache->fNext;
+ }
+ }
+ return cache;
+}
+
+size_t SkGlyphCache::ComputeMemoryUsed(const SkGlyphCache* head) {
+ size_t size = 0;
+
+ while (head != NULL) {
+ size += head->fMemoryUsed;
+ head = head->fNext;
+ }
+ return size;
+}
+
+#ifdef SK_DEBUG
+void SkGlyphCache_Globals::validate() const {
+ size_t computed = SkGlyphCache::ComputeMemoryUsed(fHead);
+ if (fTotalMemoryUsed != computed) {
+ printf("total %d, computed %d\n", (int)fTotalMemoryUsed, (int)computed);
+ }
+ SkASSERT(fTotalMemoryUsed == computed);
+}
+#endif
+
+size_t SkGlyphCache::InternalFreeCache(SkGlyphCache_Globals* globals,
+ size_t bytesNeeded) {
+ globals->validate();
+
+ size_t bytesFreed = 0;
+ int count = 0;
+
+ // don't do any "small" purges
+ size_t minToPurge = globals->fTotalMemoryUsed >> 2;
+ if (bytesNeeded < minToPurge)
+ bytesNeeded = minToPurge;
+
+ SkGlyphCache* cache = FindTail(globals->fHead);
+ while (cache != NULL && bytesFreed < bytesNeeded) {
+ SkGlyphCache* prev = cache->fPrev;
+ bytesFreed += cache->fMemoryUsed;
+
+#ifdef USE_CACHE_HASH
+ unsigned index = desc_to_hashindex(cache->fDesc);
+ if (cache == globals->fHash[index]) {
+ globals->fHash[index] = NULL;
+ }
+#endif
+
+ cache->detach(&globals->fHead);
+ SkDELETE(cache);
+ cache = prev;
+ count += 1;
+ }
+
+ SkASSERT(bytesFreed <= globals->fTotalMemoryUsed);
+ globals->fTotalMemoryUsed -= bytesFreed;
+ globals->validate();
+
+#ifdef SPEW_PURGE_STATUS
+ if (count) {
+ SkDebugf("purging %dK from font cache [%d entries]\n",
+ (int)(bytesFreed >> 10), count);
+ }
+#endif
+
+ return bytesFreed;
+}
+
diff --git a/src/core/SkGlyphCache.h b/src/core/SkGlyphCache.h
new file mode 100644
index 0000000000..2462ea506f
--- /dev/null
+++ b/src/core/SkGlyphCache.h
@@ -0,0 +1,274 @@
+/* libs/graphics/sgl/SkGlyphCache.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#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_Globals;
+
+/** \class SkGlyphCache
+
+ This class represents a strike: a specific combination of typeface, size,
+ matrix, etc., and holds the glyphs for that strike. Calling any of the
+ getUnichar.../getGlyphID... methods will return the requested glyph,
+ either instantly if it is already cahced, or by first generating it and then
+ adding it to the strike.
+
+ The strikes are held in a global list, available to all threads. To interact
+ with one, call either VisitCache() or DetachCache().
+*/
+class SkGlyphCache {
+public:
+ /** Returns a glyph with valid fAdvance and fDevKern fields.
+ The remaining fields may be valid, but that is not guaranteed. If you
+ require those, call getUnicharMetrics or getGlyphIDMetrics instead.
+ */
+ const SkGlyph& getUnicharAdvance(SkUnichar);
+ const SkGlyph& getGlyphIDAdvance(uint16_t);
+
+ /** Returns a glyph with all fields valid except fImage and fPath, which
+ may be null. If they are null, call findImage or findPath for those.
+ If they are not null, then they are valid.
+
+ This call is potentially slower than the matching ...Advance call. If
+ you only need the fAdvance/fDevKern fields, call those instead.
+ */
+ const SkGlyph& getUnicharMetrics(SkUnichar);
+ const SkGlyph& getGlyphIDMetrics(uint16_t);
+
+ /** These are variants that take the device position of the glyph. Call
+ these only if you are drawing in subpixel mode. Passing 0, 0 is
+ effectively the same as calling the variants w/o the extra params, tho
+ a tiny bit slower.
+ */
+ const SkGlyph& getUnicharMetrics(SkUnichar, SkFixed x, SkFixed y);
+ const SkGlyph& getGlyphIDMetrics(uint16_t, SkFixed x, SkFixed y);
+
+ /** Return the glyphID for the specified Unichar. If the char has already
+ been seen, use the existing cache entry. If not, ask the scalercontext
+ to compute it for us.
+ */
+ uint16_t unicharToGlyph(SkUnichar);
+
+ /** Return the image associated with the glyph. If it has not been generated
+ this will trigger that.
+ */
+ const void* findImage(const SkGlyph&);
+ /** Return the Path associated with the glyph. If it has not been generated
+ this will trigger that.
+ */
+ const SkPath* findPath(const SkGlyph&);
+
+ /** Return the vertical metrics for this strike.
+ */
+ const SkPaint::FontMetrics& getFontMetricsY() const {
+ return fFontMetricsY;
+ }
+
+ /* AuxProc/Data allow a client to associate data with this cache entry.
+ Multiple clients can use this, as their data is keyed with a function
+ pointer. In addition to serving as a key, the function pointer is called
+ with the data when the glyphcache object is deleted, so the client can
+ cleanup their data as well. NOTE: the auxProc must not try to access
+ this glyphcache in any way, since it may be in the process of being
+ deleted.
+ */
+
+ //! If the proc is found, return true and set *dataPtr to its data
+ bool getAuxProcData(void (*auxProc)(void*), void** dataPtr) const;
+ //! Add a proc/data pair to the glyphcache. proc should be non-null
+ void setAuxProc(void (*auxProc)(void*), void* auxData);
+ //! If found, remove the proc/data pair from the glyphcache (does not
+ // call the proc)
+ void removeAuxProc(void (*auxProc)(void*));
+
+ /** Call proc on all cache entries, stopping early if proc returns true.
+ The proc should not create or delete caches, since it could produce
+ deadlock.
+ */
+ static void VisitAllCaches(bool (*proc)(SkGlyphCache*, void*), void* ctx);
+
+ /** Find a matching cache entry, and call proc() with it. If none is found
+ create a new one. If the proc() returns true, detach the cache and
+ return it, otherwise leave it and return NULL.
+ */
+ static SkGlyphCache* VisitCache(const SkDescriptor* desc,
+ bool (*proc)(const SkGlyphCache*, void*),
+ void* context);
+
+ /** Given a strike that was returned by either VisitCache() or DetachCache()
+ add it back into the global cache list (after which the caller should
+ not reference it anymore.
+ */
+ static void AttachCache(SkGlyphCache*);
+
+ /** Detach a strike from the global cache matching the specified descriptor.
+ Once detached, it can be queried/modified by the current thread, and
+ when finished, be reattached to the global cache with AttachCache().
+ While detached, if another request is made with the same descriptor,
+ a different strike will be generated. This is fine. It does mean we
+ can have more than 1 strike for the same descriptor, but that will
+ eventually get purged, and the win is that different thread will never
+ block each other while a strike is being used.
+ */
+ static SkGlyphCache* DetachCache(const SkDescriptor* desc) {
+ return VisitCache(desc, DetachProc, NULL);
+ }
+
+ /** Return the approximate number of bytes used by the font cache
+ */
+ static size_t GetCacheUsed();
+
+ /** This can be called to purge old font data, in an attempt to free
+ enough bytes such that the font cache is not using more than the
+ specified number of bytes. It is thread-safe, and may be called at
+ any time.
+ Return true if some amount of the cache was purged.
+ */
+ static bool SetCacheUsed(size_t bytesUsed);
+
+private:
+ SkGlyphCache(const SkDescriptor*);
+ ~SkGlyphCache();
+
+ enum MetricsType {
+ kJustAdvance_MetricsType,
+ kFull_MetricsType
+ };
+
+ SkGlyph* lookupMetrics(uint32_t id, MetricsType);
+ static bool DetachProc(const SkGlyphCache*, void*) { return true; }
+
+ void detach(SkGlyphCache** head) {
+ if (fPrev) {
+ fPrev->fNext = fNext;
+ } else {
+ *head = fNext;
+ }
+ if (fNext) {
+ fNext->fPrev = fPrev;
+ }
+ fPrev = fNext = NULL;
+ }
+
+ void attachToHead(SkGlyphCache** head) {
+ SkASSERT(NULL == fPrev && NULL == fNext);
+ if (*head) {
+ (*head)->fPrev = this;
+ fNext = *head;
+ }
+ *head = this;
+ }
+
+ SkGlyphCache* fNext, *fPrev;
+ SkDescriptor* fDesc;
+ SkScalerContext* fScalerContext;
+ SkPaint::FontMetrics fFontMetricsY;
+
+ enum {
+ kHashBits = 8,
+ kHashCount = 1 << kHashBits,
+ kHashMask = kHashCount - 1
+ };
+ SkGlyph* fGlyphHash[kHashCount];
+ SkTDArray<SkGlyph*> fGlyphArray;
+ SkChunkAlloc fGlyphAlloc;
+ SkChunkAlloc fImageAlloc;
+
+ int fMetricsCount, fAdvanceCount;
+
+ struct CharGlyphRec {
+ uint32_t fID; // unichar + subpixel
+ SkGlyph* fGlyph;
+ };
+ // no reason to use the same kHashCount as fGlyphHash, but we do for now
+ CharGlyphRec fCharToGlyphHash[kHashCount];
+
+ enum {
+ // shift so that the top bits fall into kHashBits region
+ kShiftForHashIndex = SkGlyph::kSubShift +
+ SkGlyph::kSubBits*2 -
+ kHashBits
+ };
+
+ static inline unsigned ID2HashIndex(uint32_t id) {
+ return (id ^ (id >> kShiftForHashIndex)) & kHashMask;
+ }
+
+ // used to track (approx) how much ram is tied-up in this cache
+ size_t fMemoryUsed;
+
+ struct AuxProcRec {
+ AuxProcRec* fNext;
+ void (*fProc)(void*);
+ void* fData;
+ };
+ AuxProcRec* fAuxProcList;
+ void invokeAndRemoveAuxProcs();
+
+ // This relies on the caller to have already acquired the mutex to access the global cache
+ static size_t InternalFreeCache(SkGlyphCache_Globals*, size_t bytesNeeded);
+
+ inline static SkGlyphCache* FindTail(SkGlyphCache* head);
+ static size_t ComputeMemoryUsed(const SkGlyphCache* head);
+
+ friend class SkGlyphCache_Globals;
+};
+
+class SkAutoGlyphCache {
+public:
+ SkAutoGlyphCache(SkGlyphCache* cache) : fCache(cache) {}
+ SkAutoGlyphCache(const SkDescriptor* desc)
+ {
+ fCache = SkGlyphCache::DetachCache(desc);
+ }
+ SkAutoGlyphCache(const SkPaint& paint, const SkMatrix* matrix)
+ {
+ fCache = paint.detachCache(matrix);
+ }
+ ~SkAutoGlyphCache()
+ {
+ if (fCache)
+ SkGlyphCache::AttachCache(fCache);
+ }
+
+ SkGlyphCache* getCache() const { return fCache; }
+
+ void release()
+ {
+ if (fCache)
+ {
+ SkGlyphCache::AttachCache(fCache);
+ fCache = NULL;
+ }
+ }
+private:
+ SkGlyphCache* fCache;
+
+ static bool DetachProc(const SkGlyphCache*, void*);
+};
+
+#endif
+
diff --git a/src/core/SkGraphics.cpp b/src/core/SkGraphics.cpp
new file mode 100644
index 0000000000..64fbab90a1
--- /dev/null
+++ b/src/core/SkGraphics.cpp
@@ -0,0 +1,526 @@
+/* libs/graphics/sgl/SkGraphics.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkGraphics.h"
+
+#include "Sk64.h"
+#include "SkBlitter.h"
+#include "SkCanvas.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 "SkRandom.h"
+#include "SkRefCnt.h"
+#include "SkScalerContext.h"
+#include "SkShader.h"
+#include "SkStream.h"
+#include "SkTSearch.h"
+#include "SkTime.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+
+#if 0
+
+#define SK_SORT_TEMPLATE_TYPE int
+#define SK_SORT_TEMPLATE_NAME sort_int
+#define SK_SORT_TEMPLATE_CMP(a, b) ((a) - (b))
+#include "SkSortTemplate.h"
+
+#define SK_SORT_TEMPLATE_TYPE int*
+#define SK_SORT_TEMPLATE_NAME sort_intptr
+#define SK_SORT_TEMPLATE_CMP(a, b) (*(a) - *(b))
+#include "SkSortTemplate.h"
+
+static void test_sort()
+{
+ int array[] = { 4, 3, 7, 5, 2, 5, 1, 2, 9, 6, 7, 4, 5, 3, 1, 0 };
+ int* ptr[SK_ARRAY_COUNT(array)];
+ int i, N = SK_ARRAY_COUNT(array) - 1;
+
+ for (i = 0; i < N; i++)
+ printf(" %d", array[i]);
+ printf("\n");
+
+ for (i = 0; i < N; i++)
+ ptr[i] = &array[i];
+ sort_intptr(ptr, N);
+ for (i = 0; i < N; i++)
+ printf(" %d", *ptr[i]);
+ printf("\n");
+
+ sort_int(array, N);
+ for (i = 0; i < N; i++)
+ printf(" %d", array[i]);
+ printf("\n");
+
+}
+#endif
+
+#define SPEED_TESTx
+
+#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
+
+#define BIG_LOOP_COUNT 1000000
+#define TEXT_LOOP_COUNT 1000
+
+#ifdef SPEED_TEST
+static int test_s64(int i)
+{
+ Sk64 a, b, c;
+
+ c.set(0);
+ a.set(i);
+ b.setMul(i, i);
+ a.add(b);
+ a.add(c);
+ return c.getFixed();
+}
+
+static int test_native_64(int i)
+{
+ int16_t a, b, c;
+
+ c = 0;
+ a = i;
+ b = (int64_t)i * i;
+ a += b;
+ a += c;
+ return (int)(c >> 16);
+}
+
+static void test_drawText(SkBitmap::Config config, SkColor color)
+{
+ SkBitmap bm;
+
+ bm.setConfig(config, 320, 240);
+ bm.allocPixels();
+
+ SkCanvas canvas(bm);
+ SkPaint paint;
+
+ paint.setAntiAlias(true);
+ paint.setTextSize(SkIntToScalar(12));
+ paint.setColor(color);
+
+ SkScalar x = SkIntToScalar(20);
+ SkScalar y = SkIntToScalar(100);
+ const char* text = "Hamburgefons";
+ size_t len = strlen(text);
+
+ // draw once to populate the cache
+ canvas.drawText(text, len, x, y, paint);
+
+ SkMSec now = SkTime::GetMSecs();
+ for (int i = 0; i < TEXT_LOOP_COUNT; i++)
+ canvas.drawText(text, len, x, y, paint);
+ printf("----------- Config: %d, Color=%x, CPS = %g\n", config, color,
+ len * TEXT_LOOP_COUNT * 1000.0 / (SkTime::GetMSecs() - now));
+}
+
+#endif
+
+#include "SkFloatBits.h"
+
+static inline float fast_inc(float x) {
+ SkFloatIntUnion data;
+ data.fFloat = x;
+ data.fSignBitInt += 1;
+ return data.fFloat;
+}
+
+extern float dummy();
+int time_math() {
+ SkMSec now;
+ int i;
+ int sum = 0;
+ const int repeat = 1000000;
+ float f;
+
+ f = dummy();
+ now = SkTime::GetMSecs();
+ for (i = repeat - 1; i >= 0; --i) {
+ sum += (int)f; f = fast_inc(f);
+ sum += (int)f; f = fast_inc(f);
+ sum += (int)f; f = fast_inc(f);
+ sum += (int)f; f = fast_inc(f);
+ }
+ SkDebugf("---- native cast %d\n", SkTime::GetMSecs() - now);
+
+ f = dummy();
+ now = SkTime::GetMSecs();
+ for (i = repeat - 1; i >= 0; --i) {
+ sum += SkFloatToIntCast(f); f = fast_inc(f);
+ sum += SkFloatToIntCast(f); f = fast_inc(f);
+ sum += SkFloatToIntCast(f); f = fast_inc(f);
+ sum += SkFloatToIntCast(f); f = fast_inc(f);
+ }
+ SkDebugf("---- hack cast %d\n", SkTime::GetMSecs() - now);
+
+ f = dummy();
+ now = SkTime::GetMSecs();
+ for (i = repeat - 1; i >= 0; --i) {
+ sum += (int)floorf(f + 0.5f); f = fast_inc(f);
+ sum += (int)floorf(f + 0.5f); f = fast_inc(f);
+ sum += (int)floorf(f + 0.5f); f = fast_inc(f);
+ sum += (int)floorf(f + 0.5f); f = fast_inc(f);
+ }
+ SkDebugf("---- native round %d\n", SkTime::GetMSecs() - now);
+
+ f = dummy();
+ now = SkTime::GetMSecs();
+ for (i = repeat - 1; i >= 0; --i) {
+ sum += SkFloatToIntRound(f); f = fast_inc(f);
+ sum += SkFloatToIntRound(f); f = fast_inc(f);
+ sum += SkFloatToIntRound(f); f = fast_inc(f);
+ sum += SkFloatToIntRound(f); f = fast_inc(f);
+ }
+ SkDebugf("---- hack round %d\n", SkTime::GetMSecs() - now);
+
+ f = dummy();
+ now = SkTime::GetMSecs();
+ for (i = repeat - 1; i >= 0; --i) {
+ sum += SkFloat2Bits(floorf(f)); f = fast_inc(f);
+ sum += SkFloat2Bits(floorf(f)); f = fast_inc(f);
+ sum += SkFloat2Bits(floorf(f)); f = fast_inc(f);
+ sum += SkFloat2Bits(floorf(f)); f = fast_inc(f);
+ }
+ SkDebugf("---- native floor %d\n", SkTime::GetMSecs() - now);
+
+ f = dummy();
+ now = SkTime::GetMSecs();
+ for (i = repeat - 1; i >= 0; --i) {
+ sum += SkFloatToIntFloor(f); f = fast_inc(f);
+ sum += SkFloatToIntFloor(f); f = fast_inc(f);
+ sum += SkFloatToIntFloor(f); f = fast_inc(f);
+ sum += SkFloatToIntFloor(f); f = fast_inc(f);
+ }
+ SkDebugf("---- hack floor %d\n", SkTime::GetMSecs() - now);
+
+ return sum;
+}
+
+static float time_intToFloat() {
+ const int repeat = 1000000;
+ int i, n;
+ SkMSec now;
+ float sum = 0;
+
+ n = (int)dummy();
+ now = SkTime::GetMSecs();
+ for (i = repeat - 1; i >= 0; --i) {
+ sum += (float)n; n += 1;
+ sum += (float)n; n += 1;
+ sum += (float)n; n += 1;
+ sum += (float)n; n += 1;
+ }
+ SkDebugf("---- native i2f %d\n", SkTime::GetMSecs() - now);
+
+ n = (int)dummy();
+ now = SkTime::GetMSecs();
+ for (i = repeat - 1; i >= 0; --i) {
+ sum += SkIntToFloatCast(n); n += 1;
+ sum += SkIntToFloatCast(n); n += 1;
+ sum += SkIntToFloatCast(n); n += 1;
+ sum += SkIntToFloatCast(n); n += 1;
+ }
+ SkDebugf("---- check i2f %d\n", SkTime::GetMSecs() - now);
+
+ n = (int)dummy();
+ now = SkTime::GetMSecs();
+ for (i = repeat - 1; i >= 0; --i) {
+ sum += SkIntToFloatCast_NoOverflowCheck(n); n += 1;
+ sum += SkIntToFloatCast_NoOverflowCheck(n); n += 1;
+ sum += SkIntToFloatCast_NoOverflowCheck(n); n += 1;
+ sum += SkIntToFloatCast_NoOverflowCheck(n); n += 1;
+ }
+ SkDebugf("---- nocheck i2f %d\n", SkTime::GetMSecs() - now);
+
+ return sum;
+}
+
+void SkGraphics::Init(bool runUnitTests)
+{
+ SkGlobals::Init();
+
+// time_math();
+// time_intToFloat();
+
+#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(S8CPU),
+ typesizeline(U8CPU),
+ typesizeline(S16CPU),
+ typesizeline(U16CPU),
+
+ typesizeline(SkPoint),
+ typesizeline(SkRect),
+ typesizeline(SkMatrix),
+ typesizeline(SkPath),
+ typesizeline(SkGlyph),
+ typesizeline(SkRefCnt),
+
+ typesizeline(SkPaint),
+ typesizeline(SkCanvas),
+ typesizeline(SkBlitter),
+ typesizeline(SkShader),
+ typesizeline(SkXfermode),
+ typesizeline(SkPathEffect)
+ };
+
+#ifdef SK_CPU_BENDIAN
+ SkDebugf("SkGraphics: big-endian\n");
+#else
+ SkDebugf("SkGraphics: little-endian\n");
+#endif
+
+ {
+ char test = 0xFF;
+ int itest = test; // promote to int, see if it sign-extended
+ if (itest < 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(SkMatrix),
+ unittestline(SkGeometry),
+ unittestline(SkPath),
+ unittestline(SkPathMeasure),
+ unittestline(SkStream),
+ unittestline(SkWStream),
+ };
+
+ 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
+
+ if (false) // test asm fixmul
+ {
+ int j;
+ SkMSec now = SkTime::GetMSecs();
+ for (j = 0; j < BIG_LOOP_COUNT; j++) {
+ (void)SkFixedMul_portable(0x8000, 0x150000);
+ }
+ SkMSec now2 = SkTime::GetMSecs();
+ printf("-------- SkFixedMul_portable = %d\n", now2 - now);
+
+ for (j = 0; j < BIG_LOOP_COUNT; j++) {
+ (void)SkFixedMul(0x8000, 0x150000);
+ }
+ printf("-------- SkFixedMul = %d\n", SkTime::GetMSecs() - now2);
+
+ SkRandom rand;
+ for (j = 0; j < 10000; j++) {
+ SkFixed a = rand.nextS() >> 8;
+ SkFixed b = rand.nextS() >> 8;
+ SkFixed c1 = SkFixedMul_portable(a, b);
+ SkFixed c2 = SkFixedMul(a, b);
+ if (SkAbs32(c1 - c2) > 1)
+ printf("------ FixMul disagreement: (%x %x) slow=%x fast=%x\n", a, b, c1, c2);
+ }
+ }
+
+ if (false) // test asm fractmul
+ {
+ int j;
+ SkMSec now = SkTime::GetMSecs();
+ for (j = 0; j < BIG_LOOP_COUNT; j++) {
+ (void)SkFractMul_portable(0x800000, 0x1500000);
+ }
+ SkMSec now2 = SkTime::GetMSecs();
+ printf("-------- SkFractMul_portable = %d\n", now2 - now);
+
+ for (j = 0; j < BIG_LOOP_COUNT; j++) {
+ (void)SkFractMul(0x800000, 0x1500000);
+ }
+ printf("-------- SkFractMul = %d\n", SkTime::GetMSecs() - now2);
+
+ SkRandom rand;
+ for (j = 0; j < 10000; j++) {
+ SkFixed a = rand.nextS() >> 1;
+ SkFixed b = rand.nextS() >> 1;
+ SkFixed c1 = SkFractMul_portable(a, b);
+ SkFixed c2 = SkFractMul(a, b);
+ if (SkAbs32(c1 - c2) > 1)
+ printf("------ FractMul disagreement: (%x %x) slow=%x fast=%x\n", a, b, c1, c2);
+ }
+ }
+
+ if (false) // test asm clz
+ {
+ int j;
+ SkMSec now = SkTime::GetMSecs();
+ for (j = 0; j < BIG_LOOP_COUNT; j++) {
+ (void)SkCLZ_portable(now);
+ }
+ SkMSec now2 = SkTime::GetMSecs();
+ printf("-------- SkCLZ_portable = %d\n", now2 - now);
+
+ for (j = 0; j < BIG_LOOP_COUNT; j++) {
+ (void)SkCLZ(now);
+ }
+ printf("-------- SkCLZ = %d\n", SkTime::GetMSecs() - now2);
+
+ SkRandom rand;
+ for (j = 0; j < 10000; j++) {
+ uint32_t a = rand.nextU();
+ int c1 = SkCLZ_portable(a);
+ int c2 = SkCLZ(a);
+ if (c1 != c2)
+ printf("------ CLZ disagreement: (%x) slow=%x fast=%x\n", a, c1, c2);
+ }
+ }
+
+#ifdef SPEED_TEST
+ if (false) {
+ int i;
+ int (*proc)(int);
+
+ static const struct {
+ int (*proc)(int);
+ const char* name;
+ } gList[] = {
+ { test_s64, "Sk64" },
+ { test_native_64, "native" }
+ };
+
+ for (size_t j = 0; j < SK_ARRAY_COUNT(gList); j++) {
+ SkMSec now = SkTime::GetMSecs();
+ proc = gList[j].proc;
+ for (i = 0; i < BIG_LOOP_COUNT; i++) {
+ proc(i);
+ }
+ printf("-------- %s = %d\n", gList[j].name, SkTime::GetMSecs() - now);
+ }
+ }
+#endif
+
+ if (false) {
+ size_t i, size = 480;
+ char* buffer = (char*)sk_malloc_throw(size);
+ uint16_t* buffer16 = (uint16_t*)buffer;
+ uint32_t* buffer32 = (uint32_t*)buffer;
+
+ SkMSec now = SkTime::GetMSecs();
+ for (i = 0; i < 100000; i++) {
+ sk_memset16(buffer16, (uint16_t)i, size >> 1);
+ }
+ SkMSec now2 = SkTime::GetMSecs();
+ for (i = 0; i < 100000; i++) {
+ sk_memset16_portable(buffer16, (uint16_t)i, size >> 1);
+ }
+ SkMSec now3 = SkTime::GetMSecs();
+ printf("----------- memset16: native %d, portable %d\n", now2 - now, now3 - now2);
+
+ now = SkTime::GetMSecs();
+ for (i = 0; i < 100000; i++) {
+ sk_memset32(buffer32, i, size >> 2);
+ }
+ now2 = SkTime::GetMSecs();
+ for (i = 0; i < 100000; i++) {
+ sk_memset32_portable(buffer32, i, size >> 2);
+ }
+ now3 = SkTime::GetMSecs();
+ printf("----------- memset32: native %d, portable %d\n", now2 - now, now3 - now2);
+
+ sk_free(buffer);
+ }
+
+#ifdef SPEED_TEST
+ if (false) {
+ test_drawText(SkBitmap::kARGB_8888_Config, SK_ColorBLACK);
+ test_drawText(SkBitmap::kARGB_8888_Config, SK_ColorRED);
+ test_drawText(SkBitmap::kRGB_565_Config, SK_ColorBLACK);
+ test_drawText(SkBitmap::kRGB_565_Config, SK_ColorRED);
+ }
+#endif
+
+// if (true) {
+// test_sort();
+// }
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+#include "SkGlyphCache.h"
+
+void SkGraphics::Term() {
+ SkGraphics::SetFontCacheUsed(0);
+ SkGlobals::Term();
+}
+
+size_t SkGraphics::GetFontCacheUsed() {
+ return SkGlyphCache::GetCacheUsed();
+}
+
+bool SkGraphics::SetFontCacheUsed(size_t usageInBytes) {
+ return SkGlyphCache::SetCacheUsed(usageInBytes);
+}
+
+float dummy() { return 1.25f; }
diff --git a/src/core/SkMask.cpp b/src/core/SkMask.cpp
new file mode 100644
index 0000000000..b2376395ed
--- /dev/null
+++ b/src/core/SkMask.cpp
@@ -0,0 +1,49 @@
+/* libs/graphics/sgl/SkMask.cpp
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkMask.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;
+}
+
+/** We explicitly use this allocator for SkBimap pixels, so that we can
+ freely assign memory allocated by one class to the other.
+*/
+uint8_t* SkMask::AllocImage(size_t size)
+{
+ return (uint8_t*)sk_malloc_throw(SkAlign4(size));
+}
+
+/** We explicitly use this allocator for SkBimap pixels, so that we can
+ freely assign memory allocated by one class to the other.
+*/
+void SkMask::FreeImage(void* image)
+{
+ sk_free(image);
+}
+
diff --git a/src/core/SkMaskFilter.cpp b/src/core/SkMaskFilter.cpp
new file mode 100644
index 0000000000..56fff9763e
--- /dev/null
+++ b/src/core/SkMaskFilter.cpp
@@ -0,0 +1,62 @@
+/* libs/graphics/sgl/SkMaskFilter.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkMaskFilter.h"
+#include "SkBlitter.h"
+#include "SkBounder.h"
+#include "SkBuffer.h"
+#include "SkDraw.h"
+#include "SkRegion.h"
+
+bool SkMaskFilter::filterMask(SkMask*, const SkMask&, const SkMatrix&, SkIPoint*)
+{
+ 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)))
+ {
+ const SkIRect& cr = clipper.rect();
+ do {
+ blitter->blitMask(dstM, cr);
+ clipper.next();
+ } while (!clipper.done());
+ }
+
+ return true;
+}
+
+
diff --git a/src/core/SkMath.cpp b/src/core/SkMath.cpp
new file mode 100644
index 0000000000..c627d9b336
--- /dev/null
+++ b/src/core/SkMath.cpp
@@ -0,0 +1,932 @@
+/*
+ * Copyright (C) 2006-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkMath.h"
+#include "SkCordic.h"
+#include "SkFloatBits.h"
+#include "SkFloatingPoint.h"
+#include "Sk64.h"
+#include "SkScalar.h"
+
+#ifdef SK_SCALAR_IS_FLOAT
+ const uint32_t gIEEENotANumber = 0x7FFFFFFF;
+ const uint32_t gIEEEInfinity = 0x7F800000;
+#endif
+
+#define sub_shift(zeros, x, n) \
+ zeros -= n; \
+ x >>= n
+
+int SkCLZ_portable(uint32_t x) {
+ if (x == 0) {
+ return 32;
+ }
+
+#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
+ int zeros = 31;
+ if (x & 0xFFFF0000) {
+ sub_shift(zeros, x, 16);
+ }
+ if (x & 0xFF00) {
+ sub_shift(zeros, x, 8);
+ }
+ if (x & 0xF0) {
+ sub_shift(zeros, x, 4);
+ }
+ if (x & 0xC) {
+ sub_shift(zeros, x, 2);
+ }
+ if (x & 0x2) {
+ sub_shift(zeros, x, 1);
+ }
+#else
+ int zeros = ((x >> 16) - 1) >> 31 << 4;
+ x <<= zeros;
+
+ int nonzero = ((x >> 24) - 1) >> 31 << 3;
+ zeros += nonzero;
+ x <<= nonzero;
+
+ nonzero = ((x >> 28) - 1) >> 31 << 2;
+ zeros += nonzero;
+ x <<= nonzero;
+
+ nonzero = ((x >> 30) - 1) >> 31 << 1;
+ zeros += nonzero;
+ x <<= nonzero;
+
+ zeros += (~x) >> 31;
+#endif
+
+ return zeros;
+}
+
+int32_t SkMulDiv(int32_t numer1, int32_t numer2, int32_t denom) {
+ SkASSERT(denom);
+
+ Sk64 tmp;
+ tmp.setMul(numer1, numer2);
+ tmp.div(denom, Sk64::kTrunc_DivOption);
+ return tmp.get32();
+}
+
+int32_t SkMulShift(int32_t a, int32_t b, unsigned shift) {
+ int sign = SkExtractSign(a ^ b);
+
+ if (shift > 63) {
+ return sign;
+ }
+
+ a = SkAbs32(a);
+ b = SkAbs32(b);
+
+ uint32_t ah = a >> 16;
+ uint32_t al = a & 0xFFFF;
+ uint32_t bh = b >> 16;
+ uint32_t bl = b & 0xFFFF;
+
+ uint32_t A = ah * bh;
+ uint32_t B = ah * bl + al * bh;
+ uint32_t C = al * bl;
+
+ /* [ A ]
+ [ B ]
+ [ C ]
+ */
+ uint32_t lo = C + (B << 16);
+ int32_t hi = A + (B >> 16) + (lo < C);
+
+ if (sign < 0) {
+ hi = -hi - Sk32ToBool(lo);
+ lo = 0 - lo;
+ }
+
+ if (shift == 0) {
+#ifdef SK_DEBUGx
+ SkASSERT(((int32_t)lo >> 31) == hi);
+#endif
+ return lo;
+ } else if (shift >= 32) {
+ return hi >> (shift - 32);
+ } else {
+#ifdef SK_DEBUGx
+ int32_t tmp = hi >> shift;
+ SkASSERT(tmp == 0 || tmp == -1);
+#endif
+ // we want (hi << (32 - shift)) | (lo >> shift) but rounded
+ int roundBit = (lo >> (shift - 1)) & 1;
+ return ((hi << (32 - shift)) | (lo >> shift)) + roundBit;
+ }
+}
+
+SkFixed SkFixedMul_portable(SkFixed a, SkFixed b) {
+#if 0
+ Sk64 tmp;
+
+ tmp.setMul(a, b);
+ tmp.shiftRight(16);
+ return tmp.fLo;
+#elif defined(SkLONGLONG)
+ return (SkLONGLONG)a * b >> 16;
+#else
+ int sa = SkExtractSign(a);
+ int sb = SkExtractSign(b);
+ // now make them positive
+ a = SkApplySign(a, sa);
+ b = SkApplySign(b, sb);
+
+ uint32_t ah = a >> 16;
+ uint32_t al = a & 0xFFFF;
+ uint32_t bh = b >> 16;
+ uint32_t bl = b & 0xFFFF;
+
+ uint32_t R = ah * b + al * bh + (al * bl >> 16);
+
+ return SkApplySign(R, sa ^ sb);
+#endif
+}
+
+SkFract SkFractMul_portable(SkFract a, SkFract b) {
+#if 0
+ Sk64 tmp;
+ tmp.setMul(a, b);
+ return tmp.getFract();
+#elif defined(SkLONGLONG)
+ return (SkLONGLONG)a * b >> 30;
+#else
+ int sa = SkExtractSign(a);
+ int sb = SkExtractSign(b);
+ // now make them positive
+ a = SkApplySign(a, sa);
+ b = SkApplySign(b, sb);
+
+ uint32_t ah = a >> 16;
+ uint32_t al = a & 0xFFFF;
+ uint32_t bh = b >> 16;
+ uint32_t bl = b & 0xFFFF;
+
+ uint32_t A = ah * bh;
+ uint32_t B = ah * bl + al * bh;
+ uint32_t C = al * bl;
+
+ /* [ A ]
+ [ B ]
+ [ C ]
+ */
+ uint32_t Lo = C + (B << 16);
+ uint32_t Hi = A + (B >>16) + (Lo < C);
+
+ SkASSERT((Hi >> 29) == 0); // else overflow
+
+ int32_t R = (Hi << 2) + (Lo >> 30);
+
+ return SkApplySign(R, sa ^ sb);
+#endif
+}
+
+int SkFixedMulCommon(SkFixed a, int b, int bias) {
+ // this function only works if b is 16bits
+ SkASSERT(b == (int16_t)b);
+ SkASSERT(b >= 0);
+
+ int sa = SkExtractSign(a);
+ a = SkApplySign(a, sa);
+ uint32_t ah = a >> 16;
+ uint32_t al = a & 0xFFFF;
+ uint32_t R = ah * b + ((al * b + bias) >> 16);
+ return SkApplySign(R, sa);
+}
+
+#ifdef SK_DEBUGx
+ #define TEST_FASTINVERT
+#endif
+
+SkFixed SkFixedFastInvert(SkFixed x) {
+/* Adapted (stolen) from gglRecip()
+*/
+
+ if (x == SK_Fixed1) {
+ return SK_Fixed1;
+ }
+
+ int sign = SkExtractSign(x);
+ uint32_t a = SkApplySign(x, sign);
+
+ if (a <= 2) {
+ return SkApplySign(SK_MaxS32, sign);
+ }
+
+#ifdef TEST_FASTINVERT
+ SkFixed orig = a;
+ uint32_t slow = SkFixedDiv(SK_Fixed1, a);
+#endif
+
+ // normalize a
+ int lz = SkCLZ(a);
+ a = a << lz >> 16;
+
+ // compute 1/a approximation (0.5 <= a < 1.0)
+ uint32_t r = 0x17400 - a; // (2.90625 (~2.914) - 2*a) >> 1
+
+ // Newton-Raphson iteration:
+ // x = r*(2 - a*r) = ((r/2)*(1 - a*r/2))*4
+ r = ( (0x10000 - ((a*r)>>16)) * r ) >> 15;
+ r = ( (0x10000 - ((a*r)>>16)) * r ) >> (30 - lz);
+
+#ifdef TEST_FASTINVERT
+ SkDebugf("SkFixedFastInvert(%x %g) = %x %g Slow[%x %g]\n",
+ orig, orig/65536.,
+ r, r/65536.,
+ slow, slow/65536.);
+#endif
+
+ return SkApplySign(r, sign);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define DIVBITS_ITER(n) \
+ case n: \
+ if ((numer = (numer << 1) - denom) >= 0) \
+ result |= 1 << (n - 1); else numer += denom
+
+int32_t SkDivBits(int32_t numer, int32_t denom, int shift_bias) {
+ SkASSERT(denom != 0);
+ if (numer == 0) {
+ return 0;
+ }
+
+ // make numer and denom positive, and sign hold the resulting sign
+ int32_t sign = SkExtractSign(numer ^ denom);
+ numer = SkAbs32(numer);
+ denom = SkAbs32(denom);
+
+ int nbits = SkCLZ(numer) - 1;
+ int dbits = SkCLZ(denom) - 1;
+ int bits = shift_bias - nbits + dbits;
+
+ if (bits < 0) { // answer will underflow
+ return 0;
+ }
+ if (bits > 31) { // answer will overflow
+ return SkApplySign(SK_MaxS32, sign);
+ }
+
+ denom <<= dbits;
+ numer <<= nbits;
+
+ SkFixed result = 0;
+
+ // do the first one
+ if ((numer -= denom) >= 0) {
+ result = 1;
+ } else {
+ numer += denom;
+ }
+
+ // Now fall into our switch statement if there are more bits to compute
+ if (bits > 0) {
+ // make room for the rest of the answer bits
+ result <<= bits;
+ switch (bits) {
+ DIVBITS_ITER(31); DIVBITS_ITER(30); DIVBITS_ITER(29);
+ DIVBITS_ITER(28); DIVBITS_ITER(27); DIVBITS_ITER(26);
+ DIVBITS_ITER(25); DIVBITS_ITER(24); DIVBITS_ITER(23);
+ DIVBITS_ITER(22); DIVBITS_ITER(21); DIVBITS_ITER(20);
+ DIVBITS_ITER(19); DIVBITS_ITER(18); DIVBITS_ITER(17);
+ DIVBITS_ITER(16); DIVBITS_ITER(15); DIVBITS_ITER(14);
+ DIVBITS_ITER(13); DIVBITS_ITER(12); DIVBITS_ITER(11);
+ DIVBITS_ITER(10); DIVBITS_ITER( 9); DIVBITS_ITER( 8);
+ DIVBITS_ITER( 7); DIVBITS_ITER( 6); DIVBITS_ITER( 5);
+ DIVBITS_ITER( 4); DIVBITS_ITER( 3); DIVBITS_ITER( 2);
+ // we merge these last two together, makes GCC make better ARM
+ default:
+ DIVBITS_ITER( 1);
+ }
+ }
+
+ if (result < 0) {
+ result = SK_MaxS32;
+ }
+ return SkApplySign(result, sign);
+}
+
+/* mod(float numer, float denom) seems to always return the sign
+ of the numer, so that's what we do too
+*/
+SkFixed SkFixedMod(SkFixed numer, SkFixed denom) {
+ int sn = SkExtractSign(numer);
+ int sd = SkExtractSign(denom);
+
+ numer = SkApplySign(numer, sn);
+ denom = SkApplySign(denom, sd);
+
+ if (numer < denom) {
+ return SkApplySign(numer, sn);
+ } else if (numer == denom) {
+ return 0;
+ } else {
+ SkFixed div = SkFixedDiv(numer, denom);
+ return SkApplySign(SkFixedMul(denom, div & 0xFFFF), sn);
+ }
+}
+
+/* www.worldserver.com/turk/computergraphics/FixedSqrt.pdf
+*/
+int32_t SkSqrtBits(int32_t x, int count) {
+ SkASSERT(x >= 0 && count > 0 && (unsigned)count <= 30);
+
+ uint32_t root = 0;
+ uint32_t remHi = 0;
+ uint32_t remLo = x;
+
+ do {
+ root <<= 1;
+
+ remHi = (remHi<<2) | (remLo>>30);
+ remLo <<= 2;
+
+ uint32_t testDiv = (root << 1) + 1;
+ if (remHi >= testDiv) {
+ remHi -= testDiv;
+ root++;
+ }
+ } while (--count >= 0);
+
+ return root;
+}
+
+int32_t SkCubeRootBits(int32_t value, int bits) {
+ SkASSERT(bits > 0);
+
+ int sign = SkExtractSign(value);
+ value = SkApplySign(value, sign);
+
+ uint32_t root = 0;
+ uint32_t curr = (uint32_t)value >> 30;
+ value <<= 2;
+
+ do {
+ root <<= 1;
+ uint32_t guess = root * root + root;
+ guess = (guess << 1) + guess; // guess *= 3
+ if (guess < curr) {
+ curr -= guess + 1;
+ root |= 1;
+ }
+ curr = (curr << 3) | ((uint32_t)value >> 29);
+ value <<= 3;
+ } while (--bits);
+
+ return SkApplySign(root, sign);
+}
+
+SkFixed SkFixedMean(SkFixed a, SkFixed b) {
+ Sk64 tmp;
+
+ tmp.setMul(a, b);
+ return tmp.getSqrt();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_SCALAR_IS_FLOAT
+float SkScalarSinCos(float radians, float* cosValue) {
+ float sinValue = sk_float_sin(radians);
+
+ if (cosValue) {
+ *cosValue = sk_float_cos(radians);
+ if (SkScalarNearlyZero(*cosValue)) {
+ *cosValue = 0;
+ }
+ }
+
+ if (SkScalarNearlyZero(sinValue)) {
+ sinValue = 0;
+ }
+ return sinValue;
+}
+#endif
+
+#define INTERP_SINTABLE
+#define BUILD_TABLE_AT_RUNTIMEx
+
+#define kTableSize 256
+
+#ifdef BUILD_TABLE_AT_RUNTIME
+ static uint16_t gSkSinTable[kTableSize];
+
+ static void build_sintable(uint16_t table[]) {
+ for (int i = 0; i < kTableSize; i++) {
+ double rad = i * 3.141592653589793 / (2*kTableSize);
+ double val = sin(rad);
+ int ival = (int)(val * SK_Fixed1);
+ table[i] = SkToU16(ival);
+ }
+ }
+#else
+ #include "SkSinTable.h"
+#endif
+
+#define SK_Fract1024SizeOver2PI 0x28BE60 /* floatToFract(1024 / 2PI) */
+
+#ifdef INTERP_SINTABLE
+static SkFixed interp_table(const uint16_t table[], int index, int partial255) {
+ SkASSERT((unsigned)index < kTableSize);
+ SkASSERT((unsigned)partial255 <= 255);
+
+ SkFixed lower = table[index];
+ SkFixed upper = (index == kTableSize - 1) ? SK_Fixed1 : table[index + 1];
+
+ SkASSERT(lower < upper);
+ SkASSERT(lower >= 0);
+ SkASSERT(upper <= SK_Fixed1);
+
+ partial255 += (partial255 >> 7);
+ return lower + ((upper - lower) * partial255 >> 8);
+}
+#endif
+
+SkFixed SkFixedSinCos(SkFixed radians, SkFixed* cosValuePtr) {
+ SkASSERT(SK_ARRAY_COUNT(gSkSinTable) == kTableSize);
+
+#ifdef BUILD_TABLE_AT_RUNTIME
+ static bool gFirstTime = true;
+ if (gFirstTime) {
+ build_sintable(gSinTable);
+ gFirstTime = false;
+ }
+#endif
+
+ // make radians positive
+ SkFixed sinValue, cosValue;
+ int32_t cosSign = 0;
+ int32_t sinSign = SkExtractSign(radians);
+ radians = SkApplySign(radians, sinSign);
+ // scale it to 0...1023 ...
+
+#ifdef INTERP_SINTABLE
+ radians = SkMulDiv(radians, 2 * kTableSize * 256, SK_FixedPI);
+ int findex = radians & (kTableSize * 256 - 1);
+ int index = findex >> 8;
+ int partial = findex & 255;
+ sinValue = interp_table(gSkSinTable, index, partial);
+
+ findex = kTableSize * 256 - findex - 1;
+ index = findex >> 8;
+ partial = findex & 255;
+ cosValue = interp_table(gSkSinTable, index, partial);
+
+ int quad = ((unsigned)radians / (kTableSize * 256)) & 3;
+#else
+ radians = SkMulDiv(radians, 2 * kTableSize, SK_FixedPI);
+ int index = radians & (kTableSize - 1);
+
+ if (index == 0) {
+ sinValue = 0;
+ cosValue = SK_Fixed1;
+ } else {
+ sinValue = gSkSinTable[index];
+ cosValue = gSkSinTable[kTableSize - index];
+ }
+ int quad = ((unsigned)radians / kTableSize) & 3;
+#endif
+
+ if (quad & 1) {
+ SkTSwap<SkFixed>(sinValue, cosValue);
+ }
+ if (quad & 2) {
+ sinSign = ~sinSign;
+ }
+ if (((quad - 1) & 2) == 0) {
+ cosSign = ~cosSign;
+ }
+
+ // restore the sign for negative angles
+ sinValue = SkApplySign(sinValue, sinSign);
+ cosValue = SkApplySign(cosValue, cosSign);
+
+#ifdef SK_DEBUG
+ if (1) {
+ SkFixed sin2 = SkFixedMul(sinValue, sinValue);
+ SkFixed cos2 = SkFixedMul(cosValue, cosValue);
+ int diff = cos2 + sin2 - SK_Fixed1;
+ SkASSERT(SkAbs32(diff) <= 7);
+ }
+#endif
+
+ if (cosValuePtr) {
+ *cosValuePtr = cosValue;
+ }
+ return sinValue;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkFixed SkFixedTan(SkFixed radians) { return SkCordicTan(radians); }
+SkFixed SkFixedASin(SkFixed x) { return SkCordicASin(x); }
+SkFixed SkFixedACos(SkFixed x) { return SkCordicACos(x); }
+SkFixed SkFixedATan2(SkFixed y, SkFixed x) { return SkCordicATan2(y, x); }
+SkFixed SkFixedExp(SkFixed x) { return SkCordicExp(x); }
+SkFixed SkFixedLog(SkFixed x) { return SkCordicLog(x); }
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+#include "SkRandom.h"
+
+#ifdef SkLONGLONG
+static int symmetric_fixmul(int a, int b) {
+ int sa = SkExtractSign(a);
+ int sb = SkExtractSign(b);
+
+ a = SkApplySign(a, sa);
+ b = SkApplySign(b, sb);
+
+#if 1
+ int c = (int)(((SkLONGLONG)a * b) >> 16);
+
+ return SkApplySign(c, sa ^ sb);
+#else
+ SkLONGLONG ab = (SkLONGLONG)a * b;
+ if (sa ^ sb) {
+ ab = -ab;
+ }
+ return ab >> 16;
+#endif
+}
+#endif
+
+#include "SkPoint.h"
+
+#ifdef SK_SUPPORT_UNITTEST
+static void check_length(const SkPoint& p, SkScalar targetLen) {
+ float x = SkScalarToFloat(p.fX);
+ float y = SkScalarToFloat(p.fY);
+ float len = sk_float_sqrt(x*x + y*y);
+
+ len /= SkScalarToFloat(targetLen);
+
+ SkASSERT(len > 0.999f && len < 1.001f);
+}
+#endif
+
+#ifdef SK_CAN_USE_FLOAT
+
+static float nextFloat(SkRandom& rand) {
+ SkFloatIntUnion data;
+ data.fSignBitInt = rand.nextU();
+ return data.fFloat;
+}
+
+/* returns true if a == b as resulting from (int)x. Since it is undefined
+ what to do if the float exceeds 2^32-1, we check for that explicitly.
+*/
+static bool equal_float_native_skia(float x, uint32_t ni, uint32_t si) {
+ if (!(x == x)) { // NAN
+ return si == SK_MaxS32 || si == SK_MinS32;
+ }
+ // for out of range, C is undefined, but skia always should return NaN32
+ if (x > SK_MaxS32) {
+ return si == SK_MaxS32;
+ }
+ if (x < -SK_MaxS32) {
+ return si == SK_MinS32;
+ }
+ return si == ni;
+}
+
+static void assert_float_equal(const char op[], float x, uint32_t ni,
+ uint32_t si) {
+ if (!equal_float_native_skia(x, ni, si)) {
+ SkDebugf("-- %s float %g bits %x native %x skia %x\n", op, x, ni, si);
+ SkASSERT(!"oops");
+ }
+}
+
+static void test_float_cast(float x) {
+ int ix = (int)x;
+ int iix = SkFloatToIntCast(x);
+ assert_float_equal("cast", x, ix, iix);
+}
+
+static void test_float_floor(float x) {
+ int ix = (int)floor(x);
+ int iix = SkFloatToIntFloor(x);
+ assert_float_equal("floor", x, ix, iix);
+}
+
+static void test_float_round(float x) {
+ double xx = x + 0.5; // need intermediate double to avoid temp loss
+ int ix = (int)floor(xx);
+ int iix = SkFloatToIntRound(x);
+ assert_float_equal("round", x, ix, iix);
+}
+
+static void test_float_ceil(float x) {
+ int ix = (int)ceil(x);
+ int iix = SkFloatToIntCeil(x);
+ assert_float_equal("ceil", x, ix, iix);
+}
+
+static void test_float_conversions(float x) {
+ test_float_cast(x);
+ test_float_floor(x);
+ test_float_round(x);
+ test_float_ceil(x);
+}
+
+static void test_int2float(int ival) {
+ float x0 = (float)ival;
+ float x1 = SkIntToFloatCast(ival);
+ float x2 = SkIntToFloatCast_NoOverflowCheck(ival);
+ SkASSERT(x0 == x1);
+ SkASSERT(x0 == x2);
+}
+
+static void unittest_fastfloat() {
+ SkRandom rand;
+ size_t i;
+
+ static const float gFloats[] = {
+ 0.f, 1.f, 0.5f, 0.499999f, 0.5000001f, 1.f/3,
+ 0.000000001f, 1000000000.f, // doesn't overflow
+ 0.0000000001f, 10000000000.f // does overflow
+ };
+ for (i = 0; i < SK_ARRAY_COUNT(gFloats); i++) {
+// SkDebugf("---- test floats %g %d\n", gFloats[i], (int)gFloats[i]);
+ test_float_conversions(gFloats[i]);
+ test_float_conversions(-gFloats[i]);
+ }
+
+ for (int outer = 0; outer < 100; outer++) {
+ rand.setSeed(outer);
+ for (i = 0; i < 100000; i++) {
+ float x = nextFloat(rand);
+ test_float_conversions(x);
+ }
+
+ test_int2float(0);
+ test_int2float(1);
+ test_int2float(-1);
+ for (i = 0; i < 100000; i++) {
+ // for now only test ints that are 24bits or less, since we don't
+ // round (down) large ints the same as IEEE...
+ int ival = rand.nextU() & 0xFFFFFF;
+ test_int2float(ival);
+ test_int2float(-ival);
+ }
+ }
+}
+
+#endif
+
+static void test_muldiv255() {
+ for (int a = 0; a <= 255; a++) {
+ for (int b = 0; b <= 255; b++) {
+ int ab = a * b;
+ float s = ab / 255.0f;
+ int round = (int)floorf(s + 0.5f);
+ int trunc = (int)floorf(s);
+
+ int iround = SkMulDiv255Round(a, b);
+ int itrunc = SkMulDiv255Trunc(a, b);
+
+ SkASSERT(iround == round);
+ SkASSERT(itrunc == trunc);
+
+ SkASSERT(itrunc <= iround);
+ SkASSERT(iround <= a);
+ SkASSERT(iround <= b);
+ }
+ }
+}
+
+void SkMath::UnitTest() {
+#ifdef SK_SUPPORT_UNITTEST
+ int i;
+ int32_t x;
+ SkRandom rand;
+
+ SkToS8(127); SkToS8(-128); SkToU8(255);
+ SkToS16(32767); SkToS16(-32768); SkToU16(65535);
+ SkToS32(2*1024*1024); SkToS32(-2*1024*1024); SkToU32(4*1024*1024);
+
+ SkCordic_UnitTest();
+
+ // these should assert
+#if 0
+ SkToS8(128);
+ SkToS8(-129);
+ SkToU8(256);
+ SkToU8(-5);
+
+ SkToS16(32768);
+ SkToS16(-32769);
+ SkToU16(65536);
+ SkToU16(-5);
+
+ if (sizeof(size_t) > 4) {
+ SkToS32(4*1024*1024);
+ SkToS32(-4*1024*1024);
+ SkToU32(5*1024*1024);
+ SkToU32(-5);
+ }
+#endif
+
+ test_muldiv255();
+
+#ifdef SK_DEBUG
+ {
+ SkScalar x = SK_ScalarNaN;
+ SkASSERT(SkScalarIsNaN(x));
+ }
+#endif
+
+ for (i = 1; i <= 10; i++) {
+ x = SkCubeRootBits(i*i*i, 11);
+ SkASSERT(x == i);
+ }
+
+ x = SkFixedSqrt(SK_Fixed1);
+ SkASSERT(x == SK_Fixed1);
+ x = SkFixedSqrt(SK_Fixed1/4);
+ SkASSERT(x == SK_Fixed1/2);
+ x = SkFixedSqrt(SK_Fixed1*4);
+ SkASSERT(x == SK_Fixed1*2);
+
+ x = SkFractSqrt(SK_Fract1);
+ SkASSERT(x == SK_Fract1);
+ x = SkFractSqrt(SK_Fract1/4);
+ SkASSERT(x == SK_Fract1/2);
+ x = SkFractSqrt(SK_Fract1/16);
+ SkASSERT(x == SK_Fract1/4);
+
+ for (i = 1; i < 100; i++) {
+ x = SkFixedSqrt(SK_Fixed1 * i * i);
+ SkASSERT(x == SK_Fixed1 * i);
+ }
+
+ for (i = 0; i < 1000; i++) {
+ int value = rand.nextS16();
+ int max = rand.nextU16();
+
+ int clamp = SkClampMax(value, max);
+ int clamp2 = value < 0 ? 0 : (value > max ? max : value);
+ SkASSERT(clamp == clamp2);
+ }
+
+ for (i = 0; i < 100000; i++) {
+ SkPoint p;
+
+ p.setLength(rand.nextS(), rand.nextS(), SK_Scalar1);
+ check_length(p, SK_Scalar1);
+ p.setLength(rand.nextS() >> 13, rand.nextS() >> 13, SK_Scalar1);
+ check_length(p, SK_Scalar1);
+ }
+
+ {
+ SkFixed result = SkFixedDiv(100, 100);
+ SkASSERT(result == SK_Fixed1);
+ result = SkFixedDiv(1, SK_Fixed1);
+ SkASSERT(result == 1);
+ }
+
+#ifdef SK_CAN_USE_FLOAT
+ unittest_fastfloat();
+#endif
+
+#ifdef SkLONGLONG
+ for (i = 0; i < 100000; i++) {
+ SkFixed numer = rand.nextS();
+ SkFixed denom = rand.nextS();
+ SkFixed result = SkFixedDiv(numer, denom);
+ SkLONGLONG check = ((SkLONGLONG)numer << 16) / denom;
+
+ (void)SkCLZ(numer);
+ (void)SkCLZ(denom);
+
+ SkASSERT(result != (SkFixed)SK_NaN32);
+ if (check > SK_MaxS32) {
+ check = SK_MaxS32;
+ } else if (check < -SK_MaxS32) {
+ check = SK_MinS32;
+ }
+ SkASSERT(result == (int32_t)check);
+
+ result = SkFractDiv(numer, denom);
+ check = ((SkLONGLONG)numer << 30) / denom;
+
+ SkASSERT(result != (SkFixed)SK_NaN32);
+ if (check > SK_MaxS32) {
+ check = SK_MaxS32;
+ } else if (check < -SK_MaxS32) {
+ check = SK_MinS32;
+ }
+ SkASSERT(result == (int32_t)check);
+
+ // make them <= 2^24, so we don't overflow in fixmul
+ numer = numer << 8 >> 8;
+ denom = denom << 8 >> 8;
+
+ result = SkFixedMul(numer, denom);
+ SkFixed r2 = symmetric_fixmul(numer, denom);
+// SkASSERT(result == r2);
+
+ result = SkFixedMul(numer, numer);
+ r2 = SkFixedSquare(numer);
+ SkASSERT(result == r2);
+
+#ifdef SK_CAN_USE_FLOAT
+ if (numer >= 0 && denom >= 0) {
+ SkFixed mean = SkFixedMean(numer, denom);
+ float fm = sk_float_sqrt(sk_float_abs(SkFixedToFloat(numer) * SkFixedToFloat(denom)));
+ SkFixed mean2 = SkFloatToFixed(fm);
+ int diff = SkAbs32(mean - mean2);
+ SkASSERT(diff <= 1);
+ }
+
+ {
+ SkFixed mod = SkFixedMod(numer, denom);
+ float n = SkFixedToFloat(numer);
+ float d = SkFixedToFloat(denom);
+ float m = sk_float_mod(n, d);
+#if 0
+ SkDebugf("%g mod %g = %g [%g]\n",
+ SkFixedToFloat(numer), SkFixedToFloat(denom),
+ SkFixedToFloat(mod), m);
+#endif
+ SkASSERT(mod == 0 || (mod < 0) == (m < 0)); // ensure the same sign
+ int diff = SkAbs32(mod - SkFloatToFixed(m));
+ SkASSERT((diff >> 7) == 0);
+ }
+#endif
+ }
+#endif
+
+#ifdef SK_CAN_USE_FLOAT
+ for (i = 0; i < 100000; i++) {
+ SkFract x = rand.nextU() >> 1;
+ double xx = (double)x / SK_Fract1;
+ SkFract xr = SkFractSqrt(x);
+ SkFract check = SkFloatToFract(sqrt(xx));
+ SkASSERT(xr == check || xr == check-1 || xr == check+1);
+
+ xr = SkFixedSqrt(x);
+ xx = (double)x / SK_Fixed1;
+ check = SkFloatToFixed(sqrt(xx));
+ SkASSERT(xr == check || xr == check-1);
+
+ xr = SkSqrt32(x);
+ xx = (double)x;
+ check = (int32_t)sqrt(xx);
+ SkASSERT(xr == check || xr == check-1);
+ }
+#endif
+
+#if !defined(SK_SCALAR_IS_FLOAT) && defined(SK_CAN_USE_FLOAT)
+ {
+ SkFixed s, c;
+ s = SkFixedSinCos(0, &c);
+ SkASSERT(s == 0);
+ SkASSERT(c == SK_Fixed1);
+ }
+
+ int maxDiff = 0;
+ for (i = 0; i < 10000; i++) {
+ SkFixed rads = rand.nextS() >> 10;
+ double frads = SkFixedToFloat(rads);
+
+ SkFixed s, c;
+ s = SkScalarSinCos(rads, &c);
+
+ double fs = sin(frads);
+ double fc = cos(frads);
+
+ SkFixed is = SkFloatToFixed(fs);
+ SkFixed ic = SkFloatToFixed(fc);
+
+ maxDiff = SkMax32(maxDiff, SkAbs32(is - s));
+ maxDiff = SkMax32(maxDiff, SkAbs32(ic - c));
+ }
+ SkDebugf("SinCos: maximum error = %d\n", maxDiff);
+#endif
+#endif
+}
+
+#endif
diff --git a/src/core/SkMatrix.cpp b/src/core/SkMatrix.cpp
new file mode 100644
index 0000000000..893aea17d4
--- /dev/null
+++ b/src/core/SkMatrix.cpp
@@ -0,0 +1,1690 @@
+/* libs/corecg/SkMatrix.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkMatrix.h"
+#include "Sk64.h"
+#include "SkFloatBits.h"
+#include "SkString.h"
+
+#ifdef SK_SCALAR_IS_FLOAT
+ #define kMatrix22Elem SK_Scalar1
+#else
+ #define kMatrix22Elem SK_Fract1
+#endif
+
+/* [scale-x skew-x trans-x] [X] [X']
+ [skew-y scale-y trans-y] * [Y] = [Y']
+ [persp-0 persp-1 persp-2] [1] [1 ]
+*/
+
+void SkMatrix::reset() {
+ fMat[kMScaleX] = fMat[kMScaleY] = SK_Scalar1;
+ fMat[kMSkewX] = fMat[kMSkewY] =
+ fMat[kMTransX] = fMat[kMTransY] =
+ fMat[kMPersp0] = fMat[kMPersp1] = 0;
+ fMat[kMPersp2] = kMatrix22Elem;
+
+ this->setTypeMask(kIdentity_Mask | kRectStaysRect_Mask);
+}
+
+static inline int has_perspective(const SkMatrix& matrix) {
+ return matrix.getType() & SkMatrix::kPerspective_Mask;
+}
+
+// this guy aligns with the masks, so we can compute a mask from a varaible 0/1
+enum {
+ kTranslate_Shift,
+ kScale_Shift,
+ kAffine_Shift,
+ kPerspective_Shift,
+ kRectStaysRect_Shift
+};
+
+#ifdef SK_SCALAR_IS_FLOAT
+ static const int32_t kScalar1Int = 0x3f800000;
+ static const int32_t kPersp1Int = 0x3f800000;
+#else
+ #define scalarAsInt(x) (x)
+ static const int32_t kScalar1Int = (1 << 16);
+ static const int32_t kPersp1Int = (1 << 30);
+#endif
+
+uint8_t SkMatrix::computeTypeMask() const {
+ unsigned mask = 0;
+
+ if (SkScalarAs2sCompliment(fMat[kMPersp0]) |
+ SkScalarAs2sCompliment(fMat[kMPersp1]) |
+ (SkScalarAs2sCompliment(fMat[kMPersp2]) - kPersp1Int)) {
+ mask |= kPerspective_Mask;
+ }
+
+ if (SkScalarAs2sCompliment(fMat[kMTransX]) |
+ SkScalarAs2sCompliment(fMat[kMTransY])) {
+ mask |= kTranslate_Mask;
+ }
+
+ int m00 = SkScalarAs2sCompliment(fMat[SkMatrix::kMScaleX]);
+ int m01 = SkScalarAs2sCompliment(fMat[SkMatrix::kMSkewX]);
+ int m10 = SkScalarAs2sCompliment(fMat[SkMatrix::kMSkewY]);
+ int m11 = SkScalarAs2sCompliment(fMat[SkMatrix::kMScaleY]);
+
+ if (m01 | m10) {
+ mask |= kAffine_Mask;
+ }
+
+ if ((m00 - kScalar1Int) | (m11 - kScalar1Int)) {
+ mask |= kScale_Mask;
+ }
+
+ if ((mask & kPerspective_Mask) == 0) {
+ // map non-zero to 1
+ m00 = m00 != 0;
+ m01 = m01 != 0;
+ m10 = m10 != 0;
+ m11 = m11 != 0;
+
+ // record if the (p)rimary and (s)econdary diagonals are all 0 or
+ // all non-zero (answer is 0 or 1)
+ int dp0 = (m00 | m11) ^ 1; // true if both are 0
+ int dp1 = m00 & m11; // true if both are 1
+ int ds0 = (m01 | m10) ^ 1; // true if both are 0
+ int ds1 = m01 & m10; // true if both are 1
+
+ // return 1 if primary is 1 and secondary is 0 or
+ // primary is 0 and secondary is 1
+ mask |= ((dp0 & ds1) | (dp1 & ds0)) << kRectStaysRect_Shift;
+ }
+
+ return SkToU8(mask);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkMatrix::setTranslate(SkScalar dx, SkScalar dy) {
+ if (SkScalarAs2sCompliment(dx) | SkScalarAs2sCompliment(dy)) {
+ fMat[kMTransX] = dx;
+ fMat[kMTransY] = dy;
+
+ fMat[kMScaleX] = fMat[kMScaleY] = SK_Scalar1;
+ fMat[kMSkewX] = fMat[kMSkewY] =
+ fMat[kMPersp0] = fMat[kMPersp1] = 0;
+ fMat[kMPersp2] = kMatrix22Elem;
+
+ this->setTypeMask(kTranslate_Mask | kRectStaysRect_Mask);
+ } else {
+ this->reset();
+ }
+}
+
+bool SkMatrix::preTranslate(SkScalar dx, SkScalar dy) {
+ if (has_perspective(*this)) {
+ SkMatrix m;
+ m.setTranslate(dx, dy);
+ return this->preConcat(m);
+ }
+
+ if (SkScalarAs2sCompliment(dx) | SkScalarAs2sCompliment(dy)) {
+ fMat[kMTransX] += SkScalarMul(fMat[kMScaleX], dx) +
+ SkScalarMul(fMat[kMSkewX], dy);
+ fMat[kMTransY] += SkScalarMul(fMat[kMSkewY], dx) +
+ SkScalarMul(fMat[kMScaleY], dy);
+
+ this->setTypeMask(kUnknown_Mask);
+ }
+ return true;
+}
+
+bool SkMatrix::postTranslate(SkScalar dx, SkScalar dy) {
+ if (has_perspective(*this)) {
+ SkMatrix m;
+ m.setTranslate(dx, dy);
+ return this->postConcat(m);
+ }
+
+ if (SkScalarAs2sCompliment(dx) | SkScalarAs2sCompliment(dy)) {
+ fMat[kMTransX] += dx;
+ fMat[kMTransY] += dy;
+ this->setTypeMask(kUnknown_Mask);
+ }
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkMatrix::setScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
+ fMat[kMScaleX] = sx;
+ fMat[kMScaleY] = sy;
+ fMat[kMTransX] = px - SkScalarMul(sx, px);
+ fMat[kMTransY] = py - SkScalarMul(sy, py);
+ fMat[kMPersp2] = kMatrix22Elem;
+
+ fMat[kMSkewX] = fMat[kMSkewY] =
+ fMat[kMPersp0] = fMat[kMPersp1] = 0;
+
+ this->setTypeMask(kScale_Mask | kTranslate_Mask | kRectStaysRect_Mask);
+}
+
+void SkMatrix::setScale(SkScalar sx, SkScalar sy) {
+ fMat[kMScaleX] = sx;
+ fMat[kMScaleY] = sy;
+ fMat[kMPersp2] = kMatrix22Elem;
+
+ fMat[kMTransX] = fMat[kMTransY] =
+ fMat[kMSkewX] = fMat[kMSkewY] =
+ fMat[kMPersp0] = fMat[kMPersp1] = 0;
+
+ this->setTypeMask(kScale_Mask | kRectStaysRect_Mask);
+}
+
+bool SkMatrix::preScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
+ SkMatrix m;
+ m.setScale(sx, sy, px, py);
+ return this->preConcat(m);
+}
+
+bool SkMatrix::preScale(SkScalar sx, SkScalar sy) {
+ SkMatrix m;
+ m.setScale(sx, sy);
+ return this->preConcat(m);
+}
+
+bool SkMatrix::postScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
+ SkMatrix m;
+ m.setScale(sx, sy, px, py);
+ return this->postConcat(m);
+}
+
+bool SkMatrix::postScale(SkScalar sx, SkScalar sy) {
+ SkMatrix m;
+ m.setScale(sx, sy);
+ return this->postConcat(m);
+}
+
+#ifdef SK_SCALAR_IS_FIXED
+ static inline SkFixed roundidiv(SkFixed numer, int denom) {
+ int ns = numer >> 31;
+ int ds = denom >> 31;
+ numer = (numer ^ ns) - ns;
+ denom = (denom ^ ds) - ds;
+
+ SkFixed answer = (numer + (denom >> 1)) / denom;
+ int as = ns ^ ds;
+ return (answer ^ as) - as;
+ }
+#endif
+
+// this guy perhaps can go away, if we have a fract/high-precision way to
+// scale matrices
+bool SkMatrix::postIDiv(int divx, int divy) {
+ if (divx == 0 || divy == 0) {
+ return false;
+ }
+
+#ifdef SK_SCALAR_IS_FIXED
+ fMat[kMScaleX] = roundidiv(fMat[kMScaleX], divx);
+ fMat[kMSkewX] = roundidiv(fMat[kMSkewX], divx);
+ fMat[kMTransX] = roundidiv(fMat[kMTransX], divx);
+
+ fMat[kMScaleY] = roundidiv(fMat[kMScaleY], divy);
+ fMat[kMSkewY] = roundidiv(fMat[kMSkewY], divy);
+ fMat[kMTransY] = roundidiv(fMat[kMTransY], divy);
+#else
+ const float invX = 1.f / divx;
+ const float invY = 1.f / divy;
+
+ fMat[kMScaleX] *= invX;
+ fMat[kMSkewX] *= invX;
+ fMat[kMTransX] *= invX;
+
+ fMat[kMScaleY] *= invY;
+ fMat[kMSkewY] *= invY;
+ fMat[kMTransY] *= invY;
+#endif
+
+ this->setTypeMask(kUnknown_Mask);
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+
+void SkMatrix::setSinCos(SkScalar sinV, SkScalar cosV,
+ SkScalar px, SkScalar py) {
+ const SkScalar oneMinusCosV = SK_Scalar1 - cosV;
+
+ fMat[kMScaleX] = cosV;
+ fMat[kMSkewX] = -sinV;
+ fMat[kMTransX] = SkScalarMul(sinV, py) + SkScalarMul(oneMinusCosV, px);
+
+ fMat[kMSkewY] = sinV;
+ fMat[kMScaleY] = cosV;
+ fMat[kMTransY] = SkScalarMul(-sinV, px) + SkScalarMul(oneMinusCosV, py);
+
+ fMat[kMPersp0] = fMat[kMPersp1] = 0;
+ fMat[kMPersp2] = kMatrix22Elem;
+
+ this->setTypeMask(kUnknown_Mask);
+}
+
+void SkMatrix::setSinCos(SkScalar sinV, SkScalar cosV) {
+ fMat[kMScaleX] = cosV;
+ fMat[kMSkewX] = -sinV;
+ fMat[kMTransX] = 0;
+
+ fMat[kMSkewY] = sinV;
+ fMat[kMScaleY] = cosV;
+ fMat[kMTransY] = 0;
+
+ fMat[kMPersp0] = fMat[kMPersp1] = 0;
+ fMat[kMPersp2] = kMatrix22Elem;
+
+ this->setTypeMask(kUnknown_Mask);
+}
+
+void SkMatrix::setRotate(SkScalar degrees, SkScalar px, SkScalar py) {
+ SkScalar sinV, cosV;
+ sinV = SkScalarSinCos(SkDegreesToRadians(degrees), &cosV);
+ this->setSinCos(sinV, cosV, px, py);
+}
+
+void SkMatrix::setRotate(SkScalar degrees) {
+ SkScalar sinV, cosV;
+ sinV = SkScalarSinCos(SkDegreesToRadians(degrees), &cosV);
+ this->setSinCos(sinV, cosV);
+}
+
+bool SkMatrix::preRotate(SkScalar degrees, SkScalar px, SkScalar py) {
+ SkMatrix m;
+ m.setRotate(degrees, px, py);
+ return this->preConcat(m);
+}
+
+bool SkMatrix::preRotate(SkScalar degrees) {
+ SkMatrix m;
+ m.setRotate(degrees);
+ return this->preConcat(m);
+}
+
+bool SkMatrix::postRotate(SkScalar degrees, SkScalar px, SkScalar py) {
+ SkMatrix m;
+ m.setRotate(degrees, px, py);
+ return this->postConcat(m);
+}
+
+bool SkMatrix::postRotate(SkScalar degrees) {
+ SkMatrix m;
+ m.setRotate(degrees);
+ return this->postConcat(m);
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+
+void SkMatrix::setSkew(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
+ fMat[kMScaleX] = SK_Scalar1;
+ fMat[kMSkewX] = sx;
+ fMat[kMTransX] = SkScalarMul(-sx, py);
+
+ fMat[kMSkewY] = sy;
+ fMat[kMScaleY] = SK_Scalar1;
+ fMat[kMTransY] = SkScalarMul(-sy, px);
+
+ fMat[kMPersp0] = fMat[kMPersp1] = 0;
+ fMat[kMPersp2] = kMatrix22Elem;
+
+ this->setTypeMask(kUnknown_Mask);
+}
+
+void SkMatrix::setSkew(SkScalar sx, SkScalar sy) {
+ fMat[kMScaleX] = SK_Scalar1;
+ fMat[kMSkewX] = sx;
+ fMat[kMTransX] = 0;
+
+ fMat[kMSkewY] = sy;
+ fMat[kMScaleY] = SK_Scalar1;
+ fMat[kMTransY] = 0;
+
+ fMat[kMPersp0] = fMat[kMPersp1] = 0;
+ fMat[kMPersp2] = kMatrix22Elem;
+
+ this->setTypeMask(kUnknown_Mask);
+}
+
+bool SkMatrix::preSkew(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
+ SkMatrix m;
+ m.setSkew(sx, sy, px, py);
+ return this->preConcat(m);
+}
+
+bool SkMatrix::preSkew(SkScalar sx, SkScalar sy) {
+ SkMatrix m;
+ m.setSkew(sx, sy);
+ return this->preConcat(m);
+}
+
+bool SkMatrix::postSkew(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
+ SkMatrix m;
+ m.setSkew(sx, sy, px, py);
+ return this->postConcat(m);
+}
+
+bool SkMatrix::postSkew(SkScalar sx, SkScalar sy) {
+ SkMatrix m;
+ m.setSkew(sx, sy);
+ return this->postConcat(m);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkMatrix::setRectToRect(const SkRect& src, const SkRect& dst,
+ ScaleToFit align)
+{
+ if (src.isEmpty()) {
+ this->reset();
+ return false;
+ }
+
+ if (dst.isEmpty()) {
+ bzero(fMat, 8 * sizeof(SkScalar));
+ this->setTypeMask(kScale_Mask | kRectStaysRect_Mask);
+ } else {
+ SkScalar tx, sx = SkScalarDiv(dst.width(), src.width());
+ SkScalar ty, sy = SkScalarDiv(dst.height(), src.height());
+ bool xLarger = false;
+
+ if (align != kFill_ScaleToFit) {
+ if (sx > sy) {
+ xLarger = true;
+ sx = sy;
+ } else {
+ sy = sx;
+ }
+ }
+
+ tx = dst.fLeft - SkScalarMul(src.fLeft, sx);
+ ty = dst.fTop - SkScalarMul(src.fTop, sy);
+ if (align == kCenter_ScaleToFit || align == kEnd_ScaleToFit) {
+ SkScalar diff;
+
+ if (xLarger) {
+ diff = dst.width() - SkScalarMul(src.width(), sy);
+ } else {
+ diff = dst.height() - SkScalarMul(src.height(), sy);
+ }
+
+ if (align == kCenter_ScaleToFit) {
+ diff = SkScalarHalf(diff);
+ }
+
+ if (xLarger) {
+ tx += diff;
+ } else {
+ ty += diff;
+ }
+ }
+
+ fMat[kMScaleX] = sx;
+ fMat[kMScaleY] = sy;
+ fMat[kMTransX] = tx;
+ fMat[kMTransY] = ty;
+ fMat[kMSkewX] = fMat[kMSkewY] =
+ fMat[kMPersp0] = fMat[kMPersp1] = 0;
+
+ this->setTypeMask(kScale_Mask | kTranslate_Mask | kRectStaysRect_Mask);
+ }
+ // shared cleanup
+ fMat[kMPersp2] = kMatrix22Elem;
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_SCALAR_IS_FLOAT
+ static inline int fixmuladdmul(float a, float b, float c, float d,
+ float* result) {
+ *result = a * b + c * d;
+ return true;
+ }
+
+ static inline int fixmuladdmulshiftmul(float a, float b, float c, float d,
+ int /*shift not used*/, float scale, float* result) {
+ *result = (a * b + c * d) * scale;
+ return true;
+ }
+
+ static inline bool rowcol3(const float row[], const float col[],
+ float* result) {
+ *result = row[0] * col[0] + row[1] * col[3] + row[2] * col[6];
+ return true;
+ }
+
+ static inline int negifaddoverflows(float& result, float a, float b) {
+ result = a + b;
+ return 0;
+ }
+#else
+ static inline bool fixmuladdmul(SkFixed a, SkFixed b, SkFixed c, SkFixed d,
+ SkFixed* result) {
+ Sk64 tmp1, tmp2;
+ tmp1.setMul(a, b);
+ tmp2.setMul(c, d);
+ tmp1.add(tmp2);
+ if (tmp1.isFixed()) {
+ *result = tmp1.getFixed();
+ return true;
+ }
+ return false;
+ }
+
+ static inline bool fixmuladdmulshiftmul(SkFixed a, SkFixed b, SkFixed c,
+ SkFixed d, int shift, SkFixed scale, SkFixed* result) {
+ Sk64 tmp1, tmp2;
+ tmp1.setMul(a, b);
+ tmp2.setMul(c, d);
+ tmp1.add(tmp2);
+
+ int32_t hi = SkAbs32(tmp1.fHi);
+ int afterShift = 16;
+ if (hi >> 15) {
+ int clz = 17 - SkCLZ(hi);
+ SkASSERT(clz > 0 && clz <= 16);
+ afterShift -= clz;
+ shift += clz;
+ }
+
+ tmp1.roundRight(shift + 16);
+ SkASSERT(tmp1.is32());
+
+ tmp1.setMul(tmp1.get32(), scale);
+ tmp1.roundRight(afterShift);
+ if (tmp1.is32()) {
+ *result = tmp1.get32();
+ return true;
+ }
+ return false;
+ }
+
+ static inline SkFixed fracmuladdmul(SkFixed a, SkFract b, SkFixed c,
+ SkFract d) {
+ Sk64 tmp1, tmp2;
+ tmp1.setMul(a, b);
+ tmp2.setMul(c, d);
+ tmp1.add(tmp2);
+ return tmp1.getFract();
+ }
+
+ static inline bool rowcol3(const SkFixed row[], const SkFixed col[],
+ SkFixed* result) {
+ Sk64 tmp1, tmp2;
+
+ tmp1.setMul(row[0], col[0]); // N * fixed
+ tmp2.setMul(row[1], col[3]); // N * fixed
+ tmp1.add(tmp2);
+
+ tmp2.setMul(row[2], col[6]); // N * fract
+ tmp2.roundRight(14); // make it fixed
+ tmp1.add(tmp2);
+
+ if (tmp1.isFixed()) {
+ *result = tmp1.getFixed();
+ return true;
+ }
+ return false;
+ }
+
+ static inline int negifaddoverflows(SkFixed& result, SkFixed a, SkFixed b) {
+ SkFixed c = a + b;
+ result = c;
+ return (c ^ a) & (c ^ b);
+ }
+#endif
+
+static void normalize_perspective(SkScalar mat[9]) {
+ if (SkScalarAbs(mat[SkMatrix::kMPersp2]) > kMatrix22Elem) {
+ for (int i = 0; i < 9; i++)
+ mat[i] = SkScalarHalf(mat[i]);
+ }
+}
+
+bool SkMatrix::setConcat(const SkMatrix& a, const SkMatrix& b) {
+ TypeMask aType = a.getType();
+ TypeMask bType = b.getType();
+
+ if (0 == aType) {
+ *this = b;
+ } else if (0 == bType) {
+ *this = a;
+ } else {
+ SkMatrix tmp;
+
+ if ((aType | bType) & kPerspective_Mask) {
+ if (!rowcol3(&a.fMat[0], &b.fMat[0], &tmp.fMat[kMScaleX])) {
+ return false;
+ }
+ if (!rowcol3(&a.fMat[0], &b.fMat[1], &tmp.fMat[kMSkewX])) {
+ return false;
+ }
+ if (!rowcol3(&a.fMat[0], &b.fMat[2], &tmp.fMat[kMTransX])) {
+ return false;
+ }
+
+ if (!rowcol3(&a.fMat[3], &b.fMat[0], &tmp.fMat[kMSkewY])) {
+ return false;
+ }
+ if (!rowcol3(&a.fMat[3], &b.fMat[1], &tmp.fMat[kMScaleY])) {
+ return false;
+ }
+ if (!rowcol3(&a.fMat[3], &b.fMat[2], &tmp.fMat[kMTransY])) {
+ return false;
+ }
+
+ if (!rowcol3(&a.fMat[6], &b.fMat[0], &tmp.fMat[kMPersp0])) {
+ return false;
+ }
+ if (!rowcol3(&a.fMat[6], &b.fMat[1], &tmp.fMat[kMPersp1])) {
+ return false;
+ }
+ if (!rowcol3(&a.fMat[6], &b.fMat[2], &tmp.fMat[kMPersp2])) {
+ return false;
+ }
+
+ normalize_perspective(tmp.fMat);
+ } else { // not perspective
+ if (!fixmuladdmul(a.fMat[kMScaleX], b.fMat[kMScaleX],
+ a.fMat[kMSkewX], b.fMat[kMSkewY], &tmp.fMat[kMScaleX])) {
+ return false;
+ }
+ if (!fixmuladdmul(a.fMat[kMScaleX], b.fMat[kMSkewX],
+ a.fMat[kMSkewX], b.fMat[kMScaleY], &tmp.fMat[kMSkewX])) {
+ return false;
+ }
+ if (!fixmuladdmul(a.fMat[kMScaleX], b.fMat[kMTransX],
+ a.fMat[kMSkewX], b.fMat[kMTransY], &tmp.fMat[kMTransX])) {
+ return false;
+ }
+ if (negifaddoverflows(tmp.fMat[kMTransX], tmp.fMat[kMTransX],
+ a.fMat[kMTransX]) < 0) {
+ return false;
+ }
+
+ if (!fixmuladdmul(a.fMat[kMSkewY], b.fMat[kMScaleX],
+ a.fMat[kMScaleY], b.fMat[kMSkewY], &tmp.fMat[kMSkewY])) {
+ return false;
+ }
+ if (!fixmuladdmul(a.fMat[kMSkewY], b.fMat[kMSkewX],
+ a.fMat[kMScaleY], b.fMat[kMScaleY], &tmp.fMat[kMScaleY])) {
+ return false;
+ }
+ if (!fixmuladdmul(a.fMat[kMSkewY], b.fMat[kMTransX],
+ a.fMat[kMScaleY], b.fMat[kMTransY], &tmp.fMat[kMTransY])) {
+ return false;
+ }
+ if (negifaddoverflows(tmp.fMat[kMTransY], tmp.fMat[kMTransY],
+ a.fMat[kMTransY]) < 0) {
+ return false;
+ }
+
+ tmp.fMat[kMPersp0] = tmp.fMat[kMPersp1] = 0;
+ tmp.fMat[kMPersp2] = kMatrix22Elem;
+ }
+ *this = tmp;
+ }
+ this->setTypeMask(kUnknown_Mask);
+ return true;
+}
+
+bool SkMatrix::preConcat(const SkMatrix& mat) {
+ // check for identity first, so we don't do a needless copy of ourselves
+ // to ourselves inside setConcat()
+ return mat.isIdentity() || this->setConcat(*this, mat);
+}
+
+bool SkMatrix::postConcat(const SkMatrix& mat) {
+ // check for identity first, so we don't do a needless copy of ourselves
+ // to ourselves inside setConcat()
+ return mat.isIdentity() || this->setConcat(mat, *this);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_SCALAR_IS_FLOAT
+ #define SkPerspMul(a, b) SkScalarMul(a, b)
+ #define SkScalarMulShift(a, b, s) SkScalarMul(a, b)
+ static float sk_inv_determinant(const float mat[9], int isPerspective,
+ int* /* (only used in Fixed case) */) {
+ double det;
+
+ if (isPerspective) {
+ det = mat[SkMatrix::kMScaleX] * ((double)mat[SkMatrix::kMScaleY] * mat[SkMatrix::kMPersp2] - (double)mat[SkMatrix::kMTransY] * mat[SkMatrix::kMPersp1]) +
+ mat[SkMatrix::kMSkewX] * ((double)mat[SkMatrix::kMTransY] * mat[SkMatrix::kMPersp0] - (double)mat[SkMatrix::kMSkewY] * mat[SkMatrix::kMPersp2]) +
+ mat[SkMatrix::kMTransX] * ((double)mat[SkMatrix::kMSkewY] * mat[SkMatrix::kMPersp1] - (double)mat[SkMatrix::kMScaleY] * mat[SkMatrix::kMPersp0]);
+ } else {
+ det = (double)mat[SkMatrix::kMScaleX] * mat[SkMatrix::kMScaleY] - (double)mat[SkMatrix::kMSkewX] * mat[SkMatrix::kMSkewY];
+ }
+
+ // Since the determinant is on the order of the square of the matrix members,
+ // compare to the square of the default nearly-zero constant
+ if (SkScalarNearlyZero((float)det, SK_ScalarNearlyZero * SK_ScalarNearlyZero)) {
+ return 0;
+ }
+ return (float)(1.0 / det);
+ }
+#else
+ #define SkPerspMul(a, b) SkFractMul(a, b)
+ #define SkScalarMulShift(a, b, s) SkMulShift(a, b, s)
+ static void set_muladdmul(Sk64* dst, int32_t a, int32_t b, int32_t c,
+ int32_t d) {
+ Sk64 tmp;
+ dst->setMul(a, b);
+ tmp.setMul(c, d);
+ dst->add(tmp);
+ }
+
+ static SkFixed sk_inv_determinant(const SkFixed mat[9], int isPerspective,
+ int* shift) {
+ Sk64 tmp1, tmp2;
+
+ if (isPerspective) {
+ tmp1.setMul(mat[SkMatrix::kMScaleX], fracmuladdmul(mat[SkMatrix::kMScaleY], mat[SkMatrix::kMPersp2], -mat[SkMatrix::kMTransY], mat[SkMatrix::kMPersp1]));
+ tmp2.setMul(mat[SkMatrix::kMSkewX], fracmuladdmul(mat[SkMatrix::kMTransY], mat[SkMatrix::kMPersp0], -mat[SkMatrix::kMSkewY], mat[SkMatrix::kMPersp2]));
+ tmp1.add(tmp2);
+ tmp2.setMul(mat[SkMatrix::kMTransX], fracmuladdmul(mat[SkMatrix::kMSkewY], mat[SkMatrix::kMPersp1], -mat[SkMatrix::kMScaleY], mat[SkMatrix::kMPersp0]));
+ tmp1.add(tmp2);
+ } else {
+ tmp1.setMul(mat[SkMatrix::kMScaleX], mat[SkMatrix::kMScaleY]);
+ tmp2.setMul(mat[SkMatrix::kMSkewX], mat[SkMatrix::kMSkewY]);
+ tmp1.sub(tmp2);
+ }
+
+ int s = tmp1.getClzAbs();
+ *shift = s;
+
+ SkFixed denom;
+ if (s <= 32) {
+ denom = tmp1.getShiftRight(33 - s);
+ } else {
+ denom = (int32_t)tmp1.fLo << (s - 33);
+ }
+
+ if (denom == 0) {
+ return 0;
+ }
+ /** This could perhaps be a special fractdiv function, since both of its
+ arguments are known to have bit 31 clear and bit 30 set (when they
+ are made positive), thus eliminating the need for calling clz()
+ */
+ return SkFractDiv(SK_Fract1, denom);
+ }
+#endif
+
+bool SkMatrix::invert(SkMatrix* inv) const {
+ int isPersp = has_perspective(*this);
+ int shift;
+ SkScalar scale = sk_inv_determinant(fMat, isPersp, &shift);
+
+ if (scale == 0) { // underflow
+ return false;
+ }
+
+ if (inv) {
+ SkMatrix tmp;
+ if (inv == this)
+ inv = &tmp;
+
+ if (isPersp) {
+ shift = 61 - shift;
+ inv->fMat[kMScaleX] = SkScalarMulShift(SkPerspMul(fMat[kMScaleY], fMat[kMPersp2]) - SkPerspMul(fMat[kMTransY], fMat[kMPersp1]), scale, shift);
+ inv->fMat[kMSkewX] = SkScalarMulShift(SkPerspMul(fMat[kMTransX], fMat[kMPersp1]) - SkPerspMul(fMat[kMSkewX], fMat[kMPersp2]), scale, shift);
+ inv->fMat[kMTransX] = SkScalarMulShift(SkScalarMul(fMat[kMSkewX], fMat[kMTransY]) - SkScalarMul(fMat[kMTransX], fMat[kMScaleY]), scale, shift);
+
+ inv->fMat[kMSkewY] = SkScalarMulShift(SkPerspMul(fMat[kMTransY], fMat[kMPersp0]) - SkPerspMul(fMat[kMSkewY], fMat[kMPersp2]), scale, shift);
+ inv->fMat[kMScaleY] = SkScalarMulShift(SkPerspMul(fMat[kMScaleX], fMat[kMPersp2]) - SkPerspMul(fMat[kMTransX], fMat[kMPersp0]), scale, shift);
+ inv->fMat[kMTransY] = SkScalarMulShift(SkScalarMul(fMat[kMTransX], fMat[kMSkewY]) - SkScalarMul(fMat[kMScaleX], fMat[kMTransY]), scale, shift);
+
+ inv->fMat[kMPersp0] = SkScalarMulShift(SkScalarMul(fMat[kMSkewY], fMat[kMPersp1]) - SkScalarMul(fMat[kMScaleY], fMat[kMPersp0]), scale, shift);
+ inv->fMat[kMPersp1] = SkScalarMulShift(SkScalarMul(fMat[kMSkewX], fMat[kMPersp0]) - SkScalarMul(fMat[kMScaleX], fMat[kMPersp1]), scale, shift);
+ inv->fMat[kMPersp2] = SkScalarMulShift(SkScalarMul(fMat[kMScaleX], fMat[kMScaleY]) - SkScalarMul(fMat[kMSkewX], fMat[kMSkewY]), scale, shift);
+#ifdef SK_SCALAR_IS_FIXED
+ if (SkAbs32(inv->fMat[kMPersp2]) > SK_Fixed1) {
+ Sk64 tmp;
+
+ tmp.set(SK_Fract1);
+ tmp.shiftLeft(16);
+ tmp.div(inv->fMat[kMPersp2], Sk64::kRound_DivOption);
+
+ SkFract scale = tmp.get32();
+
+ for (int i = 0; i < 9; i++) {
+ inv->fMat[i] = SkFractMul(inv->fMat[i], scale);
+ }
+ }
+ inv->fMat[kMPersp2] = SkFixedToFract(inv->fMat[kMPersp2]);
+#endif
+ } else { // not perspective
+#ifdef SK_SCALAR_IS_FIXED
+ Sk64 tx, ty;
+ int clzNumer;
+
+ // check the 2x2 for overflow
+ {
+ int32_t value = SkAbs32(fMat[kMScaleY]);
+ value |= SkAbs32(fMat[kMSkewX]);
+ value |= SkAbs32(fMat[kMScaleX]);
+ value |= SkAbs32(fMat[kMSkewY]);
+ clzNumer = SkCLZ(value);
+ if (shift - clzNumer > 31)
+ return false; // overflow
+ }
+
+ set_muladdmul(&tx, fMat[kMSkewX], fMat[kMTransY], -fMat[kMScaleY], fMat[kMTransX]);
+ set_muladdmul(&ty, fMat[kMSkewY], fMat[kMTransX], -fMat[kMScaleX], fMat[kMTransY]);
+ // check tx,ty for overflow
+ clzNumer = SkCLZ(SkAbs32(tx.fHi) | SkAbs32(ty.fHi));
+ if (shift - clzNumer > 14) {
+ return false; // overflow
+ }
+
+ int fixedShift = 61 - shift;
+ int sk64shift = 44 - shift + clzNumer;
+
+ inv->fMat[kMScaleX] = SkMulShift(fMat[kMScaleY], scale, fixedShift);
+ inv->fMat[kMSkewX] = SkMulShift(-fMat[kMSkewX], scale, fixedShift);
+ inv->fMat[kMTransX] = SkMulShift(tx.getShiftRight(33 - clzNumer), scale, sk64shift);
+
+ inv->fMat[kMSkewY] = SkMulShift(-fMat[kMSkewY], scale, fixedShift);
+ inv->fMat[kMScaleY] = SkMulShift(fMat[kMScaleX], scale, fixedShift);
+ inv->fMat[kMTransY] = SkMulShift(ty.getShiftRight(33 - clzNumer), scale, sk64shift);
+#else
+ inv->fMat[kMScaleX] = SkScalarMul(fMat[kMScaleY], scale);
+ inv->fMat[kMSkewX] = SkScalarMul(-fMat[kMSkewX], scale);
+ if (!fixmuladdmulshiftmul(fMat[kMSkewX], fMat[kMTransY], -fMat[kMScaleY], fMat[kMTransX], shift, scale, &inv->fMat[kMTransX])) {
+ return false;
+ }
+
+ inv->fMat[kMSkewY] = SkScalarMul(-fMat[kMSkewY], scale);
+ inv->fMat[kMScaleY] = SkScalarMul(fMat[kMScaleX], scale);
+ if (!fixmuladdmulshiftmul(fMat[kMSkewY], fMat[kMTransX], -fMat[kMScaleX], fMat[kMTransY], shift, scale, &inv->fMat[kMTransY])) {
+ return false;
+ }
+#endif
+ inv->fMat[kMPersp0] = 0;
+ inv->fMat[kMPersp1] = 0;
+ inv->fMat[kMPersp2] = kMatrix22Elem;
+ }
+
+ if (inv == &tmp) {
+ *(SkMatrix*)this = tmp;
+ }
+ inv->setTypeMask(kUnknown_Mask);
+ }
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkMatrix::Identity_pts(const SkMatrix& m, SkPoint dst[],
+ const SkPoint src[], int count) {
+ SkASSERT(m.getType() == 0);
+
+ if (dst != src && count > 0)
+ memcpy(dst, src, count * sizeof(SkPoint));
+}
+
+void SkMatrix::Trans_pts(const SkMatrix& m, SkPoint dst[],
+ const SkPoint src[], int count) {
+ SkASSERT(m.getType() == kTranslate_Mask);
+
+ if (count > 0) {
+ SkScalar tx = m.fMat[kMTransX];
+ SkScalar ty = m.fMat[kMTransY];
+ do {
+ dst->fY = src->fY + ty;
+ dst->fX = src->fX + tx;
+ src += 1;
+ dst += 1;
+ } while (--count);
+ }
+}
+
+void SkMatrix::Scale_pts(const SkMatrix& m, SkPoint dst[],
+ const SkPoint src[], int count) {
+ SkASSERT(m.getType() == kScale_Mask);
+
+ if (count > 0) {
+ SkScalar mx = m.fMat[kMScaleX];
+ SkScalar my = m.fMat[kMScaleY];
+ do {
+ dst->fY = SkScalarMul(src->fY, my);
+ dst->fX = SkScalarMul(src->fX, mx);
+ src += 1;
+ dst += 1;
+ } while (--count);
+ }
+}
+
+void SkMatrix::ScaleTrans_pts(const SkMatrix& m, SkPoint dst[],
+ const SkPoint src[], int count) {
+ SkASSERT(m.getType() == (kScale_Mask | kTranslate_Mask));
+
+ if (count > 0) {
+ SkScalar mx = m.fMat[kMScaleX];
+ SkScalar my = m.fMat[kMScaleY];
+ SkScalar tx = m.fMat[kMTransX];
+ SkScalar ty = m.fMat[kMTransY];
+ do {
+ dst->fY = SkScalarMulAdd(src->fY, my, ty);
+ dst->fX = SkScalarMulAdd(src->fX, mx, tx);
+ src += 1;
+ dst += 1;
+ } while (--count);
+ }
+}
+
+void SkMatrix::Rot_pts(const SkMatrix& m, SkPoint dst[],
+ const SkPoint src[], int count) {
+ SkASSERT((m.getType() & (kPerspective_Mask | kTranslate_Mask)) == 0);
+
+ if (count > 0) {
+ SkScalar mx = m.fMat[kMScaleX];
+ SkScalar my = m.fMat[kMScaleY];
+ SkScalar kx = m.fMat[kMSkewX];
+ SkScalar ky = m.fMat[kMSkewY];
+ do {
+ SkScalar sy = src->fY;
+ SkScalar sx = src->fX;
+ src += 1;
+ dst->fY = SkScalarMul(sx, ky) + SkScalarMul(sy, my);
+ dst->fX = SkScalarMul(sx, mx) + SkScalarMul(sy, kx);
+ dst += 1;
+ } while (--count);
+ }
+}
+
+void SkMatrix::RotTrans_pts(const SkMatrix& m, SkPoint dst[],
+ const SkPoint src[], int count) {
+ SkASSERT((m.getType() & kPerspective_Mask) == 0);
+
+ if (count > 0) {
+ SkScalar mx = m.fMat[kMScaleX];
+ SkScalar my = m.fMat[kMScaleY];
+ SkScalar kx = m.fMat[kMSkewX];
+ SkScalar ky = m.fMat[kMSkewY];
+ SkScalar tx = m.fMat[kMTransX];
+ SkScalar ty = m.fMat[kMTransY];
+ do {
+ SkScalar sy = src->fY;
+ SkScalar sx = src->fX;
+ src += 1;
+ dst->fY = SkScalarMul(sx, ky) + SkScalarMulAdd(sy, my, ty);
+ dst->fX = SkScalarMul(sx, mx) + SkScalarMulAdd(sy, kx, tx);
+ dst += 1;
+ } while (--count);
+ }
+}
+
+void SkMatrix::Persp_pts(const SkMatrix& m, SkPoint dst[],
+ const SkPoint src[], int count) {
+ SkASSERT(m.getType() & kPerspective_Mask);
+
+#ifdef SK_SCALAR_IS_FIXED
+ SkFixed persp2 = SkFractToFixed(m.fMat[kMPersp2]);
+#endif
+
+ if (count > 0) {
+ do {
+ SkScalar sy = src->fY;
+ SkScalar sx = src->fX;
+ src += 1;
+
+ SkScalar x = SkScalarMul(sx, m.fMat[kMScaleX]) +
+ SkScalarMul(sy, m.fMat[kMSkewX]) + m.fMat[kMTransX];
+ SkScalar y = SkScalarMul(sx, m.fMat[kMSkewY]) +
+ SkScalarMul(sy, m.fMat[kMScaleY]) + m.fMat[kMTransY];
+#ifdef SK_SCALAR_IS_FIXED
+ SkFixed z = SkFractMul(sx, m.fMat[kMPersp0]) +
+ SkFractMul(sy, m.fMat[kMPersp1]) + persp2;
+#else
+ float z = SkScalarMul(sx, m.fMat[kMPersp0]) +
+ SkScalarMulAdd(sy, m.fMat[kMPersp1], m.fMat[kMPersp2]);
+#endif
+ if (z) {
+ z = SkScalarFastInvert(z);
+ }
+
+ dst->fY = SkScalarMul(y, z);
+ dst->fX = SkScalarMul(x, z);
+ dst += 1;
+ } while (--count);
+ }
+}
+
+const SkMatrix::MapPtsProc SkMatrix::gMapPtsProcs[] = {
+ SkMatrix::Identity_pts, SkMatrix::Trans_pts,
+ SkMatrix::Scale_pts, SkMatrix::ScaleTrans_pts,
+ SkMatrix::Rot_pts, SkMatrix::RotTrans_pts,
+ SkMatrix::Rot_pts, SkMatrix::RotTrans_pts,
+ // repeat the persp proc 8 times
+ SkMatrix::Persp_pts, SkMatrix::Persp_pts,
+ SkMatrix::Persp_pts, SkMatrix::Persp_pts,
+ SkMatrix::Persp_pts, SkMatrix::Persp_pts,
+ SkMatrix::Persp_pts, SkMatrix::Persp_pts
+};
+
+void SkMatrix::mapPoints(SkPoint dst[], const SkPoint src[], int count) const {
+ SkASSERT((dst && src && count > 0) || count == 0);
+ // no partial overlap
+ SkASSERT(src == dst || SkAbs32((int32_t)(src - dst)) >= count);
+
+ this->getMapPtsProc()(*this, dst, src, count);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkMatrix::mapVectors(SkPoint dst[], const SkPoint src[], int count) const {
+ if (this->getType() & kPerspective_Mask) {
+ SkPoint origin;
+
+ MapXYProc proc = this->getMapXYProc();
+ proc(*this, 0, 0, &origin);
+
+ for (int i = count - 1; i >= 0; --i) {
+ SkPoint tmp;
+
+ proc(*this, src[i].fX, src[i].fY, &tmp);
+ dst[i].set(tmp.fX - origin.fX, tmp.fY - origin.fY);
+ }
+ } else {
+ SkMatrix tmp = *this;
+
+ tmp.fMat[kMTransX] = tmp.fMat[kMTransY] = 0;
+ tmp.clearTypeMask(kTranslate_Mask);
+ tmp.mapPoints(dst, src, count);
+ }
+}
+
+bool SkMatrix::mapRect(SkRect* dst, const SkRect& src) const {
+ SkASSERT(dst && &src);
+
+ if (this->rectStaysRect()) {
+ this->mapPoints((SkPoint*)dst, (const SkPoint*)&src, 2);
+ dst->sort();
+ return true;
+ } else {
+ SkPoint quad[4];
+
+ src.toQuad(quad);
+ this->mapPoints(quad, quad, 4);
+ dst->set(quad, 4);
+ return false;
+ }
+}
+
+SkScalar SkMatrix::mapRadius(SkScalar radius) const {
+ SkVector vec[2];
+
+ vec[0].set(radius, 0);
+ vec[1].set(0, radius);
+ this->mapVectors(vec, 2);
+
+ SkScalar d0 = vec[0].length();
+ SkScalar d1 = vec[1].length();
+
+ return SkScalarMean(d0, d1);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkMatrix::Persp_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
+ SkPoint* pt) {
+ SkASSERT(m.getType() & kPerspective_Mask);
+
+ SkScalar x = SkScalarMul(sx, m.fMat[kMScaleX]) +
+ SkScalarMul(sy, m.fMat[kMSkewX]) + m.fMat[kMTransX];
+ SkScalar y = SkScalarMul(sx, m.fMat[kMSkewY]) +
+ SkScalarMul(sy, m.fMat[kMScaleY]) + m.fMat[kMTransY];
+#ifdef SK_SCALAR_IS_FIXED
+ SkFixed z = SkFractMul(sx, m.fMat[kMPersp0]) +
+ SkFractMul(sy, m.fMat[kMPersp1]) +
+ SkFractToFixed(m.fMat[kMPersp2]);
+#else
+ float z = SkScalarMul(sx, m.fMat[kMPersp0]) +
+ SkScalarMul(sy, m.fMat[kMPersp1]) + m.fMat[kMPersp2];
+#endif
+ if (z) {
+ z = SkScalarFastInvert(z);
+ }
+ pt->fX = SkScalarMul(x, z);
+ pt->fY = SkScalarMul(y, z);
+}
+
+#ifdef SK_SCALAR_IS_FIXED
+static SkFixed fixmuladdmul(SkFixed a, SkFixed b, SkFixed c, SkFixed d) {
+ Sk64 tmp, tmp1;
+
+ tmp.setMul(a, b);
+ tmp1.setMul(c, d);
+ return tmp.addGetFixed(tmp1);
+// tmp.add(tmp1);
+// return tmp.getFixed();
+}
+#endif
+
+void SkMatrix::RotTrans_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
+ SkPoint* pt) {
+ SkASSERT((m.getType() & (kAffine_Mask | kPerspective_Mask)) == kAffine_Mask);
+
+#ifdef SK_SCALAR_IS_FIXED
+ pt->fX = fixmuladdmul(sx, m.fMat[kMScaleX], sy, m.fMat[kMSkewX]) +
+ m.fMat[kMTransX];
+ pt->fY = fixmuladdmul(sx, m.fMat[kMSkewY], sy, m.fMat[kMScaleY]) +
+ m.fMat[kMTransY];
+#else
+ pt->fX = SkScalarMul(sx, m.fMat[kMScaleX]) +
+ SkScalarMulAdd(sy, m.fMat[kMSkewX], m.fMat[kMTransX]);
+ pt->fY = SkScalarMul(sx, m.fMat[kMSkewY]) +
+ SkScalarMulAdd(sy, m.fMat[kMScaleY], m.fMat[kMTransY]);
+#endif
+}
+
+void SkMatrix::Rot_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
+ SkPoint* pt) {
+ SkASSERT((m.getType() & (kAffine_Mask | kPerspective_Mask))== kAffine_Mask);
+ SkASSERT(0 == m.fMat[kMTransX]);
+ SkASSERT(0 == m.fMat[kMTransY]);
+
+#ifdef SK_SCALAR_IS_FIXED
+ pt->fX = fixmuladdmul(sx, m.fMat[kMScaleX], sy, m.fMat[kMSkewX]);
+ pt->fY = fixmuladdmul(sx, m.fMat[kMSkewY], sy, m.fMat[kMScaleY]);
+#else
+ pt->fX = SkScalarMul(sx, m.fMat[kMScaleX]) +
+ SkScalarMulAdd(sy, m.fMat[kMSkewX], m.fMat[kMTransX]);
+ pt->fY = SkScalarMul(sx, m.fMat[kMSkewY]) +
+ SkScalarMulAdd(sy, m.fMat[kMScaleY], m.fMat[kMTransY]);
+#endif
+}
+
+void SkMatrix::ScaleTrans_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
+ SkPoint* pt) {
+ SkASSERT((m.getType() & (kScale_Mask | kAffine_Mask | kPerspective_Mask))
+ == kScale_Mask);
+
+ pt->fX = SkScalarMulAdd(sx, m.fMat[kMScaleX], m.fMat[kMTransX]);
+ pt->fY = SkScalarMulAdd(sy, m.fMat[kMScaleY], m.fMat[kMTransY]);
+}
+
+void SkMatrix::Scale_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
+ SkPoint* pt) {
+ SkASSERT((m.getType() & (kScale_Mask | kAffine_Mask | kPerspective_Mask))
+ == kScale_Mask);
+ SkASSERT(0 == m.fMat[kMTransX]);
+ SkASSERT(0 == m.fMat[kMTransY]);
+
+ pt->fX = SkScalarMul(sx, m.fMat[kMScaleX]);
+ pt->fY = SkScalarMul(sy, m.fMat[kMScaleY]);
+}
+
+void SkMatrix::Trans_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
+ SkPoint* pt) {
+ SkASSERT(m.getType() == kTranslate_Mask);
+
+ pt->fX = sx + m.fMat[kMTransX];
+ pt->fY = sy + m.fMat[kMTransY];
+}
+
+void SkMatrix::Identity_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
+ SkPoint* pt) {
+ SkASSERT(0 == m.getType());
+
+ pt->fX = sx;
+ pt->fY = sy;
+}
+
+const SkMatrix::MapXYProc SkMatrix::gMapXYProcs[] = {
+ SkMatrix::Identity_xy, SkMatrix::Trans_xy,
+ SkMatrix::Scale_xy, SkMatrix::ScaleTrans_xy,
+ SkMatrix::Rot_xy, SkMatrix::RotTrans_xy,
+ SkMatrix::Rot_xy, SkMatrix::RotTrans_xy,
+ // repeat the persp proc 8 times
+ SkMatrix::Persp_xy, SkMatrix::Persp_xy,
+ SkMatrix::Persp_xy, SkMatrix::Persp_xy,
+ SkMatrix::Persp_xy, SkMatrix::Persp_xy,
+ SkMatrix::Persp_xy, SkMatrix::Persp_xy
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+// if its nearly zero (just made up 26, perhaps it should be bigger or smaller)
+#ifdef SK_SCALAR_IS_FIXED
+ typedef SkFract SkPerspElemType;
+ #define PerspNearlyZero(x) (SkAbs32(x) < (SK_Fract1 >> 26))
+#else
+ typedef float SkPerspElemType;
+ #define PerspNearlyZero(x) SkScalarNearlyZero(x, (1.0f / (1 << 26)))
+#endif
+
+bool SkMatrix::fixedStepInX(SkScalar y, SkFixed* stepX, SkFixed* stepY) const {
+ if (PerspNearlyZero(fMat[kMPersp0])) {
+ if (stepX || stepY) {
+ if (PerspNearlyZero(fMat[kMPersp1]) &&
+ PerspNearlyZero(fMat[kMPersp2] - kMatrix22Elem)) {
+ if (stepX) {
+ *stepX = SkScalarToFixed(fMat[kMScaleX]);
+ }
+ if (stepY) {
+ *stepY = SkScalarToFixed(fMat[kMSkewY]);
+ }
+ } else {
+#ifdef SK_SCALAR_IS_FIXED
+ SkFixed z = SkFractMul(y, fMat[kMPersp1]) +
+ SkFractToFixed(fMat[kMPersp2]);
+#else
+ float z = y * fMat[kMPersp1] + fMat[kMPersp2];
+#endif
+ if (stepX) {
+ *stepX = SkScalarToFixed(SkScalarDiv(fMat[kMScaleX], z));
+ }
+ if (stepY) {
+ *stepY = SkScalarToFixed(SkScalarDiv(fMat[kMSkewY], z));
+ }
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkPerspIter.h"
+
+SkPerspIter::SkPerspIter(const SkMatrix& m, SkScalar x0, SkScalar y0, int count)
+ : fMatrix(m), fSX(x0), fSY(y0), fCount(count) {
+ SkPoint pt;
+
+ SkMatrix::Persp_xy(m, x0, y0, &pt);
+ fX = SkScalarToFixed(pt.fX);
+ fY = SkScalarToFixed(pt.fY);
+}
+
+int SkPerspIter::next() {
+ int n = fCount;
+
+ if (0 == n) {
+ return 0;
+ }
+ SkPoint pt;
+ SkFixed x = fX;
+ SkFixed y = fY;
+ SkFixed dx, dy;
+
+ if (n >= kCount) {
+ n = kCount;
+ fSX += SkIntToScalar(kCount);
+ SkMatrix::Persp_xy(fMatrix, fSX, fSY, &pt);
+ fX = SkScalarToFixed(pt.fX);
+ fY = SkScalarToFixed(pt.fY);
+ dx = (fX - x) >> kShift;
+ dy = (fY - y) >> kShift;
+ } else {
+ fSX += SkIntToScalar(n);
+ SkMatrix::Persp_xy(fMatrix, fSX, fSY, &pt);
+ fX = SkScalarToFixed(pt.fX);
+ fY = SkScalarToFixed(pt.fY);
+ dx = (fX - x) / n;
+ dy = (fY - y) / n;
+ }
+
+ SkFixed* p = fStorage;
+ for (int i = 0; i < n; i++) {
+ *p++ = x; x += dx;
+ *p++ = y; y += dy;
+ }
+
+ fCount -= n;
+ return n;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_SCALAR_IS_FIXED
+
+static inline bool poly_to_point(SkPoint* pt, const SkPoint poly[], int count) {
+ SkFixed x = SK_Fixed1, y = SK_Fixed1;
+ SkPoint pt1, pt2;
+ Sk64 w1, w2;
+
+ if (count > 1) {
+ pt1.fX = poly[1].fX - poly[0].fX;
+ pt1.fY = poly[1].fY - poly[0].fY;
+ y = SkPoint::Length(pt1.fX, pt1.fY);
+ if (y == 0) {
+ return false;
+ }
+ switch (count) {
+ case 2:
+ break;
+ case 3:
+ pt2.fX = poly[0].fY - poly[2].fY;
+ pt2.fY = poly[2].fX - poly[0].fX;
+ goto CALC_X;
+ default:
+ pt2.fX = poly[0].fY - poly[3].fY;
+ pt2.fY = poly[3].fX - poly[0].fX;
+ CALC_X:
+ w1.setMul(pt1.fX, pt2.fX);
+ w2.setMul(pt1.fY, pt2.fY);
+ w1.add(w2);
+ w1.div(y, Sk64::kRound_DivOption);
+ if (!w1.is32()) {
+ return false;
+ }
+ x = w1.get32();
+ break;
+ }
+ }
+ pt->set(x, y);
+ return true;
+}
+
+bool SkMatrix::Poly2Proc(const SkPoint srcPt[], SkMatrix* dst,
+ const SkPoint& scalePt) {
+ // need to check if SkFixedDiv overflows...
+
+ const SkFixed scale = scalePt.fY;
+ dst->fMat[kMScaleX] = SkFixedDiv(srcPt[1].fY - srcPt[0].fY, scale);
+ dst->fMat[kMSkewY] = SkFixedDiv(srcPt[0].fX - srcPt[1].fX, scale);
+ dst->fMat[kMPersp0] = 0;
+ dst->fMat[kMSkewX] = SkFixedDiv(srcPt[1].fX - srcPt[0].fX, scale);
+ dst->fMat[kMScaleY] = SkFixedDiv(srcPt[1].fY - srcPt[0].fY, scale);
+ dst->fMat[kMPersp1] = 0;
+ dst->fMat[kMTransX] = srcPt[0].fX;
+ dst->fMat[kMTransY] = srcPt[0].fY;
+ dst->fMat[kMPersp2] = SK_Fract1;
+ dst->setTypeMask(kUnknown_Mask);
+ return true;
+}
+
+bool SkMatrix::Poly3Proc(const SkPoint srcPt[], SkMatrix* dst,
+ const SkPoint& scale) {
+ // really, need to check if SkFixedDiv overflow'd
+
+ dst->fMat[kMScaleX] = SkFixedDiv(srcPt[2].fX - srcPt[0].fX, scale.fX);
+ dst->fMat[kMSkewY] = SkFixedDiv(srcPt[2].fY - srcPt[0].fY, scale.fX);
+ dst->fMat[kMPersp0] = 0;
+ dst->fMat[kMSkewX] = SkFixedDiv(srcPt[1].fX - srcPt[0].fX, scale.fY);
+ dst->fMat[kMScaleY] = SkFixedDiv(srcPt[1].fY - srcPt[0].fY, scale.fY);
+ dst->fMat[kMPersp1] = 0;
+ dst->fMat[kMTransX] = srcPt[0].fX;
+ dst->fMat[kMTransY] = srcPt[0].fY;
+ dst->fMat[kMPersp2] = SK_Fract1;
+ dst->setTypeMask(kUnknown_Mask);
+ return true;
+}
+
+bool SkMatrix::Poly4Proc(const SkPoint srcPt[], SkMatrix* dst,
+ const SkPoint& scale) {
+ SkFract a1, a2;
+ SkFixed x0, y0, x1, y1, x2, y2;
+
+ x0 = srcPt[2].fX - srcPt[0].fX;
+ y0 = srcPt[2].fY - srcPt[0].fY;
+ x1 = srcPt[2].fX - srcPt[1].fX;
+ y1 = srcPt[2].fY - srcPt[1].fY;
+ x2 = srcPt[2].fX - srcPt[3].fX;
+ y2 = srcPt[2].fY - srcPt[3].fY;
+
+ /* check if abs(x2) > abs(y2) */
+ if ( x2 > 0 ? y2 > 0 ? x2 > y2 : x2 > -y2 : y2 > 0 ? -x2 > y2 : x2 < y2) {
+ SkFixed denom = SkMulDiv(x1, y2, x2) - y1;
+ if (0 == denom) {
+ return false;
+ }
+ a1 = SkFractDiv(SkMulDiv(x0 - x1, y2, x2) - y0 + y1, denom);
+ } else {
+ SkFixed denom = x1 - SkMulDiv(y1, x2, y2);
+ if (0 == denom) {
+ return false;
+ }
+ a1 = SkFractDiv(x0 - x1 - SkMulDiv(y0 - y1, x2, y2), denom);
+ }
+
+ /* check if abs(x1) > abs(y1) */
+ if ( x1 > 0 ? y1 > 0 ? x1 > y1 : x1 > -y1 : y1 > 0 ? -x1 > y1 : x1 < y1) {
+ SkFixed denom = y2 - SkMulDiv(x2, y1, x1);
+ if (0 == denom) {
+ return false;
+ }
+ a2 = SkFractDiv(y0 - y2 - SkMulDiv(x0 - x2, y1, x1), denom);
+ } else {
+ SkFixed denom = SkMulDiv(y2, x1, y1) - x2;
+ if (0 == denom) {
+ return false;
+ }
+ a2 = SkFractDiv(SkMulDiv(y0 - y2, x1, y1) - x0 + x2, denom);
+ }
+
+ // need to check if SkFixedDiv overflows...
+ dst->fMat[kMScaleX] = SkFixedDiv(SkFractMul(a2, srcPt[3].fX) +
+ srcPt[3].fX - srcPt[0].fX, scale.fX);
+ dst->fMat[kMSkewY] = SkFixedDiv(SkFractMul(a2, srcPt[3].fY) +
+ srcPt[3].fY - srcPt[0].fY, scale.fX);
+ dst->fMat[kMPersp0] = SkFixedDiv(a2, scale.fX);
+ dst->fMat[kMSkewX] = SkFixedDiv(SkFractMul(a1, srcPt[1].fX) +
+ srcPt[1].fX - srcPt[0].fX, scale.fY);
+ dst->fMat[kMScaleY] = SkFixedDiv(SkFractMul(a1, srcPt[1].fY) +
+ srcPt[1].fY - srcPt[0].fY, scale.fY);
+ dst->fMat[kMPersp1] = SkFixedDiv(a1, scale.fY);
+ dst->fMat[kMTransX] = srcPt[0].fX;
+ dst->fMat[kMTransY] = srcPt[0].fY;
+ dst->fMat[kMPersp2] = SK_Fract1;
+ dst->setTypeMask(kUnknown_Mask);
+ return true;
+}
+
+#else /* Scalar is float */
+
+static inline bool checkForZero(float x) {
+ return x*x == 0;
+}
+
+static inline bool poly_to_point(SkPoint* pt, const SkPoint poly[], int count) {
+ float x = 1, y = 1;
+ SkPoint pt1, pt2;
+
+ if (count > 1) {
+ pt1.fX = poly[1].fX - poly[0].fX;
+ pt1.fY = poly[1].fY - poly[0].fY;
+ y = SkPoint::Length(pt1.fX, pt1.fY);
+ if (checkForZero(y)) {
+ return false;
+ }
+ switch (count) {
+ case 2:
+ break;
+ case 3:
+ pt2.fX = poly[0].fY - poly[2].fY;
+ pt2.fY = poly[2].fX - poly[0].fX;
+ goto CALC_X;
+ default:
+ pt2.fX = poly[0].fY - poly[3].fY;
+ pt2.fY = poly[3].fX - poly[0].fX;
+ CALC_X:
+ x = SkScalarDiv(SkScalarMul(pt1.fX, pt2.fX) +
+ SkScalarMul(pt1.fY, pt2.fY), y);
+ break;
+ }
+ }
+ pt->set(x, y);
+ return true;
+}
+
+bool SkMatrix::Poly2Proc(const SkPoint srcPt[], SkMatrix* dst,
+ const SkPoint& scale) {
+ float invScale = 1 / scale.fY;
+
+ dst->fMat[kMScaleX] = (srcPt[1].fY - srcPt[0].fY) * invScale;
+ dst->fMat[kMSkewY] = (srcPt[0].fX - srcPt[1].fX) * invScale;
+ dst->fMat[kMPersp0] = 0;
+ dst->fMat[kMSkewX] = (srcPt[1].fX - srcPt[0].fX) * invScale;
+ dst->fMat[kMScaleY] = (srcPt[1].fY - srcPt[0].fY) * invScale;
+ dst->fMat[kMPersp1] = 0;
+ dst->fMat[kMTransX] = srcPt[0].fX;
+ dst->fMat[kMTransY] = srcPt[0].fY;
+ dst->fMat[kMPersp2] = 1;
+ dst->setTypeMask(kUnknown_Mask);
+ return true;
+}
+
+bool SkMatrix::Poly3Proc(const SkPoint srcPt[], SkMatrix* dst,
+ const SkPoint& scale) {
+ float invScale = 1 / scale.fX;
+ dst->fMat[kMScaleX] = (srcPt[2].fX - srcPt[0].fX) * invScale;
+ dst->fMat[kMSkewY] = (srcPt[2].fY - srcPt[0].fY) * invScale;
+ dst->fMat[kMPersp0] = 0;
+
+ invScale = 1 / scale.fY;
+ dst->fMat[kMSkewX] = (srcPt[1].fX - srcPt[0].fX) * invScale;
+ dst->fMat[kMScaleY] = (srcPt[1].fY - srcPt[0].fY) * invScale;
+ dst->fMat[kMPersp1] = 0;
+
+ dst->fMat[kMTransX] = srcPt[0].fX;
+ dst->fMat[kMTransY] = srcPt[0].fY;
+ dst->fMat[kMPersp2] = 1;
+ dst->setTypeMask(kUnknown_Mask);
+ return true;
+}
+
+bool SkMatrix::Poly4Proc(const SkPoint srcPt[], SkMatrix* dst,
+ const SkPoint& scale) {
+ float a1, a2;
+ float x0, y0, x1, y1, x2, y2;
+
+ x0 = srcPt[2].fX - srcPt[0].fX;
+ y0 = srcPt[2].fY - srcPt[0].fY;
+ x1 = srcPt[2].fX - srcPt[1].fX;
+ y1 = srcPt[2].fY - srcPt[1].fY;
+ x2 = srcPt[2].fX - srcPt[3].fX;
+ y2 = srcPt[2].fY - srcPt[3].fY;
+
+ /* check if abs(x2) > abs(y2) */
+ if ( x2 > 0 ? y2 > 0 ? x2 > y2 : x2 > -y2 : y2 > 0 ? -x2 > y2 : x2 < y2) {
+ float denom = SkScalarMulDiv(x1, y2, x2) - y1;
+ if (checkForZero(denom)) {
+ return false;
+ }
+ a1 = SkScalarDiv(SkScalarMulDiv(x0 - x1, y2, x2) - y0 + y1, denom);
+ } else {
+ float denom = x1 - SkScalarMulDiv(y1, x2, y2);
+ if (checkForZero(denom)) {
+ return false;
+ }
+ a1 = SkScalarDiv(x0 - x1 - SkScalarMulDiv(y0 - y1, x2, y2), denom);
+ }
+
+ /* check if abs(x1) > abs(y1) */
+ if ( x1 > 0 ? y1 > 0 ? x1 > y1 : x1 > -y1 : y1 > 0 ? -x1 > y1 : x1 < y1) {
+ float denom = y2 - SkScalarMulDiv(x2, y1, x1);
+ if (checkForZero(denom)) {
+ return false;
+ }
+ a2 = SkScalarDiv(y0 - y2 - SkScalarMulDiv(x0 - x2, y1, x1), denom);
+ } else {
+ float denom = SkScalarMulDiv(y2, x1, y1) - x2;
+ if (checkForZero(denom)) {
+ return false;
+ }
+ a2 = SkScalarDiv(SkScalarMulDiv(y0 - y2, x1, y1) - x0 + x2, denom);
+ }
+
+ float invScale = 1 / scale.fX;
+ dst->fMat[kMScaleX] = SkScalarMul(SkScalarMul(a2, srcPt[3].fX) +
+ srcPt[3].fX - srcPt[0].fX, invScale);
+ dst->fMat[kMSkewY] = SkScalarMul(SkScalarMul(a2, srcPt[3].fY) +
+ srcPt[3].fY - srcPt[0].fY, invScale);
+ dst->fMat[kMPersp0] = SkScalarMul(a2, invScale);
+ invScale = 1 / scale.fY;
+ dst->fMat[kMSkewX] = SkScalarMul(SkScalarMul(a1, srcPt[1].fX) +
+ srcPt[1].fX - srcPt[0].fX, invScale);
+ dst->fMat[kMScaleY] = SkScalarMul(SkScalarMul(a1, srcPt[1].fY) +
+ srcPt[1].fY - srcPt[0].fY, invScale);
+ dst->fMat[kMPersp1] = SkScalarMul(a1, invScale);
+ dst->fMat[kMTransX] = srcPt[0].fX;
+ dst->fMat[kMTransY] = srcPt[0].fY;
+ dst->fMat[kMPersp2] = 1;
+ dst->setTypeMask(kUnknown_Mask);
+ return true;
+}
+
+#endif
+
+typedef bool (*PolyMapProc)(const SkPoint[], SkMatrix*, const SkPoint&);
+
+/* Taken from Rob Johnson's original sample code in QuickDraw GX
+*/
+bool SkMatrix::setPolyToPoly(const SkPoint src[], const SkPoint dst[],
+ int count) {
+ if ((unsigned)count > 4) {
+ SkDebugf("--- SkMatrix::setPolyToPoly count out of range %d\n", count);
+ return false;
+ }
+
+ if (0 == count) {
+ this->reset();
+ return true;
+ }
+ if (1 == count) {
+ this->setTranslate(dst[0].fX - src[0].fX, dst[0].fY - src[0].fY);
+ return true;
+ }
+
+ SkPoint scale;
+ if (!poly_to_point(&scale, src, count) ||
+ SkScalarNearlyZero(scale.fX) ||
+ SkScalarNearlyZero(scale.fY)) {
+ return false;
+ }
+
+ static const PolyMapProc gPolyMapProcs[] = {
+ SkMatrix::Poly2Proc, SkMatrix::Poly3Proc, SkMatrix::Poly4Proc
+ };
+ PolyMapProc proc = gPolyMapProcs[count - 2];
+
+ SkMatrix tempMap, result;
+ tempMap.setTypeMask(kUnknown_Mask);
+
+ if (!proc(src, &tempMap, scale)) {
+ return false;
+ }
+ if (!tempMap.invert(&result)) {
+ return false;
+ }
+ if (!proc(dst, &tempMap, scale)) {
+ return false;
+ }
+ if (!result.setConcat(tempMap, result)) {
+ return false;
+ }
+ *this = result;
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkMatrix::dump() const {
+ SkString str;
+ this->toDumpString(&str);
+ SkDebugf("%s\n", str.c_str());
+}
+
+void SkMatrix::toDumpString(SkString* str) const {
+#ifdef SK_CAN_USE_FLOAT
+ str->printf("[%8.4f %8.4f %8.4f][%8.4f %8.4f %8.4f][%8.4f %8.4f %8.4f]",
+#ifdef SK_SCALAR_IS_FLOAT
+ fMat[0], fMat[1], fMat[2], fMat[3], fMat[4], fMat[5],
+ fMat[6], fMat[7], fMat[8]);
+#else
+ SkFixedToFloat(fMat[0]), SkFixedToFloat(fMat[1]), SkFixedToFloat(fMat[2]),
+ SkFixedToFloat(fMat[3]), SkFixedToFloat(fMat[4]), SkFixedToFloat(fMat[5]),
+ SkFractToFloat(fMat[6]), SkFractToFloat(fMat[7]), SkFractToFloat(fMat[8]));
+#endif
+#else // can't use float
+ str->printf("[%x %x %x][%x %x %x][%x %x %x]",
+ fMat[0], fMat[1], fMat[2], fMat[3], fMat[4], fMat[5],
+ fMat[6], fMat[7], fMat[8]);
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+void SkMatrix::UnitTest() {
+#ifdef SK_SUPPORT_UNITTEST
+ SkMatrix mat, inverse, iden1, iden2;
+
+ mat.reset();
+ mat.setTranslate(SK_Scalar1, SK_Scalar1);
+ mat.invert(&inverse);
+ inverse.dump();
+ iden1.setConcat(mat, inverse);
+ iden1.dump();
+
+ mat.setScale(SkIntToScalar(2), SkIntToScalar(2));
+ mat.invert(&inverse);
+ inverse.dump();
+ iden1.setConcat(mat, inverse);
+ iden1.dump();
+
+ mat.setScale(SK_Scalar1/2, SK_Scalar1/2);
+ mat.invert(&inverse);
+ inverse.dump();
+ iden1.setConcat(mat, inverse);
+ iden1.dump();
+ SkASSERT(iden1.isIdentity());
+
+ mat.setScale(SkIntToScalar(3), SkIntToScalar(5), SkIntToScalar(20), 0);
+ mat.postRotate(SkIntToScalar(25));
+
+ SkASSERT(mat.invert(NULL));
+ mat.invert(&inverse);
+
+ iden1.setConcat(mat, inverse);
+ iden2.setConcat(inverse, mat);
+
+ iden1.dump();
+// SkASSERT(iden1.isIdentity());
+ iden2.dump();
+// SkASSERT(iden2.isIdentity());
+
+ // rectStaysRect test
+ {
+ static const struct {
+ SkScalar m00, m01, m10, m11;
+ bool mStaysRect;
+ }
+ gRectStaysRectSamples[] = {
+ { 0, 0, 0, 0, false },
+ { 0, 0, 0, SK_Scalar1, false },
+ { 0, 0, SK_Scalar1, 0, false },
+ { 0, 0, SK_Scalar1, SK_Scalar1, false },
+ { 0, SK_Scalar1, 0, 0, false },
+ { 0, SK_Scalar1, 0, SK_Scalar1, false },
+ { 0, SK_Scalar1, SK_Scalar1, 0, true },
+ { 0, SK_Scalar1, SK_Scalar1, SK_Scalar1, false },
+ { SK_Scalar1, 0, 0, 0, false },
+ { SK_Scalar1, 0, 0, SK_Scalar1, true },
+ { SK_Scalar1, 0, SK_Scalar1, 0, false },
+ { SK_Scalar1, 0, SK_Scalar1, SK_Scalar1, false },
+ { SK_Scalar1, SK_Scalar1, 0, 0, false },
+ { SK_Scalar1, SK_Scalar1, 0, SK_Scalar1, false },
+ { SK_Scalar1, SK_Scalar1, SK_Scalar1, 0, false },
+ { SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1, false }
+ };
+
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gRectStaysRectSamples); i++) {
+ SkMatrix m;
+
+ m.reset();
+ m.set(SkMatrix::kMScaleX, gRectStaysRectSamples[i].m00);
+ m.set(SkMatrix::kMSkewX, gRectStaysRectSamples[i].m01);
+ m.set(SkMatrix::kMSkewY, gRectStaysRectSamples[i].m10);
+ m.set(SkMatrix::kMScaleY, gRectStaysRectSamples[i].m11);
+ SkASSERT(m.rectStaysRect() == gRectStaysRectSamples[i].mStaysRect);
+ }
+ }
+#endif
+}
+
+#endif
diff --git a/src/core/SkMemory_stdlib.cpp b/src/core/SkMemory_stdlib.cpp
new file mode 100644
index 0000000000..f1ac36beb0
--- /dev/null
+++ b/src/core/SkMemory_stdlib.cpp
@@ -0,0 +1,287 @@
+/* libs/corecg/SkMemory_stdlib.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkTypes.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef SK_DEBUG
+ #define SK_TAG_BLOCKS
+ // #define SK_TRACK_ALLOC // enable to see a printf for every alloc/free
+ // #define SK_CHECK_TAGS // enable to double-check debugging link list
+#endif
+
+#ifdef SK_TAG_BLOCKS
+
+#include "SkThread.h"
+
+// size this (as a multiple of 4) so that the total offset to the internal data
+// is at least a multiple of 8 (since some clients of our malloc may require
+// that.
+static const char kBlockHeaderTag[] = { 's', 'k', 'i', 'a', '1', '2', '3', '4' };
+static const char kBlockTrailerTag[] = { 'a', 'i', 'k', 's' };
+#define kByteFill 0xCD
+#define kDeleteFill 0xEF
+
+static SkMutex& get_block_mutex() {
+ static SkMutex* gBlockMutex;
+ if (NULL == gBlockMutex) {
+ gBlockMutex = new SkMutex;
+ }
+ return *gBlockMutex;
+}
+
+static struct SkBlockHeader* gHeader;
+
+struct SkBlockHeader {
+ SkBlockHeader* fNext;
+#ifdef SK_CHECK_TAGS
+ SkBlockHeader** fTop; // set to verify in debugger that block was alloc'd / freed with same gHeader
+ SkBlockHeader* fPrevious; // set to see in debugger previous block when corruption happens
+#endif
+ size_t fSize;
+ char fHeader[sizeof(kBlockHeaderTag)];
+ // data goes here. The offset to this point must be a multiple of 8
+ char fTrailer[sizeof(kBlockTrailerTag)];
+
+ void* add(size_t realSize)
+ {
+ SkAutoMutexAcquire ac(get_block_mutex());
+ InMutexValidate();
+ fNext = gHeader;
+#ifdef SK_CHECK_TAGS
+ fTop = &gHeader;
+ fPrevious = NULL;
+ if (fNext != NULL)
+ fNext->fPrevious = this;
+#endif
+ gHeader = this;
+ fSize = realSize;
+ memcpy(fHeader, kBlockHeaderTag, sizeof(kBlockHeaderTag));
+ void* result = fTrailer;
+ void* trailer = (char*)result + realSize;
+ memcpy(trailer, kBlockTrailerTag, sizeof(kBlockTrailerTag));
+ return result;
+ }
+
+ static void Dump()
+ {
+ SkAutoMutexAcquire ac(get_block_mutex());
+ InMutexValidate();
+ SkBlockHeader* header = gHeader;
+ int count = 0;
+ size_t size = 0;
+ while (header != NULL) {
+ char scratch[256];
+ char* pos = scratch;
+ size_t size = header->fSize;
+ int* data = (int*)(void*)header->fTrailer;
+ pos += sprintf(pos, "%p 0x%08zx (%7zd) ",
+ data, size, size);
+ size >>= 2;
+ size_t ints = size > 4 ? 4 : size;
+ size_t index;
+ for (index = 0; index < ints; index++)
+ pos += sprintf(pos, "0x%08x ", data[index]);
+ pos += sprintf(pos, " (");
+ for (index = 0; index < ints; index++)
+ pos += sprintf(pos, "%g ", data[index] / 65536.0f);
+ if (ints > 0)
+ --pos;
+ pos += sprintf(pos, ") \"");
+ size_t chars = size > 16 ? 16 : size;
+ char* chPtr = (char*) data;
+ for (index = 0; index < chars; index++) {
+ char ch = chPtr[index];
+ pos += sprintf(pos, "%c", ch >= ' ' && ch < 0x7f ? ch : '?');
+ }
+ pos += sprintf(pos, "\"");
+ SkDebugf("%s\n", scratch);
+ count++;
+ size += header->fSize;
+ header = header->fNext;
+ }
+ SkDebugf("--- count %d size 0x%08x (%zd) ---\n", count, size, size);
+ }
+
+ void remove() const
+ {
+ SkAutoMutexAcquire ac(get_block_mutex());
+ SkBlockHeader** findPtr = &gHeader;
+ do {
+ SkBlockHeader* find = *findPtr;
+ SkASSERT(find != NULL);
+ if (find == this) {
+ *findPtr = fNext;
+ break;
+ }
+ findPtr = &find->fNext;
+ } while (true);
+ InMutexValidate();
+ SkASSERT(memcmp(fHeader, kBlockHeaderTag, sizeof(kBlockHeaderTag)) == 0);
+ const char* trailer = fTrailer + fSize;
+ SkASSERT(memcmp(trailer, kBlockTrailerTag, sizeof(kBlockTrailerTag)) == 0);
+ }
+
+ static void Validate()
+ {
+ SkAutoMutexAcquire ac(get_block_mutex());
+ InMutexValidate();
+ }
+
+private:
+ static void InMutexValidate()
+ {
+ SkBlockHeader* header = gHeader;
+ while (header != NULL) {
+ SkASSERT(memcmp(header->fHeader, kBlockHeaderTag, sizeof(kBlockHeaderTag)) == 0);
+ char* trailer = header->fTrailer + header->fSize;
+ SkASSERT(memcmp(trailer, kBlockTrailerTag, sizeof(kBlockTrailerTag)) == 0);
+ header = header->fNext;
+ }
+ }
+};
+
+void Dump()
+{
+ SkBlockHeader::Dump();
+}
+
+void ValidateHeap()
+{
+ SkBlockHeader::Validate();
+}
+#else
+void Dump() {}
+void ValidateHeap() {}
+#endif
+
+void sk_throw()
+{
+#ifdef ANDROID
+ fprintf(stderr, "throwing...\n");
+#endif
+ SkASSERT(!"sk_throw");
+ abort();
+}
+
+void sk_out_of_memory(void)
+{
+#ifdef ANDROID
+ fprintf(stderr,"- out of memory in SGL -\n");
+#endif
+ SkASSERT(!"sk_out_of_memory");
+ abort();
+}
+
+void* sk_malloc_throw(size_t size)
+{
+ return sk_malloc_flags(size, SK_MALLOC_THROW);
+}
+
+void* sk_realloc_throw(void* addr, size_t size)
+{
+#ifdef SK_TAG_BLOCKS
+ ValidateHeap();
+ if (addr != NULL) {
+ SkBlockHeader* header = (SkBlockHeader*)
+ ((char*)addr - SK_OFFSETOF(SkBlockHeader, fTrailer));
+ header->remove();
+#ifdef SK_TRACK_ALLOC
+ printf("sk_realloc_throw %p oldSize=%zd\n", addr, header->fSize);
+#endif
+ addr = header;
+ }
+ size_t realSize = size;
+ if (size)
+ size += sizeof(SkBlockHeader);
+#endif
+
+ void* p = realloc(addr, size);
+ if (size == 0)
+ {
+ ValidateHeap();
+ return p;
+ }
+
+ if (p == NULL)
+ sk_throw();
+#ifdef SK_TAG_BLOCKS
+ else
+ {
+ SkBlockHeader* header = (SkBlockHeader*) p;
+ p = header->add(realSize);
+#ifdef SK_TRACK_ALLOC
+ printf("sk_realloc_throw %p size=%zd\n", p, realSize);
+#endif
+ }
+#endif
+ ValidateHeap();
+ return p;
+}
+
+void sk_free(void* p)
+{
+ if (p)
+ {
+ ValidateHeap();
+#ifdef SK_TAG_BLOCKS
+ SkBlockHeader* header = (SkBlockHeader*)
+ ((char*)p - SK_OFFSETOF(SkBlockHeader, fTrailer));
+ header->remove();
+#ifdef SK_TRACK_ALLOC
+ printf("sk_free %p size=%zd\n", p, header->fSize);
+#endif
+ size_t size = header->fSize + sizeof(SkBlockHeader);
+ memset(header, kDeleteFill, size);
+ p = header;
+#endif
+ ValidateHeap();
+ free(p);
+ ValidateHeap();
+ }
+}
+
+void* sk_malloc_flags(size_t size, unsigned flags)
+{
+ ValidateHeap();
+#ifdef SK_TAG_BLOCKS
+ size_t realSize = size;
+ size += sizeof(SkBlockHeader);
+#endif
+
+ void* p = malloc(size);
+ if (p == NULL)
+ {
+ if (flags & SK_MALLOC_THROW)
+ sk_throw();
+ }
+#ifdef SK_TAG_BLOCKS
+ else
+ {
+ SkBlockHeader* header = (SkBlockHeader*) p;
+ p = header->add(realSize);
+ memset(p, kByteFill, realSize);
+#ifdef SK_TRACK_ALLOC
+ printf("sk_malloc_flags %p size=%zd\n", p, realSize);
+#endif
+ }
+#endif
+ ValidateHeap();
+ return p;
+}
+
diff --git a/src/core/SkPackBits.cpp b/src/core/SkPackBits.cpp
new file mode 100644
index 0000000000..3e92841600
--- /dev/null
+++ b/src/core/SkPackBits.cpp
@@ -0,0 +1,407 @@
+#include "SkPackBits.h"
+
+#define GATHER_STATSx
+
+static inline void small_memcpy(void* SK_RESTRICT dst,
+ const void* SK_RESTRICT src, int n) {
+ SkASSERT(n > 0 && n <= 15);
+ uint8_t* d = (uint8_t*)dst;
+ const uint8_t* s = (const uint8_t*)src;
+ switch (n) {
+ case 15: *d++ = *s++;
+ case 14: *d++ = *s++;
+ case 13: *d++ = *s++;
+ case 12: *d++ = *s++;
+ case 11: *d++ = *s++;
+ case 10: *d++ = *s++;
+ case 9: *d++ = *s++;
+ case 8: *d++ = *s++;
+ case 7: *d++ = *s++;
+ case 6: *d++ = *s++;
+ case 5: *d++ = *s++;
+ case 4: *d++ = *s++;
+ case 3: *d++ = *s++;
+ case 2: *d++ = *s++;
+ case 1: *d++ = *s++;
+ case 0: break;
+ }
+}
+
+static inline void small_memset(void* dst, uint8_t value, int n) {
+ SkASSERT(n > 0 && n <= 15);
+ uint8_t* d = (uint8_t*)dst;
+ switch (n) {
+ case 15: *d++ = value;
+ case 14: *d++ = value;
+ case 13: *d++ = value;
+ case 12: *d++ = value;
+ case 11: *d++ = value;
+ case 10: *d++ = value;
+ case 9: *d++ = value;
+ case 8: *d++ = value;
+ case 7: *d++ = value;
+ case 6: *d++ = value;
+ case 5: *d++ = value;
+ case 4: *d++ = value;
+ case 3: *d++ = value;
+ case 2: *d++ = value;
+ case 1: *d++ = value;
+ case 0: break;
+ }
+}
+
+// can we do better for small counts with our own inlined memcpy/memset?
+
+#define PB_MEMSET(addr, value, count) \
+do { \
+if ((count) > 15) { \
+memset(addr, value, count); \
+} else { \
+small_memset(addr, value, count); \
+} \
+} while (0)
+
+#define PB_MEMCPY(dst, src, count) \
+do { \
+ if ((count) > 15) { \
+ memcpy(dst, src, count); \
+ } else { \
+ small_memcpy(dst, src, count); \
+ } \
+} while (0)
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef GATHER_STATS
+ static int gMemSetBuckets[129];
+ static int gMemCpyBuckets[129];
+ static int gCounter;
+
+static void register_memset_count(int n) {
+ SkASSERT((unsigned)n <= 128);
+ gMemSetBuckets[n] += 1;
+ gCounter += 1;
+
+ if ((gCounter & 0xFF) == 0) {
+ SkDebugf("----- packbits memset stats: ");
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gMemSetBuckets); i++) {
+ if (gMemSetBuckets[i]) {
+ SkDebugf(" %d:%d", i, gMemSetBuckets[i]);
+ }
+ }
+ }
+}
+static void register_memcpy_count(int n) {
+ SkASSERT((unsigned)n <= 128);
+ gMemCpyBuckets[n] += 1;
+ gCounter += 1;
+
+ if ((gCounter & 0x1FF) == 0) {
+ SkDebugf("----- packbits memcpy stats: ");
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gMemCpyBuckets); i++) {
+ if (gMemCpyBuckets[i]) {
+ SkDebugf(" %d:%d", i, gMemCpyBuckets[i]);
+ }
+ }
+ }
+}
+#else
+#define register_memset_count(n)
+#define register_memcpy_count(n)
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+size_t SkPackBits::ComputeMaxSize16(int count) {
+ // worst case is the number of 16bit values (times 2) +
+ // 1 byte per (up to) 128 entries.
+ return ((count + 127) >> 7) + (count << 1);
+}
+
+size_t SkPackBits::ComputeMaxSize8(int count) {
+ // worst case is the number of 8bit values + 1 byte per (up to) 128 entries.
+ return ((count + 127) >> 7) + count;
+}
+
+static uint8_t* flush_same16(uint8_t dst[], uint16_t value, int count) {
+ while (count > 0) {
+ int n = count;
+ if (n > 128) {
+ n = 128;
+ }
+ *dst++ = (uint8_t)(n - 1);
+ *dst++ = (uint8_t)(value >> 8);
+ *dst++ = (uint8_t)value;
+ count -= n;
+ }
+ return dst;
+}
+
+static uint8_t* flush_same8(uint8_t dst[], uint8_t value, int count) {
+ while (count > 0) {
+ int n = count;
+ if (n > 128) {
+ n = 128;
+ }
+ *dst++ = (uint8_t)(n - 1);
+ *dst++ = (uint8_t)value;
+ count -= n;
+ }
+ return dst;
+}
+
+static uint8_t* flush_diff16(uint8_t SK_RESTRICT dst[],
+ const uint16_t SK_RESTRICT src[], int count) {
+ while (count > 0) {
+ int n = count;
+ if (n > 128) {
+ n = 128;
+ }
+ *dst++ = (uint8_t)(n + 127);
+ PB_MEMCPY(dst, src, n * sizeof(uint16_t));
+ src += n;
+ dst += n * sizeof(uint16_t);
+ count -= n;
+ }
+ return dst;
+}
+
+static uint8_t* flush_diff8(uint8_t SK_RESTRICT dst[],
+ const uint8_t SK_RESTRICT src[], int count) {
+ while (count > 0) {
+ int n = count;
+ if (n > 128) {
+ n = 128;
+ }
+ *dst++ = (uint8_t)(n + 127);
+ PB_MEMCPY(dst, src, n);
+ src += n;
+ dst += n;
+ count -= n;
+ }
+ return dst;
+}
+
+size_t SkPackBits::Pack16(const uint16_t SK_RESTRICT src[], int count,
+ uint8_t SK_RESTRICT dst[]) {
+ uint8_t* origDst = dst;
+ const uint16_t* stop = src + count;
+
+ for (;;) {
+ count = stop - src;
+ SkASSERT(count >= 0);
+ if (count == 0) {
+ return dst - origDst;
+ }
+ if (1 == count) {
+ *dst++ = 0;
+ *dst++ = (uint8_t)(*src >> 8);
+ *dst++ = (uint8_t)*src;
+ return dst - origDst;
+ }
+
+ unsigned value = *src;
+ const uint16_t* s = src + 1;
+
+ if (*s == value) { // accumulate same values...
+ do {
+ s++;
+ if (s == stop) {
+ break;
+ }
+ } while (*s == value);
+ dst = flush_same16(dst, value, s - src);
+ } else { // accumulate diff values...
+ do {
+ if (++s == stop) {
+ goto FLUSH_DIFF;
+ }
+ } while (*s != s[-1]);
+ s -= 1; // back up so we don't grab one of the "same" values that follow
+ FLUSH_DIFF:
+ dst = flush_diff16(dst, src, s - src);
+ }
+ src = s;
+ }
+}
+
+size_t SkPackBits::Pack8(const uint8_t SK_RESTRICT src[], int count,
+ uint8_t SK_RESTRICT dst[]) {
+ uint8_t* origDst = dst;
+ const uint8_t* stop = src + count;
+
+ for (;;) {
+ count = stop - src;
+ SkASSERT(count >= 0);
+ if (count == 0) {
+ return dst - origDst;
+ }
+ if (1 == count) {
+ *dst++ = 0;
+ *dst++ = *src;
+ return dst - origDst;
+ }
+
+ unsigned value = *src;
+ const uint8_t* s = src + 1;
+
+ if (*s == value) { // accumulate same values...
+ do {
+ s++;
+ if (s == stop) {
+ break;
+ }
+ } while (*s == value);
+ dst = flush_same8(dst, value, s - src);
+ } else { // accumulate diff values...
+ do {
+ if (++s == stop) {
+ goto FLUSH_DIFF;
+ }
+ // only stop if we hit 3 in a row,
+ // otherwise we get bigger than compuatemax
+ } while (*s != s[-1] || s[-1] != s[-2]);
+ s -= 2; // back up so we don't grab the "same" values that follow
+ FLUSH_DIFF:
+ dst = flush_diff8(dst, src, s - src);
+ }
+ src = s;
+ }
+}
+
+#include "SkUtils.h"
+
+int SkPackBits::Unpack16(const uint8_t SK_RESTRICT src[], size_t srcSize,
+ uint16_t SK_RESTRICT dst[]) {
+ uint16_t* origDst = dst;
+ const uint8_t* stop = src + srcSize;
+
+ while (src < stop) {
+ unsigned n = *src++;
+ if (n <= 127) { // repeat count (n + 1)
+ n += 1;
+ sk_memset16(dst, (src[0] << 8) | src[1], n);
+ src += 2;
+ } else { // same count (n - 127)
+ n -= 127;
+ PB_MEMCPY(dst, src, n * sizeof(uint16_t));
+ src += n * sizeof(uint16_t);
+ }
+ dst += n;
+ }
+ SkASSERT(src == stop);
+ return dst - origDst;
+}
+
+int SkPackBits::Unpack8(const uint8_t SK_RESTRICT src[], size_t srcSize,
+ uint8_t SK_RESTRICT dst[]) {
+ uint8_t* origDst = dst;
+ const uint8_t* stop = src + srcSize;
+
+ while (src < stop) {
+ unsigned n = *src++;
+ if (n <= 127) { // repeat count (n + 1)
+ n += 1;
+ PB_MEMSET(dst, *src++, n);
+ } else { // same count (n - 127)
+ n -= 127;
+ PB_MEMCPY(dst, src, n);
+ src += n;
+ }
+ dst += n;
+ }
+ SkASSERT(src == stop);
+ return dst - origDst;
+}
+
+enum UnpackState {
+ CLEAN_STATE,
+ REPEAT_BYTE_STATE,
+ COPY_SRC_STATE
+};
+
+void SkPackBits::Unpack8(uint8_t SK_RESTRICT dst[], size_t dstSkip,
+ size_t dstWrite, const uint8_t SK_RESTRICT src[]) {
+ if (dstWrite == 0) {
+ return;
+ }
+
+ UnpackState state = CLEAN_STATE;
+ size_t stateCount = 0;
+
+ // state 1: do the skip-loop
+ while (dstSkip > 0) {
+ unsigned n = *src++;
+ if (n <= 127) { // repeat count (n + 1)
+ n += 1;
+ if (n > dstSkip) {
+ state = REPEAT_BYTE_STATE;
+ stateCount = n - dstSkip;
+ n = dstSkip;
+ // we don't increment src here, since its needed in stage 2
+ } else {
+ src++; // skip the src byte
+ }
+ } else { // same count (n - 127)
+ n -= 127;
+ if (n > dstSkip) {
+ state = COPY_SRC_STATE;
+ stateCount = n - dstSkip;
+ n = dstSkip;
+ }
+ src += n;
+ }
+ dstSkip -= n;
+ }
+
+ // stage 2: perform any catchup from the skip-stage
+ if (stateCount > dstWrite) {
+ stateCount = dstWrite;
+ }
+ switch (state) {
+ case REPEAT_BYTE_STATE:
+ SkASSERT(stateCount > 0);
+ register_memset_count(stateCount);
+ PB_MEMSET(dst, *src++, stateCount);
+ break;
+ case COPY_SRC_STATE:
+ SkASSERT(stateCount > 0);
+ register_memcpy_count(stateCount);
+ PB_MEMCPY(dst, src, stateCount);
+ src += stateCount;
+ break;
+ default:
+ SkASSERT(stateCount == 0);
+ break;
+ }
+ dst += stateCount;
+ dstWrite -= stateCount;
+
+ // copy at most dstWrite bytes into dst[]
+ while (dstWrite > 0) {
+ unsigned n = *src++;
+ if (n <= 127) { // repeat count (n + 1)
+ n += 1;
+ if (n > dstWrite) {
+ n = dstWrite;
+ }
+ register_memset_count(n);
+ PB_MEMSET(dst, *src++, n);
+ } else { // same count (n - 127)
+ n -= 127;
+ if (n > dstWrite) {
+ n = dstWrite;
+ }
+ register_memcpy_count(n);
+ PB_MEMCPY(dst, src, n);
+ src += n;
+ }
+ dst += n;
+ dstWrite -= n;
+ }
+ SkASSERT(0 == dstWrite);
+}
+
+
+
diff --git a/src/core/SkPaint.cpp b/src/core/SkPaint.cpp
new file mode 100644
index 0000000000..bb6b31eefc
--- /dev/null
+++ b/src/core/SkPaint.cpp
@@ -0,0 +1,1571 @@
+/* libs/graphics/sgl/SkPaint.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkPaint.h"
+#include "SkColorFilter.h"
+#include "SkDrawLooper.h"
+#include "SkFontHost.h"
+#include "SkMaskFilter.h"
+#include "SkPathEffect.h"
+#include "SkRasterizer.h"
+#include "SkShader.h"
+#include "SkScalerContext.h"
+#include "SkStroke.h"
+#include "SkTypeface.h"
+#include "SkXfermode.h"
+#include "SkAutoKern.h"
+
+#define SK_DefaultTextSize SkIntToScalar(12)
+
+#define SK_DefaultFlags 0 //(kNativeHintsText_Flag)
+
+SkPaint::SkPaint()
+{
+ fTypeface = NULL;
+ fTextSize = SK_DefaultTextSize;
+ fTextScaleX = SK_Scalar1;
+ fTextSkewX = 0;
+
+ fPathEffect = NULL;
+ fShader = NULL;
+ fXfermode = NULL;
+ fMaskFilter = NULL;
+ fColorFilter = NULL;
+ fRasterizer = NULL;
+ fLooper = NULL;
+
+ fColor = SK_ColorBLACK;
+ fWidth = 0;
+ fMiterLimit = SK_DefaultMiterLimit;
+ fFlags = SK_DefaultFlags;
+ fCapType = kDefault_Cap;
+ fJoinType = kDefault_Join;
+ fTextAlign = kLeft_Align;
+ fStyle = kFill_Style;
+ fTextEncoding = kUTF8_TextEncoding;
+}
+
+SkPaint::SkPaint(const SkPaint& src)
+{
+ memcpy(this, &src, sizeof(src));
+
+ fTypeface->safeRef();
+ fPathEffect->safeRef();
+ fShader->safeRef();
+ fXfermode->safeRef();
+ fMaskFilter->safeRef();
+ fColorFilter->safeRef();
+ fRasterizer->safeRef();
+ fLooper->safeRef();
+}
+
+SkPaint::~SkPaint()
+{
+ fTypeface->safeUnref();
+ fPathEffect->safeUnref();
+ fShader->safeUnref();
+ fXfermode->safeUnref();
+ fMaskFilter->safeUnref();
+ fColorFilter->safeUnref();
+ fRasterizer->safeUnref();
+ fLooper->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.fRasterizer->safeRef();
+ src.fLooper->safeRef();
+
+ fTypeface->safeUnref();
+ fPathEffect->safeUnref();
+ fShader->safeUnref();
+ fXfermode->safeUnref();
+ fMaskFilter->safeUnref();
+ fColorFilter->safeUnref();
+ fRasterizer->safeUnref();
+ fLooper->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(uint32_t flags)
+{
+ fFlags = flags;
+}
+
+void SkPaint::setAntiAlias(bool doAA)
+{
+ this->setFlags(SkSetClearMask(fFlags, doAA, kAntiAlias_Flag));
+}
+
+void SkPaint::setDither(bool doDither)
+{
+ this->setFlags(SkSetClearMask(fFlags, doDither, kDither_Flag));
+}
+
+void SkPaint::setSubpixelText(bool doSubpixel)
+{
+ this->setFlags(SkSetClearMask(fFlags, doSubpixel, kSubpixelText_Flag));
+}
+
+void SkPaint::setLinearText(bool doLinearText)
+{
+ this->setFlags(SkSetClearMask(fFlags, doLinearText, kLinearText_Flag));
+}
+
+void SkPaint::setUnderlineText(bool doUnderline)
+{
+ this->setFlags(SkSetClearMask(fFlags, doUnderline, kUnderlineText_Flag));
+}
+
+void SkPaint::setStrikeThruText(bool doStrikeThru)
+{
+ this->setFlags(SkSetClearMask(fFlags, doStrikeThru, kStrikeThruText_Flag));
+}
+
+void SkPaint::setFakeBoldText(bool doFakeBold)
+{
+ this->setFlags(SkSetClearMask(fFlags, doFakeBold, kFakeBoldText_Flag));
+}
+
+void SkPaint::setDevKernText(bool doDevKern)
+{
+ this->setFlags(SkSetClearMask(fFlags, doDevKern, kDevKernText_Flag));
+}
+
+void SkPaint::setFilterBitmap(bool doFilter)
+{
+ this->setFlags(SkSetClearMask(fFlags, doFilter, kFilterBitmap_Flag));
+}
+
+void SkPaint::setStyle(Style style)
+{
+ if ((unsigned)style < kStyleCount)
+ fStyle = style;
+#ifdef SK_DEBUG
+ else
+ SkDebugf("SkPaint::setStyle(%d) out of range\n", style);
+#endif
+}
+
+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)
+{
+ if (width >= 0)
+ fWidth = width;
+#ifdef SK_DEBUG
+ else
+ SkDebugf("SkPaint::setStrokeWidth() called with negative value\n");
+#endif
+}
+
+void SkPaint::setStrokeMiter(SkScalar limit)
+{
+ if (limit >= 0)
+ fMiterLimit = limit;
+#ifdef SK_DEBUG
+ else
+ SkDebugf("SkPaint::setStrokeMiter() called with negative value\n");
+#endif
+}
+
+void SkPaint::setStrokeCap(Cap ct)
+{
+ if ((unsigned)ct < kCapCount)
+ fCapType = SkToU8(ct);
+#ifdef SK_DEBUG
+ else
+ SkDebugf("SkPaint::setStrokeCap(%d) out of range\n", ct);
+#endif
+}
+
+void SkPaint::setStrokeJoin(Join jt)
+{
+ if ((unsigned)jt < kJoinCount)
+ fJoinType = SkToU8(jt);
+#ifdef SK_DEBUG
+ else
+ SkDebugf("SkPaint::setStrokeJoin(%d) out of range\n", jt);
+#endif
+}
+
+//////////////////////////////////////////////////////////////////
+
+void SkPaint::setTextAlign(Align align)
+{
+ if ((unsigned)align < kAlignCount)
+ fTextAlign = SkToU8(align);
+#ifdef SK_DEBUG
+ else
+ SkDebugf("SkPaint::setTextAlign(%d) out of range\n", align);
+#endif
+}
+
+void SkPaint::setTextSize(SkScalar ts)
+{
+ if (ts > 0)
+ fTextSize = ts;
+#ifdef SK_DEBUG
+ else
+ SkDebugf("SkPaint::setTextSize() called with non-positive value\n");
+#endif
+}
+
+void SkPaint::setTextScaleX(SkScalar scaleX)
+{
+ fTextScaleX = scaleX;
+}
+
+void SkPaint::setTextSkewX(SkScalar skewX)
+{
+ fTextSkewX = skewX;
+}
+
+void SkPaint::setTextEncoding(TextEncoding encoding)
+{
+ if ((unsigned)encoding <= kGlyphID_TextEncoding)
+ fTextEncoding = encoding;
+#ifdef SK_DEBUG
+ else
+ SkDebugf("SkPaint::setTextEncoding(%d) out of range\n", encoding);
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////////////
+
+SkTypeface* SkPaint::setTypeface(SkTypeface* font)
+{
+ SkRefCnt_SafeAssign(fTypeface, font);
+ return font;
+}
+
+SkRasterizer* SkPaint::setRasterizer(SkRasterizer* r)
+{
+ SkRefCnt_SafeAssign(fRasterizer, r);
+ return r;
+}
+
+SkDrawLooper* SkPaint::setLooper(SkDrawLooper* looper)
+{
+ SkRefCnt_SafeAssign(fLooper, looper);
+ return looper;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkGlyphCache.h"
+#include "SkUtils.h"
+
+int SkPaint::textToGlyphs(const void* textData, size_t byteLength,
+ uint16_t glyphs[]) const {
+ if (byteLength == 0) {
+ return 0;
+ }
+
+ SkASSERT(textData != NULL);
+
+ if (NULL == glyphs) {
+ switch (this->getTextEncoding()) {
+ case kUTF8_TextEncoding:
+ return SkUTF8_CountUnichars((const char*)textData, byteLength);
+ case kUTF16_TextEncoding:
+ return SkUTF16_CountUnichars((const uint16_t*)textData,
+ byteLength >> 1);
+ case kGlyphID_TextEncoding:
+ return byteLength >> 1;
+ default:
+ SkASSERT(!"unknown text encoding");
+ }
+ return 0;
+ }
+
+ // if we get here, we have a valid glyphs[] array, so time to fill it in
+
+ // handle this encoding before the setup for the glyphcache
+ if (this->getTextEncoding() == kGlyphID_TextEncoding) {
+ // we want to ignore the low bit of byteLength
+ memcpy(glyphs, textData, byteLength >> 1 << 1);
+ return byteLength >> 1;
+ }
+
+ SkAutoGlyphCache autoCache(*this, NULL);
+ SkGlyphCache* cache = autoCache.getCache();
+
+ const char* text = (const char*)textData;
+ const char* stop = text + byteLength;
+ uint16_t* gptr = glyphs;
+
+ switch (this->getTextEncoding()) {
+ case SkPaint::kUTF8_TextEncoding:
+ while (text < stop) {
+ *gptr++ = cache->unicharToGlyph(SkUTF8_NextUnichar(&text));
+ }
+ break;
+ case SkPaint::kUTF16_TextEncoding: {
+ const uint16_t* text16 = (const uint16_t*)text;
+ const uint16_t* stop16 = (const uint16_t*)stop;
+ while (text16 < stop16) {
+ *gptr++ = cache->unicharToGlyph(SkUTF16_NextUnichar(&text16));
+ }
+ break;
+ }
+ default:
+ SkASSERT(!"unknown text encoding");
+ }
+ return gptr - glyphs;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static uint32_t sk_glyphID_next(const char** text)
+{
+ const uint16_t* glyph = (const uint16_t*)text;
+ int32_t value = *glyph;
+ glyph += 1;
+ *text = (const char*)glyph;
+ return value;
+}
+
+static uint32_t sk_glyphID_prev(const char** text)
+{
+ const uint16_t* glyph = (const uint16_t*)text;
+ glyph -= 1;
+ int32_t value = *glyph;
+ *text = (const char*)glyph;
+ return value;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+static const SkGlyph& sk_getMetrics_utf8_next(SkGlyphCache* cache, const char** text)
+{
+ SkASSERT(cache != NULL);
+ SkASSERT(text != NULL);
+
+ return cache->getUnicharMetrics(SkUTF8_NextUnichar(text));
+}
+
+static const SkGlyph& sk_getMetrics_utf8_prev(SkGlyphCache* cache, const char** text)
+{
+ SkASSERT(cache != NULL);
+ SkASSERT(text != NULL);
+
+ return cache->getUnicharMetrics(SkUTF8_PrevUnichar(text));
+}
+
+static const SkGlyph& sk_getMetrics_utf16_next(SkGlyphCache* cache, const char** text)
+{
+ SkASSERT(cache != NULL);
+ SkASSERT(text != NULL);
+
+ return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text));
+}
+
+static const SkGlyph& sk_getMetrics_utf16_prev(SkGlyphCache* cache, const char** text)
+{
+ SkASSERT(cache != NULL);
+ SkASSERT(text != NULL);
+
+ return cache->getUnicharMetrics(SkUTF16_PrevUnichar((const uint16_t**)text));
+}
+
+static const SkGlyph& sk_getMetrics_glyph_next(SkGlyphCache* cache, const char** text)
+{
+ SkASSERT(cache != NULL);
+ SkASSERT(text != NULL);
+
+ const uint16_t* ptr = *(const uint16_t**)text;
+ unsigned glyphID = *ptr;
+ ptr += 1;
+ *text = (const char*)ptr;
+ return cache->getGlyphIDMetrics(glyphID);
+}
+
+static const SkGlyph& sk_getMetrics_glyph_prev(SkGlyphCache* cache, const char** text)
+{
+ SkASSERT(cache != NULL);
+ SkASSERT(text != NULL);
+
+ const uint16_t* ptr = *(const uint16_t**)text;
+ ptr -= 1;
+ unsigned glyphID = *ptr;
+ *text = (const char*)ptr;
+ return cache->getGlyphIDMetrics(glyphID);
+}
+
+///
+
+static const SkGlyph& sk_getAdvance_utf8_next(SkGlyphCache* cache, const char** text)
+{
+ SkASSERT(cache != NULL);
+ SkASSERT(text != NULL);
+
+ return cache->getUnicharAdvance(SkUTF8_NextUnichar(text));
+}
+
+static const SkGlyph& sk_getAdvance_utf8_prev(SkGlyphCache* cache, const char** text)
+{
+ SkASSERT(cache != NULL);
+ SkASSERT(text != NULL);
+
+ return cache->getUnicharAdvance(SkUTF8_PrevUnichar(text));
+}
+
+static const SkGlyph& sk_getAdvance_utf16_next(SkGlyphCache* cache, const char** text)
+{
+ SkASSERT(cache != NULL);
+ SkASSERT(text != NULL);
+
+ return cache->getUnicharAdvance(SkUTF16_NextUnichar((const uint16_t**)text));
+}
+
+static const SkGlyph& sk_getAdvance_utf16_prev(SkGlyphCache* cache, const char** text)
+{
+ SkASSERT(cache != NULL);
+ SkASSERT(text != NULL);
+
+ return cache->getUnicharAdvance(SkUTF16_PrevUnichar((const uint16_t**)text));
+}
+
+static const SkGlyph& sk_getAdvance_glyph_next(SkGlyphCache* cache, const char** text)
+{
+ SkASSERT(cache != NULL);
+ SkASSERT(text != NULL);
+
+ const uint16_t* ptr = *(const uint16_t**)text;
+ unsigned glyphID = *ptr;
+ ptr += 1;
+ *text = (const char*)ptr;
+ return cache->getGlyphIDAdvance(glyphID);
+}
+
+static const SkGlyph& sk_getAdvance_glyph_prev(SkGlyphCache* cache, const char** text)
+{
+ SkASSERT(cache != NULL);
+ SkASSERT(text != NULL);
+
+ const uint16_t* ptr = *(const uint16_t**)text;
+ ptr -= 1;
+ unsigned glyphID = *ptr;
+ *text = (const char*)ptr;
+ return cache->getGlyphIDAdvance(glyphID);
+}
+
+SkMeasureCacheProc SkPaint::getMeasureCacheProc(TextBufferDirection tbd,
+ bool needFullMetrics) const
+{
+ static const SkMeasureCacheProc gMeasureCacheProcs[] = {
+ sk_getMetrics_utf8_next,
+ sk_getMetrics_utf16_next,
+ sk_getMetrics_glyph_next,
+
+ sk_getMetrics_utf8_prev,
+ sk_getMetrics_utf16_prev,
+ sk_getMetrics_glyph_prev,
+
+ sk_getAdvance_utf8_next,
+ sk_getAdvance_utf16_next,
+ sk_getAdvance_glyph_next,
+
+ sk_getAdvance_utf8_prev,
+ sk_getAdvance_utf16_prev,
+ sk_getAdvance_glyph_prev
+ };
+
+ unsigned index = this->getTextEncoding();
+
+ if (kBackward_TextBufferDirection == tbd)
+ index += 3;
+ if (!needFullMetrics && !this->isDevKernText())
+ index += 6;
+
+ SkASSERT(index < SK_ARRAY_COUNT(gMeasureCacheProcs));
+ return gMeasureCacheProcs[index];
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const SkGlyph& sk_getMetrics_utf8_00(SkGlyphCache* cache,
+ const char** text, SkFixed, SkFixed)
+{
+ SkASSERT(cache != NULL);
+ SkASSERT(text != NULL);
+
+ return cache->getUnicharMetrics(SkUTF8_NextUnichar(text));
+}
+
+static const SkGlyph& sk_getMetrics_utf8_xy(SkGlyphCache* cache,
+ const char** text, SkFixed x, SkFixed y)
+{
+ SkASSERT(cache != NULL);
+ SkASSERT(text != NULL);
+
+ return cache->getUnicharMetrics(SkUTF8_NextUnichar(text), x, y);
+}
+
+static const SkGlyph& sk_getMetrics_utf16_00(SkGlyphCache* cache, const char** text,
+ SkFixed, SkFixed)
+{
+ SkASSERT(cache != NULL);
+ SkASSERT(text != NULL);
+
+ return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text));
+}
+
+static const SkGlyph& sk_getMetrics_utf16_xy(SkGlyphCache* cache,
+ const char** text, SkFixed x, SkFixed y)
+{
+ SkASSERT(cache != NULL);
+ SkASSERT(text != NULL);
+
+ return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text),
+ x, y);
+}
+
+static const SkGlyph& sk_getMetrics_glyph_00(SkGlyphCache* cache, const char** text,
+ SkFixed, SkFixed)
+{
+ SkASSERT(cache != NULL);
+ SkASSERT(text != NULL);
+
+ const uint16_t* ptr = *(const uint16_t**)text;
+ unsigned glyphID = *ptr;
+ ptr += 1;
+ *text = (const char*)ptr;
+ return cache->getGlyphIDMetrics(glyphID);
+}
+
+static const SkGlyph& sk_getMetrics_glyph_xy(SkGlyphCache* cache,
+ const char** text, SkFixed x, SkFixed y)
+{
+ SkASSERT(cache != NULL);
+ SkASSERT(text != NULL);
+
+ const uint16_t* ptr = *(const uint16_t**)text;
+ unsigned glyphID = *ptr;
+ ptr += 1;
+ *text = (const char*)ptr;
+ return cache->getGlyphIDMetrics(glyphID, x, y);
+}
+
+SkDrawCacheProc SkPaint::getDrawCacheProc() const
+{
+ static const SkDrawCacheProc gDrawCacheProcs[] = {
+ sk_getMetrics_utf8_00,
+ sk_getMetrics_utf16_00,
+ sk_getMetrics_glyph_00,
+
+ sk_getMetrics_utf8_xy,
+ sk_getMetrics_utf16_xy,
+ sk_getMetrics_glyph_xy
+ };
+
+ unsigned index = this->getTextEncoding();
+ if (fFlags & kSubpixelText_Flag)
+ index += 3;
+
+ SkASSERT(index < SK_ARRAY_COUNT(gDrawCacheProcs));
+ return gDrawCacheProcs[index];
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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 void set_bounds(const SkGlyph& g, SkRect* bounds)
+{
+ bounds->set(SkIntToScalar(g.fLeft),
+ SkIntToScalar(g.fTop),
+ SkIntToScalar(g.fLeft + g.fWidth),
+ SkIntToScalar(g.fTop + g.fHeight));
+}
+
+static void join_bounds(const SkGlyph& g, SkRect* bounds, SkFixed dx)
+{
+ SkScalar sx = SkFixedToScalar(dx);
+ bounds->join(SkIntToScalar(g.fLeft) + sx,
+ SkIntToScalar(g.fTop),
+ SkIntToScalar(g.fLeft + g.fWidth) + sx,
+ SkIntToScalar(g.fTop + g.fHeight));
+}
+
+SkScalar SkPaint::measure_text(SkGlyphCache* cache,
+ const char* text, size_t byteLength,
+ int* count, SkRect* bounds) const
+{
+ SkASSERT(count);
+ if (byteLength == 0)
+ {
+ *count = 0;
+ if (bounds)
+ bounds->setEmpty();
+ return 0;
+ }
+
+ SkMeasureCacheProc glyphCacheProc;
+ glyphCacheProc = this->getMeasureCacheProc(kForward_TextBufferDirection,
+ NULL != bounds);
+
+ int n = 1;
+ const char* stop = (const char*)text + byteLength;
+ const SkGlyph* g = &glyphCacheProc(cache, &text);
+ SkFixed x = g->fAdvanceX;
+
+ SkAutoKern autokern;
+
+ if (NULL == bounds)
+ {
+ if (this->isDevKernText())
+ {
+ int rsb;
+ for (; text < stop; n++) {
+ rsb = g->fRsbDelta;
+ g = &glyphCacheProc(cache, &text);
+ x += SkAutoKern_AdjustF(rsb, g->fLsbDelta) + g->fAdvanceX;
+ }
+ }
+ else
+ {
+ for (; text < stop; n++) {
+ x += glyphCacheProc(cache, &text).fAdvanceX;
+ }
+ }
+ }
+ else
+ {
+ set_bounds(*g, bounds);
+ if (this->isDevKernText())
+ {
+ int rsb;
+ for (; text < stop; n++) {
+ rsb = g->fRsbDelta;
+ g = &glyphCacheProc(cache, &text);
+ x += SkAutoKern_AdjustF(rsb, g->fLsbDelta);
+ join_bounds(*g, bounds, x);
+ x += g->fAdvanceX;
+ }
+ }
+ else
+ {
+ for (; text < stop; n++) {
+ g = &glyphCacheProc(cache, &text);
+ join_bounds(*g, bounds, x);
+ x += g->fAdvanceX;
+ }
+ }
+ }
+ SkASSERT(text == stop);
+
+ *count = n;
+ return SkFixedToScalar(x);
+}
+
+SkScalar SkPaint::measureText(const void* textData, size_t length,
+ SkRect* bounds, SkScalar zoom) const
+{
+ const char* text = (const char*)textData;
+ SkASSERT(text != NULL || length == 0);
+
+ SkScalar scale = 0;
+ SkAutoRestorePaintTextSizeAndFrame restore(this);
+
+ if (this->isLinearText())
+ {
+ scale = fTextSize / kCanonicalTextSizeForPaths;
+ // this gets restored by restore
+ ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths));
+ }
+
+ SkMatrix zoomMatrix, *zoomPtr = NULL;
+ if (zoom)
+ {
+ zoomMatrix.setScale(zoom, zoom);
+ zoomPtr = &zoomMatrix;
+ }
+
+ SkAutoGlyphCache autoCache(*this, zoomPtr);
+ SkGlyphCache* cache = autoCache.getCache();
+
+ SkScalar width = 0;
+
+ if (length > 0)
+ {
+ int tempCount;
+
+ width = this->measure_text(cache, text, length, &tempCount, bounds);
+ if (scale)
+ {
+ width = SkScalarMul(width, scale);
+ if (bounds)
+ {
+ bounds->fLeft = SkScalarMul(bounds->fLeft, scale);
+ bounds->fTop = SkScalarMul(bounds->fTop, scale);
+ bounds->fRight = SkScalarMul(bounds->fRight, scale);
+ bounds->fBottom = SkScalarMul(bounds->fBottom, scale);
+ }
+ }
+ }
+ return width;
+}
+
+typedef bool (*SkTextBufferPred)(const char* text, const char* stop);
+
+static bool forward_textBufferPred(const char* text, const char* stop)
+{
+ return text < stop;
+}
+
+static bool backward_textBufferPred(const char* text, const char* stop)
+{
+ return text > stop;
+}
+
+static SkTextBufferPred chooseTextBufferPred(SkPaint::TextBufferDirection tbd,
+ const char** text, size_t length, const char** stop)
+{
+ if (SkPaint::kForward_TextBufferDirection == tbd)
+ {
+ *stop = *text + length;
+ return forward_textBufferPred;
+ }
+ else
+ {
+ // text should point to the end of the buffer, and stop to the beginning
+ *stop = *text;
+ *text += length;
+ return backward_textBufferPred;
+ }
+}
+
+size_t SkPaint::breakText(const void* textD, size_t length, SkScalar maxWidth,
+ SkScalar* measuredWidth,
+ TextBufferDirection tbd) const
+{
+ if (0 == length || 0 >= maxWidth)
+ {
+ if (measuredWidth)
+ *measuredWidth = 0;
+ return 0;
+ }
+
+ SkASSERT(textD != NULL);
+ const char* text = (const char*)textD;
+
+ SkScalar scale = 0;
+ SkAutoRestorePaintTextSizeAndFrame restore(this);
+
+ if (this->isLinearText())
+ {
+ scale = fTextSize / kCanonicalTextSizeForPaths;
+ // this gets restored by restore
+ ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths));
+ }
+
+ SkAutoGlyphCache autoCache(*this, NULL);
+ SkGlyphCache* cache = autoCache.getCache();
+
+ SkMeasureCacheProc glyphCacheProc = this->getMeasureCacheProc(tbd, false);
+ const char* stop;
+ SkTextBufferPred pred = chooseTextBufferPred(tbd, &text, length, &stop);
+ SkFixed max = SkScalarToFixed(maxWidth);
+ SkFixed width = 0;
+
+ SkAutoKern autokern;
+
+ if (this->isDevKernText())
+ {
+ int rsb = 0;
+ while (pred(text, stop))
+ {
+ const char* curr = text;
+ const SkGlyph& g = glyphCacheProc(cache, &text);
+ SkFixed x = SkAutoKern_AdjustF(rsb, g.fLsbDelta) + g.fAdvanceX;
+ if ((width += x) > max)
+ {
+ width -= x;
+ text = curr;
+ break;
+ }
+ rsb = g.fRsbDelta;
+ }
+ }
+ else
+ {
+ while (pred(text, stop))
+ {
+ const char* curr = text;
+ SkFixed x = glyphCacheProc(cache, &text).fAdvanceX;
+ if ((width += x) > max)
+ {
+ width -= x;
+ text = curr;
+ break;
+ }
+ }
+ }
+
+ if (measuredWidth)
+ {
+
+ SkScalar scalarWidth = SkFixedToScalar(width);
+ if (scale)
+ scalarWidth = SkScalarMul(scalarWidth, scale);
+ *measuredWidth = scalarWidth;
+ }
+
+ // return the number of bytes measured
+ return (kForward_TextBufferDirection == tbd) ?
+ text - stop + length : stop - text + length;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static bool FontMetricsCacheProc(const SkGlyphCache* cache, void* context)
+{
+ *(SkPaint::FontMetrics*)context = cache->getFontMetricsY();
+ return false; // don't detach the cache
+}
+
+static void FontMetricsDescProc(const SkDescriptor* desc, void* context)
+{
+ SkGlyphCache::VisitCache(desc, FontMetricsCacheProc, context);
+}
+
+SkScalar SkPaint::getFontMetrics(FontMetrics* metrics, SkScalar zoom) const
+{
+ SkScalar scale = 0;
+ SkAutoRestorePaintTextSizeAndFrame restore(this);
+
+ if (this->isLinearText())
+ {
+ scale = fTextSize / kCanonicalTextSizeForPaths;
+ // this gets restored by restore
+ ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths));
+ }
+
+ SkMatrix zoomMatrix, *zoomPtr = NULL;
+ if (zoom)
+ {
+ zoomMatrix.setScale(zoom, zoom);
+ zoomPtr = &zoomMatrix;
+ }
+
+#if 0
+ SkAutoGlyphCache autoCache(*this, zoomPtr);
+ SkGlyphCache* cache = autoCache.getCache();
+ const FontMetrics& my = cache->getFontMetricsY();
+#endif
+ FontMetrics storage;
+ if (NULL == metrics)
+ metrics = &storage;
+
+ this->descriptorProc(zoomPtr, FontMetricsDescProc, metrics);
+
+ if (scale)
+ {
+ metrics->fTop = SkScalarMul(metrics->fTop, scale);
+ metrics->fAscent = SkScalarMul(metrics->fAscent, scale);
+ metrics->fDescent = SkScalarMul(metrics->fDescent, scale);
+ metrics->fBottom = SkScalarMul(metrics->fBottom, scale);
+ metrics->fLeading = SkScalarMul(metrics->fLeading, scale);
+ }
+ return metrics->fDescent - metrics->fAscent + metrics->fLeading;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+
+static void set_bounds(const SkGlyph& g, SkRect* bounds, SkScalar scale)
+{
+ bounds->set(g.fLeft * scale,
+ g.fTop * scale,
+ (g.fLeft + g.fWidth) * scale,
+ (g.fTop + g.fHeight) * scale);
+}
+
+int SkPaint::getTextWidths(const void* textData, size_t byteLength, SkScalar widths[],
+ SkRect bounds[]) const
+{
+ if (0 == byteLength)
+ return 0;
+
+ SkASSERT(NULL != textData);
+
+ if (NULL == widths && NULL == bounds)
+ return this->countText(textData, byteLength);
+
+ SkAutoRestorePaintTextSizeAndFrame restore(this);
+ SkScalar scale = 0;
+
+ if (this->isLinearText())
+ {
+ scale = fTextSize / kCanonicalTextSizeForPaths;
+ // this gets restored by restore
+ ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths));
+ }
+
+ SkAutoGlyphCache autoCache(*this, NULL);
+ SkGlyphCache* cache = autoCache.getCache();
+ SkMeasureCacheProc glyphCacheProc;
+ glyphCacheProc = this->getMeasureCacheProc(kForward_TextBufferDirection,
+ NULL != bounds);
+
+ const char* text = (const char*)textData;
+ const char* stop = text + byteLength;
+ int count = 0;
+
+ if (this->isDevKernText())
+ {
+ // we adjust the widths returned here through auto-kerning
+ SkAutoKern autokern;
+ SkFixed prevWidth = 0;
+
+ if (scale) {
+ while (text < stop) {
+ const SkGlyph& g = glyphCacheProc(cache, &text);
+ if (widths) {
+ SkFixed adjust = autokern.adjust(g);
+
+ if (count > 0) {
+ SkScalar w = SkFixedToScalar(prevWidth + adjust);
+ *widths++ = SkScalarMul(w, scale);
+ }
+ prevWidth = g.fAdvanceX;
+ }
+ if (bounds) {
+ set_bounds(g, bounds++, scale);
+ }
+ ++count;
+ }
+ if (count > 0 && widths) {
+ *widths = SkScalarMul(SkFixedToScalar(prevWidth), scale);
+ }
+ } else {
+ while (text < stop) {
+ const SkGlyph& g = glyphCacheProc(cache, &text);
+ if (widths) {
+ SkFixed adjust = autokern.adjust(g);
+
+ if (count > 0) {
+ *widths++ = SkFixedToScalar(prevWidth + adjust);
+ }
+ prevWidth = g.fAdvanceX;
+ }
+ if (bounds) {
+ set_bounds(g, bounds++);
+ }
+ ++count;
+ }
+ if (count > 0 && widths) {
+ *widths = SkFixedToScalar(prevWidth);
+ }
+ }
+ } else { // no devkern
+ if (scale) {
+ while (text < stop) {
+ const SkGlyph& g = glyphCacheProc(cache, &text);
+ if (widths) {
+ *widths++ = SkScalarMul(SkFixedToScalar(g.fAdvanceX),
+ scale);
+ }
+ if (bounds) {
+ set_bounds(g, bounds++, scale);
+ }
+ ++count;
+ }
+ } else {
+ while (text < stop) {
+ const SkGlyph& g = glyphCacheProc(cache, &text);
+ if (widths) {
+ *widths++ = SkFixedToScalar(g.fAdvanceX);
+ }
+ if (bounds) {
+ set_bounds(g, bounds++);
+ }
+ ++count;
+ }
+ }
+ }
+
+ SkASSERT(text == stop);
+ return count;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkDraw.h"
+
+void SkPaint::getTextPath(const void* textData, size_t length, SkScalar x, SkScalar y, SkPath* path) const
+{
+ const char* text = (const char*)textData;
+ SkASSERT(length == 0 || text != NULL);
+ if (text == NULL || length == 0 || path == NULL)
+ return;
+
+ SkTextToPathIter iter(text, length, *this, false, true);
+ SkMatrix matrix;
+ SkScalar prevXPos = 0;
+
+ matrix.setScale(iter.getPathScale(), iter.getPathScale());
+ 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;
+ }
+}
+
+static void add_flattenable(SkDescriptor* desc, uint32_t tag,
+ SkFlattenableWriteBuffer* buffer) {
+ buffer->flatten(desc->addEntry(tag, buffer->size(), NULL));
+}
+
+/*
+ * 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 };
+
+static SkMask::Format computeMaskFormat(const SkPaint& paint)
+{
+ uint32_t flags = paint.getFlags();
+
+ return (flags & SkPaint::kAntiAlias_Flag) ? SkMask::kA8_Format : SkMask::kBW_Format;
+}
+
+static SkScalerContext::Hints computeScalerHints(const SkPaint& paint)
+{
+ uint32_t flags = paint.getFlags();
+
+ if (flags & SkPaint::kLinearText_Flag)
+ return SkScalerContext::kNo_Hints;
+ else if (flags & SkPaint::kSubpixelText_Flag)
+ return SkScalerContext::kSubpixel_Hints;
+ else
+ return SkScalerContext::kNormal_Hints;
+}
+
+void SkScalerContext::MakeRec(const SkPaint& paint, const SkMatrix* deviceMatrix, Rec* rec)
+{
+ SkASSERT(deviceMatrix == NULL || (deviceMatrix->getType() & SkMatrix::kPerspective_Mask) == 0);
+
+ rec->fFontID = SkTypeface::UniqueID(paint.getTypeface());
+ 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.isFakeBoldText())
+ {
+ 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;
+ }
+
+ unsigned flags = SkFontHost::ComputeGammaFlag(paint);
+
+ if (paint.isDevKernText())
+ flags |= SkScalerContext::kDevKernText_Flag;
+
+ if (style != SkPaint::kFill_Style && strokeWidth > 0)
+ {
+ rec->fFrameWidth = strokeWidth;
+ rec->fMiterLimit = paint.getStrokeMiter();
+ rec->fStrokeJoin = SkToU8(paint.getStrokeJoin());
+
+ if (style == SkPaint::kStrokeAndFill_Style)
+ flags |= SkScalerContext::kFrameAndFill_Flag;
+ }
+ else
+ {
+ rec->fFrameWidth = 0;
+ rec->fMiterLimit = 0;
+ rec->fStrokeJoin = 0;
+ }
+
+ rec->fHints = SkToU8(computeScalerHints(paint));
+ rec->fMaskFormat = SkToU8(computeMaskFormat(paint));
+ rec->fFlags = SkToU8(flags);
+}
+
+#define MIN_SIZE_FOR_EFFECT_BUFFER 1024
+
+void SkPaint::descriptorProc(const SkMatrix* deviceMatrix,
+ void (*proc)(const SkDescriptor*, void*),
+ void* context) const
+{
+ SkScalerContext::Rec rec;
+
+ SkScalerContext::MakeRec(*this, deviceMatrix, &rec);
+
+ size_t descSize = sizeof(rec);
+ int entryCount = 1;
+ SkPathEffect* pe = this->getPathEffect();
+ SkMaskFilter* mf = this->getMaskFilter();
+ SkRasterizer* ra = this->getRasterizer();
+
+ SkFlattenableWriteBuffer peBuffer(MIN_SIZE_FOR_EFFECT_BUFFER);
+ SkFlattenableWriteBuffer mfBuffer(MIN_SIZE_FOR_EFFECT_BUFFER);
+ SkFlattenableWriteBuffer raBuffer(MIN_SIZE_FOR_EFFECT_BUFFER);
+
+ if (pe) {
+ peBuffer.writeFlattenable(pe);
+ descSize += peBuffer.size();
+ entryCount += 1;
+ rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing when we do the scan conversion
+ // seems like we could support kLCD as well at this point...
+ }
+ if (mf) {
+ mfBuffer.writeFlattenable(mf);
+ descSize += mfBuffer.size();
+ entryCount += 1;
+ rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing with maskfilters
+ }
+ if (ra) {
+ raBuffer.writeFlattenable(ra);
+ descSize += raBuffer.size();
+ entryCount += 1;
+ rec.fMaskFormat = SkMask::kA8_Format; // force antialiasing when we do the scan conversion
+ }
+ descSize += SkDescriptor::ComputeOverhead(entryCount);
+
+ SkAutoDescriptor ad(descSize);
+ SkDescriptor* desc = ad.getDesc();
+
+ desc->init();
+ desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
+
+ if (pe) {
+ add_flattenable(desc, kPathEffect_SkDescriptorTag, &peBuffer);
+ }
+ if (mf) {
+ add_flattenable(desc, kMaskFilter_SkDescriptorTag, &mfBuffer);
+ }
+ if (ra) {
+ add_flattenable(desc, kRasterizer_SkDescriptorTag, &raBuffer);
+ }
+
+ SkASSERT(descSize == desc->getLength());
+ desc->computeChecksum();
+
+ proc(desc, context);
+}
+
+static void DetachDescProc(const SkDescriptor* desc, void* context)
+{
+ *((SkGlyphCache**)context) = SkGlyphCache::DetachCache(desc);
+}
+
+SkGlyphCache* SkPaint::detachCache(const SkMatrix* deviceMatrix) const
+{
+ SkGlyphCache* cache;
+ this->descriptorProc(deviceMatrix, DetachDescProc, &cache);
+ return cache;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkStream.h"
+
+void SkPaint::flatten(SkFlattenableWriteBuffer& buffer) const {
+ buffer.writeTypeface(this->getTypeface());
+ buffer.writeScalar(this->getTextSize());
+ buffer.writeScalar(this->getTextScaleX());
+ buffer.writeScalar(this->getTextSkewX());
+ buffer.writeFlattenable(this->getPathEffect());
+ buffer.writeFlattenable(this->getShader());
+ buffer.writeFlattenable(this->getXfermode());
+ buffer.writeFlattenable(this->getMaskFilter());
+ buffer.writeFlattenable(this->getColorFilter());
+ buffer.writeFlattenable(this->getRasterizer());
+ buffer.writeFlattenable(this->getLooper());
+ buffer.write32(this->getColor());
+ buffer.writeScalar(this->getStrokeWidth());
+ buffer.writeScalar(this->getStrokeMiter());
+ buffer.write16(this->getFlags());
+ buffer.write8(this->getTextAlign());
+ buffer.write8(this->getStrokeCap());
+ buffer.write8(this->getStrokeJoin());
+ buffer.write8(this->getStyle());
+ buffer.write8(this->getTextEncoding());
+}
+
+void SkPaint::unflatten(SkFlattenableReadBuffer& buffer) {
+ this->setTypeface(buffer.readTypeface());
+ this->setTextSize(buffer.readScalar());
+ this->setTextScaleX(buffer.readScalar());
+ this->setTextSkewX(buffer.readScalar());
+ this->setPathEffect((SkPathEffect*) buffer.readFlattenable())->safeUnref();
+ this->setShader((SkShader*) buffer.readFlattenable())->safeUnref();
+ this->setXfermode((SkXfermode*) buffer.readFlattenable())->safeUnref();
+ this->setMaskFilter((SkMaskFilter*) buffer.readFlattenable())->safeUnref();
+ this->setColorFilter((SkColorFilter*) buffer.readFlattenable())->safeUnref();
+ this->setRasterizer((SkRasterizer*) buffer.readFlattenable())->safeUnref();
+ this->setLooper((SkDrawLooper*) buffer.readFlattenable())->safeUnref();
+ this->setColor(buffer.readU32());
+ this->setStrokeWidth(buffer.readScalar());
+ this->setStrokeMiter(buffer.readScalar());
+ this->setFlags(buffer.readU16());
+ this->setTextAlign((SkPaint::Align) buffer.readU8());
+ this->setStrokeCap((SkPaint::Cap) buffer.readU8());
+ this->setStrokeJoin((SkPaint::Join) buffer.readU8());
+ this->setStyle((SkPaint::Style) buffer.readU8());
+ this->setTextEncoding((SkPaint::TextEncoding) buffer.readU8());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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)
+}
+
+bool SkPaint::canComputeFastBounds() const {
+ // use bit-or since no need for early exit
+ return (reinterpret_cast<uintptr_t>(this->getMaskFilter()) |
+ reinterpret_cast<uintptr_t>(this->getLooper()) |
+ reinterpret_cast<uintptr_t>(this->getRasterizer()) |
+ reinterpret_cast<uintptr_t>(this->getPathEffect())) == 0;
+}
+
+const SkRect& SkPaint::computeFastBounds(const SkRect& src,
+ SkRect* storage) const {
+ SkASSERT(storage);
+
+ if (this->getStyle() != SkPaint::kFill_Style) {
+ // if we're stroked, outset the rect by the radius (and join type)
+ SkScalar radius = SkScalarHalf(this->getStrokeWidth());
+
+ if (0 == radius) { // hairline
+ radius = SK_Scalar1;
+ } else if (this->getStrokeJoin() == SkPaint::kMiter_Join) {
+ SkScalar scale = this->getStrokeMiter();
+ if (scale > SK_Scalar1) {
+ radius = SkScalarMul(radius, scale);
+ }
+ }
+ storage->set(src.fLeft - radius, src.fTop - radius,
+ src.fRight + radius, src.fBottom + radius);
+ return *storage;
+ }
+ // no adjustments needed, just return the original rect
+ return src;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+static bool has_thick_frame(const SkPaint& paint)
+{
+ return paint.getStrokeWidth() > 0 && paint.getStyle() != SkPaint::kFill_Style;
+}
+
+SkTextToPathIter::SkTextToPathIter( const char text[], size_t length,
+ const SkPaint& paint,
+ bool applyStrokeAndPathEffects,
+ bool forceLinearTextOn)
+ : fPaint(paint) /* make a copy of the paint */
+{
+ fGlyphCacheProc = paint.getMeasureCacheProc(SkPaint::kForward_TextBufferDirection,
+ true);
+
+ if (forceLinearTextOn)
+ fPaint.setLinearText(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.isLinearText() && !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 = fPaint.detachCache(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(fPaint.measure_text(fCache, text, length, &count, NULL), fScale);
+ if (paint.getTextAlign() == SkPaint::kCenter_Align)
+ width = SkScalarHalf(width);
+ xOffset = -width;
+ }
+ fXPos = xOffset;
+ fPrevAdvance = 0;
+
+ fText = text;
+ fStop = text + length;
+}
+
+SkTextToPathIter::~SkTextToPathIter()
+{
+ SkGlyphCache::AttachCache(fCache);
+}
+
+const SkPath* SkTextToPathIter::next(SkScalar* xpos)
+{
+ while (fText < fStop)
+ {
+ const SkGlyph& glyph = fGlyphCacheProc(fCache, &fText);
+
+ fXPos += SkScalarMul(SkFixedToScalar(fPrevAdvance + fAutoKern.adjust(glyph)), fScale);
+ fPrevAdvance = glyph.fAdvanceX; // + fPaint.getTextTracking();
+
+ if (glyph.fWidth)
+ {
+ if (xpos)
+ *xpos = fXPos;
+ return fCache->findPath(glyph);
+ }
+ }
+ return NULL;
+}
diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp
new file mode 100644
index 0000000000..82eb98068f
--- /dev/null
+++ b/src/core/SkPath.cpp
@@ -0,0 +1,1438 @@
+/* libs/graphics/sgl/SkPath.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkPath.h"
+#include "SkFlattenable.h"
+#include "SkMath.h"
+
+////////////////////////////////////////////////////////////////////////////
+
+/* This guy's constructor/destructor bracket a path editing operation. It is
+ used when we know the bounds of the amount we are going to add to the path
+ (usually a new contour, but not required).
+
+ It captures some state about the path up front (i.e. if it already has a
+ cached bounds), and the if it can, it updates the cache bounds explicitly,
+ avoiding the need to revisit all of the points in computeBounds().
+ */
+class SkAutoPathBoundsUpdate {
+public:
+ SkAutoPathBoundsUpdate(SkPath* path, const SkRect& r) : fRect(r) {
+ this->init(path);
+ }
+
+ SkAutoPathBoundsUpdate(SkPath* path, SkScalar left, SkScalar top,
+ SkScalar right, SkScalar bottom) {
+ fRect.set(left, top, right, bottom);
+ this->init(path);
+ }
+
+ ~SkAutoPathBoundsUpdate() {
+ if (fEmpty) {
+ fPath->fFastBounds = fRect;
+ fPath->fFastBoundsIsDirty = false;
+ } else if (!fDirty) {
+ fPath->fFastBounds.join(fRect);
+ fPath->fFastBoundsIsDirty = false;
+ }
+ }
+
+private:
+ const SkPath* fPath;
+ SkRect fRect;
+ bool fDirty;
+ bool fEmpty;
+
+ // returns true if we should proceed
+ void init(const SkPath* path) {
+ fPath = path;
+ fDirty = path->fFastBoundsIsDirty;
+ fEmpty = path->isEmpty();
+ }
+};
+
+static void compute_fast_bounds(SkRect* bounds, const SkTDArray<SkPoint>& pts) {
+ if (pts.count() <= 1) { // we ignore just 1 point (moveto)
+ bounds->set(0, 0, 0, 0);
+ } else {
+ bounds->set(pts.begin(), pts.count());
+// SkDebugf("------- compute bounds %p %d", &pts, pts.count());
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+/*
+ 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() : fFastBoundsIsDirty(true), fFillType(kWinding_FillType) {}
+
+SkPath::SkPath(const SkPath& src) {
+ SkDEBUGCODE(src.validate();)
+ *this = src;
+}
+
+SkPath::~SkPath() {
+ SkDEBUGCODE(this->validate();)
+}
+
+SkPath& SkPath::operator=(const SkPath& src) {
+ SkDEBUGCODE(src.validate();)
+
+ if (this != &src) {
+ fFastBounds = src.fFastBounds;
+ fPts = src.fPts;
+ fVerbs = src.fVerbs;
+ fFillType = src.fFillType;
+ fFastBoundsIsDirty = src.fFastBoundsIsDirty;
+ }
+ SkDEBUGCODE(this->validate();)
+ return *this;
+}
+
+void SkPath::swap(SkPath& other) {
+ SkASSERT(&other != NULL);
+
+ if (this != &other) {
+ SkTSwap<SkRect>(fFastBounds, other.fFastBounds);
+ fPts.swap(other.fPts);
+ fVerbs.swap(other.fVerbs);
+ SkTSwap<uint8_t>(fFillType, other.fFillType);
+ SkTSwap<uint8_t>(fFastBoundsIsDirty, other.fFastBoundsIsDirty);
+ }
+}
+
+void SkPath::reset() {
+ SkDEBUGCODE(this->validate();)
+
+ fPts.reset();
+ fVerbs.reset();
+ fFastBoundsIsDirty = true;
+}
+
+void SkPath::rewind() {
+ SkDEBUGCODE(this->validate();)
+
+ fPts.rewind();
+ fVerbs.rewind();
+ fFastBoundsIsDirty = true;
+}
+
+bool SkPath::isEmpty() const {
+ SkDEBUGCODE(this->validate();)
+
+ int count = fVerbs.count();
+ return count == 0 || (count == 1 && fVerbs[0] == kMove_Verb);
+}
+
+bool SkPath::isRect(SkRect*) const {
+ SkDEBUGCODE(this->validate();)
+
+ SkASSERT(!"unimplemented");
+ return false;
+}
+
+int SkPath::getPoints(SkPoint copy[], int max) const {
+ SkDEBUGCODE(this->validate();)
+
+ 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 {
+ SkDEBUGCODE(this->validate();)
+
+ 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) {
+ SkDEBUGCODE(this->validate();)
+
+ int count = fPts.count();
+ if (count == 0) {
+ this->moveTo(x, y);
+ } else {
+ fPts[count - 1].set(x, y);
+ }
+}
+
+#define ALWAYS_FAST_BOUNDS_FOR_NOW true
+
+void SkPath::computeBounds(SkRect* bounds, BoundsType bt) const {
+ SkDEBUGCODE(this->validate();)
+
+ SkASSERT(bounds);
+
+ // we BoundsType for now
+
+ if (fFastBoundsIsDirty) {
+ fFastBoundsIsDirty = false;
+ compute_fast_bounds(&fFastBounds, fPts);
+ }
+ *bounds = fFastBounds;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Construction methods
+
+void SkPath::incReserve(U16CPU inc) {
+ SkDEBUGCODE(this->validate();)
+
+ fVerbs.setReserve(fVerbs.count() + inc);
+ fPts.setReserve(fPts.count() + inc);
+
+ SkDEBUGCODE(this->validate();)
+}
+
+void SkPath::moveTo(SkScalar x, SkScalar y) {
+ SkDEBUGCODE(this->validate();)
+
+ 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);
+
+ fFastBoundsIsDirty = true;
+}
+
+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) {
+ SkDEBUGCODE(this->validate();)
+
+ if (fVerbs.count() == 0) {
+ fPts.append()->set(0, 0);
+ *fVerbs.append() = kMove_Verb;
+ }
+ fPts.append()->set(x, y);
+ *fVerbs.append() = kLine_Verb;
+
+ fFastBoundsIsDirty = true;
+}
+
+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) {
+ SkDEBUGCODE(this->validate();)
+
+ 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;
+
+ fFastBoundsIsDirty = true;
+}
+
+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) {
+ SkDEBUGCODE(this->validate();)
+
+ 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;
+
+ fFastBoundsIsDirty = true;
+}
+
+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() {
+ SkDEBUGCODE(this->validate();)
+
+ 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) {
+ SkAutoPathBoundsUpdate apbu(this, left, top, right, bottom);
+
+ this->incReserve(5);
+
+ 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) {
+ SkAutoPathBoundsUpdate apbu(this, rect);
+
+ 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();
+}
+
+static void add_corner_arc(SkPath* path, const SkRect& rect,
+ SkScalar rx, SkScalar ry, int startAngle,
+ SkPath::Direction dir, bool forceMoveTo) {
+ rx = SkMinScalar(SkScalarHalf(rect.width()), rx);
+ ry = SkMinScalar(SkScalarHalf(rect.height()), ry);
+
+ SkRect r;
+ r.set(-rx, -ry, rx, ry);
+
+ switch (startAngle) {
+ case 0:
+ r.offset(rect.fRight - r.fRight, rect.fBottom - r.fBottom);
+ break;
+ case 90:
+ r.offset(rect.fLeft - r.fLeft, rect.fBottom - r.fBottom);
+ break;
+ case 180: r.offset(rect.fLeft - r.fLeft, rect.fTop - r.fTop); break;
+ case 270: r.offset(rect.fRight - r.fRight, rect.fTop - r.fTop); break;
+ default: SkASSERT(!"unexpected startAngle in add_corner_arc");
+ }
+
+ SkScalar start = SkIntToScalar(startAngle);
+ SkScalar sweep = SkIntToScalar(90);
+ if (SkPath::kCCW_Direction == dir) {
+ start += sweep;
+ sweep = -sweep;
+ }
+
+ path->arcTo(r, start, sweep, forceMoveTo);
+}
+
+void SkPath::addRoundRect(const SkRect& rect, const SkScalar rad[],
+ Direction dir) {
+ SkAutoPathBoundsUpdate apbu(this, rect);
+
+ if (kCW_Direction == dir) {
+ add_corner_arc(this, rect, rad[0], rad[1], 180, dir, true);
+ add_corner_arc(this, rect, rad[2], rad[3], 270, dir, false);
+ add_corner_arc(this, rect, rad[4], rad[5], 0, dir, false);
+ add_corner_arc(this, rect, rad[6], rad[7], 90, dir, false);
+ } else {
+ add_corner_arc(this, rect, rad[0], rad[1], 180, dir, true);
+ add_corner_arc(this, rect, rad[6], rad[7], 90, dir, false);
+ add_corner_arc(this, rect, rad[4], rad[5], 0, dir, false);
+ add_corner_arc(this, rect, rad[2], rad[3], 270, dir, false);
+ }
+ this->close();
+}
+
+void SkPath::addOval(const SkRect& oval, Direction dir) {
+ SkAutoPathBoundsUpdate apbu(this, oval);
+
+ SkScalar cx = oval.centerX();
+ SkScalar cy = oval.centerY();
+ SkScalar rx = SkScalarHalf(oval.width());
+ SkScalar ry = SkScalarHalf(oval.height());
+#if 0 // these seem faster than using quads (1/2 the number of edges)
+ 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);
+
+ /*
+ To handle imprecision in computing the center and radii, we revert to
+ the provided bounds when we can (i.e. use oval.fLeft instead of cx-rx)
+ to ensure that we don't exceed the oval's bounds *ever*, since we want
+ to use oval for our fast-bounds, rather than have to recompute it.
+ */
+ const SkScalar L = oval.fLeft; // cx - rx
+ const SkScalar T = oval.fTop; // cy - ry
+ const SkScalar R = oval.fRight; // cx + rx
+ const SkScalar B = oval.fBottom; // cy + ry
+
+ this->incReserve(17); // 8 quads + close
+ this->moveTo(R, cy);
+ if (dir == kCCW_Direction) {
+ this->quadTo( R, cy - sy, cx + mx, cy - my);
+ this->quadTo(cx + sx, T, cx , T);
+ this->quadTo(cx - sx, T, cx - mx, cy - my);
+ this->quadTo( L, cy - sy, L, cy );
+ this->quadTo( L, cy + sy, cx - mx, cy + my);
+ this->quadTo(cx - sx, B, cx , B);
+ this->quadTo(cx + sx, B, cx + mx, cy + my);
+ this->quadTo( R, cy + sy, R, cy );
+ } else {
+ this->quadTo( R, cy + sy, cx + mx, cy + my);
+ this->quadTo(cx + sx, B, cx , B);
+ this->quadTo(cx - sx, B, cx - mx, cy + my);
+ this->quadTo( L, cy + sy, L, cy );
+ this->quadTo( L, cy - sy, cx - mx, cy - my);
+ this->quadTo(cx - sx, T, cx , T);
+ this->quadTo(cx + sx, T, cx + mx, cy - my);
+ this->quadTo( R, cy - sy, R, cy );
+ }
+#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);
+ }
+}
+
+#include "SkGeometry.h"
+
+static int build_arc_points(const SkRect& oval, SkScalar startAngle,
+ SkScalar sweepAngle,
+ SkPoint pts[kSkBuildQuadArcStorage]) {
+ SkVector start, stop;
+
+ start.fY = SkScalarSinCos(SkDegreesToRadians(startAngle), &start.fX);
+ stop.fY = SkScalarSinCos(SkDegreesToRadians(startAngle + sweepAngle),
+ &stop.fX);
+
+ SkMatrix matrix;
+
+ matrix.setScale(SkScalarHalf(oval.width()), SkScalarHalf(oval.height()));
+ matrix.postTranslate(oval.centerX(), oval.centerY());
+
+ return SkBuildQuadArc(start, stop,
+ sweepAngle > 0 ? kCW_SkRotationDirection : kCCW_SkRotationDirection,
+ &matrix, pts);
+}
+
+void SkPath::arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
+ bool forceMoveTo) {
+ if (oval.width() < 0 || oval.height() < 0) {
+ return;
+ }
+
+ SkPoint pts[kSkBuildQuadArcStorage];
+ int count = build_arc_points(oval, startAngle, sweepAngle, pts);
+ SkASSERT((count & 1) == 1);
+
+ if (fVerbs.count() == 0) {
+ forceMoveTo = true;
+ }
+ this->incReserve(count);
+ forceMoveTo ? this->moveTo(pts[0]) : this->lineTo(pts[0]);
+ for (int i = 1; i < count; i += 2) {
+ this->quadTo(pts[i], pts[i+1]);
+ }
+}
+
+void SkPath::addArc(const SkRect& oval, SkScalar startAngle,
+ SkScalar sweepAngle) {
+ if (oval.isEmpty() || 0 == sweepAngle) {
+ return;
+ }
+
+ const SkScalar kFullCircleAngle = SkIntToScalar(360);
+
+ if (sweepAngle >= kFullCircleAngle || sweepAngle <= -kFullCircleAngle) {
+ this->addOval(oval, sweepAngle > 0 ? kCW_Direction : kCCW_Direction);
+ return;
+ }
+
+ SkPoint pts[kSkBuildQuadArcStorage];
+ int count = build_arc_points(oval, startAngle, sweepAngle, pts);
+
+ this->incReserve(count);
+ this->moveTo(pts[0]);
+ for (int i = 1; i < count; i += 2) {
+ this->quadTo(pts[i], pts[i+1]);
+ }
+}
+
+/*
+ Need to handle the case when the angle is sharp, and our computed end-points
+ for the arc go behind pt1 and/or p2...
+*/
+void SkPath::arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
+ SkScalar radius) {
+ SkVector before, after;
+
+ // need to know our prev pt so we can construct tangent vectors
+ {
+ SkPoint start;
+ this->getLastPt(&start);
+ before.setNormalize(x1 - start.fX, y1 - start.fY);
+ after.setNormalize(x2 - x1, y2 - y1);
+ }
+
+ SkScalar cosh = SkPoint::DotProduct(before, after);
+ SkScalar sinh = SkPoint::CrossProduct(before, after);
+
+ if (SkScalarNearlyZero(sinh)) { // angle is too tight
+ return;
+ }
+
+ SkScalar dist = SkScalarMulDiv(radius, SK_Scalar1 - cosh, sinh);
+ if (dist < 0) {
+ dist = -dist;
+ }
+
+ SkScalar xx = x1 - SkScalarMul(dist, before.fX);
+ SkScalar yy = y1 - SkScalarMul(dist, before.fY);
+ SkRotationDirection arcDir;
+
+ // now turn before/after into normals
+ if (sinh > 0) {
+ before.rotateCCW();
+ after.rotateCCW();
+ arcDir = kCW_SkRotationDirection;
+ } else {
+ before.rotateCW();
+ after.rotateCW();
+ arcDir = kCCW_SkRotationDirection;
+ }
+
+ SkMatrix matrix;
+ SkPoint pts[kSkBuildQuadArcStorage];
+
+ matrix.setScale(radius, radius);
+ matrix.postTranslate(xx - SkScalarMul(radius, before.fX),
+ yy - SkScalarMul(radius, before.fY));
+
+ int count = SkBuildQuadArc(before, after, arcDir, &matrix, pts);
+
+ this->incReserve(count);
+ // [xx,yy] == pts[0]
+ this->lineTo(xx, yy);
+ for (int i = 1; i < count; i += 2) {
+ this->quadTo(pts[i], pts[i+1]);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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::MapPtsProc proc = matrix.getMapPtsProc();
+
+ while ((verb = iter.next(pts)) != kDone_Verb) {
+ switch (verb) {
+ case kMove_Verb:
+ proc(matrix, &pts[0], &pts[0], 1);
+ this->moveTo(pts[0]);
+ break;
+ case kLine_Verb:
+ proc(matrix, &pts[1], &pts[1], 1);
+ this->lineTo(pts[1]);
+ break;
+ case kQuad_Verb:
+ proc(matrix, &pts[1], &pts[1], 2);
+ this->quadTo(pts[1], pts[2]);
+ break;
+ case kCubic_Verb:
+ proc(matrix, &pts[1], &pts[1], 3);
+ this->cubicTo(pts[1], pts[2], pts[3]);
+ break;
+ case kClose_Verb:
+ this->close();
+ break;
+ default:
+ SkASSERT(!"unknown verb");
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const uint8_t 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;
+ }
+
+ this->incReserve(vcount);
+
+ const uint8_t* 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;
+ }
+
+ this->incReserve(vcount);
+
+ const uint8_t* 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]];
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkPath::offset(SkScalar dx, SkScalar dy, SkPath* dst) const {
+ SkMatrix matrix;
+
+ matrix.setTranslate(dx, dy);
+ 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]);
+ }
+}
+
+void SkPath::transform(const SkMatrix& matrix, SkPath* dst) const {
+ SkDEBUGCODE(this->validate();)
+ if (dst == NULL) {
+ 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);
+ matrix.mapPoints(dst->fPts.begin(), dst->fPts.count());
+ } else {
+ // remember that dst might == this, so be sure to check
+ // fFastBoundsIsDirty before we set it
+ if (!fFastBoundsIsDirty && matrix.rectStaysRect() && fPts.count() > 1) {
+ // if we're empty, fastbounds should not be mapped
+ matrix.mapRect(&dst->fFastBounds, fFastBounds);
+ dst->fFastBoundsIsDirty = false;
+ } else {
+ dst->fFastBoundsIsDirty = true;
+ }
+
+ if (this != dst) {
+ dst->fVerbs = fVerbs;
+ dst->fPts.setCount(fPts.count());
+ dst->fFillType = fFillType;
+ }
+ matrix.mapPoints(dst->fPts.begin(), fPts.begin(), fPts.count());
+ SkDEBUGCODE(dst->validate();)
+ }
+}
+
+void SkPath::updateBoundsCache() const {
+ if (fFastBoundsIsDirty) {
+ SkRect r;
+ this->computeBounds(&r, kFast_BoundsType);
+ SkASSERT(!fFastBoundsIsDirty);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+enum NeedMoveToState {
+ kAfterClose_NeedMoveToState,
+ kAfterCons_NeedMoveToState,
+ kAfterPrefix_NeedMoveToState
+};
+
+SkPath::Iter::Iter() {
+#ifdef SK_DEBUG
+ fPts = NULL;
+ 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 = NULL;
+ fVerbStop = NULL;
+ 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 == NULL || 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 (NULL == 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[]]
+*/
+
+void SkPath::flatten(SkFlattenableWriteBuffer& buffer) const {
+ SkDEBUGCODE(this->validate();)
+
+ buffer.write32(fPts.count());
+ buffer.write32(fVerbs.count());
+ buffer.write32(fFillType);
+ buffer.writeMul4(fPts.begin(), sizeof(SkPoint) * fPts.count());
+ buffer.writePad(fVerbs.begin(), fVerbs.count());
+}
+
+void SkPath::unflatten(SkFlattenableReadBuffer& buffer) {
+ 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());
+
+ fFastBoundsIsDirty = true;
+
+ SkDEBUGCODE(this->validate();)
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#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) {
+ // can skip the separater if data[i] is negative
+ stream->write(" ", 1);
+ }
+ 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
+
+void SkPath::validate() const {
+ SkASSERT(this != NULL);
+ SkASSERT((fFillType & ~3) == 0);
+ fPts.validate();
+ fVerbs.validate();
+
+ if (!fFastBoundsIsDirty) {
+ SkRect bounds;
+ compute_fast_bounds(&bounds, fPts);
+ // can't call contains(), since it returns false if the rect is empty
+ SkASSERT(fFastBounds.fLeft <= bounds.fLeft);
+ SkASSERT(fFastBounds.fTop <= bounds.fTop);
+ SkASSERT(fFastBounds.fRight >= bounds.fRight);
+ SkASSERT(fFastBounds.fBottom >= bounds.fBottom);
+ }
+}
+
+#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 uint8_t* 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/src/core/SkPathEffect.cpp b/src/core/SkPathEffect.cpp
new file mode 100644
index 0000000000..29058958e3
--- /dev/null
+++ b/src/core/SkPathEffect.cpp
@@ -0,0 +1,142 @@
+/* libs/graphics/sgl/SkPathEffect.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkPathEffect.h"
+#include "SkPath.h"
+#include "SkBuffer.h"
+
+//////////////////////////////////////////////////////////////////////////////////
+
+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(SkFlattenableWriteBuffer& buffer)
+{
+ buffer.writeFlattenable(fPE0);
+ buffer.writeFlattenable(fPE1);
+}
+
+SkPairPathEffect::SkPairPathEffect(SkFlattenableReadBuffer& buffer)
+{
+ fPE0 = (SkPathEffect*)buffer.readFlattenable();
+ fPE1 = (SkPathEffect*)buffer.readFlattenable();
+}
+
+//////////////////////////////////////////////////////////////////////////////////
+
+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);
+}
+
+//////////////////////////////////////////////////////////////////////////////////
+
+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);
+}
+
+/////////////////////////////////////////////////////////////////////////////////
+
+#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(SkFlattenableReadBuffer& buffer)
+{
+ return SkNEW_ARGS(SkStrokePathEffect, (buffer));
+}
+
+void SkStrokePathEffect::flatten(SkFlattenableWriteBuffer& buffer)
+{
+ buffer.writeScalar(fWidth);
+ buffer.writeScalar(fMiter);
+ buffer.write8(fStyle);
+ buffer.write8(fJoin);
+ buffer.write8(fCap);
+}
+
+SkStrokePathEffect::SkStrokePathEffect(SkFlattenableReadBuffer& buffer)
+{
+ fWidth = buffer.readScalar();
+ fMiter = buffer.readScalar();
+ fStyle = buffer.readU8();
+ fJoin = buffer.readU8();
+ fCap = buffer.readU8();
+}
+
+
diff --git a/src/core/SkPathHeap.cpp b/src/core/SkPathHeap.cpp
new file mode 100644
index 0000000000..f6dae5dc57
--- /dev/null
+++ b/src/core/SkPathHeap.cpp
@@ -0,0 +1,55 @@
+#include "SkPathHeap.h"
+#include "SkPath.h"
+#include "SkStream.h"
+#include "SkFlattenable.h"
+#include <new>
+
+#define kPathCount 64
+
+SkPathHeap::SkPathHeap() : fHeap(kPathCount * sizeof(SkPath)) {
+}
+
+SkPathHeap::SkPathHeap(SkFlattenableReadBuffer& buffer)
+ : fHeap(kPathCount * sizeof(SkPath)) {
+ int count = buffer.readS32();
+
+ fPaths.setCount(count);
+ SkPath** ptr = fPaths.begin();
+ SkPath* p = (SkPath*)fHeap.allocThrow(count * sizeof(SkPath));
+
+ for (int i = 0; i < count; i++) {
+ new (p) SkPath;
+ p->unflatten(buffer);
+ *ptr++ = p; // record the pointer
+ p++; // move to the next storage location
+ }
+}
+
+SkPathHeap::~SkPathHeap() {
+ SkPath** iter = fPaths.begin();
+ SkPath** stop = fPaths.end();
+ while (iter < stop) {
+ (*iter)->~SkPath();
+ iter++;
+ }
+}
+
+int SkPathHeap::append(const SkPath& path) {
+ SkPath* p = (SkPath*)fHeap.allocThrow(sizeof(SkPath));
+ new (p) SkPath(path);
+ *fPaths.append() = p;
+ return fPaths.count();
+}
+
+void SkPathHeap::flatten(SkFlattenableWriteBuffer& buffer) const {
+ int count = fPaths.count();
+
+ buffer.write32(count);
+ SkPath** iter = fPaths.begin();
+ SkPath** stop = fPaths.end();
+ while (iter < stop) {
+ (*iter)->flatten(buffer);
+ iter++;
+ }
+}
+
diff --git a/src/core/SkPathHeap.h b/src/core/SkPathHeap.h
new file mode 100644
index 0000000000..b8f3bd3abb
--- /dev/null
+++ b/src/core/SkPathHeap.h
@@ -0,0 +1,37 @@
+#ifndef SkPathHeap_DEFINED
+#define SkPathHeap_DEFINED
+
+#include "SkRefCnt.h"
+#include "SkChunkAlloc.h"
+#include "SkTDArray.h"
+
+class SkPath;
+class SkFlattenableReadBuffer;
+class SkFlattenableWriteBuffer;
+
+class SkPathHeap : public SkRefCnt {
+public:
+ SkPathHeap();
+ SkPathHeap(SkFlattenableReadBuffer&);
+ virtual ~SkPathHeap();
+
+ // called during picture-record
+ int append(const SkPath&);
+
+ // called during picture-playback
+ int count() const { return fPaths.count(); }
+ const SkPath& operator[](int index) const {
+ return *fPaths[index];
+ }
+
+ void flatten(SkFlattenableWriteBuffer&) const;
+
+private:
+ // we store the paths in the heap (placement new)
+ SkChunkAlloc fHeap;
+ // we just store ptrs into fHeap here
+ SkTDArray<SkPath*> fPaths;
+};
+
+#endif
+
diff --git a/src/core/SkPathMeasure.cpp b/src/core/SkPathMeasure.cpp
new file mode 100644
index 0000000000..ec1510df32
--- /dev/null
+++ b/src/core/SkPathMeasure.cpp
@@ -0,0 +1,598 @@
+/*
+ * Copyright (C) 2006-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "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 quad_too_curvy(const SkPoint pts[3]) {
+ // 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;
+}
+
+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 cubic_too_curvy(const SkPoint pts[4]) {
+ return cheap_dist_exceeds_limit(pts[1],
+ SkScalarInterp(pts[0].fX, pts[3].fX, SK_Scalar1/3),
+ SkScalarInterp(pts[0].fY, pts[3].fY, SK_Scalar1/3))
+ ||
+ cheap_dist_exceeds_limit(pts[2],
+ SkScalarInterp(pts[0].fX, pts[3].fX, SK_Scalar1*2/3),
+ SkScalarInterp(pts[0].fY, pts[3].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) {
+ 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->setNormalize(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, NULL);
+ 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 = NULL;
+ 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 null 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 == NULL) {
+ 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 == NULL) {
+ 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, NULL);
+ 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/src/core/SkPicture.cpp b/src/core/SkPicture.cpp
new file mode 100644
index 0000000000..07cb0a6795
--- /dev/null
+++ b/src/core/SkPicture.cpp
@@ -0,0 +1,246 @@
+/*
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkPictureFlat.h"
+#include "SkPicturePlayback.h"
+#include "SkPictureRecord.h"
+
+#include "SkCanvas.h"
+#include "SkChunkAlloc.h"
+#include "SkPicture.h"
+#include "SkRegion.h"
+#include "SkStream.h"
+#include "SkTDArray.h"
+#include "SkTSearch.h"
+#include "SkTime.h"
+
+#include "SkReader32.h"
+#include "SkWriter32.h"
+
+#define DUMP_BUFFER_SIZE 65536
+
+//#define ENABLE_TIME_DRAW // dumps milliseconds for each draw
+
+
+#ifdef SK_DEBUG
+// enable SK_DEBUG_TRACE to trace DrawType elements when
+// recorded and played back
+// #define SK_DEBUG_TRACE
+// enable SK_DEBUG_SIZE to see the size of picture components
+// #define SK_DEBUG_SIZE
+// enable SK_DEBUG_DUMP to see the contents of recorded elements
+// #define SK_DEBUG_DUMP
+// enable SK_DEBUG_VALIDATE to check internal structures for consistency
+// #define SK_DEBUG_VALIDATE
+#endif
+
+#if defined SK_DEBUG_TRACE || defined SK_DEBUG_DUMP
+const char* DrawTypeToString(DrawType drawType) {
+ switch (drawType) {
+ case UNUSED: SkDebugf("DrawType UNUSED\n"); SkASSERT(0); break;
+ case CLIP_PATH: return "CLIP_PATH";
+ case CLIP_REGION: return "CLIP_REGION";
+ case CLIP_RECT: return "CLIP_RECT";
+ case CONCAT: return "CONCAT";
+ case DRAW_BITMAP: return "DRAW_BITMAP";
+ case DRAW_BITMAP_MATRIX: return "DRAW_BITMAP_MATRIX";
+ case DRAW_BITMAP_RECT: return "DRAW_BITMAP_RECT";
+ case DRAW_PAINT: return "DRAW_PAINT";
+ case DRAW_PATH: return "DRAW_PATH";
+ case DRAW_PICTURE: return "DRAW_PICTURE";
+ case DRAW_POINTS: return "DRAW_POINTS";
+ case DRAW_POS_TEXT: return "DRAW_POS_TEXT";
+ case DRAW_POS_TEXT_H: return "DRAW_POS_TEXT_H";
+ case DRAW_RECT_GENERAL: return "DRAW_RECT_GENERAL";
+ case DRAW_RECT_SIMPLE: return "DRAW_RECT_SIMPLE";
+ case DRAW_SPRITE: return "DRAW_SPRITE";
+ case DRAW_TEXT: return "DRAW_TEXT";
+ case DRAW_TEXT_ON_PATH: return "DRAW_TEXT_ON_PATH";
+ case RESTORE: return "RESTORE";
+ case ROTATE: return "ROTATE";
+ case SAVE: return "SAVE";
+ case SAVE_LAYER: return "SAVE_LAYER";
+ case SCALE: return "SCALE";
+ case SKEW: return "SKEW";
+ case TRANSLATE: return "TRANSLATE";
+ default:
+ SkDebugf("DrawType error 0x%08x\n", drawType);
+ SkASSERT(0);
+ break;
+ }
+ SkASSERT(0);
+ return NULL;
+}
+#endif
+
+#ifdef SK_DEBUG_VALIDATE
+static void validateMatrix(const SkMatrix* matrix) {
+ SkScalar scaleX = matrix->getScaleX();
+ SkScalar scaleY = matrix->getScaleY();
+ SkScalar skewX = matrix->getSkewX();
+ SkScalar skewY = matrix->getSkewY();
+ SkScalar perspX = matrix->getPerspX();
+ SkScalar perspY = matrix->getPerspY();
+ if (scaleX != 0 && skewX != 0)
+ SkDebugf("scaleX != 0 && skewX != 0\n");
+ SkASSERT(scaleX == 0 || skewX == 0);
+ SkASSERT(scaleY == 0 || skewY == 0);
+ SkASSERT(perspX == 0);
+ SkASSERT(perspY == 0);
+}
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkPicture::SkPicture() {
+ fRecord = NULL;
+ fPlayback = NULL;
+ fWidth = fHeight = 0;
+}
+
+SkPicture::SkPicture(const SkPicture& src) : SkRefCnt() {
+ fWidth = src.fWidth;
+ fHeight = src.fHeight;
+ fRecord = NULL;
+
+ /* We want to copy the src's playback. However, if that hasn't been built
+ yet, we need to fake a call to endRecording() without actually calling
+ it (since it is destructive, and we don't want to change src).
+ */
+ if (src.fPlayback) {
+ fPlayback = SkNEW_ARGS(SkPicturePlayback, (*src.fPlayback));
+ } else if (src.fRecord) {
+ // here we do a fake src.endRecording()
+ fPlayback = SkNEW_ARGS(SkPicturePlayback, (*src.fRecord));
+ } else {
+ fPlayback = NULL;
+ }
+}
+
+SkPicture::~SkPicture() {
+ fRecord->safeUnref();
+ SkDELETE(fPlayback);
+}
+
+void SkPicture::swap(SkPicture& other) {
+ SkTSwap(fRecord, other.fRecord);
+ SkTSwap(fPlayback, other.fPlayback);
+ SkTSwap(fWidth, other.fWidth);
+ SkTSwap(fHeight, other.fHeight);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkCanvas* SkPicture::beginRecording(int width, int height) {
+ if (fPlayback) {
+ SkDELETE(fPlayback);
+ fPlayback = NULL;
+ }
+
+ if (NULL != fRecord) {
+ fRecord->unref();
+ fRecord = NULL;
+ }
+
+ fRecord = SkNEW(SkPictureRecord);
+
+ fWidth = width;
+ fHeight = height;
+
+ SkBitmap bm;
+ bm.setConfig(SkBitmap::kNo_Config, width, height);
+ fRecord->setBitmapDevice(bm);
+
+ return fRecord;
+}
+
+SkCanvas* SkPicture::getRecordingCanvas() const {
+ // will be null if we are not recording
+ return fRecord;
+}
+
+void SkPicture::endRecording() {
+ if (NULL == fPlayback) {
+ if (NULL != fRecord) {
+ fPlayback = SkNEW_ARGS(SkPicturePlayback, (*fRecord));
+ fRecord->unref();
+ fRecord = NULL;
+ }
+ }
+ SkASSERT(NULL == fRecord);
+}
+
+void SkPicture::draw(SkCanvas* surface) {
+ this->endRecording();
+ if (fPlayback) {
+ fPlayback->draw(*surface);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkStream.h"
+
+#define PICTURE_VERSION 1
+
+SkPicture::SkPicture(SkStream* stream) : SkRefCnt() {
+ if (stream->readU32() != PICTURE_VERSION) {
+ sk_throw();
+ }
+
+ fWidth = stream->readU32();
+ fHeight = stream->readU32();
+
+ fRecord = NULL;
+ fPlayback = NULL;
+
+ if (stream->readBool()) {
+ fPlayback = SkNEW_ARGS(SkPicturePlayback, (stream));
+ }
+}
+
+void SkPicture::serialize(SkWStream* stream) const {
+ SkPicturePlayback* playback = fPlayback;
+
+ if (NULL == playback && fRecord) {
+ playback = SkNEW_ARGS(SkPicturePlayback, (*fRecord));
+ }
+
+ stream->write32(PICTURE_VERSION);
+ stream->write32(fWidth);
+ stream->write32(fHeight);
+ if (playback) {
+ stream->writeBool(true);
+ playback->serialize(stream);
+ // delete playback if it is a local version (i.e. cons'd up just now)
+ if (playback != fPlayback) {
+ SkDELETE(playback);
+ }
+ } else {
+ stream->writeBool(false);
+ }
+}
+
+void SkPicture::abortPlayback() {
+ if (NULL == fPlayback) {
+ return;
+ }
+ fPlayback->abort();
+}
+
+
diff --git a/src/core/SkPictureFlat.cpp b/src/core/SkPictureFlat.cpp
new file mode 100644
index 0000000000..e221e55a20
--- /dev/null
+++ b/src/core/SkPictureFlat.cpp
@@ -0,0 +1,257 @@
+#include "SkPictureFlat.h"
+
+#include "SkColorFilter.h"
+#include "SkDrawLooper.h"
+#include "SkMaskFilter.h"
+#include "SkRasterizer.h"
+#include "SkShader.h"
+#include "SkTypeface.h"
+#include "SkXfermode.h"
+
+SkFlatData* SkFlatData::Alloc(SkChunkAlloc* heap, int32_t size, int index) {
+ SkFlatData* result = (SkFlatData*) heap->allocThrow(size + sizeof(SkFlatData));
+ result->fIndex = index;
+ result->fAllocSize = size + sizeof(result->fAllocSize);
+ return result;
+}
+
+SkFlatBitmap* SkFlatBitmap::Flatten(SkChunkAlloc* heap, const SkBitmap& bitmap,
+ int index, SkRefCntRecorder* rec) {
+ SkFlattenableWriteBuffer buffer(1024);
+ buffer.setRefCntRecorder(rec);
+
+ bitmap.flatten(buffer);
+ size_t size = buffer.size();
+ SkFlatBitmap* result = (SkFlatBitmap*) INHERITED::Alloc(heap, size, index);
+ buffer.flatten(result->fBitmapData);
+ return result;
+}
+
+SkFlatMatrix* SkFlatMatrix::Flatten(SkChunkAlloc* heap, const SkMatrix& matrix, int index) {
+ int32_t size = sizeof(SkMatrix);
+ SkFlatMatrix* result = (SkFlatMatrix*) INHERITED::Alloc(heap, size, index);
+ memcpy(&result->fMatrixData, &matrix, sizeof(SkMatrix));
+ return result;
+}
+
+#ifdef SK_DEBUG_DUMP
+void SkFlatMatrix::dump() const {
+ const SkMatrix* matrix = (const SkMatrix*) fMatrixData;
+ char pBuffer[DUMP_BUFFER_SIZE];
+ char* bufferPtr = pBuffer;
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "matrix: ");
+ SkScalar scaleX = matrix->getScaleX();
+ SkMatrix defaultMatrix;
+ defaultMatrix.reset();
+ if (scaleX != defaultMatrix.getScaleX())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "scaleX:%g ", SkScalarToFloat(scaleX));
+ SkScalar scaleY = matrix->getScaleY();
+ if (scaleY != defaultMatrix.getScaleY())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "scaleY:%g ", SkScalarToFloat(scaleY));
+ SkScalar skewX = matrix->getSkewX();
+ if (skewX != defaultMatrix.getSkewX())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "skewX:%g ", SkScalarToFloat(skewX));
+ SkScalar skewY = matrix->getSkewY();
+ if (skewY != defaultMatrix.getSkewY())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "skewY:%g ", SkScalarToFloat(skewY));
+ SkScalar translateX = matrix->getTranslateX();
+ if (translateX != defaultMatrix.getTranslateX())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "translateX:%g ", SkScalarToFloat(translateX));
+ SkScalar translateY = matrix->getTranslateY();
+ if (translateY != defaultMatrix.getTranslateY())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "translateY:%g ", SkScalarToFloat(translateY));
+ SkScalar perspX = matrix->getPerspX();
+ if (perspX != defaultMatrix.getPerspX())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "perspX:%g ", SkFractToFloat(perspX));
+ SkScalar perspY = matrix->getPerspY();
+ if (perspY != defaultMatrix.getPerspY())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "perspY:%g ", SkFractToFloat(perspY));
+ SkDebugf("%s\n", pBuffer);
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkFlatPaint* SkFlatPaint::Flatten(SkChunkAlloc* heap, const SkPaint& paint,
+ int index, SkRefCntRecorder* rec,
+ SkRefCntRecorder* faceRecorder) {
+ SkFlattenableWriteBuffer buffer(2*sizeof(SkPaint));
+ buffer.setRefCntRecorder(rec);
+ buffer.setTypefaceRecorder(faceRecorder);
+
+ paint.flatten(buffer);
+ uint32_t size = buffer.size();
+ SkFlatPaint* result = (SkFlatPaint*) INHERITED::Alloc(heap, size, index);
+ buffer.flatten(&result->fPaintData);
+ return result;
+}
+
+void SkFlatPaint::Read(const void* storage, SkPaint* paint,
+ SkRefCntPlayback* rcp, SkTypefacePlayback* facePlayback) {
+ SkFlattenableReadBuffer buffer(storage);
+ if (rcp) {
+ rcp->setupBuffer(buffer);
+ }
+ if (facePlayback) {
+ facePlayback->setupBuffer(buffer);
+ }
+ paint->unflatten(buffer);
+}
+
+#ifdef SK_DEBUG_DUMP
+void SkFlatPaint::dump() const {
+ SkPaint defaultPaint;
+ SkFlattenableReadBuffer buffer(fPaintData);
+ SkTypeface* typeface = (SkTypeface*) buffer.readPtr();
+ char pBuffer[DUMP_BUFFER_SIZE];
+ char* bufferPtr = pBuffer;
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "paint: ");
+ if (typeface != defaultPaint.getTypeface())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "typeface:%p ", typeface);
+ SkScalar textSize = buffer.readScalar();
+ if (textSize != defaultPaint.getTextSize())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "textSize:%g ", SkScalarToFloat(textSize));
+ SkScalar textScaleX = buffer.readScalar();
+ if (textScaleX != defaultPaint.getTextScaleX())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "textScaleX:%g ", SkScalarToFloat(textScaleX));
+ SkScalar textSkewX = buffer.readScalar();
+ if (textSkewX != defaultPaint.getTextSkewX())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "textSkewX:%g ", SkScalarToFloat(textSkewX));
+ const SkPathEffect* pathEffect = (const SkPathEffect*) buffer.readFlattenable();
+ if (pathEffect != defaultPaint.getPathEffect())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "pathEffect:%p ", pathEffect);
+ SkDELETE(pathEffect);
+ const SkShader* shader = (const SkShader*) buffer.readFlattenable();
+ if (shader != defaultPaint.getShader())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "shader:%p ", shader);
+ SkDELETE(shader);
+ const SkXfermode* xfermode = (const SkXfermode*) buffer.readFlattenable();
+ if (xfermode != defaultPaint.getXfermode())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "xfermode:%p ", xfermode);
+ SkDELETE(xfermode);
+ const SkMaskFilter* maskFilter = (const SkMaskFilter*) buffer.readFlattenable();
+ if (maskFilter != defaultPaint.getMaskFilter())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "maskFilter:%p ", maskFilter);
+ SkDELETE(maskFilter);
+ const SkColorFilter* colorFilter = (const SkColorFilter*) buffer.readFlattenable();
+ if (colorFilter != defaultPaint.getColorFilter())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "colorFilter:%p ", colorFilter);
+ SkDELETE(colorFilter);
+ const SkRasterizer* rasterizer = (const SkRasterizer*) buffer.readFlattenable();
+ if (rasterizer != defaultPaint.getRasterizer())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "rasterizer:%p ", rasterizer);
+ SkDELETE(rasterizer);
+ const SkDrawLooper* drawLooper = (const SkDrawLooper*) buffer.readFlattenable();
+ if (drawLooper != defaultPaint.getLooper())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "drawLooper:%p ", drawLooper);
+ SkDELETE(drawLooper);
+ unsigned color = buffer.readU32();
+ if (color != defaultPaint.getColor())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "color:0x%x ", color);
+ SkScalar strokeWidth = buffer.readScalar();
+ if (strokeWidth != defaultPaint.getStrokeWidth())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "strokeWidth:%g ", SkScalarToFloat(strokeWidth));
+ SkScalar strokeMiter = buffer.readScalar();
+ if (strokeMiter != defaultPaint.getStrokeMiter())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "strokeMiter:%g ", SkScalarToFloat(strokeMiter));
+ unsigned flags = buffer.readU16();
+ if (flags != defaultPaint.getFlags())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "flags:0x%x ", flags);
+ int align = buffer.readU8();
+ if (align != defaultPaint.getTextAlign())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "align:0x%x ", align);
+ int strokeCap = buffer.readU8();
+ if (strokeCap != defaultPaint.getStrokeCap())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "strokeCap:0x%x ", strokeCap);
+ int strokeJoin = buffer.readU8();
+ if (strokeJoin != defaultPaint.getStrokeJoin())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "align:0x%x ", strokeJoin);
+ int style = buffer.readU8();
+ if (style != defaultPaint.getStyle())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "style:0x%x ", style);
+ int textEncoding = buffer.readU8();
+ if (textEncoding != defaultPaint.getTextEncoding())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "textEncoding:0x%x ", textEncoding);
+ SkDebugf("%s\n", pBuffer);
+}
+#endif
+
+SkFlatRegion* SkFlatRegion::Flatten(SkChunkAlloc* heap, const SkRegion& region, int index) {
+ uint32_t size = region.flatten(NULL);
+ SkFlatRegion* result = (SkFlatRegion*) INHERITED::Alloc(heap, size, index);
+ region.flatten(&result->fRegionData);
+ return result;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkRefCntPlayback::SkRefCntPlayback() : fCount(0), fArray(NULL) {}
+
+SkRefCntPlayback::~SkRefCntPlayback() {
+ this->reset(NULL);
+}
+
+void SkRefCntPlayback::reset(const SkRefCntRecorder* rec) {
+ for (int i = 0; i < fCount; i++) {
+ SkASSERT(fArray[i]);
+ fArray[i]->unref();
+ }
+ SkDELETE_ARRAY(fArray);
+
+ if (rec) {
+ fCount = rec->count();
+ fArray = SkNEW_ARRAY(SkRefCnt*, fCount);
+ rec->get(fArray);
+ for (int i = 0; i < fCount; i++) {
+ fArray[i]->ref();
+ }
+ } else {
+ fCount = 0;
+ fArray = NULL;
+ }
+}
+
+void SkRefCntPlayback::setCount(int count) {
+ this->reset(NULL);
+
+ fCount = count;
+ fArray = SkNEW_ARRAY(SkRefCnt*, count);
+ bzero(fArray, count * sizeof(SkRefCnt*));
+}
+
+SkRefCnt* SkRefCntPlayback::set(int index, SkRefCnt* obj) {
+ SkASSERT((unsigned)index < (unsigned)fCount);
+ SkRefCnt_SafeAssign(fArray[index], obj);
+ return obj;
+}
+
diff --git a/src/core/SkPictureFlat.h b/src/core/SkPictureFlat.h
new file mode 100644
index 0000000000..9e7fc5e4ff
--- /dev/null
+++ b/src/core/SkPictureFlat.h
@@ -0,0 +1,206 @@
+#ifndef SkPictureFlat_DEFINED
+#define SkPictureFlat_DEFINED
+
+#include "SkChunkAlloc.h"
+#include "SkBitmap.h"
+#include "SkPicture.h"
+#include "SkMatrix.h"
+#include "SkPaint.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+
+enum DrawType {
+ UNUSED,
+ CLIP_PATH,
+ CLIP_REGION,
+ CLIP_RECT,
+ CONCAT,
+ DRAW_BITMAP,
+ DRAW_BITMAP_MATRIX,
+ DRAW_BITMAP_RECT,
+ DRAW_PAINT,
+ DRAW_PATH,
+ DRAW_PICTURE,
+ DRAW_POINTS,
+ DRAW_POS_TEXT,
+ DRAW_POS_TEXT_H,
+ DRAW_POS_TEXT_H_TOP_BOTTOM, // fast variant of DRAW_POS_TEXT_H
+ DRAW_RECT,
+ DRAW_SPRITE,
+ DRAW_TEXT,
+ DRAW_TEXT_ON_PATH,
+ DRAW_TEXT_TOP_BOTTOM, // fast variant of DRAW_TEXT
+ DRAW_VERTICES,
+ RESTORE,
+ ROTATE,
+ SAVE,
+ SAVE_LAYER,
+ SCALE,
+ SKEW,
+ TRANSLATE
+};
+
+enum DrawVertexFlags {
+ DRAW_VERTICES_HAS_TEXS = 0x01,
+ DRAW_VERTICES_HAS_COLORS = 0x02,
+ DRAW_VERTICES_HAS_INDICES = 0x04
+};
+
+class SkRefCntPlayback {
+public:
+ SkRefCntPlayback();
+ ~SkRefCntPlayback();
+
+ int count() const { return fCount; }
+
+ void reset(const SkRefCntRecorder*);
+
+ void setCount(int count);
+ SkRefCnt* set(int index, SkRefCnt*);
+
+ virtual void setupBuffer(SkFlattenableReadBuffer& buffer) const {
+ buffer.setRefCntArray(fArray, fCount);
+ }
+
+protected:
+ int fCount;
+ SkRefCnt** fArray;
+};
+
+class SkTypefacePlayback : public SkRefCntPlayback {
+public:
+ virtual void setupBuffer(SkFlattenableReadBuffer& buffer) const {
+ buffer.setTypefaceArray((SkTypeface**)fArray, fCount);
+ }
+};
+
+class SkFactoryPlayback {
+public:
+ SkFactoryPlayback(int count) : fCount(count) {
+ fArray = SkNEW_ARRAY(SkFlattenable::Factory, count);
+ }
+
+ ~SkFactoryPlayback() {
+ SkDELETE_ARRAY(fArray);
+ }
+
+ SkFlattenable::Factory* base() const { return fArray; }
+
+ void setupBuffer(SkFlattenableReadBuffer& buffer) const {
+ buffer.setFactoryPlayback(fArray, fCount);
+ }
+
+private:
+ int fCount;
+ SkFlattenable::Factory* fArray;
+};
+
+class SkFlatData {
+public:
+ static int Compare(const SkFlatData* a, const SkFlatData* b) {
+ return memcmp(&a->fAllocSize, &b->fAllocSize, a->fAllocSize);
+ }
+
+ int index() const { return fIndex; }
+
+#ifdef SK_DEBUG_SIZE
+ size_t size() const { return sizeof(fIndex) + fAllocSize; }
+#endif
+
+protected:
+ static SkFlatData* Alloc(SkChunkAlloc* heap, int32_t size, int index);
+
+ int fIndex;
+ int32_t fAllocSize;
+};
+
+class SkFlatBitmap : public SkFlatData {
+public:
+ static SkFlatBitmap* Flatten(SkChunkAlloc*, const SkBitmap&, int index,
+ SkRefCntRecorder*);
+
+ void unflatten(SkBitmap* bitmap, SkRefCntPlayback* rcp) const {
+ SkFlattenableReadBuffer buffer(fBitmapData);
+ if (rcp) {
+ rcp->setupBuffer(buffer);
+ }
+ bitmap->unflatten(buffer);
+ }
+
+#ifdef SK_DEBUG_VALIDATE
+ void validate() const {
+ // to be written
+ }
+#endif
+
+private:
+ char fBitmapData[1];
+ typedef SkFlatData INHERITED;
+};
+
+class SkFlatMatrix : public SkFlatData {
+public:
+ static SkFlatMatrix* Flatten(SkChunkAlloc* heap, const SkMatrix& matrix, int index);
+
+ void unflatten(SkMatrix* result) const {
+ memcpy(result, fMatrixData, sizeof(SkMatrix));
+ }
+
+#ifdef SK_DEBUG_DUMP
+ void dump() const;
+#endif
+
+#ifdef SK_DEBUG_VALIDATE
+ void validate() const {
+ // to be written
+ }
+#endif
+
+private:
+ char fMatrixData[1];
+ typedef SkFlatData INHERITED;
+};
+
+class SkFlatPaint : public SkFlatData {
+public:
+ static SkFlatPaint* Flatten(SkChunkAlloc* heap, const SkPaint& paint,
+ int index, SkRefCntRecorder*,
+ SkRefCntRecorder* faceRecorder);
+
+ void unflatten(SkPaint* result, SkRefCntPlayback* rcp,
+ SkTypefacePlayback* facePlayback) const {
+ Read(fPaintData, result, rcp, facePlayback);
+ }
+
+ static void Read(const void* storage, SkPaint* paint, SkRefCntPlayback*,
+ SkTypefacePlayback* facePlayback);
+
+#ifdef SK_DEBUG_DUMP
+ void dump() const;
+#endif
+
+private:
+ char fPaintData[1];
+ typedef SkFlatData INHERITED;
+};
+
+class SkFlatRegion : public SkFlatData {
+public:
+ static SkFlatRegion* Flatten(SkChunkAlloc* heap, const SkRegion& region, int index);
+
+ void unflatten(SkRegion* result) const {
+ result->unflatten(fRegionData);
+ }
+
+#ifdef SK_DEBUG_VALIDATE
+ void validate() const {
+ // to be written
+ }
+#endif
+
+private:
+ char fRegionData[1];
+ typedef SkFlatData INHERITED;
+};
+
+#endif
diff --git a/src/core/SkPicturePlayback.cpp b/src/core/SkPicturePlayback.cpp
new file mode 100644
index 0000000000..64f8f1c618
--- /dev/null
+++ b/src/core/SkPicturePlayback.cpp
@@ -0,0 +1,1338 @@
+#include "SkPicturePlayback.h"
+#include "SkPictureRecord.h"
+#include "SkTypeface.h"
+#include <new>
+
+SkPicturePlayback::SkPicturePlayback() {
+ this->init();
+}
+
+SkPicturePlayback::SkPicturePlayback(const SkPictureRecord& record) {
+#ifdef SK_DEBUG_SIZE
+ size_t overallBytes, bitmapBytes, matricesBytes,
+ paintBytes, pathBytes, pictureBytes, regionBytes;
+ int bitmaps = record.bitmaps(&bitmapBytes);
+ int matrices = record.matrices(&matricesBytes);
+ int paints = record.paints(&paintBytes);
+ int paths = record.paths(&pathBytes);
+ int pictures = record.pictures(&pictureBytes);
+ int regions = record.regions(&regionBytes);
+ SkDebugf("picture record mem used %zd (stream %zd) ", record.size(),
+ record.streamlen());
+ if (bitmaps != 0)
+ SkDebugf("bitmaps size %zd (bitmaps:%d) ", bitmapBytes, bitmaps);
+ if (matrices != 0)
+ SkDebugf("matrices size %zd (matrices:%d) ", matricesBytes, matrices);
+ if (paints != 0)
+ SkDebugf("paints size %zd (paints:%d) ", paintBytes, paints);
+ if (paths != 0)
+ SkDebugf("paths size %zd (paths:%d) ", pathBytes, paths);
+ if (pictures != 0)
+ SkDebugf("pictures size %zd (pictures:%d) ", pictureBytes, pictures);
+ if (regions != 0)
+ SkDebugf("regions size %zd (regions:%d) ", regionBytes, regions);
+ if (record.fPointWrites != 0)
+ SkDebugf("points size %zd (points:%d) ", record.fPointBytes, record.fPointWrites);
+ if (record.fRectWrites != 0)
+ SkDebugf("rects size %zd (rects:%d) ", record.fRectBytes, record.fRectWrites);
+ if (record.fTextWrites != 0)
+ SkDebugf("text size %zd (text strings:%d) ", record.fTextBytes, record.fTextWrites);
+
+ SkDebugf("\n");
+#endif
+#ifdef SK_DEBUG_DUMP
+ record.dumpMatrices();
+ record.dumpPaints();
+#endif
+
+ record.validate();
+ const SkWriter32& writer = record.writeStream();
+ init();
+ if (writer.size() == 0)
+ return;
+
+ {
+ size_t size = writer.size();
+ void* buffer = sk_malloc_throw(size);
+ writer.flatten(buffer);
+ fReader.setMemory(buffer, size); // fReader owns buffer now
+ }
+
+ // copy over the refcnt dictionary to our reader
+ //
+ fRCPlayback.reset(&record.fRCRecorder);
+ fRCPlayback.setupBuffer(fReader);
+
+ fTFPlayback.reset(&record.fTFRecorder);
+ fTFPlayback.setupBuffer(fReader);
+
+ const SkTDArray<const SkFlatBitmap* >& bitmaps = record.getBitmaps();
+ fBitmapCount = bitmaps.count();
+ if (fBitmapCount > 0) {
+ fBitmaps = SkNEW_ARRAY(SkBitmap, fBitmapCount);
+ for (const SkFlatBitmap** flatBitmapPtr = bitmaps.begin();
+ flatBitmapPtr != bitmaps.end(); flatBitmapPtr++) {
+ const SkFlatBitmap* flatBitmap = *flatBitmapPtr;
+ int index = flatBitmap->index() - 1;
+ flatBitmap->unflatten(&fBitmaps[index], &fRCPlayback);
+ }
+ }
+
+ const SkTDArray<const SkFlatMatrix* >& matrices = record.getMatrices();
+ fMatrixCount = matrices.count();
+ if (fMatrixCount > 0) {
+ fMatrices = SkNEW_ARRAY(SkMatrix, fMatrixCount);
+ for (const SkFlatMatrix** matrixPtr = matrices.begin();
+ matrixPtr != matrices.end(); matrixPtr++) {
+ const SkFlatMatrix* flatMatrix = *matrixPtr;
+ flatMatrix->unflatten(&fMatrices[flatMatrix->index() - 1]);
+ }
+ }
+
+ const SkTDArray<const SkFlatPaint* >& paints = record.getPaints();
+ fPaintCount = paints.count();
+ if (fPaintCount > 0) {
+ fPaints = SkNEW_ARRAY(SkPaint, fPaintCount);
+ for (const SkFlatPaint** flatPaintPtr = paints.begin();
+ flatPaintPtr != paints.end(); flatPaintPtr++) {
+ const SkFlatPaint* flatPaint = *flatPaintPtr;
+ int index = flatPaint->index() - 1;
+ SkASSERT((unsigned)index < (unsigned)fPaintCount);
+ flatPaint->unflatten(&fPaints[index], &fRCPlayback, &fTFPlayback);
+ }
+ }
+
+ fPathHeap = record.fPathHeap;
+ fPathHeap->safeRef();
+
+ const SkTDArray<SkPicture* >& pictures = record.getPictureRefs();
+ fPictureCount = pictures.count();
+ if (fPictureCount > 0) {
+ fPictureRefs = SkNEW_ARRAY(SkPicture*, fPictureCount);
+ for (int i = 0; i < fPictureCount; i++) {
+ fPictureRefs[i] = pictures[i];
+ fPictureRefs[i]->ref();
+ }
+ }
+
+ const SkTDArray<const SkFlatRegion* >& regions = record.getRegions();
+ fRegionCount = regions.count();
+ if (fRegionCount > 0) {
+ fRegions = SkNEW_ARRAY(SkRegion, fRegionCount);
+ for (const SkFlatRegion** flatRegionPtr = regions.begin();
+ flatRegionPtr != regions.end(); flatRegionPtr++) {
+ const SkFlatRegion* flatRegion = *flatRegionPtr;
+ flatRegion->unflatten(&fRegions[flatRegion->index() - 1]);
+ }
+ }
+
+#ifdef SK_DEBUG_SIZE
+ int overall = fPlayback->size(&overallBytes);
+ bitmaps = fPlayback->bitmaps(&bitmapBytes);
+ paints = fPlayback->paints(&paintBytes);
+ paths = fPlayback->paths(&pathBytes);
+ pictures = fPlayback->pictures(&pictureBytes);
+ regions = fPlayback->regions(&regionBytes);
+ SkDebugf("playback size %zd (objects:%d) ", overallBytes, overall);
+ if (bitmaps != 0)
+ SkDebugf("bitmaps size %zd (bitmaps:%d) ", bitmapBytes, bitmaps);
+ if (paints != 0)
+ SkDebugf("paints size %zd (paints:%d) ", paintBytes, paints);
+ if (paths != 0)
+ SkDebugf("paths size %zd (paths:%d) ", pathBytes, paths);
+ if (pictures != 0)
+ SkDebugf("pictures size %zd (pictures:%d) ", pictureBytes, pictures);
+ if (regions != 0)
+ SkDebugf("regions size %zd (regions:%d) ", regionBytes, regions);
+ SkDebugf("\n");
+#endif
+}
+
+SkPicturePlayback::SkPicturePlayback(const SkPicturePlayback& src) {
+ this->init();
+
+ // copy the data from fReader
+ {
+ size_t size = src.fReader.size();
+ void* buffer = sk_malloc_throw(size);
+ memcpy(buffer, src.fReader.base(), size);
+ fReader.setMemory(buffer, size);
+ }
+
+ int i;
+
+ fBitmapCount = src.fBitmapCount;
+ fBitmaps = SkNEW_ARRAY(SkBitmap, fBitmapCount);
+ for (i = 0; i < fBitmapCount; i++) {
+ fBitmaps[i] = src.fBitmaps[i];
+ }
+
+ fMatrixCount = src.fMatrixCount;
+ fMatrices = SkNEW_ARRAY(SkMatrix, fMatrixCount);
+ memcpy(fMatrices, src.fMatrices, fMatrixCount * sizeof(SkMatrix));
+
+ fPaintCount = src.fPaintCount;
+ fPaints = SkNEW_ARRAY(SkPaint, fPaintCount);
+ for (i = 0; i < fPaintCount; i++) {
+ fPaints[i] = src.fPaints[i];
+ }
+
+ fPathHeap = src.fPathHeap;
+ fPathHeap->safeRef();
+
+ fPictureCount = src.fPictureCount;
+ fPictureRefs = SkNEW_ARRAY(SkPicture*, fPictureCount);
+ for (int i = 0; i < fPictureCount; i++) {
+ fPictureRefs[i] = src.fPictureRefs[i];
+ fPictureRefs[i]->ref();
+ }
+
+ fRegionCount = src.fRegionCount;
+ fRegions = SkNEW_ARRAY(SkRegion, fRegionCount);
+ for (i = 0; i < fRegionCount; i++) {
+ fRegions[i] = src.fRegions[i];
+ }
+}
+
+void SkPicturePlayback::init() {
+ fBitmaps = NULL;
+ fMatrices = NULL;
+ fPaints = NULL;
+ fPathHeap = NULL;
+ fPictureRefs = NULL;
+ fRegions = NULL;
+ fBitmapCount = fMatrixCount = fPaintCount = fPictureCount =
+ fRegionCount = 0;
+
+ fFactoryPlayback = NULL;
+}
+
+SkPicturePlayback::~SkPicturePlayback() {
+ sk_free((void*) fReader.base());
+
+ SkDELETE_ARRAY(fBitmaps);
+ SkDELETE_ARRAY(fMatrices);
+ SkDELETE_ARRAY(fPaints);
+ SkDELETE_ARRAY(fRegions);
+
+ fPathHeap->safeUnref();
+
+ for (int i = 0; i < fPictureCount; i++) {
+ fPictureRefs[i]->unref();
+ }
+ SkDELETE_ARRAY(fPictureRefs);
+
+ SkDELETE(fFactoryPlayback);
+}
+
+void SkPicturePlayback::dumpSize() const {
+ SkDebugf("--- picture size: ops=%d bitmaps=%d [%d] matrices=%d [%d] paints=%d [%d] paths=%d regions=%d\n",
+ fReader.size(),
+ fBitmapCount, fBitmapCount * sizeof(SkBitmap),
+ fMatrixCount, fMatrixCount * sizeof(SkMatrix),
+ fPaintCount, fPaintCount * sizeof(SkPaint),
+ fPathHeap ? fPathHeap->count() : 0,
+ fRegionCount);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+// The chunks are writte/read in this order...
+
+#define PICT_READER_TAG SkSetFourByteTag('r', 'e', 'a', 'd')
+#define PICT_FACTORY_TAG SkSetFourByteTag('f', 'a', 'c', 't')
+#define PICT_TYPEFACE_TAG SkSetFourByteTag('t', 'p', 'f', 'c')
+#define PICT_PICTURE_TAG SkSetFourByteTag('p', 'c', 't', 'r')
+#define PICT_ARRAYS_TAG SkSetFourByteTag('a', 'r', 'a', 'y')
+// these are all inside the ARRAYS tag
+#define PICT_BITMAP_TAG SkSetFourByteTag('b', 't', 'm', 'p')
+#define PICT_MATRIX_TAG SkSetFourByteTag('m', 't', 'r', 'x')
+#define PICT_PAINT_TAG SkSetFourByteTag('p', 'n', 't', ' ')
+#define PICT_PATH_TAG SkSetFourByteTag('p', 't', 'h', ' ')
+#define PICT_REGION_TAG SkSetFourByteTag('r', 'g', 'n', ' ')
+
+
+#include "SkStream.h"
+
+static void writeTagSize(SkFlattenableWriteBuffer& buffer, uint32_t tag,
+ uint32_t size) {
+ buffer.write32(tag);
+ buffer.write32(size);
+}
+
+static void writeTagSize(SkWStream* stream, uint32_t tag,
+ uint32_t size) {
+ stream->write32(tag);
+ stream->write32(size);
+}
+
+static void writeFactories(SkWStream* stream, const SkFactoryRecorder& rec) {
+ int count = rec.count();
+
+ writeTagSize(stream, PICT_FACTORY_TAG, count);
+
+ SkAutoSTMalloc<16, SkFlattenable::Factory> storage(count);
+ SkFlattenable::Factory* array = (SkFlattenable::Factory*)storage.get();
+ rec.get(array);
+
+ for (int i = 0; i < count; i++) {
+ const char* name = SkFlattenable::FactoryToName(array[i]);
+// SkDebugf("---- write factories [%d] %p <%s>\n", i, array[i], name);
+ if (NULL == name || 0 == *name) {
+ stream->writePackedUInt(0);
+ } else {
+ uint32_t len = strlen(name);
+ stream->writePackedUInt(len);
+ stream->write(name, len);
+ }
+ }
+}
+
+static void writeTypefaces(SkWStream* stream, const SkRefCntRecorder& rec) {
+ int count = rec.count();
+
+ writeTagSize(stream, PICT_TYPEFACE_TAG, count);
+
+ SkAutoSTMalloc<16, SkTypeface*> storage(count);
+ SkTypeface** array = (SkTypeface**)storage.get();
+ rec.get((SkRefCnt**)array);
+
+ for (int i = 0; i < count; i++) {
+ array[i]->serialize(stream);
+ }
+}
+
+void SkPicturePlayback::serialize(SkWStream* stream) const {
+ writeTagSize(stream, PICT_READER_TAG, fReader.size());
+ stream->write(fReader.base(), fReader.size());
+
+ SkRefCntRecorder typefaceRecorder;
+ SkFactoryRecorder factRecorder;
+
+ SkFlattenableWriteBuffer buffer(1024);
+
+ buffer.setFlags(SkFlattenableWriteBuffer::kCrossProcess_Flag);
+ buffer.setTypefaceRecorder(&typefaceRecorder);
+ buffer.setFactoryRecorder(&factRecorder);
+
+ int i;
+
+ writeTagSize(buffer, PICT_BITMAP_TAG, fBitmapCount);
+ for (i = 0; i < fBitmapCount; i++) {
+ fBitmaps[i].flatten(buffer);
+ }
+
+ writeTagSize(buffer, PICT_MATRIX_TAG, fMatrixCount);
+ buffer.writeMul4(fMatrices, fMatrixCount * sizeof(SkMatrix));
+
+ writeTagSize(buffer, PICT_PAINT_TAG, fPaintCount);
+ for (i = 0; i < fPaintCount; i++) {
+ fPaints[i].flatten(buffer);
+ }
+
+ {
+ int count = fPathHeap ? fPathHeap->count() : 0;
+ writeTagSize(buffer, PICT_PATH_TAG, count);
+ if (count > 0) {
+ fPathHeap->flatten(buffer);
+ }
+ }
+
+ writeTagSize(buffer, PICT_REGION_TAG, fRegionCount);
+ for (i = 0; i < fRegionCount; i++) {
+ uint32_t size = fRegions[i].flatten(NULL);
+ buffer.write32(size);
+ SkAutoSMalloc<512> storage(size);
+ fRegions[i].flatten(storage.get());
+ buffer.writePad(storage.get(), size);
+ }
+
+ // now we can write to the stream again
+
+ writeFactories(stream, factRecorder);
+ writeTypefaces(stream, typefaceRecorder);
+
+ writeTagSize(stream, PICT_PICTURE_TAG, fPictureCount);
+ for (i = 0; i < fPictureCount; i++) {
+ fPictureRefs[i]->serialize(stream);
+ }
+
+ writeTagSize(stream, PICT_ARRAYS_TAG, buffer.size());
+ buffer.writeToStream(stream);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static int readTagSize(SkFlattenableReadBuffer& buffer, uint32_t expectedTag) {
+ uint32_t tag = buffer.readU32();
+ if (tag != expectedTag) {
+ sk_throw();
+ }
+ return buffer.readU32();
+}
+
+static int readTagSize(SkStream* stream, uint32_t expectedTag) {
+ uint32_t tag = stream->readU32();
+ if (tag != expectedTag) {
+ sk_throw();
+ }
+ return stream->readU32();
+}
+
+SkPicturePlayback::SkPicturePlayback(SkStream* stream) {
+ this->init();
+
+ int i;
+
+ {
+ size_t size = readTagSize(stream, PICT_READER_TAG);
+ void* storage = sk_malloc_throw(size);
+ stream->read(storage, size);
+ fReader.setMemory(storage, size);
+ }
+
+ int factoryCount = readTagSize(stream, PICT_FACTORY_TAG);
+ fFactoryPlayback = SkNEW_ARGS(SkFactoryPlayback, (factoryCount));
+ for (i = 0; i < factoryCount; i++) {
+ SkString str;
+ int len = stream->readPackedUInt();
+ str.resize(len);
+ stream->read(str.writable_str(), len);
+// SkDebugf("--- factory playback [%d] <%s>\n", i, str.c_str());
+ fFactoryPlayback->base()[i] = SkFlattenable::NameToFactory(str.c_str());
+ }
+
+ int typefaceCount = readTagSize(stream, PICT_TYPEFACE_TAG);
+ fTFPlayback.setCount(typefaceCount);
+ for (i = 0; i < typefaceCount; i++) {
+ fTFPlayback.set(i, SkTypeface::Deserialize(stream))->unref();
+ }
+
+ fPictureCount = readTagSize(stream, PICT_PICTURE_TAG);
+ fPictureRefs = SkNEW_ARRAY(SkPicture*, fPictureCount);
+ for (i = 0; i < fPictureCount; i++) {
+ fPictureRefs[i] = SkNEW_ARGS(SkPicture, (stream));
+ }
+
+ /*
+ Now read the arrays chunk, and parse using a read buffer
+ */
+ uint32_t size = readTagSize(stream, PICT_ARRAYS_TAG);
+ SkAutoMalloc storage(size);
+ stream->read(storage.get(), size);
+
+ SkFlattenableReadBuffer buffer(storage.get(), size);
+ fFactoryPlayback->setupBuffer(buffer);
+ fTFPlayback.setupBuffer(buffer);
+
+ fBitmapCount = readTagSize(buffer, PICT_BITMAP_TAG);
+ fBitmaps = SkNEW_ARRAY(SkBitmap, fBitmapCount);
+ for (i = 0; i < fBitmapCount; i++) {
+ fBitmaps[i].unflatten(buffer);
+ }
+
+ fMatrixCount = readTagSize(buffer, PICT_MATRIX_TAG);
+ fMatrices = SkNEW_ARRAY(SkMatrix, fMatrixCount);
+ buffer.read(fMatrices, fMatrixCount * sizeof(SkMatrix));
+
+ fPaintCount = readTagSize(buffer, PICT_PAINT_TAG);
+ fPaints = SkNEW_ARRAY(SkPaint, fPaintCount);
+ for (i = 0; i < fPaintCount; i++) {
+ fPaints[i].unflatten(buffer);
+ }
+
+ {
+ int count = readTagSize(buffer, PICT_PATH_TAG);
+ if (count > 0) {
+ fPathHeap = SkNEW_ARGS(SkPathHeap, (buffer));
+ }
+ }
+
+ fRegionCount = readTagSize(buffer, PICT_REGION_TAG);
+ fRegions = SkNEW_ARRAY(SkRegion, fRegionCount);
+ for (i = 0; i < fRegionCount; i++) {
+ uint32_t size = buffer.readU32();
+ uint32_t bytes = fRegions[i].unflatten(buffer.skip(size));
+ SkASSERT(size == bytes);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+void SkPicturePlayback::draw(SkCanvas& canvas) {
+#ifdef ENABLE_TIME_DRAW
+ SkAutoTime at("SkPicture::draw", 50);
+#endif
+
+ TextContainer text;
+ fReader.rewind();
+
+ while (!fReader.eof()) {
+ switch (fReader.readInt()) {
+ case CLIP_PATH: {
+ const SkPath& path = getPath();
+ SkRegion::Op op = (SkRegion::Op) getInt();
+ size_t offsetToRestore = getInt();
+ // HACK (false) until I can handle op==kReplace
+ if (!canvas.clipPath(path, op) && false) {
+ //SkDebugf("---- skip clipPath for %d bytes\n", offsetToRestore - fReader.offset());
+ fReader.setOffset(offsetToRestore);
+ }
+ } break;
+ case CLIP_REGION: {
+ const SkRegion& region = getRegion();
+ SkRegion::Op op = (SkRegion::Op) getInt();
+ size_t offsetToRestore = getInt();
+ if (!canvas.clipRegion(region, op)) {
+ //SkDebugf("---- skip clipDeviceRgn for %d bytes\n", offsetToRestore - fReader.offset());
+ fReader.setOffset(offsetToRestore);
+ }
+ } break;
+ case CLIP_RECT: {
+ const SkRect* rect = fReader.skipRect();
+ SkRegion::Op op = (SkRegion::Op) getInt();
+ size_t offsetToRestore = getInt();
+ if (!canvas.clipRect(*rect, op)) {
+ //SkDebugf("---- skip clipRect for %d bytes\n", offsetToRestore - fReader.offset());
+ fReader.setOffset(offsetToRestore);
+ }
+ } break;
+ case CONCAT:
+ canvas.concat(*getMatrix());
+ break;
+ case DRAW_BITMAP: {
+ const SkPaint* paint = getPaint();
+ const SkBitmap& bitmap = getBitmap();
+ const SkPoint* loc = fReader.skipPoint();
+ canvas.drawBitmap(bitmap, loc->fX, loc->fY, paint);
+ } break;
+ case DRAW_BITMAP_RECT: {
+ const SkPaint* paint = getPaint();
+ const SkBitmap& bitmap = getBitmap();
+ const SkIRect* src = this->getIRectPtr(); // may be null
+ const SkRect* dst = fReader.skipRect(); // required
+ canvas.drawBitmapRect(bitmap, src, *dst, paint);
+ } break;
+ case DRAW_BITMAP_MATRIX: {
+ const SkPaint* paint = getPaint();
+ const SkBitmap& bitmap = getBitmap();
+ const SkMatrix* matrix = getMatrix();
+ canvas.drawBitmapMatrix(bitmap, *matrix, paint);
+ } break;
+ case DRAW_PAINT:
+ canvas.drawPaint(*getPaint());
+ break;
+ case DRAW_PATH: {
+ const SkPaint& paint = *getPaint();
+ canvas.drawPath(getPath(), paint);
+ } break;
+ case DRAW_PICTURE:
+ canvas.drawPicture(getPicture());
+ break;
+ case DRAW_POINTS: {
+ const SkPaint& paint = *getPaint();
+ SkCanvas::PointMode mode = (SkCanvas::PointMode)getInt();
+ size_t count = getInt();
+ const SkPoint* pts = (const SkPoint*)fReader.skip(sizeof(SkPoint) * count);
+ canvas.drawPoints(mode, count, pts, paint);
+ } break;
+ case DRAW_POS_TEXT: {
+ const SkPaint& paint = *getPaint();
+ getText(&text);
+ size_t points = getInt();
+ const SkPoint* pos = (const SkPoint*)fReader.skip(points * sizeof(SkPoint));
+ canvas.drawPosText(text.text(), text.length(), pos, paint);
+ } break;
+ case DRAW_POS_TEXT_H: {
+ const SkPaint& paint = *getPaint();
+ getText(&text);
+ size_t xCount = getInt();
+ const SkScalar constY = getScalar();
+ const SkScalar* xpos = (const SkScalar*)fReader.skip(xCount * sizeof(SkScalar));
+ canvas.drawPosTextH(text.text(), text.length(), xpos, constY,
+ paint);
+ } break;
+ case DRAW_POS_TEXT_H_TOP_BOTTOM: {
+ const SkPaint& paint = *getPaint();
+ getText(&text);
+ size_t xCount = getInt();
+ const SkScalar* xpos = (const SkScalar*)fReader.skip((3 + xCount) * sizeof(SkScalar));
+ const SkScalar top = *xpos++;
+ const SkScalar bottom = *xpos++;
+ const SkScalar constY = *xpos++;
+ if (!canvas.quickRejectY(top, bottom, SkCanvas::kAA_EdgeType)) {
+ canvas.drawPosTextH(text.text(), text.length(), xpos,
+ constY, paint);
+ }
+ } break;
+ case DRAW_RECT: {
+ const SkPaint& paint = *getPaint();
+ canvas.drawRect(*fReader.skipRect(), paint);
+ } break;
+ case DRAW_SPRITE: {
+ const SkPaint* paint = getPaint();
+ const SkBitmap& bitmap = getBitmap();
+ int left = getInt();
+ int top = getInt();
+ canvas.drawSprite(bitmap, left, top, paint);
+ } break;
+ case DRAW_TEXT: {
+ const SkPaint& paint = *getPaint();
+ getText(&text);
+ SkScalar x = getScalar();
+ SkScalar y = getScalar();
+ canvas.drawText(text.text(), text.length(), x, y, paint);
+ } break;
+ case DRAW_TEXT_TOP_BOTTOM: {
+ const SkPaint& paint = *getPaint();
+ getText(&text);
+ const SkScalar* ptr = (const SkScalar*)fReader.skip(4 * sizeof(SkScalar));
+ // ptr[0] == x
+ // ptr[1] == y
+ // ptr[2] == top
+ // ptr[3] == bottom
+ if (!canvas.quickRejectY(ptr[2], ptr[3],
+ SkCanvas::kAA_EdgeType)) {
+ canvas.drawText(text.text(), text.length(), ptr[0], ptr[1],
+ paint);
+ }
+ } break;
+ case DRAW_TEXT_ON_PATH: {
+ const SkPaint& paint = *getPaint();
+ getText(&text);
+ const SkPath& path = getPath();
+ const SkMatrix* matrix = getMatrix();
+ canvas.drawTextOnPath(text.text(), text.length(), path,
+ matrix, paint);
+ } break;
+ case DRAW_VERTICES: {
+ const SkPaint& paint = *getPaint();
+ DrawVertexFlags flags = (DrawVertexFlags)getInt();
+ SkCanvas::VertexMode vmode = (SkCanvas::VertexMode)getInt();
+ int vCount = getInt();
+ const SkPoint* verts = (const SkPoint*)fReader.skip(
+ vCount * sizeof(SkPoint));
+ const SkPoint* texs = NULL;
+ const SkColor* colors = NULL;
+ const uint16_t* indices = NULL;
+ int iCount = 0;
+ if (flags & DRAW_VERTICES_HAS_TEXS) {
+ texs = (const SkPoint*)fReader.skip(
+ vCount * sizeof(SkPoint));
+ }
+ if (flags & DRAW_VERTICES_HAS_COLORS) {
+ colors = (const SkColor*)fReader.skip(
+ vCount * sizeof(SkColor));
+ }
+ if (flags & DRAW_VERTICES_HAS_INDICES) {
+ iCount = getInt();
+ indices = (const uint16_t*)fReader.skip(
+ iCount * sizeof(uint16_t));
+ }
+ canvas.drawVertices(vmode, vCount, verts, texs, colors, NULL,
+ indices, iCount, paint);
+ } break;
+ case RESTORE:
+ canvas.restore();
+ break;
+ case ROTATE:
+ canvas.rotate(getScalar());
+ break;
+ case SAVE:
+ canvas.save((SkCanvas::SaveFlags) getInt());
+ break;
+ case SAVE_LAYER: {
+ const SkRect* boundsPtr = getRectPtr();
+ const SkPaint* paint = getPaint();
+ canvas.saveLayer(boundsPtr, paint, (SkCanvas::SaveFlags) getInt());
+ } break;
+ case SCALE: {
+ SkScalar sx = getScalar();
+ SkScalar sy = getScalar();
+ canvas.scale(sx, sy);
+ } break;
+ case SKEW: {
+ SkScalar sx = getScalar();
+ SkScalar sy = getScalar();
+ canvas.skew(sx, sy);
+ } break;
+ case TRANSLATE: {
+ SkScalar dx = getScalar();
+ SkScalar dy = getScalar();
+ canvas.translate(dx, dy);
+ } break;
+ default:
+ SkASSERT(0);
+ }
+ }
+
+// this->dumpSize();
+}
+
+void SkPicturePlayback::abort() {
+ fReader.skip(fReader.size() - fReader.offset());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if 0
+uint32_t SkPicturePlayback::flatten(void* storage) const {
+ SkWBuffer buffer(storage);
+ buffer.write32(fBitmapCount);
+ int index;
+ for (index = 0; index < fBitmapCount; index++) {
+ const SkBitmap& bitmap = fBitmaps[index];
+ uint32_t size = bitmap.flatten(NULL, true);
+ buffer.write32(size);
+ void* local = buffer.skip(size);
+ bitmap.flatten(local, true);
+ }
+ buffer.write32(fPaintCount);
+ for (index = 0; index < fPaintCount; index++) {
+ SkFlattenableWriteBuffer flatWrite;
+ const SkPaint& paint = fPaints[index];
+ SkFlatPaint::Write(&flatWrite, paint);
+ uint32_t size = flatWrite.pos();
+ buffer.write32(size);
+ void* local = buffer.skip(size);
+ flatWrite.reset(local);
+ SkFlatPaint::Write(&flatWrite, paint);
+ }
+ buffer.write32(fPathCount);
+ for (index = 0; index < fPathCount; index++) {
+ const SkPath& path = fPaths[index];
+ uint32_t size = path.flatten(NULL);
+ buffer.write32(size);
+ void* local = buffer.skip(size);
+ path.flatten(local);
+ }
+
+#if 0
+ buffer.write32(fPictureCount);
+ for (index = 0; index < fPictureCount; index++) {
+ const SkPicture& picture = fPictures[index];
+ uint32_t size = picture.flatten(NULL);
+ buffer.write32(size);
+ void* local = buffer.skip(size);
+ picture.flatten(local);
+ }
+#endif
+
+ buffer.write32(fRegionCount);
+ for (index = 0; index < fRegionCount; index++) {
+ const SkRegion& region = fRegions[index];
+ size_t size = region.computeBufferSize();
+ buffer.write32(size);
+ void* local = buffer.skip(size);
+ region.writeToBuffer(local);
+ }
+ fReader.rewind();
+ size_t length = fReader.size();
+ buffer.write32(length);
+ memcpy(buffer.skip(length), fReader.base(), length);
+ return (uint32_t) buffer.pos();
+}
+
+void SkPicturePlayback::unflatten(const void* storage) {
+ SkRBuffer buffer(storage);
+ int index;
+ fBitmapCount = buffer.readU32();
+ fBitmaps = new SkBitmap[fBitmapCount];
+ for (index = 0; index < fBitmapCount; index++) {
+ uint32_t size = buffer.readU32();
+ const void* local = buffer.skip(size);
+ fBitmaps[index].unflatten(local);
+ }
+ fPaintCount = buffer.readU32();
+ fPaints = new SkPaint[fPaintCount];
+ for (index = 0; index < fPaintCount; index++) {
+ uint32_t size = buffer.readU32();
+ const void* local = buffer.skip(size);
+ SkFlatPaint::Read(local, &fPaints[index]);
+ }
+ fPathCount = buffer.readU32();
+ fPaths = new SkPath[fPathCount];
+ for (index = 0; index < fPathCount; index++) {
+ uint32_t size = buffer.readU32();
+ const void* local = buffer.skip(size);
+ fPaths[index].unflatten(local);
+ }
+
+#if 0
+ fPictureCount = buffer.readU32();
+ fPictures = new SkPicture[fPictureCount];
+ for (index = 0; index < fPictureCount; index++) {
+ uint32_t size = buffer.readU32();
+ const void* local = buffer.skip(size);
+ fPictures[index].unflatten(local);
+ }
+#endif
+
+ fRegionCount = buffer.readU32();
+ fRegions = new SkRegion[fRegionCount];
+ for (index = 0; index < fRegionCount; index++) {
+ uint32_t size = buffer.readU32();
+ const void* local = buffer.skip(size);
+ fRegions[index].readFromBuffer(local);
+ }
+ int32_t length = buffer.readS32();
+ const void* stream = buffer.skip(length);
+ fReader.setMemory(stream, length);
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG_SIZE
+int SkPicturePlayback::size(size_t* sizePtr) {
+ int objects = bitmaps(sizePtr);
+ objects += paints(sizePtr);
+ objects += paths(sizePtr);
+ objects += pictures(sizePtr);
+ objects += regions(sizePtr);
+ *sizePtr = fReader.size();
+ return objects;
+}
+
+int SkPicturePlayback::bitmaps(size_t* size) {
+ size_t result = 0;
+ for (int index = 0; index < fBitmapCount; index++) {
+ // const SkBitmap& bitmap = fBitmaps[index];
+ result += sizeof(SkBitmap); // bitmap->size();
+ }
+ *size = result;
+ return fBitmapCount;
+}
+
+int SkPicturePlayback::paints(size_t* size) {
+ size_t result = 0;
+ for (int index = 0; index < fPaintCount; index++) {
+ // const SkPaint& paint = fPaints[index];
+ result += sizeof(SkPaint); // paint->size();
+ }
+ *size = result;
+ return fPaintCount;
+}
+
+int SkPicturePlayback::paths(size_t* size) {
+ size_t result = 0;
+ for (int index = 0; index < fPathCount; index++) {
+ const SkPath& path = fPaths[index];
+ result += path.flatten(NULL);
+ }
+ *size = result;
+ return fPathCount;
+}
+
+int SkPicturePlayback::regions(size_t* size) {
+ size_t result = 0;
+ for (int index = 0; index < fRegionCount; index++) {
+ // const SkRegion& region = fRegions[index];
+ result += sizeof(SkRegion); // region->size();
+ }
+ *size = result;
+ return fRegionCount;
+}
+#endif
+
+#ifdef SK_DEBUG_DUMP
+void SkPicturePlayback::dumpBitmap(const SkBitmap& bitmap) const {
+ char pBuffer[DUMP_BUFFER_SIZE];
+ char* bufferPtr = pBuffer;
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "BitmapData bitmap%p = {", &bitmap);
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "{kWidth, %d}, ", bitmap.width());
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "{kHeight, %d}, ", bitmap.height());
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "{kRowBytes, %d}, ", bitmap.rowBytes());
+// start here;
+ SkDebugf("%s{0}};\n", pBuffer);
+}
+
+void dumpMatrix(const SkMatrix& matrix) const {
+ SkMatrix defaultMatrix;
+ defaultMatrix.reset();
+ char pBuffer[DUMP_BUFFER_SIZE];
+ char* bufferPtr = pBuffer;
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "MatrixData matrix%p = {", &matrix);
+ SkScalar scaleX = matrix.getScaleX();
+ if (scaleX != defaultMatrix.getScaleX())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "{kScaleX, %g}, ", SkScalarToFloat(scaleX));
+ SkScalar scaleY = matrix.getScaleY();
+ if (scaleY != defaultMatrix.getScaleY())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "{kScaleY, %g}, ", SkScalarToFloat(scaleY));
+ SkScalar skewX = matrix.getSkewX();
+ if (skewX != defaultMatrix.getSkewX())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "{kSkewX, %g}, ", SkScalarToFloat(skewX));
+ SkScalar skewY = matrix.getSkewY();
+ if (skewY != defaultMatrix.getSkewY())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "{kSkewY, %g}, ", SkScalarToFloat(skewY));
+ SkScalar translateX = matrix.getTranslateX();
+ if (translateX != defaultMatrix.getTranslateX())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "{kTranslateX, %g}, ", SkScalarToFloat(translateX));
+ SkScalar translateY = matrix.getTranslateY();
+ if (translateY != defaultMatrix.getTranslateY())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "{kTranslateY, %g}, ", SkScalarToFloat(translateY));
+ SkScalar perspX = matrix.getPerspX();
+ if (perspX != defaultMatrix.getPerspX())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "{kPerspX, %g}, ", SkFractToFloat(perspX));
+ SkScalar perspY = matrix.getPerspY();
+ if (perspY != defaultMatrix.getPerspY())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "{kPerspY, %g}, ", SkFractToFloat(perspY));
+ SkDebugf("%s{0}};\n", pBuffer);
+}
+
+void dumpPaint(const SkPaint& paint) const {
+ SkPaint defaultPaint;
+ char pBuffer[DUMP_BUFFER_SIZE];
+ char* bufferPtr = pBuffer;
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "PaintPointers paintPtrs%p = {", &paint);
+ const SkTypeface* typeface = paint.getTypeface();
+ if (typeface != defaultPaint.getTypeface())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "{kTypeface, %p}, ", typeface);
+ const SkPathEffect* pathEffect = paint.getPathEffect();
+ if (pathEffect != defaultPaint.getPathEffect())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "{kPathEffect, %p}, ", pathEffect);
+ const SkShader* shader = paint.getShader();
+ if (shader != defaultPaint.getShader())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "{kShader, %p}, ", shader);
+ const SkXfermode* xfermode = paint.getXfermode();
+ if (xfermode != defaultPaint.getXfermode())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "{kXfermode, %p}, ", xfermode);
+ const SkMaskFilter* maskFilter = paint.getMaskFilter();
+ if (maskFilter != defaultPaint.getMaskFilter())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "{kMaskFilter, %p}, ", maskFilter);
+ const SkColorFilter* colorFilter = paint.getColorFilter();
+ if (colorFilter != defaultPaint.getColorFilter())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "{kColorFilter, %p}, ", colorFilter);
+ const SkRasterizer* rasterizer = paint.getRasterizer();
+ if (rasterizer != defaultPaint.getRasterizer())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "{kRasterizer, %p}, ", rasterizer);
+ const SkDrawLooper* drawLooper = paint.getLooper();
+ if (drawLooper != defaultPaint.getLooper())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "{kDrawLooper, %p}, ", drawLooper);
+ SkDebugf("%s{0}};\n", pBuffer);
+ bufferPtr = pBuffer;
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "PaintScalars paintScalars%p = {", &paint);
+ SkScalar textSize = paint.getTextSize();
+ if (textSize != defaultPaint.getTextSize())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "{kTextSize, %g}, ", SkScalarToFloat(textSize));
+ SkScalar textScaleX = paint.getTextScaleX();
+ if (textScaleX != defaultPaint.getTextScaleX())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "{kTextScaleX, %g}, ", SkScalarToFloat(textScaleX));
+ SkScalar textSkewX = paint.getTextSkewX();
+ if (textSkewX != defaultPaint.getTextSkewX())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "{kTextSkewX, %g}, ", SkScalarToFloat(textSkewX));
+ SkScalar strokeWidth = paint.getStrokeWidth();
+ if (strokeWidth != defaultPaint.getStrokeWidth())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "{kStrokeWidth, %g}, ", SkScalarToFloat(strokeWidth));
+ SkScalar strokeMiter = paint.getStrokeMiter();
+ if (strokeMiter != defaultPaint.getStrokeMiter())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "{kStrokeMiter, %g}, ", SkScalarToFloat(strokeMiter));
+ SkDebugf("%s{0}};\n", pBuffer);
+ bufferPtr = pBuffer;
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "PaintInts = paintInts%p = {", &paint);
+ unsigned color = paint.getColor();
+ if (color != defaultPaint.getColor())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "{kColor, 0x%x}, ", color);
+ unsigned flags = paint.getFlags();
+ if (flags != defaultPaint.getFlags())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "{kFlags, 0x%x}, ", flags);
+ int align = paint.getTextAlign();
+ if (align != defaultPaint.getTextAlign())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "{kAlign, 0x%x}, ", align);
+ int strokeCap = paint.getStrokeCap();
+ if (strokeCap != defaultPaint.getStrokeCap())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "{kStrokeCap, 0x%x}, ", strokeCap);
+ int strokeJoin = paint.getStrokeJoin();
+ if (strokeJoin != defaultPaint.getStrokeJoin())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "{kAlign, 0x%x}, ", strokeJoin);
+ int style = paint.getStyle();
+ if (style != defaultPaint.getStyle())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "{kStyle, 0x%x}, ", style);
+ int textEncoding = paint.getTextEncoding();
+ if (textEncoding != defaultPaint.getTextEncoding())
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "{kTextEncoding, 0x%x}, ", textEncoding);
+ SkDebugf("%s{0}};\n", pBuffer);
+
+ SkDebugf("PaintData paint%p = {paintPtrs%p, paintScalars%p, paintInts%p};\n",
+ &paint, &paint, &paint, &paint);
+}
+
+void SkPicturePlayback::dumpPath(const SkPath& path) const {
+ SkDebugf("path dump unimplemented\n");
+}
+
+void SkPicturePlayback::dumpPicture(const SkPicture& picture) const {
+ SkDebugf("picture dump unimplemented\n");
+}
+
+void SkPicturePlayback::dumpRegion(const SkRegion& region) const {
+ SkDebugf("region dump unimplemented\n");
+}
+
+int SkPicturePlayback::dumpDrawType(char* bufferPtr, char* buffer, DrawType drawType) {
+ return snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer),
+ "k%s, ", DrawTypeToString(drawType));
+}
+
+int SkPicturePlayback::dumpInt(char* bufferPtr, char* buffer, char* name) {
+ return snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer),
+ "%s:%d, ", name, getInt());
+}
+
+int SkPicturePlayback::dumpRect(char* bufferPtr, char* buffer, char* name) {
+ const SkRect* rect = fReader.skipRect();
+ return snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer),
+ "%s:{l:%g t:%g r:%g b:%g}, ", name, SkScalarToFloat(rect.fLeft),
+ SkScalarToFloat(rect.fTop),
+ SkScalarToFloat(rect.fRight), SkScalarToFloat(rect.fBottom));
+}
+
+int SkPicturePlayback::dumpPoint(char* bufferPtr, char* buffer, char* name) {
+ SkPoint pt;
+ getPoint(&pt);
+ return snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer),
+ "%s:{x:%g y:%g}, ", name, SkScalarToFloat(pt.fX),
+ SkScalarToFloat(pt.fY));
+}
+
+void SkPicturePlayback::dumpPointArray(char** bufferPtrPtr, char* buffer, int count) {
+ char* bufferPtr = *bufferPtrPtr;
+ const SkPoint* pts = (const SkPoint*)fReadStream.getAtPos();
+ fReadStream.skip(sizeof(SkPoint) * count);
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer),
+ "count:%d {", count);
+ for (int index = 0; index < count; index++)
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer),
+ "{x:%g y:%g}, ", SkScalarToFloat(pts[index].fX),
+ SkScalarToFloat(pts[index].fY));
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer),
+ "} ");
+ *bufferPtrPtr = bufferPtr;
+}
+
+int SkPicturePlayback::dumpPtr(char* bufferPtr, char* buffer, char* name, void* ptr) {
+ return snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer),
+ "%s:%p, ", name, ptr);
+}
+
+int SkPicturePlayback::dumpRectPtr(char* bufferPtr, char* buffer, char* name) {
+ char result;
+ fReadStream.read(&result, sizeof(result));
+ if (result)
+ return dumpRect(bufferPtr, buffer, name);
+ else
+ return snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer),
+ "%s:NULL, ", name);
+}
+
+int SkPicturePlayback::dumpScalar(char* bufferPtr, char* buffer, char* name) {
+ return snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer),
+ "%s:%d, ", name, getScalar());
+}
+
+void SkPicturePlayback::dumpText(char** bufferPtrPtr, char* buffer) {
+ char* bufferPtr = *bufferPtrPtr;
+ int length = getInt();
+ bufferPtr += dumpDrawType(bufferPtr, buffer);
+ fReadStream.skipToAlign4();
+ char* text = (char*) fReadStream.getAtPos();
+ fReadStream.skip(length);
+ bufferPtr += dumpInt(bufferPtr, buffer, "length");
+ int limit = DUMP_BUFFER_SIZE - (bufferPtr - buffer) - 2;
+ length >>= 1;
+ if (limit > length)
+ limit = length;
+ if (limit > 0) {
+ *bufferPtr++ = '"';
+ for (int index = 0; index < limit; index++) {
+ *bufferPtr++ = *(unsigned short*) text;
+ text += sizeof(unsigned short);
+ }
+ *bufferPtr++ = '"';
+ }
+ *bufferPtrPtr = bufferPtr;
+}
+
+#define DUMP_DRAWTYPE(drawType) \
+ bufferPtr += dumpDrawType(bufferPtr, buffer, drawType)
+
+#define DUMP_INT(name) \
+ bufferPtr += dumpInt(bufferPtr, buffer, #name)
+
+#define DUMP_RECT_PTR(name) \
+ bufferPtr += dumpRectPtr(bufferPtr, buffer, #name)
+
+#define DUMP_POINT(name) \
+ bufferPtr += dumpRect(bufferPtr, buffer, #name)
+
+#define DUMP_RECT(name) \
+ bufferPtr += dumpRect(bufferPtr, buffer, #name)
+
+#define DUMP_POINT_ARRAY(count) \
+ dumpPointArray(&bufferPtr, buffer, count)
+
+#define DUMP_PTR(name, ptr) \
+ bufferPtr += dumpPtr(bufferPtr, buffer, #name, (void*) ptr)
+
+#define DUMP_SCALAR(name) \
+ bufferPtr += dumpScalar(bufferPtr, buffer, #name)
+
+#define DUMP_TEXT() \
+ dumpText(&bufferPtr, buffer)
+
+void SkPicturePlayback::dumpStream() {
+ SkDebugf("RecordStream stream = {\n");
+ DrawType drawType;
+ TextContainer text;
+ fReadStream.rewind();
+ char buffer[DUMP_BUFFER_SIZE], * bufferPtr;
+ while (fReadStream.read(&drawType, sizeof(drawType))) {
+ bufferPtr = buffer;
+ DUMP_DRAWTYPE(drawType);
+ switch (drawType) {
+ case CLIP_PATH: {
+ DUMP_PTR(SkPath, &getPath());
+ DUMP_INT(SkRegion::Op);
+ DUMP_INT(offsetToRestore);
+ } break;
+ case CLIP_REGION: {
+ DUMP_PTR(SkRegion, &getRegion());
+ DUMP_INT(SkRegion::Op);
+ DUMP_INT(offsetToRestore);
+ } break;
+ case CLIP_RECT: {
+ DUMP_RECT(rect);
+ DUMP_INT(SkRegion::Op);
+ DUMP_INT(offsetToRestore);
+ } break;
+ case CONCAT:
+ DUMP_PTR(SkMatrix, getMatrix());
+ break;
+ case DRAW_BITMAP: {
+ DUMP_PTR(SkPaint, getPaint());
+ DUMP_PTR(SkBitmap, &getBitmap());
+ DUMP_SCALAR(left);
+ DUMP_SCALAR(top);
+ } break;
+ case DRAW_PAINT:
+ DUMP_PTR(SkPaint, getPaint());
+ break;
+ case DRAW_PATH: {
+ DUMP_PTR(SkPaint, getPaint());
+ DUMP_PTR(SkPath, &getPath());
+ } break;
+ case DRAW_PICTURE: {
+ DUMP_PTR(SkPicture, &getPicture());
+ } break;
+ case DRAW_POINTS: {
+ DUMP_PTR(SkPaint, getPaint());
+ (void)getInt(); // PointMode
+ size_t count = getInt();
+ fReadStream.skipToAlign4();
+ DUMP_POINT_ARRAY(count);
+ } break;
+ case DRAW_POS_TEXT: {
+ DUMP_PTR(SkPaint, getPaint());
+ DUMP_TEXT();
+ size_t points = getInt();
+ fReadStream.skipToAlign4();
+ DUMP_POINT_ARRAY(points);
+ } break;
+ case DRAW_POS_TEXT_H: {
+ DUMP_PTR(SkPaint, getPaint());
+ DUMP_TEXT();
+ size_t points = getInt();
+ fReadStream.skipToAlign4();
+ DUMP_SCALAR(top);
+ DUMP_SCALAR(bottom);
+ DUMP_SCALAR(constY);
+ DUMP_POINT_ARRAY(points);
+ } break;
+ case DRAW_RECT: {
+ DUMP_PTR(SkPaint, getPaint());
+ DUMP_RECT(rect);
+ } break;
+ case DRAW_SPRITE: {
+ DUMP_PTR(SkPaint, getPaint());
+ DUMP_PTR(SkBitmap, &getBitmap());
+ DUMP_SCALAR(left);
+ DUMP_SCALAR(top);
+ } break;
+ case DRAW_TEXT: {
+ DUMP_PTR(SkPaint, getPaint());
+ DUMP_TEXT();
+ DUMP_SCALAR(x);
+ DUMP_SCALAR(y);
+ } break;
+ case DRAW_TEXT_ON_PATH: {
+ DUMP_PTR(SkPaint, getPaint());
+ DUMP_TEXT();
+ DUMP_PTR(SkPath, &getPath());
+ DUMP_PTR(SkMatrix, getMatrix());
+ } break;
+ case RESTORE:
+ break;
+ case ROTATE:
+ DUMP_SCALAR(rotate);
+ break;
+ case SAVE:
+ DUMP_INT(SkCanvas::SaveFlags);
+ break;
+ case SAVE_LAYER: {
+ DUMP_RECT_PTR(layer);
+ DUMP_PTR(SkPaint, getPaint());
+ DUMP_INT(SkCanvas::SaveFlags);
+ } break;
+ case SCALE: {
+ DUMP_SCALAR(sx);
+ DUMP_SCALAR(sy);
+ } break;
+ case SKEW: {
+ DUMP_SCALAR(sx);
+ DUMP_SCALAR(sy);
+ } break;
+ case TRANSLATE: {
+ DUMP_SCALAR(dx);
+ DUMP_SCALAR(dy);
+ } break;
+ default:
+ SkASSERT(0);
+ }
+ SkDebugf("%s\n", buffer);
+ }
+}
+
+void SkPicturePlayback::dump() const {
+ char pBuffer[DUMP_BUFFER_SIZE];
+ char* bufferPtr = pBuffer;
+ int index;
+ if (fBitmapCount > 0)
+ SkDebugf("// bitmaps (%d)\n", fBitmapCount);
+ for (index = 0; index < fBitmapCount; index++) {
+ const SkBitmap& bitmap = fBitmaps[index];
+ dumpBitmap(bitmap);
+ }
+ if (fBitmapCount > 0)
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "Bitmaps bitmaps = {");
+ for (index = 0; index < fBitmapCount; index++)
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "bitmap%p, ", &fBitmaps[index]);
+ if (fBitmapCount > 0)
+ SkDebugf("%s0};\n", pBuffer);
+
+ if (fMatrixCount > 0)
+ SkDebugf("// matrices (%d)\n", fMatrixCount);
+ for (index = 0; index < fMatrixCount; index++) {
+ const SkMatrix& matrix = fMatrices[index];
+ dumpMatrix(matrix);
+ }
+ bufferPtr = pBuffer;
+ if (fMatrixCount > 0)
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "Matrices matrices = {");
+ for (index = 0; index < fMatrixCount; index++)
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "matrix%p, ", &fMatrices[index]);
+ if (fMatrixCount > 0)
+ SkDebugf("%s0};\n", pBuffer);
+
+ if (fPaintCount > 0)
+ SkDebugf("// paints (%d)\n", fPaintCount);
+ for (index = 0; index < fPaintCount; index++) {
+ const SkPaint& paint = fPaints[index];
+ dumpPaint(paint);
+ }
+ bufferPtr = pBuffer;
+ if (fPaintCount > 0)
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "Paints paints = {");
+ for (index = 0; index < fPaintCount; index++)
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "paint%p, ", &fPaints[index]);
+ if (fPaintCount > 0)
+ SkDebugf("%s0};\n", pBuffer);
+
+ for (index = 0; index < fPathCount; index++) {
+ const SkPath& path = fPaths[index];
+ dumpPath(path);
+ }
+ bufferPtr = pBuffer;
+ if (fPathCount > 0)
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "Paths paths = {");
+ for (index = 0; index < fPathCount; index++)
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "path%p, ", &fPaths[index]);
+ if (fPathCount > 0)
+ SkDebugf("%s0};\n", pBuffer);
+
+ for (index = 0; index < fPictureCount; index++) {
+ dumpPicture(*fPictureRefs[index]);
+ }
+ bufferPtr = pBuffer;
+ if (fPictureCount > 0)
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "Pictures pictures = {");
+ for (index = 0; index < fPictureCount; index++)
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "picture%p, ", fPictureRefs[index]);
+ if (fPictureCount > 0)
+ SkDebugf("%s0};\n", pBuffer);
+
+ for (index = 0; index < fRegionCount; index++) {
+ const SkRegion& region = fRegions[index];
+ dumpRegion(region);
+ }
+ bufferPtr = pBuffer;
+ if (fRegionCount > 0)
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "Regions regions = {");
+ for (index = 0; index < fRegionCount; index++)
+ bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+ "region%p, ", &fRegions[index]);
+ if (fRegionCount > 0)
+ SkDebugf("%s0};\n", pBuffer);
+
+ const_cast<SkPicturePlayback*>(this)->dumpStream();
+}
+
+#endif
diff --git a/src/core/SkPicturePlayback.h b/src/core/SkPicturePlayback.h
new file mode 100644
index 0000000000..b4e69cac46
--- /dev/null
+++ b/src/core/SkPicturePlayback.h
@@ -0,0 +1,168 @@
+#ifndef SkPicturePlayback_DEFINED
+#define SkPicturePlayback_DEFINED
+
+#include "SkPicture.h"
+#include "SkReader32.h"
+
+#include "SkBitmap.h"
+#include "SkMatrix.h"
+#include "SkPaint.h"
+#include "SkPath.h"
+#include "SkPathHeap.h"
+#include "SkRegion.h"
+#include "SkPictureFlat.h"
+
+class SkPictureRecord;
+class SkStream;
+class SkWStream;
+
+class SkPicturePlayback {
+public:
+ SkPicturePlayback();
+ SkPicturePlayback(const SkPicturePlayback& src);
+ explicit SkPicturePlayback(const SkPictureRecord& record);
+ explicit SkPicturePlayback(SkStream*);
+
+ virtual ~SkPicturePlayback();
+
+ void draw(SkCanvas& canvas);
+
+ void serialize(SkWStream*) const;
+
+ void dumpSize() const;
+
+ // Can be called in the middle of playback (the draw() call). WIll abort the
+ // drawing and return from draw() after the "current" op code is done
+ void abort();
+
+private:
+
+ class TextContainer {
+ public:
+ size_t length() { return fByteLength; }
+ const void* text() { return (const void*) fText; }
+ size_t fByteLength;
+ const char* fText;
+ };
+
+ const SkBitmap& getBitmap() {
+ int index = getInt();
+ SkASSERT(index > 0);
+ return fBitmaps[index - 1];
+ }
+
+ int getIndex() { return fReader.readInt(); }
+ int getInt() { return fReader.readInt(); }
+
+ const SkMatrix* getMatrix() {
+ int index = getInt();
+ if (index == 0) {
+ return NULL;
+ }
+ SkASSERT(index > 0 && index <= fMatrixCount);
+ return &fMatrices[index - 1];
+ }
+
+ const SkPath& getPath() {
+ return (*fPathHeap)[getInt() - 1];
+ }
+
+ SkPicture& getPicture() {
+ int index = getInt();
+ SkASSERT(index > 0 && index <= fPictureCount);
+ return *fPictureRefs[index - 1];
+ }
+
+ const SkPaint* getPaint() {
+ int index = getInt();
+ if (index == 0) {
+ return NULL;
+ }
+ SkASSERT(index > 0 && index <= fPaintCount);
+ return &fPaints[index - 1];
+ }
+
+ const SkRect* getRectPtr() {
+ if (fReader.readBool()) {
+ return fReader.skipRect();
+ } else {
+ return NULL;
+ }
+ }
+
+ const SkIRect* getIRectPtr() {
+ if (fReader.readBool()) {
+ return (const SkIRect*)fReader.skip(sizeof(SkIRect));
+ } else {
+ return NULL;
+ }
+ }
+
+ const SkRegion& getRegion() {
+ int index = getInt();
+ SkASSERT(index > 0);
+ return fRegions[index - 1];
+ }
+
+ SkScalar getScalar() { return fReader.readScalar(); }
+
+ void getText(TextContainer* text) {
+ size_t length = text->fByteLength = getInt();
+ text->fText = (const char*)fReader.skip(length);
+ }
+
+ void init();
+
+#ifdef SK_DEBUG_SIZE
+public:
+ int size(size_t* sizePtr);
+ int bitmaps(size_t* size);
+ int paints(size_t* size);
+ int paths(size_t* size);
+ int regions(size_t* size);
+#endif
+
+#ifdef SK_DEBUG_DUMP
+private:
+ void dumpBitmap(const SkBitmap& bitmap) const;
+ void dumpMatrix(const SkMatrix& matrix) const;
+ void dumpPaint(const SkPaint& paint) const;
+ void dumpPath(const SkPath& path) const;
+ void dumpPicture(const SkPicture& picture) const;
+ void dumpRegion(const SkRegion& region) const;
+ int dumpDrawType(char* bufferPtr, char* buffer, DrawType drawType);
+ int dumpInt(char* bufferPtr, char* buffer, char* name);
+ int dumpRect(char* bufferPtr, char* buffer, char* name);
+ int dumpPoint(char* bufferPtr, char* buffer, char* name);
+ void dumpPointArray(char** bufferPtrPtr, char* buffer, int count);
+ int dumpPtr(char* bufferPtr, char* buffer, char* name, void* ptr);
+ int dumpRectPtr(char* bufferPtr, char* buffer, char* name);
+ int dumpScalar(char* bufferPtr, char* buffer, char* name);
+ void dumpText(char** bufferPtrPtr, char* buffer);
+ void dumpStream();
+
+public:
+ void dump() const;
+#endif
+
+private:
+ SkPathHeap* fPathHeap; // reference counted
+ SkBitmap* fBitmaps;
+ int fBitmapCount;
+ SkMatrix* fMatrices;
+ int fMatrixCount;
+ SkPaint* fPaints;
+ int fPaintCount;
+ SkRegion* fRegions;
+ int fRegionCount;
+ mutable SkFlattenableReadBuffer fReader;
+
+ SkPicture** fPictureRefs;
+ int fPictureCount;
+
+ SkRefCntPlayback fRCPlayback;
+ SkTypefacePlayback fTFPlayback;
+ SkFactoryPlayback* fFactoryPlayback;
+};
+
+#endif
diff --git a/src/core/SkPictureRecord.cpp b/src/core/SkPictureRecord.cpp
new file mode 100644
index 0000000000..6e7c645ff4
--- /dev/null
+++ b/src/core/SkPictureRecord.cpp
@@ -0,0 +1,698 @@
+#include "SkPictureRecord.h"
+#include "SkTSearch.h"
+
+#define MIN_WRITER_SIZE 16384
+#define HEAP_BLOCK_SIZE 4096
+
+SkPictureRecord::SkPictureRecord() :
+ fHeap(HEAP_BLOCK_SIZE), fWriter(MIN_WRITER_SIZE) {
+ fBitmapIndex = fMatrixIndex = fPaintIndex = fRegionIndex = 1;
+#ifdef SK_DEBUG_SIZE
+ fPointBytes = fRectBytes = fTextBytes = 0;
+ fPointWrites = fRectWrites = fTextWrites = 0;
+#endif
+
+ fRestoreOffsetStack.setReserve(32);
+ fRestoreOffsetStack.push(0);
+
+ fPathHeap = NULL; // lazy allocate
+}
+
+SkPictureRecord::~SkPictureRecord() {
+ reset();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int SkPictureRecord::save(SaveFlags flags) {
+ addDraw(SAVE);
+ addInt(flags);
+
+ fRestoreOffsetStack.push(0);
+
+ validate();
+ return this->INHERITED::save(flags);
+}
+
+int SkPictureRecord::saveLayer(const SkRect* bounds, const SkPaint* paint,
+ SaveFlags flags) {
+ addDraw(SAVE_LAYER);
+ addRectPtr(bounds);
+ addPaintPtr(paint);
+ addInt(flags);
+
+ fRestoreOffsetStack.push(0);
+
+ validate();
+ return this->INHERITED::saveLayer(bounds, paint, flags);
+}
+
+void SkPictureRecord::restore() {
+
+ // patch up the clip offsets
+ {
+ uint32_t restoreOffset = (uint32_t)fWriter.size();
+ uint32_t offset = fRestoreOffsetStack.top();
+ while (offset) {
+ uint32_t* peek = fWriter.peek32(offset);
+ offset = *peek;
+ *peek = restoreOffset;
+ }
+ fRestoreOffsetStack.pop();
+ }
+
+ addDraw(RESTORE);
+ validate();
+ return this->INHERITED::restore();
+}
+
+bool SkPictureRecord::translate(SkScalar dx, SkScalar dy) {
+ addDraw(TRANSLATE);
+ addScalar(dx);
+ addScalar(dy);
+ validate();
+ return this->INHERITED::translate(dx, dy);
+}
+
+bool SkPictureRecord::scale(SkScalar sx, SkScalar sy) {
+ addDraw(SCALE);
+ addScalar(sx);
+ addScalar(sy);
+ validate();
+ return this->INHERITED::scale(sx, sy);
+}
+
+bool SkPictureRecord::rotate(SkScalar degrees) {
+ addDraw(ROTATE);
+ addScalar(degrees);
+ validate();
+ return this->INHERITED::rotate(degrees);
+}
+
+bool SkPictureRecord::skew(SkScalar sx, SkScalar sy) {
+ addDraw(SKEW);
+ addScalar(sx);
+ addScalar(sy);
+ validate();
+ return this->INHERITED::skew(sx, sy);
+}
+
+bool SkPictureRecord::concat(const SkMatrix& matrix) {
+ validate();
+ addDraw(CONCAT);
+ addMatrix(matrix);
+ validate();
+ return this->INHERITED::concat(matrix);
+}
+
+bool SkPictureRecord::clipRect(const SkRect& rect, SkRegion::Op op) {
+ addDraw(CLIP_RECT);
+ addRect(rect);
+ addInt(op);
+
+ size_t offset = fWriter.size();
+ addInt(fRestoreOffsetStack.top());
+ fRestoreOffsetStack.top() = offset;
+
+ validate();
+ return this->INHERITED::clipRect(rect, op);
+}
+
+bool SkPictureRecord::clipPath(const SkPath& path, SkRegion::Op op) {
+ addDraw(CLIP_PATH);
+ addPath(path);
+ addInt(op);
+
+ size_t offset = fWriter.size();
+ addInt(fRestoreOffsetStack.top());
+ fRestoreOffsetStack.top() = offset;
+
+ validate();
+ return this->INHERITED::clipPath(path, op);
+}
+
+bool SkPictureRecord::clipRegion(const SkRegion& region, SkRegion::Op op) {
+ addDraw(CLIP_REGION);
+ addRegion(region);
+ addInt(op);
+
+ size_t offset = fWriter.size();
+ addInt(fRestoreOffsetStack.top());
+ fRestoreOffsetStack.top() = offset;
+
+ validate();
+ return this->INHERITED::clipRegion(region, op);
+}
+
+void SkPictureRecord::drawPaint(const SkPaint& paint) {
+ addDraw(DRAW_PAINT);
+ addPaint(paint);
+ validate();
+}
+
+void SkPictureRecord::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
+ const SkPaint& paint) {
+ addDraw(DRAW_POINTS);
+ addPaint(paint);
+ addInt(mode);
+ addInt(count);
+ fWriter.writeMul4(pts, count * sizeof(SkPoint));
+ validate();
+}
+
+void SkPictureRecord::drawRect(const SkRect& rect, const SkPaint& paint) {
+ addDraw(DRAW_RECT);
+ addPaint(paint);
+ addRect(rect);
+ validate();
+}
+
+void SkPictureRecord::drawPath(const SkPath& path, const SkPaint& paint) {
+ addDraw(DRAW_PATH);
+ addPaint(paint);
+ addPath(path);
+ validate();
+}
+
+void SkPictureRecord::drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
+ const SkPaint* paint = NULL) {
+ addDraw(DRAW_BITMAP);
+ addPaintPtr(paint);
+ addBitmap(bitmap);
+ addScalar(left);
+ addScalar(top);
+ validate();
+}
+
+void SkPictureRecord::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
+ const SkRect& dst, const SkPaint* paint) {
+ addDraw(DRAW_BITMAP_RECT);
+ addPaintPtr(paint);
+ addBitmap(bitmap);
+ addIRectPtr(src); // may be null
+ addRect(dst);
+ validate();
+}
+
+void SkPictureRecord::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
+ const SkPaint* paint) {
+ addDraw(DRAW_BITMAP_MATRIX);
+ addPaintPtr(paint);
+ addBitmap(bitmap);
+ addMatrix(matrix);
+ validate();
+}
+
+void SkPictureRecord::drawSprite(const SkBitmap& bitmap, int left, int top,
+ const SkPaint* paint = NULL) {
+ addDraw(DRAW_SPRITE);
+ addPaintPtr(paint);
+ addBitmap(bitmap);
+ addInt(left);
+ addInt(top);
+ validate();
+}
+
+void SkPictureRecord::addFontMetricsTopBottom(const SkPaint& paint,
+ SkScalar baselineY) {
+ SkPaint::FontMetrics metrics;
+ paint.getFontMetrics(&metrics);
+ SkRect bounds;
+ // construct a rect so we can see any adjustments from the paint.
+ // we use 0,1 for left,right, just so the rect isn't empty
+ bounds.set(0, metrics.fTop + baselineY,
+ SK_Scalar1, metrics.fBottom + baselineY);
+ (void)paint.computeFastBounds(bounds, &bounds);
+ // now record the top and bottom
+ addScalar(bounds.fTop);
+ addScalar(bounds.fBottom);
+}
+
+void SkPictureRecord::drawText(const void* text, size_t byteLength, SkScalar x,
+ SkScalar y, const SkPaint& paint) {
+ bool fast = paint.canComputeFastBounds();
+
+ addDraw(fast ? DRAW_TEXT_TOP_BOTTOM : DRAW_TEXT);
+ addPaint(paint);
+ addText(text, byteLength);
+ addScalar(x);
+ addScalar(y);
+ if (fast) {
+ addFontMetricsTopBottom(paint, y);
+ }
+ validate();
+}
+
+void SkPictureRecord::drawPosText(const void* text, size_t byteLength,
+ const SkPoint pos[], const SkPaint& paint) {
+ size_t points = paint.countText(text, byteLength);
+ if (0 == points)
+ return;
+
+ bool canUseDrawH = true;
+ // check if the caller really should have used drawPosTextH()
+ {
+ const SkScalar firstY = pos[0].fY;
+ for (size_t index = 1; index < points; index++) {
+ if (pos[index].fY != firstY) {
+ canUseDrawH = false;
+ break;
+ }
+ }
+ }
+
+ bool fast = canUseDrawH && paint.canComputeFastBounds();
+
+ if (fast) {
+ addDraw(DRAW_POS_TEXT_H_TOP_BOTTOM);
+ } else {
+ addDraw(canUseDrawH ? DRAW_POS_TEXT_H : DRAW_POS_TEXT);
+ }
+ addPaint(paint);
+ addText(text, byteLength);
+ addInt(points);
+
+#ifdef SK_DEBUG_SIZE
+ size_t start = fWriter.size();
+#endif
+ if (canUseDrawH) {
+ if (fast) {
+ addFontMetricsTopBottom(paint, pos[0].fY);
+ }
+ addScalar(pos[0].fY);
+ SkScalar* xptr = (SkScalar*)fWriter.reserve(points * sizeof(SkScalar));
+ for (size_t index = 0; index < points; index++)
+ *xptr++ = pos[index].fX;
+ }
+ else {
+ fWriter.writeMul4(pos, points * sizeof(SkPoint));
+ }
+#ifdef SK_DEBUG_SIZE
+ fPointBytes += fWriter.size() - start;
+ fPointWrites += points;
+#endif
+ validate();
+}
+
+void SkPictureRecord::drawPosTextH(const void* text, size_t byteLength,
+ const SkScalar xpos[], SkScalar constY,
+ const SkPaint& paint) {
+ size_t points = paint.countText(text, byteLength);
+ if (0 == points)
+ return;
+
+ bool fast = paint.canComputeFastBounds();
+
+ addDraw(fast ? DRAW_POS_TEXT_H_TOP_BOTTOM : DRAW_POS_TEXT_H);
+ addPaint(paint);
+ addText(text, byteLength);
+ addInt(points);
+
+#ifdef SK_DEBUG_SIZE
+ size_t start = fWriter.size();
+#endif
+ if (fast) {
+ addFontMetricsTopBottom(paint, constY);
+ }
+ addScalar(constY);
+ fWriter.writeMul4(xpos, points * sizeof(SkScalar));
+#ifdef SK_DEBUG_SIZE
+ fPointBytes += fWriter.size() - start;
+ fPointWrites += points;
+#endif
+ validate();
+}
+
+void SkPictureRecord::drawTextOnPath(const void* text, size_t byteLength,
+ const SkPath& path, const SkMatrix* matrix,
+ const SkPaint& paint) {
+ addDraw(DRAW_TEXT_ON_PATH);
+ addPaint(paint);
+ addText(text, byteLength);
+ addPath(path);
+ addMatrixPtr(matrix);
+ validate();
+}
+
+void SkPictureRecord::drawPicture(SkPicture& picture) {
+ addDraw(DRAW_PICTURE);
+ addPicture(picture);
+ validate();
+}
+
+void SkPictureRecord::drawVertices(VertexMode vmode, int vertexCount,
+ const SkPoint vertices[], const SkPoint texs[],
+ const SkColor colors[], SkXfermode*,
+ const uint16_t indices[], int indexCount,
+ const SkPaint& paint) {
+ uint32_t flags = 0;
+ if (texs) {
+ flags |= DRAW_VERTICES_HAS_TEXS;
+ }
+ if (colors) {
+ flags |= DRAW_VERTICES_HAS_COLORS;
+ }
+ if (indexCount > 0) {
+ flags |= DRAW_VERTICES_HAS_INDICES;
+ }
+
+ addDraw(DRAW_VERTICES);
+ addPaint(paint);
+ addInt(flags);
+ addInt(vmode);
+ addInt(vertexCount);
+ addPoints(vertices, vertexCount);
+ if (flags & DRAW_VERTICES_HAS_TEXS) {
+ addPoints(texs, vertexCount);
+ }
+ if (flags & DRAW_VERTICES_HAS_COLORS) {
+ fWriter.writeMul4(colors, vertexCount * sizeof(SkColor));
+ }
+ if (flags & DRAW_VERTICES_HAS_INDICES) {
+ addInt(indexCount);
+ fWriter.writePad(indices, indexCount * sizeof(uint16_t));
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkPictureRecord::reset() {
+ fPathHeap->safeUnref();
+ fPathHeap = NULL;
+
+ fBitmaps.reset();
+ fMatrices.reset();
+ fPaints.reset();
+ fPictureRefs.unrefAll();
+ fRegions.reset();
+ fWriter.reset();
+ fHeap.reset();
+
+ fRestoreOffsetStack.setCount(1);
+ fRestoreOffsetStack.top() = 0;
+
+ fRCRecorder.reset();
+ fTFRecorder.reset();
+}
+
+void SkPictureRecord::addBitmap(const SkBitmap& bitmap) {
+ addInt(find(fBitmaps, bitmap));
+}
+
+void SkPictureRecord::addMatrix(const SkMatrix& matrix) {
+ addMatrixPtr(&matrix);
+}
+
+void SkPictureRecord::addMatrixPtr(const SkMatrix* matrix) {
+ addInt(find(fMatrices, matrix));
+}
+
+void SkPictureRecord::addPaint(const SkPaint& paint) {
+ addPaintPtr(&paint);
+}
+
+void SkPictureRecord::addPaintPtr(const SkPaint* paint) {
+ addInt(find(fPaints, paint));
+}
+
+void SkPictureRecord::addPath(const SkPath& path) {
+ if (NULL == fPathHeap) {
+ fPathHeap = SkNEW(SkPathHeap);
+ }
+ addInt(fPathHeap->append(path));
+}
+
+void SkPictureRecord::addPicture(SkPicture& picture) {
+ int index = fPictureRefs.find(&picture);
+ if (index < 0) { // not found
+ index = fPictureRefs.count();
+ *fPictureRefs.append() = &picture;
+ picture.ref();
+ }
+ // follow the convention of recording a 1-based index
+ addInt(index + 1);
+}
+
+void SkPictureRecord::addPoint(const SkPoint& point) {
+#ifdef SK_DEBUG_SIZE
+ size_t start = fWriter.size();
+#endif
+ fWriter.writePoint(point);
+#ifdef SK_DEBUG_SIZE
+ fPointBytes += fWriter.size() - start;
+ fPointWrites++;
+#endif
+}
+
+void SkPictureRecord::addPoints(const SkPoint pts[], int count) {
+ fWriter.writeMul4(pts, count * sizeof(SkPoint));
+#ifdef SK_DEBUG_SIZE
+ fPointBytes += count * sizeof(SkPoint);
+ fPointWrites++;
+#endif
+}
+
+void SkPictureRecord::addRect(const SkRect& rect) {
+#ifdef SK_DEBUG_SIZE
+ size_t start = fWriter.size();
+#endif
+ fWriter.writeRect(rect);
+#ifdef SK_DEBUG_SIZE
+ fRectBytes += fWriter.size() - start;
+ fRectWrites++;
+#endif
+}
+
+void SkPictureRecord::addRectPtr(const SkRect* rect) {
+ if (fWriter.writeBool(rect != NULL)) {
+ fWriter.writeRect(*rect);
+ }
+}
+
+void SkPictureRecord::addIRectPtr(const SkIRect* rect) {
+ if (fWriter.writeBool(rect != NULL)) {
+ *(SkIRect*)fWriter.reserve(sizeof(SkIRect)) = *rect;
+ }
+}
+
+void SkPictureRecord::addRegion(const SkRegion& region) {
+ addInt(find(fRegions, region));
+}
+
+void SkPictureRecord::addText(const void* text, size_t byteLength) {
+#ifdef SK_DEBUG_SIZE
+ size_t start = fWriter.size();
+#endif
+ addInt(byteLength);
+ fWriter.writePad(text, byteLength);
+#ifdef SK_DEBUG_SIZE
+ fTextBytes += fWriter.size() - start;
+ fTextWrites++;
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int SkPictureRecord::find(SkTDArray<const SkFlatBitmap* >& bitmaps, const SkBitmap& bitmap) {
+ SkFlatBitmap* flat = SkFlatBitmap::Flatten(&fHeap, bitmap, fBitmapIndex,
+ &fRCRecorder);
+ int index = SkTSearch<SkFlatData>((const SkFlatData**) bitmaps.begin(),
+ bitmaps.count(), (SkFlatData*) flat, sizeof(flat), &SkFlatData::Compare);
+ if (index >= 0) {
+// SkBitmap bitmap;
+// flat->unflatten(&bitmap); // balance ref count
+ return bitmaps[index]->index();
+ }
+ index = ~index;
+ *bitmaps.insert(index) = flat;
+ return fBitmapIndex++;
+}
+
+int SkPictureRecord::find(SkTDArray<const SkFlatMatrix* >& matrices, const SkMatrix* matrix) {
+ if (matrix == NULL)
+ return 0;
+ SkFlatMatrix* flat = SkFlatMatrix::Flatten(&fHeap, *matrix, fMatrixIndex);
+ int index = SkTSearch<SkFlatData>((const SkFlatData**) matrices.begin(),
+ matrices.count(), (SkFlatData*) flat, sizeof(flat), &SkFlatData::Compare);
+ if (index >= 0)
+ return matrices[index]->index();
+ index = ~index;
+ *matrices.insert(index) = flat;
+ return fMatrixIndex++;
+}
+
+int SkPictureRecord::find(SkTDArray<const SkFlatPaint* >& paints, const SkPaint* paint) {
+ if (paint == NULL) {
+ return 0;
+ }
+
+ SkFlatPaint* flat = SkFlatPaint::Flatten(&fHeap, *paint, fPaintIndex,
+ &fRCRecorder, &fTFRecorder);
+ int index = SkTSearch<SkFlatData>((const SkFlatData**) paints.begin(),
+ paints.count(), (SkFlatData*) flat, sizeof(flat), &SkFlatData::Compare);
+ if (index >= 0) {
+ return paints[index]->index();
+ }
+
+ index = ~index;
+ *paints.insert(index) = flat;
+ return fPaintIndex++;
+}
+
+int SkPictureRecord::find(SkTDArray<const SkFlatRegion* >& regions, const SkRegion& region) {
+ SkFlatRegion* flat = SkFlatRegion::Flatten(&fHeap, region, fRegionIndex);
+ int index = SkTSearch<SkFlatData>((const SkFlatData**) regions.begin(),
+ regions.count(), (SkFlatData*) flat, sizeof(flat), &SkFlatData::Compare);
+ if (index >= 0)
+ return regions[index]->index();
+ index = ~index;
+ *regions.insert(index) = flat;
+ return fRegionIndex++;
+}
+
+#ifdef SK_DEBUG_DUMP
+void SkPictureRecord::dumpMatrices() {
+ int count = fMatrices.count();
+ SkMatrix defaultMatrix;
+ defaultMatrix.reset();
+ for (int index = 0; index < count; index++) {
+ const SkFlatMatrix* flatMatrix = fMatrices[index];
+ flatMatrix->dump();
+ }
+}
+
+void SkPictureRecord::dumpPaints() {
+ int count = fPaints.count();
+ for (int index = 0; index < count; index++)
+ fPaints[index]->dump();
+}
+#endif
+
+#ifdef SK_DEBUG_SIZE
+size_t SkPictureRecord::size() const {
+ size_t result = 0;
+ size_t sizeData;
+ bitmaps(&sizeData);
+ result += sizeData;
+ matrices(&sizeData);
+ result += sizeData;
+ paints(&sizeData);
+ result += sizeData;
+ paths(&sizeData);
+ result += sizeData;
+ pictures(&sizeData);
+ result += sizeData;
+ regions(&sizeData);
+ result += sizeData;
+ result += streamlen();
+ return result;
+}
+
+int SkPictureRecord::bitmaps(size_t* size) const {
+ size_t result = 0;
+ int count = fBitmaps.count();
+ for (int index = 0; index < count; index++)
+ result += sizeof(fBitmaps[index]) + fBitmaps[index]->size();
+ *size = result;
+ return count;
+}
+
+int SkPictureRecord::matrices(size_t* size) const {
+ int count = fMatrices.count();
+ *size = sizeof(fMatrices[0]) * count;
+ return count;
+}
+
+int SkPictureRecord::paints(size_t* size) const {
+ size_t result = 0;
+ int count = fPaints.count();
+ for (int index = 0; index < count; index++)
+ result += sizeof(fPaints[index]) + fPaints[index]->size();
+ *size = result;
+ return count;
+}
+
+int SkPictureRecord::paths(size_t* size) const {
+ size_t result = 0;
+ int count = fPaths.count();
+ for (int index = 0; index < count; index++)
+ result += sizeof(fPaths[index]) + fPaths[index]->size();
+ *size = result;
+ return count;
+}
+
+int SkPictureRecord::regions(size_t* size) const {
+ size_t result = 0;
+ int count = fRegions.count();
+ for (int index = 0; index < count; index++)
+ result += sizeof(fRegions[index]) + fRegions[index]->size();
+ *size = result;
+ return count;
+}
+
+size_t SkPictureRecord::streamlen() const {
+ return fWriter.size();
+}
+#endif
+
+#ifdef SK_DEBUG_VALIDATE
+void SkPictureRecord::validate() const {
+ validateBitmaps();
+ validateMatrices();
+ validatePaints();
+ validatePaths();
+ validatePictures();
+ validateRegions();
+}
+
+void SkPictureRecord::validateBitmaps() const {
+ int count = fBitmaps.count();
+ SkASSERT((unsigned) count < 0x1000);
+ for (int index = 0; index < count; index++) {
+ const SkFlatBitmap* bitPtr = fBitmaps[index];
+ SkASSERT(bitPtr);
+ bitPtr->validate();
+ }
+}
+
+void SkPictureRecord::validateMatrices() const {
+ int count = fMatrices.count();
+ SkASSERT((unsigned) count < 0x1000);
+ for (int index = 0; index < count; index++) {
+ const SkFlatMatrix* matrix = fMatrices[index];
+ SkASSERT(matrix);
+ matrix->validate();
+ }
+}
+
+void SkPictureRecord::validatePaints() const {
+ int count = fPaints.count();
+ SkASSERT((unsigned) count < 0x1000);
+ for (int index = 0; index < count; index++) {
+ const SkFlatPaint* paint = fPaints[index];
+ SkASSERT(paint);
+// paint->validate();
+ }
+}
+
+void SkPictureRecord::validatePaths() const {
+ int count = fPaths.count();
+ SkASSERT((unsigned) count < 0x1000);
+ for (int index = 0; index < count; index++) {
+ const SkFlatPath* path = fPaths[index];
+ SkASSERT(path);
+ path->validate();
+ }
+}
+
+void SkPictureRecord::validateRegions() const {
+ int count = fRegions.count();
+ SkASSERT((unsigned) count < 0x1000);
+ for (int index = 0; index < count; index++) {
+ const SkFlatRegion* region = fRegions[index];
+ SkASSERT(region);
+ region->validate();
+ }
+}
+#endif
+
diff --git a/src/core/SkPictureRecord.h b/src/core/SkPictureRecord.h
new file mode 100644
index 0000000000..d25d23ae71
--- /dev/null
+++ b/src/core/SkPictureRecord.h
@@ -0,0 +1,178 @@
+#ifndef SkPictureRecord_DEFINED
+#define SkPictureRecord_DEFINED
+
+#include "SkCanvas.h"
+#include "SkFlattenable.h"
+#include "SkPathHeap.h"
+#include "SkPicture.h"
+#include "SkPictureFlat.h"
+#include "SkTemplates.h"
+#include "SkWriter32.h"
+
+class SkPictureRecord : public SkCanvas {
+public:
+ SkPictureRecord();
+ virtual ~SkPictureRecord();
+
+ // overrides from SkCanvas
+ virtual int save(SaveFlags);
+ virtual int saveLayer(const SkRect* bounds, const SkPaint*, SaveFlags);
+ virtual void restore();
+ virtual bool translate(SkScalar dx, SkScalar dy);
+ virtual bool scale(SkScalar sx, SkScalar sy);
+ virtual bool rotate(SkScalar degrees);
+ virtual bool skew(SkScalar sx, SkScalar sy);
+ virtual bool concat(const SkMatrix& matrix);
+ virtual bool clipRect(const SkRect& rect, SkRegion::Op op);
+ virtual bool clipPath(const SkPath& path, SkRegion::Op op);
+ virtual bool clipRegion(const SkRegion& region, SkRegion::Op op);
+ virtual void drawPaint(const SkPaint& paint);
+ virtual void drawPoints(PointMode, size_t count, const SkPoint pts[],
+ const SkPaint&);
+ virtual void drawRect(const SkRect& rect, const SkPaint&);
+ virtual void drawPath(const SkPath& path, const SkPaint&);
+ virtual void drawBitmap(const SkBitmap&, SkScalar left, SkScalar top,
+ const SkPaint*);
+ virtual void drawBitmapRect(const SkBitmap&, const SkIRect* src,
+ const SkRect& dst, const SkPaint*);
+ virtual void drawBitmapMatrix(const SkBitmap&, const SkMatrix&,
+ const SkPaint*);
+ virtual void drawSprite(const SkBitmap&, int left, int top,
+ const SkPaint*);
+ virtual void drawText(const void* text, size_t byteLength, SkScalar x,
+ SkScalar y, const SkPaint&);
+ virtual void drawPosText(const void* text, size_t byteLength,
+ const SkPoint pos[], const SkPaint&);
+ virtual void drawPosTextH(const void* text, size_t byteLength,
+ const SkScalar xpos[], SkScalar constY, const SkPaint&);
+ virtual void drawTextOnPath(const void* text, size_t byteLength,
+ const SkPath& path, const SkMatrix* matrix,
+ const SkPaint&);
+ virtual void drawPicture(SkPicture& picture);
+ virtual void drawVertices(VertexMode, int vertexCount,
+ const SkPoint vertices[], const SkPoint texs[],
+ const SkColor colors[], SkXfermode*,
+ const uint16_t indices[], int indexCount,
+ const SkPaint&);
+
+ void addFontMetricsTopBottom(const SkPaint& paint, SkScalar baselineY);
+
+ const SkTDArray<const SkFlatBitmap* >& getBitmaps() const {
+ return fBitmaps;
+ }
+ const SkTDArray<const SkFlatMatrix* >& getMatrices() const {
+ return fMatrices;
+ }
+ const SkTDArray<const SkFlatPaint* >& getPaints() const {
+ return fPaints;
+ }
+ const SkTDArray<SkPicture* >& getPictureRefs() const {
+ return fPictureRefs;
+ }
+ const SkTDArray<const SkFlatRegion* >& getRegions() const {
+ return fRegions;
+ }
+
+ void reset();
+
+ const SkWriter32& writeStream() const {
+ return fWriter;
+ }
+
+private:
+ SkTDArray<uint32_t> fRestoreOffsetStack;
+
+ void addDraw(DrawType drawType) {
+#ifdef SK_DEBUG_TRACE
+ SkDebugf("add %s\n", DrawTypeToString(drawType));
+#endif
+ fWriter.writeInt(drawType);
+ }
+ void addInt(int value) {
+ fWriter.writeInt(value);
+ }
+ void addScalar(SkScalar scalar) {
+ fWriter.writeScalar(scalar);
+ }
+
+ void addBitmap(const SkBitmap& bitmap);
+ void addMatrix(const SkMatrix& matrix);
+ void addMatrixPtr(const SkMatrix* matrix);
+ void addPaint(const SkPaint& paint);
+ void addPaintPtr(const SkPaint* paint);
+ void addPath(const SkPath& path);
+ void addPicture(SkPicture& picture);
+ void addPoint(const SkPoint& point);
+ void addPoints(const SkPoint pts[], int count);
+ void addRect(const SkRect& rect);
+ void addRectPtr(const SkRect* rect);
+ void addIRectPtr(const SkIRect* rect);
+ void addRegion(const SkRegion& region);
+ void addText(const void* text, size_t byteLength);
+
+ int find(SkTDArray<const SkFlatBitmap* >& bitmaps,
+ const SkBitmap& bitmap);
+ int find(SkTDArray<const SkFlatMatrix* >& matrices,
+ const SkMatrix* matrix);
+ int find(SkTDArray<const SkFlatPaint* >& paints, const SkPaint* paint);
+ int find(SkTDArray<const SkFlatRegion* >& regions, const SkRegion& region);
+
+#ifdef SK_DEBUG_DUMP
+public:
+ void dumpMatrices();
+ void dumpPaints();
+#endif
+
+#ifdef SK_DEBUG_SIZE
+public:
+ size_t size() const;
+ int bitmaps(size_t* size) const;
+ int matrices(size_t* size) const;
+ int paints(size_t* size) const;
+ int paths(size_t* size) const;
+ int regions(size_t* size) const;
+ size_t streamlen() const;
+
+ size_t fPointBytes, fRectBytes, fTextBytes;
+ int fPointWrites, fRectWrites, fTextWrites;
+#endif
+
+#ifdef SK_DEBUG_VALIDATE
+public:
+ void validate() const;
+private:
+ void validateBitmaps() const;
+ void validateMatrices() const;
+ void validatePaints() const;
+ void validatePaths() const;
+ void validateRegions() const;
+#else
+public:
+ void validate() const {}
+#endif
+
+private:
+ SkChunkAlloc fHeap;
+ int fBitmapIndex;
+ SkTDArray<const SkFlatBitmap* > fBitmaps;
+ int fMatrixIndex;
+ SkTDArray<const SkFlatMatrix* > fMatrices;
+ int fPaintIndex;
+ SkTDArray<const SkFlatPaint* > fPaints;
+ int fRegionIndex;
+ SkTDArray<const SkFlatRegion* > fRegions;
+ SkPathHeap* fPathHeap; // reference counted
+ SkWriter32 fWriter;
+
+ // we ref each item in this array
+ SkTDArray<SkPicture*> fPictureRefs;
+
+ SkRefCntRecorder fRCRecorder;
+ SkRefCntRecorder fTFRecorder;
+
+ friend class SkPicturePlayback;
+
+ typedef SkCanvas INHERITED;
+};
+
+#endif
diff --git a/src/core/SkPixelRef.cpp b/src/core/SkPixelRef.cpp
new file mode 100644
index 0000000000..adfc3c02e9
--- /dev/null
+++ b/src/core/SkPixelRef.cpp
@@ -0,0 +1,129 @@
+#include "SkPixelRef.h"
+#include "SkFlattenable.h"
+#include "SkThread.h"
+
+static SkMutex gPixelRefMutex;
+static int32_t gPixelRefGenerationID;
+
+SkPixelRef::SkPixelRef(SkMutex* mutex) {
+ if (NULL == mutex) {
+ mutex = &gPixelRefMutex;
+ }
+ fMutex = mutex;
+ fPixels = NULL;
+ fColorTable = NULL; // we do not track ownership of this
+ fLockCount = 0;
+ fGenerationID = 0; // signal to rebuild
+ fIsImmutable = false;
+}
+
+SkPixelRef::SkPixelRef(SkFlattenableReadBuffer& buffer, SkMutex* mutex) {
+ if (NULL == mutex) {
+ mutex = &gPixelRefMutex;
+ }
+ fMutex = mutex;
+ fPixels = NULL;
+ fColorTable = NULL; // we do not track ownership of this
+ fLockCount = 0;
+ fGenerationID = 0; // signal to rebuild
+ fIsImmutable = buffer.readBool();
+}
+
+void SkPixelRef::flatten(SkFlattenableWriteBuffer& buffer) const {
+ buffer.writeBool(fIsImmutable);
+}
+
+void SkPixelRef::lockPixels() {
+ SkAutoMutexAcquire ac(*fMutex);
+
+ if (1 == ++fLockCount) {
+ fPixels = this->onLockPixels(&fColorTable);
+ }
+}
+
+void SkPixelRef::unlockPixels() {
+ SkAutoMutexAcquire ac(*fMutex);
+
+ SkASSERT(fLockCount > 0);
+ if (0 == --fLockCount) {
+ this->onUnlockPixels();
+ fPixels = NULL;
+ fColorTable = NULL;
+ }
+}
+
+uint32_t SkPixelRef::getGenerationID() const {
+ uint32_t genID = fGenerationID;
+ if (0 == genID) {
+ // do a loop in case our global wraps around, as we never want to
+ // return a 0
+ do {
+ genID = sk_atomic_inc(&gPixelRefGenerationID) + 1;
+ } while (0 == genID);
+ fGenerationID = genID;
+ }
+ return genID;
+}
+
+void SkPixelRef::notifyPixelsChanged() {
+ if (fIsImmutable) {
+ SkDebugf("========== notifyPixelsChanged called on immutable pixelref");
+ sk_throw();
+ }
+ // this signals us to recompute this next time around
+ fGenerationID = 0;
+}
+
+void SkPixelRef::setImmutable() {
+ fIsImmutable = true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define MAX_PAIR_COUNT 16
+
+struct Pair {
+ const char* fName;
+ SkPixelRef::Factory fFactory;
+};
+
+static int gCount;
+static Pair gPairs[MAX_PAIR_COUNT];
+
+void SkPixelRef::Register(const char name[], Factory factory) {
+ SkASSERT(name);
+ SkASSERT(factory);
+
+ static bool gOnce;
+ if (!gOnce) {
+ gCount = 0;
+ gOnce = true;
+ }
+
+ SkASSERT(gCount < MAX_PAIR_COUNT);
+
+ gPairs[gCount].fName = name;
+ gPairs[gCount].fFactory = factory;
+ gCount += 1;
+}
+
+SkPixelRef::Factory SkPixelRef::NameToFactory(const char name[]) {
+ const Pair* pairs = gPairs;
+ for (int i = gCount - 1; i >= 0; --i) {
+ if (strcmp(pairs[i].fName, name) == 0) {
+ return pairs[i].fFactory;
+ }
+ }
+ return NULL;
+}
+
+const char* SkPixelRef::FactoryToName(Factory fact) {
+ const Pair* pairs = gPairs;
+ for (int i = gCount - 1; i >= 0; --i) {
+ if (pairs[i].fFactory == fact) {
+ return pairs[i].fName;
+ }
+ }
+ return NULL;
+}
+
diff --git a/src/core/SkPoint.cpp b/src/core/SkPoint.cpp
new file mode 100644
index 0000000000..704c2baa63
--- /dev/null
+++ b/src/core/SkPoint.cpp
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2006-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkPoint.h"
+
+void SkIPoint::rotateCW(SkIPoint* dst) const {
+ SkASSERT(dst);
+
+ // use a tmp in case this == dst
+ int32_t tmp = fX;
+ dst->fX = -fY;
+ dst->fY = tmp;
+}
+
+void SkIPoint::rotateCCW(SkIPoint* dst) const {
+ SkASSERT(dst);
+
+ // use a tmp in case this == dst
+ int32_t tmp = fX;
+ dst->fX = fY;
+ dst->fY = -tmp;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkPoint::rotateCW(SkPoint* dst) const {
+ SkASSERT(dst);
+
+ // use a tmp in case this == dst
+ SkScalar tmp = fX;
+ dst->fX = -fY;
+ dst->fY = tmp;
+}
+
+void SkPoint::rotateCCW(SkPoint* dst) const {
+ SkASSERT(dst);
+
+ // use a tmp in case this == dst
+ SkScalar tmp = fX;
+ dst->fX = fY;
+ dst->fY = -tmp;
+}
+
+void SkPoint::scale(SkScalar scale, SkPoint* dst) const {
+ SkASSERT(dst);
+ dst->set(SkScalarMul(fX, scale), SkScalarMul(fY, scale));
+}
+
+#define kNearlyZero (SK_Scalar1 / 8092)
+
+bool SkPoint::normalize() {
+ return this->setLength(fX, fY, SK_Scalar1);
+}
+
+bool SkPoint::setNormalize(SkScalar x, SkScalar y) {
+ return this->setLength(x, y, SK_Scalar1);
+}
+
+bool SkPoint::setLength(SkScalar length) {
+ return this->setLength(fX, fY, length);
+}
+
+#ifdef SK_SCALAR_IS_FLOAT
+
+SkScalar SkPoint::Length(SkScalar dx, SkScalar dy) {
+ return sk_float_sqrt(dx * dx + dy * dy);
+}
+
+bool SkPoint::setLength(float x, float y, float length) {
+ float mag = sk_float_sqrt(x * x + y * y);
+ if (mag > kNearlyZero) {
+ length /= mag;
+ fX = x * length;
+ fY = y * length;
+ return true;
+ }
+ return false;
+}
+
+#else
+
+#include "Sk64.h"
+
+SkScalar SkPoint::Length(SkScalar dx, SkScalar dy) {
+ Sk64 tmp1, tmp2;
+
+ tmp1.setMul(dx, dx);
+ tmp2.setMul(dy, dy);
+ tmp1.add(tmp2);
+
+ return tmp1.getSqrt();
+}
+
+#ifdef SK_DEBUGx
+static SkFixed fixlen(SkFixed x, SkFixed y) {
+ float fx = (float)x;
+ float fy = (float)y;
+
+ return (int)floorf(sqrtf(fx*fx + fy*fy) + 0.5f);
+}
+#endif
+
+static inline uint32_t squarefixed(unsigned x) {
+ x >>= 16;
+ return x*x;
+}
+
+#if 1 // Newton iter for setLength
+
+static inline unsigned invsqrt_iter(unsigned V, unsigned U) {
+ unsigned x = V * U >> 14;
+ x = x * U >> 14;
+ x = (3 << 14) - x;
+ x = (U >> 1) * x >> 14;
+ return x;
+}
+
+static const uint16_t gInvSqrt14GuessTable[] = {
+ 0x4000, 0x3c57, 0x393e, 0x3695, 0x3441, 0x3235, 0x3061,
+ 0x2ebd, 0x2d41, 0x2be7, 0x2aaa, 0x2987, 0x287a, 0x2780,
+ 0x2698, 0x25be, 0x24f3, 0x2434, 0x2380, 0x22d6, 0x2235,
+ 0x219d, 0x210c, 0x2083, 0x2000, 0x1f82, 0x1f0b, 0x1e99,
+ 0x1e2b, 0x1dc2, 0x1d5d, 0x1cfc, 0x1c9f, 0x1c45, 0x1bee,
+ 0x1b9b, 0x1b4a, 0x1afc, 0x1ab0, 0x1a67, 0x1a20, 0x19dc,
+ 0x1999, 0x1959, 0x191a, 0x18dd, 0x18a2, 0x1868, 0x1830,
+ 0x17fa, 0x17c4, 0x1791, 0x175e, 0x172d, 0x16fd, 0x16ce
+};
+
+#define BUILD_INVSQRT_TABLEx
+#ifdef BUILD_INVSQRT_TABLE
+static void build_invsqrt14_guess_table() {
+ for (int i = 8; i <= 63; i++) {
+ unsigned x = SkToU16((1 << 28) / SkSqrt32(i << 25));
+ printf("0x%x, ", x);
+ }
+ printf("\n");
+}
+#endif
+
+static unsigned fast_invsqrt(uint32_t x) {
+#ifdef BUILD_INVSQRT_TABLE
+ unsigned top2 = x >> 25;
+ SkASSERT(top2 >= 8 && top2 <= 63);
+
+ static bool gOnce;
+ if (!gOnce) {
+ build_invsqrt14_guess_table();
+ gOnce = true;
+ }
+#endif
+
+ unsigned V = x >> 14; // make V .14
+
+ unsigned top = x >> 25;
+ SkASSERT(top >= 8 && top <= 63);
+ SkASSERT(top - 8 < SK_ARRAY_COUNT(gInvSqrt14GuessTable));
+ unsigned U = gInvSqrt14GuessTable[top - 8];
+
+ U = invsqrt_iter(V, U);
+ return invsqrt_iter(V, U);
+}
+
+/* We "normalize" x,y to be .14 values (so we can square them and stay 32bits.
+ Then we Newton-iterate this in .14 space to compute the invser-sqrt, and
+ scale by it at the end. The .14 space means we can execute our iterations
+ and stay in 32bits as well, making the multiplies much cheaper than calling
+ SkFixedMul.
+*/
+bool SkPoint::setLength(SkFixed ox, SkFixed oy, SkFixed length) {
+ if (ox == 0) {
+ if (oy == 0) {
+ return false;
+ }
+ this->set(0, SkApplySign(length, SkExtractSign(oy)));
+ return true;
+ }
+ if (oy == 0) {
+ this->set(SkApplySign(length, SkExtractSign(ox)), 0);
+ return true;
+ }
+
+ unsigned x = SkAbs32(ox);
+ unsigned y = SkAbs32(oy);
+ int zeros = SkCLZ(x | y);
+
+ // make x,y 1.14 values so our fast sqr won't overflow
+ if (zeros > 17) {
+ x <<= zeros - 17;
+ y <<= zeros - 17;
+ } else {
+ x >>= 17 - zeros;
+ y >>= 17 - zeros;
+ }
+ SkASSERT((x | y) <= 0x7FFF);
+
+ unsigned invrt = fast_invsqrt(x*x + y*y);
+
+ x = x * invrt >> 12;
+ y = y * invrt >> 12;
+
+ if (length != SK_Fixed1) {
+ x = SkFixedMul(x, length);
+ y = SkFixedMul(y, length);
+ }
+ this->set(SkApplySign(x, SkExtractSign(ox)),
+ SkApplySign(y, SkExtractSign(oy)));
+ return true;
+}
+#else
+/*
+ Normalize x,y, and then scale them by length.
+
+ The obvious way to do this would be the following:
+ S64 tmp1, tmp2;
+ tmp1.setMul(x,x);
+ tmp2.setMul(y,y);
+ tmp1.add(tmp2);
+ len = tmp1.getSqrt();
+ x' = SkFixedDiv(x, len);
+ y' = SkFixedDiv(y, len);
+ This is fine, but slower than what we do below.
+
+ The present technique does not compute the starting length, but
+ rather fiddles with x,y iteratively, all the while checking its
+ magnitude^2 (avoiding a sqrt).
+
+ We normalize by first shifting x,y so that at least one of them
+ has bit 31 set (after taking the abs of them).
+ Then we loop, refining x,y by squaring them and comparing
+ against a very large 1.0 (1 << 28), and then adding or subtracting
+ a delta (which itself is reduced by half each time through the loop).
+ For speed we want the squaring to be with a simple integer mul. To keep
+ that from overflowing we shift our coordinates down until we are dealing
+ with at most 15 bits (2^15-1)^2 * 2 says withing 32 bits)
+ When our square is close to 1.0, we shift x,y down into fixed range.
+*/
+bool SkPoint::setLength(SkFixed ox, SkFixed oy, SkFixed length) {
+ if (ox == 0) {
+ if (oy == 0)
+ return false;
+ this->set(0, SkApplySign(length, SkExtractSign(oy)));
+ return true;
+ }
+ if (oy == 0) {
+ this->set(SkApplySign(length, SkExtractSign(ox)), 0);
+ return true;
+ }
+
+ SkFixed x = SkAbs32(ox);
+ SkFixed y = SkAbs32(oy);
+
+ // shift x,y so that the greater of them is 15bits (1.14 fixed point)
+ {
+ int shift = SkCLZ(x | y);
+ // make them .30
+ x <<= shift - 1;
+ y <<= shift - 1;
+ }
+
+ SkFixed dx = x;
+ SkFixed dy = y;
+
+ for (int i = 0; i < 17; i++) {
+ dx >>= 1;
+ dy >>= 1;
+
+ U32 len2 = squarefixed(x) + squarefixed(y);
+ if (len2 >> 28) {
+ x -= dx;
+ y -= dy;
+ } else {
+ x += dx;
+ y += dy;
+ }
+ }
+ x >>= 14;
+ y >>= 14;
+
+#ifdef SK_DEBUGx // measure how far we are from unit-length
+ {
+ static int gMaxError;
+ static int gMaxDiff;
+
+ SkFixed len = fixlen(x, y);
+ int err = len - SK_Fixed1;
+ err = SkAbs32(err);
+
+ if (err > gMaxError) {
+ gMaxError = err;
+ SkDebugf("gMaxError %d\n", err);
+ }
+
+ float fx = SkAbs32(ox)/65536.0f;
+ float fy = SkAbs32(oy)/65536.0f;
+ float mag = sqrtf(fx*fx + fy*fy);
+ fx /= mag;
+ fy /= mag;
+ SkFixed xx = (int)floorf(fx * 65536 + 0.5f);
+ SkFixed yy = (int)floorf(fy * 65536 + 0.5f);
+ err = SkMax32(SkAbs32(xx-x), SkAbs32(yy-y));
+ if (err > gMaxDiff) {
+ gMaxDiff = err;
+ SkDebugf("gMaxDiff %d\n", err);
+ }
+ }
+#endif
+
+ x = SkApplySign(x, SkExtractSign(ox));
+ y = SkApplySign(y, SkExtractSign(oy));
+ if (length != SK_Fixed1) {
+ x = SkFixedMul(x, length);
+ y = SkFixedMul(y, length);
+ }
+
+ this->set(x, y);
+ return true;
+}
+#endif
+
+#endif
+
diff --git a/src/core/SkProcSpriteBlitter.cpp b/src/core/SkProcSpriteBlitter.cpp
new file mode 100644
index 0000000000..f727581e24
--- /dev/null
+++ b/src/core/SkProcSpriteBlitter.cpp
@@ -0,0 +1,55 @@
+/* libs/graphics/sgl/SkProcSpriteBlitter.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#if 0 // experimental
+
+class SkProcSpriteBlitter : public SkSpriteBlitter {
+public:
+ typedef void (*Proc)(void* dst, const void* src, int count, const SkPMColor 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 SkPMColor* ctable = NULL;
+
+ 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;
+ uint8_t fSrcShift, fDstShift;
+};
+
+#endif
diff --git a/src/core/SkPtrRecorder.cpp b/src/core/SkPtrRecorder.cpp
new file mode 100644
index 0000000000..4f774ec6ab
--- /dev/null
+++ b/src/core/SkPtrRecorder.cpp
@@ -0,0 +1,53 @@
+#include "SkPtrRecorder.h"
+#include "SkTSearch.h"
+
+void SkPtrRecorder::reset() {
+ Pair* p = fList.begin();
+ Pair* stop = fList.end();
+ while (p < stop) {
+ this->decPtr(p->fPtr);
+ p += 1;
+ }
+ fList.reset();
+}
+
+int SkPtrRecorder::Cmp(const Pair& a, const Pair& b) {
+ return (char*)a.fPtr - (char*)b.fPtr;
+}
+
+uint32_t SkPtrRecorder::recordPtr(void* ptr) {
+ if (NULL == ptr) {
+ return 0;
+ }
+
+ int count = fList.count();
+ Pair pair;
+ pair.fPtr = ptr;
+
+ int index = SkTSearch<Pair>(fList.begin(), count, pair, sizeof(pair), &Cmp);
+ if (index < 0) {
+ index = ~index; // turn it back into an index for insertion
+ this->incPtr(ptr);
+ pair.fIndex = count + 1;
+ *fList.insert(index) = pair;
+ return count + 1;
+ } else {
+ return fList[index].fIndex;
+ }
+}
+
+void SkPtrRecorder::getPtrs(void* array[]) const {
+ int count = fList.count();
+ if (count > 0) {
+ SkASSERT(array);
+ const Pair* p = fList.begin();
+ // p->fIndex is base-1, so we need to subtract to find its slot
+ for (int i = 0; i < count; i++) {
+ int index = p[i].fIndex - 1;
+ SkASSERT((unsigned)index < (unsigned)count);
+ array[index] = p[i].fPtr;
+ }
+ }
+}
+
+
diff --git a/src/core/SkRasterizer.cpp b/src/core/SkRasterizer.cpp
new file mode 100644
index 0000000000..f1d087df55
--- /dev/null
+++ b/src/core/SkRasterizer.cpp
@@ -0,0 +1,62 @@
+/* libs/graphics/sgl/SkRasterizer.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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(SkFlattenableReadBuffer&) {}
+
+bool SkRasterizer::rasterize(const SkPath& fillPath, const SkMatrix& matrix,
+ const SkIRect* clipBounds, SkMaskFilter* filter,
+ SkMask* mask, SkMask::CreateMode mode)
+{
+ SkIRect storage;
+
+ if (clipBounds && filter && SkMask::kJustRenderImage_CreateMode != mode)
+ {
+ SkIPoint 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 SkIRect* clipBounds,
+ SkMask* mask, SkMask::CreateMode mode)
+{
+ SkPath devPath;
+
+ fillPath.transform(matrix, &devPath);
+ return SkDraw::DrawToMask(devPath, clipBounds, NULL, NULL, mask, mode);
+}
+
diff --git a/src/core/SkRect.cpp b/src/core/SkRect.cpp
new file mode 100644
index 0000000000..d602754c5b
--- /dev/null
+++ b/src/core/SkRect.cpp
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkRect.h"
+
+void SkIRect::join(int32_t left, int32_t top, int32_t right, int32_t bottom)
+{
+ // do nothing if the params are empty
+ if (left >= right || top >= bottom)
+ return;
+
+ // if we are empty, just assign
+ if (fLeft >= fRight || fTop >= fBottom)
+ this->set(left, top, right, bottom);
+ else
+ {
+ if (left < fLeft) fLeft = left;
+ if (top < fTop) fTop = top;
+ if (right > fRight) fRight = right;
+ if (bottom > fBottom) fBottom = bottom;
+ }
+}
+
+void SkIRect::sort()
+{
+ if (fLeft > fRight)
+ SkTSwap<int32_t>(fLeft, fRight);
+ if (fTop > fBottom)
+ SkTSwap<int32_t>(fTop, fBottom);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+void SkRect::sort()
+{
+ if (fLeft > fRight)
+ SkTSwap<SkScalar>(fLeft, fRight);
+ if (fTop > fBottom)
+ SkTSwap<SkScalar>(fTop, fBottom);
+}
+
+void SkRect::toQuad(SkPoint quad[4]) const
+{
+ SkASSERT(quad);
+
+ quad[0].set(fLeft, fTop);
+ quad[1].set(fRight, fTop);
+ quad[2].set(fRight, fBottom);
+ quad[3].set(fLeft, fBottom);
+}
+
+void SkRect::set(const SkPoint pts[], int count)
+{
+ SkASSERT((pts && count > 0) || count == 0);
+
+ if (count <= 0) {
+ bzero(this, sizeof(SkRect));
+ } else {
+#ifdef SK_SCALAR_SLOW_COMPARES
+ int32_t l, t, r, b;
+
+ l = r = SkScalarAs2sCompliment(pts[0].fX);
+ t = b = SkScalarAs2sCompliment(pts[0].fY);
+
+ for (int i = 1; i < count; i++) {
+ int32_t x = SkScalarAs2sCompliment(pts[i].fX);
+ int32_t y = SkScalarAs2sCompliment(pts[i].fY);
+
+ if (x < l) l = x; else if (x > r) r = x;
+ if (y < t) t = y; else if (y > b) b = y;
+ }
+ this->set(Sk2sComplimentAsScalar(l),
+ Sk2sComplimentAsScalar(t),
+ Sk2sComplimentAsScalar(r),
+ Sk2sComplimentAsScalar(b));
+#else
+ SkScalar l, t, r, b;
+
+ l = r = pts[0].fX;
+ t = b = pts[0].fY;
+
+ for (int i = 1; i < count; i++) {
+ SkScalar x = pts[i].fX;
+ SkScalar y = pts[i].fY;
+
+ if (x < l) l = x; else if (x > r) r = x;
+ if (y < t) t = y; else if (y > b) b = y;
+ }
+ this->set(l, t, r, b);
+#endif
+ }
+}
+
+bool SkRect::intersect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom)
+{
+ if (left < right && top < bottom && !this->isEmpty() && // check for empties
+ fLeft < right && left < fRight && fTop < bottom && top < fBottom)
+ {
+ if (fLeft < left) fLeft = left;
+ if (fTop < top) fTop = top;
+ if (fRight > right) fRight = right;
+ if (fBottom > bottom) fBottom = bottom;
+ return true;
+ }
+ return false;
+}
+
+bool SkRect::intersect(const SkRect& r)
+{
+ SkASSERT(&r);
+ return this->intersect(r.fLeft, r.fTop, r.fRight, r.fBottom);
+}
+
+void SkRect::join(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom)
+{
+ // do nothing if the params are empty
+ if (left >= right || top >= bottom)
+ return;
+
+ // if we are empty, just assign
+ if (fLeft >= fRight || fTop >= fBottom)
+ this->set(left, top, right, bottom);
+ else
+ {
+ if (left < fLeft) fLeft = left;
+ if (top < fTop) fTop = top;
+ if (right > fRight) fRight = right;
+ if (bottom > fBottom) fBottom = bottom;
+ }
+}
+
+
diff --git a/src/core/SkRefCnt.cpp b/src/core/SkRefCnt.cpp
new file mode 100644
index 0000000000..fea100524f
--- /dev/null
+++ b/src/core/SkRefCnt.cpp
@@ -0,0 +1,48 @@
+/* libs/graphics/sgl/SkRefCnt.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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 = NULL;
+ return true;
+ }
+ return false;
+}
+
+SkRefCnt* SkAutoUnref::detach() {
+ SkRefCnt* obj = fObj;
+ fObj = NULL;
+ return obj;
+}
+
diff --git a/src/core/SkRegion.cpp b/src/core/SkRegion.cpp
new file mode 100644
index 0000000000..b48f3bbc0b
--- /dev/null
+++ b/src/core/SkRegion.cpp
@@ -0,0 +1,1341 @@
+/* libs/corecg/SkRegion.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkRegionPriv.h"
+#include "SkTemplates.h"
+#include "SkThread.h"
+
+SkDEBUGCODE(int32_t gRgnAllocCounter;)
+
+/////////////////////////////////////////////////////////////////////////////////////////////////
+
+/* Pass in a scanline, beginning with the Left value of the pair (i.e. not the Y beginning)
+*/
+static SkRegion::RunType* skip_scanline(const SkRegion::RunType runs[])
+{
+ while (runs[0] != SkRegion::kRunTypeSentinel)
+ {
+ SkASSERT(runs[0] < runs[1]); // valid span
+ runs += 2;
+ }
+ return (SkRegion::RunType*)(runs + 1); // return past the X-sentinel
+}
+
+static SkRegion::RunType* find_y(const SkRegion::RunType runs[], int y)
+{
+ int top = *runs++;
+ if (top <= y)
+ {
+ for (;;)
+ {
+ int bot = *runs++;
+ if (bot > y)
+ {
+ if (bot == SkRegion::kRunTypeSentinel || *runs == SkRegion::kRunTypeSentinel)
+ break;
+ return (SkRegion::RunType*)runs;
+ }
+ top = bot;
+ runs = skip_scanline(runs);
+ }
+ }
+ return NULL;
+}
+
+// returns true if runs are just a rect
+bool SkRegion::ComputeRunBounds(const SkRegion::RunType runs[], int count, SkIRect* bounds)
+{
+ assert_sentinel(runs[0], false); // top
+
+ if (count == kRectRegionRuns)
+ {
+ assert_sentinel(runs[1], false); // bottom
+ assert_sentinel(runs[2], false); // left
+ assert_sentinel(runs[3], false); // right
+ assert_sentinel(runs[4], true);
+ assert_sentinel(runs[5], true);
+
+ SkASSERT(runs[0] < runs[1]); // valid height
+ SkASSERT(runs[2] < runs[3]); // valid width
+
+ bounds->set(runs[2], runs[0], runs[3], runs[1]);
+ return true;
+ }
+
+ int left = SK_MaxS32;
+ int rite = SK_MinS32;
+ int bot;
+
+ bounds->fTop = *runs++;
+ do {
+ bot = *runs++;
+ if (*runs < SkRegion::kRunTypeSentinel)
+ {
+ if (left > *runs)
+ left = *runs;
+ runs = skip_scanline(runs);
+ if (rite < runs[-2])
+ rite = runs[-2];
+ }
+ else
+ runs += 1; // skip X-sentinel
+ } while (runs[0] < SkRegion::kRunTypeSentinel);
+ bounds->fLeft = left;
+ bounds->fRight = rite;
+ bounds->fBottom = bot;
+ return false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+SkRegion::SkRegion()
+{
+ fBounds.set(0, 0, 0, 0);
+ fRunHead = SkRegion_gEmptyRunHeadPtr;
+}
+
+SkRegion::SkRegion(const SkRegion& src)
+{
+ fRunHead = SkRegion_gEmptyRunHeadPtr; // just need a value that won't trigger sk_free(fRunHead)
+ this->setRegion(src);
+}
+
+SkRegion::SkRegion(const SkIRect& rect)
+{
+ fRunHead = SkRegion_gEmptyRunHeadPtr; // just need a value that won't trigger sk_free(fRunHead)
+ this->setRect(rect);
+}
+
+SkRegion::~SkRegion()
+{
+ this->freeRuns();
+}
+
+void SkRegion::freeRuns()
+{
+ if (fRunHead->isComplex())
+ {
+ SkASSERT(fRunHead->fRefCnt >= 1);
+ if (sk_atomic_dec(&fRunHead->fRefCnt) == 1)
+ {
+ //SkASSERT(gRgnAllocCounter > 0);
+ //SkDEBUGCODE(sk_atomic_dec(&gRgnAllocCounter));
+ //SkDEBUGF(("************** gRgnAllocCounter::free %d\n", gRgnAllocCounter));
+ sk_free(fRunHead);
+ }
+ }
+}
+
+void SkRegion::allocateRuns(int count)
+{
+ fRunHead = RunHead::Alloc(count);
+}
+
+SkRegion& SkRegion::operator=(const SkRegion& src)
+{
+ (void)this->setRegion(src);
+ return *this;
+}
+
+void SkRegion::swap(SkRegion& other)
+{
+ SkTSwap<SkIRect>(fBounds, other.fBounds);
+ SkTSwap<RunHead*>(fRunHead, other.fRunHead);
+}
+
+bool SkRegion::setEmpty()
+{
+ this->freeRuns();
+ fBounds.set(0, 0, 0, 0);
+ fRunHead = SkRegion_gEmptyRunHeadPtr;
+ return false;
+}
+
+bool SkRegion::setRect(int32_t left, int32_t top, int32_t right, int32_t bottom)
+{
+ if (left >= right || top >= bottom)
+ return this->setEmpty();
+
+ this->freeRuns();
+ fBounds.set(left, top, right, bottom);
+ fRunHead = SkRegion_gRectRunHeadPtr;
+ return true;
+}
+
+bool SkRegion::setRect(const SkIRect& r)
+{
+ return this->setRect(r.fLeft, r.fTop, r.fRight, r.fBottom);
+}
+
+bool SkRegion::setRegion(const SkRegion& src)
+{
+ if (this != &src)
+ {
+ this->freeRuns();
+
+ fBounds = src.fBounds;
+ fRunHead = src.fRunHead;
+ if (fRunHead->isComplex())
+ sk_atomic_inc(&fRunHead->fRefCnt);
+ }
+ return fRunHead != SkRegion_gEmptyRunHeadPtr;
+}
+
+bool SkRegion::op(const SkIRect& rect, const SkRegion& rgn, Op op)
+{
+ SkRegion tmp(rect);
+
+ return this->op(tmp, rgn, op);
+}
+
+bool SkRegion::op(const SkRegion& rgn, const SkIRect& rect, Op op)
+{
+ SkRegion tmp(rect);
+
+ return this->op(rgn, tmp, op);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+
+int SkRegion::count_runtype_values(int* itop, int* ibot) const
+{
+ if (this == NULL)
+ {
+ *itop = SK_MinS32;
+ *ibot = SK_MaxS32;
+ return 0;
+ }
+
+ int maxT;
+
+ if (this->isRect())
+ maxT = 2;
+ else
+ {
+ SkASSERT(this->isComplex());
+ // skip the top
+ const RunType* runs = fRunHead->readonly_runs() + 1;
+ maxT = 0;
+
+ do {
+ const RunType* next = skip_scanline(runs + 1);
+ SkASSERT(next > runs);
+ int T = (int)(next - runs - 1);
+ if (maxT < T)
+ maxT = T;
+ runs = next;
+ } while (runs[0] < SkRegion::kRunTypeSentinel);
+ }
+ *itop = fBounds.fTop;
+ *ibot = fBounds.fBottom;
+ return maxT;
+}
+
+bool SkRegion::setRuns(RunType runs[], int count)
+{
+ SkDEBUGCODE(this->validate();)
+ SkASSERT(count > 0);
+
+ if (count <= 2)
+ {
+ // SkDEBUGF(("setRuns: empty\n"));
+ assert_sentinel(runs[count-1], true);
+ return this->setEmpty();
+ }
+
+ // trim off any empty spans from the top and bottom
+ // weird I should need this, perhaps op() could be smarter...
+ if (count > kRectRegionRuns)
+ {
+ RunType* stop = runs + count;
+ assert_sentinel(runs[0], false); // top
+ assert_sentinel(runs[1], false); // bottom
+ if (runs[2] == SkRegion::kRunTypeSentinel) // should be first left...
+ {
+ runs += 2; // skip empty initial span
+ runs[0] = runs[-1]; // set new top to prev bottom
+ assert_sentinel(runs[1], false); // bot: a sentinal would mean two in a row
+ assert_sentinel(runs[2], false); // left
+ assert_sentinel(runs[3], false); // right
+ }
+
+ // now check for a trailing empty span
+ assert_sentinel(stop[-1], true);
+ assert_sentinel(stop[-2], true);
+ assert_sentinel(stop[-3], false); // should be last right
+ if (stop[-4] == SkRegion::kRunTypeSentinel) // eek, stop[-3] was a bottom with no x-runs
+ {
+ stop[-3] = SkRegion::kRunTypeSentinel; // kill empty last span
+ stop -= 2;
+ assert_sentinel(stop[-1], true);
+ assert_sentinel(stop[-2], true);
+ assert_sentinel(stop[-3], false);
+ assert_sentinel(stop[-4], false);
+ assert_sentinel(stop[-5], false);
+ }
+ count = (int)(stop - runs);
+ }
+
+ SkASSERT(count >= kRectRegionRuns);
+
+ if (ComputeRunBounds(runs, count, &fBounds))
+ {
+ // SkDEBUGF(("setRuns: rect[%d %d %d %d]\n", fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom));
+ return this->setRect(fBounds);
+ }
+
+ // if we get here, we need to become a complex region
+
+ if (!fRunHead->isComplex() || fRunHead->fRunCount != count)
+ {
+#ifdef SK_DEBUGx
+ SkDebugf("setRuns: rgn [");
+ {
+ const RunType* r = runs;
+
+ SkDebugf(" top: %d\n", *r++);
+ while (*r < SkRegion::kRunTypeSentinel)
+ {
+ SkDebugf(" bottom: %d", *r++);
+ while (*r < SkRegion::kRunTypeSentinel)
+ {
+ SkDebugf(" [%d %d]", r[0], r[1]);
+ r += 2;
+ }
+ SkDebugf("\n");
+ }
+ }
+#endif
+ this->freeRuns();
+ this->allocateRuns(count);
+ }
+
+ // must call this before we can write directly into runs()
+ // in case we are sharing the buffer with another region (copy on write)
+ fRunHead = fRunHead->ensureWritable();
+ memcpy(fRunHead->writable_runs(), runs, count * sizeof(RunType));
+
+ SkDEBUGCODE(this->validate();)
+
+ return true;
+}
+
+void SkRegion::BuildRectRuns(const SkIRect& bounds,
+ RunType runs[kRectRegionRuns])
+{
+ runs[0] = bounds.fTop;
+ runs[1] = bounds.fBottom;
+ runs[2] = bounds.fLeft;
+ runs[3] = bounds.fRight;
+ runs[4] = kRunTypeSentinel;
+ runs[5] = kRunTypeSentinel;
+}
+
+static SkRegion::RunType* find_scanline(const SkRegion::RunType runs[], int y)
+{
+ SkASSERT(y >= runs[0]); // if this fails, we didn't do a quick check on the boudns
+
+ runs += 1; // skip top-Y
+ for (;;)
+ {
+ if (runs[0] == SkRegion::kRunTypeSentinel)
+ break;
+ if (y < runs[0])
+ return (SkRegion::RunType*)&runs[1];
+ runs = skip_scanline(runs + 1); // skip the Y value before calling
+ }
+ return NULL;
+}
+
+bool SkRegion::contains(int x, int y) const
+{
+ if (!fBounds.contains(x, y))
+ return false;
+
+ if (this->isRect())
+ return true;
+
+ SkASSERT(this->isComplex());
+ const RunType* runs = find_scanline(fRunHead->readonly_runs(), y);
+
+ if (runs)
+ { for (;;)
+ { if (x < runs[0])
+ break;
+ if (x < runs[1])
+ return true;
+ runs += 2;
+ }
+ }
+ return false;
+}
+
+bool SkRegion::contains(const SkIRect& r) const
+{
+ SkRegion tmp(r);
+
+ return this->contains(tmp);
+}
+
+bool SkRegion::contains(const SkRegion& rgn) const
+{
+ if (this->isEmpty() || rgn.isEmpty() || !fBounds.contains(rgn.fBounds))
+ return false;
+
+ if (this->isRect())
+ return true;
+
+ SkRegion tmp;
+
+ tmp.op(*this, rgn, kUnion_Op);
+ return tmp == *this;
+}
+
+const SkRegion::RunType* SkRegion::getRuns(RunType tmpStorage[], int* count) const
+{
+ SkASSERT(tmpStorage && count);
+ const RunType* runs = tmpStorage;
+
+ if (this->isEmpty())
+ {
+ tmpStorage[0] = kRunTypeSentinel;
+ *count = 1;
+ }
+ else if (this->isRect())
+ {
+ BuildRectRuns(fBounds, tmpStorage);
+ *count = kRectRegionRuns;
+ }
+ else
+ {
+ *count = fRunHead->fRunCount;
+ runs = fRunHead->readonly_runs();
+ }
+ return runs;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+bool SkRegion::intersects(const SkIRect& r) const {
+ if (this->isEmpty() || r.isEmpty()) {
+ return false;
+ }
+
+ if (!SkIRect::Intersects(fBounds, r)) {
+ return false;
+ }
+
+ if (this->isRect()) {
+ return true;
+ }
+
+ // we are complex
+ SkRegion tmp;
+ return tmp.op(*this, r, kIntersect_Op);
+}
+
+bool SkRegion::intersects(const SkRegion& rgn) const {
+ if (this->isEmpty() || rgn.isEmpty()) {
+ return false;
+ }
+
+ if (!SkIRect::Intersects(fBounds, rgn.fBounds)) {
+ return false;
+ }
+
+ if (this->isRect() && rgn.isRect()) {
+ return true;
+ }
+
+ // one or both of us is complex
+ // TODO: write a faster version that aborts as soon as we write the first
+ // non-empty span, to avoid build the entire result
+ SkRegion tmp;
+ return tmp.op(*this, rgn, kIntersect_Op);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+int operator==(const SkRegion& a, const SkRegion& b)
+{
+ SkDEBUGCODE(a.validate();)
+ SkDEBUGCODE(b.validate();)
+
+ if (&a == &b)
+ return true;
+ if (a.fBounds != b.fBounds)
+ return false;
+
+ const SkRegion::RunHead* ah = a.fRunHead;
+ const SkRegion::RunHead* bh = b.fRunHead;
+
+ // this catches empties and rects being equal
+ if (ah == bh)
+ return true;
+
+ // now we insist that both are complex (but different ptrs)
+ if (!ah->isComplex() || !bh->isComplex())
+ return false;
+
+ return ah->fRunCount == bh->fRunCount &&
+ !memcmp(ah->readonly_runs(), bh->readonly_runs(),
+ ah->fRunCount * sizeof(SkRegion::RunType));
+}
+
+void SkRegion::translate(int dx, int dy, SkRegion* dst) const
+{
+ SkDEBUGCODE(this->validate();)
+
+ if (NULL == dst)
+ return;
+
+ if (this->isEmpty())
+ dst->setEmpty();
+ else if (this->isRect())
+ dst->setRect(fBounds.fLeft + dx, fBounds.fTop + dy,
+ fBounds.fRight + dx, fBounds.fBottom + dy);
+ else
+ {
+ if (this == dst)
+ {
+ dst->fRunHead = dst->fRunHead->ensureWritable();
+ }
+ else
+ {
+ SkRegion tmp;
+ tmp.allocateRuns(fRunHead->fRunCount);
+ tmp.fBounds = fBounds;
+ dst->swap(tmp);
+ }
+
+ dst->fBounds.offset(dx, dy);
+
+ const RunType* sruns = fRunHead->readonly_runs();
+ RunType* druns = dst->fRunHead->writable_runs();
+
+ *druns++ = (SkRegion::RunType)(*sruns++ + dy); // top
+ for (;;)
+ {
+ int bottom = *sruns++;
+ if (bottom == kRunTypeSentinel)
+ break;
+ *druns++ = (SkRegion::RunType)(bottom + dy); // bottom;
+ for (;;)
+ {
+ int x = *sruns++;
+ if (x == kRunTypeSentinel)
+ break;
+ *druns++ = (SkRegion::RunType)(x + dx);
+ *druns++ = (SkRegion::RunType)(*sruns++ + dx);
+ }
+ *druns++ = kRunTypeSentinel; // x sentinel
+ }
+ *druns++ = kRunTypeSentinel; // y sentinel
+
+ SkASSERT(sruns - fRunHead->readonly_runs() == fRunHead->fRunCount);
+ SkASSERT(druns - dst->fRunHead->readonly_runs() == dst->fRunHead->fRunCount);
+ }
+
+ SkDEBUGCODE(this->validate();)
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+#if defined _WIN32 && _MSC_VER >= 1300 // disable warning : local variable used without having been initialized
+#pragma warning ( push )
+#pragma warning ( disable : 4701 )
+#endif
+
+#ifdef SK_DEBUG
+static void assert_valid_pair(int left, int rite)
+{
+ SkASSERT(left == SkRegion::kRunTypeSentinel || left < rite);
+}
+#else
+ #define assert_valid_pair(left, rite)
+#endif
+
+struct spanRec {
+ const SkRegion::RunType* fA_runs;
+ const SkRegion::RunType* fB_runs;
+ int fA_left, fA_rite, fB_left, fB_rite;
+ int fLeft, fRite, fInside;
+
+ void init(const SkRegion::RunType a_runs[], const SkRegion::RunType b_runs[])
+ {
+ fA_left = *a_runs++;
+ fA_rite = *a_runs++;
+ fB_left = *b_runs++;
+ fB_rite = *b_runs++;
+
+ fA_runs = a_runs;
+ fB_runs = b_runs;
+ }
+
+ bool done() const
+ {
+ SkASSERT(fA_left <= SkRegion::kRunTypeSentinel);
+ SkASSERT(fB_left <= SkRegion::kRunTypeSentinel);
+ return fA_left == SkRegion::kRunTypeSentinel && fB_left == SkRegion::kRunTypeSentinel;
+ }
+
+ void next()
+ {
+ assert_valid_pair(fA_left, fA_rite);
+ assert_valid_pair(fB_left, fB_rite);
+
+ int inside, left, rite SK_INIT_TO_AVOID_WARNING;
+ bool a_flush = false;
+ bool b_flush = false;
+
+ int a_left = fA_left;
+ int a_rite = fA_rite;
+ int b_left = fB_left;
+ int b_rite = fB_rite;
+
+ if (a_left < b_left)
+ {
+ inside = 1;
+ left = a_left;
+ if (a_rite <= b_left) // [...] <...>
+ {
+ rite = a_rite;
+ a_flush = true;
+ }
+ else // [...<..]...> or [...<...>...]
+ rite = a_left = b_left;
+ }
+ else if (b_left < a_left)
+ {
+ inside = 2;
+ left = b_left;
+ if (b_rite <= a_left) // [...] <...>
+ {
+ rite = b_rite;
+ b_flush = true;
+ }
+ else // [...<..]...> or [...<...>...]
+ rite = b_left = a_left;
+ }
+ else // a_left == b_left
+ {
+ inside = 3;
+ left = a_left; // or b_left
+ if (a_rite <= b_rite)
+ {
+ rite = b_left = a_rite;
+ a_flush = true;
+ }
+ if (b_rite <= a_rite)
+ {
+ rite = a_left = b_rite;
+ b_flush = true;
+ }
+ }
+
+ if (a_flush)
+ {
+ a_left = *fA_runs++;
+ a_rite = *fA_runs++;
+ }
+ if (b_flush)
+ {
+ b_left = *fB_runs++;
+ b_rite = *fB_runs++;
+ }
+
+ SkASSERT(left <= rite);
+
+ // now update our state
+ fA_left = a_left;
+ fA_rite = a_rite;
+ fB_left = b_left;
+ fB_rite = b_rite;
+
+ fLeft = left;
+ fRite = rite;
+ fInside = inside;
+ }
+};
+
+static SkRegion::RunType* operate_on_span(const SkRegion::RunType a_runs[],
+ const SkRegion::RunType b_runs[],
+ SkRegion::RunType dst[],
+ int min, int max)
+{
+ spanRec rec;
+ bool firstInterval = true;
+
+ rec.init(a_runs, b_runs);
+
+ while (!rec.done())
+ {
+ rec.next();
+
+ int left = rec.fLeft;
+ int rite = rec.fRite;
+
+ // add left,rite to our dst buffer (checking for coincidence
+ if ((unsigned)(rec.fInside - min) <= (unsigned)(max - min) &&
+ left < rite) // skip if equal
+ {
+ if (firstInterval || dst[-1] < left)
+ {
+ *dst++ = (SkRegion::RunType)(left);
+ *dst++ = (SkRegion::RunType)(rite);
+ firstInterval = false;
+ }
+ else // update the right edge
+ dst[-1] = (SkRegion::RunType)(rite);
+ }
+ }
+
+ *dst++ = SkRegion::kRunTypeSentinel;
+ return dst;
+}
+
+#if defined _WIN32 && _MSC_VER >= 1300
+#pragma warning ( pop )
+#endif
+
+static const struct {
+ uint8_t fMin;
+ uint8_t fMax;
+} gOpMinMax[] = {
+ { 1, 1 }, // Difference
+ { 3, 3 }, // Intersection
+ { 1, 3 }, // Union
+ { 1, 2 } // XOR
+};
+
+class RgnOper {
+public:
+ RgnOper(int top, SkRegion::RunType dst[], SkRegion::Op op)
+ {
+ // need to ensure that the op enum lines up with our minmax array
+ SkASSERT(SkRegion::kDifference_Op == 0);
+ SkASSERT(SkRegion::kIntersect_Op == 1);
+ SkASSERT(SkRegion::kUnion_Op == 2);
+ SkASSERT(SkRegion::kXOR_Op == 3);
+ SkASSERT((unsigned)op <= 3);
+
+ fStartDst = dst;
+ fPrevDst = dst + 1;
+ fPrevLen = 0; // will never match a length from operate_on_span
+ fTop = (SkRegion::RunType)(top); // just a first guess, we might update this
+
+ fMin = gOpMinMax[op].fMin;
+ fMax = gOpMinMax[op].fMax;
+ }
+
+ void addSpan(int bottom, const SkRegion::RunType a_runs[], const SkRegion::RunType b_runs[])
+ {
+ SkRegion::RunType* start = fPrevDst + fPrevLen + 1; // skip X values and slot for the next Y
+ SkRegion::RunType* stop = operate_on_span(a_runs, b_runs, start, fMin, fMax);
+ size_t len = stop - start;
+
+ if (fPrevLen == len && !memcmp(fPrevDst, start, len * sizeof(SkRegion::RunType))) // update Y value
+ fPrevDst[-1] = (SkRegion::RunType)(bottom);
+ else // accept the new span
+ {
+ if (len == 1 && fPrevLen == 0) {
+ fTop = (SkRegion::RunType)(bottom); // just update our bottom
+ } else {
+ start[-1] = (SkRegion::RunType)(bottom);
+ fPrevDst = start;
+ fPrevLen = len;
+ }
+ }
+ }
+
+ int flush()
+ {
+ fStartDst[0] = fTop;
+ fPrevDst[fPrevLen] = SkRegion::kRunTypeSentinel;
+ return (int)(fPrevDst - fStartDst + fPrevLen + 1);
+ }
+
+ uint8_t fMin, fMax;
+
+private:
+ SkRegion::RunType* fStartDst;
+ SkRegion::RunType* fPrevDst;
+ size_t fPrevLen;
+ SkRegion::RunType fTop;
+};
+
+static int operate( const SkRegion::RunType a_runs[],
+ const SkRegion::RunType b_runs[],
+ SkRegion::RunType dst[],
+ SkRegion::Op op)
+{
+ const SkRegion::RunType sentinel = SkRegion::kRunTypeSentinel;
+
+ int a_top = *a_runs++;
+ int a_bot = *a_runs++;
+ int b_top = *b_runs++;
+ int b_bot = *b_runs++;
+
+ assert_sentinel(a_top, false);
+ assert_sentinel(a_bot, false);
+ assert_sentinel(b_top, false);
+ assert_sentinel(b_bot, false);
+
+ RgnOper oper(SkMin32(a_top, b_top), dst, op);
+
+ bool firstInterval = true;
+ int prevBot = SkRegion::kRunTypeSentinel; // so we fail the first test
+
+ while (a_bot < SkRegion::kRunTypeSentinel || b_bot < SkRegion::kRunTypeSentinel)
+ {
+ int top, bot SK_INIT_TO_AVOID_WARNING;
+ const SkRegion::RunType* run0 = &sentinel;
+ const SkRegion::RunType* run1 = &sentinel;
+ bool a_flush = false;
+ bool b_flush = false;
+ int inside;
+
+ if (a_top < b_top)
+ {
+ inside = 1;
+ top = a_top;
+ run0 = a_runs;
+ if (a_bot <= b_top) // [...] <...>
+ {
+ bot = a_bot;
+ a_flush = true;
+ }
+ else // [...<..]...> or [...<...>...]
+ bot = a_top = b_top;
+ }
+ else if (b_top < a_top)
+ {
+ inside = 2;
+ top = b_top;
+ run1 = b_runs;
+ if (b_bot <= a_top) // [...] <...>
+ {
+ bot = b_bot;
+ b_flush = true;
+ }
+ else // [...<..]...> or [...<...>...]
+ bot = b_top = a_top;
+ }
+ else // a_top == b_top
+ {
+ inside = 3;
+ top = a_top; // or b_top
+ run0 = a_runs;
+ run1 = b_runs;
+ if (a_bot <= b_bot)
+ {
+ bot = b_top = a_bot;
+ a_flush = true;
+ }
+ if (b_bot <= a_bot)
+ {
+ bot = a_top = b_bot;
+ b_flush = true;
+ }
+ }
+
+ if (top > prevBot)
+ oper.addSpan(top, &sentinel, &sentinel);
+
+// if ((unsigned)(inside - oper.fMin) <= (unsigned)(oper.fMax - oper.fMin))
+ {
+ oper.addSpan(bot, run0, run1);
+ firstInterval = false;
+ }
+
+ if (a_flush)
+ {
+ a_runs = skip_scanline(a_runs);
+ a_top = a_bot;
+ a_bot = *a_runs++;
+ if (a_bot == SkRegion::kRunTypeSentinel)
+ a_top = a_bot;
+ }
+ if (b_flush)
+ {
+ b_runs = skip_scanline(b_runs);
+ b_top = b_bot;
+ b_bot = *b_runs++;
+ if (b_bot == SkRegion::kRunTypeSentinel)
+ b_top = b_bot;
+ }
+
+ prevBot = bot;
+ }
+ return oper.flush();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/* Given count RunTypes in a complex region, return the worst case number of
+ logical intervals that represents (i.e. number of rects that would be
+ returned from the iterator).
+
+ We could just return count/2, since there must be at least 2 values per
+ interval, but we can first trim off the const overhead of the initial TOP
+ value, plus the final BOTTOM + 2 sentinels.
+ */
+static int count_to_intervals(int count) {
+ SkASSERT(count >= 6); // a single rect is 6 values
+ return (count - 4) >> 1;
+}
+
+/* Given a number of intervals, what is the worst case representation of that
+ many intervals?
+
+ Worst case (from a storage perspective), is a vertical stack of single
+ intervals: TOP + N * (BOTTOM LEFT RIGHT SENTINEL) + SENTINEL
+ */
+static int intervals_to_count(int intervals) {
+ return 1 + intervals * 4 + 1;
+}
+
+/* Given the counts of RunTypes in two regions, return the worst-case number
+ of RunTypes need to store the result after a region-op.
+ */
+static int compute_worst_case_count(int a_count, int b_count) {
+ int a_intervals = count_to_intervals(a_count);
+ int b_intervals = count_to_intervals(b_count);
+ // Our heuristic worst case is ai * (bi + 1) + bi * (ai + 1)
+ int intervals = 2 * a_intervals * b_intervals + a_intervals + b_intervals;
+ // convert back to number of RunType values
+ return intervals_to_count(intervals);
+}
+
+bool SkRegion::op(const SkRegion& rgnaOrig, const SkRegion& rgnbOrig, Op op)
+{
+ SkDEBUGCODE(this->validate();)
+
+ SkASSERT((unsigned)op < kOpCount);
+
+ if (kReplace_Op == op)
+ return this->set(rgnbOrig);
+
+ // swith to using pointers, so we can swap them as needed
+ const SkRegion* rgna = &rgnaOrig;
+ const SkRegion* rgnb = &rgnbOrig;
+ // after this point, do not refer to rgnaOrig or rgnbOrig!!!
+
+ // collaps difference and reverse-difference into just difference
+ if (kReverseDifference_Op == op)
+ {
+ SkTSwap<const SkRegion*>(rgna, rgnb);
+ op = kDifference_Op;
+ }
+
+ SkIRect bounds;
+ bool a_empty = rgna->isEmpty();
+ bool b_empty = rgnb->isEmpty();
+ bool a_rect = rgna->isRect();
+ bool b_rect = rgnb->isRect();
+
+ switch (op) {
+ case kDifference_Op:
+ if (a_empty)
+ return this->setEmpty();
+ if (b_empty || !SkIRect::Intersects(rgna->fBounds, rgnb->fBounds))
+ return this->setRegion(*rgna);
+ break;
+
+ case kIntersect_Op:
+ if ((a_empty | b_empty)
+ || !bounds.intersect(rgna->fBounds, rgnb->fBounds))
+ return this->setEmpty();
+ if (a_rect & b_rect)
+ return this->setRect(bounds);
+ break;
+
+ case kUnion_Op:
+ if (a_empty)
+ return this->setRegion(*rgnb);
+ if (b_empty)
+ return this->setRegion(*rgna);
+ if (a_rect && rgna->fBounds.contains(rgnb->fBounds))
+ return this->setRegion(*rgna);
+ if (b_rect && rgnb->fBounds.contains(rgna->fBounds))
+ return this->setRegion(*rgnb);
+ break;
+
+ case kXOR_Op:
+ if (a_empty)
+ return this->setRegion(*rgnb);
+ if (b_empty)
+ return this->setRegion(*rgna);
+ break;
+ default:
+ SkASSERT(!"unknown region op");
+ return !this->isEmpty();
+ }
+
+ RunType tmpA[kRectRegionRuns];
+ RunType tmpB[kRectRegionRuns];
+
+ int a_count, b_count;
+ const RunType* a_runs = rgna->getRuns(tmpA, &a_count);
+ const RunType* b_runs = rgnb->getRuns(tmpB, &b_count);
+
+ int dstCount = compute_worst_case_count(a_count, b_count);
+ SkAutoSTMalloc<32, RunType> array(dstCount);
+
+ int count = operate(a_runs, b_runs, array.get(), op);
+ SkASSERT(count <= dstCount);
+ return this->setRuns(array.get(), count);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkBuffer.h"
+
+uint32_t SkRegion::flatten(void* storage) const {
+ if (NULL == storage) {
+ uint32_t size = sizeof(int32_t); // -1 (empty), 0 (rect), runCount
+ if (!this->isEmpty()) {
+ size += sizeof(fBounds);
+ if (this->isComplex()) {
+ size += fRunHead->fRunCount * sizeof(RunType);
+ }
+ }
+ return size;
+ }
+
+ SkWBuffer buffer(storage);
+
+ if (this->isEmpty()) {
+ buffer.write32(-1);
+ } else {
+ bool isRect = this->isRect();
+
+ buffer.write32(isRect ? 0 : fRunHead->fRunCount);
+ buffer.write(&fBounds, sizeof(fBounds));
+
+ if (!isRect) {
+ buffer.write(fRunHead->readonly_runs(),
+ fRunHead->fRunCount * sizeof(RunType));
+ }
+ }
+ return buffer.pos();
+}
+
+uint32_t SkRegion::unflatten(const void* storage) {
+ SkRBuffer buffer(storage);
+ SkRegion tmp;
+ int32_t count;
+
+ count = buffer.readS32();
+ if (count >= 0) {
+ buffer.read(&tmp.fBounds, sizeof(tmp.fBounds));
+ if (count == 0) {
+ tmp.fRunHead = SkRegion_gRectRunHeadPtr;
+ } else {
+ tmp.allocateRuns(count);
+ buffer.read(tmp.fRunHead->writable_runs(), count * sizeof(RunType));
+ }
+ }
+ this->swap(tmp);
+ return buffer.pos();
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+static const SkRegion::RunType* validate_line(const SkRegion::RunType run[], const SkIRect& bounds)
+{
+ // *run is the bottom of the current span
+ SkASSERT(*run > bounds.fTop);
+ SkASSERT(*run <= bounds.fBottom);
+ run += 1;
+
+ // check for empty span
+ if (*run != SkRegion::kRunTypeSentinel)
+ {
+ int prevRite = bounds.fLeft - 1;
+ do {
+ int left = *run++;
+ int rite = *run++;
+ SkASSERT(left < rite);
+ SkASSERT(left > prevRite);
+ SkASSERT(rite <= bounds.fRight);
+ prevRite = rite;
+ } while (*run < SkRegion::kRunTypeSentinel);
+ }
+ return run + 1; // skip sentinel
+}
+
+void SkRegion::validate() const
+{
+ if (this->isEmpty())
+ {
+ // check for explicit empty (the zero rect), so we can compare rects to know when
+ // two regions are equal (i.e. emptyRectA == emptyRectB)
+ // this is stricter than just asserting fBounds.isEmpty()
+ SkASSERT(fBounds.fLeft == 0 && fBounds.fTop == 0 && fBounds.fRight == 0 && fBounds.fBottom == 0);
+ }
+ else
+ {
+ SkASSERT(!fBounds.isEmpty());
+ if (!this->isRect())
+ {
+ SkASSERT(fRunHead->fRefCnt >= 1);
+ SkASSERT(fRunHead->fRunCount >= kRectRegionRuns);
+
+ const RunType* run = fRunHead->readonly_runs();
+ const RunType* stop = run + fRunHead->fRunCount;
+
+ // check that our bounds match our runs
+ {
+ SkIRect bounds;
+ bool isARect = ComputeRunBounds(run, stop - run, &bounds);
+ SkASSERT(!isARect);
+ SkASSERT(bounds == fBounds);
+ }
+
+ SkASSERT(*run == fBounds.fTop);
+ run++;
+ do {
+ run = validate_line(run, fBounds);
+ } while (*run < kRunTypeSentinel);
+ SkASSERT(run + 1 == stop);
+ }
+ }
+}
+
+void SkRegion::dump() const
+{
+ if (this->isEmpty())
+ SkDebugf(" rgn: empty\n");
+ else
+ {
+ SkDebugf(" rgn: [%d %d %d %d]", fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
+ if (this->isComplex())
+ {
+ const RunType* runs = fRunHead->readonly_runs();
+ for (int i = 0; i < fRunHead->fRunCount; i++)
+ SkDebugf(" %d", runs[i]);
+ }
+ SkDebugf("\n");
+ }
+}
+
+#endif
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+SkRegion::Iterator::Iterator(const SkRegion& rgn) {
+ this->reset(rgn);
+}
+
+bool SkRegion::Iterator::rewind() {
+ if (fRgn) {
+ this->reset(*fRgn);
+ return true;
+ }
+ return false;
+}
+
+void SkRegion::Iterator::reset(const SkRegion& rgn) {
+ fRgn = &rgn;
+ if (rgn.isEmpty()) {
+ fDone = true;
+ } else {
+ fDone = false;
+ if (rgn.isRect()) {
+ fRect = rgn.fBounds;
+ fRuns = NULL;
+ } else {
+ fRuns = rgn.fRunHead->readonly_runs();
+ fRect.set(fRuns[2], fRuns[0], fRuns[3], fRuns[1]);
+ fRuns += 4;
+ }
+ }
+}
+
+void SkRegion::Iterator::next() {
+ if (fDone) {
+ return;
+ }
+
+ if (fRuns == NULL) { // rect case
+ fDone = true;
+ return;
+ }
+
+ const RunType* runs = fRuns;
+
+ if (runs[0] < kRunTypeSentinel) { // valid X value
+ fRect.fLeft = runs[0];
+ fRect.fRight = runs[1];
+ runs += 2;
+ } else { // we're at the end of a line
+ runs += 1;
+ if (runs[0] < kRunTypeSentinel) { // valid Y value
+ if (runs[1] == kRunTypeSentinel) { // empty line
+ fRect.fTop = runs[0];
+ runs += 2;
+ } else {
+ fRect.fTop = fRect.fBottom;
+ }
+
+ fRect.fBottom = runs[0];
+ assert_sentinel(runs[1], false);
+ fRect.fLeft = runs[1];
+ fRect.fRight = runs[2];
+ runs += 3;
+ } else { // end of rgn
+ fDone = true;
+ }
+ }
+ fRuns = runs;
+}
+
+SkRegion::Cliperator::Cliperator(const SkRegion& rgn, const SkIRect& clip)
+ : fIter(rgn), fClip(clip), fDone(true) {
+ const SkIRect& r = fIter.rect();
+
+ while (!fIter.done()) {
+ if (r.fTop >= clip.fBottom) {
+ break;
+ }
+ if (fRect.intersect(clip, r)) {
+ fDone = false;
+ break;
+ }
+ fIter.next();
+ }
+}
+
+void SkRegion::Cliperator::next() {
+ if (fDone) {
+ return;
+ }
+
+ const SkIRect& r = fIter.rect();
+
+ fDone = true;
+ fIter.next();
+ while (!fIter.done()) {
+ if (r.fTop >= fClip.fBottom) {
+ break;
+ }
+ if (fRect.intersect(fClip, r)) {
+ fDone = false;
+ break;
+ }
+ fIter.next();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////
+
+SkRegion::Spanerator::Spanerator(const SkRegion& rgn, int y, int left, int right)
+{
+ SkDEBUGCODE(rgn.validate();)
+
+ const SkIRect& r = rgn.getBounds();
+
+ fDone = true;
+ if (!rgn.isEmpty() && y >= r.fTop && y < r.fBottom && right > r.fLeft && left < r.fRight)
+ {
+ if (rgn.isRect())
+ {
+ if (left < r.fLeft)
+ left = r.fLeft;
+ if (right > r.fRight)
+ right = r.fRight;
+
+ fLeft = left;
+ fRight = right;
+ fRuns = NULL; // means we're a rect, not a rgn
+ fDone = false;
+ }
+ else
+ {
+ const SkRegion::RunType* runs = find_y(rgn.fRunHead->readonly_runs(), y);
+ if (runs)
+ {
+ for (;;)
+ {
+ if (runs[0] >= right) // runs[0..1] is to the right of the span, so we're done
+ break;
+ if (runs[1] <= left) // runs[0..1] is to the left of the span, so continue
+ {
+ runs += 2;
+ continue;
+ }
+ // runs[0..1] intersects the span
+ fRuns = runs;
+ fLeft = left;
+ fRight = right;
+ fDone = false;
+ break;
+ }
+ }
+ }
+ }
+}
+
+bool SkRegion::Spanerator::next(int* left, int* right)
+{
+ if (fDone) return false;
+
+ if (fRuns == NULL) // we're a rect
+ {
+ fDone = true; // ok, now we're done
+ if (left) *left = fLeft;
+ if (right) *right = fRight;
+ return true; // this interval is legal
+ }
+
+ const SkRegion::RunType* runs = fRuns;
+
+ if (runs[0] >= fRight)
+ {
+ fDone = true;
+ return false;
+ }
+
+ SkASSERT(runs[1] > fLeft);
+
+ if (left)
+ *left = SkMax32(fLeft, runs[0]);
+ if (right)
+ *right = SkMin32(fRight, runs[1]);
+ fRuns = runs + 2;
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+bool SkRegion::debugSetRuns(const RunType runs[], int count) {
+ // we need to make a copy, since the real method may modify the array, and
+ // so it cannot be const.
+
+ SkAutoTArray<RunType> storage(count);
+ memcpy(storage.get(), runs, count * sizeof(RunType));
+ return this->setRuns(storage.get(), count);
+}
+
+#endif
+
+
diff --git a/src/core/SkRegionPriv.h b/src/core/SkRegionPriv.h
new file mode 100644
index 0000000000..70f882835e
--- /dev/null
+++ b/src/core/SkRegionPriv.h
@@ -0,0 +1,89 @@
+/* libs/corecg/SkRegionPriv.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef SkRegionPriv_DEFINED
+#define SkRegionPriv_DEFINED
+
+#include "SkRegion.h"
+#include "SkThread.h"
+
+#define assert_sentinel(value, isSentinel) \
+ SkASSERT(((value) == SkRegion::kRunTypeSentinel) == isSentinel)
+
+//SkDEBUGCODE(extern int32_t gRgnAllocCounter;)
+
+struct SkRegion::RunHead {
+ int32_t fRefCnt;
+ int32_t fRunCount;
+
+ static RunHead* Alloc(int count)
+ {
+ //SkDEBUGCODE(sk_atomic_inc(&gRgnAllocCounter);)
+ //SkDEBUGF(("************** gRgnAllocCounter::alloc %d\n", gRgnAllocCounter));
+
+ SkASSERT(count >= SkRegion::kRectRegionRuns);
+
+ RunHead* head = (RunHead*)sk_malloc_throw(sizeof(RunHead) + count * sizeof(RunType));
+ head->fRefCnt = 1;
+ head->fRunCount = count;
+ return head;
+ }
+
+ bool isComplex() const
+ {
+ return this != SkRegion_gEmptyRunHeadPtr && this != SkRegion_gRectRunHeadPtr;
+ }
+
+ SkRegion::RunType* writable_runs()
+ {
+ SkASSERT(this->isComplex());
+ SkASSERT(fRefCnt == 1);
+ return (SkRegion::RunType*)(this + 1);
+ }
+ const SkRegion::RunType* readonly_runs() const
+ {
+ SkASSERT(this->isComplex());
+ return (const SkRegion::RunType*)(this + 1);
+ }
+
+ RunHead* ensureWritable()
+ {
+ SkASSERT(this->isComplex());
+
+ RunHead* writable = this;
+ if (fRefCnt > 1)
+ {
+ // We need to alloc & copy the current region before we call
+ // sk_atomic_dec because it could be freed in the meantime,
+ // otherwise.
+ writable = Alloc(fRunCount);
+ memcpy(writable->writable_runs(), this->readonly_runs(),
+ fRunCount * sizeof(RunType));
+
+ // fRefCount might have changed since we last checked.
+ // If we own the last reference at this point, we need to
+ // free the memory.
+ if (sk_atomic_dec(&fRefCnt) == 1)
+ {
+ sk_free(this);
+ }
+ }
+ return writable;
+ }
+};
+
+#endif
diff --git a/src/core/SkRegion_path.cpp b/src/core/SkRegion_path.cpp
new file mode 100644
index 0000000000..d00baf9195
--- /dev/null
+++ b/src/core/SkRegion_path.cpp
@@ -0,0 +1,480 @@
+/* libs/graphics/sgl/SkRegion_path.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkRegionPriv.h"
+#include "SkBlitter.h"
+#include "SkScan.h"
+#include "SkTDArray.h"
+#include "SkPath.h"
+
+class SkRgnBuilder : public SkBlitter {
+public:
+ virtual ~SkRgnBuilder();
+
+ // returns true if it could allocate the working storage needed
+ bool init(int maxHeight, int maxTransitions);
+
+ void done() {
+ if (fCurrScanline != NULL) {
+ fCurrScanline->fXCount = (SkRegion::RunType)((int)(fCurrXPtr - fCurrScanline->firstX()));
+ if (!this->collapsWithPrev()) { // flush the last line
+ fCurrScanline = fCurrScanline->nextScanline();
+ }
+ }
+ }
+
+ int computeRunCount() const;
+ void copyToRect(SkIRect*) const;
+ void copyToRgn(SkRegion::RunType 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 {
+ SkRegion::RunType fLastY;
+ SkRegion::RunType fXCount;
+
+ SkRegion::RunType* firstX() const { return (SkRegion::RunType*)(this + 1); }
+ Scanline* nextScanline() const {
+ return (Scanline*)((SkRegion::RunType*)(this + 1) + fXCount);
+ }
+ };
+ SkRegion::RunType* fStorage;
+ Scanline* fCurrScanline;
+ Scanline* fPrevScanline;
+ // points at next avialable x[] in fCurrScanline
+ SkRegion::RunType* fCurrXPtr;
+ SkRegion::RunType fTop; // first Y value
+
+ int fStorageCount;
+
+ bool collapsWithPrev() {
+ if (fPrevScanline != NULL &&
+ fPrevScanline->fLastY + 1 == fCurrScanline->fLastY &&
+ fPrevScanline->fXCount == fCurrScanline->fXCount &&
+ !memcmp(fPrevScanline->firstX(),
+ fCurrScanline->firstX(),
+ fCurrScanline->fXCount * sizeof(SkRegion::RunType)))
+ {
+ // update the height of fPrevScanline
+ fPrevScanline->fLastY = fCurrScanline->fLastY;
+ return true;
+ }
+ return false;
+ }
+};
+
+SkRgnBuilder::~SkRgnBuilder() {
+ sk_free(fStorage);
+}
+
+bool SkRgnBuilder::init(int maxHeight, int maxTransitions) {
+ if ((maxHeight | maxTransitions) < 0) {
+ return false;
+ }
+
+ Sk64 count, size;
+
+ // compute the count with +1 and +3 slop for the working buffer
+ count.setMul(maxHeight + 1, 3 + maxTransitions);
+ if (!count.is32() || count.isNeg()) {
+ return false;
+ }
+ fStorageCount = count.get32();
+
+ size.setMul(fStorageCount, sizeof(SkRegion::RunType));
+ if (!size.is32() || size.isNeg()) {
+ return false;
+ }
+
+ fStorage = (SkRegion::RunType*)sk_malloc_flags(size.get32(), 0);
+ if (NULL == fStorage) {
+ return false;
+ }
+
+ fCurrScanline = NULL; // signal empty collection
+ fPrevScanline = NULL; // signal first scanline
+ return true;
+}
+
+void SkRgnBuilder::blitH(int x, int y, int width) {
+ if (fCurrScanline == NULL) { // first time
+ fTop = (SkRegion::RunType)(y);
+ fCurrScanline = (Scanline*)fStorage;
+ fCurrScanline->fLastY = (SkRegion::RunType)(y);
+ fCurrXPtr = fCurrScanline->firstX();
+ } else {
+ SkASSERT(y >= fCurrScanline->fLastY);
+
+ if (y > fCurrScanline->fLastY) {
+ // if we get here, we're done with fCurrScanline
+ fCurrScanline->fXCount = (SkRegion::RunType)((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 = (SkRegion::RunType)(y - 1);
+ fCurrScanline->fXCount = 0;
+ fCurrScanline = fCurrScanline->nextScanline();
+ }
+ // setup for the new curr line
+ fCurrScanline->fLastY = (SkRegion::RunType)(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] = (SkRegion::RunType)(x + width);
+ } else {
+ fCurrXPtr[0] = (SkRegion::RunType)(x);
+ fCurrXPtr[1] = (SkRegion::RunType)(x + width);
+ fCurrXPtr += 2;
+ }
+ SkASSERT(fCurrXPtr - fStorage < fStorageCount);
+}
+
+int SkRgnBuilder::computeRunCount() const {
+ if (fCurrScanline == NULL) {
+ return 0;
+ }
+
+ const SkRegion::RunType* line = fStorage;
+ const SkRegion::RunType* stop = (const SkRegion::RunType*)fCurrScanline;
+
+ return 2 + (int)(stop - line);
+}
+
+void SkRgnBuilder::copyToRect(SkIRect* r) const {
+ SkASSERT(fCurrScanline != NULL);
+ SkASSERT((const SkRegion::RunType*)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(SkRegion::RunType runs[]) const {
+ SkASSERT(fCurrScanline != NULL);
+ SkASSERT((const SkRegion::RunType*)fCurrScanline - fStorage > 4);
+
+ const Scanline* line = (const Scanline*)fStorage;
+ const Scanline* stop = fCurrScanline;
+
+ *runs++ = fTop;
+ do {
+ *runs++ = (SkRegion::RunType)(line->fLastY + 1);
+ int count = line->fXCount;
+ if (count) {
+ memcpy(runs, line->firstX(), count * sizeof(SkRegion::RunType));
+ runs += count;
+ }
+ *runs++ = SkRegion::kRunTypeSentinel;
+ line = line->nextScanline();
+ } while (line < stop);
+ SkASSERT(line == stop);
+ *runs = SkRegion::kRunTypeSentinel;
+}
+
+static int count_path_runtype_values(const SkPath& path, int* itop, int* ibot) {
+ static const uint8_t gPathVerbToInitialLastIndex[] = {
+ 0, // kMove_Verb
+ 1, // kLine_Verb
+ 2, // kQuad_Verb
+ 3, // kCubic_Verb
+ 0, // kClose_Verb
+ 0 // kDone_Verb
+ };
+
+ static const uint8_t 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) {
+ maxEdges += gPathVerbToMaxEdges[verb];
+
+ int lastIndex = gPathVerbToInitialLastIndex[verb];
+ if (lastIndex > 0) {
+ for (int i = 1; i <= lastIndex; i++) {
+ if (top > pts[i].fY) {
+ top = pts[i].fY;
+ } else if (bot < pts[i].fY) {
+ bot = pts[i].fY;
+ }
+ }
+ } else if (SkPath::kMove_Verb == verb) {
+ if (top > pts[0].fY) {
+ top = pts[0].fY;
+ } else if (bot < pts[0].fY) {
+ bot = pts[0].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 (clip.isEmpty()) {
+ return this->setEmpty();
+ }
+
+ if (path.isEmpty()) {
+ if (path.isInverseFillType()) {
+ return this->set(clip);
+ } else {
+ 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;
+
+ 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;
+
+ if (!builder.init(bot - top, SkMax32(pathTransitions, clipTransitions))) {
+ // can't allocate working space, so return false
+ return this->setEmpty();
+ }
+
+ 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->writable_runs());
+ ComputeRunBounds(tmp.fRunHead->readonly_runs(), count, &tmp.fBounds);
+ this->swap(tmp);
+ }
+ SkDEBUGCODE(this->validate();)
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////////////
+
+struct Edge {
+ enum {
+ kY0Link = 0x01,
+ kY1Link = 0x02,
+
+ kCompleteLink = (kY0Link | kY1Link)
+ };
+
+ SkRegion::RunType fX;
+ SkRegion::RunType fY0, fY1;
+ uint8_t fFlags;
+ Edge* fNext;
+
+ void set(int x, int y0, int y1) {
+ SkASSERT(y0 != y1);
+
+ fX = (SkRegion::RunType)(x);
+ fY0 = (SkRegion::RunType)(y0);
+ fY1 = (SkRegion::RunType)(y1);
+ fFlags = 0;
+ SkDEBUGCODE(fNext = NULL;)
+ }
+
+ int top() const {
+ return SkFastMin32(fY0, fY1);
+ }
+};
+
+static void find_link(Edge* base, Edge* stop) {
+ SkASSERT(base < stop);
+
+ if (base->fFlags == Edge::kCompleteLink) {
+ SkASSERT(base->fNext);
+ return;
+ }
+
+ SkASSERT(base + 1 < stop);
+
+ int y0 = base->fY0;
+ int y1 = base->fY1;
+
+ Edge* e = base;
+ if ((base->fFlags & Edge::kY0Link) == 0) {
+ for (;;) {
+ e += 1;
+ if ((e->fFlags & Edge::kY1Link) == 0 && y0 == e->fY1) {
+ SkASSERT(NULL == e->fNext);
+ e->fNext = base;
+ e->fFlags = SkToU8(e->fFlags | Edge::kY1Link);
+ break;
+ }
+ }
+ }
+
+ e = base;
+ if ((base->fFlags & Edge::kY1Link) == 0) {
+ for (;;) {
+ e += 1;
+ if ((e->fFlags & Edge::kY0Link) == 0 && y1 == e->fY0) {
+ SkASSERT(NULL == base->fNext);
+ base->fNext = e;
+ e->fFlags = SkToU8(e->fFlags | Edge::kY0Link);
+ break;
+ }
+ }
+ }
+
+ base->fFlags = Edge::kCompleteLink;
+}
+
+static int extract_path(Edge* edge, Edge* stop, SkPath* path) {
+ while (0 == edge->fFlags) {
+ edge++; // skip over "used" edges
+ }
+
+ SkASSERT(edge < stop);
+
+ Edge* base = edge;
+ Edge* prev = edge;
+ edge = edge->fNext;
+ SkASSERT(edge != base);
+
+ int count = 1;
+ path->moveTo(SkIntToScalar(prev->fX), SkIntToScalar(prev->fY0));
+ prev->fFlags = 0;
+ do {
+ if (prev->fX != edge->fX || prev->fY1 != edge->fY0) { // skip collinear
+ path->lineTo(SkIntToScalar(prev->fX), SkIntToScalar(prev->fY1)); // V
+ path->lineTo(SkIntToScalar(edge->fX), SkIntToScalar(edge->fY0)); // H
+ }
+ prev = edge;
+ edge = edge->fNext;
+ count += 1;
+ prev->fFlags = 0;
+ } while (edge != base);
+ path->lineTo(SkIntToScalar(prev->fX), SkIntToScalar(prev->fY1)); // V
+ path->close();
+ return count;
+}
+
+#include "SkTSearch.h"
+
+static int EdgeProc(const Edge* a, const Edge* b) {
+ return (a->fX == b->fX) ? a->top() - b->top() : a->fX - b->fX;
+}
+
+bool SkRegion::getBoundaryPath(SkPath* path) const {
+ if (this->isEmpty()) {
+ return false;
+ }
+
+ const SkIRect& 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<Edge> edges;
+
+ for (const SkIRect& r = iter.rect(); !iter.done(); iter.next()) {
+ Edge* edge = edges.append(2);
+ edge[0].set(r.fLeft, r.fBottom, r.fTop);
+ edge[1].set(r.fRight, r.fTop, r.fBottom);
+ }
+ SkQSort(edges.begin(), edges.count(), sizeof(Edge), (SkQSortCompareProc)EdgeProc);
+
+ int count = edges.count();
+ Edge* start = edges.begin();
+ Edge* stop = start + count;
+ Edge* e;
+
+ for (e = start; e != stop; e++) {
+ find_link(e, stop);
+ }
+
+#ifdef SK_DEBUG
+ for (e = start; e != stop; e++) {
+ SkASSERT(e->fNext != NULL);
+ SkASSERT(e->fFlags == Edge::kCompleteLink);
+ }
+#endif
+
+ path->incReserve(count << 1);
+ do {
+ SkASSERT(count > 1);
+ count -= extract_path(start, stop, path);
+ } while (count > 0);
+
+ return true;
+}
+
diff --git a/src/core/SkScalerContext.cpp b/src/core/SkScalerContext.cpp
new file mode 100644
index 0000000000..854c4de8a3
--- /dev/null
+++ b/src/core/SkScalerContext.cpp
@@ -0,0 +1,541 @@
+/* libs/graphics/sgl/SkScalerContext.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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"
+
+#ifdef SK_DEBUG
+// #define TRACK_MISSING_CHARS
+#endif
+
+#define ComputeBWRowBytes(width) (((unsigned)(width) + 7) >> 3)
+
+static const uint8_t* gBlackGammaTable;
+static const uint8_t* gWhiteGammaTable;
+
+void SkGlyph::toMask(SkMask* mask) const {
+ SkASSERT(mask);
+
+ mask->fImage = (uint8_t*)fImage;
+ mask->fBounds.set(fLeft, fTop, fLeft + fWidth, fTop + fHeight);
+ mask->fRowBytes = this->rowBytes();
+ mask->fFormat = fMaskFormat;
+}
+
+size_t SkGlyph::computeImageSize() const {
+ size_t size = this->rowBytes() * 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) {
+ SkFlattenableReadBuffer buffer(data, len);
+ obj = buffer.readFlattenable();
+ SkASSERT(buffer.offset() == buffer.size());
+ }
+ return obj;
+}
+
+SkScalerContext::SkScalerContext(const SkDescriptor* desc)
+ : fPathEffect(NULL), fMaskFilter(NULL)
+{
+ static bool gHaveGammaTables;
+ if (!gHaveGammaTables) {
+ const uint8_t* tables[2];
+ SkFontHost::GetGammaTables(tables);
+ gBlackGammaTable = tables[0];
+ gWhiteGammaTable = tables[1];
+ gHaveGammaTables = true;
+ }
+
+ fBaseGlyphCount = 0;
+ fAuxScalerContext = NULL;
+
+ 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 format %d join %d\n",
+ rec->fFrameWidth, rec->fMiterLimit, rec->fHints, rec->fFrameAndFill,
+ rec->fMaskFormat, 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();
+
+ SkDELETE(fAuxScalerContext);
+}
+
+SkScalerContext* SkScalerContext::loadAuxContext() const {
+ if (NULL == fAuxScalerContext) {
+ fAuxScalerContext = SkFontHost::CreateFallbackScalerContext(fRec);
+ if (NULL != fAuxScalerContext) {
+ fAuxScalerContext->setBaseGlyphCount(this->getGlyphCount());
+ }
+ }
+ return fAuxScalerContext;
+}
+
+#ifdef TRACK_MISSING_CHARS
+ static uint8_t gMissingChars[1 << 13];
+#endif
+
+uint16_t SkScalerContext::charToGlyphID(SkUnichar uni) {
+ unsigned glyphID = this->generateCharToGlyph(uni);
+
+ if (0 == glyphID) { // try auxcontext
+ SkScalerContext* ctx = this->loadAuxContext();
+ if (NULL != ctx) {
+ glyphID = ctx->generateCharToGlyph(uni);
+ if (0 != glyphID) { // only fiddle with it if its not missing
+ glyphID += this->getGlyphCount();
+ if (glyphID > 0xFFFF) {
+ glyphID = 0;
+ }
+ }
+ }
+ }
+#ifdef TRACK_MISSING_CHARS
+ if (0 == glyphID) {
+ bool announce = false;
+ if (uni > 0xFFFF) { // we don't record these
+ announce = true;
+ } else {
+ unsigned index = uni >> 3;
+ unsigned mask = 1 << (uni & 7);
+ SkASSERT(index < SK_ARRAY_COUNT(gMissingChars));
+ if ((gMissingChars[index] & mask) == 0) {
+ gMissingChars[index] |= mask;
+ announce = true;
+ }
+ }
+ if (announce) {
+ printf(">>> MISSING CHAR <<< 0x%04X\n", uni);
+ }
+ }
+#endif
+ return SkToU16(glyphID);
+}
+
+/* Internal routine to resolve auxContextID into a real context.
+ Only makes sense to call once the glyph has been given a
+ valid auxGlyphID.
+*/
+SkScalerContext* SkScalerContext::getGlyphContext(const SkGlyph& glyph) const {
+ SkScalerContext* ctx = const_cast<SkScalerContext*>(this);
+
+ if (glyph.getGlyphID() >= this->getGlyphCount()) {
+ ctx = this->loadAuxContext();
+ if (NULL == ctx) { // if no aux, just return us
+ ctx = const_cast<SkScalerContext*>(this);
+ }
+ }
+ return ctx;
+}
+
+static int plus_minus_pin(int value, int max) {
+ SkASSERT(max >= 0);
+
+ if (value > max) {
+ value = max;
+ } else if (value < -max) {
+ value = -max;
+ }
+ return value;
+}
+
+void SkScalerContext::getAdvance(SkGlyph* glyph) {
+ // mark us as just having a valid advance
+ glyph->fMaskFormat = MASK_FORMAT_JUST_ADVANCE;
+ // we mark the format before making the call, in case the impl
+ // internally ends up calling its generateMetrics, which is OK
+ // albeit slower than strictly necessary
+ this->getGlyphContext(*glyph)->generateAdvance(glyph);
+}
+
+void SkScalerContext::getMetrics(SkGlyph* glyph) {
+ this->getGlyphContext(*glyph)->generateMetrics(glyph);
+
+ // for now we have separate cache entries for devkerning on and off
+ // in the future we might share caches, but make our measure/draw
+ // code make the distinction. Thus we zap the values if the caller
+ // has not asked for them.
+ if ((fRec.fFlags & SkScalerContext::kDevKernText_Flag) == 0) {
+ // no devkern, so zap the fields
+ glyph->fLsbDelta = glyph->fRsbDelta = 0;
+ }
+
+ // if either dimension is empty, zap the image bounds of the glyph
+ if (0 == glyph->fWidth || 0 == glyph->fHeight) {
+ glyph->fWidth = 0;
+ glyph->fHeight = 0;
+ glyph->fTop = 0;
+ glyph->fLeft = 0;
+ glyph->fMaskFormat = 0;
+ 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;
+ SkIRect 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 = fRec.fMaskFormat;
+
+ if (fMaskFilter) {
+ SkMask src, dst;
+ SkMatrix matrix;
+
+ glyph->toMask(&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;
+ }
+ }
+}
+
+void SkScalerContext::getImage(const SkGlyph& origGlyph) {
+ const SkGlyph* glyph = &origGlyph;
+ SkGlyph tmpGlyph;
+
+ if (fMaskFilter) { // restore the prefilter bounds
+ tmpGlyph.fID = origGlyph.fID;
+
+ // 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;
+
+ glyph->toMask(&mask);
+ mask.fFormat = SkMask::kA8_Format;
+ bzero(glyph->fImage, mask.computeImageSize());
+
+ 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 (SkMask::kA8_Format == fRec.fMaskFormat) {
+ config = SkBitmap::kA8_Config;
+ paint.setAntiAlias(true);
+ } else {
+ SkASSERT(SkMask::kBW_Format == fRec.fMaskFormat);
+ config = SkBitmap::kA1_Config;
+ paint.setAntiAlias(false);
+ }
+
+ clip.setRect(0, 0, glyph->fWidth, glyph->fHeight);
+ matrix.setTranslate(-SkIntToScalar(glyph->fLeft),
+ -SkIntToScalar(glyph->fTop));
+ bm.setConfig(config, glyph->fWidth, glyph->fHeight,
+ glyph->rowBytes());
+ bm.setPixels(glyph->fImage);
+ bzero(glyph->fImage, bm.height() * bm.rowBytes());
+
+ draw.fClip = &clip;
+ draw.fMatrix = &matrix;
+ draw.fBitmap = &bm;
+ draw.fBounder = NULL;
+ draw.drawPath(devPath, paint);
+ }
+ } else {
+ this->getGlyphContext(*glyph)->generateImage(*glyph);
+ }
+
+ if (fMaskFilter) {
+ SkMask srcM, dstM;
+ SkMatrix matrix;
+
+ // the src glyph image shouldn't be 3D
+ SkASSERT(SkMask::k3D_Format != glyph->fMaskFormat);
+ glyph->toMask(&srcM);
+ fRec.getMatrixFrom2x2(&matrix);
+
+ if (fMaskFilter->filterMask(&dstM, srcM, matrix, NULL)) {
+ int width = SkFastMin32(origGlyph.fWidth, dstM.fBounds.width());
+ int height = SkFastMin32(origGlyph.fHeight, dstM.fBounds.height());
+ int dstRB = origGlyph.rowBytes();
+ int srcRB = 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;
+ }
+
+ // clean out our glyph, since it may be larger than dstM
+ //bzero(dst, height * dstRB);
+
+ while (--height >= 0) {
+ memcpy(dst, src, width);
+ src += srcRB;
+ dst += dstRB;
+ }
+ SkMask::FreeImage(dstM.fImage);
+ }
+ }
+
+ // check to see if we should filter the alpha channel
+
+ if (NULL == fMaskFilter &&
+ fRec.fMaskFormat != SkMask::kBW_Format &&
+ (fRec.fFlags & (kGammaForBlack_Flag | kGammaForWhite_Flag)) != 0)
+ {
+ const uint8_t* table = (fRec.fFlags & kGammaForBlack_Flag) ? gBlackGammaTable : gWhiteGammaTable;
+ if (NULL != table)
+ {
+ uint8_t* dst = (uint8_t*)origGlyph.fImage;
+ unsigned rowBytes = origGlyph.rowBytes();
+
+ for (int y = origGlyph.fHeight - 1; y >= 0; --y)
+ {
+ for (int x = origGlyph.fWidth - 1; x >= 0; --x)
+ dst[x] = table[dst[x]];
+ dst += rowBytes;
+ }
+ }
+ }
+}
+
+void SkScalerContext::getPath(const SkGlyph& glyph, SkPath* path)
+{
+ this->internalGetPath(glyph, NULL, path, NULL);
+}
+
+void SkScalerContext::getFontMetrics(SkPaint::FontMetrics* mx, SkPaint::FontMetrics* my)
+{
+ this->generateFontMetrics(mx, my);
+}
+
+///////////////////////////////////////////////////////////////////////
+
+void SkScalerContext::internalGetPath(const SkGlyph& glyph, SkPath* fillPath, SkPath* devPath, SkMatrix* fillToDevMatrix)
+{
+ SkPath path;
+
+ this->getGlyphContext(glyph)->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(SkToBool(fRec.fFlags & kFrameAndFill_Flag));
+ 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);
+ }
+
+ if (devPath)
+ devPath->updateBoundsCache();
+ if (fillPath)
+ fillPath->updateBoundsCache();
+}
+
+
+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);
+ if (fPreSkewX)
+ m->postSkew(fPreSkewX, 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/src/core/SkScan.cpp b/src/core/SkScan.cpp
new file mode 100644
index 0000000000..013b0eafcc
--- /dev/null
+++ b/src/core/SkScan.cpp
@@ -0,0 +1,75 @@
+/* libs/graphics/sgl/SkScan.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkScan.h"
+#include "SkBlitter.h"
+#include "SkRegion.h"
+
+static inline void blitrect(SkBlitter* blitter, const SkIRect& r) {
+ blitter->blitRect(r.fLeft, r.fTop, r.width(), r.height());
+}
+
+void SkScan::FillIRect(const SkIRect& r, const SkRegion* clip,
+ SkBlitter* blitter) {
+ if (!r.isEmpty()) {
+ if (clip) {
+ if (clip->isRect()) {
+ const SkIRect& clipBounds = clip->getBounds();
+
+ if (clipBounds.contains(r)) {
+ blitrect(blitter, r);
+ } else {
+ SkIRect rr = r;
+ if (rr.intersect(clipBounds)) {
+ blitrect(blitter, rr);
+ }
+ }
+ } else {
+ SkRegion::Cliperator cliper(*clip, r);
+ const SkIRect& rr = cliper.rect();
+
+ while (!cliper.done()) {
+ blitrect(blitter, rr);
+ cliper.next();
+ }
+ }
+ } else {
+ blitrect(blitter, r);
+ }
+ }
+}
+
+void SkScan::FillXRect(const SkXRect& xr, const SkRegion* clip,
+ SkBlitter* blitter) {
+ SkIRect r;
+
+ XRect_round(xr, &r);
+ SkScan::FillIRect(r, clip, blitter);
+}
+
+#ifdef SK_SCALAR_IS_FLOAT
+
+void SkScan::FillRect(const SkRect& r, const SkRegion* clip,
+ SkBlitter* blitter) {
+ SkIRect ir;
+
+ r.round(&ir);
+ SkScan::FillIRect(ir, clip, blitter);
+}
+
+#endif
+
diff --git a/src/core/SkScan.h b/src/core/SkScan.h
new file mode 100644
index 0000000000..379c016b41
--- /dev/null
+++ b/src/core/SkScan.h
@@ -0,0 +1,123 @@
+/* libs/graphics/sgl/SkScan.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef SkScan_DEFINED
+#define SkScan_DEFINED
+
+#include "SkRect.h"
+
+class SkRegion;
+class SkBlitter;
+class SkPath;
+
+/** Defines a fixed-point rectangle, identical to the integer SkIRect, but its
+ coordinates are treated as SkFixed rather than int32_t.
+*/
+typedef SkIRect SkXRect;
+
+class SkScan {
+public:
+ static void FillIRect(const SkIRect&, const SkRegion* clip, SkBlitter*);
+ static void FillXRect(const SkXRect&, const SkRegion* clip, SkBlitter*);
+
+#ifdef SK_SCALAR_IS_FIXED
+ static void FillRect(const SkRect& rect, const SkRegion* clip,
+ SkBlitter* blitter) {
+ SkScan::FillXRect(*(const SkXRect*)&rect, clip, blitter);
+ }
+#else
+ static void FillRect(const SkRect&, const SkRegion* clip, SkBlitter*);
+#endif
+
+ static void FillTriangle(const SkPoint pts[], const SkRegion*, SkBlitter*);
+ static void FillPath(const SkPath&, const SkRegion& clip, SkBlitter*);
+
+ static void FillTriangle(const SkPoint& a, const SkPoint& b,
+ const SkPoint& c, const SkRegion* clip,
+ SkBlitter* blitter) {
+ SkPoint pts[3];
+ pts[0] = a;
+ pts[1] = b;
+ pts[2] = c;
+ FillTriangle(pts, clip, blitter);
+ }
+
+ 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 AntiFillXRect(const SkXRect&, const SkRegion* clip, SkBlitter*);
+#ifdef SK_SCALAR_IS_FIXED
+ static void AntiFillRect(const SkRect& rect, const SkRegion* clip,
+ SkBlitter* blitter) {
+ SkScan::AntiFillXRect(*(const SkXRect*)&rect, clip, blitter);
+ }
+#else
+ static void AntiFillRect(const SkRect&, const SkRegion* clip, SkBlitter*);
+#endif
+
+ 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*);
+};
+
+/** Assign an SkXRect from a SkIRect, by promoting the src rect's coordinates
+ from int to SkFixed. Does not check for overflow if the src coordinates
+ exceed 32K
+*/
+static void XRect_set(SkXRect* xr, const SkIRect& src) {
+ xr->fLeft = SkIntToFixed(src.fLeft);
+ xr->fTop = SkIntToFixed(src.fTop);
+ xr->fRight = SkIntToFixed(src.fRight);
+ xr->fBottom = SkIntToFixed(src.fBottom);
+}
+
+/** Assign an SkXRect from a SkRect, by promoting the src rect's coordinates
+ from SkScalar to SkFixed. Does not check for overflow if the src coordinates
+ exceed 32K
+*/
+static void XRect_set(SkXRect* xr, const SkRect& src) {
+ xr->fLeft = SkScalarToFixed(src.fLeft);
+ xr->fTop = SkScalarToFixed(src.fTop);
+ xr->fRight = SkScalarToFixed(src.fRight);
+ xr->fBottom = SkScalarToFixed(src.fBottom);
+}
+
+/** Round the SkXRect coordinates, and store the result in the SkIRect.
+*/
+static void XRect_round(const SkXRect& xr, SkIRect* dst) {
+ dst->fLeft = SkFixedRound(xr.fLeft);
+ dst->fTop = SkFixedRound(xr.fTop);
+ dst->fRight = SkFixedRound(xr.fRight);
+ dst->fBottom = SkFixedRound(xr.fBottom);
+}
+
+/** Round the SkXRect coordinates out (i.e. use floor for left/top, and ceiling
+ for right/bottom), and store the result in the SkIRect.
+*/
+static void XRect_roundOut(const SkXRect& xr, SkIRect* dst) {
+ dst->fLeft = SkFixedFloor(xr.fLeft);
+ dst->fTop = SkFixedFloor(xr.fTop);
+ dst->fRight = SkFixedCeil(xr.fRight);
+ dst->fBottom = SkFixedCeil(xr.fBottom);
+}
+
+#endif
diff --git a/src/core/SkScanPriv.h b/src/core/SkScanPriv.h
new file mode 100644
index 0000000000..43dfdef486
--- /dev/null
+++ b/src/core/SkScanPriv.h
@@ -0,0 +1,48 @@
+/* libs/graphics/sgl/SkScanPriv.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef SkScanPriv_DEFINED
+#define SkScanPriv_DEFINED
+
+#include "SkScan.h"
+#include "SkBlitter.h"
+
+class SkScanClipper {
+public:
+ SkScanClipper(SkBlitter* blitter, const SkRegion* clip, const SkIRect& bounds);
+
+ SkBlitter* getBlitter() const { return fBlitter; }
+ const SkIRect* getClipRect() const { return fClipRect; }
+
+private:
+ SkRectClipBlitter fRectBlitter;
+ SkRgnClipBlitter fRgnBlitter;
+ SkBlitter* fBlitter;
+ const SkIRect* fClipRect;
+};
+
+// clipRect == null means path is entirely inside the clip
+void sk_fill_path(const SkPath& path, const SkIRect* clipRect,
+ SkBlitter* blitter, int stop_y, int shiftEdgesUp,
+ const SkRegion& clipRgn);
+
+// blit the rects above and below avoid, clipped to clp
+void sk_blit_above_and_below(SkBlitter* blitter, const SkIRect& avoid,
+ const SkRegion& clip);
+
+#endif
+
diff --git a/src/core/SkScan_AntiPath.cpp b/src/core/SkScan_AntiPath.cpp
new file mode 100644
index 0000000000..9cdeeaa56e
--- /dev/null
+++ b/src/core/SkScan_AntiPath.cpp
@@ -0,0 +1,406 @@
+/* libs/graphics/sgl/SkScan_AntiPath.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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 BaseSuperBlitter : public SkBlitter {
+public:
+ BaseSuperBlitter(SkBlitter* realBlitter, const SkIRect& ir,
+ const SkRegion& clip);
+
+ virtual void blitAntiH(int x, int y, const SkAlpha antialias[],
+ const int16_t 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?");
+ }
+
+protected:
+ SkBlitter* fRealBlitter;
+ int fCurrIY;
+ int fWidth, fLeft, fSuperLeft;
+
+ SkDEBUGCODE(int fCurrX;)
+ SkDEBUGCODE(int fCurrY;)
+};
+
+BaseSuperBlitter::BaseSuperBlitter(SkBlitter* realBlitter, const SkIRect& ir,
+ const SkRegion& clip) {
+ fRealBlitter = realBlitter;
+
+ // take the union of the ir bounds and clip, since we may be called with an
+ // inverse filltype
+ const int left = SkMin32(ir.fLeft, clip.getBounds().fLeft);
+ const int right = SkMax32(ir.fRight, clip.getBounds().fRight);
+
+ fLeft = left;
+ fSuperLeft = left << SHIFT;
+ fWidth = right - left;
+ fCurrIY = -1;
+ SkDEBUGCODE(fCurrX = -1; fCurrY = -1;)
+}
+
+class SuperBlitter : public BaseSuperBlitter {
+public:
+ SuperBlitter(SkBlitter* realBlitter, const SkIRect& ir,
+ const SkRegion& clip);
+
+ virtual ~SuperBlitter() {
+ this->flush();
+ sk_free(fRuns.fRuns);
+ }
+
+ void flush();
+
+ virtual void blitH(int x, int y, int width);
+
+private:
+ SkAlphaRuns fRuns;
+};
+
+SuperBlitter::SuperBlitter(SkBlitter* realBlitter, const SkIRect& ir,
+ const SkRegion& clip)
+ : BaseSuperBlitter(realBlitter, ir, clip) {
+ const int width = fWidth;
+
+ // extra one to store the zero at the end
+ fRuns.fRuns = (int16_t*)sk_malloc_throw((width + 1 + (width + 2)/2) * sizeof(int16_t));
+ fRuns.fAlpha = (uint8_t*)(fRuns.fRuns + width + 1);
+ fRuns.reset(width);
+}
+
+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;
+ // hack, until I figure out why my cubics (I think) go beyond the bounds
+ if (x < 0)
+ {
+ width += x;
+ x = 0;
+ }
+
+#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
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class MaskSuperBlitter : public BaseSuperBlitter {
+public:
+ MaskSuperBlitter(SkBlitter* realBlitter, const SkIRect& ir,
+ const SkRegion& clip);
+ virtual ~MaskSuperBlitter() {
+ fRealBlitter->blitMask(fMask, fClipRect);
+ }
+
+ virtual void blitH(int x, int y, int width);
+
+ static bool CanHandleRect(const SkIRect& bounds)
+ {
+ int width = bounds.width();
+ int rb = SkAlign4(width);
+
+ return (width <= MaskSuperBlitter::kMAX_WIDTH) &&
+ (rb * bounds.height() <= MaskSuperBlitter::kMAX_STORAGE);
+ }
+
+private:
+ enum {
+ kMAX_WIDTH = 32, // so we don't try to do very wide things, where the RLE blitter would be faster
+ kMAX_STORAGE = 1024
+ };
+
+ SkMask fMask;
+ SkIRect fClipRect;
+ // we add 1 because add_aa_span can write (unchanged) 1 extra byte at the end, rather than
+ // perform a test to see if stopAlpha != 0
+ uint32_t fStorage[(kMAX_STORAGE >> 2) + 1];
+};
+
+MaskSuperBlitter::MaskSuperBlitter(SkBlitter* realBlitter, const SkIRect& ir,
+ const SkRegion& clip)
+ : BaseSuperBlitter(realBlitter, ir, clip) {
+ SkASSERT(CanHandleRect(ir));
+
+ fMask.fImage = (uint8_t*)fStorage;
+ fMask.fBounds = ir;
+ fMask.fRowBytes = ir.width();
+ fMask.fFormat = SkMask::kA8_Format;
+
+ fClipRect = ir;
+ fClipRect.intersect(clip.getBounds());
+
+ // For valgrind, write 1 extra byte at the end so we don't read
+ // uninitialized memory. See comment in add_aa_span and fStorage[].
+ memset(fStorage, 0, fMask.fBounds.height() * fMask.fRowBytes + 1);
+}
+
+static void add_aa_span(uint8_t* alpha, U8CPU startAlpha)
+{
+ /* 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.
+ */
+ unsigned tmp = *alpha + startAlpha;
+ SkASSERT(tmp <= 256);
+ *alpha = SkToU8(tmp - (tmp >> 8));
+}
+
+static void add_aa_span(uint8_t* alpha, U8CPU startAlpha, int middleCount, U8CPU stopAlpha, U8CPU maxValue)
+{
+ SkASSERT(middleCount >= 0);
+
+ /* 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.
+ */
+ unsigned tmp = *alpha + startAlpha;
+ SkASSERT(tmp <= 256);
+ *alpha++ = SkToU8(tmp - (tmp >> 8));
+
+ while (--middleCount >= 0)
+ {
+ alpha[0] = SkToU8(alpha[0] + maxValue);
+ alpha += 1;
+ }
+
+ // potentially this can be off the end of our "legal" alpha values, but that
+ // only happens if stopAlpha is also 0. Rather than test for stopAlpha != 0
+ // every time (slow), we just do it, and ensure that we've allocated extra space
+ // (see the + 1 comment in fStorage[]
+ *alpha = SkToU8(*alpha + stopAlpha);
+}
+
+void MaskSuperBlitter::blitH(int x, int y, int width)
+{
+ int iy = (y >> SHIFT);
+
+ SkASSERT(iy >= fMask.fBounds.fTop && iy < fMask.fBounds.fBottom);
+ iy -= fMask.fBounds.fTop; // make it relative to 0
+
+#ifdef SK_DEBUG
+ {
+ int ix = x >> SHIFT;
+ SkASSERT(ix >= fMask.fBounds.fLeft && ix < fMask.fBounds.fRight);
+ }
+#endif
+
+ x -= (fMask.fBounds.fLeft << SHIFT);
+
+ // hack, until I figure out why my cubics (I think) go beyond the bounds
+ if (x < 0)
+ {
+ width += x;
+ x = 0;
+ }
+
+ // 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);
+
+ uint8_t* row = fMask.fImage + iy * fMask.fRowBytes + (x >> SHIFT);
+
+ 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)
+ {
+ add_aa_span(row, coverage_to_alpha(fe - fb));
+ }
+ else
+ {
+ fb = (1 << SHIFT) - fb;
+ add_aa_span(row, coverage_to_alpha(fb), n, coverage_to_alpha(fe),
+ (1 << (8 - SHIFT)) - (((y & MASK) + 1) >> SHIFT));
+ }
+
+#ifdef SK_DEBUG
+ 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.isEmpty()) {
+ return;
+ }
+
+ SkRect r;
+ SkIRect 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)) {
+ // can't supersample, try drawing w/o antialiasing
+ SkScan::FillPath(path, clip, blitter);
+ return;
+ }
+
+ SkScanClipper clipper(blitter, &clip, ir);
+ const SkIRect* clipRect = clipper.getClipRect();
+
+ if (clipper.getBlitter() == NULL) { // clipped out
+ if (path.isInverseFillType()) {
+ blitter->blitRegion(clip);
+ }
+ return;
+ }
+
+ // now use the (possibly wrapped) blitter
+ blitter = clipper.getBlitter();
+
+ if (path.isInverseFillType()) {
+ sk_blit_above_and_below(blitter, ir, clip);
+ }
+
+ SkIRect superRect, *superClipRect = NULL;
+
+ if (clipRect)
+ {
+ superRect.set( clipRect->fLeft << SHIFT, clipRect->fTop << SHIFT,
+ clipRect->fRight << SHIFT, clipRect->fBottom << SHIFT);
+ superClipRect = &superRect;
+ }
+
+ // MaskSuperBlitter can't handle drawing outside of ir, so we can't use it
+ // if we're an inverse filltype
+ if (!path.isInverseFillType() && MaskSuperBlitter::CanHandleRect(ir))
+ {
+ MaskSuperBlitter superBlit(blitter, ir, clip);
+ sk_fill_path(path, superClipRect, &superBlit, ir.fBottom, SHIFT, clip);
+ }
+ else
+ {
+ SuperBlitter superBlit(blitter, ir, clip);
+ sk_fill_path(path, superClipRect, &superBlit, ir.fBottom, SHIFT, clip);
+ }
+}
diff --git a/src/core/SkScan_Antihair.cpp b/src/core/SkScan_Antihair.cpp
new file mode 100644
index 0000000000..04e4690e63
--- /dev/null
+++ b/src/core/SkScan_Antihair.cpp
@@ -0,0 +1,645 @@
+/* libs/graphics/sgl/SkScan_Antihair.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkScan.h"
+#include "SkBlitter.h"
+#include "SkColorPriv.h"
+#include "SkRegion.h"
+#include "SkFDot6.h"
+
+#define HLINE_STACK_BUFFER 100
+
+static inline int SmallDot6Scale(int value, int dot6) {
+ SkASSERT((int16_t)value == value);
+ SkASSERT((unsigned)dot6 <= 64);
+ return SkMulS16(value, dot6) >> 6;
+}
+
+//#define TEST_GAMMA
+
+#ifdef TEST_GAMMA
+ static uint8_t 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, U8CPU alpha)
+{
+ SkASSERT(count > 0);
+
+ int16_t runs[HLINE_STACK_BUFFER + 1];
+ uint8_t 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 SkFixed hline(int x, int stopx, SkFixed fy, SkFixed /*slope*/, SkBlitter* blitter, int mod64)
+{
+ SkASSERT(x < stopx);
+ int count = stopx - x;
+ fy += SK_Fixed1/2;
+
+ int y = fy >> 16;
+ uint8_t a = (uint8_t)(fy >> 8);
+
+ // lower line
+ unsigned ma = SmallDot6Scale(a, mod64);
+ if (ma) {
+ call_hline_blitter(blitter, x, y, count, ma);
+ }
+
+ // upper line
+ ma = SmallDot6Scale(255 - a, mod64);
+ if (ma) {
+ call_hline_blitter(blitter, x, y - 1, count, ma);
+ }
+
+ return fy - SK_Fixed1/2;
+}
+
+static SkFixed horish(int x, int stopx, SkFixed fy, SkFixed dy, SkBlitter* blitter, int mod64)
+{
+ SkASSERT(x < stopx);
+
+#ifdef TEST_GAMMA
+ const uint8_t* gamma = gGammaTable;
+#endif
+ int16_t runs[2];
+ uint8_t aa[1];
+
+ runs[0] = 1;
+ runs[1] = 0;
+
+ fy += SK_Fixed1/2;
+ do {
+ int lower_y = fy >> 16;
+ uint8_t a = (uint8_t)(fy >> 8);
+ unsigned ma = SmallDot6Scale(a, mod64);
+ if (ma)
+ {
+ aa[0] = ApplyGamma(gamma, ma);
+ 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);
+ }
+ ma = SmallDot6Scale(255 - a, mod64);
+ if (ma)
+ {
+ aa[0] = ApplyGamma(gamma, ma);
+ 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);
+
+ return fy - SK_Fixed1/2;
+}
+
+static SkFixed vline(int y, int stopy, SkFixed fx, SkFixed /*slope*/, SkBlitter* blitter, int mod64)
+{
+ SkASSERT(y < stopy);
+ fx += SK_Fixed1/2;
+
+ int x = fx >> 16;
+ int a = (uint8_t)(fx >> 8);
+
+ unsigned ma = SmallDot6Scale(a, mod64);
+ if (ma)
+ blitter->blitV(x, y, stopy - y, ApplyGamma(gGammaTable, ma));
+ ma = SmallDot6Scale(255 - a, mod64);
+ if (ma)
+ blitter->blitV(x - 1, y, stopy - y, ApplyGamma(gGammaTable, ma));
+
+ return fx - SK_Fixed1/2;
+}
+
+static SkFixed vertish(int y, int stopy, SkFixed fx, SkFixed dx, SkBlitter* blitter, int mod64)
+{
+ SkASSERT(y < stopy);
+#ifdef TEST_GAMMA
+ const uint8_t* gamma = gGammaTable;
+#endif
+ int16_t runs[3];
+ uint8_t aa[2];
+
+ runs[0] = 1;
+ runs[2] = 0;
+
+ fx += SK_Fixed1/2;
+ do {
+ int x = fx >> 16;
+ uint8_t a = (uint8_t)(fx >> 8);
+
+ aa[0] = ApplyGamma(gamma, SmallDot6Scale(255 - a, mod64));
+ aa[1] = ApplyGamma(gamma, SmallDot6Scale(a, mod64));
+ // 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);
+
+ return fx - SK_Fixed1/2;
+}
+
+typedef SkFixed (*LineProc)(int istart, int istop, SkFixed fstart, SkFixed slope, SkBlitter*, int);
+
+static inline SkFixed fastfixdiv(SkFDot6 a, SkFDot6 b)
+{
+ SkASSERT((a << 16 >> 16) == a);
+ SkASSERT(b != 0);
+ return (a << 16) / b;
+}
+
+static void do_anti_hairline(SkFDot6 x0, SkFDot6 y0, SkFDot6 x1, SkFDot6 y1,
+ const SkIRect* 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 scaleStart, scaleStop;
+ 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 = SkFDot6Floor(x0);
+ istop = SkFDot6Ceil(x1);
+ fstart = SkFDot6ToFixed(y0);
+ if (y0 == y1) { // completely horizontal, take fast case
+ slope = 0;
+ proc = hline;
+ } else {
+ slope = fastfixdiv(y1 - y0, x1 - x0);
+ SkASSERT(slope >= -SK_Fixed1 && slope <= SK_Fixed1);
+ fstart += (slope * (32 - (x0 & 63)) + 32) >> 6;
+ proc = horish;
+ }
+
+ SkASSERT(istop > istart);
+ if (istop - istart == 1) {
+ scaleStart = x1 - x0;
+ SkASSERT(scaleStart >= 0 && scaleStart <= 64);
+ scaleStop = 0;
+ } else {
+ scaleStart = 64 - (x0 & 63);
+ scaleStop = x1 & 63;
+ }
+
+ if (clip)
+ {
+ if (istart >= clip->fRight || istop <= clip->fLeft)
+ return;
+ if (istart < clip->fLeft)
+ {
+ fstart += slope * (clip->fLeft - istart);
+ istart = clip->fLeft;
+ scaleStart = 64;
+ }
+ if (istop > clip->fRight) {
+ istop = clip->fRight;
+ scaleStop = 64;
+ }
+ 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 = NULL;
+ }
+ }
+ else // mostly vertical
+ {
+ if (y0 > y1) // we want to go top-to-bottom
+ {
+ SkTSwap<SkFDot6>(x0, x1);
+ SkTSwap<SkFDot6>(y0, y1);
+ }
+
+ istart = SkFDot6Floor(y0);
+ istop = SkFDot6Ceil(y1);
+ fstart = SkFDot6ToFixed(x0);
+ if (x0 == x1)
+ {
+ if (y0 == y1) { // are we zero length?
+ return; // nothing to do
+ }
+ slope = 0;
+ proc = vline;
+ }
+ else
+ {
+ slope = fastfixdiv(x1 - x0, y1 - y0);
+ SkASSERT(slope <= SK_Fixed1 && slope >= -SK_Fixed1);
+ fstart += (slope * (32 - (y0 & 63)) + 32) >> 6;
+ proc = vertish;
+ }
+
+ SkASSERT(istop > istart);
+ if (istop - istart == 1) {
+ scaleStart = y1 - y0;
+ SkASSERT(scaleStart >= 0 && scaleStart <= 64);
+ scaleStop = 0;
+ } else {
+ scaleStart = 64 - (y0 & 63);
+ scaleStop = y1 & 63;
+ }
+
+ if (clip)
+ {
+ if (istart >= clip->fBottom || istop <= clip->fTop)
+ return;
+ if (istart < clip->fTop)
+ {
+ fstart += slope * (clip->fTop - istart);
+ istart = clip->fTop;
+ scaleStart = 64;
+ }
+ if (istop > clip->fBottom) {
+ istop = clip->fBottom;
+ scaleStop = 64;
+ }
+ 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 = NULL;
+ }
+ }
+
+ SkRectClipBlitter rectClipper;
+ if (clip)
+ {
+ rectClipper.init(blitter, *clip);
+ blitter = &rectClipper;
+ }
+
+ fstart = proc(istart, istart + 1, fstart, slope, blitter, scaleStart);
+ istart += 1;
+ int fullSpans = istop - istart - 1;
+ if (fullSpans > 0) {
+ fstart = proc(istart, istart + fullSpans, fstart, slope, blitter, 64);
+ }
+ if (scaleStop > 0) {
+ proc(istop - 1, istop, fstart, slope, blitter, scaleStop);
+ }
+}
+
+void SkScan::AntiHairLine(const SkPoint& pt0, const SkPoint& pt1,
+ const SkRegion* clip, SkBlitter* blitter)
+{
+ if (clip && clip->isEmpty())
+ return;
+
+ SkASSERT(clip == NULL || !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);
+ SkIRect ir;
+
+ ir.set( SkFDot6Floor(left) - 1,
+ SkFDot6Floor(top) - 1,
+ SkFDot6Ceil(right) + 1,
+ SkFDot6Ceil(bottom) + 1);
+
+ if (clip->quickReject(ir))
+ return;
+ if (!clip->quickContains(ir))
+ {
+ SkRegion::Cliperator iter(*clip, ir);
+ const SkIRect* 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, NULL, blitter);
+}
+
+void SkScan::AntiHairRect(const SkRect& rect, const SkRegion* clip, SkBlitter* blitter)
+{
+ if (clip)
+ {
+ SkIRect 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 = NULL;
+ }
+
+ 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);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+
+typedef int FDot8; // 24.8 integer fixed point
+
+static inline FDot8 SkFixedToFDot8(SkFixed x) {
+ return (x + 0x80) >> 8;
+}
+
+static void do_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha, SkBlitter* blitter)
+{
+ SkASSERT(L < R);
+
+ if ((L >> 8) == ((R - 1) >> 8)) // 1x1 pixel
+ {
+ blitter->blitV(L >> 8, top, 1, SkAlphaMul(alpha, R - L));
+ return;
+ }
+
+ int left = L >> 8;
+
+ if (L & 0xFF)
+ {
+ blitter->blitV(left, top, 1, SkAlphaMul(alpha, 256 - (L & 0xFF)));
+ left += 1;
+ }
+
+ int rite = R >> 8;
+ int width = rite - left;
+ if (width > 0)
+ call_hline_blitter(blitter, left, top, width, alpha);
+
+ if (R & 0xFF)
+ blitter->blitV(rite, top, 1, SkAlphaMul(alpha, R & 0xFF));
+}
+
+static void antifillrect(const SkXRect& xr, SkBlitter* blitter)
+{
+ FDot8 L = SkFixedToFDot8(xr.fLeft);
+ FDot8 T = SkFixedToFDot8(xr.fTop);
+ FDot8 R = SkFixedToFDot8(xr.fRight);
+ FDot8 B = SkFixedToFDot8(xr.fBottom);
+
+ // check for empty now that we're in our reduced precision space
+ if (L >= R || T >= B)
+ return;
+
+ int top = T >> 8;
+ if (top == ((B - 1) >> 8)) // just one scanline high
+ {
+ do_scanline(L, top, R, B - T - 1, blitter);
+ return;
+ }
+
+ if (T & 0xFF)
+ {
+ do_scanline(L, top, R, 256 - (T & 0xFF), blitter);
+ top += 1;
+ }
+
+ int bot = B >> 8;
+ int height = bot - top;
+ if (height > 0)
+ {
+ int left = L >> 8;
+ if (L & 0xFF)
+ {
+ blitter->blitV(left, top, height, 256 - (L & 0xFF));
+ left += 1;
+ }
+ int rite = R >> 8;
+ int width = rite - left;
+ if (width > 0)
+ blitter->blitRect(left, top, width, height);
+ if (R & 0xFF)
+ blitter->blitV(rite, top, height, R & 0xFF);
+ }
+
+ if (B & 0xFF)
+ do_scanline(L, bot, R, B & 0xFF, blitter);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkScan::AntiFillXRect(const SkXRect& xr, const SkRegion* clip,
+ SkBlitter* blitter) {
+ if (clip) {
+ SkIRect outerBounds;
+ XRect_roundOut(xr, &outerBounds);
+
+ if (clip->isRect()) {
+ const SkIRect& clipBounds = clip->getBounds();
+
+ if (clipBounds.contains(outerBounds)) {
+ antifillrect(xr, blitter);
+ } else {
+ SkXRect tmpR;
+ // this keeps our original edges fractional
+ XRect_set(&tmpR, clipBounds);
+ if (tmpR.intersect(xr)) {
+ antifillrect(tmpR, blitter);
+ }
+ }
+ } else {
+ SkRegion::Cliperator clipper(*clip, outerBounds);
+ const SkIRect& rr = clipper.rect();
+
+ while (!clipper.done()) {
+ SkXRect tmpR;
+
+ // this keeps our original edges fractional
+ XRect_set(&tmpR, rr);
+ if (tmpR.intersect(xr)) {
+ antifillrect(tmpR, blitter);
+ }
+ clipper.next();
+ }
+ }
+ } else {
+ antifillrect(xr, blitter);
+ }
+}
+
+#ifdef SK_SCALAR_IS_FLOAT
+
+/* This guy takes a float-rect, but with the key improvement that it has
+ already been clipped, so we know that it is safe to convert it into a
+ XRect (fixedpoint), as it won't overflow.
+*/
+static void antifillrect(const SkRect& r, SkBlitter* blitter) {
+ SkXRect xr;
+
+ XRect_set(&xr, r);
+ antifillrect(xr, blitter);
+}
+
+/* We repeat the clipping logic of AntiFillXRect because the float rect might
+ overflow if we blindly converted it to an XRect. This sucks that we have to
+ repeat the clipping logic, but I don't see how to share the code/logic.
+
+ We clip r (as needed) into one or more (smaller) float rects, and then pass
+ those to our version of antifillrect, which converts it into an XRect and
+ then calls the blit.
+*/
+void SkScan::AntiFillRect(const SkRect& r, const SkRegion* clip,
+ SkBlitter* blitter) {
+ if (clip) {
+ SkIRect outerBounds;
+ r.roundOut(&outerBounds);
+
+ if (clip->isRect()) {
+ const SkIRect& clipBounds = clip->getBounds();
+
+ if (clipBounds.contains(outerBounds)) {
+ antifillrect(r, blitter);
+ } else {
+ SkRect tmpR;
+ // this keeps our original edges fractional
+ tmpR.set(clipBounds);
+ if (tmpR.intersect(r)) {
+ antifillrect(tmpR, blitter);
+ }
+ }
+ } else {
+ SkRegion::Cliperator clipper(*clip, outerBounds);
+ const SkIRect& rr = clipper.rect();
+
+ while (!clipper.done()) {
+ SkRect tmpR;
+ // this keeps our original edges fractional
+ tmpR.set(rr);
+ if (tmpR.intersect(r)) {
+ antifillrect(tmpR, blitter);
+ }
+ clipper.next();
+ }
+ }
+ } else {
+ antifillrect(r, blitter);
+ }
+}
+
+#endif
+
+
diff --git a/src/core/SkScan_Hairline.cpp b/src/core/SkScan_Hairline.cpp
new file mode 100644
index 0000000000..6a66754c01
--- /dev/null
+++ b/src/core/SkScan_Hairline.cpp
@@ -0,0 +1,345 @@
+/* libs/graphics/sgl/SkScan_Hairline.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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;
+ SkIRect ir;
+ SkPoint pts[2];
+
+ pts[0] = pt0;
+ pts[1] = pt1;
+ r.set(pts, 2);
+ r.roundOut(&ir);
+
+ // if we're given a horizontal or vertical line
+ // this rect could be empty (in area), in which case
+ // clip->quickReject() will always return true.
+ // hence we bloat the rect to avoid that case
+ if (ir.width() == 0)
+ ir.fRight += 1;
+ if (ir.height() == 0)
+ ir.fBottom += 1;
+
+ if (clip->quickReject(ir))
+ return;
+ if (clip->quickContains(ir))
+ clip = NULL;
+ 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) + (slope * ((32 - x0) & 63) >> 6);
+
+ 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) + (slope * ((32 - y0) & 63) >> 6);
+
+ vertline(iy0, iy1, startX, slope, blitter);
+ }
+}
+
+// we don't just draw 4 lines, 'cause that can leave a gap in the bottom-right
+// and double-hit the top-left.
+void SkScan::HairRect(const SkRect& rect, const SkRegion* clip, SkBlitter* blitter)
+{
+ SkBlitterClipper clipper;
+ SkIRect r;
+
+ r.set(SkScalarToFixed(rect.fLeft) >> 16,
+ SkScalarToFixed(rect.fTop) >> 16,
+ (SkScalarToFixed(rect.fRight) >> 16) + 1,
+ (SkScalarToFixed(rect.fBottom) >> 16) + 1);
+
+ if (clip)
+ {
+ if (clip->quickReject(r))
+ return;
+ if (!clip->quickContains(r))
+ blitter = clipper.apply(blitter, clip);
+ }
+
+ int width = r.width();
+ int height = r.height();
+
+ if ((width | height) == 0)
+ return;
+ if (width <= 2 || height <= 2)
+ {
+ blitter->blitRect(r.fLeft, r.fTop, width, height);
+ return;
+ }
+ // if we get here, we know we have 4 segments to draw
+ blitter->blitH(r.fLeft, r.fTop, width); // top
+ blitter->blitRect(r.fLeft, r.fTop + 1, 1, height - 2); // left
+ blitter->blitRect(r.fRight - 1, r.fTop + 1, 1, height - 2); // right
+ blitter->blitH(r.fLeft, r.fBottom - 1, width); // bottom
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkPath.h"
+#include "SkGeometry.h"
+
+static bool quad_too_curvy(const SkPoint pts[3])
+{
+ return true;
+}
+
+static int compute_int_quad_dist(const SkPoint pts[3]) {
+ // compute the vector between the control point ([1]) and the middle of the
+ // line connecting the start and end ([0] and [2])
+ SkScalar dx = SkScalarHalf(pts[0].fX + pts[2].fX) - pts[1].fX;
+ SkScalar dy = SkScalarHalf(pts[0].fY + pts[2].fY) - pts[1].fY;
+ // we want everyone to be positive
+ dx = SkScalarAbs(dx);
+ dy = SkScalarAbs(dy);
+ // convert to whole pixel values (use ceiling to be conservative)
+ int idx = SkScalarCeil(dx);
+ int idy = SkScalarCeil(dy);
+ // use the cheap approx for distance
+ if (idx > idy) {
+ return idx + (idy >> 1);
+ } else {
+ return idy + (idx >> 1);
+ }
+}
+
+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
+ int count = 1 << level;
+ const SkScalar dt = SkFixedToScalar(SK_Fixed1 >> level);
+ SkScalar t = dt;
+ SkPoint prevPt = pts[0];
+ for (int i = 1; i < count; i++) {
+ SkPoint nextPt;
+ SkEvalQuadAt(pts, t, &nextPt);
+ lineproc(prevPt, nextPt, clip, blitter);
+ t += dt;
+ prevPt = nextPt;
+ }
+ // draw the last line explicitly to 1.0, in case t didn't match that exactly
+ lineproc(prevPt, 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 SkIRect* clipR = NULL;
+
+ if (clip)
+ {
+ SkRect bounds;
+ SkIRect ibounds;
+
+ path.computeBounds(&bounds, SkPath::kFast_BoundsType);
+ bounds.roundOut(&ibounds);
+ ibounds.inset(-1, -1);
+
+ if (clip->quickReject(ibounds))
+ return;
+
+ if (clip->quickContains(ibounds))
+ clip = NULL;
+ 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: {
+ int d = compute_int_quad_dist(pts);
+ /* quadratics approach the line connecting their start and end points
+ 4x closer with each subdivision, so we compute the number of
+ subdivisions to be the minimum need to get that distance to be less
+ than a pixel.
+ */
+ int level = (33 - SkCLZ(d)) >> 1;
+// SkDebugf("----- distance %d computedLevel %d\n", d, computedLevel);
+ // sanity check on level (from the previous version)
+ if (level > kMaxQuadSubdivideLevel) {
+ level = kMaxQuadSubdivideLevel;
+ }
+ hairquad(pts, clip, blitter, level, 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/src/core/SkScan_Path.cpp b/src/core/SkScan_Path.cpp
new file mode 100644
index 0000000000..8b589916d6
--- /dev/null
+++ b/src/core/SkScan_Path.cpp
@@ -0,0 +1,671 @@
+/* libs/graphics/sgl/SkScan_Path.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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_MinS32
+#define kEDGE_TAIL_Y SK_MaxS32
+
+#ifdef SK_DEBUG
+ static void validate_sort(const SkEdge* edge)
+ {
+ int y = kEDGE_HEAD_Y;
+
+ while (edge->fFirstY != SK_MaxS32)
+ {
+ 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
+
+typedef void (*PrePostProc)(SkBlitter* blitter, int y, bool isStartOfScanline);
+#define PREPOST_START true
+#define PREPOST_END false
+
+static void walk_edges(SkEdge* prevHead, SkPath::FillType fillType,
+ SkBlitter* blitter, int stop_y, PrePostProc proc)
+{
+ validate_sort(prevHead->fNext);
+
+ int curr_y = prevHead->fNext->fFirstY;
+ // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
+ int windingMask = (fillType & 1) ? 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);
+
+ if (proc) {
+ proc(blitter, curr_y, PREPOST_START); // pre-proc
+ }
+
+ 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);
+ }
+
+ if (proc) {
+ proc(blitter, curr_y, PREPOST_END); // post-proc
+ }
+
+ 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);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+// this guy overrides blitH, and will call its proxy blitter with the inverse
+// of the spans it is given (clipped to the left/right of the cliprect)
+//
+// used to implement inverse filltypes on paths
+//
+class InverseBlitter : public SkBlitter {
+public:
+ void setBlitter(SkBlitter* blitter, const SkIRect& clip, int shift) {
+ fBlitter = blitter;
+ fFirstX = clip.fLeft << shift;
+ fLastX = clip.fRight << shift;
+ }
+ void prepost(int y, bool isStart) {
+ if (isStart) {
+ fPrevX = fFirstX;
+ } else {
+ int invWidth = fLastX - fPrevX;
+ if (invWidth > 0) {
+ fBlitter->blitH(fPrevX, y, invWidth);
+ }
+ }
+ }
+
+ // overrides
+ virtual void blitH(int x, int y, int width) {
+ int invWidth = x - fPrevX;
+ if (invWidth > 0) {
+ fBlitter->blitH(fPrevX, y, invWidth);
+ }
+ fPrevX = x + width;
+ }
+
+ // we do not expect to get called with these entrypoints
+ virtual void blitAntiH(int, int, const SkAlpha[], const int16_t runs[]) {
+ SkASSERT(!"blitAntiH unexpected");
+ }
+ virtual void blitV(int x, int y, int height, SkAlpha alpha) {
+ SkASSERT(!"blitV unexpected");
+ }
+ virtual void blitRect(int x, int y, int width, int height) {
+ SkASSERT(!"blitRect unexpected");
+ }
+ virtual void blitMask(const SkMask&, const SkIRect& clip) {
+ SkASSERT(!"blitMask unexpected");
+ }
+ virtual const SkBitmap* justAnOpaqueColor(uint32_t* value) {
+ SkASSERT(!"justAnOpaqueColor unexpected");
+ return NULL;
+ }
+
+private:
+ SkBlitter* fBlitter;
+ int fFirstX, fLastX, fPrevX;
+};
+
+static void PrePostInverseBlitterProc(SkBlitter* blitter, int y, bool isStart) {
+ ((InverseBlitter*)blitter)->prepost(y, isStart);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#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 SkIRect* 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[0], pts[1], 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];
+}
+
+/* 'quick' computation of the max sized needed to allocated for
+ our edgelist.
+*/
+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(NULL)) != 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;
+}
+
+/* Much faster than worst_case_edge_count, but over estimates even more
+*/
+static int cheap_worst_case_edge_count(const SkPath& path, size_t* storage)
+{
+ int ptCount = path.getPoints(NULL, 0);
+ int edgeCount = ptCount;
+ *storage = edgeCount * sizeof(SkCubicEdge);
+ return edgeCount;
+}
+
+// clipRect may be null, even though we always have a clip. This indicates that
+// the path is contained in the clip, and so we can ignore it during the blit
+//
+// clipRect (if no null) has already been shifted up
+//
+void sk_fill_path(const SkPath& path, const SkIRect* clipRect, SkBlitter* blitter,
+ int stop_y, int shiftEdgesUp, const SkRegion& clipRgn)
+{
+ SkASSERT(&path && blitter);
+
+ size_t size;
+ int maxCount = cheap_worst_case_edge_count(path, &size);
+
+#ifdef SK_DEBUG
+ {
+ size_t size2;
+ int maxCount2 = worst_case_edge_count(path, &size2);
+
+ SkASSERT(maxCount >= maxCount2 && size >= size2);
+ }
+#endif
+
+ 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 = NULL;
+ headEdge.fNext = edge;
+ headEdge.fFirstY = kEDGE_HEAD_Y;
+ headEdge.fX = SK_MinS32;
+ edge->fPrev = &headEdge;
+
+ tailEdge.fPrev = last;
+ tailEdge.fNext = NULL;
+ tailEdge.fFirstY = kEDGE_TAIL_Y;
+ last->fNext = &tailEdge;
+
+ // now edge is the head of the sorted linklist
+
+ stop_y <<= shiftEdgesUp;
+ if (clipRect && stop_y > clipRect->fBottom) {
+ stop_y = clipRect->fBottom;
+ }
+
+ InverseBlitter ib;
+ PrePostProc proc = NULL;
+
+ if (path.isInverseFillType()) {
+ ib.setBlitter(blitter, clipRgn.getBounds(), shiftEdgesUp);
+ blitter = &ib;
+ proc = PrePostInverseBlitterProc;
+ }
+
+ walk_edges(&headEdge, path.getFillType(), blitter, stop_y, proc);
+}
+
+void sk_blit_above_and_below(SkBlitter* blitter, const SkIRect& ir,
+ const SkRegion& clip) {
+ const SkIRect& cr = clip.getBounds();
+ SkIRect tmp;
+
+ tmp.fLeft = cr.fLeft;
+ tmp.fRight = cr.fRight;
+
+ tmp.fTop = cr.fTop;
+ tmp.fBottom = ir.fTop;
+ if (!tmp.isEmpty()) {
+ blitter->blitRectRegion(tmp, clip);
+ }
+
+ tmp.fTop = ir.fBottom;
+ tmp.fBottom = cr.fBottom;
+ if (!tmp.isEmpty()) {
+ blitter->blitRectRegion(tmp, clip);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+SkScanClipper::SkScanClipper(SkBlitter* blitter, const SkRegion* clip, const SkIRect& ir)
+{
+ fBlitter = NULL; // null means blit nothing
+ fClipRect = NULL;
+
+ if (clip)
+ {
+ fClipRect = &clip->getBounds();
+ if (!SkIRect::Intersects(*fClipRect, ir)) // completely clipped out
+ return;
+
+ if (clip->isRect())
+ {
+ if (fClipRect->contains(ir))
+ fClipRect = NULL;
+ 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.isEmpty()) {
+ return;
+ }
+
+ SkRect r;
+ SkIRect ir;
+
+ path.computeBounds(&r, SkPath::kFast_BoundsType);
+ r.round(&ir);
+ if (ir.isEmpty()) {
+ if (path.isInverseFillType()) {
+ blitter->blitRegion(clip);
+ }
+ return;
+ }
+
+ SkScanClipper clipper(blitter, &clip, ir);
+
+ blitter = clipper.getBlitter();
+ if (blitter) {
+ if (path.isInverseFillType()) {
+ sk_blit_above_and_below(blitter, ir, clip);
+ }
+ sk_fill_path(path, clipper.getClipRect(), blitter, ir.fBottom, 0, clip);
+ } else {
+ // what does it mean to not have a blitter if path.isInverseFillType???
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static int build_tri_edges(SkEdge edge[], const SkPoint pts[],
+ const SkIRect* clipRect, SkEdge* list[]) {
+ SkEdge** start = list;
+
+ if (edge->setLine(pts[0], pts[1], clipRect, 0)) {
+ *list++ = edge;
+ edge = (SkEdge*)((char*)edge + sizeof(SkEdge));
+ }
+ if (edge->setLine(pts[1], pts[2], clipRect, 0)) {
+ *list++ = edge;
+ edge = (SkEdge*)((char*)edge + sizeof(SkEdge));
+ }
+ if (edge->setLine(pts[2], pts[0], clipRect, 0)) {
+ *list++ = edge;
+ }
+ return (int)(list - start);
+}
+
+
+void sk_fill_triangle(const SkPoint pts[], const SkIRect* clipRect,
+ SkBlitter* blitter, const SkIRect& ir) {
+ SkASSERT(pts && blitter);
+
+ SkEdge edgeStorage[3];
+ SkEdge* list[3];
+
+ int count = build_tri_edges(edgeStorage, pts, clipRect, list);
+ if (count < 2) {
+ return;
+ }
+
+ SkEdge headEdge, tailEdge, *last;
+
+ // this returns the first and last edge after they're sorted into a dlink list
+ SkEdge* edge = sort_edges(list, count, &last);
+
+ headEdge.fPrev = NULL;
+ headEdge.fNext = edge;
+ headEdge.fFirstY = kEDGE_HEAD_Y;
+ headEdge.fX = SK_MinS32;
+ edge->fPrev = &headEdge;
+
+ tailEdge.fPrev = last;
+ tailEdge.fNext = NULL;
+ 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, SkPath::kEvenOdd_FillType, blitter, stop_y, NULL);
+}
+
+void SkScan::FillTriangle(const SkPoint pts[], const SkRegion* clip,
+ SkBlitter* blitter) {
+ if (clip && clip->isEmpty()) {
+ return;
+ }
+
+ SkRect r;
+ SkIRect ir;
+ r.set(pts, 3);
+ r.round(&ir);
+ if (ir.isEmpty()) {
+ return;
+ }
+
+ SkScanClipper clipper(blitter, clip, ir);
+
+ blitter = clipper.getBlitter();
+ if (NULL != blitter) {
+ sk_fill_triangle(pts, clipper.getClipRect(), blitter, ir);
+ }
+}
+
diff --git a/src/core/SkShader.cpp b/src/core/SkShader.cpp
new file mode 100644
index 0000000000..dd9c8590b8
--- /dev/null
+++ b/src/core/SkShader.cpp
@@ -0,0 +1,284 @@
+/* libs/graphics/sgl/SkShader.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkShader.h"
+#include "SkPaint.h"
+
+SkShader::SkShader() : fLocalMatrix(NULL) {
+ SkDEBUGCODE(fInSession = false;)
+}
+
+SkShader::SkShader(SkFlattenableReadBuffer& buffer)
+ : INHERITED(buffer), fLocalMatrix(NULL) {
+ if (buffer.readBool()) {
+ SkMatrix matrix;
+ buffer.read(&matrix, sizeof(matrix));
+ setLocalMatrix(matrix);
+ }
+ SkDEBUGCODE(fInSession = false;)
+}
+
+SkShader::~SkShader() {
+ SkASSERT(!fInSession);
+ sk_free(fLocalMatrix);
+}
+
+void SkShader::beginSession() {
+ SkASSERT(!fInSession);
+ SkDEBUGCODE(fInSession = true;)
+}
+
+void SkShader::endSession() {
+ SkASSERT(fInSession);
+ SkDEBUGCODE(fInSession = false;)
+}
+
+void SkShader::flatten(SkFlattenableWriteBuffer& buffer) {
+ this->INHERITED::flatten(buffer);
+ buffer.writeBool(fLocalMatrix != NULL);
+ if (fLocalMatrix) {
+ buffer.writeMul4(fLocalMatrix, sizeof(SkMatrix));
+ }
+}
+
+bool SkShader::getLocalMatrix(SkMatrix* localM) const {
+ if (fLocalMatrix) {
+ if (localM) {
+ *localM = *fLocalMatrix;
+ }
+ return true;
+ } else {
+ if (localM) {
+ localM->reset();
+ }
+ return false;
+ }
+}
+
+void SkShader::setLocalMatrix(const SkMatrix& localM) {
+ if (localM.isIdentity()) {
+ this->resetLocalMatrix();
+ } else {
+ if (fLocalMatrix == NULL) {
+ fLocalMatrix = (SkMatrix*)sk_malloc_throw(sizeof(SkMatrix));
+ }
+ *fLocalMatrix = localM;
+ }
+}
+
+void SkShader::resetLocalMatrix() {
+ if (fLocalMatrix) {
+ sk_free(fLocalMatrix);
+ fLocalMatrix = NULL;
+ }
+}
+
+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)) {
+ fTotalInverseClass = (uint8_t)ComputeMatrixClass(fTotalInverse);
+ return true;
+ }
+ return false;
+}
+
+#include "SkColorPriv.h"
+
+void SkShader::shadeSpan16(int x, int y, uint16_t span16[], int count) {
+ SkASSERT(span16);
+ SkASSERT(count > 0);
+ SkASSERT(this->canCallShadeSpan16());
+
+ // basically, if we get here, the subclass screwed up
+ SkASSERT(!"kHasSpan16 flag is set, but shadeSpan16() 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, uint8_t alpha[], int count) {
+ SkASSERT(count > 0);
+
+ SkPMColor colors[kTempColorCount];
+
+ while ((count -= kTempColorCount) >= 0) {
+ this->shadeSpan(x, y, colors, kTempColorCount);
+ x += kTempColorCount;
+
+ const uint8_t* srcA = (const uint8_t*)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 uint8_t* srcA = (const uint8_t*)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 uint8_t* srcA = (const uint8_t*)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, NULL, NULL)) {
+ mc = kFixedStepInX_MatrixClass;
+ } else {
+ mc = kPerspective_MatrixClass;
+ }
+ }
+ return mc;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+bool SkShader::asABitmap(SkBitmap*, SkMatrix*, TileMode*) {
+ return false;
+}
+
+SkShader* SkShader::CreateBitmapShader(const SkBitmap& src,
+ TileMode tmx, TileMode tmy) {
+ return SkShader::CreateBitmapShader(src, tmx, tmy, NULL, 0);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+#include "SkColorShader.h"
+#include "SkUtils.h"
+
+SkColorShader::SkColorShader(SkFlattenableReadBuffer& b) : INHERITED(b) {
+ fInheritColor = b.readU8();
+ if (fInheritColor) {
+ return;
+ }
+ fColor = b.readU32();
+}
+
+void SkColorShader::flatten(SkFlattenableWriteBuffer& buffer) {
+ this->INHERITED::flatten(buffer);
+ buffer.write8(fInheritColor);
+ if (fInheritColor) {
+ return;
+ }
+ buffer.write32(fColor);
+}
+
+uint32_t SkColorShader::getFlags() {
+ return (SkGetPackedA32(fPMColor) == 255 ? kOpaqueAlpha_Flag : 0) |
+ kHasSpan16_Flag;
+}
+
+uint8_t SkColorShader::getSpan16Alpha() const {
+ return SkGetPackedA32(fPMColor);
+}
+
+bool SkColorShader::setContext(const SkBitmap& device, const SkPaint& paint,
+ const SkMatrix& matrix) {
+ if (!this->INHERITED::setContext(device, paint, matrix)) {
+ return false;
+ }
+
+ SkColor c;
+ unsigned a;
+
+ if (fInheritColor) {
+ c = paint.getColor();
+ a = SkColorGetA(c);
+ } else {
+ c = fColor;
+ a = SkAlphaMul(SkColorGetA(c), SkAlpha255To256(paint.getAlpha()));
+ }
+
+ unsigned r = SkColorGetR(c);
+ unsigned g = SkColorGetG(c);
+ unsigned b = SkColorGetB(c);
+
+ // we want this before we apply any alpha
+ fColor16 = SkPack888ToRGB16(r, g, b);
+
+ if (a != 255) {
+ a = SkAlpha255To256(a);
+ r = SkAlphaMul(r, a);
+ g = SkAlphaMul(g, a);
+ b = SkAlphaMul(b, a);
+ }
+ fPMColor = SkPackARGB32(a, r, g, b);
+
+ return true;
+}
+
+void SkColorShader::shadeSpan(int x, int y, SkPMColor span[], int count) {
+ sk_memset32(span, fPMColor, count);
+}
+
+void SkColorShader::shadeSpan16(int x, int y, uint16_t span[], int count) {
+ sk_memset16(span, fColor16, count);
+}
+
+void SkColorShader::shadeSpanAlpha(int x, int y, uint8_t alpha[], int count) {
+ memset(alpha, SkGetPackedA32(fPMColor), count);
+}
+
diff --git a/src/core/SkSinTable.h b/src/core/SkSinTable.h
new file mode 100644
index 0000000000..9b4477d2d5
--- /dev/null
+++ b/src/core/SkSinTable.h
@@ -0,0 +1,285 @@
+/* libs/corecg/SkSinTable.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#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 uint16_t 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/src/core/SkSpriteBlitter.h b/src/core/SkSpriteBlitter.h
new file mode 100644
index 0000000000..9f7764df82
--- /dev/null
+++ b/src/core/SkSpriteBlitter.h
@@ -0,0 +1,55 @@
+/* libs/graphics/sgl/SkSpriteBlitter.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef SkSpriteBlitter_DEFINED
+#define SkSpriteBlitter_DEFINED
+
+#include "SkBlitter.h"
+#include "SkBitmap.h"
+
+class SkPaint;
+
+class SkSpriteBlitter : public SkBlitter {
+public:
+ SkSpriteBlitter(const SkBitmap& source);
+ virtual ~SkSpriteBlitter();
+
+ virtual void setup(const SkBitmap& device, int left, int top,
+ const SkPaint& paint);
+
+ // overrides
+#ifdef SK_DEBUG
+ virtual void blitH(int x, int y, int width);
+ virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]);
+ virtual void blitV(int x, int y, int height, SkAlpha alpha);
+ virtual void blitMask(const SkMask&, const SkIRect& clip);
+#endif
+
+ static SkSpriteBlitter* ChooseD16(const SkBitmap& source, const SkPaint&,
+ void* storage, size_t storageSize);
+ static SkSpriteBlitter* ChooseD32(const SkBitmap& source, const SkPaint&,
+ void* storage, size_t storageSize);
+
+protected:
+ const SkBitmap* fDevice;
+ const SkBitmap* fSource;
+ int fLeft, fTop;
+ const SkPaint* fPaint;
+};
+
+#endif
+
diff --git a/src/core/SkSpriteBlitterTemplate.h b/src/core/SkSpriteBlitterTemplate.h
new file mode 100644
index 0000000000..2ab2b3a5ca
--- /dev/null
+++ b/src/core/SkSpriteBlitterTemplate.h
@@ -0,0 +1,91 @@
+/* libs/graphics/sgl/SkSpriteBlitterTemplate.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+
+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;
+ SK_RESTRICT SkSPRITE_DST_TYPE* dst =fDevice->SkSPRITE_DST_GETADDR(x, y);
+ const SK_RESTRICT 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
+
+#ifdef SkSPRITE_ROW_PROC
+ SkSPRITE_ROW_PROC(d, s, width, x, y);
+#else
+ int w = width;
+ do {
+ SkSPRITE_SRC_TYPE sc = *s++;
+ SkSPRITE_BLIT_PIXEL(d, sc);
+ d += 1;
+ } while (--w != 0);
+#endif
+ dst = (SK_RESTRICT SkSPRITE_DST_TYPE*)((char*)dst + dstRB);
+ src = (const SK_RESTRICT SkSPRITE_SRC_TYPE*)
+ ((const char*)src + srcRB);
+ SkSPRITE_NEXT_ROW
+#ifdef SkSPRITE_ROW_PROC
+ y += 1;
+#endif
+ } 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
+
+#ifdef SkSPRITE_ROW_PROC
+ #undef SkSPRITE_ROW_PROC
+#endif
+
diff --git a/src/core/SkSpriteBlitter_ARGB32.cpp b/src/core/SkSpriteBlitter_ARGB32.cpp
new file mode 100644
index 0000000000..6addde7b0f
--- /dev/null
+++ b/src/core/SkSpriteBlitter_ARGB32.cpp
@@ -0,0 +1,314 @@
+/* libs/graphics/sgl/SkSpriteBlitter_ARGB32.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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); \
+ uint32_t 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);
+ SK_RESTRICT uint32_t* dst = fDevice->getAddr32(x, y);
+ const SK_RESTRICT 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 = (SK_RESTRICT uint32_t*)((char*)dst + dstRB);
+ src = (const SK_RESTRICT uint32_t*)((const char*)src + srcRB);
+ } while (--height != 0);
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkColorFilter.h"
+#include "SkXfermode.h"
+
+class Sprite_D32_XferFilter : public SkSpriteBlitter {
+public:
+ Sprite_D32_XferFilter(const SkBitmap& source, const SkPaint& paint)
+ : SkSpriteBlitter(source) {
+ fColorFilter = paint.getColorFilter();
+ fColorFilter->safeRef();
+
+ fXfermode = paint.getXfermode();
+ fXfermode->safeRef();
+
+ fBufferSize = 0;
+ fBuffer = NULL;
+ }
+
+ virtual ~Sprite_D32_XferFilter() {
+ delete[] fBuffer;
+ fXfermode->safeUnref();
+ fColorFilter->safeUnref();
+ }
+
+ virtual void setup(const SkBitmap& device, int left, int top,
+ const SkPaint& paint) {
+ this->INHERITED::setup(device, left, top, paint);
+
+ int width = device.width();
+ if (width > fBufferSize) {
+ fBufferSize = width;
+ delete[] fBuffer;
+ fBuffer = new SkPMColor[width];
+ }
+ }
+
+protected:
+ SkColorFilter* fColorFilter;
+ SkXfermode* fXfermode;
+ int fBufferSize;
+ SkPMColor* fBuffer;
+
+private:
+ typedef SkSpriteBlitter INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class Sprite_D32_S32A_XferFilter : public Sprite_D32_XferFilter {
+public:
+ Sprite_D32_S32A_XferFilter(const SkBitmap& source, const SkPaint& paint)
+ : Sprite_D32_XferFilter(source, paint) {}
+
+ virtual void blitRect(int x, int y, int width, int height) {
+ SkASSERT(width > 0 && height > 0);
+ SK_RESTRICT uint32_t* dst = fDevice->getAddr32(x, y);
+ const SK_RESTRICT uint32_t* src = fSource->getAddr32(x - fLeft,
+ y - fTop);
+ unsigned dstRB = fDevice->rowBytes();
+ unsigned srcRB = fSource->rowBytes();
+ SkColorFilter* colorFilter = fColorFilter;
+ SkXfermode* xfermode = fXfermode;
+
+ do {
+ const SkPMColor* tmp = src;
+
+ if (NULL != colorFilter) {
+ colorFilter->filterSpan(src, width, fBuffer);
+ tmp = fBuffer;
+ }
+
+ if (NULL != xfermode) {
+ xfermode->xfer32(dst, tmp, width, NULL);
+ } else {
+ for (int i = 0; i < width; i++) {
+ dst[i] = SkPMSrcOver(tmp[i], dst[i]);
+ }
+ }
+
+ dst = (SK_RESTRICT uint32_t*)((char*)dst + dstRB);
+ src = (const SK_RESTRICT uint32_t*)((const char*)src + srcRB);
+ } while (--height != 0);
+ }
+
+private:
+ typedef Sprite_D32_XferFilter INHERITED;
+};
+
+static void fillbuffer(SK_RESTRICT SkPMColor dst[],
+ const SK_RESTRICT SkPMColor16 src[], int count) {
+ SkASSERT(count > 0);
+
+ do {
+ *dst++ = SkPixel4444ToPixel32(*src++);
+ } while (--count != 0);
+}
+
+class Sprite_D32_S4444_XferFilter : public Sprite_D32_XferFilter {
+public:
+ Sprite_D32_S4444_XferFilter(const SkBitmap& source, const SkPaint& paint)
+ : Sprite_D32_XferFilter(source, paint) {}
+
+ virtual void blitRect(int x, int y, int width, int height) {
+ SkASSERT(width > 0 && height > 0);
+ SK_RESTRICT SkPMColor* dst = fDevice->getAddr32(x, y);
+ const SK_RESTRICT SkPMColor16* src = fSource->getAddr16(x - fLeft,
+ y - fTop);
+ unsigned dstRB = fDevice->rowBytes();
+ unsigned srcRB = fSource->rowBytes();
+ SK_RESTRICT SkPMColor* buffer = fBuffer;
+ SkColorFilter* colorFilter = fColorFilter;
+ SkXfermode* xfermode = fXfermode;
+
+ do {
+ fillbuffer(buffer, src, width);
+
+ if (NULL != colorFilter) {
+ colorFilter->filterSpan(buffer, width, buffer);
+ }
+ if (NULL != xfermode) {
+ xfermode->xfer32(dst, buffer, width, NULL);
+ } else {
+ for (int i = 0; i < width; i++) {
+ dst[i] = SkPMSrcOver(buffer[i], dst[i]);
+ }
+ }
+
+ dst = (SK_RESTRICT SkPMColor*)((char*)dst + dstRB);
+ src = (const SK_RESTRICT SkPMColor16*)((const char*)src + srcRB);
+ } while (--height != 0);
+ }
+
+private:
+ typedef Sprite_D32_XferFilter INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void src_row(SK_RESTRICT SkPMColor dst[],
+ const SK_RESTRICT SkPMColor16 src[], int count) {
+ do {
+ *dst = SkPixel4444ToPixel32(*src);
+ src += 1;
+ dst += 1;
+ } while (--count != 0);
+}
+
+class Sprite_D32_S4444_Opaque : public SkSpriteBlitter {
+public:
+ Sprite_D32_S4444_Opaque(const SkBitmap& source) : SkSpriteBlitter(source) {}
+
+ virtual void blitRect(int x, int y, int width, int height) {
+ SkASSERT(width > 0 && height > 0);
+ SK_RESTRICT SkPMColor* dst = fDevice->getAddr32(x, y);
+ const SK_RESTRICT SkPMColor16* src = fSource->getAddr16(x - fLeft,
+ y - fTop);
+ unsigned dstRB = fDevice->rowBytes();
+ unsigned srcRB = fSource->rowBytes();
+
+ do {
+ src_row(dst, src, width);
+ dst = (SK_RESTRICT SkPMColor*)((char*)dst + dstRB);
+ src = (const SK_RESTRICT SkPMColor16*)((const char*)src + srcRB);
+ } while (--height != 0);
+ }
+};
+
+static void srcover_row(SK_RESTRICT SkPMColor dst[],
+ const SK_RESTRICT SkPMColor16 src[], int count) {
+ do {
+ *dst = SkPMSrcOver(SkPixel4444ToPixel32(*src), *dst);
+ src += 1;
+ dst += 1;
+ } while (--count != 0);
+}
+
+class Sprite_D32_S4444 : public SkSpriteBlitter {
+public:
+ Sprite_D32_S4444(const SkBitmap& source) : SkSpriteBlitter(source) {}
+
+ virtual void blitRect(int x, int y, int width, int height) {
+ SkASSERT(width > 0 && height > 0);
+ SK_RESTRICT SkPMColor* dst = fDevice->getAddr32(x, y);
+ const SK_RESTRICT SkPMColor16* src = fSource->getAddr16(x - fLeft,
+ y - fTop);
+ unsigned dstRB = fDevice->rowBytes();
+ unsigned srcRB = fSource->rowBytes();
+
+ do {
+ srcover_row(dst, src, width);
+ dst = (SK_RESTRICT SkPMColor*)((char*)dst + dstRB);
+ src = (const SK_RESTRICT SkPMColor16*)((const char*)src + srcRB);
+ } while (--height != 0);
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkTemplatesPriv.h"
+
+SkSpriteBlitter* SkSpriteBlitter::ChooseD32(const SkBitmap& source,
+ const SkPaint& paint,
+ void* storage, size_t storageSize) {
+ if (paint.getMaskFilter() != NULL || paint.getAlpha() != 0xFF) {
+ return NULL;
+ }
+
+ SkXfermode* xfermode = paint.getXfermode();
+ SkColorFilter* filter = paint.getColorFilter();
+ SkSpriteBlitter* blitter = NULL;
+
+ switch (source.getConfig()) {
+ case SkBitmap::kARGB_4444_Config:
+ if (xfermode || filter) {
+ SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D32_S4444_XferFilter,
+ storage, storageSize, (source, paint));
+ } else if (source.isOpaque()) {
+ SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D32_S4444_Opaque,
+ storage, storageSize, (source));
+ } else {
+ SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D32_S4444,
+ storage, storageSize, (source));
+ }
+ break;
+ case SkBitmap::kARGB_8888_Config:
+ if (xfermode || filter) {
+ SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D32_S32A_XferFilter,
+ storage, storageSize, (source, paint));
+ } else 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/src/core/SkSpriteBlitter_RGB16.cpp b/src/core/SkSpriteBlitter_RGB16.cpp
new file mode 100644
index 0000000000..a158637b87
--- /dev/null
+++ b/src/core/SkSpriteBlitter_RGB16.cpp
@@ -0,0 +1,320 @@
+/* libs/graphics/sgl/SkSpriteBlitter_RGB16.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkSpriteBlitter.h"
+#include "SkBlitRow.h"
+#include "SkTemplates.h"
+#include "SkUtils.h"
+#include "SkColorPriv.h"
+
+#define D16_S32A_Opaque_Pixel(dst, sc) \
+do { \
+ if (sc) { \
+ *dst = SkSrcOver32To16(sc, *dst); \
+ } \
+} while (0)
+
+static inline void D16_S32A_Blend_Pixel_helper(uint16_t* dst, SkPMColor sc,
+ unsigned src_scale) {
+ uint16_t dc = *dst;
+ unsigned sa = SkGetPackedA32(sc);
+ unsigned dr, dg, db;
+
+ if (255 == sa) {
+ 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)
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+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) {
+ SK_RESTRICT uint16_t* dst = fDevice->getAddr16(x, y);
+ const SK_RESTRICT 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 = SkBlendRGB16(sc, dc, scale); \
+ } while (0)
+
+#define SkSPRITE_CLASSNAME Sprite_D16_S16_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 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 D16_S4444_Opaque(dst, sc) \
+ do { \
+ uint16_t dc = *dst; \
+ *dst = SkSrcOver4444To16(sc, dc); \
+ } while (0)
+
+#define SkSPRITE_CLASSNAME Sprite_D16_S4444_Opaque
+#define SkSPRITE_ARGS
+#define SkSPRITE_FIELDS
+#define SkSPRITE_INIT
+#define SkSPRITE_DST_TYPE uint16_t
+#define SkSPRITE_SRC_TYPE SkPMColor16
+#define SkSPRITE_DST_GETADDR getAddr16
+#define SkSPRITE_SRC_GETADDR getAddr16
+#define SkSPRITE_PREAMBLE(srcBM, x, y)
+#define SkSPRITE_BLIT_PIXEL(dst, src) D16_S4444_Opaque(dst, src)
+#define SkSPRITE_NEXT_ROW
+#define SkSPRITE_POSTAMBLE(srcBM)
+#include "SkSpriteBlitterTemplate.h"
+
+#define D16_S4444_Blend(dst, sc, scale16) \
+ do { \
+ uint16_t dc = *dst; \
+ *dst = SkBlend4444To16(sc, dc, scale16); \
+ } while (0)
+
+
+#define SkSPRITE_CLASSNAME Sprite_D16_S4444_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 uint16_t
+#define SkSPRITE_DST_GETADDR getAddr16
+#define SkSPRITE_SRC_GETADDR getAddr16
+#define SkSPRITE_PREAMBLE(srcBM, x, y) int scale = SkAlpha15To16(fSrcAlpha);
+#define SkSPRITE_BLIT_PIXEL(dst, src) D16_S4444_Blend(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"
+
+///////////////////////////////////////////////////////////////////////////////
+
+class Sprite_D16_S32_BlitRowProc : public SkSpriteBlitter {
+public:
+ Sprite_D16_S32_BlitRowProc(const SkBitmap& source)
+ : SkSpriteBlitter(source) {}
+
+ // overrides
+
+ virtual void setup(const SkBitmap& device, int left, int top,
+ const SkPaint& paint) {
+ this->INHERITED::setup(device, left, top, paint);
+
+ unsigned flags = 0;
+
+ if (paint.getAlpha() < 0xFF) {
+ flags |= SkBlitRow::kGlobalAlpha_Flag;
+ }
+ if (!fSource->isOpaque()) {
+ flags |= SkBlitRow::kSrcPixelAlpha_Flag;
+ }
+ if (paint.isDither()) {
+ flags |= SkBlitRow::kDither_Flag;
+ }
+ fProc = SkBlitRow::Factory(flags, SkBitmap::kRGB_565_Config);
+ }
+
+ virtual void blitRect(int x, int y, int width, int height) {
+ SK_RESTRICT uint16_t* dst = fDevice->getAddr16(x, y);
+ const SK_RESTRICT SkPMColor* src = fSource->getAddr32(x - fLeft,
+ y - fTop);
+ unsigned dstRB = fDevice->rowBytes();
+ unsigned srcRB = fSource->rowBytes();
+ SkBlitRow::Proc proc = fProc;
+ U8CPU alpha = fPaint->getAlpha();
+
+ while (--height >= 0) {
+ proc(dst, src, width, alpha, x, y);
+ y += 1;
+ dst = (SK_RESTRICT uint16_t*)((char*)dst + dstRB);
+ src = (const SK_RESTRICT SkPMColor*)((const char*)src + srcRB);
+ }
+ }
+
+private:
+ SkBlitRow::Proc fProc;
+
+ typedef SkSpriteBlitter INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkTemplatesPriv.h"
+
+SkSpriteBlitter* SkSpriteBlitter::ChooseD16(const SkBitmap& source,
+ const SkPaint& paint,
+ void* storage, size_t storageSize) {
+ if (paint.getMaskFilter() != NULL) { // may add cases for this
+ return NULL;
+ }
+ if (paint.getXfermode() != NULL) { // may add cases for this
+ return NULL;
+ }
+ if (paint.getColorFilter() != NULL) { // may add cases for this
+ return NULL;
+ }
+
+ SkSpriteBlitter* blitter = NULL;
+ unsigned alpha = paint.getAlpha();
+
+ switch (source.getConfig()) {
+ case SkBitmap::kARGB_8888_Config:
+ SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_S32_BlitRowProc,
+ storage, storageSize, (source));
+ break;
+ case SkBitmap::kARGB_4444_Config:
+ if (255 == alpha) {
+ SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_S4444_Opaque,
+ storage, storageSize, (source));
+ } else {
+ SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_S4444_Blend,
+ storage, storageSize, (source, alpha >> 4));
+ }
+ break;
+ case SkBitmap::kRGB_565_Config:
+ if (255 == alpha) {
+ 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 (source.isOpaque()) {
+ if (255 == alpha) {
+ 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 (255 == alpha) {
+ 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/src/core/SkString.cpp b/src/core/SkString.cpp
new file mode 100644
index 0000000000..2e5d9466a1
--- /dev/null
+++ b/src/core/SkString.cpp
@@ -0,0 +1,642 @@
+/* libs/graphics/sgl/SkString.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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);
+ if (x == 10000) {
+ x -= 1;
+ }
+ *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]
+const SkString::Rec SkString::gEmptyRec = { 0, 0, 0 };
+
+#define SizeOfRec() (gEmptyRec.data() - (const char*)&gEmptyRec)
+
+SkString::Rec* SkString::AllocRec(const char text[], U16CPU len)
+{
+ Rec* rec;
+
+ if (len == 0)
+ rec = const_cast<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(SizeOfRec() + 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 != &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(gEmptyRec.fLength == 0);
+ SkASSERT(gEmptyRec.fRefCnt == 0);
+ SkASSERT(gEmptyRec.data()[0] == 0);
+
+ if (fRec != &gEmptyRec)
+ {
+ SkASSERT(fRec->fLength > 0);
+ SkASSERT(fRec->fRefCnt > 0);
+ SkASSERT(fRec->data()[fRec->fLength] == 0);
+ }
+ SkASSERT(fStr == c_str());
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////
+
+SkString::SkString() : fRec(const_cast<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(NULL, (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 != NULL);
+
+ 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 = const_cast<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 uint16_t src[])
+{
+ int count = 0;
+
+ while (src[count])
+ count += 1;
+ setUTF16(src, count);
+}
+
+void SkString::setUTF16(const uint16_t src[], size_t count)
+{
+ 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 (size_t i = 0; i < count; i++)
+ p[i] = SkToU8(src[i]);
+ p[count] = 0;
+ }
+ else
+ {
+ SkString tmp(count); // puts a null terminator at the end of the string
+ char* p = tmp.writable_str();
+
+ for (size_t 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, int32_t dec)
+{
+ char buffer[SkStrAppendS32_MaxSize];
+ char* stop = SkStrAppendS32(buffer, dec);
+ this->insert(offset, buffer, stop - buffer);
+}
+
+void SkString::insertHex(size_t offset, uint32_t 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 <stdio.h>
+
+// number of bytes (on the stack) to receive the printf result
+static const size_t kBufferSize = 256;
+
+#ifdef SK_BUILD_FOR_WIN
+ #define VSNPRINTF _vsnprintf
+#else
+ #define VSNPRINTF vsnprintf
+#endif
+
+#define ARGS_TO_BUFFER(format, buffer, size) \
+ do { \
+ va_list args; \
+ va_start(args, format); \
+ VSNPRINTF(buffer, size, format, args); \
+ va_end(args); \
+ } while (0)
+
+void SkString::printf(const char format[], ...) {
+ char buffer[kBufferSize];
+ ARGS_TO_BUFFER(format, buffer, kBufferSize);
+
+ this->set(buffer, strlen(buffer));
+}
+
+void SkString::appendf(const char format[], ...) {
+ char buffer[kBufferSize];
+ ARGS_TO_BUFFER(format, buffer, kBufferSize);
+
+ this->append(buffer, strlen(buffer));
+}
+
+void SkString::prependf(const char format[], ...) {
+ char buffer[kBufferSize];
+ ARGS_TO_BUFFER(format, buffer, kBufferSize);
+
+ this->prepend(buffer, strlen(buffer));
+}
+
+#undef VSNPRINTF
+
+///////////////////////////////////////////////////////////////////////////
+
+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((int32_t)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 = (uint16_t*)sk_malloc_throw((len + 1) * sizeof(uint16_t));
+
+ uint16_t* 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(NULL, 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/src/core/SkStroke.cpp b/src/core/SkStroke.cpp
new file mode 100644
index 0000000000..86dff488cb
--- /dev/null
+++ b/src/core/SkStroke.cpp
@@ -0,0 +1,645 @@
+/*
+ * Copyright (C) 2006-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "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->setNormalize(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->setNormalize(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 : NULL);
+ fOuter.reversePathTo(fInner);
+ // cap the start
+ fCapper(&fOuter, fFirstPt, -fFirstNormal, fFirstOuterPt,
+ fPrevIsLine ? &fInner : NULL);
+ fOuter.close();
+ }
+ }
+ fInner.reset();
+ fSegmentCount = -1;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkPathStroker::SkPathStroker(SkScalar radius, SkScalar miterLimit,
+ SkPaint::Cap cap, SkPaint::Join join)
+ : fRadius(radius) {
+
+ /* This is only used when join is miter_join, but we initialize it here
+ so that it is always defined, to fis valgrind warnings.
+ */
+ fInvMiterLimit = 0;
+
+ 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.setNormalize(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 = (uint8_t)p.getStrokeCap();
+ fJoin = (uint8_t)p.getStrokeJoin();
+ fDoFill = SkToU8(p.getStyle() == SkPaint::kStrokeAndFill_Style);
+}
+
+SkStroke::SkStroke(const SkPaint& p, SkScalar width) {
+ fWidth = width;
+ fMiterLimit = p.getStrokeMiter();
+ fCap = (uint8_t)p.getStrokeCap();
+ fJoin = (uint8_t)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);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_SCALAR_IS_FIXED
+ /* return non-zero if the path is too big, and should be shrunk to avoid
+ overflows during intermediate calculations. Note that we compute the
+ bounds for this. If we had a custom callback/walker for paths, we could
+ perhaps go faster by using that, and just perform the abs | in that
+ routine
+ */
+ static int needs_to_shrink(const SkPath& path) {
+ SkRect r;
+ path.computeBounds(&r, SkPath::kFast_BoundsType);
+ SkFixed mask = SkAbs32(r.fLeft);
+ mask |= SkAbs32(r.fTop);
+ mask |= SkAbs32(r.fRight);
+ mask |= SkAbs32(r.fBottom);
+ // we need the top 3 bits clear (after abs) to avoid overflow
+ return mask >> 29;
+ }
+
+ static void identity_proc(SkPoint pts[], int count) {}
+ static void shift_down_2_proc(SkPoint pts[], int count) {
+ for (int i = 0; i < count; i++) {
+ pts->fX >>= 2;
+ pts->fY >>= 2;
+ pts += 1;
+ }
+ }
+ #define APPLY_PROC(proc, pts, count) proc(pts, count)
+#else // float does need any of this
+ #define APPLY_PROC(proc, pts, count)
+#endif
+
+void SkStroke::strokePath(const SkPath& src, SkPath* dst) const {
+ SkASSERT(&src != NULL && dst != NULL);
+
+ SkScalar radius = SkScalarHalf(fWidth);
+
+ dst->reset();
+ if (radius <= 0) {
+ return;
+ }
+
+#ifdef SK_SCALAR_IS_FIXED
+ void (*proc)(SkPoint pts[], int count) = identity_proc;
+ if (needs_to_shrink(src)) {
+ proc = shift_down_2_proc;
+ radius >>= 2;
+ if (radius == 0) {
+ return;
+ }
+ }
+#endif
+
+ SkPathStroker stroker(radius, 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:
+ APPLY_PROC(proc, &pts[0], 1);
+ stroker.moveTo(pts[0]);
+ break;
+ case SkPath::kLine_Verb:
+ APPLY_PROC(proc, &pts[1], 1);
+ stroker.lineTo(pts[1]);
+ lastSegment = verb;
+ break;
+ case SkPath::kQuad_Verb:
+ APPLY_PROC(proc, &pts[1], 2);
+ stroker.quadTo(pts[1], pts[2]);
+ lastSegment = verb;
+ break;
+ case SkPath::kCubic_Verb:
+ APPLY_PROC(proc, &pts[1], 3);
+ 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);
+
+#ifdef SK_SCALAR_IS_FIXED
+ // undo our previous down_shift
+ if (shift_down_2_proc == proc) {
+ // need a real shift methid on path. antialias paths could use this too
+ SkMatrix matrix;
+ matrix.setScale(SkIntToScalar(4), SkIntToScalar(4));
+ dst->transform(matrix);
+ }
+#endif
+
+ 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/src/core/SkStrokerPriv.cpp b/src/core/SkStrokerPriv.cpp
new file mode 100644
index 0000000000..07833ca112
--- /dev/null
+++ b/src/core/SkStrokerPriv.cpp
@@ -0,0 +1,275 @@
+/* libs/graphics/sgl/SkStrokerPriv.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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 HandleInnerJoin(SkPath* inner, const SkPoint& pivot, const SkVector& after)
+{
+#if 1
+ /* In the degenerate case that the stroke radius is larger than our segments
+ just connecting the two inner segments may "show through" as a funny
+ diagonal. To pseudo-fix this, we go through the pivot point. This adds
+ an extra point/edge, but I can't see a cheap way to know when this is
+ not needed :(
+ */
+ inner->lineTo(pivot.fX, pivot.fY);
+#endif
+
+ inner->lineTo(pivot.fX - after.fX, pivot.fY - after.fY);
+}
+
+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);
+
+ if (!is_clockwise(beforeUnitNormal, afterUnitNormal))
+ {
+ SkTSwap<SkPath*>(outer, inner);
+ after.negate();
+ }
+
+ outer->lineTo(pivot.fX + after.fX, pivot.fY + after.fY);
+ HandleInnerJoin(inner, pivot, after);
+}
+
+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);
+ matrix.postTranslate(pivot.fX, pivot.fY);
+ int count = SkBuildQuadArc(before, after, dir, &matrix, pts);
+ SkASSERT((count & 1) == 1);
+
+ if (count > 1)
+ {
+ 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);
+ HandleInnerJoin(inner, pivot, after);
+ }
+}
+
+#ifdef SK_SCALAR_IS_FLOAT
+ #define kOneOverSqrt2 (0.707106781f)
+#else
+ #define kOneOverSqrt2 (46341)
+#endif
+
+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;
+ }
+
+ ccw = !is_clockwise(before, after);
+ if (ccw)
+ {
+ SkTSwap<SkPath*>(outer, inner);
+ before.negate();
+ after.negate();
+ }
+
+ /* Before we enter the world of square-roots and divides,
+ check if we're trying to join an upright right angle
+ (common case for stroking rectangles). If so, special case
+ that (for speed an accuracy).
+ Note: we only need to check one normal if dot==0
+ */
+ if (0 == dotProd && invMiterLimit <= kOneOverSqrt2)
+ {
+ mid.set(SkScalarMul(before.fX + after.fX, radius),
+ SkScalarMul(before.fY + after.fY, radius));
+ goto DO_MITER;
+ }
+
+ /* 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;
+ }
+
+ // 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));
+DO_MITER:
+ 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);
+ HandleInnerJoin(inner, pivot, after);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+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/src/core/SkStrokerPriv.h b/src/core/SkStrokerPriv.h
new file mode 100644
index 0000000000..ecb9bde5b1
--- /dev/null
+++ b/src/core/SkStrokerPriv.h
@@ -0,0 +1,50 @@
+/* libs/graphics/sgl/SkStrokerPriv.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#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/src/core/SkTSearch.cpp b/src/core/SkTSearch.cpp
new file mode 100644
index 0000000000..bab348f6e2
--- /dev/null
+++ b/src/core/SkTSearch.cpp
@@ -0,0 +1,219 @@
+/* libs/graphics/sgl/SkTSearch.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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)
+{
+ if (count <= 0)
+ return ~0;
+
+ SkASSERT(base != NULL);
+
+ 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);
+}
+
+int SkStrLCSearch(const char*const* base, int count, const char target[],
+ size_t len, size_t elemSize)
+{
+ SkASSERT(target);
+
+ SkAutoAsciiToLC tolc(target, len);
+
+ return SkStrSearch(base, count, tolc.lc(), len, elemSize);
+}
+
+int SkStrLCSearch(const char*const* base, int count, const char target[],
+ size_t elemSize)
+{
+ return SkStrLCSearch(base, count, target, strlen(target), elemSize);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+SkAutoAsciiToLC::SkAutoAsciiToLC(const char str[], size_t len)
+{
+ // see if we need to compute the length
+ if ((long)len < 0) {
+ len = strlen(str);
+ }
+ fLength = len;
+
+ // assign lc to our preallocated storage if len is small enough, or allocate
+ // it on the heap
+ char* lc;
+ if (len <= STORAGE) {
+ lc = fStorage;
+ } else {
+ lc = (char*)sk_malloc_throw(len + 1);
+ }
+ fLC = lc;
+
+ // convert any asii to lower-case. we let non-ascii (utf8) chars pass
+ // through unchanged
+ for (int i = (int)(len - 1); i >= 0; --i) {
+ int c = str[i];
+ if ((c & 0x80) == 0) { // is just ascii
+ c = tolower(c);
+ }
+ lc[i] = c;
+ }
+ lc[len] = 0;
+}
+
+SkAutoAsciiToLC::~SkAutoAsciiToLC()
+{
+ if (fLC != fStorage) {
+ sk_free(fLC);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+#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/src/core/SkTSort.h b/src/core/SkTSort.h
new file mode 100644
index 0000000000..fba49e214f
--- /dev/null
+++ b/src/core/SkTSort.h
@@ -0,0 +1,65 @@
+/* libs/graphics/sgl/SkTSort.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#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/src/core/SkTemplatesPriv.h b/src/core/SkTemplatesPriv.h
new file mode 100644
index 0000000000..b0a95a0343
--- /dev/null
+++ b/src/core/SkTemplatesPriv.h
@@ -0,0 +1,84 @@
+/* libs/graphics/sgl/SkTemplatesPriv.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#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 = NULL;
+ return obj;
+ }
+private:
+ T* fObj;
+ void* fStorage;
+};
+
+#endif
diff --git a/src/core/SkTypeface.cpp b/src/core/SkTypeface.cpp
new file mode 100644
index 0000000000..9821c51eba
--- /dev/null
+++ b/src/core/SkTypeface.cpp
@@ -0,0 +1,64 @@
+#include "SkTypeface.h"
+#include "SkFontHost.h"
+
+static const SkTypeface* resolve_null_typeface(const SkTypeface* face)
+{
+ if (NULL == face) {
+ face = SkFontHost::FindTypeface(NULL, NULL, SkTypeface::kNormal);
+ SkASSERT(face);
+ }
+ return face;
+}
+
+uint32_t SkTypeface::UniqueID(const SkTypeface* face)
+{
+ return resolve_null_typeface(face)->uniqueID();
+}
+
+bool SkTypeface::Equal(const SkTypeface* facea, const SkTypeface* faceb)
+{
+ return resolve_null_typeface(facea)->uniqueID() ==
+ resolve_null_typeface(faceb)->uniqueID();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkTypeface* SkTypeface::Create(const char name[], Style style)
+{
+ SkTypeface* face = SkFontHost::FindTypeface(NULL, name, style);
+ face->ref();
+ return face;
+}
+
+SkTypeface* SkTypeface::CreateFromTypeface(const SkTypeface* family, Style s)
+{
+ family = resolve_null_typeface(family);
+ SkTypeface* face = SkFontHost::FindTypeface(family, NULL, s);
+ face->ref();
+ return face;
+}
+
+SkTypeface* SkTypeface::CreateFromStream(SkStream* stream)
+{
+ return SkFontHost::CreateTypeface(stream);
+}
+
+#include "SkMMapStream.h"
+SkTypeface* SkTypeface::CreateFromFile(const char path[])
+{
+ return SkFontHost::CreateTypeface(SkNEW_ARGS(SkMMAPStream, (path)));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkTypeface::serialize(SkWStream* stream) const {
+ SkFontHost::Serialize(this, stream);
+}
+
+SkTypeface* SkTypeface::Deserialize(SkStream* stream) {
+ SkTypeface* face = SkFontHost::Deserialize(stream);
+ face->ref();
+ return face;
+}
+
+
diff --git a/src/core/SkUnPreMultiply.cpp b/src/core/SkUnPreMultiply.cpp
new file mode 100644
index 0000000000..371af32752
--- /dev/null
+++ b/src/core/SkUnPreMultiply.cpp
@@ -0,0 +1,73 @@
+#include "SkUnPreMultiply.h"
+#include "SkColorPriv.h"
+
+SkColor SkUnPreMultiply::PMColorToColor(SkPMColor c) {
+ const unsigned a = SkGetPackedA32(c);
+ const Scale scale = GetScale(a);
+ return SkColorSetARGB(a,
+ ApplyScale(scale, SkGetPackedR32(c)),
+ ApplyScale(scale, SkGetPackedG32(c)),
+ ApplyScale(scale, SkGetPackedB32(c)));
+}
+
+const uint32_t SkUnPreMultiply::gTable[] = {
+ 0x00000000, 0xFF000000, 0x7F800000, 0x55000000, 0x3FC00000, 0x33000000, 0x2A800000, 0x246DB6DB,
+ 0x1FE00000, 0x1C555555, 0x19800000, 0x172E8BA3, 0x15400000, 0x139D89D9, 0x1236DB6E, 0x11000000,
+ 0x0FF00000, 0x0F000000, 0x0E2AAAAB, 0x0D6BCA1B, 0x0CC00000, 0x0C249249, 0x0B9745D1, 0x0B1642C8,
+ 0x0AA00000, 0x0A333333, 0x09CEC4EC, 0x0971C71C, 0x091B6DB7, 0x08CB08D4, 0x08800000, 0x0839CE74,
+ 0x07F80000, 0x07BA2E8C, 0x07800000, 0x07492492, 0x07155555, 0x06E45307, 0x06B5E50D, 0x0689D89E,
+ 0x06600000, 0x063831F4, 0x06124925, 0x05EE23B9, 0x05CBA2E9, 0x05AAAAAB, 0x058B2164, 0x056CEFA9,
+ 0x05500000, 0x05343EB2, 0x0519999A, 0x05000000, 0x04E76276, 0x04CFB2B8, 0x04B8E38E, 0x04A2E8BA,
+ 0x048DB6DB, 0x0479435E, 0x0465846A, 0x045270D0, 0x04400000, 0x042E29F8, 0x041CE73A, 0x040C30C3,
+ 0x03FC0000, 0x03EC4EC5, 0x03DD1746, 0x03CE540F, 0x03C00000, 0x03B21643, 0x03A49249, 0x03976FC6,
+ 0x038AAAAB, 0x037E3F20, 0x03722983, 0x03666666, 0x035AF287, 0x034FCACE, 0x0344EC4F, 0x033A5441,
+ 0x03300000, 0x0325ED09, 0x031C18FA, 0x0312818B, 0x03092492, 0x03000000, 0x02F711DC, 0x02EE5847,
+ 0x02E5D174, 0x02DD7BAF, 0x02D55555, 0x02CD5CD6, 0x02C590B2, 0x02BDEF7C, 0x02B677D4, 0x02AF286C,
+ 0x02A80000, 0x02A0FD5C, 0x029A1F59, 0x029364D9, 0x028CCCCD, 0x0286562E, 0x02800000, 0x0279C952,
+ 0x0273B13B, 0x026DB6DB, 0x0267D95C, 0x026217ED, 0x025C71C7, 0x0256E62A, 0x0251745D, 0x024C1BAD,
+ 0x0246DB6E, 0x0241B2F9, 0x023CA1AF, 0x0237A6F5, 0x0232C235, 0x022DF2DF, 0x02293868, 0x02249249,
+ 0x02200000, 0x021B810F, 0x021714FC, 0x0212BB51, 0x020E739D, 0x020A3D71, 0x02061862, 0x02020408,
+ 0x01FE0000, 0x01FA0BE8, 0x01F62762, 0x01F25214, 0x01EE8BA3, 0x01EAD3BB, 0x01E72A08, 0x01E38E39,
+ 0x01E00000, 0x01DC7F11, 0x01D90B21, 0x01D5A3EA, 0x01D24925, 0x01CEFA8E, 0x01CBB7E3, 0x01C880E5,
+ 0x01C55555, 0x01C234F7, 0x01BF1F90, 0x01BC14E6, 0x01B914C2, 0x01B61EED, 0x01B33333, 0x01B05161,
+ 0x01AD7943, 0x01AAAAAB, 0x01A7E567, 0x01A5294A, 0x01A27627, 0x019FCBD2, 0x019D2A20, 0x019A90E8,
+ 0x01980000, 0x01957741, 0x0192F685, 0x01907DA5, 0x018E0C7D, 0x018BA2E9, 0x018940C5, 0x0186E5F1,
+ 0x01849249, 0x018245AE, 0x01800000, 0x017DC11F, 0x017B88EE, 0x0179574E, 0x01772C23, 0x01750750,
+ 0x0172E8BA, 0x0170D045, 0x016EBDD8, 0x016CB157, 0x016AAAAB, 0x0168A9B9, 0x0166AE6B, 0x0164B8A8,
+ 0x0162C859, 0x0160DD68, 0x015EF7BE, 0x015D1746, 0x015B3BEA, 0x01596596, 0x01579436, 0x0155C7B5,
+ 0x01540000, 0x01523D04, 0x01507EAE, 0x014EC4EC, 0x014D0FAC, 0x014B5EDD, 0x0149B26D, 0x01480A4B,
+ 0x01466666, 0x0144C6B0, 0x01432B17, 0x0141938C, 0x01400000, 0x013E7064, 0x013CE4A9, 0x013B5CC1,
+ 0x0139D89E, 0x01385831, 0x0136DB6E, 0x01356246, 0x0133ECAE, 0x01327A97, 0x01310BF6, 0x012FA0BF,
+ 0x012E38E4, 0x012CD45A, 0x012B7315, 0x012A150B, 0x0128BA2F, 0x01276276, 0x01260DD6, 0x0124BC45,
+ 0x01236DB7, 0x01222222, 0x0120D97D, 0x011F93BC, 0x011E50D8, 0x011D10C5, 0x011BD37A, 0x011A98EF,
+ 0x0119611A, 0x01182BF3, 0x0116F970, 0x0115C988, 0x01149C34, 0x0113716B, 0x01124925, 0x01112359,
+ 0x01100000, 0x010EDF12, 0x010DC087, 0x010CA458, 0x010B8A7E, 0x010A72F0, 0x01095DA9, 0x01084AA0,
+ 0x010739CE, 0x01062B2E, 0x01051EB8, 0x01041466, 0x01030C31, 0x01020612, 0x01010204, 0x01000000
+};
+
+#ifdef BUILD_DIVIDE_TABLE
+void SkUnPreMultiply_BuildTable() {
+ for (unsigned i = 0; i <= 255; i++) {
+ uint32_t scale;
+
+ if (0 == i) {
+ scale = 0;
+ } else {
+ scale = ((255 << 24) + (i >> 1)) / i;
+ }
+
+ SkDebugf(" 0x%08X,", scale);
+ if ((i & 7) == 7) {
+ SkDebugf("\n");
+ }
+
+ // test the result
+ for (int j = 1; j <= i; j++) {
+ uint32_t test = (j * scale + (1 << 23)) >> 24;
+ uint32_t div = roundf(j * 255.0f / i);
+ int diff = SkAbs32(test - div);
+ SkASSERT(diff <= 1 && test <= 255);
+ }
+ }
+}
+#endif
diff --git a/src/core/SkUtils.cpp b/src/core/SkUtils.cpp
new file mode 100644
index 0000000000..4f5ba6eaf8
--- /dev/null
+++ b/src/core/SkUtils.cpp
@@ -0,0 +1,574 @@
+/* libs/graphics/sgl/SkUtils.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "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
+
+///////////////////////////////////////////////////////////////////////////
+
+void sk_memset16_portable(uint16_t dst[], uint16_t 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)
+ {
+ uint32_t* dst32 = (uint32_t*)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;
+}
+
+void sk_memset32_portable(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);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+/* 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 uint8_t*)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 uint8_t* p = (const uint8_t*)utf8;
+ int c = *p;
+ int hic = c << 24;
+
+ assert_utf8_leadingbyte(c);
+
+ if (hic < 0)
+ {
+ uint32_t mask = (uint32_t)~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 uint8_t* p = (const uint8_t*)*ptr;
+ int c = *p;
+ int hic = c << 24;
+
+ assert_utf8_leadingbyte(c);
+
+ if (hic < 0)
+ {
+ uint32_t mask = (uint32_t)~0x3F;
+ hic <<= 1;
+ do {
+ c = (c << 6) | (*++p & 0x3F);
+ mask <<= 5;
+ } while ((hic <<= 1) < 0);
+ c &= ~mask;
+ }
+ *ptr = (char*)p + 1;
+ return c;
+}
+
+SkUnichar SkUTF8_PrevUnichar(const char** ptr)
+{
+ SkASSERT(NULL != ptr && NULL != *ptr);
+
+ const char* p = *ptr;
+
+ if (*--p & 0x80)
+ while (*--p & 0x40)
+ ;
+
+ *ptr = (char*)p;
+ return SkUTF8_NextUnichar(&p);
+}
+
+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;
+}
+
+SkUnichar SkUTF16_PrevUnichar(const uint16_t** srcPtr)
+{
+ SkASSERT(srcPtr && *srcPtr);
+
+ const uint16_t* src = *srcPtr;
+ SkUnichar c = *--src;
+
+ SkASSERT(!SkUTF16_IsHighSurrogate(c));
+ if (SkUTF16_IsLowSurrogate(c))
+ {
+ unsigned c2 = *--src;
+ SkASSERT(SkUTF16_IsHighSurrogate(c2));
+ c = (c2 << 10) + c + (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;
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+
+#include <stdlib.h>
+
+static int round_to_K(size_t bytes)
+{
+ return (bytes + 512) >> 10;
+}
+
+SkAutoMemoryUsageProbe::SkAutoMemoryUsageProbe(const char label[])
+ : fLabel(label)
+{
+#if 0
+ struct mallinfo mi = mallinfo();
+
+ fBytesAllocated = mi.uordblks;
+#endif
+}
+
+SkAutoMemoryUsageProbe::~SkAutoMemoryUsageProbe()
+{
+#if 0
+ struct mallinfo mi = mallinfo();
+
+ printf("SkAutoMemoryUsageProbe ");
+ if (fLabel)
+ printf("<%s> ", fLabel);
+ printf("delta %dK, current total allocated %dK\n",
+ round_to_K(mi.uordblks - fBytesAllocated),
+ round_to_K(mi.uordblks));
+#endif
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+
+#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/src/core/SkWriter32.cpp b/src/core/SkWriter32.cpp
new file mode 100644
index 0000000000..61d0051bcc
--- /dev/null
+++ b/src/core/SkWriter32.cpp
@@ -0,0 +1,170 @@
+#include "SkWriter32.h"
+
+struct SkWriter32::Block {
+ Block* fNext;
+ size_t fSize;
+ size_t fAllocated;
+
+ size_t available() const { return fSize - fAllocated; }
+ char* base() { return (char*)(this + 1); }
+ const char* base() const { return (const char*)(this + 1); }
+
+ uint32_t* alloc(size_t size)
+ {
+ SkASSERT(SkAlign4(size) == size);
+ SkASSERT(this->available() >= size);
+ void* ptr = this->base() + fAllocated;
+ fAllocated += size;
+ SkASSERT(fAllocated <= fSize);
+ return (uint32_t*)ptr;
+ }
+
+ uint32_t* peek32(size_t offset)
+ {
+ SkASSERT(offset <= fAllocated + 4);
+ void* ptr = this->base() + offset;
+ return (uint32_t*)ptr;
+ }
+
+ static Block* Create(size_t size)
+ {
+ SkASSERT(SkAlign4(size) == size);
+ Block* block = (Block*)sk_malloc_throw(sizeof(Block) + size);
+ block->fNext = NULL;
+ block->fSize = size;
+ block->fAllocated = 0;
+ return block;
+ }
+};
+
+static size_t compute_block_size(size_t currSize, size_t minSize)
+{
+ if (currSize < minSize)
+ currSize = minSize;
+
+ currSize += (currSize >> 1);
+ return SkAlign4(currSize);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkWriter32::~SkWriter32()
+{
+ this->reset();
+}
+
+void SkWriter32::reset()
+{
+ Block* block = fHead;
+ while (block)
+ {
+ Block* next = block->fNext;
+ sk_free(block);
+ block = next;
+ }
+ fHead = fTail = NULL;
+ fSize = 0;
+}
+
+uint32_t* SkWriter32::reserve(size_t size)
+{
+ SkASSERT(SkAlign4(size) == size);
+
+ Block* block = fTail;
+
+ if (NULL == block)
+ {
+ SkASSERT(NULL == fHead);
+ fHead = fTail = block = Block::Create(SkMax32(size, fMinSize));
+ }
+ else if (block->available() < size)
+ {
+ fTail = Block::Create(SkMax32(size, fMinSize));
+ block->fNext = fTail;
+ block = fTail;
+ }
+
+ fSize += size;
+
+ return block->alloc(size);
+}
+
+uint32_t* SkWriter32::peek32(size_t offset)
+{
+ SkASSERT(SkAlign4(offset) == offset);
+ SkASSERT(offset <= fSize);
+
+ Block* block = fHead;
+ SkASSERT(NULL != block);
+
+ while (offset >= block->fAllocated)
+ {
+ offset -= block->fAllocated;
+ block = block->fNext;
+ SkASSERT(NULL != block);
+ }
+ return block->peek32(offset);
+}
+
+void SkWriter32::flatten(void* dst) const
+{
+ const Block* block = fHead;
+ SkDEBUGCODE(size_t total = 0;)
+
+ while (block)
+ {
+ size_t allocated = block->fAllocated;
+ memcpy(dst, block->base(), allocated);
+ dst = (char*)dst + allocated;
+ block = block->fNext;
+
+ SkDEBUGCODE(total += allocated;)
+ SkASSERT(total <= fSize);
+ }
+ SkASSERT(total == fSize);
+}
+
+void SkWriter32::writePad(const void* src, size_t size) {
+ size_t alignedSize = SkAlign4(size);
+ char* dst = (char*)this->reserve(alignedSize);
+ memcpy(dst, src, size);
+ dst += size;
+ int n = alignedSize - size;
+ while (--n >= 0) {
+ *dst++ = 0;
+ }
+}
+
+#include "SkStream.h"
+
+size_t SkWriter32::readFromStream(SkStream* stream, size_t length) {
+ char scratch[1024];
+ const size_t MAX = sizeof(scratch);
+ size_t remaining = length;
+
+ while (remaining != 0) {
+ size_t n = remaining;
+ if (n > MAX) {
+ n = MAX;
+ }
+ size_t bytes = stream->read(scratch, n);
+ this->writePad(scratch, bytes);
+ remaining -= bytes;
+ if (bytes != n) {
+ break;
+ }
+ }
+ return length - remaining;
+}
+
+bool SkWriter32::writeToStream(SkWStream* stream) {
+ const Block* block = fHead;
+ while (block) {
+ if (!stream->write(block->base(), block->fAllocated)) {
+ return false;
+ }
+ block = block->fNext;
+ }
+ return true;
+}
+
diff --git a/src/core/SkXfermode.cpp b/src/core/SkXfermode.cpp
new file mode 100644
index 0000000000..e8a202dc08
--- /dev/null
+++ b/src/core/SkXfermode.cpp
@@ -0,0 +1,978 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkXfermode.h"
+#include "SkColorPriv.h"
+
+#define SkAlphaMulAlpha(a, b) SkMulDiv255Round(a, b)
+
+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);
+}
+
+// idea for higher precision blends in xfer procs (and slightly faster)
+// see DstATop as a probable caller
+static U8CPU mulmuldiv255round(U8CPU a, U8CPU b, U8CPU c, U8CPU d) {
+ SkASSERT(a <= 255);
+ SkASSERT(b <= 255);
+ SkASSERT(c <= 255);
+ SkASSERT(d <= 255);
+ unsigned prod = SkMulS16(a, b) + SkMulS16(c, d) + 128;
+ unsigned result = (prod + (prod >> 8)) >> 8;
+ SkASSERT(result <= 255);
+ return result;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkXfermode::asCoeff(Coeff* src, Coeff* dst) {
+ return false;
+}
+
+SkPMColor SkXfermode::xferColor(SkPMColor src, SkPMColor dst) {
+ // no-op. subclasses should override this
+ return dst;
+}
+
+void SkXfermode::xfer32(SK_RESTRICT SkPMColor dst[],
+ const SK_RESTRICT SkPMColor src[], int count,
+ const SK_RESTRICT SkAlpha aa[]) {
+ SkASSERT(dst && src && count >= 0);
+
+ if (NULL == aa) {
+ for (int i = count - 1; i >= 0; --i) {
+ dst[i] = this->xferColor(src[i], dst[i]);
+ }
+ } else {
+ for (int i = count - 1; i >= 0; --i) {
+ unsigned a = aa[i];
+ if (0 != a) {
+ SkPMColor dstC = dst[i];
+ SkPMColor C = this->xferColor(src[i], dstC);
+ if (0xFF != a) {
+ C = SkFourByteInterp(C, dstC, a);
+ }
+ dst[i] = C;
+ }
+ }
+ }
+}
+
+void SkXfermode::xfer16(SK_RESTRICT uint16_t dst[],
+ const SK_RESTRICT SkPMColor src[], int count,
+ const SK_RESTRICT SkAlpha aa[]) {
+ SkASSERT(dst && src && count >= 0);
+
+ if (NULL == aa) {
+ for (int i = count - 1; i >= 0; --i) {
+ SkPMColor dstC = SkPixel16ToPixel32(dst[i]);
+ dst[i] = SkPixel32ToPixel16_ToU16(this->xferColor(src[i], dstC));
+ }
+ } else {
+ for (int i = count - 1; i >= 0; --i) {
+ unsigned a = aa[i];
+ if (0 != a) {
+ SkPMColor dstC = SkPixel16ToPixel32(dst[i]);
+ SkPMColor C = this->xferColor(src[i], dstC);
+ if (0xFF != a) {
+ C = SkFourByteInterp(C, dstC, a);
+ }
+ dst[i] = SkPixel32ToPixel16_ToU16(C);
+ }
+ }
+ }
+}
+
+void SkXfermode::xfer4444(SK_RESTRICT SkPMColor16 dst[],
+ const SK_RESTRICT SkPMColor src[], int count,
+ const SK_RESTRICT SkAlpha aa[])
+{
+ SkASSERT(dst && src && count >= 0);
+
+ if (NULL == aa) {
+ for (int i = count - 1; i >= 0; --i) {
+ SkPMColor dstC = SkPixel4444ToPixel32(dst[i]);
+ dst[i] = SkPixel32ToPixel4444(this->xferColor(src[i], dstC));
+ }
+ } else {
+ for (int i = count - 1; i >= 0; --i) {
+ unsigned a = aa[i];
+ if (0 != a) {
+ SkPMColor dstC = SkPixel4444ToPixel32(dst[i]);
+ SkPMColor C = this->xferColor(src[i], dstC);
+ if (0xFF != a) {
+ C = SkFourByteInterp(C, dstC, a);
+ }
+ dst[i] = SkPixel32ToPixel4444(C);
+ }
+ }
+ }
+}
+
+void SkXfermode::xferA8(SK_RESTRICT SkAlpha dst[],
+ const SkPMColor src[], int count,
+ const SK_RESTRICT SkAlpha aa[])
+{
+ SkASSERT(dst && src && count >= 0);
+
+ if (NULL == aa) {
+ for (int i = count - 1; i >= 0; --i) {
+ SkPMColor res = this->xferColor(src[i], (dst[i] << SK_A32_SHIFT));
+ dst[i] = SkToU8(SkGetPackedA32(res));
+ }
+ } else {
+ for (int i = count - 1; i >= 0; --i) {
+ unsigned a = aa[i];
+ if (0 != a) {
+ SkAlpha dstA = dst[i];
+ unsigned A = SkGetPackedA32(this->xferColor(src[i],
+ (SkPMColor)(dstA << SK_A32_SHIFT)));
+ if (0xFF != a) {
+ A = SkAlphaBlend(A, dstA, SkAlpha255To256(a));
+ }
+ dst[i] = SkToU8(A);
+ }
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkProcXfermode::xfer32(SK_RESTRICT SkPMColor dst[],
+ const SK_RESTRICT SkPMColor src[], int count,
+ const SK_RESTRICT SkAlpha aa[]) {
+ SkASSERT(dst && src && count >= 0);
+
+ SkXfermodeProc proc = fProc;
+
+ if (NULL != 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 (0 != a) {
+ SkPMColor dstC = dst[i];
+ SkPMColor C = proc(src[i], dstC);
+ if (a != 0xFF) {
+ C = SkFourByteInterp(C, dstC, a);
+ }
+ dst[i] = C;
+ }
+ }
+ }
+ }
+}
+
+void SkProcXfermode::xfer16(SK_RESTRICT uint16_t dst[],
+ const SK_RESTRICT SkPMColor src[], int count,
+ const SK_RESTRICT SkAlpha aa[]) {
+ SkASSERT(dst && src && count >= 0);
+
+ SkXfermodeProc proc = fProc;
+
+ if (NULL != 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 (0 != a) {
+ SkPMColor dstC = SkPixel16ToPixel32(dst[i]);
+ SkPMColor C = proc(src[i], dstC);
+ if (0xFF != a) {
+ C = SkFourByteInterp(C, dstC, a);
+ }
+ dst[i] = SkPixel32ToPixel16_ToU16(C);
+ }
+ }
+ }
+ }
+}
+
+void SkProcXfermode::xfer4444(SK_RESTRICT SkPMColor16 dst[],
+ const SK_RESTRICT SkPMColor src[], int count,
+ const SK_RESTRICT SkAlpha aa[]) {
+ SkASSERT(dst && src && count >= 0);
+
+ SkXfermodeProc proc = fProc;
+
+ if (NULL != proc) {
+ if (NULL == aa) {
+ for (int i = count - 1; i >= 0; --i) {
+ SkPMColor dstC = SkPixel4444ToPixel32(dst[i]);
+ dst[i] = SkPixel32ToPixel4444(proc(src[i], dstC));
+ }
+ } else {
+ for (int i = count - 1; i >= 0; --i) {
+ unsigned a = aa[i];
+ if (0 != a) {
+ SkPMColor dstC = SkPixel4444ToPixel32(dst[i]);
+ SkPMColor C = proc(src[i], dstC);
+ if (0xFF != a) {
+ C = SkFourByteInterp(C, dstC, a);
+ }
+ dst[i] = SkPixel32ToPixel4444(C);
+ }
+ }
+ }
+ }
+}
+
+void SkProcXfermode::xferA8(SK_RESTRICT SkAlpha dst[],
+ const SK_RESTRICT SkPMColor src[], int count,
+ const SK_RESTRICT SkAlpha aa[]) {
+ SkASSERT(dst && src && count >= 0);
+
+ SkXfermodeProc proc = fProc;
+
+ if (NULL != proc) {
+ if (NULL == aa) {
+ for (int i = count - 1; i >= 0; --i) {
+ SkPMColor res = proc(src[i], dst[i] << SK_A32_SHIFT);
+ dst[i] = SkToU8(SkGetPackedA32(res));
+ }
+ } else {
+ for (int i = count - 1; i >= 0; --i) {
+ unsigned a = aa[i];
+ if (0 != a) {
+ SkAlpha dstA = dst[i];
+ SkPMColor res = proc(src[i], dstA << SK_A32_SHIFT);
+ unsigned A = SkGetPackedA32(res);
+ if (0xFF != a) {
+ A = SkAlphaBlend(A, dstA, SkAlpha255To256(a));
+ }
+ dst[i] = SkToU8(A);
+ }
+ }
+ }
+ }
+}
+
+SkProcXfermode::SkProcXfermode(SkFlattenableReadBuffer& buffer)
+ : SkXfermode(buffer) {
+ fProc = (SkXfermodeProc)buffer.readFunctionPtr();
+}
+
+void SkProcXfermode::flatten(SkFlattenableWriteBuffer& buffer) {
+ buffer.writeFunctionPtr((void*)fProc);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+class SkProcCoeffXfermode : public SkProcXfermode {
+public:
+ SkProcCoeffXfermode(SkXfermodeProc proc, Coeff sc, Coeff dc)
+ : INHERITED(proc), fSrcCoeff(sc), fDstCoeff(dc) {
+ }
+
+ virtual bool asCoeff(Coeff* sc, Coeff* dc) {
+ if (sc) {
+ *sc = fSrcCoeff;
+ }
+ if (dc) {
+ *dc = fDstCoeff;
+ }
+ return true;
+ }
+
+ virtual Factory getFactory() { return CreateProc; }
+ virtual void flatten(SkFlattenableWriteBuffer& buffer) {
+ this->INHERITED::flatten(buffer);
+ buffer.write32(fSrcCoeff);
+ buffer.write32(fDstCoeff);
+ }
+
+protected:
+ SkProcCoeffXfermode(SkFlattenableReadBuffer& buffer)
+ : INHERITED(buffer) {
+ fSrcCoeff = (Coeff)buffer.readU32();
+ fDstCoeff = (Coeff)buffer.readU32();
+ }
+
+private:
+ Coeff fSrcCoeff, fDstCoeff;
+
+ static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+ return SkNEW_ARGS(SkProcCoeffXfermode, (buffer)); }
+
+ typedef SkProcXfermode INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+// 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]
+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);
+ unsigned ida = 255 - da;
+
+ return SkPackARGB32(sa + da - SkAlphaMulAlpha(sa, da),
+ SkGetPackedR32(dst) + SkAlphaMulAlpha(ida, SkGetPackedR32(src)),
+ SkGetPackedG32(dst) + SkAlphaMulAlpha(ida, SkGetPackedG32(src)),
+ SkGetPackedB32(dst) + SkAlphaMulAlpha(ida, 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 isa = 255 - sa;
+
+ return SkPackARGB32(da,
+ SkAlphaMulAlpha(da, SkGetPackedR32(src)) +
+ SkAlphaMulAlpha(isa, SkGetPackedR32(dst)),
+ SkAlphaMulAlpha(da, SkGetPackedG32(src)) +
+ SkAlphaMulAlpha(isa, SkGetPackedG32(dst)),
+ SkAlphaMulAlpha(da, SkGetPackedB32(src)) +
+ SkAlphaMulAlpha(isa, SkGetPackedB32(dst)));
+}
+
+// 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 ida = 255 - da;
+
+ return SkPackARGB32(sa,
+ SkAlphaMulAlpha(ida, SkGetPackedR32(src)) +
+ SkAlphaMulAlpha(sa, SkGetPackedR32(dst)),
+ SkAlphaMulAlpha(ida, SkGetPackedG32(src)) +
+ SkAlphaMulAlpha(sa, SkGetPackedG32(dst)),
+ SkAlphaMulAlpha(ida, SkGetPackedB32(src)) +
+ SkAlphaMulAlpha(sa, SkGetPackedB32(dst)));
+}
+
+// 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 isa = 255 - sa;
+ unsigned ida = 255 - da;
+
+ return SkPackARGB32(sa + da - (SkAlphaMulAlpha(sa, da) << 1),
+ SkAlphaMulAlpha(ida, SkGetPackedR32(src)) +
+ SkAlphaMulAlpha(isa, SkGetPackedR32(dst)),
+ SkAlphaMulAlpha(ida, SkGetPackedG32(src)) +
+ SkAlphaMulAlpha(isa, SkGetPackedG32(dst)),
+ SkAlphaMulAlpha(ida, SkGetPackedB32(src)) +
+ SkAlphaMulAlpha(isa, SkGetPackedB32(dst)));
+}
+
+
+// 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));
+}
+
+static SkPMColor mult_modeproc(SkPMColor src, SkPMColor dst) {
+ int a = SkAlphaMulAlpha(SkGetPackedA32(src), SkGetPackedA32(dst));
+ int r = SkAlphaMulAlpha(SkGetPackedR32(src), SkGetPackedR32(dst));
+ int g = SkAlphaMulAlpha(SkGetPackedG32(src), SkGetPackedG32(dst));
+ int b = SkAlphaMulAlpha(SkGetPackedB32(src), SkGetPackedB32(dst));
+ return SkPackARGB32(a, r, g, b);
+}
+
+static inline int screen_byte(int a, int b) {
+ return a + b - SkAlphaMulAlpha(a, b);
+}
+
+static SkPMColor screen_modeproc(SkPMColor src, SkPMColor dst) {
+ int a = screen_byte(SkGetPackedA32(src), SkGetPackedA32(dst));
+ int r = screen_byte(SkGetPackedR32(src), SkGetPackedR32(dst));
+ int g = screen_byte(SkGetPackedG32(src), SkGetPackedG32(dst));
+ int b = screen_byte(SkGetPackedB32(src), SkGetPackedB32(dst));
+ return SkPackARGB32(a, r, g, b);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkClearXfermode : public SkProcCoeffXfermode {
+public:
+ SkClearXfermode() : SkProcCoeffXfermode(clear_modeproc,
+ kZero_Coeff, kZero_Coeff) {}
+
+ virtual void xfer32(SK_RESTRICT SkPMColor dst[],
+ const SK_RESTRICT SkPMColor[], int count,
+ const SK_RESTRICT 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 (0xFF == a) {
+ dst[i] = 0;
+ } else if (a != 0) {
+ dst[i] = SkAlphaMulQ(dst[i], SkAlpha255To256(255 - a));
+ }
+ }
+ }
+ }
+ virtual void xferA8(SK_RESTRICT SkAlpha dst[],
+ const SK_RESTRICT SkPMColor[], int count,
+ const SK_RESTRICT 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 (0xFF == a) {
+ dst[i] = 0;
+ } else if (0 != a) {
+ dst[i] = SkAlphaMulAlpha(dst[i], 255 - a);
+ }
+ }
+ }
+ }
+
+ virtual Factory getFactory() { return CreateProc; }
+
+private:
+ SkClearXfermode(SkFlattenableReadBuffer& buffer)
+ : SkProcCoeffXfermode(buffer) {}
+
+ static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+ return SkNEW_ARGS(SkClearXfermode, (buffer));
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkSrcXfermode : public SkProcCoeffXfermode {
+public:
+ SkSrcXfermode() : SkProcCoeffXfermode(src_modeproc,
+ kOne_Coeff, kZero_Coeff) {}
+
+ virtual void xfer32(SK_RESTRICT SkPMColor dst[],
+ const SK_RESTRICT SkPMColor src[], int count,
+ const SK_RESTRICT 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(SK_RESTRICT SkAlpha dst[],
+ const SK_RESTRICT SkPMColor src[], int count,
+ const SK_RESTRICT 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 (0 != a) {
+ 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; }
+
+private:
+ SkSrcXfermode(SkFlattenableReadBuffer& buffer)
+ : SkProcCoeffXfermode(buffer) {}
+
+ static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+ return SkNEW_ARGS(SkSrcXfermode, (buffer));
+ }
+};
+
+class SkDstInXfermode : public SkProcCoeffXfermode {
+public:
+ SkDstInXfermode() : SkProcCoeffXfermode(dstin_modeproc,
+ kZero_Coeff, kSA_Coeff) {}
+
+ virtual void xfer32(SK_RESTRICT SkPMColor dst[],
+ const SK_RESTRICT SkPMColor src[], int count,
+ const SK_RESTRICT SkAlpha aa[]) {
+ SkASSERT(dst && src);
+
+ if (count <= 0) {
+ return;
+ }
+ if (NULL != aa) {
+ return this->INHERITED::xfer32(dst, src, count, aa);
+ }
+
+ do {
+ unsigned a = SkGetPackedA32(*src);
+ *dst = SkAlphaMulQ(*dst, SkAlpha255To256(a));
+ dst++;
+ src++;
+ } while (--count != 0);
+ }
+
+ virtual Factory getFactory() { return CreateProc; }
+
+private:
+ SkDstInXfermode(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
+
+ static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+ return SkNEW_ARGS(SkDstInXfermode, (buffer));
+ }
+
+ typedef SkProcCoeffXfermode INHERITED;
+};
+
+class SkDstOutXfermode : public SkProcCoeffXfermode {
+public:
+ SkDstOutXfermode() : SkProcCoeffXfermode(dstout_modeproc,
+ kZero_Coeff, kISA_Coeff) {}
+
+ virtual void xfer32(SK_RESTRICT SkPMColor dst[],
+ const SK_RESTRICT SkPMColor src[], int count,
+ const SK_RESTRICT SkAlpha aa[]) {
+ SkASSERT(dst && src);
+
+ if (count <= 0) {
+ return;
+ }
+ if (NULL != aa) {
+ return this->INHERITED::xfer32(dst, src, count, aa);
+ }
+
+ do {
+ unsigned a = SkGetPackedA32(*src);
+ *dst = SkAlphaMulQ(*dst, SkAlpha255To256(255 - a));
+ dst++;
+ src++;
+ } while (--count != 0);
+ }
+
+ virtual Factory getFactory() { return CreateProc; }
+
+private:
+ SkDstOutXfermode(SkFlattenableReadBuffer& buffer)
+ : INHERITED(buffer) {}
+
+ static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+ return SkNEW_ARGS(SkDstOutXfermode, (buffer));
+ }
+
+ typedef SkProcCoeffXfermode INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkPorterDuff.h"
+
+struct ProcCoeff {
+ SkXfermodeProc fProc;
+ SkXfermode::Coeff fSC;
+ SkXfermode::Coeff fDC;
+};
+
+static const ProcCoeff gProcCoeffs[] = {
+ { clear_modeproc, SkXfermode::kZero_Coeff, SkXfermode::kZero_Coeff },
+ { src_modeproc, SkXfermode::kOne_Coeff, SkXfermode::kZero_Coeff },
+ { dst_modeproc, SkXfermode::kZero_Coeff, SkXfermode::kOne_Coeff },
+ { srcover_modeproc, SkXfermode::kOne_Coeff, SkXfermode::kISA_Coeff },
+ { dstover_modeproc, SkXfermode::kIDA_Coeff, SkXfermode::kOne_Coeff },
+ { srcin_modeproc, SkXfermode::kDA_Coeff, SkXfermode::kZero_Coeff },
+ { dstin_modeproc, SkXfermode::kZero_Coeff, SkXfermode::kSA_Coeff },
+ { srcout_modeproc, SkXfermode::kIDA_Coeff, SkXfermode::kZero_Coeff },
+ { dstout_modeproc, SkXfermode::kZero_Coeff, SkXfermode::kISA_Coeff },
+ { srcatop_modeproc, SkXfermode::kDA_Coeff, SkXfermode::kISA_Coeff },
+ { dstatop_modeproc, SkXfermode::kIDA_Coeff, SkXfermode::kSA_Coeff },
+ { xor_modeproc, SkXfermode::kIDA_Coeff, SkXfermode::kISA_Coeff },
+ // these two can't be represented as coefficients
+ { darken_modeproc, SkXfermode::Coeff(-1), SkXfermode::Coeff(-1) },
+ { lighten_modeproc, SkXfermode::Coeff(-1), SkXfermode::Coeff(-1) },
+ // these can use coefficients
+ { mult_modeproc, SkXfermode::kZero_Coeff, SkXfermode::kSC_Coeff },
+ { screen_modeproc, SkXfermode::kOne_Coeff, SkXfermode::kISC_Coeff }
+};
+
+SkXfermode* SkPorterDuff::CreateXfermode(SkPorterDuff::Mode mode) {
+ SkASSERT(SK_ARRAY_COUNT(gProcCoeffs) == 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;
+ case kDstIn_Mode:
+ return SkNEW(SkDstInXfermode);
+ case kDstOut_Mode:
+ return SkNEW(SkDstOutXfermode);
+ // these two can't be represented with Coeff
+ case kDarken_Mode:
+ return SkNEW_ARGS(SkProcXfermode, (darken_modeproc));
+ case kLighten_Mode:
+ return SkNEW_ARGS(SkProcXfermode, (lighten_modeproc));
+ // use the table
+ default: {
+ const ProcCoeff& rec = gProcCoeffs[mode];
+ SkASSERT((unsigned)rec.fSC < SkXfermode::kCoeffCount);
+ SkASSERT((unsigned)rec.fDC < SkXfermode::kCoeffCount);
+ return SkNEW_ARGS(SkProcCoeffXfermode, (rec.fProc,
+ rec.fSC, rec.fDC));
+ }
+ }
+}
+
+bool SkPorterDuff::IsMode(SkXfermode* xfer, Mode* mode) {
+ if (NULL == xfer) {
+ if (mode) {
+ *mode = kSrcOver_Mode;
+ }
+ return true;
+ }
+
+ SkXfermode::Coeff sc, dc;
+ if (xfer->asCoeff(&sc, &dc)) {
+ SkASSERT((unsigned)sc < (unsigned)SkXfermode::kCoeffCount);
+ SkASSERT((unsigned)dc < (unsigned)SkXfermode::kCoeffCount);
+
+ const ProcCoeff* rec = gProcCoeffs;
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gProcCoeffs); i++) {
+ if (rec[i].fSC == sc && rec[i].fDC == dc) {
+ if (mode) {
+ *mode = SkPorterDuff::Mode(i);
+ }
+ return true;
+ }
+ }
+ }
+
+ // no coefficients, or not found in our table
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#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);
+
+ const size_t N = SK_ARRAY_COUNT(gProcCoeffs);
+ for (size_t i = 0; i < N; i++) {
+ gProcCoeffs[i].fProc(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 = gProcCoeffs[mode].fProc;
+ }
+ return proc;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//////////// 16bit xfermode procs
+
+#ifdef SK_DEBUG
+static bool require_255(SkPMColor src) { return SkGetPackedA32(src) == 0xFF; }
+static bool require_0(SkPMColor src) { return SkGetPackedA32(src) == 0; }
+#endif
+
+static uint16_t src_modeproc16_255(SkPMColor src, uint16_t dst) {
+ SkASSERT(require_255(src));
+ return SkPixel32ToPixel16(src);
+}
+
+static uint16_t dst_modeproc16(SkPMColor src, uint16_t dst) {
+ return dst;
+}
+
+static uint16_t srcover_modeproc16_0(SkPMColor src, uint16_t dst) {
+ SkASSERT(require_0(src));
+ return dst;
+}
+
+static uint16_t srcover_modeproc16_255(SkPMColor src, uint16_t dst) {
+ SkASSERT(require_255(src));
+ return SkPixel32ToPixel16(src);
+}
+
+static uint16_t dstover_modeproc16_0(SkPMColor src, uint16_t dst) {
+ SkASSERT(require_0(src));
+ return dst;
+}
+
+static uint16_t dstover_modeproc16_255(SkPMColor src, uint16_t dst) {
+ SkASSERT(require_255(src));
+ return dst;
+}
+
+static uint16_t srcin_modeproc16_255(SkPMColor src, uint16_t dst) {
+ SkASSERT(require_255(src));
+ return SkPixel32ToPixel16(src);
+}
+
+static uint16_t dstin_modeproc16_255(SkPMColor src, uint16_t dst) {
+ SkASSERT(require_255(src));
+ return dst;
+}
+
+static uint16_t dstout_modeproc16_0(SkPMColor src, uint16_t dst) {
+ SkASSERT(require_0(src));
+ return dst;
+}
+
+static uint16_t srcatop_modeproc16(SkPMColor src, uint16_t dst) {
+ unsigned isa = 255 - SkGetPackedA32(src);
+
+ return SkPackRGB16(
+ SkPacked32ToR16(src) + SkAlphaMulAlpha(SkGetPackedR16(dst), isa),
+ SkPacked32ToG16(src) + SkAlphaMulAlpha(SkGetPackedG16(dst), isa),
+ SkPacked32ToB16(src) + SkAlphaMulAlpha(SkGetPackedB16(dst), isa));
+}
+
+static uint16_t srcatop_modeproc16_0(SkPMColor src, uint16_t dst) {
+ SkASSERT(require_0(src));
+ return dst;
+}
+
+static uint16_t srcatop_modeproc16_255(SkPMColor src, uint16_t dst) {
+ SkASSERT(require_255(src));
+ return SkPixel32ToPixel16(src);
+}
+
+static uint16_t dstatop_modeproc16_255(SkPMColor src, uint16_t dst) {
+ SkASSERT(require_255(src));
+ return dst;
+}
+
+/*********
+ darken and lighten boil down to this.
+
+ darken = (1 - Sa) * Dc + min(Sc, Dc)
+ lighten = (1 - Sa) * Dc + max(Sc, Dc)
+
+ if (Sa == 0) these become
+ darken = Dc + min(0, Dc) = 0
+ lighten = Dc + max(0, Dc) = Dc
+
+ if (Sa == 1) these become
+ darken = min(Sc, Dc)
+ lighten = max(Sc, Dc)
+*/
+
+static uint16_t darken_modeproc16_0(SkPMColor src, uint16_t dst) {
+ SkASSERT(require_0(src));
+ return 0;
+}
+
+static uint16_t darken_modeproc16_255(SkPMColor src, uint16_t dst) {
+ SkASSERT(require_255(src));
+ unsigned r = SkFastMin32(SkPacked32ToR16(src), SkGetPackedR16(dst));
+ unsigned g = SkFastMin32(SkPacked32ToG16(src), SkGetPackedG16(dst));
+ unsigned b = SkFastMin32(SkPacked32ToB16(src), SkGetPackedB16(dst));
+ return SkPackRGB16(r, g, b);
+}
+
+static uint16_t lighten_modeproc16_0(SkPMColor src, uint16_t dst) {
+ SkASSERT(require_0(src));
+ return dst;
+}
+
+static uint16_t lighten_modeproc16_255(SkPMColor src, uint16_t dst) {
+ SkASSERT(require_255(src));
+ unsigned r = SkMax32(SkPacked32ToR16(src), SkGetPackedR16(dst));
+ unsigned g = SkMax32(SkPacked32ToG16(src), SkGetPackedG16(dst));
+ unsigned b = SkMax32(SkPacked32ToB16(src), SkGetPackedB16(dst));
+ return SkPackRGB16(r, g, b);
+}
+
+struct Proc16Rec {
+ SkXfermodeProc16 fProc16_0;
+ SkXfermodeProc16 fProc16_255;
+ SkXfermodeProc16 fProc16_General;
+};
+
+static const Proc16Rec gPorterDuffModeProcs16[] = {
+ { NULL, NULL, NULL }, // CLEAR
+ { NULL, src_modeproc16_255, NULL },
+ { dst_modeproc16, dst_modeproc16, dst_modeproc16 },
+ { srcover_modeproc16_0, srcover_modeproc16_255, NULL },
+ { dstover_modeproc16_0, dstover_modeproc16_255, NULL },
+ { NULL, srcin_modeproc16_255, NULL },
+ { NULL, dstin_modeproc16_255, NULL },
+ { NULL, NULL, NULL },// SRC_OUT
+ { dstout_modeproc16_0, NULL, NULL },
+ { srcatop_modeproc16_0, srcatop_modeproc16_255, srcatop_modeproc16 },
+ { NULL, dstatop_modeproc16_255, NULL },
+ { NULL, NULL, NULL }, // XOR
+ { darken_modeproc16_0, darken_modeproc16_255, NULL },
+ { lighten_modeproc16_0, lighten_modeproc16_255, NULL },
+ { NULL, NULL, NULL },//multiply
+ { NULL, NULL, NULL }// screen
+};
+
+SkXfermodeProc16 SkPorterDuff::GetXfermodeProc16(Mode mode, SkColor srcColor) {
+ SkXfermodeProc16 proc16 = NULL;
+
+ if ((unsigned)mode < SkPorterDuff::kModeCount) {
+ const Proc16Rec& rec = gPorterDuffModeProcs16[mode];
+
+ unsigned a = SkColorGetA(srcColor);
+
+ if (0 == a) {
+ proc16 = rec.fProc16_0;
+ } else if (255 == a) {
+ proc16 = rec.fProc16_255;
+ } else {
+ proc16 = rec.fProc16_General;
+ }
+ }
+ return proc16;
+}
+