diff options
Diffstat (limited to 'libsgl/sgl')
93 files changed, 31428 insertions, 0 deletions
diff --git a/libsgl/sgl/ARGB32_Clamp_Bilinear_BitmapShader.h b/libsgl/sgl/ARGB32_Clamp_Bilinear_BitmapShader.h new file mode 100644 index 0000000..c7e23af --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkAlphaRuns.cpp b/libsgl/sgl/SkAlphaRuns.cpp new file mode 100644 index 0000000..46b0206 --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkAntiRun.h b/libsgl/sgl/SkAntiRun.h new file mode 100644 index 0000000..12930e6 --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkAutoKern.h b/libsgl/sgl/SkAutoKern.h new file mode 100644 index 0000000..023cb6b --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkBitmap.cpp b/libsgl/sgl/SkBitmap.cpp new file mode 100644 index 0000000..3edb8c6 --- /dev/null +++ b/libsgl/sgl/SkBitmap.cpp @@ -0,0 +1,1239 @@ +/* + * 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 { + int 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; +} + +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::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 (fPixelRef) { + if (fPixelLockCount > 0) { + SkASSERT(fPixelRef->getLockCount() > 0); + } else { + SkASSERT(NULL == fPixels); + SkASSERT(NULL == fColorTable); + } + } +} +#endif + diff --git a/libsgl/sgl/SkBitmapProcShader.cpp b/libsgl/sgl/SkBitmapProcShader.cpp new file mode 100644 index 0000000..8685c9f --- /dev/null +++ b/libsgl/sgl/SkBitmapProcShader.cpp @@ -0,0 +1,194 @@ +#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); diff --git a/libsgl/sgl/SkBitmapProcShader.h b/libsgl/sgl/SkBitmapProcShader.h new file mode 100644 index 0000000..ed790c8 --- /dev/null +++ b/libsgl/sgl/SkBitmapProcShader.h @@ -0,0 +1,56 @@ +/* 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)); + } + +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/libsgl/sgl/SkBitmapProcState.cpp b/libsgl/sgl/SkBitmapProcState.cpp new file mode 100644 index 0000000..428921d --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkBitmapProcState.h b/libsgl/sgl/SkBitmapProcState.h new file mode 100644 index 0000000..1366d3b --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkBitmapProcState_matrix.h b/libsgl/sgl/SkBitmapProcState_matrix.h new file mode 100644 index 0000000..1212a41 --- /dev/null +++ b/libsgl/sgl/SkBitmapProcState_matrix.h @@ -0,0 +1,273 @@ + +#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 + + // invert x+half, y+half and convert to fixed + SkFixed fx = s.fInvSy * y + s.fInvTyPlusHalf; + *xy++ = TILEY_PROCF(fx, (s.fBitmap->height() - 1)); + // invert X + SkFixed dx = s.fInvSx; + fx = dx * x + s.fInvTxPlusHalf; + unsigned maxX = s.fBitmap->width() - 1; + +#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; +#if 0 + uint16_t* xx = (uint16_t*)xy; + for (i = (count >> 2); i > 0; --i) { + *xx++ = TILEX_PROCF(fx, maxX); fx += dx; + *xx++ = TILEX_PROCF(fx, maxX); fx += dx; + *xx++ = TILEX_PROCF(fx, maxX); fx += dx; + *xx++ = TILEX_PROCF(fx, maxX); fx += dx; + } + for (i = (count & 3); i > 0; --i) { + *xx++ = TILEX_PROCF(fx, maxX); fx += dx; + } +#else + 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; + } +#endif + } +} + +// 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); + // compute our two Y values up front + { + unsigned maxY = s.fBitmap->height() - 1; + SkFixed fy = s.fInvSy * y + s.fInvTyPlusHalf - (s.fFilterOneY >> 1); + *xy++ = PACK_FILTER_Y_NAME(fy, maxY, s.fFilterOneY PREAMBLE_ARG_Y); + } + + unsigned maxX = s.fBitmap->width() - 1; + SkFixed one = s.fFilterOneX; + SkFixed dx = s.fInvSx; + SkFixed fx = dx * x + s.fInvTxPlusHalf - (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/libsgl/sgl/SkBitmapProcState_matrixProcs.cpp b/libsgl/sgl/SkBitmapProcState_matrixProcs.cpp new file mode 100644 index 0000000..beb21c8 --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkBitmapProcState_sample.h b/libsgl/sgl/SkBitmapProcState_sample.h new file mode 100644 index 0000000..122ccf8 --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkBitmapSampler.cpp b/libsgl/sgl/SkBitmapSampler.cpp new file mode 100644 index 0000000..045efd1 --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkBitmapSampler.h b/libsgl/sgl/SkBitmapSampler.h new file mode 100644 index 0000000..eeef3b3 --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkBitmapSamplerTemplate.h b/libsgl/sgl/SkBitmapSamplerTemplate.h new file mode 100644 index 0000000..00df10c --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkBitmapShader.cpp b/libsgl/sgl/SkBitmapShader.cpp new file mode 100644 index 0000000..5d70dd3 --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkBitmapShader.h b/libsgl/sgl/SkBitmapShader.h new file mode 100644 index 0000000..8d40a4b --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkBitmapShader16BilerpTemplate.h b/libsgl/sgl/SkBitmapShader16BilerpTemplate.h new file mode 100644 index 0000000..b70801e --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkBitmapShaderTemplate.h b/libsgl/sgl/SkBitmapShaderTemplate.h new file mode 100644 index 0000000..0174138 --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkBitmap_scroll.cpp b/libsgl/sgl/SkBitmap_scroll.cpp new file mode 100644 index 0000000..f9f197d --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkBlitBWMaskTemplate.h b/libsgl/sgl/SkBlitBWMaskTemplate.h new file mode 100644 index 0000000..e433d36 --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkBlitRow.h b/libsgl/sgl/SkBlitRow.h new file mode 100644 index 0000000..bb6a29b --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkBlitRow_D16.cpp b/libsgl/sgl/SkBlitRow_D16.cpp new file mode 100644 index 0000000..f40df36 --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkBlitRow_D4444.cpp b/libsgl/sgl/SkBlitRow_D4444.cpp new file mode 100644 index 0000000..e60c721 --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkBlitter.cpp b/libsgl/sgl/SkBlitter.cpp new file mode 100644 index 0000000..9208429 --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkBlitter.h b/libsgl/sgl/SkBlitter.h new file mode 100644 index 0000000..11b84fd --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkBlitter_4444.cpp b/libsgl/sgl/SkBlitter_4444.cpp new file mode 100644 index 0000000..22b3b7b --- /dev/null +++ b/libsgl/sgl/SkBlitter_4444.cpp @@ -0,0 +1,495 @@ +/* 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; +public: +SkARGB4444_Shader_Blitter(const SkBitmap& device, const SkPaint& paint) + : INHERITED(device, paint) +{ + fBuffer = (SkPMColor*)sk_malloc_throw(device.width() * (sizeof(SkPMColor))); + + (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* span = fBuffer; + 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 { + // count is almost always 1 + for (int i = count - 1; i >= 0; --i) { + xfer->xfer4444(&device[i], &span[i], count, antialias); + } + } + } + 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/libsgl/sgl/SkBlitter_A1.cpp b/libsgl/sgl/SkBlitter_A1.cpp new file mode 100644 index 0000000..1a91a26 --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkBlitter_A8.cpp b/libsgl/sgl/SkBlitter_A8.cpp new file mode 100644 index 0000000..18b0881 --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkBlitter_ARGB32.cpp b/libsgl/sgl/SkBlitter_ARGB32.cpp new file mode 100644 index 0000000..ed2fc0e --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkBlitter_RGB16.cpp b/libsgl/sgl/SkBlitter_RGB16.cpp new file mode 100644 index 0000000..b253662 --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkBlitter_Sprite.cpp b/libsgl/sgl/SkBlitter_Sprite.cpp new file mode 100644 index 0000000..f0da166 --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkCanvas.cpp b/libsgl/sgl/SkCanvas.cpp new file mode 100644 index 0000000..42e4be8 --- /dev/null +++ b/libsgl/sgl/SkCanvas.cpp @@ -0,0 +1,1323 @@ +/* + * 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 "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 + +/////////////////////////////////////////////////////////////////////////////// + +/* 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; + 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; } + +private: + SkCanvas* fCanvas; + const DeviceCM* fCurrLayer; + 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; + + 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; + + // 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; + return fMCRec->fMatrix->preTranslate(dx, dy); +} + +bool SkCanvas::scale(SkScalar sx, SkScalar sy) { + fDeviceCMDirty = true; + return fMCRec->fMatrix->preScale(sx, sy); +} + +bool SkCanvas::rotate(SkScalar degrees) { + fDeviceCMDirty = true; + return fMCRec->fMatrix->preRotate(degrees); +} + +bool SkCanvas::skew(SkScalar sx, SkScalar sy) { + fDeviceCMDirty = true; + return fMCRec->fMatrix->preSkew(sx, sy); +} + +bool SkCanvas::concat(const SkMatrix& matrix) { + fDeviceCMDirty = true; + return fMCRec->fMatrix->preConcat(matrix); +} + +void SkCanvas::setMatrix(const SkMatrix& matrix) { + fDeviceCMDirty = 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; + 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; + + 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; + return fMCRec->fRegion->op(rgn, op); +} + +bool SkCanvas::quickReject(const SkRect& rect, EdgeType et) const { + if (fMCRec->fRegion->isEmpty() || rect.isEmpty()) { + return true; + } + + SkRect r; + SkIRect ir; + + fMCRec->fMatrix->mapRect(&r, rect); + if (kAA_EdgeType == et) { + r.roundOut(&ir); + } else { + r.round(&ir); + } + return fMCRec->fRegion->quickReject(ir); +} + +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::kExact_BoundsType); + return this->quickReject(r, et); + } + + SkPath dstPath; + SkRect r; + SkIRect ir; + + path.transform(*fMCRec->fMatrix, &dstPath); + dstPath.computeBounds(&r, SkPath::kExact_BoundsType); + if (kAA_EdgeType == et) { + r.roundOut(&ir); + } else { + r.round(&ir); + } + return fMCRec->fRegion->quickReject(ir); +} + +bool SkCanvas::quickRejectY(SkScalar top, SkScalar bottom, EdgeType et) const { + if (fMCRec->fRegion->isEmpty() || top >= bottom) { + return true; + } + + const SkMatrix& matrix = *fMCRec->fMatrix; + + // if we're rotated/skewed/perspective, give up (for now) + // TODO: cache this attribute of the matrix? or specialized query method? + // TODO: if rotate=90 or 270 is common, we can handle those too... + if (matrix.getType() & ~(SkMatrix::kTranslate_Mask | + SkMatrix::kScale_Mask)) { + return false; + } + // transform top/botttom into device coordinates + const SkScalar sy = matrix[SkMatrix::kMScaleY]; + const SkScalar ty = matrix[SkMatrix::kMTransY]; + top = SkScalarMulAdd(top, sy, ty); + bottom = SkScalarMulAdd(bottom, sy, ty); + + // if the scale flipped us, flip back + if (top > bottom) { + SkTSwap<SkScalar>(top, bottom); + } + // now round based on the edge type + int ymin, ymax; + if (kAA_EdgeType == et) { + ymin = SkScalarFloor(top); + ymax = SkScalarCeil(bottom); + } else { + ymin = SkScalarRound(top); + ymax = SkScalarRound(bottom); + } + + // now compare against the bounds of the clip + const SkIRect& bounds = fMCRec->fRegion->getBounds(); + return ymin >= bounds.fBottom || ymax <= bounds.fTop; +} + +bool SkCanvas::getClipBounds(SkRect* bounds) const { + const SkRegion& clip = *fMCRec->fRegion; + if (clip.isEmpty()) { + if (bounds) { + bounds->setEmpty(); + } + return false; + } + + if (NULL != bounds) { + SkMatrix inverse; + SkRect r; + + // TODO: should we cache the inverse (with a dirty bit)? + fMCRec->fMatrix->invert(&inverse); + r.set(clip.getBounds()); + 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) { + 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) { + 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();) + + 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, paint != NULL && paint->isAntiAlias() ? + kAA_EdgeType : kBW_EdgeType)) { + 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; + } + + SkPath path; + SkRect r; + + r.set(cx - radius, cy - radius, cx + radius, cy + radius); + 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) { + 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) { + 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 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/libsgl/sgl/SkColor.cpp b/libsgl/sgl/SkColor.cpp new file mode 100644 index 0000000..93adcd7 --- /dev/null +++ b/libsgl/sgl/SkColor.cpp @@ -0,0 +1,136 @@ +/* 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] = hsv[1] = hsv[2] = v; + return; + } + + SkScalar s = ByteDivToScalar(delta, max); + SkASSERT(s >= 0 && s <= SK_Scalar1); + + SkScalar h; + if (r == max) { + h = ByteDivToScalar(g - b, delta); + } else if (g == max) { + h = SkIntToScalar(2) + ByteDivToScalar(b - r, delta); + } else { // b == max + h = SkIntToScalar(4) + ByteDivToScalar(r - g, delta); + } + + h *= 60; + if (h < 0) { + h += SkIntToScalar(360); + } + SkASSERT(h >= 0 && h < SkIntToScalar(360)); + + hsv[0] = h; + hsv[1] = s; + hsv[2] = v; +} + +static inline U8CPU UnitScalarToByte(SkScalar x) { + if (x < 0) { + return 0; + } + if (x >= SK_Scalar1) { + return 255; + } + return SkScalarToFixed(x) >> 8; +} + +SkColor SkHSVToColor(U8CPU a, const SkScalar hsv[3]) { + SkASSERT(hsv); + + U8CPU s = UnitScalarToByte(hsv[1]); + U8CPU v = UnitScalarToByte(hsv[2]); + + if (0 == s) { // shade of gray + return SkColorSetARGB(a, v, v, v); + } + SkFixed hx = (hsv[0] < 0 || hsv[0] >= SkIntToScalar(360)) ? 0 : SkScalarToFixed(hsv[0]/60); + SkFixed f = hx & 0xFFFF; + + unsigned v_scale = SkAlpha255To256(v); + unsigned p = SkAlphaMul(255 - s, v_scale); + unsigned q = SkAlphaMul(255 - (s * f >> 16), v_scale); + unsigned t = SkAlphaMul(255 - (s * (SK_Fixed1 - f) >> 16), v_scale); + + unsigned r, g, b; + + SkASSERT((unsigned)(hx >> 16) < 6); + switch (hx >> 16) { + case 0: r = v; g = t; b = p; break; + case 1: r = q; g = v; b = p; break; + case 2: r = p; g = v; b = t; break; + case 3: r = p; g = q; b = v; break; + case 4: r = t; g = p; b = v; break; + default: r = v; g = p; b = q; break; + } + return SkColorSetARGB(a, r, g, b); +} + diff --git a/libsgl/sgl/SkColorFilter.cpp b/libsgl/sgl/SkColorFilter.cpp new file mode 100644 index 0000000..bb4be48 --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkColorTable.cpp b/libsgl/sgl/SkColorTable.cpp new file mode 100644 index 0000000..f991da3 --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkCoreBlitters.h b/libsgl/sgl/SkCoreBlitters.h new file mode 100644 index 0000000..5b3497e --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkDeque.cpp b/libsgl/sgl/SkDeque.cpp new file mode 100644 index 0000000..4f15051 --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkDevice.cpp b/libsgl/sgl/SkDevice.cpp new file mode 100644 index 0000000..139174d --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkDither.cpp b/libsgl/sgl/SkDither.cpp new file mode 100644 index 0000000..53a8573 --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkDraw.cpp b/libsgl/sgl/SkDraw.cpp new file mode 100644 index 0000000..2f0ddff --- /dev/null +++ b/libsgl/sgl/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 "SkShaderExtras.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/libsgl/sgl/SkDrawProcs.h b/libsgl/sgl/SkDrawProcs.h new file mode 100644 index 0000000..d64c088 --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkEdge.cpp b/libsgl/sgl/SkEdge.cpp new file mode 100644 index 0000000..743737a --- /dev/null +++ b/libsgl/sgl/SkEdge.cpp @@ -0,0 +1,483 @@ +/* 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 = SkToS16(top); + fLastY = SkToS16(bot - 1); + fCurveCount = 0; + fWinding = SkToS8(winding); + fCurveShift = 0; + + if (clip) { + this->chopLineWithClip(*clip); + } + return 1; +} + +// called from a curve subclass +int SkEdge::updateLine(SkFixed x0, SkFixed y0, SkFixed x1, SkFixed y1) +{ + SkASSERT(fWinding == 1 || fWinding == -1); + SkASSERT(fCurveCount != 0); +// SkASSERT(fCurveShift != 0); + + y0 >>= 10; + y1 >>= 10; + + SkASSERT(y0 <= y1); + + int top = SkFDot6Round(y0); + int bot = SkFDot6Round(y1); + +// SkASSERT(top >= fFirstY); + + // are we a zero-height line? + if (top == bot) + return 0; + + x0 >>= 10; + x1 >>= 10; + + SkFixed slope = SkFDot6Div(x1 - x0, y1 - y0); + + fX = SkFDot6ToFixed(x0 + SkFixedMul(slope, (32 - y0) & 63)); // + SK_Fixed1/2 + fDX = slope; + fFirstY = SkToS16(top); + fLastY = SkToS16(bot - 1); + + return 1; +} + +void SkEdge::chopLineWithClip(const 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 7 (e.g. 128) + Note that this limits the number of times we can subdivide a curve, but 128 + seems more than adequate. +*/ +#define MAX_COEFF_SHIFT 7 + +static inline SkFDot6 cheap_distance(SkFDot6 dx, SkFDot6 dy) +{ + dx = SkAbs32(dx); + dy = SkAbs32(dy); + // return max + min/2 + if (dx > dy) + dx += dy >> 1; + else + dx = dy + (dx >> 1); + return dx; +} + +static inline int diff_to_shift(SkFDot6 dx, SkFDot6 dy) +{ + // cheap calc of distance from center of p0-p2 to the center of the curve + SkFDot6 dist = cheap_distance(dx, dy); + + // shift down dist (it is currently in dot6) + // down by 5 should give us 1/2 pixel accuracy (assuming our dist is accurate...) + // this is chosen by heuristic: make it as big as possible (to minimize segments) + // ... but small enough so that our curves still look smooth + dist >>= 5; + + // each subdivision (shift value) cuts this dist (error) by 1/4 + return (32 - SkCLZ(dist)) >> 1; +} + +int SkQuadraticEdge::setQuadratic(const SkPoint pts[3], const 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/libsgl/sgl/SkEdge.h b/libsgl/sgl/SkEdge.h new file mode 100644 index 0000000..67a5d89 --- /dev/null +++ b/libsgl/sgl/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; + int16_t fFirstY; + int16_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/libsgl/sgl/SkFP.h b/libsgl/sgl/SkFP.h new file mode 100644 index 0000000..6c0c526 --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkFilterProc.cpp b/libsgl/sgl/SkFilterProc.cpp new file mode 100644 index 0000000..814bafe --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkFilterProc.h b/libsgl/sgl/SkFilterProc.h new file mode 100644 index 0000000..8d90474 --- /dev/null +++ b/libsgl/sgl/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 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 SkFilter32Proc* SkGetFilter32Proc22Row(const SkFilterProc* table, + unsigned y) +{ + SkASSERT(table); + // extract low 2 bits and shift up 2 + return &table[y << 30 >> 28]; +} + +inline SkFilter32Proc SkGetFilter32Proc22RowProc(const SkFilterProc* 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/libsgl/sgl/SkFlattenable.cpp b/libsgl/sgl/SkFlattenable.cpp new file mode 100644 index 0000000..08dd59c --- /dev/null +++ b/libsgl/sgl/SkFlattenable.cpp @@ -0,0 +1,259 @@ +#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; +} + diff --git a/libsgl/sgl/SkGeometry.cpp b/libsgl/sgl/SkGeometry.cpp new file mode 100644 index 0000000..4f22e92 --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkGeometry.h b/libsgl/sgl/SkGeometry.h new file mode 100644 index 0000000..571159f --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkGlobals.cpp b/libsgl/sgl/SkGlobals.cpp new file mode 100644 index 0000000..bc72b97 --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkGlyphCache.cpp b/libsgl/sgl/SkGlyphCache.cpp new file mode 100644 index 0000000..4efc895 --- /dev/null +++ b/libsgl/sgl/SkGlyphCache.cpp @@ -0,0 +1,628 @@ +/* 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_HASHxxxxx + +/////////////////////////////////////////////////////////////////////////////// + +#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) { + // 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 { + 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) { + // 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 { + 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) { + glyph = this->lookupMetrics(glyphID, kFull_MetricsType); + fGlyphHash[index] = glyph; + } else { + 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) { + glyph = this->lookupMetrics(id, kFull_MetricsType); + fGlyphHash[index] = glyph; + } else { + 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/libsgl/sgl/SkGlyphCache.h b/libsgl/sgl/SkGlyphCache.h new file mode 100644 index 0000000..cc268c0 --- /dev/null +++ b/libsgl/sgl/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 = 6, + 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/libsgl/sgl/SkGraphics.cpp b/libsgl/sgl/SkGraphics.cpp new file mode 100644 index 0000000..ed9ab02 --- /dev/null +++ b/libsgl/sgl/SkGraphics.cpp @@ -0,0 +1,528 @@ +/* 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 "SkDOM.h" +#include "SkFloat.h" +#include "SkGeometry.h" +#include "SkGlobals.h" +#include "SkMath.h" +#include "SkMatrix.h" +#include "SkPath.h" +#include "SkPathEffect.h" +#include "SkPathMeasure.h" +#include "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" +#include "SkImageDecoder.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/libsgl/sgl/SkMask.cpp b/libsgl/sgl/SkMask.cpp new file mode 100644 index 0000000..b237639 --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkMaskFilter.cpp b/libsgl/sgl/SkMaskFilter.cpp new file mode 100644 index 0000000..56fff97 --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkPackBits.cpp b/libsgl/sgl/SkPackBits.cpp new file mode 100644 index 0000000..3e92841 --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkPaint.cpp b/libsgl/sgl/SkPaint.cpp new file mode 100644 index 0000000..3341f47 --- /dev/null +++ b/libsgl/sgl/SkPaint.cpp @@ -0,0 +1,1539 @@ +/* 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) +} + +//////////////////////////////////////////////////////////////////////////////////////// + +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/libsgl/sgl/SkPath.cpp b/libsgl/sgl/SkPath.cpp new file mode 100644 index 0000000..dd1290b --- /dev/null +++ b/libsgl/sgl/SkPath.cpp @@ -0,0 +1,1370 @@ +/* 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" + +/* + 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; + if (fPts.count() <= 1) { // we ignore just 1 point (moveto) + fFastBounds.set(0, 0, 0, 0); + } else { + fFastBounds.set(fPts.begin(), fPts.count()); + } + } +#ifdef SK_DEBUG + else { // check that our cache is correct + SkRect r; + if (fPts.count() <= 1) { // we ignore just 1 point (moveto) + r.set(0, 0, 0, 0); + } else { + r.set(fPts.begin(), fPts.count()); + } + SkASSERT(r == fFastBounds); + } +#endif + + *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) { + 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) { + 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) { + + 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) { + 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); + + this->incReserve(17); // 8 quads + close + this->moveTo(cx + rx, cy); + if (dir == kCCW_Direction) { + this->quadTo(cx + rx, cy - sy, cx + mx, cy - my); + this->quadTo(cx + sx, cy - ry, cx + 0, cy - ry); + this->quadTo(cx - sx, cy - ry, cx - mx, cy - my); + this->quadTo(cx - rx, cy - sy, cx - rx, cy - 0); + this->quadTo(cx - rx, cy + sy, cx - mx, cy + my); + this->quadTo(cx - sx, cy + ry, cx - 0, cy + ry); + this->quadTo(cx + sx, cy + ry, cx + mx, cy + my); + this->quadTo(cx + rx, cy + sy, cx + rx, cy + 0); + } else { + this->quadTo(cx + rx, cy + sy, cx + mx, cy + my); + this->quadTo(cx + sx, cy + ry, cx - 0, cy + ry); + this->quadTo(cx - sx, cy + ry, cx - mx, cy + my); + this->quadTo(cx - rx, cy + sy, cx - rx, cy - 0); + this->quadTo(cx - rx, cy - sy, cx - mx, cy - my); + this->quadTo(cx - sx, cy - ry, cx + 0, cy - ry); + this->quadTo(cx + sx, cy - ry, cx + mx, cy - my); + this->quadTo(cx + rx, cy - sy, cx + rx, cy + 0); + } +#endif + this->close(); +} + +void SkPath::addCircle(SkScalar x, SkScalar y, SkScalar r, Direction dir) { + if (r > 0) { + SkRect rect; + rect.set(x - r, y - r, x + r, y + r); + this->addOval(rect, dir); + } +} + +#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 { + 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()); + } +} + +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); + if (!fFastBoundsIsDirty) { + SkASSERT(fFastBounds.width() >= 0 && fFastBounds.height() >= 0); + } + fPts.validate(); + fVerbs.validate(); +} + +#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/libsgl/sgl/SkPathEffect.cpp b/libsgl/sgl/SkPathEffect.cpp new file mode 100644 index 0000000..2905895 --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkPathMeasure.cpp b/libsgl/sgl/SkPathMeasure.cpp new file mode 100644 index 0000000..ec1510d --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkPicture.cpp b/libsgl/sgl/SkPicture.cpp new file mode 100644 index 0000000..96deadd --- /dev/null +++ b/libsgl/sgl/SkPicture.cpp @@ -0,0 +1,238 @@ +/* +** +** 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); + } +} + diff --git a/libsgl/sgl/SkPixelRef.cpp b/libsgl/sgl/SkPixelRef.cpp new file mode 100644 index 0000000..adfc3c0 --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkProcSpriteBlitter.cpp b/libsgl/sgl/SkProcSpriteBlitter.cpp new file mode 100644 index 0000000..f727581 --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkPtrRecorder.cpp b/libsgl/sgl/SkPtrRecorder.cpp new file mode 100644 index 0000000..4f774ec --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkRasterizer.cpp b/libsgl/sgl/SkRasterizer.cpp new file mode 100644 index 0000000..f1d087d --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkRefCnt.cpp b/libsgl/sgl/SkRefCnt.cpp new file mode 100644 index 0000000..fea1005 --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkRegion_path.cpp b/libsgl/sgl/SkRegion_path.cpp new file mode 100644 index 0000000..6f56bff --- /dev/null +++ b/libsgl/sgl/SkRegion_path.cpp @@ -0,0 +1,457 @@ +/* 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(); + + void 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); +} + +void SkRgnBuilder::init(int maxHeight, int maxTransitions) { + int count = maxHeight * (3 + maxTransitions); + + // add maxTransitions to have slop for working buffer + fStorageCount = count + 3 + maxTransitions; + fStorage = (SkRegion::RunType*)sk_malloc_throw(fStorageCount * sizeof(SkRegion::RunType)); + + fCurrScanline = NULL; // signal empty collection + fPrevScanline = NULL; // signal first scanline +} + +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; + + builder.init(bot - top, SkMax32(pathTransitions, clipTransitions)); + SkScan::FillPath(path, clip, &builder); + builder.done(); + + int count = builder.computeRunCount(); + if (count == 0) { + return this->setEmpty(); + } else if (count == kRectRegionRuns) { + builder.copyToRect(&fBounds); + this->setRect(fBounds); + } else { + SkRegion tmp; + + tmp.fRunHead = RunHead::Alloc(count); + builder.copyToRgn(tmp.fRunHead->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/libsgl/sgl/SkScalerContext.cpp b/libsgl/sgl/SkScalerContext.cpp new file mode 100644 index 0000000..accc3ab --- /dev/null +++ b/libsgl/sgl/SkScalerContext.cpp @@ -0,0 +1,540 @@ +/* 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 (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*)glyph->fImage; + unsigned rowBytes = glyph->rowBytes(); + + for (int y = glyph->fHeight - 1; y >= 0; --y) + { + for (int x = glyph->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/libsgl/sgl/SkScan.cpp b/libsgl/sgl/SkScan.cpp new file mode 100644 index 0000000..013b0ea --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkScan.h b/libsgl/sgl/SkScan.h new file mode 100644 index 0000000..379c016 --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkScanPriv.h b/libsgl/sgl/SkScanPriv.h new file mode 100644 index 0000000..43dfdef --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkScan_AntiPath.cpp b/libsgl/sgl/SkScan_AntiPath.cpp new file mode 100644 index 0000000..9cdeeaa --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkScan_Antihair.cpp b/libsgl/sgl/SkScan_Antihair.cpp new file mode 100644 index 0000000..04e4690 --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkScan_Hairline.cpp b/libsgl/sgl/SkScan_Hairline.cpp new file mode 100644 index 0000000..6a66754 --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkScan_Path.cpp b/libsgl/sgl/SkScan_Path.cpp new file mode 100644 index 0000000..0d962ba --- /dev/null +++ b/libsgl/sgl/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_MinS16 +#define kEDGE_TAIL_Y SK_MaxS16 + +#ifdef SK_DEBUG + static void validate_sort(const SkEdge* edge) + { + int y = kEDGE_HEAD_Y; + + while (edge->fFirstY != SK_MaxS16) + { + edge->validate(); + SkASSERT(y <= edge->fFirstY); + + y = edge->fFirstY; + edge = edge->fNext; + } + } +#else + #define validate_sort(edge) +#endif + +static inline void remove_edge(SkEdge* edge) +{ + edge->fPrev->fNext = edge->fNext; + edge->fNext->fPrev = edge->fPrev; +} + +static inline void swap_edges(SkEdge* prev, SkEdge* next) +{ + SkASSERT(prev->fNext == next && next->fPrev == prev); + + // remove prev from the list + prev->fPrev->fNext = next; + next->fPrev = prev->fPrev; + + // insert prev after next + prev->fNext = next->fNext; + next->fNext->fPrev = prev; + next->fNext = prev; + prev->fPrev = next; +} + +static void backward_insert_edge_based_on_x(SkEdge* edge SkDECLAREPARAM(int, curr_y)) +{ + SkFixed x = edge->fX; + + for (;;) + { + SkEdge* prev = edge->fPrev; + + // add 1 to curr_y since we may have added new edges (built from curves) + // that start on the next scanline + SkASSERT(prev && prev->fFirstY <= curr_y + 1); + + if (prev->fX <= x) + break; + + swap_edges(prev, edge); + } +} + +static void insert_new_edges(SkEdge* newEdge, int curr_y) +{ + SkASSERT(newEdge->fFirstY >= curr_y); + + while (newEdge->fFirstY == curr_y) + { + SkEdge* next = newEdge->fNext; + backward_insert_edge_based_on_x(newEdge SkPARAM(curr_y)); + newEdge = next; + } +} + +#ifdef SK_DEBUG +static void validate_edges_for_y(const SkEdge* edge, int curr_y) +{ + while (edge->fFirstY <= curr_y) + { + SkASSERT(edge->fPrev && edge->fNext); + SkASSERT(edge->fPrev->fNext == edge); + SkASSERT(edge->fNext->fPrev == edge); + SkASSERT(edge->fFirstY <= edge->fLastY); + + SkASSERT(edge->fPrev->fX <= edge->fX); + edge = edge->fNext; + } +} +#else + #define validate_edges_for_y(edge, curr_y) +#endif + +#if defined _WIN32 && _MSC_VER >= 1300 // disable warning : local variable used without having been initialized +#pragma warning ( push ) +#pragma warning ( disable : 4701 ) +#endif + +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/libsgl/sgl/SkShader.cpp b/libsgl/sgl/SkShader.cpp new file mode 100644 index 0000000..dd9c859 --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkSpriteBlitter.h b/libsgl/sgl/SkSpriteBlitter.h new file mode 100644 index 0000000..9f7764d --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkSpriteBlitterTemplate.h b/libsgl/sgl/SkSpriteBlitterTemplate.h new file mode 100644 index 0000000..2ab2b3a --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkSpriteBlitter_ARGB32.cpp b/libsgl/sgl/SkSpriteBlitter_ARGB32.cpp new file mode 100644 index 0000000..6addde7 --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkSpriteBlitter_RGB16.cpp b/libsgl/sgl/SkSpriteBlitter_RGB16.cpp new file mode 100644 index 0000000..a158637 --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkString.cpp b/libsgl/sgl/SkString.cpp new file mode 100644 index 0000000..c37453d --- /dev/null +++ b/libsgl/sgl/SkString.cpp @@ -0,0 +1,625 @@ +/* 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); + *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 <stdarg.h> +#if defined(SK_BUILD_FOR_WIN) || defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_UNIX) + #include <stdio.h> +#endif + +void SkString::printf(const char format[], ...) +{ + static const size_t kBufferSize = 100; + + char buffer[kBufferSize + 1]; + +#ifdef SK_BUILD_FOR_WIN + va_list args; + va_start(args, format); + _vsnprintf(buffer, kBufferSize, format, args); + va_end(args); +#elif defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_UNIX) + va_list args; + va_start(args, format); + vsnprintf(buffer, kBufferSize, format, args); + va_end(args); +#else + buffer[0] = 0; +#endif + + this->set(buffer, strlen(buffer)); +} + +/////////////////////////////////////////////////////////////////////////// + +void SkString::remove(size_t offset, size_t length) +{ + size_t size = this->size(); + + if (offset < size) + { + if (offset + length > size) + length = size - offset; + if (length > 0) + { + SkASSERT(size > length); + SkString tmp(size - length); + char* dst = tmp.writable_str(); + const char* src = this->c_str(); + + if (offset) + { + SkASSERT(offset <= tmp.size()); + memcpy(dst, src, offset); + } + size_t tail = size - offset - length; + SkASSERT((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/libsgl/sgl/SkStroke.cpp b/libsgl/sgl/SkStroke.cpp new file mode 100644 index 0000000..86dff48 --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkStrokerPriv.cpp b/libsgl/sgl/SkStrokerPriv.cpp new file mode 100644 index 0000000..07833ca --- /dev/null +++ b/libsgl/sgl/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(¶llel); + + if (otherPath) + { + path->setLastPt(pivot.fX + normal.fX + parallel.fX, pivot.fY + normal.fY + parallel.fY); + path->lineTo(pivot.fX - normal.fX + parallel.fX, pivot.fY - normal.fY + parallel.fY); + } + else + { + path->lineTo(pivot.fX + normal.fX + parallel.fX, pivot.fY + normal.fY + parallel.fY); + path->lineTo(pivot.fX - normal.fX + parallel.fX, pivot.fY - normal.fY + parallel.fY); + path->lineTo(stop.fX, stop.fY); + } +} + +///////////////////////////////////////////////////////////////////////////// + +static bool is_clockwise(const SkVector& before, const SkVector& after) +{ + return SkScalarMul(before.fX, after.fY) - SkScalarMul(before.fY, after.fX) > 0; +} + +enum AngleType { + kNearly180_AngleType, + kSharp_AngleType, + kShallow_AngleType, + kNearlyLine_AngleType +}; + +static AngleType Dot2AngleType(SkScalar dot) +{ +// need more precise fixed normalization +// SkASSERT(SkScalarAbs(dot) <= SK_Scalar1 + SK_ScalarNearlyZero); + + if (dot >= 0) // shallow or line + return SkScalarNearlyZero(SK_Scalar1 - dot) ? kNearlyLine_AngleType : kShallow_AngleType; + else // sharp or 180 + return SkScalarNearlyZero(SK_Scalar1 + dot) ? kNearly180_AngleType : kSharp_AngleType; +} + +static void 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/libsgl/sgl/SkStrokerPriv.h b/libsgl/sgl/SkStrokerPriv.h new file mode 100644 index 0000000..ecb9bde --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkTSearch.cpp b/libsgl/sgl/SkTSearch.cpp new file mode 100644 index 0000000..bab348f --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkTSort.h b/libsgl/sgl/SkTSort.h new file mode 100644 index 0000000..fba49e2 --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkTemplatesPriv.h b/libsgl/sgl/SkTemplatesPriv.h new file mode 100644 index 0000000..b0a95a0 --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkTypeface.cpp b/libsgl/sgl/SkTypeface.cpp new file mode 100644 index 0000000..9821c51 --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkUnPreMultiply.cpp b/libsgl/sgl/SkUnPreMultiply.cpp new file mode 100644 index 0000000..371af32 --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkUtils.cpp b/libsgl/sgl/SkUtils.cpp new file mode 100644 index 0000000..4f5ba6e --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkWriter32.cpp b/libsgl/sgl/SkWriter32.cpp new file mode 100644 index 0000000..61d0051 --- /dev/null +++ b/libsgl/sgl/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/libsgl/sgl/SkXfermode.cpp b/libsgl/sgl/SkXfermode.cpp new file mode 100644 index 0000000..3608b94 --- /dev/null +++ b/libsgl/sgl/SkXfermode.cpp @@ -0,0 +1,965 @@ +/* + * 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); +} + +/////////////////////////////////////////////////////////////////////////////// + +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; +} + |