Roll leveldb from r73 to r75.
[chromium-blink-merge.git] / skia / ext / bitmap_platform_device_mac.cc
blobb7b05e50a3c8187dcf39880ea920495ef44de06a
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>
8 #include <time.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"
20 namespace skia {
22 namespace {
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);
42 #else
43 #error We require that Skia's and CoreGraphics's recommended \
44 image memory layout match.
45 #endif
46 #undef HAS_ARGB_SHIFTS
48 if (!context)
49 return NULL;
51 // Change the coordinate system to match WebCore's
52 CGContextTranslateCTM(context, 0, height);
53 CGContextScaleCTM(context, 1.0, -1.0);
55 return context;
58 } // namespace
60 BitmapPlatformDevice::BitmapPlatformDeviceData::BitmapPlatformDeviceData(
61 CGContextRef bitmap)
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.
68 SkIRect rect;
69 rect.set(0, 0,
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
75 // in LoadConfig().
76 CGContextSaveGState(bitmap_context_);
79 BitmapPlatformDevice::BitmapPlatformDeviceData::~BitmapPlatformDeviceData() {
80 if (bitmap_context_)
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;
95 config_dirty_ = true;
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
119 // data.
120 BitmapPlatformDevice* BitmapPlatformDevice::Create(CGContextRef context,
121 int width,
122 int height,
123 bool is_opaque) {
124 if (RasterDeviceTooBigToAllocate(width, height))
125 return NULL;
127 SkBitmap bitmap;
128 bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
129 if (bitmap.allocPixels() != true)
130 return NULL;
132 void* data = NULL;
133 if (context) {
134 data = CGBitmapContextGetData(context);
135 bitmap.setPixels(data);
136 } else {
137 data = bitmap.getPixels();
140 bitmap.setIsOpaque(is_opaque);
142 // If we were given data, then don't clobber it!
143 #ifndef NDEBUG
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
149 #endif
151 if (!context) {
152 context = CGContextForData(data, width, height);
153 if (!context)
154 return NULL;
155 } else
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);
165 return rv;
168 BitmapPlatformDevice* BitmapPlatformDevice::CreateAndClear(int width,
169 int height,
170 bool is_opaque) {
171 BitmapPlatformDevice* device = Create(NULL, width, height, is_opaque);
172 if (!is_opaque)
173 device->accessBitmap(true).eraseARGB(0, 0, 0, 0);
174 return device;
177 BitmapPlatformDevice* BitmapPlatformDevice::CreateWithData(uint8_t* data,
178 int width,
179 int height,
180 bool is_opaque) {
181 CGContextRef context = NULL;
182 if (data)
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.
189 if (context)
190 CGContextRelease(context);
192 return rv;
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)
199 : SkDevice(bitmap),
200 data_(data) {
201 SetPlatformDevice(this, this);
204 BitmapPlatformDevice::~BitmapPlatformDevice() {
207 CGContextRef BitmapPlatformDevice::GetBitmapContext() {
208 data_->LoadConfig();
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()) {
222 created_dc = true;
223 GetBitmapContext();
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());
229 CGRect bounds;
230 bounds.origin.x = x;
231 bounds.origin.y = y;
232 if (src_rect) {
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);
238 } else {
239 bounds.size.width = width();
240 bounds.size.height = height();
241 CGContextDrawImage(context, bounds, image);
243 CGImageRelease(image);
245 if (created_dc)
246 data_->ReleaseBitmapContext();
249 const SkBitmap& BitmapPlatformDevice::onAccessBitmap(SkBitmap* bitmap) {
250 // Not needed in CoreGraphics
251 return *bitmap;
254 SkDevice* BitmapPlatformDevice::onCreateCompatibleDevice(
255 SkBitmap::Config config, int width, int height, bool isOpaque,
256 Usage /*usage*/) {
257 SkASSERT(config == SkBitmap::kARGB_8888_Config);
258 SkDevice* bitmap_device = BitmapPlatformDevice::CreateAndClear(width, height,
259 isOpaque);
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() {
282 if (surface_)
283 CGContextRelease(surface_);
286 bool PlatformBitmap::Allocate(int width, int height, bool is_opaque) {
287 if (RasterDeviceTooBigToAllocate(width, height))
288 return false;
290 bitmap_.setConfig(SkBitmap::kARGB_8888_Config, width, height, width * 4);
291 if (!bitmap_.allocPixels())
292 return false;
294 if (!is_opaque)
295 bitmap_.eraseColor(0);
296 bitmap_.setIsOpaque(is_opaque);
298 surface_ = CGContextForData(bitmap_.getPixels(), bitmap_.width(),
299 bitmap_.height());
300 return true;
303 } // namespace skia