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