// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "skia/ext/platform_device.h" #include "skia/ext/skia_utils_win.h" #include "third_party/skia/include/core/SkMatrix.h" #include "third_party/skia/include/core/SkPath.h" #include "third_party/skia/include/core/SkRegion.h" #include "third_party/skia/include/core/SkUtils.h" namespace skia { void InitializeDC(HDC context) { // Enables world transformation. // If the GM_ADVANCED graphics mode is set, GDI always draws arcs in the // counterclockwise direction in logical space. This is equivalent to the // statement that, in the GM_ADVANCED graphics mode, both arc control points // and arcs themselves fully respect the device context's world-to-device // transformation. BOOL res = SetGraphicsMode(context, GM_ADVANCED); SkASSERT(res != 0); // Enables dithering. res = SetStretchBltMode(context, HALFTONE); SkASSERT(res != 0); // As per SetStretchBltMode() documentation, SetBrushOrgEx() must be called // right after. res = SetBrushOrgEx(context, 0, 0, NULL); SkASSERT(res != 0); // Sets up default orientation. res = SetArcDirection(context, AD_CLOCKWISE); SkASSERT(res != 0); // Sets up default colors. res = SetBkColor(context, RGB(255, 255, 255)); SkASSERT(res != CLR_INVALID); res = SetTextColor(context, RGB(0, 0, 0)); SkASSERT(res != CLR_INVALID); res = SetDCBrushColor(context, RGB(255, 255, 255)); SkASSERT(res != CLR_INVALID); res = SetDCPenColor(context, RGB(0, 0, 0)); SkASSERT(res != CLR_INVALID); // Sets up default transparency. res = SetBkMode(context, OPAQUE); SkASSERT(res != 0); res = SetROP2(context, R2_COPYPEN); SkASSERT(res != 0); } PlatformSurface PlatformDevice::BeginPlatformPaint() { return 0; } void PlatformDevice::EndPlatformPaint() { // We don't clear the DC here since it will be likely to be used again. // Flushing will be done in onAccessBitmap. } void PlatformDevice::DrawToNativeContext(PlatformSurface surface, int x, int y, const PlatformRect* src_rect) { } // static bool PlatformDevice::LoadPathToDC(HDC context, const SkPath& path) { switch (path.getFillType()) { case SkPath::kWinding_FillType: { int res = SetPolyFillMode(context, WINDING); SkASSERT(res != 0); break; } case SkPath::kEvenOdd_FillType: { int res = SetPolyFillMode(context, ALTERNATE); SkASSERT(res != 0); break; } default: { SkASSERT(false); break; } } BOOL res = BeginPath(context); if (!res) { return false; } CubicPaths paths; if (!SkPathToCubicPaths(&paths, path)) return false; std::vector<POINT> points; for (CubicPaths::const_iterator path(paths.begin()); path != paths.end(); ++path) { if (!path->size()) continue; points.resize(0); points.reserve(path->size() * 3 / 4 + 1); points.push_back(SkPointToPOINT(path->front().p[0])); for (CubicPath::const_iterator point(path->begin()); point != path->end(); ++point) { // Never add point->p[0] points.push_back(SkPointToPOINT(point->p[1])); points.push_back(SkPointToPOINT(point->p[2])); points.push_back(SkPointToPOINT(point->p[3])); } SkASSERT((points.size() - 1) % 3 == 0); // This is slightly inefficient since all straight line and quadratic lines // are "upgraded" to a cubic line. // TODO(maruel): http://b/1147346 We should use // PolyDraw/PolyBezier/Polyline whenever possible. res = PolyBezier(context, &points.front(), static_cast<DWORD>(points.size())); SkASSERT(res != 0); if (res == 0) break; } if (res == 0) { // Make sure the path is discarded. AbortPath(context); } else { res = EndPath(context); SkASSERT(res != 0); } return true; } // static void PlatformDevice::LoadTransformToDC(HDC dc, const SkMatrix& matrix) { XFORM xf; xf.eM11 = matrix[SkMatrix::kMScaleX]; xf.eM21 = matrix[SkMatrix::kMSkewX]; xf.eDx = matrix[SkMatrix::kMTransX]; xf.eM12 = matrix[SkMatrix::kMSkewY]; xf.eM22 = matrix[SkMatrix::kMScaleY]; xf.eDy = matrix[SkMatrix::kMTransY]; SetWorldTransform(dc, &xf); } // static bool PlatformDevice::SkPathToCubicPaths(CubicPaths* paths, const SkPath& skpath) { paths->clear(); CubicPath* current_path = NULL; SkPoint current_points[4]; CubicPoints points_to_add; SkPath::Iter iter(skpath, false); for (SkPath::Verb verb = iter.next(current_points); verb != SkPath::kDone_Verb; verb = iter.next(current_points)) { switch (verb) { case SkPath::kMove_Verb: { // iter.next returns 1 point // Ignores it since the point is copied in the next operation. See // SkPath::Iter::next() for reference. paths->push_back(CubicPath()); current_path = &paths->back(); // Skip point addition. continue; } case SkPath::kLine_Verb: { // iter.next returns 2 points points_to_add.p[0] = current_points[0]; points_to_add.p[1] = current_points[0]; points_to_add.p[2] = current_points[1]; points_to_add.p[3] = current_points[1]; break; } case SkPath::kQuad_Verb: { // iter.next returns 3 points points_to_add.p[0] = current_points[0]; points_to_add.p[1] = current_points[1]; points_to_add.p[2] = current_points[2]; points_to_add.p[3] = current_points[2]; break; } case SkPath::kCubic_Verb: { // iter.next returns 4 points points_to_add.p[0] = current_points[0]; points_to_add.p[1] = current_points[1]; points_to_add.p[2] = current_points[2]; points_to_add.p[3] = current_points[3]; break; } case SkPath::kClose_Verb: { // iter.next returns 1 point (the last point) paths->push_back(CubicPath()); current_path = &paths->back(); continue; } default: { current_path = NULL; // Will return false. break; } } SkASSERT(current_path); if (!current_path) { paths->clear(); return false; } current_path->push_back(points_to_add); } return true; } // static void PlatformDevice::LoadClippingRegionToDC(HDC context, const SkRegion& region, const SkMatrix& transformation) { HRGN hrgn; if (region.isEmpty()) { // region can be empty, in which case everything will be clipped. hrgn = CreateRectRgn(0, 0, 0, 0); } else if (region.isRect()) { // We don't apply transformation, because the translation is already applied // to the region. hrgn = CreateRectRgnIndirect(&SkIRectToRECT(region.getBounds())); } else { // It is complex. SkPath path; region.getBoundaryPath(&path); // Clip. Note that windows clipping regions are not affected by the // transform so apply it manually. // Since the transform is given as the original translation of canvas, we // should apply it in reverse. SkMatrix t(transformation); t.setTranslateX(-t.getTranslateX()); t.setTranslateY(-t.getTranslateY()); path.transform(t); LoadPathToDC(context, path); hrgn = PathToRegion(context); } int result = SelectClipRgn(context, hrgn); SkASSERT(result != ERROR); result = DeleteObject(hrgn); SkASSERT(result != 0); } } // namespace skia