]> Pileus Git - ~andy/gtk/blob - gdk-pixbuf/io-wbmp.c
New function for loading an image from a file either via module->load or
[~andy/gtk] / gdk-pixbuf / io-wbmp.c
1 /* GdkPixbuf library - Wireless Bitmap image loader
2  *
3  * Copyright (C) 2000 Red Hat, Inc.
4  *
5  * Authors: Elliot Lee <sopwith@redhat.com
6  *
7  * Based on io-bmp.c
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the
21  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22  * Boston, MA 02111-1307, USA.
23  */
24
25 /*
26
27 Known bugs:
28         * Since this is based off the libgd implementation, no extended headers implemented (not required for a WAP client)
29 */
30
31 #include <config.h>
32 #include <stdio.h>
33 #ifdef HAVE_UNISTD_H
34 #include <unistd.h>
35 #endif
36 #include <string.h>
37 #include "gdk-pixbuf-private.h"
38 #include "gdk-pixbuf-io.h"
39
40 \f
41
42 /* Progressive loading */
43
44 struct wbmp_progressive_state {
45   ModulePreparedNotifyFunc prepared_func;
46   ModuleUpdatedNotifyFunc updated_func;
47   gpointer user_data;
48
49   gboolean need_type : 1;
50   gboolean need_header : 1;
51   gboolean need_width : 1;
52   gboolean need_height : 1;
53   gboolean needmore : 1;
54   gboolean call_progressive_updates : 1;
55
56   guchar last_buf[16]; /* Just needs to be big enough to hold the largest datum requestable via 'getin' */
57   guint last_len;
58
59   int type;
60   int width, height, curx, cury;
61
62   GdkPixbuf *pixbuf;    /* Our "target" */
63 };
64
65 static gpointer
66 gdk_pixbuf__wbmp_image_begin_load(ModuleSizeFunc size_func, 
67                                   ModulePreparedNotifyFunc prepared_func,
68                                   ModuleUpdatedNotifyFunc updated_func,
69                                   gpointer user_data,
70                                   GError **error);
71
72 static gboolean gdk_pixbuf__wbmp_image_stop_load(gpointer data, GError **error);
73 static gboolean gdk_pixbuf__wbmp_image_load_increment(gpointer data,
74                                                       const guchar * buf,
75                                                       guint size,
76                                                       GError **error);
77
78 /* 
79  * func - called when we have pixmap created (but no image data)
80  * user_data - passed as arg 1 to func
81  * return context (opaque to user)
82  */
83
84 static gpointer
85 gdk_pixbuf__wbmp_image_begin_load(ModuleSizeFunc size_func, 
86                                   ModulePreparedNotifyFunc prepared_func,
87                                   ModuleUpdatedNotifyFunc updated_func,
88                                   gpointer user_data,
89                                   GError **error)
90 {
91         struct wbmp_progressive_state *context;
92
93         context = g_new0(struct wbmp_progressive_state, 1);
94         context->prepared_func = prepared_func;
95         context->updated_func = updated_func;
96         context->user_data = user_data;
97
98         context->needmore = context->need_type = context->need_header = context->need_width = context->need_height = TRUE;
99         context->call_progressive_updates = TRUE;
100         context->pixbuf = NULL;
101
102         return (gpointer) context;
103 }
104
105 /*
106  * context - returned from image_begin_load
107  *
108  * free context, unref gdk_pixbuf
109  */
110 static gboolean gdk_pixbuf__wbmp_image_stop_load(gpointer data,
111                                                  GError **error)
112 {
113         struct wbmp_progressive_state *context =
114             (struct wbmp_progressive_state *) data;
115
116         /* FIXME this thing needs to report errors if
117          * we have unused image data
118          */
119         
120         g_return_val_if_fail(context != NULL, TRUE);
121         if (context->pixbuf)
122           g_object_unref(context->pixbuf);
123
124         g_free(context);
125
126         return TRUE;
127 }
128
129 static gboolean
130 getin(struct wbmp_progressive_state *context, const guchar **buf, guint *buf_size, guchar *ptr, int datum_size)
131 {
132   int last_num, buf_num;
133
134   if((context->last_len + *buf_size) < datum_size)
135     return FALSE;
136
137   /* We know we can pull it out of there */
138   last_num = MIN(datum_size, context->last_len);
139   buf_num = MIN(datum_size-last_num, *buf_size);
140   memcpy(ptr, context->last_buf, last_num);
141   memcpy(ptr+last_num, *buf, buf_num);
142          
143   context->last_len -= last_num;
144   if(context->last_len)
145     memmove(context->last_buf, context->last_buf+last_num, context->last_len);
146   *buf_size -= buf_num;
147   *buf += buf_num;
148
149   return TRUE;
150 }
151
152 static gboolean
153 save_rest(struct wbmp_progressive_state *context, const guchar *buf, guint buf_size)
154 {
155   if(buf_size > (sizeof(context->last_buf) - context->last_len))
156     return FALSE;
157
158   memcpy(context->last_buf+context->last_len, buf, buf_size);
159   context->last_len += buf_size;
160
161   return TRUE;
162 }
163
164 static gboolean
165 get_mbi(struct wbmp_progressive_state *context, const guchar **buf, guint *buf_size, int *val)
166 {
167   guchar intbuf[16];
168   int i, n;
169   gboolean rv;
170
171   *val = 0;
172   n = i = 0;
173   do {
174     rv = getin(context, buf, buf_size, intbuf+n, 1);
175     if(!rv)
176       goto out;
177     *val <<= 7;
178     *val |= intbuf[n] & 0x7F;
179     n++;
180   } while(n < sizeof(intbuf) && (intbuf[n-1] & 0x80));
181
182  out:
183   if(!rv || (intbuf[n-1] & 0x80))
184     {
185       rv = save_rest(context, intbuf, n);
186
187       if(!rv)
188         g_error("Couldn't save_rest of intbuf");
189       return FALSE;
190     }
191
192   return TRUE;
193 }
194
195 /*
196  * context - from image_begin_load
197  * buf - new image data
198  * size - length of new image data
199  *
200  * append image data onto inrecrementally built output image
201  */
202 static gboolean gdk_pixbuf__wbmp_image_load_increment(gpointer data,
203                                                       const guchar * buf,
204                                                       guint size, GError **error)
205 {
206         struct wbmp_progressive_state *context =
207             (struct wbmp_progressive_state *) data;
208         gboolean bv = FALSE;
209
210         do
211           {
212             if(context->need_type)
213               {
214                 guchar val;
215
216                 bv = getin(context, &buf, &size, &val, 1);
217                 if(bv)
218                   {
219                     context->type = val;
220                     context->need_type = FALSE;
221                   }
222               }
223             else if(context->need_header)
224               {
225                 guchar val;
226
227                 bv = getin(context, &buf, &size, &val, 1);
228                 if(bv)
229                   {
230                     /* We skip over the extended header - val is unused */
231                     if(!(val & 0x80))
232                       context->need_header = FALSE;
233                   }
234               }
235             else if(context->need_width)
236               {
237                 bv = get_mbi(context, &buf, &size, &context->width);
238                 if(bv) {
239                   context->need_width = FALSE;
240
241                   if (context->width <= 0) {
242                     g_set_error (error,
243                                  GDK_PIXBUF_ERROR,
244                                  GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
245                                  _("Image has zero width"));
246
247                     return FALSE;
248                   }
249                 }
250                 
251               }
252             else if(context->need_height)
253               {
254                 bv = get_mbi(context, &buf, &size, &context->height);
255                 if(bv)
256                   {
257                     context->need_height = FALSE;
258
259                     if (context->height <= 0) {
260                       g_set_error (error,
261                                    GDK_PIXBUF_ERROR,
262                                    GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
263                                    _("Image has zero height"));
264                       
265                       return FALSE;
266                     }
267
268                     context->pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, context->width, context->height);
269                     
270                     if (!context->pixbuf) {
271                       g_set_error (error,
272                                    GDK_PIXBUF_ERROR,
273                                    GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
274                                    _("Not enough memory to load image"));
275                       return FALSE;
276                     }
277
278
279                     if(context->prepared_func)
280                       context->prepared_func(context->pixbuf, NULL, context->user_data);
281                   }
282               }
283             else if(context->needmore)
284               {
285                 int first_row;
286                 first_row = context->cury;
287                 for( ; context->cury < context->height; context->cury++, context->curx = 0)
288                   {
289                     for( ; context->curx < context->width; context->curx += 8)
290                       {
291                         guchar byte;
292                         guchar *ptr;
293                         int xoff;
294                         bv = getin(context, &buf, &size, &byte, 1);
295                         if(!bv)
296                           goto out;
297
298                         ptr = context->pixbuf->pixels + context->pixbuf->rowstride * context->cury + context->curx * 3;
299                         for(xoff = 7; xoff >= 0; xoff--, ptr += 3)
300                           {
301                             guchar pixval;
302
303                             if (context->curx + (7 - xoff) == context->width)
304                               break;
305
306                             if(byte & (1<<xoff))
307                               pixval = 0xFF;
308                             else
309                               pixval = 0x0;
310
311                             ptr[0] = ptr[1] = ptr[2] = pixval;
312                           }
313                       }
314                   }
315                 context->needmore = FALSE;
316
317               out:
318                 if(context->updated_func)
319                   context->updated_func(context->pixbuf, 0, first_row, context->width, context->cury - first_row + 1,
320                                         context->user_data);
321               }
322             else
323               bv = FALSE; /* Nothing left to do, stop feeding me data! */
324
325           } while(bv);
326
327         if(size) {
328           bv = save_rest(context, buf, size);
329           if (!bv) {
330               g_set_error (error,
331                            GDK_PIXBUF_ERROR,
332                            GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
333                            _("Couldn't save the rest"));
334
335               return FALSE;
336           }
337         }
338         return TRUE;
339 }
340
341 void
342 gdk_pixbuf__wbmp_fill_vtable (GdkPixbufModule *module)
343 {
344   module->begin_load = gdk_pixbuf__wbmp_image_begin_load;
345   module->stop_load = gdk_pixbuf__wbmp_image_stop_load;
346   module->load_increment = gdk_pixbuf__wbmp_image_load_increment;
347 }