• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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