diff options
Diffstat (limited to 'skia')
-rw-r--r-- | skia/ext/skia_utils_mac.h | 20 | ||||
-rw-r--r-- | skia/ext/skia_utils_mac.mm | 169 |
2 files changed, 127 insertions, 62 deletions
diff --git a/skia/ext/skia_utils_mac.h b/skia/ext/skia_utils_mac.h index 6a0efde..e087867 100644 --- a/skia/ext/skia_utils_mac.h +++ b/skia/ext/skia_utils_mac.h @@ -109,16 +109,36 @@ SK_API NSImage* SkBitmapToNSImage(const SkBitmap& icon); class SK_API SkiaBitLocker { public: explicit SkiaBitLocker(SkCanvas* canvas); + SkiaBitLocker(SkCanvas* canvas, const SkIRect& userClipRect); ~SkiaBitLocker(); CGContextRef cgContext(); + bool hasEmptyClipRegion() const; private: void releaseIfNeeded(); + SkIRect computeDirtyRect(); + SkCanvas* canvas_; + + // If the user specified a clip rect it would draw into then the locker may + // skip the step of searching for a rect bounding the pixels that the user + // has drawn into. + bool userClipRectSpecified_; + CGContextRef cgContext_; SkBitmap bitmap_; SkIPoint bitmapOffset_; + + // True if we are drawing to |canvas_|'s SkBaseDevice's bits directly through + // |bitmap_|. Otherwise, the bits in |bitmap_| are our allocation and need to + // be copied over to |canvas_|. bool useDeviceBits_; + + // True if |bitmap_| is a dummy 1x1 bitmap allocated for the sake of creating + // a non-NULL CGContext (it is invalid to use a NULL CGContext), and will not + // be copied to |canvas_|. This will happen if |canvas_|'s clip region is + // empty. + bool bitmapIsDummy_; }; diff --git a/skia/ext/skia_utils_mac.mm b/skia/ext/skia_utils_mac.mm index 4f7ddd9..d6b6041 100644 --- a/skia/ext/skia_utils_mac.mm +++ b/skia/ext/skia_utils_mac.mm @@ -267,81 +267,107 @@ NSImage* SkBitmapToNSImage(const SkBitmap& skiaBitmap) { SkiaBitLocker::SkiaBitLocker(SkCanvas* canvas) : canvas_(canvas), - cgContext_(0) { + userClipRectSpecified_(false), + cgContext_(0), + useDeviceBits_(false), + bitmapIsDummy_(false) { +} + +SkiaBitLocker::SkiaBitLocker(SkCanvas* canvas, const SkIRect& userClipRect) + : canvas_(canvas), + userClipRectSpecified_(true), + cgContext_(0), + useDeviceBits_(false), + bitmapIsDummy_(false) { + canvas_->save(); + canvas_->clipRect(SkRect::MakeFromIRect(userClipRect)); } SkiaBitLocker::~SkiaBitLocker() { releaseIfNeeded(); + if (userClipRectSpecified_) + canvas_->restore(); } -// This must be called to balance calls to cgContext -void SkiaBitLocker::releaseIfNeeded() { - if (!cgContext_) - return; - if (useDeviceBits_) { - bitmap_.unlockPixels(); - } else { - // Find the bits that were drawn to. - SkAutoLockPixels lockedPixels(bitmap_); - const uint32_t* pixelBase - = reinterpret_cast<uint32_t*>(bitmap_.getPixels()); - int rowPixels = bitmap_.rowBytesAsPixels(); - int width = bitmap_.width(); - int height = bitmap_.height(); - SkIRect bounds; - bounds.fTop = 0; - int x; - int y = -1; - const uint32_t* pixels = pixelBase; - while (++y < height) { - for (x = 0; x < width; ++x) { - if (pixels[x]) { - bounds.fTop = y; - goto foundTop; - } +SkIRect SkiaBitLocker::computeDirtyRect() { + // If the user specified a clip region, assume that it was tight and that the + // dirty rect is approximately the whole bitmap. + if (userClipRectSpecified_) + return SkIRect::MakeWH(bitmap_.width(), bitmap_.height()); + + // Find the bits that were drawn to. + SkAutoLockPixels lockedPixels(bitmap_); + const uint32_t* pixelBase + = reinterpret_cast<uint32_t*>(bitmap_.getPixels()); + int rowPixels = bitmap_.rowBytesAsPixels(); + int width = bitmap_.width(); + int height = bitmap_.height(); + SkIRect bounds; + bounds.fTop = 0; + int x; + int y = -1; + const uint32_t* pixels = pixelBase; + while (++y < height) { + for (x = 0; x < width; ++x) { + if (pixels[x]) { + bounds.fTop = y; + goto foundTop; } - pixels += rowPixels; } + pixels += rowPixels; + } foundTop: - bounds.fBottom = height; - y = height; - pixels = pixelBase + rowPixels * (y - 1); - while (--y > bounds.fTop) { - for (x = 0; x < width; ++x) { - if (pixels[x]) { - bounds.fBottom = y + 1; - goto foundBottom; - } + bounds.fBottom = height; + y = height; + pixels = pixelBase + rowPixels * (y - 1); + while (--y > bounds.fTop) { + for (x = 0; x < width; ++x) { + if (pixels[x]) { + bounds.fBottom = y + 1; + goto foundBottom; } - pixels -= rowPixels; } + pixels -= rowPixels; + } foundBottom: - bounds.fLeft = 0; - x = -1; - while (++x < width) { - pixels = pixelBase + rowPixels * bounds.fTop; - for (y = bounds.fTop; y < bounds.fBottom; ++y) { - if (pixels[x]) { - bounds.fLeft = x; - goto foundLeft; - } - pixels += rowPixels; + bounds.fLeft = 0; + x = -1; + while (++x < width) { + pixels = pixelBase + rowPixels * bounds.fTop; + for (y = bounds.fTop; y < bounds.fBottom; ++y) { + if (pixels[x]) { + bounds.fLeft = x; + goto foundLeft; } + pixels += rowPixels; } + } foundLeft: - bounds.fRight = width; - x = width; - while (--x > bounds.fLeft) { - pixels = pixelBase + rowPixels * bounds.fTop; - for (y = bounds.fTop; y < bounds.fBottom; ++y) { - if (pixels[x]) { - bounds.fRight = x + 1; - goto foundRight; - } - pixels += rowPixels; + bounds.fRight = width; + x = width; + while (--x > bounds.fLeft) { + pixels = pixelBase + rowPixels * bounds.fTop; + for (y = bounds.fTop; y < bounds.fBottom; ++y) { + if (pixels[x]) { + bounds.fRight = x + 1; + goto foundRight; } + pixels += rowPixels; } + } foundRight: + return bounds; +} + +// This must be called to balance calls to cgContext +void SkiaBitLocker::releaseIfNeeded() { + if (!cgContext_) + return; + if (useDeviceBits_) { + bitmap_.unlockPixels(); + } else if (!bitmapIsDummy_) { + // Find the bits that were drawn to. + SkIRect bounds = computeDirtyRect(); SkBitmap subset; if (!bitmap_.extractSubset(&subset, bounds)) { return; @@ -362,12 +388,19 @@ foundRight: } CGContextRelease(cgContext_); cgContext_ = 0; + useDeviceBits_ = false; + bitmapIsDummy_ = false; } CGContextRef SkiaBitLocker::cgContext() { SkIRect clip_bounds; - if (!canvas_->getClipDeviceBounds(&clip_bounds)) - return 0; // the clip is empty, nothing to draw + if (!canvas_->getClipDeviceBounds(&clip_bounds)) { + // If the clip is empty, then there is nothing to draw. The caller may + // attempt to draw (to-be-clipped) results, so ensure there is a dummy + // non-NULL CGContext to use. + bitmapIsDummy_ = true; + clip_bounds = SkIRect::MakeXYWH(0, 0, 1, 1); + } SkBaseDevice* device = canvas_->getTopDevice(); DCHECK(device); @@ -387,13 +420,20 @@ CGContextRef SkiaBitLocker::cgContext() { // Only draw directly if we have pixels, and we're only rect-clipped. // If not, we allocate an offscreen and draw into that, relying on the // compositing step to apply skia's clip. - useDeviceBits_ = deviceBits.getPixels() && canvas_->isClipRect(); + useDeviceBits_ = deviceBits.getPixels() && + canvas_->isClipRect() && + !bitmapIsDummy_; if (useDeviceBits_) { - if (!deviceBits.extractSubset(&bitmap_, clip_bounds)) + bool result = deviceBits.extractSubset(&bitmap_, clip_bounds); + DCHECK(result); + if (!result) return 0; bitmap_.lockPixels(); } else { - if (!bitmap_.allocN32Pixels(clip_bounds.width(), clip_bounds.height())) + bool result = bitmap_.allocN32Pixels( + clip_bounds.width(), clip_bounds.height()); + DCHECK(result); + if (!result) return 0; bitmap_.eraseColor(0); } @@ -402,6 +442,7 @@ CGContextRef SkiaBitLocker::cgContext() { cgContext_ = CGBitmapContextCreate(bitmap_.getPixels(), bitmap_.width(), bitmap_.height(), 8, bitmap_.rowBytes(), colorSpace, kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst); + DCHECK(cgContext_); SkMatrix matrix = canvas_->getTotalMatrix(); matrix.postTranslate(-SkIntToScalar(bitmapOffset_.x()), @@ -414,4 +455,8 @@ CGContextRef SkiaBitLocker::cgContext() { return cgContext_; } +bool SkiaBitLocker::hasEmptyClipRegion() const { + return canvas_->isClipEmpty(); +} + } // namespace gfx |