| // 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 "skia/ext/bitmap_platform_device_cairo.h" |
| #include "skia/ext/platform_canvas.h" |
| |
| #if defined(OS_OPENBSD) |
| #include <cairo.h> |
| #else |
| #include <cairo/cairo.h> |
| #endif |
| |
| namespace skia { |
| |
| namespace { |
| |
| void CairoSurfaceReleaseProc(void*, void* context) { |
| SkASSERT(context); |
| cairo_surface_destroy(static_cast<cairo_surface_t*>(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::MakeN32Premul(cairo_image_surface_get_width(surface), |
| cairo_image_surface_get_height(surface)); |
| return dst->installPixels(info, |
| cairo_image_surface_get_data(surface), |
| cairo_image_surface_get_stride(surface), |
| &CairoSurfaceReleaseProc, |
| static_cast<void*>(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 |
| device->eraseColor(SkColorSetARGB(255, 0, 255, 128)); |
| #endif |
| |
| return device; |
| } |
| |
| BitmapPlatformDevice* BitmapPlatformDevice::CreateAndClear(int width, |
| int height, |
| bool is_opaque) { |
| // The Linux port always constructs initialized bitmaps, so there is no extra |
| // work to perform here. |
| return Create(width, height, is_opaque); |
| } |
| |
| 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 SkImageInfo& info, |
| Usage /*usage*/) { |
| SkASSERT(info.colorType() == kPMColor_SkColorType); |
| return BitmapPlatformDevice::Create(info.width(), info.height(), |
| info.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::DrawToNativeContext( |
| PlatformSurface surface, int x, int y, const PlatformRect* src_rect) { |
| // Should never be called on Linux. |
| SkASSERT(false); |
| } |
| |
| 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<SkBaseDevice> dev = skia::AdoptRef( |
| BitmapPlatformDevice::Create(width, height, is_opaque, data)); |
| return CreateCanvas(dev, failureType); |
| } |
| |
| // Port of PlatformBitmap to linux |
| PlatformBitmap::~PlatformBitmap() { |
| cairo_destroy(surface_); |
| } |
| |
| bool PlatformBitmap::Allocate(int width, int height, bool is_opaque) { |
| // The SkBitmap allocates and owns the bitmap memory; PlatformBitmap owns the |
| // cairo drawing context tied to the bitmap. The SkBitmap's pixelRef can |
| // outlive the PlatformBitmap if additional copies are made. |
| int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); |
| |
| cairo_surface_t* surf = cairo_image_surface_create( |
| CAIRO_FORMAT_ARGB32, |
| width, |
| height); |
| if (cairo_surface_status(surf) != CAIRO_STATUS_SUCCESS) { |
| cairo_surface_destroy(surf); |
| return false; |
| } |
| return InstallCairoSurfacePixels(&bitmap_, surf, is_opaque); |
| } |
| |
| } // namespace skia |