diff options
author | Derek Sollenberger <djsollen@google.com> | 2011-07-26 11:32:25 -0400 |
---|---|---|
committer | Derek Sollenberger <djsollen@google.com> | 2011-07-26 11:32:25 -0400 |
commit | df9bcf50153969dcaaa2c869a1980724509bcc16 (patch) | |
tree | 53cc8e34122fdc45fff4e5413004fa1e5dfa7fac | |
parent | 8c52c218e8b00b33f837ac4891fcde3a08817169 (diff) | |
download | skia-df9bcf50153969dcaaa2c869a1980724509bcc16.tar.gz |
Fix shader crash when drawing a bitmap.
This change has also been submitted upstream to Skia in r1954.
bug: 5060654
Change-Id: Iac9ce0d9150c59e4db6653081d7f46843ea8f2bf
-rw-r--r-- | src/core/SkDraw.cpp | 198 |
1 files changed, 98 insertions, 100 deletions
diff --git a/src/core/SkDraw.cpp b/src/core/SkDraw.cpp index 74a30e9c91..5001296049 100644 --- a/src/core/SkDraw.cpp +++ b/src/core/SkDraw.cpp @@ -38,50 +38,33 @@ //#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 { +/** Helper for allocating small blitters on the stack. + */ +class SkAutoBlitterChoose : SkNoncopyable { public: + SkAutoBlitterChoose() { + fBlitter = NULL; + } 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; } + void choose(const SkBitmap& device, const SkMatrix& matrix, + const SkPaint& paint) { + SkASSERT(!fBlitter); + fBlitter = SkBlitter::Choose(device, matrix, paint, + fStorage, sizeof(fStorage)); + } + private: SkBlitter* fBlitter; uint32_t fStorage[kBlitterStorageLongCount]; @@ -95,23 +78,30 @@ SkAutoBlitterChoose::~SkAutoBlitterChoose() { } } -class SkAutoBitmapShaderInstall { +/** + * Since we are providing the storage for the shader (to avoid the perf cost + * of calling new) we insist that in our destructor we can account for all + * owners of the shader. + */ +class SkAutoBitmapShaderInstall : SkNoncopyable { public: - SkAutoBitmapShaderInstall(const SkBitmap& src, const SkPaint* paint) - : fPaint((SkPaint*)paint) { - fPrevShader = paint->getShader(); - SkSafeRef(fPrevShader); - fPaint->setShader(SkShader::CreateBitmapShader( src, + SkAutoBitmapShaderInstall(const SkBitmap& src, const SkPaint& paint) + : fPaint(paint) /* makes a copy of the paint */ { + fPaint.setShader(SkShader::CreateBitmapShader(src, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, fStorage, sizeof(fStorage))); + // we deliberately left the shader with an owner-count of 2 + SkASSERT(2 == fPaint.getShader()->getRefCnt()); } ~SkAutoBitmapShaderInstall() { - SkShader* shader = fPaint->getShader(); + SkShader* shader = fPaint.getShader(); + // since we manually destroy shader, we insist that owners == 2 + SkASSERT(2 == shader->getRefCnt()); - fPaint->setShader(fPrevShader); - SkSafeUnref(fPrevShader); + fPaint.setShader(NULL); // unref the shader by 1 + // now destroy to take care of the 2nd owner-count if ((void*)shader == (void*)fStorage) { shader->~SkShader(); } else { @@ -119,31 +109,12 @@ public: } } -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 - } + // return the new paint that has the shader applied + const SkPaint& paintWithShader() const { return fPaint; } private: - SkPaint& fPaint; - SkPaint::Style fStyle; - - // illegal - SkAutoPaintStyleRestore(const SkAutoPaintStyleRestore&); - SkAutoPaintStyleRestore& operator=(const SkAutoPaintStyleRestore&); + SkPaint fPaint; // copy of caller's paint (which we then modify) + uint32_t fStorage[kBlitterStorageLongCount]; }; /////////////////////////////////////////////////////////////////////////////// @@ -560,18 +531,6 @@ void SkDraw::drawPoints(SkCanvas::PointMode mode, size_t count, 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();) @@ -581,6 +540,19 @@ void SkDraw::drawPoints(SkCanvas::PointMode mode, size_t count, return; } + if (fBounder) { + if (!bounder_points(fBounder, mode, count, pts, paint, *fMatrix)) { + return; + } + + // clear the bounder and call this again, so we don't invoke the bounder + // later if we happen to call ourselves for drawRect, drawPath, etc. + SkDraw noBounder(*this); + noBounder.fBounder = NULL; + noBounder.drawPoints(mode, count, pts, paint, forceUseDevice); + return; + } + PtProcRec rec; if (!forceUseDevice && rec.init(mode, paint, fMatrix, fClip)) { SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, paint); @@ -610,12 +582,13 @@ void SkDraw::drawPoints(SkCanvas::PointMode mode, size_t count, switch (mode) { case SkCanvas::kPoints_PointMode: { // temporarily mark the paint as filling. - SkAutoPaintStyleRestore restore(paint, SkPaint::kFill_Style); + SkPaint newPaint(paint); + newPaint.setStyle(SkPaint::kFill_Style); - SkScalar width = paint.getStrokeWidth(); + SkScalar width = newPaint.getStrokeWidth(); SkScalar radius = SkScalarHalf(width); - if (paint.getStrokeCap() == SkPaint::kRound_Cap) { + if (newPaint.getStrokeCap() == SkPaint::kRound_Cap) { SkPath path; SkMatrix preMatrix; @@ -625,10 +598,11 @@ void SkDraw::drawPoints(SkCanvas::PointMode mode, size_t count, // pass true for the last point, since we can modify // then path then if (fDevice) { - fDevice->drawPath(*this, path, paint, &preMatrix, + fDevice->drawPath(*this, path, newPaint, &preMatrix, (count-1) == i); } else { - this->drawPath(path, paint, &preMatrix, (count-1) == i); + this->drawPath(path, newPaint, &preMatrix, + (count-1) == i); } } } else { @@ -640,9 +614,9 @@ void SkDraw::drawPoints(SkCanvas::PointMode mode, size_t count, r.fRight = r.fLeft + width; r.fBottom = r.fTop + width; if (fDevice) { - fDevice->drawRect(*this, r, paint); + fDevice->drawRect(*this, r, newPaint); } else { - this->drawRect(r, paint); + this->drawRect(r, newPaint); } } } @@ -722,6 +696,11 @@ SkDraw::RectType SkDraw::ComputeRectType(const SkPaint& paint, return rtype; } +static SkPoint* rect_points(SkRect& r, int index) { + SkASSERT((unsigned)index < 2); + return &((SkPoint*)(void*)&r)[index]; +} + void SkDraw::drawRect(const SkRect& rect, const SkPaint& paint) const { SkDEBUGCODE(this->validate();) @@ -1079,11 +1058,11 @@ void SkDraw::drawBitmapAsMask(const SkBitmap& bitmap, // we manually build a shader and draw that into our new mask SkPaint tmpPaint; tmpPaint.setFlags(paint.getFlags()); - SkAutoBitmapShaderInstall install(bitmap, &tmpPaint); + SkAutoBitmapShaderInstall install(bitmap, tmpPaint); SkRect rr; rr.set(0, 0, SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height())); - c.drawRect(rr, tmpPaint); + c.drawRect(rr, install.paintWithShader()); } this->drawDevMask(mask, paint); } @@ -1107,14 +1086,14 @@ static bool clipped_out(const SkMatrix& matrix, const SkRegion& clip, } void SkDraw::drawBitmap(const SkBitmap& bitmap, const SkMatrix& prematrix, - const SkPaint& paint) const { + const SkPaint& origPaint) 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)) { + (origPaint.getAlpha() == 0 && origPaint.getXfermode() == NULL)) { return; } @@ -1125,7 +1104,8 @@ void SkDraw::drawBitmap(const SkBitmap& bitmap, const SkMatrix& prematrix, } #endif - SkAutoPaintStyleRestore restore(paint, SkPaint::kFill_Style); + SkPaint paint(origPaint); + paint.setStyle(SkPaint::kFill_Style); SkMatrix matrix; if (!matrix.setConcat(*fMatrix, prematrix)) { @@ -1190,25 +1170,25 @@ void SkDraw::drawBitmap(const SkBitmap& bitmap, const SkMatrix& prematrix, if (bitmap.getConfig() == SkBitmap::kA8_Config) { draw.drawBitmapAsMask(bitmap, paint); } else { - SkAutoBitmapShaderInstall install(bitmap, &paint); + 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); + draw.drawRect(r, install.paintWithShader()); } } void SkDraw::drawSprite(const SkBitmap& bitmap, int x, int y, - const SkPaint& paint) const { + const SkPaint& origPaint) 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)) { + (origPaint.getAlpha() == 0 && origPaint.getXfermode() == NULL)) { return; } @@ -1219,7 +1199,8 @@ void SkDraw::drawSprite(const SkBitmap& bitmap, int x, int y, return; // nothing to draw } - SkAutoPaintStyleRestore restore(paint, SkPaint::kFill_Style); + SkPaint paint(origPaint); + paint.setStyle(SkPaint::kFill_Style); if (NULL == paint.getColorFilter()) { uint32_t storage[kBlitterStorageLongCount]; @@ -1244,7 +1225,8 @@ void SkDraw::drawSprite(const SkBitmap& bitmap, int x, int y, } } - SkAutoBitmapShaderInstall install(bitmap, &paint); + SkAutoBitmapShaderInstall install(bitmap, paint); + const SkPaint& shaderPaint = install.paintWithShader(); SkMatrix matrix; SkRect r; @@ -1254,14 +1236,14 @@ void SkDraw::drawSprite(const SkBitmap& bitmap, int x, int y, // tell the shader our offset matrix.setTranslate(r.fLeft, r.fTop); - paint.getShader()->setLocalMatrix(matrix); + shaderPaint.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); + draw.drawRect(r, shaderPaint); } /////////////////////////////////////////////////////////////////////////////// @@ -1487,6 +1469,14 @@ static void D1G_Bounder(const SkDraw1Glyph& state, } } +static bool hasCustomD1GProc(const SkDraw& draw) { + return draw.fProcs && draw.fProcs->fD1GProc; +} + +static bool needsRasterTextBlit(const SkDraw& draw) { + return !hasCustomD1GProc(draw); +} + SkDraw1Glyph::Proc SkDraw1Glyph::init(const SkDraw* draw, SkBlitter* blitter, SkGlyphCache* cache) { fDraw = draw; @@ -1496,7 +1486,7 @@ SkDraw1Glyph::Proc SkDraw1Glyph::init(const SkDraw* draw, SkBlitter* blitter, fBlitter = blitter; fCache = cache; - if (draw->fProcs && draw->fProcs->fD1GProc) { + if (hasCustomD1GProc(*draw)) { return draw->fProcs->fD1GProc; } @@ -1572,7 +1562,7 @@ void SkDraw::drawText(const char text[], size_t byteLength, const SkMatrix* matrix = fMatrix; SkFixed finalFYMask = ~0xFFFF; // trunc fy; - if (fProcs && fProcs->fD1GProc) { + if (hasCustomD1GProc(*this)) { // only support the fMVMatrix (for now) for the GPU case, which also // sets the fD1GProc if (fMVMatrix) { @@ -1583,7 +1573,6 @@ void SkDraw::drawText(const char text[], size_t byteLength, SkAutoGlyphCache autoCache(paint, matrix); SkGlyphCache* cache = autoCache.getCache(); - SkAutoBlitterChoose blitter(*fBitmap, *matrix, paint); // transform our starting point { @@ -1630,6 +1619,11 @@ void SkDraw::drawText(const char text[], size_t byteLength, fy += SK_FixedHalf; fyMask &= finalFYMask; + SkAutoBlitterChoose blitter; + if (needsRasterTextBlit(*this)) { + blitter.choose(*fBitmap, *matrix, paint); + } + SkAutoKern autokern; SkDraw1Glyph d1g; SkDraw1Glyph::Proc proc = d1g.init(this, blitter.get(), cache); @@ -1767,7 +1761,7 @@ void SkDraw::drawPosText(const char text[], size_t byteLength, } const SkMatrix* matrix = fMatrix; - if (fProcs && fProcs->fD1GProc) { + if (hasCustomD1GProc(*this)) { // only support the fMVMatrix (for now) for the GPU case, which also // sets the fD1GProc if (fMVMatrix) { @@ -1778,8 +1772,12 @@ void SkDraw::drawPosText(const char text[], size_t byteLength, SkDrawCacheProc glyphCacheProc = paint.getDrawCacheProc(); SkAutoGlyphCache autoCache(paint, matrix); SkGlyphCache* cache = autoCache.getCache(); - SkAutoBlitterChoose blitter(*fBitmap, *matrix, paint); + SkAutoBlitterChoose blitter; + if (needsRasterTextBlit(*this)) { + blitter.choose(*fBitmap, *matrix, paint); + } + const char* stop = text + byteLength; AlignProc alignProc = pick_align_proc(paint.getTextAlign()); SkDraw1Glyph d1g; |