| OLD | NEW |
| (Empty) | |
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "platform/mac/GraphicsContextCanvas.h" |
| 6 |
| 7 #import <AppKit/AppKit.h> |
| 8 #import <CoreGraphics/CoreGraphics.h> |
| 9 |
| 10 #include "skia/ext/skia_utils_mac.h" |
| 11 #include "wtf/RetainPtr.h" |
| 12 |
| 13 namespace blink { |
| 14 |
| 15 GraphicsContextCanvas::GraphicsContextCanvas(PaintCanvas* canvas, |
| 16 const SkIRect& userClipRect, |
| 17 SkScalar bitmapScaleFactor) |
| 18 : m_canvas(canvas), |
| 19 m_cgContext(0), |
| 20 m_bitmapScaleFactor(bitmapScaleFactor), |
| 21 m_useDeviceBits(false), |
| 22 m_bitmapIsDummy(false) { |
| 23 m_canvas->save(); |
| 24 m_canvas->clipRect(SkRect::MakeFromIRect(userClipRect)); |
| 25 } |
| 26 |
| 27 GraphicsContextCanvas::~GraphicsContextCanvas() { |
| 28 releaseIfNeeded(); |
| 29 m_canvas->restore(); |
| 30 } |
| 31 |
| 32 SkIRect GraphicsContextCanvas::computeDirtyRect() { |
| 33 // If the user specified a clip region, assume that it was tight and that the |
| 34 // dirty rect is approximately the whole bitmap. |
| 35 return SkIRect::MakeWH(m_offscreen.width(), m_offscreen.height()); |
| 36 } |
| 37 |
| 38 // This must be called to balance calls to cgContext |
| 39 void GraphicsContextCanvas::releaseIfNeeded() { |
| 40 if (!m_cgContext) |
| 41 return; |
| 42 if (!m_useDeviceBits && !m_bitmapIsDummy) { |
| 43 // Find the bits that were drawn to. |
| 44 SkIRect bounds = computeDirtyRect(); |
| 45 SkBitmap subset; |
| 46 if (!m_offscreen.extractSubset(&subset, bounds)) { |
| 47 return; |
| 48 } |
| 49 subset.setImmutable(); // Prevents a defensive copy inside Skia. |
| 50 m_canvas->save(); |
| 51 m_canvas->setMatrix(SkMatrix::I()); // Reset back to device space. |
| 52 m_canvas->translate(bounds.x() + m_bitmapOffset.x(), |
| 53 bounds.y() + m_bitmapOffset.y()); |
| 54 m_canvas->scale(1.f / m_bitmapScaleFactor, 1.f / m_bitmapScaleFactor); |
| 55 m_canvas->drawBitmap(subset, 0, 0); |
| 56 m_canvas->restore(); |
| 57 } |
| 58 CGContextRelease(m_cgContext); |
| 59 m_cgContext = 0; |
| 60 m_useDeviceBits = false; |
| 61 m_bitmapIsDummy = false; |
| 62 } |
| 63 |
| 64 CGContextRef GraphicsContextCanvas::cgContext() { |
| 65 releaseIfNeeded(); // This flushes any prior bitmap use |
| 66 |
| 67 SkIRect clip_bounds; |
| 68 if (!m_canvas->getDeviceClipBounds(&clip_bounds)) { |
| 69 // If the clip is empty, then there is nothing to draw. The caller may |
| 70 // attempt to draw (to-be-clipped) results, so ensure there is a dummy |
| 71 // non-NULL CGContext to use. |
| 72 m_bitmapIsDummy = true; |
| 73 clip_bounds = SkIRect::MakeXYWH(0, 0, 1, 1); |
| 74 } |
| 75 |
| 76 // remember the top/left, in case we need to compose this later |
| 77 m_bitmapOffset.set(clip_bounds.x(), clip_bounds.y()); |
| 78 |
| 79 // Now make clip_bounds be relative to the current layer/device |
| 80 if (!m_bitmapIsDummy) { |
| 81 m_canvas->temporary_internal_describeTopLayer(nullptr, &clip_bounds); |
| 82 } |
| 83 |
| 84 SkPixmap devicePixels; |
| 85 ToPixmap(m_canvas, &devicePixels); |
| 86 |
| 87 // Only draw directly if we have pixels, and we're only rect-clipped. |
| 88 // If not, we allocate an offscreen and draw into that, relying on the |
| 89 // compositing step to apply skia's clip. |
| 90 m_useDeviceBits = |
| 91 devicePixels.addr() && m_canvas->isClipRect() && !m_bitmapIsDummy; |
| 92 WTF::RetainPtr<CGColorSpace> colorSpace(CGColorSpaceCreateDeviceRGB()); |
| 93 |
| 94 int displayHeight; |
| 95 if (m_useDeviceBits) { |
| 96 SkPixmap subset; |
| 97 bool result = devicePixels.extractSubset(&subset, clip_bounds); |
| 98 DCHECK(result); |
| 99 if (!result) |
| 100 return 0; |
| 101 displayHeight = subset.height(); |
| 102 m_cgContext = CGBitmapContextCreate( |
| 103 subset.writable_addr(), subset.width(), subset.height(), 8, |
| 104 subset.rowBytes(), colorSpace.get(), |
| 105 kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst); |
| 106 } else { |
| 107 bool result = m_offscreen.tryAllocN32Pixels( |
| 108 SkScalarCeilToInt(m_bitmapScaleFactor * clip_bounds.width()), |
| 109 SkScalarCeilToInt(m_bitmapScaleFactor * clip_bounds.height())); |
| 110 DCHECK(result); |
| 111 if (!result) |
| 112 return 0; |
| 113 m_offscreen.eraseColor(0); |
| 114 displayHeight = m_offscreen.height(); |
| 115 m_cgContext = CGBitmapContextCreate( |
| 116 m_offscreen.getPixels(), m_offscreen.width(), m_offscreen.height(), 8, |
| 117 m_offscreen.rowBytes(), colorSpace.get(), |
| 118 kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst); |
| 119 } |
| 120 DCHECK(m_cgContext); |
| 121 |
| 122 SkMatrix matrix = m_canvas->getTotalMatrix(); |
| 123 matrix.postTranslate(-SkIntToScalar(m_bitmapOffset.x()), |
| 124 -SkIntToScalar(m_bitmapOffset.y())); |
| 125 matrix.postScale(m_bitmapScaleFactor, -m_bitmapScaleFactor); |
| 126 matrix.postTranslate(0, SkIntToScalar(displayHeight)); |
| 127 |
| 128 CGContextConcatCTM(m_cgContext, skia::SkMatrixToCGAffineTransform(matrix)); |
| 129 |
| 130 return m_cgContext; |
| 131 } |
| 132 |
| 133 bool GraphicsContextCanvas::hasEmptyClipRegion() const { |
| 134 return m_canvas->isClipEmpty(); |
| 135 } |
| 136 |
| 137 } // namespace blink |
| OLD | NEW |