WPF Bitmap / BitmapFrame to multi-page / multi-frame TIFF with JPEG encoding












0












$begingroup$


I was searching for a long time how to write multi-page TIFFs with the JPEG encoding. TIFFs support JPEG encoded frames but the built-in encoder in .NET Framework does not have JPEG as a compression option.



The code is based on the answer to this question: https://stackoverflow.com/questions/14811496/tiff-with-jpeg-compression-much-larger-than-original-jpeg
but does not rely on the FreeImage library.



First up, a class to convert BitmapFrame or Bitmap to a JPEG image:



using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Windows.Media.Imaging;

namespace TIFF
{
public class Jpeg
{
public byte Data;
public uint Width;
public uint Height;
public uint HorizontalResolution;
public uint VerticalResolution;

public Jpeg(byte data, uint width, uint height, uint horizontalResolution, uint verticalResolution)
{
this.Data = data;
this.Width = width;
this.Height = height;
this.HorizontalResolution = horizontalResolution;
this.VerticalResolution = verticalResolution;
}

public static Jpeg FromBitmapFrame(BitmapFrame bitmap, long quality)
{
Jpeg jpeg;

using (var stream = new MemoryStream())
{
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
encoder.QualityLevel = 90;
encoder.Frames.Add(bitmap);
encoder.Save(stream);

jpeg = new Jpeg(stream.ToArray(), (uint)bitmap.Width, (uint)bitmap.Height, (uint)bitmap.DpiX, (uint)bitmap.DpiY);
}

return jpeg;
}

public static Jpeg FromBitmap(Bitmap bitmap, long quality)
{
Jpeg jpeg;
using (var stream = new MemoryStream())
{
ImageCodecInfo jpgEncoder = GetEncoder(ImageFormat.Jpeg);
Encoder encoder = Encoder.Quality;
EncoderParameters parameters = new EncoderParameters(1);
parameters.Param[0] = new EncoderParameter(encoder, quality);
bitmap.Save(stream, jpgEncoder, parameters);

jpeg = new Jpeg(stream.ToArray(), (uint) bitmap.Width, (uint) bitmap.Height, (uint) bitmap.HorizontalResolution, (uint) bitmap.VerticalResolution);
}

return jpeg;
}

private static ImageCodecInfo GetEncoder(ImageFormat format)
{
ImageCodecInfo codecs = ImageCodecInfo.GetImageDecoders();
foreach (ImageCodecInfo codec in codecs)
{
if (codec.FormatID == format.Guid)
{
return codec;
}
}
return null;
}
}
}


Next, a class to create the TIFF image



using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Windows.Media.Imaging;

namespace TIFF
{
public static class JpegTiff
{
public static byte Create(List<BitmapFrame> frames, long quality)
{
List<Jpeg> jpegs = new List<Jpeg>();

foreach (var frame in frames)
{
jpegs.Add(Jpeg.FromBitmapFrame(frame, quality));
}

return WrapJpegs(jpegs);
}

public static byte Create(List<Bitmap> bitmaps, string filename, long quality)
{
List<Jpeg> jpegs = new List<Jpeg>();

foreach (var bitmap in bitmaps)
{
jpegs.Add(Jpeg.FromBitmap(bitmap, quality));
}

return WrapJpegs(jpegs);
}

private static byte WrapJpegs(List<Jpeg> jpegs)
{
if (jpegs == null || jpegs.Count == 0 || jpegs.FindIndex(b => b.Data.Length == 0) > -1)
throw new ArgumentNullException("Image Data must not be null or empty");

MemoryStream tiffData = new MemoryStream();
BinaryWriter writer = new BinaryWriter(tiffData);
uint offset = 8; // size of header, offset to IFD
ushort entryCount = 14; // entries per IFD

#region IFH - Image file header

// magic number
if (BitConverter.IsLittleEndian)
writer.Write(0x002A4949);
else
writer.Write(0x4D4D002A);

// offset to (first) IFD
writer.Write(offset);

#endregion IFH

#region IFD Image file directory

// write image file directories for each jpeg
for (int i = 0; offset > 0; i++)
{
var jpeg = jpegs[i];
uint width = jpeg.Width;
uint length = jpeg.Height;
uint xres = jpeg.HorizontalResolution;
uint yres = jpeg.VerticalResolution;

// count of entries:
writer.Write(entryCount);

offset += 6 + 12 * (uint)entryCount; // add lengths of entries, entry-count and next-ifd-offset

// TIFF-fields / IFD-entrys:
// {TAG, TYPE (3 = short, 4 = long, 5 = rational), COUNT, VALUE/OFFSET}
uint[,] fields = new uint[,] {
{254, 4, 1, 0}, // NewSubfileType
{256, 4, 1, width}, // ImageWidth
{257, 4, 1, length}, // ImageLength
{258, 3, 3, offset}, // BitsPerSample
{259, 3, 1, 7}, // Compression (new JPEG)
{262, 3, 1, 6}, //PhotometricInterpretation (YCbCr)
{273, 4, 1, offset + 22}, // StripOffsets (offset IFH + entries + values of BitsPerSample & YResolution & XResolution)
{277, 3, 1, 3}, // SamplesPerPixel
{278, 4, 1, length}, // RowsPerStrip
{279, 4, 1, (uint)jpegs[i].Data.LongLength}, // StripByteCounts
{282, 5, 1, offset + 6}, // XResolution (offset IFH + entries + values of BitsPerSample)
{283, 5, 1, offset + 14}, // YResolution (offset IFH + entries + values of BitsPerSample & YResolution)
{284, 3, 1, 1}, // PlanarConfiguration (chunky)
{296, 3, 1, 2} // ResolutionUnit
};

// write fields
for (int f = 0; f < fields.GetLength(0); f++)
{
writer.Write((ushort)fields[f, 0]);
writer.Write((ushort)fields[f, 1]);
writer.Write(fields[f, 2]);
writer.Write(fields[f, 3]);
}

// offset of next IFD
if (i == jpegs.Count - 1)
offset = 0;
else
offset += 22 + (uint)jpegs[i].Data.LongLength; // add values (of fields) length and jpeg length
writer.Write(offset);

#region values of fields

// BitsPerSample
writer.Write((ushort)8);
writer.Write((ushort)8);
writer.Write((ushort)8);

// XResolution
writer.Write(xres);
writer.Write(1);

// YResolution
writer.Write(yres);
writer.Write(1);

#endregion values of fields

// actual image Data
writer.Write(jpegs[i].Data);
}
#endregion IFD

writer.Close();
return tiffData.ToArray();
}
}
}


It could probably be improved by passing in the stream to write to instead of returning a byte array.










share|improve this question











$endgroup$

















    0












    $begingroup$


    I was searching for a long time how to write multi-page TIFFs with the JPEG encoding. TIFFs support JPEG encoded frames but the built-in encoder in .NET Framework does not have JPEG as a compression option.



    The code is based on the answer to this question: https://stackoverflow.com/questions/14811496/tiff-with-jpeg-compression-much-larger-than-original-jpeg
    but does not rely on the FreeImage library.



    First up, a class to convert BitmapFrame or Bitmap to a JPEG image:



    using System.Drawing;
    using System.Drawing.Imaging;
    using System.IO;
    using System.Windows.Media.Imaging;

    namespace TIFF
    {
    public class Jpeg
    {
    public byte Data;
    public uint Width;
    public uint Height;
    public uint HorizontalResolution;
    public uint VerticalResolution;

    public Jpeg(byte data, uint width, uint height, uint horizontalResolution, uint verticalResolution)
    {
    this.Data = data;
    this.Width = width;
    this.Height = height;
    this.HorizontalResolution = horizontalResolution;
    this.VerticalResolution = verticalResolution;
    }

    public static Jpeg FromBitmapFrame(BitmapFrame bitmap, long quality)
    {
    Jpeg jpeg;

    using (var stream = new MemoryStream())
    {
    JpegBitmapEncoder encoder = new JpegBitmapEncoder();
    encoder.QualityLevel = 90;
    encoder.Frames.Add(bitmap);
    encoder.Save(stream);

    jpeg = new Jpeg(stream.ToArray(), (uint)bitmap.Width, (uint)bitmap.Height, (uint)bitmap.DpiX, (uint)bitmap.DpiY);
    }

    return jpeg;
    }

    public static Jpeg FromBitmap(Bitmap bitmap, long quality)
    {
    Jpeg jpeg;
    using (var stream = new MemoryStream())
    {
    ImageCodecInfo jpgEncoder = GetEncoder(ImageFormat.Jpeg);
    Encoder encoder = Encoder.Quality;
    EncoderParameters parameters = new EncoderParameters(1);
    parameters.Param[0] = new EncoderParameter(encoder, quality);
    bitmap.Save(stream, jpgEncoder, parameters);

    jpeg = new Jpeg(stream.ToArray(), (uint) bitmap.Width, (uint) bitmap.Height, (uint) bitmap.HorizontalResolution, (uint) bitmap.VerticalResolution);
    }

    return jpeg;
    }

    private static ImageCodecInfo GetEncoder(ImageFormat format)
    {
    ImageCodecInfo codecs = ImageCodecInfo.GetImageDecoders();
    foreach (ImageCodecInfo codec in codecs)
    {
    if (codec.FormatID == format.Guid)
    {
    return codec;
    }
    }
    return null;
    }
    }
    }


    Next, a class to create the TIFF image



    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.IO;
    using System.Windows.Media.Imaging;

    namespace TIFF
    {
    public static class JpegTiff
    {
    public static byte Create(List<BitmapFrame> frames, long quality)
    {
    List<Jpeg> jpegs = new List<Jpeg>();

    foreach (var frame in frames)
    {
    jpegs.Add(Jpeg.FromBitmapFrame(frame, quality));
    }

    return WrapJpegs(jpegs);
    }

    public static byte Create(List<Bitmap> bitmaps, string filename, long quality)
    {
    List<Jpeg> jpegs = new List<Jpeg>();

    foreach (var bitmap in bitmaps)
    {
    jpegs.Add(Jpeg.FromBitmap(bitmap, quality));
    }

    return WrapJpegs(jpegs);
    }

    private static byte WrapJpegs(List<Jpeg> jpegs)
    {
    if (jpegs == null || jpegs.Count == 0 || jpegs.FindIndex(b => b.Data.Length == 0) > -1)
    throw new ArgumentNullException("Image Data must not be null or empty");

    MemoryStream tiffData = new MemoryStream();
    BinaryWriter writer = new BinaryWriter(tiffData);
    uint offset = 8; // size of header, offset to IFD
    ushort entryCount = 14; // entries per IFD

    #region IFH - Image file header

    // magic number
    if (BitConverter.IsLittleEndian)
    writer.Write(0x002A4949);
    else
    writer.Write(0x4D4D002A);

    // offset to (first) IFD
    writer.Write(offset);

    #endregion IFH

    #region IFD Image file directory

    // write image file directories for each jpeg
    for (int i = 0; offset > 0; i++)
    {
    var jpeg = jpegs[i];
    uint width = jpeg.Width;
    uint length = jpeg.Height;
    uint xres = jpeg.HorizontalResolution;
    uint yres = jpeg.VerticalResolution;

    // count of entries:
    writer.Write(entryCount);

    offset += 6 + 12 * (uint)entryCount; // add lengths of entries, entry-count and next-ifd-offset

    // TIFF-fields / IFD-entrys:
    // {TAG, TYPE (3 = short, 4 = long, 5 = rational), COUNT, VALUE/OFFSET}
    uint[,] fields = new uint[,] {
    {254, 4, 1, 0}, // NewSubfileType
    {256, 4, 1, width}, // ImageWidth
    {257, 4, 1, length}, // ImageLength
    {258, 3, 3, offset}, // BitsPerSample
    {259, 3, 1, 7}, // Compression (new JPEG)
    {262, 3, 1, 6}, //PhotometricInterpretation (YCbCr)
    {273, 4, 1, offset + 22}, // StripOffsets (offset IFH + entries + values of BitsPerSample & YResolution & XResolution)
    {277, 3, 1, 3}, // SamplesPerPixel
    {278, 4, 1, length}, // RowsPerStrip
    {279, 4, 1, (uint)jpegs[i].Data.LongLength}, // StripByteCounts
    {282, 5, 1, offset + 6}, // XResolution (offset IFH + entries + values of BitsPerSample)
    {283, 5, 1, offset + 14}, // YResolution (offset IFH + entries + values of BitsPerSample & YResolution)
    {284, 3, 1, 1}, // PlanarConfiguration (chunky)
    {296, 3, 1, 2} // ResolutionUnit
    };

    // write fields
    for (int f = 0; f < fields.GetLength(0); f++)
    {
    writer.Write((ushort)fields[f, 0]);
    writer.Write((ushort)fields[f, 1]);
    writer.Write(fields[f, 2]);
    writer.Write(fields[f, 3]);
    }

    // offset of next IFD
    if (i == jpegs.Count - 1)
    offset = 0;
    else
    offset += 22 + (uint)jpegs[i].Data.LongLength; // add values (of fields) length and jpeg length
    writer.Write(offset);

    #region values of fields

    // BitsPerSample
    writer.Write((ushort)8);
    writer.Write((ushort)8);
    writer.Write((ushort)8);

    // XResolution
    writer.Write(xres);
    writer.Write(1);

    // YResolution
    writer.Write(yres);
    writer.Write(1);

    #endregion values of fields

    // actual image Data
    writer.Write(jpegs[i].Data);
    }
    #endregion IFD

    writer.Close();
    return tiffData.ToArray();
    }
    }
    }


    It could probably be improved by passing in the stream to write to instead of returning a byte array.










    share|improve this question











    $endgroup$















      0












      0








      0





      $begingroup$


      I was searching for a long time how to write multi-page TIFFs with the JPEG encoding. TIFFs support JPEG encoded frames but the built-in encoder in .NET Framework does not have JPEG as a compression option.



      The code is based on the answer to this question: https://stackoverflow.com/questions/14811496/tiff-with-jpeg-compression-much-larger-than-original-jpeg
      but does not rely on the FreeImage library.



      First up, a class to convert BitmapFrame or Bitmap to a JPEG image:



      using System.Drawing;
      using System.Drawing.Imaging;
      using System.IO;
      using System.Windows.Media.Imaging;

      namespace TIFF
      {
      public class Jpeg
      {
      public byte Data;
      public uint Width;
      public uint Height;
      public uint HorizontalResolution;
      public uint VerticalResolution;

      public Jpeg(byte data, uint width, uint height, uint horizontalResolution, uint verticalResolution)
      {
      this.Data = data;
      this.Width = width;
      this.Height = height;
      this.HorizontalResolution = horizontalResolution;
      this.VerticalResolution = verticalResolution;
      }

      public static Jpeg FromBitmapFrame(BitmapFrame bitmap, long quality)
      {
      Jpeg jpeg;

      using (var stream = new MemoryStream())
      {
      JpegBitmapEncoder encoder = new JpegBitmapEncoder();
      encoder.QualityLevel = 90;
      encoder.Frames.Add(bitmap);
      encoder.Save(stream);

      jpeg = new Jpeg(stream.ToArray(), (uint)bitmap.Width, (uint)bitmap.Height, (uint)bitmap.DpiX, (uint)bitmap.DpiY);
      }

      return jpeg;
      }

      public static Jpeg FromBitmap(Bitmap bitmap, long quality)
      {
      Jpeg jpeg;
      using (var stream = new MemoryStream())
      {
      ImageCodecInfo jpgEncoder = GetEncoder(ImageFormat.Jpeg);
      Encoder encoder = Encoder.Quality;
      EncoderParameters parameters = new EncoderParameters(1);
      parameters.Param[0] = new EncoderParameter(encoder, quality);
      bitmap.Save(stream, jpgEncoder, parameters);

      jpeg = new Jpeg(stream.ToArray(), (uint) bitmap.Width, (uint) bitmap.Height, (uint) bitmap.HorizontalResolution, (uint) bitmap.VerticalResolution);
      }

      return jpeg;
      }

      private static ImageCodecInfo GetEncoder(ImageFormat format)
      {
      ImageCodecInfo codecs = ImageCodecInfo.GetImageDecoders();
      foreach (ImageCodecInfo codec in codecs)
      {
      if (codec.FormatID == format.Guid)
      {
      return codec;
      }
      }
      return null;
      }
      }
      }


      Next, a class to create the TIFF image



      using System;
      using System.Collections.Generic;
      using System.Drawing;
      using System.IO;
      using System.Windows.Media.Imaging;

      namespace TIFF
      {
      public static class JpegTiff
      {
      public static byte Create(List<BitmapFrame> frames, long quality)
      {
      List<Jpeg> jpegs = new List<Jpeg>();

      foreach (var frame in frames)
      {
      jpegs.Add(Jpeg.FromBitmapFrame(frame, quality));
      }

      return WrapJpegs(jpegs);
      }

      public static byte Create(List<Bitmap> bitmaps, string filename, long quality)
      {
      List<Jpeg> jpegs = new List<Jpeg>();

      foreach (var bitmap in bitmaps)
      {
      jpegs.Add(Jpeg.FromBitmap(bitmap, quality));
      }

      return WrapJpegs(jpegs);
      }

      private static byte WrapJpegs(List<Jpeg> jpegs)
      {
      if (jpegs == null || jpegs.Count == 0 || jpegs.FindIndex(b => b.Data.Length == 0) > -1)
      throw new ArgumentNullException("Image Data must not be null or empty");

      MemoryStream tiffData = new MemoryStream();
      BinaryWriter writer = new BinaryWriter(tiffData);
      uint offset = 8; // size of header, offset to IFD
      ushort entryCount = 14; // entries per IFD

      #region IFH - Image file header

      // magic number
      if (BitConverter.IsLittleEndian)
      writer.Write(0x002A4949);
      else
      writer.Write(0x4D4D002A);

      // offset to (first) IFD
      writer.Write(offset);

      #endregion IFH

      #region IFD Image file directory

      // write image file directories for each jpeg
      for (int i = 0; offset > 0; i++)
      {
      var jpeg = jpegs[i];
      uint width = jpeg.Width;
      uint length = jpeg.Height;
      uint xres = jpeg.HorizontalResolution;
      uint yres = jpeg.VerticalResolution;

      // count of entries:
      writer.Write(entryCount);

      offset += 6 + 12 * (uint)entryCount; // add lengths of entries, entry-count and next-ifd-offset

      // TIFF-fields / IFD-entrys:
      // {TAG, TYPE (3 = short, 4 = long, 5 = rational), COUNT, VALUE/OFFSET}
      uint[,] fields = new uint[,] {
      {254, 4, 1, 0}, // NewSubfileType
      {256, 4, 1, width}, // ImageWidth
      {257, 4, 1, length}, // ImageLength
      {258, 3, 3, offset}, // BitsPerSample
      {259, 3, 1, 7}, // Compression (new JPEG)
      {262, 3, 1, 6}, //PhotometricInterpretation (YCbCr)
      {273, 4, 1, offset + 22}, // StripOffsets (offset IFH + entries + values of BitsPerSample & YResolution & XResolution)
      {277, 3, 1, 3}, // SamplesPerPixel
      {278, 4, 1, length}, // RowsPerStrip
      {279, 4, 1, (uint)jpegs[i].Data.LongLength}, // StripByteCounts
      {282, 5, 1, offset + 6}, // XResolution (offset IFH + entries + values of BitsPerSample)
      {283, 5, 1, offset + 14}, // YResolution (offset IFH + entries + values of BitsPerSample & YResolution)
      {284, 3, 1, 1}, // PlanarConfiguration (chunky)
      {296, 3, 1, 2} // ResolutionUnit
      };

      // write fields
      for (int f = 0; f < fields.GetLength(0); f++)
      {
      writer.Write((ushort)fields[f, 0]);
      writer.Write((ushort)fields[f, 1]);
      writer.Write(fields[f, 2]);
      writer.Write(fields[f, 3]);
      }

      // offset of next IFD
      if (i == jpegs.Count - 1)
      offset = 0;
      else
      offset += 22 + (uint)jpegs[i].Data.LongLength; // add values (of fields) length and jpeg length
      writer.Write(offset);

      #region values of fields

      // BitsPerSample
      writer.Write((ushort)8);
      writer.Write((ushort)8);
      writer.Write((ushort)8);

      // XResolution
      writer.Write(xres);
      writer.Write(1);

      // YResolution
      writer.Write(yres);
      writer.Write(1);

      #endregion values of fields

      // actual image Data
      writer.Write(jpegs[i].Data);
      }
      #endregion IFD

      writer.Close();
      return tiffData.ToArray();
      }
      }
      }


      It could probably be improved by passing in the stream to write to instead of returning a byte array.










      share|improve this question











      $endgroup$




      I was searching for a long time how to write multi-page TIFFs with the JPEG encoding. TIFFs support JPEG encoded frames but the built-in encoder in .NET Framework does not have JPEG as a compression option.



      The code is based on the answer to this question: https://stackoverflow.com/questions/14811496/tiff-with-jpeg-compression-much-larger-than-original-jpeg
      but does not rely on the FreeImage library.



      First up, a class to convert BitmapFrame or Bitmap to a JPEG image:



      using System.Drawing;
      using System.Drawing.Imaging;
      using System.IO;
      using System.Windows.Media.Imaging;

      namespace TIFF
      {
      public class Jpeg
      {
      public byte Data;
      public uint Width;
      public uint Height;
      public uint HorizontalResolution;
      public uint VerticalResolution;

      public Jpeg(byte data, uint width, uint height, uint horizontalResolution, uint verticalResolution)
      {
      this.Data = data;
      this.Width = width;
      this.Height = height;
      this.HorizontalResolution = horizontalResolution;
      this.VerticalResolution = verticalResolution;
      }

      public static Jpeg FromBitmapFrame(BitmapFrame bitmap, long quality)
      {
      Jpeg jpeg;

      using (var stream = new MemoryStream())
      {
      JpegBitmapEncoder encoder = new JpegBitmapEncoder();
      encoder.QualityLevel = 90;
      encoder.Frames.Add(bitmap);
      encoder.Save(stream);

      jpeg = new Jpeg(stream.ToArray(), (uint)bitmap.Width, (uint)bitmap.Height, (uint)bitmap.DpiX, (uint)bitmap.DpiY);
      }

      return jpeg;
      }

      public static Jpeg FromBitmap(Bitmap bitmap, long quality)
      {
      Jpeg jpeg;
      using (var stream = new MemoryStream())
      {
      ImageCodecInfo jpgEncoder = GetEncoder(ImageFormat.Jpeg);
      Encoder encoder = Encoder.Quality;
      EncoderParameters parameters = new EncoderParameters(1);
      parameters.Param[0] = new EncoderParameter(encoder, quality);
      bitmap.Save(stream, jpgEncoder, parameters);

      jpeg = new Jpeg(stream.ToArray(), (uint) bitmap.Width, (uint) bitmap.Height, (uint) bitmap.HorizontalResolution, (uint) bitmap.VerticalResolution);
      }

      return jpeg;
      }

      private static ImageCodecInfo GetEncoder(ImageFormat format)
      {
      ImageCodecInfo codecs = ImageCodecInfo.GetImageDecoders();
      foreach (ImageCodecInfo codec in codecs)
      {
      if (codec.FormatID == format.Guid)
      {
      return codec;
      }
      }
      return null;
      }
      }
      }


      Next, a class to create the TIFF image



      using System;
      using System.Collections.Generic;
      using System.Drawing;
      using System.IO;
      using System.Windows.Media.Imaging;

      namespace TIFF
      {
      public static class JpegTiff
      {
      public static byte Create(List<BitmapFrame> frames, long quality)
      {
      List<Jpeg> jpegs = new List<Jpeg>();

      foreach (var frame in frames)
      {
      jpegs.Add(Jpeg.FromBitmapFrame(frame, quality));
      }

      return WrapJpegs(jpegs);
      }

      public static byte Create(List<Bitmap> bitmaps, string filename, long quality)
      {
      List<Jpeg> jpegs = new List<Jpeg>();

      foreach (var bitmap in bitmaps)
      {
      jpegs.Add(Jpeg.FromBitmap(bitmap, quality));
      }

      return WrapJpegs(jpegs);
      }

      private static byte WrapJpegs(List<Jpeg> jpegs)
      {
      if (jpegs == null || jpegs.Count == 0 || jpegs.FindIndex(b => b.Data.Length == 0) > -1)
      throw new ArgumentNullException("Image Data must not be null or empty");

      MemoryStream tiffData = new MemoryStream();
      BinaryWriter writer = new BinaryWriter(tiffData);
      uint offset = 8; // size of header, offset to IFD
      ushort entryCount = 14; // entries per IFD

      #region IFH - Image file header

      // magic number
      if (BitConverter.IsLittleEndian)
      writer.Write(0x002A4949);
      else
      writer.Write(0x4D4D002A);

      // offset to (first) IFD
      writer.Write(offset);

      #endregion IFH

      #region IFD Image file directory

      // write image file directories for each jpeg
      for (int i = 0; offset > 0; i++)
      {
      var jpeg = jpegs[i];
      uint width = jpeg.Width;
      uint length = jpeg.Height;
      uint xres = jpeg.HorizontalResolution;
      uint yres = jpeg.VerticalResolution;

      // count of entries:
      writer.Write(entryCount);

      offset += 6 + 12 * (uint)entryCount; // add lengths of entries, entry-count and next-ifd-offset

      // TIFF-fields / IFD-entrys:
      // {TAG, TYPE (3 = short, 4 = long, 5 = rational), COUNT, VALUE/OFFSET}
      uint[,] fields = new uint[,] {
      {254, 4, 1, 0}, // NewSubfileType
      {256, 4, 1, width}, // ImageWidth
      {257, 4, 1, length}, // ImageLength
      {258, 3, 3, offset}, // BitsPerSample
      {259, 3, 1, 7}, // Compression (new JPEG)
      {262, 3, 1, 6}, //PhotometricInterpretation (YCbCr)
      {273, 4, 1, offset + 22}, // StripOffsets (offset IFH + entries + values of BitsPerSample & YResolution & XResolution)
      {277, 3, 1, 3}, // SamplesPerPixel
      {278, 4, 1, length}, // RowsPerStrip
      {279, 4, 1, (uint)jpegs[i].Data.LongLength}, // StripByteCounts
      {282, 5, 1, offset + 6}, // XResolution (offset IFH + entries + values of BitsPerSample)
      {283, 5, 1, offset + 14}, // YResolution (offset IFH + entries + values of BitsPerSample & YResolution)
      {284, 3, 1, 1}, // PlanarConfiguration (chunky)
      {296, 3, 1, 2} // ResolutionUnit
      };

      // write fields
      for (int f = 0; f < fields.GetLength(0); f++)
      {
      writer.Write((ushort)fields[f, 0]);
      writer.Write((ushort)fields[f, 1]);
      writer.Write(fields[f, 2]);
      writer.Write(fields[f, 3]);
      }

      // offset of next IFD
      if (i == jpegs.Count - 1)
      offset = 0;
      else
      offset += 22 + (uint)jpegs[i].Data.LongLength; // add values (of fields) length and jpeg length
      writer.Write(offset);

      #region values of fields

      // BitsPerSample
      writer.Write((ushort)8);
      writer.Write((ushort)8);
      writer.Write((ushort)8);

      // XResolution
      writer.Write(xres);
      writer.Write(1);

      // YResolution
      writer.Write(yres);
      writer.Write(1);

      #endregion values of fields

      // actual image Data
      writer.Write(jpegs[i].Data);
      }
      #endregion IFD

      writer.Close();
      return tiffData.ToArray();
      }
      }
      }


      It could probably be improved by passing in the stream to write to instead of returning a byte array.







      c# image wpf compression






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited 1 min ago









      Heslacher

      45.1k463159




      45.1k463159










      asked 48 mins ago









      geometrikalgeometrikal

      220413




      220413






















          0






          active

          oldest

          votes











          Your Answer





          StackExchange.ifUsing("editor", function () {
          return StackExchange.using("mathjaxEditing", function () {
          StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
          StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
          });
          });
          }, "mathjax-editing");

          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "196"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: false,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f214812%2fwpf-bitmap-bitmapframe-to-multi-page-multi-frame-tiff-with-jpeg-encoding%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          0






          active

          oldest

          votes








          0






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes
















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Code Review Stack Exchange!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          Use MathJax to format equations. MathJax reference.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f214812%2fwpf-bitmap-bitmapframe-to-multi-page-multi-frame-tiff-with-jpeg-encoding%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          How to make a Squid Proxy server?

          Is this a new Fibonacci Identity?

          19世紀