2 Vampyre Imaging Library
4 http://imaginglib.sourceforge.net
6 The contents of this file are used with permission, subject to the Mozilla
7 Public License Version 1.1 (the "License"); you may not use this file except
8 in compliance with the License. You may obtain a copy of the License at
9 http://www.mozilla.org/MPL/MPL-1.1.html
11 Software distributed under the License is distributed on an "AS IS" basis,
12 WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
13 the specific language governing rights and limitations under the License.
15 Alternatively, the contents of this file may be used under the terms of the
16 GNU Lesser General Public License (the "LGPL License"), in which case the
17 provisions of the LGPL License are applicable instead of those above.
18 If you wish to allow use of your version of this file only under the terms
19 of the LGPL License and not to allow others to use your version of this file
20 under the MPL, indicate your decision by deleting the provisions above and
21 replace them with the notice and other provisions required by the LGPL
22 License. If you do not delete the provisions above, a recipient may use
23 your version of this file under either the MPL or the LGPL License.
25 For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html
28 { This unit is heart of Imaging library. It contains basic functions for
29 manipulating image data as well as various image file format support.}
32 {$I ImagingOptions.inc}
37 SysUtils
, Classes
, Types
, ImagingTypes
;
40 { Default Imaging excepton class }
41 EImagingError
= class(Exception
);
42 { Raised when function receives bad image (not passed TestImage).}
43 EImagingBadImage
= class(Exception
)
48 { Dynamic array of TImageData records }
49 TDynImageDataArray
= array of TImageData
;
52 { ------------------------------------------------------------------------
53 Low Level Interface Functions
54 ------------------------------------------------------------------------}
58 { Initializes image (all is set to zeroes). Call this for each image
59 before using it (before calling every other function) to be sure there
60 are no random-filled bytes (which would cause errors later).}
61 procedure InitImage(var Image
: TImageData
);
62 { Creates empty image of given dimensions and format. Image is filled with
63 transparent black color (A=0, R=0, G=0, B=0).}
64 function NewImage(Width
, Height
: LongInt; Format
: TImageFormat
;
65 var Image
: TImageData
): Boolean;
66 { Returns True if given TImageData record is valid.}
67 function TestImage(const Image
: TImageData
): Boolean;
68 { Frees given image data. Ater this call image is in the same state
69 as after calling InitImage. If image is not valid (dost not pass TestImage
70 test) it is only zeroed by calling InitImage.}
71 procedure FreeImage(var Image
: TImageData
);
72 { Call FreeImage() on all images in given dynamic array and sets its
74 procedure FreeImagesInArray(var Images
: TDynImageDataArray
);
75 { Returns True if all TImageData records in given array are valid. Returns False
76 if at least one is invalid or if array is empty.}
77 function TestImagesInArray(const Images
: TDynImageDataArray
): Boolean;
78 { Checks given file for every supported image file format and if
79 the file is in one of them returns its string identifier
80 (which can be used in LoadFromStream/LoadFromMem type functions).
81 If file is not in any of the supported formats empty string is returned.}
82 function DetermineFileFormat(const FileName
: string): string;
83 { Checks given stream for every supported image file format and if
84 the stream is in one of them returns its string identifier
85 (which can be used in LoadFromStream/LoadFromMem type functions).
86 If stream is not in any of the supported formats empty string is returned.}
87 function DetermineStreamFormat(Stream
: TStream
): string;
88 { Checks given memory for every supported image file format and if
89 the memory is in one of them returns its string identifier
90 (which can be used in LoadFromStream/LoadFromMem type functions).
91 If memory is not in any of the supported formats empty string is returned.}
92 function DetermineMemoryFormat(Data
: Pointer; Size
: LongInt): string;
93 { Checks that an apropriate file format is supported purely from inspecting
94 the given file name's extension (not contents of the file itself).
95 The file need not exist.}
96 function IsFileFormatSupported(const FileName
: string): Boolean;
97 { Enumerates all registered image file formats. Descriptive name,
98 default extension, masks (like '*.jpg,*.jfif') and some capabilities
99 of each format are returned. To enumerate all formats start with Index at 0 and
100 call EnumFileFormats with given Index in loop until it returns False (Index is
101 automatically increased by 1 in function's body on successful call).}
102 function EnumFileFormats(var Index
: LongInt; var Name
, DefaultExt
, Masks
: string;
103 var CanSaveImages
, IsMultiImageFormat
: Boolean): Boolean;
105 { Loading Functions }
107 { Loads single image from given file.}
108 function LoadImageFromFile(const FileName
: string; var Image
: TImageData
): Boolean;
109 { Loads single image from given stream. If function fails stream position
111 function LoadImageFromStream(Stream
: TStream
; var Image
: TImageData
): Boolean;
112 { Loads single image from given memory location.}
113 function LoadImageFromMemory(Data
: Pointer; Size
: LongInt; var Image
: TImageData
): Boolean;
114 { Loads multiple images from given file.}
115 function LoadMultiImageFromFile(const FileName
: string;
116 var Images
: TDynImageDataArray
): Boolean;
117 { Loads multiple images from given stream. If function fails stream position
119 function LoadMultiImageFromStream(Stream
: TStream
;
120 var Images
: TDynImageDataArray
): Boolean;
121 { Loads multiple images from given memory location.}
122 function LoadMultiImageFromMemory(Data
: Pointer; Size
: LongInt;
123 var Images
: TDynImageDataArray
): Boolean;
127 { Saves single image to given file.}
128 function SaveImageToFile(const FileName
: string; const Image
: TImageData
): Boolean;
129 { Saves single image to given stream. If function fails stream position
130 is not changed. Ext identifies desired image file format (jpg, png, dds, ...).}
131 function SaveImageToStream(const Ext
: string; Stream
: TStream
;
132 const Image
: TImageData
): Boolean;
133 { Saves single image to given memory location. Memory must be allocated and its
134 size is passed in Size parameter in which number of written bytes is returned.
135 Ext identifies desired image file format (jpg, png, dds, ...).}
136 function SaveImageToMemory(const Ext
: string; Data
: Pointer; var Size
: LongInt;
137 const Image
: TImageData
): Boolean;
138 { Saves multiple images to given file. If format supports
139 only single level images and there are multiple images to be saved,
140 they are saved as sequence of files img000.jpg, img001.jpg ....).}
141 function SaveMultiImageToFile(const FileName
: string;
142 const Images
: TDynImageDataArray
): Boolean;
143 { Saves multiple images to given stream. If format supports
144 only single level images and there are multiple images to be saved,
145 they are saved one after another to the stream. If function fails stream
146 position is not changed. Ext identifies desired image file format (jpg, png, dds, ...).}
147 function SaveMultiImageToStream(const Ext
: string; Stream
: TStream
;
148 const Images
: TDynImageDataArray
): Boolean;
149 { Saves multiple images to given memory location. If format supports
150 only single level images and there are multiple images to be saved,
151 they are saved one after another to the memory. Memory must be allocated and
152 its size is passed in Size parameter in which number of written bytes is returned.
153 Ext identifies desired image file format (jpg, png, dds, ...).}
154 function SaveMultiImageToMemory(const Ext
: string; Data
: Pointer;
155 var Size
: LongInt; const Images
: TDynImageDataArray
): Boolean;
157 { Manipulation Functions }
159 { Creates identical copy of image data. Clone should be initialized
160 by InitImage or it should be vaild image which will be freed by CloneImage.}
161 function CloneImage(const Image
: TImageData
; var Clone
: TImageData
): Boolean;
162 { Converts image to the given format.}
163 function ConvertImage(var Image
: TImageData
; DestFormat
: TImageFormat
): Boolean;
164 { Flips given image. Reverses the image along its horizontal axis - the top
165 becomes the bottom and vice versa.}
166 function FlipImage(var Image
: TImageData
): Boolean;
167 { Mirrors given image. Reverses the image along its vertical axis \97 the left
168 side becomes the right and vice versa.}
169 function MirrorImage(var Image
: TImageData
): Boolean;
170 { Resizes given image to new dimensions. Nearest, bilinear, or bicubic filtering
171 can be used. Input Image must already be created - use NewImage to create new images.}
172 function ResizeImage(var Image
: TImageData
; NewWidth
, NewHeight
: LongInt;
173 Filter
: TResizeFilter
): Boolean;
174 { Swaps SrcChannel and DstChannel color or alpha channels of image.
175 Use ChannelRed, ChannelBlue, ChannelGreen, ChannelAlpha constants to
177 function SwapChannels(var Image
: TImageData
; SrcChannel
, DstChannel
: LongInt): Boolean;
178 { Reduces the number of colors of the Image. Currently MaxColors must be in
179 range <2, 4096>. Color reduction works also for alpha channel. Note that for
180 large images and big number of colors it can be very slow.
181 Output format of the image is the same as input format.}
182 function ReduceColors(var Image
: TImageData
; MaxColors
: LongInt): Boolean;
183 { Generates mipmaps for image. Levels is the number of desired mipmaps levels
184 with zero (or some invalid number) meaning all possible levels.}
185 function GenerateMipMaps(const Image
: TImageData
; Levels
: LongInt;
186 var MipMaps
: TDynImageDataArray
): Boolean;
187 { Maps image to existing palette producing image in ifIndex8 format.
188 Pal must be allocated to at least Entries * SizeOf(TColor32Rec) bytes.
189 As resulting image is in 8bit indexed format Entries must be lower or
191 function MapImageToPalette(var Image
: TImageData
; Pal
: PPalette32
;
192 Entries
: LongInt): Boolean;
193 { Splits image into XChunks x YChunks subimages. Default size of each chunk is
194 ChunkWidth x ChunkHeight. If PreserveSize si True chunks at the edges of
195 the image are also ChunkWidth x ChunkHeight sized and empty space is filled
196 with optional Fill pixels. After calling this function XChunks contains number of
197 chunks along x axis and YChunks along y axis. To access chunk [X, Y] use this
198 index: Chunks[Y * XChunks + X].}
199 function SplitImage(var Image
: TImageData
; var Chunks
: TDynImageDataArray
;
200 ChunkWidth
, ChunkHeight
: LongInt; var XChunks
, YChunks
: LongInt;
201 PreserveSize
: Boolean; Fill
: Pointer = nil): Boolean;
202 { Creates palette with MaxColors based on the colors of images in Images array.
203 Use it when you want to convert several images to indexed format using
204 single palette for all of them. If ConvertImages is True images in array
205 are converted to indexed format using resulting palette. if it is False
206 images are left intact and only resulting palatte is returned in Pal.
207 Pal must be allocated to have at least MaxColors entries.}
208 function MakePaletteForImages(var Images
: TDynImageDataArray
; Pal
: PPalette32
;
209 MaxColors
: LongInt; ConvertImages
: Boolean): Boolean;
210 { Rotates image by Angle degrees counterclockwise. All angles are allowed.}
211 procedure RotateImage(var Image
: TImageData
; Angle
: Single);
213 { Drawing/Pixel functions }
215 { Copies rectangular part of SrcImage to DstImage. No blending is performed -
216 alpha is simply copied to destination image. Operates also with
217 negative X and Y coordinates.
218 Note that copying is fastest for images in the same data format
219 (and slowest for images in special formats).}
220 function CopyRect(const SrcImage
: TImageData
; SrcX
, SrcY
, Width
, Height
: LongInt;
221 var DstImage
: TImageData
; DstX
, DstY
: LongInt): Boolean;
222 { Fills given rectangle of image with given pixel fill data. Fill should point
223 to the pixel in the same format as the given image is in.}
224 function FillRect(var Image
: TImageData
; X
, Y
, Width
, Height
: LongInt; FillColor
: Pointer): Boolean;
225 { Replaces pixels with OldPixel in the given rectangle by NewPixel.
226 OldPixel and NewPixel should point to the pixels in the same format
227 as the given image is in.}
228 function ReplaceColor(var Image
: TImageData
; X
, Y
, Width
, Height
: LongInt;
229 OldColor
, NewColor
: Pointer): Boolean;
230 { Stretches the contents of the source rectangle to the destination rectangle
231 with optional resampling. No blending is performed - alpha is
232 simply copied/resampled to destination image. Note that stretching is
233 fastest for images in the same data format (and slowest for
234 images in special formats).}
235 function StretchRect(const SrcImage
: TImageData
; SrcX
, SrcY
, SrcWidth
,
236 SrcHeight
: LongInt; var DstImage
: TImageData
; DstX
, DstY
, DstWidth
,
237 DstHeight
: LongInt; Filter
: TResizeFilter
): Boolean;
238 { Copies pixel of Image at [X, Y] to memory pointed at by Pixel. Doesn't
239 work with special formats.}
240 procedure GetPixelDirect(const Image
: TImageData
; X
, Y
: LongInt; Pixel
: Pointer);
241 { Copies pixel from memory pointed at by Pixel to Image at position [X, Y].
242 Doesn't work with special formats.}
243 procedure SetPixelDirect(const Image
: TImageData
; X
, Y
: LongInt; Pixel
: Pointer);
244 { Function for getting pixel colors. Native pixel is read from Image and
245 then translated to 32 bit ARGB. Works for all image formats (except special)
246 so it is not very fast.}
247 function GetPixel32(const Image
: TImageData
; X
, Y
: LongInt): TColor32Rec
;
248 { Procedure for setting pixel colors. Input 32 bit ARGB color is translated to
249 native format and then written to Image. Works for all image formats (except special)
250 so it is not very fast.}
251 procedure SetPixel32(const Image
: TImageData
; X
, Y
: LongInt; const Color
: TColor32Rec
);
252 { Function for getting pixel colors. Native pixel is read from Image and
253 then translated to FP ARGB. Works for all image formats (except special)
254 so it is not very fast.}
255 function GetPixelFP(const Image
: TImageData
; X
, Y
: LongInt): TColorFPRec
;
256 { Procedure for setting pixel colors. Input FP ARGB color is translated to
257 native format and then written to Image. Works for all image formats (except special)
258 so it is not very fast.}
259 procedure SetPixelFP(const Image
: TImageData
; X
, Y
: LongInt; const Color
: TColorFPRec
);
261 { Palette Functions }
263 { Allocates new palette with Entries ARGB color entries.}
264 procedure NewPalette(Entries
: LongInt; var Pal
: PPalette32
);
265 { Frees given palette.}
266 procedure FreePalette(var Pal
: PPalette32
);
267 { Copies Count palette entries from SrcPal starting at index SrcIdx to
268 DstPal at index DstPal.}
269 procedure CopyPalette(SrcPal
, DstPal
: PPalette32
; SrcIdx
, DstIdx
, Count
: LongInt);
270 { Returns index of color in palette or index of nearest color if exact match
271 is not found. Pal must have at least Entries color entries.}
272 function FindColor(Pal
: PPalette32
; Entries
: LongInt; Color
: TColor32
): LongInt;
273 { Creates grayscale palette where each color channel has the same value.
274 Pal must have at least Entries color entries.}
275 procedure FillGrayscalePalette(Pal
: PPalette32
; Entries
: LongInt);
276 { Creates palette with given bitcount for each channel.
277 2^(RBits + GBits + BBits) should be equl to Entries. Examples:
278 (3, 3, 2) will create palette with all possible colors of R3G3B2 format
279 and (8, 0, 0) will create palette with 256 shades of red.
280 Pal must be allocated to at least Entries * SizeOf(TColor32Rec) bytes.}
281 procedure FillCustomPalette(Pal
: PPalette32
; Entries
: LongInt; RBits
, GBits
,
282 BBits
: Byte; Alpha
: Byte = $FF);
283 { Swaps SrcChannel and DstChannel color or alpha channels of palette.
284 Use ChannelRed, ChannelBlue, ChannelGreen, ChannelAlpha constants to
285 identify channels. Pal must be allocated to at least
286 Entries * SizeOf(TColor32Rec) bytes.}
287 procedure SwapChannelsOfPalette(Pal
: PPalette32
; Entries
, SrcChannel
,
288 DstChannel
: LongInt);
290 { Options Functions }
292 { Sets value of integer option specified by OptionId parameter.
293 Option Ids are constans starting ImagingXXX.}
294 function SetOption(OptionId
, Value
: LongInt): Boolean;
295 { Returns value of integer option specified by OptionId parameter. If OptionId is
296 invalid, InvalidOption is returned. Option Ids are constans
297 starting ImagingXXX.}
298 function GetOption(OptionId
: LongInt): LongInt;
299 { Pushes current values of all options on the stack. Returns True
300 if successfull (max stack depth is 8 now). }
301 function PushOptions
: Boolean;
302 { Pops back values of all options from the top of the stack. Returns True
303 if successfull (max stack depth is 8 now). }
304 function PopOptions
: Boolean;
306 { Image Format Functions }
308 { Returns short information about given image format.}
309 function GetImageFormatInfo(Format
: TImageFormat
; out Info
: TImageFormatInfo
): Boolean;
310 { Returns size in bytes of Width x Height area of pixels. Works for all formats.}
311 function GetPixelsSize(Format
: TImageFormat
; Width
, Height
: LongInt): LongInt;
315 { User can set his own file IO functions used when loading from/saving to
316 files by this function.}
317 procedure SetUserFileIO(OpenProc
: TOpenProc
; CloseProc
: TCloseProc
; EofProc
: TEofProc
; SeekProc
:
318 TSeekProc
; TellProc
: TTellProc
; ReadProc
: TReadProc
; WriteProc
: TWriteProc
);
319 { Sets file IO functions to Imaging default.}
320 procedure ResetFileIO
;
322 { Raw Image IO Functions }
324 procedure ReadRawImageFromFile(const FileName
: string; Width
, Height
: Integer;
325 Format
: TImageFormat
; var Image
: TImageData
; Offset
: Integer = 0; RowLength
: Integer = 0);
326 procedure ReadRawImageFromStream(Stream
: TStream
; Width
, Height
: Integer;
327 Format
: TImageFormat
; var Image
: TImageData
; Offset
: Integer = 0; RowLength
: Integer = 0);
328 procedure ReadRawImageFromMemory(Data
: Pointer; DataSize
: Integer; Width
, Height
: Integer;
329 Format
: TImageFormat
; var Image
: TImageData
; Offset
: Integer = 0; RowLength
: Integer = 0);
330 procedure ReadRawImageRect(Data
: Pointer; Left
, Top
, Width
, Height
: Integer;
331 var Image
: TImageData
; Offset
: Integer = 0; RowLength
: Integer = 0);
333 procedure WriteRawImageToFile(const FileName
: string; const Image
: TImageData
;
334 Offset
: Integer = 0; RowLength
: Integer = 0);
335 procedure WriteRawImageToStream(Stream
: TStream
; const Image
: TImageData
;
336 Offset
: Integer = 0; RowLength
: Integer = 0);
337 procedure WriteRawImageToMemory(Data
: Pointer; DataSize
: Integer; const Image
: TImageData
;
338 Offset
: Integer = 0; RowLength
: Integer = 0);
339 procedure WriteRawImageRect(Data
: Pointer; Left
, Top
, Width
, Height
: Integer;
340 const Image
: TImageData
; Offset
: Integer = 0; RowLength
: Integer = 0);
342 { Convenience/helper Functions }
344 procedure ResizeImageToFit(const SrcImage
: TImageData
; FitWidth
, FitHeight
: Integer;
345 Filter
: TResizeFilter
; var DestImage
: TImageData
);
348 { ------------------------------------------------------------------------
350 ------------------------------------------------------------------------}
353 { Set of TImageFormat enum.}
354 TImageFormats
= set of TImageFormat
;
356 { Record containg set of IO functions internaly used by image loaders/savers.}
357 TIOFunctions
= record
366 PIOFunctions
= ^TIOFunctions
;
369 TFileFormatFeature
= (
377 TFileFormatFeatures
= set of TFileFormatFeature
;
381 { Base class for various image file format loaders/savers which
382 descend from this class. If you want to add support for new image file
383 format the best way is probably to look at TImageFileFormat descendants'
384 implementations that are already part of Imaging.}
386 TImageFileFormat
= class
388 FExtensions
: TStringList
;
390 function GetCanLoad
: Boolean;
391 function GetCanSave
: Boolean;
392 function GetIsMultiImageFormat
: Boolean;
393 { Does various checks and actions before LoadData method is called.}
394 function PrepareLoad(Handle
: TImagingHandle
; var Images
: TDynImageDataArray
;
395 OnlyFirstFrame
: Boolean): Boolean;
396 { Processes some actions according to result of LoadData.}
397 function PostLoadCheck(var Images
: TDynImageDataArray
; LoadResult
: Boolean): Boolean;
398 { Helper function to be called in SaveData methods of descendants (ensures proper
399 index and sets FFirstIdx and FLastIdx for multi-images).}
400 function PrepareSave(Handle
: TImagingHandle
; const Images
: TDynImageDataArray
;
401 var Index
: LongInt): Boolean;
402 { Returns file open mode used for saving images. Depends on defined Features.}
403 function GetSaveOpenMode
: TOpenMode
;
406 FFeatures
: TFileFormatFeatures
;
407 FSupportedFormats
: TImageFormats
;
408 FFirstIdx
, FLastIdx
: LongInt;
409 FMetadata
: TMetadata
;
410 { Descendants must override this method and define file format name and
412 procedure Define
; virtual;
413 { Defines filename masks for this image file format. AMasks should be
414 in format '*.ext1,*.ext2,umajo.*'.}
415 procedure AddMasks(const AMasks
: string);
416 function GetFormatInfo(Format
: TImageFormat
): TImageFormatInfo
;
417 { Returns set of TImageData formats that can be saved in this file format
418 without need for conversion.}
419 function GetSupportedFormats
: TImageFormats
; virtual;
420 { Method which must be overrided in descendants if they' are be capable
421 of loading images. Images are already freed and length is set to zero
422 whenever this method gets called. Also Handle is assured to be valid
423 and contains data that passed TestFormat method's check.}
424 function LoadData(Handle
: TImagingHandle
; var Images
: TDynImageDataArray
;
425 OnlyFirstFrame
: Boolean): Boolean; virtual;
426 { Method which must be overriden in descendants if they are be capable
427 of saving images. Images are checked to have length >0 and
428 that they contain valid images. For single-image file formats
429 Index contain valid index to Images array (to image which should be saved).
430 Multi-image formats should use FFirstIdx and FLastIdx fields to
431 to get all images that are to be saved.}
432 function SaveData(Handle
: TImagingHandle
; const Images
: TDynImageDataArray
;
433 Index
: LongInt): Boolean; virtual;
434 { This method is called internaly by MakeCompatible when input image
435 is in format not supported by this file format. Image is clone of
436 MakeCompatible's input and Info is its extended format info.}
437 procedure ConvertToSupported(var Image
: TImageData
;
438 const Info
: TImageFormatInfo
); virtual;
439 { Returns True if given image is supported for saving by this file format.
440 Most file formats don't need to override this method. It checks
441 (in this base class) if Image's format is in SupportedFromats set.
442 But you may override it if you want further checks
443 (proper widht and height for example).}
444 function IsSupported(const Image
: TImageData
): Boolean; virtual;
446 constructor Create(AMetadata
: TMetadata
= nil); virtual;
447 destructor Destroy
; override;
449 { Loads images from file source.}
450 function LoadFromFile(const FileName
: string; var Images
: TDynImageDataArray
;
451 OnlyFirstLevel
: Boolean = False): Boolean;
452 { Loads images from stream source.}
453 function LoadFromStream(Stream
: TStream
; var Images
: TDynImageDataArray
;
454 OnlyFirstLevel
: Boolean = False): Boolean;
455 { Loads images from memory source.}
456 function LoadFromMemory(Data
: Pointer; Size
: LongInt;
457 var Images
: TDynImageDataArray
; OnlyFirstLevel
: Boolean = False): Boolean;
459 { Saves images to file. If format supports only single level images and
460 there are multiple images to be saved, they are saved as sequence of
461 independent images (for example SaveToFile saves sequence of
462 files img000.jpg, img001.jpg ....).}
463 function SaveToFile(const FileName
: string; const Images
: TDynImageDataArray
;
464 OnlyFirstLevel
: Boolean = False): Boolean;
465 { Saves images to stream. If format supports only single level images and
466 there are multiple images to be saved, they are saved as sequence of
468 function SaveToStream(Stream
: TStream
; const Images
: TDynImageDataArray
;
469 OnlyFirstLevel
: Boolean = False): Boolean;
470 { Saves images to memory. If format supports only single level images and
471 there are multiple images to be saved, they are saved as sequence of
472 independent images. Data must be already allocated and their size passed
473 as Size parameter, number of written bytes is then returned in the same
475 function SaveToMemory(Data
: Pointer; var Size
: LongInt;
476 const Images
: TDynImageDataArray
; OnlyFirstLevel
: Boolean = False): Boolean;
478 { Makes Image compatible with this file format (that means it is in one
479 of data formats in Supported formats set). If input is already
480 in supported format then Compatible just use value from input
481 (Compatible := Image) so must not free it after you are done with it
482 (image bits pointer points to input image's bits).
483 If input is not in supported format then it is cloned to Compatible
484 and concerted to one of supported formats (which one dependeds on
485 this file format). If image is cloned MustBeFreed is set to True
486 to indicated that you must free Compatible after you are done with it.}
487 function MakeCompatible(const Image
: TImageData
; var Compatible
: TImageData
;
488 out MustBeFreed
: Boolean): Boolean;
489 { Returns True if data located in source identified by Handle
490 represent valid image in current format.}
491 function TestFormat(Handle
: TImagingHandle
): Boolean; virtual;
492 { Resturns True if the given FileName matches filter for this file format.
493 For most formats it just checks filename extensions.
494 It uses filename masks in from Masks property so it can recognize
495 filenames like this 'umajoXXXumajo.j0j' if one of themasks is
497 function TestFileName(const FileName
: string): Boolean;
498 { Descendants use this method to check if their options (registered with
499 constant Ids for SetOption/GetOption interface or accessible as properties
500 of descendants) have valid values and make necessary changes.}
501 procedure CheckOptionsValidity
; virtual;
503 { Description of this format.}
504 property Name
: string read FName
;
505 { Indicates whether images in this format can be loaded.}
506 property CanLoad
: Boolean read GetCanLoad
;
507 { Indicates whether images in this format can be saved.}
508 property CanSave
: Boolean read GetCanSave
;
509 { Indicates whether images in this format can contain multiple image levels.}
510 property IsMultiImageFormat
: Boolean read GetIsMultiImageFormat
;
511 { List of filename extensions for this format.}
512 property Extensions
: TStringList read FExtensions
;
513 { List of filename masks that are used to associate filenames
514 with TImageFileFormat descendants. Typical mask looks like
515 '*.bmp' or 'texture.*' (supports file formats which use filename instead
516 of extension to identify image files).}
517 property Masks
: TStringList read FMasks
;
518 { Set of TImageFormats supported by saving functions of this format. Images
519 can be saved only in one those formats.}
520 property SupportedFormats
: TImageFormats read GetSupportedFormats
;
524 { Class reference for TImageFileFormat class}
525 TImageFileFormatClass
= class of TImageFileFormat
;
527 { Physical resolution unit.}
529 ruSizeInMicroMeters
, // value is pixel size in micrometers
530 ruDpi
, // value is pixels/dots per inch
531 ruDpm
, // value is pixels/dots per meter
532 ruDpcm
// value is pixels/dots per centimeter
535 { Class for storage of single metadata item.}
536 TMetadataItem
= class
543 { Metadata manager class.}
546 FLoadMetaItems
: TStringList
;
547 FSaveMetaItems
: TStringList
;
548 procedure AddMetaToList(List
: TStringList
; const Id
: string; const Value
: Variant; ImageIndex
: Integer);
549 procedure ClearMetaList(List
: TStringList
);
550 function GetMetaById(const Id
: string): Variant;
551 function GetMetaByIdMulti(const Id
: string; ImageIndex
: Integer): Variant;
552 function GetMetaCount
: Integer;
553 function GetMetaByIdx(Index
: Integer): TMetadataItem
;
554 function GetSaveMetaById(const Id
: string): Variant;
555 function GetSaveMetaByIdMulti(const Id
: string; ImageIndex
: Integer): Variant;
556 procedure TranslateUnits(ResolutionUnit
: TResolutionUnit
; var XRes
, YRes
: Single);
559 destructor Destroy
; override;
561 procedure SetMetaItem(const Id
: string; const Value
: Variant; ImageIndex
: Integer = 0);
562 procedure SetMetaItemForSaving(const Id
: string; const Value
: Variant; ImageIndex
: Integer = 0);
563 function HasMetaItem(const Id
: string; ImageIndex
: Integer = 0): Boolean;
564 function HasMetaItemForSaving(const Id
: string; ImageIndex
: Integer = 0): Boolean;
566 procedure ClearMetaItems
;
567 procedure ClearMetaItemsForSaving
;
568 function GetMetaItemName(const Id
: string; ImageIndex
: Integer): string;
569 { Copies loaded meta items to items-for-save stack. Use this when you want to
570 save metadata that have been just loaded (e.g. resaving image in
571 different file format but keeping the metadata).}
572 procedure CopyLoadedMetaItemsForSaving
;
574 function GetPhysicalPixelSize(ResUnit
: TResolutionUnit
; var XSize
,
575 YSize
: Single; MetaForSave
: Boolean = False; ImageIndex
: Integer = 0): Boolean;
576 procedure SetPhysicalPixelSize(ResUnit
: TResolutionUnit
; XSize
, YSize
: Single;
577 MetaForSave
: Boolean = False; ImageIndex
: Integer = 0);
579 property MetaItems
[const Id
: string]: Variant read GetMetaById
;
580 property MetaItemsMulti
[const Id
: string; ImageIndex
: Integer]: Variant read GetMetaByIdMulti
;
581 { Number of loaded metadata items.}
582 property MetaItemCount
: Integer read GetMetaCount
;
583 property MetaItemsByIdx
[Index
: Integer]: TMetadataItem read GetMetaByIdx
;
584 property MetaItemsForSaving
[const Id
: string]: Variant read GetSaveMetaById
;
585 property MetaItemsForSavingMulti
[const Id
: string; ImageIndex
: Integer]: Variant read GetSaveMetaByIdMulti
;
589 { Metadata item id constants }
591 { Physical size of one pixel in micrometers. Type of value is Float.}
592 SMetaPhysicalPixelSizeX
= 'PhysicalPixelSizeX';
593 SMetaPhysicalPixelSizeY
= 'PhysicalPixelSizeY';
594 { Delay for frame of animation (how long it should stay visible) in milliseconds.
595 Type of value is Integer.}
596 SMetaFrameDelay
= 'FrameDelay';
597 { Number of times animation should be looped (0 = infinite looping). Type is Int. }
598 SMetaAnimationLoops
= 'AnimationLoops';
599 { Gamma correction value. Type is Float.}
600 SMetaGamma
= 'Gamma';
601 { Exposure value for HDR etc. Type is Float.}
602 SMetaExposure
= 'Exposure';
603 { EXIF image metadata raw blob.}
604 SMetaExifBlob
= 'ExifBlob';
605 { XMP image metadata raw blob.}
606 SMetaXmpBlob
= 'XmpBlob';
607 { IPTC image metadata raw blob.}
608 SMetaIptcBlob
= 'IptcBlob';
611 GlobalMetadata
: TMetadata
;
613 { Returns symbolic name of given format.}
614 function GetFormatName(Format
: TImageFormat
): string;
615 { Returns string with information about given Image.}
616 function ImageToStr(const Image
: TImageData
): string;
617 { Returns Imaging version string in format 'Major.Minor.Patch'.}
618 function GetVersionStr
: string;
619 { If Condition is True then TruePart is retured, otherwise FalsePart is returned.}
620 function IffFormat(Condition
: Boolean; const TruePart
, FalsePart
: TImageFormat
): TImageFormat
;
622 { Registers new option so it can be used by SetOption and GetOption functions.
623 Returns True if registration was succesful - that is Id is valid and is
624 not already taken by another option.}
625 function RegisterOption(OptionId
: LongInt; Variable
: PLongInt): Boolean;
627 { Registers new image loader/saver so it can be used by LoadFrom/SaveTo
629 procedure RegisterImageFileFormat(AClass
: TImageFileFormatClass
);
630 { Returns image format loader/saver according to given extension
631 or nil if not found.}
632 function FindImageFileFormatByExt(const Ext
: string): TImageFileFormat
;
633 { Returns image format loader/saver according to given filename
634 or nil if not found.}
635 function FindImageFileFormatByName(const FileName
: string): TImageFileFormat
;
636 { Returns image format loader/saver based on its class
637 or nil if not found or not registered.}
638 function FindImageFileFormatByClass(AClass
: TImageFileFormatClass
): TImageFileFormat
;
639 { Returns number of registered image file format loaders/saver.}
640 function GetFileFormatCount
: LongInt;
641 { Returns image file format loader/saver at given index. Index must be
642 in range [0..GetFileFormatCount - 1] otherwise nil is returned.}
643 function GetFileFormatAtIndex(Index
: LongInt): TImageFileFormat
;
644 { Returns filter string for usage with open and save picture dialogs
645 which contains all registered image file formats.
646 Set OpenFileFilter to True if you want filter for open dialog
647 and to False if you want save dialog filter (formats that cannot save to files
649 For open dialog filter for all known graphic files
650 (like All(*.jpg;*.png;....) is added too at the first index.}
651 function GetImageFileFormatsFilter(OpenFileFilter
: Boolean): string;
652 { Returns file extension (without dot) of image format selected
653 by given filter index. Used filter string is defined by GetImageFileFormatsFilter
654 function. This function can be used with save dialogs (with filters created
655 by GetImageFileFormatsFilter) to get the extension of file format selected
656 in dialog quickly. Index is in range 1..N (as FilterIndex property
657 of TOpenDialog/TSaveDialog)}
658 function GetFilterIndexExtension(Index
: LongInt; OpenFileFilter
: Boolean): string;
659 { Returns filter index of image file format of file specified by FileName. Used filter
660 string is defined by GetImageFileFormatsFilter function.
661 Returned index is in range 1..N (as FilterIndex property of TOpenDialog/TSaveDialog)}
662 function GetFileNameFilterIndex(const FileName
: string; OpenFileFilter
: Boolean): LongInt;
664 { Returns current IO functions.}
665 function GetIO
: TIOFunctions
;
666 { Raises EImagingError with given message.}
667 procedure RaiseImaging(const Msg
: string; const Args
: array of const); overload
;
668 procedure RaiseImaging(const Msg
: string); overload
; {$IFDEF USE_INLINE}inline;{$ENDIF}
671 SImagingLibTitle
= 'Vampyre Imaging Library';
676 {$IFNDEF DONT_LINK_BITMAP}
679 {$IFNDEF DONT_LINK_JPEG}
682 {$IF not Defined(DONT_LINK_PNG) or not Defined(DONT_LINK_MNG) or not Defined(DONT_LINK_JNG)}
683 ImagingNetworkGraphics
,
685 {$IFNDEF DONT_LINK_GIF}
688 {$IFNDEF DONT_LINK_DDS}
691 {$IFNDEF DONT_LINK_TARGA}
694 {$IFNDEF DONT_LINK_PNM}
697 {$IFNDEF DONT_LINK_RADHDR}
700 {$IFNDEF DONT_LINK_EXTRAS}
704 ImagingFormats
, ImagingUtility
, ImagingIO
, Variants
;
707 SExceptMsg
= 'Exception Message';
708 SAllFilter
= 'All Images';
709 SUnknownFormat
= 'Unknown and unsupported format';
711 SErrorFreeImage
= 'Error while freeing image. %s';
712 SErrorCloneImage
= 'Error while cloning image. %s';
713 SErrorFlipImage
= 'Error while flipping image. %s';
714 SErrorMirrorImage
= 'Error while mirroring image. %s';
715 SErrorResizeImage
= 'Error while resizing image. %s';
716 SErrorSwapImage
= 'Error while swapping channels of image. %s';
717 SFileFormatCanNotLoad
= 'Image Format "%s" does not support loading images.';
718 SFileFormatCanNotSave
= 'Image Format "%s" does not support saving images.';
719 SErrorNewImage
= 'Error while creating image data with params: Width=%d ' +
720 'Height=%d Format=%s.';
721 SErrorConvertImage
= 'Error while converting image to format "%s". %s';
722 SImageInfo
= 'Image @%p info: Width = %dpx, Height = %dpx, ' +
723 'Format = %s, Size = %.0n %s, Bits @%p, Palette @%p.';
724 SImageInfoInvalid
= 'Access violation encountered when getting info on ' +
725 'image at address %p.';
726 SFileNotValid
= 'File "%s" is not valid image in "%s" format.';
727 SStreamNotValid
= 'Stream %p does not contain valid image in "%s" format.';
728 SMemoryNotValid
= 'Memory %p (%d Bytes) does not contain valid image ' +
730 SErrorLoadingFile
= 'Error while loading images from file "%s" (file format: %s).';
731 SErrorLoadingStream
= 'Error while loading images from stream %p (file format: %s).';
732 SErrorLoadingMemory
= 'Error while loading images from memory %p (%d Bytes) (file format: %s).';
733 SErrorSavingFile
= 'Error while saving images to file "%s" (file format: %s).';
734 SErrorSavingStream
= 'Error while saving images to stream %p (file format: %s).';
735 SErrorSavingMemory
= 'Error while saving images to memory %p (%d Bytes) (file format: %s).';
736 SErrorFindColor
= 'Error while finding color in palette @%p with %d entries.';
737 SErrorGrayscalePalette
= 'Error while filling grayscale palette @%p with %d entries.';
738 SErrorCustomPalette
= 'Error while filling custom palette @%p with %d entries.';
739 SErrorSwapPalette
= 'Error while swapping channels of palette @%p with %d entries.';
740 SErrorReduceColors
= 'Error while reducing number of colors of image to %d. %s';
741 SErrorGenerateMipMaps
= 'Error while generating %d mipmap levels for image %s';
742 SImagesNotValid
= 'One or more images are not valid.';
743 SErrorCopyRect
= 'Error while copying rect from image %s to image %s.';
744 SErrorMapImage
= 'Error while mapping image %s to palette.';
745 SErrorFillRect
= 'Error while filling rectangle X:%d Y:%d W:%d H:%d in image %s';
746 SErrorSplitImage
= 'Error while splitting image %s to %dx%d sized chunks.';
747 SErrorMakePaletteForImages
= 'Error while making %d color palette for %d images.';
748 SErrorNewPalette
= 'Error while creating new palette with %d entries';
749 SErrorFreePalette
= 'Error while freeing palette @%p';
750 SErrorCopyPalette
= 'Error while copying %d entries from palette @%p to @%p';
751 SErrorReplaceColor
= 'Error while replacing colors in rectangle X:%d Y:%d W:%d H:%d of image %s';
752 SErrorRotateImage
= 'Error while rotating image %s by %.2n degrees';
753 SErrorStretchRect
= 'Error while stretching rect from image %s to image %s.';
754 SErrorEmptyStream
= 'Input stream has no data. Check Position property.';
755 SErrorInvalidInputImage
= 'Invalid input image.';
757 SErrorBadImage
= 'Bad image detected.';
760 // Initial size of array with options information
761 InitialOptions
= 256;
762 // Max depth of the option stack
763 OptionStackDepth
= 8;
764 // Do not change the default format now, its too late
765 DefaultImageFormat
: TImageFormat
= ifA8R8G8B8
;
766 // Format used to create metadata IDs for frames loaded form multiimages.
767 SMetaIdForSubImage
= '%s/%d';
770 TOptionArray
= array of PLongInt;
771 TOptionValueArray
= array of LongInt;
773 TOptionStack
= class(TObject
)
775 FStack
: array[0..OptionStackDepth
- 1] of TOptionValueArray
;
779 destructor Destroy
; override;
780 function Push
: Boolean;
781 function Pop
: Boolean;
785 // Currently set IO functions
787 // List with all registered TImageFileFormat classes
788 ImageFileFormats
: TList
= nil;
789 // Aarray with registered options (pointers to their values)
790 Options
: TOptionArray
= nil;
791 // Array containing addional infomation about every image format
792 ImageFormatInfos
: TImageFormatInfoArray
;
793 // Stack used by PushOptions/PopOtions functions
794 OptionStack
: TOptionStack
= nil;
796 // Variable for ImagingColorReduction option
797 ColorReductionMask
: LongInt = $FF;
798 // Variable for ImagingLoadOverrideFormat option
799 LoadOverrideFormat
: TImageFormat
= ifUnknown
;
800 // Variable for ImagingSaveOverrideFormat option
801 SaveOverrideFormat
: TImageFormat
= ifUnknown
;
802 // Variable for ImagingSaveOverrideFormat option
803 MipMapFilter
: TSamplingFilter
= sfLinear
;
804 // Variable for ImagingBinaryTreshold option
805 BinaryTreshold
: Integer = 128;
809 constructor EImagingBadImage
.Create
;
811 inherited Create(SErrorBadImage
);
814 { Internal unit functions }
816 { Modifies option value to be in the allowed range. Works only
817 for options registered in this unit.}
818 function CheckOptionValue(OptionId
, Value
: LongInt): LongInt; forward;
819 { Sets IO functions to file IO.}
820 procedure SetFileIO
; forward;
821 { Sets IO functions to stream IO.}
822 procedure SetStreamIO
; forward;
823 { Sets IO functions to memory IO.}
824 procedure SetMemoryIO
; forward;
825 { Inits image format infos array.}
826 procedure InitImageFormats
; forward;
827 { Freew image format infos array.}
828 procedure FreeImageFileFormats
; forward;
829 { Creates options array and stack.}
830 procedure InitOptions
; forward;
831 { Frees options array and stack.}
832 procedure FreeOptions
; forward;
834 function UpdateExceptMessage(E
: Exception
; const MsgToPrepend
: string; const Args
: array of const): Exception
;
837 E
.Message := Format(MsgToPrepend
, Args
) + ' ' + SExceptMsg
+ ': ' + E
.Message
840 { ------------------------------------------------------------------------
841 Low Level Interface Functions
842 ------------------------------------------------------------------------}
844 { General Functions }
846 procedure InitImage(var Image
: TImageData
);
848 FillChar(Image
, SizeOf(Image
), 0);
851 function NewImage(Width
, Height
: LongInt; Format
: TImageFormat
; var Image
:
852 TImageData
): Boolean;
854 FInfo
: PImageFormatInfo
;
856 Assert((Width
> 0) and (Height
>0));
857 Assert(IsImageFormatValid(Format
));
861 Image
.Width
:= Width
;
862 Image
.Height
:= Height
;
863 // Select default data format if selected
864 if (Format
= ifDefault
) then
865 Image
.Format
:= DefaultImageFormat
867 Image
.Format
:= Format
;
868 // Get extended format info
869 FInfo
:= ImageFormatInfos
[Image
.Format
];
875 // Check image dimensions and calculate its size in bytes
876 FInfo
.CheckDimensions(FInfo
.Format
, Image
.Width
, Image
.Height
);
877 Image
.Size
:= FInfo
.GetPixelsSize(FInfo
.Format
, Image
.Width
, Image
.Height
);
878 if Image
.Size
= 0 then
883 // Image bits are allocated and set to zeroes
884 GetMem(Image
.Bits
, Image
.Size
);
885 FillChar(Image
.Bits
^, Image
.Size
, 0);
886 // Palette is allocated and set to zeroes
887 if FInfo
.PaletteEntries
> 0 then
889 GetMem(Image
.Palette
, FInfo
.PaletteEntries
* SizeOf(TColor32Rec
));
890 FillChar(Image
.Palette
^, FInfo
.PaletteEntries
* SizeOf(TColor32Rec
), 0);
892 Result
:= TestImage(Image
);
897 FreeMem(Image
.Palette
);
899 raise UpdateExceptMessage(E
, SErrorNewImage
, [Width
, Height
, GetFormatName(Format
)]);
904 function TestImage(const Image
: TImageData
): Boolean;
907 Result
:= (LongInt(Image
.Format
) >= LongInt(Low(TImageFormat
))) and
908 (LongInt(Image
.Format
) <= LongInt(High(TImageFormat
))) and
909 (ImageFormatInfos
[Image
.Format
] <> nil) and
910 (Assigned(ImageFormatInfos
[Image
.Format
].GetPixelsSize
) and
911 (ImageFormatInfos
[Image
.Format
].GetPixelsSize(Image
.Format
,
912 Image
.Width
, Image
.Height
) = Image
.Size
));
914 // Possible int overflows or other errors
919 procedure FreeImage(var Image
: TImageData
);
922 if TestImage(Image
) then
924 FreeMemNil(Image
.Bits
);
925 FreeMemNil(Image
.Palette
);
929 raise UpdateExceptMessage(GetExceptObject
, SErrorFreeImage
, [ImageToStr(Image
)]);
933 procedure FreeImagesInArray(var Images
: TDynImageDataArray
);
937 if Length(Images
) > 0 then
939 for I
:= 0 to Length(Images
) - 1 do
940 FreeImage(Images
[I
]);
941 SetLength(Images
, 0);
945 function TestImagesInArray(const Images
: TDynImageDataArray
): Boolean;
949 if Length(Images
) > 0 then
952 for I
:= 0 to Length(Images
) - 1 do
954 Result
:= Result
and TestImage(Images
[I
]);
963 function DetermineFileFormat(const FileName
: string): string;
966 Fmt
: TImageFileFormat
;
967 Handle
: TImagingHandle
;
969 Assert(FileName
<> '');
972 Handle
:= IO
.Open(PChar(FileName
), omReadOnly
);
974 // First file format according to FileName and test if the data in
975 // file is really in that format
976 for I
:= 0 to ImageFileFormats
.Count
- 1 do
978 Fmt
:= TImageFileFormat(ImageFileFormats
[I
]);
979 if Fmt
.TestFileName(FileName
) and Fmt
.TestFormat(Handle
) then
981 Result
:= Fmt
.Extensions
[0];
985 // No file format was found with filename search so try data-based search
986 for I
:= 0 to ImageFileFormats
.Count
- 1 do
988 Fmt
:= TImageFileFormat(ImageFileFormats
[I
]);
989 if Fmt
.TestFormat(Handle
) then
991 Result
:= Fmt
.Extensions
[0];
1000 function DetermineStreamFormat(Stream
: TStream
): string;
1003 Fmt
: TImageFileFormat
;
1004 Handle
: TImagingHandle
;
1006 Assert(Stream
<> nil);
1009 Handle
:= IO
.Open(Pointer(Stream
), omReadOnly
);
1011 for I
:= 0 to ImageFileFormats
.Count
- 1 do
1013 Fmt
:= TImageFileFormat(ImageFileFormats
[I
]);
1014 if Fmt
.TestFormat(Handle
) then
1016 Result
:= Fmt
.Extensions
[0];
1025 function DetermineMemoryFormat(Data
: Pointer; Size
: LongInt): string;
1028 Fmt
: TImageFileFormat
;
1029 Handle
: TImagingHandle
;
1030 IORec
: TMemoryIORec
;
1032 Assert((Data
<> nil) and (Size
> 0));
1036 IORec
.Position
:= 0;
1038 Handle
:= IO
.Open(@IORec
, omReadOnly
);
1040 for I
:= 0 to ImageFileFormats
.Count
- 1 do
1042 Fmt
:= TImageFileFormat(ImageFileFormats
[I
]);
1043 if Fmt
.TestFormat(Handle
) then
1045 Result
:= Fmt
.Extensions
[0];
1054 function IsFileFormatSupported(const FileName
: string): Boolean;
1056 Result
:= FindImageFileFormatByName(FileName
) <> nil;
1059 function EnumFileFormats(var Index
: LongInt; var Name
, DefaultExt
, Masks
: string;
1060 var CanSaveImages
, IsMultiImageFormat
: Boolean): Boolean;
1062 FileFmt
: TImageFileFormat
;
1064 FileFmt
:= GetFileFormatAtIndex(Index
);
1065 Result
:= FileFmt
<> nil;
1068 Name
:= FileFmt
.Name
;
1069 DefaultExt
:= FileFmt
.Extensions
[0];
1070 Masks
:= FileFmt
.Masks
.DelimitedText
;
1071 CanSaveImages
:= FileFmt
.CanSave
;
1072 IsMultiImageFormat
:= FileFmt
.IsMultiImageFormat
;
1080 CanSaveImages
:= False;
1081 IsMultiImageFormat
:= False;
1085 { Loading Functions }
1087 function LoadImageFromFile(const FileName
: string; var Image
: TImageData
):
1090 Format
: TImageFileFormat
;
1091 IArray
: TDynImageDataArray
;
1094 Assert(FileName
<> '');
1096 Format
:= FindImageFileFormatByExt(DetermineFileFormat(FileName
));
1097 if Format
<> nil then
1100 Result
:= Format
.LoadFromFile(FileName
, IArray
, True);
1101 if Result
and (Length(IArray
) > 0) then
1104 for I
:= 1 to Length(IArray
) - 1 do
1105 FreeImage(IArray
[I
]);
1112 function LoadImageFromStream(Stream
: TStream
; var Image
: TImageData
): Boolean;
1114 Format
: TImageFileFormat
;
1115 IArray
: TDynImageDataArray
;
1118 Assert(Stream
<> nil);
1119 if Stream
.Size
- Stream
.Position
= 0 then
1120 RaiseImaging(SErrorEmptyStream
, []);
1122 Format
:= FindImageFileFormatByExt(DetermineStreamFormat(Stream
));
1123 if Format
<> nil then
1126 Result
:= Format
.LoadFromStream(Stream
, IArray
, True);
1127 if Result
and (Length(IArray
) > 0) then
1130 for I
:= 1 to Length(IArray
) - 1 do
1131 FreeImage(IArray
[I
]);
1138 function LoadImageFromMemory(Data
: Pointer; Size
: LongInt; var Image
: TImageData
): Boolean;
1140 Format
: TImageFileFormat
;
1141 IArray
: TDynImageDataArray
;
1144 Assert((Data
<> nil) and (Size
> 0));
1146 Format
:= FindImageFileFormatByExt(DetermineMemoryFormat(Data
, Size
));
1147 if Format
<> nil then
1150 Result
:= Format
.LoadFromMemory(Data
, Size
, IArray
, True);
1151 if Result
and (Length(IArray
) > 0) then
1154 for I
:= 1 to Length(IArray
) - 1 do
1155 FreeImage(IArray
[I
]);
1162 function LoadMultiImageFromFile(const FileName
: string; var Images
:
1163 TDynImageDataArray
): Boolean;
1165 Format
: TImageFileFormat
;
1167 Assert(FileName
<> '');
1169 Format
:= FindImageFileFormatByExt(DetermineFileFormat(FileName
));
1170 if Format
<> nil then
1172 FreeImagesInArray(Images
);
1173 Result
:= Format
.LoadFromFile(FileName
, Images
);
1177 function LoadMultiImageFromStream(Stream
: TStream
; var Images
: TDynImageDataArray
): Boolean;
1179 Format
: TImageFileFormat
;
1181 Assert(Stream
<> nil);
1182 if Stream
.Size
- Stream
.Position
= 0 then
1183 RaiseImaging(SErrorEmptyStream
, []);
1185 Format
:= FindImageFileFormatByExt(DetermineStreamFormat(Stream
));
1186 if Format
<> nil then
1188 FreeImagesInArray(Images
);
1189 Result
:= Format
.LoadFromStream(Stream
, Images
);
1193 function LoadMultiImageFromMemory(Data
: Pointer; Size
: LongInt;
1194 var Images
: TDynImageDataArray
): Boolean;
1196 Format
: TImageFileFormat
;
1198 Assert((Data
<> nil) and (Size
> 0));
1200 Format
:= FindImageFileFormatByExt(DetermineMemoryFormat(Data
, Size
));
1201 if Format
<> nil then
1203 FreeImagesInArray(Images
);
1204 Result
:= Format
.LoadFromMemory(Data
, Size
, Images
);
1208 { Saving Functions }
1210 function SaveImageToFile(const FileName
: string; const Image
: TImageData
): Boolean;
1212 Format
: TImageFileFormat
;
1213 IArray
: TDynImageDataArray
;
1215 Assert(FileName
<> '');
1217 Format
:= FindImageFileFormatByName(FileName
);
1218 if Format
<> nil then
1220 SetLength(IArray
, 1);
1222 Result
:= Format
.SaveToFile(FileName
, IArray
, True);
1226 function SaveImageToStream(const Ext
: string; Stream
: TStream
;
1227 const Image
: TImageData
): Boolean;
1229 Format
: TImageFileFormat
;
1230 IArray
: TDynImageDataArray
;
1232 Assert((Ext
<> '') and (Stream
<> nil));
1234 Format
:= FindImageFileFormatByExt(Ext
);
1235 if Format
<> nil then
1237 SetLength(IArray
, 1);
1239 Result
:= Format
.SaveToStream(Stream
, IArray
, True);
1243 function SaveImageToMemory(const Ext
: string; Data
: Pointer; var Size
: LongInt;
1244 const Image
: TImageData
): Boolean;
1246 Format
: TImageFileFormat
;
1247 IArray
: TDynImageDataArray
;
1249 Assert((Ext
<> '') and (Data
<> nil) and (Size
> 0));
1251 Format
:= FindImageFileFormatByExt(Ext
);
1252 if Format
<> nil then
1254 SetLength(IArray
, 1);
1256 Result
:= Format
.SaveToMemory(Data
, Size
, IArray
, True);
1260 function SaveMultiImageToFile(const FileName
: string;
1261 const Images
: TDynImageDataArray
): Boolean;
1263 Format
: TImageFileFormat
;
1265 Assert(FileName
<> '');
1267 Format
:= FindImageFileFormatByName(FileName
);
1268 if Format
<> nil then
1269 Result
:= Format
.SaveToFile(FileName
, Images
);
1272 function SaveMultiImageToStream(const Ext
: string; Stream
: TStream
;
1273 const Images
: TDynImageDataArray
): Boolean;
1275 Format
: TImageFileFormat
;
1277 Assert((Ext
<> '') and (Stream
<> nil));
1279 Format
:= FindImageFileFormatByExt(Ext
);
1280 if Format
<> nil then
1281 Result
:= Format
.SaveToStream(Stream
, Images
);
1284 function SaveMultiImageToMemory(const Ext
: string; Data
: Pointer;
1285 var Size
: LongInt; const Images
: TDynImageDataArray
): Boolean;
1287 Format
: TImageFileFormat
;
1289 Assert((Ext
<> '') and (Data
<> nil) and (Size
> 0));
1291 Format
:= FindImageFileFormatByExt(Ext
);
1292 if Format
<> nil then
1293 Result
:= Format
.SaveToMemory(Data
, Size
, Images
);
1296 { Manipulation Functions }
1298 function CloneImage(const Image
: TImageData
; var Clone
: TImageData
): Boolean;
1300 Info
: PImageFormatInfo
;
1303 if TestImage(Image
) then
1305 if TestImage(Clone
) and (Image
.Bits
<> Clone
.Bits
) then
1310 Info
:= ImageFormatInfos
[Image
.Format
];
1311 Clone
.Width
:= Image
.Width
;
1312 Clone
.Height
:= Image
.Height
;
1313 Clone
.Format
:= Image
.Format
;
1314 Clone
.Size
:= Image
.Size
;
1316 if Info
.PaletteEntries
> 0 then
1318 GetMem(Clone
.Palette
, Info
.PaletteEntries
* SizeOf(TColor32Rec
));
1319 Move(Image
.Palette
^, Clone
.Palette
^, Info
.PaletteEntries
*
1320 SizeOf(TColor32Rec
));
1323 GetMem(Clone
.Bits
, Clone
.Size
);
1324 Move(Image
.Bits
^, Clone
.Bits
^, Clone
.Size
);
1327 raise UpdateExceptMessage(GetExceptObject
, SErrorCloneImage
, [ImageToStr(Image
)]);
1331 function ConvertImage(var Image
: TImageData
; DestFormat
: TImageFormat
): Boolean;
1335 NewSize
, NumPixels
: LongInt;
1336 SrcInfo
, DstInfo
: PImageFormatInfo
;
1338 Assert(IsImageFormatValid(DestFormat
));
1340 if TestImage(Image
) then
1343 // If default format is set we use DefaultImageFormat
1344 if DestFormat
= ifDefault
then
1345 DestFormat
:= DefaultImageFormat
;
1346 SrcInfo
:= ImageFormatInfos
[Format
];
1347 DstInfo
:= ImageFormatInfos
[DestFormat
];
1348 if SrcInfo
= DstInfo
then
1350 // There is nothing to convert - src is alredy in dest format
1354 // Exit Src or Dest format is invalid
1355 if (SrcInfo
= nil) or (DstInfo
= nil) then Exit
;
1356 // If dest format is just src with swapped channels we call
1357 // SwapChannels instead
1358 if (SrcInfo
.RBSwapFormat
= DestFormat
) and
1359 (DstInfo
.RBSwapFormat
= SrcInfo
.Format
) then
1361 Result
:= SwapChannels(Image
, ChannelRed
, ChannelBlue
);
1362 Image
.Format
:= SrcInfo
.RBSwapFormat
;
1366 if (not SrcInfo
.IsSpecial
) and (not DstInfo
.IsSpecial
) then
1368 NumPixels
:= Width
* Height
;
1369 NewSize
:= NumPixels
* DstInfo
.BytesPerPixel
;
1370 GetMem(NewData
, NewSize
);
1371 FillChar(NewData
^, NewSize
, 0);
1372 GetMem(NewPal
, DstInfo
.PaletteEntries
* SizeOf(TColor32Rec
));
1373 FillChar(NewPal
^, DstInfo
.PaletteEntries
* SizeOf(TColor32Rec
), 0);
1375 if SrcInfo
.IsIndexed
then
1377 // Source: indexed format
1378 if DstInfo
.IsIndexed
then
1379 IndexToIndex(NumPixels
, Bits
, NewData
, SrcInfo
, DstInfo
, Palette
, NewPal
)
1380 else if DstInfo
.HasGrayChannel
then
1381 IndexToGray(NumPixels
, Bits
, NewData
, SrcInfo
, DstInfo
, Palette
)
1382 else if DstInfo
.IsFloatingPoint
then
1383 IndexToFloat(NumPixels
, Bits
, NewData
, SrcInfo
, DstInfo
, Palette
)
1385 IndexToChannel(NumPixels
, Bits
, NewData
, SrcInfo
, DstInfo
, Palette
);
1387 else if SrcInfo
.HasGrayChannel
then
1389 // Source: grayscale format
1390 if DstInfo
.IsIndexed
then
1391 GrayToIndex(NumPixels
, Bits
, NewData
, SrcInfo
, DstInfo
, NewPal
)
1392 else if DstInfo
.HasGrayChannel
then
1393 GrayToGray(NumPixels
, Bits
, NewData
, SrcInfo
, DstInfo
)
1394 else if DstInfo
.IsFloatingPoint
then
1395 GrayToFloat(NumPixels
, Bits
, NewData
, SrcInfo
, DstInfo
)
1397 GrayToChannel(NumPixels
, Bits
, NewData
, SrcInfo
, DstInfo
);
1399 else if SrcInfo
.IsFloatingPoint
then
1401 // Source: floating point format
1402 if DstInfo
.IsIndexed
then
1403 FloatToIndex(NumPixels
, Bits
, NewData
, SrcInfo
, DstInfo
, NewPal
)
1404 else if DstInfo
.HasGrayChannel
then
1405 FloatToGray(NumPixels
, Bits
, NewData
, SrcInfo
, DstInfo
)
1406 else if DstInfo
.IsFloatingPoint
then
1407 FloatToFloat(NumPixels
, Bits
, NewData
, SrcInfo
, DstInfo
)
1409 FloatToChannel(NumPixels
, Bits
, NewData
, SrcInfo
, DstInfo
);
1413 // Source: standard multi channel image
1414 if DstInfo
.IsIndexed
then
1415 ChannelToIndex(NumPixels
, Bits
, NewData
, SrcInfo
, DstInfo
, NewPal
)
1416 else if DstInfo
.HasGrayChannel
then
1417 ChannelToGray(NumPixels
, Bits
, NewData
, SrcInfo
, DstInfo
)
1418 else if DstInfo
.IsFloatingPoint
then
1419 ChannelToFloat(NumPixels
, Bits
, NewData
, SrcInfo
, DstInfo
)
1421 ChannelToChannel(NumPixels
, Bits
, NewData
, SrcInfo
, DstInfo
);
1425 FreeMemNil(Palette
);
1426 Format
:= DestFormat
;
1432 ConvertSpecial(Image
, SrcInfo
, DstInfo
);
1434 Assert(SrcInfo
.Format
<> Image
.Format
);
1438 raise UpdateExceptMessage(GetExceptObject
, SErrorConvertImage
, [GetFormatName(DestFormat
), ImageToStr(Image
)]);
1442 function FlipImage(var Image
: TImageData
): Boolean;
1444 P1
, P2
, Buff
: Pointer;
1445 WidthBytes
, I
: LongInt;
1446 OldFmt
: TImageFormat
;
1449 OldFmt
:= Image
.Format
;
1450 if TestImage(Image
) then
1453 if ImageFormatInfos
[OldFmt
].IsSpecial
then
1454 ConvertImage(Image
, ifDefault
);
1456 WidthBytes
:= Width
* ImageFormatInfos
[Format
].BytesPerPixel
;
1457 GetMem(Buff
, WidthBytes
);
1459 // Swap all scanlines of image
1460 for I
:= 0 to Height
div 2 - 1 do
1462 P1
:= @PByteArray(Bits
)[I
* WidthBytes
];
1463 P2
:= @PByteArray(Bits
)[(Height
- I
- 1) * WidthBytes
];
1464 Move(P1
^, Buff
^, WidthBytes
);
1465 Move(P2
^, P1
^, WidthBytes
);
1466 Move(Buff
^, P2
^, WidthBytes
);
1472 if OldFmt
<> Format
then
1473 ConvertImage(Image
, OldFmt
);
1477 RaiseImaging(SErrorFlipImage
, [ImageToStr(Image
)]);
1481 function MirrorImage(var Image
: TImageData
): Boolean;
1485 Bpp
, Y
, X
, WidthDiv2
, WidthBytes
, XLeft
, XRight
: LongInt;
1486 OldFmt
: TImageFormat
;
1489 OldFmt
:= Image
.Format
;
1490 if TestImage(Image
) then
1493 if ImageFormatInfos
[OldFmt
].IsSpecial
then
1494 ConvertImage(Image
, ifDefault
);
1496 Bpp
:= ImageFormatInfos
[Format
].BytesPerPixel
;
1497 WidthDiv2
:= Width
div 2;
1498 WidthBytes
:= Width
* Bpp
;
1499 // Mirror all pixels on each scanline of image
1500 for Y
:= 0 to Height
- 1 do
1502 Scanline
:= @PByteArray(Bits
)[Y
* WidthBytes
];
1504 XRight
:= (Width
- 1) * Bpp
;
1505 for X
:= 0 to WidthDiv2
- 1 do
1507 CopyPixel(@PByteArray(Scanline
)[XLeft
], @Buff
, Bpp
);
1508 CopyPixel(@PByteArray(Scanline
)[XRight
],
1509 @PByteArray(Scanline
)[XLeft
], Bpp
);
1510 CopyPixel(@Buff
, @PByteArray(Scanline
)[XRight
], Bpp
);
1516 if OldFmt
<> Format
then
1517 ConvertImage(Image
, OldFmt
);
1521 RaiseImaging(SErrorMirrorImage
, [ImageToStr(Image
)]);
1525 function ResizeImage(var Image
: TImageData
; NewWidth
, NewHeight
: LongInt;
1526 Filter
: TResizeFilter
): Boolean;
1528 WorkImage
: TImageData
;
1530 Assert((NewWidth
> 0) and (NewHeight
> 0), 'New width or height is zero.');
1532 if TestImage(Image
) and ((Image
.Width
<> NewWidth
) or (Image
.Height
<> NewHeight
)) then
1534 InitImage(WorkImage
);
1535 // Create new image with desired dimensions
1536 NewImage(NewWidth
, NewHeight
, Image
.Format
, WorkImage
);
1537 // Stretch pixels from old image to new one
1538 StretchRect(Image
, 0, 0, Image
.Width
, Image
.Height
,
1539 WorkImage
, 0, 0, WorkImage
.Width
, WorkImage
.Height
, Filter
);
1540 // Free old image and assign new image to it
1541 FreeMemNil(Image
.Bits
);
1542 if Image
.Palette
<> nil then
1544 FreeMem(WorkImage
.Palette
);
1545 WorkImage
.Palette
:= Image
.Palette
;
1550 raise UpdateExceptMessage(GetExceptObject
, SErrorResizeImage
, [ImageToStr(Image
)]);
1554 function SwapChannels(var Image
: TImageData
; SrcChannel
, DstChannel
: LongInt): Boolean;
1556 I
, NumPixels
: LongInt;
1557 Info
: PImageFormatInfo
;
1564 Assert((SrcChannel
in [0..3]) and (DstChannel
in [0..3]));
1566 if TestImage(Image
) and (SrcChannel
<> DstChannel
) then
1569 NumPixels
:= Width
* Height
;
1570 Info
:= ImageFormatInfos
[Format
];
1573 if (Info
.Format
= ifR8G8B8
) or ((Info
.Format
= ifA8R8G8B8
) and
1574 (SrcChannel
<> ChannelAlpha
) and (DstChannel
<> ChannelAlpha
)) then
1576 // Swap channels of most common formats R8G8B8 and A8R8G8B8 (no alpha)
1577 for I
:= 0 to NumPixels
- 1 do
1578 with PColor24Rec(Data
)^ do
1580 Swap
:= Channels
[SrcChannel
];
1581 Channels
[SrcChannel
] := Channels
[DstChannel
];
1582 Channels
[DstChannel
] := Swap
;
1583 Inc(Data
, Info
.BytesPerPixel
);
1586 else if Info
.IsIndexed
then
1588 // Swap palette channels of indexed images
1589 SwapChannelsOfPalette(Palette
, Info
.PaletteEntries
, SrcChannel
, DstChannel
)
1591 else if Info
.IsFloatingPoint
then
1593 // Swap channels of floating point images
1594 for I
:= 0 to NumPixels
- 1 do
1596 FloatGetSrcPixel(Data
, Info
, PixF
);
1599 SwapF
:= Channels
[SrcChannel
];
1600 Channels
[SrcChannel
] := Channels
[DstChannel
];
1601 Channels
[DstChannel
] := SwapF
;
1603 FloatSetDstPixel(Data
, Info
, PixF
);
1604 Inc(Data
, Info
.BytesPerPixel
);
1607 else if Info
.IsSpecial
then
1609 // Swap channels of special format images
1610 ConvertImage(Image
, ifDefault
);
1611 SwapChannels(Image
, SrcChannel
, DstChannel
);
1612 ConvertImage(Image
, Info
.Format
);
1614 else if Info
.HasGrayChannel
and Info
.HasAlphaChannel
and
1615 ((SrcChannel
= ChannelAlpha
) or (DstChannel
= ChannelAlpha
)) then
1617 for I
:= 0 to NumPixels
- 1 do
1619 // If we have grayscale image with alpha and alpha is channel
1620 // to be swapped, we swap it. No other alternative for gray images,
1621 // just alpha and something
1622 GrayGetSrcPixel(Data
, Info
, Pix64
, Alpha
);
1626 GraySetDstPixel(Data
, Info
, Pix64
, Alpha
);
1627 Inc(Data
, Info
.BytesPerPixel
);
1632 // Then do general swap on other channel image formats
1633 for I
:= 0 to NumPixels
- 1 do
1635 ChannelGetSrcPixel(Data
, Info
, Pix64
);
1638 Swap
:= Channels
[SrcChannel
];
1639 Channels
[SrcChannel
] := Channels
[DstChannel
];
1640 Channels
[DstChannel
] := Swap
;
1642 ChannelSetDstPixel(Data
, Info
, Pix64
);
1643 Inc(Data
, Info
.BytesPerPixel
);
1649 RaiseImaging(SErrorSwapImage
, [ImageToStr(Image
)]);
1653 function ReduceColors(var Image
: TImageData
; MaxColors
: LongInt): Boolean;
1655 TmpInfo
: TImageFormatInfo
;
1657 I
, NumPixels
: LongInt;
1660 OldFmt
: TImageFormat
;
1663 if TestImage(Image
) then
1666 // First create temp image info and allocate output bits and palette
1667 MaxColors
:= ClampInt(MaxColors
, 2, High(Word));
1669 FillChar(TmpInfo
, SizeOf(TmpInfo
), 0);
1670 TmpInfo
.PaletteEntries
:= MaxColors
;
1671 TmpInfo
.BytesPerPixel
:= 2;
1672 NumPixels
:= Width
* Height
;
1673 GetMem(Data
, NumPixels
* TmpInfo
.BytesPerPixel
);
1674 GetMem(Pal
, MaxColors
* SizeOf(TColor32Rec
));
1675 ConvertImage(Image
, ifA8R8G8B8
);
1676 // We use median cut algorithm to create reduced palette and to
1677 // fill Data with indices to this palette
1678 ReduceColorsMedianCut(NumPixels
, Bits
, PByte(Data
),
1679 ImageFormatInfos
[Format
], @TmpInfo
, MaxColors
, ColorReductionMask
, Pal
);
1682 // Then we write reduced colors to the input image
1683 for I
:= 0 to NumPixels
- 1 do
1685 Col
.Color
:= Pal
[Index
^].Color
;
1691 // And convert it to its original format
1692 ConvertImage(Image
, OldFmt
);
1695 RaiseImaging(SErrorReduceColors
, [MaxColors
, ImageToStr(Image
)]);
1699 function GenerateMipMaps(const Image
: TImageData
; Levels
: LongInt;
1700 var MipMaps
: TDynImageDataArray
): Boolean;
1702 Width
, Height
, I
, Count
: LongInt;
1703 Info
: TImageFormatInfo
;
1704 CompatibleCopy
: TImageData
;
1707 if TestImage(Image
) then
1709 Width
:= Image
.Width
;
1710 Height
:= Image
.Height
;
1711 // We compute number of possible mipmap levels and if
1712 // the given levels are invalid or zero we use this value
1713 Count
:= GetNumMipMapLevels(Width
, Height
);
1714 if (Levels
<= 0) or (Levels
> Count
) then
1717 // If we have special format image we create copy to allow pixel access.
1718 // This is also done in FillMipMapLevel which is called for each level
1719 // but then the main big image would be converted to compatible
1721 GetImageFormatInfo(Image
.Format
, Info
);
1722 if Info
.IsSpecial
then
1724 InitImage(CompatibleCopy
);
1725 CloneImage(Image
, CompatibleCopy
);
1726 ConvertImage(CompatibleCopy
, ifDefault
);
1729 CompatibleCopy
:= Image
;
1731 FreeImagesInArray(MipMaps
);
1732 SetLength(MipMaps
, Levels
);
1733 CloneImage(Image
, MipMaps
[0]);
1735 for I
:= 1 to Levels
- 1 do
1737 Width
:= Width
shr 1;
1738 Height
:= Height
shr 1;
1739 if Width
< 1 then Width
:= 1;
1740 if Height
< 1 then Height
:= 1;
1741 FillMipMapLevel(CompatibleCopy
, Width
, Height
, MipMaps
[I
]);
1744 if CompatibleCopy
.Format
<> MipMaps
[0].Format
then
1746 // Must convert smaller levels to proper format
1747 for I
:= 1 to High(MipMaps
) do
1748 ConvertImage(MipMaps
[I
], MipMaps
[0].Format
);
1749 FreeImage(CompatibleCopy
);
1754 RaiseImaging(SErrorGenerateMipMaps
, [Levels
, ImageToStr(Image
)]);
1758 function MapImageToPalette(var Image
: TImageData
; Pal
: PPalette32
;
1759 Entries
: LongInt): Boolean;
1761 function FindNearestColor(Pal
: PPalette32
; Entries
: LongInt; Col
: TColor32Rec
): LongInt;
1763 I
, MinDif
, Dif
: LongInt;
1767 for I
:= 0 to Entries
- 1 do
1770 Dif
:= Abs(R
- Col
.R
);
1771 if Dif
> MinDif
then Continue
;
1772 Dif
:= Dif
+ Abs(G
- Col
.G
);
1773 if Dif
> MinDif
then Continue
;
1774 Dif
:= Dif
+ Abs(B
- Col
.B
);
1775 if Dif
> MinDif
then Continue
;
1776 Dif
:= Dif
+ Abs(A
- Col
.A
);
1777 if Dif
< MinDif
then
1786 I
, MaxEntries
: LongInt;
1788 PColor
: PColor32Rec
;
1789 CloneARGB
: TImageData
;
1790 Info
: PImageFormatInfo
;
1792 Assert((Entries
>= 2) and (Entries
<= 256));
1795 if TestImage(Image
) then
1797 // We create clone of source image in A8R8G8B8 and
1798 // then recreate source image in ifIndex8 format
1799 // with palette taken from Pal parameter
1800 InitImage(CloneARGB
);
1801 CloneImage(Image
, CloneARGB
);
1802 ConvertImage(CloneARGB
, ifA8R8G8B8
);
1804 NewImage(CloneARGB
.Width
, CloneARGB
.Height
, ifIndex8
, Image
);
1806 Info
:= ImageFormatInfos
[Image
.Format
];
1807 MaxEntries
:= Min(Info
.PaletteEntries
, Entries
);
1808 Move(Pal
^, Image
.Palette
^, MaxEntries
* SizeOf(TColor32Rec
));
1809 PIndex
:= Image
.Bits
;
1810 PColor
:= CloneARGB
.Bits
;
1812 // For every pixel of ARGB clone we find closest color in
1813 // given palette and assign its index to resulting image's pixel
1814 // procedure used here is very slow but simple and memory usage friendly
1815 // (contrary to other methods)
1816 for I
:= 0 to Image
.Width
* Image
.Height
- 1 do
1818 PIndex
^ := Byte(FindNearestColor(Image
.Palette
, MaxEntries
, PColor
^));
1823 FreeImage(CloneARGB
);
1826 raise UpdateExceptMessage(GetExceptObject
, SErrorMapImage
, [ImageToStr(Image
)]);
1830 function SplitImage(var Image
: TImageData
; var Chunks
: TDynImageDataArray
;
1831 ChunkWidth
, ChunkHeight
: LongInt; var XChunks
, YChunks
: LongInt;
1832 PreserveSize
: Boolean; Fill
: Pointer): Boolean;
1834 X
, Y
, XTrunc
, YTrunc
: LongInt;
1836 Info
: PImageFormatInfo
;
1837 OldFmt
: TImageFormat
;
1839 Assert((ChunkWidth
> 0) and (ChunkHeight
> 0));
1841 OldFmt
:= Image
.Format
;
1842 FreeImagesInArray(Chunks
);
1844 if TestImage(Image
) then
1846 Info
:= ImageFormatInfos
[Image
.Format
];
1847 if Info
.IsSpecial
then
1848 ConvertImage(Image
, ifDefault
);
1850 // We compute make sure that chunks are not larger than source image or negative
1851 ChunkWidth
:= ClampInt(ChunkWidth
, 0, Image
.Width
);
1852 ChunkHeight
:= ClampInt(ChunkHeight
, 0, Image
.Height
);
1853 // Number of chunks along X and Y axes is computed
1854 XChunks
:= Trunc(Ceil(Image
.Width
/ ChunkWidth
));
1855 YChunks
:= Trunc(Ceil(Image
.Height
/ ChunkHeight
));
1856 SetLength(Chunks
, XChunks
* YChunks
);
1858 // For every chunk we create new image and copy a portion of
1859 // the source image to it. If chunk is on the edge of the source image
1860 // we fill enpty space with Fill pixel data if PreserveSize is set or
1861 // make the chunk smaller if it is not set
1862 for Y
:= 0 to YChunks
- 1 do
1863 for X
:= 0 to XChunks
- 1 do
1865 // Determine if current chunk is on the edge of original image
1866 NotOnEdge
:= ((X
< XChunks
- 1) and (Y
< YChunks
- 1)) or
1867 ((Image
.Width
mod ChunkWidth
= 0) and (Image
.Height
mod ChunkHeight
= 0));
1869 if PreserveSize
or NotOnEdge
then
1871 // We should preserve chunk sizes or we are somewhere inside original image
1872 NewImage(ChunkWidth
, ChunkHeight
, Image
.Format
, Chunks
[Y
* XChunks
+ X
]);
1873 if (not NotOnEdge
) and (Fill
<> nil) then
1874 FillRect(Chunks
[Y
* XChunks
+ X
], 0, 0, ChunkWidth
, ChunkHeight
, Fill
);
1875 CopyRect(Image
, X
* ChunkWidth
, Y
* ChunkHeight
, ChunkWidth
, ChunkHeight
,
1876 Chunks
[Y
* XChunks
+ X
], 0, 0);
1880 // Create smaller edge chunk
1881 XTrunc
:= Image
.Width
- X
* ChunkWidth
;
1882 YTrunc
:= Image
.Height
- Y
* ChunkHeight
;
1883 NewImage(XTrunc
, YTrunc
, Image
.Format
, Chunks
[Y
* XChunks
+ X
]);
1884 CopyRect(Image
, X
* ChunkWidth
, Y
* ChunkHeight
, XTrunc
, YTrunc
,
1885 Chunks
[Y
* XChunks
+ X
], 0, 0);
1888 // If source image is in indexed format we copy its palette to chunk
1889 if Info
.IsIndexed
then
1891 Move(Image
.Palette
^, Chunks
[Y
* XChunks
+ X
].Palette
^,
1892 Info
.PaletteEntries
* SizeOf(TColor32Rec
));
1896 if OldFmt
<> Image
.Format
then
1898 ConvertImage(Image
, OldFmt
);
1899 for X
:= 0 to Length(Chunks
) - 1 do
1900 ConvertImage(Chunks
[X
], OldFmt
);
1905 raise UpdateExceptMessage(GetExceptObject
, SErrorSplitImage
,
1906 [ImageToStr(Image
), ChunkWidth
, ChunkHeight
]);
1910 function MakePaletteForImages(var Images
: TDynImageDataArray
; Pal
: PPalette32
;
1911 MaxColors
: LongInt; ConvertImages
: Boolean): Boolean;
1914 SrcInfo
, DstInfo
: PImageFormatInfo
;
1915 Target
, TempImage
: TImageData
;
1916 DstFormat
: TImageFormat
;
1918 Assert((Pal
<> nil) and (MaxColors
> 0));
1920 InitImage(TempImage
);
1922 if TestImagesInArray(Images
) then
1924 // Null the color histogram
1925 ReduceColorsMedianCut(0, nil, nil, nil, nil, 0, 0, nil, [raCreateHistogram
]);
1926 for I
:= 0 to Length(Images
) - 1 do
1928 SrcInfo
:= ImageFormatInfos
[Images
[I
].Format
];
1929 if SrcInfo
.IsIndexed
or SrcInfo
.IsSpecial
then
1931 // create temp image in supported format for updating histogram
1932 CloneImage(Images
[I
], TempImage
);
1933 ConvertImage(TempImage
, ifA8R8G8B8
);
1934 SrcInfo
:= ImageFormatInfos
[TempImage
.Format
];
1937 TempImage
:= Images
[I
];
1939 // Update histogram with colors of each input image
1940 ReduceColorsMedianCut(TempImage
.Width
* TempImage
.Height
, TempImage
.Bits
,
1941 nil, SrcInfo
, nil, MaxColors
, ColorReductionMask
, nil, [raUpdateHistogram
]);
1943 if Images
[I
].Bits
<> TempImage
.Bits
then
1944 FreeImage(TempImage
);
1946 // Construct reduced color map from the histogram
1947 ReduceColorsMedianCut(0, nil, nil, nil, nil, MaxColors
, ColorReductionMask
,
1948 Pal
, [raMakeColorMap
]);
1950 if ConvertImages
then
1952 DstFormat
:= ifIndex8
;
1953 DstInfo
:= ImageFormatInfos
[DstFormat
];
1954 MaxColors
:= Min(DstInfo
.PaletteEntries
, MaxColors
);
1956 for I
:= 0 to Length(Images
) - 1 do
1958 SrcInfo
:= ImageFormatInfos
[Images
[I
].Format
];
1959 if SrcInfo
.IsIndexed
or SrcInfo
.IsSpecial
then
1961 // If source image is in format not supported by ReduceColorsMedianCut
1963 ConvertImage(Images
[I
], ifA8R8G8B8
);
1964 SrcInfo
:= ImageFormatInfos
[Images
[I
].Format
];
1968 NewImage(Images
[I
].Width
, Images
[I
].Height
, DstFormat
, Target
);
1969 // We map each input image to reduced palette and replace
1970 // image in array with mapped image
1971 ReduceColorsMedianCut(Images
[I
].Width
* Images
[I
].Height
, Images
[I
].Bits
,
1972 Target
.Bits
, SrcInfo
, DstInfo
, MaxColors
, 0, nil, [raMapImage
]);
1973 Move(Pal
^, Target
.Palette
^, MaxColors
* SizeOf(TColor32Rec
));
1975 FreeImage(Images
[I
]);
1976 Images
[I
] := Target
;
1981 RaiseImaging(SErrorMakePaletteForImages
, [MaxColors
, Length(Images
)]);
1985 procedure RotateImage(var Image
: TImageData
; Angle
: Single);
1987 OldFmt
: TImageFormat
;
1989 procedure XShear(var Src
, Dst
: TImageData
; Row
, Offset
, Weight
, Bpp
: Integer);
1991 I
, J
, XPos
: Integer;
1992 PixSrc
, PixLeft
, PixOldLeft
: TColor32Rec
;
1993 LineDst
: PByteArray
;
1996 SrcPtr
:= @PByteArray(Src
.Bits
)[Row
* Src
.Width
* Bpp
];
1997 LineDst
:= @PByteArray(Dst
.Bits
)[Row
* Dst
.Width
* Bpp
];
1998 PixOldLeft
.Color
:= 0;
2000 for I
:= 0 to Src
.Width
- 1 do
2002 CopyPixel(SrcPtr
, @PixSrc
, Bpp
);
2003 for J
:= 0 to Bpp
- 1 do
2004 PixLeft
.Channels
[J
] := MulDiv(PixSrc
.Channels
[J
], Weight
, 256);
2007 if (XPos
>= 0) and (XPos
< Dst
.Width
) then
2009 for J
:= 0 to Bpp
- 1 do
2010 PixSrc
.Channels
[J
] := ClampToByte(PixSrc
.Channels
[J
] - (PixLeft
.Channels
[J
] - PixOldLeft
.Channels
[J
]));
2011 CopyPixel(@PixSrc
, @LineDst
[XPos
* Bpp
], Bpp
);
2013 PixOldLeft
:= PixLeft
;
2014 Inc(PByte(SrcPtr
), Bpp
);
2017 XPos
:= Src
.Width
+ Offset
;
2018 if XPos
< Dst
.Width
then
2019 CopyPixel(@PixOldLeft
, @LineDst
[XPos
* Bpp
], Bpp
);
2022 procedure YShear(var Src
, Dst
: TImageData
; Col
, Offset
, Weight
, Bpp
: Integer);
2024 I
, J
, YPos
: Integer;
2025 PixSrc
, PixLeft
, PixOldLeft
: TColor32Rec
;
2028 SrcPtr
:= @PByteArray(Src
.Bits
)[Col
* Bpp
];
2029 PixOldLeft
.Color
:= 0;
2031 for I
:= 0 to Src
.Height
- 1 do
2033 CopyPixel(SrcPtr
, @PixSrc
, Bpp
);
2034 for J
:= 0 to Bpp
- 1 do
2035 PixLeft
.Channels
[J
] := MulDiv(PixSrc
.Channels
[J
], Weight
, 256);
2038 if (YPos
>= 0) and (YPos
< Dst
.Height
) then
2040 for J
:= 0 to Bpp
- 1 do
2041 PixSrc
.Channels
[J
] := ClampToByte(PixSrc
.Channels
[J
] - (PixLeft
.Channels
[J
] - PixOldLeft
.Channels
[J
]));
2042 CopyPixel(@PixSrc
, @PByteArray(Dst
.Bits
)[(YPos
* Dst
.Width
+ Col
) * Bpp
], Bpp
);
2044 PixOldLeft
:= PixLeft
;
2045 Inc(SrcPtr
, Src
.Width
* Bpp
);
2048 YPos
:= Src
.Height
+ Offset
;
2049 if YPos
< Dst
.Height
then
2050 CopyPixel(@PixOldLeft
, @PByteArray(Dst
.Bits
)[(YPos
* Dst
.Width
+ Col
) * Bpp
], Bpp
);
2053 procedure Rotate45(var Image
: TImageData
; Angle
: Single);
2055 TempImage1
, TempImage2
: TImageData
;
2056 AngleRad
, AngleTan
, AngleSin
, AngleCos
, Shear
: Single;
2057 I
, DstWidth
, DstHeight
, SrcWidth
, SrcHeight
, Bpp
: Integer;
2058 SrcFmt
, TempFormat
: TImageFormat
;
2059 Info
: TImageFormatInfo
;
2061 AngleRad
:= Angle
* Pi
/ 180;
2062 AngleSin
:= Sin(AngleRad
);
2063 AngleCos
:= Cos(AngleRad
);
2064 AngleTan
:= Sin(AngleRad
/ 2) / Cos(AngleRad
/ 2);
2065 SrcWidth
:= Image
.Width
;
2066 SrcHeight
:= Image
.Height
;
2067 SrcFmt
:= Image
.Format
;
2069 if not (SrcFmt
in [ifR8G8B8
..ifX8R8G8B8
, ifGray8
..ifGray32
, ifA16Gray16
]) then
2070 ConvertImage(Image
, ifA8R8G8B8
);
2072 TempFormat
:= Image
.Format
;
2073 GetImageFormatInfo(TempFormat
, Info
);
2074 Bpp
:= Info
.BytesPerPixel
;
2076 // 1st shear (horizontal)
2077 DstWidth
:= Trunc(SrcWidth
+ SrcHeight
* Abs(AngleTan
) + 0.5);
2078 DstHeight
:= SrcHeight
;
2079 InitImage(TempImage1
);
2080 NewImage(DstWidth
, DstHeight
, TempFormat
, TempImage1
);
2082 for I
:= 0 to DstHeight
- 1 do
2084 if AngleTan
>= 0 then
2085 Shear
:= (I
+ 0.5) * AngleTan
2087 Shear
:= (I
- DstHeight
+ 0.5) * AngleTan
;
2088 XShear(Image
, TempImage1
, I
, Floor(Shear
), Trunc(255 * (Shear
- Floor(Shear
)) + 1), Bpp
);
2091 // 2nd shear (vertical)
2093 DstHeight
:= Trunc(SrcWidth
* Abs(AngleSin
) + SrcHeight
* AngleCos
+ 0.5) + 1;
2094 InitImage(TempImage2
);
2095 NewImage(DstWidth
, DstHeight
, TempFormat
, TempImage2
);
2097 if AngleSin
>= 0 then
2098 Shear
:= (SrcWidth
- 1) * AngleSin
2100 Shear
:= (SrcWidth
- DstWidth
) * -AngleSin
;
2102 for I
:= 0 to DstWidth
- 1 do
2104 YShear(TempImage1
, TempImage2
, I
, Floor(Shear
), Trunc(255 * (Shear
- Floor(Shear
)) + 1), Bpp
);
2105 Shear
:= Shear
- AngleSin
;
2108 // 3rd shear (horizontal)
2109 FreeImage(TempImage1
);
2110 DstWidth
:= Trunc(SrcHeight
* Abs(AngleSin
) + SrcWidth
* AngleCos
+ 0.5) + 1;
2111 NewImage(DstWidth
, DstHeight
, TempFormat
, Image
);
2113 if AngleSin
>= 0 then
2114 Shear
:= (SrcWidth
- 1) * AngleSin
* -AngleTan
2116 Shear
:= ((SrcWidth
- 1) * -AngleSin
+ (1 - DstHeight
)) * AngleTan
;
2118 for I
:= 0 to DstHeight
- 1 do
2120 XShear(TempImage2
, Image
, I
, Floor(Shear
), Trunc(255 * (Shear
- Floor(Shear
)) + 1), Bpp
);
2121 Shear
:= Shear
+ AngleTan
;
2124 FreeImage(TempImage2
);
2125 if Image
.Format
<> SrcFmt
then
2126 ConvertImage(Image
, SrcFmt
);
2129 procedure RotateMul90(var Image
: TImageData
; Angle
: Integer);
2131 RotImage
: TImageData
;
2132 X
, Y
, BytesPerPixel
: Integer;
2135 InitImage(RotImage
);
2136 BytesPerPixel
:= ImageFormatInfos
[Image
.Format
].BytesPerPixel
;
2138 if ((Angle
= 90) or (Angle
= 270)) and (Image
.Width
<> Image
.Height
) then
2139 NewImage(Image
.Height
, Image
.Width
, Image
.Format
, RotImage
)
2141 NewImage(Image
.Width
, Image
.Height
, Image
.Format
, RotImage
);
2143 RotPix
:= RotImage
.Bits
;
2147 for Y
:= 0 to RotImage
.Height
- 1 do
2149 Pix
:= @PByteArray(Image
.Bits
)[(Image
.Width
- Y
- 1) * BytesPerPixel
];
2150 for X
:= 0 to RotImage
.Width
- 1 do
2152 CopyPixel(Pix
, RotPix
, BytesPerPixel
);
2153 Inc(RotPix
, BytesPerPixel
);
2154 Inc(Pix
, Image
.Width
* BytesPerPixel
);
2160 Pix
:= @PByteArray(Image
.Bits
)[((Image
.Height
- 1) * Image
.Width
+
2161 (Image
.Width
- 1)) * BytesPerPixel
];
2162 for Y
:= 0 to RotImage
.Height
- 1 do
2163 for X
:= 0 to RotImage
.Width
- 1 do
2165 CopyPixel(Pix
, RotPix
, BytesPerPixel
);
2166 Inc(RotPix
, BytesPerPixel
);
2167 Dec(Pix
, BytesPerPixel
);
2172 for Y
:= 0 to RotImage
.Height
- 1 do
2174 Pix
:= @PByteArray(Image
.Bits
)[((Image
.Height
- 1) * Image
.Width
+ Y
) * BytesPerPixel
];
2175 for X
:= 0 to RotImage
.Width
- 1 do
2177 CopyPixel(Pix
, RotPix
, BytesPerPixel
);
2178 Inc(RotPix
, BytesPerPixel
);
2179 Dec(Pix
, Image
.Width
* BytesPerPixel
);
2185 FreeMemNil(Image
.Bits
);
2186 RotImage
.Palette
:= Image
.Palette
;
2191 if TestImage(Image
) then
2193 while Angle
>= 360 do
2194 Angle
:= Angle
- 360;
2196 Angle
:= Angle
+ 360;
2198 if (Angle
= 0) or (Abs(Angle
) = 360) then
2201 OldFmt
:= Image
.Format
;
2202 if ImageFormatInfos
[Image
.Format
].IsSpecial
then
2203 ConvertImage(Image
, ifDefault
);
2205 if (Angle
> 45) and (Angle
<= 135) then
2207 RotateMul90(Image
, 90);
2208 Angle
:= Angle
- 90;
2210 else if (Angle
> 135) and (Angle
<= 225) then
2212 RotateMul90(Image
, 180);
2213 Angle
:= Angle
- 180;
2215 else if (Angle
> 225) and (Angle
<= 315) then
2217 RotateMul90(Image
, 270);
2218 Angle
:= Angle
- 270;
2222 Rotate45(Image
, Angle
);
2224 if OldFmt
<> Image
.Format
then
2225 ConvertImage(Image
, OldFmt
);
2228 raise UpdateExceptMessage(GetExceptObject
, SErrorRotateImage
, [ImageToStr(Image
), Angle
]);
2232 { Drawing/Pixel functions }
2234 function CopyRect(const SrcImage
: TImageData
; SrcX
, SrcY
, Width
, Height
: LongInt;
2235 var DstImage
: TImageData
; DstX
, DstY
: LongInt): Boolean;
2237 Info
: PImageFormatInfo
;
2238 I
, SrcWidthBytes
, DstWidthBytes
, MoveBytes
: LongInt;
2239 SrcPointer
, DstPointer
: PByte;
2240 WorkImage
: TImageData
;
2241 OldFormat
: TImageFormat
;
2244 OldFormat
:= ifUnknown
;
2245 if TestImage(SrcImage
) and TestImage(DstImage
) then
2247 // Make sure we are still copying image to image, not invalid pointer to protected memory
2248 ClipCopyBounds(SrcX
, SrcY
, Width
, Height
, DstX
, DstY
, SrcImage
.Width
, SrcImage
.Height
,
2249 Rect(0, 0, DstImage
.Width
, DstImage
.Height
));
2251 if (Width
> 0) and (Height
> 0) then
2253 Info
:= ImageFormatInfos
[DstImage
.Format
];
2254 if Info
.IsSpecial
then
2256 // If dest image is in special format we convert it to default
2257 OldFormat
:= Info
.Format
;
2258 ConvertImage(DstImage
, ifDefault
);
2259 Info
:= ImageFormatInfos
[DstImage
.Format
];
2261 if SrcImage
.Format
<> DstImage
.Format
then
2263 // If images are in different format source is converted to dest's format
2264 InitImage(WorkImage
);
2265 CloneImage(SrcImage
, WorkImage
);
2266 ConvertImage(WorkImage
, DstImage
.Format
);
2269 WorkImage
:= SrcImage
;
2271 MoveBytes
:= Width
* Info
.BytesPerPixel
;
2272 DstWidthBytes
:= DstImage
.Width
* Info
.BytesPerPixel
;
2273 DstPointer
:= @PByteArray(DstImage
.Bits
)[DstY
* DstWidthBytes
+
2274 DstX
* Info
.BytesPerPixel
];
2275 SrcWidthBytes
:= WorkImage
.Width
* Info
.BytesPerPixel
;
2276 SrcPointer
:= @PByteArray(WorkImage
.Bits
)[SrcY
* SrcWidthBytes
+
2277 SrcX
* Info
.BytesPerPixel
];
2279 for I
:= 0 to Height
- 1 do
2281 Move(SrcPointer
^, DstPointer
^, MoveBytes
);
2282 Inc(SrcPointer
, SrcWidthBytes
);
2283 Inc(DstPointer
, DstWidthBytes
);
2285 // If dest image was in special format we convert it back
2286 if OldFormat
<> ifUnknown
then
2287 ConvertImage(DstImage
, OldFormat
);
2288 // Working image must be freed if it is not the same as source image
2289 if WorkImage
.Bits
<> SrcImage
.Bits
then
2290 FreeImage(WorkImage
);
2295 RaiseImaging(SErrorCopyRect
, [ImageToStr(SrcImage
), ImageToStr(DstImage
)]);
2299 function FillRect(var Image
: TImageData
; X
, Y
, Width
, Height
: LongInt;
2300 FillColor
: Pointer): Boolean;
2302 Info
: PImageFormatInfo
;
2303 I
, J
, ImageWidthBytes
, RectWidthBytes
, Bpp
: Longint;
2304 LinePointer
, PixPointer
: PByte;
2305 OldFmt
: TImageFormat
;
2308 if TestImage(Image
) then
2310 ClipRectBounds(X
, Y
, Width
, Height
, Rect(0, 0, Image
.Width
, Image
.Height
));
2312 if (Width
> 0) and (Height
> 0) then
2314 OldFmt
:= Image
.Format
;
2315 if ImageFormatInfos
[OldFmt
].IsSpecial
then
2316 ConvertImage(Image
, ifDefault
);
2318 Info
:= ImageFormatInfos
[Image
.Format
];
2319 Bpp
:= Info
.BytesPerPixel
;
2320 ImageWidthBytes
:= Image
.Width
* Bpp
;
2321 RectWidthBytes
:= Width
* Bpp
;
2322 LinePointer
:= @PByteArray(Image
.Bits
)[Y
* ImageWidthBytes
+ X
* Bpp
];
2324 for I
:= 0 to Height
- 1 do
2327 1: FillMemoryByte(LinePointer
, RectWidthBytes
, PByte(FillColor
)^);
2328 2: FillMemoryWord(LinePointer
, RectWidthBytes
, PWord(FillColor
)^);
2329 4: FillMemoryLongWord(LinePointer
, RectWidthBytes
, PLongWord(FillColor
)^);
2331 PixPointer
:= LinePointer
;
2332 for J
:= 0 to Width
- 1 do
2334 CopyPixel(FillColor
, PixPointer
, Bpp
);
2335 Inc(PixPointer
, Bpp
);
2338 Inc(LinePointer
, ImageWidthBytes
);
2341 if OldFmt
<> Image
.Format
then
2342 ConvertImage(Image
, OldFmt
);
2347 RaiseImaging(SErrorFillRect
, [X
, Y
, Width
, Height
, ImageToStr(Image
)]);
2351 function ReplaceColor(var Image
: TImageData
; X
, Y
, Width
, Height
: LongInt;
2352 OldColor
, NewColor
: Pointer): Boolean;
2354 Info
: PImageFormatInfo
;
2355 I
, J
, WidthBytes
, Bpp
: Longint;
2356 LinePointer
, PixPointer
: PByte;
2357 OldFmt
: TImageFormat
;
2359 Assert((OldColor
<> nil) and (NewColor
<> nil));
2361 if TestImage(Image
) then
2363 ClipRectBounds(X
, Y
, Width
, Height
, Rect(0, 0, Image
.Width
, Image
.Height
));
2365 if (Width
> 0) and (Height
> 0) then
2367 OldFmt
:= Image
.Format
;
2368 if ImageFormatInfos
[OldFmt
].IsSpecial
then
2369 ConvertImage(Image
, ifDefault
);
2371 Info
:= ImageFormatInfos
[Image
.Format
];
2372 Bpp
:= Info
.BytesPerPixel
;
2373 WidthBytes
:= Image
.Width
* Bpp
;
2374 LinePointer
:= @PByteArray(Image
.Bits
)[Y
* WidthBytes
+ X
* Bpp
];
2376 for I
:= 0 to Height
- 1 do
2378 PixPointer
:= LinePointer
;
2379 for J
:= 0 to Width
- 1 do
2381 if ComparePixels(PixPointer
, OldColor
, Bpp
) then
2382 CopyPixel(NewColor
, PixPointer
, Bpp
);
2383 Inc(PixPointer
, Bpp
);
2385 Inc(LinePointer
, WidthBytes
);
2388 if OldFmt
<> Image
.Format
then
2389 ConvertImage(Image
, OldFmt
);
2394 RaiseImaging(SErrorReplaceColor
, [X
, Y
, Width
, Height
, ImageToStr(Image
)]);
2398 function StretchRect(const SrcImage
: TImageData
; SrcX
, SrcY
, SrcWidth
,
2399 SrcHeight
: LongInt; var DstImage
: TImageData
; DstX
, DstY
, DstWidth
,
2400 DstHeight
: LongInt; Filter
: TResizeFilter
): Boolean;
2402 Info
: PImageFormatInfo
;
2403 WorkImage
: TImageData
;
2404 OldFormat
: TImageFormat
;
2405 Resampling
: TSamplingFilter
;
2408 OldFormat
:= ifUnknown
;
2409 if TestImage(SrcImage
) and TestImage(DstImage
) then
2411 // Make sure we are still copying image to image, not invalid pointer to protected memory
2412 ClipStretchBounds(SrcX
, SrcY
, SrcWidth
, SrcHeight
, DstX
, DstY
, DstWidth
, DstHeight
,
2413 SrcImage
.Width
, SrcImage
.Height
, Rect(0, 0, DstImage
.Width
, DstImage
.Height
));
2415 if (SrcWidth
= DstWidth
) and (SrcHeight
= DstHeight
) then
2417 // If source and dest rectangles have the same size call CopyRect
2418 Result
:= CopyRect(SrcImage
, SrcX
, SrcY
, SrcWidth
, SrcHeight
, DstImage
, DstX
, DstY
);
2420 else if (SrcWidth
> 0) and (SrcHeight
> 0) and (DstWidth
> 0) and (DstHeight
> 0) then
2422 // If source and dest rectangles don't have the same size we do stretch
2423 Info
:= ImageFormatInfos
[DstImage
.Format
];
2425 if Info
.IsSpecial
then
2427 // If dest image is in special format we convert it to default
2428 OldFormat
:= Info
.Format
;
2429 ConvertImage(DstImage
, ifDefault
);
2430 Info
:= ImageFormatInfos
[DstImage
.Format
];
2433 if SrcImage
.Format
<> DstImage
.Format
then
2435 // If images are in different format source is converted to dest's format
2436 InitImage(WorkImage
);
2437 CloneImage(SrcImage
, WorkImage
);
2438 ConvertImage(WorkImage
, DstImage
.Format
);
2441 WorkImage
:= SrcImage
;
2443 // Only pixel resize is supported for indexed images
2444 if Info
.IsIndexed
then
2445 Filter
:= rfNearest
;
2447 if Filter
= rfNearest
then
2449 StretchNearest(WorkImage
, SrcX
, SrcY
, SrcWidth
, SrcHeight
,
2450 DstImage
, DstX
, DstY
, DstWidth
, DstHeight
);
2454 Resampling
:= sfNearest
;
2456 rfBilinear
: Resampling
:= sfLinear
;
2457 rfBicubic
: Resampling
:= DefaultCubicFilter
;
2458 rfLanczos
: Resampling
:= sfLanczos
;
2460 StretchResample(WorkImage
, SrcX
, SrcY
, SrcWidth
, SrcHeight
,
2461 DstImage
, DstX
, DstY
, DstWidth
, DstHeight
, Resampling
);
2464 // If dest image was in special format we convert it back
2465 if OldFormat
<> ifUnknown
then
2466 ConvertImage(DstImage
, OldFormat
);
2467 // Working image must be freed if it is not the same as source image
2468 if WorkImage
.Bits
<> SrcImage
.Bits
then
2469 FreeImage(WorkImage
);
2474 RaiseImaging(SErrorStretchRect
, [ImageToStr(SrcImage
), ImageToStr(DstImage
)]);
2478 procedure GetPixelDirect(const Image
: TImageData
; X
, Y
: LongInt; Pixel
: Pointer);
2480 BytesPerPixel
: LongInt;
2482 Assert(Pixel
<> nil);
2483 BytesPerPixel
:= ImageFormatInfos
[Image
.Format
].BytesPerPixel
;
2484 CopyPixel(@PByteArray(Image
.Bits
)[(Y
* Image
.Width
+ X
) * BytesPerPixel
],
2485 Pixel
, BytesPerPixel
);
2488 procedure SetPixelDirect(const Image
: TImageData
; X
, Y
: LongInt; Pixel
: Pointer);
2490 BytesPerPixel
: LongInt;
2492 Assert(Pixel
<> nil);
2493 BytesPerPixel
:= ImageFormatInfos
[Image
.Format
].BytesPerPixel
;
2494 CopyPixel(Pixel
, @PByteArray(Image
.Bits
)[(Y
* Image
.Width
+ X
) * BytesPerPixel
],
2498 function GetPixel32(const Image
: TImageData
; X
, Y
: LongInt): TColor32Rec
;
2500 Info
: PImageFormatInfo
;
2503 Info
:= ImageFormatInfos
[Image
.Format
];
2504 Data
:= @PByteArray(Image
.Bits
)[(Y
* Image
.Width
+ X
) * Info
.BytesPerPixel
];
2505 Result
:= GetPixel32Generic(Data
, Info
, Image
.Palette
);
2508 procedure SetPixel32(const Image
: TImageData
; X
, Y
: LongInt; const Color
: TColor32Rec
);
2510 Info
: PImageFormatInfo
;
2513 Info
:= ImageFormatInfos
[Image
.Format
];
2514 Data
:= @PByteArray(Image
.Bits
)[(Y
* Image
.Width
+ X
) * Info
.BytesPerPixel
];
2515 SetPixel32Generic(Data
, Info
, Image
.Palette
, Color
);
2518 function GetPixelFP(const Image
: TImageData
; X
, Y
: LongInt): TColorFPRec
;
2520 Info
: PImageFormatInfo
;
2523 Info
:= ImageFormatInfos
[Image
.Format
];
2524 Data
:= @PByteArray(Image
.Bits
)[(Y
* Image
.Width
+ X
) * Info
.BytesPerPixel
];
2525 Result
:= GetPixelFPGeneric(Data
, Info
, Image
.Palette
);
2528 procedure SetPixelFP(const Image
: TImageData
; X
, Y
: LongInt; const Color
: TColorFPRec
);
2530 Info
: PImageFormatInfo
;
2533 Info
:= ImageFormatInfos
[Image
.Format
];
2534 Data
:= @PByteArray(Image
.Bits
)[(Y
* Image
.Width
+ X
) * Info
.BytesPerPixel
];
2535 SetPixelFPGeneric(Data
, Info
, Image
.Palette
, Color
);
2538 { Palette Functions }
2540 procedure NewPalette(Entries
: LongInt; var Pal
: PPalette32
);
2542 Assert((Entries
> 2) and (Entries
<= 65535));
2544 GetMem(Pal
, Entries
* SizeOf(TColor32Rec
));
2545 FillChar(Pal
^, Entries
* SizeOf(TColor32Rec
), $FF);
2547 RaiseImaging(SErrorNewPalette
, [Entries
]);
2551 procedure FreePalette(var Pal
: PPalette32
);
2556 RaiseImaging(SErrorFreePalette
, [Pal
]);
2560 procedure CopyPalette(SrcPal
, DstPal
: PPalette32
; SrcIdx
, DstIdx
, Count
: LongInt);
2562 Assert((SrcPal
<> nil) and (DstPal
<> nil));
2563 Assert((SrcIdx
>= 0) and (DstIdx
>= 0) and (Count
>= 0));
2565 Move(SrcPal
[SrcIdx
], DstPal
[DstIdx
], Count
* SizeOf(TColor32Rec
));
2567 RaiseImaging(SErrorCopyPalette
, [Count
, SrcPal
, DstPal
]);
2571 function FindColor(Pal
: PPalette32
; Entries
: LongInt; Color
: TColor32
):
2575 I
, MinDif
, Dif
: LongInt;
2581 // First try to find exact match
2582 for I
:= 0 to Entries
- 1 do
2585 if (A
= Col
.A
) and (R
= Col
.R
) and
2586 (G
= Col
.G
) and (B
= Col
.B
) then
2593 // If exact match was not found, find nearest color
2595 for I
:= 0 to Entries
- 1 do
2598 Dif
:= Abs(R
- Col
.R
);
2599 if Dif
> MinDif
then Continue
;
2600 Dif
:= Dif
+ Abs(G
- Col
.G
);
2601 if Dif
> MinDif
then Continue
;
2602 Dif
:= Dif
+ Abs(B
- Col
.B
);
2603 if Dif
> MinDif
then Continue
;
2604 Dif
:= Dif
+ Abs(A
- Col
.A
);
2605 if Dif
< MinDif
then
2612 RaiseImaging(SErrorFindColor
, [Pal
, Entries
]);
2616 procedure FillGrayscalePalette(Pal
: PPalette32
; Entries
: LongInt);
2622 for I
:= 0 to Entries
- 1 do
2631 RaiseImaging(SErrorGrayscalePalette
, [Pal
, Entries
]);
2635 procedure FillCustomPalette(Pal
: PPalette32
; Entries
: LongInt; RBits
, GBits
,
2636 BBits
: Byte; Alpha
: Byte = $FF);
2638 I
, TotalBits
, MaxEntries
: LongInt;
2641 TotalBits
:= RBits
+ GBits
+ BBits
;
2642 MaxEntries
:= Min(Pow2Int(TotalBits
), Entries
);
2643 FillChar(Pal
^, Entries
* SizeOf(TColor32Rec
), 0);
2645 for I
:= 0 to MaxEntries
- 1 do
2650 R
:= ((I
shr Max(0, GBits
+ BBits
- 1)) and (1 shl RBits
- 1)) * 255 div (1 shl RBits
- 1);
2652 G
:= ((I
shr Max(0, BBits
- 1)) and (1 shl GBits
- 1)) * 255 div (1 shl GBits
- 1);
2654 B
:= ((I
shr 0) and (1 shl BBits
- 1)) * 255 div (1 shl BBits
- 1);
2657 RaiseImaging(SErrorCustomPalette
, [Pal
, Entries
]);
2661 procedure SwapChannelsOfPalette(Pal
: PPalette32
; Entries
, SrcChannel
,
2662 DstChannel
: LongInt);
2668 Assert((SrcChannel
in [0..3]) and (DstChannel
in [0..3]));
2670 for I
:= 0 to Entries
- 1 do
2673 Swap
:= Channels
[SrcChannel
];
2674 Channels
[SrcChannel
] := Channels
[DstChannel
];
2675 Channels
[DstChannel
] := Swap
;
2678 RaiseImaging(SErrorSwapPalette
, [Pal
, Entries
]);
2682 { Options Functions }
2684 function SetOption(OptionId
, Value
: LongInt): Boolean;
2687 if (OptionId
>= 0) and (OptionId
< Length(Options
)) and
2688 (Options
[OptionID
] <> nil) then
2690 Options
[OptionID
]^ := CheckOptionValue(OptionId
, Value
);
2695 function GetOption(OptionId
: LongInt): LongInt;
2697 Result
:= InvalidOption
;
2698 if (OptionId
>= 0) and (OptionId
< Length(Options
)) and
2699 (Options
[OptionID
] <> nil) then
2701 Result
:= Options
[OptionID
]^;
2705 function PushOptions
: Boolean;
2707 Result
:= OptionStack
.Push
;
2710 function PopOptions
: Boolean;
2712 Result
:= OptionStack
.Pop
;
2715 { Image Format Functions }
2717 function GetImageFormatInfo(Format
: TImageFormat
; out Info
: TImageFormatInfo
): Boolean;
2719 FillChar(Info
, SizeOf(Info
), 0);
2720 if ImageFormatInfos
[Format
] <> nil then
2722 Info
:= ImageFormatInfos
[Format
]^;
2729 function GetPixelsSize(Format
: TImageFormat
; Width
, Height
: LongInt): LongInt;
2731 if ImageFormatInfos
[Format
] <> nil then
2732 Result
:= ImageFormatInfos
[Format
].GetPixelsSize(Format
, Width
, Height
)
2739 procedure SetUserFileIO(OpenProc
: TOpenProc
;
2740 CloseProc
: TCloseProc
; EofProc
: TEofProc
; SeekProc
: TSeekProc
; TellProc
:
2741 TTellProc
; ReadProc
: TReadProc
; WriteProc
: TWriteProc
);
2743 FileIO
.Open
:= OpenProc
;
2744 FileIO
.Close
:= CloseProc
;
2745 FileIO
.Eof
:= EofProc
;
2746 FileIO
.Seek
:= SeekProc
;
2747 FileIO
.Tell
:= TellProc
;
2748 FileIO
.Read
:= ReadProc
;
2749 FileIO
.Write
:= WriteProc
;
2752 procedure ResetFileIO
;
2754 FileIO
:= OriginalFileIO
;
2757 { Raw Image IO Functions }
2759 procedure ReadRawImage(Handle
: TImagingHandle
; Width
, Height
: Integer;
2760 Format
: TImageFormat
; out Image
: TImageData
; Offset
, RowLength
: Integer);
2762 WidthBytes
, I
: Integer;
2763 Info
: PImageFormatInfo
;
2765 Info
:= ImageFormatInfos
[Format
];
2766 // Calc scanline size
2767 WidthBytes
:= Info
.GetPixelsSize(Format
, Width
, 1);
2768 if RowLength
= 0 then
2769 RowLength
:= WidthBytes
;
2770 // Create new image if needed - don't need to allocate new one if there is already
2771 // one with desired size and format
2772 if (Image
.Width
<> Width
) or (Image
.Height
<> Height
) or (Image
.Format
<> Format
) then
2773 NewImage(Width
, Height
, Format
, Image
);
2774 // Move past the header
2775 IO
.Seek(Handle
, Offset
, smFromCurrent
);
2776 // Read scanlines from input
2777 for I
:= 0 to Height
- 1 do
2779 IO
.Read(Handle
, @PByteArray(Image
.Bits
)[I
* WidthBytes
], WidthBytes
);
2780 IO
.Seek(Handle
, RowLength
- WidthBytes
, smFromCurrent
);
2784 procedure ReadRawImageFromFile(const FileName
: string; Width
, Height
: Integer;
2785 Format
: TImageFormat
; var Image
: TImageData
; Offset
, RowLength
: Integer);
2787 Handle
: TImagingHandle
;
2789 Assert(FileName
<> '');
2790 // Set IO ops to file ops and open given file
2792 Handle
:= IO
.Open(PChar(FileName
), omReadOnly
);
2794 ReadRawImage(Handle
, Width
, Height
, Format
, Image
, Offset
, RowLength
);
2800 procedure ReadRawImageFromStream(Stream
: TStream
; Width
, Height
: Integer;
2801 Format
: TImageFormat
; var Image
: TImageData
; Offset
, RowLength
: Integer);
2803 Handle
: TImagingHandle
;
2805 Assert(Stream
<> nil);
2806 if Stream
.Size
- Stream
.Position
= 0 then
2807 RaiseImaging(SErrorEmptyStream
, []);
2808 // Set IO ops to stream ops and open given stream
2810 Handle
:= IO
.Open(Pointer(Stream
), omReadOnly
);
2812 ReadRawImage(Handle
, Width
, Height
, Format
, Image
, Offset
, RowLength
);
2818 procedure ReadRawImageFromMemory(Data
: Pointer; DataSize
: Integer; Width
, Height
: Integer;
2819 Format
: TImageFormat
; var Image
: TImageData
; Offset
, RowLength
: Integer);
2821 Handle
: TImagingHandle
;
2822 MemRec
: TMemoryIORec
;
2824 Assert((Data
<> nil) and (DataSize
> 0));
2825 // Set IO ops to memory ops and open given stream
2827 MemRec
:= PrepareMemIO(Data
, DataSize
);
2828 Handle
:= IO
.Open(@MemRec
, omReadOnly
);
2830 ReadRawImage(Handle
, Width
, Height
, Format
, Image
, Offset
, RowLength
);
2836 procedure ReadRawImageRect(Data
: Pointer; Left
, Top
, Width
, Height
: Integer;
2837 var Image
: TImageData
; Offset
, RowLength
: Integer);
2839 DestScanBytes
, RectBytes
, I
: Integer;
2840 Info
: PImageFormatInfo
;
2843 Assert(Data
<> nil);
2844 Assert((Left
+ Width
<= Image
.Width
) and (Top
+ Height
<= Image
.Height
));
2845 Info
:= ImageFormatInfos
[Image
.Format
];
2847 // Calc scanline size
2848 DestScanBytes
:= Info
.GetPixelsSize(Info
.Format
, Image
.Width
, 1);
2849 RectBytes
:= Info
.GetPixelsSize(Info
.Format
, Width
, 1);
2850 if RowLength
= 0 then
2851 RowLength
:= RectBytes
;
2854 Dest
:= @PByteArray(Image
.Bits
)[Top
* DestScanBytes
+ Info
.GetPixelsSize(Info
.Format
, Left
, 1)];
2855 // Move past the header
2858 // Read lines into rect in the existing image
2859 for I
:= 0 to Height
- 1 do
2861 Move(Src
^, Dest
^, RectBytes
);
2862 Inc(Src
, RowLength
);
2863 Inc(Dest
, DestScanBytes
);
2867 procedure WriteRawImage(Handle
: TImagingHandle
; const Image
: TImageData
;
2868 Offset
, RowLength
: Integer);
2870 WidthBytes
, I
: Integer;
2871 Info
: PImageFormatInfo
;
2873 Info
:= ImageFormatInfos
[Image
.Format
];
2874 // Calc scanline size
2875 WidthBytes
:= Info
.GetPixelsSize(Image
.Format
, Image
.Width
, 1);
2876 if RowLength
= 0 then
2877 RowLength
:= WidthBytes
;
2878 // Move past the header
2879 IO
.Seek(Handle
, Offset
, smFromCurrent
);
2880 // Write scanlines to output
2881 for I
:= 0 to Image
.Height
- 1 do
2883 IO
.Write(Handle
, @PByteArray(Image
.Bits
)[I
* WidthBytes
], WidthBytes
);
2884 IO
.Seek(Handle
, RowLength
- WidthBytes
, smFromCurrent
);
2888 procedure WriteRawImageToFile(const FileName
: string; const Image
: TImageData
;
2889 Offset
, RowLength
: Integer);
2891 Handle
: TImagingHandle
;
2893 Assert(FileName
<> '');
2894 // Set IO ops to file ops and open given file
2896 Handle
:= IO
.Open(PChar(FileName
), omCreate
);
2898 WriteRawImage(Handle
, Image
, Offset
, RowLength
);
2904 procedure WriteRawImageToStream(Stream
: TStream
; const Image
: TImageData
;
2905 Offset
, RowLength
: Integer);
2907 Handle
: TImagingHandle
;
2909 Assert(Stream
<> nil);
2910 // Set IO ops to stream ops and open given stream
2912 Handle
:= IO
.Open(Pointer(Stream
), omCreate
);
2914 WriteRawImage(Handle
, Image
, Offset
, RowLength
);
2920 procedure WriteRawImageToMemory(Data
: Pointer; DataSize
: Integer; const Image
: TImageData
;
2921 Offset
, RowLength
: Integer);
2923 Handle
: TImagingHandle
;
2924 MemRec
: TMemoryIORec
;
2926 Assert((Data
<> nil) and (DataSize
> 0));
2927 // Set IO ops to memory ops and open given stream
2929 MemRec
:= PrepareMemIO(Data
, DataSize
);
2930 Handle
:= IO
.Open(@MemRec
, omCreate
);
2932 WriteRawImage(Handle
, Image
, Offset
, RowLength
);
2938 procedure WriteRawImageRect(Data
: Pointer; Left
, Top
, Width
, Height
: Integer;
2939 const Image
: TImageData
; Offset
, RowLength
: Integer);
2941 SrcScanBytes
, RectBytes
, I
: Integer;
2942 Info
: PImageFormatInfo
;
2945 Assert(Data
<> nil);
2946 Assert((Left
+ Width
<= Image
.Width
) and (Top
+ Height
<= Image
.Height
));
2947 Info
:= ImageFormatInfos
[Image
.Format
];
2949 // Calc scanline size
2950 SrcScanBytes
:= Info
.GetPixelsSize(Info
.Format
, Image
.Width
, 1);
2951 RectBytes
:= Info
.GetPixelsSize(Info
.Format
, Width
, 1);
2952 if RowLength
= 0 then
2953 RowLength
:= RectBytes
;
2955 Src
:= @PByteArray(Image
.Bits
)[Top
* SrcScanBytes
+ Info
.GetPixelsSize(Info
.Format
, Left
, 1)];
2957 // Move past the header
2960 // Write lines from rect of the existing image
2961 for I
:= 0 to Height
- 1 do
2963 Move(Src
^, Dest
^, RectBytes
);
2964 Inc(Dest
, RowLength
);
2965 Inc(Src
, SrcScanBytes
);
2969 { Convenience/helper Functions }
2971 procedure ResizeImageToFit(const SrcImage
: TImageData
; FitWidth
, FitHeight
: Integer;
2972 Filter
: TResizeFilter
; var DestImage
: TImageData
);
2974 CurSize
, FitSize
, DestSize
: TSize
;
2976 if not TestImage(SrcImage
) then
2977 raise EImagingError
.Create(SErrorInvalidInputImage
);
2979 FitSize
.CX
:= FitWidth
;
2980 FitSize
.CY
:= FitHeight
;
2981 CurSize
.CX
:= SrcImage
.Width
;
2982 CurSize
.CY
:= SrcImage
.Height
;
2983 DestSize
:= ImagingUtility
.ScaleSizeToFit(CurSize
, FitSize
);
2985 NewImage(Max(DestSize
.CX
, 1), Max(DestSize
.CY
, 1), SrcImage
.Format
, DestImage
);
2986 if SrcImage
.Palette
<> nil then
2987 CopyPalette(SrcImage
.Palette
, DestImage
.Palette
, 0, 0, ImageFormatInfos
[SrcImage
.Format
].PaletteEntries
);
2989 StretchRect(SrcImage
, 0, 0, CurSize
.CX
, CurSize
.CY
, DestImage
, 0, 0,
2990 DestSize
.CX
, DestSize
.CY
, Filter
);
2993 { ------------------------------------------------------------------------
2995 ------------------------------------------------------------------------}
2997 function GetFormatName(Format
: TImageFormat
): string;
2999 if ImageFormatInfos
[Format
] <> nil then
3000 Result
:= ImageFormatInfos
[Format
].Name
3002 Result
:= SUnknownFormat
;
3005 function ImageToStr(const Image
: TImageData
): string;
3009 if TestImage(Image
) then
3013 if ImgSize
> 8192 then
3014 ImgSize
:= ImgSize
div 1024;
3015 Result
:= SysUtils
.Format(SImageInfo
, [@Image
, Width
, Height
,
3016 GetFormatName(Format
), ImgSize
+ 0.0, Iff(ImgSize
= Size
, 'B', 'KiB'), Bits
,
3020 Result
:= SysUtils
.Format(SImageInfoInvalid
, [@Image
]);
3023 function GetVersionStr
: string;
3025 Result
:= Format('%.1d.%.2d.%.1d', [ImagingVersionMajor
,
3026 ImagingVersionMinor
, ImagingVersionPatch
]);
3029 function IffFormat(Condition
: Boolean; const TruePart
, FalsePart
: TImageFormat
): TImageFormat
;
3034 Result
:= FalsePart
;
3037 procedure RegisterImageFileFormat(AClass
: TImageFileFormatClass
);
3039 Assert(AClass
<> nil);
3040 if ImageFileFormats
= nil then
3041 ImageFileFormats
:= TList
.Create
;
3042 if GlobalMetadata
= nil then
3043 GlobalMetadata
:= TMetadata
.Create
;
3044 if ImageFileFormats
<> nil then
3045 ImageFileFormats
.Add(AClass
.Create
);
3048 function RegisterOption(OptionId
: LongInt; Variable
: PLongInt): Boolean;
3051 if Options
= nil then
3054 Assert(Variable
<> nil);
3056 if OptionId
>= Length(Options
) then
3057 SetLength(Options
, OptionId
+ InitialOptions
);
3058 if (OptionId
>= 0) and (OptionId
< Length(Options
)) {and (Options[OptionId] = nil) - must be able to override existing } then
3060 Options
[OptionId
] := Variable
;
3065 function FindImageFileFormatByExt(const Ext
: string): TImageFileFormat
;
3070 for I
:= ImageFileFormats
.Count
- 1 downto 0 do
3071 if TImageFileFormat(ImageFileFormats
[I
]).Extensions
.IndexOf(Ext
) >= 0 then
3073 Result
:= TImageFileFormat(ImageFileFormats
[I
]);
3078 function FindImageFileFormatByName(const FileName
: string): TImageFileFormat
;
3083 for I
:= ImageFileFormats
.Count
- 1 downto 0 do
3084 if TImageFileFormat(ImageFileFormats
[I
]).TestFileName(FileName
) then
3086 Result
:= TImageFileFormat(ImageFileFormats
[I
]);
3091 function FindImageFileFormatByClass(AClass
: TImageFileFormatClass
): TImageFileFormat
;
3096 for I
:= 0 to ImageFileFormats
.Count
- 1 do
3097 if TImageFileFormat(ImageFileFormats
[I
]) is AClass
then
3099 Result
:= TObject(ImageFileFormats
[I
]) as TImageFileFormat
;
3104 function GetFileFormatCount
: LongInt;
3106 Result
:= ImageFileFormats
.Count
;
3109 function GetFileFormatAtIndex(Index
: LongInt): TImageFileFormat
;
3111 if (Index
>= 0) and (Index
< ImageFileFormats
.Count
) then
3112 Result
:= TImageFileFormat(ImageFileFormats
[Index
])
3117 function GetImageFileFormatsFilter(OpenFileFilter
: Boolean): string;
3119 I
, J
, Count
: LongInt;
3120 Descriptions
: string;
3121 Filters
, CurFilter
: string;
3122 FileFormat
: TImageFileFormat
;
3128 for I
:= 0 to ImageFileFormats
.Count
- 1 do
3130 FileFormat
:= TObject(ImageFileFormats
[I
]) as TImageFileFormat
;
3132 // If we are creating filter for save dialog and this format cannot save
3133 // files the we skip it
3134 if not OpenFileFilter
and not FileFormat
.CanSave
then
3138 for J
:= 0 to FileFormat
.Masks
.Count
- 1 do
3140 CurFilter
:= CurFilter
+ FileFormat
.Masks
[J
];
3141 if J
< FileFormat
.Masks
.Count
- 1 then
3142 CurFilter
:= CurFilter
+ ';';
3145 FmtStr(Descriptions
, '%s%s (%s)|%2:s', [Descriptions
, FileFormat
.Name
, CurFilter
]);
3146 if Filters
<> '' then
3147 FmtStr(Filters
, '%s;%s', [Filters
, CurFilter
])
3149 Filters
:= CurFilter
;
3151 if I
< ImageFileFormats
.Count
- 1 then
3152 Descriptions
:= Descriptions
+ '|';
3157 if (Count
> 1) and OpenFileFilter
then
3158 FmtStr(Descriptions
, '%s (%s)|%1:s|%s', [SAllFilter
, Filters
, Descriptions
]);
3160 Result
:= Descriptions
;
3163 function GetFilterIndexExtension(Index
: LongInt; OpenFileFilter
: Boolean): string;
3166 FileFormat
: TImageFileFormat
;
3168 // -1 because filter indices are in 1..n range
3171 if OpenFileFilter
then
3177 if (Index
>= 0) and (Index
< ImageFileFormats
.Count
) then
3180 for I
:= 0 to ImageFileFormats
.Count
- 1 do
3182 FileFormat
:= TObject(ImageFileFormats
[I
]) as TImageFileFormat
;
3183 if not OpenFileFilter
and not FileFormat
.CanSave
then
3185 if Index
= Count
then
3187 if FileFormat
.Extensions
.Count
> 0 then
3188 Result
:= FileFormat
.Extensions
[0];
3196 function GetFileNameFilterIndex(const FileName
: string; OpenFileFilter
: Boolean): LongInt;
3199 FileFormat
: TImageFileFormat
;
3202 for I
:= 0 to ImageFileFormats
.Count
- 1 do
3204 FileFormat
:= TObject(ImageFileFormats
[I
]) as TImageFileFormat
;
3205 if not OpenFileFilter
and not FileFormat
.CanSave
then
3207 if FileFormat
.TestFileName(FileName
) then
3209 // +1 because filter indices are in 1..n range
3211 if OpenFileFilter
then
3220 function GetIO
: TIOFunctions
;
3225 procedure RaiseImaging(const Msg
: string; const Args
: array of const);
3230 if GetExceptObject
<> nil then
3232 WholeMsg
:= WholeMsg
+ ' ' + SExceptMsg
+ ': ' +
3233 GetExceptObject
.Message;
3235 raise EImagingError
.CreateFmt(WholeMsg
, Args
);
3238 procedure RaiseImaging(const Msg
: string);
3240 RaiseImaging(Msg
, []);
3243 { Internal unit functions }
3245 function CheckOptionValue(OptionId
, Value
: LongInt): LongInt;
3248 ImagingColorReductionMask
:
3249 Result
:= ClampInt(Value
, 0, $FF);
3250 ImagingLoadOverrideFormat
, ImagingSaveOverrideFormat
:
3251 Result
:= Iff(ImagingFormats
.IsImageFormatValid(TImageFormat(Value
)),
3252 Value
, LongInt(ifUnknown
));
3253 ImagingMipMapFilter
: Result
:= ClampInt(Value
, Ord(Low(TSamplingFilter
)),
3254 Ord(High(TSamplingFilter
)));
3260 procedure SetFileIO
;
3265 procedure SetStreamIO
;
3270 procedure SetMemoryIO
;
3275 procedure InitImageFormats
;
3277 ImagingFormats
.InitImageFormats(ImageFormatInfos
);
3280 procedure FreeImageFileFormats
;
3284 if ImageFileFormats
<> nil then
3285 for I
:= 0 to ImageFileFormats
.Count
- 1 do
3286 TImageFileFormat(ImageFileFormats
[I
]).Free
;
3287 FreeAndNil(ImageFileFormats
);
3290 procedure InitOptions
;
3292 SetLength(Options
, InitialOptions
);
3293 OptionStack
:= TOptionStack
.Create
;
3296 procedure FreeOptions
;
3298 SetLength(Options
, 0);
3299 FreeAndNil(OptionStack
);
3303 TImageFileFormat class implementation
3306 constructor TImageFileFormat
.Create(AMetadata
: TMetadata
);
3309 FName
:= SUnknownFormat
;
3310 FExtensions
:= TStringList
.Create
;
3311 FMasks
:= TStringList
.Create
;
3312 if AMetadata
= nil then
3313 FMetadata
:= GlobalMetadata
3315 FMetadata
:= AMetadata
;
3319 destructor TImageFileFormat
.Destroy
;
3326 procedure TImageFileFormat
.Define
;
3330 function TImageFileFormat
.PrepareLoad(Handle
: TImagingHandle
;
3331 var Images
: TDynImageDataArray
; OnlyFirstFrame
: Boolean): Boolean;
3333 FMetadata
.ClearMetaItems
; // Clear old metadata
3334 FreeImagesInArray(Images
);
3335 SetLength(Images
, 0);
3336 Result
:= Handle
<> nil;
3339 function TImageFileFormat
.PostLoadCheck(var Images
: TDynImageDataArray
;
3340 LoadResult
: Boolean): Boolean;
3344 if not LoadResult
then
3346 FreeImagesInArray(Images
);
3347 SetLength(Images
, 0);
3352 Result
:= (Length(Images
) > 0) and TestImagesInArray(Images
);
3356 // Convert to overriden format if it is set
3357 if LoadOverrideFormat
<> ifUnknown
then
3358 for I
:= Low(Images
) to High(Images
) do
3359 ConvertImage(Images
[I
], LoadOverrideFormat
);
3364 function TImageFileFormat
.PrepareSave(Handle
: TImagingHandle
;
3365 const Images
: TDynImageDataArray
; var Index
: Integer): Boolean;
3369 CheckOptionsValidity
;
3373 Len
:= Length(Images
);
3376 // If there are no images to be saved exit
3377 if Len
= 0 then Exit
;
3379 // Check index of image to be saved (-1 as index means save all images)
3380 if IsMultiImageFormat
then
3382 if (Index
>= Len
) then
3389 FLastIdx
:= Len
- 1;
3397 for I
:= FFirstIdx
to FLastIdx
- 1 do
3399 if not TestImage(Images
[I
]) then
3405 if (Index
>= Len
) or (Index
< 0) then
3407 if not TestImage(Images
[Index
]) then
3415 procedure TImageFileFormat
.AddMasks(const AMasks
: string);
3421 FMasks
.CommaText
:= AMasks
;
3422 FMasks
.Delimiter
:= ';';
3424 for I
:= 0 to FMasks
.Count
- 1 do
3426 FMasks
[I
] := Trim(FMasks
[I
]);
3427 Ext
:= GetFileExt(FMasks
[I
]);
3428 if (Ext
<> '') and (Ext
<> '*') then
3429 FExtensions
.Add(Ext
);
3433 function TImageFileFormat
.GetFormatInfo(Format
: TImageFormat
): TImageFormatInfo
;
3435 Result
:= ImageFormatInfos
[Format
]^;
3438 function TImageFileFormat
.GetSupportedFormats
: TImageFormats
;
3440 Result
:= FSupportedFormats
;
3443 function TImageFileFormat
.LoadData(Handle
: TImagingHandle
;
3444 var Images
: TDynImageDataArray
; OnlyFirstFrame
: Boolean): Boolean;
3447 RaiseImaging(SFileFormatCanNotLoad
, [FName
]);
3450 function TImageFileFormat
.SaveData(Handle
: TImagingHandle
;
3451 const Images
: TDynImageDataArray
; Index
: LongInt): Boolean;
3454 RaiseImaging(SFileFormatCanNotSave
, [FName
]);
3457 procedure TImageFileFormat
.ConvertToSupported(var Image
: TImageData
;
3458 const Info
: TImageFormatInfo
);
3462 function TImageFileFormat
.IsSupported(const Image
: TImageData
): Boolean;
3464 Result
:= Image
.Format
in GetSupportedFormats
;
3467 function TImageFileFormat
.LoadFromFile(const FileName
: string;
3468 var Images
: TDynImageDataArray
; OnlyFirstLevel
: Boolean): Boolean;
3470 Handle
: TImagingHandle
;
3475 // Set IO ops to file ops and open given file
3477 Handle
:= IO
.Open(PChar(FileName
), omReadOnly
);
3479 // Test if file contains valid image and if so then load it
3480 if TestFormat(Handle
) then
3482 Result
:= PrepareLoad(Handle
, Images
, OnlyFirstLevel
) and
3483 LoadData(Handle
, Images
, OnlyFirstlevel
);
3484 Result
:= PostLoadCheck(Images
, Result
);
3487 RaiseImaging(SFileNotValid
, [FileName
, Name
]);
3492 RaiseImaging(SErrorLoadingFile
, [FileName
, FExtensions
[0]]);
3496 function TImageFileFormat
.LoadFromStream(Stream
: TStream
;
3497 var Images
: TDynImageDataArray
; OnlyFirstLevel
: Boolean): Boolean;
3499 Handle
: TImagingHandle
;
3503 OldPosition
:= Stream
.Position
;
3506 // Set IO ops to stream ops and "open" given memory
3508 Handle
:= IO
.Open(Pointer(Stream
), omReadOnly
);
3510 // Test if stream contains valid image and if so then load it
3511 if TestFormat(Handle
) then
3513 Result
:= PrepareLoad(Handle
, Images
, OnlyFirstLevel
) and
3514 LoadData(Handle
, Images
, OnlyFirstlevel
);
3515 Result
:= PostLoadCheck(Images
, Result
);
3518 RaiseImaging(SStreamNotValid
, [@Stream
, Name
]);
3523 Stream
.Position
:= OldPosition
;
3524 FreeImagesInArray(Images
);
3525 RaiseImaging(SErrorLoadingStream
, [@Stream
, FExtensions
[0]]);
3529 function TImageFileFormat
.LoadFromMemory(Data
: Pointer; Size
: LongInt; var
3530 Images
: TDynImageDataArray
; OnlyFirstLevel
: Boolean): Boolean;
3532 Handle
: TImagingHandle
;
3533 IORec
: TMemoryIORec
;
3538 // Set IO ops to memory ops and "open" given memory
3540 IORec
:= PrepareMemIO(Data
, Size
);
3541 Handle
:= IO
.Open(@IORec
,omReadOnly
);
3543 // Test if memory contains valid image and if so then load it
3544 if TestFormat(Handle
) then
3546 Result
:= PrepareLoad(Handle
, Images
, OnlyFirstLevel
) and
3547 LoadData(Handle
, Images
, OnlyFirstlevel
);
3548 Result
:= PostLoadCheck(Images
, Result
);
3551 RaiseImaging(SMemoryNotValid
, [Data
, Size
, Name
]);
3556 RaiseImaging(SErrorLoadingMemory
, [Data
, Size
, FExtensions
[0]]);
3560 function TImageFileFormat
.SaveToFile(const FileName
: string;
3561 const Images
: TDynImageDataArray
; OnlyFirstLevel
: Boolean): Boolean;
3563 Handle
: TImagingHandle
;
3564 Len
, Index
, I
: LongInt;
3568 if CanSave
and TestImagesInArray(Images
) then
3571 Len
:= Length(Images
);
3572 if IsMultiImageFormat
or
3573 (not IsMultiImageFormat
and (OnlyFirstLevel
or (Len
= 1))) then
3575 Handle
:= IO
.Open(PChar(FileName
), GetSaveOpenMode
);
3577 if OnlyFirstLevel
then
3581 // Write multi image to one file
3582 Result
:= PrepareSave(Handle
, Images
, Index
) and SaveData(Handle
, Images
, Index
);
3589 // Write multi image to file sequence
3590 Ext
:= ExtractFileExt(FileName
);
3591 FName
:= ChangeFileExt(FileName
, '');
3593 for I
:= 0 to Len
- 1 do
3595 Handle
:= IO
.Open(PChar(Format(FName
+ '%.3d' + Ext
, [I
])), GetSaveOpenMode
);
3598 Result
:= Result
and PrepareSave(Handle
, Images
, Index
) and
3599 SaveData(Handle
, Images
, Index
);
3608 raise UpdateExceptMessage(GetExceptObject
, SErrorSavingFile
, [FileName
, FExtensions
[0]]);
3612 function TImageFileFormat
.SaveToStream(Stream
: TStream
;
3613 const Images
: TDynImageDataArray
; OnlyFirstLevel
: Boolean): Boolean;
3615 Handle
: TImagingHandle
;
3616 Len
, Index
, I
: LongInt;
3620 OldPosition
:= Stream
.Position
;
3621 if CanSave
and TestImagesInArray(Images
) then
3624 Handle
:= IO
.Open(PChar(Stream
), GetSaveOpenMode
);
3626 if IsMultiImageFormat
or OnlyFirstLevel
then
3628 if OnlyFirstLevel
then
3632 // Write multi image in one run
3633 Result
:= PrepareSave(Handle
, Images
, Index
) and SaveData(Handle
, Images
, Index
);
3637 // Write multi image to sequence
3639 Len
:= Length(Images
);
3640 for I
:= 0 to Len
- 1 do
3643 Result
:= Result
and PrepareSave(Handle
, Images
, Index
) and
3644 SaveData(Handle
, Images
, Index
);
3653 Stream
.Position
:= OldPosition
;
3654 raise UpdateExceptMessage(GetExceptObject
, SErrorSavingStream
, [@Stream
, FExtensions
[0]]);
3658 function TImageFileFormat
.SaveToMemory(Data
: Pointer; var Size
: LongInt;
3659 const Images
: TDynImageDataArray
; OnlyFirstLevel
: Boolean): Boolean;
3661 Handle
: TImagingHandle
;
3662 Len
, Index
, I
: LongInt;
3663 IORec
: TMemoryIORec
;
3666 if CanSave
and TestImagesInArray(Images
) then
3669 IORec
:= PrepareMemIO(Data
, Size
);
3670 Handle
:= IO
.Open(PChar(@IORec
), GetSaveOpenMode
);
3672 if IsMultiImageFormat
or OnlyFirstLevel
then
3674 if OnlyFirstLevel
then
3678 // Write multi image in one run
3679 Result
:= PrepareSave(Handle
, Images
, Index
) and SaveData(Handle
, Images
, Index
);
3683 // Write multi image to sequence
3685 Len
:= Length(Images
);
3686 for I
:= 0 to Len
- 1 do
3689 Result
:= Result
and PrepareSave(Handle
, Images
, Index
) and
3690 SaveData(Handle
, Images
, Index
);
3695 Size
:= IORec
.Position
;
3700 raise UpdateExceptMessage(GetExceptObject
, SErrorSavingMemory
, [Data
, Size
, FExtensions
[0]]);
3704 function TImageFileFormat
.MakeCompatible(const Image
: TImageData
;
3705 var Compatible
: TImageData
; out MustBeFreed
: Boolean): Boolean;
3707 InitImage(Compatible
);
3709 if SaveOverrideFormat
<> ifUnknown
then
3711 // Save format override is active. Clone input and convert it to override format.
3712 CloneImage(Image
, Compatible
);
3713 ConvertImage(Compatible
, SaveOverrideFormat
);
3714 // Now check if override format is supported by file format. If it is not
3715 // then file format specific conversion (virtual method) is called.
3716 Result
:= IsSupported(Compatible
);
3719 ConvertToSupported(Compatible
, GetFormatInfo(Compatible
.Format
));
3720 Result
:= IsSupported(Compatible
);
3722 end // Add IsCompatible function! not only checking by Format
3723 else if IsSupported(Image
) then
3725 // No save format override and input is in format supported by this
3726 // file format. Just copy Image's fields to Compatible
3727 Compatible
:= Image
;
3732 // No override and input's format is not compatible with file format.
3733 // Clone it and the call file format specific conversion (virtual method).
3734 CloneImage(Image
, Compatible
);
3735 ConvertToSupported(Compatible
, GetFormatInfo(Compatible
.Format
));
3736 Result
:= IsSupported(Compatible
);
3738 // Tell the user that he must free Compatible after he's done with it
3740 MustBeFreed
:= Image
.Bits
<> Compatible
.Bits
;
3743 function TImageFileFormat
.TestFormat(Handle
: TImagingHandle
): Boolean;
3748 function TImageFileFormat
.TestFileName(const FileName
: string): Boolean;
3753 OnlyName
:= ExtractFileName(FileName
);
3754 // For each mask test if filename matches it
3755 for I
:= 0 to FMasks
.Count
- 1 do
3756 if StrMaskMatch(OnlyName
, FMasks
[I
], False) then
3764 procedure TImageFileFormat
.CheckOptionsValidity
;
3768 function TImageFileFormat
.GetCanLoad
: Boolean;
3770 Result
:= ffLoad
in FFeatures
;
3773 function TImageFileFormat
.GetCanSave
: Boolean;
3775 Result
:= ffSave
in FFeatures
;
3778 function TImageFileFormat
.GetIsMultiImageFormat
: Boolean;
3780 Result
:= ffMultiImage
in FFeatures
;
3783 function TImageFileFormat
.GetSaveOpenMode
: TOpenMode
;
3786 //if ffReadOnSave in FFeatures then
3787 // Result := omReadWrite
3792 { TOptionStack class implementation }
3794 constructor TOptionStack
.Create
;
3800 destructor TOptionStack
.Destroy
;
3804 for I
:= 0 to OptionStackDepth
- 1 do
3805 SetLength(FStack
[I
], 0);
3809 function TOptionStack
.Pop
: Boolean;
3814 if FPosition
>= 0 then
3816 SetLength(Options
, Length(FStack
[FPosition
]));
3817 for I
:= 0 to Length(FStack
[FPosition
]) - 1 do
3818 if Options
[I
] <> nil then
3819 Options
[I
]^ := FStack
[FPosition
, I
];
3825 function TOptionStack
.Push
: Boolean;
3830 if FPosition
< OptionStackDepth
- 1 then
3833 SetLength(FStack
[FPosition
], Length(Options
));
3834 for I
:= 0 to Length(Options
) - 1 do
3835 if Options
[I
] <> nil then
3836 FStack
[FPosition
, I
] := Options
[I
]^;
3843 procedure TMetadata
.SetMetaItem(const Id
: string; const Value
: Variant;
3844 ImageIndex
: Integer);
3846 AddMetaToList(FLoadMetaItems
, Id
, Value
, ImageIndex
);
3849 procedure TMetadata
.SetMetaItemForSaving(const Id
: string; const Value
: Variant;
3850 ImageIndex
: Integer);
3852 AddMetaToList(FSaveMetaItems
, Id
, Value
, ImageIndex
);
3855 procedure TMetadata
.AddMetaToList(List
: TStringList
; const Id
: string;
3856 const Value
: Variant; ImageIndex
: Integer);
3858 Item
: TMetadataItem
;
3862 FullId
:= GetMetaItemName(Id
, ImageIndex
);
3863 if List
.Find(FullId
, Idx
) then
3864 (List
.Objects
[Idx
] as TMetadataItem
).Value
:= Value
3867 Item
:= TMetadataItem
.Create
;
3869 Item
.ImageIndex
:= ImageIndex
;
3870 Item
.Value
:= Value
;
3871 List
.AddObject(FullId
, Item
);
3875 procedure TMetadata
.ClearMetaItems
;
3877 ClearMetaList(FLoadMetaItems
);
3880 procedure TMetadata
.ClearMetaItemsForSaving
;
3882 ClearMetaList(FSaveMetaItems
);
3885 procedure TMetadata
.ClearMetaList(List
: TStringList
);
3889 for I
:= 0 to List
.Count
- 1 do
3890 List
.Objects
[I
].Free
;
3894 procedure TMetadata
.CopyLoadedMetaItemsForSaving
;
3897 Copy
, Orig
: TMetadataItem
;
3899 ClearMetaItemsForSaving
;
3900 for I
:= 0 to FLoadMetaItems
.Count
- 1 do
3902 Orig
:= TMetadataItem(FLoadMetaItems
.Objects
[I
]);
3903 Copy
:= TMetadataItem
.Create
;
3905 Copy
.ImageIndex
:= Orig
.ImageIndex
;
3906 Copy
.Value
:= Orig
.Value
;
3907 FSaveMetaItems
.AddObject(GetMetaItemName(Copy
.Id
, Copy
.ImageIndex
), Copy
);
3911 constructor TMetadata
.Create
;
3914 FLoadMetaItems
:= TStringList
.Create
;
3915 FLoadMetaItems
.Sorted
:= True;
3916 FSaveMetaItems
:= TStringList
.Create
;
3917 FSaveMetaItems
.Sorted
:= True;
3920 destructor TMetadata
.Destroy
;
3923 ClearMetaItemsForSaving
;
3924 FLoadMetaItems
.Free
;
3925 FSaveMetaItems
.Free
;
3929 function TMetadata
.GetMetaById(const Id
: string): Variant;
3933 if FLoadMetaItems
.Find(Id
, Idx
) then
3934 Result
:= (FLoadMetaItems
.Objects
[Idx
] as TMetadataItem
).Value
3936 Result
:= Variants
.Null
;
3939 function TMetadata
.GetMetaByIdMulti(const Id
: string; ImageIndex
: Integer): Variant;
3941 Result
:= GetMetaById(GetMetaItemName(Id
, ImageIndex
));
3944 function TMetadata
.GetSaveMetaById(const Id
: string): Variant;
3948 if FSaveMetaItems
.Find(Id
, Idx
) then
3949 Result
:= (FSaveMetaItems
.Objects
[Idx
] as TMetadataItem
).Value
3951 Result
:= Variants
.Null
;
3954 function TMetadata
.GetSaveMetaByIdMulti(const Id
: string;
3955 ImageIndex
: Integer): Variant;
3957 Result
:= GetSaveMetaById(GetMetaItemName(Id
, ImageIndex
));
3960 function TMetadata
.GetMetaByIdx(Index
: Integer): TMetadataItem
;
3962 Result
:= FLoadMetaItems
.Objects
[Index
] as TMetadataItem
;
3965 function TMetadata
.GetMetaCount
: Integer;
3967 Result
:= FLoadMetaItems
.Count
;
3970 function TMetadata
.GetMetaItemName(const Id
: string;
3971 ImageIndex
: Integer): string;
3973 Result
:= Iff(ImageIndex
= 0, Id
, Format(SMetaIdForSubImage
, [Id
, ImageIndex
]));
3976 function TMetadata
.GetPhysicalPixelSize(ResUnit
: TResolutionUnit
; var XSize
,
3977 YSize
: Single; MetaForSave
: Boolean; ImageIndex
: Integer): Boolean;
3979 TGetter
= function(const Id
: string; ImageIndex
: Integer): Variant of object;
3982 XMeta
, YMeta
: Variant;
3985 Getter
:= GetSaveMetaByIdMulti
3987 Getter
:= GetMetaByIdMulti
;
3989 XMeta
:= Getter(SMetaPhysicalPixelSizeX
, ImageIndex
);
3990 YMeta
:= Getter(SMetaPhysicalPixelSizeY
, ImageIndex
);
3994 Result
:= not VarIsNull(XMeta
) or not VarIsNull(YMeta
);
3999 if not VarIsNull(XMeta
) then
4001 if not VarIsNull(YMeta
) then
4009 TranslateUnits(ResUnit
, XSize
, YSize
);
4012 procedure TMetadata
.SetPhysicalPixelSize(ResUnit
: TResolutionUnit
; XSize
,
4013 YSize
: Single; MetaForSave
: Boolean; ImageIndex
: Integer);
4015 TAdder
= procedure(const Id
: string; const Value
: Variant; ImageIndex
: Integer) of object;
4019 TranslateUnits(ResUnit
, XSize
, YSize
);
4022 Adder
:= SetMetaItemForSaving
4024 Adder
:= SetMetaItem
;
4026 Adder(SMetaPhysicalPixelSizeX
, XSize
, ImageIndex
);
4027 Adder(SMetaPhysicalPixelSizeY
, YSize
, ImageIndex
);
4030 procedure TMetadata
.TranslateUnits(ResolutionUnit
: TResolutionUnit
; var XRes
,
4035 case ResolutionUnit
of
4036 ruDpi
: UnitSize
:= 25400;
4037 ruDpm
: UnitSize
:= 1e06
;
4038 ruDpcm
: UnitSize
:= 1e04
;
4042 if ResolutionUnit
<> ruSizeInMicroMeters
then
4044 XRes
:= UnitSize
/ XRes
;
4045 YRes
:= UnitSize
/ YRes
;
4049 function TMetadata
.HasMetaItem(const Id
: string; ImageIndex
: Integer): Boolean;
4051 Result
:= GetMetaByIdMulti(Id
, ImageIndex
) <> Variants
.Null
;
4054 function TMetadata
.HasMetaItemForSaving(const Id
: string; ImageIndex
: Integer): Boolean;
4056 Result
:= GetSaveMetaByIdMulti(Id
, ImageIndex
) <> Variants
.Null
;
4061 {$IF CompilerVersion >= 18}
4062 System
.ReportMemoryLeaksOnShutdown
:= True;
4065 if GlobalMetadata
= nil then
4066 GlobalMetadata
:= TMetadata
.Create
;
4067 if ImageFileFormats
= nil then
4068 ImageFileFormats
:= TList
.Create
;
4070 RegisterOption(ImagingColorReductionMask
, @ColorReductionMask
);
4071 RegisterOption(ImagingLoadOverrideFormat
, @LoadOverrideFormat
);
4072 RegisterOption(ImagingSaveOverrideFormat
, @SaveOverrideFormat
);
4073 RegisterOption(ImagingMipMapFilter
, @MipMapFilter
);
4074 RegisterOption(ImagingBinaryTreshold
, @BinaryTreshold
);
4077 FreeImageFileFormats
;
4078 GlobalMetadata
.Free
;
4083 -- TODOS ----------------------------------------------------
4086 -- 0.77.1 ---------------------------------------------------
4087 - Updated IO Open functions according to changes in ImagingTypes.
4088 - Fixed bug in SplitImage that could cause wrong size of edge chunks.
4089 - Metadata support fixes and extensions (frame delays, animation loops).
4091 -- 0.26.5 Changes/Bug Fixes ---------------------------------
4092 - Started reworking exception raising to keep the original class type
4093 (e.g. in NewImage EOutOfMemory could be raised but was hidden
4094 by EImagingError raised afterwards in NewImage try/except).
4095 - Fixed possible AV in Rotate45 subproc of RotateImage.
4096 - Added ReadRawXXX and WriteRawXXX functions for raw image bits IO.
4097 - Implemented ImagingBinaryTreshold option.
4098 - Added support for simple image metadata loading/saving.
4099 - Moved file format definition (name, exts, caps, ...) from
4100 constructor to new Define method.
4101 - Fixed some memory leaks caused by failures during image loading.
4103 -- 0.26.3 Changes/Bug Fixes ---------------------------------
4104 - Extended RotateImage to allow arbitrary angle rotations.
4105 - Reversed the order file formats list is searched so
4106 if you register a new one it will be found sooner than
4108 - Fixed memory leak in ResizeImage ocurring when resizing
4111 -- 0.26.1 Changes/Bug Fixes ---------------------------------
4112 - Added position/size checks to LoadFromStream functions.
4113 - Changed conditional compilation in impl. uses section to reflect changes
4116 -- 0.24.3 Changes/Bug Fixes ---------------------------------
4117 - GenerateMipMaps now generates all smaller levels from
4118 original big image (better results when using more advanced filters).
4119 Also conversion to compatible image format is now done here not
4120 in FillMipMapLevel (that is called for every mipmap level).
4122 -- 0.23 Changes/Bug Fixes -----------------------------------
4123 - MakePaletteForImages now works correctly for indexed and special format images
4124 - Fixed bug in StretchRect: Image was not properly stretched if
4125 src and dst dimensions differed only in height.
4126 - ConvertImage now fills new image with zeroes to avoid random data in
4127 some conversions (RGB->XRGB)
4128 - Changed RegisterOption procedure to function
4129 - Changed bunch of palette functions from low level interface to procedure
4130 (there was no reason for them to be functions).
4131 - Changed FreeImage and FreeImagesInArray functions to procedures.
4132 - Added many assertions, come try-finally, other checks, and small code
4135 -- 0.21 Changes/Bug Fixes -----------------------------------
4136 - GenerateMipMaps threw failed assertion when input was indexed or special,
4138 - Added CheckOptionsValidity to TImageFileFormat and its decendants.
4139 - Unit ImagingExtras which registers file formats in Extras package
4140 is now automatically added to uses clause if LINK_EXTRAS symbol is
4141 defined in ImagingOptions.inc file.
4142 - Added EnumFileFormats function to low level interface.
4143 - Fixed bug in SwapChannels which could cause AV when swapping alpha
4144 channel of A8R8G8B8 images.
4145 - Converting loaded images to ImagingOverrideFormat is now done
4146 in PostLoadCheck method to avoid code duplicity.
4147 - Added GetFileFormatCount and GetFileFormatAtIndex functions
4148 - Bug in ConvertImage: if some format was converted to similar format
4149 only with swapped channels (R16G16B16<>B16G16R16) then channels were
4150 swapped correctly but new data format (swapped one) was not set.
4151 - Made TImageFileFormat.MakeCompatible public non-virtual method
4152 (and modified its function). Created new virtual
4153 ConvertToSupported which should be overriden by descendants.
4154 Main reason for doint this is to avoid duplicate code that was in all
4155 TImageFileFormat's descendants.
4156 - Changed TImageFileFormat.GetFormatInfo's result type to TImageFormatInfo.
4157 - Split overloaded FindImageFileFormat functions to
4158 FindImageFileFormatByClass and FindImageFileFormatByExt and created new
4159 FindImageFileFormatByName which operates on whole filenames.
4160 - Function GetExtensionFilterIndex renamed to GetFileNameFilterIndex
4161 (because it now works with filenames not extensions).
4162 - DetermineFileFormat now first searches by filename and if not found
4164 - Added TestFileName method to TImageFileFormat.
4165 - Updated GetImageFileFormatsFilter to uses Masks instead of Extensions
4166 property of TImageFileFormat. Also you can now request
4167 OpenDialog and SaveDialog type filters
4168 - Added Masks property and AddMasks method to TImageFileFormat.
4169 AddMasks replaces AddExtensions, it uses filename masks instead
4170 of sime filename extensions to identify supported files.
4171 - Changed TImageFileFormat.LoadData procedure to function and
4172 moved varios duplicate code from its descandats (check index,...)
4173 here to TImageFileFormat helper methods.
4174 - Changed TImageFileFormat.SaveData procedure to function and
4175 moved varios duplicate code from its descandats (check index,...)
4176 here to TImageFileFormat helper methods.
4177 - Removed RAISE_EXCEPTIONS define, exceptions are now raised everytime
4178 - Added MustBeFreed parameter to TImageFileFormat.MakeComptible method
4179 that indicates that compatible image returned by this method must be
4180 freed after its usage.
4182 -- 0.19 Changes/Bug Fixes -----------------------------------
4183 - fixed bug in NewImage: if given format was ifDefault it wasn't
4184 replaced with DefaultImageFormat constant which caused problems later
4186 - fixed bug in RotateImage which caused that rotated special format
4187 images were whole black
4188 - LoadImageFromXXX and LoadMultiImageFromXXX now use DetermineXXXFormat
4189 when choosing proper loader, this eliminated need for Ext parameter
4190 in stream and memory loading functions
4191 - added GetVersionStr function
4192 - fixed bug in ResizeImage which caued indexed images to lose their
4193 palette during process resulting in whole black image
4194 - Clipping in ...Rect functions now uses clipping procs from ImagingUtility,
4195 it also works better
4196 - FillRect optimization for 8, 16, and 32 bit formats
4197 - added pixel set/get functions to low level interface:
4198 GetPixelDirect, SetPixelDirect, GetPixel32, SetPixel32,
4199 GetPixelFP, SetPixelFP
4200 - removed GetPixelBytes low level intf function - redundant
4201 (same data can be obtained by GetImageFormatInfo)
4202 - made small changes in many parts of library to compile
4203 on AMD64 CPU (Linux with FPC)
4204 - changed InitImage to procedure (function was pointless)
4205 - Method TestFormat of TImageFileFormat class made public
4207 - added function IsFileFormatSupported to low level interface
4208 (contributed by Paul Michell)
4209 - fixed some missing format arguments from error strings
4210 which caused Format function to raise exception
4211 - removed forgotten debug code that disabled filtered resizing of images with
4212 channel bitcounts > 8
4214 -- 0.17 Changes/Bug Fixes -----------------------------------
4215 - changed order of parameters of CopyRect function
4216 - GenerateMipMaps now filters mipmap levels
4217 - ResizeImage functions was extended to allow bilinear and bicubic filtering
4218 - added StretchRect function to low level interface
4219 - added functions GetImageFileFormatsFilter, GetFilterIndexExtension,
4220 and GetExtensionFilterIndex
4222 -- 0.15 Changes/Bug Fixes -----------------------------------
4223 - added function RotateImage to low level interface
4224 - moved TImageFormatInfo record and types required by it to
4225 ImagingTypes unit, changed GetImageFormatInfo low level
4226 interface function to return TImageFormatInfo instead of short info
4227 - added checking of options values validity before they are used
4228 - fixed possible memory leak in CloneImage
4229 - added ReplaceColor function to low level interface
4230 - new function FindImageFileFormat by class added
4232 -- 0.13 Changes/Bug Fixes -----------------------------------
4233 - added DetermineFileFormat, DetermineStreamFormat, DetermineMemoryFormat,
4234 GetPixelsSize functions to low level interface
4235 - added NewPalette, CopyPalette, FreePalette functions
4236 to low level interface
4237 - added MapImageToPalette, FillRect, SplitImage, MakePaletteForImages
4238 functions to low level interface
4239 - fixed buggy FillCustomPalette function (possible div by zero and others)
4240 - added CopyRect function to low level interface
4241 - Member functions of TImageFormatInfo record implemented for all formats
4242 - before saving images TestImagesInArray is called now
4243 - added TestImagesInArray function to low level interface
4244 - added GenerateMipMaps function to low level interface
4245 - stream position in load/save from/to stream is now set to position before
4246 function was called if error occurs
4247 - when error occured during load/save from/to file file handle
4249 - CloneImage returned always False