DCHECK((image != 0) ^ (image_rep != 0));
SkBitmap bitmap;
- bitmap.setConfig(SkBitmap::kARGB_8888_Config,
- size.width,
- size.height,
- 0,
- is_opaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
-
- if (!bitmap.allocPixels())
+ if (!bitmap.tryAllocN32Pixels(size.width, size.height, is_opaque))
return bitmap; // Return |bitmap| which should respond true to isNull().
SkiaBitLocker::SkiaBitLocker(SkCanvas* canvas)
: canvas_(canvas),
- cgContext_(0) {
+ userClipRectSpecified_(false),
+ cgContext_(0),
+ bitmapScaleFactor_(1),
+ useDeviceBits_(false),
+ bitmapIsDummy_(false) {
+}
+
+SkiaBitLocker::SkiaBitLocker(SkCanvas* canvas,
+ const SkIRect& userClipRect,
+ SkScalar bitmapScaleFactor)
+ : canvas_(canvas),
+ userClipRectSpecified_(true),
+ cgContext_(0),
+ bitmapScaleFactor_(bitmapScaleFactor),
+ 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;
return;
canvas_->save();
canvas_->concat(inverse);
- canvas_->drawBitmap(subset, bounds.fLeft, bounds.fTop);
+ canvas_->translate(bounds.x() + bitmapOffset_.x(),
+ bounds.y() + bitmapOffset_.y());
+ canvas_->scale(1.f / bitmapScaleFactor_, 1.f / bitmapScaleFactor_);
+ canvas_->drawBitmap(subset, 0, 0);
canvas_->restore();
}
CGContextRelease(cgContext_);
cgContext_ = 0;
+ useDeviceBits_ = false;
+ bitmapIsDummy_ = false;
}
CGContextRef SkiaBitLocker::cgContext() {
+ SkIRect clip_bounds;
+ 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);
if (!device)
return 0;
+
releaseIfNeeded(); // This flushes any prior bitmap use
+
+ // remember the top/left, in case we need to compose this later
+ bitmapOffset_.set(clip_bounds.x(), clip_bounds.y());
+
+ // Now make clip_bounds be relative to the current layer/device
+ clip_bounds.offset(-device->getOrigin());
+
const SkBitmap& deviceBits = device->accessBitmap(true);
- useDeviceBits_ = deviceBits.getPixels();
+
+ // 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() &&
+ !bitmapIsDummy_;
if (useDeviceBits_) {
- bitmap_ = deviceBits;
+ bool result = deviceBits.extractSubset(&bitmap_, clip_bounds);
+ DCHECK(result);
+ if (!result)
+ return 0;
bitmap_.lockPixels();
} else {
- bitmap_.setConfig(
- SkBitmap::kARGB_8888_Config, deviceBits.width(), deviceBits.height());
- bitmap_.allocPixels();
+ bool result = bitmap_.tryAllocN32Pixels(
+ SkScalarCeilToInt(bitmapScaleFactor_ * clip_bounds.width()),
+ SkScalarCeilToInt(bitmapScaleFactor_ * clip_bounds.height()));
+ DCHECK(result);
+ if (!result)
+ return 0;
bitmap_.eraseColor(0);
}
base::ScopedCFTypeRef<CGColorSpaceRef> colorSpace(
cgContext_ = CGBitmapContextCreate(bitmap_.getPixels(), bitmap_.width(),
bitmap_.height(), 8, bitmap_.rowBytes(), colorSpace,
kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst);
+ DCHECK(cgContext_);
- // Apply device matrix.
- CGAffineTransform contentsTransform = CGAffineTransformMakeScale(1, -1);
- contentsTransform = CGAffineTransformTranslate(contentsTransform, 0,
- -device->height());
- CGContextConcatCTM(cgContext_, contentsTransform);
-
- const SkIPoint& pt = device->getOrigin();
- // Skip applying the clip when not writing directly to device.
- // They're applied in the offscreen case when the bitmap is drawn.
- if (useDeviceBits_) {
- // Apply clip in device coordinates.
- CGMutablePathRef clipPath = CGPathCreateMutable();
- const SkRegion& clipRgn = canvas_->getTotalClip();
- if (clipRgn.isEmpty()) {
- // CoreGraphics does not consider a newly created path to be empty.
- // Explicitly set it to empty so the subsequent drawing is clipped out.
- // It would be better to make the CGContext hidden if there was a CG
- // call that does that.
- CGPathAddRect(clipPath, 0, CGRectMake(0, 0, 0, 0));
- }
- SkRegion::Iterator iter(clipRgn);
- const SkIPoint& pt = device->getOrigin();
- for (; !iter.done(); iter.next()) {
- SkIRect skRect = iter.rect();
- skRect.offset(-pt);
- CGRect cgRect = SkIRectToCGRect(skRect);
- CGPathAddRect(clipPath, 0, cgRect);
- }
- CGContextAddPath(cgContext_, clipPath);
- CGContextClip(cgContext_);
- CGPathRelease(clipPath);
- }
+ SkMatrix matrix = canvas_->getTotalMatrix();
+ matrix.postTranslate(-SkIntToScalar(bitmapOffset_.x()),
+ -SkIntToScalar(bitmapOffset_.y()));
+ matrix.postScale(bitmapScaleFactor_, -bitmapScaleFactor_);
+ matrix.postTranslate(0, SkIntToScalar(bitmap_.height()));
- // Apply content matrix.
- SkMatrix skMatrix = canvas_->getTotalMatrix();
- skMatrix.postTranslate(-SkIntToScalar(pt.fX), -SkIntToScalar(pt.fY));
- CGAffineTransform affine = SkMatrixToCGAffineTransform(skMatrix);
- CGContextConcatCTM(cgContext_, affine);
+ CGContextConcatCTM(cgContext_, SkMatrixToCGAffineTransform(matrix));
return cgContext_;
}
+bool SkiaBitLocker::hasEmptyClipRegion() const {
+ return canvas_->isClipEmpty();
+}
+
} // namespace gfx