fda5ad6847026dbd8d25de3e0749b3ad34e4b56b
[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         //printf("glk_image_get_info(%d)\n", image);
26
27         /* Lookup the proper resource */
28         blorb_error = giblorb_load_resource(glk_data->resource_map, giblorb_method_FilePos, &res, giblorb_ID_Pict, image);
29         if(blorb_error != giblorb_err_None) {
30                 WARNING_S( "Error loading resource", giblorb_get_error_message(blorb_error) );
31                 return FALSE;
32         }
33
34         if(width == NULL && height == NULL) {
35                 /* No size requested, don't bother loading the image */
36                 giblorb_unload_chunk(glk_data->resource_map, image);
37                 return TRUE;
38         }
39
40         /* Load the resource */
41         GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
42         g_signal_connect( loader, "size-prepared", G_CALLBACK(on_size_prepared), info ); 
43         glk_stream_set_position(glk_data->resource_file, res.data.startpos, seekmode_Start);
44         buffer = g_malloc( BUFFER_SIZE * sizeof(guchar) );
45
46         guint32 total_read = 0;
47         size_determined = FALSE;
48         while(total_read < res.length && !size_determined) {
49                 guint32 num_read = glk_get_buffer_stream(glk_data->resource_file, (char *) buffer, BUFFER_SIZE);
50
51                 if( !gdk_pixbuf_loader_write(loader, buffer, MIN(BUFFER_SIZE, num_read), &pixbuf_error) ) {
52                         WARNING_S("Cannot read image", pixbuf_error->message);
53                         giblorb_unload_chunk(glk_data->resource_map, image);
54                         gdk_pixbuf_loader_close(loader, &pixbuf_error);
55                         g_free(buffer);
56                         return FALSE;
57                 }
58
59                 total_read += num_read;
60         }
61         giblorb_unload_chunk(glk_data->resource_map, image);
62         gdk_pixbuf_loader_close(loader, &pixbuf_error);
63         g_free(buffer);
64
65         /* Determine the image dimensions */
66         g_mutex_lock(glk_data->resource_lock);
67         while(!size_determined) {
68                 /* Wait for the PixbufLoader to finish reading the image size */
69                 g_cond_wait(glk_data->resource_info_available, glk_data->resource_lock);
70         }
71         g_mutex_unlock(glk_data->resource_lock);
72
73         if(width != NULL)
74                 *width = info->width;
75         if(height != NULL)
76                 *height =info->height;
77         g_free(info);
78
79         return TRUE;
80 }
81
82 void
83 on_size_prepared(GdkPixbufLoader *loader, gint width, gint height, struct image_info *info)
84 {
85         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
86
87         g_mutex_lock(glk_data->resource_lock);
88         info->width = width;
89         info->height = height;
90         size_determined = TRUE;
91         g_cond_broadcast(glk_data->resource_info_available);
92         g_mutex_unlock(glk_data->resource_lock);
93 }
94
95 glui32
96 glk_image_draw(winid_t win, glui32 image, glsi32 val1, glsi32 val2)
97 {
98         VALID_WINDOW(win, return FALSE);
99         g_return_val_if_fail(win->type == wintype_Graphics, FALSE);
100
101         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
102         giblorb_result_t res;
103         giblorb_err_t blorb_error = 0;
104         GError *pixbuf_error = NULL;
105         struct image_info *info = g_new0(struct image_info, 1);
106         info->resource_number = image;
107         guchar *buffer;
108         GdkPixmap *canvas;
109
110         /* Lookup the proper resource */
111         blorb_error = giblorb_load_resource(glk_data->resource_map, giblorb_method_FilePos, &res, giblorb_ID_Pict, image);
112         if(blorb_error != giblorb_err_None) {
113                 WARNING_S( "Error loading resource", giblorb_get_error_message(blorb_error) );
114                 return FALSE;
115         }
116
117         /* Load the resource */
118         GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
119         g_signal_connect( loader, "closed", G_CALLBACK(on_pixbuf_closed), NULL ); 
120         glk_stream_set_position(glk_data->resource_file, res.data.startpos, seekmode_Start);
121         buffer = g_malloc( BUFFER_SIZE * sizeof(guchar) );
122
123         guint32 total_read = 0;
124         image_loaded = FALSE;
125         while(total_read < res.length) {
126                 guint32 num_read = glk_get_buffer_stream(glk_data->resource_file, (char *) buffer, BUFFER_SIZE);
127
128                 if( !gdk_pixbuf_loader_write(loader, buffer, MIN(BUFFER_SIZE, num_read), &pixbuf_error) ) {
129                         WARNING_S("Cannot read image", pixbuf_error->message);
130                         giblorb_unload_chunk(glk_data->resource_map, image);
131                         gdk_pixbuf_loader_close(loader, &pixbuf_error);
132                         g_free(buffer);
133                         return FALSE;
134                 }
135
136                 total_read += num_read;
137         }
138
139         if( !gdk_pixbuf_loader_close(loader, &pixbuf_error) ) {
140                 WARNING_S("Cannot read image", pixbuf_error->message);
141                 giblorb_unload_chunk(glk_data->resource_map, image);
142                 g_free(buffer);
143                 return FALSE;
144         }
145
146         if(!image_loaded) {
147                 /* Wait for the PixbufLoader to finish loading the image */
148                 g_mutex_lock(glk_data->resource_lock);
149                 while(!image_loaded) {
150                         g_cond_wait(glk_data->resource_loaded, glk_data->resource_lock);
151                 }
152                 g_mutex_unlock(glk_data->resource_lock);
153         }
154
155         giblorb_unload_chunk(glk_data->resource_map, image);
156         g_free(buffer);
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         printf("Drawing image %d\n", (int)image);
171
172         // TODO: fix hang
173         gdk_draw_pixbuf( GDK_DRAWABLE(canvas), NULL, pixbuf, 0, 0, val1, val2, -1, -1, GDK_RGB_DITHER_NONE, 0, 0 );
174
175         /* Update the screen */
176         gtk_widget_queue_draw(win->widget);
177
178         return TRUE;
179 }
180
181 void
182 on_pixbuf_closed(GdkPixbufLoader *loader, gpointer data)
183 {
184         printf("closed\n");
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
193
194 glui32
195 glk_image_draw_scaled(winid_t win, glui32 image, glsi32 val1, glsi32 val2, glui32 width, glui32 height)
196 {
197         return TRUE;
198 }
199
200 void
201 glk_window_set_background_color(winid_t win, glui32 color) {
202         win->background_color = color;
203 }
204
205 void
206 glk_window_fill_rect(winid_t win, glui32 color, glsi32 left, glsi32 top, glui32 width, glui32 height)
207 {
208         VALID_WINDOW(win, return);
209         g_return_if_fail(win->type == wintype_Graphics);
210
211         GdkPixmap *map;
212         gtk_image_get_pixmap( GTK_IMAGE(win->widget), &map, NULL );
213         gdk_draw_rectangle( GDK_DRAWABLE(map), win->widget->style->white_gc, TRUE, left, top, width, height);
214         gtk_widget_queue_draw(win->widget);
215 }
216
217 void
218 glk_window_erase_rect(winid_t win, glsi32 left, glsi32 top, glui32 width, glui32 height)
219 {
220         printf("erasing rect: %d %d %d %d\n", left, top, width, height);
221         glk_window_fill_rect(win, win->background_color, left, top, width, height);
222 }
223
224 void glk_window_flow_break(winid_t win)
225 {
226 }
227
228 /*** Called when the graphics window is resized. Resize the backing pixmap if necessary ***/
229 void
230 on_graphics_size_allocate(GtkWidget *widget, GtkAllocation *allocation, winid_t win)
231 {
232         printf("allocate to: %dx%d\n", allocation->width, allocation->height);
233         GdkPixmap *oldmap;
234         gtk_image_get_pixmap( GTK_IMAGE(widget), &oldmap, NULL );
235         gint oldwidth = 0;
236         gint oldheight = 0;
237  
238         /* Determine whether a pixmap exists with the correct size */
239         gboolean needs_resize = FALSE;
240         if(oldmap == NULL)
241                 needs_resize = TRUE;
242         else {
243                 gdk_drawable_get_size( GDK_DRAWABLE(oldmap), &oldwidth, &oldheight );
244                 if(oldwidth != allocation->width || oldheight != allocation->height)
245                         needs_resize = TRUE;
246         }
247
248         if(needs_resize) {
249                 printf("needs resize\n");
250                 /* Create a new pixmap */
251                 GdkPixmap *newmap = gdk_pixmap_new(widget->window, allocation->width, allocation->height, -1);
252                 gdk_draw_rectangle( GDK_DRAWABLE(newmap), widget->style->white_gc, TRUE, 0, 0, allocation->width, allocation->height);
253
254                 /* Copy the contents of the old pixmap */
255                 if(oldmap != NULL)
256                         gdk_draw_drawable( GDK_DRAWABLE(newmap), widget->style->white_gc, GDK_DRAWABLE(oldmap), 0, 0, 0, 0, oldwidth, oldheight);
257                 
258                 /* Use the new pixmap */
259                 gtk_image_set_from_pixmap( GTK_IMAGE(widget), newmap, NULL );
260                 g_object_unref(newmap);
261         }
262 }
263