io-wbmp.c 9.42 KB
Newer Older
1
/* -*- mode: C; c-file-style: "linux" -*- */
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
/* GdkPixbuf library - Wireless Bitmap image loader
 *
 * Copyright (C) 2000 Red Hat, Inc.
 *
 * Authors: Elliot Lee <sopwith@redhat.com
 *
 * Based on io-bmp.c
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

/*

Known bugs:
        * Since this is based off the libgd implementation, no extended headers implemented (not required for a WAP client)
*/

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



/* Progressive loading */

struct wbmp_progressive_state {
46 47
  GdkPixbufModulePreparedFunc prepared_func;
  GdkPixbufModuleUpdatedFunc updated_func;
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
  gpointer user_data;

  gboolean need_type : 1;
  gboolean need_header : 1;
  gboolean need_width : 1;
  gboolean need_height : 1;
  gboolean needmore : 1;
  gboolean call_progressive_updates : 1;

  guchar last_buf[16]; /* Just needs to be big enough to hold the largest datum requestable via 'getin' */
  guint last_len;

  int type;
  int width, height, curx, cury;

  GdkPixbuf *pixbuf;	/* Our "target" */
};

66
static gpointer
67 68 69
gdk_pixbuf__wbmp_image_begin_load(GdkPixbufModuleSizeFunc size_func, 
                                  GdkPixbufModulePreparedFunc prepared_func,
				  GdkPixbufModuleUpdatedFunc updated_func,
Havoc Pennington's avatar
Havoc Pennington committed
70
                                  gpointer user_data,
Havoc Pennington's avatar
Havoc Pennington committed
71
                                  GError **error);
72

73 74 75 76 77
static gboolean gdk_pixbuf__wbmp_image_stop_load(gpointer data, GError **error);
static gboolean gdk_pixbuf__wbmp_image_load_increment(gpointer data,
                                                      const guchar * buf,
                                                      guint size,
                                                      GError **error);
78 79 80 81 82 83 84

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

85
static gpointer
86 87 88
gdk_pixbuf__wbmp_image_begin_load(GdkPixbufModuleSizeFunc size_func, 
                                  GdkPixbufModulePreparedFunc prepared_func,
                                  GdkPixbufModuleUpdatedFunc updated_func,
Havoc Pennington's avatar
Havoc Pennington committed
89
                                  gpointer user_data,
Havoc Pennington's avatar
Havoc Pennington committed
90
                                  GError **error)
91
{
92
	struct wbmp_progressive_state *context;
93

94
	context = g_new0(struct wbmp_progressive_state, 1);
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
	context->prepared_func = prepared_func;
	context->updated_func = updated_func;
	context->user_data = user_data;

	context->needmore = context->need_type = context->need_header = context->need_width = context->need_height = TRUE;
	context->call_progressive_updates = TRUE;
	context->pixbuf = NULL;

	return (gpointer) context;
}

/*
 * context - returned from image_begin_load
 *
 * free context, unref gdk_pixbuf
 */
111 112
static gboolean gdk_pixbuf__wbmp_image_stop_load(gpointer data,
                                                 GError **error)
113
{
114 115
	struct wbmp_progressive_state *context =
	    (struct wbmp_progressive_state *) data;
116

117 118 119 120 121
        /* FIXME this thing needs to report errors if
         * we have unused image data
         */
        
	g_return_val_if_fail(context != NULL, TRUE);
122
	if (context->pixbuf)
123
	  g_object_unref(context->pixbuf);
124 125

	g_free(context);
126 127

        return TRUE;
128 129 130
}

static gboolean
131
getin(struct wbmp_progressive_state *context, const guchar **buf, guint *buf_size, guchar *ptr, int datum_size)
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
{
  int last_num, buf_num;

  if((context->last_len + *buf_size) < datum_size)
    return FALSE;

  /* We know we can pull it out of there */
  last_num = MIN(datum_size, context->last_len);
  buf_num = MIN(datum_size-last_num, *buf_size);
  memcpy(ptr, context->last_buf, last_num);
  memcpy(ptr+last_num, *buf, buf_num);
	 
  context->last_len -= last_num;
  if(context->last_len)
    memmove(context->last_buf, context->last_buf+last_num, context->last_len);
  *buf_size -= buf_num;
  *buf += buf_num;
149 150

  return TRUE;
151 152 153
}

static gboolean
154
save_rest(struct wbmp_progressive_state *context, const guchar *buf, guint buf_size)
155 156 157 158 159 160 161 162 163 164 165
{
  if(buf_size > (sizeof(context->last_buf) - context->last_len))
    return FALSE;

  memcpy(context->last_buf+context->last_len, buf, buf_size);
  context->last_len += buf_size;

  return TRUE;
}

static gboolean
166
get_mbi(struct wbmp_progressive_state *context, const guchar **buf, guint *buf_size, int *val)
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
{
  guchar intbuf[16];
  int i, n;
  gboolean rv;

  *val = 0;
  n = i = 0;
  do {
    rv = getin(context, buf, buf_size, intbuf+n, 1);
    if(!rv)
      goto out;
    *val <<= 7;
    *val |= intbuf[n] & 0x7F;
    n++;
  } while(n < sizeof(intbuf) && (intbuf[n-1] & 0x80));

 out:
184
  if(!rv || (intbuf[n-1] & 0x80))
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
    {
      rv = save_rest(context, intbuf, n);

      if(!rv)
	g_error("Couldn't save_rest of intbuf");
      return FALSE;
    }

  return TRUE;
}

/*
 * context - from image_begin_load
 * buf - new image data
 * size - length of new image data
 *
 * append image data onto inrecrementally built output image
 */
203 204 205
static gboolean gdk_pixbuf__wbmp_image_load_increment(gpointer data,
                                                      const guchar * buf,
                                                      guint size, GError **error)
206
{
207 208
	struct wbmp_progressive_state *context =
	    (struct wbmp_progressive_state *) data;
Elliot Lee's avatar
Elliot Lee committed
209
	gboolean bv = FALSE;
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

	do
	  {
	    if(context->need_type)
	      {
		guchar val;

		bv = getin(context, &buf, &size, &val, 1);
		if(bv)
		  {
		    context->type = val;
		    context->need_type = FALSE;
		  }
	      }
	    else if(context->need_header)
	      {
		guchar val;

		bv = getin(context, &buf, &size, &val, 1);
		if(bv)
		  {
		    /* We skip over the extended header - val is unused */
		    if(!(val & 0x80))
		      context->need_header = FALSE;
		  }
	      }
	    else if(context->need_width)
	      {
		bv = get_mbi(context, &buf, &size, &context->width);
239
		if(bv) {
240
		  context->need_width = FALSE;
241 242 243 244 245 246 247 248 249 250 251

                  if (context->width <= 0) {
		    g_set_error (error,
				 GDK_PIXBUF_ERROR,
				 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
				 _("Image has zero width"));

		    return FALSE;
		  }
                }
		
252 253 254 255 256 257 258
	      }
	    else if(context->need_height)
	      {
		bv = get_mbi(context, &buf, &size, &context->height);
		if(bv)
		  {
		    context->need_height = FALSE;
259 260 261 262 263 264 265 266 267 268

		    if (context->height <= 0) {
		      g_set_error (error,
				   GDK_PIXBUF_ERROR,
				   GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
				   _("Image has zero height"));
		      
		      return FALSE;
		    }

269
		    context->pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, context->width, context->height);
270 271 272 273 274 275 276 277 278
		    
		    if (!context->pixbuf) {
		      g_set_error (error,
				   GDK_PIXBUF_ERROR,
				   GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
				   _("Not enough memory to load image"));
		      return FALSE;
		    }

279

280
		    if(context->prepared_func)
Havoc Pennington's avatar
Havoc Pennington committed
281
		      context->prepared_func(context->pixbuf, NULL, context->user_data);
282 283 284 285 286 287 288 289
		  }
	      }
	    else if(context->needmore)
	      {
		int first_row;
		first_row = context->cury;
		for( ; context->cury < context->height; context->cury++, context->curx = 0)
		  {
290
		    for( ; context->curx < context->width; context->curx += 8)
291 292 293 294 295 296 297 298 299
		      {
			guchar byte;
			guchar *ptr;
			int xoff;
			bv = getin(context, &buf, &size, &byte, 1);
			if(!bv)
			  goto out;

			ptr = context->pixbuf->pixels + context->pixbuf->rowstride * context->cury + context->curx * 3;
300
			for(xoff = 7; xoff >= 0; xoff--, ptr += 3)
301 302 303
			  {
			    guchar pixval;

304 305 306
			    if (context->curx + (7 - xoff) == context->width)
			      break;

307 308 309 310 311 312 313 314 315 316 317 318
			    if(byte & (1<<xoff))
			      pixval = 0xFF;
			    else
			      pixval = 0x0;

			    ptr[0] = ptr[1] = ptr[2] = pixval;
			  }
		      }
		  }
		context->needmore = FALSE;

	      out:
319 320 321
		if(context->updated_func)
		  context->updated_func(context->pixbuf, 0, first_row, context->width, context->cury - first_row + 1,
					context->user_data);
322 323 324 325 326 327
	      }
	    else
	      bv = FALSE; /* Nothing left to do, stop feeding me data! */

	  } while(bv);

328 329 330 331 332 333 334 335 336 337 338 339
	if(size) {
	  bv = save_rest(context, buf, size);
	  if (!bv) {
	      g_set_error (error,
			   GDK_PIXBUF_ERROR,
			   GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
			   _("Couldn't save the rest"));

	      return FALSE;
	  }
	}
	return TRUE;
340
}
341 342

void
343
MODULE_ENTRY (wbmp, fill_vtable) (GdkPixbufModule *module)
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
	module->begin_load = gdk_pixbuf__wbmp_image_begin_load;
	module->stop_load = gdk_pixbuf__wbmp_image_stop_load;
	module->load_increment = gdk_pixbuf__wbmp_image_load_increment;
}

void
MODULE_ENTRY (wbmp, fill_info) (GdkPixbufFormat *info)
{
	static GdkPixbufModulePattern signature[] = {
		{ " ", "z", 1 }, 
		{ NULL, NULL, 0 }
	};
	static gchar * mime_types[] = {
		"image/vnd.wap.wbmp",
		NULL
	};
	static gchar * extensions[] = {
		"wbmp",
		NULL
	};

	info->name = "wbmp";
	info->signature = signature;
	info->description = N_("The WBMP image format");
	info->mime_types = mime_types;
	info->extensions = extensions;
	info->flags = 0;
372
}