Added correct resize handling
[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
10 static gboolean size_determined;
11
12 glui32
13 glk_image_get_info(glui32 image, glui32 *width, glui32 *height)
14 {
15         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
16         giblorb_result_t res;
17         giblorb_err_t blorb_error = 0;
18         GError *pixbuf_error = NULL;
19         struct image_info *info = g_new0(struct image_info, 1);
20         info->resource_number = image;
21         guchar *buffer;
22
23         //printf("glk_image_get_info(%d)\n", image);
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         if(!size_determined) {
65                 WARNING("Cannot read image size");
66                 g_free(info);
67                 return FALSE;
68         }
69
70         if(width != NULL)
71                 *width = info->width;
72         if(height != NULL)
73                 *height =info->height;
74         g_free(info);
75
76         return TRUE;
77 }
78
79 void
80 on_size_prepared(GdkPixbufLoader *loader, gint width, gint height, struct image_info *info)
81 {
82         info->width = width;
83         info->height = height;
84         size_determined = TRUE;
85 }
86
87 /*** Called when the graphics window is resized. Resize the backing pixmap if necessary ***/
88 void
89 on_graphics_size_allocate(GtkWidget *widget, GtkAllocation *allocation, winid_t win)
90 {
91         printf("allocate to: %dx%d\n", allocation->width, allocation->height);
92         GdkPixmap *oldmap;
93         gtk_image_get_pixmap( GTK_IMAGE(widget), &oldmap, NULL );
94         gint oldwidth = 0;
95         gint oldheight = 0;
96  
97         /* Determine whether a pixmap exists with the correct size */
98         gboolean needs_resize = FALSE;
99         if(oldmap == NULL)
100                 needs_resize = TRUE;
101         else {
102                 gdk_drawable_get_size( GDK_DRAWABLE(oldmap), &oldwidth, &oldheight );
103                 if(oldwidth != allocation->width || oldheight != allocation->height)
104                         needs_resize = TRUE;
105         }
106
107         if(needs_resize) {
108                 printf("needs resize\n");
109                 /* Create a new pixmap */
110                 GdkPixmap *newmap = gdk_pixmap_new(widget->window, allocation->width, allocation->height, -1);
111                 gdk_draw_rectangle( GDK_DRAWABLE(newmap), widget->style->white_gc, TRUE, 0, 0, allocation->width, allocation->height);
112
113                 /* Copy the contents of the old pixmap */
114                 if(oldmap != NULL)
115                         gdk_draw_drawable( GDK_DRAWABLE(newmap), widget->style->white_gc, GDK_DRAWABLE(oldmap), 0, 0, 0, 0, oldwidth, oldheight);
116                 
117                 /* Use the new pixmap */
118                 gtk_image_set_from_pixmap( GTK_IMAGE(widget), newmap, NULL );
119                 g_object_unref(newmap);
120         }
121 }
122
123 glui32
124 glk_image_draw(winid_t win, glui32 image, glsi32 val1, glsi32 val2) {
125         VALID_WINDOW(win, return FALSE);
126         g_return_val_if_fail(win->type == wintype_Graphics, FALSE);
127         printf("Drawing image %d\n", (int)image);
128
129         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
130         giblorb_result_t res;
131         giblorb_err_t blorb_error = 0;
132         GError *pixbuf_error = NULL;
133         struct image_info *info = g_new0(struct image_info, 1);
134         info->resource_number = image;
135         guchar *buffer;
136         GdkPixmap *canvas;
137
138         /* Lookup the proper resource */
139         blorb_error = giblorb_load_resource(glk_data->resource_map, giblorb_method_FilePos, &res, giblorb_ID_Pict, image);
140         if(blorb_error != giblorb_err_None) {
141                 WARNING_S( "Error loading resource", giblorb_get_error_message(blorb_error) );
142                 return FALSE;
143         }
144
145         /* Load the resource */
146         GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
147         glk_stream_set_position(glk_data->resource_file, res.data.startpos, seekmode_Start);
148         buffer = g_malloc( BUFFER_SIZE * sizeof(guchar) );
149
150         guint32 total_read = 0;
151         while(total_read < res.length) {
152                 guint32 num_read = glk_get_buffer_stream(glk_data->resource_file, (char *) buffer, BUFFER_SIZE);
153
154                 if( !gdk_pixbuf_loader_write(loader, buffer, MIN(BUFFER_SIZE, num_read), &pixbuf_error) ) {
155                         WARNING_S("Cannot read image", pixbuf_error->message);
156                         giblorb_unload_chunk(glk_data->resource_map, image);
157                         gdk_pixbuf_loader_close(loader, &pixbuf_error);
158                         g_free(buffer);
159                         return FALSE;
160                 }
161
162                 total_read += num_read;
163         }
164         printf("Loading done\n");
165         giblorb_unload_chunk(glk_data->resource_map, image);
166         g_free(buffer);
167
168         gtk_image_get_pixmap( GTK_IMAGE(win->widget), &canvas, NULL );
169         if(canvas == NULL) {
170                 WARNING("Could not get pixmap");
171                 gdk_pixbuf_loader_close(loader, &pixbuf_error);
172                 return FALSE;
173         }
174
175         GdkPixbuf *pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
176         if(pixbuf == NULL) {
177                 WARNING("Could not read image");
178                 gdk_pixbuf_loader_close(loader, &pixbuf_error);
179                 return FALSE;
180         }
181
182         // TODO: FIX hang?
183         gdk_draw_pixbuf( GDK_DRAWABLE(canvas), NULL, pixbuf, 0, 0, val1, val2, -1, -1, GDK_RGB_DITHER_NONE, 0, 0 );
184         gdk_pixbuf_loader_close(loader, &pixbuf_error);
185
186         /* Update the screen */
187         gtk_widget_queue_draw(win->widget);
188
189         return TRUE;
190 }
191
192 glui32
193 glk_image_draw_scaled(winid_t win, glui32 image, glsi32 val1, glsi32 val2, glui32 width, glui32 height)
194 {
195         return TRUE;
196 }
197
198 void
199 glk_window_set_background_color(winid_t win, glui32 color) {
200         win->background_color = color;
201 }
202
203 void
204 glk_window_fill_rect(winid_t win, glui32 color, glsi32 left, glsi32 top, glui32 width, glui32 height)
205 {
206         VALID_WINDOW(win, return);
207         g_return_if_fail(win->type == wintype_Graphics);
208
209         GdkPixmap *map;
210         gtk_image_get_pixmap( GTK_IMAGE(win->widget), &map, NULL );
211         gdk_draw_rectangle( GDK_DRAWABLE(map), win->widget->style->white_gc, TRUE, left, top, width, height);
212         gtk_widget_queue_draw(win->widget);
213 }
214
215 void
216 glk_window_erase_rect(winid_t win, glsi32 left, glsi32 top, glui32 width, glui32 height)
217 {
218         printf("erasing rect: %d %d %d %d\n", left, top, width, height);
219         glk_window_fill_rect(win, win->background_color, left, top, width, height);
220 }
221
222 void glk_window_flow_break(winid_t win)
223 {
224 }