1c114be31a
For the asynchronous loading we need a function call that parses a file, given its path, and retrieves the image width and height. This commit adds cogl_bitmap_get_size_from_file() to the CoglBitmap API.
358 lines
9.2 KiB
C
358 lines
9.2 KiB
C
/*
|
|
* Clutter COGL
|
|
*
|
|
* A basic GL/GLES Abstraction/Utility Layer
|
|
*
|
|
* Authored By Matthew Allum <mallum@openedhand.com>
|
|
*
|
|
* Copyright (C) 2007 OpenedHand
|
|
*
|
|
* 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, write to the
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "cogl.h"
|
|
#include "cogl-internal.h"
|
|
#include "cogl-bitmap.h"
|
|
|
|
#include <string.h>
|
|
|
|
#ifdef USE_QUARTZ
|
|
#include <ApplicationServices/ApplicationServices.h>
|
|
#elif defined(USE_GDKPIXBUF)
|
|
#include <gdk-pixbuf/gdk-pixbuf.h>
|
|
#endif
|
|
|
|
gboolean
|
|
_cogl_bitmap_can_convert (CoglPixelFormat src, CoglPixelFormat dst)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
_cogl_bitmap_can_unpremult (CoglPixelFormat format)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
_cogl_bitmap_convert (const CoglBitmap *bmp,
|
|
CoglBitmap *dst_bmp,
|
|
CoglPixelFormat dst_format)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
_cogl_bitmap_unpremult (const CoglBitmap *bmp,
|
|
CoglBitmap *dst_bmp)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
#ifdef USE_QUARTZ
|
|
|
|
/* lacking GdkPixbuf and other useful GError domains, define one of our own */
|
|
|
|
#define COGL_BITMAP_ERROR cogl_bitmap_error_quark ()
|
|
|
|
typedef enum {
|
|
COGL_BITMAP_ERROR_FAILED,
|
|
COGL_BITMAP_ERROR_UNKNOWN_TYPE,
|
|
COGL_BITMAP_ERROR_CORRUPT_IMAGE
|
|
} CoglBitmapError;
|
|
|
|
GQuark
|
|
cogl_bitmap_error_quark (void)
|
|
{
|
|
return g_quark_from_static_string ("cogl-bitmap-error-quark");
|
|
}
|
|
|
|
gboolean
|
|
_cogl_bitmap_get_size_from_file (const gchar *filename,
|
|
gint *width,
|
|
gint *height)
|
|
{
|
|
if (width)
|
|
*width = 0;
|
|
|
|
if (height)
|
|
*height = 0;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* the error does not contain the filename as the caller already has it */
|
|
gboolean
|
|
_cogl_bitmap_from_file (CoglBitmap *bmp,
|
|
const gchar *filename,
|
|
GError **error)
|
|
{
|
|
g_assert (bmp != NULL);
|
|
g_assert (filename != NULL);
|
|
g_assert (error == NULL || *error == NULL);
|
|
|
|
CFURLRef url = CFURLCreateFromFileSystemRepresentation (NULL, (guchar*)filename, strlen(filename), false);
|
|
CGImageSourceRef image_source = CGImageSourceCreateWithURL (url, NULL);
|
|
int save_errno = errno;
|
|
CFRelease (url);
|
|
if (image_source == NULL)
|
|
{
|
|
/* doesn't exist, not readable, etc. */
|
|
g_set_error (error, COGL_BITMAP_ERROR, COGL_BITMAP_ERROR_FAILED,
|
|
"%s", g_strerror (save_errno));
|
|
return FALSE;
|
|
}
|
|
|
|
/* Unknown images would be cleanly caught as zero width/height below, but try
|
|
* to provide better error message
|
|
*/
|
|
CFStringRef type = CGImageSourceGetType (image_source);
|
|
if (type == NULL)
|
|
{
|
|
CFRelease (image_source);
|
|
g_set_error (error, COGL_BITMAP_ERROR, COGL_BITMAP_ERROR_UNKNOWN_TYPE,
|
|
"Unknown image type");
|
|
return FALSE;
|
|
}
|
|
CFRelease (type);
|
|
|
|
CGImageRef image = CGImageSourceCreateImageAtIndex (image_source, 0, NULL);
|
|
CFRelease (image_source);
|
|
|
|
size_t width = CGImageGetWidth (image);
|
|
size_t height = CGImageGetHeight (image);
|
|
if (width == 0 || height == 0)
|
|
{
|
|
/* incomplete or corrupt */
|
|
CFRelease (image);
|
|
g_set_error (error, COGL_BITMAP_ERROR, COGL_BITMAP_ERROR_CORRUPT_IMAGE,
|
|
"Image has zero width or height");
|
|
return FALSE;
|
|
}
|
|
|
|
/* allocate buffer big enough to hold pixel data */
|
|
size_t rowstride;
|
|
CGBitmapInfo bitmap_info = CGImageGetBitmapInfo (image);
|
|
if ((bitmap_info & kCGBitmapAlphaInfoMask) == kCGImageAlphaNone)
|
|
{
|
|
bitmap_info = kCGImageAlphaNone;
|
|
rowstride = 3 * width;
|
|
}
|
|
else
|
|
{
|
|
bitmap_info = kCGImageAlphaPremultipliedFirst;
|
|
rowstride = 4 * width;
|
|
}
|
|
guint8 *out_data = g_malloc0 (height * rowstride);
|
|
|
|
/* render to buffer */
|
|
CGColorSpaceRef color_space = CGColorSpaceCreateWithName (kCGColorSpaceGenericRGB);
|
|
CGContextRef bitmap_context = CGBitmapContextCreate (out_data,
|
|
width, height, 8,
|
|
rowstride, color_space,
|
|
bitmap_info);
|
|
CGColorSpaceRelease (color_space);
|
|
|
|
const CGRect rect = {{0, 0}, {width, height}};
|
|
CGContextDrawImage (bitmap_context, rect, image);
|
|
|
|
CGImageRelease (image);
|
|
CGContextRelease (bitmap_context);
|
|
|
|
/* store bitmap info */
|
|
bmp->data = out_data;
|
|
bmp->format = bitmap_info == kCGImageAlphaPremultipliedFirst
|
|
? COGL_PIXEL_FORMAT_ARGB_8888
|
|
: COGL_PIXEL_FORMAT_RGB_888;
|
|
bmp->width = width;
|
|
bmp->height = height;
|
|
bmp->rowstride = rowstride;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#elif defined(USE_GDKPIXBUF)
|
|
|
|
gboolean
|
|
_cogl_bitmap_get_size_from_file (const gchar *filename,
|
|
gint *width,
|
|
gint *height)
|
|
{
|
|
g_return_val_if_fail (filename != NULL, FALSE);
|
|
|
|
if (gdk_pixbuf_get_file_info (filename, width, height) != NULL)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
_cogl_bitmap_from_file (CoglBitmap *bmp,
|
|
const gchar *filename,
|
|
GError **error)
|
|
{
|
|
GdkPixbuf *pixbuf;
|
|
gboolean has_alpha;
|
|
GdkColorspace color_space;
|
|
CoglPixelFormat pixel_format;
|
|
gint width;
|
|
gint height;
|
|
gint rowstride;
|
|
gint bits_per_sample;
|
|
gint n_channels;
|
|
gint last_row_size;
|
|
guchar *pixels;
|
|
guchar *out_data;
|
|
guchar *out;
|
|
gint r;
|
|
|
|
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
|
|
|
if (bmp == NULL)
|
|
return 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);
|
|
|
|
/* The docs say this is the right way */
|
|
last_row_size = width * ((n_channels * bits_per_sample + 7) / 8);
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/* FIXME: Any way to destroy pixbuf but retain pixel data? */
|
|
|
|
pixels = gdk_pixbuf_get_pixels (pixbuf);
|
|
out_data = (guchar*) g_malloc (height * rowstride);
|
|
out = out_data;
|
|
|
|
/* Copy up to last row */
|
|
for (r = 0; r < height-1; ++r)
|
|
{
|
|
memcpy (out, pixels, rowstride);
|
|
pixels += rowstride;
|
|
out += rowstride;
|
|
}
|
|
|
|
/* Copy last row */
|
|
memcpy (out, pixels, last_row_size);
|
|
|
|
/* Destroy GdkPixbuf object */
|
|
g_object_unref (pixbuf);
|
|
|
|
/* Store bitmap info */
|
|
bmp->data = out_data; /* The stored data the same alignment constraints as a
|
|
* gdkpixbuf but stores a full rowstride in the last
|
|
* scanline
|
|
*/
|
|
bmp->format = pixel_format;
|
|
bmp->width = width;
|
|
bmp->height = height;
|
|
bmp->rowstride = rowstride;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#else
|
|
|
|
#include "stb_image.c"
|
|
|
|
gboolean
|
|
_cogl_bitmap_get_size_from_file (const gchar *filename,
|
|
gint *width,
|
|
gint *height)
|
|
{
|
|
if (width)
|
|
*width = 0;
|
|
|
|
if (height)
|
|
*height = 0;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
_cogl_bitmap_from_file (CoglBitmap *bmp,
|
|
const gchar *filename,
|
|
GError **error)
|
|
{
|
|
gint stb_pixel_format;
|
|
gint width;
|
|
gint height;
|
|
guchar *pixels;
|
|
|
|
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
|
|
|
if (bmp == NULL)
|
|
return 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->data = g_memdup (pixels, height * width * 4);
|
|
bmp->format = COGL_PIXEL_FORMAT_RGBA_8888;
|
|
bmp->width = width;
|
|
bmp->height = height;
|
|
bmp->rowstride = width * 4;
|
|
|
|
free (pixels);
|
|
|
|
return TRUE;
|
|
}
|
|
#endif
|