mutter/cogl/cogl-bitmap-pixbuf.c
Neil Roberts d18b59d9e6 Add a public cogl_bitmap_new_for_data
This creates a CoglBitmap which points into an existing buffer in
system memory. That way it can be used to create a texture or to read
pixel data into. The function replaces the existing internal function
_cogl_bitmap_new_from_data but removes the destroy notify call back.
If the application wants notification of destruction it can just use
the cogl_object_set_user_data function as normal. Internally there is
now a convenience function to create a bitmap for system memory and
automatically free the buffer using that mechanism.

The name of the function is inspired by
cairo_image_surface_create_for_data which has similar semantics.

Reviewed-by: Robert Bragg <robert@linux.intel.com>
2012-03-14 12:19:56 +00:00

299 lines
8.3 KiB
C

/*
* Cogl
*
* An object oriented GL/GLES Abstraction/Utility Layer
*
* Copyright (C) 2007,2008,2009 Intel Corporation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "cogl-util.h"
#include "cogl-internal.h"
#include "cogl-bitmap-private.h"
#include "cogl-context-private.h"
#include <string.h>
#ifdef USE_QUARTZ
#include <ApplicationServices/ApplicationServices.h>
#elif defined(USE_GDKPIXBUF)
#include <gdk-pixbuf/gdk-pixbuf.h>
#endif
#ifdef USE_QUARTZ
gboolean
_cogl_bitmap_get_size_from_file (const char *filename,
int *width,
int *height)
{
if (width)
*width = 0;
if (height)
*height = 0;
return TRUE;
}
/* the error does not contain the filename as the caller already has it */
CoglBitmap *
_cogl_bitmap_from_file (const char *filename,
GError **error)
{
CFURLRef url;
CGImageSourceRef image_source;
CGImageRef image;
int save_errno;
CFStringRef type;
gsize width, height, rowstride;
guint8 *out_data;
CGColorSpaceRef color_space;
CGContextRef bitmap_context;
g_assert (filename != NULL);
g_assert (error == NULL || *error == NULL);
url = CFURLCreateFromFileSystemRepresentation (NULL,
(guchar *) filename,
strlen (filename),
false);
image_source = CGImageSourceCreateWithURL (url, NULL);
save_errno = errno;
CFRelease (url);
if (image_source == NULL)
{
/* doesn't exist, not readable, etc. */
g_set_error_literal (error,
COGL_BITMAP_ERROR,
COGL_BITMAP_ERROR_FAILED,
g_strerror (save_errno));
return NULL;
}
/* Unknown images would be cleanly caught as zero width/height below, but try
* to provide better error message
*/
type = CGImageSourceGetType (image_source);
if (type == NULL)
{
CFRelease (image_source);
g_set_error_literal (error,
COGL_BITMAP_ERROR,
COGL_BITMAP_ERROR_UNKNOWN_TYPE,
"Unknown image type");
return NULL;
}
CFRelease (type);
image = CGImageSourceCreateImageAtIndex (image_source, 0, NULL);
CFRelease (image_source);
width = CGImageGetWidth (image);
height = CGImageGetHeight (image);
if (width == 0 || height == 0)
{
/* incomplete or corrupt */
CFRelease (image);
g_set_error_literal (error,
COGL_BITMAP_ERROR,
COGL_BITMAP_ERROR_CORRUPT_IMAGE,
"Image has zero width or height");
return NULL;
}
/* allocate buffer big enough to hold pixel data */
rowstride = 4 * width;
out_data = g_malloc0 (height * rowstride);
/* render to buffer */
color_space = CGColorSpaceCreateWithName (kCGColorSpaceGenericRGB);
bitmap_context = CGBitmapContextCreate (out_data,
width, height, 8,
rowstride, color_space,
kCGImageAlphaPremultipliedFirst);
CGColorSpaceRelease (color_space);
{
const CGRect rect = {{0, 0}, {width, height}};
CGContextDrawImage (bitmap_context, rect, image);
}
CGImageRelease (image);
CGContextRelease (bitmap_context);
/* store bitmap info */
return _cogl_bitmap_new_from_data (out_data,
COGL_PIXEL_FORMAT_ARGB_8888,
width, height,
rowstride,
(CoglBitmapDestroyNotify) g_free,
NULL);
}
#elif defined(USE_GDKPIXBUF)
gboolean
_cogl_bitmap_get_size_from_file (const char *filename,
int *width,
int *height)
{
_COGL_RETURN_VAL_IF_FAIL (filename != NULL, FALSE);
if (gdk_pixbuf_get_file_info (filename, width, height) != NULL)
return TRUE;
return FALSE;
}
CoglBitmap *
_cogl_bitmap_from_file (const char *filename,
GError **error)
{
static CoglUserDataKey pixbuf_key;
GdkPixbuf *pixbuf;
gboolean has_alpha;
GdkColorspace color_space;
CoglPixelFormat pixel_format;
int width;
int height;
int rowstride;
int bits_per_sample;
int n_channels;
CoglBitmap *bmp;
_COGL_GET_CONTEXT (ctx, NULL);
_COGL_RETURN_VAL_IF_FAIL (error == NULL || *error == NULL, FALSE);
/* Load from file using GdkPixbuf */
pixbuf = gdk_pixbuf_new_from_file (filename, error);
if (pixbuf == NULL)
return FALSE;
/* Get pixbuf properties */
has_alpha = gdk_pixbuf_get_has_alpha (pixbuf);
color_space = gdk_pixbuf_get_colorspace (pixbuf);
width = gdk_pixbuf_get_width (pixbuf);
height = gdk_pixbuf_get_height (pixbuf);
rowstride = gdk_pixbuf_get_rowstride (pixbuf);
bits_per_sample = gdk_pixbuf_get_bits_per_sample (pixbuf);
n_channels = gdk_pixbuf_get_n_channels (pixbuf);
/* According to current docs this should be true and so
* the translation to cogl pixel format below valid */
g_assert (bits_per_sample == 8);
if (has_alpha)
g_assert (n_channels == 4);
else
g_assert (n_channels == 3);
/* Translate to cogl pixel format */
switch (color_space)
{
case GDK_COLORSPACE_RGB:
/* The only format supported by GdkPixbuf so far */
pixel_format = has_alpha ?
COGL_PIXEL_FORMAT_RGBA_8888 :
COGL_PIXEL_FORMAT_RGB_888;
break;
default:
/* Ouch, spec changed! */
g_object_unref (pixbuf);
return FALSE;
}
/* We just use the data directly from the pixbuf so that we don't
have to copy to a seperate buffer. Note that Cogl is expected not
to read past the end of bpp*width on the last row even if the
rowstride is much larger so we don't need to worry about
GdkPixbuf's semantics that it may under-allocate the buffer. */
bmp = cogl_bitmap_new_for_data (ctx,
width,
height,
pixel_format,
rowstride,
gdk_pixbuf_get_pixels (pixbuf));
cogl_object_set_user_data (COGL_OBJECT (bmp),
&pixbuf_key,
pixbuf,
g_object_unref);
return bmp;
}
#else
#include "stb_image.c"
gboolean
_cogl_bitmap_get_size_from_file (const char *filename,
int *width,
int *height)
{
if (width)
*width = 0;
if (height)
*height = 0;
return TRUE;
}
CoglBitmap *
_cogl_bitmap_from_file (const char *filename,
GError **error)
{
CoglBitmap *bmp;
int stb_pixel_format;
int width;
int height;
guint8 *pixels;
_COGL_RETURN_VAL_IF_FAIL (error == NULL || *error == NULL, FALSE);
/* Load from file using stb */
pixels = stbi_load (filename,
&width, &height, &stb_pixel_format,
STBI_rgb_alpha);
if (pixels == NULL)
return FALSE;
/* Store bitmap info */
bmp = _cogl_bitmap_new_from_data (g_memdup (pixels, height * width * 4),
COGL_PIXEL_FORMAT_RGBA_8888,
width, height,
width * 4,
(CoglBitmapDestroyNotify) g_free,
NULL);
free (pixels);
return bmp;
}
#endif