io-ras.c 11.5 KB
Newer Older
1
/* GdkPixbuf library - SUNRAS image loader
2 3 4 5 6 7
 *
 * Copyright (C) 1999 The Free Software Foundation
 *
 * Authors: Arjan van de Ven <arjan@fenrus.demon.nl>
 *          Federico Mena-Quintero <federico@gimp.org>
 *
8 9
 * Based on io-gif.c, io-tiff.c and io-png.c
 *
10
 * This library is free software; you can redistribute it and/or
11
 * modify it under the terms of the GNU Lesser General Public
12 13 14 15 16 17
 * 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
18
 * Lesser General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU Lesser General Public
21 22 23 24
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
25

26 27 28
/*

Known bugs:
29
	* Compressed rasterfiles don't work yet
30 31

*/
32 33 34

#include <config.h>
#include <stdio.h>
Tor Lillqvist's avatar
Tor Lillqvist committed
35
#ifdef HAVE_UNISTD_H
36
#include <unistd.h>
Tor Lillqvist's avatar
Tor Lillqvist committed
37
#endif
38
#include <string.h>
39
#include "gdk-pixbuf-private.h"
40 41
#include "gdk-pixbuf-io.h"

42 43


44 45
/* 
   Header structure for sunras files.
46
   All values are in big-endian order on disk
47 48
   
   Note: Every scanline is padded to be a multiple of 16 bits
49
 */
50 51 52 53 54 55 56 57 58 59

struct rasterfile {
	guint magic;
	guint width;
	guint height;
	guint depth;
	guint length;
	guint type;
	guint maptype;
	guint maplength;
60 61 62
};

/* 
63 64
	This does a byte-order swap. Does glib have something like
	be32_to_cpu() ??
65
*/
66

67
/* Progressive loading */
68

69 70 71 72
struct ras_progressive_state {
	ModulePreparedNotifyFunc prepared_func;
	ModuleUpdatedNotifyFunc updated_func;
	gpointer user_data;
73

74 75 76
	gint HeaderSize;	/* The size of the header-part (incl colormap) */
	guchar *HeaderBuf;	/* The buffer for the header (incl colormap) */
	gint HeaderDone;	/* The nr of bytes actually in HeaderBuf */
77

78 79 80 81
	gint LineWidth;		/* The width of a line in bytes */
	guchar *LineBuf;	/* Buffer for 1 line */
	gint LineDone;		/* # of bytes in LineBuf */
	gint Lines;		/* # of finished lines */
82

83 84 85 86 87
	gint RasType;		/*  32 = BGRA
				   24 = BGR
				   8 = 8 bit colormapped
				   1  = 1 bit bitonal 
				 */
88 89


90
	struct rasterfile Header;	/* Decoded (BE->CPU) header */
91 92


93 94 95
	GdkPixbuf *pixbuf;	/* Our "target" */
};

96
static gpointer
Federico Mena Quintero's avatar
Federico Mena Quintero committed
97 98
gdk_pixbuf__ras_image_begin_load(ModulePreparedNotifyFunc prepared_func,
				 ModuleUpdatedNotifyFunc updated_func,
Havoc Pennington's avatar
Havoc Pennington committed
99 100
				 gpointer user_data,
                                 GError **error);
101 102 103 104
static gboolean gdk_pixbuf__ras_image_stop_load(gpointer data, GError **error);
static gboolean gdk_pixbuf__ras_image_load_increment(gpointer data,
                                                     const guchar * buf, guint size,
                                                     GError **error);
105

106 107


108
/* Shared library entry point */
109
static GdkPixbuf *gdk_pixbuf__ras_image_load(FILE * f, GError **error)
110
{
111 112 113
	guchar *membuf;
	size_t length;
	struct ras_progressive_state *State;
114
	
115 116
	GdkPixbuf *pb;
	
Havoc Pennington's avatar
Havoc Pennington committed
117
	State = gdk_pixbuf__ras_image_begin_load(NULL, NULL, NULL, error);
118 119 120 121 122 123 124
	
	membuf = g_malloc(4096);
	
	g_assert(membuf != NULL);
	
	while (feof(f) == 0) {
		length = fread(membuf, 1, 4096, f);
Havoc Pennington's avatar
Havoc Pennington committed
125 126
                if (!gdk_pixbuf__ras_image_load_increment(State, membuf, length,
                                                          error)) {
127
                        gdk_pixbuf__ras_image_stop_load (State, NULL);
Havoc Pennington's avatar
Havoc Pennington committed
128 129 130
                        return NULL;
                }
	}
131 132
	g_free(membuf);
	if (State->pixbuf != NULL)
133
		g_object_ref(State->pixbuf);
134 135 136

	pb = State->pixbuf;

137
	gdk_pixbuf__ras_image_stop_load(State, NULL);
138
	return pb;
139
}
140

141 142 143
static void RAS2State(struct rasterfile *RAS,
		      struct ras_progressive_state *State)
{
144 145 146 147 148 149
	State->Header.width = GUINT32_FROM_BE(RAS->width);
	State->Header.height = GUINT32_FROM_BE(RAS->height);
	State->Header.depth = GUINT32_FROM_BE(RAS->depth);
	State->Header.type = GUINT32_FROM_BE(RAS->type);
	State->Header.maptype = GUINT32_FROM_BE(RAS->maptype);
	State->Header.maplength = GUINT32_FROM_BE(RAS->maplength);
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165

	g_assert(State->Header.maplength <= 768);	/* Otherwise, we are in trouble */

	State->RasType = State->Header.depth;	/* This may be less trivial someday */
	State->HeaderSize = 32 + State->Header.maplength;

	if (State->RasType == 32)
		State->LineWidth = State->Header.width * 4;
	if (State->RasType == 24)
		State->LineWidth = State->Header.width * 3;
	if (State->RasType == 8)
		State->LineWidth = State->Header.width * 1;
	if (State->RasType == 1) {
		State->LineWidth = State->Header.width / 8;
		if ((State->Header.width & 7) != 0)
			State->LineWidth++;
166
	}
167

168 169 170
	/* Now padd the line to be a multiple of 16 bits */
	if ((State->LineWidth & 1) != 0)
		State->LineWidth++;
171

172 173
	if (State->LineBuf == NULL)
		State->LineBuf = g_malloc(State->LineWidth);
174

175
	g_assert(State->LineBuf != NULL);
176 177


178 179
	if (State->pixbuf == NULL) {
		if (State->RasType == 32)
180
			State->pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE,
181 182 183 184 185 186 187 188
						       8,
						       (gint)
						       State->Header.width,
						       (gint)
						       State->Header.
						       height);
		else
			State->pixbuf =
189
			    gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8,
190 191 192 193 194
					   (gint) State->Header.width,
					   (gint) State->Header.height);
		if (State->prepared_func != NULL)
			/* Notify the client that we are ready to go */
			(*State->prepared_func) (State->pixbuf,
Havoc Pennington's avatar
Havoc Pennington committed
195
                                                 NULL,
196
						 State->user_data);
197

198 199 200 201 202 203 204 205 206 207
	}
	
	if ((State->Header.maplength==0)&&(State->RasType==1)) {
		State->HeaderBuf[32] = 255;
		State->HeaderBuf[33] = 0;
		State->HeaderBuf[34] = 255;
		State->HeaderBuf[35] = 0;
		State->HeaderBuf[36] = 255;
		State->HeaderBuf[37] = 0;
	}
208

209
}
210 211 212 213 214 215 216

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

217
static gpointer
Federico Mena Quintero's avatar
Federico Mena Quintero committed
218 219
gdk_pixbuf__ras_image_begin_load(ModulePreparedNotifyFunc prepared_func,
				 ModuleUpdatedNotifyFunc updated_func,
Havoc Pennington's avatar
Havoc Pennington committed
220 221
				 gpointer user_data,
                                 GError **error)
222 223 224 225 226 227 228
{
	struct ras_progressive_state *context;

	context = g_new0(struct ras_progressive_state, 1);
	context->prepared_func = prepared_func;
	context->updated_func = updated_func;
	context->user_data = user_data;
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244

	context->HeaderSize = 32;
	context->HeaderBuf = g_malloc(32 + 768);	/* 32 for rasheader,
							   768 for the colormap */
	context->HeaderDone = 0;

	context->LineWidth = 0;
	context->LineBuf = NULL;
	context->LineDone = 0;
	context->Lines = 0;

	context->RasType = 0;

	memset(&context->Header, 0, sizeof(struct rasterfile));


245
	context->pixbuf = NULL;
246

247 248 249 250 251 252 253 254 255

	return (gpointer) context;
}

/*
 * context - returned from image_begin_load
 *
 * free context, unref gdk_pixbuf
 */
256 257
static gboolean
gdk_pixbuf__ras_image_stop_load(gpointer data, GError **error)
258 259 260 261
{
	struct ras_progressive_state *context =
	    (struct ras_progressive_state *) data;

262 263 264
        /* FIXME this thing needs to report errors if
         * we have unused image data
         */
265

266
	g_return_val_if_fail(context != NULL, TRUE);
267

268 269 270 271 272
	if (context->LineBuf != NULL)
		g_free(context->LineBuf);
	if (context->HeaderBuf != NULL)
		g_free(context->HeaderBuf);

273
	if (context->pixbuf)
274
		g_object_unref(context->pixbuf);
275 276

	g_free(context);
277 278

        return TRUE;
279 280
}

281 282 283 284 285 286 287 288 289 290 291
/* 
 OneLine is called when enough data is received to process 1 line 
 of pixels 
 */

static void OneLine32(struct ras_progressive_state *context)
{
	gint X;
	guchar *Pixels;

	X = 0;
292
	Pixels = context->pixbuf->pixels + context->pixbuf->rowstride * context->Lines;
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
	while (X < context->Header.width) {
		/* The joys of having a BGR byteorder */
		Pixels[X * 4 + 0] = context->LineBuf[X * 4 + 2];
		Pixels[X * 4 + 1] = context->LineBuf[X * 4 + 1];
		Pixels[X * 4 + 2] = context->LineBuf[X * 4 + 0];
		Pixels[X * 4 + 3] = context->LineBuf[X * 4 + 3];
		X++;
	}
}

static void OneLine24(struct ras_progressive_state *context)
{
	gint X;
	guchar *Pixels;

	X = 0;
309
	Pixels = context->pixbuf->pixels + context->pixbuf->rowstride * context->Lines;
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
	while (X < context->Header.width) {
		/* The joys of having a BGR byteorder */
		Pixels[X * 3 + 0] = context->LineBuf[X * 3 + 2];
		Pixels[X * 3 + 1] = context->LineBuf[X * 3 + 1];
		Pixels[X * 3 + 2] = context->LineBuf[X * 3 + 0];
		X++;
	}

}

static void OneLine8(struct ras_progressive_state *context)
{
	gint X;
	guchar *Pixels;

	X = 0;
326
	Pixels = context->pixbuf->pixels + context->pixbuf->rowstride * context->Lines;
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344
	while (X < context->Header.width) {
		/* The joys of having a BGR byteorder */
		Pixels[X * 3 + 0] =
		    context->HeaderBuf[context->LineBuf[X] + 32];
		Pixels[X * 3 + 1] =
		    context->HeaderBuf[context->LineBuf[X] + 256 + 32];
		Pixels[X * 3 + 2] =
		    context->HeaderBuf[context->LineBuf[X] + 512 + 32];
		X++;
	}
}

static void OneLine1(struct ras_progressive_state *context)
{
	gint X;
	guchar *Pixels;

	X = 0;
345
	Pixels = context->pixbuf->pixels + context->pixbuf->rowstride * context->Lines;
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
	while (X < context->Header.width) {
		int Bit;
		
		Bit = (context->LineBuf[X/8])>>(7-(X&7));
		Bit = Bit & 1;
		/* The joys of having a BGR byteorder */
		Pixels[X * 3 + 0] =
		    context->HeaderBuf[Bit + 32];
		Pixels[X * 3 + 1] =
		    context->HeaderBuf[Bit + 2 + 32];
		Pixels[X * 3 + 2] =
		    context->HeaderBuf[Bit + 4 + 32];
		X++;
	}
}


static void OneLine(struct ras_progressive_state *context)
{
	if (context->RasType == 32)
		OneLine32(context);
	if (context->RasType == 24)
		OneLine24(context);
	if (context->RasType == 8)
		OneLine8(context);
	if (context->RasType == 1)
		OneLine1(context);

	context->LineDone = 0;
	if (context->Lines > context->Header.height)
		return;
	context->Lines++;

	if (context->updated_func != NULL) {
		(*context->updated_func) (context->pixbuf,
					  0,
					  context->Lines,
					  context->Header.width,
384 385
					  context->Header.height,
					  context->user_data);
386 387 388

	}
}
389 390 391 392 393 394 395 396

/*
 * context - from image_begin_load
 * buf - new image data
 * size - length of new image data
 *
 * append image data onto inrecrementally built output image
 */
397 398 399
static gboolean
gdk_pixbuf__ras_image_load_increment(gpointer data,
                                     const guchar * buf, guint size,
Havoc Pennington's avatar
Havoc Pennington committed
400
                                     GError **error)
401 402 403 404
{
	struct ras_progressive_state *context =
	    (struct ras_progressive_state *) data;

405
	gint BytesToCopy;
406 407

	while (size > 0) {
408 409 410 411 412 413 414
		if (context->HeaderDone < context->HeaderSize) {	/* We still 
									   have headerbytes to do */
			BytesToCopy =
			    context->HeaderSize - context->HeaderDone;
			if (BytesToCopy > size)
				BytesToCopy = size;

415
			memmove(context->HeaderBuf + context->HeaderDone,
416 417 418 419 420 421 422 423 424 425 426 427 428 429
			       buf, BytesToCopy);

			size -= BytesToCopy;
			buf += BytesToCopy;
			context->HeaderDone += BytesToCopy;

		} else {
			/* Pixeldata only */
			BytesToCopy =
			    context->LineWidth - context->LineDone;
			if (BytesToCopy > size)
				BytesToCopy = size;

			if (BytesToCopy > 0) {
430
				memmove(context->LineBuf +
431 432 433 434 435 436 437 438 439 440
				       context->LineDone, buf,
				       BytesToCopy);

				size -= BytesToCopy;
				buf += BytesToCopy;
				context->LineDone += BytesToCopy;
			}
			if ((context->LineDone >= context->LineWidth) &&
			    (context->LineWidth > 0))
				OneLine(context);
441 442 443 444


		}

445 446 447 448 449
		if (context->HeaderDone >= 32)
			RAS2State((struct rasterfile *) context->HeaderBuf,
				  context);


450 451 452
	}

	return TRUE;
453
}
454 455 456 457 458 459 460 461 462

void
gdk_pixbuf__ras_fill_vtable (GdkPixbufModule *module)
{
  module->load = gdk_pixbuf__ras_image_load;
  module->begin_load = gdk_pixbuf__ras_image_begin_load;
  module->stop_load = gdk_pixbuf__ras_image_stop_load;
  module->load_increment = gdk_pixbuf__ras_image_load_increment;
}