Adding frames to a Multi-Frame TIFF

Multi-page TIFF images are not directly editable inasmuch as you cannot open one up, slap a new image into it and then save it. You have to go through the process of opening the image, extracting the frames, creating a new TIFF image and saving all the frames back into it again.

The frames in a TIFF file can be selected using the SelectActiveFrame method and the contents of the successive frames extracted to separate bitmaps. A new multi-frame TIFF is then created and the images saved in-order to the new image.

The following application shown in Figure 1 and listed in C# and VB shows how to append or insert frames into an existing TIFF image.

Figure 1. The MultiTiffManager application

 

Here's how it works.

  • An image is loaded. If it has multiple frames, they are all extracted to separate images and then placed in a specialized ImageItem class. These ImageItems are used as items in an owner-drawn ListBox.

  • The ListBox displays each item as a graphic, you can scroll up and down the list to see the thumbnail of the images in the TIFF.

  • Appending an image adds a selected image to the end of the list.

  • Inserting an image places the image at the insert point designated by the numeric up-down control.

  • Removing an image deletes the item from the index specified in the numeric up-down control.

  • Save to a new TIFF using the "Done" button. This saves the first image in the list as the master and adds pages one at a time until all the pages in the list are saved into a single file.

Show me code in C#

using System;

using System.IO;

using System.Drawing;

using System.Drawing.Drawing2D;

using System.Drawing.Imaging;

using System.Collections;

using System.ComponentModel;

using System.Windows.Forms;

using System.Data;

 

namespace MultiTiffManager

{

  /// <summary>

  /// A simple Multi-page TIFF file editor.

  /// </summary>

  public class Form1 : System.Windows.Forms.Form

  {

    private System.Windows.Forms.ListBox listBox1;

    private System.Windows.Forms.Button button1;

    private System.Windows.Forms.Button button2;

    private System.Windows.Forms.NumericUpDown numericUpDown1;

    private System.Windows.Forms.Button button3;

    private System.Windows.Forms.Button button4;

    private System.Windows.Forms.Button button5;

    private System.Windows.Forms.Button button6;

    /// <summary>

    /// Required designer variable.

    /// </summary>

    private System.ComponentModel.Container components = null;

 

    public Form1()

    {

      //

      // Required for Windows Form Designer support

      //

      InitializeComponent();

 

      //

      // TODO: Add any constructor code after InitializeComponent call

      //

    }

 

    /// <summary>

    /// Clean up any resources being used.

    /// </summary>

    protected override void Dispose( bool disposing )

    {

      if( disposing )

      {

        if (components != null)

        {

          components.Dispose();

        }

      }

      base.Dispose( disposing );

    }

 

    #region Windows Form Designer generated code

    /// <summary>

    /// Required method for Designer support - do not modify

    /// the contents of this method with the code editor.

    /// </summary>

    private void InitializeComponent()

    {

      this.listBox1 = new System.Windows.Forms.ListBox();

      this.button1 = new System.Windows.Forms.Button();

      this.button2 = new System.Windows.Forms.Button();

      this.numericUpDown1 = new System.Windows.Forms.NumericUpDown();

      this.button3 = new System.Windows.Forms.Button();

      this.button4 = new System.Windows.Forms.Button();

      this.button5 = new System.Windows.Forms.Button();

      this.button6 = new System.Windows.Forms.Button();

      ((System.ComponentModel.ISupportInitialize)(this.numericUpDown1)).BeginInit();

      this.SuspendLayout();

      //

      // listBox1

      //

      this.listBox1.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawVariable;

      this.listBox1.IntegralHeight = false;

      this.listBox1.Location = new System.Drawing.Point(8, 8);

      this.listBox1.Name = "listBox1";

      this.listBox1.Size = new System.Drawing.Size(200, 256);

      this.listBox1.TabIndex = 0;

      this.listBox1.MeasureItem += new System.Windows.Forms.MeasureItemEventHandler(this.listBox1_MeasureItem);

      this.listBox1.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.listBox1_DrawItem);

      this.listBox1.SelectedIndexChanged += new System.EventHandler(this.listBox1_SelectedIndexChanged);

      //

      // button1

      //

      this.button1.Location = new System.Drawing.Point(280, 16);

      this.button1.Name = "button1";

      this.button1.TabIndex = 1;

      this.button1.Text = "Load";

      this.button1.Click += new System.EventHandler(this.button1_Click);

      //

      // button2

      //

      this.button2.Location = new System.Drawing.Point(280, 48);

      this.button2.Name = "button2";

      this.button2.TabIndex = 2;

      this.button2.Text = "Append";

      this.button2.Click += new System.EventHandler(this.button2_Click);

      //

      // numericUpDown1

      //

      this.numericUpDown1.Enabled = false;

      this.numericUpDown1.Location = new System.Drawing.Point(224, 104);

      this.numericUpDown1.Maximum = new System.Decimal(new int[] {

                                       10000,

                                       0,

                                       0,

                                       0});

      this.numericUpDown1.Name = "numericUpDown1";

      this.numericUpDown1.Size = new System.Drawing.Size(64, 20);

      this.numericUpDown1.TabIndex = 3;

      this.numericUpDown1.ValueChanged += new System.EventHandler(this.numericUpDown1_ValueChanged);

      //

      // button3

      //

      this.button3.Enabled = false;

      this.button3.Location = new System.Drawing.Point(320, 80);

      this.button3.Name = "button3";

      this.button3.TabIndex = 2;

      this.button3.Text = "Insert";

      this.button3.Click += new System.EventHandler(this.button3_Click);

      //

      // button4

      //

      this.button4.Location = new System.Drawing.Point(280, 168);

      this.button4.Name = "button4";

      this.button4.TabIndex = 2;

      this.button4.Text = "Done";

      this.button4.Click += new System.EventHandler(this.button4_Click);

      //

      // button5

      //

      this.button5.Location = new System.Drawing.Point(280, 216);

      this.button5.Name = "button5";

      this.button5.TabIndex = 2;

      this.button5.Text = "Cancel";

      this.button5.Click += new System.EventHandler(this.button5_Click);

      //

      // button6

      //

      this.button6.Enabled = false;

      this.button6.Location = new System.Drawing.Point(320, 120);

      this.button6.Name = "button6";

      this.button6.TabIndex = 2;

      this.button6.Text = "Remove";

      this.button6.Click += new System.EventHandler(this.button6_Click);

      //

      // Form1

      //

      this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);

      this.ClientSize = new System.Drawing.Size(408, 269);

      this.Controls.Add(this.numericUpDown1);

      this.Controls.Add(this.button2);

      this.Controls.Add(this.button1);

      this.Controls.Add(this.listBox1);

      this.Controls.Add(this.button3);

      this.Controls.Add(this.button4);

      this.Controls.Add(this.button5);

      this.Controls.Add(this.button6);

      this.Name = "Form1";

      this.Text = "Form1";

      ((System.ComponentModel.ISupportInitialize)(this.numericUpDown1)).EndInit();

      this.ResumeLayout(false);

 

    }

    #endregion

 

    /// <summary>

    /// The main entry point for the application.

    /// </summary>

    [STAThread]

    static void Main()

    {

      Application.Run(new Form1());

    }

 

    /// <summary>

    /// Saves the TIFF file with any changes such as added or removed pages.

    /// </summary>

    /// <param name="sender"></param>

    /// <param name="e"></param>

    private void button4_Click(object sender, System.EventArgs e)

    {

      //saves a new TIFF file with all of the items in the list.

      if(this.listBox1.Items.Count==0)

        return;

 

      SaveFileDialog dlg=new SaveFileDialog();

      dlg.Filter="TIFF files|*.tiff";

      dlg.FileName=this.Text;

      if(dlg.ShowDialog()==DialogResult.OK)

      {

 

        if(this.listBox1.Items.Count>1)

        {

          //the first item in the list is the master frame.

          ImageItem ii=(ImageItem)this.listBox1.Items[0];

          Bitmap MasterBitmap=(Bitmap)ii.Image;

 

          //Select the image encoder

          Encoder enc=Encoder.SaveFlag;

          ImageCodecInfo info=null;

 

          foreach(ImageCodecInfo ice in ImageCodecInfo.GetImageEncoders())

            if(ice.MimeType=="image/tiff")

              info=ice;

 

          EncoderParameters ep=new EncoderParameters(1);

 

          ep.Param[0]=new EncoderParameter(enc,(long)EncoderValue.MultiFrame);

 

          //Save the master bitmap

          MasterBitmap.Save(dlg.FileName,info,ep);

 

          ep.Param[0]=new EncoderParameter(enc,(long)EncoderValue.FrameDimensionPage);

 

          //add all images from index 1 to n

          for(int i=1; i<this.listBox1.Items.Count;i++)

          { 

            ii=(ImageItem)this.listBox1.Items[i];

            MasterBitmap.SaveAdd(ii.Image,ep);

          }

          ep.Param[0]=new EncoderParameter(enc,(long)EncoderValue.Flush);

          //close out the file.

          MasterBitmap.SaveAdd(ep);

        }

        else

        {

          //do a simple save...

          ((ImageItem)this.listBox1.Items[0]).Image.Save(dlg.FileName,ImageFormat.Tiff);

        }

 

        this.Text=dlg.FileName;

 

      }

      

    }

 

    /// <summary>

    /// Loads the selected image file

    /// </summary>

    /// <param name="sender"></param>

    /// <param name="e"></param>

    private void button1_Click(object sender, System.EventArgs e)

    {

      OpenFileDialog dlg=new OpenFileDialog();

      dlg.Filter="Image files|*.bmp;*.tiff;*.jpg;*.gif";

      if(dlg.ShowDialog()==DialogResult.OK)

        DoOpen(dlg.FileName);

    }

 

    /// <summary>

    /// Opens a TIFF image and extracts the frames

    /// </summary>

    /// <param name="filename">The filename</param>

    private void DoOpen(string filename)

    {

      ClearItems();

      //Open the image using a file stream in case the user wants to save

      //the image back to the same file.

      FileStream fs=File.Open(filename,FileMode.Open,FileAccess.Read);

      Bitmap bm=(Bitmap)Bitmap.FromStream(fs);

 

      if(bm.GetFrameCount(FrameDimension.Page)>1)

      {

        for(int i=0; i<bm.GetFrameCount(FrameDimension.Page); i++)

        {

          bm.SelectActiveFrame(FrameDimension.Page, i);

          Bitmap temp=new Bitmap(bm.Width,bm.Height);

          Graphics g=Graphics.FromImage(temp);

          g.InterpolationMode=InterpolationMode.NearestNeighbor;

          g.DrawImageUnscaled(bm,0,0);

          g.Dispose();

          AddFrameToList(temp,-1);

        }

      }

      else

        AddFrameToList(bm,0);

 

      fs.Close();

      bm.Dispose();

      this.Text=filename;

    }

 

    /// <summary>

    /// Removes all items from the list

    /// </summary>

    private void ClearItems()

    {

      for(int i=0; i<this.listBox1.Items.Count; i++)

      {

        ImageItem item=(ImageItem)this.listBox1.Items[i];

        item.Image.Dispose();

      }

      this.listBox1.Items.Clear();

    }

 

    /// <summary>

    /// Adds a frame to the list.

    /// </summary>

    /// <param name="i"></param>

    /// <param name="pos"></param>

    private void AddFrameToList(Image i, int pos)

    {

      if(pos==-1)

        this.listBox1.Items.Add(new ImageItem(i));

      else

        this.listBox1.Items.Insert(pos,new ImageItem(i));

 

      this.numericUpDown1.Enabled=true;

      this.button3.Enabled=true;

      this.button6.Enabled=true;

    }

 

    /// <summary>

    /// Measures the ListBox items

    /// </summary>

    /// <param name="sender"></param>

    /// <param name="e"></param>

    private void listBox1_MeasureItem(object sender, System.Windows.Forms.MeasureItemEventArgs e)

    {

      ImageItem item=(ImageItem)this.listBox1.Items[e.Index];

      float r=this.listBox1.ClientSize.Width;

      r/=item.Image.Width;

 

      e.ItemHeight=(int)(r*item.Image.Height);

      e.ItemWidth=(int)(r*item.Image.Width);

    }

 

    /// <summary>

    /// Draws the ListBox items

    /// </summary>

    /// <param name="sender"></param>

    /// <param name="e"></param>

    private void listBox1_DrawItem(object sender, System.Windows.Forms.DrawItemEventArgs e)

    {

      if(this.listBox1.Items.Count<1)

        return;

      ImageItem i=(ImageItem)this.listBox1.Items[e.Index];

      e.Graphics.DrawImage(i.Image,e.Bounds,0,0,i.Image.Width,i.Image.Width,GraphicsUnit.Pixel);

    }

 

    /// <summary>

    /// Handles the Append commmand

    /// </summary>

    /// <param name="sender"></param>

    /// <param name="e"></param>

    private void button2_Click(object sender, System.EventArgs e)

    {

      //Append an image to the list.

      OpenFileDialog dlg=new OpenFileDialog();

      dlg.Filter="Image files|*.bmp;*.jpg;*.gif;*.tif;*.png";

      if(dlg.ShowDialog()==DialogResult.OK)

      {

        Bitmap toAppend=(Bitmap)Image.FromFile(dlg.FileName);

        this.AddFrameToList(toAppend,-1);

      }

    }

 

 

    private void numericUpDown1_ValueChanged(object sender, System.EventArgs e)

    {

      if(this.numericUpDown1.Value>=this.listBox1.Items.Count)

        this.numericUpDown1.Value=this.listBox1.Items.Count-1;

    }

 

    /// <summary>

    /// Handles the "insert" click

    /// </summary>

    /// <param name="sender"></param>

    /// <param name="e"></param>

    private void button3_Click(object sender, System.EventArgs e)

    {

      //inserts an image at the index specified by the numeric up-down control.

      OpenFileDialog dlg=new OpenFileDialog();

      dlg.Filter="Image files|*.bmp;*.jpg;*.gif;*.tif;*.png";

      if(dlg.ShowDialog()==DialogResult.OK)

      {

        Bitmap toAppend=(Bitmap)Image.FromFile(dlg.FileName);

        this.AddFrameToList(toAppend,(int)this.numericUpDown1.Value);

      }

    }

 

    /// <summary>

    /// handles the "Remove" command

    /// </summary>

    /// <param name="sender"></param>

    /// <param name="e"></param>

    private void button6_Click(object sender, System.EventArgs e)

    {

      this.listBox1.Items.RemoveAt((int)this.numericUpDown1.Value);

      if(this.listBox1.Items.Count==0)

      {

        this.numericUpDown1.Enabled=false;

        this.button3.Enabled=false;

        this.button6.Enabled=false;

        return;

      }

      if(this.numericUpDown1.Value>=this.listBox1.Items.Count)

        this.numericUpDown1.Value=this.listBox1.Items.Count-1;

    }

 

    /// <summary>

    /// Exits the program with a little sanity check

    /// </summary>

    /// <param name="sender"></param>

    /// <param name="e"></param>

    private void button5_Click(object sender, System.EventArgs e)

    {

      if(MessageBox.Show("Are you sure you want to quit?","Exit TiffManager",MessageBoxButtons.YesNo)==DialogResult.Yes)

        Application.Exit();

    }

 

    /// <summary>

    /// updates the numeric up-down with the image number for inserts or deletes

    /// </summary>

    /// <param name="sender"></param>

    /// <param name="e"></param>

    private void listBox1_SelectedIndexChanged(object sender, System.EventArgs e)

    {

      if(this.listBox1.SelectedIndex==-1)

        return;

      this.numericUpDown1.Value=this.listBox1.SelectedIndex;

    }

 

 

  }

 

  /// <summary>

  /// Holds an image for the ListBox

  /// </summary>

  public class ImageItem

  {

    Bitmap _image;

    public Bitmap Image

    {

      get{return _image;}

    }

 

    protected ImageItem()

    {

    }

 

    public ImageItem(Image i)

    {

      this._image=(Bitmap)i;

    }

  }

}

 

Show me code in VB

 

Imports System

Imports System.IO

Imports System.Drawing

Imports System.Drawing.Drawing2D

Imports System.Drawing.Imaging

Imports System.Collections

Imports System.ComponentModel

Imports System.Windows.Forms