[gdk-pixbuf] Allow saving 1-bit mono TIFFs used in faxes
- From: Robert Ancell <rancell src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gdk-pixbuf] Allow saving 1-bit mono TIFFs used in faxes
- Date: Thu, 23 Oct 2014 04:06:22 +0000 (UTC)
commit 3cfe0a9e9a8724eb4358410428d3cf47fc983644
Author: Mukund Sivaraman <muks banu com>
Date: Wed Oct 22 15:51:40 2014 -0400
Allow saving 1-bit mono TIFFs used in faxes
While working on a GTK+ 3 port of Hildon, I'm upstreaming some patches
from Maemo's repositories. This one is repurposed from:
http://maemo.gitorious.org/hildon/gtk/commit/005de8d5
From: Christian Dywan <christian lanedo com>
Date: Wed, 20 Jan 2010 11:22:29 +0100
Subject: [PATCH] Fixes: NB#116221 - Sharing TIF image with Size Option
Large makes the image much larger than the original one when shared
to OVI or Flickr.
This patch combined other bits which were removed, and the code was
modified a little.
https://bugzilla.gnome.org/show_bug.cgi?id=578876
gdk-pixbuf/gdk-pixbuf-io.c | 10 ++-
gdk-pixbuf/io-tiff.c | 236 ++++++++++++++++++++++++++++++++++++--------
2 files changed, 203 insertions(+), 43 deletions(-)
---
diff --git a/gdk-pixbuf/gdk-pixbuf-io.c b/gdk-pixbuf/gdk-pixbuf-io.c
index d1d6cf8..63a0299 100644
--- a/gdk-pixbuf/gdk-pixbuf-io.c
+++ b/gdk-pixbuf/gdk-pixbuf-io.c
@@ -2404,9 +2404,13 @@ gdk_pixbuf_real_save_to_callback (GdkPixbuf *pixbuf,
* gdk_pixbuf_save (pixbuf, handle, "png", &error, "icc-profile", contents_encode, NULL);
* ]|
*
- * TIFF images recognize a "compression" option which acceps an integer value.
- * Among the codecs are 1 None, 2 Huffman, 5 LZW, 7 JPEG and 8 Deflate, see
- * the libtiff documentation and tiff.h for all supported codec values.
+ * TIFF images recognize: (1) a "bits-per-sample" option (integer) which
+ * can be either 1 for saving bi-level CCITTFAX4 images, or 8 for saving
+ * 8-bits per sample; (2) a "compression" option (integer) which can be
+ * 1 for no compression, 2 for Huffman, 5 for LZW, 7 for JPEG and 8 for
+ * DEFLATE (see the libtiff documentation and tiff.h for all supported
+ * codec values); (3) an "icc-profile" option (zero-terminated string)
+ * containing a base64 encoded ICC color profile.
*
* ICO images can be saved in depth 16, 24, or 32, by using the "depth"
* parameter. When the ICO saver is given "x_hot" and "y_hot" parameters,
diff --git a/gdk-pixbuf/io-tiff.c b/gdk-pixbuf/io-tiff.c
index 262b1fb..d015cde 100644
--- a/gdk-pixbuf/io-tiff.c
+++ b/gdk-pixbuf/io-tiff.c
@@ -8,6 +8,8 @@
* Federico Mena-Quintero <federico gimp org>
* Jonathan Blandford <jrb redhat com>
* S�ren Sandmann <sandmann daimi au dk>
+ * Christian Dywan <christian lanedo com>
+ * Mukund Sivaraman <muks banu com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -85,6 +87,7 @@ tiff_image_parse (TIFF *tiff, TiffContext *context, GError **error)
guchar *pixels = NULL;
gint width, height, rowstride, bytes;
GdkPixbuf *pixbuf;
+ guint16 bits_per_sample = 0;
uint16 orientation = 0;
uint16 transform = 0;
uint16 codec;
@@ -173,6 +176,15 @@ tiff_image_parse (TIFF *tiff, TiffContext *context, GError **error)
return NULL;
}
+ /* Save the bits per sample as an option since pixbufs are
+ expected to be always 8 bits per sample. */
+ TIFFGetField (tiff, TIFFTAG_BITSPERSAMPLE, &bits_per_sample);
+ if (bits_per_sample > 0) {
+ gchar str[5];
+ g_snprintf (str, sizeof (str), "%d", bits_per_sample);
+ gdk_pixbuf_set_option (pixbuf, "bits-per-sample", str);
+ }
+
/* Set the "orientation" key associated with this image. libtiff
orientation handling is odd, so further processing is required
by higher-level functions based on this tag. If the embedded
@@ -591,6 +603,44 @@ free_save_context (TiffSaveContext *context)
g_free (context);
}
+static void
+copy_gray_row (gint *dest,
+ guchar *src,
+ gint width,
+ gboolean has_alpha)
+{
+ gint i;
+ guchar *p;
+
+ p = src;
+ for (i = 0; i < width; i++) {
+ int pr, pg, pb, pv;
+
+ pr = *p++;
+ pg = *p++;
+ pb = *p++;
+
+ if (has_alpha) {
+ int pa = *p++;
+
+ /* Premul alpha to simulate it */
+ if (pa > 0) {
+ pr = pr * pa / 255;
+ pg = pg * pa / 255;
+ pb = pb * pa / 255;
+ } else {
+ pr = pg = pb = 0;
+ }
+ }
+
+ /* Calculate value MAX(MAX(r,g),b) */
+ pv = pr > pg ? pr : pg;
+ pv = pv > pb ? pv : pb;
+
+ *dest++ = pv;
+ }
+}
+
static gboolean
gdk_pixbuf__tiff_image_save_to_callback (GdkPixbufSaveFunc save_func,
gpointer user_data,
@@ -601,14 +651,16 @@ gdk_pixbuf__tiff_image_save_to_callback (GdkPixbufSaveFunc save_func,
{
TIFF *tiff;
gint width, height, rowstride;
+ const gchar *bits_per_sample = NULL;
+ long bps;
+ const gchar *compression = NULL;
guchar *pixels;
gboolean has_alpha;
gushort alpha_samples[1] = { EXTRASAMPLE_UNASSALPHA };
int y;
TiffSaveContext *context;
gboolean retval;
- guchar *icc_profile = NULL;
- gsize icc_profile_size = 0;
+ const gchar *icc_profile = NULL;
tiff_set_handlers ();
@@ -638,9 +690,6 @@ gdk_pixbuf__tiff_image_save_to_callback (GdkPixbufSaveFunc save_func,
TIFFSetField (tiff, TIFFTAG_IMAGEWIDTH, width);
TIFFSetField (tiff, TIFFTAG_IMAGELENGTH, height);
- TIFFSetField (tiff, TIFFTAG_BITSPERSAMPLE, 8);
- TIFFSetField (tiff, TIFFTAG_SAMPLESPERPIXEL, has_alpha ? 4 : 3);
- TIFFSetField (tiff, TIFFTAG_ROWSPERSTRIP, height);
/* libtiff supports a number of 'codecs' such as:
1 None, 2 Huffman, 5 LZW, 7 JPEG, 8 Deflate, see tiff.h */
@@ -648,48 +697,156 @@ gdk_pixbuf__tiff_image_save_to_callback (GdkPixbufSaveFunc save_func,
guint i = 0;
while (keys[i]) {
- if (g_str_equal (keys[i], "compression")) {
- guint16 codec = strtol (values[i], NULL, 0);
- if (TIFFIsCODECConfigured (codec))
- TIFFSetField (tiff, TIFFTAG_COMPRESSION, codec);
- else {
- g_set_error_literal (error,
- GDK_PIXBUF_ERROR,
- GDK_PIXBUF_ERROR_FAILED,
- _("TIFF compression doesn't refer to a valid codec."));
- retval = FALSE;
- goto cleanup;
- }
- } else if (g_str_equal (keys[i], "icc-profile")) {
+ if (g_str_equal (keys[i], "bits-per-sample"))
+ bits_per_sample = values[i];
+ else if (g_str_equal (keys[i], "compression"))
+ compression = values[i];
+ else if (g_str_equal (keys[i], "icc-profile"))
+ icc_profile = values[i];
+ i++;
+ }
+ }
+
+ /* Use 8 bits per sample by default, if none was recorded or
+ specified. */
+ if (!bits_per_sample)
+ bits_per_sample = "8";
+
+ /* Use DEFLATE compression (8) by default, if none was recorded
+ or specified. */
+ if (!compression)
+ compression = "8";
+
+ /* libtiff supports a number of 'codecs' such as:
+ 1 None, 2 Huffman, 5 LZW, 7 JPEG, 8 Deflate, see tiff.h */
+ guint16 codec = strtol (compression, NULL, 0);
+
+ if (TIFFIsCODECConfigured (codec))
+ TIFFSetField (tiff, TIFFTAG_COMPRESSION, codec);
+ else {
+ g_set_error_literal (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_FAILED,
+ _("TIFF compression doesn't refer to a valid codec."));
+ retval = FALSE;
+ goto cleanup;
+ }
+
+ /* We support 1-bit or 8-bit saving */
+ bps = atol (bits_per_sample);
+ if (bps == 1) {
+ TIFFSetField (tiff, TIFFTAG_BITSPERSAMPLE, 1);
+ TIFFSetField (tiff, TIFFTAG_SAMPLESPERPIXEL, 1);
+ TIFFSetField (tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
+ TIFFSetField (tiff, TIFFTAG_COMPRESSION, COMPRESSION_CCITTFAX4);
+ } else if (bps == 8) {
+ TIFFSetField (tiff, TIFFTAG_BITSPERSAMPLE, 8);
+ TIFFSetField (tiff, TIFFTAG_SAMPLESPERPIXEL, has_alpha ? 4 : 3);
+ TIFFSetField (tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
+
+ if (has_alpha)
+ TIFFSetField (tiff, TIFFTAG_EXTRASAMPLES, 1, alpha_samples);
+
+ if (icc_profile != NULL) {
+ guchar *icc_profile_buf;
+ gsize icc_profile_size;
+
/* decode from base64 */
- icc_profile = g_base64_decode (values[i], &icc_profile_size);
+ icc_profile_buf = g_base64_decode (icc_profile, &icc_profile_size);
if (icc_profile_size < 127) {
- g_set_error (error,
- GDK_PIXBUF_ERROR,
- GDK_PIXBUF_ERROR_BAD_OPTION,
- _("Color profile has invalid length %d."),
- (gint)icc_profile_size);
- retval = FALSE;
- goto cleanup;
+ g_set_error (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_BAD_OPTION,
+ _("Color profile has invalid length %d."),
+ (gint) icc_profile_size);
+ retval = FALSE;
+ g_free (icc_profile_buf);
+ goto cleanup;
}
- }
- i++;
- }
- }
- if (has_alpha)
- TIFFSetField (tiff, TIFFTAG_EXTRASAMPLES, 1, alpha_samples);
+ TIFFSetField (tiff, TIFFTAG_ICCPROFILE, icc_profile_size, icc_profile_buf);
+ g_free (icc_profile_buf);
+ }
+ } else {
+ /* The passed bits-per-sample is not supported. */
+ g_set_error_literal (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_FAILED,
+ _("TIFF bits-per-sample doesn't contain a supported value."));
+ retval = FALSE;
+ goto cleanup;
+ }
- TIFFSetField (tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
- TIFFSetField (tiff, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB);
+ TIFFSetField (tiff, TIFFTAG_ROWSPERSTRIP, height);
+ TIFFSetField (tiff, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB);
TIFFSetField (tiff, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
- if (icc_profile != NULL)
- TIFFSetField (tiff, TIFFTAG_ICCPROFILE, icc_profile_size, icc_profile);
+ if (bps == 1) {
+ guchar *mono_row;
+ gint *dith_row_1, *dith_row_2, *dith_row_tmp;
+
+ dith_row_1 = g_new (gint, width);
+ dith_row_2 = g_new (gint, width);
+ mono_row = g_malloc ((width + 7) / 8);
+
+ copy_gray_row (dith_row_1, pixels, width, has_alpha);
+
+ for (y = 0; y < height; y++) {
+ guint x;
+ gint *p;
+
+ memset (mono_row, 0, (width + 7) / 8);
- for (y = 0; y < height; y++) {
- if (TIFFWriteScanline (tiff, pixels + y * rowstride, y, 0) == -1)
- break;
+ if (y > 0) {
+ dith_row_tmp = dith_row_1;
+ dith_row_1 = dith_row_2;
+ dith_row_2 = dith_row_tmp;
+ }
+
+ if (y < (height - 1))
+ copy_gray_row (dith_row_2, pixels + ((y + 1) * rowstride), width, has_alpha);
+
+ p = dith_row_1;
+ for (x = 0; x < width; x++) {
+ gint p_old, p_new, quant_error;
+
+ /* Apply Floyd-Steinberg dithering */
+
+ p_old = *p++;
+
+ if (p_old > 127)
+ p_new = 255;
+ else
+ p_new = 0;
+
+ quant_error = p_old - p_new;
+ if (x < (width - 1))
+ dith_row_1[x + 1] += 7 * quant_error / 16;
+ if (y < (height - 1)) {
+ if (x > 0)
+ dith_row_2[x - 1] += 3 * quant_error / 16;
+
+ dith_row_2[x] += 5 * quant_error / 16;
+
+ if (x < (width - 1))
+ dith_row_2[x + 1] += quant_error / 16;
+ }
+
+ if (p_new > 127)
+ mono_row[x / 8] |= (0x1 << (7 - (x % 8)));
+ }
+
+ if (TIFFWriteScanline (tiff, mono_row, y, 0) == -1)
+ break;
+ }
+ g_free (mono_row);
+ g_free (dith_row_1);
+ g_free (dith_row_2);
+ } else {
+ for (y = 0; y < height; y++) {
+ if (TIFFWriteScanline (tiff, pixels + y * rowstride, y, 0) == -1)
+ break;
+ }
}
if (y < height) {
@@ -708,7 +865,6 @@ gdk_pixbuf__tiff_image_save_to_callback (GdkPixbufSaveFunc save_func,
retval = save_func (context->buffer, context->used, error, user_data);
cleanup:
- g_free (icc_profile);
free_save_context (context);
return retval;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]