gvdevice_gd.c 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  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 "gdioctx_wrapper.h"
  12. #include <assert.h>
  13. #include <gvc/gvplugin_device.h>
  14. #include <gvc/gvio.h>
  15. #include <limits.h>
  16. #include <gd.h>
  17. #include <stdbool.h>
  18. #include <stddef.h>
  19. #include <string.h>
  20. int gvdevice_gd_putBuf (gdIOCtx *context, const void *buffer, int len)
  21. {
  22. gd_context_t *gd_context = get_containing_context(context);
  23. assert(len >= 0);
  24. const size_t result = gvwrite(gd_context->job, buffer, (size_t)len);
  25. assert(result <= (size_t)len);
  26. return (int)result;
  27. }
  28. void gvdevice_gd_putC (gdIOCtx *context, int C)
  29. {
  30. gd_context_t *gd_context = get_containing_context(context);
  31. char c = (char)C;
  32. gvwrite(gd_context->job, &c, 1);
  33. }
  34. #ifdef HAVE_PANGOCAIRO
  35. typedef enum {
  36. FORMAT_GIF,
  37. FORMAT_JPEG,
  38. FORMAT_PNG,
  39. FORMAT_WBMP,
  40. FORMAT_GD,
  41. FORMAT_GD2,
  42. FORMAT_XBM,
  43. } format_type;
  44. static void gd_format(GVJ_t * job)
  45. {
  46. gdImagePtr im;
  47. unsigned int x, y;
  48. const unsigned char *data = (unsigned char *)job->imagedata;
  49. unsigned int width = job->width;
  50. unsigned int height = job->height;
  51. gd_context_t gd_context;
  52. memset(&gd_context, 0, sizeof(gd_context));
  53. gd_context.ctx.putBuf = gvdevice_gd_putBuf;
  54. gd_context.ctx.putC = gvdevice_gd_putC;
  55. gd_context.job = job;
  56. assert(width <= INT_MAX);
  57. assert(height <= INT_MAX);
  58. im = gdImageCreateTrueColor((int)width, (int)height);
  59. switch (job->device.id) {
  60. #ifdef HAVE_GD_PNG
  61. case FORMAT_PNG:
  62. for (y = 0; y < height; y++) {
  63. for (x = 0; x < width; x++) {
  64. const int r = *data++;
  65. const int g = *data++;
  66. const int b = *data++;
  67. // gd’s alpha is 7-bit, so scale down ÷2 from our 8-bit
  68. const int alpha = *data++ >> 1;
  69. const int color = r | (g << 8) | (b << 16) | ((0x7f - alpha) << 24);
  70. im->tpixels[y][x] = color;
  71. }
  72. }
  73. break;
  74. #endif
  75. default:
  76. /* pick an off-white color, so that transparent backgrounds look white in jpgs */
  77. #define TRANSPARENT 0x7ffffffe
  78. gdImageColorTransparent(im, TRANSPARENT);
  79. gdImageAlphaBlending(im, false);
  80. for (y = 0; y < height; y++) {
  81. for (x = 0; x < width; x++) {
  82. const int r = *data++;
  83. const int g = *data++;
  84. const int b = *data++;
  85. // gd’s alpha is 7-bit, so scale down ÷2 from our 8-bit
  86. const int alpha = *data++ >> 1;
  87. const int color = r | (g << 8) | (b << 16) | ((0x7f - alpha) << 24);
  88. if (alpha >= 0x20)
  89. /* if not > 75% transparent */
  90. im->tpixels[y][x] = color;
  91. else
  92. im->tpixels[y][x] = TRANSPARENT;
  93. }
  94. }
  95. break;
  96. }
  97. switch (job->device.id) {
  98. #ifdef HAVE_GD_GIF
  99. case FORMAT_GIF:
  100. gdImageTrueColorToPalette(im, 0, 256);
  101. gdImageGifCtx(im, &gd_context.ctx);
  102. break;
  103. #endif
  104. #ifdef HAVE_GD_JPEG
  105. case FORMAT_JPEG:
  106. /*
  107. * Write IM to OUTFILE as a JFIF-formatted JPEG image, using
  108. * quality JPEG_QUALITY. If JPEG_QUALITY is in the range
  109. * 0-100, increasing values represent higher quality but also
  110. * larger image size. If JPEG_QUALITY is negative, the
  111. * IJG JPEG library's default quality is used (which should
  112. * be near optimal for many applications). See the IJG JPEG
  113. * library documentation for more details.
  114. */
  115. #define JPEG_QUALITY -1
  116. gdImageJpegCtx(im, &gd_context.ctx, JPEG_QUALITY);
  117. break;
  118. #endif
  119. #ifdef HAVE_GD_PNG
  120. case FORMAT_PNG:
  121. gdImageTrueColorToPalette(im, 0, 256);
  122. gdImagePngCtx(im, &gd_context.ctx);
  123. break;
  124. #endif
  125. case FORMAT_GD:
  126. gdImageGd(im, job->output_file);
  127. break;
  128. case FORMAT_GD2:
  129. #define GD2_CHUNKSIZE 128
  130. #define GD2_RAW 1
  131. #define GD2_COMPRESSED 2
  132. gdImageGd2(im, job->output_file, GD2_CHUNKSIZE, GD2_COMPRESSED);
  133. break;
  134. #ifdef HAVE_GD_GIF
  135. case FORMAT_WBMP:
  136. {
  137. /* Use black for the foreground color for the B&W wbmp image. */
  138. int black = gdImageColorResolveAlpha(im, 0, 0, 0, gdAlphaOpaque);
  139. gdImageWBMPCtx(im, black, &gd_context.ctx);
  140. }
  141. break;
  142. #endif
  143. break;
  144. default:
  145. break;
  146. }
  147. gdImageDestroy(im);
  148. }
  149. static gvdevice_engine_t gd_engine = {
  150. NULL, /* gd_initialize */
  151. gd_format,
  152. NULL, /* gd_finalize */
  153. };
  154. static gvdevice_features_t device_features_gd = {
  155. GVDEVICE_BINARY_FORMAT
  156. | GVDEVICE_DOES_TRUECOLOR,/* flags */
  157. {0.,0.}, /* default margin - points */
  158. {0.,0.}, /* default page width, height - points */
  159. {96.,96.}, /* dpi */
  160. };
  161. static gvdevice_features_t device_features_gd_no_writer = {
  162. GVDEVICE_BINARY_FORMAT
  163. | GVDEVICE_NO_WRITER
  164. | GVDEVICE_DOES_TRUECOLOR,/* flags */
  165. {0.,0.}, /* default margin - points */
  166. {0.,0.}, /* default page width, height - points */
  167. {96.,96.}, /* dpi */
  168. };
  169. #endif
  170. gvplugin_installed_t gvdevice_gd_types[] = {
  171. #ifdef HAVE_PANGOCAIRO
  172. #ifdef HAVE_GD_GIF
  173. {FORMAT_GIF, "gif:cairo", 10, &gd_engine, &device_features_gd},
  174. {FORMAT_WBMP, "wbmp:cairo", 5, &gd_engine, &device_features_gd},
  175. #endif
  176. #ifdef HAVE_GD_JPEG
  177. {FORMAT_JPEG, "jpe:cairo", 5, &gd_engine, &device_features_gd},
  178. {FORMAT_JPEG, "jpeg:cairo", 5, &gd_engine, &device_features_gd},
  179. {FORMAT_JPEG, "jpg:cairo", 5, &gd_engine, &device_features_gd},
  180. #endif
  181. #ifdef HAVE_GD_PNG
  182. {FORMAT_PNG, "png:cairo", 5, &gd_engine, &device_features_gd},
  183. #endif
  184. {FORMAT_GD, "gd:cairo", 5, &gd_engine, &device_features_gd_no_writer},
  185. {FORMAT_GD2, "gd2:cairo", 5, &gd_engine, &device_features_gd_no_writer},
  186. #endif
  187. {0, NULL, 0, NULL, NULL}
  188. };