gvloadimage_webp.c 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. /*************************************************************************
  2. * Copyright (c) 2011 AT&T Intellectual Property
  3. * All rights reserved. This program and the accompanying materials
  4. * are made available under the terms of the Eclipse Public License v1.0
  5. * which accompanies this distribution, and is available at
  6. * https://www.eclipse.org/legal/epl-v10.html
  7. *
  8. * Contributors: Details at https://graphviz.org
  9. *************************************************************************/
  10. #include "config.h"
  11. #include <stdbool.h>
  12. #include <stdio.h>
  13. #include <stdlib.h>
  14. #include <string.h>
  15. #include <util/prisize_t.h>
  16. #include <gvc/gvplugin_loadimage.h>
  17. #include <gvc/gvio.h>
  18. #ifdef HAVE_WEBP
  19. #ifdef HAVE_PANGOCAIRO
  20. #include <cairo.h>
  21. #include <webp/decode.h>
  22. static const char* const kStatusMessages[] = {
  23. "OK", "OUT_OF_MEMORY", "INVALID_PARAM", "BITSTREAM_ERROR",
  24. "UNSUPPORTED_FEATURE", "SUSPENDED", "USER_ABORT", "NOT_ENOUGH_DATA"
  25. };
  26. typedef enum {
  27. FORMAT_WEBP_CAIRO,
  28. } format_type;
  29. static void webp_freeimage(usershape_t *us)
  30. {
  31. cairo_surface_destroy(us->data);
  32. }
  33. static cairo_surface_t* webp_really_loadimage(const char *in_file, FILE* const in)
  34. {
  35. WebPDecoderConfig config;
  36. WebPDecBuffer* const output_buffer = &config.output;
  37. WebPBitstreamFeatures* const bitstream = &config.input;
  38. VP8StatusCode status = VP8_STATUS_OK;
  39. cairo_surface_t *surface = NULL; /* source surface */
  40. int ok;
  41. void* data = NULL;
  42. if (!WebPInitDecoderConfig(&config)) {
  43. fprintf(stderr, "Error: WebP library version mismatch!\n");
  44. return NULL;
  45. }
  46. fseek(in, 0, SEEK_END);
  47. long size = ftell(in);
  48. if (size < 0) {
  49. fprintf(stderr, "Error: WebP could not determine %s size\n", in_file);
  50. return NULL;
  51. }
  52. size_t data_size = (size_t)size;
  53. rewind(in);
  54. data = malloc(data_size);
  55. ok = data_size == 0 || (data != NULL && fread(data, data_size, 1, in) == 1);
  56. if (!ok) {
  57. fprintf(stderr, "Error: WebP could not read %" PRISIZE_T
  58. " bytes of data from %s\n", data_size, in_file);
  59. free(data);
  60. return NULL;
  61. }
  62. status = WebPGetFeatures(data, data_size, bitstream);
  63. if (status != VP8_STATUS_OK) {
  64. goto end;
  65. }
  66. output_buffer->colorspace = MODE_RGBA;
  67. status = WebPDecode(data, data_size, &config);
  68. /* FIXME - this is ugly */
  69. if (! bitstream->has_alpha) {
  70. int x, y;
  71. unsigned char *p, t;
  72. for (y = 0; y < output_buffer->height; y++) {
  73. p = output_buffer->u.RGBA.rgba + output_buffer->u.RGBA.stride * y;
  74. for (x = 0; x < output_buffer->width; x++) {
  75. t = p[0]; /* swap red/blue */
  76. p[0] = p[2];
  77. p[2] = t;
  78. p += 4;
  79. }
  80. }
  81. }
  82. end:
  83. free(data);
  84. ok = status == VP8_STATUS_OK;
  85. if (!ok) {
  86. fprintf(stderr, "Error: WebP decoding of %s failed.\n", in_file);
  87. fprintf(stderr, "Status: %d (%s)\n", status, kStatusMessages[status]);
  88. return NULL;
  89. }
  90. surface = cairo_image_surface_create_for_data (
  91. output_buffer->u.RGBA.rgba,
  92. CAIRO_FORMAT_ARGB32,
  93. output_buffer->width,
  94. output_buffer->height,
  95. output_buffer->u.RGBA.stride);
  96. return surface;
  97. }
  98. /* get image either from cached surface, or from freskly loaded surface */
  99. static cairo_surface_t* webp_loadimage(GVJ_t * job, usershape_t *us)
  100. {
  101. cairo_surface_t *surface = NULL; /* source surface */
  102. assert(job);
  103. assert(us);
  104. assert(us->name);
  105. if (us->data) {
  106. if (us->datafree == webp_freeimage)
  107. surface = us->data; /* use cached data */
  108. else {
  109. us->datafree(us); /* free incompatible cache data */
  110. us->datafree = NULL;
  111. us->data = NULL;
  112. }
  113. }
  114. if (!surface) { /* read file into cache */
  115. if (!gvusershape_file_access(us))
  116. return NULL;
  117. switch (us->type) {
  118. case FT_WEBP:
  119. if ((surface = webp_really_loadimage(us->name, us->f)))
  120. cairo_surface_reference(surface);
  121. break;
  122. default:
  123. surface = NULL;
  124. }
  125. if (surface) {
  126. us->data = surface;
  127. us->datafree = webp_freeimage;
  128. }
  129. gvusershape_file_release(us);
  130. }
  131. return surface;
  132. }
  133. /* paint image into required location in graph */
  134. static void webp_loadimage_cairo(GVJ_t * job, usershape_t *us, boxf b, bool filled)
  135. {
  136. (void)filled;
  137. cairo_t *cr = job->context; /* target context */
  138. cairo_surface_t *surface; /* source surface */
  139. surface = webp_loadimage(job, us);
  140. if (surface) {
  141. cairo_save(cr);
  142. cairo_translate(cr, b.LL.x, -b.UR.y);
  143. cairo_scale(cr, (b.UR.x - b.LL.x) / us->w, (b.UR.y - b.LL.y) / us->h);
  144. cairo_set_source_surface (cr, surface, 0, 0);
  145. cairo_paint (cr);
  146. cairo_restore(cr);
  147. }
  148. }
  149. static gvloadimage_engine_t engine_webp = {
  150. webp_loadimage_cairo
  151. };
  152. #endif
  153. #endif
  154. gvplugin_installed_t gvloadimage_webp_types[] = {
  155. #ifdef HAVE_WEBP
  156. #ifdef HAVE_PANGOCAIRO
  157. {FORMAT_WEBP_CAIRO, "webp:cairo", 1, &engine_webp, NULL},
  158. #endif
  159. #endif
  160. {0, NULL, 0, NULL, NULL}
  161. };