Graphviz 13.0.0~dev.20250608.0154
Loading...
Searching...
No Matches
gvdevice_gd.c
Go to the documentation of this file.
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
11#include "config.h"
12#include "gdioctx_wrapper.h"
13
14#include <assert.h>
15#include <gvc/gvplugin_device.h>
16#include <gvc/gvio.h>
17#include <limits.h>
18
19#include <gd.h>
20#include <stdbool.h>
21#include <stddef.h>
22#include <string.h>
23
24int gvdevice_gd_putBuf (gdIOCtx *context, const void *buffer, int len)
25{
26 gd_context_t *gd_context = get_containing_context(context);
27 assert(len >= 0);
28 const size_t result = gvwrite(gd_context->job, buffer, (size_t)len);
29 assert(result <= (size_t)len);
30 return (int)result;
31}
32
33void gvdevice_gd_putC (gdIOCtx *context, int C)
34{
35 gd_context_t *gd_context = get_containing_context(context);
36 char c = (char)C;
37
38 gvwrite(gd_context->job, &c, 1);
39}
40
41#ifdef HAVE_PANGOCAIRO
42enum {
50};
51
53static unsigned d2u(double v) {
54 if (v > UINT_MAX) {
55 return UINT_MAX;
56 }
57 if (v < 0) {
58 return 0;
59 }
60 const double rounded = round(v);
61 return (unsigned)rounded;
62}
63
64static void gd_format(GVJ_t * job)
65{
66 gdImagePtr im;
67 unsigned int x, y;
68 const unsigned char *data = job->imagedata;
69 unsigned int width = job->width;
70 unsigned int height = job->height;
71 gd_context_t gd_context;
72 memset(&gd_context, 0, sizeof(gd_context));
73
74 gd_context.ctx.putBuf = gvdevice_gd_putBuf;
75 gd_context.ctx.putC = gvdevice_gd_putC;
76 gd_context.job = job;
77
78 assert(width <= INT_MAX);
79 assert(height <= INT_MAX);
80 im = gdImageCreateTrueColor((int)width, (int)height);
81 switch (job->device.id) {
82#ifdef HAVE_GD_PNG
83 case FORMAT_PNG:
84 for (y = 0; y < height; y++) {
85 for (x = 0; x < width; x++) {
86 const int r = *data++;
87 const int g = *data++;
88 const int b = *data++;
89 // gd’s alpha is 7-bit, so scale down ÷2 from our 8-bit
90 const int alpha = *data++ >> 1;
91 const int color = r | (g << 8) | (b << 16) | ((0x7f - alpha) << 24);
92 im->tpixels[y][x] = color;
93 }
94 }
95 gdImageResolutionX(im) = d2u(job->dpi.x);
96 gdImageResolutionY(im) = d2u(job->dpi.y);
97 break;
98#endif
99 default:
100/* pick an off-white color, so that transparent backgrounds look white in jpgs */
101#define TRANSPARENT 0x7ffffffe
102
103 gdImageColorTransparent(im, TRANSPARENT);
104 gdImageAlphaBlending(im, false);
105 for (y = 0; y < height; y++) {
106 for (x = 0; x < width; x++) {
107 const int r = *data++;
108 const int g = *data++;
109 const int b = *data++;
110 // gd’s alpha is 7-bit, so scale down ÷2 from our 8-bit
111 const int alpha = *data++ >> 1;
112 const int color = r | (g << 8) | (b << 16) | ((0x7f - alpha) << 24);
113 if (alpha >= 0x20)
114 /* if not > 75% transparent */
115 im->tpixels[y][x] = color;
116 else
117 im->tpixels[y][x] = TRANSPARENT;
118 }
119 }
120 break;
121 }
122
123 switch (job->device.id) {
124#ifdef HAVE_GD_GIF
125 case FORMAT_GIF:
126 gdImageTrueColorToPalette(im, 0, 256);
127 gdImageGifCtx(im, &gd_context.ctx);
128 break;
129#endif
130
131#ifdef HAVE_GD_JPEG
132 case FORMAT_JPEG:
133 /*
134 * Write IM to OUTFILE as a JFIF-formatted JPEG image, using
135 * quality JPEG_QUALITY. If JPEG_QUALITY is in the range
136 * 0-100, increasing values represent higher quality but also
137 * larger image size. If JPEG_QUALITY is negative, the
138 * IJG JPEG library's default quality is used (which should
139 * be near optimal for many applications). See the IJG JPEG
140 * library documentation for more details.
141 */
142#define JPEG_QUALITY -1
143 gdImageJpegCtx(im, &gd_context.ctx, JPEG_QUALITY);
144 break;
145#endif
146
147#ifdef HAVE_GD_PNG
148 case FORMAT_PNG:
149 gdImageTrueColorToPalette(im, 0, 256);
150 gdImagePngCtx(im, &gd_context.ctx);
151 break;
152#endif
153
154 case FORMAT_GD:
155 gdImageGd(im, job->output_file);
156 break;
157
158 case FORMAT_GD2:
159#define GD2_CHUNKSIZE 128
160#define GD2_RAW 1
161#define GD2_COMPRESSED 2
162 gdImageGd2(im, job->output_file, GD2_CHUNKSIZE, GD2_COMPRESSED);
163 break;
164
165#ifdef HAVE_GD_GIF
166 case FORMAT_WBMP:
167 {
168 /* Use black for the foreground color for the B&W wbmp image. */
169 int black = gdImageColorResolveAlpha(im, 0, 0, 0, gdAlphaOpaque);
170 gdImageWBMPCtx(im, black, &gd_context.ctx);
171 }
172 break;
173#endif
174
175 break;
176 default:
177 break;
178 }
179
180 gdImageDestroy(im);
181}
182
183static gvdevice_engine_t gd_engine = {
184 NULL, /* gd_initialize */
185 gd_format,
186 NULL, /* gd_finalize */
187};
188
189static gvdevice_features_t device_features_gd = {
191 | GVDEVICE_DOES_TRUECOLOR,/* flags */
192 {0.,0.}, /* default margin - points */
193 {0.,0.}, /* default page width, height - points */
194 {96.,96.}, /* dpi */
195};
196
197static gvdevice_features_t device_features_gd_no_writer = {
200 | GVDEVICE_DOES_TRUECOLOR,/* flags */
201 {0.,0.}, /* default margin - points */
202 {0.,0.}, /* default page width, height - points */
203 {96.,96.}, /* dpi */
204};
205#endif
206
208#ifdef HAVE_PANGOCAIRO
209
210#ifdef HAVE_GD_GIF
211 {FORMAT_GIF, "gif:cairo", 10, &gd_engine, &device_features_gd},
212 {FORMAT_WBMP, "wbmp:cairo", 5, &gd_engine, &device_features_gd},
213#endif
214
215#ifdef HAVE_GD_JPEG
216 {FORMAT_JPEG, "jpe:cairo", 5, &gd_engine, &device_features_gd},
217 {FORMAT_JPEG, "jpeg:cairo", 5, &gd_engine, &device_features_gd},
218 {FORMAT_JPEG, "jpg:cairo", 5, &gd_engine, &device_features_gd},
219#endif
220
221#ifdef HAVE_GD_PNG
222 {FORMAT_PNG, "png:cairo", 5, &gd_engine, &device_features_gd},
223#endif
224
225 {FORMAT_GD, "gd:cairo", 5, &gd_engine, &device_features_gd_no_writer},
226 {FORMAT_GD2, "gd2:cairo", 5, &gd_engine, &device_features_gd_no_writer},
227
228#endif
229 {0, NULL, 0, NULL, NULL}
230};
static gd_context_t * get_containing_context(gdIOCtx *ctx)
static double len(glCompPoint p)
Definition glutils.c:150
node NULL
Definition grammar.y:180
#define GVDEVICE_NO_WRITER
Definition gvcjob.h:93
#define GVDEVICE_DOES_TRUECOLOR
Definition gvcjob.h:90
#define GVDEVICE_BINARY_FORMAT
Definition gvcjob.h:91
static void color(Agraph_t *g)
Definition gvcolor.c:129
size_t gvwrite(GVJ_t *job, const char *s, size_t len)
Definition gvdevice.c:182
int gvdevice_gd_putBuf(gdIOCtx *context, const void *buffer, int len)
Definition gvdevice_gd.c:24
void gvdevice_gd_putC(gdIOCtx *context, int C)
Definition gvdevice_gd.c:33
gvplugin_installed_t gvdevice_gd_types[]
static const char black[]
@ FORMAT_JPEG
Definition gvrender_gd.c:37
@ FORMAT_GD
Definition gvrender_gd.c:40
@ FORMAT_GD2
Definition gvrender_gd.c:41
@ FORMAT_WBMP
Definition gvrender_gd.c:39
@ FORMAT_GIF
Definition gvrender_gd.c:36
@ FORMAT_XBM
Definition gvrender_gd.c:42
@ FORMAT_PNG
Definition gvrender_gd.c:38
#define C
Definition pack.c:30
#define alpha
Definition shapes.c:4058
pointf dpi
Definition gvcjob.h:325
gvplugin_active_device_t device
Definition gvcjob.h:286
unsigned char * imagedata
location of imagedata
Definition gvcjob.h:297
FILE * output_file
Definition gvcjob.h:277
unsigned int width
Definition gvcjob.h:327
unsigned int height
Definition gvcjob.h:328
Definition legal.c:50
ingroup plugin_api
Definition gvplugin.h:35
double x
Definition geom.h:29
double y
Definition geom.h:29