gdk-pixbuf-data.c 11.8 KB
Newer Older
1
/* GdkPixbuf library - Image creation from in-memory buffers
2
 *
3 4 5 6 7
 * Copyright (C) 1999 The Free Software Foundation
 *
 * Author: Federico Mena-Quintero <federico@gimp.org>
 *
 * This library is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU Lesser General Public
9 10 11 12 13 14
 * 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
15
 * Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public
18 19 20
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
21 22 23
 */

#include <config.h>
24
#include "gdk-pixbuf.h"
25
#include "gdk-pixbuf-private.h"
Havoc Pennington's avatar
Havoc Pennington committed
26
#include "gdk-pixbuf-i18n.h"
27 28
#include <stdlib.h>
#include <string.h>
29

30

31

32 33 34
/**
 * gdk_pixbuf_new_from_data:
 * @data: Image data in 8-bit/sample packed format.
35
 * @colorspace: Colorspace for the image data.
36
 * @has_alpha: Whether the data has an opacity channel.
37
 * @bits_per_sample: Number of bits per sample.
38 39 40
 * @width: Width of the image in pixels.
 * @height: Height of the image in pixels.
 * @rowstride: Distance in bytes between rows.
41
 * @destroy_fn: Function used to free the data when the pixbuf's reference count
42
 * drops to zero, or NULL if the data should not be freed.
43
 * @destroy_fn_data: Closure data to pass to the destroy notification function.
44
 * 
45 46
 * Creates a new #GdkPixbuf out of in-memory image data.  Currently only RGB
 * images with 8 bits per sample are supported.
47
 * 
Arturo Espinosa's avatar
Arturo Espinosa committed
48
 * Return value: A newly-created #GdkPixbuf structure with a reference count of
49 50 51
 * 1.
 **/
GdkPixbuf *
52 53 54
gdk_pixbuf_new_from_data (const guchar *data, GdkColorspace colorspace, gboolean has_alpha,
			  int bits_per_sample, int width, int height, int rowstride,
			  GdkPixbufDestroyNotify destroy_fn, gpointer destroy_fn_data)
55
{
56
	GdkPixbuf *pixbuf;
57

58
	/* Only 8-bit/sample RGB buffers are supported for now */
59

60
	g_return_val_if_fail (data != NULL, NULL);
61 62
	g_return_val_if_fail (colorspace == GDK_COLORSPACE_RGB, NULL);
	g_return_val_if_fail (bits_per_sample == 8, NULL);
63 64
	g_return_val_if_fail (width > 0, NULL);
	g_return_val_if_fail (height > 0, NULL);
65

66
	pixbuf = g_object_new (GDK_TYPE_PIXBUF, NULL);
67
        
68 69 70 71 72 73 74 75 76 77
	pixbuf->colorspace = colorspace;
	pixbuf->n_channels = has_alpha ? 4 : 3;
	pixbuf->bits_per_sample = bits_per_sample;
	pixbuf->has_alpha = has_alpha ? TRUE : FALSE;
	pixbuf->width = width;
	pixbuf->height = height;
	pixbuf->rowstride = rowstride;
	pixbuf->pixels = (guchar *) data;
	pixbuf->destroy_fn = destroy_fn;
	pixbuf->destroy_fn_data = destroy_fn_data;
78

79
	return pixbuf;
80
}
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109

static guint32
read_int (const guchar **p)
{
        guint32 num;

        /* Note most significant bytes are first in the byte stream */
        num =
          (*p)[3]         |
          ((*p)[2] << 8)  |
          ((*p)[1] << 16) |
          ((*p)[0] << 24);

        *p += 4;

        return num;
}

static gboolean
read_bool (const guchar **p)
{
        gboolean val = **p != 0;
        
        ++(*p);
        
        return val;
}

static GdkPixbuf*
Havoc Pennington's avatar
Havoc Pennington committed
110 111 112 113
read_raw_inline (const guchar *data,
                 gboolean      copy_pixels,
                 int           length,
                 GError      **error)
114 115 116 117 118 119 120 121 122
{
        GdkPixbuf *pixbuf;
        const guchar *p = data;
        guint32 rowstride, width, height, colorspace,
                n_channels, bits_per_sample;
        gboolean has_alpha;
        
        if (length >= 0 && length < 12) {
                /* Not enough buffer to hold the width/height/rowstride */
Havoc Pennington's avatar
Havoc Pennington committed
123 124 125 126 127
                g_set_error (error,
                             GDK_PIXBUF_ERROR,
                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
                             _("Image data is partially missing"));
                
128 129 130 131 132 133 134
                return NULL;
        }

        rowstride = read_int (&p);
        width = read_int (&p);
        height = read_int (&p);

Havoc Pennington's avatar
Havoc Pennington committed
135 136 137 138 139
        if (rowstride < width) {
                g_set_error (error,
                             GDK_PIXBUF_ERROR,
                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
                             _("Image has an incorrect pixel rowstride, perhaps the data was corrupted somehow."));
140
                return NULL; /* bad data from untrusted source. */
Havoc Pennington's avatar
Havoc Pennington committed
141 142
        }
        
143 144 145 146 147 148 149
        /* rowstride >= width, so we can trust width */
        
        length -= 12;

        /* There's some better way like G_MAXINT/height > rowstride
         * but I'm not sure it works, so stick to this for now.
         */
Havoc Pennington's avatar
Havoc Pennington committed
150 151 152 153 154 155
        if (((double)height) * ((double)rowstride) > (double)G_MAXINT) {
                g_set_error (error,
                             GDK_PIXBUF_ERROR,
                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
                             _("Image size is impossibly large, perhaps the data was corrupted somehow"));
                
156
                return NULL; /* overflow */
Havoc Pennington's avatar
Havoc Pennington committed
157 158
        }

159 160 161 162 163
        if (length >= 0 &&
            length < (height * rowstride + 13)) {
                /* Not enough buffer to hold the remaining header
                 * information plus the data.
                 */
Havoc Pennington's avatar
Havoc Pennington committed
164 165 166
                g_set_error (error,
                             GDK_PIXBUF_ERROR,
                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
167
                             _("Image data is partially missing, probably it was corrupted somehow."));
168 169 170
                
                return NULL;
        }
Havoc Pennington's avatar
Havoc Pennington committed
171

172 173 174 175 176 177
        /* Read the remaining 13 bytes of header information */
            
        has_alpha = read_bool (&p) != FALSE;
        colorspace = read_int (&p);
        n_channels = read_int (&p);
        bits_per_sample = read_int (&p);
Havoc Pennington's avatar
Havoc Pennington committed
178 179 180 181 182 183 184
        
        if (colorspace != GDK_COLORSPACE_RGB) {
                g_set_error (error,
                             GDK_PIXBUF_ERROR,
                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
                             _("Image has an unknown colorspace code (%d), perhaps the image data was corrupted"),
                             colorspace);
185
                return NULL;
Havoc Pennington's avatar
Havoc Pennington committed
186
        }
187

Havoc Pennington's avatar
Havoc Pennington committed
188 189 190 191 192 193
        if (bits_per_sample != 8) {
                g_set_error (error,
                             GDK_PIXBUF_ERROR,
                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
                             _("Image has an improper number of bits per sample (%d), perhaps the image data was corrupted"),
                             bits_per_sample);
194
                return NULL;
Havoc Pennington's avatar
Havoc Pennington committed
195 196 197 198 199 200 201 202
        }
        
        if (has_alpha && n_channels != 4) {
                g_set_error (error,
                             GDK_PIXBUF_ERROR,
                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
                             _("Image has an improper number of channels (%d), perhaps the image data was corrupted"),
                             n_channels);
203
                return NULL;
Havoc Pennington's avatar
Havoc Pennington committed
204 205 206 207 208 209 210 211
        }
        
        if (!has_alpha && n_channels != 3) {
                g_set_error (error,
                             GDK_PIXBUF_ERROR,
                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
                             _("Image has an improper number of channels (%d), perhaps the image data was corrupted"),
                             n_channels);
212
                return NULL;
Havoc Pennington's avatar
Havoc Pennington committed
213 214
        }
        
215 216 217 218 219 220 221 222
        if (copy_pixels) {
                guchar *pixels;
                gint dest_rowstride;
                gint row;
                
                pixbuf = gdk_pixbuf_new (colorspace,
                                         has_alpha, bits_per_sample,
                                         width, height);
Havoc Pennington's avatar
Havoc Pennington committed
223 224 225 226 227 228 229 230 231 232
                
                if (pixbuf == NULL) {
                        g_set_error (error,
                                     GDK_PIXBUF_ERROR,
                                     GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
                                     _("Not enough memory to store a %d by %d image; try exiting some applications to free memory."),
                                     width, height);
                        return NULL;
                }
                
233 234
                pixels = gdk_pixbuf_get_pixels (pixbuf);
                dest_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
Havoc Pennington's avatar
Havoc Pennington committed
235
                
236 237 238 239 240 241 242 243 244 245 246 247 248 249
                for (row = 0; row < height; row++) {
                        memcpy (pixels, p, rowstride);
                        pixels += dest_rowstride;
                        p += rowstride;
                }
        } else {
                pixbuf = gdk_pixbuf_new_from_data (p,
                                                   colorspace,
                                                   has_alpha,
                                                   bits_per_sample,
                                                   width, height,
                                                   rowstride,
                                                   NULL, NULL);
        }
Havoc Pennington's avatar
Havoc Pennington committed
250
        
251 252 253 254 255
        return pixbuf;
}

/**
 * gdk_pixbuf_new_from_inline:
Havoc Pennington's avatar
Havoc Pennington committed
256
 * @inline_pixbuf: An inlined GdkPixbuf
257
 * @copy_pixels: whether to copy the pixels out of the inline data, or to use them in-place
Havoc Pennington's avatar
Havoc Pennington committed
258 259
 * @length: length of the inline data
 * @error: return location for error
260 261
 *
 * Create a #GdkPixbuf from a custom format invented to store pixbuf
Havoc Pennington's avatar
Havoc Pennington committed
262 263 264 265 266
 * data in C program code. This library comes with a program called
 * "make-inline-pixbuf" that can write out a variable definition
 * containing an inlined pixbuf.  This is useful if you want to ship a
 * program with images, but don't want to depend on any external
 * files.
267 268 269 270 271
 * 
 * The inline data format contains the pixels in #GdkPixbuf's native
 * format.  Since the inline pixbuf is read-only static data, you
 * don't need to copy it unless you intend to write to it.
 * 
Havoc Pennington's avatar
Havoc Pennington committed
272 273 274 275 276 277
 * If you create a pixbuf from const inline data compiled into your
 * program, it's probably safe to ignore errors, since things will
 * always succeed.  For non-const inline data, you could get out of
 * memory. For untrusted inline data located at runtime, you could
 * have corrupt inline data in addition.
 * 
278
 * Return value: A newly-created #GdkPixbuf structure with a reference count of
Havoc Pennington's avatar
Havoc Pennington committed
279
 * 1, or NULL If error is set.
280 281 282 283
 **/
GdkPixbuf*
gdk_pixbuf_new_from_inline   (const guchar *inline_pixbuf,
                              gboolean      copy_pixels,
Havoc Pennington's avatar
Havoc Pennington committed
284 285
                              int           length,
                              GError      **error)
286 287 288 289 290 291 292 293 294
{
        const guchar *p;
        GdkPixbuf *pixbuf;
        GdkPixbufInlineFormat format;

        if (length >= 0 && length < 8) {
                /* not enough bytes to contain even the magic number
                 * and format code.
                 */
Havoc Pennington's avatar
Havoc Pennington committed
295 296 297 298
                g_set_error (error,
                             GDK_PIXBUF_ERROR,
                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
                             _("Image contained no data."));
299 300 301 302 303 304
                return NULL;
        }
        
        p = inline_pixbuf;

        if (read_int (&p) != GDK_PIXBUF_INLINE_MAGIC_NUMBER) {
Havoc Pennington's avatar
Havoc Pennington committed
305 306 307 308
                g_set_error (error,
                             GDK_PIXBUF_ERROR,
                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
                             _("Image isn't in the correct format (inline GdkPixbuf format)"));
309 310 311 312 313 314 315 316
                return NULL;
        }

        format = read_int (&p);

        switch (format)
        {
        case GDK_PIXBUF_INLINE_RAW:
Havoc Pennington's avatar
Havoc Pennington committed
317
                pixbuf = read_raw_inline (p, copy_pixels, length - 8, error);
318 319 320
                break;

        default:
Havoc Pennington's avatar
Havoc Pennington committed
321 322 323 324 325
                g_set_error (error,
                             GDK_PIXBUF_ERROR,
                             GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
                             _("This version of the software is unable to read images with type code %d"),
                             format);
326 327 328 329 330 331
                return NULL;
        }

        return pixbuf;
}