| 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> |
| 11 | 11 |
| 12 #include "base/logging.h" | 12 #include "base/logging.h" |
| 13 #include "base/mac/scoped_cftyperef.h" | 13 #include "base/mac/scoped_cftyperef.h" |
| 14 #include "base/mac/scoped_nsobject.h" | 14 #include "base/mac/scoped_nsobject.h" |
| 15 #include "skia/ext/bitmap_platform_device_mac.h" | |
| 16 #include "skia/ext/platform_canvas.h" | 15 #include "skia/ext/platform_canvas.h" |
| 17 #include "third_party/skia/include/utils/mac/SkCGUtils.h" | 16 #include "third_party/skia/include/utils/mac/SkCGUtils.h" |
| 18 | 17 |
| 19 namespace { | 18 namespace { |
| 20 | 19 |
| 21 // Draws an NSImage or an NSImageRep with a given size into a SkBitmap. | 20 // Draws an NSImage or an NSImageRep with a given size into a SkBitmap. |
| 22 SkBitmap NSImageOrNSImageRepToSkBitmapWithColorSpace( | 21 SkBitmap NSImageOrNSImageRepToSkBitmapWithColorSpace( |
| 23 NSImage* image, | 22 NSImage* image, |
| 24 NSImageRep* image_rep, | 23 NSImageRep* image_rep, |
| 25 NSSize size, | 24 NSSize size, |
| (...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 184 count:4]; | 183 count:4]; |
| 185 } | 184 } |
| 186 | 185 |
| 187 SkBitmap CGImageToSkBitmap(CGImageRef image) { | 186 SkBitmap CGImageToSkBitmap(CGImageRef image) { |
| 188 if (!image) | 187 if (!image) |
| 189 return SkBitmap(); | 188 return SkBitmap(); |
| 190 | 189 |
| 191 int width = CGImageGetWidth(image); | 190 int width = CGImageGetWidth(image); |
| 192 int height = CGImageGetHeight(image); | 191 int height = CGImageGetHeight(image); |
| 193 | 192 |
| 194 sk_sp<skia::BitmapPlatformDevice> device( | 193 sk_sp<SkCanvas> canvas(skia::CreatePlatformCanvas( |
| 195 skia::BitmapPlatformDevice::Create(NULL, width, height, false)); | 194 nullptr, width, height, false, RETURN_NULL_ON_FAILURE)); |
| 196 | |
| 197 sk_sp<SkCanvas> canvas(skia::CreateCanvas(device, RETURN_NULL_ON_FAILURE)); | |
| 198 ScopedPlatformPaint p(canvas.get()); | 195 ScopedPlatformPaint p(canvas.get()); |
| 199 CGContextRef context = p.GetPlatformSurface(); | 196 CGContextRef context = p.GetPlatformSurface(); |
| 200 | 197 |
| 201 // We need to invert the y-axis of the canvas so that Core Graphics drawing | 198 // We need to invert the y-axis of the canvas so that Core Graphics drawing |
| 202 // happens right-side up. Skia has an upper-left origin and CG has a lower- | 199 // happens right-side up. Skia has an upper-left origin and CG has a lower- |
| 203 // left one. | 200 // left one. |
| 204 CGContextScaleCTM(context, 1.0, -1.0); | 201 CGContextScaleCTM(context, 1.0, -1.0); |
| 205 CGContextTranslateCTM(context, 0, -height); | 202 CGContextTranslateCTM(context, 0, -height); |
| 206 | 203 |
| 207 // We want to copy transparent pixels from |image|, instead of blending it | 204 // We want to copy transparent pixels from |image|, instead of blending it |
| 208 // onto uninitialized pixels. | 205 // onto uninitialized pixels. |
| 209 CGContextSetBlendMode(context, kCGBlendModeCopy); | 206 CGContextSetBlendMode(context, kCGBlendModeCopy); |
| 210 | 207 |
| 211 CGRect rect = CGRectMake(0, 0, width, height); | 208 CGRect rect = CGRectMake(0, 0, width, height); |
| 212 CGContextDrawImage(context, rect, image); | 209 CGContextDrawImage(context, rect, image); |
| 213 | 210 |
| 214 // Because |device| will be cleaned up and will take its pixels with it, we | 211 return skia::ReadPixels(canvas.get()); |
| 215 // copy it to the stack and return it. | |
| 216 SkBitmap bitmap = device->accessBitmap(false); | |
| 217 | |
| 218 return bitmap; | |
| 219 } | 212 } |
| 220 | 213 |
| 221 SkBitmap NSImageToSkBitmapWithColorSpace( | 214 SkBitmap NSImageToSkBitmapWithColorSpace( |
| 222 NSImage* image, bool is_opaque, CGColorSpaceRef color_space) { | 215 NSImage* image, bool is_opaque, CGColorSpaceRef color_space) { |
| 223 return NSImageOrNSImageRepToSkBitmapWithColorSpace( | 216 return NSImageOrNSImageRepToSkBitmapWithColorSpace( |
| 224 image, nil, [image size], is_opaque, color_space); | 217 image, nil, [image size], is_opaque, color_space); |
| 225 } | 218 } |
| 226 | 219 |
| 227 SkBitmap NSImageRepToSkBitmapWithColorSpace(NSImageRep* image_rep, | 220 SkBitmap NSImageRepToSkBitmapWithColorSpace(NSImageRep* image_rep, |
| 228 NSSize size, | 221 NSSize size, |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 262 [image setSize:NSMakeSize(skiaBitmap.width(), skiaBitmap.height())]; | 255 [image setSize:NSMakeSize(skiaBitmap.width(), skiaBitmap.height())]; |
| 263 return [image.release() autorelease]; | 256 return [image.release() autorelease]; |
| 264 } | 257 } |
| 265 | 258 |
| 266 NSImage* SkBitmapToNSImage(const SkBitmap& skiaBitmap) { | 259 NSImage* SkBitmapToNSImage(const SkBitmap& skiaBitmap) { |
| 267 base::ScopedCFTypeRef<CGColorSpaceRef> colorSpace( | 260 base::ScopedCFTypeRef<CGColorSpaceRef> colorSpace( |
| 268 CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB)); | 261 CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB)); |
| 269 return SkBitmapToNSImageWithColorSpace(skiaBitmap, colorSpace.get()); | 262 return SkBitmapToNSImageWithColorSpace(skiaBitmap, colorSpace.get()); |
| 270 } | 263 } |
| 271 | 264 |
| 272 SkiaBitLocker::SkiaBitLocker(SkCanvas* canvas) | |
| 273 : canvas_(canvas), | |
| 274 userClipRectSpecified_(false), | |
| 275 cgContext_(0), | |
| 276 bitmapScaleFactor_(1), | |
| 277 useDeviceBits_(false), | |
| 278 bitmapIsDummy_(false) { | |
| 279 } | |
| 280 | |
| 281 SkiaBitLocker::SkiaBitLocker(SkCanvas* canvas, | 265 SkiaBitLocker::SkiaBitLocker(SkCanvas* canvas, |
| 282 const SkIRect& userClipRect, | 266 const SkIRect& userClipRect, |
| 283 SkScalar bitmapScaleFactor) | 267 SkScalar bitmapScaleFactor) |
| 284 : canvas_(canvas), | 268 : canvas_(canvas), |
| 285 userClipRectSpecified_(true), | |
| 286 cgContext_(0), | 269 cgContext_(0), |
| 287 bitmapScaleFactor_(bitmapScaleFactor), | 270 bitmapScaleFactor_(bitmapScaleFactor), |
| 288 useDeviceBits_(false), | 271 useDeviceBits_(false), |
| 289 bitmapIsDummy_(false) { | 272 bitmapIsDummy_(false) { |
| 290 canvas_->save(); | 273 canvas_->save(); |
| 291 canvas_->clipRect(SkRect::MakeFromIRect(userClipRect)); | 274 canvas_->clipRect(SkRect::MakeFromIRect(userClipRect)); |
| 292 } | 275 } |
| 293 | 276 |
| 294 SkiaBitLocker::~SkiaBitLocker() { | 277 SkiaBitLocker::~SkiaBitLocker() { |
| 295 releaseIfNeeded(); | 278 releaseIfNeeded(); |
| 296 if (userClipRectSpecified_) | 279 canvas_->restore(); |
| 297 canvas_->restore(); | |
| 298 } | 280 } |
| 299 | 281 |
| 300 SkIRect SkiaBitLocker::computeDirtyRect() { | 282 SkIRect SkiaBitLocker::computeDirtyRect() { |
| 301 // If the user specified a clip region, assume that it was tight and that the | 283 // If the user specified a clip region, assume that it was tight and that the |
| 302 // dirty rect is approximately the whole bitmap. | 284 // dirty rect is approximately the whole bitmap. |
| 303 if (userClipRectSpecified_) | 285 return SkIRect::MakeWH(offscreen_.width(), offscreen_.height()); |
| 304 return SkIRect::MakeWH(offscreen_.width(), offscreen_.height()); | |
| 305 | |
| 306 // Find the bits that were drawn to. | |
| 307 SkAutoLockPixels lockedPixels(offscreen_); | |
| 308 const uint32_t* pixelBase | |
| 309 = reinterpret_cast<uint32_t*>(offscreen_.getPixels()); | |
| 310 int rowPixels = offscreen_.rowBytesAsPixels(); | |
| 311 int width = offscreen_.width(); | |
| 312 int height = offscreen_.height(); | |
| 313 SkIRect bounds; | |
| 314 bounds.fTop = 0; | |
| 315 int x; | |
| 316 int y = -1; | |
| 317 const uint32_t* pixels = pixelBase; | |
| 318 while (++y < height) { | |
| 319 for (x = 0; x < width; ++x) { | |
| 320 if (pixels[x]) { | |
| 321 bounds.fTop = y; | |
| 322 goto foundTop; | |
| 323 } | |
| 324 } | |
| 325 pixels += rowPixels; | |
| 326 } | |
| 327 foundTop: | |
| 328 bounds.fBottom = height; | |
| 329 y = height; | |
| 330 pixels = pixelBase + rowPixels * (y - 1); | |
| 331 while (--y > bounds.fTop) { | |
| 332 for (x = 0; x < width; ++x) { | |
| 333 if (pixels[x]) { | |
| 334 bounds.fBottom = y + 1; | |
| 335 goto foundBottom; | |
| 336 } | |
| 337 } | |
| 338 pixels -= rowPixels; | |
| 339 } | |
| 340 foundBottom: | |
| 341 bounds.fLeft = 0; | |
| 342 x = -1; | |
| 343 while (++x < width) { | |
| 344 pixels = pixelBase + rowPixels * bounds.fTop; | |
| 345 for (y = bounds.fTop; y < bounds.fBottom; ++y) { | |
| 346 if (pixels[x]) { | |
| 347 bounds.fLeft = x; | |
| 348 goto foundLeft; | |
| 349 } | |
| 350 pixels += rowPixels; | |
| 351 } | |
| 352 } | |
| 353 foundLeft: | |
| 354 bounds.fRight = width; | |
| 355 x = width; | |
| 356 while (--x > bounds.fLeft) { | |
| 357 pixels = pixelBase + rowPixels * bounds.fTop; | |
| 358 for (y = bounds.fTop; y < bounds.fBottom; ++y) { | |
| 359 if (pixels[x]) { | |
| 360 bounds.fRight = x + 1; | |
| 361 goto foundRight; | |
| 362 } | |
| 363 pixels += rowPixels; | |
| 364 } | |
| 365 } | |
| 366 foundRight: | |
| 367 return bounds; | |
| 368 } | 286 } |
| 369 | 287 |
| 370 // This must be called to balance calls to cgContext | 288 // This must be called to balance calls to cgContext |
| 371 void SkiaBitLocker::releaseIfNeeded() { | 289 void SkiaBitLocker::releaseIfNeeded() { |
| 372 if (!cgContext_) | 290 if (!cgContext_) |
| 373 return; | 291 return; |
| 374 if (!useDeviceBits_ && !bitmapIsDummy_) { | 292 if (!useDeviceBits_ && !bitmapIsDummy_) { |
| 375 // Find the bits that were drawn to. | 293 // Find the bits that were drawn to. |
| 376 SkIRect bounds = computeDirtyRect(); | 294 SkIRect bounds = computeDirtyRect(); |
| 377 SkBitmap subset; | 295 SkBitmap subset; |
| (...skipping 18 matching lines...) Expand all Loading... |
| 396 CGContextRef SkiaBitLocker::cgContext() { | 314 CGContextRef SkiaBitLocker::cgContext() { |
| 397 SkIRect clip_bounds; | 315 SkIRect clip_bounds; |
| 398 if (!canvas_->getClipDeviceBounds(&clip_bounds)) { | 316 if (!canvas_->getClipDeviceBounds(&clip_bounds)) { |
| 399 // If the clip is empty, then there is nothing to draw. The caller may | 317 // If the clip is empty, then there is nothing to draw. The caller may |
| 400 // attempt to draw (to-be-clipped) results, so ensure there is a dummy | 318 // attempt to draw (to-be-clipped) results, so ensure there is a dummy |
| 401 // non-NULL CGContext to use. | 319 // non-NULL CGContext to use. |
| 402 bitmapIsDummy_ = true; | 320 bitmapIsDummy_ = true; |
| 403 clip_bounds = SkIRect::MakeXYWH(0, 0, 1, 1); | 321 clip_bounds = SkIRect::MakeXYWH(0, 0, 1, 1); |
| 404 } | 322 } |
| 405 | 323 |
| 406 SkBaseDevice* device = canvas_->getTopDevice(); | |
| 407 DCHECK(device); | |
| 408 if (!device) | |
| 409 return 0; | |
| 410 | |
| 411 releaseIfNeeded(); // This flushes any prior bitmap use | 324 releaseIfNeeded(); // This flushes any prior bitmap use |
| 412 | 325 |
| 413 // remember the top/left, in case we need to compose this later | 326 // remember the top/left, in case we need to compose this later |
| 414 bitmapOffset_.set(clip_bounds.x(), clip_bounds.y()); | 327 bitmapOffset_.set(clip_bounds.x(), clip_bounds.y()); |
| 415 | 328 |
| 416 // Now make clip_bounds be relative to the current layer/device | 329 // Now make clip_bounds be relative to the current layer/device |
| 417 clip_bounds.offset(-device->getOrigin()); | 330 if (!bitmapIsDummy_) { |
| 331 canvas_->temporary_internal_describeTopLayer(nullptr, &clip_bounds); |
| 332 } |
| 418 | 333 |
| 419 SkPixmap devicePixels; | 334 SkPixmap devicePixels; |
| 420 skia::GetWritablePixels(canvas_, &devicePixels); | 335 skia::GetWritablePixels(canvas_, &devicePixels); |
| 421 | 336 |
| 422 // Only draw directly if we have pixels, and we're only rect-clipped. | 337 // Only draw directly if we have pixels, and we're only rect-clipped. |
| 423 // If not, we allocate an offscreen and draw into that, relying on the | 338 // If not, we allocate an offscreen and draw into that, relying on the |
| 424 // compositing step to apply skia's clip. | 339 // compositing step to apply skia's clip. |
| 425 useDeviceBits_ = devicePixels.addr() && | 340 useDeviceBits_ = devicePixels.addr() && |
| 426 canvas_->isClipRect() && | 341 canvas_->isClipRect() && |
| 427 !bitmapIsDummy_; | 342 !bitmapIsDummy_; |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 463 CGContextConcatCTM(cgContext_, SkMatrixToCGAffineTransform(matrix)); | 378 CGContextConcatCTM(cgContext_, SkMatrixToCGAffineTransform(matrix)); |
| 464 | 379 |
| 465 return cgContext_; | 380 return cgContext_; |
| 466 } | 381 } |
| 467 | 382 |
| 468 bool SkiaBitLocker::hasEmptyClipRegion() const { | 383 bool SkiaBitLocker::hasEmptyClipRegion() const { |
| 469 return canvas_->isClipEmpty(); | 384 return canvas_->isClipEmpty(); |
| 470 } | 385 } |
| 471 | 386 |
| 472 } // namespace skia | 387 } // namespace skia |
| OLD | NEW |