| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "skia/ext/skia_utils_mac.h" | 5 #include "skia/ext/skia_utils_mac.h" |
| 6 | 6 |
| 7 #import <AppKit/AppKit.h> | 7 #import <AppKit/AppKit.h> |
| 8 #include <stdint.h> | 8 #include <stdint.h> |
| 9 | 9 |
| 10 #include <memory> | 10 #include <memory> |
| (...skipping 223 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 234 [image setSize:NSMakeSize(skiaBitmap.width(), skiaBitmap.height())]; | 234 [image setSize:NSMakeSize(skiaBitmap.width(), skiaBitmap.height())]; |
| 235 return [image.release() autorelease]; | 235 return [image.release() autorelease]; |
| 236 } | 236 } |
| 237 | 237 |
| 238 NSImage* SkBitmapToNSImage(const SkBitmap& skiaBitmap) { | 238 NSImage* SkBitmapToNSImage(const SkBitmap& skiaBitmap) { |
| 239 base::ScopedCFTypeRef<CGColorSpaceRef> colorSpace( | 239 base::ScopedCFTypeRef<CGColorSpaceRef> colorSpace( |
| 240 CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB)); | 240 CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB)); |
| 241 return SkBitmapToNSImageWithColorSpace(skiaBitmap, colorSpace.get()); | 241 return SkBitmapToNSImageWithColorSpace(skiaBitmap, colorSpace.get()); |
| 242 } | 242 } |
| 243 | 243 |
| 244 SkiaBitLocker::SkiaBitLocker(SkCanvas* canvas, | |
| 245 const SkIRect& userClipRect, | |
| 246 SkScalar bitmapScaleFactor) | |
| 247 : canvas_(canvas), | |
| 248 cgContext_(0), | |
| 249 bitmapScaleFactor_(bitmapScaleFactor), | |
| 250 useDeviceBits_(false), | |
| 251 bitmapIsDummy_(false) { | |
| 252 canvas_->save(); | |
| 253 canvas_->clipRect(SkRect::MakeFromIRect(userClipRect)); | |
| 254 } | |
| 255 | |
| 256 SkiaBitLocker::~SkiaBitLocker() { | |
| 257 releaseIfNeeded(); | |
| 258 canvas_->restore(); | |
| 259 } | |
| 260 | |
| 261 SkIRect SkiaBitLocker::computeDirtyRect() { | |
| 262 // If the user specified a clip region, assume that it was tight and that the | |
| 263 // dirty rect is approximately the whole bitmap. | |
| 264 return SkIRect::MakeWH(offscreen_.width(), offscreen_.height()); | |
| 265 } | |
| 266 | |
| 267 // This must be called to balance calls to cgContext | |
| 268 void SkiaBitLocker::releaseIfNeeded() { | |
| 269 if (!cgContext_) | |
| 270 return; | |
| 271 if (!useDeviceBits_ && !bitmapIsDummy_) { | |
| 272 // Find the bits that were drawn to. | |
| 273 SkIRect bounds = computeDirtyRect(); | |
| 274 SkBitmap subset; | |
| 275 if (!offscreen_.extractSubset(&subset, bounds)) { | |
| 276 return; | |
| 277 } | |
| 278 subset.setImmutable(); // Prevents a defensive copy inside Skia. | |
| 279 canvas_->save(); | |
| 280 canvas_->setMatrix(SkMatrix::I()); // Reset back to device space. | |
| 281 canvas_->translate(bounds.x() + bitmapOffset_.x(), | |
| 282 bounds.y() + bitmapOffset_.y()); | |
| 283 canvas_->scale(1.f / bitmapScaleFactor_, 1.f / bitmapScaleFactor_); | |
| 284 canvas_->drawBitmap(subset, 0, 0); | |
| 285 canvas_->restore(); | |
| 286 } | |
| 287 CGContextRelease(cgContext_); | |
| 288 cgContext_ = 0; | |
| 289 useDeviceBits_ = false; | |
| 290 bitmapIsDummy_ = false; | |
| 291 } | |
| 292 | |
| 293 CGContextRef SkiaBitLocker::cgContext() { | |
| 294 releaseIfNeeded(); // This flushes any prior bitmap use | |
| 295 | |
| 296 SkIRect clip_bounds; | |
| 297 if (!canvas_->getDeviceClipBounds(&clip_bounds)) { | |
| 298 // If the clip is empty, then there is nothing to draw. The caller may | |
| 299 // attempt to draw (to-be-clipped) results, so ensure there is a dummy | |
| 300 // non-NULL CGContext to use. | |
| 301 bitmapIsDummy_ = true; | |
| 302 clip_bounds = SkIRect::MakeXYWH(0, 0, 1, 1); | |
| 303 } | |
| 304 | |
| 305 // remember the top/left, in case we need to compose this later | |
| 306 bitmapOffset_.set(clip_bounds.x(), clip_bounds.y()); | |
| 307 | |
| 308 // Now make clip_bounds be relative to the current layer/device | |
| 309 if (!bitmapIsDummy_) { | |
| 310 canvas_->temporary_internal_describeTopLayer(nullptr, &clip_bounds); | |
| 311 } | |
| 312 | |
| 313 SkPixmap devicePixels; | |
| 314 skia::GetWritablePixels(canvas_, &devicePixels); | |
| 315 | |
| 316 // Only draw directly if we have pixels, and we're only rect-clipped. | |
| 317 // If not, we allocate an offscreen and draw into that, relying on the | |
| 318 // compositing step to apply skia's clip. | |
| 319 useDeviceBits_ = devicePixels.addr() && | |
| 320 canvas_->isClipRect() && | |
| 321 !bitmapIsDummy_; | |
| 322 base::ScopedCFTypeRef<CGColorSpaceRef> colorSpace( | |
| 323 CGColorSpaceCreateDeviceRGB()); | |
| 324 | |
| 325 int displayHeight; | |
| 326 if (useDeviceBits_) { | |
| 327 SkPixmap subset; | |
| 328 bool result = devicePixels.extractSubset(&subset, clip_bounds); | |
| 329 DCHECK(result); | |
| 330 if (!result) | |
| 331 return 0; | |
| 332 displayHeight = subset.height(); | |
| 333 cgContext_ = CGBitmapContextCreate(subset.writable_addr(), subset.width(), | |
| 334 subset.height(), 8, subset.rowBytes(), colorSpace, | |
| 335 kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst); | |
| 336 } else { | |
| 337 bool result = offscreen_.tryAllocN32Pixels( | |
| 338 SkScalarCeilToInt(bitmapScaleFactor_ * clip_bounds.width()), | |
| 339 SkScalarCeilToInt(bitmapScaleFactor_ * clip_bounds.height())); | |
| 340 DCHECK(result); | |
| 341 if (!result) | |
| 342 return 0; | |
| 343 offscreen_.eraseColor(0); | |
| 344 displayHeight = offscreen_.height(); | |
| 345 cgContext_ = CGBitmapContextCreate(offscreen_.getPixels(), | |
| 346 offscreen_.width(), offscreen_.height(), 8, offscreen_.rowBytes(), | |
| 347 colorSpace, kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst); | |
| 348 } | |
| 349 DCHECK(cgContext_); | |
| 350 | |
| 351 SkMatrix matrix = canvas_->getTotalMatrix(); | |
| 352 matrix.postTranslate(-SkIntToScalar(bitmapOffset_.x()), | |
| 353 -SkIntToScalar(bitmapOffset_.y())); | |
| 354 matrix.postScale(bitmapScaleFactor_, -bitmapScaleFactor_); | |
| 355 matrix.postTranslate(0, SkIntToScalar(displayHeight)); | |
| 356 | |
| 357 CGContextConcatCTM(cgContext_, SkMatrixToCGAffineTransform(matrix)); | |
| 358 | |
| 359 return cgContext_; | |
| 360 } | |
| 361 | |
| 362 bool SkiaBitLocker::hasEmptyClipRegion() const { | |
| 363 return canvas_->isClipEmpty(); | |
| 364 } | |
| 365 | |
| 366 } // namespace skia | 244 } // namespace skia |
| OLD | NEW |