]> Pileus Git - ~andy/gtk/blob - gdk-pixbuf/io-wbmp.c
Add a 'w' in the right spot.
[~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 #include <unistd.h>
34 #include <string.h>
35 #include "gdk-pixbuf-private.h"
36 #include "gdk-pixbuf-io.h"
37
38 \f
39
40 /* Progressive loading */
41
42 struct wbmp_progressive_state {
43   ModulePreparedNotifyFunc prepared_func;
44   ModuleUpdatedNotifyFunc updated_func;
45   gpointer user_data;
46
47   gboolean need_type : 1;
48   gboolean need_header : 1;
49   gboolean need_width : 1;
50   gboolean need_height : 1;
51   gboolean needmore : 1;
52   gboolean call_progressive_updates : 1;
53
54   guchar last_buf[16]; /* Just needs to be big enough to hold the largest datum requestable via 'getin' */
55   guint last_len;
56
57   int type;
58   int width, height, curx, cury;
59
60   GdkPixbuf *pixbuf;    /* Our "target" */
61 };
62
63 gpointer
64 gdk_pixbuf__wbmp_image_begin_load(ModulePreparedNotifyFunc prepared_func,
65                                   ModuleUpdatedNotifyFunc updated_func,
66                                   ModuleFrameDoneNotifyFunc frame_done_func,
67                                   ModuleAnimationDoneNotifyFunc
68                                   anim_done_func, gpointer user_data);
69
70 void gdk_pixbuf__wbmp_image_stop_load(gpointer data);
71 gboolean gdk_pixbuf__wbmp_image_load_increment(gpointer data, guchar * buf,
72                                                guint size);
73
74
75 /* Shared library entry point --> This should be removed when
76    generic_image_load enters gdk-pixbuf-io. */
77 GdkPixbuf *gdk_pixbuf__wbmp_image_load(FILE * f)
78 {
79         size_t length;
80         char membuf[4096];
81         struct wbmp_progressive_state *State;
82
83         GdkPixbuf *pb;
84
85         State = gdk_pixbuf__wbmp_image_begin_load(NULL, NULL, NULL, NULL, NULL);
86
87         while (feof(f) == 0) {
88                 length = fread(membuf, 1, 4096, f);
89                 if (length > 0)
90                   gdk_pixbuf__wbmp_image_load_increment(State,
91                                                         membuf,
92                                                         length);
93
94         }
95         if (State->pixbuf != NULL)
96                 gdk_pixbuf_ref(State->pixbuf);
97
98         pb = State->pixbuf;
99
100         gdk_pixbuf__wbmp_image_stop_load(State);
101         return pb;
102 }
103
104 /* 
105  * func - called when we have pixmap created (but no image data)
106  * user_data - passed as arg 1 to func
107  * return context (opaque to user)
108  */
109
110 gpointer
111 gdk_pixbuf__wbmp_image_begin_load(ModulePreparedNotifyFunc prepared_func,
112                                  ModuleUpdatedNotifyFunc updated_func,
113                                  ModuleFrameDoneNotifyFunc frame_done_func,
114                                  ModuleAnimationDoneNotifyFunc
115                                  anim_done_func, gpointer user_data)
116 {
117         struct wbmp_progressive_state *context;
118
119         context = g_new0(struct wbmp_progressive_state, 1);
120         context->prepared_func = prepared_func;
121         context->updated_func = updated_func;
122         context->user_data = user_data;
123
124         context->needmore = context->need_type = context->need_header = context->need_width = context->need_height = TRUE;
125         context->call_progressive_updates = TRUE;
126         context->pixbuf = NULL;
127
128         return (gpointer) context;
129 }
130
131 /*
132  * context - returned from image_begin_load
133  *
134  * free context, unref gdk_pixbuf
135  */
136 void gdk_pixbuf__wbmp_image_stop_load(gpointer data)
137 {
138         struct wbmp_progressive_state *context =
139             (struct wbmp_progressive_state *) data;
140
141         g_return_if_fail(context != NULL);
142         if (context->pixbuf)
143           gdk_pixbuf_unref(context->pixbuf);
144
145         g_free(context);
146 }
147
148 static gboolean
149 getin(struct wbmp_progressive_state *context, guchar **buf, guint *buf_size, guchar *ptr, int datum_size)
150 {
151   int last_num, buf_num;
152
153   if((context->last_len + *buf_size) < datum_size)
154     return FALSE;
155
156   /* We know we can pull it out of there */
157   last_num = MIN(datum_size, context->last_len);
158   buf_num = MIN(datum_size-last_num, *buf_size);
159   memcpy(ptr, context->last_buf, last_num);
160   memcpy(ptr+last_num, *buf, buf_num);
161          
162   context->last_len -= last_num;
163   if(context->last_len)
164     memmove(context->last_buf, context->last_buf+last_num, context->last_len);
165   *buf_size -= buf_num;
166   *buf += buf_num;
167
168   return TRUE;
169 }
170
171 static gboolean
172 save_rest(struct wbmp_progressive_state *context, const guchar *buf, guint buf_size)
173 {
174   if(buf_size > (sizeof(context->last_buf) - context->last_len))
175     return FALSE;
176
177   memcpy(context->last_buf+context->last_len, buf, buf_size);
178   context->last_len += buf_size;
179
180   return TRUE;
181 }
182
183 static gboolean
184 get_mbi(struct wbmp_progressive_state *context, guchar **buf, guint *buf_size, int *val)
185 {
186   guchar intbuf[16];
187   int i, n;
188   gboolean rv;
189
190   *val = 0;
191   n = i = 0;
192   do {
193     rv = getin(context, buf, buf_size, intbuf+n, 1);
194     if(!rv)
195       goto out;
196     *val <<= 7;
197     *val |= intbuf[n] & 0x7F;
198     n++;
199   } while(n < sizeof(intbuf) && (intbuf[n-1] & 0x80));
200
201  out:
202   if(!rv || !(intbuf[n-1] & 0x80))
203     {
204       rv = save_rest(context, intbuf, n);
205
206       if(!rv)
207         g_error("Couldn't save_rest of intbuf");
208       return FALSE;
209     }
210
211   return TRUE;
212 }
213
214 /*
215  * context - from image_begin_load
216  * buf - new image data
217  * size - length of new image data
218  *
219  * append image data onto inrecrementally built output image
220  */
221 gboolean gdk_pixbuf__wbmp_image_load_increment(gpointer data, guchar * buf,
222                                               guint size)
223 {
224         struct wbmp_progressive_state *context =
225             (struct wbmp_progressive_state *) data;
226         gboolean bv;
227
228         do
229           {
230             if(context->need_type)
231               {
232                 guchar val;
233
234                 bv = getin(context, &buf, &size, &val, 1);
235                 if(bv)
236                   {
237                     context->type = val;
238                     context->need_type = FALSE;
239                   }
240               }
241             else if(context->need_header)
242               {
243                 guchar val;
244
245                 bv = getin(context, &buf, &size, &val, 1);
246                 if(bv)
247                   {
248                     /* We skip over the extended header - val is unused */
249                     if(!(val & 0x80))
250                       context->need_header = FALSE;
251                   }
252               }
253             else if(context->need_width)
254               {
255                 bv = get_mbi(context, &buf, &size, &context->width);
256                 if(bv)
257                   context->need_width = FALSE;
258               }
259             else if(context->need_height)
260               {
261                 bv = get_mbi(context, &buf, &size, &context->height);
262                 if(bv)
263                   {
264                     context->need_height = FALSE;
265                     context->pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, context->width, context->height);
266                     if(context->prepared_func)
267                       context->prepared_func(context->pixbuf, context->user_data);
268                   }
269               }
270             else if(context->needmore)
271               {
272                 int first_row;
273                 first_row = context->cury;
274                 for( ; context->cury < context->height; context->cury++, context->curx = 0)
275                   {
276                     for( ; context->curx < context->width; context->curx += 8)
277                       {
278                         guchar byte;
279                         guchar *ptr;
280                         int xoff;
281                         bv = getin(context, &buf, &size, &byte, 1);
282                         if(!bv)
283                           goto out;
284
285                         ptr = context->pixbuf->pixels + context->pixbuf->rowstride * context->cury + context->curx * 3;
286                         for(xoff = 0; xoff < 8; xoff++, ptr += 3)
287                           {
288                             guchar pixval;
289
290                             if(byte & (1<<xoff))
291                               pixval = 0xFF;
292                             else
293                               pixval = 0x0;
294
295                             ptr[0] = ptr[1] = ptr[2] = pixval;
296                           }
297                       }
298                   }
299                 context->needmore = FALSE;
300
301               out:
302                 context->updated_func(context->pixbuf, 0, first_row, context->width, context->cury - first_row + 1,
303                                       context->user_data);
304               }
305             else
306               bv = FALSE; /* Nothing left to do, stop feeding me data! */
307
308           } while(bv);
309
310         if(size)
311           return save_rest(context, buf, size);
312         else
313           return context->needmore;
314 }