io-pnm.c 19.6 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
 * 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>
26
#include <ctype.h>
27 28 29 30
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>
31
#include "gdk-pixbuf-private.h"
32
#include "gdk-pixbuf-io.h"
33 34 35 36 37


#define PNM_BUF_SIZE 4096

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

typedef enum {
Havoc Pennington's avatar
Havoc Pennington committed
42
	PNM_FORMAT_PGM = 1,
43 44 45 46 47 48 49 50
	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
51 52 53
	guchar buffer[PNM_BUF_SIZE];
	guchar *byte;
	guint nbytes;
54 55 56
} PnmIOBuffer;

typedef struct {
Havoc Pennington's avatar
Havoc Pennington committed
57
	ModuleUpdatedNotifyFunc updated_func;
58
	ModulePreparedNotifyFunc prepared_func;
Havoc Pennington's avatar
Havoc Pennington committed
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
	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;
79 80 81
	
} PnmLoaderContext;

Havoc Pennington's avatar
Havoc Pennington committed
82 83 84 85 86 87 88 89
GdkPixbuf   *gdk_pixbuf__pnm_image_load          (FILE *f);
gpointer    gdk_pixbuf__pnm_image_begin_load     (ModulePreparedNotifyFunc func, 
						  ModuleUpdatedNotifyFunc func2,
						  ModuleFrameDoneNotifyFunc frame_done_func,
						  ModuleAnimationDoneNotifyFunc anim_done_func,
						  gpointer user_data);
void        gdk_pixbuf__pnm_image_stop_load      (gpointer context);
gboolean    gdk_pixbuf__pnm_image_load_increment (gpointer context, guchar *buf, guint size);
90

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

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


102 103 104 105 106
/* 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       */
107
static void
108
explode_bitmap_into_buf (PnmLoaderContext *context)
109
{
110 111 112 113 114
	gint j;
	guchar *from, *to, data;
	gint bit;
	guchar *dptr;
	gint wid, x, y;
Havoc Pennington's avatar
Havoc Pennington committed
115
	
116 117
	g_return_if_fail (context != NULL);
	g_return_if_fail (context->dptr != NULL);
Havoc Pennington's avatar
Havoc Pennington committed
118
	
119 120 121 122
	/* 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
123 124
	
	from = dptr + ((wid - 1) / 8);
125
	to   = dptr + (wid - 1) * 3;
Michael Fulbright's avatar
Michael Fulbright committed
126
/*	bit  = 7 - (((y+1)*wid-1) % 8); */
Havoc Pennington's avatar
Havoc Pennington committed
127 128
	bit  = 7 - ((wid-1) % 8);
	
129 130 131
	/* get first byte and align properly */
	data = from[0];
	for (j = 0; j < bit; j++, data >>= 1);
Havoc Pennington's avatar
Havoc Pennington committed
132
	
133
	for (x = wid-1; x >= 0; x--) {
Michael Fulbright's avatar
Michael Fulbright committed
134
/*		g_print ("%c",  (data & 1) ? '*' : ' '); */
Havoc Pennington's avatar
Havoc Pennington committed
135 136 137
		
		to[0] = to[1] = to[2] = (data & 0x01) ? 0x00 : 0xff;
		
138 139
		to -= 3;
		bit++;
Havoc Pennington's avatar
Havoc Pennington committed
140
		
141 142 143 144 145 146 147
		if (bit > 7) {
			from--;
			data = from[0];
			bit = 0;
		} else {
			data >>= 1;
		}
148
	}
Havoc Pennington's avatar
Havoc Pennington committed
149
	
Michael Fulbright's avatar
Michael Fulbright committed
150
/*	g_print ("\n"); */
151 152 153 154
}

/* explode gray image row into rgb components in pixbuf */
static void
155
explode_gray_into_buf (PnmLoaderContext *context)
156
{
157
	gint j;
158
	guchar *from, *to;
159
	guint w;
Havoc Pennington's avatar
Havoc Pennington committed
160
	
161 162
	g_return_if_fail (context != NULL);
	g_return_if_fail (context->dptr != NULL);
Havoc Pennington's avatar
Havoc Pennington committed
163
	
164 165 166
	/* Expand grey->colour.  Expand from the end of the
	 * memory down, so we can use the same buffer.
	 */
167 168 169
	w = context->width;
	from = context->dptr + w - 1;
	to = context->dptr + (w - 1) * 3;
170 171 172 173 174 175 176 177 178
	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
179 180 181
/* skip over whitespace and comments in input buffer */
static gint
pnm_skip_whitespace (PnmIOBuffer *inbuf)
182
{
Havoc Pennington's avatar
Havoc Pennington committed
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
	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 */
			for ( ; *inptr != '\n' && inptr < inend; inptr++);
		} else if (!isspace (*inptr)) {
			inbuf->byte = inptr;
			inbuf->nbytes = (guint) (inend - inptr);
			return PNM_OK;
200 201
		}
	}
Havoc Pennington's avatar
Havoc Pennington committed
202 203 204 205 206
	
	inbuf->byte = inptr;
	inbuf->nbytes = (guint) (inend - inptr);
	
	return PNM_SUSPEND;
207 208
}

Havoc Pennington's avatar
Havoc Pennington committed
209
/* read next number from buffer */
210
static gint
Havoc Pennington's avatar
Havoc Pennington committed
211
pnm_read_next_value (PnmIOBuffer *inbuf, guint *value)
212
{
Havoc Pennington's avatar
Havoc Pennington committed
213 214 215 216
	register guchar *inptr, *word, *p;
	guchar *inend, buf[128];
	gchar *endptr;
	gint retval;
217
	
Havoc Pennington's avatar
Havoc Pennington committed
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
	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 */
	if ((retval = pnm_skip_whitespace (inbuf)) != PNM_OK)
		return retval;
	
	inend = inbuf->byte + inbuf->nbytes;
	inptr = inbuf->byte;
	
	/* copy this pnm 'word' into a temp buffer */
	for (p = inptr, word = buf; (p < inend) && !isspace (*p) && (p - inptr < 128); p++, word++)
		*word = *p;
	*word = '\0';
	
	/* hmmm, there must be more data to this 'word' */
	if (!isspace (*p))
		return PNM_SUSPEND;
	
	/* get the value */
	*value = strtol (buf, &endptr, 10);
	if (*endptr != '\0')
		return PNM_FATAL_ERR;
	
	inbuf->byte = p;
	inbuf->nbytes = (guint) (inend - p);
	
	return PNM_OK;
247 248 249 250 251 252 253
}

/* 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
254
	gint retval;
255
	
Havoc Pennington's avatar
Havoc Pennington committed
256
	g_return_val_if_fail (context != NULL, PNM_FATAL_ERR);
257
	
Havoc Pennington's avatar
Havoc Pennington committed
258
	inbuf = &context->inbuf;
259
	
Havoc Pennington's avatar
Havoc Pennington committed
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
	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;
		
		if (*inbuf->byte != 'P')
			return PNM_FATAL_ERR;
		
		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:
			return PNM_FATAL_ERR;
		}
		
		if (!inbuf->nbytes)
			return PNM_SUSPEND;
		
		inbuf->byte++;
		inbuf->nbytes--;
300 301
	}
	
Havoc Pennington's avatar
Havoc Pennington committed
302 303 304 305 306 307 308 309 310 311 312 313 314
	if (!context->width) {
		/* read the pixmap width */
		guint width = 0;
		
		retval = pnm_read_next_value (inbuf, &width);
		
		if (retval != PNM_OK)
			return retval;
		
		if (!width)
			return PNM_FATAL_ERR;
		
		context->width = width;
315 316
	}
	
Havoc Pennington's avatar
Havoc Pennington committed
317 318 319 320 321 322 323 324 325 326 327 328 329
	if (!context->height) {
		/* read the pixmap height */
		guint height = 0;
		
		retval = pnm_read_next_value (inbuf, &height);
		
		if (retval != PNM_OK)
			return retval;
		
		if (!height)
			return PNM_FATAL_ERR;
		
		context->height = height;
330 331
	}
	
Havoc Pennington's avatar
Havoc Pennington committed
332
	switch (context->type) {
333 334 335 336
	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
337 338 339 340 341 342 343 344
		if (!context->maxval) {
			retval = pnm_read_next_value (inbuf, &context->maxval);
			
			if (retval != PNM_OK)
				return retval;
			
			if (context->maxval == 0)
				return PNM_FATAL_ERR;
345 346 347 348 349
		}
		break;
	default:
		break;
	}
Havoc Pennington's avatar
Havoc Pennington committed
350
	
351
	return PNM_OK;
352 353 354
}

static gint
355
pnm_read_raw_scanline (PnmLoaderContext *context)
356 357
{
	PnmIOBuffer *inbuf;
Havoc Pennington's avatar
Havoc Pennington committed
358 359 360 361 362
	guint numbytes, offset;
	guint numpix = 0;
	guchar *dest;
	guint i;
	
363
	g_return_val_if_fail (context != NULL, PNM_FATAL_ERR);
Havoc Pennington's avatar
Havoc Pennington committed
364
	
365
	inbuf = &context->inbuf;
Havoc Pennington's avatar
Havoc Pennington committed
366
	
367 368
	switch (context->type) {
	case PNM_FORMAT_PBM_RAW:
Havoc Pennington's avatar
Havoc Pennington committed
369
		numpix = inbuf->nbytes * 8;
370
		break;
371
	case PNM_FORMAT_PGM_RAW:
Havoc Pennington's avatar
Havoc Pennington committed
372
		numpix = inbuf->nbytes;
373
		break;
374
	case PNM_FORMAT_PPM_RAW:
Havoc Pennington's avatar
Havoc Pennington committed
375
		numpix = inbuf->nbytes / 3;
376 377 378 379 380 381 382
		break;
	default:
		g_warning ("io-pnm.c: Illegal raw pnm type!\n");
		return PNM_FATAL_ERR;
	}
	
	numpix = MIN (numpix, context->width - context->output_col);
Havoc Pennington's avatar
Havoc Pennington committed
383 384
	
	if (!numpix)
385 386
		return PNM_SUSPEND;
	
Havoc Pennington's avatar
Havoc Pennington committed
387
	context->dptr = context->pixels + context->output_row * context->rowstride;
388 389 390
	
	switch (context->type) {
	case PNM_FORMAT_PBM_RAW:
Havoc Pennington's avatar
Havoc Pennington committed
391 392
		numbytes = (numpix / 8) + ((numpix % 8) ? 1 : 0);
		offset = context->output_col / 8;
393 394 395 396 397 398
		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
399 400
		numbytes = numpix * 3;
		offset = context->output_col * 3;
401 402 403
		break;
	default:
		g_warning ("io-pnm.c: Illegal raw pnm type!\n");
Arturo Espinosa's avatar
Arturo Espinosa committed
404
		return PNM_FATAL_ERR;
405 406
	}
	
Havoc Pennington's avatar
Havoc Pennington committed
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
	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:
		g_warning ("Invalid raw pnm format!");
	}
	
	inbuf->byte += numbytes;
	inbuf->nbytes -= numbytes;
	
438 439
	context->output_col += numpix;
	if (context->output_col == context->width) {
Havoc Pennington's avatar
Havoc Pennington committed
440 441 442
		if (context->type == PNM_FORMAT_PBM_RAW)
			explode_bitmap_into_buf (context);
		else if (context->type == PNM_FORMAT_PGM_RAW)
443 444 445 446 447 448 449
			explode_gray_into_buf (context);
		
		context->output_col = 0;
		context->output_row++;
	} else {
		return PNM_SUSPEND;
	}
Havoc Pennington's avatar
Havoc Pennington committed
450
	
451 452 453 454 455 456
	return PNM_OK;
}

static gint
pnm_read_ascii_scanline (PnmLoaderContext *context)
{
Havoc Pennington's avatar
Havoc Pennington committed
457 458 459
	PnmIOBuffer *inbuf;
	guint offset;
	guint value, numval, i;
460 461
	guchar data;
	guchar mask;
Havoc Pennington's avatar
Havoc Pennington committed
462 463 464
	guchar *dptr;
	gint retval;
	
465
	g_return_val_if_fail (context != NULL, PNM_FATAL_ERR);
Havoc Pennington's avatar
Havoc Pennington committed
466
	
Arturo Espinosa's avatar
Arturo Espinosa committed
467
	data = mask = 0;
Havoc Pennington's avatar
Havoc Pennington committed
468
	
469
	inbuf = &context->inbuf;
Havoc Pennington's avatar
Havoc Pennington committed
470 471 472
	
	context->dptr = context->pixels + context->output_row * context->rowstride;
	
473 474
	switch (context->type) {
	case PNM_FORMAT_PBM:
Michael Fulbright's avatar
Michael Fulbright committed
475
		numval = MIN (8, context->width - context->output_col);
Havoc Pennington's avatar
Havoc Pennington committed
476
		offset = context->output_col / 8;
477 478 479
		break;
	case PNM_FORMAT_PGM:
		numval = 1;
480
		offset = context->output_col;
481 482 483
		break;
	case PNM_FORMAT_PPM:
		numval = 3;
Havoc Pennington's avatar
Havoc Pennington committed
484
		offset = context->output_col * 3;
485 486 487 488 489 490
		break;
		
	default:
		g_warning ("Can't happen\n");
		return PNM_FATAL_ERR;
	}
Havoc Pennington's avatar
Havoc Pennington committed
491 492 493
	
	dptr = context->dptr + offset + context->scan_state;
	
494 495 496 497
	while (TRUE) {
		if (context->type == PNM_FORMAT_PBM) {
			mask = 0x80;
			data = 0;
Michael Fulbright's avatar
Michael Fulbright committed
498
			numval = MIN (8, context->width - context->output_col);
499
		}
Havoc Pennington's avatar
Havoc Pennington committed
500 501 502 503 504 505 506
		
		for (i = context->scan_state; i < numval; i++) {
			retval = pnm_read_next_value (inbuf, &value);
			if (retval != PNM_OK) {
				/* save state and return */
				context->scan_state = i;
				return retval;
507
			}
Havoc Pennington's avatar
Havoc Pennington committed
508
			
509 510 511 512 513
			switch (context->type) {
			case PNM_FORMAT_PBM:
				if (value)
					data |= mask;
				mask >>= 1;
514
				
515 516 517
				break;
			case PNM_FORMAT_PGM:
			case PNM_FORMAT_PPM:
Havoc Pennington's avatar
Havoc Pennington committed
518 519 520 521 522
				/* scale the color to an 8-bit color depth */
				if (value > context->maxval)
					*dptr++ = 255;
				else
					*dptr++ = (guchar)(255 * value / context->maxval);
523 524
				break;
			default:
Havoc Pennington's avatar
Havoc Pennington committed
525
				g_warning ("io-pnm.c: Illegal ascii pnm type!\n");
526 527
				break;
			}
528
		}
529
		
Havoc Pennington's avatar
Havoc Pennington committed
530 531
		context->scan_state = 0;
		
Michael Fulbright's avatar
Michael Fulbright committed
532 533
		if (context->type == PNM_FORMAT_PBM) {
			*dptr++ = data;
534
			context->output_col += numval;
Michael Fulbright's avatar
Michael Fulbright committed
535 536 537
		} else {
			context->output_col++;
		}
Havoc Pennington's avatar
Havoc Pennington committed
538
		
539
		if (context->output_col == context->width) {
Havoc Pennington's avatar
Havoc Pennington committed
540 541 542
			if (context->type == PNM_FORMAT_PBM)
				explode_bitmap_into_buf (context);
			else if (context->type == PNM_FORMAT_PGM)
543
				explode_gray_into_buf (context);
Havoc Pennington's avatar
Havoc Pennington committed
544
			
545 546 547 548
			context->output_col = 0;
			context->output_row++;
			break;
		}
549
	}
Havoc Pennington's avatar
Havoc Pennington committed
550
	
551 552 553
	return PNM_OK;
}

Havoc Pennington's avatar
Havoc Pennington committed
554
/* returns 1 if a scanline was converted, 0 means we ran out of data */
555 556 557
static gint
pnm_read_scanline (PnmLoaderContext *context)
{
Havoc Pennington's avatar
Havoc Pennington committed
558 559
	gint retval;
	
560
	g_return_val_if_fail (context != NULL, PNM_FATAL_ERR);
Havoc Pennington's avatar
Havoc Pennington committed
561
	
562 563 564 565 566 567
	/* 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
568 569 570
		retval = pnm_read_raw_scanline (context);
		if (retval != PNM_OK)
			return retval;
571 572 573 574
		break;
	case PNM_FORMAT_PBM:
	case PNM_FORMAT_PGM:
	case PNM_FORMAT_PPM:
Havoc Pennington's avatar
Havoc Pennington committed
575 576 577
		retval = pnm_read_ascii_scanline (context);
		if (retval != PNM_OK)
			return retval;
578
		break;
579
	default:
580
		g_warning ("Cannot load these image types (yet)\n");
581 582
		return PNM_FATAL_ERR;
	}
Havoc Pennington's avatar
Havoc Pennington committed
583
	
584
	return PNM_OK;
585 586 587 588
}

/* Shared library entry point */
GdkPixbuf *
Federico Mena Quintero's avatar
Federico Mena Quintero committed
589
gdk_pixbuf__pnm_image_load (FILE *f)
590 591 592
{
	PnmLoaderContext context;
	PnmIOBuffer *inbuf;
Havoc Pennington's avatar
Havoc Pennington committed
593 594 595
	gint nbytes;
	gint retval;
	
596
	/* pretend to be doing progressive loading */
Arturo Espinosa's avatar
Arturo Espinosa committed
597 598
	context.updated_func = NULL;
	context.prepared_func = NULL;
599
	context.user_data = NULL;
Havoc Pennington's avatar
Havoc Pennington committed
600 601 602 603 604 605
	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
606 607
	context.pixels = NULL;
	context.pixbuf = NULL;
Havoc Pennington's avatar
Havoc Pennington committed
608 609 610 611
	context.got_header = FALSE;
	context.did_prescan = FALSE;
	context.scan_state = 0;
	
612
	inbuf = &context.inbuf;
Havoc Pennington's avatar
Havoc Pennington committed
613 614 615 616
	
	while (!feof (f)) {
		guint num_to_read;
		
617
		/* keep buffer as full as possible */
Havoc Pennington's avatar
Havoc Pennington committed
618 619 620 621 622 623 624 625 626 627
		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 */
		if (nbytes == 0 && ferror (f)) {
			/* we ran out of data? */
628 629
			if (context.pixbuf)
				gdk_pixbuf_unref (context.pixbuf);
Havoc Pennington's avatar
Havoc Pennington committed
630
			g_warning ("io-pnm.c: Ran out of data.\n");
631 632
			return NULL;
		}
Havoc Pennington's avatar
Havoc Pennington committed
633 634 635 636
		
		inbuf->nbytes += nbytes;
		inbuf->byte = inbuf->buffer;
		
637 638
		/* get header if needed */
		if (!context.got_header) {
Havoc Pennington's avatar
Havoc Pennington committed
639 640
			retval = pnm_read_header (&context);
			if (retval == PNM_FATAL_ERR)
641
				return NULL;
Havoc Pennington's avatar
Havoc Pennington committed
642
			else if (retval == PNM_SUSPEND)
643
				continue;
Havoc Pennington's avatar
Havoc Pennington committed
644
			
645 646
			context.got_header = TRUE;
		}
Havoc Pennington's avatar
Havoc Pennington committed
647
		
648 649
		/* scan until we hit image data */
		if (!context.did_prescan) {
Havoc Pennington's avatar
Havoc Pennington committed
650 651 652 653
			retval = pnm_skip_whitespace (inbuf);
			if (retval == PNM_FATAL_ERR)
				return NULL;
			else if (retval == PNM_SUSPEND)
654
				continue;
Havoc Pennington's avatar
Havoc Pennington committed
655
			
656 657 658
			context.did_prescan = TRUE;
			context.output_row = 0;
			context.output_col = 0;
Havoc Pennington's avatar
Havoc Pennington committed
659
			
Michael Fulbright's avatar
Michael Fulbright committed
660
			context.rowstride = context.width * 3;
Havoc Pennington's avatar
Havoc Pennington committed
661 662
			context.pixels = g_malloc (context.height * context.width * 3);
			
Michael Fulbright's avatar
Michael Fulbright committed
663
			if (!context.pixels) {
664
				/* Failed to allocate memory */
Havoc Pennington's avatar
Havoc Pennington committed
665
				g_warning ("Couldn't allocate pixel buf");
666
			}
667
		}
Havoc Pennington's avatar
Havoc Pennington committed
668
		
669 670
		/* if we got here we're reading image data */
		while (context.output_row < context.height) {
Havoc Pennington's avatar
Havoc Pennington committed
671 672 673
			retval = pnm_read_scanline (&context);
			
			if (retval == PNM_SUSPEND) {
674
				break;
Havoc Pennington's avatar
Havoc Pennington committed
675
			} else if (retval == PNM_FATAL_ERR) {
676 677
				if (context.pixbuf)
					gdk_pixbuf_unref (context.pixbuf);
678 679 680 681
				g_warning ("io-pnm.c: error reading rows..\n");
				return NULL;
			}
		}
Havoc Pennington's avatar
Havoc Pennington committed
682
		
683 684 685 686 687
		if (context.output_row < context.height)
			continue;
		else
			break;
	}
Havoc Pennington's avatar
Havoc Pennington committed
688
	
689
	return gdk_pixbuf_new_from_data (context.pixels, GDK_COLORSPACE_RGB, FALSE, 8,
690 691
					 context.width, context.height, 
					 context.width * 3, free_buffer, NULL);
692 693 694 695 696 697 698 699 700 701

}

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

gpointer
Federico Mena Quintero's avatar
Federico Mena Quintero committed
702 703 704 705 706
gdk_pixbuf__pnm_image_begin_load (ModulePreparedNotifyFunc prepared_func, 
				  ModuleUpdatedNotifyFunc  updated_func,
				  ModuleFrameDoneNotifyFunc frame_done_func,
				  ModuleAnimationDoneNotifyFunc anim_done_func,
				  gpointer user_data)
707
{
708
	PnmLoaderContext *context;
Havoc Pennington's avatar
Havoc Pennington committed
709
	
710
	context = g_new0 (PnmLoaderContext, 1);
711 712 713
	context->prepared_func = prepared_func;
	context->updated_func  = updated_func;
	context->user_data = user_data;
Havoc Pennington's avatar
Havoc Pennington committed
714 715 716
	context->width = 0;
	context->height = 0;
	context->maxval = 0;
Arturo Espinosa's avatar
Arturo Espinosa committed
717 718
	context->pixbuf = NULL;
	context->pixels = NULL;
719 720
	context->got_header = FALSE;
	context->did_prescan = FALSE;
Havoc Pennington's avatar
Havoc Pennington committed
721 722 723 724 725
	context->scan_state = 0;
	
	context->inbuf.nbytes = 0;
	context->inbuf.byte  = NULL;
	
726 727 728 729 730 731 732 733 734
	return (gpointer) context;
}

/*
 * context - returned from image_begin_load
 *
 * free context, unref gdk_pixbuf
 */
void
Federico Mena Quintero's avatar
Federico Mena Quintero committed
735
gdk_pixbuf__pnm_image_stop_load (gpointer data)
736
{
737
	PnmLoaderContext *context = (PnmLoaderContext *) data;
Havoc Pennington's avatar
Havoc Pennington committed
738
	
739
	g_return_if_fail (context != NULL);
Havoc Pennington's avatar
Havoc Pennington committed
740
	
741 742
	if (context->pixbuf)
		gdk_pixbuf_unref (context->pixbuf);
Havoc Pennington's avatar
Havoc Pennington committed
743
	
744 745 746 747 748 749 750 751 752 753 754
	g_free (context);
}

/*
 * context - from image_begin_load
 * buf - new image data
 * size - length of new image data
 *
 * append image data onto inrecrementally built output image
 */
gboolean
Federico Mena Quintero's avatar
Federico Mena Quintero committed
755
gdk_pixbuf__pnm_image_load_increment (gpointer data, guchar *buf, guint size)
756
{
757
	PnmLoaderContext *context = (PnmLoaderContext *)data;
Havoc Pennington's avatar
Havoc Pennington committed
758 759 760
	PnmIOBuffer *inbuf;
	guchar *old_byte;
	guint old_nbytes;
761
	guchar *bufhd;
Havoc Pennington's avatar
Havoc Pennington committed
762 763 764
	guint num_left, spinguard;
	gint retval;
	
765 766
	g_return_val_if_fail (context != NULL, FALSE);
	g_return_val_if_fail (buf != NULL, FALSE);
Havoc Pennington's avatar
Havoc Pennington committed
767
	
768 769
	bufhd = buf;
	inbuf = &context->inbuf;
Havoc Pennington's avatar
Havoc Pennington committed
770 771 772
	old_nbytes = inbuf->nbytes;
	old_byte  = inbuf->byte;
	
773 774 775 776
	num_left = size;
	spinguard = 0;
	while (TRUE) {
		guint num_to_copy;
Havoc Pennington's avatar
Havoc Pennington committed
777
		
778
		/* keep buffer as full as possible */
Havoc Pennington's avatar
Havoc Pennington committed
779
		num_to_copy = MIN (PNM_BUF_SIZE - inbuf->nbytes, num_left);
780 781 782
		
		if (num_to_copy == 0)
			spinguard++;
Havoc Pennington's avatar
Havoc Pennington committed
783
		
784
		if (spinguard > 1)
785
			return TRUE;
Havoc Pennington's avatar
Havoc Pennington committed
786 787 788 789 790
		
		if (inbuf->byte != NULL && inbuf->nbytes > 0)
			memmove (inbuf->buffer, inbuf->byte, inbuf->nbytes);
		
		memcpy (inbuf->buffer + inbuf->nbytes, bufhd, num_to_copy);
791
		bufhd += num_to_copy;
Havoc Pennington's avatar
Havoc Pennington committed
792 793
		inbuf->nbytes += num_to_copy;
		inbuf->byte = inbuf->buffer;
794 795 796
		num_left -= num_to_copy;
		
		/* ran out of data and we haven't exited main loop */
Havoc Pennington's avatar
Havoc Pennington committed
797
		if (inbuf->nbytes == 0)
798
			return TRUE;
Havoc Pennington's avatar
Havoc Pennington committed
799
		
800
		/* get header if needed */
801
		if (!context->got_header) {
Havoc Pennington's avatar
Havoc Pennington committed
802 803 804
			retval = pnm_read_header (context);
			
			if (retval == PNM_FATAL_ERR)
805
				return FALSE;
Havoc Pennington's avatar
Havoc Pennington committed
806
			else if (retval == PNM_SUSPEND)
807
				continue;
Havoc Pennington's avatar
Havoc Pennington committed
808
			
809
			context->got_header = TRUE;
810
		}
Havoc Pennington's avatar
Havoc Pennington committed
811
		
812 813
		/* scan until we hit image data */
		if (!context->did_prescan) {
Havoc Pennington's avatar
Havoc Pennington committed
814 815 816 817 818
			retval = pnm_skip_whitespace (inbuf);
			
			if (retval == PNM_FATAL_ERR)
				return FALSE;
			else if (retval == PNM_SUSPEND)
819
				continue;
Havoc Pennington's avatar
Havoc Pennington committed
820
						
821 822 823
			context->did_prescan = TRUE;
			context->output_row = 0;
			context->output_col = 0;
Havoc Pennington's avatar
Havoc Pennington committed
824 825 826 827 828 829 830
			
			context->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, 
							  FALSE,
							  8, 
							  context->width,
							  context->height);
			
831 832 833 834
			if (context->pixbuf == NULL) {
				/* Failed to allocate memory */
				g_error ("Couldn't allocate gdkpixbuf");
			}
Havoc Pennington's avatar
Havoc Pennington committed
835
			
836 837
			context->pixels = context->pixbuf->pixels;
			context->rowstride = context->pixbuf->rowstride;
Havoc Pennington's avatar
Havoc Pennington committed
838
			
839 840 841
			/* Notify the client that we are ready to go */
			(* context->prepared_func) (context->pixbuf,
						    context->user_data);
842
		}
Havoc Pennington's avatar
Havoc Pennington committed
843
		
844 845
		/* if we got here we're reading image data */
		while (context->output_row < context->height) {
Havoc Pennington's avatar
Havoc Pennington committed
846 847 848
			retval = pnm_read_scanline (context);
			
			if (retval == PNM_SUSPEND) {
849
				break;
Havoc Pennington's avatar
Havoc Pennington committed
850
			} else if (retval == PNM_FATAL_ERR) {
851 852
				if (context->pixbuf)
					gdk_pixbuf_unref (context->pixbuf);
Havoc Pennington's avatar
Havoc Pennington committed
853
				g_warning ("io-pnm.c: error reading rows.\n");
854
				return FALSE;
Havoc Pennington's avatar
Havoc Pennington committed
855
			} else if (retval == PNM_OK) {	
856 857
				/* send updated signal */
				(* context->updated_func) (context->pixbuf,
858 859 860 861 862
							   0, 
							   context->output_row-1,
							   context->width, 
							   1,
							   context->user_data);
863 864
			}
		}
Havoc Pennington's avatar
Havoc Pennington committed
865
		
866 867 868 869
		if (context->output_row < context->height)
			continue;
		else
			break;
870
	}
Havoc Pennington's avatar
Havoc Pennington committed
871
	
872 873
	return TRUE;
}