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 14namespace skia { 15 16namespace { 17 18void 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. 30bool 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 48void 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 60void 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 73void BitmapPlatformDevice::SetMatrixClip( 74 const SkMatrix& transform, 75 const SkRegion& region) { 76 transform_ = transform; 77 clip_region_ = region; 78 config_dirty_ = true; 79} 80 81void 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. 99BitmapPlatformDevice* 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 121BitmapPlatformDevice* 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 137BitmapPlatformDevice* 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 145BitmapPlatformDevice* 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. 156BitmapPlatformDevice::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 166BitmapPlatformDevice::~BitmapPlatformDevice() { 167 cairo_destroy(cairo_); 168} 169 170SkBaseDevice* BitmapPlatformDevice::onCreateDevice(const SkImageInfo& info, 171 Usage /*usage*/) { 172 SkASSERT(info.colorType() == kN32_SkColorType); 173 return BitmapPlatformDevice::Create(info.width(), info.height(), 174 info.isOpaque()); 175} 176 177cairo_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 188void 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 194void BitmapPlatformDevice::setMatrixClip(const SkMatrix& transform, 195 const SkRegion& region, 196 const SkClipStack&) { 197 SetMatrixClip(transform, region); 198} 199 200// PlatformCanvas impl 201 202SkCanvas* 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 210PlatformBitmap::~PlatformBitmap() { 211 cairo_destroy(surface_); 212} 213 214bool 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