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