Home | History | Annotate | Download | only in ext
      1 // Copyright 2013 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/bitmap_platform_device_cairo.h"
      6 #include "skia/ext/platform_canvas.h"
      7 
      8 #if defined(OS_OPENBSD)
      9 #include <cairo.h>
     10 #else
     11 #include <cairo/cairo.h>
     12 #endif
     13 
     14 namespace skia {
     15 
     16 namespace {
     17 
     18 void CairoSurfaceReleaseProc(void*, void* context) {
     19   SkASSERT(context);
     20   cairo_surface_destroy(static_cast<cairo_surface_t*>(context));
     21 }
     22 
     23 // Back the destination bitmap by a Cairo surface.  The bitmap's
     24 // pixelRef takes ownership of the passed-in surface and will call
     25 // cairo_surface_destroy() upon destruction.
     26 //
     27 // Note: it may immediately destroy the surface, if it fails to create a bitmap
     28 // with pixels, thus the caller must either ref() the surface before hand, or
     29 // it must not refer to the surface after this call.
     30 bool InstallCairoSurfacePixels(SkBitmap* dst,
     31                                cairo_surface_t* surface,
     32                                bool is_opaque) {
     33   SkASSERT(dst);
     34   if (!surface) {
     35     return false;
     36   }
     37   SkImageInfo info
     38       = SkImageInfo::MakeN32Premul(cairo_image_surface_get_width(surface),
     39                                    cairo_image_surface_get_height(surface));
     40   return dst->installPixels(info,
     41                             cairo_image_surface_get_data(surface),
     42                             cairo_image_surface_get_stride(surface),
     43                             NULL,
     44                             &CairoSurfaceReleaseProc,
     45                             static_cast<void*>(surface));
     46 }
     47 
     48 void LoadMatrixToContext(cairo_t* context, const SkMatrix& matrix) {
     49   cairo_matrix_t cairo_matrix;
     50   cairo_matrix_init(&cairo_matrix,
     51                     SkScalarToFloat(matrix.getScaleX()),
     52                     SkScalarToFloat(matrix.getSkewY()),
     53                     SkScalarToFloat(matrix.getSkewX()),
     54                     SkScalarToFloat(matrix.getScaleY()),
     55                     SkScalarToFloat(matrix.getTranslateX()),
     56                     SkScalarToFloat(matrix.getTranslateY()));
     57   cairo_set_matrix(context, &cairo_matrix);
     58 }
     59 
     60 void LoadClipToContext(cairo_t* context, const SkRegion& clip) {
     61   cairo_reset_clip(context);
     62 
     63   // TODO(brettw) support non-rect clips.
     64   SkIRect bounding = clip.getBounds();
     65   cairo_rectangle(context, bounding.fLeft, bounding.fTop,
     66                   bounding.fRight - bounding.fLeft,
     67                   bounding.fBottom - bounding.fTop);
     68   cairo_clip(context);
     69 }
     70 
     71 }  // namespace
     72 
     73 void BitmapPlatformDevice::SetMatrixClip(
     74     const SkMatrix& transform,
     75     const SkRegion& region) {
     76   transform_ = transform;
     77   clip_region_ = region;
     78   config_dirty_ = true;
     79 }
     80 
     81 void BitmapPlatformDevice::LoadConfig() {
     82   if (!config_dirty_ || !cairo_)
     83     return;  // Nothing to do.
     84   config_dirty_ = false;
     85 
     86   // Load the identity matrix since this is what our clip is relative to.
     87   cairo_matrix_t cairo_matrix;
     88   cairo_matrix_init_identity(&cairo_matrix);
     89   cairo_set_matrix(cairo_, &cairo_matrix);
     90 
     91   LoadClipToContext(cairo_, clip_region_);
     92   LoadMatrixToContext(cairo_, transform_);
     93 }
     94 
     95 // We use this static factory function instead of the regular constructor so
     96 // that we can create the pixel data before calling the constructor. This is
     97 // required so that we can call the base class' constructor with the pixel
     98 // data.
     99 BitmapPlatformDevice* BitmapPlatformDevice::Create(int width, int height,
    100                                                    bool is_opaque,
    101                                                    cairo_surface_t* surface) {
    102   if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
    103     cairo_surface_destroy(surface);
    104     return NULL;
    105   }
    106 
    107   // must call this before trying to install the surface, since that may result
    108   // in the surface being destroyed.
    109   cairo_t* cairo = cairo_create(surface);
    110 
    111   SkBitmap bitmap;
    112   if (!InstallCairoSurfacePixels(&bitmap, surface, is_opaque)) {
    113     cairo_destroy(cairo);
    114     return NULL;
    115   }
    116 
    117   // The device object will take ownership of the graphics context.
    118   return new BitmapPlatformDevice(bitmap, cairo);
    119 }
    120 
    121 BitmapPlatformDevice* BitmapPlatformDevice::Create(int width, int height,
    122                                                    bool is_opaque) {
    123   // This initializes the bitmap to all zeros.
    124   cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
    125                                                         width, height);
    126 
    127   BitmapPlatformDevice* device = Create(width, height, is_opaque, surface);
    128 
    129 #ifndef NDEBUG
    130   if (device && is_opaque)  // Fill with bright bluish green
    131     device->eraseColor(SkColorSetARGB(255, 0, 255, 128));
    132 #endif
    133 
    134   return device;
    135 }
    136 
    137 BitmapPlatformDevice* BitmapPlatformDevice::CreateAndClear(int width,
    138                                                            int height,
    139                                                            bool is_opaque) {
    140   // The Linux port always constructs initialized bitmaps, so there is no extra
    141   // work to perform here.
    142   return Create(width, height, is_opaque);
    143 }
    144 
    145 BitmapPlatformDevice* BitmapPlatformDevice::Create(int width, int height,
    146                                                    bool is_opaque,
    147                                                    uint8_t* data) {
    148   cairo_surface_t* surface = cairo_image_surface_create_for_data(
    149       data, CAIRO_FORMAT_ARGB32, width, height,
    150       cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width));
    151 
    152   return Create(width, height, is_opaque, surface);
    153 }
    154 
    155 // Ownership of the cairo object is transferred.
    156 BitmapPlatformDevice::BitmapPlatformDevice(
    157     const SkBitmap& bitmap,
    158     cairo_t* cairo)
    159     : SkBitmapDevice(bitmap),
    160       cairo_(cairo),
    161       config_dirty_(true),
    162       transform_(SkMatrix::I()) {  // Want to load the config next time.
    163   SetPlatformDevice(this, this);
    164 }
    165 
    166 BitmapPlatformDevice::~BitmapPlatformDevice() {
    167   cairo_destroy(cairo_);
    168 }
    169 
    170 SkBaseDevice* BitmapPlatformDevice::onCreateDevice(const SkImageInfo& info,
    171                                                    Usage /*usage*/) {
    172   SkASSERT(info.colorType() == kPMColor_SkColorType);
    173   return BitmapPlatformDevice::Create(info.width(), info.height(),
    174                                       info.isOpaque());
    175 }
    176 
    177 cairo_t* BitmapPlatformDevice::BeginPlatformPaint() {
    178   LoadConfig();
    179   cairo_surface_t* surface = cairo_get_target(cairo_);
    180   // Tell cairo to flush anything it has pending.
    181   cairo_surface_flush(surface);
    182   // Tell Cairo that we (probably) modified (actually, will modify) its pixel
    183   // buffer directly.
    184   cairo_surface_mark_dirty(surface);
    185   return cairo_;
    186 }
    187 
    188 void BitmapPlatformDevice::DrawToNativeContext(
    189     PlatformSurface surface, int x, int y, const PlatformRect* src_rect) {
    190   // Should never be called on Linux.
    191   SkASSERT(false);
    192 }
    193 
    194 void BitmapPlatformDevice::setMatrixClip(const SkMatrix& transform,
    195                                          const SkRegion& region,
    196                                          const SkClipStack&) {
    197   SetMatrixClip(transform, region);
    198 }
    199 
    200 // PlatformCanvas impl
    201 
    202 SkCanvas* CreatePlatformCanvas(int width, int height, bool is_opaque,
    203                                uint8_t* data, OnFailureType failureType) {
    204   skia::RefPtr<SkBaseDevice> dev = skia::AdoptRef(
    205       BitmapPlatformDevice::Create(width, height, is_opaque, data));
    206   return CreateCanvas(dev, failureType);
    207 }
    208 
    209 // Port of PlatformBitmap to linux
    210 PlatformBitmap::~PlatformBitmap() {
    211   cairo_destroy(surface_);
    212 }
    213 
    214 bool PlatformBitmap::Allocate(int width, int height, bool is_opaque) {
    215   // The SkBitmap allocates and owns the bitmap memory; PlatformBitmap owns the
    216   // cairo drawing context tied to the bitmap. The SkBitmap's pixelRef can
    217   // outlive the PlatformBitmap if additional copies are made.
    218   int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
    219 
    220   cairo_surface_t* surf = cairo_image_surface_create(
    221       CAIRO_FORMAT_ARGB32,
    222       width,
    223       height);
    224   if (cairo_surface_status(surf) != CAIRO_STATUS_SUCCESS) {
    225     cairo_surface_destroy(surf);
    226     return false;
    227   }
    228   return InstallCairoSurfacePixels(&bitmap_, surf, is_opaque);
    229 }
    230 
    231 }  // namespace skia
    232