blob: 3619eb252771e9f80e2c2f02756047adad44593f [file] [log] [blame]
// Copyright 2013 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_cairo.h"
#include "skia/ext/platform_canvas.h"
#if defined(OS_OPENBSD)
#include <cairo.h>
#else
#include <cairo/cairo.h>
#endif
namespace skia {
namespace {
// CairoSurfacePixelRef is an SkPixelRef that is backed by a cairo surface.
class SK_API CairoSurfacePixelRef : public SkPixelRef {
public:
// The constructor takes ownership of the passed-in surface.
explicit CairoSurfacePixelRef(const SkImageInfo& info,
cairo_surface_t* surface);
virtual ~CairoSurfacePixelRef();
SK_DECLARE_UNFLATTENABLE_OBJECT();
protected:
virtual void* onLockPixels(SkColorTable**) SK_OVERRIDE;
virtual void onUnlockPixels() SK_OVERRIDE;
private:
cairo_surface_t* surface_;
};
CairoSurfacePixelRef::CairoSurfacePixelRef(const SkImageInfo& info,
cairo_surface_t* surface)
: SkPixelRef(info), surface_(surface) {
}
CairoSurfacePixelRef::~CairoSurfacePixelRef() {
if (surface_)
cairo_surface_destroy(surface_);
}
void* CairoSurfacePixelRef::onLockPixels(SkColorTable** color_table) {
*color_table = NULL;
return cairo_image_surface_get_data(surface_);
}
void CairoSurfacePixelRef::onUnlockPixels() {
// Nothing to do.
return;
}
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
void BitmapPlatformDevice::SetMatrixClip(
const SkMatrix& transform,
const SkRegion& region) {
transform_ = transform;
clip_region_ = region;
config_dirty_ = true;
}
void BitmapPlatformDevice::LoadConfig() {
if (!config_dirty_ || !cairo_)
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(cairo_, &cairo_matrix);
LoadClipToContext(cairo_, clip_region_);
LoadMatrixToContext(cairo_, 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) {
if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
cairo_surface_destroy(surface);
return NULL;
}
SkImageInfo info = {
width,
height,
kPMColor_SkColorType,
is_opaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType
};
SkBitmap bitmap;
bitmap.setConfig(info, cairo_image_surface_get_stride(surface));
RefPtr<SkPixelRef> pixel_ref = AdoptRef(new CairoSurfacePixelRef(info,
surface));
bitmap.setPixelRef(pixel_ref.get());
// The device object will take ownership of the graphics context.
return new BitmapPlatformDevice(bitmap, surface);
}
BitmapPlatformDevice* BitmapPlatformDevice::Create(int width, int height,
bool is_opaque) {
// This initializes the bitmap to all zeros.
cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
width, height);
BitmapPlatformDevice* device = Create(width, height, is_opaque, surface);
#ifndef NDEBUG
if (device && is_opaque) // Fill with bright bluish green
device->eraseColor(SkColorSetARGB(255, 0, 255, 128));
#endif
return device;
}
BitmapPlatformDevice* BitmapPlatformDevice::CreateAndClear(int width,
int height,
bool is_opaque) {
// The Linux port always constructs initialized bitmaps, so there is no extra
// work to perform here.
return Create(width, height, is_opaque);
}
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 SkBitmapDevice's bitmap.
BitmapPlatformDevice::BitmapPlatformDevice(
const SkBitmap& bitmap,
cairo_surface_t* surface)
: SkBitmapDevice(bitmap),
cairo_(cairo_create(surface)),
config_dirty_(true),
transform_(SkMatrix::I()) { // Want to load the config next time.
SetPlatformDevice(this, this);
}
BitmapPlatformDevice::~BitmapPlatformDevice() {
cairo_destroy(cairo_);
}
SkBaseDevice* BitmapPlatformDevice::onCreateCompatibleDevice(
SkBitmap::Config config, int width, int height, bool isOpaque,
Usage /*usage*/) {
SkASSERT(config == SkBitmap::kARGB_8888_Config);
return BitmapPlatformDevice::Create(width, height, isOpaque);
}
cairo_t* BitmapPlatformDevice::BeginPlatformPaint() {
LoadConfig();
cairo_surface_t* surface = cairo_get_target(cairo_);
// Tell cairo to flush anything it has pending.
cairo_surface_flush(surface);
// Tell Cairo that we (probably) modified (actually, will modify) its pixel
// buffer directly.
cairo_surface_mark_dirty(surface);
return cairo_;
}
void BitmapPlatformDevice::DrawToNativeContext(
PlatformSurface surface, int x, int y, const PlatformRect* src_rect) {
// Should never be called on Linux.
SkASSERT(false);
}
void BitmapPlatformDevice::setMatrixClip(const SkMatrix& transform,
const SkRegion& region,
const SkClipStack&) {
SetMatrixClip(transform, region);
}
// PlatformCanvas impl
SkCanvas* CreatePlatformCanvas(int width, int height, bool is_opaque,
uint8_t* data, OnFailureType failureType) {
skia::RefPtr<SkBaseDevice> dev = skia::AdoptRef(
BitmapPlatformDevice::Create(width, height, is_opaque, data));
return CreateCanvas(dev, failureType);
}
// Port of PlatformBitmap to linux
PlatformBitmap::~PlatformBitmap() {
cairo_destroy(surface_);
}
bool PlatformBitmap::Allocate(int width, int height, bool is_opaque) {
SkImageInfo info = {
width,
height,
kPMColor_SkColorType,
is_opaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType
};
// The SkBitmap allocates and owns the bitmap memory; PlatformBitmap owns the
// cairo drawing context tied to the bitmap. The SkBitmap's pixelRef can
// outlive the PlatformBitmap if additional copies are made.
int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
bitmap_.setConfig(info, stride);
cairo_surface_t* surf = cairo_image_surface_create(
CAIRO_FORMAT_ARGB32,
width,
height);
if (cairo_surface_status(surf) != CAIRO_STATUS_SUCCESS) {
cairo_surface_destroy(surf);
return false;
}
RefPtr<SkPixelRef> pixel_ref = AdoptRef(new CairoSurfacePixelRef(info, surf));
bitmap_.setPixelRef(pixel_ref.get());
return true;
}
} // namespace skia