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