Home | History | Annotate | Download | only in ext
      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 "skia/ext/platform_device.h"
      6 #include "skia/ext/bitmap_platform_device.h"
      7 
      8 #import <ApplicationServices/ApplicationServices.h>
      9 #include "skia/ext/skia_utils_mac.h"
     10 #include "third_party/skia/include/core/SkMatrix.h"
     11 #include "third_party/skia/include/core/SkPath.h"
     12 #include "third_party/skia/include/core/SkTypes.h"
     13 #include "third_party/skia/include/core/SkUtils.h"
     14 
     15 namespace skia {
     16 
     17 CGContextRef GetBitmapContext(SkBaseDevice* device) {
     18   PlatformDevice* platform_device = GetPlatformDevice(device);
     19   if (platform_device)
     20     return platform_device->GetBitmapContext();
     21 
     22   return NULL;
     23 }
     24 
     25 CGContextRef PlatformDevice::BeginPlatformPaint() {
     26   return GetBitmapContext();
     27 }
     28 
     29 void PlatformDevice::EndPlatformPaint() {
     30   // Flushing will be done in onAccessBitmap.
     31 }
     32 
     33 // Set up the CGContextRef for peaceful coexistence with Skia
     34 void PlatformDevice::InitializeCGContext(CGContextRef context) {
     35   // CG defaults to the same settings as Skia
     36 }
     37 
     38 // static
     39 void PlatformDevice::LoadPathToCGContext(CGContextRef context,
     40                                          const SkPath& path) {
     41   // instead of a persistent attribute of the context, CG specifies the fill
     42   // type per call, so we just have to load up the geometry.
     43   CGContextBeginPath(context);
     44 
     45   SkPoint points[4] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
     46   SkPath::Iter iter(path, false);
     47   for (SkPath::Verb verb = iter.next(points); verb != SkPath::kDone_Verb;
     48        verb = iter.next(points)) {
     49     switch (verb) {
     50       case SkPath::kMove_Verb: {  // iter.next returns 1 point
     51         CGContextMoveToPoint(context, points[0].fX, points[0].fY);
     52         break;
     53       }
     54       case SkPath::kLine_Verb: {  // iter.next returns 2 points
     55         CGContextAddLineToPoint(context, points[1].fX, points[1].fY);
     56         break;
     57       }
     58       case SkPath::kQuad_Verb: {  // iter.next returns 3 points
     59         CGContextAddQuadCurveToPoint(context, points[1].fX, points[1].fY,
     60                                      points[2].fX, points[2].fY);
     61         break;
     62       }
     63       case SkPath::kCubic_Verb: {  // iter.next returns 4 points
     64         CGContextAddCurveToPoint(context, points[1].fX, points[1].fY,
     65                                  points[2].fX, points[2].fY,
     66                                  points[3].fX, points[3].fY);
     67         break;
     68       }
     69       case SkPath::kClose_Verb: {  // iter.next returns 1 point (the last point)
     70         break;
     71       }
     72       case SkPath::kDone_Verb:  // iter.next returns 0 points
     73       default: {
     74         SkASSERT(false);
     75         break;
     76       }
     77     }
     78   }
     79   CGContextClosePath(context);
     80 }
     81 
     82 // static
     83 void PlatformDevice::LoadTransformToCGContext(CGContextRef context,
     84                                               const SkMatrix& matrix) {
     85   // CoreGraphics can concatenate transforms, but not reset the current one.
     86   // So in order to get the required behavior here, we need to first make
     87   // the current transformation matrix identity and only then load the new one.
     88 
     89   // Reset matrix to identity.
     90   CGAffineTransform orig_cg_matrix = CGContextGetCTM(context);
     91   CGAffineTransform orig_cg_matrix_inv = CGAffineTransformInvert(
     92       orig_cg_matrix);
     93   CGContextConcatCTM(context, orig_cg_matrix_inv);
     94 
     95   // assert that we have indeed returned to the identity Matrix.
     96   SkASSERT(CGAffineTransformIsIdentity(CGContextGetCTM(context)));
     97 
     98   // Convert xform to CG-land.
     99   // Our coordinate system is flipped to match WebKit's so we need to modify
    100   // the xform to match that.
    101   SkMatrix transformed_matrix = matrix;
    102   SkScalar sy = matrix.getScaleY() * (SkScalar)-1;
    103   transformed_matrix.setScaleY(sy);
    104   size_t height = CGBitmapContextGetHeight(context);
    105   SkScalar ty = -matrix.getTranslateY(); // y axis is flipped.
    106   transformed_matrix.setTranslateY(ty + (SkScalar)height);
    107 
    108   CGAffineTransform cg_matrix = gfx::SkMatrixToCGAffineTransform(
    109       transformed_matrix);
    110 
    111   // Load final transform into context.
    112   CGContextConcatCTM(context, cg_matrix);
    113 }
    114 
    115 // static
    116 void PlatformDevice::LoadClippingRegionToCGContext(
    117          CGContextRef context,
    118          const SkRegion& region,
    119          const SkMatrix& transformation) {
    120   if (region.isEmpty()) {
    121     // region can be empty, in which case everything will be clipped.
    122     SkRect rect;
    123     rect.setEmpty();
    124     CGContextClipToRect(context, gfx::SkRectToCGRect(rect));
    125   } else if (region.isRect()) {
    126     // CoreGraphics applies the current transform to clip rects, which is
    127     // unwanted. Inverse-transform the rect before sending it to CG. This only
    128     // works for translations and scaling, but not for rotations (but the
    129     // viewport is never rotated anyway).
    130     SkMatrix t;
    131     bool did_invert = transformation.invert(&t);
    132     if (!did_invert)
    133       t.reset();
    134     // Do the transformation.
    135     SkRect rect;
    136     rect.set(region.getBounds());
    137     t.mapRect(&rect);
    138     SkIRect irect;
    139     rect.round(&irect);
    140     CGContextClipToRect(context, gfx::SkIRectToCGRect(irect));
    141   } else {
    142     // It is complex.
    143     SkPath path;
    144     region.getBoundaryPath(&path);
    145     // Clip. Note that windows clipping regions are not affected by the
    146     // transform so apply it manually.
    147     path.transform(transformation);
    148     // TODO(playmobil): Implement.
    149     SkASSERT(false);
    150     // LoadPathToDC(context, path);
    151     // hrgn = PathToRegion(context);
    152   }
    153 }
    154 
    155 }  // namespace skia
    156