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 // CairoSurfacePixelRef is an SkPixelRef that is backed by a cairo surface.
     19 class SK_API CairoSurfacePixelRef : public SkPixelRef {
     20  public:
     21   // The constructor takes ownership of the passed-in surface.
     22   explicit CairoSurfacePixelRef(const SkImageInfo& info,
     23                                 cairo_surface_t* surface);
     24   virtual ~CairoSurfacePixelRef();
     25 
     26   SK_DECLARE_UNFLATTENABLE_OBJECT();
     27 
     28  protected:
     29   virtual void* onLockPixels(SkColorTable**) SK_OVERRIDE;
     30   virtual void onUnlockPixels() SK_OVERRIDE;
     31 
     32  private:
     33   cairo_surface_t* surface_;
     34 };
     35 
     36 CairoSurfacePixelRef::CairoSurfacePixelRef(const SkImageInfo& info,
     37                                            cairo_surface_t* surface)
     38     : SkPixelRef(info), surface_(surface) {
     39 }
     40 
     41 CairoSurfacePixelRef::~CairoSurfacePixelRef() {
     42   if (surface_)
     43     cairo_surface_destroy(surface_);
     44 }
     45 
     46 void* CairoSurfacePixelRef::onLockPixels(SkColorTable** color_table) {
     47   *color_table = NULL;
     48   return cairo_image_surface_get_data(surface_);
     49 }
     50 
     51 void CairoSurfacePixelRef::onUnlockPixels() {
     52   // Nothing to do.
     53   return;
     54 }
     55 
     56 void LoadMatrixToContext(cairo_t* context, const SkMatrix& matrix) {
     57   cairo_matrix_t cairo_matrix;
     58   cairo_matrix_init(&cairo_matrix,
     59                     SkScalarToFloat(matrix.getScaleX()),
     60                     SkScalarToFloat(matrix.getSkewY()),
     61                     SkScalarToFloat(matrix.getSkewX()),
     62                     SkScalarToFloat(matrix.getScaleY()),
     63                     SkScalarToFloat(matrix.getTranslateX()),
     64                     SkScalarToFloat(matrix.getTranslateY()));
     65   cairo_set_matrix(context, &cairo_matrix);
     66 }
     67 
     68 void LoadClipToContext(cairo_t* context, const SkRegion& clip) {
     69   cairo_reset_clip(context);
     70 
     71   // TODO(brettw) support non-rect clips.
     72   SkIRect bounding = clip.getBounds();
     73   cairo_rectangle(context, bounding.fLeft, bounding.fTop,
     74                   bounding.fRight - bounding.fLeft,
     75                   bounding.fBottom - bounding.fTop);
     76   cairo_clip(context);
     77 }
     78 
     79 }  // namespace
     80 
     81 void BitmapPlatformDevice::SetMatrixClip(
     82     const SkMatrix& transform,
     83     const SkRegion& region) {
     84   transform_ = transform;
     85   clip_region_ = region;
     86   config_dirty_ = true;
     87 }
     88 
     89 void BitmapPlatformDevice::LoadConfig() {
     90   if (!config_dirty_ || !cairo_)
     91     return;  // Nothing to do.
     92   config_dirty_ = false;
     93 
     94   // Load the identity matrix since this is what our clip is relative to.
     95   cairo_matrix_t cairo_matrix;
     96   cairo_matrix_init_identity(&cairo_matrix);
     97   cairo_set_matrix(cairo_, &cairo_matrix);
     98 
     99   LoadClipToContext(cairo_, clip_region_);
    100   LoadMatrixToContext(cairo_, transform_);
    101 }
    102 
    103 // We use this static factory function instead of the regular constructor so
    104 // that we can create the pixel data before calling the constructor. This is
    105 // required so that we can call the base class' constructor with the pixel
    106 // data.
    107 BitmapPlatformDevice* BitmapPlatformDevice::Create(int width, int height,
    108                                                    bool is_opaque,
    109                                                    cairo_surface_t* surface) {
    110   if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
    111     cairo_surface_destroy(surface);
    112     return NULL;
    113   }
    114 
    115   SkImageInfo info = {
    116     width,
    117     height,
    118     kPMColor_SkColorType,
    119     is_opaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType
    120   };
    121 
    122   SkBitmap bitmap;
    123   bitmap.setConfig(info, cairo_image_surface_get_stride(surface));
    124   RefPtr<SkPixelRef> pixel_ref = AdoptRef(new CairoSurfacePixelRef(info,
    125                                                                    surface));
    126   bitmap.setPixelRef(pixel_ref.get());
    127 
    128   // The device object will take ownership of the graphics context.
    129   return new BitmapPlatformDevice(bitmap, surface);
    130 }
    131 
    132 BitmapPlatformDevice* BitmapPlatformDevice::Create(int width, int height,
    133                                                    bool is_opaque) {
    134   // This initializes the bitmap to all zeros.
    135   cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
    136                                                         width, height);
    137 
    138   BitmapPlatformDevice* device = Create(width, height, is_opaque, surface);
    139 
    140 #ifndef NDEBUG
    141   if (device && is_opaque)  // Fill with bright bluish green
    142     device->eraseColor(SkColorSetARGB(255, 0, 255, 128));
    143 #endif
    144 
    145   return device;
    146 }
    147 
    148 BitmapPlatformDevice* BitmapPlatformDevice::CreateAndClear(int width,
    149                                                            int height,
    150                                                            bool is_opaque) {
    151   // The Linux port always constructs initialized bitmaps, so there is no extra
    152   // work to perform here.
    153   return Create(width, height, is_opaque);
    154 }
    155 
    156 BitmapPlatformDevice* BitmapPlatformDevice::Create(int width, int height,
    157                                                    bool is_opaque,
    158                                                    uint8_t* data) {
    159   cairo_surface_t* surface = cairo_image_surface_create_for_data(
    160       data, CAIRO_FORMAT_ARGB32, width, height,
    161       cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width));
    162 
    163   return Create(width, height, is_opaque, surface);
    164 }
    165 
    166 // The device will own the bitmap, which corresponds to also owning the pixel
    167 // data. Therefore, we do not transfer ownership to the SkBitmapDevice's bitmap.
    168 BitmapPlatformDevice::BitmapPlatformDevice(
    169     const SkBitmap& bitmap,
    170     cairo_surface_t* surface)
    171     : SkBitmapDevice(bitmap),
    172       cairo_(cairo_create(surface)),
    173       config_dirty_(true),
    174       transform_(SkMatrix::I()) {  // Want to load the config next time.
    175   SetPlatformDevice(this, this);
    176 }
    177 
    178 BitmapPlatformDevice::~BitmapPlatformDevice() {
    179   cairo_destroy(cairo_);
    180 }
    181 
    182 SkBaseDevice* BitmapPlatformDevice::onCreateCompatibleDevice(
    183     SkBitmap::Config config, int width, int height, bool isOpaque,
    184     Usage /*usage*/) {
    185   SkASSERT(config == SkBitmap::kARGB_8888_Config);
    186   return BitmapPlatformDevice::Create(width, height, isOpaque);
    187 }
    188 
    189 cairo_t* BitmapPlatformDevice::BeginPlatformPaint() {
    190   LoadConfig();
    191   cairo_surface_t* surface = cairo_get_target(cairo_);
    192   // Tell cairo to flush anything it has pending.
    193   cairo_surface_flush(surface);
    194   // Tell Cairo that we (probably) modified (actually, will modify) its pixel
    195   // buffer directly.
    196   cairo_surface_mark_dirty(surface);
    197   return cairo_;
    198 }
    199 
    200 void BitmapPlatformDevice::DrawToNativeContext(
    201     PlatformSurface surface, int x, int y, const PlatformRect* src_rect) {
    202   // Should never be called on Linux.
    203   SkASSERT(false);
    204 }
    205 
    206 void BitmapPlatformDevice::setMatrixClip(const SkMatrix& transform,
    207                                          const SkRegion& region,
    208                                          const SkClipStack&) {
    209   SetMatrixClip(transform, region);
    210 }
    211 
    212 // PlatformCanvas impl
    213 
    214 SkCanvas* CreatePlatformCanvas(int width, int height, bool is_opaque,
    215                                uint8_t* data, OnFailureType failureType) {
    216   skia::RefPtr<SkBaseDevice> dev = skia::AdoptRef(
    217       BitmapPlatformDevice::Create(width, height, is_opaque, data));
    218   return CreateCanvas(dev, failureType);
    219 }
    220 
    221 // Port of PlatformBitmap to linux
    222 PlatformBitmap::~PlatformBitmap() {
    223   cairo_destroy(surface_);
    224 }
    225 
    226 bool PlatformBitmap::Allocate(int width, int height, bool is_opaque) {
    227   SkImageInfo info = {
    228     width,
    229     height,
    230     kPMColor_SkColorType,
    231     is_opaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType
    232   };
    233 
    234   // The SkBitmap allocates and owns the bitmap memory; PlatformBitmap owns the
    235   // cairo drawing context tied to the bitmap. The SkBitmap's pixelRef can
    236   // outlive the PlatformBitmap if additional copies are made.
    237   int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
    238   bitmap_.setConfig(info, stride);
    239 
    240   cairo_surface_t* surf = cairo_image_surface_create(
    241       CAIRO_FORMAT_ARGB32,
    242       width,
    243       height);
    244   if (cairo_surface_status(surf) != CAIRO_STATUS_SUCCESS) {
    245     cairo_surface_destroy(surf);
    246     return false;
    247   }
    248   RefPtr<SkPixelRef> pixel_ref = AdoptRef(new CairoSurfacePixelRef(info, surf));
    249   bitmap_.setPixelRef(pixel_ref.get());
    250   return true;
    251 }
    252 
    253 }  // namespace skia
    254