forked from KolibriOS/kolibrios
985 lines
27 KiB
C++
985 lines
27 KiB
C++
|
/* vim:set ts=8 sw=4 noet cin: */
|
||
|
/* cairo - a vector graphics library with display and print output
|
||
|
*
|
||
|
* Copyright © 2005 Christian Biesinger <cbiesinger@web.de>
|
||
|
*
|
||
|
* This library is free software; you can redistribute it and/or
|
||
|
* modify it either under the terms of the GNU Lesser General Public
|
||
|
* License version 2.1 as published by the Free Software Foundation
|
||
|
* (the "LGPL") or, at your option, under the terms of the Mozilla
|
||
|
* Public License Version 1.1 (the "MPL"). If you do not alter this
|
||
|
* notice, a recipient may use your version of this file under either
|
||
|
* the MPL or the LGPL.
|
||
|
*
|
||
|
* You should have received a copy of the LGPL along with this library
|
||
|
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
|
||
|
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
|
||
|
* You should have received a copy of the MPL along with this library
|
||
|
* in the file COPYING-MPL-1.1
|
||
|
*
|
||
|
* The contents of this file are subject to the Mozilla Public License
|
||
|
* Version 1.1 (the "License"); you may not use this file except in
|
||
|
* compliance with the License. You may obtain a copy of the License at
|
||
|
* http://www.mozilla.org/MPL/
|
||
|
*
|
||
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
|
||
|
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
|
||
|
* the specific language governing rights and limitations.
|
||
|
*
|
||
|
* The Original Code is the cairo graphics library.
|
||
|
*
|
||
|
* The Initial Developer of the Original Code is Christian Biesinger
|
||
|
* <cbiesinger@web.de>
|
||
|
*
|
||
|
* Contributor(s):
|
||
|
*/
|
||
|
|
||
|
// This is a C++ file in order to use the C++ BeOS API
|
||
|
|
||
|
#include "cairoint.h"
|
||
|
|
||
|
#include "cairo-beos.h"
|
||
|
|
||
|
#include "cairo-error-private.h"
|
||
|
#include "cairo-image-surface-inline.h"
|
||
|
|
||
|
#include <new>
|
||
|
|
||
|
#include <Bitmap.h>
|
||
|
#include <Region.h>
|
||
|
#if 0
|
||
|
#include <DirectWindow.h>
|
||
|
#endif
|
||
|
#include <Screen.h>
|
||
|
#include <Window.h>
|
||
|
#include <Locker.h>
|
||
|
|
||
|
/**
|
||
|
* SECTION:beos-surface
|
||
|
* @Title: BeOS Surfaces
|
||
|
* @Short_Description: BeOS surface support
|
||
|
* @See_Also: #cairo_surface_t
|
||
|
*
|
||
|
* The BeOS surface is used to render cairo graphics to BeOS views
|
||
|
* and bitmaps.
|
||
|
**/
|
||
|
|
||
|
#define CAIRO_INT_STATUS_SUCCESS (cairo_int_status_t)(CAIRO_STATUS_SUCCESS)
|
||
|
|
||
|
struct cairo_beos_surface_t {
|
||
|
cairo_surface_t base;
|
||
|
|
||
|
cairo_region_t *clip_region;
|
||
|
|
||
|
BView* view;
|
||
|
|
||
|
/*
|
||
|
* A view is either attached to a bitmap, a window, or unattached.
|
||
|
* If it is attached to a window, we can copy data out of it using BScreen.
|
||
|
* If it is attached to a bitmap, we can read the bitmap data.
|
||
|
* If it is not attached, it doesn't draw anything, we need not bother.
|
||
|
*
|
||
|
* Since there doesn't seem to be a way to get the bitmap from a view if it
|
||
|
* is attached to one, we have to use a special surface creation function.
|
||
|
*/
|
||
|
|
||
|
BBitmap* bitmap;
|
||
|
|
||
|
// If true, surface and view should be deleted when this surface is
|
||
|
// destroyed
|
||
|
bool owns_bitmap_view;
|
||
|
};
|
||
|
|
||
|
class AutoLockView {
|
||
|
public:
|
||
|
AutoLockView(BView* view) : mView(view) {
|
||
|
mOK = mView->LockLooper();
|
||
|
}
|
||
|
|
||
|
~AutoLockView() {
|
||
|
if (mOK)
|
||
|
mView->UnlockLooper();
|
||
|
}
|
||
|
|
||
|
operator bool() {
|
||
|
return mOK;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
BView* mView;
|
||
|
bool mOK;
|
||
|
};
|
||
|
|
||
|
static cairo_surface_t *
|
||
|
_cairo_beos_surface_create_internal (BView* view,
|
||
|
BBitmap* bmp,
|
||
|
bool owns_bitmap_view = false);
|
||
|
|
||
|
static inline BRect
|
||
|
_cairo_rectangle_to_brect (const cairo_rectangle_int_t* rect)
|
||
|
{
|
||
|
// A BRect is one pixel wider than you'd think
|
||
|
return BRect (rect->x, rect->y,
|
||
|
rect->x + rect->width - 1,
|
||
|
rect->y + rect->height - 1);
|
||
|
}
|
||
|
|
||
|
static inline cairo_rectangle_int_t
|
||
|
_brect_to_cairo_rectangle (const BRect &rect)
|
||
|
{
|
||
|
cairo_rectangle_int_t retval;
|
||
|
retval.x = floor (rect.left);
|
||
|
retval.y = floor (rect.top);
|
||
|
retval.width = ceil (rect.right) - retval.x + 1;
|
||
|
retval.height = ceil (rect.bottom) - rectval.y + 1;
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
static inline rgb_color
|
||
|
_cairo_color_to_be_color (const cairo_color_t *color)
|
||
|
{
|
||
|
// This factor ensures a uniform distribution of numbers
|
||
|
const float factor = 256 - 1e-5;
|
||
|
// Using doubles to have non-premultiplied colors
|
||
|
rgb_color be_color = { uint8(color->red * factor),
|
||
|
uint8(color->green * factor),
|
||
|
uint8(color->blue * factor),
|
||
|
uint8(color->alpha * factor) };
|
||
|
|
||
|
return be_color;
|
||
|
}
|
||
|
|
||
|
enum ViewCopyStatus {
|
||
|
OK,
|
||
|
NOT_VISIBLE, // The view or the interest rect is not visible on screen
|
||
|
ERROR // The view was visible, but the rect could not be copied. Probably OOM
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* _cairo_beos_view_to_bitmap:
|
||
|
* @bitmap: [out] The resulting bitmap.
|
||
|
* @rect: [out] The rectangle that was copied, in the view's coordinate system
|
||
|
* @interestRect: If non-null, only this part of the view will be copied (view's coord system).
|
||
|
*
|
||
|
* Gets the contents of the view as a BBitmap*. Caller must delete the bitmap.
|
||
|
**/
|
||
|
static ViewCopyStatus
|
||
|
_cairo_beos_view_to_bitmap (BView* view,
|
||
|
BBitmap** bitmap,
|
||
|
BRect* rect = NULL,
|
||
|
const BRect* interestRect = NULL)
|
||
|
{
|
||
|
*bitmap = NULL;
|
||
|
|
||
|
BWindow* wnd = view->Window();
|
||
|
// If we have no window, can't do anything
|
||
|
if (!wnd)
|
||
|
return NOT_VISIBLE;
|
||
|
|
||
|
view->Sync();
|
||
|
wnd->Sync();
|
||
|
|
||
|
#if 0
|
||
|
// Is it a direct window?
|
||
|
BDirectWindow* directWnd = dynamic_cast<BDirectWindow*>(wnd);
|
||
|
if (directWnd) {
|
||
|
// WRITEME
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// Is it visible? If so, we can copy the content off the screen
|
||
|
if (wnd->IsHidden())
|
||
|
return NOT_VISIBLE;
|
||
|
|
||
|
BRect rectToCopy(view->Bounds());
|
||
|
if (interestRect)
|
||
|
rectToCopy = rectToCopy & *interestRect;
|
||
|
|
||
|
if (!rectToCopy.IsValid())
|
||
|
return NOT_VISIBLE;
|
||
|
|
||
|
BScreen screen(wnd);
|
||
|
BRect screenRect(view->ConvertToScreen(rectToCopy));
|
||
|
screenRect = screenRect & screen.Frame();
|
||
|
|
||
|
if (!screen.IsValid())
|
||
|
return NOT_VISIBLE;
|
||
|
|
||
|
if (rect)
|
||
|
*rect = view->ConvertFromScreen(screenRect);
|
||
|
|
||
|
if (screen.GetBitmap(bitmap, false, &screenRect) == B_OK)
|
||
|
return OK;
|
||
|
|
||
|
return ERROR;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
unpremultiply_bgra (unsigned char* data,
|
||
|
int width,
|
||
|
int height,
|
||
|
int stride,
|
||
|
unsigned char* retdata)
|
||
|
{
|
||
|
unsigned char* end = data + stride * height;
|
||
|
for (unsigned char* in = data, *out = retdata;
|
||
|
in < end;
|
||
|
in += stride, out += stride)
|
||
|
{
|
||
|
for (int i = 0; i < width; i ++) {
|
||
|
uint8_t *b = &out[4*i];
|
||
|
uint32_t pixel;
|
||
|
uint8_t alpha;
|
||
|
|
||
|
memcpy (&pixel, &data[4*i], sizeof (uint32_t));
|
||
|
alpha = pixel & 0xff;
|
||
|
if (alpha == 0) {
|
||
|
b[0] = b[1] = b[2] = b[3] = 0;
|
||
|
} else {
|
||
|
b[0] = (((pixel >> 24) & 0xff) * 255 + alpha / 2) / alpha;
|
||
|
b[1] = (((pixel >> 16) & 0xff) * 255 + alpha / 2) / alpha;
|
||
|
b[2] = (((pixel >> 8) & 0xff) * 255 + alpha / 2) / alpha;
|
||
|
b[3] = alpha;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static inline int
|
||
|
multiply_alpha (int alpha, int color)
|
||
|
{
|
||
|
int temp = (alpha * color) + 0x80;
|
||
|
return ((temp + (temp >> 8)) >> 8);
|
||
|
}
|
||
|
|
||
|
static unsigned char*
|
||
|
premultiply_bgra (unsigned char* data,
|
||
|
int width,
|
||
|
int height,
|
||
|
int stride)
|
||
|
{
|
||
|
uint8_t * retdata = reinterpret_cast<unsigned char*>(_cairo_malloc_ab(height, stride));
|
||
|
if (!retdata)
|
||
|
return NULL;
|
||
|
|
||
|
uint8_t * end = data + stride * height;
|
||
|
for (uint8_t * in = data, *out = retdata;
|
||
|
in < end;
|
||
|
in += stride, out += stride)
|
||
|
{
|
||
|
for (int i = 0; i < width; i ++) {
|
||
|
uint8_t *base = &in[4*i];
|
||
|
uint8_t alpha = base[3];
|
||
|
uint32_t p;
|
||
|
|
||
|
if (alpha == 0) {
|
||
|
p = 0;
|
||
|
} else {
|
||
|
uint8_t blue = base[0];
|
||
|
uint8_t green = base[1];
|
||
|
uint8_t red = base[2];
|
||
|
|
||
|
if (alpha != 0xff) {
|
||
|
blue = multiply_alpha (alpha, blue);
|
||
|
green = multiply_alpha (alpha, green);
|
||
|
red = multiply_alpha (alpha, red);
|
||
|
}
|
||
|
p = (alpha << 0) | (red << 8) | (green << 16) | (blue << 24);
|
||
|
}
|
||
|
memcpy (&out[4*i], &p, sizeof (uint32_t));
|
||
|
}
|
||
|
}
|
||
|
return retdata;
|
||
|
}
|
||
|
|
||
|
static cairo_int_status_t
|
||
|
_cairo_beos_surface_set_clip_region (cairo_beos_surface_t *surface,
|
||
|
cairo_region_t *region)
|
||
|
{
|
||
|
cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>(
|
||
|
abstract_surface);
|
||
|
AutoLockView locker(surface->view);
|
||
|
assert (locker);
|
||
|
|
||
|
if (region == surface->clip_region)
|
||
|
return CAIRO_INT_STATUS_SUCCESS;
|
||
|
|
||
|
cairo_region_destroy (surface->clip_region);
|
||
|
surface->clip_region = cairo_region_reference (region);
|
||
|
|
||
|
if (region == NULL) {
|
||
|
// No clipping
|
||
|
surface->view->ConstrainClippingRegion(NULL);
|
||
|
return CAIRO_INT_STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
int count = cairo_region_num_rectangles (region);
|
||
|
BRegion bregion;
|
||
|
for (int i = 0; i < count; ++i) {
|
||
|
cairo_rectangle_int_t rect;
|
||
|
|
||
|
cairo_region_get_rectangle (region, i, &rect);
|
||
|
// Have to subtract one, because for pixman, the second coordinate
|
||
|
// lies outside the rectangle.
|
||
|
bregion.Include (_cairo_rectangle_to_brect (&rect));
|
||
|
}
|
||
|
surface->view->ConstrainClippingRegion(&bregion);
|
||
|
return CAIRO_INT_STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* _cairo_beos_bitmap_to_surface:
|
||
|
*
|
||
|
* Returns an addrefed image surface for a BBitmap. The bitmap need not outlive
|
||
|
* the surface.
|
||
|
**/
|
||
|
static cairo_image_surface_t*
|
||
|
_cairo_beos_bitmap_to_surface (BBitmap* bitmap)
|
||
|
{
|
||
|
color_space format = bitmap->ColorSpace();
|
||
|
if (format != B_RGB32 && format != B_RGBA32) {
|
||
|
BBitmap bmp(bitmap->Bounds(), B_RGB32, true);
|
||
|
BView view(bitmap->Bounds(), "Cairo bitmap drawing view",
|
||
|
B_FOLLOW_ALL_SIDES, 0);
|
||
|
bmp.AddChild(&view);
|
||
|
|
||
|
view.LockLooper();
|
||
|
|
||
|
view.DrawBitmap(bitmap, BPoint(0.0, 0.0));
|
||
|
view.Sync();
|
||
|
|
||
|
cairo_image_surface_t* imgsurf = _cairo_beos_bitmap_to_surface(&bmp);
|
||
|
|
||
|
view.UnlockLooper();
|
||
|
bmp.RemoveChild(&view);
|
||
|
return imgsurf;
|
||
|
}
|
||
|
|
||
|
cairo_format_t cformat = format == B_RGB32 ?
|
||
|
CAIRO_FORMAT_RGB24 : CAIRO_FORMAT_ARGB32;
|
||
|
|
||
|
BRect bounds(bitmap->Bounds());
|
||
|
unsigned char* bits = reinterpret_cast<unsigned char*>(bitmap->Bits());
|
||
|
int width = bounds.IntegerWidth() + 1;
|
||
|
int height = bounds.IntegerHeight() + 1;
|
||
|
unsigned char* premultiplied;
|
||
|
if (cformat == CAIRO_FORMAT_ARGB32) {
|
||
|
premultiplied = premultiply_bgra (bits, width, height,
|
||
|
bitmap->BytesPerRow());
|
||
|
} else {
|
||
|
premultiplied = reinterpret_cast<unsigned char*>(
|
||
|
_cairo_malloc_ab(bitmap->BytesPerRow(), height));
|
||
|
if (premultiplied)
|
||
|
memcpy(premultiplied, bits, bitmap->BytesPerRow() * height);
|
||
|
}
|
||
|
if (!premultiplied)
|
||
|
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
|
||
|
|
||
|
cairo_image_surface_t* surf = reinterpret_cast<cairo_image_surface_t*>
|
||
|
(cairo_image_surface_create_for_data(premultiplied,
|
||
|
cformat,
|
||
|
width,
|
||
|
height,
|
||
|
bitmap->BytesPerRow()));
|
||
|
if (surf->base.status)
|
||
|
free(premultiplied);
|
||
|
else
|
||
|
_cairo_image_surface_assume_ownership_of_data(surf);
|
||
|
return surf;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* _cairo_image_surface_to_bitmap:
|
||
|
*
|
||
|
* Converts an image surface to a BBitmap. The return value must be freed with
|
||
|
* delete.
|
||
|
**/
|
||
|
static BBitmap*
|
||
|
_cairo_image_surface_to_bitmap (cairo_image_surface_t* surface)
|
||
|
{
|
||
|
BRect size(0.0, 0.0, surface->width - 1, surface->height - 1);
|
||
|
switch (surface->format) {
|
||
|
case CAIRO_FORMAT_ARGB32: {
|
||
|
BBitmap* data = new BBitmap(size, B_RGBA32);
|
||
|
unpremultiply_bgra (surface->data,
|
||
|
surface->width,
|
||
|
surface->height,
|
||
|
surface->stride,
|
||
|
reinterpret_cast<unsigned char*>(data->Bits()));
|
||
|
return data;
|
||
|
}
|
||
|
case CAIRO_FORMAT_RGB24: {
|
||
|
BBitmap* data = new BBitmap(size, B_RGB32);
|
||
|
memcpy(data->Bits(), surface->data, surface->height * surface->stride);
|
||
|
return data;
|
||
|
}
|
||
|
default:
|
||
|
assert(0);
|
||
|
return NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* _cairo_op_to_be_op:
|
||
|
*
|
||
|
* Converts a cairo drawing operator to a beos drawing_mode. Returns true if
|
||
|
* the operator could be converted, false otherwise.
|
||
|
**/
|
||
|
static bool
|
||
|
_cairo_op_to_be_op (cairo_operator_t cairo_op,
|
||
|
drawing_mode* beos_op)
|
||
|
{
|
||
|
switch (cairo_op) {
|
||
|
case CAIRO_OPERATOR_SOURCE:
|
||
|
*beos_op = B_OP_COPY;
|
||
|
return true;
|
||
|
case CAIRO_OPERATOR_OVER:
|
||
|
*beos_op = B_OP_ALPHA;
|
||
|
return true;
|
||
|
|
||
|
case CAIRO_OPERATOR_ADD:
|
||
|
// Does not actually work
|
||
|
// XXX This is a fundamental compositing operator, it has to work!
|
||
|
#if 1
|
||
|
return false;
|
||
|
#else
|
||
|
*beos_op = B_OP_ADD;
|
||
|
return true;
|
||
|
#endif
|
||
|
|
||
|
case CAIRO_OPERATOR_CLEAR:
|
||
|
// Does not map to B_OP_ERASE - it replaces the dest with the low
|
||
|
// color, instead of transparency; could be done by setting low
|
||
|
// color appropriately.
|
||
|
|
||
|
case CAIRO_OPERATOR_IN:
|
||
|
case CAIRO_OPERATOR_OUT:
|
||
|
case CAIRO_OPERATOR_ATOP:
|
||
|
|
||
|
case CAIRO_OPERATOR_DEST:
|
||
|
case CAIRO_OPERATOR_DEST_OVER:
|
||
|
case CAIRO_OPERATOR_DEST_IN:
|
||
|
case CAIRO_OPERATOR_DEST_OUT:
|
||
|
case CAIRO_OPERATOR_DEST_ATOP:
|
||
|
|
||
|
case CAIRO_OPERATOR_XOR:
|
||
|
case CAIRO_OPERATOR_SATURATE:
|
||
|
|
||
|
default:
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static cairo_surface_t *
|
||
|
_cairo_beos_surface_create_similar (void *abstract_surface,
|
||
|
cairo_content_t content,
|
||
|
int width,
|
||
|
int height)
|
||
|
{
|
||
|
cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>(
|
||
|
abstract_surface);
|
||
|
|
||
|
if (width <= 0)
|
||
|
width = 1;
|
||
|
if (height <= 0)
|
||
|
height = 1;
|
||
|
|
||
|
BRect rect(0.0, 0.0, width - 1, height - 1);
|
||
|
BBitmap* bmp;
|
||
|
switch (content) {
|
||
|
case CAIRO_CONTENT_ALPHA:
|
||
|
return NULL;
|
||
|
case CAIRO_CONTENT_COLOR_ALPHA:
|
||
|
bmp = new BBitmap(rect, B_RGBA32, true);
|
||
|
break;
|
||
|
case CAIRO_CONTENT_COLOR:
|
||
|
// Match the color depth
|
||
|
if (surface->bitmap) {
|
||
|
color_space space = surface->bitmap->ColorSpace();
|
||
|
// No alpha was requested -> make sure not to return
|
||
|
// a surface with alpha
|
||
|
if (space == B_RGBA32)
|
||
|
space = B_RGB32;
|
||
|
if (space == B_RGBA15)
|
||
|
space = B_RGB15;
|
||
|
bmp = new BBitmap(rect, space, true);
|
||
|
} else {
|
||
|
BScreen scr(surface->view->Window());
|
||
|
color_space space = B_RGB32;
|
||
|
if (scr.IsValid())
|
||
|
space = scr.ColorSpace();
|
||
|
bmp = new BBitmap(rect, space, true);
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
ASSERT_NOT_REACHED;
|
||
|
return NULL;
|
||
|
}
|
||
|
BView* view = new BView(rect, "Cairo bitmap view", B_FOLLOW_ALL_SIDES, 0);
|
||
|
bmp->AddChild(view);
|
||
|
return _cairo_beos_surface_create_internal(view, bmp, true);
|
||
|
}
|
||
|
|
||
|
static cairo_status_t
|
||
|
_cairo_beos_surface_finish (void *abstract_surface)
|
||
|
{
|
||
|
cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>(
|
||
|
abstract_surface);
|
||
|
if (surface->owns_bitmap_view) {
|
||
|
if (surface->bitmap)
|
||
|
surface->bitmap->RemoveChild(surface->view);
|
||
|
|
||
|
delete surface->view;
|
||
|
delete surface->bitmap;
|
||
|
|
||
|
surface->view = NULL;
|
||
|
surface->bitmap = NULL;
|
||
|
}
|
||
|
|
||
|
cairo_region_destroy (surface->clip_region);
|
||
|
|
||
|
return CAIRO_STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
static cairo_status_t
|
||
|
_cairo_beos_surface_acquire_source_image (void *abstract_surface,
|
||
|
cairo_image_surface_t **image_out,
|
||
|
void **image_extra)
|
||
|
{
|
||
|
cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>(
|
||
|
abstract_surface);
|
||
|
AutoLockView locker(surface->view);
|
||
|
if (!locker)
|
||
|
return CAIRO_STATUS_NO_MEMORY; /// XXX not exactly right, but what can we do?
|
||
|
|
||
|
|
||
|
surface->view->Sync();
|
||
|
|
||
|
if (surface->bitmap) {
|
||
|
*image_out = _cairo_beos_bitmap_to_surface (surface->bitmap);
|
||
|
if (unlikely ((*image_out)->base.status))
|
||
|
return (*image_out)->base.status;
|
||
|
|
||
|
*image_extra = NULL;
|
||
|
return CAIRO_STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
BBitmap* bmp;
|
||
|
if (_cairo_beos_view_to_bitmap(surface->view, &bmp) != OK)
|
||
|
return CAIRO_STATUS_NO_MEMORY; /// XXX incorrect if the error was NOT_VISIBLE
|
||
|
|
||
|
*image_out = _cairo_beos_bitmap_to_surface (bmp);
|
||
|
if (unlikely ((*image_out)->base.status)) {
|
||
|
delete bmp;
|
||
|
return (*image_out)->base.status;
|
||
|
}
|
||
|
*image_extra = bmp;
|
||
|
|
||
|
return CAIRO_STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
_cairo_beos_surface_release_source_image (void *abstract_surface,
|
||
|
cairo_image_surface_t *image,
|
||
|
void *image_extra)
|
||
|
{
|
||
|
cairo_surface_destroy (&image->base);
|
||
|
|
||
|
if (image_extra != NULL) {
|
||
|
BBitmap* bmp = static_cast<BBitmap*>(image_extra);
|
||
|
delete bmp;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static cairo_status_t
|
||
|
_cairo_beos_surface_acquire_dest_image (void *abstract_surface,
|
||
|
cairo_rectangle_int_t *interest_rect,
|
||
|
cairo_image_surface_t **image_out,
|
||
|
cairo_rectangle_int_t *image_rect,
|
||
|
void **image_extra)
|
||
|
{
|
||
|
cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>(
|
||
|
abstract_surface);
|
||
|
|
||
|
AutoLockView locker(surface->view);
|
||
|
if (!locker) {
|
||
|
*image_out = NULL;
|
||
|
*image_extra = NULL;
|
||
|
return (cairo_status_t) CAIRO_INT_STATUS_NOTHING_TO_DO;
|
||
|
}
|
||
|
|
||
|
if (surface->bitmap) {
|
||
|
surface->view->Sync();
|
||
|
*image_out = _cairo_beos_bitmap_to_surface(surface->bitmap);
|
||
|
if (unlikely ((*image_out)->base.status))
|
||
|
return (*image_out)->base.status;
|
||
|
|
||
|
image_rect->x = 0;
|
||
|
image_rect->y = 0;
|
||
|
image_rect->width = (*image_out)->width;
|
||
|
image_rect->height = (*image_out)->height;
|
||
|
|
||
|
*image_extra = NULL;
|
||
|
return CAIRO_STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
BRect b_interest_rect (_cairo_rectangle_to_brect (interest_rect));
|
||
|
|
||
|
BRect rect;
|
||
|
BBitmap* bitmap;
|
||
|
ViewCopyStatus status = _cairo_beos_view_to_bitmap(surface->view, &bitmap,
|
||
|
&rect, &b_interest_rect);
|
||
|
if (status == NOT_VISIBLE) {
|
||
|
*image_out = NULL;
|
||
|
*image_extra = NULL;
|
||
|
return CAIRO_STATUS_SUCCESS;
|
||
|
}
|
||
|
if (status == ERROR)
|
||
|
return CAIRO_STATUS_NO_MEMORY;
|
||
|
|
||
|
*image_rect = _brect_to_cairo_rectangle(rect);
|
||
|
*image_out = _cairo_beos_bitmap_to_surface(bitmap);
|
||
|
delete bitmap;
|
||
|
if (unlikely ((*image_out)->base.status))
|
||
|
return (*image_out)->base.status;
|
||
|
|
||
|
*image_extra = NULL;
|
||
|
|
||
|
return CAIRO_STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
|
||
|
static void
|
||
|
_cairo_beos_surface_release_dest_image (void *abstract_surface,
|
||
|
cairo_rectangle_int_t *intersect_rect,
|
||
|
cairo_image_surface_t *image,
|
||
|
cairo_rectangle_int_t *image_rect,
|
||
|
void *image_extra)
|
||
|
{
|
||
|
cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>(
|
||
|
abstract_surface);
|
||
|
|
||
|
AutoLockView locker(surface->view);
|
||
|
if (!locker)
|
||
|
return;
|
||
|
|
||
|
BBitmap* bitmap_to_draw = _cairo_image_surface_to_bitmap(image);
|
||
|
surface->view->PushState();
|
||
|
|
||
|
surface->view->SetDrawingMode(B_OP_COPY);
|
||
|
|
||
|
surface->view->DrawBitmap (bitmap_to_draw,
|
||
|
_cairo_rectangle_to_brect (image_rect));
|
||
|
|
||
|
surface->view->PopState();
|
||
|
|
||
|
delete bitmap_to_draw;
|
||
|
cairo_surface_destroy(&image->base);
|
||
|
}
|
||
|
|
||
|
static cairo_int_status_t
|
||
|
_cairo_beos_surface_composite (cairo_operator_t op,
|
||
|
cairo_pattern_t *src,
|
||
|
cairo_pattern_t *mask,
|
||
|
void *dst,
|
||
|
int src_x,
|
||
|
int src_y,
|
||
|
int mask_x,
|
||
|
int mask_y,
|
||
|
int dst_x,
|
||
|
int dst_y,
|
||
|
unsigned int width,
|
||
|
unsigned int height,
|
||
|
cairo_region_t *clip_region)
|
||
|
{
|
||
|
cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>(
|
||
|
dst);
|
||
|
cairo_int_status_t status;
|
||
|
AutoLockView locker(surface->view);
|
||
|
if (!locker)
|
||
|
return CAIRO_INT_STATUS_SUCCESS;
|
||
|
|
||
|
drawing_mode mode;
|
||
|
if (!_cairo_op_to_be_op(op, &mode))
|
||
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
||
|
|
||
|
// XXX Masks are not yet supported
|
||
|
if (mask)
|
||
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
||
|
|
||
|
// XXX should eventually support the others
|
||
|
if (src->type != CAIRO_PATTERN_TYPE_SURFACE ||
|
||
|
src->extend != CAIRO_EXTEND_NONE)
|
||
|
{
|
||
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
||
|
}
|
||
|
|
||
|
// Can we maybe support other matrices as well? (scale? if the filter is right)
|
||
|
int itx, ity;
|
||
|
if (!_cairo_matrix_is_integer_translation(&src->matrix, &itx, &ity))
|
||
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
||
|
|
||
|
status = _cairo_beos_surface_set_clip_region (surface, clip_region);
|
||
|
if (unlikely (status))
|
||
|
return status;
|
||
|
|
||
|
BRect srcRect(src_x + itx,
|
||
|
src_y + ity,
|
||
|
src_x + itx + width - 1,
|
||
|
src_y + ity + height - 1);
|
||
|
BRect dstRect(dst_x, dst_y, dst_x + width - 1, dst_y + height - 1);
|
||
|
|
||
|
cairo_surface_t* src_surface = reinterpret_cast<cairo_surface_pattern_t*>(src)->
|
||
|
surface;
|
||
|
|
||
|
// Get a bitmap
|
||
|
BBitmap* bmp = NULL;
|
||
|
bool free_bmp = false;
|
||
|
if (_cairo_surface_is_image(src_surface)) {
|
||
|
cairo_image_surface_t* img_surface =
|
||
|
reinterpret_cast<cairo_image_surface_t*>(src_surface);
|
||
|
|
||
|
bmp = _cairo_image_surface_to_bitmap(img_surface);
|
||
|
free_bmp = true;
|
||
|
} else if (src_surface->backend == surface->base.backend) {
|
||
|
cairo_beos_surface_t *beos_surface =
|
||
|
reinterpret_cast<cairo_beos_surface_t*>(src_surface);
|
||
|
if (beos_surface->bitmap) {
|
||
|
AutoLockView locker(beos_surface->view);
|
||
|
if (locker)
|
||
|
beos_surface->view->Sync();
|
||
|
bmp = beos_surface->bitmap;
|
||
|
} else {
|
||
|
_cairo_beos_view_to_bitmap(surface->view, &bmp);
|
||
|
free_bmp = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!bmp)
|
||
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
||
|
|
||
|
// So, BeOS seems to screw up painting an opaque bitmap onto a
|
||
|
// translucent one (it makes them partly transparent). Just return
|
||
|
// unsupported.
|
||
|
if (bmp->ColorSpace() == B_RGB32 && surface->bitmap &&
|
||
|
surface->bitmap->ColorSpace() == B_RGBA32 &&
|
||
|
(mode == B_OP_COPY || mode == B_OP_ALPHA))
|
||
|
{
|
||
|
if (free_bmp)
|
||
|
delete bmp;
|
||
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
||
|
}
|
||
|
|
||
|
// Draw it on screen.
|
||
|
surface->view->PushState();
|
||
|
|
||
|
// If our image rect is only a subrect of the desired size, and we
|
||
|
// aren't using B_OP_ALPHA, then we need to fill the rect first.
|
||
|
if (mode == B_OP_COPY && !bmp->Bounds().Contains(srcRect)) {
|
||
|
rgb_color black = { 0, 0, 0, 0 };
|
||
|
|
||
|
surface->view->SetDrawingMode(mode);
|
||
|
surface->view->SetHighColor(black);
|
||
|
surface->view->FillRect(dstRect);
|
||
|
}
|
||
|
|
||
|
if (mode == B_OP_ALPHA && bmp->ColorSpace() == B_RGB32) {
|
||
|
mode = B_OP_COPY;
|
||
|
}
|
||
|
surface->view->SetDrawingMode(mode);
|
||
|
|
||
|
if (surface->bitmap && surface->bitmap->ColorSpace() == B_RGBA32)
|
||
|
surface->view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE);
|
||
|
else
|
||
|
surface->view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
|
||
|
|
||
|
surface->view->DrawBitmap(bmp, srcRect, dstRect);
|
||
|
|
||
|
surface->view->PopState();
|
||
|
|
||
|
if (free_bmp)
|
||
|
delete bmp;
|
||
|
|
||
|
return CAIRO_INT_STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
|
||
|
static cairo_int_status_t
|
||
|
_cairo_beos_surface_fill_rectangles (void *abstract_surface,
|
||
|
cairo_operator_t op,
|
||
|
const cairo_color_t *color,
|
||
|
cairo_rectangle_int_t *rects,
|
||
|
int num_rects,
|
||
|
cairo_region_t *clip_region)
|
||
|
{
|
||
|
cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>(
|
||
|
abstract_surface);
|
||
|
cairo_int_status_t status;
|
||
|
|
||
|
if (num_rects <= 0)
|
||
|
return CAIRO_INT_STATUS_SUCCESS;
|
||
|
|
||
|
AutoLockView locker(surface->view);
|
||
|
if (!locker)
|
||
|
return CAIRO_INT_STATUS_SUCCESS;
|
||
|
|
||
|
drawing_mode mode;
|
||
|
if (!_cairo_op_to_be_op(op, &mode))
|
||
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
||
|
|
||
|
status = _cairo_beos_surface_set_clip_region (surface, clip_region);
|
||
|
if (unlikely (status))
|
||
|
return status;
|
||
|
|
||
|
rgb_color be_color = _cairo_color_to_be_color(color);
|
||
|
|
||
|
if (mode == B_OP_ALPHA && be_color.alpha == 0xFF)
|
||
|
mode = B_OP_COPY;
|
||
|
|
||
|
// For CAIRO_OPERATOR_SOURCE, cairo expects us to use the premultiplied
|
||
|
// color info. This is only relevant when drawing into an rgb24 buffer
|
||
|
// (as for others, we can convert when asked for the image)
|
||
|
if (mode == B_OP_COPY && be_color.alpha != 0xFF &&
|
||
|
(!surface->bitmap || surface->bitmap->ColorSpace() != B_RGBA32))
|
||
|
{
|
||
|
be_color.red = color->red_short >> 8;
|
||
|
be_color.green = color->green_short >> 8;
|
||
|
be_color.blue = color->blue_short >> 8;
|
||
|
}
|
||
|
|
||
|
surface->view->PushState();
|
||
|
|
||
|
surface->view->SetDrawingMode(mode);
|
||
|
surface->view->SetHighColor(be_color);
|
||
|
if (surface->bitmap && surface->bitmap->ColorSpace() == B_RGBA32)
|
||
|
surface->view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_COMPOSITE);
|
||
|
else
|
||
|
surface->view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY);
|
||
|
|
||
|
for (int i = 0; i < num_rects; ++i)
|
||
|
surface->view->FillRect (_cairo_rectangle_to_brect (&rects[i]));
|
||
|
|
||
|
surface->view->PopState();
|
||
|
|
||
|
return CAIRO_INT_STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
static cairo_bool_t
|
||
|
_cairo_beos_surface_get_extents (void *abstract_surface,
|
||
|
cairo_rectangle_int_t *rectangle)
|
||
|
{
|
||
|
cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>(
|
||
|
abstract_surface);
|
||
|
AutoLockView locker(surface->view);
|
||
|
if (!locker)
|
||
|
return FALSE;
|
||
|
|
||
|
*rectangle = _brect_to_cairo_rectangle (surface->view->Bounds());
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
static const struct _cairo_surface_backend cairo_beos_surface_backend = {
|
||
|
CAIRO_SURFACE_TYPE_BEOS,
|
||
|
_cairo_beos_surface_create_similar,
|
||
|
_cairo_beos_surface_finish,
|
||
|
_cairo_beos_surface_acquire_source_image,
|
||
|
_cairo_beos_surface_release_source_image,
|
||
|
_cairo_beos_surface_acquire_dest_image,
|
||
|
_cairo_beos_surface_release_dest_image,
|
||
|
NULL, /* clone_similar */
|
||
|
_cairo_beos_surface_composite, /* composite */
|
||
|
_cairo_beos_surface_fill_rectangles,
|
||
|
NULL, /* composite_trapezoids */
|
||
|
NULL, /* create_span_renderer */
|
||
|
NULL, /* check_span_renderer */
|
||
|
NULL, /* copy_page */
|
||
|
NULL, /* show_page */
|
||
|
_cairo_beos_surface_get_extents,
|
||
|
NULL, /* old_show_glyphs */
|
||
|
NULL, /* get_font_options */
|
||
|
NULL, /* flush */
|
||
|
NULL, /* mark_dirty_rectangle */
|
||
|
NULL, /* scaled_font_fini */
|
||
|
NULL, /* scaled_glyph_fini */
|
||
|
|
||
|
NULL, /* paint */
|
||
|
NULL, /* mask */
|
||
|
NULL, /* stroke */
|
||
|
NULL, /* fill */
|
||
|
NULL /* show_glyphs */
|
||
|
};
|
||
|
|
||
|
static cairo_surface_t *
|
||
|
_cairo_beos_surface_create_internal (BView* view,
|
||
|
BBitmap* bmp,
|
||
|
bool owns_bitmap_view)
|
||
|
{
|
||
|
// Must use malloc, because cairo code will use free() on the surface
|
||
|
cairo_beos_surface_t *surface = static_cast<cairo_beos_surface_t*>(
|
||
|
malloc(sizeof(cairo_beos_surface_t)));
|
||
|
if (surface == NULL) {
|
||
|
_cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||
|
return const_cast<cairo_surface_t*>(&_cairo_surface_nil);
|
||
|
}
|
||
|
|
||
|
cairo_content_t content = CAIRO_CONTENT_COLOR;
|
||
|
if (bmp && (bmp->ColorSpace() == B_RGBA32 || bmp->ColorSpace() == B_RGBA15))
|
||
|
content = CAIRO_CONTENT_COLOR_ALPHA;
|
||
|
_cairo_surface_init (&surface->base,
|
||
|
&cairo_beos_surface_backend,
|
||
|
NULL, /* device */
|
||
|
content);
|
||
|
|
||
|
surface->view = view;
|
||
|
surface->bitmap = bmp;
|
||
|
surface->owns_bitmap_view = owns_bitmap_view;
|
||
|
|
||
|
surface->clip_region = NULL;
|
||
|
|
||
|
return &surface->base;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* cairo_beos_surface_create:
|
||
|
* @view: The view to draw on
|
||
|
*
|
||
|
* Creates a Cairo surface that draws onto a BeOS BView.
|
||
|
* The caller must ensure that the view does not get deleted before the surface.
|
||
|
* If the view is attached to a bitmap rather than an on-screen window, use
|
||
|
* cairo_beos_surface_create_for_bitmap() instead of this function.
|
||
|
*
|
||
|
* Since: TBD
|
||
|
**/
|
||
|
cairo_surface_t *
|
||
|
cairo_beos_surface_create (BView* view)
|
||
|
{
|
||
|
return cairo_beos_surface_create_for_bitmap(view, NULL);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* cairo_beos_surface_create_for_bitmap:
|
||
|
* @view: The view to draw on
|
||
|
* @bmp: The bitmap to which the view is attached
|
||
|
*
|
||
|
* Creates a Cairo surface that draws onto a BeOS BView which is attached to a
|
||
|
* BBitmap.
|
||
|
* The caller must ensure that the view and the bitmap do not get deleted
|
||
|
* before the surface.
|
||
|
*
|
||
|
* For views that draw to a bitmap (as opposed to a screen), use this function
|
||
|
* rather than cairo_beos_surface_create(). Not using this function WILL lead to
|
||
|
* incorrect behaviour.
|
||
|
*
|
||
|
* For now, only views that draw to the entire area of bmp are supported.
|
||
|
* The view must already be attached to the bitmap.
|
||
|
*
|
||
|
* Since: TBD
|
||
|
**/
|
||
|
cairo_surface_t *
|
||
|
cairo_beos_surface_create_for_bitmap (BView* view,
|
||
|
BBitmap* bmp)
|
||
|
{
|
||
|
return _cairo_beos_surface_create_internal(view, bmp);
|
||
|
}
|