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