Home | History | Annotate | Download | only in ext
      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