[ptx] nona, gimp-2, TIFF_mask multilater tiff files

Pablo d'Angelo pablo at mathematik.uni-ulm.de
Wed Jan 7 20:13:08 GMT 2004


On Wed, 07 Jan 2004, Pablo d'Angelo wrote:

> I'd rather like to see the masked content in a viewer that doesn't care
> about alpha than crippling the image while saving it!
> 
> gimp also writes premultiplied files.. urgh. Should we try to submit a
> patch? (sorry, I won't have time to do this).

Hmm, I thought, while waiting for my pizza, I could add the alpha and layer
stuff... Well it took a bit longer, but here it is.

Now it should be able to read the files properly, but I haven't compiled the
full gimp, so it is untested. I attached the diff to gimp 2 pre1.

Hopefully somebody can try it out and report if it works.

Beware, the plugin can't save multiple layers.. save to .xcf instead of tif.

ciao
  Pablo
--
http://wurm.wohnheim.uni-ulm.de/~redman/
Please use PGP
-------------- next part --------------
--- tiff_old.c	2003-12-26 15:59:00.000000000 +0100
+++ tiff.c	2004-01-07 21:12:33.000000000 +0100
@@ -14,6 +14,10 @@
  * TIFF file, then a parasite is created and vice versa.
  * peter at kirchgessner.net -- 29 Oct 2002
  * Progress bar only when run interactive
+ * Added support for non-premultiplied alpha channels, and layer
+ * offsets
+ * pablo.dangelo at web.de -- 7 Jan 2003
+ *
  */
 
 /*
@@ -80,12 +84,14 @@
 			     gushort       bps,
 			     gushort       photomet,
 			     gboolean      alpha,
+			     gboolean      alpha_premult,
 			     gint          extra);
 static void   load_tiles    (TIFF         *tif,
 			     channel_data *channel,
 			     gushort       bps,
 			     gushort       photomet,
 			     gboolean      alpha,
+			     gboolean      alpha_premult,
 			     gint          extra);
 
 static void   read_separate (guchar       *source,
@@ -97,6 +103,7 @@
 			     gint          rows,
 			     gint          cols,
                              gboolean      alpha,
+			     gboolean      alpha_premult,
 			     gint          extra,
 			     gint          sample);
 static void   read_16bit    (guchar       *source,
@@ -107,6 +114,7 @@
 			     gint          rows,
 			     gint          cols,
 			     gboolean      alpha,
+			     gboolean      alpha_premult,
 			     gint          extra,
 			     gint          align);
 static void   read_8bit     (guchar       *source,
@@ -117,6 +125,7 @@
 			     gint          rows,
 			     gint          cols,
 			     gboolean      alpha,
+			     gboolean      alpha_premult,
 			     gint          extra,
 			     gint          align);
 static void   read_default  (guchar       *source,
@@ -128,6 +137,7 @@
 			     gint          rows,
 			     gint          cols,
 			     gboolean      alpha,
+			     gboolean      alpha_premult,
 			     gint          extra,
 			     gint          align);
 
@@ -399,8 +409,13 @@
   guint16       orientation;
   gint          cols, rows;
   gboolean      alpha;
+  gboolean      alpha_premult = FALSE;
   gint          image = 0, image_type = GIMP_RGB;
   gint          layer, layer_type     = GIMP_RGB_IMAGE;
+  gfloat        layer_offset_x = 0.0;
+  gfloat        layer_offset_y = 0.0;
+  gint          max_row = 0;
+  gint          max_col = 0;
   gushort       extra, *extra_types;
   channel_data *channel = NULL;
 
@@ -485,6 +500,23 @@
       if (extra > 0 && (extra_types[0] == EXTRASAMPLE_ASSOCALPHA))
         {
           alpha = TRUE;
+          alpha_premult = TRUE;
+          --extra;
+        }
+      else if (extra > 0 && (extra_types[0] == EXTRASAMPLE_UNASSALPHA))
+        {
+          alpha = TRUE;
+          alpha_premult = FALSE;
+          --extra;
+        }
+      else if (extra > 0 && (extra_types[0] == EXTRASAMPLE_UNSPECIFIED))
+        {
+          /* assuming unassociated alpha if unspecified */
+          g_message ("alpha channel type not defined for file %s. "
+                     "Assuming alpha is not premultiplied",
+                     filename);
+          alpha = TRUE;
+          alpha_premult = FALSE;
           --extra;
         }
       else
@@ -658,8 +690,18 @@
            not xres.  This is left as an exercise for the reader - they
            should feel free to shoot the author of the broken program
            that produced the damaged TIFF file in the first place. */
+
+        /* handle layer offset */
+        if (!TIFFGetField (tif, TIFFTAG_XPOSITION, &layer_offset_x))
+          layer_offset_x = (gfloat)0.0;
+        if (!TIFFGetField (tif, TIFFTAG_YPOSITION, &layer_offset_y))
+          layer_offset_y = (gfloat)0.0;
+    
+        layer_offset_x = layer_offset_x * xres;
+        layer_offset_y = layer_offset_y * yres;
       }
 
+
       /* Install colormap for INDEXED images only */
       if (image_type == GIMP_INDEXED)
         {
@@ -682,14 +724,26 @@
       /* Allocate channel_data for all channels, even the background layer */
       channel = g_new0 (channel_data, extra + 1);
 
-      if (ilayer == 0)
-        name = g_strdup (_("Background"));
+      /* try and use layer name from tiff file */
+      name = 0;
+      if (TIFFGetField (tif, TIFFTAG_PAGENAME, &name))
+        {
+          layer = gimp_layer_new (image, name,
+                                  cols, rows, layer_type, 100, GIMP_NORMAL_MODE);
+          name = 0;
+        }
       else
-        name = g_strdup_printf (_("Page %d"), ilayer);
+        {
+          if (ilayer == 0)
+            name = g_strdup (_("Background"));
+          else
+            name = g_strdup_printf (_("Page %d"), ilayer);
+
+          layer = gimp_layer_new (image, name,
+                                  cols, rows, layer_type, 100, GIMP_NORMAL_MODE);
+          g_free (name);
+        }
 
-      layer = gimp_layer_new (image, name,
-                              cols, rows, layer_type, 100, GIMP_NORMAL_MODE);
-      g_free (name);
 
       channel[0].ID       = layer;
       channel[0].drawable = gimp_drawable_get (layer);
@@ -719,11 +773,11 @@
         }
       else if (TIFFIsTiled(tif))
         {
-          load_tiles (tif, channel, bps, photomet, alpha, extra);
+          load_tiles (tif, channel, bps, photomet, alpha, alpha_premult, extra);
         }
       else
         { /* Load scanlines in tile_height chunks */
-          load_lines (tif, channel, bps, photomet, alpha, extra);
+          load_lines (tif, channel, bps, photomet, alpha, alpha_premult, extra);
         }
 
       if (TIFFGetField (tif, TIFFTAG_ORIENTATION, &orientation))
@@ -772,6 +826,24 @@
       g_free (channel);
       channel = NULL;
 
+      if ( max_col < ((int)(layer_offset_x + 0.5) + cols))
+        {
+          max_col = ((int)(layer_offset_x + 0.5) + cols);
+        }
+
+      if ( max_row < ((int)(layer_offset_y + 0.5) + rows))
+        {
+          max_row = ((int)(layer_offset_y + 0.5) + rows);
+        }
+
+      /* position the layer */
+      if ( layer_offset_x > 0 || layer_offset_y > 0 ) { 
+        gimp_layer_set_offsets( layer,
+                                (int)(layer_offset_x+0.5),
+                                (int)(layer_offset_y+0.5));
+      }
+
+      
       if (ilayer > 0 && !alpha)
         gimp_layer_add_alpha (layer);
 
@@ -780,6 +852,9 @@
     }
   while (TIFFReadDirectory (tif));
 
+  // resize image, to show all layers
+  gimp_image_resize ( image, max_col, max_row, 0, 0);
+
   gimp_image_undo_enable (image);
 
   return image;
@@ -821,6 +896,7 @@
             gushort       bps,
             gushort       photomet,
             gboolean      alpha,
+            gboolean      alpha_premult,
             gint          extra)
 {
   uint16  planar= PLANARCONFIG_CONTIG;
@@ -862,17 +938,17 @@
           if (bps == 16)
             {
               read_16bit (buffer, channel, photomet, y, x, rows, cols, alpha,
-                          extra, tileWidth - cols);
+                          alpha_premult, extra, tileWidth - cols);
             }
           else if (bps == 8)
             {
               read_8bit (buffer, channel, photomet, y, x, rows, cols, alpha,
-                         extra, tileWidth - cols);
+                         alpha_premult, extra, tileWidth - cols);
             }
           else
             {
               read_default (buffer, channel, bps, photomet, y, x, rows, cols,
-                            alpha, extra, tileWidth - cols);
+                            alpha, alpha_premult, extra, tileWidth - cols);
             }
         }
       progress+= one_row;
@@ -890,6 +966,7 @@
 	    gushort       bps,
             gushort       photomet,
 	    gboolean      alpha,
+            gboolean      alpha_premult,
             gint          extra)
 {
   uint16  planar= PLANARCONFIG_CONTIG;
@@ -926,17 +1003,17 @@
           if (bps == 16)
             {
               read_16bit (buffer, channel, photomet, y, 0, rows, cols,
-                          alpha, extra, 0);
+                          alpha, alpha_premult, extra, 0);
             }
           else if (bps == 8)
             {
               read_8bit (buffer, channel, photomet, y, 0, rows, cols,
-                         alpha, extra, 0);
+                         alpha, alpha_premult, extra, 0);
             }
           else
             {
               read_default (buffer, channel, bps, photomet, y, 0, rows, cols,
-                            alpha, extra, 0);
+                            alpha, alpha_premult, extra, 0);
             }
         }
     }
@@ -957,7 +1034,7 @@
                 TIFFReadScanline(tif, buffer + i * lineSize, y + i, s);
 
               read_separate (buffer, channel, bps, photomet,
-                             y, 0, rows, cols, alpha, extra, s);
+                             y, 0, rows, cols, alpha, alpha_premult, extra, s);
             }
         }
     }
@@ -977,6 +1054,7 @@
 	    gint          rows,
 	    gint          cols,
 	    gboolean      alpha,
+            gboolean      alpha_premult,
             gint          extra,
             gint          align)
 {
@@ -1008,16 +1086,24 @@
             case PHOTOMETRIC_MINISBLACK:
               if (alpha)
                 {
-                  gray_val  = *source; source += 2;
-                  alpha_val = *source; source += 2;
-                  gray_val  = MIN (gray_val, alpha_val);
+                  if (alpha_premult)
+                    {
+                      gray_val  = *source; source += 2;
+                      alpha_val = *source; source += 2;
+                      gray_val  = MIN (gray_val, alpha_val);
+
+                      if (alpha_val)
+                        *dest++ = gray_val * 255 / alpha_val;
+                      else
+                        *dest++ = 0;
 
-                  if (alpha_val)
-                    *dest++ = gray_val * 255 / alpha_val;
+                      *dest++ = alpha_val;
+                    }
                   else
-                    *dest++ = 0;
-
-                  *dest++ = alpha_val;
+                    {
+                      *dest++ = *source; source += 2;
+                      *dest++ = *source; source += 2;
+                    }
                 }
               else
                 {
@@ -1028,16 +1114,21 @@
             case PHOTOMETRIC_MINISWHITE:
               if (alpha)
                 {
-                  gray_val  = *source; source += 2;
-                  alpha_val = *source; source += 2;
-                  gray_val  = MIN (gray_val, alpha_val);
-
-                  if (alpha_val)
-                    *dest++ = ((alpha_val - gray_val) * 255) / alpha_val;
-                  else
-                    *dest++ = 0;
+                  if (alpha_premult) {
+                    gray_val  = *source; source += 2;
+                    alpha_val = *source; source += 2;
+                    gray_val  = MIN (gray_val, alpha_val);
+
+                    if (alpha_val)
+                      *dest++ = ((alpha_val - gray_val) * 255) / alpha_val;
+                    else
+                      *dest++ = 0;
 
-                  *dest++ = alpha_val;
+                    *dest++ = alpha_val;
+                  } else {
+                    *dest++ = *source; source += 2;
+                    *dest++ = *source; source += 2;
+                  }
                 }
               else
                 {
@@ -1053,26 +1144,36 @@
             case PHOTOMETRIC_RGB:
               if (alpha)
                 {
-                  red_val   = *source; source += 2;
-                  green_val = *source; source += 2;
-                  blue_val  = *source; source += 2;
-                  alpha_val = *source; source += 2;
-                  red_val   = MIN (red_val,   alpha_val);
-                  green_val = MIN (green_val, alpha_val);
-                  blue_val  = MIN (blue_val,  alpha_val);
-                  if (alpha_val)
+                  if (alpha_premult)
                     {
-                      *dest++ = (red_val   * 255) / alpha_val;
-                      *dest++ = (green_val * 255) / alpha_val;
-                      *dest++ = (blue_val  * 255) / alpha_val;
+                      red_val   = *source; source += 2;
+                      green_val = *source; source += 2;
+                      blue_val  = *source; source += 2;
+                      alpha_val = *source; source += 2;
+                      red_val   = MIN (red_val,   alpha_val);
+                      green_val = MIN (green_val, alpha_val);
+                      blue_val  = MIN (blue_val,  alpha_val);
+                      if (alpha_val)
+                        {
+                          *dest++ = (red_val   * 255) / alpha_val;
+                          *dest++ = (green_val * 255) / alpha_val;
+                          *dest++ = (blue_val  * 255) / alpha_val;
+                        }
+                      else
+                        {
+                          *dest++ = 0;
+                          *dest++ = 0;
+                          *dest++ = 0;
+                        }
+                      *dest++ = alpha_val;
                     }
                   else
                     {
-                      *dest++ = 0;
-                      *dest++ = 0;
-                      *dest++ = 0;
+                      *dest++ = *source; source += 2;
+                      *dest++ = *source; source += 2;
+                      *dest++ = *source; source += 2;
+                      *dest++ = *source; source += 2;
                     }
-                  *dest++ = alpha_val;
                 }
               else
                 {
@@ -1123,6 +1224,7 @@
 	   gint          rows,
 	   gint          cols,
 	   gboolean      alpha,
+           gboolean      alpha_premult,
 	   gint          extra,
 	   gint          align)
 {
@@ -1150,14 +1252,19 @@
             case PHOTOMETRIC_MINISBLACK:
               if (alpha)
                 {
-                  gray_val= *source++;
-                  alpha_val= *source++;
-                  gray_val= MIN(gray_val, alpha_val);
-                  if (alpha_val)
-                    *dest++ = gray_val * 255 / alpha_val;
-                  else
-                    *dest++ = 0;
-                  *dest++ = alpha_val;
+                  if (alpha_premult) {
+                    gray_val= *source++;
+                    alpha_val= *source++;
+                    gray_val= MIN(gray_val, alpha_val);
+                    if (alpha_val)
+                      *dest++ = gray_val * 255 / alpha_val;
+                    else
+                      *dest++ = 0;
+                    *dest++ = alpha_val;
+                  } else {
+                    *dest++ = *source; source++;
+                    *dest++ = *source; source++;
+                  }
                 }
               else
                 {
@@ -1168,15 +1275,23 @@
             case PHOTOMETRIC_MINISWHITE:
               if (alpha)
                 {
-                  gray_val  = *source++;
-                  alpha_val = *source++;
-                  gray_val  = MIN (gray_val, alpha_val);
-
-                  if (alpha_val)
-                    *dest++ = ((alpha_val - gray_val) * 255) / alpha_val;
+                  if (alpha_premult)
+                    {
+                      gray_val  = *source++;
+                      alpha_val = *source++;
+                      gray_val  = MIN (gray_val, alpha_val);
+
+                      if (alpha_val)
+                        *dest++ = ((alpha_val - gray_val) * 255) / alpha_val;
+                      else
+                        *dest++ = 0;
+                      *dest++ = alpha_val;
+                    }
                   else
-                    *dest++ = 0;
-                  *dest++ = alpha_val;
+                    {
+                      *dest++ = *source; source++;
+                      *dest++ = *source; source++;
+                    }
                 }
               else
                 {
@@ -1193,27 +1308,37 @@
             case PHOTOMETRIC_RGB:
               if (alpha)
                 {
-                  red_val   = *source++;
-                  green_val = *source++;
-                  blue_val  = *source++;
-                  alpha_val = *source++;
-                  red_val   = MIN (red_val, alpha_val);
-                  blue_val  = MIN (blue_val, alpha_val);
-                  green_val = MIN (green_val, alpha_val);
-
-                  if (alpha_val)
+                  if (alpha_premult)
                     {
-                      *dest++ = (red_val   * 255) / alpha_val;
-                      *dest++ = (green_val * 255) / alpha_val;
-                      *dest++ = (blue_val  * 255) / alpha_val;
+                      red_val   = *source++;
+                      green_val = *source++;
+                      blue_val  = *source++;
+                      alpha_val = *source++;
+                      red_val   = MIN (red_val, alpha_val);
+                      blue_val  = MIN (blue_val, alpha_val);
+                      green_val = MIN (green_val, alpha_val);
+
+                      if (alpha_val)
+                        {
+                          *dest++ = (red_val   * 255) / alpha_val;
+                          *dest++ = (green_val * 255) / alpha_val;
+                          *dest++ = (blue_val  * 255) / alpha_val;
+                        }
+                      else
+                        {
+                          *dest++ = 0;
+                          *dest++ = 0;
+                          *dest++ = 0;
+                        }
+                      *dest++ = alpha_val;
                     }
                   else
-                    {
-                      *dest++ = 0;
-                      *dest++ = 0;
-                      *dest++ = 0;
-                    }
-                  *dest++ = alpha_val;
+                  {
+                    *dest++ = *source; source++;
+                    *dest++ = *source; source++;
+                    *dest++ = *source; source++;
+                    *dest++ = *source; source++;
+                  }
                 }
               else
                 {
@@ -1276,6 +1401,7 @@
 	      gint          rows,
 	      gint          cols,
 	      gboolean      alpha,
+              gboolean      alpha_premult,
 	      gint          extra,
               gint          align)
 {
@@ -1306,14 +1432,22 @@
               if (alpha)
                 {
                   NEXTSAMPLE (alpha_val);
-                  gray_val= MIN (gray_val, alpha_val);
+                  if (alpha_premult)
+                    {
+                      gray_val= MIN (gray_val, alpha_val);
 
-                  if (alpha_val)
-                    *dest++ = (gray_val * 65025) / (alpha_val * maxval);
-                  else
-                    *dest++ = 0;
+                      if (alpha_val)
+                        *dest++ = (gray_val * 65025) / (alpha_val * maxval);
+                      else
+                        *dest++ = 0;
 
-                  *dest++ = alpha_val;
+                      *dest++ = alpha_val;
+                    }
+                  else
+                    {
+                      *dest++ = (gray_val * 255) / maxval;
+                      *dest++ = alpha_val;
+                    }
                 }
               else
                 {
@@ -1326,14 +1460,19 @@
               if (alpha)
                 {
                   NEXTSAMPLE (alpha_val);
-                  gray_val= MIN (gray_val, alpha_val);
+                  if (alpha_premult) {
+                    gray_val= MIN (gray_val, alpha_val);
 
-                  if (alpha_val)
-                    *dest++ = ((maxval - gray_val) * 65025) / (alpha_val * maxval);
-                  else
-                    *dest++ = 0;
+                    if (alpha_val)
+                      *dest++ = ((maxval - gray_val) * 65025) / (alpha_val * maxval);
+                    else
+                      *dest++ = 0;
 
-                  *dest++ = alpha_val;
+                    *dest++ = alpha_val;
+                  } else {
+                    *dest++ = ((maxval - gray_val) * 255) / maxval;
+                    *dest++ = alpha_val;
+                  }
                 }
               else
                 {
@@ -1354,24 +1493,31 @@
               if (alpha)
                 {
                   NEXTSAMPLE (alpha_val);
-                  red_val   = MIN (red_val, alpha_val);
-                  blue_val  = MIN (blue_val, alpha_val);
-                  green_val = MIN (green_val, alpha_val);
+                  if (alpha_premult) {
+                    red_val   = MIN (red_val, alpha_val);
+                    blue_val  = MIN (blue_val, alpha_val);
+                    green_val = MIN (green_val, alpha_val);
 
-                  if (alpha_val)
-                    {
-                      *dest++ = (red_val   * 255) / alpha_val;
-                      *dest++ = (green_val * 255) / alpha_val;
-                      *dest++ = (blue_val  * 255) / alpha_val;
-                    }
-                  else
-                    {
-                      *dest++ = 0;
-                      *dest++ = 0;
-                      *dest++ = 0;
-                    }
+                    if (alpha_val)
+                      {
+                        *dest++ = (red_val   * 255) / alpha_val;
+                        *dest++ = (green_val * 255) / alpha_val;
+                        *dest++ = (blue_val  * 255) / alpha_val;
+                      }
+                    else
+                      {
+                        *dest++ = 0;
+                        *dest++ = 0;
+                        *dest++ = 0;
+                      }
 
-                  *dest++ = alpha_val;
+                    *dest++ = alpha_val;
+                  } else {
+                    *dest++ = red_val;
+                    *dest++ = green_val;
+                    *dest++ = blue_val;
+                    *dest++ = alpha_val;
+                  }
                 }
               else
                 {
@@ -1428,6 +1574,7 @@
 	       gint          rows,
 	       gint          cols,
                gboolean      alpha,
+               gboolean      alpha_premult,
 	       gint          extra,
 	       gint          sample)
 {
@@ -1628,7 +1775,7 @@
     }
   if (alpha)
     {
-      extra_samples [0] = EXTRASAMPLE_ASSOCALPHA;
+      extra_samples [0] = EXTRASAMPLE_UNASSALPHA;
       TIFFSetField (tif, TIFFTAG_EXTRASAMPLES, 1, extra_samples);
     }
   TIFFSetField (tif, TIFFTAG_PHOTOMETRIC, photometric);
@@ -1755,8 +1902,8 @@
 	    case GIMP_GRAYA_IMAGE:
 	      for (col = 0; col < cols*samplesperpixel; col+=samplesperpixel)
 		{
-		  /* pre-multiply gray by alpha */
-		  data[col + 0] = (t[col + 0] * t[col + 1]) / 255;
+                  /* do not premultiply */
+		  data[col + 0] = t[col + 0];
 		  data[col + 1] = t[col + 1];  /* alpha channel */
 		}
 	      success = (TIFFWriteScanline (tif, data, row, 0) >= 0);
@@ -1767,10 +1914,10 @@
 	    case GIMP_RGBA_IMAGE:
 	      for (col = 0; col < cols*samplesperpixel; col+=samplesperpixel)
 		{
-		  /* pre-multiply rgb by alpha */
-		  data[col+0] = t[col + 0] * t[col + 3] / 255;
-		  data[col+1] = t[col + 1] * t[col + 3] / 255;
-		  data[col+2] = t[col + 2] * t[col + 3] / 255;
+		  /* do not pre-multiply */
+		  data[col+0] = t[col + 0];
+		  data[col+1] = t[col + 1];
+		  data[col+2] = t[col + 2];
 		  data[col+3] = t[col + 3];  /* alpha channel */
 		}
 	      success = (TIFFWriteScanline (tif, data, row, 0) >= 0);


More information about the ptX mailing list