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