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