Crahing bug fixed :)
[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         glk_image_draw(win, image, val1, val2);
200         return TRUE;
201 }
202
203 void
204 glk_window_set_background_color(winid_t win, glui32 color) {
205         win->background_color = color;
206 }
207
208 void
209 glk_window_fill_rect(winid_t win, glui32 color, glsi32 left, glsi32 top, glui32 width, glui32 height)
210 {
211         VALID_WINDOW(win, return);
212         g_return_if_fail(win->type == wintype_Graphics);
213
214
215         gdk_threads_enter();
216
217         GdkPixmap *map;
218         gtk_image_get_pixmap( GTK_IMAGE(win->widget), &map, NULL );
219         gdk_draw_rectangle( GDK_DRAWABLE(map), win->widget->style->white_gc, TRUE, left, top, width, height);
220         gtk_widget_queue_draw(win->widget);
221
222         gdk_threads_leave();
223 }
224
225 void
226 glk_window_erase_rect(winid_t win, glsi32 left, glsi32 top, glui32 width, glui32 height)
227 {
228         glk_window_fill_rect(win, win->background_color, left, top, width, height);
229 }
230
231 void glk_window_flow_break(winid_t win)
232 {
233 }
234
235 /*** Called when the graphics window is resized. Resize the backing pixmap if necessary ***/
236 void
237 on_graphics_size_allocate(GtkWidget *widget, GtkAllocation *allocation, winid_t win)
238
239         GdkPixmap *oldmap;
240         gtk_image_get_pixmap( GTK_IMAGE(widget), &oldmap, NULL );
241         gint oldwidth = 0;
242         gint oldheight = 0;
243  
244         /* Determine whether a pixmap exists with the correct size */
245         gboolean needs_resize = FALSE;
246         if(oldmap == NULL)
247                 needs_resize = TRUE;
248         else {
249                 gdk_drawable_get_size( GDK_DRAWABLE(oldmap), &oldwidth, &oldheight );
250                 if(oldwidth != allocation->width || oldheight != allocation->height)
251                         needs_resize = TRUE;
252         }
253
254         if(needs_resize) {
255                 /* Create a new pixmap */
256                 GdkPixmap *newmap = gdk_pixmap_new(widget->window, allocation->width, allocation->height, -1);
257                 gdk_draw_rectangle( GDK_DRAWABLE(newmap), widget->style->white_gc, TRUE, 0, 0, allocation->width, allocation->height);
258
259                 /* Copy the contents of the old pixmap */
260                 if(oldmap != NULL)
261                         gdk_draw_drawable( GDK_DRAWABLE(newmap), widget->style->white_gc, GDK_DRAWABLE(oldmap), 0, 0, 0, 0, oldwidth, oldheight);
262                 
263                 /* Use the new pixmap */
264                 gtk_image_set_from_pixmap( GTK_IMAGE(widget), newmap, NULL );
265                 g_object_unref(newmap);
266         }
267 }
268