1 /* -*- Mode: C; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 3 -*- */
2
3 /*
4 * PNG image saver plugin for GImageView
5 * Copyright (C) 2001 Takuro Ashie
6 *
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.
11 *
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.
16 *
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.
20 *
21 * $Id: png_saver.c,v 1.9 2003/07/13 13:20:18 makeinu Exp $
22 */
23
24 #include <stdio.h>
25 #include <png.h>
26
27 #include "gimv_image.h"
28
29 #include "gimv_plugin.h"
30 #include "gimv_image_saver.h"
31
32 static gboolean save_png (GimvImageSaver *saver,
33 GimvImage *image,
34 const gchar *filename,
35 const gchar *format);
36
37 static GimvImageSaverPlugin plugin_impl[] =
38 {
39 {GIMV_IMAGE_SAVER_IF_VERSION, "png", save_png, NULL},
40 };
41
42 GIMV_PLUGIN_GET_IMPL(plugin_impl, GIMV_PLUGIN_IMAGE_SAVER)
43
44 GimvPluginInfo gimv_plugin_info =
45 {
46 if_version: GIMV_PLUGIN_IF_VERSION,
47 name: N_("PNG Image Saver"),
48 version: "0.2.0",
49 author: N_("Takuro Ashie"),
50 description: NULL,
51 get_implement: gimv_plugin_get_impl,
52 get_mime_type: NULL,
53 get_prefs_ui: NULL,
54 };
55
56
57 static png_text *
create_png_text(GimvImageSaver * saver,const gchar * filename,gint * n_text)58 create_png_text (GimvImageSaver *saver,
59 const gchar *filename,
60 gint *n_text)
61 {
62 png_text *text;
63 gint i;
64
65 g_return_val_if_fail (n_text, NULL);
66
67 *n_text = gimv_image_saver_get_n_comments (saver) + 2;
68
69 text = g_new0 (png_text, *n_text);
70
71 text[0].key = "Title";
72 text[0].text = (gchar *) filename;
73 text[0].compression = PNG_TEXT_COMPRESSION_NONE;
74 text[1].key = "Software";
75 text[1].text = GIMV_PROG_NAME;
76 text[1].compression = PNG_TEXT_COMPRESSION_NONE;
77
78 for (i = 2; i < *n_text; i++) {
79 const gchar *key, *value;
80 if (gimv_image_saver_get_comment (saver, i - 2, &key, &value)) {
81 text[i - 2].key = (gchar *) key;
82 text[i - 2].text = (gchar *) value;
83 text[i - 2].compression = PNG_TEXT_COMPRESSION_NONE;
84 } else {
85 g_warning ("invalid saver comment length!");
86 *n_text = i;
87 break;
88 }
89 }
90
91 return text;
92 }
93
94
95 static gboolean
save_png(GimvImageSaver * saver,GimvImage * image,const gchar * filename,const gchar * format)96 save_png (GimvImageSaver *saver,
97 GimvImage *image,
98 const gchar *filename,
99 const gchar *format)
100 {
101 FILE *handle;
102 gchar *buffer;
103 gboolean has_alpha;
104 gint width, height, depth, rowstride;
105 guchar *pixels;
106 png_structp png_ptr;
107 png_infop info_ptr;
108 png_text *text = NULL;
109 gint i, n_text;
110
111 g_return_val_if_fail (GIMV_IS_IMAGE_SAVER (saver), FALSE);
112 g_return_val_if_fail (image != NULL, FALSE);
113 g_return_val_if_fail (filename != NULL, FALSE);
114 g_return_val_if_fail (filename[0] != '\0', FALSE);
115
116 handle = fopen (filename, "wb");
117 if (handle == NULL)
118 return FALSE;
119
120 png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
121 if (png_ptr == NULL) {
122 fclose (handle);
123 return FALSE;
124 }
125
126 info_ptr = png_create_info_struct (png_ptr);
127 if (info_ptr == NULL) {
128 png_destroy_write_struct (&png_ptr, (png_infopp)NULL);
129 fclose (handle);
130 return FALSE;
131 }
132
133 if (setjmp (png_jmpbuf(png_ptr))) {
134 png_destroy_write_struct (&png_ptr, &info_ptr);
135 fclose (handle);
136 return FALSE;
137 }
138
139 png_init_io (png_ptr, handle);
140
141 has_alpha = gimv_image_has_alpha (image);
142 width = gimv_image_width (image);
143 height = gimv_image_height (image);
144 depth = gimv_image_depth (image);
145 pixels = gimv_image_get_pixels (image);
146 rowstride = gimv_image_rowstride (image);
147
148 png_set_IHDR (png_ptr, info_ptr, width, height,
149 depth, PNG_COLOR_TYPE_RGB_ALPHA,
150 PNG_INTERLACE_NONE,
151 PNG_COMPRESSION_TYPE_DEFAULT,
152 PNG_FILTER_TYPE_DEFAULT);
153
154 /* Some text to go with the png image */
155 text = create_png_text (saver, filename, &n_text);
156 if (text)
157 png_set_text (png_ptr, info_ptr, text, n_text);
158
159 /* Write header data */
160 png_write_info (png_ptr, info_ptr);
161
162 /* if there is no alpha in the data, allocate buffer to expand into */
163 if (has_alpha)
164 buffer = NULL;
165 else
166 buffer = g_malloc(4 * width);
167
168 /* pump the raster data into libpng, one scan line at a time */
169 for (i = 0; i < height; i++) {
170 if (has_alpha) {
171 png_bytep row_pointer = pixels;
172 png_write_row (png_ptr, row_pointer);
173 } else {
174 /* expand RGB to RGBA using an opaque alpha value */
175 gint x;
176 gchar *buffer_ptr = buffer;
177 gchar *source_ptr = pixels;
178 for (x = 0; x < width; x++) {
179 *buffer_ptr++ = *source_ptr++;
180 *buffer_ptr++ = *source_ptr++;
181 *buffer_ptr++ = *source_ptr++;
182 *buffer_ptr++ = 255;
183 }
184 png_write_row (png_ptr, (png_bytep) buffer);
185 }
186 pixels += rowstride;
187 }
188
189 png_write_end (png_ptr, info_ptr);
190 png_destroy_write_struct (&png_ptr, &info_ptr);
191
192 g_free (text);
193 g_free (buffer);
194
195 fclose (handle);
196
197 return TRUE;
198 }
199