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

Arturo Espinosa's avatar
Arturo Espinosa committed
24
#include <config.h>
25
#include <string.h>
26
#include <glib.h>
27
#include "gdk-pixbuf-private.h"
28
#include "gdk-pixbuf-io.h"
Arturo Espinosa's avatar
Arturo Espinosa committed
29

Tor Lillqvist's avatar
Tor Lillqvist committed
30 31 32 33 34
#ifdef G_OS_WIN32
#define STRICT
#include <windows.h>
#endif

35 36


37
static gboolean
38
pixbuf_check_png (guchar *buffer, int size)
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
{
	if (size < 28)
		return FALSE;

	if (buffer [0] != 0x89 ||
	    buffer [1] != 'P' ||
	    buffer [2] != 'N' ||
	    buffer [3] != 'G' ||
	    buffer [4] != 0x0d ||
	    buffer [5] != 0x0a ||
	    buffer [6] != 0x1a ||
	    buffer [7] != 0x0a)
		return FALSE;

	return TRUE;
}

static gboolean
57
pixbuf_check_jpeg (guchar *buffer, int size)
58 59 60 61 62 63
{
	if (size < 10)
		return FALSE;

	if (buffer [0] != 0xff || buffer [1] != 0xd8)
		return FALSE;
64

65 66 67 68
	return TRUE;
}

static gboolean
69
pixbuf_check_tiff (guchar *buffer, int size)
70 71 72 73
{
	if (size < 10)
		return FALSE;

74 75 76
	if (buffer [0] == 'M' &&
	    buffer [1] == 'M' &&
	    buffer [2] == 0   &&
Mark Crichton's avatar
Mark Crichton committed
77
	    buffer [3] == 0x2a)
78 79
		return TRUE;

80 81 82
	if (buffer [0] == 'I' &&
	    buffer [1] == 'I' &&
	    buffer [2] == 0x2a &&
Mark Crichton's avatar
Mark Crichton committed
83
	    buffer [3] == 0)
84
		return TRUE;
85

86 87 88 89
	return FALSE;
}

static gboolean
90
pixbuf_check_gif (guchar *buffer, int size)
91 92 93
{
	if (size < 20)
		return FALSE;
94

95 96
	if (strncmp (buffer, "GIF8", 4) == 0)
		return TRUE;
97

98 99 100 101
	return FALSE;
}

static gboolean
102
pixbuf_check_xpm (guchar *buffer, int size)
103 104 105
{
	if (size < 20)
		return FALSE;
106

107 108
	if (strncmp (buffer, "/* XPM */", 9) == 0)
		return TRUE;
109

110 111 112 113
	return FALSE;
}

static gboolean
114
pixbuf_check_pnm (guchar *buffer, int size)
115 116 117 118
{
	if (size < 20)
		return FALSE;

119
	if (buffer [0] == 'P') {
120 121 122 123 124 125 126 127 128 129
		if (buffer [1] == '1' ||
		    buffer [1] == '2' ||
		    buffer [1] == '3' ||
		    buffer [1] == '4' ||
		    buffer [1] == '5' ||
		    buffer [1] == '6')
			return TRUE;
	}
	return FALSE;
}
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
static gboolean
pixbuf_check_sunras (guchar *buffer, int size)
{
	if (size < 32)
		return FALSE;

	if (buffer [0] != 0x59 ||
	    buffer [1] != 0xA6 ||
	    buffer [2] != 0x6A ||
	    buffer [3] != 0x95)
		return FALSE;

	return TRUE;
}

145 146 147 148 149 150 151 152 153
static gboolean
pixbuf_check_ico (guchar *buffer, int size)
{
	/* Note that this may cause false positives, but .ico's don't
	   have a magic number.*/
	if (size < 6)
		return FALSE;
	if (buffer [0] != 0x0 ||
	    buffer [1] != 0x0 ||
154
	    ((buffer [2] != 0x1)&&(buffer[2]!=0x2)) ||
155 156 157 158 159 160 161
	    buffer [3] != 0x0 ||
	    buffer [5] != 0x0 )
		return FALSE;

	return TRUE;
}

162 163 164 165 166 167 168 169 170 171 172 173 174

static gboolean
pixbuf_check_bmp (guchar *buffer, int size)
{
	if (size < 20)
		return FALSE;

	if (buffer [0] != 'B' || buffer [1] != 'M')
		return FALSE;

	return TRUE;
}

Federico Mena Quintero's avatar
Federico Mena Quintero committed
175
static GdkPixbufModule file_formats [] = {
Arturo Espinosa's avatar
Arturo Espinosa committed
176 177 178 179
	{ "png",  pixbuf_check_png, NULL,  NULL, NULL, NULL, NULL, NULL },
	{ "jpeg", pixbuf_check_jpeg, NULL, NULL, NULL, NULL, NULL, NULL },
	{ "tiff", pixbuf_check_tiff, NULL, NULL, NULL, NULL, NULL, NULL },
	{ "gif",  pixbuf_check_gif, NULL,  NULL, NULL, NULL, NULL, NULL },
180
#define XPM_FILE_FORMAT_INDEX 4
Arturo Espinosa's avatar
Arturo Espinosa committed
181
	{ "xpm",  pixbuf_check_xpm, NULL,  NULL, NULL, NULL, NULL, NULL },
182
	{ "pnm",  pixbuf_check_pnm, NULL,  NULL, NULL, NULL, NULL, NULL },
183
	{ "ras",  pixbuf_check_sunras, NULL,  NULL, NULL, NULL, NULL, NULL },
184
	{ "ico",  pixbuf_check_ico, NULL,  NULL, NULL, NULL, NULL, NULL },
Arturo Espinosa's avatar
Arturo Espinosa committed
185
	{ "bmp",  pixbuf_check_bmp, NULL,  NULL, NULL, NULL, NULL, NULL },
186
	{ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
Arturo Espinosa's avatar
Arturo Espinosa committed
187 188
};

Federico Mena Quintero's avatar
Federico Mena Quintero committed
189 190 191 192 193 194 195 196 197 198 199 200
#ifdef USE_GMODULE 
static gboolean
pixbuf_module_symbol (GModule *module, const char *module_name, const char *symbol_name, gpointer *symbol)
{
	char *full_symbol_name = g_strconcat ("gdk_pixbuf__", module_name, "_", symbol_name, NULL);
	gboolean return_value;

	return_value = g_module_symbol (module, full_symbol_name, symbol);
	g_free (full_symbol_name);
	
	return return_value;
}
201

Tor Lillqvist's avatar
Tor Lillqvist committed
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
#ifdef G_OS_WIN32

/* What would be the right place for this function? Also
 * gtk needs this function (to find the gtkrc and themes).
 * But it seems stupid for the gdk-pixbuf DLL to depend
 * on the gtk DLL. Should it be in the gdk DLL? Or should we
 * have a small static library at the top gtk+ level?
 */

static gchar *
gtk_win32_get_installation_directory (void)
{
  static gboolean been_here = FALSE;
  static gchar gtk_installation_dir[200];
  gchar win_dir[100];
  HKEY reg_key = NULL;
  DWORD type;
  DWORD nbytes = sizeof (gtk_installation_dir);

  if (been_here)
    return gtk_installation_dir;

  been_here = TRUE;

  if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, "Software\\GNU\\GTk+", 0,
		    KEY_QUERY_VALUE, &reg_key) != ERROR_SUCCESS
      || RegQueryValueEx (reg_key, "InstallationDirectory", 0,
			  &type, gtk_installation_dir, &nbytes) != ERROR_SUCCESS
      || type != REG_SZ)
    {
      /* Uh oh. Use hard-coded %WinDir%\gtk+ value */
      GetWindowsDirectory (win_dir, sizeof (win_dir));
      sprintf (gtk_installation_dir, "%s\\gtk+", win_dir);
    }

  if (reg_key != NULL)
    RegCloseKey (reg_key);

  return gtk_installation_dir;
}

static char *
get_libdir (void)
{
  static char *libdir = NULL;

  if (libdir == NULL)
    libdir = g_strdup_printf (gtk_win32_get_installation_directory (),
			      G_DIR_SEPARATOR_S,
			      "loaders",
			      NULL);

  return libdir;
}

#define PIXBUF_LIBDIR get_libdir ()

#endif

261 262 263 264 265
/* actually load the image handler - gdk_pixbuf_get_module only get a */
/* reference to the module to load, it doesn't actually load it       */
/* perhaps these actions should be combined in one function           */
void
gdk_pixbuf_load_module (GdkPixbufModule *image_module)
Arturo Espinosa's avatar
Arturo Espinosa committed
266
{
Mark Crichton's avatar
Mark Crichton committed
267
	char *module_name;
268 269
	char *path;
	GModule *module;
Jonathan Blandford's avatar
Jonathan Blandford committed
270
	gpointer load_sym;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
271 272
	char *name;
	
273
        g_return_if_fail (image_module->module == NULL);
274

Federico Mena Quintero's avatar
Federico Mena Quintero committed
275 276
	name = image_module->module_name;
	
277
	module_name = g_strconcat ("pixbufloader-", name, NULL);
278
	path = g_module_build_path (PIXBUF_LIBDIR, module_name);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
279

280
	module = g_module_open (path, G_MODULE_BIND_LAZY);
281
	if (!module) {
Owen Taylor's avatar
Owen Taylor committed
282 283 284 285 286
                /* Debug feature, check in GDK_PIXBUF_MODULEDIR, or working directory */
	        char *dir = g_getenv ("GDK_PIXBUF_MODULEDIR");
		if (!dir)
			dir = "";
	  
287
                g_free (path);
Owen Taylor's avatar
Owen Taylor committed
288
                path = g_module_build_path (dir, module_name);
289
                module = g_module_open (path, G_MODULE_BIND_LAZY);
290 291

                if (!module) {
292
                        g_warning ("Unable to load module: %s: %s", path, g_module_error ());
293
                        g_free (module_name);
294
                        g_free (path);
295 296
                        return;
                }
297
                g_free (path);
298 299 300 301 302
	} else {
                g_free (path);
        }

        g_free (module_name);
303

304
	image_module->module = module;
305

Federico Mena Quintero's avatar
Federico Mena Quintero committed
306
	if (pixbuf_module_symbol (module, name, "image_load", &load_sym))
307
		image_module->load = load_sym;
308

Federico Mena Quintero's avatar
Federico Mena Quintero committed
309
        if (pixbuf_module_symbol (module, name, "image_load_xpm_data", &load_sym))
310
		image_module->load_xpm_data = load_sym;
Jonathan Blandford's avatar
Jonathan Blandford committed
311

Federico Mena Quintero's avatar
Federico Mena Quintero committed
312
        if (pixbuf_module_symbol (module, name, "image_begin_load", &load_sym))
Jonathan Blandford's avatar
Jonathan Blandford committed
313 314
		image_module->begin_load = load_sym;

Federico Mena Quintero's avatar
Federico Mena Quintero committed
315
        if (pixbuf_module_symbol (module, name, "image_stop_load", &load_sym))
Jonathan Blandford's avatar
Jonathan Blandford committed
316 317
		image_module->stop_load = load_sym;

Federico Mena Quintero's avatar
Federico Mena Quintero committed
318
        if (pixbuf_module_symbol (module, name, "image_load_increment", &load_sym))
Jonathan Blandford's avatar
Jonathan Blandford committed
319
		image_module->load_increment = load_sym;
320

Federico Mena Quintero's avatar
Federico Mena Quintero committed
321
        if (pixbuf_module_symbol (module, name, "image_load_animation", &load_sym))
322
		image_module->load_animation = load_sym;
Arturo Espinosa's avatar
Arturo Espinosa committed
323
}
Federico Mena Quintero's avatar
Federico Mena Quintero committed
324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449
#else

#define mname(type,fn) gdk_pixbuf__ ## type ## _image_ ##fn
#define m_load(type)  extern GdkPixbuf * mname(type,load) (FILE *f);
#define m_load_xpm_data(type)  extern GdkPixbuf * mname(type,load_xpm_data) (const char **data);
#define m_begin_load(type)  \
   extern gpointer mname(type,begin_load) (ModulePreparedNotifyFunc prepare_func, \
				 ModuleUpdatedNotifyFunc update_func, \
				 ModuleFrameDoneNotifyFunc frame_done_func,\
				 ModuleAnimationDoneNotifyFunc anim_done_func,\
				 gpointer user_data);
#define m_stop_load(type)  extern void mname(type,stop_load) (gpointer context);
#define m_load_increment(type)  extern gboolean mname(type,load_increment) (gpointer context, const guchar *buf, guint size);
#define m_load_animation(type)  extern GdkPixbufAnimation * mname(type,load_animation) (FILE *f);

m_load (png);
m_begin_load (png);
m_load_increment (png);
m_stop_load (png);
m_load (bmp);
m_begin_load (bmp);
m_load_increment (bmp);
m_stop_load (bmp);
m_load (gif);
m_begin_load (gif);
m_load_increment (gif);
m_stop_load (gif);
m_load_animation (gif);
m_load (ico);
m_begin_load (ico);
m_load_increment (ico);
m_stop_load (ico);
m_load (jpeg);
m_begin_load (jpeg);
m_load_increment (jpeg);
m_stop_load (jpeg);
m_load (pnm);
m_begin_load (pnm);
m_load_increment (pnm);
m_stop_load (pnm);
m_load (ras);
m_begin_load (ras);
m_load_increment (ras);
m_stop_load (ras);
m_load (tiff);
m_begin_load (tiff);
m_load_increment (tiff);
m_stop_load (tiff);
m_load (xpm);
m_load_xpm_data (xpm);

void
gdk_pixbuf_load_module (GdkPixbufModule *image_module)
{
	image_module->module = (void *) 1;
	
	if (strcmp (image_module->module_name, "png") == 0){
		image_module->load           = mname (png,load);
		image_module->begin_load     = mname (png,begin_load);
		image_module->load_increment = mname (png,load_increment);
		image_module->stop_load      = mname (png,stop_load);
		return;
	}

	if (strcmp (image_module->module_name, "bmp") == 0){
		image_module->load           = mname (bmp,load);
		image_module->begin_load     = mname (bmp,begin_load);
		image_module->load_increment = mname (bmp,load_increment);
		image_module->stop_load      = mname (bmp,stop_load);
		return;
	}

	if (strcmp (image_module->module_name, "gif") == 0){
		image_module->load           = mname (gif,load);
		image_module->begin_load     = mname (gif,begin_load);
		image_module->load_increment = mname (gif,load_increment);
		image_module->stop_load      = mname (gif,stop_load);
		image_module->load_animation = mname (gif,load_animation);
		return;
	}

	if (strcmp (image_module->module_name, "ico") == 0){
		image_module->load           = mname (ico,load);
		image_module->begin_load     = mname (ico,begin_load);
		image_module->load_increment = mname (ico,load_increment);
		image_module->stop_load      = mname (ico,stop_load);
		return;
	}

	if (strcmp (image_module->module_name, "jpeg") == 0){
		image_module->load           = mname (jpeg,load);
		image_module->begin_load     = mname (jpeg,begin_load);
		image_module->load_increment = mname (jpeg,load_increment);
		image_module->stop_load      = mname (jpeg,stop_load);
		return;
	}
	if (strcmp (image_module->module_name, "pnm") == 0){
		image_module->load           = mname (pnm,load);
		image_module->begin_load     = mname (pnm,begin_load);
		image_module->load_increment = mname (pnm,load_increment);
		image_module->stop_load      = mname (pnm,stop_load);
		return;
	}
	if (strcmp (image_module->module_name, "ras") == 0){
		image_module->load           = mname (ras,load);
		image_module->begin_load     = mname (ras,begin_load);
		image_module->load_increment = mname (ras,load_increment);
		image_module->stop_load      = mname (ras,stop_load);
		return;
	}
	if (strcmp (image_module->module_name, "tiff") == 0){
		image_module->load           = mname (tiff,load);
		image_module->begin_load     = mname (tiff,begin_load);
		image_module->load_increment = mname (tiff,load_increment);
		image_module->stop_load      = mname (tiff,stop_load);
		return;
	}
	if (strcmp (image_module->module_name, "xpm") == 0){
		image_module->load           = mname (xpm,load);
		image_module->load_xpm_data  = mname (xpm,load_xpm_data);
		return;
	}
}


#endif
Arturo Espinosa's avatar
Arturo Espinosa committed
450

451 452


453
GdkPixbufModule *
454
gdk_pixbuf_get_module (guchar *buffer, guint size)
455
{
456
	int i;
457 458 459 460 461

	for (i = 0; file_formats [i].module_name; i++) {
		if ((* file_formats [i].format_check) (buffer, size))
			return &(file_formats[i]);
	}
462

463 464 465
	return NULL;
}

Federico Mena Quintero's avatar
Federico Mena Quintero committed
466 467 468
/**
 * gdk_pixbuf_new_from_file:
 * @filename: Name of file to load.
Federico Mena Quintero's avatar
Federico Mena Quintero committed
469
 *
Federico Mena Quintero's avatar
Federico Mena Quintero committed
470 471
 * Creates a new pixbuf by loading an image from a file.  The file format is
 * detected automatically.
Federico Mena Quintero's avatar
Federico Mena Quintero committed
472 473 474 475 476
 *
 * Return value: A newly-created pixbuf with a reference count of 1, or NULL if
 * any of several error conditions occurred:  the file could not be opened,
 * there was no loader for the file's format, there was not enough memory to
 * allocate the image buffer, or the image file contained invalid data.
Federico Mena Quintero's avatar
Federico Mena Quintero committed
477
 **/
478
GdkPixbuf *
479
gdk_pixbuf_new_from_file (const char *filename)
Arturo Espinosa's avatar
Arturo Espinosa committed
480
{
481
	GdkPixbuf *pixbuf;
482
	int size;
Arturo Espinosa's avatar
Arturo Espinosa committed
483
	FILE *f;
484
	guchar buffer [128];
485
	GdkPixbufModule *image_module;
Arturo Espinosa's avatar
Arturo Espinosa committed
486

487 488
	g_return_val_if_fail (filename != NULL, NULL);

Tor Lillqvist's avatar
Tor Lillqvist committed
489
	f = fopen (filename, "rb");
Arturo Espinosa's avatar
Arturo Espinosa committed
490 491
	if (!f)
		return NULL;
492

493 494
	size = fread (&buffer, 1, sizeof (buffer), f);
	if (size == 0) {
495
		fclose (f);
Arturo Espinosa's avatar
Arturo Espinosa committed
496
		return NULL;
497
	}
Arturo Espinosa's avatar
Arturo Espinosa committed
498

499
	image_module = gdk_pixbuf_get_module (buffer, size);
500 501
	if (!image_module) {
		g_warning ("Unable to find handler for file: %s", filename);
502
		fclose (f);
503 504
		return NULL;
	}
505

506 507
	if (image_module->module == NULL)
		gdk_pixbuf_load_module (image_module);
508

509 510 511
	if (image_module->load == NULL) {
		fclose (f);
		return NULL;
Arturo Espinosa's avatar
Arturo Espinosa committed
512 513
	}

514 515
	fseek (f, 0, SEEK_SET);
	pixbuf = (* image_module->load) (f);
516
	fclose (f);
517 518

	return pixbuf;
Arturo Espinosa's avatar
Arturo Espinosa committed
519
}
520

Federico Mena Quintero's avatar
Federico Mena Quintero committed
521 522
/**
 * gdk_pixbuf_new_from_xpm_data:
Federico Mena Quintero's avatar
Federico Mena Quintero committed
523
 * @data: Pointer to inline XPM data.
524
 *
Federico Mena Quintero's avatar
Federico Mena Quintero committed
525 526
 * Creates a new pixbuf by parsing XPM data in memory.  This data is commonly
 * the result of including an XPM file into a program's C source.
527
 *
Federico Mena Quintero's avatar
Federico Mena Quintero committed
528 529
 * Return value: A newly-created pixbuf with a reference count of 1.
 **/
530
GdkPixbuf *
531
gdk_pixbuf_new_from_xpm_data (const char **data)
532
{
533 534
	GdkPixbuf *(* load_xpm_data) (const char **data);
	GdkPixbuf *pixbuf;
535

536 537
	if (file_formats[XPM_FILE_FORMAT_INDEX].module == NULL)
		gdk_pixbuf_load_module (&file_formats[XPM_FILE_FORMAT_INDEX]);
538

539 540
	if (file_formats[XPM_FILE_FORMAT_INDEX].module == NULL) {
		g_warning ("Can't find gdk-pixbuf module for parsing inline XPM data");
541
		return NULL;
542 543
	} else if (file_formats[XPM_FILE_FORMAT_INDEX].load_xpm_data == NULL) {
		g_warning ("gdk-pixbuf XPM module lacks XPM data capability");
544
		return NULL;
545 546
	} else
		load_xpm_data = file_formats[XPM_FILE_FORMAT_INDEX].load_xpm_data;
547

548 549
	pixbuf = (* load_xpm_data) (data);
	return pixbuf;
550
}