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