Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
branch: master
2447 lines (2043 sloc) 70.258 kb
/*
* bitmap.c
*
* Copyright (c) 2003 Alexandre Pigolkine
* Copyright (C) 2007 Novell, Inc (http://www.novell.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
* NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Authors:
* Alexandre Pigolkine (pigolkine@gmx.de)
* Vladimir Vukicevic (vladimir@pobox.com)
* Jordi Mas (jordi@ximian.com)
* Jonathan Gilbert (logic@deltaq.org)
* Sebastien Pouliot <sebastien@ximian.com>
*/
#include "gdiplus-private.h"
#include "bitmap-private.h"
static GpStatus gdip_bitmap_clone_data_rect (BitmapData *srcData, Rect *srcRect, BitmapData *destData, Rect *destRect);
/* The default indexed palettes. This code was generated by a tiny C# program.
*/
static const unsigned int default_Format1bppIndexed_palette[2] = {
0xFF000000, 0xFFFFFFFF
};
static const unsigned int default_Format4bppIndexed_palette[16] = {
0xFF000000, 0xFF800000, 0xFF008000, 0xFF808000, 0xFF000080, 0xFF800080, 0xFF008080, 0xFF808080, 0xFFC0C0C0, 0xFFFF0000, 0xFF00FF00, 0xFFFFFF00, 0xFF0000FF, 0xFFFF00FF, 0xFF00FFFF, 0xFFFFFFFF
};
static const unsigned int default_Format8bppIndexed_palette[256] = {
0xFF000000, 0xFF800000, 0xFF008000, 0xFF808000, 0xFF000080, 0xFF800080, 0xFF008080, 0xFF808080, 0xFFC0C0C0, 0xFFFF0000, 0xFF00FF00, 0xFFFFFF00, 0xFF0000FF, 0xFFFF00FF, 0xFF00FFFF, 0xFFFFFFFF,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF000000, 0xFF000033, 0xFF000066, 0xFF000099, 0xFF0000CC, 0xFF0000FF, 0xFF003300, 0xFF003333,
0xFF003366, 0xFF003399, 0xFF0033CC, 0xFF0033FF, 0xFF006600, 0xFF006633, 0xFF006666, 0xFF006699, 0xFF0066CC, 0xFF0066FF, 0xFF009900, 0xFF009933, 0xFF009966, 0xFF009999, 0xFF0099CC, 0xFF0099FF,
0xFF00CC00, 0xFF00CC33, 0xFF00CC66, 0xFF00CC99, 0xFF00CCCC, 0xFF00CCFF, 0xFF00FF00, 0xFF00FF33, 0xFF00FF66, 0xFF00FF99, 0xFF00FFCC, 0xFF00FFFF, 0xFF330000, 0xFF330033, 0xFF330066, 0xFF330099,
0xFF3300CC, 0xFF3300FF, 0xFF333300, 0xFF333333, 0xFF333366, 0xFF333399, 0xFF3333CC, 0xFF3333FF, 0xFF336600, 0xFF336633, 0xFF336666, 0xFF336699, 0xFF3366CC, 0xFF3366FF, 0xFF339900, 0xFF339933,
0xFF339966, 0xFF339999, 0xFF3399CC, 0xFF3399FF, 0xFF33CC00, 0xFF33CC33, 0xFF33CC66, 0xFF33CC99, 0xFF33CCCC, 0xFF33CCFF, 0xFF33FF00, 0xFF33FF33, 0xFF33FF66, 0xFF33FF99, 0xFF33FFCC, 0xFF33FFFF,
0xFF660000, 0xFF660033, 0xFF660066, 0xFF660099, 0xFF6600CC, 0xFF6600FF, 0xFF663300, 0xFF663333, 0xFF663366, 0xFF663399, 0xFF6633CC, 0xFF6633FF, 0xFF666600, 0xFF666633, 0xFF666666, 0xFF666699,
0xFF6666CC, 0xFF6666FF, 0xFF669900, 0xFF669933, 0xFF669966, 0xFF669999, 0xFF6699CC, 0xFF6699FF, 0xFF66CC00, 0xFF66CC33, 0xFF66CC66, 0xFF66CC99, 0xFF66CCCC, 0xFF66CCFF, 0xFF66FF00, 0xFF66FF33,
0xFF66FF66, 0xFF66FF99, 0xFF66FFCC, 0xFF66FFFF, 0xFF990000, 0xFF990033, 0xFF990066, 0xFF990099, 0xFF9900CC, 0xFF9900FF, 0xFF993300, 0xFF993333, 0xFF993366, 0xFF993399, 0xFF9933CC, 0xFF9933FF,
0xFF996600, 0xFF996633, 0xFF996666, 0xFF996699, 0xFF9966CC, 0xFF9966FF, 0xFF999900, 0xFF999933, 0xFF999966, 0xFF999999, 0xFF9999CC, 0xFF9999FF, 0xFF99CC00, 0xFF99CC33, 0xFF99CC66, 0xFF99CC99,
0xFF99CCCC, 0xFF99CCFF, 0xFF99FF00, 0xFF99FF33, 0xFF99FF66, 0xFF99FF99, 0xFF99FFCC, 0xFF99FFFF, 0xFFCC0000, 0xFFCC0033, 0xFFCC0066, 0xFFCC0099, 0xFFCC00CC, 0xFFCC00FF, 0xFFCC3300, 0xFFCC3333,
0xFFCC3366, 0xFFCC3399, 0xFFCC33CC, 0xFFCC33FF, 0xFFCC6600, 0xFFCC6633, 0xFFCC6666, 0xFFCC6699, 0xFFCC66CC, 0xFFCC66FF, 0xFFCC9900, 0xFFCC9933, 0xFFCC9966, 0xFFCC9999, 0xFFCC99CC, 0xFFCC99FF,
0xFFCCCC00, 0xFFCCCC33, 0xFFCCCC66, 0xFFCCCC99, 0xFFCCCCCC, 0xFFCCCCFF, 0xFFCCFF00, 0xFFCCFF33, 0xFFCCFF66, 0xFFCCFF99, 0xFFCCFFCC, 0xFFCCFFFF, 0xFFFF0000, 0xFFFF0033, 0xFFFF0066, 0xFFFF0099,
0xFFFF00CC, 0xFFFF00FF, 0xFFFF3300, 0xFFFF3333, 0xFFFF3366, 0xFFFF3399, 0xFFFF33CC, 0xFFFF33FF, 0xFFFF6600, 0xFFFF6633, 0xFFFF6666, 0xFFFF6699, 0xFFFF66CC, 0xFFFF66FF, 0xFFFF9900, 0xFFFF9933,
0xFFFF9966, 0xFFFF9999, 0xFFFF99CC, 0xFFFF99FF, 0xFFFFCC00, 0xFFFFCC33, 0xFFFFCC66, 0xFFFFCC99, 0xFFFFCCCC, 0xFFFFCCFF, 0xFFFFFF00, 0xFFFFFF33, 0xFFFFFF66, 0xFFFFFF99, 0xFFFFFFCC, 0xFFFFFFFF
};
/*
Those are the only pixel formats that we really support
*/
static BOOL
gdip_is_a_supported_pixelformat (PixelFormat fmt)
{
switch (fmt) {
case PixelFormat1bppIndexed:
case PixelFormat4bppIndexed:
case PixelFormat8bppIndexed:
case PixelFormat24bppRGB:
case PixelFormat32bppARGB:
case PixelFormat32bppPARGB:
case PixelFormat32bppRGB:
return TRUE;
default:
return FALSE;
}
}
static BOOL
gdip_is_an_alpha_pixelformat (PixelFormat format)
{
return ((format & PixelFormatAlpha) != 0);
}
/*
Returns TRUE if the Bitmap contains indexed (palettized) data.
*/
BOOL
gdip_is_an_indexed_pixelformat (PixelFormat fmt)
{
return ((fmt & PixelFormatIndexed) != 0);
}
void
gdip_bitmap_init (GpBitmap *bitmap)
{
if (bitmap == NULL)
return;
memset (bitmap, 0, sizeof (GpBitmap));
bitmap->type = ImageTypeBitmap;
bitmap->image_format = INVALID;
}
static GpStatus
gdip_propertyitems_clone(PropertyItem *src, PropertyItem **dest, int count)
{
PropertyItem *result;
int i;
int j;
if (dest == NULL) {
return InvalidParameter;
}
if (src == NULL) {
*dest = NULL;
return Ok;
}
result = GdipAlloc(sizeof(PropertyItem) * count);
if (result == NULL) {
return OutOfMemory;
}
for (i = 0; i < count; i++) {
result[i].id = src[i].id;
result[i].length = src[i].length;
result[i].type = src[i].type;
if ((src[i].value != NULL) && (src[i].length > 0)) {
result[i].value = GdipAlloc(src[i].length);
if (result[i].value == NULL) {
for (j = 0; j < i; j++) {
if (result[j].value != NULL) {
GdipFree(result[j].value);
}
}
GdipFree (result);
return OutOfMemory;
}
memcpy(result[i].value, src[i].value, src[i].length);
} else {
result[i].value = NULL;
}
}
*dest = result;
return Ok;
}
static GpStatus
gdip_propertyitems_dispose(PropertyItem *property, int count)
{
int i;
if (property == NULL) {
return Ok;
}
for (i = 0; i < count; i++) {
if (property[i].value != NULL) {
GdipFree(property[i].value);
}
}
GdipFree(property);
return Ok;
}
static GpStatus
gdip_bitmapdata_init (BitmapData *data)
{
if (data != NULL) {
memset(data, 0, sizeof(BitmapData));
return Ok;
}
return InvalidParameter;
}
GpStatus
gdip_property_get_short(int offset, void *value, unsigned short *result)
{
BYTE *ptr = (BYTE*)value;
*result = ptr[0] + (ptr[1] << 8);
return Ok;
}
GpStatus
gdip_property_get_long(int offset, void *value, guint32 *result)
{
BYTE *ptr = (BYTE*)value;
*result = ptr[0] + (ptr[1] << 8) + (ptr[2] << 16) + (ptr[3] << 24);
return Ok;
}
GpStatus
gdip_property_get_srational(int offset, void *value, unsigned short *numerator, unsigned short *denominator)
{
BYTE *ptr = (BYTE*)value;
*numerator = ptr[0] + (ptr[1] << 8);
*denominator = ptr[2] + (ptr[3] << 8);
return Ok;
}
GpStatus
gdip_property_get_rational(int offset, void *value, guint32 *numerator, guint32 *denominator)
{
BYTE *ptr = (BYTE*)value;
*numerator = ptr[0] + (ptr[1] << 8) + (ptr[2] << 16) + (ptr[3] << 24);
*denominator = ptr[4] + (ptr[5] << 8) + (ptr[6] << 16) + (ptr[7] << 24);
return Ok;
}
GpStatus
gdip_bitmapdata_property_add_long(BitmapData *bitmap_data, PROPID id, guint32 value)
{
BYTE buffer[4];
buffer[0] = value & 0xff;
buffer[1] = (value & 0x0000ff00) >> 8;
buffer[2] = (value & 0x00ff0000) >> 16;
buffer[3] = (value & 0xff000000) >> 24;
return gdip_bitmapdata_property_add (bitmap_data, id, 4, PropertyTagTypeLong, buffer);
}
GpStatus
gdip_bitmapdata_property_add_ASCII(BitmapData *bitmap_data, PROPID id, BYTE *value)
{
return gdip_bitmapdata_property_add (bitmap_data, id, strlen((char *)value) + 1, PropertyTagTypeASCII, value);
}
GpStatus
gdip_bitmapdata_property_add_byte (BitmapData *bitmap_data, PROPID id, BYTE value)
{
return gdip_bitmapdata_property_add (bitmap_data, id, 1, PropertyTagTypeByte, &value);
}
GpStatus
gdip_bitmapdata_property_add_short(BitmapData *bitmap_data, PROPID id, unsigned short value)
{
BYTE buffer[2];
buffer[0] = value & 0xff;
buffer[1] = (value & 0x0ff00) >> 8;
return gdip_bitmapdata_property_add (bitmap_data, id, 2, PropertyTagTypeShort, buffer);
}
GpStatus
gdip_bitmapdata_property_add_rational(BitmapData *bitmap_data, PROPID id, guint32 numerator, guint32 denominator)
{
BYTE buffer[8];
buffer[0] = numerator & 0xff;
buffer[1] = (numerator & 0x0000ff00) >> 8;
buffer[2] = (numerator & 0x00ff0000) >> 16;
buffer[3] = (numerator & 0xff000000) >> 24;
buffer[4] = denominator & 0xff;
buffer[5] = (denominator & 0x0000ff00) >> 8;
buffer[6] = (denominator & 0x00ff0000) >> 16;
buffer[7] = (denominator & 0xff000000) >> 24;
return gdip_bitmapdata_property_add (bitmap_data, id, 8, PropertyTagTypeRational, buffer);
}
GpStatus
gdip_bitmapdata_property_add_srational(BitmapData *bitmap_data, PROPID id, unsigned short numerator, unsigned short denominator)
{
BYTE buffer[4];
buffer[0] = numerator & 0xff;
buffer[1] = (numerator & 0x0ff00) >> 8;
buffer[2] = denominator & 0xff;
buffer[3] = (denominator & 0xff00) >> 8;
return gdip_bitmapdata_property_add (bitmap_data, id, 8, PropertyTagTypeRational, buffer);
}
GpStatus
gdip_bitmapdata_property_add(BitmapData *bitmap_data, PROPID id, ULONG length, WORD type, VOID *value)
{
int property_count;
if (bitmap_data == NULL) {
return InvalidParameter;
}
property_count = bitmap_data->property_count;
if (bitmap_data->property == NULL) {
bitmap_data->property = GdipAlloc(sizeof(PropertyItem));
} else {
bitmap_data->property = gdip_realloc (bitmap_data->property, sizeof(PropertyItem) * (property_count + 1));
}
if (bitmap_data->property == NULL) {
bitmap_data->property_count = 0;
return OutOfMemory;
}
if ((value != NULL) && (length > 0)) {
bitmap_data->property[property_count].value = GdipAlloc(length);
if (bitmap_data->property[property_count].value == NULL) {
return OutOfMemory;
}
memcpy(bitmap_data->property[property_count].value, value, length);
} else {
bitmap_data->property[property_count].value = NULL;
}
bitmap_data->property[property_count].id = id;
bitmap_data->property[property_count].length = length;
bitmap_data->property[property_count].type = type;
bitmap_data->property_count++;
return Ok;
}
GpStatus
gdip_bitmapdata_property_remove_id(BitmapData *bitmap_data, PROPID id)
{
int i;
for (i = 0; i < bitmap_data->property_count; i++) {
if (bitmap_data->property[i].id == id) {
return gdip_bitmapdata_property_remove_index(bitmap_data, i);
}
}
return PropertyNotFound;
}
GpStatus
gdip_bitmapdata_property_remove_index(BitmapData *bitmap_data, int index)
{
BYTE *src;
BYTE *dest;
if (index >= bitmap_data->property_count) {
return PropertyNotFound;
}
/* We don't realloc the array, more overhead than savings */
if ((index + 1) < bitmap_data->property_count) {
if (bitmap_data->property[index].value != NULL) {
GdipFree(bitmap_data->property[index].value);
}
memmove(&bitmap_data->property[index], &bitmap_data->property[index + 1], (bitmap_data->property_count - index - 1) * sizeof(PropertyItem));
}
bitmap_data->property_count--;
return Ok;
}
GpStatus
gdip_bitmapdata_property_find_id(BitmapData *bitmap_data, PROPID id, int *index)
{
int i;
if (index == NULL) {
return InvalidParameter;
}
for (i = 0; i < bitmap_data->property_count; i++) {
if (bitmap_data->property[i].id == id) {
*index = i;
return Ok;
}
}
return PropertyNotFound;
}
GpStatus
gdip_bitmapdata_clone(BitmapData *src, BitmapData **dest, int count)
{
GpStatus status;
BitmapData *result;
int i;
if (dest == NULL) {
return InvalidParameter;
}
if (src == NULL) {
*dest = NULL;
return Ok;
}
result = GdipAlloc(sizeof(BitmapData) * count);
if (result == NULL) {
return OutOfMemory;
}
for (i = 0; i < count; i++) {
result[i].width = src[i].width;
result[i].height = src[i].height;
result[i].stride = src[i].stride;
result[i].pixel_format = src[i].pixel_format;
result[i].reserved = GBD_OWN_SCAN0; /* We're duplicating SCAN0, we always own it*/
result[i].dpi_horz = src[i].dpi_horz;
result[i].dpi_vert = src[i].dpi_vert;
result[i].image_flags = src[i].image_flags;
result[i].top = src[i].top;
result[i].left = src[i].left;
result[i].x = src[i].x;
result[i].y = src[i].y;
result[i].transparent = src[i].transparent;
if (src[i].scan0 != NULL) {
result[i].scan0 = GdipAlloc(src[i].stride * src[i].height);
if (result[i].scan0 == NULL) {
GdipFree(result);
return OutOfMemory;
}
memcpy(result[i].scan0, src[i].scan0, src[i].stride * src[i].height);
} else {
result[i].scan0 = NULL;
}
result[i].palette = gdip_palette_clone (src[i].palette);
result[i].property_count = src[i].property_count;
status = gdip_propertyitems_clone(src[i].property, &result[i].property, src[i].property_count);
if (status != Ok) {
int j;
for (j = 0; j < i; j++) {
if (result[j].scan0 != NULL) {
GdipFree(result[j].scan0);
}
if (result[j].property != NULL) {
gdip_propertyitems_dispose(result[j].property, result[j].property_count);
}
}
GdipFree(result);
return status;
}
}
*dest = result;
return Ok;
}
static GpStatus
gdip_bitmapdata_dispose(BitmapData *bitmap, int count)
{
int index;
if (bitmap == NULL) {
return Ok;
}
for (index = 0; index < count; index++) {
if ((bitmap[index].scan0 != NULL) && ((bitmap[index].reserved & GBD_OWN_SCAN0) != 0)) {
GdipFree(bitmap[index].scan0);
}
if (bitmap[index].palette != NULL) {
GdipFree(bitmap[index].palette);
}
gdip_propertyitems_dispose(bitmap[index].property, bitmap[index].property_count);
}
GdipFree(bitmap);
return Ok;
}
/* Add a new frame for the given dimension, if it does not exist. Return pointer to frame for dimension */
FrameData *
gdip_frame_add(GpBitmap *bitmap, const GUID *dimension)
{
int i;
if (bitmap == NULL) {
return NULL;
}
if (bitmap->frames == NULL) {
bitmap->frames = GdipAlloc(sizeof(FrameData));
if (bitmap->frames == NULL) {
return NULL;
}
bitmap->num_of_frames = 1;
bitmap->frames[0].count = 0;
bitmap->frames[0].bitmap = NULL;
bitmap->frames[0].frame_dimension = *dimension;
return bitmap->frames;
} else {
/* Check if this dimension already exists, if not, add it */
for (i = 0; i < bitmap->num_of_frames; i++) {
if (memcmp(&bitmap->frames[i].frame_dimension, dimension, sizeof(GUID)) == 0) {
return &bitmap->frames[i];
}
}
/* No frame for the requested dimension exists */
bitmap->num_of_frames++;
bitmap->frames = gdip_realloc (bitmap->frames, sizeof(FrameData) * (bitmap->num_of_frames));
if (bitmap->frames == NULL) {
return NULL;
}
bitmap->frames[bitmap->num_of_frames - 1].count = 0;
bitmap->frames[bitmap->num_of_frames - 1].bitmap = NULL;
bitmap->frames[bitmap->num_of_frames - 1].frame_dimension = *dimension;
return &bitmap->frames[bitmap->num_of_frames - 1];
}
}
/* Add a new BitmapData structure to an existing frame, return pointer to the new structure */
BitmapData *
gdip_frame_add_bitmapdata(FrameData *frame)
{
if (frame == NULL) {
return NULL;
}
if (frame->bitmap == NULL) {
frame->bitmap = GdipAlloc(sizeof(BitmapData));
if (frame->bitmap == NULL) {
return NULL;
}
gdip_bitmapdata_init(frame->bitmap);
frame->count++;
return frame->bitmap;
}
frame->bitmap = gdip_realloc (frame->bitmap, sizeof(BitmapData) * (frame->count + 1));
if (frame->bitmap == NULL) {
return NULL;
}
gdip_bitmapdata_init(&frame->bitmap[frame->count]);
frame->count++;
return &frame->bitmap[frame->count - 1];
}
ColorPalette*
gdip_palette_clone (ColorPalette *original)
{
if (!original)
return NULL;
/* ColorPalette definition already includes a (single) ARGB value */
int size = sizeof (ColorPalette) + sizeof (ARGB) * (original->Count - 1);
ColorPalette *result = GdipAlloc (size);
if (result)
memcpy (result, original, size);
return result;
}
GpStatus
gdip_bitmap_setactive(GpBitmap *bitmap, const GUID *dimension, int index)
{
int i;
if (bitmap == NULL) {
return InvalidParameter;
}
/* Invalidate the cached surface */
if (bitmap->surface != NULL) {
cairo_surface_destroy(bitmap->surface);
bitmap->surface = NULL;
}
if ((bitmap->num_of_frames == 0) || (bitmap->frames == NULL)) {
bitmap->active_frame = 0;
bitmap->active_bitmap_no = 0;
bitmap->active_bitmap = NULL;
return Ok;
}
if (dimension == NULL) {
if (bitmap->frames[0].count <= index) {
return InvalidParameter;
}
bitmap->active_frame = 0;
bitmap->active_bitmap_no = index;
bitmap->active_bitmap = &bitmap->frames[0].bitmap[index];
return Ok;
}
for (i = 0; i < bitmap->num_of_frames; i++) {
if (memcmp(&bitmap->frames[i].frame_dimension, dimension, sizeof(GUID)) == 0) {
if (bitmap->frames[i].count <= index) {
return InvalidParameter;
}
bitmap->active_frame = i;
bitmap->active_bitmap_no = index;
bitmap->active_bitmap = &bitmap->frames[i].bitmap[index];
return Ok;
}
}
bitmap->active_frame = 0;
bitmap->active_bitmap_no = 0;
bitmap->active_bitmap = NULL;
return InvalidParameter;
}
GpStatus
gdip_bitmap_clone (GpBitmap *bitmap, GpBitmap **clonedbitmap)
{
GpBitmap *result;
int frame;
int image;
GpStatus status;
result = (GpBitmap *) GdipAlloc (sizeof (GpBitmap));
if (result == NULL) {
return OutOfMemory;
}
/* Copy simple types */
result->type = bitmap->type;
result->image_format = bitmap->image_format;
result->num_of_frames = bitmap->num_of_frames;
result->active_frame = bitmap->active_frame;
result->active_bitmap_no = bitmap->active_bitmap_no;
result->active_bitmap = NULL;
result->cairo_format = bitmap->cairo_format;
result->surface = NULL;
/* Allocate and copy frames, properties and bitmap data */
if (bitmap->frames != NULL) {
result->frames = GdipAlloc(sizeof (FrameData) * result->num_of_frames);
for (frame = 0; frame < result->num_of_frames; frame++) {
result->frames[frame].count = bitmap->frames[frame].count;
result->frames[frame].frame_dimension = bitmap->frames[frame].frame_dimension;
result->frames[frame].bitmap = NULL;
status = gdip_bitmapdata_clone (bitmap->frames[frame].bitmap, &result->frames[frame].bitmap, bitmap->frames[frame].count);
if (status != Ok)
goto fail;
}
result->active_bitmap = &result->frames[result->active_frame].bitmap[result->active_bitmap_no];
} else {
bitmap->frames = NULL;
}
*clonedbitmap = result;
return Ok;
fail:
gdip_bitmap_dispose(result);
return status;
}
GpBitmap *
gdip_bitmap_new(void)
{
GpBitmap *result;
result = (GpBitmap *) GdipAlloc (sizeof (GpBitmap));
if (result != NULL) {
gdip_bitmap_init (result);
}
return result;
}
/* Create a new bitmap, and create a frame with the given dimension */
GpBitmap *
gdip_bitmap_new_with_frame (const GUID *dimension, BOOL add_bitmapdata)
{
GpBitmap *result;
FrameData *frame;
if (dimension == NULL) {
dimension = &gdip_image_frameDimension_page_guid;
}
result = gdip_bitmap_new();
if (result != NULL) {
frame = gdip_frame_add(result, dimension);
if ((frame != NULL) && add_bitmapdata) {
BitmapData *bitmap_data;
bitmap_data = gdip_frame_add_bitmapdata(frame);
if (bitmap_data != NULL) {
result->active_bitmap = bitmap_data;
}
}
}
return result;
}
GpStatus
gdip_bitmap_dispose (GpBitmap *bitmap)
{
/* Might be called with partially filled bitmaps, so check values to be freed instead of assuming */
if (!bitmap)
return Ok;
if (bitmap->frames) {
int frame;
for (frame = 0; frame < bitmap->num_of_frames; frame++) {
gdip_bitmapdata_dispose (bitmap->frames[frame].bitmap, bitmap->frames[frame].count);
}
GdipFree (bitmap->frames);
}
if (bitmap->surface)
cairo_surface_destroy (bitmap->surface);
GdipFree (bitmap);
return Ok;
}
/* coverity[+alloc : arg-*1] */
GpStatus
GdipCreateBitmapFromFile (GDIPCONST WCHAR* filename, GpBitmap **bitmap)
{
GpStatus status = GdipLoadImageFromFile (filename, (GpImage **) bitmap);
if (status == OutOfMemory)
status = InvalidParameter;
return status;
}
/* coverity[+alloc : arg-*1] */
GpStatus
GdipCreateBitmapFromFileICM (GDIPCONST WCHAR* filename, GpBitmap **bitmap)
{
/* ICM isn't supported */
return GdipCreateBitmapFromFile (filename, bitmap);
}
/* coverity[+alloc : arg-*5] */
GpStatus
GdipCreateBitmapFromScan0 (int width, int height, int stride, PixelFormat format, BYTE *scan0, GpBitmap **bitmap)
{
GpStatus status;
FrameData *frame;
GpBitmap *result;
BitmapData *bitmap_data;
int cairo_format;
int flags = 0;
if (width <= 0 || height <= 0) {
return InvalidParameter;
}
switch (format) {
case PixelFormat24bppRGB: {
cairo_format = CAIRO_FORMAT_RGB24;
break;
}
case PixelFormat32bppARGB:
case PixelFormat32bppPARGB:
flags = ImageFlagsHasAlpha;
/* fall through */
case PixelFormat32bppRGB:
cairo_format = CAIRO_FORMAT_ARGB32;
break;
case PixelFormat16bppRGB555:
case PixelFormat16bppRGB565:
/* fake them as 32bpp RGB as Cairo deprecated CAIRO_FORMAT_RGB16_565 support */
/* why 32bpp ? because that's the result of MS GDI+ when loading them, even if the bitmap is empty */
format = PixelFormat32bppRGB;
stride *= 2;
cairo_format = CAIRO_FORMAT_ARGB32;
break;
case PixelFormat8bppIndexed:
case PixelFormat4bppIndexed: {
cairo_format = CAIRO_FORMAT_A8;
break;
}
case PixelFormat1bppIndexed: {
cairo_format = CAIRO_FORMAT_A1;
break;
}
default: {
*bitmap = NULL;
return NotImplemented;
}
}
result = gdip_bitmap_new ();
if (result == NULL) {
return OutOfMemory;
}
result->image_format = MEMBMP;
result->cairo_format = cairo_format;
result->surface = NULL;
result->active_frame = 0;
result->active_bitmap_no = 0;
result->active_bitmap = NULL;
/* Create single 'page' frame, with single bitmap */
frame = gdip_frame_add(result, &gdip_image_frameDimension_page_guid);
if (frame == NULL) {
status = OutOfMemory;
goto fail;
}
bitmap_data = gdip_frame_add_bitmapdata(frame);
if (bitmap_data == NULL) {
status = OutOfMemory;
goto fail;
}
/* populate first bitmap in first frame */
bitmap_data->width = width;
bitmap_data->height = height;
bitmap_data->pixel_format = format;
bitmap_data->image_flags = flags;
if (stride == 0) {
if (gdip_is_an_indexed_pixelformat(format)) {
stride = ((gdip_get_pixel_format_depth(format) * width) + 7) / 8;
} else {
stride = (gdip_get_pixel_format_components (format) * gdip_get_pixel_format_depth (format) * width) / 8;
}
/* make sure the stride aligns the next row to a 32 bits boundary */
gdip_align_stride (stride);
}
bitmap_data->stride = stride;
if (scan0 == NULL) {
scan0 = GdipAlloc (stride * height);
if (scan0 == NULL) {
status = OutOfMemory;
goto fail;
}
if ((gdip_get_pixel_format_bpp(format) < 16) || gdip_is_an_alpha_pixelformat(format)) {
memset (scan0, 0, stride * height);
} else {
/* Since the pixel format is not an alpha pixel format (i.e., it is
* either PixelFormat24bppRGB or PixelFormat32bppRGB), the image should be
* initially black, not initially transparent. Thus, we need to set
* the alpha channel, which the user code doesn't think exists but
* Cairo is still paying attention to, to 0xFF.
*/
int x;
int y;
ARGB solid_black;
ARGB *scan;
/* Make sure the alpha channel is at the right end of the word. */
set_pixel_bgra (&solid_black, 0, 0, 0, 0, 0xFF);
for (y=0; y < height; y++) {
scan = (ARGB *)((char *)scan0 + y * stride);
for (x=0; x < width; x++) {
scan[x] = solid_black;
}
}
}
bitmap_data->reserved = GBD_OWN_SCAN0;
}
bitmap_data->scan0 = scan0;
/* Make sure indexed images have a palette */
if (gdip_is_an_indexed_pixelformat (format)) {
int palette_entries;
int header_size;
int bytes_needed;
const unsigned int *default_palette;
int i;
palette_entries = 1 << gdip_get_pixel_format_depth(format);
header_size = sizeof(ColorPalette) - sizeof(ARGB);
bytes_needed = header_size + palette_entries * sizeof(ARGB);
bitmap_data->palette = GdipAlloc (bytes_needed);
if (bitmap_data->palette == NULL) {
status = OutOfMemory;
goto fail;
}
bitmap_data->palette->Count = palette_entries;
bitmap_data->palette->Flags = COLOR_PALETTE_FLAGS_DEFAULT;
switch (format) {
case PixelFormat1bppIndexed:
default_palette = default_Format1bppIndexed_palette;
bitmap_data->palette->Flags |= COLOR_PALETTE_FLAGS_GREYSCALE;
break;
case PixelFormat4bppIndexed:
default_palette = default_Format4bppIndexed_palette;
break;
case PixelFormat8bppIndexed:
default_palette = default_Format8bppIndexed_palette;
bitmap_data->palette->Flags |= COLOR_PALETTE_FLAGS_HALFTONE;
break;
default:
default_palette = NULL; /* Suppress unassigned val used warning */
palette_entries = 0; /* make sure we don't try to access default_palette later */
}
#if WORDS_BIGENDIAN
for (i=0; i < palette_entries; i++) {
set_pixel_bgra (bitmap_data->palette->Entries, (i << 2), default_palette[i],
(default_palette[i] >> 8), (default_palette[i] >> 16), (default_palette[i] >> 24));
}
#else
memcpy (bitmap_data->palette->Entries, default_palette, palette_entries * 4);
#endif
} else {
bitmap_data->palette = NULL;
}
gdip_bitmap_setactive(result, NULL, 0);
*bitmap = result;
return Ok;
fail:
gdip_bitmap_dispose(result);
return status;
}
/* coverity[+alloc : arg-*3] */
GpStatus
GdipCreateBitmapFromGraphics (int width, int height, GpGraphics *graphics, GpBitmap **bitmap)
{
GpBitmap *result;
FrameData *frame;
BitmapData *bitmap_data;
int stride;
stride = width * 4;
gdip_align_stride (stride);
result = gdip_bitmap_new ();
result->image_format = MEMBMP;
result->cairo_format = CAIRO_FORMAT_ARGB32;
frame = gdip_frame_add(result, &gdip_image_frameDimension_page_guid);
if (frame == NULL) {
goto fail;
}
bitmap_data = gdip_frame_add_bitmapdata(frame);
if (bitmap_data == NULL) {
goto fail;
}
bitmap_data->width = width;
bitmap_data->height = height;
bitmap_data->stride = stride;
bitmap_data->pixel_format = PixelFormat32bppARGB;
bitmap_data->reserved = GBD_OWN_SCAN0;
bitmap_data->scan0 = GdipAlloc(stride * height);
if (bitmap_data->scan0 == NULL) {
goto fail;
}
memset (bitmap_data->scan0, 0, stride * height);
gdip_bitmap_setactive(result, NULL, 0);
*bitmap = result;
return Ok;
fail:
gdip_bitmap_dispose(result);
return OutOfMemory;
}
GpStatus
GdipCreateBitmapFromHBITMAP (HBITMAP hbm, HPALETTE hpal, GpBitmap** bitmap)
{
if (!bitmap || !hbm)
return InvalidParameter;
return GdipCloneImage ((GpImage *)hbm, (GpImage**)bitmap);
}
GpStatus
GdipCreateHBITMAPFromBitmap (GpBitmap* bitmap, HBITMAP *hbmReturn, ARGB background)
{
/*
* Note: the handle must survive disposing the bitmap. This means that there's no interoperable way to free this memory.
* For libgdiplus you must use GdipDisposeImage on the handle, while on Windows you should call the DeleteObject function.
*/
GpStatus status = GdipCloneImage ((GpImage *)bitmap, (GpImage**)hbmReturn);
if (status != Ok)
return status;
((GpImage*)*hbmReturn)->image_format = MEMBMP;
((GpImage*)*hbmReturn)->active_bitmap->image_flags |= ImageFlagsUndocumented; /* 0x00040000 */
((GpImage*)*hbmReturn)->active_bitmap->image_flags &= ~ImageFlagsHasAlpha; /* 0x00000002 */
return Ok;
}
GpStatus
GdipCreateBitmapFromHICON (HICON hicon, GpBitmap** bitmap)
{
GpStatus status;
if (!hicon || !bitmap)
return InvalidParameter;
status = GdipCloneImage ((GpImage *)hicon, (GpImage**)bitmap);
if (status == Ok) {
/* ColorPalette definition includes one ARGB member (ARGB Entries[1]). In reality there are
* Count entries allocated, where Count can be 0 (and required to substract the ARGB size)
*/
/* coverity[buffer_alloc] */
ColorPalette *palette = (ColorPalette*)GdipAlloc (sizeof (ColorPalette) - sizeof (ARGB));
if (!palette)
return OutOfMemory;
palette->Flags = 0;
palette->Count = 0;
status = GdipSetImagePalette ((GpImage*)*bitmap, palette);
(*bitmap)->image_format = MEMBMP;
(*bitmap)->active_bitmap->image_flags |= ImageFlagsUndocumented;/* 0x00040000 */
(*bitmap)->active_bitmap->image_flags &= ~ImageFlagsHasAlpha; /* 0x00000002 */
GdipFree (palette);
}
return status;
}
GpStatus
GdipCreateHICONFromBitmap (GpBitmap* bitmap, HICON *hbmReturn)
{
/*
* Note: the handle must survive disposing the bitmap. This means that there's no interoperable way to free this memory.
* For libgdiplus you must use GdipDisposeImage on the handle, while on Windows you should call the DestroyIcon function.
*/
return GdipCloneImage ((GpImage *)bitmap, (GpImage**)hbmReturn);
}
GpStatus
GdipCreateBitmapFromResource (HINSTANCE hInstance, GDIPCONST WCHAR *lpBitmapName, GpBitmap** bitmap)
{
return(NotImplemented);
}
/* coverity[+alloc : arg-*6] */
GpStatus
GdipCloneBitmapAreaI (int x, int y, int width, int height, PixelFormat format,
GpBitmap *original, GpBitmap **bitmap)
{
GpBitmap *result;
FrameData *frame;
BitmapData *bitmap_data;
Rect sr = { x, y, width, height };
Rect dr = { 0, 0, width, height };
GpStatus status;
if ((original == NULL) || (bitmap == NULL) || (original->active_bitmap == NULL)) {
return InvalidParameter;
}
if (((x + width) > original->active_bitmap->width) || ((y + height) > original->active_bitmap->height)) {
return InvalidParameter;
}
result = gdip_bitmap_new_with_frame(NULL, TRUE);
if (result == NULL) {
return OutOfMemory;
}
result->image_format = original->image_format;
status = gdip_bitmap_clone_data_rect (original->active_bitmap, &sr, result->active_bitmap, &dr);
if (status != Ok) {
goto fail;
}
result->cairo_format = original->cairo_format;
*bitmap = result;
return Ok;
fail:
gdip_bitmap_dispose (result);
return status;
}
/* coverity[+alloc : arg-*6] */
GpStatus
GdipCloneBitmapArea (float x, float y, float w, float h, PixelFormat format,
GpBitmap *original, GpBitmap **bitmap)
{
return GdipCloneBitmapAreaI ((int) x, (int) y, (int) w, (int) h, format,
original, bitmap);
}
static void
gdip_copy_strides (void *dst, int dstStride, void *src, int srcStride, int realBytes, int height)
{
int i;
for (i = 0; i < height; i++) {
memcpy (dst, src, realBytes);
dst += dstStride;
src += srcStride;
}
}
/*
* Copy srcRect region in srcData to destRect region in destData. No conversion is done. Assumes
* BitmapData is straight from a GpBitmap. src and dest rects must be the same width/height and
* bits must be of the same PixelFormat.
*/
static GpStatus
gdip_bitmap_clone_data_rect (BitmapData *srcData, Rect *srcRect, BitmapData *destData, Rect *destRect)
{
int dest_components;
int dest_depth;
if ((srcData == NULL) || (srcRect == NULL) || (destData == NULL) || (destRect == NULL) || (srcRect->Width != destRect->Width) || (srcRect->Height != destRect->Height)) {
return InvalidParameter;
}
if (!gdip_is_a_supported_pixelformat (srcData->pixel_format)) {
return NotImplemented;
}
dest_components = gdip_get_pixel_format_components (destData->pixel_format);
if (destData->scan0 == NULL) {
dest_components = gdip_get_pixel_format_components (srcData->pixel_format);
dest_depth = gdip_get_pixel_format_depth (srcData->pixel_format);
destData->pixel_format = srcData->pixel_format;
destData->stride = ((destRect->Width * dest_components * dest_depth) >> 3);
gdip_align_stride (destData->stride);
destData->scan0 = GdipAlloc (destData->stride * destRect->Height);
if (destData== NULL) {
return OutOfMemory;
}
destData->width = destRect->Width;
destData->height = destRect->Height;
destData->pixel_format = srcData->pixel_format;
destData->reserved = GBD_OWN_SCAN0;
if (srcData->palette) {
destData->palette = gdip_palette_clone (srcData->palette);
if (!destData->palette) {
GdipFree (destData->scan0);
destData->scan0 = NULL;
return OutOfMemory;
}
}
}
if (!gdip_is_an_indexed_pixelformat (srcData->pixel_format)) {
gdip_copy_strides (destData->scan0, destData->stride,
srcData->scan0 + (srcData->stride * srcRect->Y) + (gdip_get_pixel_format_components (srcData->pixel_format)
* srcRect->X), srcData->stride, destRect->Width * dest_components, destRect->Height);
} else {
int src_depth;
int src_first_x_bit_index;
int width_bits;
int src_first_x_bit_offset_into_byte;
src_depth = gdip_get_pixel_format_depth (srcData->pixel_format);
/* first, check if the bits are aligned onto a byte boundary */
src_first_x_bit_index = srcRect->X * src_depth;
width_bits = destRect->Width * src_depth;
src_first_x_bit_offset_into_byte = src_first_x_bit_index & 7;
if (src_first_x_bit_offset_into_byte == 0) {
/* the fast path: no mid-byte bit mangling required :-)
* this will always be the case for 8-bit images.
* basically, the source bits are aligned to the destination
* bytes, and it doesn't matter if the width isn't a multiple
* of 8 bits, because the remainder is guaranteed to be
* allocated by the stride, and will be ignored because the
* width will indicate fewer pixels than actually end up being
* copied.
*/
gdip_copy_strides (
destData->scan0, destData->stride,
srcData->scan0 + (src_first_x_bit_index / 8) + (srcData->stride * srcRect->Y),
srcData->stride, width_bits / 8, destRect->Height);
} else {
/* the not-so-fast path: no bits are aligned, so the entire image requires bit juggling. */
BYTE *src_scan;
BYTE *src_scan0;
BYTE *dest_scan;
BYTE *dest_scan0;
unsigned short buffer;
int left_shift;
int x;
int y;
src_scan0 = srcData->scan0;
dest_scan0 = destData->scan0;
left_shift = src_first_x_bit_offset_into_byte;
/* move the src_scan0 up to the first byte with pixel data involved in the copy */
src_scan0 += srcRect->Y * srcData->stride;
src_scan0 += (src_first_x_bit_offset_into_byte / 8);
for (y=0; y < destRect->Height; y++) {
src_scan = src_scan0 + y * srcData->stride;
dest_scan = dest_scan0 + y * destData->stride;
/* jump-start the packing function. it avoids double-sampling the source bits by
* using buffer as a shift register; bits 8-15 are the current packed dest pixel,
* and some of bits 0-7 are not used, to permit alignment of the pixel data.
*/
buffer = src_scan[0] << left_shift;
for (x = 1; x < destRect->Width; x++) {
buffer <<= 8;
buffer |= src_scan[x] << left_shift;
dest_scan[0] = (buffer >> 8);
}
}
}
}
return Ok;
}
/*
* According to GDI+ testing,
* RGB[A] PixelFormats can be converted to/from any other RGB[A] pixel format, with or without alpha.
* We should support all of these:
*
* 32bpp argb - 16bpp argb 1555
* 32bpp argb - 16bpp rgb 555
* 32bpp argb - 16bpp rgb 565
* 32bpp argb - 24bpp rgb 888
* 32bpp argb - 32bpp Pargb
* 32bpp argb - 32bpp rgb
*
* Upconversion is allowed (e.g 8bpp indexed to 32bpp rgb), but no downconversion (32bpp to 8bpp indexed)
*
*/
static int
gdip_is_pixel_format_conversion_valid (PixelFormat src, PixelFormat dest)
{
if (src == dest) {
return 1;
}
/* non-GDI supported formats can't be converted */
if (!(src & PixelFormatGDI)) {
return 0;
}
/* We don't allow converting *to* indexed formats */
if (dest & PixelFormatIndexed) {
return 0;
}
/* These are the RGB formats */
if ((src & PixelFormatGDI) && !(src & PixelFormatExtended)) {
/* all of these should be supported, but we only report the
* ones we really can do for now */
/* We can't handle converting to/from the 565/555/1555 ones */
if ((src & 0xff00) == 16 || (dest & 0xff00) == 16) {
return 0;
}
return 1;
}
return 0;
}
#if FALSE
/* PixelFormat24bppRGB is internally stored by Cairo as a four bytes. Convert it to 3-byte (RGB) */
int
gdip_from_ARGB_to_RGB (BYTE *src, int width, int height, int stride, BYTE **dest, int* dest_stride)
{
int x;
int y;
BYTE *result;
BYTE *pos_src;
BYTE *pos_dest;
int src_components = 4; /* ARGB */
int dest_components = 3; /* RGB */
int stride = ((dest_components << 3) * width) >> 3);
gdip_align_stride (stride);
*dest_stride = stride;
result = GdipAlloc (*dest_stride * height);
if (result == NULL) {
return OutOfMemory;
}
memset (result, 0, *dest_stride * height);
for (y = 0, pos_src = src, pos_dest = result; y < height; y++, pos_src += stride, pos_dest += *dest_stride) {
for (x = 0; x < width; x++) {
pos_dest[0 + (dest_components * x)] = pos_src[0 + (src_components * x)];
pos_dest[1 + (dest_components * x)] = pos_src[1 + (src_components * x)];
pos_dest[2 + (dest_components * x)] = pos_src[2 + (src_components * x)];
}
}
*dest = result;
return Ok;
}
/* PixelFormat24bppRGB is internally stored by Cairo as a three bytes. Convert it to 4-byte (ARGB) */
int
gdip_from_RGB_to_ARGB (BYTE *src, int width, int height, int stride, BYTE **dest, int* dest_stride)
{
int x;
int y;
BYTE *result;
BYTE *pos_src;
BYTE *pos_dest;
int src_components = 3; /* RGB */
int dest_components = 4; /* ARGB */
int stride = ((dest_components << 3) * width) >> 3);
gdip_align_stride (stride);
*dest_stride = stride;
result = GdipAlloc (*dest_stride * height);
if (result == NULL) {
return OutOfMemory;
}
memset (result, 0, *dest_stride * height);
for (y = 0, pos_src = src, pos_dest = result; y < height; y++, pos_src += stride, pos_dest += *dest_stride) {
for (x = 0; x < width; x++) {
pos_dest[0 + (dest_components * x)] = pos_src[0 + (src_components * x)];
pos_dest[1 + (dest_components * x)] = pos_src[1 + (src_components * x)];
pos_dest[2 + (dest_components * x)] = pos_src[2 + (src_components * x)];
pos_dest[3 + (dest_components * x)] = 0xff;
}
}
*dest = result;
return Ok;
}
#endif
GpStatus
gdip_init_pixel_stream (StreamingState *state, BitmapData *data, int x, int y, int w, int h)
{
if ((state == NULL) || (data == NULL) || (data->scan0 == NULL)) {
return InvalidParameter;
}
/* Ensure that the rectangle requested is legal. */
if ((x < 0) || (y < 0) || ((x + w) > data->width) || ((y + h) > data->height)) {
return InvalidParameter;
}
/* Initialize the StreamingState structure to point at the first pixel. */
state->region.X = x;
state->region.Y = y;
state->region.Width = w;
state->region.Height = h;
state->x = x;
state->y = y;
state->p = -1; /* ensure that the buffer will be preloaded on the first call, for indexed formats */
/* The following computation will compute the byte pointer that _contains_ the first
* pixel; this doesn't necessarily mean that the pixel is aligned to the byte. This
* will be handled in gdip_pixel_stream_get_next () each time it starts a new row.
*/
state->scan = (BYTE*)(data->scan0) + y * data->stride;
switch (data->pixel_format) {
case PixelFormat1bppIndexed:
state->one_pixel_mask = 0x01;
state->one_pixel_shift = 1;
state->pixels_per_byte = 8;
state->scan += (x >> 3); /* x * 1 / 8 */
break;
case PixelFormat4bppIndexed:
state->one_pixel_mask = 0x0F;
state->one_pixel_shift = 4;
state->pixels_per_byte = 2;
state->scan += (x >> 1); /* x * 4 / 8 */
break;
case PixelFormat8bppIndexed:
state->one_pixel_mask = 0xFF;
state->one_pixel_shift = 8;
state->pixels_per_byte = 1;
state->scan += x; /* x * 8 / 8 */
break;
case PixelFormat24bppRGB:
/* GDI+ use 3 bytes for 24 bpp while Cairo use 4 bytes */
if (data->reserved & GBD_TRUE24BPP) {
state->pixels_per_byte = -3;
state->scan += ((x * 3) >> 3); /* x * 3 / 8 */
break;
}
/* else continue (don't break) */
case PixelFormat32bppRGB:
default:
/* indicate full RGB processing */
state->pixels_per_byte = -(gdip_get_pixel_format_bpp (data->pixel_format) >> 3);
state->scan -= x * state->pixels_per_byte;
break;
}
state->data = data;
return Ok;
}
static BOOL
gdip_pixel_stream_has_next (StreamingState *state)
{
if (state != NULL) {
return (state->p >= 0)
|| ((state->y < (state->region.Y + state->region.Height))
&& (state->x < (state->region.X + state->region.Width)));
} else {
return FALSE;
}
}
unsigned int /* <-- can be an ARGB or a palette index */
gdip_pixel_stream_get_next (StreamingState *state)
{
unsigned int ret;
if (state == NULL) {
int bright_pink;
set_pixel_bgra(&bright_pink, 0,
0xFF, 0x00, 0xFF, 0xFF); /* bright pink; hopefully this will get somebody's attention :-) */
return bright_pink;
}
/* Note: This function does not check whether the end of the region has been hit. This function can
* potentially overrun memory buffers! gdip_pixel_stream_has_next () must be used in conjunction
* with this function.
*/
if (state->pixels_per_byte == 1) {
/* A fast path for 8-bit indexed data: pixels are byte-aligned, so no special unpacking is required. */
ret = *state->scan;
state->scan++;
state->x++;
if (state->x >= (state->region.X + state->region.Width)) {
state->x = state->region.X;
state->y++;
state->scan = (BYTE*)(state->data->scan0)
+ state->y * state->data->stride
+ state->x;
}
} else if (state->pixels_per_byte > 0) {
/* We have an indexed format (the RGB formats can't fit a whole pixel into a single byte). */
if (state->p < 0) {
state->buffer = *state->scan;
state->scan++;
state->p = 0;
if (state->x == state->region.X) {
/* First pixel of the row; check whether it is aligned to the byte or not. */
int index_into_byte = state->x & (state->pixels_per_byte - 1);
if (index_into_byte != 0) {
/* Not aligned; need to advance the buffer to the
* first pixel in the stream region.
*/
state->buffer <<= (index_into_byte * state->one_pixel_shift);
state->p = index_into_byte;
}
}
}
state->buffer <<= state->one_pixel_shift;
ret = (state->buffer >> 8) & state->one_pixel_mask;
state->x++;
state->p++;
/* Have we hit the end of the buffer? */
if (state->p >= state->pixels_per_byte)
state->p = -1;
if (state->x >= (state->region.X + state->region.Width)) {
state->x = state->region.X;
state->y++;
state->scan = (BYTE*)(state->data->scan0)
+ state->y * state->data->stride
+ state->x * gdip_get_pixel_format_bpp (state->data->pixel_format) / 8;
state->p = -1;
}
} else {
/* We have an RGB format. In the current implementation, these are always stored as
* CAIRO_FORMAT_ARGB. This makes this section very easy to implement. If native
* support for 15- and 16-bit pixel formats needs to be added in the future, though,
* then this is where it needs to be done.
*
* In order to simplify advancing the state->scan pointer, the state->pixels_per_byte
* member is set to the number of bytes per pixel, negated. That is, for 24-bit
* formats, it is set to -3, and for 32-bit formats, it is set to -4.
*
* Note that pixel streams do not support 48- and 64-bit data at this time.
*/
if (state->pixels_per_byte == -4) {
#if WORDS_BIGENDIAN
ret = state->scan [0] | (state->scan [1] << 8) | (state->scan [2] << 16) | (state->scan [3] << 24);
#else
ret = *(unsigned int *)state->scan;
#endif
} else {
/* Special case: 24-bit data needs to have the cairo format alpha component forced
* to 0xFF, or many operations will do nothing (or do strange things if the alpha
* channel contains garbage).
*/
ret = state->scan [0] | (state->scan [1] << 8) | (state->scan [2] << 16) | 0xFF000000;
}
state->scan -= state->pixels_per_byte;
state->x++;
if (state->x >= (state->region.X + state->region.Width)) {
state->x = state->region.X;
state->y++;
state->scan = (BYTE*)(state->data->scan0)
+ state->y * state->data->stride
+ state->x * -state->pixels_per_byte;
}
}
return ret;
}
static void
gdip_pixel_stream_set_next (StreamingState *state, unsigned int pixel_value)
{
if (state == NULL) {
return;
}
/* Note: This function does not check whether the end of the region has been hit. This function can
* potentially overrun memory buffers! gdip_pixel_stream_has_next () must be used in conjunction
* with this function.
*/
if (state->pixels_per_byte == 1) {
/* A fast path for 8-bit indexed data: pixels are byte-aligned, so no special packing is required. */
*state->scan = pixel_value;
state->scan++;
state->x++;
if (state->x >= (state->region.X + state->region.Width)) {
state->x = state->region.X;
state->y++;
state->scan = (BYTE*)(state->data->scan0)
+ state->y * state->data->stride
+ state->x;
}
} else if (state->pixels_per_byte > 0) {
/* We have an indexed format (the RGB formats can't fit a whole pixel into a single byte). */
if (state->p < 0) {
state->p = 0;
if (state->x == state->region.X) {
/* First pixel of the row; check whether it is aligned to the byte or not. */
int index_into_byte = state->x & (state->pixels_per_byte - 1);
if (index_into_byte == 0) {
/* It is aligned; all we need to do is clear the buffer. */
state->buffer = 0;
}
else {
/* It is not aligned; the buffer needs to be pre-loaded with those
* pixels that are to the left of the first pixel to be set.
*/
state->buffer = (*state->scan << (index_into_byte * state->one_pixel_shift));
state->p = index_into_byte;
}
}
}
state->buffer <<= state->one_pixel_shift;
state->buffer |= ((pixel_value & state->one_pixel_mask) << 8);
state->x++;
state->p++;
/* Have we hit the end of the buffer? */
if (state->p >= state->pixels_per_byte) {
*state->scan = (state->buffer >> 8);
state->scan++;
state->p = -1;
}
if (state->x >= (state->region.X + state->region.Width)) {
if (state->p >= 0) {
int existing_mask = 0;
while (state->p < state->pixels_per_byte) {
existing_mask <<= state->one_pixel_shift;
existing_mask |= state->one_pixel_mask;
state->buffer <<= state->one_pixel_shift;
state->p++;
}
*state->scan = (*state->scan & existing_mask) | (state->buffer >> 8);
}
state->x = state->region.X;
state->y++;
state->scan = (BYTE*)(state->data->scan0)
+ state->y * state->data->stride
+ state->x * gdip_get_pixel_format_bpp (state->data->pixel_format) / 8;
state->p = -1;
}
} else {
/* We have an RGB format. In the current implementation, these are always stored as
* CAIRO_FORMAT_ARGB. This makes this section very easy to implement. If native
* support for 15- and 16-bit pixel formats needs to be added in the future, though,
* then this is where it needs to be done.
*
* In order to simplify advancing the state->scan pointer, the state->pixels_per_byte
* member is set to the number of bytes per pixel, negated. That is, for 24-bit
* formats, it is set to -3, and for 32-bit formats, it is set to -4.
*
* Note that pixel streams do not support 48- and 64-bit data at this time.
*/
if (state->pixels_per_byte == -4) {
#if WORDS_BIGENDIAN
state->scan [0] = (pixel_value >> 24);
state->scan [1] = (pixel_value >> 16);
state->scan [2] = (pixel_value >> 8);
state->scan [3] = (state->data->pixel_format == PixelFormat32bppRGB) ? 0xFF : pixel_value;
#else
if (state->data->pixel_format == PixelFormat32bppRGB)
pixel_value |= 0xFF000000;
*(unsigned int *)state->scan = pixel_value;
#endif
} else {
/* ensure we don't get one byte over our allocated buffer */
#if WORDS_BIGENDIAN
state->scan [0] = (pixel_value >> 24);
state->scan [1] = (pixel_value >> 16);
state->scan [2] = (pixel_value >> 8);
#else
state->scan [2] = (pixel_value >> 16);
state->scan [1] = (pixel_value >> 8);
state->scan [0] = pixel_value;
#endif
}
state->scan -= state->pixels_per_byte;
state->x++;
if (state->x >= (state->region.X + state->region.Width)) {
state->x = state->region.X;
state->y++;
state->scan = (BYTE*)(state->data->scan0)
+ state->y * state->data->stride
+ state->x * -state->pixels_per_byte;
}
}
}
static BOOL /* <-- TRUE if optimisation was possible and copy done, else FALSE */
gdip_pixel_stream_copy_optimized (StreamingState *dst_state, StreamingState *src_state)
{
unsigned int ret;
if (src_state == NULL) return FALSE;
if (dst_state == NULL) return FALSE;
/* in a first shot we only support copy operations for cases where source and destination storage width is exactly the same */
if (src_state->pixels_per_byte != dst_state->pixels_per_byte) return FALSE;
//if (src_state->data->pixel_format == PixelFormat32bppRGB) return FALSE;
/* if the target is in 32bpp format with only RGB but no alpha then we should forcefully set all alpha bits to 0xff -> optimize this later! */
if (dst_state->pixels_per_byte == -4) {
if (dst_state->data->pixel_format == PixelFormat32bppRGB) return FALSE;
}
/* optimisation code differs for different bytes per pixel (== pixels per byte) */
if (src_state->pixels_per_byte == 1) {
/* A fast path for 8-bit indexed data: pixels are byte-aligned, so no special unpacking is required. */
return FALSE; /* optimize this later */
} else if (src_state->pixels_per_byte > 0) {
/* We have an indexed format (the RGB formats can't fit a whole pixel into a single byte). */
return FALSE; /* optimize this later */
} else if (src_state->pixels_per_byte == 0) {
return FALSE; /* unknown format - dont know how to handle; this is a sanity check for crash prevention in the following code */
} else {
/* We have an RGB format. In the current implementation, these are always stored as
* CAIRO_FORMAT_ARGB. This makes this section very easy to implement. If native
* support for 15- and 16-bit pixel formats needs to be added in the future, though,
* then this is where it needs to be done.
*
* In order to simplify advancing the state->scan pointer, the state->pixels_per_byte
* member is set to the number of bytes per pixel, negated. That is, for 24-bit
* formats, it is set to -3, and for 32-bit formats, it is set to -4.
*
* Note that pixel streams do not support 48- and 64-bit data at this time.
*/
#if WORDS_BIGENDIAN
return FALSE; /* optimize this later */
#else
int bytes_per_pixel = -src_state->pixels_per_byte;
int bytes_per_line = bytes_per_pixel * src_state->region.Width;
int bytes_per_region = bytes_per_line * src_state->region.Height;
int src_stride = src_state->data->stride;
int dst_stride = dst_state->data->stride;
/* calculate region base addresses (this might resemble the current ..._state->scan value, but we simply dont care) */
BYTE* src_region = (BYTE*)(src_state->data->scan0) + src_state->region.Y * src_stride + src_state->region.X * bytes_per_pixel;
BYTE* dst_region = (BYTE*)(dst_state->data->scan0) + dst_state->region.Y * dst_stride + dst_state->region.X * bytes_per_pixel;
/* check if thze data at both locations is in a perfectly consecutive arrangement */
if ((src_state->region.Width * bytes_per_pixel == src_stride)
&& (dst_state->region.Width * bytes_per_pixel == dst_stride)) {
memcpy (dst_region, src_region, bytes_per_region);
} else {
BYTE *src = src_region;
BYTE *dst = dst_region;
int lines;
for (lines = src_state->region.Height; lines; lines--) {
memcpy (dst, src, bytes_per_line);
src += src_stride;
dst += dst_stride;
}
}
/* all data got copied */
/* adjust the position index and the scan value to the end of the region */
src_state->x = src_state->region.X + src_state->region.Width;
src_state->y = src_state->region.Y + src_state->region.Height;
src_state->scan += src_state->region.Y * src_stride;
dst_state->x = dst_state->region.X + dst_state->region.Width;
dst_state->y = dst_state->region.Y + dst_state->region.Height;
dst_state->scan += dst_state->region.Y * dst_stride;
#endif
}
return TRUE;
}
/**
* srcData - input data
* srcRect - rectangle of input data to place in destData
* destData - where to place output; only the PixelFormat field is needed,
* which specifies the output type.
* destRect - destination rectangle in output.
*
* assumes that:
* - non-null data
* - rectangles are valid
* - the pixel format conversion has already been validated.
*/
static GpStatus
gdip_bitmap_change_rect_pixel_format (BitmapData *srcData, Rect *srcRect, BitmapData *destData, Rect *destRect)
{
PixelFormat srcFormat;
PixelFormat destFormat;
StreamingState srcStream;
StreamingState destStream;
Rect effectiveDestRect;
GpStatus status;
srcFormat = srcData->pixel_format;
destFormat = destData->pixel_format;
if (!gdip_is_pixel_format_conversion_valid (srcFormat, destFormat))
return InvalidParameter;
if (!destData->scan0)
return InvalidParameter;
/* Check that the destRect lies fully within the destData buffer. */
if ((destRect->X + destRect->Width > destData->width) || (destRect->Y + destRect->Height > destData->height))
return InvalidParameter;
effectiveDestRect = *destRect;
if (effectiveDestRect.Width > srcRect->Width) {
effectiveDestRect.Width = srcRect->Width;
}
if (effectiveDestRect.Height > srcRect->Height) {
effectiveDestRect.Height = srcRect->Height;
}
/* Fire up the pixel streams. */
status = gdip_init_pixel_stream (&srcStream, srcData, srcRect->X, srcRect->Y, srcRect->Width, srcRect->Height);
if (status != Ok) {
return status;
}
status = gdip_init_pixel_stream (&destStream, destData, effectiveDestRect.X, effectiveDestRect.Y, effectiveDestRect.Width, effectiveDestRect.Height);
if (status != Ok) {
return status;
}
/* Move the data; special path going from indexed to not-indexed */
if ((srcFormat & PixelFormatIndexed) && !(destFormat & PixelFormatIndexed)) {
int pixel;
while (gdip_pixel_stream_has_next (&srcStream)) {
pixel = gdip_pixel_stream_get_next (&srcStream);
/* Look up the pixel in the palette and get the ARGB value */
pixel = srcData->palette->Entries[pixel];
#if G_BYTE_ORDER != G_LITTLE_ENDIAN
pixel = GUINT32_FROM_LE (pixel);
#endif
gdip_pixel_stream_set_next (&destStream, pixel);
}
} else {
if (!gdip_pixel_stream_copy_optimized (&destStream, &srcStream)) {
while (gdip_pixel_stream_has_next (&srcStream)) {
gdip_pixel_stream_set_next (&destStream, gdip_pixel_stream_get_next (&srcStream));
}
}
}
return Ok;
}
#if FALSE
static BOOL
gdip_is_a_32bit_pixelformat (PixelFormat format)
{
switch (format) {
case PixelFormat32bppRGB:
case PixelFormat32bppARGB:
case PixelFormat32bppPARGB: /* all of these use CAIRO_FORMAT_ARGB, which is 4 bytes wide */
return TRUE;
default:
return FALSE;
}
}
BOOL
gdip_can_window_without_copy (BitmapData *data, Rect *rect, int format)
{
int bpp = gdip_get_pixel_format_bpp (format);
if (format != data->pixel_format) {
/* can't possibly reinterpret bits from one indexed pixel
* format as being of another indexed pixel format...
*/
if (gdip_is_an_indexed_pixelformat (format) || gdip_is_an_indexed_pixelformat (data->pixel_format)) {
return FALSE;
}
/* ...but we can probably handle 24-bit<->32-bit and
* 32-bit alpha<->32-bit opaque without copying data,
* since these are all stored as CAIRO_FORMAT_ARGB
* internally.
*/
if (!gdip_is_a_32bit_pixelformat (format) || !gdip_is_a_32bit_pixelformat (data->pixel_format)) {
return FALSE;
}
}
/* okay, so the pixel formats are compatible. now, make sure
* the rectangle lies on byte boundaries; if it doesn't, then
* pixels will have to be shuffled. =/
*/
/* 8bpp and above are guaranteed to be byte-aligned */
if (bpp >= 8) {
return TRUE;
} else {
int left_bit_offset = rect->X * bpp;
int width_bit_count = rect->Width * bpp;
/* check whether the values are byte-aligned */
return ((left_bit_offset & 7) | (width_bit_count & 7)) == 0;
}
}
void
gdip_make_alpha_opaque (BitmapData *data)
{
BYTE *scan0;
int y;
int x;
int o;
int f;
/* sanity check; make sure we aren't mangling any image data */
if ((data->pixel_format != PixelFormat32bppARGB) && (data->pixel_format != PixelFormat32bppRGB)) {
return;
}
scan0 = (BYTE*)data->scan0;
f = data->stride - 4 * data->width;
for (y = 0, o = 0; y < data->height; y++, o += f) {
for (x = 0; x < data->width; x++, o += 4) {
scan0[o + 3] = 0xff; /* set alpha to fully-opaque */
}
}
}
#endif
GpStatus
GdipBitmapLockBits (GpBitmap *bitmap, GDIPCONST Rect *srcRect, UINT flags, PixelFormat format, BitmapData *locked_data)
{
int dest_pixel_format_bpp;
int dest_stride;
int dest_size;
BYTE *dest_scan0;
Rect destRect;
GpStatus status;
BitmapData *root_data;
if (!bitmap || !srcRect || !locked_data)
return InvalidParameter;
root_data = bitmap->active_bitmap;
/* Is this bitmap already locked? */
if (root_data->reserved & GBD_LOCKED)
return Win32Error;
/* Make sure the srcRect makes sense */
if ((srcRect->X < 0) || (srcRect->Y < 0) || (srcRect->Width < 0) || (srcRect->Height < 0))
return InvalidParameter;
if (((srcRect->X + srcRect->Width) > root_data->width) || ((srcRect->Y + srcRect->Height) > root_data->height))
return InvalidParameter;
/* if the current and specified format are different, that the bitmap is indexed and that we ask for write then... */
if ((root_data->pixel_format != format) && gdip_is_an_indexed_pixelformat (root_data->pixel_format) &&
((flags & ImageLockModeWrite) != 0)) {
return InvalidParameter;
}
if (!gdip_is_a_supported_pixelformat (format))
return NotImplemented;
destRect.X = 0;
destRect.Y = 0;
destRect.Width = srcRect->Width;
destRect.Height = srcRect->Height;
/* Common stuff */
if ((flags & ImageLockModeWrite) != 0) {
locked_data->reserved |= GBD_WRITE_OK;
locked_data->image_flags &= ~ImageFlagsReadOnly;
} else {
locked_data->reserved &= ~GBD_WRITE_OK;
locked_data->image_flags |= ImageFlagsReadOnly;
}
if ((format & PixelFormatAlpha) != 0) {
locked_data->image_flags |= ImageFlagsHasAlpha;
}
locked_data->reserved |= GBD_LOCKED;
locked_data->reserved |= GBD_OWN_SCAN0;
root_data->reserved |= GBD_LOCKED;
switch (format) {
case PixelFormat24bppRGB:
/* workaround a hack we have (because Cairo use 32bits in this case) */
dest_pixel_format_bpp = 24;
locked_data->reserved |= GBD_TRUE24BPP;
break;
default:
dest_pixel_format_bpp = gdip_get_pixel_format_bpp (format);
break;
}
dest_stride = (srcRect->Width * dest_pixel_format_bpp + 7) >> 3;
gdip_align_stride (dest_stride);
dest_size = srcRect->Height * dest_stride;
if ((flags & ImageLockModeUserInputBuf) == 0) {
locked_data->scan0 = GdipAlloc(dest_size);
if (locked_data->scan0 == NULL) {
return OutOfMemory;
}
} else {
/* User is supposed to have provided the buffer */
if (locked_data->scan0 == NULL) {
return InvalidParameter;
}
locked_data->reserved &= ~GBD_OWN_SCAN0;
}
locked_data->width = srcRect->Width;
locked_data->height = srcRect->Height;
locked_data->stride = dest_stride;
locked_data->pixel_format = format;
locked_data->x = srcRect->X;
locked_data->y = srcRect->Y;
locked_data->palette = NULL;
/* If the user wants the original data to be readable, then convert the bits. */
status = Ok;
if ((flags & ImageLockModeRead) != 0) {
status = gdip_bitmap_change_rect_pixel_format (root_data, (GpRect*)srcRect, locked_data, &destRect);
if ((status != Ok) && ((flags & ImageLockModeUserInputBuf) == 0)) {
GdipFree (locked_data->scan0);
locked_data->scan0 = NULL;
}
}
return status;
}
GpStatus
GdipBitmapUnlockBits (GpBitmap *bitmap, BitmapData *locked_data)
{
GpStatus status;
BitmapData *root_data;;
if ((bitmap == NULL) || (locked_data == NULL)) {
return InvalidParameter;
}
root_data = bitmap->active_bitmap;
/* It is not safe to assume that the correct BitmapData has been passed in.
* Sanity check: Make sure the locked data is in fact locked.*/
if (!(root_data->reserved & GBD_LOCKED) || !(locked_data->reserved & GBD_LOCKED)) {
return Win32Error;
}
/* Sanity check: Make sure the locked data's size is consistent with having
* been returned from LockBits (). */
if ((locked_data->width > root_data->width) || (locked_data->height > root_data->height)) {
return InvalidParameter;
}
/* We need to copy the locked data back to the root data's Scan0 if the image was writeable */
if ((locked_data->reserved & GBD_WRITE_OK) != 0) {
Rect srcRect = { 0, 0, locked_data->width, locked_data->height };
Rect destRect = { locked_data->x, locked_data->y, locked_data->width, locked_data->height };
status = gdip_bitmap_change_rect_pixel_format (locked_data, &srcRect, root_data, &destRect);
} else {
status = Ok;
}
if ((locked_data->reserved & GBD_OWN_SCAN0) != 0) {
GdipFree(locked_data->scan0);
locked_data->scan0 = NULL;
locked_data->reserved &= ~GBD_OWN_SCAN0;
}
if (locked_data->palette) {
GdipFree(locked_data->palette);
locked_data->palette = NULL;
}
locked_data->reserved &= ~GBD_LOCKED;
root_data->reserved &= ~GBD_LOCKED;
return status;
}
GpStatus
GdipBitmapSetPixel (GpBitmap *bitmap, int x, int y, ARGB color)
{
BitmapData *data;
BYTE *v;
if ((bitmap == NULL) || (bitmap->active_bitmap == NULL)) {
return InvalidParameter;
}
data = bitmap->active_bitmap;
if ((x < 0) || (x > data->width) || (y < 0) || (y > data->height) || (data->reserved & GBD_LOCKED)) {
return InvalidParameter;
}
if (gdip_is_an_indexed_pixelformat (data->pixel_format))
return InvalidParameter;
v = (BYTE*)(data->scan0) + y * data->stride;
switch (data->pixel_format) {
case PixelFormat24bppRGB:
case PixelFormat32bppRGB:
color |= 0xFF000000; /* force the alpha for Cairo */
/* fall through */
case PixelFormat32bppARGB:
case PixelFormat32bppPARGB: {
ARGB *scan = (ARGB *)v;
scan[x] = color;
break;
}
case PixelFormat16bppGrayScale:
return InvalidParameter;
default:
return NotImplemented;
}
return Ok;
}
GpStatus
GdipBitmapGetPixel (GpBitmap *bitmap, int x, int y, ARGB *color)
{
BitmapData *data;
if ((bitmap == NULL) || (bitmap->active_bitmap == NULL) || (color == NULL)) {
return InvalidParameter;
}
data = bitmap->active_bitmap;
if ((x < 0) || (x >= data->width) || (y < 0) || (y >= data->height) || (data->reserved & GBD_LOCKED)) {
return InvalidParameter;
}
if (gdip_is_an_indexed_pixelformat (data->pixel_format)) {
StreamingState pixel_stream;
GpStatus status;
unsigned int palette_index;
if (data->palette == NULL) {
return InvalidParameter;
}
status = gdip_init_pixel_stream (&pixel_stream, data, x, y, 1, 1);
if (status != Ok) {
return status;
}
palette_index = gdip_pixel_stream_get_next (&pixel_stream);
if (palette_index >= data->palette->Count) {
return InvalidParameter;
}
*color = data->palette->Entries[palette_index];
} else {
BYTE *v = ((BYTE*)data->scan0) + y * data->stride;
switch (data->pixel_format) {
case PixelFormat16bppGrayScale:
return InvalidParameter;
case PixelFormat24bppRGB:
case PixelFormat32bppARGB:
case PixelFormat32bppPARGB:
case PixelFormat32bppRGB: {
ARGB *scan = (ARGB *)v;
*color = scan[x];
break;
}
default:
return NotImplemented;
}
}
return Ok;
}
GpStatus
GdipBitmapSetResolution (GpBitmap *bitmap, float xdpi, float ydpi)
{
if (!bitmap || !bitmap->active_bitmap || isnan(xdpi) || isnan(xdpi) || (xdpi <= 0.0f) || (ydpi <= 0.0f))
return InvalidParameter;
bitmap->active_bitmap->dpi_horz = xdpi;
bitmap->active_bitmap->dpi_vert = ydpi;
bitmap->active_bitmap->image_flags |= ImageFlagsHasRealDPI;
return Ok;
}
cairo_surface_t *
gdip_bitmap_ensure_surface (GpBitmap *bitmap)
{
cairo_format_t format;
BitmapData *data = bitmap->active_bitmap;
if (bitmap->surface || !data || !data->scan0)
return bitmap->surface;
switch (data->pixel_format) {
case PixelFormat24bppRGB:
format = CAIRO_FORMAT_RGB24;
break;
case PixelFormat32bppARGB: /* premultiplication is required */
case PixelFormat32bppRGB: /* no alpha */
case PixelFormat32bppPARGB: /* alpha already premultiplied */
format = CAIRO_FORMAT_ARGB32;
break;
default:
g_warning ("gdip_bitmap_ensure_surface: Unable to create a surface for raw bitmap data of format 0x%08x", data->pixel_format);
return NULL;
}
bitmap->surface = cairo_image_surface_create_for_data ((BYTE*)data->scan0, format,
data->width, data->height, data->stride);
return bitmap->surface;
}
BOOL
gdip_bitmap_format_needs_premultiplication (GpBitmap *bitmap)
{
return (bitmap->active_bitmap->pixel_format == PixelFormat32bppARGB);
}
BYTE*
gdip_bitmap_get_premultiplied_scan0 (GpBitmap *bitmap)
{
BitmapData *data = bitmap->active_bitmap;
BYTE* premul = (BYTE*) GdipAlloc (data->height * data->stride);
if (!premul)
return NULL;
BYTE *source = (BYTE*)data->scan0;
BYTE *target = premul;
int y, x;
for (y = 0; y < data->height; y++) {
ARGB *sp = (ARGB*) source;
ARGB *tp = (ARGB*) target;
for (x = 0; x < data->width; x++) {
BYTE r, g, b, a;
get_pixel_bgra (*sp, b, g, r, a);
if (a < 0xff) {
b = pre_multiplied_table [b][a];
g = pre_multiplied_table [g][a];
r = pre_multiplied_table [r][a];
set_pixel_bgra (tp, 0, b, g, r, a);
} else {
*tp = *sp;
}
sp++;
tp++;
}
source += data->stride;
target += data->stride;
}
return premul;
}
GpBitmap *
gdip_convert_indexed_to_rgb (GpBitmap *indexed_bmp)
{
BitmapData *data;
ColorPalette *palette;
int rgb_stride;
int rgb_bytes;
int force_alpha;
int one_pixel_mask;
int one_pixel_shift;
int pixels_per_byte;
ARGB *rgb_scan0;
int p;
int x;
int y;
GpBitmap *ret;
GpStatus status;
BYTE *indexed_scan;
ARGB *rgb_scan;
int pixels_this_byte;
unsigned short sample;
int index;
int transparent;
int format;
data = indexed_bmp->active_bitmap;
if (data == NULL) {
return NULL;
}
palette = data->palette;
if (palette == NULL || !gdip_is_an_indexed_pixelformat (data->pixel_format)) {
return NULL;
}
switch (data->pixel_format) {
case PixelFormat1bppIndexed:
one_pixel_mask = 0x01;
one_pixel_shift = 1;
pixels_per_byte = 8;
break;
case PixelFormat4bppIndexed:
one_pixel_mask = 0x0F;
one_pixel_shift = 4;
pixels_per_byte = 2;
break;
case PixelFormat8bppIndexed:
one_pixel_mask = 0xFF;
one_pixel_shift = 8;
pixels_per_byte = 1;
break;
default: /* something is wrong!! */
return NULL;
}
if ((palette->Flags & PaletteFlagsHasAlpha) == 0) {
format = PixelFormat32bppRGB;
set_pixel_bgra (&force_alpha, 0, 0, 0, 0, 0xFF); /* full alpha bits set */
} else {
format = PixelFormat32bppARGB;
force_alpha = 0;
}
rgb_stride = data->width * sizeof (ARGB);
/* ensure 32bits alignment */
gdip_align_stride (rgb_stride);
rgb_bytes = data->height * rgb_stride;
/* allocate the RGB frame */
rgb_scan0 = GdipAlloc (rgb_bytes);
if (rgb_scan0 == NULL) { /* out of memory?? */
return NULL;
}
/* convert the indexed pixels into RGB values and store them into the RGB frame */
for (y=0; y < data->height; y++) {
indexed_scan = (BYTE*)(data->scan0) + y * data->stride;
rgb_scan = rgb_scan0 + (y * data->width);
/* Speed up the 8bpp case */
if (pixels_per_byte == 1) {
for (x = 0; x < data->width; x++) {
index = *indexed_scan++;
rgb_scan [x] = palette->Entries [index] | force_alpha;
}
continue;
}
/* For 4bpp and 1bpp */
for (x=0; x < data->width; x += pixels_per_byte) {
pixels_this_byte = pixels_per_byte;
sample = *indexed_scan;
indexed_scan++;
if (x + pixels_this_byte >= data->width) {
pixels_this_byte = data->width - x;
}
for (p=0; p < pixels_this_byte; p++) {
sample <<= one_pixel_shift;
index = (sample >> 8) & one_pixel_mask;
rgb_scan[x + p] = palette->Entries[index] | force_alpha;
}
}
}
/* try to get a GpBitmap out of it :-) */
status = GdipCreateBitmapFromScan0 (data->width, data->height, rgb_stride, format, (BYTE*)rgb_scan0, &ret);
if (status == Ok) {
ret->active_bitmap->reserved = GBD_OWN_SCAN0;
return ret;
}
if (ret != NULL) {
gdip_bitmap_dispose(ret);
}
if (rgb_scan0 != NULL) {
GdipFree (rgb_scan0);
}
return NULL;
}
ColorPalette*
gdip_create_greyscale_palette (int num_colors)
{
ColorPalette *palette;
int i;
if ((num_colors < 0) || (num_colors > 256))
return NULL;
/* ColorPalette definition already include 1 ARGB member */
palette = GdipAlloc (sizeof(ColorPalette) + (num_colors - 1) * sizeof(ARGB));
if (!palette)
return NULL;
palette->Count = num_colors;
palette->Flags = 0; /* not every codec sets the ImageFlagsColorSpaceGRAY flag*/
/* always force alpha to opaque */
if (num_colors == 256) {
for (i = 0; i < 256; i++)
set_pixel_bgra (&palette->Entries[i], 0, i, i, i, 0xFF);
} else {
for (i = 0; i < num_colors; i++) {
int intensity = i * 255 / (num_colors - 1);
set_pixel_bgra (&palette->Entries[i], 0, intensity, intensity, intensity, 0xFF);
}
}
return palette;
}
Jump to Line
Something went wrong with that request. Please try again.