/// <summary>
/// Class that allows to get information about BMP page.
/// </summary>
public class SimpleBmpPage : Vintasoft.Imaging.Codecs.ImageFiles.ImagePage
{
#region Fields
/// <summary>
/// BMP page info header length.
/// </summary>
internal const uint BITMAPINFOHEADER_LENGTH = 40;
/// <summary>
/// Image stride (row width in bytes).
/// </summary>
int _stride;
/// <summary>
/// Indicates that image raw data is reversed.
/// </summary>
bool _reverseReadWrite;
/// <summary>
/// Image raw data offset, in bytes.
/// </summary>
long _offsetToImageData;
/// <summary>
/// Image length, in bytes.
/// </summary>
uint _imageSize;
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="SimpleBmpPage"/> class.
/// </summary>
/// <param name="source">Source of the image page.</param>
/// <param name="offset">Offset of the image page in the source.</param>
public SimpleBmpPage(Vintasoft.Imaging.Codecs.ImageFiles.ImageFileSource source, long offset)
: base(source, offset)
{
_offsetToImageData = offset;
Parse();
}
/// <summary>
/// Initializes a new instance of the <see cref="SimpleBmpPage"/> class.
/// </summary>
/// <param name="source">Source of the image page.</param>
/// <param name="image">New image.</param>
public SimpleBmpPage(Vintasoft.Imaging.Codecs.ImageFiles.ImageFileSource source,
Vintasoft.Imaging.VintasoftImage image)
: base(source, 0)
{
// sets the image settings
_offsetToImageData = 0;
_imagePixelFormat = image.PixelFormat;
_width = image.Width;
_height = image.Height;
_resolution = image.Resolution;
_hasResolution = true;
_bitsPerPixel = image.BitsPerPixel;
// calculate stride of image
_stride = Vintasoft.Imaging.VintasoftBitmap.GetStride(_imagePixelFormat, Width);
// image is reverse
_reverseReadWrite = true;
// encode image data
EncodeImageData(Source, image);
// set the image length
_imageSize = (uint)Source.Length;
// set bmp page length
SetLength(BITMAPINFOHEADER_LENGTH + _imageSize);
}
#endregion
#region Properties
int _width;
/// <summary>
/// Gets the width, in pixels, of the image page.
/// </summary>
public override int Width
{
get
{
return _width;
}
}
int _height;
/// <summary>
/// Gets the height, in pixels, of the image page.
/// </summary>
public override int Height
{
get
{
return System.Math.Abs(_height);
}
}
int _bitsPerPixel;
/// <summary>
/// Gets the bit depth of the image page.
/// </summary>
public override int BitsPerPixel
{
get
{
return _bitsPerPixel;
}
}
Vintasoft.Imaging.Palette _palette = null;
/// <summary>
/// Gets the palette of the image page.
/// </summary>
public override Vintasoft.Imaging.Palette Palette
{
get
{
return _palette;
}
}
Vintasoft.Imaging.Resolution _resolution = Vintasoft.Imaging.Resolution.Empty;
/// <summary>
/// Gets or sets the resolution, in pixels per inch, of the image page.
/// </summary>
/// <value>
/// The resolution loaded from an image metadata if <see cref="HasResolution"/> returns <b>true</b>;
/// otherwise, the default (screen) resolution.
/// </value>
/// <seealso cref="HasResolution"/>
public override Vintasoft.Imaging.Resolution Resolution
{
get
{
if (_resolution == Vintasoft.Imaging.Resolution.Empty)
return Vintasoft.Imaging.ImagingEnvironment.ScreenResolution;
return _resolution;
}
set
{
throw new System.NotImplementedException();
}
}
bool _hasResolution = false;
/// <summary>
/// Gets a value indicating whether the information about image resolution is stored
/// in a BMP page.
/// </summary>
/// <value>
/// <b>True</b> - the <see cref="Resolution"/> property returns resolution loaded from image file;<br />
/// <b>false</b> - the <see cref="Resolution"/> property returns the default (screen) resolution.
/// </value>
/// <seealso cref="Resolution"/>
public override bool HasResolution
{
get
{
return _hasResolution;
}
}
Vintasoft.Imaging.PixelFormat _imagePixelFormat = Vintasoft.Imaging.PixelFormat.Undefined;
/// <summary>
/// Gets the pixel format of this page.
/// </summary>
public Vintasoft.Imaging.PixelFormat PixelFormat
{
get
{
return _imagePixelFormat;
}
}
#endregion
#region Methods
#region PUBLIC
/// <summary>
/// Gets the image associated with this image page.
/// </summary>
/// <param name="decodingSettings">Decoding settings used for decode the image of page.</param>
/// <param name="progressDelegate">Progress delegate.
/// Can be set to null (Nothing in Visual Basic).</param>
/// <returns>The image associated with this BMP page if image was loaded successfully;
/// otherwise, null.</returns>
public override Vintasoft.Imaging.VintasoftImage GetImage(
Vintasoft.Imaging.Codecs.Decoders.DecodingSettings decodingSettings,
System.EventHandler<Vintasoft.Imaging.ProgressEventArgs> progressDelegate)
{
// create image
Vintasoft.Imaging.VintasoftImage image = new Vintasoft.Imaging.VintasoftImage(Width, Height, _imagePixelFormat);
// if resolution is empty
if (_resolution.IsEmpty())
image.Resolution = Vintasoft.Imaging.ImagingEnvironment.ScreenResolution;
else
{
_hasResolution = true;
image.Resolution = _resolution;
}
// open pixel manipulator
Vintasoft.Imaging.PixelManipulator pixelManipulator = image.OpenPixelManipulator();
System.Drawing.Rectangle lockPixelsRectangle = new System.Drawing.Rectangle(0, 0, Width, Height);
// lock pixels
pixelManipulator.LockPixelsForSerialAccess(lockPixelsRectangle, Vintasoft.Imaging.BitmapLockMode.Write,
_imagePixelFormat, new byte[_stride], _reverseReadWrite);
try
{
// lock the original image source (image source from which BMP page must be read)
lock (Source.Stream)
{
// go to the start of image data in original image source
Source.Position = _offsetToImageData;
// get serial buffer of pixel manipulator
byte[] buffer = pixelManipulator.SerialBuffer;
int yStart = Height - 1;
int previousProgress = -1;
double progressStep = 100.0 / yStart;
double currentProgress = -progressStep;
// for each image row
for (int y = yStart; y >= 0; y--)
{
// if progress must be generated
if (progressDelegate != null)
{
// calculate current progress
currentProgress += progressStep;
// if previous and current progress are different
if (previousProgress != (int)System.Math.Round(currentProgress))
{
// get the progress integer value
previousProgress = (int)System.Math.Round(currentProgress);
// raise the progress event
progressDelegate(this, new Vintasoft.Imaging.ProgressEventArgs(previousProgress));
}
}
// read row data from image source
Source.ReadBytes(buffer);
// write row data to the pixel manipulator
pixelManipulator.WriteRowAndMoveToNext();
}
}
}
finally
{
// unlock pixels
pixelManipulator.UnlockPixels();
// close pixel manipulator
image.ClosePixelManipulator(true);
}
return image;
}
/// <summary>
/// Saves the page to the image source.
/// </summary>
/// <param name="source">Image source.</param>
/// <param name="progressController">Progress controller.</param>
public override void Save(Vintasoft.Imaging.Codecs.ImageFiles.ImageFileSource source,
Vintasoft.Imaging.Utils.IProgressController progressController)
{
// raise the Saving event
OnSaving(System.EventArgs.Empty);
// if progress controller is specified
if (progressController != null)
{
// specify that there is only one progress stage
progressController.Start(1);
// start the progress stage
progressController.NextStage(this, false);
}
// lock the destination image source (image source where BMP page must be written)
lock (source.Stream)
{
// write header to the destination image source
WriteHeader(source);
// lock the original image source (image source from which BMP page must be read)
lock (Source.Stream)
{
// go to the start of image data in original image source
Source.Position = _offsetToImageData;
// create buffer for row data
byte[] buffer = new byte[_stride];
// if progress controller is specified
if (progressController != null)
// specify the count of progress steps
progressController.StartSteps((int)System.Math.Ceiling((double)_imageSize / buffer.Length));
uint writeBytesCount = 0;
// while the count of written bytes less than image size
while (writeBytesCount != _imageSize)
{
// if progress controller is specified
if (progressController != null)
{
// raise progress and check if saving process must be canceled
if (!progressController.NextStep(this, true))
// cancel saving process
break;
}
// get count of bytes to read
int bytesToRead = (int)System.Math.Min(_imageSize - writeBytesCount, buffer.Length);
// read image row data from original image source
this.Source.ReadBytes(buffer, 0, bytesToRead);
// write image row data to the destination image source
source.WriteBytes(buffer, 0, bytesToRead);
// update the count of written bytes
writeBytesCount += (uint)bytesToRead;
}
// if progress controller is specified
if (progressController != null)
// finish a sequence of progress steps
progressController.FinishSteps(this);
}
}
// if progress controller is specified
if (progressController != null)
// finish the progress stage
progressController.Finish(this);
// raise the Saved event
OnSaved(new Vintasoft.Imaging.Codecs.ImageFiles.ImageFileBlockSourceInfoChangedEventArgs());
}
#endregion
#region PROTECTED
/// <summary>
/// Parse the BMP page.
/// </summary>
protected override void Parse()
{
// lock the original image source (image source from which BMP page must be read)
lock (Source.Stream)
{
// read info block length
uint infoBlockLength = Source.ReadUInt32();
// if info block is not standard
if (infoBlockLength != BITMAPINFOHEADER_LENGTH)
throw new System.NotSupportedException("Unsupported bitmap info block.");
// LONG biWidth
_width = Source.ReadInt32();
// LONG biHeight
_height = Source.ReadInt32();
_reverseReadWrite = _height > 0;
// WORD biPlanes
Source.Position += 2;
// WORD biBitCount
System.UInt16 bitCount = Source.ReadUInt16();
_bitsPerPixel = bitCount;
switch (bitCount)
{
case 24:
_imagePixelFormat = Vintasoft.Imaging.PixelFormat.Bgr24;
break;
case 32:
_imagePixelFormat = Vintasoft.Imaging.PixelFormat.Bgr32;
break;
default:
throw new System.NotSupportedException("Unsupported pixel format.");
}
// calculate stride
_stride = Vintasoft.Imaging.VintasoftBitmap.GetStride(_imagePixelFormat, Width);
// DWORD biCompression;
System.UInt32 compression = Source.ReadUInt32();
// if image is compressed
if (compression != 0)
throw new System.NotSupportedException("Compressed data is not supported.");
// DWORD biSizeImage;
_imageSize = Source.ReadUInt32();
if (_imageSize == 0)
_imageSize = (uint)(_stride * Height);
// LONG biXPelsPerMeter;
float dpiX = Source.ReadInt32() / (100f * (1f / 2.54f));
// LONG biYPelsPerMeter;
float dpiY = Source.ReadInt32() / (100f * (1f / 2.54f));
// set resolution
_resolution = new Vintasoft.Imaging.Resolution(dpiX, dpiY);
_hasResolution = true;
// set page block
SetLength(BITMAPINFOHEADER_LENGTH + _imageSize);
}
}
#endregion
#region INTERNAL
/// <summary>
/// Writes BMP page header.
/// </summary>
/// <param name="source">Image source where BMP page must be written.</param>
internal void WriteHeader(Vintasoft.Imaging.Codecs.ImageFiles.ImageFileSource source)
{
// lock the destination image source (image source where BMP page must be written)
lock (source.Stream)
{
// DWORD biSize
source.WriteUInt32(BITMAPINFOHEADER_LENGTH);
// LONG biWidth
source.WriteInt32(_width);
// LONG biHeight
source.WriteInt32(_height);
// WORD biPlanes
source.WriteUInt16(1);
// WORD biBitCount
source.WriteUInt16((System.UInt16)_bitsPerPixel);
// DWORD biCompression
source.WriteUInt32(0);
// DWORD biSizeImage
source.WriteUInt32(_imageSize);
int dpiX = 0;
int dpiY = 0;
// if resolution is not empty
if (!_resolution.IsEmpty())
{
dpiX = (int)System.Math.Round(_resolution.Horizontal * (100.0f * (1.0f / 2.54f)));
dpiY = (int)System.Math.Round(_resolution.Vertical * (100.0f * (1.0f / 2.54f)));
}
// LONG biXPelsPerMeter
source.WriteInt32(dpiX);
// LONG biYPelsPerMeter
source.WriteInt32(dpiY);
// DWORD biClrUsed
source.WriteUInt32(0);
// DWORD biClrImportant
source.WriteUInt32(0);
}
}
#endregion
#region PRIVATE
/// <summary>
/// Encodes an image data.
/// </summary>
/// <param name="source">Image source.</param>
/// <param name="image">Image.</param>
private void EncodeImageData(Vintasoft.Imaging.Codecs.ImageFiles.ImageFileSource source,
Vintasoft.Imaging.VintasoftImage image)
{
// open the pixel manipulator
Vintasoft.Imaging.PixelManipulator pixelManipulator = image.OpenPixelManipulator();
// lock pixels
System.Drawing.Rectangle lockPixelsRectangle = new System.Drawing.Rectangle(0, 0, Width, Height);
pixelManipulator.LockPixelsForSerialAccess(lockPixelsRectangle, Vintasoft.Imaging.BitmapLockMode.Write,
_imagePixelFormat, new byte[_stride], _reverseReadWrite);
try
{
// lock the original image source (image source from which BMP page must be read)
lock (Source.Stream)
{
// go to the start of image data in original image source
Source.Position = _offsetToImageData;
// get serial buffer of pixel manipulator
byte[] buffer = pixelManipulator.SerialBuffer;
// for each image row
int yStart = Height - 1;
for (int y = yStart; y >= 0; y--)
{
// read row data from pixel manipulator
pixelManipulator.ReadRowAndMoveToNext();
// write row data to the image source
Source.WriteBytes(buffer);
}
}
}
finally
{
// unlock pixels
pixelManipulator.UnlockPixels();
// close the pixel manipulator
image.ClosePixelManipulator(false);
}
}
#endregion
#endregion
}