Question

I read a TIFF image with multiple Frames using below code

Stream imageStreamSource = new FileStream(inputFile, FileMode.Open, 
                                          FileAccess.Read, FileShare.Read);
TiffBitmapDecoder decoder = new TiffBitmapDecoder(imageStreamSource,
                               BitmapCreateOptions.PreservePixelFormat,    
                               BitmapCacheOption.Default);
foreach(BitmapFrame frame in decoder.Frames)
{                
    using (Stream output = new FileStream(outputFile, FileMode.Create))
    {
        using (MemoryStream outStream = new MemoryStream())
        {
            JpegBitmapEncoder enc = new JpegBitmapEncoder();
            enc.Frames.Add(BitmapFrame.Create(frame));
            enc.Save(output); //It overrides and finally i get the last image only
            //  outStream.CopyTo(output); this also not working.       
        }
    }

}          

I would like to create a one JPEG image contains all the frame. But I have no idea how we can create/append a JPEG image from JpegBitmapEncoder?

Expected resulted image should be like below

  --------------
  |            |
  | Frame1 img |
  |            |
  |------------|
  |            |
  | Frame2 img |
  |            |
  |------------|
  |            |
  | Frame3 img |
  |            |
  |------------|

outStream.CopyTo(output); this line not at all copying bytes to file. 0 bytes always

Was it helpful?

Solution

It looks like your foreach should be inside the using(..) statement. Otherwise you are always creating a new output stream for (overwriting) the output file:

using (Stream output = new FileStream(outputFile, FileMode.Create))
{
   foreach(BitmapFrame frame in decoder.Frames)
   {                   
        JpegBitmapEncoder enc = new JpegBitmapEncoder();
        enc.Frames.Add(BitmapFrame.Create(frame));
        enc.Save(output); //It overrides and finally i get the last image only

        //  outStream.CopyTo(output); this also not working.       
   }
 }

Another aspect I noticed is in the sample code you posted you were creating a MemoryStream but were never using it so it was superfluous to your point.

That said, it looks like you don't just want to write the different JPEG frames to one file, one after the other but instead create a single large JPEG image that contains all the extracted TIFF frames pasted together.

In order to do so you will have to first determine the final size of your JPEG image by iterating through the TIFF frames and accumulating the height (if they are not all the same height) and the maximum width you need, if they are not all the same width.

int totalHeight = 0;
int maximumWidth = 0;

foreach(BitmapFrame frame in decoder.Frames)
{          
    maximumWidth = Math.Max(maximumWidth, frame.PixelWidth);         
    totalHeight = totalHeight + frame.PixelHeight;
}

Then, you will want to create a memory bitmap image of the right final size.

var visual = new DrawingVisual();
using (var ctx = visual.RenderOpen())
{
    foreach(BitmapFrame frame in decoder.Frames)
    {
        maximumWidth = Math.Max(maximumWidth, frame.PixelWidth);                     

        ctx.DrawImage(frame, new Rect(0, totalHeight, imageWidth, imageHeight));

        totalHeight = totalHeight + frame.PixelHeight;
    }
}

// Converts the Visual (DrawingVisual) into a BitmapSource
var bmp = new RenderTargetBitmap(maximumWidth, totalHeight, 96, 96, PixelFormats.Pbgra32);
bmp.Render(visual);

// Finally, save the rendered bitmap as a jpeg to the output stream
JpegBitmapEncoder enc = new JpegBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(bmp));

using (Stream output = new FileStream(outputFile, FileMode.Create))
{
    enc.Save(output);
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top