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
3 // found in the LICENSE file.
5 #include "skia/ext/bitmap_platform_device_mac.h"
7 #import <ApplicationServices/ApplicationServices.h>
10 #include "base/mac/mac_util.h"
11 #include "base/memory/ref_counted.h"
12 #include "skia/ext/bitmap_platform_device_data.h"
13 #include "skia/ext/platform_canvas.h"
14 #include "skia/ext/skia_utils_mac.h"
15 #include "third_party/skia/include/core/SkMatrix.h"
16 #include "third_party/skia/include/core/SkRegion.h"
17 #include "third_party/skia/include/core/SkTypes.h"
18 #include "third_party/skia/include/core/SkUtils.h"
24 static CGContextRef
CGContextForData(void* data
, int width
, int height
) {
25 #define HAS_ARGB_SHIFTS(a, r, g, b) \
26 (SK_A32_SHIFT == (a) && SK_R32_SHIFT == (r) \
27 && SK_G32_SHIFT == (g) && SK_B32_SHIFT == (b))
28 #if defined(SK_CPU_LENDIAN) && HAS_ARGB_SHIFTS(24, 16, 8, 0)
29 // Allocate a bitmap context with 4 components per pixel (BGRA). Apple
30 // recommends these flags for improved CG performance.
32 // CGBitmapContextCreate returns NULL if width/height are 0. However, our
33 // callers expect to get a canvas back (which they later resize/reallocate)
34 // so we pin the dimensions here.
35 width
= SkMax32(1, width
);
36 height
= SkMax32(1, height
);
37 CGContextRef context
=
38 CGBitmapContextCreate(data
, width
, height
, 8, width
* 4,
39 base::mac::GetSystemColorSpace(),
40 kCGImageAlphaPremultipliedFirst
|
41 kCGBitmapByteOrder32Host
);
43 #error We require that Skia's and CoreGraphics's recommended \
44 image memory layout match.
46 #undef HAS_ARGB_SHIFTS
51 // Change the coordinate system to match WebCore's
52 CGContextTranslateCTM(context
, 0, height
);
53 CGContextScaleCTM(context
, 1.0, -1.0);
60 BitmapPlatformDevice::BitmapPlatformDeviceData::BitmapPlatformDeviceData(
62 : bitmap_context_(bitmap
),
63 config_dirty_(true), // Want to load the config next time.
64 transform_(SkMatrix::I()) {
65 SkASSERT(bitmap_context_
);
66 // Initialize the clip region to the entire bitmap.
70 CGBitmapContextGetWidth(bitmap_context_
),
71 CGBitmapContextGetHeight(bitmap_context_
));
72 clip_region_
= SkRegion(rect
);
73 CGContextRetain(bitmap_context_
);
74 // We must save the state once so that we can use the restore/save trick
76 CGContextSaveGState(bitmap_context_
);
79 BitmapPlatformDevice::BitmapPlatformDeviceData::~BitmapPlatformDeviceData() {
81 CGContextRelease(bitmap_context_
);
84 void BitmapPlatformDevice::BitmapPlatformDeviceData::ReleaseBitmapContext() {
85 SkASSERT(bitmap_context_
);
86 CGContextRelease(bitmap_context_
);
87 bitmap_context_
= NULL
;
90 void BitmapPlatformDevice::BitmapPlatformDeviceData::SetMatrixClip(
91 const SkMatrix
& transform
,
92 const SkRegion
& region
) {
93 transform_
= transform
;
94 clip_region_
= region
;
98 void BitmapPlatformDevice::BitmapPlatformDeviceData::LoadConfig() {
99 if (!config_dirty_
|| !bitmap_context_
)
100 return; // Nothing to do.
101 config_dirty_
= false;
103 // We must restore and then save the state of the graphics context since the
104 // calls to Load the clipping region to the context are strictly cummulative,
105 // i.e., you can't replace a clip rect, other than with a save/restore.
106 // But this implies that no other changes to the state are done elsewhere.
107 // If we ever get to need to change this, then we must replace the clip rect
108 // calls in LoadClippingRegionToCGContext() with an image mask instead.
109 CGContextRestoreGState(bitmap_context_
);
110 CGContextSaveGState(bitmap_context_
);
111 LoadTransformToCGContext(bitmap_context_
, transform_
);
112 LoadClippingRegionToCGContext(bitmap_context_
, clip_region_
, transform_
);
116 // We use this static factory function instead of the regular constructor so
117 // that we can create the pixel data before calling the constructor. This is
118 // required so that we can call the base class' constructor with the pixel
120 BitmapPlatformDevice
* BitmapPlatformDevice::Create(CGContextRef context
,
124 if (RasterDeviceTooBigToAllocate(width
, height
))
128 bitmap
.setConfig(SkBitmap::kARGB_8888_Config
, width
, height
);
129 if (bitmap
.allocPixels() != true)
134 data
= CGBitmapContextGetData(context
);
135 bitmap
.setPixels(data
);
137 data
= bitmap
.getPixels();
140 bitmap
.setIsOpaque(is_opaque
);
142 // If we were given data, then don't clobber it!
144 if (!context
&& is_opaque
) {
145 // To aid in finding bugs, we set the background color to something
146 // obviously wrong so it will be noticable when it is not cleared
147 bitmap
.eraseARGB(255, 0, 255, 128); // bright bluish green
152 context
= CGContextForData(data
, width
, height
);
156 CGContextRetain(context
);
158 BitmapPlatformDevice
* rv
= new BitmapPlatformDevice(
159 skia::AdoptRef(new BitmapPlatformDeviceData(context
)), bitmap
);
161 // The device object took ownership of the graphics context with its own
162 // CGContextRetain call.
163 CGContextRelease(context
);
168 BitmapPlatformDevice
* BitmapPlatformDevice::CreateAndClear(int width
,
171 BitmapPlatformDevice
* device
= Create(NULL
, width
, height
, is_opaque
);
173 device
->accessBitmap(true).eraseARGB(0, 0, 0, 0);
177 BitmapPlatformDevice
* BitmapPlatformDevice::CreateWithData(uint8_t* data
,
181 CGContextRef context
= NULL
;
183 context
= CGContextForData(data
, width
, height
);
185 BitmapPlatformDevice
* rv
= Create(context
, width
, height
, is_opaque
);
187 // The device object took ownership of the graphics context with its own
188 // CGContextRetain call.
190 CGContextRelease(context
);
195 // The device will own the bitmap, which corresponds to also owning the pixel
196 // data. Therefore, we do not transfer ownership to the SkDevice's bitmap.
197 BitmapPlatformDevice::BitmapPlatformDevice(
198 const skia::RefPtr
<BitmapPlatformDeviceData
>& data
, const SkBitmap
& bitmap
)
201 SetPlatformDevice(this, this);
204 BitmapPlatformDevice::~BitmapPlatformDevice() {
207 CGContextRef
BitmapPlatformDevice::GetBitmapContext() {
209 return data_
->bitmap_context();
212 void BitmapPlatformDevice::setMatrixClip(const SkMatrix
& transform
,
213 const SkRegion
& region
,
214 const SkClipStack
&) {
215 data_
->SetMatrixClip(transform
, region
);
218 void BitmapPlatformDevice::DrawToNativeContext(CGContextRef context
, int x
,
219 int y
, const CGRect
* src_rect
) {
220 bool created_dc
= false;
221 if (!data_
->bitmap_context()) {
226 // this should not make a copy of the bits, since we're not doing
227 // anything to trigger copy on write
228 CGImageRef image
= CGBitmapContextCreateImage(data_
->bitmap_context());
233 bounds
.size
.width
= src_rect
->size
.width
;
234 bounds
.size
.height
= src_rect
->size
.height
;
235 CGImageRef sub_image
= CGImageCreateWithImageInRect(image
, *src_rect
);
236 CGContextDrawImage(context
, bounds
, sub_image
);
237 CGImageRelease(sub_image
);
239 bounds
.size
.width
= width();
240 bounds
.size
.height
= height();
241 CGContextDrawImage(context
, bounds
, image
);
243 CGImageRelease(image
);
246 data_
->ReleaseBitmapContext();
249 const SkBitmap
& BitmapPlatformDevice::onAccessBitmap(SkBitmap
* bitmap
) {
250 // Not needed in CoreGraphics
254 SkDevice
* BitmapPlatformDevice::onCreateCompatibleDevice(
255 SkBitmap::Config config
, int width
, int height
, bool isOpaque
,
257 SkASSERT(config
== SkBitmap::kARGB_8888_Config
);
258 SkDevice
* bitmap_device
= BitmapPlatformDevice::CreateAndClear(width
, height
,
260 return bitmap_device
;
263 // PlatformCanvas impl
265 SkCanvas
* CreatePlatformCanvas(CGContextRef ctx
, int width
, int height
,
266 bool is_opaque
, OnFailureType failureType
) {
267 skia::RefPtr
<SkDevice
> dev
= skia::AdoptRef(
268 BitmapPlatformDevice::Create(ctx
, width
, height
, is_opaque
));
269 return CreateCanvas(dev
, failureType
);
272 SkCanvas
* CreatePlatformCanvas(int width
, int height
, bool is_opaque
,
273 uint8_t* data
, OnFailureType failureType
) {
274 skia::RefPtr
<SkDevice
> dev
= skia::AdoptRef(
275 BitmapPlatformDevice::CreateWithData(data
, width
, height
, is_opaque
));
276 return CreateCanvas(dev
, failureType
);
279 // Port of PlatformBitmap to mac
281 PlatformBitmap::~PlatformBitmap() {
283 CGContextRelease(surface_
);
286 bool PlatformBitmap::Allocate(int width
, int height
, bool is_opaque
) {
287 if (RasterDeviceTooBigToAllocate(width
, height
))
290 bitmap_
.setConfig(SkBitmap::kARGB_8888_Config
, width
, height
, width
* 4);
291 if (!bitmap_
.allocPixels())
295 bitmap_
.eraseColor(0);
296 bitmap_
.setIsOpaque(is_opaque
);
298 surface_
= CGContextForData(bitmap_
.getPixels(), bitmap_
.width(),