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.
4
5 #include "skia/ext/platform_device.h"
6
7 #include "skia/ext/skia_utils_win.h"
8 #include "third_party/skia/include/core/SkMatrix.h"
9 #include "third_party/skia/include/core/SkPath.h"
10 #include "third_party/skia/include/core/SkRegion.h"
11 #include "third_party/skia/include/core/SkUtils.h"
12
13 namespace skia {
14
InitializeDC(HDC context)15 void InitializeDC(HDC context) {
16 // Enables world transformation.
17 // If the GM_ADVANCED graphics mode is set, GDI always draws arcs in the
18 // counterclockwise direction in logical space. This is equivalent to the
19 // statement that, in the GM_ADVANCED graphics mode, both arc control points
20 // and arcs themselves fully respect the device context's world-to-device
21 // transformation.
22 BOOL res = SetGraphicsMode(context, GM_ADVANCED);
23 SkASSERT(res != 0);
24
25 // Enables dithering.
26 res = SetStretchBltMode(context, HALFTONE);
27 SkASSERT(res != 0);
28 // As per SetStretchBltMode() documentation, SetBrushOrgEx() must be called
29 // right after.
30 res = SetBrushOrgEx(context, 0, 0, NULL);
31 SkASSERT(res != 0);
32
33 // Sets up default orientation.
34 res = SetArcDirection(context, AD_CLOCKWISE);
35 SkASSERT(res != 0);
36
37 // Sets up default colors.
38 res = SetBkColor(context, RGB(255, 255, 255));
39 SkASSERT(res != CLR_INVALID);
40 res = SetTextColor(context, RGB(0, 0, 0));
41 SkASSERT(res != CLR_INVALID);
42 res = SetDCBrushColor(context, RGB(255, 255, 255));
43 SkASSERT(res != CLR_INVALID);
44 res = SetDCPenColor(context, RGB(0, 0, 0));
45 SkASSERT(res != CLR_INVALID);
46
47 // Sets up default transparency.
48 res = SetBkMode(context, OPAQUE);
49 SkASSERT(res != 0);
50 res = SetROP2(context, R2_COPYPEN);
51 SkASSERT(res != 0);
52 }
53
BeginPlatformPaint()54 PlatformSurface PlatformDevice::BeginPlatformPaint() {
55 return 0;
56 }
57
EndPlatformPaint()58 void PlatformDevice::EndPlatformPaint() {
59 // We don't clear the DC here since it will be likely to be used again.
60 // Flushing will be done in onAccessBitmap.
61 }
62
DrawToNativeContext(PlatformSurface surface,int x,int y,const PlatformRect * src_rect)63 void PlatformDevice::DrawToNativeContext(PlatformSurface surface, int x, int y,
64 const PlatformRect* src_rect) {
65 }
66
67 // static
LoadPathToDC(HDC context,const SkPath & path)68 bool PlatformDevice::LoadPathToDC(HDC context, const SkPath& path) {
69 switch (path.getFillType()) {
70 case SkPath::kWinding_FillType: {
71 int res = SetPolyFillMode(context, WINDING);
72 SkASSERT(res != 0);
73 break;
74 }
75 case SkPath::kEvenOdd_FillType: {
76 int res = SetPolyFillMode(context, ALTERNATE);
77 SkASSERT(res != 0);
78 break;
79 }
80 default: {
81 SkASSERT(false);
82 break;
83 }
84 }
85 BOOL res = BeginPath(context);
86 if (!res) {
87 return false;
88 }
89
90 CubicPaths paths;
91 if (!SkPathToCubicPaths(&paths, path))
92 return false;
93
94 std::vector<POINT> points;
95 for (CubicPaths::const_iterator path(paths.begin()); path != paths.end();
96 ++path) {
97 if (!path->size())
98 continue;
99 points.resize(0);
100 points.reserve(path->size() * 3 / 4 + 1);
101 points.push_back(SkPointToPOINT(path->front().p[0]));
102 for (CubicPath::const_iterator point(path->begin()); point != path->end();
103 ++point) {
104 // Never add point->p[0]
105 points.push_back(SkPointToPOINT(point->p[1]));
106 points.push_back(SkPointToPOINT(point->p[2]));
107 points.push_back(SkPointToPOINT(point->p[3]));
108 }
109 SkASSERT((points.size() - 1) % 3 == 0);
110 // This is slightly inefficient since all straight line and quadratic lines
111 // are "upgraded" to a cubic line.
112 // TODO(maruel): http://b/1147346 We should use
113 // PolyDraw/PolyBezier/Polyline whenever possible.
114 res = PolyBezier(context, &points.front(),
115 static_cast<DWORD>(points.size()));
116 SkASSERT(res != 0);
117 if (res == 0)
118 break;
119 }
120 if (res == 0) {
121 // Make sure the path is discarded.
122 AbortPath(context);
123 } else {
124 res = EndPath(context);
125 SkASSERT(res != 0);
126 }
127 return true;
128 }
129
130 // static
LoadTransformToDC(HDC dc,const SkMatrix & matrix)131 void PlatformDevice::LoadTransformToDC(HDC dc, const SkMatrix& matrix) {
132 XFORM xf;
133 xf.eM11 = matrix[SkMatrix::kMScaleX];
134 xf.eM21 = matrix[SkMatrix::kMSkewX];
135 xf.eDx = matrix[SkMatrix::kMTransX];
136 xf.eM12 = matrix[SkMatrix::kMSkewY];
137 xf.eM22 = matrix[SkMatrix::kMScaleY];
138 xf.eDy = matrix[SkMatrix::kMTransY];
139 SetWorldTransform(dc, &xf);
140 }
141
142 // static
SkPathToCubicPaths(CubicPaths * paths,const SkPath & skpath)143 bool PlatformDevice::SkPathToCubicPaths(CubicPaths* paths,
144 const SkPath& skpath) {
145 paths->clear();
146 CubicPath* current_path = NULL;
147 SkPoint current_points[4];
148 CubicPoints points_to_add;
149 SkPath::Iter iter(skpath, false);
150 for (SkPath::Verb verb = iter.next(current_points);
151 verb != SkPath::kDone_Verb;
152 verb = iter.next(current_points)) {
153 switch (verb) {
154 case SkPath::kMove_Verb: { // iter.next returns 1 point
155 // Ignores it since the point is copied in the next operation. See
156 // SkPath::Iter::next() for reference.
157 paths->push_back(CubicPath());
158 current_path = &paths->back();
159 // Skip point addition.
160 continue;
161 }
162 case SkPath::kLine_Verb: { // iter.next returns 2 points
163 points_to_add.p[0] = current_points[0];
164 points_to_add.p[1] = current_points[0];
165 points_to_add.p[2] = current_points[1];
166 points_to_add.p[3] = current_points[1];
167 break;
168 }
169 case SkPath::kQuad_Verb: { // iter.next returns 3 points
170 points_to_add.p[0] = current_points[0];
171 points_to_add.p[1] = current_points[1];
172 points_to_add.p[2] = current_points[2];
173 points_to_add.p[3] = current_points[2];
174 break;
175 }
176 case SkPath::kCubic_Verb: { // iter.next returns 4 points
177 points_to_add.p[0] = current_points[0];
178 points_to_add.p[1] = current_points[1];
179 points_to_add.p[2] = current_points[2];
180 points_to_add.p[3] = current_points[3];
181 break;
182 }
183 case SkPath::kClose_Verb: { // iter.next returns 1 point (the last point)
184 paths->push_back(CubicPath());
185 current_path = &paths->back();
186 continue;
187 }
188 default: {
189 current_path = NULL;
190 // Will return false.
191 break;
192 }
193 }
194 SkASSERT(current_path);
195 if (!current_path) {
196 paths->clear();
197 return false;
198 }
199 current_path->push_back(points_to_add);
200 }
201 return true;
202 }
203
204 // static
LoadClippingRegionToDC(HDC context,const SkRegion & region,const SkMatrix & transformation)205 void PlatformDevice::LoadClippingRegionToDC(HDC context,
206 const SkRegion& region,
207 const SkMatrix& transformation) {
208 HRGN hrgn;
209 if (region.isEmpty()) {
210 // region can be empty, in which case everything will be clipped.
211 hrgn = CreateRectRgn(0, 0, 0, 0);
212 } else if (region.isRect()) {
213 // We don't apply transformation, because the translation is already applied
214 // to the region.
215 hrgn = CreateRectRgnIndirect(&SkIRectToRECT(region.getBounds()));
216 } else {
217 // It is complex.
218 SkPath path;
219 region.getBoundaryPath(&path);
220 // Clip. Note that windows clipping regions are not affected by the
221 // transform so apply it manually.
222 // Since the transform is given as the original translation of canvas, we
223 // should apply it in reverse.
224 SkMatrix t(transformation);
225 t.setTranslateX(-t.getTranslateX());
226 t.setTranslateY(-t.getTranslateY());
227 path.transform(t);
228 LoadPathToDC(context, path);
229 hrgn = PathToRegion(context);
230 }
231 int result = SelectClipRgn(context, hrgn);
232 SkASSERT(result != ERROR);
233 result = DeleteObject(hrgn);
234 SkASSERT(result != 0);
235 }
236
237 } // namespace skia
238