// Copyright (c) 2006-2008 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/bitmap_platform_device.h" #import <ApplicationServices/ApplicationServices.h> #include "skia/ext/skia_utils_mac.h" #include "third_party/skia/include/core/SkMatrix.h" #include "third_party/skia/include/core/SkPath.h" #include "third_party/skia/include/core/SkTypes.h" #include "third_party/skia/include/core/SkUtils.h" namespace skia { CGContextRef GetBitmapContext(SkBaseDevice* device) { PlatformDevice* platform_device = GetPlatformDevice(device); if (platform_device) return platform_device->GetBitmapContext(); return NULL; } CGContextRef PlatformDevice::BeginPlatformPaint() { return GetBitmapContext(); } void PlatformDevice::EndPlatformPaint() { // Flushing will be done in onAccessBitmap. } // Set up the CGContextRef for peaceful coexistence with Skia void PlatformDevice::InitializeCGContext(CGContextRef context) { // CG defaults to the same settings as Skia } // static void PlatformDevice::LoadPathToCGContext(CGContextRef context, const SkPath& path) { // instead of a persistent attribute of the context, CG specifies the fill // type per call, so we just have to load up the geometry. CGContextBeginPath(context); SkPoint points[4] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} }; SkPath::Iter iter(path, false); for (SkPath::Verb verb = iter.next(points); verb != SkPath::kDone_Verb; verb = iter.next(points)) { switch (verb) { case SkPath::kMove_Verb: { // iter.next returns 1 point CGContextMoveToPoint(context, points[0].fX, points[0].fY); break; } case SkPath::kLine_Verb: { // iter.next returns 2 points CGContextAddLineToPoint(context, points[1].fX, points[1].fY); break; } case SkPath::kQuad_Verb: { // iter.next returns 3 points CGContextAddQuadCurveToPoint(context, points[1].fX, points[1].fY, points[2].fX, points[2].fY); break; } case SkPath::kCubic_Verb: { // iter.next returns 4 points CGContextAddCurveToPoint(context, points[1].fX, points[1].fY, points[2].fX, points[2].fY, points[3].fX, points[3].fY); break; } case SkPath::kClose_Verb: { // iter.next returns 1 point (the last point) break; } case SkPath::kDone_Verb: // iter.next returns 0 points default: { SkASSERT(false); break; } } } CGContextClosePath(context); } // static void PlatformDevice::LoadTransformToCGContext(CGContextRef context, const SkMatrix& matrix) { // CoreGraphics can concatenate transforms, but not reset the current one. // So in order to get the required behavior here, we need to first make // the current transformation matrix identity and only then load the new one. // Reset matrix to identity. CGAffineTransform orig_cg_matrix = CGContextGetCTM(context); CGAffineTransform orig_cg_matrix_inv = CGAffineTransformInvert( orig_cg_matrix); CGContextConcatCTM(context, orig_cg_matrix_inv); // assert that we have indeed returned to the identity Matrix. SkASSERT(CGAffineTransformIsIdentity(CGContextGetCTM(context))); // Convert xform to CG-land. // Our coordinate system is flipped to match WebKit's so we need to modify // the xform to match that. SkMatrix transformed_matrix = matrix; SkScalar sy = matrix.getScaleY() * (SkScalar)-1; transformed_matrix.setScaleY(sy); size_t height = CGBitmapContextGetHeight(context); SkScalar ty = -matrix.getTranslateY(); // y axis is flipped. transformed_matrix.setTranslateY(ty + (SkScalar)height); CGAffineTransform cg_matrix = gfx::SkMatrixToCGAffineTransform( transformed_matrix); // Load final transform into context. CGContextConcatCTM(context, cg_matrix); } // static void PlatformDevice::LoadClippingRegionToCGContext( CGContextRef context, const SkRegion& region, const SkMatrix& transformation) { if (region.isEmpty()) { // region can be empty, in which case everything will be clipped. SkRect rect; rect.setEmpty(); CGContextClipToRect(context, gfx::SkRectToCGRect(rect)); } else if (region.isRect()) { // CoreGraphics applies the current transform to clip rects, which is // unwanted. Inverse-transform the rect before sending it to CG. This only // works for translations and scaling, but not for rotations (but the // viewport is never rotated anyway). SkMatrix t; bool did_invert = transformation.invert(&t); if (!did_invert) t.reset(); // Do the transformation. SkRect rect; rect.set(region.getBounds()); t.mapRect(&rect); SkIRect irect; rect.round(&irect); CGContextClipToRect(context, gfx::SkIRectToCGRect(irect)); } 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. path.transform(transformation); // TODO(playmobil): Implement. SkASSERT(false); // LoadPathToDC(context, path); // hrgn = PathToRegion(context); } } } // namespace skia