// Copyright 2013 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 "build/build_config.h" #include "skia/ext/bitmap_platform_device_cairo.h" #include "skia/ext/platform_canvas.h" #if defined(OS_OPENBSD) #include #else #include #endif namespace skia { namespace { void CairoSurfaceReleaseProc(void*, void* context) { SkASSERT(context); cairo_surface_destroy(static_cast(context)); } // Back the destination bitmap by a Cairo surface. The bitmap's // pixelRef takes ownership of the passed-in surface and will call // cairo_surface_destroy() upon destruction. // // Note: it may immediately destroy the surface, if it fails to create a bitmap // with pixels, thus the caller must either ref() the surface before hand, or // it must not refer to the surface after this call. bool InstallCairoSurfacePixels(SkBitmap* dst, cairo_surface_t* surface, bool is_opaque) { SkASSERT(dst); if (!surface) { return false; } SkImageInfo info = SkImageInfo::MakeN32(cairo_image_surface_get_width(surface), cairo_image_surface_get_height(surface), is_opaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType); return dst->installPixels(info, cairo_image_surface_get_data(surface), cairo_image_surface_get_stride(surface), NULL, &CairoSurfaceReleaseProc, static_cast(surface)); } void LoadMatrixToContext(cairo_t* context, const SkMatrix& matrix) { cairo_matrix_t cairo_matrix; cairo_matrix_init(&cairo_matrix, SkScalarToFloat(matrix.getScaleX()), SkScalarToFloat(matrix.getSkewY()), SkScalarToFloat(matrix.getSkewX()), SkScalarToFloat(matrix.getScaleY()), SkScalarToFloat(matrix.getTranslateX()), SkScalarToFloat(matrix.getTranslateY())); cairo_set_matrix(context, &cairo_matrix); } void LoadClipToContext(cairo_t* context, const SkRegion& clip) { cairo_reset_clip(context); // TODO(brettw) support non-rect clips. SkIRect bounding = clip.getBounds(); cairo_rectangle(context, bounding.fLeft, bounding.fTop, bounding.fRight - bounding.fLeft, bounding.fBottom - bounding.fTop); cairo_clip(context); } } // namespace void BitmapPlatformDevice::SetMatrixClip( const SkMatrix& transform, const SkRegion& region) { transform_ = transform; clip_region_ = region; config_dirty_ = true; } void BitmapPlatformDevice::LoadConfig() { if (!config_dirty_ || !cairo_) return; // Nothing to do. config_dirty_ = false; // Load the identity matrix since this is what our clip is relative to. cairo_matrix_t cairo_matrix; cairo_matrix_init_identity(&cairo_matrix); cairo_set_matrix(cairo_, &cairo_matrix); LoadClipToContext(cairo_, clip_region_); LoadMatrixToContext(cairo_, transform_); } // We use this static factory function instead of the regular constructor so // that we can create the pixel data before calling the constructor. This is // required so that we can call the base class' constructor with the pixel // data. BitmapPlatformDevice* BitmapPlatformDevice::Create(int width, int height, bool is_opaque, cairo_surface_t* surface) { if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) { cairo_surface_destroy(surface); return NULL; } // must call this before trying to install the surface, since that may result // in the surface being destroyed. cairo_t* cairo = cairo_create(surface); SkBitmap bitmap; if (!InstallCairoSurfacePixels(&bitmap, surface, is_opaque)) { cairo_destroy(cairo); return NULL; } // The device object will take ownership of the graphics context. return new BitmapPlatformDevice(bitmap, cairo); } BitmapPlatformDevice* BitmapPlatformDevice::Create(int width, int height, bool is_opaque) { // This initializes the bitmap to all zeros. cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); BitmapPlatformDevice* device = Create(width, height, is_opaque, surface); #ifndef NDEBUG if (device && is_opaque) // Fill with bright bluish green SkCanvas(device).drawColor(0xFF00FF80); #endif return device; } BitmapPlatformDevice* BitmapPlatformDevice::Create(int width, int height, bool is_opaque, uint8_t* data) { cairo_surface_t* surface = cairo_image_surface_create_for_data( data, CAIRO_FORMAT_ARGB32, width, height, cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width)); return Create(width, height, is_opaque, surface); } // Ownership of the cairo object is transferred. BitmapPlatformDevice::BitmapPlatformDevice( const SkBitmap& bitmap, cairo_t* cairo) : SkBitmapDevice(bitmap), cairo_(cairo), config_dirty_(true), transform_(SkMatrix::I()) { // Want to load the config next time. SetPlatformDevice(this, this); } BitmapPlatformDevice::~BitmapPlatformDevice() { cairo_destroy(cairo_); } SkBaseDevice* BitmapPlatformDevice::onCreateDevice(const CreateInfo& info, const SkPaint*) { SkASSERT(info.fInfo.colorType() == kN32_SkColorType); return BitmapPlatformDevice::Create(info.fInfo.width(), info.fInfo.height(), info.fInfo.isOpaque()); } cairo_t* BitmapPlatformDevice::BeginPlatformPaint() { LoadConfig(); cairo_surface_t* surface = cairo_get_target(cairo_); // Tell cairo to flush anything it has pending. cairo_surface_flush(surface); // Tell Cairo that we (probably) modified (actually, will modify) its pixel // buffer directly. cairo_surface_mark_dirty(surface); return cairo_; } void BitmapPlatformDevice::setMatrixClip(const SkMatrix& transform, const SkRegion& region, const SkClipStack&) { SetMatrixClip(transform, region); } // PlatformCanvas impl SkCanvas* CreatePlatformCanvas(int width, int height, bool is_opaque, uint8_t* data, OnFailureType failureType) { skia::RefPtr dev = skia::AdoptRef( BitmapPlatformDevice::Create(width, height, is_opaque, data)); return CreateCanvas(dev, failureType); } } // namespace skia