1 // Copyright (c) 2006-2008 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/platform_device.h"
6 #include "skia/ext/bitmap_platform_device.h"
8 #import <ApplicationServices/ApplicationServices.h>
9 #include "skia/ext/skia_utils_mac.h"
10 #include "third_party/skia/include/core/SkMatrix.h"
11 #include "third_party/skia/include/core/SkPath.h"
12 #include "third_party/skia/include/core/SkTypes.h"
13 #include "third_party/skia/include/core/SkUtils.h"
17 CGContextRef
GetBitmapContext(SkDevice
* device
) {
18 PlatformDevice
* platform_device
= GetPlatformDevice(device
);
20 return platform_device
->GetBitmapContext();
25 CGContextRef
PlatformDevice::BeginPlatformPaint() {
26 return GetBitmapContext();
29 void PlatformDevice::EndPlatformPaint() {
30 // Flushing will be done in onAccessBitmap.
33 // Set up the CGContextRef for peaceful coexistence with Skia
34 void PlatformDevice::InitializeCGContext(CGContextRef context
) {
35 // CG defaults to the same settings as Skia
39 void PlatformDevice::LoadPathToCGContext(CGContextRef context
,
41 // instead of a persistent attribute of the context, CG specifies the fill
42 // type per call, so we just have to load up the geometry.
43 CGContextBeginPath(context
);
45 SkPoint points
[4] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
46 SkPath::Iter
iter(path
, false);
47 for (SkPath::Verb verb
= iter
.next(points
); verb
!= SkPath::kDone_Verb
;
48 verb
= iter
.next(points
)) {
50 case SkPath::kMove_Verb
: { // iter.next returns 1 point
51 CGContextMoveToPoint(context
, points
[0].fX
, points
[0].fY
);
54 case SkPath::kLine_Verb
: { // iter.next returns 2 points
55 CGContextAddLineToPoint(context
, points
[1].fX
, points
[1].fY
);
58 case SkPath::kQuad_Verb
: { // iter.next returns 3 points
59 CGContextAddQuadCurveToPoint(context
, points
[1].fX
, points
[1].fY
,
60 points
[2].fX
, points
[2].fY
);
63 case SkPath::kCubic_Verb
: { // iter.next returns 4 points
64 CGContextAddCurveToPoint(context
, points
[1].fX
, points
[1].fY
,
65 points
[2].fX
, points
[2].fY
,
66 points
[3].fX
, points
[3].fY
);
69 case SkPath::kClose_Verb
: { // iter.next returns 1 point (the last point)
72 case SkPath::kDone_Verb
: // iter.next returns 0 points
79 CGContextClosePath(context
);
83 void PlatformDevice::LoadTransformToCGContext(CGContextRef context
,
84 const SkMatrix
& matrix
) {
85 // CoreGraphics can concatenate transforms, but not reset the current one.
86 // So in order to get the required behavior here, we need to first make
87 // the current transformation matrix identity and only then load the new one.
89 // Reset matrix to identity.
90 CGAffineTransform orig_cg_matrix
= CGContextGetCTM(context
);
91 CGAffineTransform orig_cg_matrix_inv
= CGAffineTransformInvert(
93 CGContextConcatCTM(context
, orig_cg_matrix_inv
);
95 // assert that we have indeed returned to the identity Matrix.
96 SkASSERT(CGAffineTransformIsIdentity(CGContextGetCTM(context
)));
98 // Convert xform to CG-land.
99 // Our coordinate system is flipped to match WebKit's so we need to modify
100 // the xform to match that.
101 SkMatrix transformed_matrix
= matrix
;
102 SkScalar sy
= matrix
.getScaleY() * (SkScalar
)-1;
103 transformed_matrix
.setScaleY(sy
);
104 size_t height
= CGBitmapContextGetHeight(context
);
105 SkScalar ty
= -matrix
.getTranslateY(); // y axis is flipped.
106 transformed_matrix
.setTranslateY(ty
+ (SkScalar
)height
);
108 CGAffineTransform cg_matrix
= gfx::SkMatrixToCGAffineTransform(
111 // Load final transform into context.
112 CGContextConcatCTM(context
, cg_matrix
);
116 void PlatformDevice::LoadClippingRegionToCGContext(
117 CGContextRef context
,
118 const SkRegion
& region
,
119 const SkMatrix
& transformation
) {
120 if (region
.isEmpty()) {
121 // region can be empty, in which case everything will be clipped.
124 CGContextClipToRect(context
, gfx::SkRectToCGRect(rect
));
125 } else if (region
.isRect()) {
126 // CoreGraphics applies the current transform to clip rects, which is
127 // unwanted. Inverse-transform the rect before sending it to CG. This only
128 // works for translations and scaling, but not for rotations (but the
129 // viewport is never rotated anyway).
131 bool did_invert
= transformation
.invert(&t
);
134 // Do the transformation.
136 rect
.set(region
.getBounds());
140 CGContextClipToRect(context
, gfx::SkIRectToCGRect(irect
));
144 region
.getBoundaryPath(&path
);
145 // Clip. Note that windows clipping regions are not affected by the
146 // transform so apply it manually.
147 path
.transform(transformation
);
148 // TODO(playmobil): Implement.
150 // LoadPathToDC(context, path);
151 // hrgn = PathToRegion(context);