/**
 * _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_rgba(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 NULL;

    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;
}
static cairo_status_t
_get_image_surface (cairo_xcb_surface_t     *surface,
		    cairo_rectangle_int16_t *interest_rect,
		    cairo_image_surface_t  **image_out,
		    cairo_rectangle_int16_t *image_rect)
{
    cairo_image_surface_t *image;
    XCBGetImageRep *imagerep;
    int bpp, bytes_per_line;
    int x1, y1, x2, y2;
    unsigned char *data;
    cairo_format_t format;
    cairo_format_masks_t masks;

    x1 = 0;
    y1 = 0;
    x2 = surface->width;
    y2 = surface->height;

    if (interest_rect) {
	cairo_rectangle_int16_t rect;

	rect.x = interest_rect->x;
	rect.y = interest_rect->y;
	rect.width = interest_rect->width;
	rect.height = interest_rect->height;

	if (rect.x > x1)
	    x1 = rect.x;
	if (rect.y > y1)
	    y1 = rect.y;
	if (rect.x + rect.width < x2)
	    x2 = rect.x + rect.width;
	if (rect.y + rect.height < y2)
	    y2 = rect.y + rect.height;

	if (x1 >= x2 || y1 >= y2) {
	    *image_out = NULL;
	    return CAIRO_STATUS_SUCCESS;
	}
    }

    if (image_rect) {
	image_rect->x = x1;
	image_rect->y = y1;
	image_rect->width = x2 - x1;
	image_rect->height = y2 - y1;
    }

    /* XXX: This should try to use the XShm extension if available */

    if (surface->use_pixmap == 0)
    {
	XCBGenericError *error;
	imagerep = XCBGetImageReply(surface->dpy,
				    XCBGetImage(surface->dpy, XCBImageFormatZPixmap,
						surface->drawable,
						x1, y1,
						x2 - x1, y2 - y1,
						AllPlanes), &error);

	/* If we get an error, the surface must have been a window,
	 * so retry with the safe code path.
	 */
	if (error)
	    surface->use_pixmap = CAIRO_ASSUME_PIXMAP;
    }
    else
    {
	surface->use_pixmap--;
	imagerep = NULL;
    }

    if (!imagerep)
    {
	/* XCBGetImage from a window is dangerous because it can
	 * produce errors if the window is unmapped or partially
	 * outside the screen. We could check for errors and
	 * retry, but to keep things simple, we just create a
	 * temporary pixmap
	 */
	XCBDRAWABLE drawable;
	drawable.pixmap = XCBPIXMAPNew (surface->dpy);
	XCBCreatePixmap (surface->dpy,
			 surface->depth,
			 drawable.pixmap,
			 surface->drawable,
			 x2 - x1, y2 - y1);
	_cairo_xcb_surface_ensure_gc (surface);

	XCBCopyArea (surface->dpy, surface->drawable, drawable, surface->gc,
		     x1, y1, 0, 0, x2 - x1, y2 - y1);

	imagerep = XCBGetImageReply(surface->dpy,
				    XCBGetImage(surface->dpy, XCBImageFormatZPixmap,
						drawable,
						x1, y1,
						x2 - x1, y2 - y1,
						AllPlanes), 0);
	XCBFreePixmap (surface->dpy, drawable.pixmap);

    }
    if (!imagerep)
	return CAIRO_STATUS_NO_MEMORY;

    bpp = _bits_per_pixel(surface->dpy, imagerep->depth);
    bytes_per_line = _bytes_per_line(surface->dpy, surface->width, bpp);

    data = malloc (bytes_per_line * surface->height);
    if (data == NULL) {
	free (imagerep);
	return CAIRO_STATUS_NO_MEMORY;
    }

    memcpy (data, XCBGetImageData (imagerep), bytes_per_line * surface->height);
    free (imagerep);

    /*
     * Compute the pixel format masks from either an XCBVISUALTYPE or
     * a XCBRenderPCTFORMINFO, failing we assume the drawable is an
     * alpha-only pixmap as it could only have been created that way
     * through the cairo_xlib_surface_create_for_bitmap function.
     */
    if (surface->visual) {
	masks.bpp = bpp;
	masks.alpha_mask = 0;
	masks.red_mask = surface->visual->red_mask;
	masks.green_mask = surface->visual->green_mask;
	masks.blue_mask = surface->visual->blue_mask;
    } else if (surface->has_format) {
	masks.bpp = bpp;
	masks.red_mask = (unsigned long)surface->format.direct.red_mask << surface->format.direct.red_shift;
	masks.green_mask = (unsigned long)surface->format.direct.green_mask << surface->format.direct.green_shift;
	masks.blue_mask = (unsigned long)surface->format.direct.blue_mask << surface->format.direct.blue_shift;
	masks.alpha_mask = (unsigned long)surface->format.direct.alpha_mask << surface->format.direct.alpha_shift;
    } else {
	masks.bpp = bpp;
	masks.red_mask = 0;
	masks.green_mask = 0;
	masks.blue_mask = 0;
	if (surface->depth < 32)
	    masks.alpha_mask = (1 << surface->depth) - 1;
	else
	    masks.alpha_mask = 0xffffffff;
    }

    /*
     * Prefer to use a standard pixman format instead of the
     * general masks case.
     */
    if (_CAIRO_MASK_FORMAT (&masks, &format)) {
	image = (cairo_image_surface_t *)
	    cairo_image_surface_create_for_data (data,
						 format,
						 x2 - x1,
						 y2 - y1,
						 bytes_per_line);
	if (image->base.status)
	    goto FAIL;
    } else {
	/*
	 * XXX This can't work.  We must convert the data to one of the
	 * supported pixman formats.  Pixman needs another function
	 * which takes data in an arbitrary format and converts it
	 * to something supported by that library.
	 */
	image = (cairo_image_surface_t *)
	    _cairo_image_surface_create_with_masks (data,
						    &masks,
						    x2 - x1,
						    y2 - y1,
						    bytes_per_line);
	if (image->base.status)
	    goto FAIL;
    }

    /* Let the surface take ownership of the data */
    _cairo_image_surface_assume_ownership_of_data (image);

    *image_out = image;
    return CAIRO_STATUS_SUCCESS;

 FAIL:
    free (data);
    return CAIRO_STATUS_NO_MEMORY;
}