blob: ccd8d273aab5450aff83eb11b48e200bbc4a9f46 [file] [log] [blame]
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "skia/ext/bitmap_platform_device_linux.h"
#include <cairo/cairo.h>
namespace skia {
namespace {
void LoadMatrixToContext(cairo_t* context, const SkMatrix& matrix) {
cairo_matrix_t cairo_matrix;
cairo_matrix_init(&cairo_matrix,
SkScalarToFloat(matrix.getScaleX()),
SkScalarToFloat(matrix.getSkewY()),
SkScalarToFloat(matrix.getSkewX()),
SkScalarToFloat(matrix.getScaleY()),
SkScalarToFloat(matrix.getTranslateX()),
SkScalarToFloat(matrix.getTranslateY()));
cairo_set_matrix(context, &cairo_matrix);
}
void LoadClipToContext(cairo_t* context, const SkRegion& clip) {
cairo_reset_clip(context);
// TODO(brettw) support non-rect clips.
SkIRect bounding = clip.getBounds();
cairo_rectangle(context, bounding.fLeft, bounding.fTop,
bounding.fRight - bounding.fLeft,
bounding.fBottom - bounding.fTop);
cairo_clip(context);
}
} // namespace
// -----------------------------------------------------------------------------
// These objects are reference counted and own a Cairo surface. The surface is
// the backing store for a Skia bitmap and we reference count it so that we can
// copy BitmapPlatformDevice objects without having to copy all the image
// data.
// -----------------------------------------------------------------------------
class BitmapPlatformDevice::BitmapPlatformDeviceData
: public base::RefCounted<BitmapPlatformDeviceData> {
public:
explicit BitmapPlatformDeviceData(cairo_surface_t* surface);
cairo_t* GetContext();
cairo_surface_t* GetSurface();
// Sets the transform and clip operations. This will not update the Cairo
// surface, but will mark the config as dirty. The next call of LoadConfig
// will pick up these changes.
void SetMatrixClip(const SkMatrix& transform, const SkRegion& region);
protected:
void LoadConfig();
// The Cairo surface inside this DC.
cairo_t* context_;
cairo_surface_t *const surface_;
// True when there is a transform or clip that has not been set to the
// surface. The surface is retrieved for every text operation, and the
// transform and clip do not change as much. We can save time by not loading
// the clip and transform for every one.
bool config_dirty_;
// Translation assigned to the DC: we need to keep track of this separately
// so it can be updated even if the DC isn't created yet.
SkMatrix transform_;
// The current clipping
SkRegion clip_region_;
// Disallow copy & assign.
BitmapPlatformDeviceData(const BitmapPlatformDeviceData&);
BitmapPlatformDeviceData& operator=(
const BitmapPlatformDeviceData&);
private:
friend class base::RefCounted<BitmapPlatformDeviceData>;
~BitmapPlatformDeviceData();
};
BitmapPlatformDevice::BitmapPlatformDeviceData::BitmapPlatformDeviceData(
cairo_surface_t* surface)
: surface_(surface),
config_dirty_(true) { // Want to load the config next time.
context_ = cairo_create(surface);
}
BitmapPlatformDevice::BitmapPlatformDeviceData::~BitmapPlatformDeviceData() {
cairo_destroy(context_);
cairo_surface_destroy(surface_);
}
cairo_t* BitmapPlatformDevice::BitmapPlatformDeviceData::GetContext() {
LoadConfig();
return context_;
}
void BitmapPlatformDevice::BitmapPlatformDeviceData::SetMatrixClip(
const SkMatrix& transform,
const SkRegion& region) {
transform_ = transform;
clip_region_ = region;
config_dirty_ = true;
}
cairo_surface_t*
BitmapPlatformDevice::BitmapPlatformDeviceData::GetSurface() {
// TODO(brettw) this function should be removed.
LoadConfig();
return surface_;
}
void BitmapPlatformDevice::BitmapPlatformDeviceData::LoadConfig() {
if (!config_dirty_ || !context_)
return; // Nothing to do.
config_dirty_ = false;
// Load the identity matrix since this is what our clip is relative to.
cairo_matrix_t cairo_matrix;
cairo_matrix_init_identity(&cairo_matrix);
cairo_set_matrix(context_, &cairo_matrix);
LoadClipToContext(context_, clip_region_);
LoadMatrixToContext(context_, transform_);
}
// We use this static factory function instead of the regular constructor so
// that we can create the pixel data before calling the constructor. This is
// required so that we can call the base class' constructor with the pixel
// data.
BitmapPlatformDevice* BitmapPlatformDevice::Create(int width, int height,
bool is_opaque,
cairo_surface_t* surface) {
SkBitmap bitmap;
bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height,
cairo_image_surface_get_stride(surface));
bitmap.setPixels(cairo_image_surface_get_data(surface));
bitmap.setIsOpaque(is_opaque);
// The device object will take ownership of the graphics context.
return new BitmapPlatformDevice
(bitmap, new BitmapPlatformDeviceData(surface));
}
BitmapPlatformDevice* BitmapPlatformDevice::Create(int width, int height,
bool is_opaque) {
cairo_surface_t* surface =
cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
width, height);
BitmapPlatformDevice* device = Create(width, height, is_opaque, surface);
#ifndef NDEBUG
if (is_opaque) // Fill with bright bluish green
device->eraseColor(SkColorSetARGB(255, 0, 255, 128));
#endif
return device;
}
BitmapPlatformDevice* BitmapPlatformDevice::Create(int width, int height,
bool is_opaque,
uint8_t* data) {
cairo_surface_t* surface = cairo_image_surface_create_for_data(
data, CAIRO_FORMAT_ARGB32, width, height,
cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width));
return Create(width, height, is_opaque, surface);
}
// The device will own the bitmap, which corresponds to also owning the pixel
// data. Therefore, we do not transfer ownership to the SkDevice's bitmap.
BitmapPlatformDevice::BitmapPlatformDevice(
const SkBitmap& bitmap,
BitmapPlatformDeviceData* data)
: PlatformDevice(bitmap),
data_(data) {
}
BitmapPlatformDevice::BitmapPlatformDevice(
const BitmapPlatformDevice& other)
: PlatformDevice(const_cast<BitmapPlatformDevice&>(
other).accessBitmap(true)),
data_(other.data_) {
}
BitmapPlatformDevice::~BitmapPlatformDevice() {
}
cairo_t* BitmapPlatformDevice::beginPlatformPaint() {
return data_->GetContext();
}
void BitmapPlatformDevice::setMatrixClip(const SkMatrix& transform,
const SkRegion& region) {
data_->SetMatrixClip(transform, region);
}
BitmapPlatformDevice& BitmapPlatformDevice::operator=(
const BitmapPlatformDevice& other) {
data_ = other.data_;
return *this;
}
} // namespace skia