io-pnm.c 24.4 KB
Newer Older
Havoc Pennington's avatar
Havoc Pennington committed
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 3 4 5
/* GdkPixbuf library - PNM image loader
 *
 * Copyright (C) 1999 Red Hat, Inc.
 *
Havoc Pennington's avatar
Havoc Pennington committed
6 7
 * Authors: Jeffrey Stedfast <fejj@helixcode.com>
 *          Michael Fulbright <drmike@redhat.com>
8 9
 *
 * This library is free software; you can redistribute it and/or
Havoc Pennington's avatar
Havoc Pennington committed
10
 * modify it under the terms of the GNU Library General Public
11 12 13 14 15 16
 * 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
Havoc Pennington's avatar
Havoc Pennington committed
17
 * Library General Public License for more details.
18
 *
Havoc Pennington's avatar
Havoc Pennington committed
19
 * You should have received a copy of the GNU Library General Public
20 21 22 23 24 25 26 27 28 29
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>
30
#include "gdk-pixbuf-private.h"
31
#include "gdk-pixbuf-io.h"
32 33 34 35 36


#define PNM_BUF_SIZE 4096

#define PNM_FATAL_ERR  -1
Havoc Pennington's avatar
Havoc Pennington committed
37 38
#define PNM_SUSPEND     0
#define PNM_OK          1
39 40

typedef enum {
Havoc Pennington's avatar
Havoc Pennington committed
41
	PNM_FORMAT_PGM = 1,
42 43 44 45 46 47 48 49
	PNM_FORMAT_PGM_RAW,
	PNM_FORMAT_PPM,
	PNM_FORMAT_PPM_RAW,
	PNM_FORMAT_PBM,
	PNM_FORMAT_PBM_RAW
} PnmFormat;

typedef struct {
Havoc Pennington's avatar
Havoc Pennington committed
50 51 52
	guchar buffer[PNM_BUF_SIZE];
	guchar *byte;
	guint nbytes;
53 54 55
} PnmIOBuffer;

typedef struct {
Havoc Pennington's avatar
Havoc Pennington committed
56
	ModuleUpdatedNotifyFunc updated_func;
57
	ModulePreparedNotifyFunc prepared_func;
Havoc Pennington's avatar
Havoc Pennington committed
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
	gpointer user_data;
	
	GdkPixbuf *pixbuf;
	guchar *pixels;        /* incoming pixel data buffer */
	guchar *dptr;          /* current position in pixbuf */
	
	PnmIOBuffer inbuf;
	
	guint width;
	guint height;
	guint maxval;
	guint rowstride;
	PnmFormat type;
	
	guint output_row;      /* last row to be completed */
	guint output_col;
	gboolean did_prescan;  /* are we in image data yet? */
	gboolean got_header;   /* have we loaded pnm header? */
	
	guint scan_state;
Havoc Pennington's avatar
Havoc Pennington committed
78 79

	GError **error;
80 81 82
	
} PnmLoaderContext;

83 84 85 86 87 88 89 90 91
static GdkPixbuf   *gdk_pixbuf__pnm_image_load          (FILE *f, GError **error);
static gpointer    gdk_pixbuf__pnm_image_begin_load     (ModulePreparedNotifyFunc func, 
							 ModuleUpdatedNotifyFunc func2,
							 gpointer user_data,
							 GError **error);
static gboolean    gdk_pixbuf__pnm_image_stop_load      (gpointer context, GError **error);
static gboolean    gdk_pixbuf__pnm_image_load_increment (gpointer context,
							 const guchar *buf, guint size,
							 GError **error);
92

Havoc Pennington's avatar
Havoc Pennington committed
93 94
static void explode_bitmap_into_buf              (PnmLoaderContext *context);
static void explode_gray_into_buf                (PnmLoaderContext *context);
95

96
/* Destroy notification function for the pixbuf */
97
static void
98
free_buffer (guchar *pixels, gpointer data)
99
{
Havoc Pennington's avatar
Havoc Pennington committed
100
	g_free (pixels);
101 102 103
}


104 105 106 107 108
/* explode bitmap data into rgb components         */
/* we need to know what the row so we can          */
/* do sub-byte expansion (since 1 byte = 8 pixels) */
/* context->dptr MUST point at first byte in incoming data  */
/* which corresponds to first pixel of row y       */
109
static void
110
explode_bitmap_into_buf (PnmLoaderContext *context)
111
{
112 113 114 115 116
	gint j;
	guchar *from, *to, data;
	gint bit;
	guchar *dptr;
	gint wid, x, y;
Havoc Pennington's avatar
Havoc Pennington committed
117
	
118 119
	g_return_if_fail (context != NULL);
	g_return_if_fail (context->dptr != NULL);
Havoc Pennington's avatar
Havoc Pennington committed
120
	
121 122 123 124
	/* I'm no clever bit-hacker so I'm sure this can be optimized */
	dptr = context->dptr;
	y    = context->output_row;
	wid  = context->width;
Havoc Pennington's avatar
Havoc Pennington committed
125 126
	
	from = dptr + ((wid - 1) / 8);
127
	to   = dptr + (wid - 1) * 3;
Michael Fulbright's avatar
Michael Fulbright committed
128
/*	bit  = 7 - (((y+1)*wid-1) % 8); */
Havoc Pennington's avatar
Havoc Pennington committed
129 130
	bit  = 7 - ((wid-1) % 8);
	
131 132 133
	/* get first byte and align properly */
	data = from[0];
	for (j = 0; j < bit; j++, data >>= 1);
Havoc Pennington's avatar
Havoc Pennington committed
134
	
135
	for (x = wid-1; x >= 0; x--) {
Michael Fulbright's avatar
Michael Fulbright committed
136
/*		g_print ("%c",  (data & 1) ? '*' : ' '); */
Havoc Pennington's avatar
Havoc Pennington committed
137 138 139
		
		to[0] = to[1] = to[2] = (data & 0x01) ? 0x00 : 0xff;
		
140 141
		to -= 3;
		bit++;
Havoc Pennington's avatar
Havoc Pennington committed
142
		
143 144 145 146 147 148 149
		if (bit > 7) {
			from--;
			data = from[0];
			bit = 0;
		} else {
			data >>= 1;
		}
150
	}
Havoc Pennington's avatar
Havoc Pennington committed
151
	
Michael Fulbright's avatar
Michael Fulbright committed
152
/*	g_print ("\n"); */
153 154 155 156
}

/* explode gray image row into rgb components in pixbuf */
static void
157
explode_gray_into_buf (PnmLoaderContext *context)
158
{
159
	gint j;
160
	guchar *from, *to;
161
	guint w;
Havoc Pennington's avatar
Havoc Pennington committed
162
	
163 164
	g_return_if_fail (context != NULL);
	g_return_if_fail (context->dptr != NULL);
Havoc Pennington's avatar
Havoc Pennington committed
165
	
166 167 168
	/* Expand grey->colour.  Expand from the end of the
	 * memory down, so we can use the same buffer.
	 */
169 170 171
	w = context->width;
	from = context->dptr + w - 1;
	to = context->dptr + (w - 1) * 3;
172 173 174 175 176 177 178 179 180
	for (j = w - 1; j >= 0; j--) {
		to[0] = from[0];
		to[1] = from[0];
		to[2] = from[0];
		to -= 3;
		from--;
	}
}

Havoc Pennington's avatar
Havoc Pennington committed
181 182
/* skip over whitespace and comments in input buffer */
static gint
Havoc Pennington's avatar
Havoc Pennington committed
183
pnm_skip_whitespace (PnmIOBuffer *inbuf, GError **error)
184
{
Havoc Pennington's avatar
Havoc Pennington committed
185 186 187 188 189 190 191 192 193 194 195 196
	register guchar *inptr;
	guchar *inend;
	
	g_return_val_if_fail (inbuf != NULL, PNM_FATAL_ERR);
	g_return_val_if_fail (inbuf->byte != NULL, PNM_FATAL_ERR);
	
	inend = inbuf->byte + inbuf->nbytes;
	inptr = inbuf->byte;
	
	for ( ; inptr < inend; inptr++) {
		if (*inptr == '#') {
			/* in comment - skip to the end of this line */
197 198 199 200 201 202 203 204
			for ( ; *inptr != '\n' && inptr < inend; inptr++)
				;
			
			if ( *inptr != '\n' ) {
				/* couldn't read whole comment */
				return PNM_SUSPEND;
			}
			
205
		} else if (!g_ascii_isspace (*inptr)) {
Havoc Pennington's avatar
Havoc Pennington committed
206 207 208
			inbuf->byte = inptr;
			inbuf->nbytes = (guint) (inend - inptr);
			return PNM_OK;
209 210
		}
	}
Havoc Pennington's avatar
Havoc Pennington committed
211 212 213 214 215
	
	inbuf->byte = inptr;
	inbuf->nbytes = (guint) (inend - inptr);
	
	return PNM_SUSPEND;
216 217
}

Havoc Pennington's avatar
Havoc Pennington committed
218
/* read next number from buffer */
219
static gint
Havoc Pennington's avatar
Havoc Pennington committed
220
pnm_read_next_value (PnmIOBuffer *inbuf, guint *value, GError **error)
221
{
Havoc Pennington's avatar
Havoc Pennington committed
222
	register guchar *inptr, *word, *p;
223
	guchar *inend, buf[129];
Havoc Pennington's avatar
Havoc Pennington committed
224 225
	gchar *endptr;
	gint retval;
226
	glong result;
227
	
Havoc Pennington's avatar
Havoc Pennington committed
228 229 230 231 232
	g_return_val_if_fail (inbuf != NULL, PNM_FATAL_ERR);
	g_return_val_if_fail (inbuf->byte != NULL, PNM_FATAL_ERR);
	g_return_val_if_fail (value != NULL, PNM_FATAL_ERR);
	
	/* skip white space */
Havoc Pennington's avatar
Havoc Pennington committed
233
	if ((retval = pnm_skip_whitespace (inbuf, error)) != PNM_OK)
Havoc Pennington's avatar
Havoc Pennington committed
234 235 236 237 238 239
		return retval;
	
	inend = inbuf->byte + inbuf->nbytes;
	inptr = inbuf->byte;
	
	/* copy this pnm 'word' into a temp buffer */
240
	for (p = inptr, word = buf; (p < inend) && !g_ascii_isspace (*p) && (*p != '#') && (p - inptr < 128); p++, word++)
Havoc Pennington's avatar
Havoc Pennington committed
241 242 243 244
		*word = *p;
	*word = '\0';
	
	/* hmmm, there must be more data to this 'word' */
245 246
	if (!g_ascii_isspace (*p) && (*p != '#')  && (p - inptr < 128))
	    return PNM_SUSPEND;
Havoc Pennington's avatar
Havoc Pennington committed
247 248
	
	/* get the value */
249 250
	result = strtol (buf, &endptr, 10);
	if (*endptr != '\0' || result < 0) {
Havoc Pennington's avatar
Havoc Pennington committed
251 252 253 254
		g_set_error (error,
			     GDK_PIXBUF_ERROR,
			     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
			     _("PNM loader expected to find an integer, but didn't"));
Havoc Pennington's avatar
Havoc Pennington committed
255
		return PNM_FATAL_ERR;
Havoc Pennington's avatar
Havoc Pennington committed
256
	}
257 258
	*value = result;

Havoc Pennington's avatar
Havoc Pennington committed
259 260 261 262
	inbuf->byte = p;
	inbuf->nbytes = (guint) (inend - p);
	
	return PNM_OK;
263 264 265 266 267 268 269
}

/* returns PNM_OK, PNM_SUSPEND, or PNM_FATAL_ERR */
static gint
pnm_read_header (PnmLoaderContext *context)
{
	PnmIOBuffer *inbuf;
Havoc Pennington's avatar
Havoc Pennington committed
270
	gint retval;
271
	
Havoc Pennington's avatar
Havoc Pennington committed
272
	g_return_val_if_fail (context != NULL, PNM_FATAL_ERR);
273
	
Havoc Pennington's avatar
Havoc Pennington committed
274
	inbuf = &context->inbuf;
275
	
Havoc Pennington's avatar
Havoc Pennington committed
276 277 278 279 280 281
	if (!context->type) {
		/* file must start with a 'P' followed by a numeral  */
		/* so loop till we get enough data to determine type */
		if (inbuf->nbytes < 2)
			return PNM_SUSPEND;
		
Havoc Pennington's avatar
Havoc Pennington committed
282 283 284
		if (*inbuf->byte != 'P') {
			g_set_error (context->error,
				     GDK_PIXBUF_ERROR,
285
				     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
Havoc Pennington's avatar
Havoc Pennington committed
286
				     _("PNM file has an incorrect initial byte"));
Havoc Pennington's avatar
Havoc Pennington committed
287
			return PNM_FATAL_ERR;
Havoc Pennington's avatar
Havoc Pennington committed
288
		}
Havoc Pennington's avatar
Havoc Pennington committed
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
		
		inbuf->byte++;
		inbuf->nbytes--;
		
		switch (*inbuf->byte) {
		case '1':
			context->type = PNM_FORMAT_PBM;
			break;
		case '2':
			context->type = PNM_FORMAT_PGM;
			break;
		case '3':
			context->type = PNM_FORMAT_PPM;
			break;
		case '4':
			context->type = PNM_FORMAT_PBM_RAW;
			break;
		case '5':
			context->type = PNM_FORMAT_PGM_RAW;
			break;
		case '6':
			context->type = PNM_FORMAT_PPM_RAW;
			break;
		default:
Havoc Pennington's avatar
Havoc Pennington committed
313 314
			g_set_error (context->error,
				     GDK_PIXBUF_ERROR,
315
				     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
Havoc Pennington's avatar
Havoc Pennington committed
316
				     _("PNM file is not in a recognized PNM subformat"));
Havoc Pennington's avatar
Havoc Pennington committed
317 318 319 320 321 322 323 324
			return PNM_FATAL_ERR;
		}
		
		if (!inbuf->nbytes)
			return PNM_SUSPEND;
		
		inbuf->byte++;
		inbuf->nbytes--;
325 326
	}
	
Havoc Pennington's avatar
Havoc Pennington committed
327 328 329 330
	if (!context->width) {
		/* read the pixmap width */
		guint width = 0;
		
Havoc Pennington's avatar
Havoc Pennington committed
331 332
		retval = pnm_read_next_value (inbuf, &width,
					      context->error);
Havoc Pennington's avatar
Havoc Pennington committed
333
		
Havoc Pennington's avatar
Havoc Pennington committed
334
		if (retval != PNM_OK) 
Havoc Pennington's avatar
Havoc Pennington committed
335 336
			return retval;
		
Havoc Pennington's avatar
Havoc Pennington committed
337 338 339
		if (!width) {
			g_set_error (context->error,
				     GDK_PIXBUF_ERROR,
340
				     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
Havoc Pennington's avatar
Havoc Pennington committed
341
				     _("PNM file has an image width of 0"));
Havoc Pennington's avatar
Havoc Pennington committed
342
			return PNM_FATAL_ERR;
Havoc Pennington's avatar
Havoc Pennington committed
343
		}
Havoc Pennington's avatar
Havoc Pennington committed
344 345
		
		context->width = width;
346 347
	}
	
Havoc Pennington's avatar
Havoc Pennington committed
348 349 350 351
	if (!context->height) {
		/* read the pixmap height */
		guint height = 0;
		
Havoc Pennington's avatar
Havoc Pennington committed
352 353
		retval = pnm_read_next_value (inbuf, &height,
					      context->error);
Havoc Pennington's avatar
Havoc Pennington committed
354 355 356 357
		
		if (retval != PNM_OK)
			return retval;
		
Havoc Pennington's avatar
Havoc Pennington committed
358 359 360
		if (!height) {
			g_set_error (context->error,
				     GDK_PIXBUF_ERROR,
361
				     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
Havoc Pennington's avatar
Havoc Pennington committed
362
				     _("PNM file has an image height of 0"));
Havoc Pennington's avatar
Havoc Pennington committed
363
			return PNM_FATAL_ERR;
Havoc Pennington's avatar
Havoc Pennington committed
364
		}
Havoc Pennington's avatar
Havoc Pennington committed
365 366
		
		context->height = height;
367 368
	}
	
Havoc Pennington's avatar
Havoc Pennington committed
369
	switch (context->type) {
370 371 372 373
	case PNM_FORMAT_PPM:
	case PNM_FORMAT_PPM_RAW:
	case PNM_FORMAT_PGM:
	case PNM_FORMAT_PGM_RAW:
Havoc Pennington's avatar
Havoc Pennington committed
374
		if (!context->maxval) {
Havoc Pennington's avatar
Havoc Pennington committed
375 376
			retval = pnm_read_next_value (inbuf, &context->maxval,
						      context->error);
Havoc Pennington's avatar
Havoc Pennington committed
377 378 379 380
			
			if (retval != PNM_OK)
				return retval;
			
Havoc Pennington's avatar
Havoc Pennington committed
381 382 383
			if (context->maxval == 0) {
				g_set_error (context->error,
					     GDK_PIXBUF_ERROR,
384
					     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
385 386 387 388 389 390 391
 					     _("Maximum color value in PNM file is 0"));
				return PNM_FATAL_ERR;
			}

			if (context->maxval > 65535) {
				g_set_error (context->error,
					     GDK_PIXBUF_ERROR,
392
					     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
393 394 395 396 397 398 399
 					     _("Maximum color value in PNM file is too large"));
				return PNM_FATAL_ERR;
			}

			if (context->maxval > 255) {
				g_set_error (context->error,
					     GDK_PIXBUF_ERROR,
400
					     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
401
 					     _("Can't handle PNM files with maximum color values greater than 255"));
Havoc Pennington's avatar
Havoc Pennington committed
402
				return PNM_FATAL_ERR;
Havoc Pennington's avatar
Havoc Pennington committed
403
			}
404 405 406 407 408
		}
		break;
	default:
		break;
	}
Havoc Pennington's avatar
Havoc Pennington committed
409
	
410
	return PNM_OK;
411 412 413
}

static gint
414
pnm_read_raw_scanline (PnmLoaderContext *context)
415 416
{
	PnmIOBuffer *inbuf;
Havoc Pennington's avatar
Havoc Pennington committed
417 418 419 420 421
	guint numbytes, offset;
	guint numpix = 0;
	guchar *dest;
	guint i;
	
422
	g_return_val_if_fail (context != NULL, PNM_FATAL_ERR);
Havoc Pennington's avatar
Havoc Pennington committed
423
	
424
	inbuf = &context->inbuf;
Havoc Pennington's avatar
Havoc Pennington committed
425
	
426 427
	switch (context->type) {
	case PNM_FORMAT_PBM_RAW:
Havoc Pennington's avatar
Havoc Pennington committed
428
		numpix = inbuf->nbytes * 8;
429
		break;
430
	case PNM_FORMAT_PGM_RAW:
Havoc Pennington's avatar
Havoc Pennington committed
431
		numpix = inbuf->nbytes;
432
		break;
433
	case PNM_FORMAT_PPM_RAW:
Havoc Pennington's avatar
Havoc Pennington committed
434
		numpix = inbuf->nbytes / 3;
435 436
		break;
	default:
Havoc Pennington's avatar
Havoc Pennington committed
437 438 439 440
		g_set_error (context->error,
			     GDK_PIXBUF_ERROR,
			     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
			     _("Raw PNM image type is invalid"));
441 442 443 444
		return PNM_FATAL_ERR;
	}
	
	numpix = MIN (numpix, context->width - context->output_col);
Havoc Pennington's avatar
Havoc Pennington committed
445 446
	
	if (!numpix)
447 448
		return PNM_SUSPEND;
	
Havoc Pennington's avatar
Havoc Pennington committed
449
	context->dptr = context->pixels + context->output_row * context->rowstride;
450 451 452
	
	switch (context->type) {
	case PNM_FORMAT_PBM_RAW:
Havoc Pennington's avatar
Havoc Pennington committed
453 454
		numbytes = (numpix / 8) + ((numpix % 8) ? 1 : 0);
		offset = context->output_col / 8;
455 456 457 458 459 460
		break;
	case PNM_FORMAT_PGM_RAW:
		numbytes = numpix;
		offset = context->output_col;
		break;
	case PNM_FORMAT_PPM_RAW:
Havoc Pennington's avatar
Havoc Pennington committed
461 462
		numbytes = numpix * 3;
		offset = context->output_col * 3;
463 464
		break;
	default:
Havoc Pennington's avatar
Havoc Pennington committed
465 466 467 468
		g_set_error (context->error,
			     GDK_PIXBUF_ERROR,
			     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
			     _("Raw PNM image type is invalid"));
Arturo Espinosa's avatar
Arturo Espinosa committed
469
		return PNM_FATAL_ERR;
470 471
	}
	
Havoc Pennington's avatar
Havoc Pennington committed
472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496
	switch (context->type) {
	case PNM_FORMAT_PBM_RAW:
		dest = context->dptr + offset;		
		memcpy (dest, inbuf->byte, numbytes);
		break;
	case PNM_FORMAT_PGM_RAW:
	case PNM_FORMAT_PPM_RAW:
		dest = context->dptr + offset;
		
		if (context->maxval == 255) {
			/* special-case optimization */
			memcpy (dest, inbuf->byte, numbytes);
		} else {
			for (i = 0; i < numbytes; i++) {
				guchar *byte = inbuf->byte + i;
				
				/* scale the color to an 8-bit color depth */
				if (*byte > context->maxval)
					*dest++ = 255;
				else
					*dest++ = (guchar) (255 * *byte / context->maxval);
			}
		}
		break;
	default:
Havoc Pennington's avatar
Havoc Pennington committed
497 498 499 500 501
		g_set_error (context->error,
			     GDK_PIXBUF_ERROR,
			     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
			     _("Raw PNM image type is invalid"));
		return PNM_FATAL_ERR;
Havoc Pennington's avatar
Havoc Pennington committed
502 503 504 505 506
	}
	
	inbuf->byte += numbytes;
	inbuf->nbytes -= numbytes;
	
507 508
	context->output_col += numpix;
	if (context->output_col == context->width) {
Havoc Pennington's avatar
Havoc Pennington committed
509 510 511
		if (context->type == PNM_FORMAT_PBM_RAW)
			explode_bitmap_into_buf (context);
		else if (context->type == PNM_FORMAT_PGM_RAW)
512 513 514 515 516 517 518
			explode_gray_into_buf (context);
		
		context->output_col = 0;
		context->output_row++;
	} else {
		return PNM_SUSPEND;
	}
Havoc Pennington's avatar
Havoc Pennington committed
519
	
520 521 522 523 524 525
	return PNM_OK;
}

static gint
pnm_read_ascii_scanline (PnmLoaderContext *context)
{
Havoc Pennington's avatar
Havoc Pennington committed
526 527 528
	PnmIOBuffer *inbuf;
	guint offset;
	guint value, numval, i;
529 530
	guchar data;
	guchar mask;
Havoc Pennington's avatar
Havoc Pennington committed
531 532 533
	guchar *dptr;
	gint retval;
	
534
	g_return_val_if_fail (context != NULL, PNM_FATAL_ERR);
Havoc Pennington's avatar
Havoc Pennington committed
535
	
Arturo Espinosa's avatar
Arturo Espinosa committed
536
	data = mask = 0;
Havoc Pennington's avatar
Havoc Pennington committed
537
	
538
	inbuf = &context->inbuf;
Havoc Pennington's avatar
Havoc Pennington committed
539 540 541
	
	context->dptr = context->pixels + context->output_row * context->rowstride;
	
542 543
	switch (context->type) {
	case PNM_FORMAT_PBM:
Michael Fulbright's avatar
Michael Fulbright committed
544
		numval = MIN (8, context->width - context->output_col);
Havoc Pennington's avatar
Havoc Pennington committed
545
		offset = context->output_col / 8;
546 547 548
		break;
	case PNM_FORMAT_PGM:
		numval = 1;
549
		offset = context->output_col;
550 551 552
		break;
	case PNM_FORMAT_PPM:
		numval = 3;
Havoc Pennington's avatar
Havoc Pennington committed
553
		offset = context->output_col * 3;
554 555 556
		break;
		
	default:
Havoc Pennington's avatar
Havoc Pennington committed
557 558 559 560 561
		g_set_error (context->error,
			     GDK_PIXBUF_ERROR,
			     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
			     _("PNM image format is invalid"));

562 563
		return PNM_FATAL_ERR;
	}
Havoc Pennington's avatar
Havoc Pennington committed
564 565 566
	
	dptr = context->dptr + offset + context->scan_state;
	
567 568 569 570
	while (TRUE) {
		if (context->type == PNM_FORMAT_PBM) {
			mask = 0x80;
			data = 0;
Michael Fulbright's avatar
Michael Fulbright committed
571
			numval = MIN (8, context->width - context->output_col);
572
		}
Havoc Pennington's avatar
Havoc Pennington committed
573 574
		
		for (i = context->scan_state; i < numval; i++) {
Havoc Pennington's avatar
Havoc Pennington committed
575 576
			retval = pnm_read_next_value (inbuf, &value,
						      context->error);
Havoc Pennington's avatar
Havoc Pennington committed
577 578 579 580
			if (retval != PNM_OK) {
				/* save state and return */
				context->scan_state = i;
				return retval;
581
			}
Havoc Pennington's avatar
Havoc Pennington committed
582
			
583 584 585 586 587
			switch (context->type) {
			case PNM_FORMAT_PBM:
				if (value)
					data |= mask;
				mask >>= 1;
588
				
589 590 591
				break;
			case PNM_FORMAT_PGM:
			case PNM_FORMAT_PPM:
Havoc Pennington's avatar
Havoc Pennington committed
592 593 594 595 596
				/* scale the color to an 8-bit color depth */
				if (value > context->maxval)
					*dptr++ = 255;
				else
					*dptr++ = (guchar)(255 * value / context->maxval);
597 598
				break;
			default:
Havoc Pennington's avatar
Havoc Pennington committed
599 600 601 602 603
				g_set_error (context->error,
					     GDK_PIXBUF_ERROR,
					     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
					     _("PNM image format is invalid"));
				return PNM_FATAL_ERR;
604 605
				break;
			}
606
		}
607
		
Havoc Pennington's avatar
Havoc Pennington committed
608 609
		context->scan_state = 0;
		
Michael Fulbright's avatar
Michael Fulbright committed
610 611
		if (context->type == PNM_FORMAT_PBM) {
			*dptr++ = data;
612
			context->output_col += numval;
Michael Fulbright's avatar
Michael Fulbright committed
613 614 615
		} else {
			context->output_col++;
		}
Havoc Pennington's avatar
Havoc Pennington committed
616
		
617
		if (context->output_col == context->width) {
Havoc Pennington's avatar
Havoc Pennington committed
618 619 620
			if (context->type == PNM_FORMAT_PBM)
				explode_bitmap_into_buf (context);
			else if (context->type == PNM_FORMAT_PGM)
621
				explode_gray_into_buf (context);
Havoc Pennington's avatar
Havoc Pennington committed
622
			
623 624 625 626
			context->output_col = 0;
			context->output_row++;
			break;
		}
627
	}
Havoc Pennington's avatar
Havoc Pennington committed
628
	
629 630 631
	return PNM_OK;
}

Havoc Pennington's avatar
Havoc Pennington committed
632
/* returns 1 if a scanline was converted, 0 means we ran out of data */
633 634 635
static gint
pnm_read_scanline (PnmLoaderContext *context)
{
Havoc Pennington's avatar
Havoc Pennington committed
636 637
	gint retval;
	
638
	g_return_val_if_fail (context != NULL, PNM_FATAL_ERR);
Havoc Pennington's avatar
Havoc Pennington committed
639
	
640 641 642 643 644 645
	/* read in image data */
	/* for raw formats this is trivial */
	switch (context->type) {
	case PNM_FORMAT_PBM_RAW:
	case PNM_FORMAT_PGM_RAW:
	case PNM_FORMAT_PPM_RAW:
Havoc Pennington's avatar
Havoc Pennington committed
646 647 648
		retval = pnm_read_raw_scanline (context);
		if (retval != PNM_OK)
			return retval;
649 650 651 652
		break;
	case PNM_FORMAT_PBM:
	case PNM_FORMAT_PGM:
	case PNM_FORMAT_PPM:
Havoc Pennington's avatar
Havoc Pennington committed
653 654 655
		retval = pnm_read_ascii_scanline (context);
		if (retval != PNM_OK)
			return retval;
656
		break;
657
	default:
Havoc Pennington's avatar
Havoc Pennington committed
658 659 660 661 662
		g_set_error (context->error,
			     GDK_PIXBUF_ERROR,
			     GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
			     _("PNM image loader does not support this PNM subformat"));

663 664
		return PNM_FATAL_ERR;
	}
Havoc Pennington's avatar
Havoc Pennington committed
665
	
666
	return PNM_OK;
667 668 669
}

/* Shared library entry point */
670
static GdkPixbuf *
Havoc Pennington's avatar
Havoc Pennington committed
671
gdk_pixbuf__pnm_image_load (FILE *f, GError **error)
672 673 674
{
	PnmLoaderContext context;
	PnmIOBuffer *inbuf;
Havoc Pennington's avatar
Havoc Pennington committed
675 676 677
	gint nbytes;
	gint retval;
	
678
	/* pretend to be doing progressive loading */
Arturo Espinosa's avatar
Arturo Espinosa committed
679 680
	context.updated_func = NULL;
	context.prepared_func = NULL;
681
	context.user_data = NULL;
Havoc Pennington's avatar
Havoc Pennington committed
682 683 684 685 686 687
	context.type = 0;
	context.inbuf.nbytes = 0;
	context.inbuf.byte = NULL;
	context.width = 0;
	context.height = 0;
	context.maxval = 0;
Arturo Espinosa's avatar
Arturo Espinosa committed
688 689
	context.pixels = NULL;
	context.pixbuf = NULL;
Havoc Pennington's avatar
Havoc Pennington committed
690 691 692
	context.got_header = FALSE;
	context.did_prescan = FALSE;
	context.scan_state = 0;
Havoc Pennington's avatar
Havoc Pennington committed
693
	context.error = error;
Havoc Pennington's avatar
Havoc Pennington committed
694
	
695
	inbuf = &context.inbuf;
Havoc Pennington's avatar
Havoc Pennington committed
696
	
697
	while (TRUE) {
Havoc Pennington's avatar
Havoc Pennington committed
698 699
		guint num_to_read;
		
700
		/* keep buffer as full as possible */
Havoc Pennington's avatar
Havoc Pennington committed
701 702 703 704 705 706 707 708
		num_to_read = PNM_BUF_SIZE - inbuf->nbytes;
		
		if (inbuf->byte != NULL && inbuf->nbytes > 0)
			memmove (inbuf->buffer, inbuf->byte, inbuf->nbytes);
		
		nbytes = fread (inbuf->buffer + inbuf->nbytes, 1, num_to_read, f);
		
		/* error checking */
709
		if (nbytes == 0) {
Havoc Pennington's avatar
Havoc Pennington committed
710
			/* we ran out of data? */
711
			if (context.pixbuf)
712
				g_object_unref (context.pixbuf);
713 714 715 716
			g_set_error (error,
				     GDK_PIXBUF_ERROR,
				     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
				     _("Premature end-of-file encountered"));
717 718
			return NULL;
		}
Havoc Pennington's avatar
Havoc Pennington committed
719 720 721 722
		
		inbuf->nbytes += nbytes;
		inbuf->byte = inbuf->buffer;
		
723 724
		/* get header if needed */
		if (!context.got_header) {
Havoc Pennington's avatar
Havoc Pennington committed
725 726
			retval = pnm_read_header (&context);
			if (retval == PNM_FATAL_ERR)
727
				return NULL;
Havoc Pennington's avatar
Havoc Pennington committed
728
			else if (retval == PNM_SUSPEND)
729
				continue;
Havoc Pennington's avatar
Havoc Pennington committed
730
			
731 732
			context.got_header = TRUE;
		}
Havoc Pennington's avatar
Havoc Pennington committed
733
		
734 735
		/* scan until we hit image data */
		if (!context.did_prescan) {
736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762
			switch (context.type) {
			case PNM_FORMAT_PBM_RAW:
			case PNM_FORMAT_PGM_RAW:
			case PNM_FORMAT_PPM_RAW:
				if (inbuf->nbytes <= 0)
					continue;
				/* raw formats require exactly one whitespace */
				if (!g_ascii_isspace(*(inbuf->byte))) 
					{
						g_set_error (error,
							     GDK_PIXBUF_ERROR,
							     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
							     _("Raw PNM formats require exactly one whitespace before sample data"));
						return NULL;
					}
				inbuf->nbytes--;
				inbuf->byte++;
				break;
			default:
				retval = pnm_skip_whitespace (inbuf,
							      context.error);
				if (retval == PNM_FATAL_ERR)
					return NULL;
				else if (retval == PNM_SUSPEND)
					continue;
				break;
			}
763 764 765
			context.did_prescan = TRUE;
			context.output_row = 0;
			context.output_col = 0;
Havoc Pennington's avatar
Havoc Pennington committed
766
			
Michael Fulbright's avatar
Michael Fulbright committed
767
			context.rowstride = context.width * 3;
768
			context.pixels = g_try_malloc (context.height * context.width * 3);
Havoc Pennington's avatar
Havoc Pennington committed
769
			
Michael Fulbright's avatar
Michael Fulbright committed
770
			if (!context.pixels) {
771
				/* Failed to allocate memory */
772 773 774 775 776
				g_set_error (error,
					     GDK_PIXBUF_ERROR,
					     GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
					     _("Can't allocate memory for loading PNM image"));
				return NULL;
777
			}
778
		}
Havoc Pennington's avatar
Havoc Pennington committed
779
		
780 781
		/* if we got here we're reading image data */
		while (context.output_row < context.height) {
Havoc Pennington's avatar
Havoc Pennington committed
782 783 784
			retval = pnm_read_scanline (&context);
			
			if (retval == PNM_SUSPEND) {
785
				break;
Havoc Pennington's avatar
Havoc Pennington committed
786
			} else if (retval == PNM_FATAL_ERR) {
787
				if (context.pixbuf)
788
					g_object_unref (context.pixbuf);
Havoc Pennington's avatar
Havoc Pennington committed
789

790 791 792
				return NULL;
			}
		}
Havoc Pennington's avatar
Havoc Pennington committed
793
		
794 795 796 797 798
		if (context.output_row < context.height)
			continue;
		else
			break;
	}
Havoc Pennington's avatar
Havoc Pennington committed
799
	
800
	return gdk_pixbuf_new_from_data (context.pixels, GDK_COLORSPACE_RGB, FALSE, 8,
801 802
					 context.width, context.height, 
					 context.width * 3, free_buffer, NULL);
803 804 805 806 807 808 809 810 811

}

/* 
 * func - called when we have pixmap created (but no image data)
 * user_data - passed as arg 1 to func
 * return context (opaque to user)
 */

812
static gpointer
Federico Mena Quintero's avatar
Federico Mena Quintero committed
813 814
gdk_pixbuf__pnm_image_begin_load (ModulePreparedNotifyFunc prepared_func, 
				  ModuleUpdatedNotifyFunc  updated_func,
Havoc Pennington's avatar
Havoc Pennington committed
815 816
				  gpointer user_data,
				  GError **error)
817
{
818
	PnmLoaderContext *context;
Havoc Pennington's avatar
Havoc Pennington committed
819
	
820 821 822 823 824 825 826 827
	context = g_try_malloc (sizeof (PnmLoaderContext));
	if (!context) {
		g_set_error(error, GDK_PIXBUF_ERROR, 
			    GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
			    _("Insufficient memory to load PNM context struct"));
		return NULL;
	}
	memset (context, 0, sizeof (PnmLoaderContext));
828 829 830
	context->prepared_func = prepared_func;
	context->updated_func  = updated_func;
	context->user_data = user_data;
Havoc Pennington's avatar
Havoc Pennington committed
831 832 833
	context->width = 0;
	context->height = 0;
	context->maxval = 0;
Arturo Espinosa's avatar
Arturo Espinosa committed
834 835
	context->pixbuf = NULL;
	context->pixels = NULL;
836 837
	context->got_header = FALSE;
	context->did_prescan = FALSE;
Havoc Pennington's avatar
Havoc Pennington committed
838 839 840 841
	context->scan_state = 0;
	
	context->inbuf.nbytes = 0;
	context->inbuf.byte  = NULL;
Havoc Pennington's avatar
Havoc Pennington committed
842 843

	context->error = error;
Havoc Pennington's avatar
Havoc Pennington committed
844
	
845 846 847 848 849 850 851 852
	return (gpointer) context;
}

/*
 * context - returned from image_begin_load
 *
 * free context, unref gdk_pixbuf
 */
853 854 855
static gboolean
gdk_pixbuf__pnm_image_stop_load (gpointer data,
				 GError **error)
856
{
857
	PnmLoaderContext *context = (PnmLoaderContext *) data;
858
	gboolean retval = TRUE;
Havoc Pennington's avatar
Havoc Pennington committed
859
	
860
	g_return_val_if_fail (context != NULL, TRUE);
Havoc Pennington's avatar
Havoc Pennington committed
861
	
862
	if (context->pixbuf)
863
		g_object_unref (context->pixbuf);
864

865 866 867 868 869
#if 0
	/* We should ignore trailing newlines and we can't
	   generally complain about trailing stuff at all, since 
	   pnm allows to put multiple images in a file
	*/
870 871 872 873 874 875 876
	if (context->inbuf.nbytes > 0) {
		g_set_error (error,
			     GDK_PIXBUF_ERROR,
			     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
			     _("Unexpected end of PNM image data"));
		retval = FALSE;
	}
877
#endif
Havoc Pennington's avatar
Havoc Pennington committed
878
	
879
	g_free (context);
880 881

	return retval;
882 883 884 885 886 887 888 889 890
}

/*
 * context - from image_begin_load
 * buf - new image data
 * size - length of new image data
 *
 * append image data onto inrecrementally built output image
 */
891 892 893
static gboolean
gdk_pixbuf__pnm_image_load_increment (gpointer data,
				      const guchar *buf, guint size,
Havoc Pennington's avatar
Havoc Pennington committed
894
				      GError **error)
895
{
896
	PnmLoaderContext *context = (PnmLoaderContext *)data;
Havoc Pennington's avatar
Havoc Pennington committed
897 898 899
	PnmIOBuffer *inbuf;
	guchar *old_byte;
	guint old_nbytes;
900
	const guchar *bufhd;
Havoc Pennington's avatar
Havoc Pennington committed
901 902 903
	guint num_left, spinguard;
	gint retval;
	
904 905
	g_return_val_if_fail (context != NULL, FALSE);
	g_return_val_if_fail (buf != NULL, FALSE);
Havoc Pennington's avatar
Havoc Pennington committed
906 907

	context->error = error;
Havoc Pennington's avatar
Havoc Pennington committed
908
	
909 910
	bufhd = buf;
	inbuf = &context->inbuf;
Havoc Pennington's avatar
Havoc Pennington committed
911 912 913
	old_nbytes = inbuf->nbytes;
	old_byte  = inbuf->byte;
	
914 915 916 917
	num_left = size;
	spinguard = 0;
	while (TRUE) {
		guint num_to_copy;
Havoc Pennington's avatar
Havoc Pennington committed
918
		
919
		/* keep buffer as full as possible */
Havoc Pennington's avatar
Havoc Pennington committed
920
		num_to_copy = MIN (PNM_BUF_SIZE - inbuf->nbytes, num_left);
921 922 923
		
		if (num_to_copy == 0)
			spinguard++;
Havoc Pennington's avatar
Havoc Pennington committed
924
		
925
		if (spinguard > 1)
926
			return TRUE;
Havoc Pennington's avatar
Havoc Pennington committed
927 928 929 930 931
		
		if (inbuf->byte != NULL && inbuf->nbytes > 0)
			memmove (inbuf->buffer, inbuf->byte, inbuf->nbytes);
		
		memcpy (inbuf->buffer + inbuf->nbytes, bufhd, num_to_copy);
932
		bufhd += num_to_copy;