1 /* Dia -- a diagram creation/manipulation program
2 * Copyright (C) 1998 Alexander Larsson
4 * export_png.c: export a diagram to a PNG file.
5 * Copyright (C) 2000 James Henstridge
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 #if defined(HAVE_LIBPNG) && defined(HAVE_LIBART)
35 #include "render_libart.h"
36 #include "dialibartrenderer.h"
38 #include "app_procs.h"
41 /* the dots per centimetre to render this diagram at */
42 /* this matches the setting `100%' setting in dia. */
45 /* the height of the band to use when rendering. Smaller bands mean
46 * rendering is slower, but less memory is used. Setting this to G_MAXINT
47 * should get the renderer to use one pass. */
48 #define BAND_HEIGHT 50
50 struct png_callback_data
{
53 gchar
*size
; /* for command line option --size */
56 /* Static data. When the dialog is not reentrant, you could have all data
57 be static. I don't like it that way, though:) I only hold static that
58 which pertains to the dialog itself (including the aspect ratio, as that
59 is used to connect the two entries). */
60 static GtkWidget
*export_png_dialog
;
61 static GtkSpinButton
*export_png_width_entry
, *export_png_height_entry
;
62 static GtkWidget
*export_png_okay_button
, *export_png_cancel_button
;
63 static real export_png_aspect_ratio
;
65 /* The heart of the png exporter.
66 Deals with a bit of dialog handling and all the rendering and writing.
67 The dialog is not used when dia is non-interactive (export mode)
70 export_png_ok(GtkButton
*button
, gpointer userdata
)
72 struct png_callback_data
*cbdata
= (struct png_callback_data
*)userdata
;
73 DiagramData
*data
= cbdata
->data
;
74 DiaRenderer
*renderer
;
75 DiaLibartRenderer
*la_renderer
;
76 Rectangle
*ext
= &data
->extents
;
78 guint32 width
, height
, band
, row
, i
;
80 guint32 imagewidth
= 0, imageheight
= 0;
81 long req_width
, req_height
;
90 width
= (guint32
) ((ext
->right
- ext
->left
) * DPCM
* data
->paper
.scaling
);
91 height
= (guint32
) ((ext
->bottom
- ext
->top
) * DPCM
* data
->paper
.scaling
);
93 if (app_is_interactive()) {
94 /* We don't want multiple clicks:) */
95 gtk_widget_hide(export_png_dialog
);
97 imagewidth
= gtk_spin_button_get_value_as_int(export_png_width_entry
);
98 imageheight
= gtk_spin_button_get_value_as_int(export_png_height_entry
);
100 if (cbdata
&& cbdata
->size
) {
101 float ratio
= (float) width
/(float) height
;
103 parse_size(cbdata
->size
, &req_width
, &req_height
);
104 if (req_width
&& !req_height
) {
105 imagewidth
= req_width
;
106 imageheight
= req_width
/ ratio
;
107 } else if (req_height
&& !req_width
) {
108 imagewidth
= req_height
* ratio
;
109 imageheight
= req_height
;
110 } else if (req_width
&& req_height
) {
111 imagewidth
= req_width
;
112 imageheight
= req_height
;
116 imageheight
= height
;
120 imagezoom
= ((real
)imageheight
/height
) * DPCM
* data
->paper
.scaling
;
122 /* we render in bands to try to keep memory consumption down ... */
123 band
= MIN(imageheight
, BAND_HEIGHT
);
124 band_height
= (real
)band
/ imagezoom
;
127 visible
.bottom
= MIN(visible
.bottom
,
128 visible
.top
+ band_height
);
130 renderer
= new_libart_renderer(dia_transform_new (&visible
, &imagezoom
), 0);
131 la_renderer
= DIA_LIBART_RENDERER (renderer
);
132 dia_renderer_set_size(renderer
, NULL
, imagewidth
, band
);
134 fp
= fopen(cbdata
->filename
, "wb");
136 message_error(_("Can't open output file %s: %s\n"), cbdata
->filename
, strerror(errno
));
140 png
= png_create_write_struct(PNG_LIBPNG_VER_STRING
,
144 message_error(_("Could not create PNG write structure"));
148 /* allocate/initialise the image information data */
149 info
= png_create_info_struct(png
);
152 png_destroy_write_struct(&png
, (png_infopp
)NULL
);
153 message_error(_("Could not create PNG header info structure"));
157 /* set error handling ... */
158 if (setjmp(png
->jmpbuf
)) {
160 png_destroy_write_struct(&png
, &info
);
161 message_error(_("Error occurred while writing PNG"));
164 /* the compiler said these may be clobbered by setjmp, so we set it again
166 if (app_is_interactive()) {
167 imagewidth
= gtk_spin_button_get_value_as_int(export_png_width_entry
);
168 imageheight
= gtk_spin_button_get_value_as_int(export_png_height_entry
);
170 if (cbdata
&& cbdata
->size
) {
171 float ratio
= (float) width
/(float) height
;
173 parse_size(cbdata
->size
, &req_width
, &req_height
);
174 if (req_width
&& !req_height
) {
175 imagewidth
= req_width
;
176 imageheight
= req_width
/ ratio
;
177 } else if (req_height
&& !req_width
) {
178 imagewidth
= req_height
* ratio
;
179 imageheight
= req_height
;
180 } else if (req_width
&& req_height
) {
181 imagewidth
= req_width
;
182 imageheight
= req_height
;
186 imageheight
= height
;
189 band
= MIN(imageheight
, BAND_HEIGHT
);
191 png_init_io(png
, fp
);
194 png_set_IHDR(png
, info
, imagewidth
, imageheight
, 8,
195 PNG_COLOR_TYPE_RGB
, PNG_INTERLACE_NONE
,
196 PNG_COMPRESSION_TYPE_BASE
, PNG_FILTER_TYPE_BASE
);
200 png_set_sBIT(png
, info
, &sig_bit
);
202 png_set_pHYs(png
, info
,
203 imagewidth
/width
*DPCM
*100,
204 imageheight
/height
*DPCM
*100,
205 PNG_RESOLUTION_METER
);
207 png_write_info(png
, info
);
208 png_set_shift(png
, &sig_bit
);
209 png_set_packing(png
);
211 row_ptr
= g_new(png_bytep
, band
);
213 for (row
= 0; row
< imageheight
; row
+= band
) {
215 for (i
= 0; i
< imagewidth
*band
; i
++) {
216 la_renderer
->rgb_buffer
[3*i
] = 0xff * data
->bg_color
.red
;
217 la_renderer
->rgb_buffer
[3*i
+1] = 0xff * data
->bg_color
.green
;
218 la_renderer
->rgb_buffer
[3*i
+2] = 0xff * data
->bg_color
.blue
;
220 data_render(data
, renderer
, &visible
, NULL
,NULL
);
221 /* write rows to png file */
222 for (i
= 0; i
< band
; i
++)
223 row_ptr
[i
] = la_renderer
->rgb_buffer
+ 3 * i
* imagewidth
;
224 png_write_rows(png
, row_ptr
, MIN(band
, imageheight
- row
));
226 visible
.top
+= band_height
;
227 visible
.bottom
+= band_height
;
230 png_write_end(png
, info
);
231 png_destroy_write_struct(&png
, &info
);
235 g_object_unref(renderer
);
236 if (app_is_interactive()) {
237 gtk_signal_disconnect_by_data(GTK_OBJECT(export_png_okay_button
),
239 gtk_signal_disconnect_by_data(GTK_OBJECT(export_png_cancel_button
),
242 g_free(cbdata
->filename
);
247 /* Stuff to do when cancelling:
248 disconnect signals (since the dialog persists)
253 export_png_cancel(GtkButton
*button
, gpointer userdata
) {
254 struct png_callback_data
*cbdata
= (struct png_callback_data
*)userdata
;
256 gtk_signal_disconnect_by_data(GTK_OBJECT(export_png_okay_button
), userdata
);
257 gtk_signal_disconnect_by_data(GTK_OBJECT(export_png_cancel_button
), userdata
);
259 gtk_widget_hide(export_png_dialog
);
260 g_free(cbdata
->filename
);
264 /* Adjust the aspect ratio */
266 export_png_ratio(GtkAdjustment
*limits
, gpointer userdata
)
268 /* This variable makes sure that we don't have a loopback effect. */
269 static gboolean in_progress
;
270 if (in_progress
) return;
272 if (userdata
== export_png_height_entry
) {
273 gtk_spin_button_set_value(GTK_SPIN_BUTTON(userdata
),
274 (int)((real
)gtk_spin_button_get_value_as_int(export_png_width_entry
))/export_png_aspect_ratio
);
276 gtk_spin_button_set_value(GTK_SPIN_BUTTON(userdata
),
277 (int)((real
)gtk_spin_button_get_value_as_int(export_png_height_entry
))*export_png_aspect_ratio
);
283 export_png(DiagramData
*data
, const gchar
*filename
,
284 const gchar
*diafilename
, void* user_data
)
286 /* Create the callback data. Can't be stack allocated, as the function
287 returns before the callback is called. Must be freed by the
289 struct png_callback_data
*cbdata
=
290 (struct png_callback_data
*) g_new0(struct png_callback_data
, 1);
291 Rectangle
*ext
= &data
->extents
;
292 guint32 width
, height
;
294 /* Note that this dialog, while not modal, is no reentrant, as it creates
295 a single dialog and uses that every time. Trying to do two exports at
296 the same time will lead to confusion.
299 if (export_png_dialog
== NULL
&& app_is_interactive()) {
300 /* Create a dialog */
301 export_png_dialog
= dialog_make(_("PNG Export Options"),
303 &export_png_okay_button
,
304 &export_png_cancel_button
);
305 /* Add two integer entries */
306 export_png_width_entry
=
307 dialog_add_spinbutton(export_png_dialog
, _("Image width:"),
309 export_png_height_entry
=
310 dialog_add_spinbutton(export_png_dialog
, _("Image height:"),
313 /* Make sure that the aspect ratio stays the same */
314 g_signal_connect(GTK_OBJECT(gtk_spin_button_get_adjustment(export_png_width_entry
)),
316 G_CALLBACK(export_png_ratio
), (gpointer
)export_png_height_entry
);
317 g_signal_connect(GTK_OBJECT(gtk_spin_button_get_adjustment(export_png_height_entry
)),
319 G_CALLBACK(export_png_ratio
), (gpointer
)export_png_width_entry
);
323 /* Store pertinent data in callback data structure */
325 cbdata
->filename
= g_strdup(filename
);
327 if (app_is_interactive()) {
328 /* Find the default size */
329 width
= (guint32
) ((ext
->right
- ext
->left
) * DPCM
* data
->paper
.scaling
);
330 height
= (guint32
) ((ext
->bottom
- ext
->top
) * DPCM
* data
->paper
.scaling
);
332 /* Store aspect ratio */
333 export_png_aspect_ratio
= ((real
)width
)/height
;
335 /* Set the default size */
336 gtk_spin_button_set_value(export_png_width_entry
, (float)width
);
337 /* This is set from the aspect ratio */
338 /* gtk_spin_button_set_value(export_png_height_entry, (float)height);*/
340 /* Set OK and Cancel buttons to call the relevant callbacks with cbdata */
341 g_signal_connect(GTK_OBJECT(export_png_okay_button
), "clicked",
342 G_CALLBACK(export_png_ok
), (gpointer
)cbdata
);
343 g_signal_connect(GTK_OBJECT(export_png_cancel_button
), "clicked",
344 G_CALLBACK(export_png_cancel
), (gpointer
)cbdata
);
346 /* Show the whole thing */
347 gtk_widget_show_all(export_png_dialog
);
349 cbdata
->size
= (gchar
*) user_data
;
350 export_png_ok(NULL
, cbdata
);
354 static const gchar
*extensions
[] = { "png", NULL
};
355 DiaExportFilter png_export_filter
= {
356 N_("Portable Network Graphics"),