Remove unused did_first_* fields from InternalDocumentStateData.
[chromium-blink-merge.git] / skia / ext / bitmap_platform_device_mac.cc
blob087cf39d7fdfec0bb89681f59f9288ff52f020b3
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.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/SkPath.h"
17 #include "third_party/skia/include/core/SkRegion.h"
18 #include "third_party/skia/include/core/SkTypes.h"
19 #include "third_party/skia/include/core/SkUtils.h"
21 namespace skia {
23 namespace {
25 static CGContextRef CGContextForData(void* data, int width, int height) {
26 #define HAS_ARGB_SHIFTS(a, r, g, b) \
27 (SK_A32_SHIFT == (a) && SK_R32_SHIFT == (r) \
28 && SK_G32_SHIFT == (g) && SK_B32_SHIFT == (b))
29 #if defined(SK_CPU_LENDIAN) && HAS_ARGB_SHIFTS(24, 16, 8, 0)
30 // Allocate a bitmap context with 4 components per pixel (BGRA). Apple
31 // recommends these flags for improved CG performance.
33 // CGBitmapContextCreate returns NULL if width/height are 0. However, our
34 // callers expect to get a canvas back (which they later resize/reallocate)
35 // so we pin the dimensions here.
36 width = SkMax32(1, width);
37 height = SkMax32(1, height);
38 CGContextRef context =
39 CGBitmapContextCreate(data, width, height, 8, width * 4,
40 base::mac::GetSystemColorSpace(),
41 kCGImageAlphaPremultipliedFirst |
42 kCGBitmapByteOrder32Host);
43 #else
44 #error We require that Skia's and CoreGraphics's recommended \
45 image memory layout match.
46 #endif
47 #undef HAS_ARGB_SHIFTS
49 if (!context)
50 return NULL;
52 // Change the coordinate system to match WebCore's
53 CGContextTranslateCTM(context, 0, height);
54 CGContextScaleCTM(context, 1.0, -1.0);
56 return context;
59 } // namespace
61 void BitmapPlatformDevice::ReleaseBitmapContext() {
62 SkASSERT(bitmap_context_);
63 CGContextRelease(bitmap_context_);
64 bitmap_context_ = NULL;
67 void BitmapPlatformDevice::SetMatrixClip(
68 const SkMatrix& transform,
69 const SkRegion& region) {
70 transform_ = transform;
71 clip_region_ = region;
72 config_dirty_ = true;
75 // Loads the specified Skia transform into the device context
76 static void LoadTransformToCGContext(CGContextRef context,
77 const SkMatrix& matrix) {
78 // CoreGraphics can concatenate transforms, but not reset the current one.
79 // So in order to get the required behavior here, we need to first make
80 // the current transformation matrix identity and only then load the new one.
82 // Reset matrix to identity.
83 CGAffineTransform orig_cg_matrix = CGContextGetCTM(context);
84 CGAffineTransform orig_cg_matrix_inv =
85 CGAffineTransformInvert(orig_cg_matrix);
86 CGContextConcatCTM(context, orig_cg_matrix_inv);
88 // assert that we have indeed returned to the identity Matrix.
89 SkASSERT(CGAffineTransformIsIdentity(CGContextGetCTM(context)));
91 // Convert xform to CG-land.
92 // Our coordinate system is flipped to match WebKit's so we need to modify
93 // the xform to match that.
94 SkMatrix transformed_matrix = matrix;
95 SkScalar sy = -matrix.getScaleY();
96 transformed_matrix.setScaleY(sy);
97 size_t height = CGBitmapContextGetHeight(context);
98 SkScalar ty = -matrix.getTranslateY(); // y axis is flipped.
99 transformed_matrix.setTranslateY(ty + (SkScalar)height);
101 CGAffineTransform cg_matrix =
102 gfx::SkMatrixToCGAffineTransform(transformed_matrix);
104 // Load final transform into context.
105 CGContextConcatCTM(context, cg_matrix);
108 // Loads a SkRegion into the CG context.
109 static void LoadClippingRegionToCGContext(CGContextRef context,
110 const SkRegion& region,
111 const SkMatrix& transformation) {
112 if (region.isEmpty()) {
113 // region can be empty, in which case everything will be clipped.
114 SkRect rect;
115 rect.setEmpty();
116 CGContextClipToRect(context, gfx::SkRectToCGRect(rect));
117 } else if (region.isRect()) {
118 // CoreGraphics applies the current transform to clip rects, which is
119 // unwanted. Inverse-transform the rect before sending it to CG. This only
120 // works for translations and scaling, but not for rotations (but the
121 // viewport is never rotated anyway).
122 SkMatrix t;
123 bool did_invert = transformation.invert(&t);
124 if (!did_invert)
125 t.reset();
126 // Do the transformation.
127 SkRect rect;
128 rect.set(region.getBounds());
129 t.mapRect(&rect);
130 SkIRect irect;
131 rect.round(&irect);
132 CGContextClipToRect(context, gfx::SkIRectToCGRect(irect));
133 } else {
134 // It is complex.
135 SkPath path;
136 region.getBoundaryPath(&path);
137 // Clip. Note that windows clipping regions are not affected by the
138 // transform so apply it manually.
139 path.transform(transformation);
140 // TODO(playmobil): Implement.
141 SkASSERT(false);
142 // LoadPathToDC(context, path);
143 // hrgn = PathToRegion(context);
147 void BitmapPlatformDevice::LoadConfig() {
148 if (!config_dirty_ || !bitmap_context_)
149 return; // Nothing to do.
150 config_dirty_ = false;
152 // We must restore and then save the state of the graphics context since the
153 // calls to Load the clipping region to the context are strictly cummulative,
154 // i.e., you can't replace a clip rect, other than with a save/restore.
155 // But this implies that no other changes to the state are done elsewhere.
156 // If we ever get to need to change this, then we must replace the clip rect
157 // calls in LoadClippingRegionToCGContext() with an image mask instead.
158 CGContextRestoreGState(bitmap_context_);
159 CGContextSaveGState(bitmap_context_);
160 LoadTransformToCGContext(bitmap_context_, transform_);
161 LoadClippingRegionToCGContext(bitmap_context_, clip_region_, transform_);
165 // We use this static factory function instead of the regular constructor so
166 // that we can create the pixel data before calling the constructor. This is
167 // required so that we can call the base class' constructor with the pixel
168 // data.
169 BitmapPlatformDevice* BitmapPlatformDevice::Create(CGContextRef context,
170 int width,
171 int height,
172 bool is_opaque,
173 bool do_clear) {
174 if (RasterDeviceTooBigToAllocate(width, height))
175 return NULL;
177 SkBitmap bitmap;
178 // TODO: verify that the CG Context's pixels will have tight rowbytes or pass in the correct
179 // rowbytes for the case when context != NULL.
180 bitmap.setInfo(SkImageInfo::MakeN32(width, height, is_opaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType));
182 void* data;
183 if (context) {
184 data = CGBitmapContextGetData(context);
185 bitmap.setPixels(data);
186 } else {
187 if (!bitmap.tryAllocPixels())
188 return NULL;
189 data = bitmap.getPixels();
191 if (do_clear)
192 memset(data, 0, bitmap.getSafeSize());
194 // If we were given data, then don't clobber it!
195 #ifndef NDEBUG
196 if (!context && is_opaque) {
197 // To aid in finding bugs, we set the background color to something
198 // obviously wrong so it will be noticable when it is not cleared
199 bitmap.eraseARGB(255, 0, 255, 128); // bright bluish green
201 #endif
203 if (!context) {
204 context = CGContextForData(data, width, height);
205 if (!context)
206 return NULL;
207 } else
208 CGContextRetain(context);
210 BitmapPlatformDevice* rv = new BitmapPlatformDevice(context, bitmap);
212 // The device object took ownership of the graphics context with its own
213 // CGContextRetain call.
214 CGContextRelease(context);
216 return rv;
219 BitmapPlatformDevice* BitmapPlatformDevice::CreateWithData(uint8_t* data,
220 int width,
221 int height,
222 bool is_opaque) {
223 CGContextRef context = NULL;
224 if (data)
225 context = CGContextForData(data, width, height);
227 BitmapPlatformDevice* rv = Create(context, width, height, is_opaque, false);
229 // The device object took ownership of the graphics context with its own
230 // CGContextRetain call.
231 if (context)
232 CGContextRelease(context);
234 return rv;
237 // The device will own the bitmap, which corresponds to also owning the pixel
238 // data. Therefore, we do not transfer ownership to the SkBitmapDevice's bitmap.
239 BitmapPlatformDevice::BitmapPlatformDevice(
240 CGContextRef context, const SkBitmap& bitmap)
241 : SkBitmapDevice(bitmap),
242 bitmap_context_(context),
243 config_dirty_(true), // Want to load the config next time.
244 transform_(SkMatrix::I()) {
245 SetPlatformDevice(this, this);
246 SkASSERT(bitmap_context_);
247 // Initialize the clip region to the entire bitmap.
249 SkIRect rect;
250 rect.set(0, 0,
251 CGBitmapContextGetWidth(bitmap_context_),
252 CGBitmapContextGetHeight(bitmap_context_));
253 clip_region_ = SkRegion(rect);
254 CGContextRetain(bitmap_context_);
255 // We must save the state once so that we can use the restore/save trick
256 // in LoadConfig().
257 CGContextSaveGState(bitmap_context_);
260 BitmapPlatformDevice::~BitmapPlatformDevice() {
261 if (bitmap_context_)
262 CGContextRelease(bitmap_context_);
265 CGContextRef BitmapPlatformDevice::GetBitmapContext() {
266 LoadConfig();
267 return bitmap_context_;
270 void BitmapPlatformDevice::setMatrixClip(const SkMatrix& transform,
271 const SkRegion& region,
272 const SkClipStack&) {
273 SetMatrixClip(transform, region);
276 SkBaseDevice* BitmapPlatformDevice::onCreateDevice(const CreateInfo& cinfo,
277 const SkPaint*) {
278 const SkImageInfo& info = cinfo.fInfo;
279 const bool do_clear = !info.isOpaque();
280 SkASSERT(info.colorType() == kN32_SkColorType);
281 return Create(NULL, info.width(), info.height(), info.isOpaque(), do_clear);
284 // PlatformCanvas impl
286 SkCanvas* CreatePlatformCanvas(CGContextRef ctx, int width, int height,
287 bool is_opaque, OnFailureType failureType) {
288 const bool do_clear = false;
289 skia::RefPtr<SkBaseDevice> dev = skia::AdoptRef(
290 BitmapPlatformDevice::Create(ctx, width, height, is_opaque, do_clear));
291 return CreateCanvas(dev, failureType);
294 SkCanvas* CreatePlatformCanvas(int width, int height, bool is_opaque,
295 uint8_t* data, OnFailureType failureType) {
296 skia::RefPtr<SkBaseDevice> dev = skia::AdoptRef(
297 BitmapPlatformDevice::CreateWithData(data, width, height, is_opaque));
298 return CreateCanvas(dev, failureType);
301 // Port of PlatformBitmap to mac
303 PlatformBitmap::~PlatformBitmap() {
304 if (surface_)
305 CGContextRelease(surface_);
308 bool PlatformBitmap::Allocate(int width, int height, bool is_opaque) {
309 if (RasterDeviceTooBigToAllocate(width, height))
310 return false;
312 if (!bitmap_.tryAllocN32Pixels(width, height, is_opaque))
313 return false;
315 if (!is_opaque)
316 bitmap_.eraseColor(0);
318 surface_ = CGContextForData(bitmap_.getPixels(), bitmap_.width(),
319 bitmap_.height());
320 return true;
323 } // namespace skia