3d941457731723cb16e1ff2fa168027f17e282ff
[rodin/chimara.git] / libchimara / graphics.c
1 #include "graphics.h"
2 #include "chimara-glk-private.h"
3 #include "magic.h"
4
5 #define BUFFER_SIZE (1024)
6
7 extern GPrivate *glk_data_key;
8 void on_size_prepared(GdkPixbufLoader *loader, gint width, gint height, struct image_info *info);
9 void on_pixbuf_closed(GdkPixbufLoader *loader, gpointer data);
10
11 static gboolean size_determined;
12 static gboolean image_loaded;
13
14 glui32
15 glk_image_get_info(glui32 image, glui32 *width, glui32 *height)
16 {
17         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
18         giblorb_result_t res;
19         giblorb_err_t blorb_error = 0;
20         GError *pixbuf_error = NULL;
21         struct image_info *info = g_new0(struct image_info, 1);
22         info->resource_number = image;
23         guchar *buffer;
24
25         /* Lookup the proper resource */
26         blorb_error = giblorb_load_resource(glk_data->resource_map, giblorb_method_FilePos, &res, giblorb_ID_Pict, image);
27         if(blorb_error != giblorb_err_None) {
28                 WARNING_S( "Error loading resource", giblorb_get_error_message(blorb_error) );
29                 return FALSE;
30         }
31
32         if(width == NULL && height == NULL) {
33                 /* No size requested, don't bother loading the image */
34                 giblorb_unload_chunk(glk_data->resource_map, image);
35                 return TRUE;
36         }
37
38         /* Load the resource */
39         GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
40         g_signal_connect( loader, "size-prepared", G_CALLBACK(on_size_prepared), info ); 
41         glk_stream_set_position(glk_data->resource_file, res.data.startpos, seekmode_Start);
42         buffer = g_malloc( BUFFER_SIZE * sizeof(guchar) );
43
44         guint32 total_read = 0;
45         size_determined = FALSE;
46         while(total_read < res.length && !size_determined) {
47                 guint32 num_read = glk_get_buffer_stream(glk_data->resource_file, (char *) buffer, BUFFER_SIZE);
48
49                 if( !gdk_pixbuf_loader_write(loader, buffer, MIN(BUFFER_SIZE, num_read), &pixbuf_error) ) {
50                         WARNING_S("Cannot read image", pixbuf_error->message);
51                         giblorb_unload_chunk(glk_data->resource_map, image);
52                         gdk_pixbuf_loader_close(loader, &pixbuf_error);
53                         g_free(buffer);
54                         return FALSE;
55                 }
56
57                 total_read += num_read;
58         }
59         giblorb_unload_chunk(glk_data->resource_map, image);
60         gdk_pixbuf_loader_close(loader, &pixbuf_error);
61         g_free(buffer);
62
63         /* Determine the image dimensions */
64         g_mutex_lock(glk_data->resource_lock);
65         while(!size_determined) {
66                 /* Wait for the PixbufLoader to finish reading the image size */
67                 g_cond_wait(glk_data->resource_info_available, glk_data->resource_lock);
68         }
69         g_mutex_unlock(glk_data->resource_lock);
70
71         if(width != NULL)
72                 *width = info->width;
73         if(height != NULL)
74                 *height =info->height;
75         g_free(info);
76
77         return TRUE;
78 }
79
80 void
81 on_size_prepared(GdkPixbufLoader *loader, gint width, gint height, struct image_info *info)
82 {
83         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
84
85         g_mutex_lock(glk_data->resource_lock);
86         info->width = width;
87         info->height = height;
88         size_determined = TRUE;
89         g_cond_broadcast(glk_data->resource_info_available);
90         g_mutex_unlock(glk_data->resource_lock);
91 }
92
93 glui32
94 glk_image_draw(winid_t win, glui32 image, glsi32 val1, glsi32 val2)
95 {
96         VALID_WINDOW(win, return FALSE);
97         g_return_val_if_fail(win->type == wintype_Graphics, FALSE);
98
99         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
100         giblorb_result_t res;
101         giblorb_err_t blorb_error = 0;
102         GError *pixbuf_error = NULL;
103         struct image_info *info = g_new0(struct image_info, 1);
104         info->resource_number = image;
105         guchar *buffer;
106         GdkPixmap *canvas;
107
108         /* Lookup the proper resource */
109         blorb_error = giblorb_load_resource(glk_data->resource_map, giblorb_method_FilePos, &res, giblorb_ID_Pict, image);
110         if(blorb_error != giblorb_err_None) {
111                 WARNING_S( "Error loading resource", giblorb_get_error_message(blorb_error) );
112                 return FALSE;
113         }
114
115         /* Load the resource */
116         GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
117         g_signal_connect( loader, "closed", G_CALLBACK(on_pixbuf_closed), NULL ); 
118         glk_stream_set_position(glk_data->resource_file, res.data.startpos, seekmode_Start);
119         buffer = g_malloc( BUFFER_SIZE * sizeof(guchar) );
120
121         guint32 total_read = 0;
122         image_loaded = FALSE;
123         while(total_read < res.length) {
124                 guint32 num_read = glk_get_buffer_stream(glk_data->resource_file, (char *) buffer, BUFFER_SIZE);
125
126                 if( !gdk_pixbuf_loader_write(loader, buffer, MIN(BUFFER_SIZE, num_read), &pixbuf_error) ) {
127                         WARNING_S("Cannot read image", pixbuf_error->message);
128                         giblorb_unload_chunk(glk_data->resource_map, image);
129                         gdk_pixbuf_loader_close(loader, &pixbuf_error);
130                         g_free(buffer);
131                         return FALSE;
132                 }
133
134                 total_read += num_read;
135         }
136
137         if( !gdk_pixbuf_loader_close(loader, &pixbuf_error) ) {
138                 WARNING_S("Cannot read image", pixbuf_error->message);
139                 giblorb_unload_chunk(glk_data->resource_map, image);
140                 g_free(buffer);
141                 return FALSE;
142         }
143
144         if(!image_loaded) {
145                 /* Wait for the PixbufLoader to finish loading the image */
146                 g_mutex_lock(glk_data->resource_lock);
147                 while(!image_loaded) {
148                         g_cond_wait(glk_data->resource_loaded, glk_data->resource_lock);
149                 }
150                 g_mutex_unlock(glk_data->resource_lock);
151         }
152
153         giblorb_unload_chunk(glk_data->resource_map, image);
154         g_free(buffer);
155         
156         gdk_threads_enter();
157
158         gtk_image_get_pixmap( GTK_IMAGE(win->widget), &canvas, NULL );
159         if(canvas == NULL) {
160                 WARNING("Could not get pixmap");
161                 return FALSE;
162         }
163
164         GdkPixbuf *pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
165         if(pixbuf == NULL) {
166                 WARNING("Could not read image");
167                 return FALSE;
168         }
169
170         gdk_draw_pixbuf( GDK_DRAWABLE(canvas), NULL, pixbuf, 0, 0, val1, val2, -1, -1, GDK_RGB_DITHER_NONE, 0, 0 );
171
172         /* Update the screen */
173         gtk_widget_queue_draw(win->widget);
174
175         gdk_threads_leave();
176
177         return TRUE;
178 }
179
180 void
181 on_pixbuf_closed(GdkPixbufLoader *loader, gpointer data)
182 {
183         gdk_threads_enter();
184
185         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
186
187         g_mutex_lock(glk_data->resource_lock);
188         image_loaded = TRUE;
189         g_cond_broadcast(glk_data->resource_loaded);
190         g_mutex_unlock(glk_data->resource_lock);
191
192         gdk_threads_leave();
193 }
194
195
196 glui32
197 glk_image_draw_scaled(winid_t win, glui32 image, glsi32 val1, glsi32 val2, glui32 width, glui32 height)
198 {
199         VALID_WINDOW(win, return FALSE);
200         g_return_val_if_fail(win->type == wintype_Graphics, FALSE);
201
202         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
203         giblorb_result_t res;
204         giblorb_err_t blorb_error = 0;
205         GError *pixbuf_error = NULL;
206         struct image_info *info = g_new0(struct image_info, 1);
207         info->resource_number = image;
208         guchar *buffer;
209         GdkPixmap *canvas;
210
211         /* Lookup the proper resource */
212         blorb_error = giblorb_load_resource(glk_data->resource_map, giblorb_method_FilePos, &res, giblorb_ID_Pict, image);
213         if(blorb_error != giblorb_err_None) {
214                 WARNING_S( "Error loading resource", giblorb_get_error_message(blorb_error) );
215                 return FALSE;
216         }
217
218         /* Load the resource */
219         GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
220         g_signal_connect( loader, "closed", G_CALLBACK(on_pixbuf_closed), NULL ); 
221         gdk_pixbuf_loader_set_size(loader, width, height);
222         glk_stream_set_position(glk_data->resource_file, res.data.startpos, seekmode_Start);
223         buffer = g_malloc( BUFFER_SIZE * sizeof(guchar) );
224
225         guint32 total_read = 0;
226         image_loaded = FALSE;
227         while(total_read < res.length) {
228                 guint32 num_read = glk_get_buffer_stream(glk_data->resource_file, (char *) buffer, BUFFER_SIZE);
229
230                 if( !gdk_pixbuf_loader_write(loader, buffer, MIN(BUFFER_SIZE, num_read), &pixbuf_error) ) {
231                         WARNING_S("Cannot read image", pixbuf_error->message);
232                         giblorb_unload_chunk(glk_data->resource_map, image);
233                         gdk_pixbuf_loader_close(loader, &pixbuf_error);
234                         g_free(buffer);
235                         return FALSE;
236                 }
237
238                 total_read += num_read;
239         }
240
241         if( !gdk_pixbuf_loader_close(loader, &pixbuf_error) ) {
242                 WARNING_S("Cannot read image", pixbuf_error->message);
243                 giblorb_unload_chunk(glk_data->resource_map, image);
244                 g_free(buffer);
245                 return FALSE;
246         }
247
248         if(!image_loaded) {
249                 /* Wait for the PixbufLoader to finish loading the image */
250                 g_mutex_lock(glk_data->resource_lock);
251                 while(!image_loaded) {
252                         g_cond_wait(glk_data->resource_loaded, glk_data->resource_lock);
253                 }
254                 g_mutex_unlock(glk_data->resource_lock);
255         }
256
257         giblorb_unload_chunk(glk_data->resource_map, image);
258         g_free(buffer);
259         
260         gdk_threads_enter();
261
262         gtk_image_get_pixmap( GTK_IMAGE(win->widget), &canvas, NULL );
263         if(canvas == NULL) {
264                 WARNING("Could not get pixmap");
265                 return FALSE;
266         }
267
268         GdkPixbuf *pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
269         if(pixbuf == NULL) {
270                 WARNING("Could not read image");
271                 return FALSE;
272         }
273
274         gdk_draw_pixbuf( GDK_DRAWABLE(canvas), NULL, pixbuf, 0, 0, val1, val2, -1, -1, GDK_RGB_DITHER_NONE, 0, 0 );
275
276         /* Update the screen */
277         gtk_widget_queue_draw(win->widget);
278
279         gdk_threads_leave();
280
281         return TRUE;
282 }
283
284 void
285 glk_window_set_background_color(winid_t win, glui32 color) {
286         win->background_color = color;
287 }
288
289 void
290 glk_window_fill_rect(winid_t win, glui32 color, glsi32 left, glsi32 top, glui32 width, glui32 height)
291 {
292         VALID_WINDOW(win, return);
293         g_return_if_fail(win->type == wintype_Graphics);
294
295
296         gdk_threads_enter();
297
298         GdkPixmap *map;
299         gtk_image_get_pixmap( GTK_IMAGE(win->widget), &map, NULL );
300         gdk_draw_rectangle( GDK_DRAWABLE(map), win->widget->style->white_gc, TRUE, left, top, width, height);
301         gtk_widget_queue_draw(win->widget);
302
303         gdk_threads_leave();
304 }
305
306 void
307 glk_window_erase_rect(winid_t win, glsi32 left, glsi32 top, glui32 width, glui32 height)
308 {
309         glk_window_fill_rect(win, win->background_color, left, top, width, height);
310 }
311
312 void glk_window_flow_break(winid_t win)
313 {
314 }
315
316 /*** Called when the graphics window is resized. Resize the backing pixmap if necessary ***/
317 void
318 on_graphics_size_allocate(GtkWidget *widget, GtkAllocation *allocation, winid_t win)
319
320         GdkPixmap *oldmap;
321         gtk_image_get_pixmap( GTK_IMAGE(widget), &oldmap, NULL );
322         gint oldwidth = 0;
323         gint oldheight = 0;
324  
325         /* Determine whether a pixmap exists with the correct size */
326         gboolean needs_resize = FALSE;
327         if(oldmap == NULL)
328                 needs_resize = TRUE;
329         else {
330                 gdk_drawable_get_size( GDK_DRAWABLE(oldmap), &oldwidth, &oldheight );
331                 if(oldwidth != allocation->width || oldheight != allocation->height)
332                         needs_resize = TRUE;
333         }
334
335         if(needs_resize) {
336                 /* Create a new pixmap */
337                 GdkPixmap *newmap = gdk_pixmap_new(widget->window, allocation->width, allocation->height, -1);
338                 gdk_draw_rectangle( GDK_DRAWABLE(newmap), widget->style->white_gc, TRUE, 0, 0, allocation->width, allocation->height);
339
340                 /* Copy the contents of the old pixmap */
341                 if(oldmap != NULL)
342                         gdk_draw_drawable( GDK_DRAWABLE(newmap), widget->style->white_gc, GDK_DRAWABLE(oldmap), 0, 0, 0, 0, oldwidth, oldheight);
343                 
344                 /* Use the new pixmap */
345                 gtk_image_set_from_pixmap( GTK_IMAGE(widget), newmap, NULL );
346                 g_object_unref(newmap);
347         }
348 }
349