JWM Source Documentation
image.c
Go to the documentation of this file.
1 
10 #include "jwm.h"
11 
12 #ifndef MAKE_DEPEND
13 
14  /* We should include png.h here. See jwm.h for an explanation. */
15 
16 # ifdef USE_XPM
17 # include <X11/xpm.h>
18 # endif
19 # ifdef USE_JPEG
20 # include <jpeglib.h>
21 # endif
22 # ifdef USE_CAIRO
23 # include <cairo.h>
24 # include <cairo-svg.h>
25 # endif
26 # ifdef USE_RSVG
27 # include <librsvg/rsvg.h>
28 # endif
29 #endif /* MAKE_DEPEND */
30 
31 #include "image.h"
32 #include "main.h"
33 #include "error.h"
34 #include "color.h"
35 #include "misc.h"
36 
37 #ifdef USE_CAIRO
38 #ifdef USE_RSVG
39 static ImageNode *LoadSVGImage(const char *fileName, int width, int height,
40  char preserveAspect);
41 #endif
42 #endif
43 #ifdef USE_JPEG
44 static ImageNode *LoadJPEGImage(const char *fileName, int width, int height);
45 #endif
46 #ifdef USE_PNG
47 static ImageNode *LoadPNGImage(const char *fileName);
48 #endif
49 #ifdef USE_XPM
50 static ImageNode *LoadXPMImage(const char *fileName);
51 #endif
52 #ifdef USE_XBM
53 static ImageNode *LoadXBMImage(const char *fileName);
54 #endif
55 #ifdef USE_ICONS
56 static ImageNode *CreateImageFromXImages(XImage *image, XImage *shape);
57 #endif
58 
59 #ifdef USE_XPM
60 static int AllocateColor(Display *d, Colormap cmap, char *name,
61  XColor *c, void *closure);
62 static int FreeColors(Display *d, Colormap cmap, Pixel *pixels, int n,
63  void *closure);
64 #endif
65 
67 ImageNode *LoadImage(const char *fileName, int width, int height,
68  char preserveAspect)
69 {
70  unsigned nameLength;
71  ImageNode *result = NULL;
72  if(!fileName) {
73  return result;
74  }
75 
76  nameLength = strlen(fileName);
77  if(JUNLIKELY(nameLength == 0)) {
78  return result;
79  }
80 
81  /* Attempt to load the file as a PNG image. */
82 #ifdef USE_PNG
83  if(nameLength >= 4
84  && !StrCmpNoCase(&fileName[nameLength - 4], ".png")) {
85  result = LoadPNGImage(fileName);
86  if(result) {
87  return result;
88  }
89  }
90 #endif
91 
92  /* Attempt to load the file as a JPEG image. */
93 #ifdef USE_JPEG
94  if( (nameLength >= 4
95  && !StrCmpNoCase(&fileName[nameLength - 4], ".jpg"))
96  || (nameLength >= 5
97  && !StrCmpNoCase(&fileName[nameLength - 5], ".jpeg"))) {
98  result = LoadJPEGImage(fileName, width, height);
99  if(result) {
100  return result;
101  }
102  }
103 #endif
104 
105  /* Attempt to load the file as an SVG image. */
106 #ifdef USE_CAIRO
107 #ifdef USE_RSVG
108  if(nameLength >= 4
109  && !StrCmpNoCase(&fileName[nameLength - 4], ".svg")) {
110  result = LoadSVGImage(fileName, width, height, preserveAspect);
111  if(result) {
112  return result;
113  }
114  }
115 #endif
116 #endif
117 
118  /* Attempt to load the file as an XPM image. */
119 #ifdef USE_XPM
120  if(nameLength >= 4
121  && !StrCmpNoCase(&fileName[nameLength - 4], ".xpm")) {
122  result = LoadXPMImage(fileName);
123  if(result) {
124  return result;
125  }
126  }
127 #endif
128 
129  /* Attempt to load the file as an XBM image. */
130 #ifdef USE_XBM
131  if(nameLength >= 4
132  && !StrCmpNoCase(&fileName[nameLength - 4], ".xbm")) {
133  result = LoadXBMImage(fileName);
134  if(result) {
135  return result;
136  }
137  }
138 #endif
139 
140  return result;
141 
142 }
143 
145 #ifdef USE_ICONS
146 ImageNode *LoadImageFromDrawable(Drawable pmap, Pixmap mask)
147 {
148  ImageNode *result = NULL;
149  XImage *mask_image = NULL;
150  XImage *icon_image = NULL;
151  Window rwindow;
152  int x, y;
153  unsigned int width, height;
154  unsigned int border_width;
155  unsigned int depth;
156 
157  JXGetGeometry(display, pmap, &rwindow, &x, &y, &width, &height,
158  &border_width, &depth);
159  icon_image = JXGetImage(display, pmap, 0, 0, width, height,
160  AllPlanes, ZPixmap);
161  if(mask != None) {
162  mask_image = JXGetImage(display, mask, 0, 0, width, height, 1, ZPixmap);
163  }
164  result = CreateImageFromXImages(icon_image, mask_image);
165  if(icon_image) {
166  JXDestroyImage(icon_image);
167  }
168  if(mask_image) {
169  JXDestroyImage(mask_image);
170  }
171  return result;
172 }
173 #endif
174 
179 #ifdef USE_PNG
180 ImageNode *LoadPNGImage(const char *fileName)
181 {
182 
183  static ImageNode *result;
184  static FILE *fd;
185  static unsigned char **rows;
186  static png_structp pngData;
187  static png_infop pngInfo;
188  static png_infop pngEndInfo;
189 
190  unsigned char header[8];
191  unsigned long rowBytes;
192  int bitDepth, colorType;
193  unsigned int x, y;
194  png_uint_32 width;
195  png_uint_32 height;
196 
197  Assert(fileName);
198 
199  result = NULL;
200  fd = NULL;
201  rows = NULL;
202  pngData = NULL;
203  pngInfo = NULL;
204  pngEndInfo = NULL;
205 
206  fd = fopen(fileName, "rb");
207  if(!fd) {
208  return NULL;
209  }
210 
211  x = fread(header, 1, sizeof(header), fd);
212  if(x != sizeof(header) || png_sig_cmp(header, 0, sizeof(header))) {
213  fclose(fd);
214  return NULL;
215  }
216 
217  pngData = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
218  if(JUNLIKELY(!pngData)) {
219  fclose(fd);
220  Warning(_("could not create read struct for PNG image: %s"), fileName);
221  return NULL;
222  }
223 
224  if(JUNLIKELY(setjmp(png_jmpbuf(pngData)))) {
225  png_destroy_read_struct(&pngData, &pngInfo, &pngEndInfo);
226  if(fd) {
227  fclose(fd);
228  }
229  if(rows) {
230  ReleaseStack(rows);
231  }
232  DestroyImage(result);
233  Warning(_("error reading PNG image: %s"), fileName);
234  return NULL;
235  }
236 
237  pngInfo = png_create_info_struct(pngData);
238  if(JUNLIKELY(!pngInfo)) {
239  png_destroy_read_struct(&pngData, NULL, NULL);
240  fclose(fd);
241  Warning(_("could not create info struct for PNG image: %s"), fileName);
242  return NULL;
243  }
244 
245  pngEndInfo = png_create_info_struct(pngData);
246  if(JUNLIKELY(!pngEndInfo)) {
247  png_destroy_read_struct(&pngData, &pngInfo, NULL);
248  fclose(fd);
249  Warning("could not create end info struct for PNG image: %s", fileName);
250  return NULL;
251  }
252 
253  png_init_io(pngData, fd);
254  png_set_sig_bytes(pngData, sizeof(header));
255 
256  png_read_info(pngData, pngInfo);
257 
258  png_get_IHDR(pngData, pngInfo, &width, &height,
259  &bitDepth, &colorType, NULL, NULL, NULL);
260  result = CreateImage(width, height, 0);
261 
262  png_set_expand(pngData);
263 
264  if(bitDepth == 16) {
265  png_set_strip_16(pngData);
266  } else if(bitDepth < 8) {
267  png_set_packing(pngData);
268  }
269 
270  png_set_swap_alpha(pngData);
271  png_set_filler(pngData, 0xFF, PNG_FILLER_BEFORE);
272 
273  if(colorType == PNG_COLOR_TYPE_GRAY
274  || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
275  png_set_gray_to_rgb(pngData);
276  }
277 
278  png_read_update_info(pngData, pngInfo);
279 
280  rowBytes = png_get_rowbytes(pngData, pngInfo);
281  rows = AllocateStack(result->height * sizeof(result->data));
282  y = 0;
283  for(x = 0; x < result->height; x++) {
284  rows[x] = &result->data[y];
285  y += rowBytes;
286  }
287 
288  png_read_image(pngData, rows);
289 
290  png_read_end(pngData, pngInfo);
291  png_destroy_read_struct(&pngData, &pngInfo, &pngEndInfo);
292 
293  fclose(fd);
294 
295  ReleaseStack(rows);
296  rows = NULL;
297 
298  return result;
299 
300 }
301 #endif /* USE_PNG */
302 
304 #ifdef USE_JPEG
305 
306 typedef struct {
307  struct jpeg_error_mgr pub;
308  jmp_buf jbuffer;
309 } JPEGErrorStruct;
310 
311 static void JPEGErrorHandler(j_common_ptr cinfo) {
312  JPEGErrorStruct *es = (JPEGErrorStruct*)cinfo->err;
313  longjmp(es->jbuffer, 1);
314 }
315 
316 ImageNode *LoadJPEGImage(const char *fileName, int width, int height)
317 {
318  static ImageNode *result;
319  static struct jpeg_decompress_struct cinfo;
320  static FILE *fd;
321  static JSAMPARRAY buffer;
322  static JPEGErrorStruct jerr;
323 
324  int rowStride;
325  int x;
326  int inIndex, outIndex;
327 
328  /* Open the file. */
329  fd = fopen(fileName, "rb");
330  if(fd == NULL) {
331  return NULL;
332  }
333 
334  /* Make sure everything is initialized so we can recover from errors. */
335  result = NULL;
336  buffer = NULL;
337 
338  /* Setup the error handler. */
339  cinfo.err = jpeg_std_error(&jerr.pub);
340  jerr.pub.error_exit = JPEGErrorHandler;
341 
342  /* Control will return here if an error was encountered. */
343  if(setjmp(jerr.jbuffer)) {
344  DestroyImage(result);
345  jpeg_destroy_decompress(&cinfo);
346  fclose(fd);
347  return NULL;
348  }
349 
350  /* Prepare to load the file. */
351  jpeg_create_decompress(&cinfo);
352  jpeg_stdio_src(&cinfo, fd);
353 
354  /* Check the header. */
355  jpeg_read_header(&cinfo, TRUE);
356 
357  /* Pick an appropriate scale for the image.
358  * We scale the image by the scale value for the dimension with
359  * the smallest absolute change.
360  */
361  jpeg_calc_output_dimensions(&cinfo);
362  if(width != 0 && height != 0) {
363  /* Scale using n/8 with n in [1..8]. */
364  int ratio;
365  if(abs((int)cinfo.output_width - width)
366  < abs((int)cinfo.output_height - height)) {
367  ratio = (width << 4) / cinfo.output_width;
368  } else {
369  ratio = (height << 4) / cinfo.output_height;
370  }
371  cinfo.scale_num = Max(1, Min(8, (ratio >> 2)));
372  cinfo.scale_denom = 8;
373  }
374 
375  /* Start decompression. */
376  jpeg_start_decompress(&cinfo);
377  rowStride = cinfo.output_width * cinfo.output_components;
378  buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo,
379  JPOOL_IMAGE, rowStride, 1);
380 
381  result = CreateImage(cinfo.output_width, cinfo.output_height, 0);
382 
383  /* Read lines. */
384  outIndex = 0;
385  while(cinfo.output_scanline < cinfo.output_height) {
386  jpeg_read_scanlines(&cinfo, buffer, 1);
387  inIndex = 0;
388  for(x = 0; x < result->width; x++) {
389  switch(cinfo.output_components) {
390  case 1: /* Grayscale. */
391  result->data[outIndex + 1] = GETJSAMPLE(buffer[0][inIndex]);
392  result->data[outIndex + 2] = GETJSAMPLE(buffer[0][inIndex]);
393  result->data[outIndex + 3] = GETJSAMPLE(buffer[0][inIndex]);
394  inIndex += 1;
395  break;
396  default: /* RGB */
397  result->data[outIndex + 1] = GETJSAMPLE(buffer[0][inIndex + 0]);
398  result->data[outIndex + 2] = GETJSAMPLE(buffer[0][inIndex + 1]);
399  result->data[outIndex + 3] = GETJSAMPLE(buffer[0][inIndex + 2]);
400  inIndex += 3;
401  break;
402  }
403  result->data[outIndex + 0] = 0xFF;
404  outIndex += 4;
405  }
406  }
407 
408  /* Clean up. */
409  jpeg_destroy_decompress(&cinfo);
410  fclose(fd);
411 
412  return result;
413 
414 }
415 #endif /* USE_JPEG */
416 
417 #ifdef USE_CAIRO
418 #ifdef USE_RSVG
419 ImageNode *LoadSVGImage(const char *fileName, int width, int height,
420  char preserveAspect)
421 {
422 
423 #if !GLIB_CHECK_VERSION(2, 35, 0)
424  static char initialized = 0;
425 #endif
426  ImageNode *result = NULL;
427  RsvgHandle *rh;
428  RsvgDimensionData dim;
429  GError *e;
430  cairo_surface_t *target;
431  cairo_t *context;
432  int stride;
433  int i;
434  float xscale, yscale;
435 
436  Assert(fileName);
437 
438 #if !GLIB_CHECK_VERSION(2, 35, 0)
439  if(!initialized) {
440  initialized = 1;
441  g_type_init();
442  }
443 #endif
444 
445  /* Load the image from the file. */
446  e = NULL;
447  rh = rsvg_handle_new_from_file(fileName, &e);
448  if(!rh) {
449  g_error_free(e);
450  return NULL;
451  }
452 
453  rsvg_handle_get_dimensions(rh, &dim);
454  if(width == 0 || height == 0) {
455  width = dim.width;
456  height = dim.height;
457  xscale = 1.0;
458  yscale = 1.0;
459  } else if(preserveAspect) {
460  if(abs(dim.width - width) < abs(dim.height - height)) {
461  xscale = (float)width / dim.width;
462  height = dim.height * xscale;
463  } else {
464  xscale = (float)height / dim.height;
465  width = dim.width * xscale;
466  }
467  yscale = xscale;
468  } else {
469  xscale = (float)width / dim.width;
470  yscale = (float)height / dim.height;
471  }
472 
473  result = CreateImage(width, height, 0);
474  memset(result->data, 0, width * height * 4);
475 
476  /* Create the target surface. */
477  stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
478  target = cairo_image_surface_create_for_data(result->data,
479  CAIRO_FORMAT_ARGB32,
480  width, height, stride);
481  context = cairo_create(target);
482  cairo_scale(context, xscale, yscale);
483  cairo_paint_with_alpha(context, 0.0);
484  rsvg_handle_render_cairo(rh, context);
485  cairo_destroy(context);
486  cairo_surface_destroy(target);
487  g_object_unref(rh);
488 
489  for(i = 0; i < 4 * width * height; i += 4) {
490  const unsigned int temp = *(unsigned int*)&result->data[i];
491  const unsigned int alpha = (temp >> 24) & 0xFF;
492  const unsigned int red = (temp >> 16) & 0xFF;
493  const unsigned int green = (temp >> 8) & 0xFF;
494  const unsigned int blue = (temp >> 0) & 0xFF;
495  result->data[i + 0] = alpha;
496  if(alpha > 0) {
497  result->data[i + 1] = (red * 255) / alpha;
498  result->data[i + 2] = (green * 255) / alpha;
499  result->data[i + 3] = (blue * 255) / alpha;
500  }
501  }
502 
503  return result;
504 
505 }
506 #endif /* USE_RSVG */
507 #endif /* USE_CAIRO */
508 
510 #ifdef USE_XPM
511 ImageNode *LoadXPMImage(const char *fileName)
512 {
513 
514  ImageNode *result = NULL;
515 
516  XpmAttributes attr;
517  XImage *image;
518  XImage *shape;
519  int rc;
520 
521  Assert(fileName);
522 
523  attr.valuemask = XpmAllocColor | XpmFreeColors | XpmColorClosure;
524  attr.alloc_color = AllocateColor;
525  attr.free_colors = FreeColors;
526  attr.color_closure = NULL;
527  rc = XpmReadFileToImage(display, (char*)fileName, &image, &shape, &attr);
528  if(rc == XpmSuccess) {
529  result = CreateImageFromXImages(image, shape);
530  JXDestroyImage(image);
531  if(shape) {
532  JXDestroyImage(shape);
533  }
534  }
535 
536  return result;
537 
538 }
539 #endif /* USE_XPM */
540 
542 #ifdef USE_XBM
543 ImageNode *LoadXBMImage(const char *fileName)
544 {
545  ImageNode *result = NULL;
546  unsigned char *data;
547  unsigned width, height;
548  int xhot, yhot;
549  int rc;
550 
551  rc = XReadBitmapFileData(fileName, &width, &height, &data, &xhot, &yhot);
552  if(rc == BitmapSuccess) {
553  result = CreateImage(width, height, 1);
554  memcpy(result->data, data, (width * height + 7) / 8);
555  XFree(data);
556  }
557 
558  return result;
559 }
560 #endif /* USE_XBM */
561 
563 #ifdef USE_ICONS
564 #define HASH_SIZE 16
565 ImageNode *CreateImageFromXImages(XImage *image, XImage *shape)
566 {
567  XColor colors[HASH_SIZE];
568  ImageNode *result;
569  unsigned char *dest;
570  int x, y;
571 
572  memset(colors, 0xFF, sizeof(colors));
573  result = CreateImage(image->width, image->height, 0);
574  dest = result->data;
575  for(y = 0; y < image->height; y++) {
576  for(x = 0; x < image->width; x++) {
577  const unsigned long pixel = XGetPixel(image, x, y);
578  *dest++ = (!shape || XGetPixel(shape, x, y)) ? 255 : 0;
579  if(image->depth == 1) {
580  const unsigned char value = pixel ? 0 : 255;
581  *dest++ = value;
582  *dest++ = value;
583  *dest++ = value;
584  } else{
585  const unsigned index = pixel % HASH_SIZE;
586  if(colors[index].pixel != pixel) {
587  colors[index].pixel = pixel;
588  JXQueryColor(display, rootColormap, &colors[index]);
589  }
590  *dest++ = (unsigned char)(colors[index].red >> 8);
591  *dest++ = (unsigned char)(colors[index].green >> 8);
592  *dest++ = (unsigned char)(colors[index].blue >> 8);
593  }
594  }
595  }
596 
597  return result;
598 }
599 #undef HASH_SIZE
600 #endif /* USE_ICONS */
601 
602 ImageNode *CreateImage(unsigned width, unsigned height, char bitmap)
603 {
604  unsigned image_size;
605  if(bitmap) {
606  image_size = (width * height + 7) / 8;
607  } else {
608  image_size = 4 * width * height;
609  }
610  ImageNode *image = Allocate(sizeof(ImageNode));
611  image->data = Allocate(image_size);
612  image->next = NULL;
613  image->bitmap = bitmap;
614  image->width = width;
615  image->height = height;
616 #ifdef USE_XRENDER
617  image->render = haveRender;
618 #endif
619  return image;
620 }
621 
623 void DestroyImage(ImageNode *image) {
624  while(image) {
625  ImageNode *next = image->next;
626  if(image->data) {
627  Release(image->data);
628  }
629  Release(image);
630  image = next;
631  }
632 }
633 
635 #ifdef USE_XPM
636 int AllocateColor(Display *d, Colormap cmap, char *name,
637  XColor *c, void *closure)
638 {
639  if(name) {
640  if(!JXParseColor(d, cmap, name, c)) {
641  return -1;
642  }
643  }
644 
645  GetColor(c);
646  return 1;
647 }
648 #endif /* USE_XPM */
649 
653 #ifdef USE_XPM
654 int FreeColors(Display *d, Colormap cmap, Pixel *pixels, int n,
655  void *closure)
656 {
657  return 1;
658 }
659 #endif /* USE_XPM */

joewing.net / Projects / JWM