| /* 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); |
| } |