"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 }