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 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 54 PlatformSurface PlatformDevice::BeginPlatformPaint() { 55 return 0; 56 } 57 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 63 void PlatformDevice::DrawToNativeContext(PlatformSurface surface, int x, int y, 64 const PlatformRect* src_rect) { 65 } 66 67 // static 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 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 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 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