gvloadimage_gd.c 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  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 <assert.h>
  12. #include <stdbool.h>
  13. #include <stdlib.h>
  14. #include <string.h>
  15. #include <util/alloc.h>
  16. #ifdef HAVE_PANGOCAIRO
  17. #include <cairo.h>
  18. #endif
  19. #include <gvc/gvplugin_loadimage.h>
  20. #include <gvc/gvio.h>
  21. #include <gd.h>
  22. typedef enum {
  23. FORMAT_PNG_GD, FORMAT_GIF_GD, FORMAT_JPG_GD, FORMAT_GD_GD, FORMAT_GD2_GD, FORMAT_XPM_GD, FORMAT_WBMP_GD, FORMAT_XBM_GD,
  24. FORMAT_PNG_PS, FORMAT_GIF_PS, FORMAT_JPG_PS, FORMAT_GD_PS, FORMAT_GD2_PS, FORMAT_XPM_PS, FORMAT_WBMP_PS, FORMAT_XBM_PS,
  25. FORMAT_PNG_CAIRO, FORMAT_GIF_CAIRO, FORMAT_JPG_CAIRO, FORMAT_GD_CAIRO, FORMAT_GD2_CAIRO, FORMAT_XPM_CAIRO, FORMAT_WBMP_CAIRO, FORMAT_XBM_CAIRO,
  26. } format_type;
  27. static void gd_freeimage(usershape_t *us)
  28. {
  29. gdImageDestroy(us->data);
  30. }
  31. static gdImagePtr gd_loadimage(GVJ_t * job, usershape_t *us)
  32. {
  33. assert(job);
  34. assert(us);
  35. assert(us->name);
  36. if (us->data) {
  37. if (us->datafree != gd_freeimage) {
  38. us->datafree(us); /* free incompatible cache data */
  39. us->data = NULL;
  40. us->datafree = NULL;
  41. }
  42. }
  43. if (!us->data) { /* read file into cache */
  44. if (!gvusershape_file_access(us))
  45. return NULL;
  46. switch (us->type) {
  47. #ifdef HAVE_GD_PNG
  48. case FT_PNG:
  49. us->data = gdImageCreateFromPng(us->f);
  50. break;
  51. #endif
  52. #ifdef HAVE_GD_GIF
  53. case FT_GIF:
  54. us->data = gdImageCreateFromGif(us->f);
  55. break;
  56. #endif
  57. #ifdef HAVE_GD_JPEG
  58. case FT_JPEG:
  59. us->data = gdImageCreateFromJpeg(us->f);
  60. break;
  61. #endif
  62. default:
  63. break;
  64. }
  65. if (us->data)
  66. us->datafree = gd_freeimage;
  67. gvusershape_file_release(us);
  68. }
  69. return us->data;
  70. }
  71. static gdImagePtr gd_rotateimage(gdImagePtr im, int rotation)
  72. {
  73. gdImagePtr im2 = gdImageCreate(im->sy, im->sx);
  74. gdImageCopyRotated(im2, im, im2->sx / 2., im2->sy / 2.,
  75. 0, 0, im->sx, im->sy, rotation);
  76. gdImageDestroy(im);
  77. return im2;
  78. }
  79. static void gd_loadimage_gd(GVJ_t * job, usershape_t *us, boxf b, bool filled)
  80. {
  81. (void)filled;
  82. gdImagePtr im2, im = job->context;
  83. if ((im2 = gd_loadimage(job, us))) {
  84. if (job->rotation)
  85. im2 = gd_rotateimage(im2, job->rotation);
  86. gdImageCopyResized(im, im2, ROUND(b.LL.x), ROUND(b.LL.y), 0, 0,
  87. ROUND(b.UR.x - b.LL.x), ROUND(b.UR.y - b.LL.y), im2->sx, im2->sy);
  88. }
  89. }
  90. #ifdef HAVE_PANGOCAIRO
  91. static void gd_loadimage_cairo(GVJ_t * job, usershape_t *us, boxf b, bool filled)
  92. {
  93. (void)filled;
  94. cairo_t *cr = job->context; /* target context */
  95. int x, y, width, height;
  96. cairo_surface_t *surface; /* source surface */
  97. gdImagePtr im;
  98. if ((im = gd_loadimage(job, us))) {
  99. width = im->sx;
  100. height = im->sy;
  101. const int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32,
  102. width);
  103. assert(stride >= 0);
  104. assert(height >= 0);
  105. unsigned char *data = gv_calloc((size_t)stride, (size_t)height);
  106. surface = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_ARGB32,
  107. width, height, stride);
  108. if (im->trueColor) {
  109. if (im->saveAlphaFlag) {
  110. for (y = 0; y < height; y++) {
  111. for (x = 0; x < width; x++) {
  112. const int px = gdImageTrueColorPixel(im, x, y);
  113. *data++ = (unsigned char)gdTrueColorGetBlue(px);
  114. *data++ = (unsigned char)gdTrueColorGetGreen(px);
  115. *data++ = (unsigned char)gdTrueColorGetRed(px);
  116. // gd’s alpha is 7-bit, so scale up ×2 to our 8-bit
  117. *data++ = (unsigned char)((0x7F - gdTrueColorGetAlpha(px)) << 1);
  118. }
  119. }
  120. }
  121. else {
  122. for (y = 0; y < height; y++) {
  123. for (x = 0; x < width; x++) {
  124. const int px = gdImageTrueColorPixel(im, x, y);
  125. *data++ = (unsigned char)gdTrueColorGetBlue(px);
  126. *data++ = (unsigned char)gdTrueColorGetGreen(px);
  127. *data++ = (unsigned char)gdTrueColorGetRed(px);
  128. *data++ = 0xFF;
  129. }
  130. }
  131. }
  132. }
  133. else {
  134. for (y = 0; y < height; y++) {
  135. for (x = 0; x < width; x++) {
  136. const int px = gdImagePalettePixel(im, x, y);
  137. *data++ = (unsigned char)im->blue[px];
  138. *data++ = (unsigned char)im->green[px];
  139. *data++ = (unsigned char)im->red[px];
  140. *data++ = px == im->transparent ? 0x00 : 0xff;
  141. }
  142. }
  143. }
  144. cairo_save(cr);
  145. cairo_translate(cr, b.LL.x, -b.UR.y);
  146. cairo_scale(cr, (b.UR.x - b.LL.x) / us->w, (b.UR.y - b.LL.y) / us->h);
  147. cairo_set_source_surface (cr, surface, 0, 0);
  148. cairo_paint (cr);
  149. cairo_restore(cr);
  150. cairo_surface_destroy(surface);
  151. free(data);
  152. }
  153. }
  154. #endif
  155. static void gd_loadimage_ps(GVJ_t * job, usershape_t *us, boxf b, bool filled)
  156. {
  157. (void)filled;
  158. gdImagePtr im = NULL;
  159. int X, Y, x, y, px;
  160. if ((im = gd_loadimage(job, us))) {
  161. X = im->sx;
  162. Y = im->sy;
  163. gvputs(job, "save\n");
  164. /* define image data as string array (one per raster line) */
  165. gvputs(job, "/myctr 0 def\n");
  166. gvputs(job, "/myarray [\n");
  167. if (im->trueColor) {
  168. for (y = 0; y < Y; y++) {
  169. gvputs(job, "<");
  170. for (x = 0; x < X; x++) {
  171. px = gdImageTrueColorPixel(im, x, y);
  172. gvprintf(job, "%02x%02x%02x",
  173. gdTrueColorGetRed(px),
  174. gdTrueColorGetGreen(px),
  175. gdTrueColorGetBlue(px));
  176. }
  177. gvputs(job, ">\n");
  178. }
  179. }
  180. else {
  181. for (y = 0; y < Y; y++) {
  182. gvputs(job, "<");
  183. for (x = 0; x < X; x++) {
  184. px = gdImagePalettePixel(im, x, y);
  185. gvprintf(job, "%02x%02x%02x",
  186. im->red[px],
  187. im->green[px],
  188. im->blue[px]);
  189. }
  190. gvputs(job, ">\n");
  191. }
  192. }
  193. gvputs(job, "] def\n");
  194. gvputs(job,"/myproc { myarray myctr get /myctr myctr 1 add def } def\n");
  195. /* this sets the position of the image */
  196. gvprintf(job, "%g %g translate\n",
  197. b.LL.x + (b.UR.x - b.LL.x) * (1. - (job->dpi.x) / 96.) / 2.,
  198. b.LL.y + (b.UR.y - b.LL.y) * (1. - (job->dpi.y) / 96.) / 2.);
  199. /* this sets the rendered size to fit the box */
  200. gvprintf(job,"%g %g scale\n",
  201. (b.UR.x - b.LL.x) * (job->dpi.x) / 96.,
  202. (b.UR.y - b.LL.y) * (job->dpi.y) / 96.);
  203. /* xsize ysize bits-per-sample [matrix] */
  204. gvprintf(job, "%d %d 8 [%d 0 0 %d 0 %d]\n", X, Y, X, -Y, Y);
  205. gvputs(job, "{myproc} false 3 colorimage\n");
  206. gvputs(job, "restore\n");
  207. }
  208. }
  209. static gvloadimage_engine_t engine = {
  210. gd_loadimage_gd
  211. };
  212. static gvloadimage_engine_t engine_ps = {
  213. gd_loadimage_ps
  214. };
  215. #ifdef HAVE_PANGOCAIRO
  216. static gvloadimage_engine_t engine_cairo = {
  217. gd_loadimage_cairo
  218. };
  219. #endif
  220. gvplugin_installed_t gvloadimage_gd_types[] = {
  221. {FORMAT_GD_GD, "gd:gd", 1, &engine, NULL},
  222. {FORMAT_GD2_GD, "gd2:gd", 1, &engine, NULL},
  223. #ifdef HAVE_GD_GIF
  224. {FORMAT_GIF_GD, "gif:gd", 1, &engine, NULL},
  225. #endif
  226. #ifdef HAVE_GD_JPEG
  227. {FORMAT_JPG_GD, "jpeg:gd", 1, &engine, NULL},
  228. {FORMAT_JPG_GD, "jpe:gd", 1, &engine, NULL},
  229. {FORMAT_JPG_GD, "jpg:gd", 1, &engine, NULL},
  230. #endif
  231. #ifdef HAVE_GD_PNG
  232. {FORMAT_PNG_GD, "png:gd", 1, &engine, NULL},
  233. #endif
  234. #ifdef HAVE_GD_WBMP
  235. {FORMAT_WBMP_GD, "wbmp:gd", 1, &engine, NULL},
  236. #endif
  237. #ifdef HAVE_GD_XPM
  238. {FORMAT_XBM_GD, "xbm:gd", 1, &engine, NULL},
  239. #endif
  240. {FORMAT_GD_PS, "gd:ps", 1, &engine_ps, NULL},
  241. {FORMAT_GD_PS, "gd:lasi", 1, &engine_ps, NULL},
  242. {FORMAT_GD2_PS, "gd2:ps", 1, &engine_ps, NULL},
  243. {FORMAT_GD2_PS, "gd2:lasi", 1, &engine_ps, NULL},
  244. #ifdef HAVE_GD_GIF
  245. {FORMAT_GIF_PS, "gif:ps", 1, &engine_ps, NULL},
  246. {FORMAT_GIF_PS, "gif:lasi", 1, &engine_ps, NULL},
  247. #endif
  248. #ifdef HAVE_GD_JPEG
  249. {FORMAT_JPG_PS, "jpeg:ps", 1, &engine_ps, NULL},
  250. {FORMAT_JPG_PS, "jpg:ps", 1, &engine_ps, NULL},
  251. {FORMAT_JPG_PS, "jpe:ps", 1, &engine_ps, NULL},
  252. {FORMAT_JPG_PS, "jpeg:lasi", 1, &engine_ps, NULL},
  253. {FORMAT_JPG_PS, "jpg:lasi", 1, &engine_ps, NULL},
  254. {FORMAT_JPG_PS, "jpe:lasi", 1, &engine_ps, NULL},
  255. #endif
  256. #ifdef HAVE_GD_PNG
  257. {FORMAT_PNG_PS, "png:ps", 1, &engine_ps, NULL},
  258. {FORMAT_PNG_PS, "png:lasi", 1, &engine_ps, NULL},
  259. #endif
  260. #ifdef HAVE_GD_WBMP
  261. {FORMAT_WBMP_PS, "wbmp:ps", 1, &engine_ps, NULL},
  262. {FORMAT_WBMP_PS, "wbmp:lasi", 1, &engine_ps, NULL},
  263. #endif
  264. #ifdef HAVE_GD_XPM
  265. {FORMAT_XBM_PS, "xbm:ps", 1, &engine_ps, NULL},
  266. {FORMAT_XBM_PS, "xbm:lasi", 1, &engine_ps, NULL},
  267. #endif
  268. #ifdef HAVE_PANGOCAIRO
  269. {FORMAT_GD_CAIRO, "gd:cairo", 1, &engine_cairo, NULL},
  270. {FORMAT_GD2_CAIRO, "gd2:cairo", 1, &engine_cairo, NULL},
  271. #ifdef HAVE_GD_GIF
  272. {FORMAT_GIF_CAIRO, "gif:cairo", 1, &engine_cairo, NULL},
  273. #endif
  274. #ifdef HAVE_GD_JPEG
  275. {FORMAT_JPG_CAIRO, "jpeg:cairo", 1, &engine_cairo, NULL},
  276. {FORMAT_JPG_CAIRO, "jpg:cairo", 1, &engine_cairo, NULL},
  277. {FORMAT_JPG_CAIRO, "jpe:cairo", 1, &engine_cairo, NULL},
  278. #endif
  279. #ifdef HAVE_GD_PNG
  280. {FORMAT_PNG_CAIRO, "png:cairo", -1, &engine_cairo, NULL},
  281. #endif
  282. #ifdef HAVE_GD_WBMP
  283. {FORMAT_WBMP_CAIRO, "wbmp:cairo", 1, &engine_cairo, NULL},
  284. #endif
  285. #ifdef HAVE_GD_XPM
  286. {FORMAT_XBM_CAIRO, "xbm:cairo", 1, &engine_cairo, NULL},
  287. #endif
  288. #endif /* HAVE_PANGOCAIRO */
  289. {0, NULL, 0, NULL, NULL}
  290. };