CentrED/Imaging/ImagingComponents.pas

1294 lines
41 KiB
Plaintext
Raw Normal View History

{
$Id: ImagingComponents.pas 110 2007-11-18 21:23:59Z galfar $
Vampyre Imaging Library
by Marek Mauder
http://imaginglib.sourceforge.net
The contents of this file are used with permission, subject to the Mozilla
Public License Version 1.1 (the "License"); you may not use this file except
in compliance with the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/MPL-1.1.html
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
the specific language governing rights and limitations under the License.
Alternatively, the contents of this file may be used under the terms of the
GNU Lesser General Public License (the "LGPL License"), in which case the
provisions of the LGPL License are applicable instead of those above.
If you wish to allow use of your version of this file only under the terms
of the LGPL License and not to allow others to use your version of this file
under the MPL, indicate your decision by deleting the provisions above and
replace them with the notice and other provisions required by the LGPL
License. If you do not delete the provisions above, a recipient may use
your version of this file under either the MPL or the LGPL License.
For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html
}
{ This unit contains VCL/CLX/LCL TGraphic descendant which uses Imaging library
for saving and loading.}
unit ImagingComponents;
{$I ImagingOptions.inc}
interface
uses
SysUtils, Types, Classes,
{$IFDEF MSWINDOWS}
Windows,
{$ENDIF}
{$IFDEF COMPONENT_SET_VCL}
Graphics,
{$ENDIF}
{$IFDEF COMPONENT_SET_CLX}
Qt,
QGraphics,
{$ENDIF}
{$IFDEF COMPONENT_SET_LCL}
InterfaceBase,
GraphType,
Graphics,
LCLType,
LCLIntf,
{$ENDIF}
ImagingTypes, Imaging, ImagingClasses;
type
{ Graphic class which uses Imaging to load images.
It has standard TBitmap class as ancestor and it can
Assign also to/from TImageData structres and TBaseImage
classes. For saving is uses inherited TBitmap methods.
This class is automatically registered to TPicture for all
file extensions supported by Imaging (useful only for loading).
If you just want to load images in various formats you can use this
class or simply use TPicture.LoadFromXXX which will create this class
automatically. For TGraphic class that saves with Imaging look
at TImagingGraphicForSave class.}
TImagingGraphic = class(TBitmap)
protected
procedure ReadDataFromStream(Stream: TStream); virtual;
procedure AssignTo(Dest: TPersistent); override;
public
{ Loads new image from the stream. It can load all image
file formats supported by Imaging (and enabled of course)
even though it is called by descendant class capable of
saving only one file format.}
procedure LoadFromStream(Stream: TStream); override;
{ Copies the image contained in Source to this graphic object.
Supports also TBaseImage descendants from ImagingClasses unit. }
procedure Assign(Source: TPersistent); override;
{ Copies the image contained in TBaseImage to this graphic object.}
procedure AssignFromImage(Image: TBaseImage);
{ Copies the current image to TBaseImage object.}
procedure AssignToImage(Image: TBaseImage);
{ Copies the image contained in TImageData structure to this graphic object.}
procedure AssignFromImageData(const ImageData: TImageData);
{ Copies the current image to TImageData structure.}
procedure AssignToImageData(var ImageData: TImageData);
end;
TImagingGraphicClass = class of TImagingGraphic;
{ Base class for file format specific TGraphic classes that use
Imaging for saving. Each descendant class can load all file formats
supported by Imaging but save only one format (TImagingBitmap
for *.bmp, TImagingJpeg for *.jpg). Format specific classes also
allow easy access to Imaging options that affect saving of files
(they are properties here).}
TImagingGraphicForSave = class(TImagingGraphic)
protected
FDefaultFileExt: string;
FSavingFormat: TImageFormat;
procedure WriteDataToStream(Stream: TStream); virtual;
public
constructor Create; override;
{ Saves the current image to the stream. It is saved in the
file format according to the DefaultFileExt property.
So each descendant class can save some other file format.}
procedure SaveToStream(Stream: TStream); override;
{ Returns TImageFileFormat descendant for this graphic class.}
class function GetFileFormat: TImageFileFormat; virtual; abstract;
{$IFDEF COMPONENT_SET_LCL}
{ Returns file extensions of this graphic class.}
class function GetFileExtensions: string; override;
{ Returns default MIME type of this graphic class.}
function GetDefaultMimeType: string; override;
{$ENDIF}
{ Default (the most common) file extension of this graphic class.}
property DefaultFileExt: string read FDefaultFileExt;
end;
TImagingGraphicForSaveClass = class of TImagingGraphicForSave;
{$IFDEF LINK_BITMAP}
{ TImagingGraphic descendant for loading/saving Windows bitmaps.
VCL/CLX/LCL all have native support for bitmaps so you might
want to disable this class (although you can save bitmaps with
RLE compression with this class).}
TImagingBitmap = class(TImagingGraphicForSave)
protected
FUseRLE: Boolean;
public
constructor Create; override;
procedure SaveToStream(Stream: TStream); override;
class function GetFileFormat: TImageFileFormat; override;
{ See ImagingBitmapRLE option for details.}
property UseRLE: Boolean read FUseRLE write FUseRLE;
end;
{$ENDIF}
{$IFDEF LINK_JPEG}
{ TImagingGraphic descendant for loading/saving JPEG images.}
TImagingJpeg = class(TImagingGraphicForSave)
protected
FQuality: LongInt;
FProgressive: Boolean;
public
constructor Create; override;
procedure SaveToStream(Stream: TStream); override;
class function GetFileFormat: TImageFileFormat; override;
{$IFDEF COMPONENT_SET_LCL}
function GetDefaultMimeType: string; override;
{$ENDIF}
{ See ImagingJpegQuality option for details.}
property Quality: LongInt read FQuality write FQuality;
{ See ImagingJpegProgressive option for details.}
property Progressive: Boolean read FProgressive write FProgressive;
end;
{$ENDIF}
{$IFDEF LINK_PNG}
{ TImagingGraphic descendant for loading/saving PNG images.}
TImagingPNG = class(TImagingGraphicForSave)
protected
FPreFilter: LongInt;
FCompressLevel: LongInt;
public
constructor Create; override;
procedure SaveToStream(Stream: TStream); override;
class function GetFileFormat: TImageFileFormat; override;
{ See ImagingPNGPreFilter option for details.}
property PreFilter: LongInt read FPreFilter write FPreFilter;
{ See ImagingPNGCompressLevel option for details.}
property CompressLevel: LongInt read FCompressLevel write FCompressLevel;
end;
{$ENDIF}
{$IFDEF LINK_GIF}
{ TImagingGraphic descendant for loading/saving GIF images.}
TImagingGIF = class(TImagingGraphicForSave)
public
class function GetFileFormat: TImageFileFormat; override;
end;
{$ENDIF}
{$IFDEF LINK_TARGA}
{ TImagingGraphic descendant for loading/saving Targa images.}
TImagingTarga = class(TImagingGraphicForSave)
protected
FUseRLE: Boolean;
public
constructor Create; override;
procedure SaveToStream(Stream: TStream); override;
class function GetFileFormat: TImageFileFormat; override;
{ See ImagingTargaRLE option for details.}
property UseRLE: Boolean read FUseRLE write FUseRLE;
end;
{$ENDIF}
{$IFDEF LINK_DDS}
{ Compresssion type used when saving DDS files by TImagingDds.}
TDDSCompresion = (dcNone, dcDXT1, dcDXT3, dcDXT5);
{ TImagingGraphic descendant for loading/saving DDS images.}
TImagingDDS = class(TImagingGraphicForSave)
protected
FCompression: TDDSCompresion;
public
constructor Create; override;
procedure SaveToStream(Stream: TStream); override;
class function GetFileFormat: TImageFileFormat; override;
{ You can choose compression type used when saving DDS file.
dcNone means that file will be saved in the current bitmaps pixel format.}
property Compression: TDDSCompresion read FCompression write FCompression;
end;
{$ENDIF}
{$IFDEF LINK_MNG}
{ TImagingGraphic descendant for loading/saving MNG images.}
TImagingMNG = class(TImagingGraphicForSave)
protected
FLossyCompression: Boolean;
FLossyAlpha: Boolean;
FPreFilter: LongInt;
FCompressLevel: LongInt;
FQuality: LongInt;
FProgressive: Boolean;
public
constructor Create; override;
procedure SaveToStream(Stream: TStream); override;
class function GetFileFormat: TImageFileFormat; override;
{$IFDEF COMPONENT_SET_LCL}
function GetDefaultMimeType: string; override;
{$ENDIF}
{ See ImagingMNGLossyCompression option for details.}
property LossyCompression: Boolean read FLossyCompression write FLossyCompression;
{ See ImagingMNGLossyAlpha option for details.}
property LossyAlpha: Boolean read FLossyAlpha write FLossyAlpha;
{ See ImagingMNGPreFilter option for details.}
property PreFilter: LongInt read FPreFilter write FPreFilter;
{ See ImagingMNGCompressLevel option for details.}
property CompressLevel: LongInt read FCompressLevel write FCompressLevel;
{ See ImagingMNGQuality option for details.}
property Quality: LongInt read FQuality write FQuality;
{ See ImagingMNGProgressive option for details.}
property Progressive: Boolean read FProgressive write FProgressive;
end;
{$ENDIF}
{$IFDEF LINK_JNG}
{ TImagingGraphic descendant for loading/saving JNG images.}
TImagingJNG = class(TImagingGraphicForSave)
protected
FLossyAlpha: Boolean;
FAlphaPreFilter: LongInt;
FAlphaCompressLevel: LongInt;
FQuality: LongInt;
FProgressive: Boolean;
public
constructor Create; override;
procedure SaveToStream(Stream: TStream); override;
class function GetFileFormat: TImageFileFormat; override;
{ See ImagingJNGLossyAlpha option for details.}
property LossyAlpha: Boolean read FLossyAlpha write FLossyAlpha;
{ See ImagingJNGPreFilter option for details.}
property AlphaPreFilter: LongInt read FAlphaPreFilter write FAlphaPreFilter;
{ See ImagingJNGCompressLevel option for details.}
property AlphaCompressLevel: LongInt read FAlphaCompressLevel write FAlphaCompressLevel;
{ See ImagingJNGQuality option for details.}
property Quality: LongInt read FQuality write FQuality;
{ See ImagingJNGProgressive option for details.}
property Progressive: Boolean read FProgressive write FProgressive;
end;
{$ENDIF}
{ Returns bitmap pixel format with the closest match with given data format.}
function DataFormatToPixelFormat(Format: TImageFormat): TPixelFormat;
{ Returns data format with closest match with given bitmap pixel format.}
function PixelFormatToDataFormat(Format: TPixelFormat): TImageFormat;
{ Converts TImageData structure to VCL/CLX/LCL bitmap.}
procedure ConvertDataToBitmap(const Data: TImageData; Bitmap: TBitmap);
{ Converts VCL/CLX/LCL bitmap to TImageData structure.}
procedure ConvertBitmapToData(Bitmap: TBitmap; var Data: TImageData);
{ Converts TBaseImage instance to VCL/CLX/LCL bitmap.}
procedure ConvertImageToBitmap(Image: TBaseImage; Bitmap: TBitmap);
{ Converts VCL/CLX/LCL bitmap to TBaseImage. Image must exist before
procedure is called. It overwrites its current image data.
When Image is TMultiImage only the current image level is overwritten.}
procedure ConvertBitmapToImage(Bitmap: TBitmap; Image: TBaseImage);
{ Displays image stored in TImageData structure onto TCanvas. This procedure
draws image without converting from Imaging format to TBitmap.
Only [ifA8R8G8B8, ifX8R8G8B8] image formats are supported. Use this
when you want displaying images that change frequently (because converting to
TBitmap by ConvertImageDataToBitmap is generally slow). Dest and Src
rectangles represent coordinates in the form (X1, Y1, X2, Y2).}
procedure DisplayImageData(DstCanvas: TCanvas; const DstRect: TRect; const ImageData: TImageData; const SrcRect: TRect);
{ Displays image onto TCanvas at position [DstX, DstY]. This procedure
draws image without converting from Imaging format to TBitmap.
Only [ifA8R8G8B8, ifX8R8G8B8] image formats are supported. Use this
when you want displaying images that change frequently (because converting to
TBitmap by ConvertImageDataToBitmap is generally slow).}
procedure DisplayImage(DstCanvas: TCanvas; DstX, DstY: LongInt; Image: TBaseImage); overload;
{ Displays image onto TCanvas to rectangle DstRect. This procedure
draws image without converting from Imaging format to TBitmap.
Only [ifA8R8G8B8, ifX8R8G8B8] image formats are supported. Use this
when you want displaying images that change frequently (because converting to
TBitmap by ConvertImageDataToBitmap is generally slow).}
procedure DisplayImage(DstCanvas: TCanvas; const DstRect: TRect; Image: TBaseImage); overload;
{ Displays part of the image specified by SrcRect onto TCanvas to rectangle DstRect.
This procedure draws image without converting from Imaging format to TBitmap.
Only [ifA8R8G8B8, ifX8R8G8B8] image formats are supported. Use this
when you want displaying images that change frequently (because converting to
TBitmap by ConvertImageDataToBitmap is generally slow).}
procedure DisplayImage(DstCanvas: TCanvas; const DstRect: TRect; Image: TBaseImage; const SrcRect: TRect); overload;
{$IFDEF MSWINDOWS}
{ Displays image stored in TImageData structure onto Windows device context.
Behaviour is the same as of DisplayImageData.}
procedure DisplayImageDataOnDC(DC: HDC; const DstRect: TRect; const ImageData: TImageData; const SrcRect: TRect);
{$ENDIF}
implementation
uses
{$IF Defined(UNIX) and Defined(COMPONENT_SET_LCL)}
{$IFDEF LCLGTK2}
GLib2, GDK2, GTK2, GTKDef, GTKProc,
{$ELSE}
GDK, GTK, GTKDef, GTKProc,
{$ENDIF}
{$IFEND}
{$IFDEF LINK_BITMAP}
ImagingBitmap,
{$ENDIF}
{$IFDEF LINK_JPEG}
ImagingJpeg,
{$ENDIF}
{$IFDEF LINK_GIF}
ImagingGif,
{$ENDIF}
{$IFDEF LINK_TARGA}
ImagingTarga,
{$ENDIF}
{$IFDEF LINK_DDS}
ImagingDds,
{$ENDIF}
{$IF Defined(LINK_PNG) or Defined(LINK_MNG) or Defined(LINK_JNG)}
ImagingNetworkGraphics,
{$IFEND}
ImagingUtility;
resourcestring
SBadFormatDataToBitmap = 'Cannot find compatible bitmap format for image %s';
SBadFormatBitmapToData = 'Cannot find compatible data format for bitmap %p';
SBadFormatDisplay = 'Unsupported image format passed';
SImagingGraphicName = 'Imaging Graphic AllInOne';
{ Registers types to VCL/CLX/LCL.}
procedure RegisterTypes;
var
I: LongInt;
procedure RegisterFileFormatAllInOne(Format: TImageFileFormat);
var
I: LongInt;
begin
for I := 0 to Format.Extensions.Count - 1 do
TPicture.RegisterFileFormat(Format.Extensions[I], SImagingGraphicName,
TImagingGraphic);
end;
procedure RegisterFileFormat(AClass: TImagingGraphicForSaveClass);
var
I: LongInt;
begin
for I := 0 to AClass.GetFileFormat.Extensions.Count - 1 do
TPicture.RegisterFileFormat(AClass.GetFileFormat.Extensions[I],
AClass.GetFileFormat.Name, AClass);
end;
begin
for I := Imaging.GetFileFormatCount - 1 downto 0 do
RegisterFileFormatAllInOne(Imaging.GetFileFormatAtIndex(I));
{$IFNDEF COMPONENT_SET_CLX}Classes.RegisterClass(TImagingGraphic);{$ENDIF}
{$IFDEF LINK_TARGA}
RegisterFileFormat(TImagingTarga);
{$IFNDEF COMPONENT_SET_CLX}Classes.RegisterClass(TImagingTarga);{$ENDIF}
{$ENDIF}
{$IFDEF LINK_DDS}
RegisterFileFormat(TImagingDDS);
{$IFNDEF COMPONENT_SET_CLX}Classes.RegisterClass(TImagingDDS);{$ENDIF}
{$ENDIF}
{$IFDEF LINK_JNG}
RegisterFileFormat(TImagingJNG);
{$IFNDEF COMPONENT_SET_CLX}Classes.RegisterClass(TImagingJNG);{$ENDIF}
{$ENDIF}
{$IFDEF LINK_MNG}
RegisterFileFormat(TImagingMNG);
{$IFNDEF COMPONENT_SET_CLX}Classes.RegisterClass(TImagingMNG);{$ENDIF}
{$ENDIF}
{$IFDEF LINK_GIF}
RegisterFileFormat(TImagingGIF);
{$IFNDEF COMPONENT_SET_CLX}Classes.RegisterClass(TImagingGIF);{$ENDIF}
{$ENDIF}
{$IFDEF LINK_PNG}
{$IFDEF COMPONENT_SET_LCL}
// Unregister Lazarus<75> default PNG loader which crashes on some PNG files
TPicture.UnregisterGraphicClass(TPortableNetworkGraphic);
{$ENDIF}
RegisterFileFormat(TImagingPNG);
{$IFNDEF COMPONENT_SET_CLX}Classes.RegisterClass(TImagingPNG);{$ENDIF}
{$ENDIF}
{$IFDEF LINK_JPEG}
RegisterFileFormat(TImagingJpeg);
{$IFNDEF COMPONENT_SET_CLX}Classes.RegisterClass(TImagingJpeg);{$ENDIF}
{$ENDIF}
{$IFDEF LINK_BITMAP}
RegisterFileFormat(TImagingBitmap);
{$IFNDEF COMPONENT_SET_CLX}Classes.RegisterClass(TImagingBitmap);{$ENDIF}
{$ENDIF}
end;
{ Unregisters types from VCL/CLX/LCL.}
procedure UnRegisterTypes;
begin
{$IFDEF LINK_BITMAP}
TPicture.UnregisterGraphicClass(TImagingBitmap);
{$IFNDEF COMPONENT_SET_CLX}Classes.UnRegisterClass(TImagingBitmap);{$ENDIF}
{$ENDIF}
{$IFDEF LINK_JPEG}
TPicture.UnregisterGraphicClass(TImagingJpeg);
{$IFNDEF COMPONENT_SET_CLX}Classes.UnRegisterClass(TImagingJpeg);{$ENDIF}
{$ENDIF}
{$IFDEF LINK_PNG}
TPicture.UnregisterGraphicClass(TImagingPNG);
{$IFNDEF COMPONENT_SET_CLX}Classes.UnRegisterClass(TImagingPNG);{$ENDIF}
{$ENDIF}
{$IFDEF LINK_GIF}
TPicture.UnregisterGraphicClass(TImagingGIF);
{$IFNDEF COMPONENT_SET_CLX}Classes.UnRegisterClass(TImagingGIF);{$ENDIF}
{$ENDIF}
{$IFDEF LINK_TARGA}
TPicture.UnregisterGraphicClass(TImagingTarga);
{$IFNDEF COMPONENT_SET_CLX}Classes.UnRegisterClass(TImagingTarga);{$ENDIF}
{$ENDIF}
{$IFDEF LINK_DDS}
TPicture.UnregisterGraphicClass(TImagingDDS);
{$IFNDEF COMPONENT_SET_CLX}Classes.UnRegisterClass(TImagingDDS);{$ENDIF}
{$ENDIF}
TPicture.UnregisterGraphicClass(TImagingGraphic);
{$IFNDEF COMPONENT_SET_CLX}Classes.UnRegisterClass(TImagingGraphic);{$ENDIF}
end;
function DataFormatToPixelFormat(Format: TImageFormat): TPixelFormat;
begin
case Format of
{$IFNDEF COMPONENT_SET_LCL}
ifIndex8: Result := pf8bit;
{$ENDIF}
{$IF (not Defined(COMPONENT_SET_CLX)) and (not Defined(COMPONENT_SET_LCL))}
ifR5G6B5: Result := pf16bit;
ifR8G8B8: Result := pf24bit;
{$IFEND}
ifA8R8G8B8,
ifX8R8G8B8: Result := pf32bit;
else
Result := pfCustom;
end;
end;
function PixelFormatToDataFormat(Format: TPixelFormat): TImageFormat;
begin
case Format of
pf8bit: Result := ifIndex8;
{$IFNDEF COMPONENT_SET_CLX}
pf15bit: Result := ifA1R5G5B5;
pf16bit: Result := ifR5G6B5;
pf24bit: Result := ifR8G8B8;
{$ENDIF}
pf32bit: Result := ifA8R8G8B8;
else
Result := ifUnknown;
end;
end;
procedure ConvertDataToBitmap(const Data: TImageData; Bitmap: TBitmap);
var
I, LineBytes: LongInt;
PF: TPixelFormat;
Info: TImageFormatInfo;
WorkData: TImageData;
{$IFDEF COMPONENT_SET_VCL}
LogPalette: TMaxLogPalette;
{$ENDIF}
{$IFDEF COMPONENT_SET_CLX}
ColorTable: PPalette32;
{$ENDIF}
{$IFDEF COMPONENT_SET_LCL}
RawImage: TRawImage;
ImgHandle, ImgMaskHandle: HBitmap;
{$ENDIF}
begin
PF := DataFormatToPixelFormat(Data.Format);
GetImageFormatInfo(Data.Format, Info);
if PF = pfCustom then
begin
// Convert from formats not supported by Graphics unit
Imaging.InitImage(WorkData);
Imaging.CloneImage(Data, WorkData);
if Info.IsFloatingPoint or Info.HasAlphaChannel or Info.IsSpecial then
Imaging.ConvertImage(WorkData, ifA8R8G8B8)
else
{$IFNDEF COMPONENT_SET_LCL}
if Info.IsIndexed or Info.HasGrayChannel then
Imaging.ConvertImage(WorkData, ifIndex8)
else
{$ENDIF}
{$IF (not Defined(COMPONENT_SET_CLX)) and (not Defined(COMPONENT_SET_LCL))}
if Info.UsePixelFormat then
Imaging.ConvertImage(WorkData, ifR5G6B5)
else
Imaging.ConvertImage(WorkData, ifR8G8B8);
{$ELSE}
Imaging.ConvertImage(WorkData, ifA8R8G8B8);
{$IFEND}
PF := DataFormatToPixelFormat(WorkData.Format);
GetImageFormatInfo(WorkData.Format, Info);
end
else
WorkData := Data;
if PF = pfCustom then
RaiseImaging(SBadFormatDataToBitmap, [ImageToStr(WorkData)]);
LineBytes := WorkData.Width * Info.BytesPerPixel;
{$IFDEF COMPONENT_SET_VCL}
Bitmap.Width := WorkData.Width;
Bitmap.Height := WorkData.Height;
Bitmap.PixelFormat := PF;
if (PF = pf8bit) and (WorkData.Palette <> nil) then
begin
// Copy palette, this must be done before copying bits
FillChar(LogPalette, SizeOf(LogPalette), 0);
LogPalette.palVersion := $300;
LogPalette.palNumEntries := Info.PaletteEntries;
for I := 0 to Info.PaletteEntries - 1 do
with LogPalette do
begin
palPalEntry[I].peRed := WorkData.Palette[I].R;
palPalEntry[I].peGreen := WorkData.Palette[I].G;
palPalEntry[I].peBlue := WorkData.Palette[I].B;
end;
Bitmap.Palette := CreatePalette(PLogPalette(@LogPalette)^);
end;
// Copy scanlines
for I := 0 to WorkData.Height - 1 do
Move(PByteArray(WorkData.Bits)[I * LineBytes], Bitmap.Scanline[I]^, LineBytes);
{$ENDIF}
{$IFDEF COMPONENT_SET_CLX}
Bitmap.Width := WorkData.Width;
Bitmap.Height := WorkData.Height;
Bitmap.PixelFormat := PF;
if (PF = pf8bit) and (WorkData.Palette <> nil) then
begin
// Copy palette
ColorTable := Bitmap.ColorTable;
for I := 0 to Info.PaletteEntries - 1 do
with ColorTable[I] do
begin
R := WorkData.Palette[I].R;
G := WorkData.Palette[I].G;
B := WorkData.Palette[I].B;
end;
end;
// Copy scanlines
for I := 0 to WorkData.Height - 1 do
Move(PByteArray(WorkData.Bits)[I * LineBytes], Bitmap.Scanline[I]^, LineBytes);
{$ENDIF}
{$IFDEF COMPONENT_SET_LCL}
// Create 32bit raw image from image data
FillChar(RawImage, SizeOf(RawImage), 0);
with RawImage.Description do
begin
Width := WorkData.Width;
Height := WorkData.Height;
BitsPerPixel := Info.BytesPerPixel * 8;
Format := ricfRGBA;
LineEnd := rileByteBoundary;
BitOrder := riboBitsInOrder;
ByteOrder := riboLSBFirst;
LineOrder := riloTopToBottom;
AlphaPrec := 8;
RedPrec := 8;
GreenPrec := 8;
BluePrec := 8;
AlphaShift := 24;
RedShift := 16;
GreenShift := 8;
BlueShift := 0;
Depth := 24;
end;
RawImage.Data := WorkData.Bits;
RawImage.DataSize := WorkData.Size;
// Create bitmap from raw image
{ If you get complitation error here upgrade to Lazarus 0.9.24+ }
if RawImage_CreateBitmaps(RawImage, ImgHandle, ImgMaskHandle, False) then
begin
Bitmap.Handle := ImgHandle;
Bitmap.MaskHandle := ImgMaskHandle;
end;
{$ENDIF}
if WorkData.Bits <> Data.Bits then
Imaging.FreeImage(WorkData);
end;
procedure ConvertBitmapToData(Bitmap: TBitmap; var Data: TImageData);
var
I, LineBytes: LongInt;
Format: TImageFormat;
Info: TImageFormatInfo;
{$IFDEF COMPONENT_SET_VCL}
Colors: Word;
LogPalette: TMaxLogPalette;
{$ENDIF}
{$IFDEF COMPONENT_SET_CLX}
ColorTable: PPalette32;
{$ENDIF}
{$IFDEF COMPONENT_SET_LCL}
RawImage: TRawImage;
LineLazBytes: LongInt;
{$ENDIF}
begin
{$IFDEF COMPONENT_SET_LCL}
// In the current Lazarus 0.9.10 Bitmap.PixelFormat property is useless.
// We cannot change bitmap's format by changing it (it will just release
// old image but not convert it to new format) nor we can determine bitmaps's
// current format (it is usually set to pfDevice). So bitmap's format is obtained
// trough RawImage api and cannot be changed to mirror some Imaging format
// (so formats with no coresponding Imaging format cannot be saved now).
{ If you get complitation error here upgrade to Lazarus 0.9.24+ }
if RawImage_DescriptionFromBitmap(Bitmap.Handle, RawImage.Description) then
case RawImage.Description.BitsPerPixel of
8: Format := ifIndex8;
16:
if RawImage.Description.Depth = 15 then
Format := ifA1R5G5B5
else
Format := ifR5G6B5;
24: Format := ifR8G8B8;
32: Format := ifA8R8G8B8;
48: Format := ifR16G16B16;
64: Format := ifA16R16G16B16;
else
Format := ifUnknown;
end;
{$ELSE}
Format := PixelFormatToDataFormat(Bitmap.PixelFormat);
if Format = ifUnknown then
begin
// Convert from formats not supported by Imaging (1/4 bit)
if Bitmap.PixelFormat < pf8bit then
Bitmap.PixelFormat := pf8bit
else
Bitmap.PixelFormat := pf32bit;
Format := PixelFormatToDataFormat(Bitmap.PixelFormat);
end;
{$ENDIF}
if Format = ifUnknown then
RaiseImaging(SBadFormatBitmapToData, []);
Imaging.NewImage(Bitmap.Width, Bitmap.Height, Format, Data);
GetImageFormatInfo(Data.Format, Info);
LineBytes := Data.Width * Info.BytesPerPixel;
{$IFDEF COMPONENT_SET_VCL}
if (Format = ifIndex8) and (GetObject(Bitmap.Palette, SizeOf(Colors),
@Colors) <> 0) then
begin
// Copy palette
GetPaletteEntries(Bitmap.Palette, 0, Colors, LogPalette.palPalEntry);
if Colors > Info.PaletteEntries then
Colors := Info.PaletteEntries;
for I := 0 to Colors - 1 do
with LogPalette do
begin
Data.Palette[I].A := $FF;
Data.Palette[I].R := palPalEntry[I].peRed;
Data.Palette[I].G := palPalEntry[I].peGreen;
Data.Palette[I].B := palPalEntry[I].peBlue;
end;
end;
// Copy scanlines
for I := 0 to Data.Height - 1 do
Move(Bitmap.ScanLine[I]^, PByteArray(Data.Bits)[I * LineBytes], LineBytes);
{$ENDIF}
{$IFDEF COMPONENT_SET_CLX}
if Format = ifIndex8 then
begin
// Copy palette
ColorTable := Bitmap.ColorTable;
for I := 0 to Info.PaletteEntries - 1 do
with ColorTable[I] do
begin
Data.Palette[I].A := $FF;
Data.Palette[I].R := R;
Data.Palette[I].G := G;
Data.Palette[I].B := B;
end;
end;
// Copy scanlines
for I := 0 to Data.Height - 1 do
Move(Bitmap.ScanLine[I]^, PByteArray(Data.Bits)[I * LineBytes], LineBytes);
{$ENDIF}
{$IFDEF COMPONENT_SET_LCL}
// Get raw image from bitmap (mask handle must be 0 or expect violations)
{ If you get complitation error here upgrade to Lazarus 0.9.24+ }
if RawImage_FromBitmap(RawImage, Bitmap.Handle, 0, Classes.Rect(0, 0, Data.Width, Data.Height)) then
begin
LineLazBytes := GetBytesPerLine(Data.Width, RawImage.Description.BitsPerPixel,
RawImage.Description.LineEnd);
// Copy scanlines
for I := 0 to Data.Height - 1 do
Move(PByteArray(RawImage.Data)[I * LineLazBytes],
PByteArray(Data.Bits)[I * LineBytes], LineBytes);
{ If you get complitation error here upgrade to Lazarus 0.9.24+ }
RawImage.FreeData;
end;
{$ENDIF}
end;
procedure ConvertImageToBitmap(Image: TBaseImage; Bitmap: TBitmap);
begin
ConvertDataToBitmap(Image.ImageDataPointer^, Bitmap);
end;
procedure ConvertBitmapToImage(Bitmap: TBitmap; Image: TBaseImage);
begin
ConvertBitmapToData(Bitmap, Image.ImageDataPointer^);
end;
{$IFDEF MSWINDOWS}
procedure DisplayImageDataOnDC(DC: HDC; const DstRect: TRect; const ImageData: TImageData; const SrcRect: TRect);
var
OldMode: Integer;
BitmapInfo: Windows.TBitmapInfo;
begin
if TestImage(ImageData) then
begin
Assert(ImageData.Format in [ifA8R8G8B8, ifX8R8G8B8], SBadFormatDisplay);
OldMode := Windows.SetStretchBltMode(DC, COLORONCOLOR);
FillChar(BitmapInfo, SizeOf(BitmapInfo), 0);
with BitmapInfo.bmiHeader do
begin
biSize := SizeOf(TBitmapInfoHeader);
biPlanes := 1;
biBitCount := 32;
biCompression := BI_RGB;
biWidth := ImageData.Width;
biHeight := -ImageData.Height;
biSizeImage := ImageData.Size;
biXPelsPerMeter := 0;
biYPelsPerMeter := 0;
biClrUsed := 0;
biClrImportant := 0;
end;
try
with SrcRect, ImageData do
Windows.StretchDIBits(DC, DstRect.Left, DstRect.Top,
DstRect.Right - DstRect.Left, DstRect.Bottom - DstRect.Top, Left,
Top, Right - Left, Bottom - Top, Bits, BitmapInfo, DIB_RGB_COLORS, SRCCOPY);
finally
Windows.SetStretchBltMode(DC, OldMode);
end;
end;
end;
{$ENDIF}
procedure DisplayImageData(DstCanvas: TCanvas; const DstRect: TRect; const ImageData: TImageData; const SrcRect: TRect);
{$IF Defined(MSWINDOWS) and not Defined(COMPONENT_SET_CLX)}
begin
DisplayImageDataOnDC(DstCanvas.Handle, DstRect, ImageData, SrcRect);
end;
{$ELSEIF Defined(COMPONENT_SET_CLX)}
var
Bitmap: TBitmap;
//Handle: LongWord;
begin
(*
// It would be nice if this worked:
DstCanvas.Start;
Handle := QPainter_handle(DstCanvas.Handle);
{$IFDEF MSWINDOWS}
DisplayImageDataOnDC(Handle, DstRect, ImageData, SrcRect);
{$ELSE}
DisplayImageDataOnX(Handle, DstRect, ImageData, SrcRect);
{$ENDIF}
DstCanvas.Stop;
*)
Bitmap := TBitmap.Create;
try
ConvertDataToBitmap(ImageData, Bitmap);
DstCanvas.CopyRect(DstRect, Bitmap.Canvas, SrcRect);
finally
Bitmap.Free;
end;
end;
{$ELSEIF Defined(UNIX) and Defined(COMPONENT_SET_LCL)}
procedure GDKDrawBitmap(Dest: HDC; DstX, DstY: Integer; SrcX, SrcY,
SrcWidth, SrcHeight: Integer; ImageData: TImageData);
var
P: TPoint;
begin
P := TGtkDeviceContext(Dest).Offset;
Inc(DstX, P.X);
Inc(DstY, P.Y);
gdk_draw_rgb_32_image(TGtkDeviceContext(Dest).Drawable, TGtkDeviceContext(Dest).GC,
DstX, DstY, SrcWidth, SrcHeight, GDK_RGB_DITHER_NONE,
@PLongWordArray(ImageData.Bits)[SrcY * ImageData.Width + SrcX], ImageData.Width * 4);
end;
var
DisplayImage: TImageData;
NewWidth, NewHeight: Integer;
SrcBounds, DstBounds, DstClip: TRect;
begin
if TestImage(ImageData) then
begin
Assert(ImageData.Format in [ifA8R8G8B8, ifX8R8G8B8], SBadFormatDisplay);
InitImage(DisplayImage);
SrcBounds := RectToBounds(SrcRect);
DstBounds := RectToBounds(DstRect);
WidgetSet.GetClipBox(DstCanvas.Handle, @DstClip);
ClipStretchBounds(SrcBounds.Left, SrcBounds.Top, SrcBounds.Right, SrcBounds.Bottom,
DstBounds.Left, DstBounds.Top, DstBounds.Right, DstBounds.Bottom, ImageData.Width,
ImageData.Height, DstClip);
NewWidth := DstBounds.Right;
NewHeight := DstBounds.Bottom;
if (NewWidth > 0) and (NewHeight > 0) then
begin
if (SrcBounds.Right = NewWidth) and (SrcBounds.Bottom = NewHeight) then
try
CloneImage(ImageData, DisplayImage);
// Swap R-B channels for GTK display compatability!
SwapChannels(DisplayImage, ChannelRed, ChannelBlue);
GDKDrawBitmap(DstCanvas.Handle, DstBounds.Left, DstBounds.Top,
SrcBounds.Left, SrcBounds.Top, NewWidth, NewHeight, DisplayImage);
finally
FreeImage(DisplayImage);
end
else
try
// Create new image with desired dimensions
NewImage(NewWidth, NewHeight, ImageData.Format, DisplayImage);
// Stretch pixels from old image to new one TResizeFilter = (rfNearest, rfBilinear, rfBicubic);
StretchRect(ImageData, SrcBounds.Left, SrcBounds.Top, SrcBounds.Right,
SrcBounds.Bottom, DisplayImage, 0, 0, NewWidth, NewHeight, rfNearest);
// Swap R-B channels for GTK display compatability!
SwapChannels(DisplayImage, ChannelRed, ChannelBlue);
GDKDrawBitmap(DstCanvas.Handle, DstBounds.Left, DstBounds.Top, 0, 0,
NewWidth, NewHeight, DisplayImage);
finally
FreeImage(DisplayImage);
end
end;
end;
end;
{$IFEND}
procedure DisplayImage(DstCanvas: TCanvas; DstX, DstY: LongInt; Image: TBaseImage);
begin
DisplayImageData(DstCanvas, BoundsToRect(DstX, DstY, Image.Width, Image.Height),
Image.ImageDataPointer^, Image.BoundsRect);
end;
procedure DisplayImage(DstCanvas: TCanvas; const DstRect: TRect; Image: TBaseImage);
begin
DisplayImageData(DstCanvas, DstRect, Image.ImageDataPointer^, Image.BoundsRect);
end;
procedure DisplayImage(DstCanvas: TCanvas; const DstRect: TRect; Image: TBaseImage; const SrcRect: TRect);
begin
DisplayImageData(DstCanvas, DstRect, Image.ImageDataPointer^, SrcRect);
end;
{ TImagingGraphic class implementation }
procedure TImagingGraphic.LoadFromStream(Stream: TStream);
begin
ReadDataFromStream(Stream);
end;
procedure TImagingGraphic.ReadDataFromStream(Stream: TStream);
var
Image: TSingleImage;
begin
Image := TSingleImage.Create;
try
Image.LoadFromStream(Stream);
Assign(Image);
finally
Image.Free;
end;
end;
procedure TImagingGraphic.AssignTo(Dest: TPersistent);
var
Arr: TDynImageDataArray;
begin
if Dest is TSingleImage then
begin
AssignToImage(TSingleImage(Dest))
end
else if Dest is TMultiImage then
begin
SetLength(Arr, 1);
AssignToImageData(Arr[0]);
TMultiImage(Dest).CreateFromArray(Arr);
Imaging.FreeImagesInArray(Arr);
end
else
inherited AssignTo(Dest);
end;
procedure TImagingGraphic.Assign(Source: TPersistent);
begin
if Source is TBaseImage then
AssignFromImage(TBaseImage(Source))
else
inherited Assign(Source);
end;
procedure TImagingGraphic.AssignFromImage(Image: TBaseImage);
begin
if (Image <> nil) and Image.Valid then
AssignFromImageData(Image.ImageDataPointer^);
end;
procedure TImagingGraphic.AssignToImage(Image: TBaseImage);
begin
if (Image <> nil) and (Image.ImageDataPointer <> nil) then
AssignToImageData(Image.ImageDataPointer^);
end;
procedure TImagingGraphic.AssignFromImageData(const ImageData: TImageData);
begin
if Imaging.TestImage(ImageData) then
ConvertDataToBitmap(ImageData, Self);
end;
procedure TImagingGraphic.AssignToImageData(var ImageData: TImageData);
begin
Imaging.FreeImage(ImageData);
ConvertBitmapToData(Self, ImageData);
end;
{ TImagingGraphicForSave class implementation }
constructor TImagingGraphicForSave.Create;
begin
inherited Create;
FDefaultFileExt := GetFileFormat.Extensions[0];
FSavingFormat := ifUnknown;
GetFileFormat.CheckOptionsValidity;
end;
procedure TImagingGraphicForSave.WriteDataToStream(Stream: TStream);
var
Image: TSingleImage;
begin
if FDefaultFileExt <> '' then
begin
Image := TSingleImage.Create;
try
Image.Assign(Self);
if FSavingFormat <> ifUnknown then
Image.Format := FSavingFormat;
Image.SaveToStream(FDefaultFileExt, Stream);
finally
Image.Free;
end;
end;
end;
procedure TImagingGraphicForSave.SaveToStream(Stream: TStream);
begin
WriteDataToStream(Stream);
end;
{$IFDEF COMPONENT_SET_LCL}
class function TImagingGraphicForSave.GetFileExtensions: string;
begin
Result := StringReplace(GetFileFormat.Extensions.CommaText, ',', ';', [rfReplaceAll]);
end;
function TImagingGraphicForSave.GetDefaultMimeType: string;
begin
Result := 'image/' + FDefaultFileExt;
end;
{$ENDIF}
{$IFDEF LINK_BITMAP}
{ TImagingBitmap class implementation }
constructor TImagingBitmap.Create;
begin
inherited Create;
FUseRLE := (GetFileFormat as TBitmapFileFormat).UseRLE;
end;
class function TImagingBitmap.GetFileFormat: TImageFileFormat;
begin
Result := FindImageFileFormatByClass(TBitmapFileFormat);
end;
procedure TImagingBitmap.SaveToStream(Stream: TStream);
begin
Imaging.PushOptions;
Imaging.SetOption(ImagingBitmapRLE, Ord(FUseRLE));
inherited SaveToStream(Stream);
Imaging.PopOptions;
end;
{$ENDIF}
{$IFDEF LINK_JPEG}
{ TImagingJpeg class implementation }
constructor TImagingJpeg.Create;
begin
inherited Create;
FQuality := (GetFileFormat as TJpegFileFormat).Quality;
FProgressive := (GetFileFormat as TJpegFileFormat).Progressive;
end;
class function TImagingJpeg.GetFileFormat: TImageFileFormat;
begin
Result := FindImageFileFormatByClass(TJpegFileFormat);
end;
{$IFDEF COMPONENT_SET_LCL}
function TImagingJpeg.GetDefaultMimeType: string;
begin
Result := 'image/jpeg';
end;
{$ENDIF}
procedure TImagingJpeg.SaveToStream(Stream: TStream);
begin
Imaging.PushOptions;
Imaging.SetOption(ImagingJpegQuality, FQuality);
Imaging.SetOption(ImagingJpegProgressive, Ord(FProgressive));
inherited SaveToStream(Stream);
Imaging.PopOptions;
end;
{$ENDIF}
{$IFDEF LINK_PNG}
{ TImagingPNG class implementation }
constructor TImagingPNG.Create;
begin
inherited Create;
FPreFilter := (GetFileFormat as TPNGFileFormat).PreFilter;
FCompressLevel := (GetFileFormat as TPNGFileFormat).CompressLevel;
end;
class function TImagingPNG.GetFileFormat: TImageFileFormat;
begin
Result := FindImageFileFormatByClass(TPNGFileFormat);
end;
procedure TImagingPNG.SaveToStream(Stream: TStream);
begin
Imaging.PushOptions;
Imaging.SetOption(ImagingPNGPreFilter, FPreFilter);
Imaging.SetOption(ImagingPNGCompressLevel, FCompressLevel);
inherited SaveToStream(Stream);
Imaging.PopOptions;
end;
{$ENDIF}
{$IFDEF LINK_GIF}
{ TImagingGIF class implementation}
class function TImagingGIF.GetFileFormat: TImageFileFormat;
begin
Result := FindImageFileFormatByClass(TGIFFileFormat);
end;
{$ENDIF}
{$IFDEF LINK_TARGA}
{ TImagingTarga class implementation }
constructor TImagingTarga.Create;
begin
inherited Create;
FUseRLE := (GetFileFormat as TTargaFileFormat).UseRLE;
end;
class function TImagingTarga.GetFileFormat: TImageFileFormat;
begin
Result := FindImageFileFormatByClass(TTargaFileFormat);
end;
procedure TImagingTarga.SaveToStream(Stream: TStream);
begin
Imaging.PushOptions;
Imaging.SetOption(ImagingTargaRLE, Ord(FUseRLE));
inherited SaveToStream(Stream);
Imaging.PopOptions;
end;
{$ENDIF}
{$IFDEF LINK_DDS}
{ TImagingDDS class implementation }
constructor TImagingDDS.Create;
begin
inherited Create;
FCompression := dcNone;
end;
class function TImagingDDS.GetFileFormat: TImageFileFormat;
begin
Result := FindImageFileFormatByClass(TDDSFileFormat);
end;
procedure TImagingDDS.SaveToStream(Stream: TStream);
begin
case FCompression of
dcNone: FSavingFormat := ifUnknown;
dcDXT1: FSavingFormat := ifDXT1;
dcDXT3: FSavingFormat := ifDXT3;
dcDXT5: FSavingFormat := ifDXT5;
end;
Imaging.PushOptions;
Imaging.SetOption(ImagingDDSSaveCubeMap, Ord(False));
Imaging.SetOption(ImagingDDSSaveVolume, Ord(False));
Imaging.SetOption(ImagingDDSSaveMipMapCount, 1);
Imaging.SetOption(ImagingDDSSaveDepth, 1);
inherited SaveToStream(Stream);
Imaging.PopOptions;
end;
{$ENDIF}
{$IFDEF LINK_MNG}
{ TImagingMNG class implementation }
constructor TImagingMNG.Create;
begin
inherited Create;
FLossyCompression := (GetFileFormat as TMNGFileFormat).LossyCompression;
FLossyAlpha := (GetFileFormat as TMNGFileFormat).LossyAlpha;
FPreFilter := (GetFileFormat as TMNGFileFormat).PreFilter;
FCompressLevel := (GetFileFormat as TMNGFileFormat).CompressLevel;
FQuality := (GetFileFormat as TMNGFileFormat).Quality;
FProgressive := (GetFileFormat as TMNGFileFormat).Progressive;
end;
class function TImagingMNG.GetFileFormat: TImageFileFormat;
begin
Result := FindImageFileFormatByClass(TMNGFileFormat);
end;
{$IFDEF COMPONENT_SET_LCL}
function TImagingMNG.GetDefaultMimeType: string;
begin
Result := 'video/mng';
end;
{$ENDIF}
procedure TImagingMNG.SaveToStream(Stream: TStream);
begin
Imaging.PushOptions;
Imaging.SetOption(ImagingMNGLossyCompression, Ord(FLossyCompression));
Imaging.SetOption(ImagingMNGLossyAlpha, Ord(FLossyAlpha));
Imaging.SetOption(ImagingMNGPreFilter, FPreFilter);
Imaging.SetOption(ImagingMNGCompressLevel, FCompressLevel);
Imaging.SetOption(ImagingMNGQuality, FQuality);
Imaging.SetOption(ImagingMNGProgressive, Ord(FProgressive));
inherited SaveToStream(Stream);
Imaging.PopOptions;
end;
{$ENDIF}
{$IFDEF LINK_JNG}
{ TImagingJNG class implementation }
constructor TImagingJNG.Create;
begin
inherited Create;
FLossyAlpha := (GetFileFormat as TJNGFileFormat).LossyAlpha;
FAlphaPreFilter := (GetFileFormat as TJNGFileFormat).PreFilter;
FAlphaCompressLevel := (GetFileFormat as TJNGFileFormat).CompressLevel;
FQuality := (GetFileFormat as TJNGFileFormat).Quality;
FProgressive := (GetFileFormat as TJNGFileFormat).Progressive;
end;
class function TImagingJNG.GetFileFormat: TImageFileFormat;
begin
Result := FindImageFileFormatByClass(TJNGFileFormat);
end;
procedure TImagingJNG.SaveToStream(Stream: TStream);
begin
Imaging.PushOptions;
Imaging.SetOption(ImagingJNGLossyALpha, Ord(FLossyAlpha));
Imaging.SetOption(ImagingJNGAlphaPreFilter, FAlphaPreFilter);
Imaging.SetOption(ImagingJNGAlphaCompressLevel, FAlphaCompressLevel);
Imaging.SetOption(ImagingJNGQuality, FQuality);
Imaging.SetOption(ImagingJNGProgressive, Ord(FProgressive));
inherited SaveToStream(Stream);
Imaging.PopOptions;
end;
{$ENDIF}
initialization
RegisterTypes;
finalization
UnRegisterTypes;
{
File Notes:
-- TODOS ----------------------------------------------------
- nothing now
-- 0.24.1 Changes/Bug Fixes ---------------------------------
- Fixed wrong IFDEF causing that Imaging wouldn't compile in Lazarus
with GTK2 target.
- Added commnets with code for Lazarus rev. 11861+ regarding
RawImage interface. Replace current code with that in comments
if you use Lazarus from SVN. New RawImage interface will be used by
default after next Lazarus release.
-- 0.23 Changes/Bug Fixes -----------------------------------
- Added TImagingGIF.
-- 0.21 Changes/Bug Fixes -----------------------------------
- Uses only high level interface now (except for saving options).
- Slightly changed class hierarchy. TImagingGraphic is now only for loading
and base class for savers is new TImagingGraphicForSave. Also
TImagingGraphic is now registered with all supported file formats
by TPicture's format support.
-- 0.19 Changes/Bug Fixes -----------------------------------
- added DisplayImage procedures (thanks to Paul Michell, modified)
- removed RegisterTypes and UnRegisterTypes from interface section,
they are called automatically
- added procedures: ConvertImageToBitmap and ConvertBitmapToImage
-- 0.17 Changes/Bug Fixes -----------------------------------
- LCL data to bitmap conversion didn<64>t work in Linux, fixed
- added MNG file format
- added JNG file format
-- 0.15 Changes/Bug Fixes -----------------------------------
- made it LCL compatible
- made it CLX compatible
- added all initial stuff
}
end.