From b761984a9fdb89ac1b588a92f3b37b50eb3d6a9f Mon Sep 17 00:00:00 2001 From: "djsollen@google.com" Date: Wed, 4 Sep 2013 15:51:35 +0000 Subject: cherrypick rev 10981 into branch m30_1599 to fix http://crbug.com/278141 git-svn-id: http://skia.googlecode.com/svn/branches/chrome/m30_1599/src@11077 2bbb7eff-a529-9590-31e7-b0007b416f81 --- core/SkDeviceLooper.cpp | 114 ++++++++++++++++++++++++++++++++++++++++++++++++ core/SkDeviceLooper.h | 96 ++++++++++++++++++++++++++++++++++++++++ core/SkDraw.cpp | 91 +++++++++++++++++++++----------------- 3 files changed, 260 insertions(+), 41 deletions(-) create mode 100644 core/SkDeviceLooper.cpp create mode 100644 core/SkDeviceLooper.h diff --git a/core/SkDeviceLooper.cpp b/core/SkDeviceLooper.cpp new file mode 100644 index 00000000..4122fd7a --- /dev/null +++ b/core/SkDeviceLooper.cpp @@ -0,0 +1,114 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkDeviceLooper.h" + +SkDeviceLooper::SkDeviceLooper(const SkBitmap& base, + const SkRasterClip& rc, + const SkIRect& bounds, bool aa) +: fBaseBitmap(base) +, fBaseRC(rc) +, fDelta(aa ? kAA_Delta : kBW_Delta) +{ + // sentinels that next() has not yet been called, and so our mapper functions + // should not be called either. + fCurrBitmap = NULL; + fCurrRC = NULL; + + SkIRect bitmapBounds = SkIRect::MakeWH(base.width(), base.height()); + if (!fClippedBounds.intersect(bounds, bitmapBounds)) { + fState = kDone_State; + } else if (this->fitsInDelta(bounds)) { + fState = kSimple_State; + } else { + // back up by 1 DX, so that next() will put us in a correct starting + // position. + fCurrOffset.set(fClippedBounds.left() - fDelta, + fClippedBounds.top()); + fState = kComplex_State; + } +} + +SkDeviceLooper::~SkDeviceLooper() { +} + +void SkDeviceLooper::mapRect(SkRect* dst, const SkRect& src) const { + SkASSERT(kDone_State != fState); + SkASSERT(fCurrBitmap); + SkASSERT(fCurrRC); + + *dst = src; + dst->offset(SkIntToScalar(-fCurrOffset.fX), + SkIntToScalar(-fCurrOffset.fY)); +} + +void SkDeviceLooper::mapMatrix(SkMatrix* dst, const SkMatrix& src) const { + SkASSERT(kDone_State != fState); + SkASSERT(fCurrBitmap); + SkASSERT(fCurrRC); + + *dst = src; + dst->postTranslate(SkIntToScalar(-fCurrOffset.fX), + SkIntToScalar(-fCurrOffset.fY)); +} + +bool SkDeviceLooper::computeCurrBitmapAndClip() { + SkASSERT(kComplex_State == fState); + + SkIRect r = SkIRect::MakeXYWH(fCurrOffset.x(), fCurrOffset.y(), + fDelta, fDelta); + if (!fBaseBitmap.extractSubset(&fSubsetBitmap, r)) { + fState = kDone_State; + return false; + } + fSubsetBitmap.lockPixels(); + + fBaseRC.translate(-r.left(), -r.top(), &fSubsetRC); + (void)fSubsetRC.op(SkIRect::MakeWH(fDelta, fDelta), SkRegion::kIntersect_Op); + + fCurrBitmap = &fSubsetBitmap; + fCurrRC = &fSubsetRC; + return true; +} + +bool SkDeviceLooper::next() { + switch (fState) { + case kDone_State: + // in theory, we should not get called here, since we must have + // previously returned false, but we check anyway. + break; + + case kSimple_State: + // first time for simple + if (NULL == fCurrBitmap) { + fCurrBitmap = &fBaseBitmap; + fCurrRC = &fBaseRC; + fCurrOffset.set(0, 0); + return true; + } + // 2nd time for simple, we are done + break; + + case kComplex_State: + // need to propogate fCurrOffset through clippedbounds + // left to right, until we wrap around and move down + + if (fCurrOffset.x() + fDelta < fClippedBounds.right()) { + fCurrOffset.fX += fDelta; + return this->computeCurrBitmapAndClip(); + } + fCurrOffset.fX = fClippedBounds.left(); + if (fCurrOffset.y() + fDelta < fClippedBounds.bottom()) { + fCurrOffset.fY += fDelta; + return this->computeCurrBitmapAndClip(); + } + break; + } + + fState = kDone_State; + return false; +} diff --git a/core/SkDeviceLooper.h b/core/SkDeviceLooper.h new file mode 100644 index 00000000..405173dd --- /dev/null +++ b/core/SkDeviceLooper.h @@ -0,0 +1,96 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkDeviceLooper_DEFINED +#define SkDeviceLooper_DEFINED + +#include "SkBitmap.h" +#include "SkMatrix.h" +#include "SkRasterClip.h" + +/** + * Helper class to manage "tiling" a large coordinate space into managable + * chunks, where managable means areas that are <= some max critical coordinate + * size. + * + * The constructor takes an antialiasing bool, which affects what this maximum + * allowable size is: If we're drawing BW, then we need coordinates to stay + * safely within fixed-point range (we use +- 16K, to give ourselves room to + * add/subtract two fixed values and still be in range. If we're drawing AA, + * then we reduce that size by the amount that the supersampler scan converter + * needs (at the moment, that is 4X, so the "safe" range is +- 4K). + * + * For performance reasons, the class first checks to see if any help is needed + * at all, and if not (i.e. the specified bounds and base bitmap area already + * in the safe-zone, then the class does nothing (effectively). + */ +class SkDeviceLooper { +public: + SkDeviceLooper(const SkBitmap& base, const SkRasterClip&, + const SkIRect& bounds, bool aa); + ~SkDeviceLooper(); + + const SkBitmap& getBitmap() const { + SkASSERT(kDone_State != fState); + SkASSERT(fCurrBitmap); + return *fCurrBitmap; + } + + const SkRasterClip& getRC() const { + SkASSERT(kDone_State != fState); + SkASSERT(fCurrRC); + return *fCurrRC; + } + + void mapRect(SkRect* dst, const SkRect& src) const; + void mapMatrix(SkMatrix* dst, const SkMatrix& src) const; + + /** + * Call next to setup the looper to return a valid coordinate chunk. + * Each time this returns true, it is safe to call mapRect() and + * mapMatrix(), to convert from "global" coordinate values to ones that + * are local to this chunk. + * + * When next() returns false, the list of chunks is done, and mapRect() + * and mapMatrix() should no longer be called. + */ + bool next(); + +private: + const SkBitmap& fBaseBitmap; + const SkRasterClip& fBaseRC; + + enum State { + kDone_State, // iteration is complete, getters will assert + kSimple_State, // no translate/clip mods needed + kComplex_State + }; + + // storage for our tiled versions. Perhaps could use SkTLazy + SkBitmap fSubsetBitmap; + SkRasterClip fSubsetRC; + + const SkBitmap* fCurrBitmap; + const SkRasterClip* fCurrRC; + SkIRect fClippedBounds; + SkIPoint fCurrOffset; + int fDelta; + State fState; + + enum Delta { + kBW_Delta = 1 << 14, // 16K, gives room to spare for fixedpoint + kAA_Delta = kBW_Delta >> 2 // supersample 4x + }; + + bool fitsInDelta(const SkIRect& r) const { + return r.right() < fDelta && r.bottom() < fDelta; + } + + bool computeCurrBitmapAndClip(); +}; + +#endif diff --git a/core/SkDraw.cpp b/core/SkDraw.cpp index 671da49d..a9d5fbb0 100644 --- a/core/SkDraw.cpp +++ b/core/SkDraw.cpp @@ -5,13 +5,13 @@ * found in the LICENSE file. */ - #include "SkDraw.h" #include "SkBlitter.h" #include "SkBounder.h" #include "SkCanvas.h" #include "SkColorPriv.h" #include "SkDevice.h" +#include "SkDeviceLooper.h" #include "SkFixed.h" #include "SkMaskFilter.h" #include "SkPaint.h" @@ -873,48 +873,56 @@ void SkDraw::drawRect(const SkRect& rect, const SkPaint& paint) const { } // look for the quick exit, before we build a blitter - if (true) { - SkIRect ir; - devRect.roundOut(&ir); - if (paint.getStyle() != SkPaint::kFill_Style) { - // extra space for hairlines - ir.inset(-1, -1); - } - if (fRC->quickReject(ir)) - return; + SkIRect ir; + devRect.roundOut(&ir); + if (paint.getStyle() != SkPaint::kFill_Style) { + // extra space for hairlines + ir.inset(-1, -1); + } + if (fRC->quickReject(ir)) { + return; } - SkAutoBlitterChoose blitterStorage(*fBitmap, matrix, paint); - const SkRasterClip& clip = *fRC; - SkBlitter* blitter = blitterStorage.get(); - - // we want to "fill" if we are kFill or kStrokeAndFill, since in the latter - // case we are also hairline (if we've gotten to here), which devolves to - // effectively just kFill - switch (rtype) { - case kFill_RectType: - if (paint.isAntiAlias()) { - SkScan::AntiFillRect(devRect, clip, blitter); - } else { - SkScan::FillRect(devRect, clip, blitter); - } - break; - case kStroke_RectType: - if (paint.isAntiAlias()) { - SkScan::AntiFrameRect(devRect, strokeSize, clip, blitter); - } else { - SkScan::FrameRect(devRect, strokeSize, clip, blitter); - } - break; - case kHair_RectType: - if (paint.isAntiAlias()) { - SkScan::AntiHairRect(devRect, clip, blitter); - } else { - SkScan::HairRect(devRect, clip, blitter); - } - break; - default: - SkDEBUGFAIL("bad rtype"); + SkDeviceLooper looper(*fBitmap, *fRC, ir, paint.isAntiAlias()); + while (looper.next()) { + SkRect localDevRect; + looper.mapRect(&localDevRect, devRect); + SkMatrix localMatrix; + looper.mapMatrix(&localMatrix, matrix); + + SkAutoBlitterChoose blitterStorage(looper.getBitmap(), localMatrix, + paint); + const SkRasterClip& clip = looper.getRC(); + SkBlitter* blitter = blitterStorage.get(); + + // we want to "fill" if we are kFill or kStrokeAndFill, since in the latter + // case we are also hairline (if we've gotten to here), which devolves to + // effectively just kFill + switch (rtype) { + case kFill_RectType: + if (paint.isAntiAlias()) { + SkScan::AntiFillRect(localDevRect, clip, blitter); + } else { + SkScan::FillRect(localDevRect, clip, blitter); + } + break; + case kStroke_RectType: + if (paint.isAntiAlias()) { + SkScan::AntiFrameRect(localDevRect, strokeSize, clip, blitter); + } else { + SkScan::FrameRect(localDevRect, strokeSize, clip, blitter); + } + break; + case kHair_RectType: + if (paint.isAntiAlias()) { + SkScan::AntiHairRect(localDevRect, clip, blitter); + } else { + SkScan::HairRect(localDevRect, clip, blitter); + } + break; + default: + SkDEBUGFAIL("bad rtype"); + } } } @@ -2825,3 +2833,4 @@ bool SkDraw::DrawToMask(const SkPath& devPath, const SkIRect* clipBounds, return true; } + -- cgit v1.2.3