"Fossies" - the Fresh Open Source Software Archive 
Member "xzgv-0.9.2/src/backend.c" (3 Sep 2017, 12075 Bytes) of package /linux/misc/old/xzgv-0.9.2.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "backend.c" see the
Fossies "Dox" file reference documentation.
1 /* xzgv - picture viewer for X, with file selector.
2 * Copyright (C) 1999,2000 Russell Marks.
3 * Copyright (C) 2007 Reuben Thomas.
4 * See main.c for license details.
5 *
6 * backend.c - picture rendering and (to a certain extent) loading.
7 *
8 * This is intended to be a reasonably generic wrapper for the library
9 * which is actually doing the work, to ease any transition, or allow
10 * extra backends.
11 *
12 * The basic assumptions are:
13 *
14 * - all pictures loaded as 24-bit.
15 *
16 * - a 24-bit copy is stored in an opaque (or mostly opaque) image
17 * structure of some sort; this is then rendered as needed.
18 *
19 * - there can be at least one pixmap `associated' with the image;
20 * that is, you can say `render', and it does that saving the pixmap
21 * details somewhere, so you can just say `draw' later. This could
22 * be emulated via xzgv_image's `backend_ext' pointer if need be.
23 *
24 * - it's possible to get a closest-match colour.
25 *
26 * - XXX probably others :-)
27 */
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <time.h>
33 #include <gtk/gtk.h>
34 #include <libexif/exif-data.h>
35
36 #include "backend.h"
37
38
39
40
41 /* *******************************************************************
42 * *******************************************************************
43 * ************ ************
44 * ************ gdk-pixbuf backend ************
45 * ************ ************
46 * *******************************************************************
47 * *******************************************************************
48 */
49 #include <gdk-pixbuf/gdk-pixbuf.h>
50
51 /* get backend image, casted to appropriate type */
52 #define BACKEND_IMAGE(x) ((x)->backend_image)
53
54 /* Dithering type */
55 GdkRgbDither dither_type;
56
57 /* do any initialisation the backend needs. Should include any
58 * visual/colormap change required.
59 * returns 1 if ok, else 0. (If 0, should output descriptive error msg.)
60 */
61 int backend_init(void)
62 {
63 gtk_widget_set_default_colormap(gdk_rgb_get_cmap());
64 gtk_widget_set_default_visual(gdk_rgb_get_visual());
65
66 dither_type = GDK_RGB_DITHER_NORMAL;
67
68 return(1);
69 }
70
71
72 /* init an image - the usual thing is to clear out the struct.
73 * You don't need to do anything here if it's not required by the
74 * backend; it's primarily to set xzgv_image correctly.
75 * Note that stuff done here should be stuff which NEVER fails.
76 */
77 void backend_image_init(xzgv_image *image)
78 {
79 image->rgb=NULL;
80 image->w=0; image->h=0;
81 image->backend_image=NULL;
82 image->backend_ext=NULL;
83 }
84
85
86 /* convenience function to update `public' info in xzgv_image
87 * from private info. You don't have to have this, but you *do*
88 * have to keep those fields up-to-date somehow.
89 */
90 static void public_info_update(xzgv_image *image)
91 {
92 image->rgb=gdk_pixbuf_get_pixels(BACKEND_IMAGE(image));
93 image->w=gdk_pixbuf_get_width(BACKEND_IMAGE(image));
94 image->h=gdk_pixbuf_get_height(BACKEND_IMAGE(image));
95 }
96
97
98 /* mark an image as `changed', i.e. `dirty' it. */
99 void backend_image_changed(xzgv_image *image)
100 {
101 /* XXX */
102 }
103
104
105 /* flip image vertically. Should `dirty' image if needed.
106 * Should also update xzgv_image's rgb/w/h fields (use public_info_update()).
107 */
108 void backend_flip_vert(xzgv_image *image)
109 {
110 GdkPixbuf *new_img = gdk_pixbuf_flip(BACKEND_IMAGE(image), FALSE);
111 g_object_unref(BACKEND_IMAGE(image));
112 BACKEND_IMAGE(image) = new_img;
113 public_info_update(image);
114 }
115
116
117 /* flip image horizontally, similarly. */
118 void backend_flip_horiz(xzgv_image *image)
119 {
120 GdkPixbuf *new_img = gdk_pixbuf_flip(BACKEND_IMAGE(image), TRUE);
121 g_object_unref(BACKEND_IMAGE(image));
122 BACKEND_IMAGE(image) = new_img;
123 public_info_update(image);
124 }
125
126
127 /* rotate image clockwise, similarly. */
128 void backend_rotate_cw(xzgv_image *image)
129 {
130 GdkPixbuf *new_img = gdk_pixbuf_rotate_simple(BACKEND_IMAGE(image), GDK_PIXBUF_ROTATE_CLOCKWISE);
131 g_object_unref(BACKEND_IMAGE(image));
132 BACKEND_IMAGE(image) = new_img;
133 public_info_update(image);
134 }
135
136
137 /* rotate image anti-clockwise, similarly. */
138 void backend_rotate_acw(xzgv_image *image)
139 {
140 GdkPixbuf *new_img = gdk_pixbuf_rotate_simple(BACKEND_IMAGE(image), GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE);
141 g_object_unref(BACKEND_IMAGE(image));
142 BACKEND_IMAGE(image) = new_img;
143 public_info_update(image);
144 }
145
146
147 /* create an image from RGB data, given width and height.
148 * This version should do so *non-destructively*, by making a copy of
149 * the data - the data passed to it should be left intact (and if it was
150 * malloced, will need to later be freed by the caller).
151 */
152 xzgv_image *backend_create_image_from_data(unsigned char *rgb,int w,int h)
153 {
154 unsigned char *rgbcopy;
155
156 /* no non-destructive version, so copy and use destructive one */
157 if((rgbcopy=malloc(w*h*3))==NULL)
158 return(NULL);
159
160 memcpy(rgbcopy,rgb,w*h*3);
161 return(backend_create_image_from_data_destructively(rgbcopy,w,h));
162 }
163
164
165 /* create an image from RGB data, *destructively*.
166 * This takes over the rgb data passed to it, such that a) the caller
167 * should NOT free it, and b) the data may change (probably not now,
168 * but perhaps later if we do a flip or something).
169 *
170 * *On error, the rgb data must be freed.* This is, after all, meant
171 * to be destructive, such that the caller need not care about the
172 * rgb data after. It also means that the rgb data MUST have been
173 * malloced... :-)
174 *
175 * Obviously this version should be faster if the backend supports it;
176 * if not, call backend_create_image_from_data() then free().
177 */
178 xzgv_image *backend_create_image_from_data_destructively(unsigned char *rgb,
179 int w,int h)
180 {
181 GdkPixbuf *backim;
182 xzgv_image *im;
183
184 if((im=malloc(sizeof(xzgv_image)))==NULL)
185 return(NULL);
186
187 if((backim=gdk_pixbuf_new_from_data(rgb,GDK_COLORSPACE_RGB,FALSE,8,
188 w,h,w*3,
189 (GdkPixbufDestroyNotify)free,NULL))==NULL)
190 {
191 free(im);
192 free(rgb); /* since it failed */
193 return(NULL);
194 }
195
196 backend_image_init(im);
197 im->backend_image=backim;
198 public_info_update(im);
199
200 return(im);
201 }
202
203 int backend_get_orientation_from_file(char *filename)
204 {
205 GdkPixbufFormat *imform;
206 gchar *format;
207 ExifData *ed;
208 ExifEntry *entry;
209 ExifShort orient;
210 static const ExifShort xzgv_orient[]={0,0,1,3,2,7,4,6,5};
211
212 if((imform=gdk_pixbuf_get_file_info(filename,NULL,NULL))==NULL) return 0;
213 if((format=gdk_pixbuf_format_get_name(imform))==NULL) return 0;
214 if(!strcmp(format, "jpeg") || !strcmp(format, "tiff"))
215 {
216 if((ed=exif_data_new_from_file(filename))==NULL) return 0;
217 if((entry=exif_data_get_entry(ed,EXIF_TAG_ORIENTATION))==NULL) return 0;
218 if((orient=exif_get_short(entry->data,exif_data_get_byte_order(ed)))>sizeof(xzgv_orient)-1) return 0;
219 orient=xzgv_orient[orient];
220 return (int)orient;
221 }
222 return 0;
223 }
224
225 /* create an image from a given picture file.
226 * The most important formats should eventually be dealt with by xzgv
227 * directly, so it would be acceptable for this to just use netpbm or
228 * ImageMagick to read the file, then use
229 * backend_create_image_from_data_destructively()
230 * on it. Or if the backend has a file reader which returns an image,
231 * you could use that.
232 *
233 * This must return NULL (freeing image if necessary) if the picture
234 * is larger than 32767 pixels in either dimension.
235 */
236 xzgv_image *backend_create_image_from_file(char *filename)
237 {
238 GdkPixbuf *backim;
239 xzgv_image *im;
240 GError *gerror = NULL;
241
242 if((im=malloc(sizeof(xzgv_image)))==NULL)
243 return(NULL);
244
245 /* XXX does this deal with the 32767 issue or not? */
246 if((backim=gdk_pixbuf_new_from_file((const char *)filename, &gerror))==NULL)
247 {
248 free(im);
249 return(NULL);
250 }
251
252 backend_image_init(im);
253 im->backend_image=backim;
254 public_info_update(im);
255
256 return(im);
257 }
258
259
260 /* render image at (x,y) in window (at actual size).
261 * This is a fairly high-level one, but most backends will probably
262 * support it, and xzgv does need it.
263 * It should not leave any random pixmaps lying around. :-)
264 */
265 void backend_render_image_into_window(xzgv_image *image,GdkWindow *win,
266 int x,int y)
267 {
268 /* XXX this assumes only one window is rendered into */
269 static GdkGC *gc=NULL;
270
271 if(!gc)
272 gc=gdk_gc_new(win);
273
274 gdk_draw_pixbuf(win,gc,BACKEND_IMAGE(image),
275 0,0,x,y,image->w,image->h,
276 dither_type,0,0);
277 }
278
279
280 /* render a pixmap from the image (at the given size), which is then
281 * associated with it.
282 *
283 * returns 1 if this failed, else 0.
284 *
285 * (Use xzgv_image's `backend_ext' field to save the pixmap pointer.)
286 */
287 int backend_render_pixmap_for_image(xzgv_image *image,int x,int y)
288 {
289 GdkPixbuf *backim;
290 GdkPixmap *pixmap;
291 int same=0;
292
293 if(x==image->w && y==image->h)
294 {
295 same=1;
296 backim=BACKEND_IMAGE(image);
297 }
298 else
299 {
300 if((backim=gdk_pixbuf_scale_simple(BACKEND_IMAGE(image),x,y,
301 GDK_INTERP_NEAREST))==NULL)
302 return(0);
303 }
304
305 gdk_pixbuf_render_pixmap_and_mask(backim,&pixmap,NULL,128);
306
307 if(image->backend_ext) /* not normally the case */
308 backend_pixmap_destroy((GdkPixmap *)image->backend_ext);
309
310 image->backend_ext=(void *)pixmap;
311
312 if(!same)
313 g_object_unref(backim);
314
315 return(1);
316 }
317
318
319 /* return the most recently rendered pixmap associated with the image,
320 * de-associating it from the image. The returned pixmap should not be
321 * modified. Returns NULL if there was no associated pixmap.
322 */
323 GdkPixmap *backend_get_and_detach_pixmap(xzgv_image *image)
324 {
325 GdkPixmap *ret=image->backend_ext;
326
327 image->backend_ext=NULL;
328 return(ret);
329 }
330
331
332 /* free a pixmap generated by the backend.
333 * (The assumption is there may be some caching system which will
334 * be less than pleased if we don't do it the way it wants.)
335 */
336 void backend_pixmap_destroy(GdkPixmap *pixmap)
337 {
338 g_object_unref(pixmap);
339 }
340
341
342 /* destroy image. */
343 void backend_image_destroy(xzgv_image *image)
344 {
345 if(image->backend_ext)
346 backend_pixmap_destroy((GdkPixmap *)image->backend_ext);
347
348 if(image->backend_image)
349 g_object_unref(BACKEND_IMAGE(image));
350
351 free(image);
352 }
353
354
355 /* get high-colour (15/16-bit) dithering status.
356 * returns 1 if enabled, 0 if disabled, else -1 which indicates that
357 * either the current visual does not support hicol dithering, or
358 * this backend doesn't support it. (Since it's optional.)
359 */
360 int backend_get_hicol_dither(void)
361 {
362 return (dither_type == GDK_RGB_DITHER_MAX) ? TRUE : FALSE;
363 }
364
365
366 /* set high-colour dithering status.
367 * Should not be called if backend_get_hicol_dither() returned -1.
368 */
369 void backend_set_hicol_dither(int on)
370 {
371 dither_type = on ? GDK_RGB_DITHER_MAX : GDK_RGB_DITHER_NORMAL;
372 }
373
374
375 /* get colour which most closely matches arg's RGB fields
376 * preferably setting those fields to actual RGB value of
377 * colour returned (in col->pixel).
378 */
379 void backend_get_closest_colour(GdkColor *col)
380 {
381 /* this seems to be the closest I can manage */
382 col->pixel=gdk_rgb_xpixel_from_rgb(
383 (guint32)(((col->red>>8)<<16)|(col->green&0xff00)|(col->blue>>8)));
384 }
385
386
387 /* return visual currently being used.
388 * should be able to use GDK call for this if need be, but this
389 * call gives you the option to get it right for sure. :-)
390 */
391 GdkVisual *backend_get_visual(void)
392 {
393 return(gdk_rgb_get_visual());
394 }
395
396
397 /* set value mapping to apply to all three colour channels when
398 * rendering. While image *is* an arg here, a global setting would
399 * be sufficient as long as it doesn't mangle already-rendered
400 * pixmaps.
401 */
402 void backend_set_value_mapping(xzgv_image *image,unsigned char *map)
403 {
404 /* XXX GDK seems not to have this. */
405 }
406
407
408 /* a fairly high-level one, which is unfortunately required:
409 *
410 * read XPM data from string array and render into pixmap, also returning
411 * a bitmap mask matching any transparent parts. Any image used
412 * on the way should be freed, making it a straight XPM to
413 * pixmap/bitmap job.
414 */
415 int backend_create_pixmap_from_xpm_data(const char **data,
416 GdkPixmap **pixmap,GdkBitmap **mask)
417 {
418 GdkPixbuf *backim=gdk_pixbuf_new_from_xpm_data(data);
419
420 if(backim==NULL)
421 {
422 *pixmap=NULL;
423 *mask=NULL;
424 return(0);
425 }
426
427 gdk_pixbuf_render_pixmap_and_mask(backim,pixmap,mask,128);
428
429 g_object_unref(backim);
430
431 return(1);
432 }