Repo for the search and displace ingest module that takes odf, docx and pdf and transforms it into .md to be used with search and displace operations
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4350 lines
141 KiB

3 years ago
  1. {
  2. Vampyre Imaging Library
  3. by Marek Mauder
  4. http://imaginglib.sourceforge.net
  5. The contents of this file are used with permission, subject to the Mozilla
  6. Public License Version 1.1 (the "License"); you may not use this file except
  7. in compliance with the License. You may obtain a copy of the License at
  8. http://www.mozilla.org/MPL/MPL-1.1.html
  9. Software distributed under the License is distributed on an "AS IS" basis,
  10. WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
  11. the specific language governing rights and limitations under the License.
  12. Alternatively, the contents of this file may be used under the terms of the
  13. GNU Lesser General Public License (the "LGPL License"), in which case the
  14. provisions of the LGPL License are applicable instead of those above.
  15. If you wish to allow use of your version of this file only under the terms
  16. of the LGPL License and not to allow others to use your version of this file
  17. under the MPL, indicate your decision by deleting the provisions above and
  18. replace them with the notice and other provisions required by the LGPL
  19. License. If you do not delete the provisions above, a recipient may use
  20. your version of this file under either the MPL or the LGPL License.
  21. For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html
  22. }
  23. { This unit is heart of Imaging library. It contains basic functions for
  24. manipulating image data as well as various image file format support.}
  25. unit Imaging;
  26. {$I ImagingOptions.inc}
  27. interface
  28. uses
  29. SysUtils, Classes, Types, ImagingTypes;
  30. type
  31. { Default Imaging excepton class }
  32. EImagingError = class(Exception);
  33. { Raised when function receives bad image (not passed TestImage).}
  34. EImagingBadImage = class(Exception)
  35. public
  36. constructor Create;
  37. end;
  38. { Dynamic array of TImageData records }
  39. TDynImageDataArray = array of TImageData;
  40. { ------------------------------------------------------------------------
  41. Low Level Interface Functions
  42. ------------------------------------------------------------------------}
  43. { General Functions }
  44. { Initializes image (all is set to zeroes). Call this for each image
  45. before using it (before calling every other function) to be sure there
  46. are no random-filled bytes (which would cause errors later).}
  47. procedure InitImage(var Image: TImageData);
  48. { Creates empty image of given dimensions and format. Image is filled with
  49. transparent black color (A=0, R=0, G=0, B=0).}
  50. function NewImage(Width, Height: LongInt; Format: TImageFormat;
  51. var Image: TImageData): Boolean;
  52. { Returns True if given TImageData record is valid.}
  53. function TestImage(const Image: TImageData): Boolean;
  54. { Frees given image data. Ater this call image is in the same state
  55. as after calling InitImage. If image is not valid (dost not pass TestImage
  56. test) it is only zeroed by calling InitImage.}
  57. procedure FreeImage(var Image: TImageData);
  58. { Call FreeImage() on all images in given dynamic array and sets its
  59. length to zero.}
  60. procedure FreeImagesInArray(var Images: TDynImageDataArray);
  61. { Returns True if all TImageData records in given array are valid. Returns False
  62. if at least one is invalid or if array is empty.}
  63. function TestImagesInArray(const Images: TDynImageDataArray): Boolean;
  64. { Checks given file for every supported image file format and if
  65. the file is in one of them returns its string identifier
  66. (which can be used in LoadFromStream/LoadFromMem type functions).
  67. If file is not in any of the supported formats empty string is returned.}
  68. function DetermineFileFormat(const FileName: string): string;
  69. { Checks given stream for every supported image file format and if
  70. the stream is in one of them returns its string identifier
  71. (which can be used in LoadFromStream/LoadFromMem type functions).
  72. If stream is not in any of the supported formats empty string is returned.}
  73. function DetermineStreamFormat(Stream: TStream): string;
  74. { Checks given memory for every supported image file format and if
  75. the memory is in one of them returns its string identifier
  76. (which can be used in LoadFromStream/LoadFromMem type functions).
  77. If memory is not in any of the supported formats empty string is returned.}
  78. function DetermineMemoryFormat(Data: Pointer; Size: LongInt): string;
  79. { Checks that an apropriate file format is supported purely from inspecting
  80. the given file name's extension (not contents of the file itself).
  81. The file need not exist.}
  82. function IsFileFormatSupported(const FileName: string): Boolean;
  83. { Enumerates all registered image file formats. Descriptive name,
  84. default extension, masks (like '*.jpg,*.jfif') and some capabilities
  85. of each format are returned. To enumerate all formats start with Index at 0 and
  86. call EnumFileFormats with given Index in loop until it returns False (Index is
  87. automatically increased by 1 in function's body on successful call).}
  88. function EnumFileFormats(var Index: LongInt; var Name, DefaultExt, Masks: string;
  89. var CanSaveImages, IsMultiImageFormat: Boolean): Boolean;
  90. { Loading Functions }
  91. { Loads single image from given file.}
  92. function LoadImageFromFile(const FileName: string; var Image: TImageData): Boolean;
  93. { Loads single image from given stream. If function fails stream position
  94. is not changed.}
  95. function LoadImageFromStream(Stream: TStream; var Image: TImageData): Boolean;
  96. { Loads single image from given memory location.}
  97. function LoadImageFromMemory(Data: Pointer; Size: LongInt; var Image: TImageData): Boolean;
  98. { Loads multiple images from given file.}
  99. function LoadMultiImageFromFile(const FileName: string;
  100. var Images: TDynImageDataArray): Boolean;
  101. { Loads multiple images from given stream. If function fails stream position
  102. is not changed.}
  103. function LoadMultiImageFromStream(Stream: TStream;
  104. var Images: TDynImageDataArray): Boolean;
  105. { Loads multiple images from given memory location.}
  106. function LoadMultiImageFromMemory(Data: Pointer; Size: LongInt;
  107. var Images: TDynImageDataArray): Boolean;
  108. { Saving Functions }
  109. { Saves single image to given file.}
  110. function SaveImageToFile(const FileName: string; const Image: TImageData): Boolean;
  111. { Saves single image to given stream. If function fails stream position
  112. is not changed. Ext identifies desired image file format (jpg, png, dds, ...).}
  113. function SaveImageToStream(const Ext: string; Stream: TStream;
  114. const Image: TImageData): Boolean;
  115. { Saves single image to given memory location. Memory must be allocated and its
  116. size is passed in Size parameter in which number of written bytes is returned.
  117. Ext identifies desired image file format (jpg, png, dds, ...).}
  118. function SaveImageToMemory(const Ext: string; Data: Pointer; var Size: LongInt;
  119. const Image: TImageData): Boolean;
  120. { Saves multiple images to given file. If format supports
  121. only single level images and there are multiple images to be saved,
  122. they are saved as sequence of files img000.jpg, img001.jpg ....).}
  123. function SaveMultiImageToFile(const FileName: string;
  124. const Images: TDynImageDataArray): Boolean;
  125. { Saves multiple images to given stream. If format supports
  126. only single level images and there are multiple images to be saved,
  127. they are saved one after another to the stream. If function fails stream
  128. position is not changed. Ext identifies desired image file format (jpg, png, dds, ...).}
  129. function SaveMultiImageToStream(const Ext: string; Stream: TStream;
  130. const Images: TDynImageDataArray): Boolean;
  131. { Saves multiple images to given memory location. If format supports
  132. only single level images and there are multiple images to be saved,
  133. they are saved one after another to the memory. Memory must be allocated and
  134. its 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 SaveMultiImageToMemory(const Ext: string; Data: Pointer;
  137. var Size: LongInt; const Images: TDynImageDataArray): Boolean;
  138. { Manipulation Functions }
  139. { Creates identical copy of image data. Clone should be initialized
  140. by InitImage or it should be vaild image which will be freed by CloneImage.}
  141. function CloneImage(const Image: TImageData; var Clone: TImageData): Boolean;
  142. { Converts image to the given format.}
  143. function ConvertImage(var Image: TImageData; DestFormat: TImageFormat): Boolean;
  144. { Flips given image. Reverses the image along its horizontal axis - the top
  145. becomes the bottom and vice versa.}
  146. function FlipImage(var Image: TImageData): Boolean;
  147. { Mirrors given image. Reverses the image along its vertical axis � the left
  148. side becomes the right and vice versa.}
  149. function MirrorImage(var Image: TImageData): Boolean;
  150. { Resizes given image to new dimensions. Nearest, bilinear, or bicubic filtering
  151. can be used. Input Image must already be created - use NewImage to create new images.}
  152. function ResizeImage(var Image: TImageData; NewWidth, NewHeight: LongInt;
  153. Filter: TResizeFilter): Boolean;
  154. { Swaps SrcChannel and DstChannel color or alpha channels of image.
  155. Use ChannelRed, ChannelBlue, ChannelGreen, ChannelAlpha constants to
  156. identify channels.}
  157. function SwapChannels(var Image: TImageData; SrcChannel, DstChannel: LongInt): Boolean;
  158. { Reduces the number of colors of the Image. Currently MaxColors must be in
  159. range <2, 4096>. Color reduction works also for alpha channel. Note that for
  160. large images and big number of colors it can be very slow.
  161. Output format of the image is the same as input format.}
  162. function ReduceColors(var Image: TImageData; MaxColors: LongInt): Boolean;
  163. { Generates mipmaps for image. Levels is the number of desired mipmaps levels
  164. with zero (or some invalid number) meaning all possible levels.}
  165. function GenerateMipMaps(const Image: TImageData; Levels: LongInt;
  166. var MipMaps: TDynImageDataArray): Boolean;
  167. { Maps image to existing palette producing image in ifIndex8 format.
  168. Pal must be allocated to at least Entries * SizeOf(TColor32Rec) bytes.
  169. As resulting image is in 8bit indexed format Entries must be lower or
  170. equal to 256.}
  171. function MapImageToPalette(var Image: TImageData; Pal: PPalette32;
  172. Entries: LongInt): Boolean;
  173. { Splits image into XChunks x YChunks subimages. Default size of each chunk is
  174. ChunkWidth x ChunkHeight. If PreserveSize si True chunks at the edges of
  175. the image are also ChunkWidth x ChunkHeight sized and empty space is filled
  176. with optional Fill pixels. After calling this function XChunks contains number of
  177. chunks along x axis and YChunks along y axis. To access chunk [X, Y] use this
  178. index: Chunks[Y * XChunks + X].}
  179. function SplitImage(var Image: TImageData; var Chunks: TDynImageDataArray;
  180. ChunkWidth, ChunkHeight: LongInt; var XChunks, YChunks: LongInt;
  181. PreserveSize: Boolean; Fill: Pointer = nil): Boolean;
  182. { Creates palette with MaxColors based on the colors of images in Images array.
  183. Use it when you want to convert several images to indexed format using
  184. single palette for all of them. If ConvertImages is True images in array
  185. are converted to indexed format using resulting palette. if it is False
  186. images are left intact and only resulting palatte is returned in Pal.
  187. Pal must be allocated to have at least MaxColors entries.}
  188. function MakePaletteForImages(var Images: TDynImageDataArray; Pal: PPalette32;
  189. MaxColors: LongInt; ConvertImages: Boolean): Boolean;
  190. { Rotates image by Angle degrees counterclockwise. All angles are allowed.}
  191. procedure RotateImage(var Image: TImageData; Angle: Single);
  192. { Drawing/Pixel functions }
  193. { Copies rectangular part of SrcImage to DstImage. No blending is performed -
  194. alpha is simply copied to destination image. Operates also with
  195. negative X and Y coordinates.
  196. Note that copying is fastest for images in the same data format
  197. (and slowest for images in special formats).}
  198. function CopyRect(const SrcImage: TImageData; SrcX, SrcY, Width, Height: LongInt;
  199. var DstImage: TImageData; DstX, DstY: LongInt): Boolean;
  200. { Fills given rectangle of image with given pixel fill data. Fill should point
  201. to the pixel in the same format as the given image is in.}
  202. function FillRect(var Image: TImageData; X, Y, Width, Height: LongInt; FillColor: Pointer): Boolean;
  203. { Replaces pixels with OldPixel in the given rectangle by NewPixel.
  204. OldPixel and NewPixel should point to the pixels in the same format
  205. as the given image is in.}
  206. function ReplaceColor(var Image: TImageData; X, Y, Width, Height: LongInt;
  207. OldColor, NewColor: Pointer): Boolean;
  208. { Stretches the contents of the source rectangle to the destination rectangle
  209. with optional resampling. No blending is performed - alpha is
  210. simply copied/resampled to destination image. Note that stretching is
  211. fastest for images in the same data format (and slowest for
  212. images in special formats).}
  213. function StretchRect(const SrcImage: TImageData; SrcX, SrcY, SrcWidth,
  214. SrcHeight: LongInt; var DstImage: TImageData; DstX, DstY, DstWidth,
  215. DstHeight: LongInt; Filter: TResizeFilter): Boolean;
  216. { Copies pixel of Image at [X, Y] to memory pointed at by Pixel. Doesn't
  217. work with special formats.}
  218. procedure GetPixelDirect(const Image: TImageData; X, Y: LongInt; Pixel: Pointer);
  219. { Copies pixel from memory pointed at by Pixel to Image at position [X, Y].
  220. Doesn't work with special formats.}
  221. procedure SetPixelDirect(const Image: TImageData; X, Y: LongInt; Pixel: Pointer);
  222. { Function for getting pixel colors. Native pixel is read from Image and
  223. then translated to 32 bit ARGB. Works for all image formats (except special)
  224. so it is not very fast.}
  225. function GetPixel32(const Image: TImageData; X, Y: LongInt): TColor32Rec;
  226. { Procedure for setting pixel colors. Input 32 bit ARGB color is translated to
  227. native format and then written to Image. Works for all image formats (except special)
  228. so it is not very fast.}
  229. procedure SetPixel32(const Image: TImageData; X, Y: LongInt; const Color: TColor32Rec);
  230. { Function for getting pixel colors. Native pixel is read from Image and
  231. then translated to FP ARGB. Works for all image formats (except special)
  232. so it is not very fast.}
  233. function GetPixelFP(const Image: TImageData; X, Y: LongInt): TColorFPRec;
  234. { Procedure for setting pixel colors. Input FP ARGB color is translated to
  235. native format and then written to Image. Works for all image formats (except special)
  236. so it is not very fast.}
  237. procedure SetPixelFP(const Image: TImageData; X, Y: LongInt; const Color: TColorFPRec);
  238. { Palette Functions }
  239. { Allocates new palette with Entries ARGB color entries.}
  240. procedure NewPalette(Entries: LongInt; var Pal: PPalette32);
  241. { Frees given palette.}
  242. procedure FreePalette(var Pal: PPalette32);
  243. { Copies Count palette entries from SrcPal starting at index SrcIdx to
  244. DstPal at index DstPal.}
  245. procedure CopyPalette(SrcPal, DstPal: PPalette32; SrcIdx, DstIdx, Count: LongInt);
  246. { Returns index of color in palette or index of nearest color if exact match
  247. is not found. Pal must have at least Entries color entries.}
  248. function FindColor(Pal: PPalette32; Entries: LongInt; Color: TColor32): LongInt;
  249. { Creates grayscale palette where each color channel has the same value.
  250. Pal must have at least Entries color entries.}
  251. procedure FillGrayscalePalette(Pal: PPalette32; Entries: LongInt);
  252. { Creates palette with given bitcount for each channel.
  253. 2^(RBits + GBits + BBits) should be equl to Entries. Examples:
  254. (3, 3, 2) will create palette with all possible colors of R3G3B2 format
  255. and (8, 0, 0) will create palette with 256 shades of red.
  256. Pal must be allocated to at least Entries * SizeOf(TColor32Rec) bytes.}
  257. procedure FillCustomPalette(Pal: PPalette32; Entries: LongInt; RBits, GBits,
  258. BBits: Byte; Alpha: Byte = $FF);
  259. { Swaps SrcChannel and DstChannel color or alpha channels of palette.
  260. Use ChannelRed, ChannelBlue, ChannelGreen, ChannelAlpha constants to
  261. identify channels. Pal must be allocated to at least
  262. Entries * SizeOf(TColor32Rec) bytes.}
  263. procedure SwapChannelsOfPalette(Pal: PPalette32; Entries, SrcChannel,
  264. DstChannel: LongInt);
  265. { Options Functions }
  266. { Sets value of integer option specified by OptionId parameter.
  267. Option Ids are constans starting ImagingXXX.}
  268. function SetOption(OptionId, Value: LongInt): Boolean;
  269. { Returns value of integer option specified by OptionId parameter. If OptionId is
  270. invalid, InvalidOption is returned. Option Ids are constans
  271. starting ImagingXXX.}
  272. function GetOption(OptionId: LongInt): LongInt;
  273. { Pushes current values of all options on the stack. Returns True
  274. if successfull (max stack depth is 8 now). }
  275. function PushOptions: Boolean;
  276. { Pops back values of all options from the top of the stack. Returns True
  277. if successfull (max stack depth is 8 now). }
  278. function PopOptions: Boolean;
  279. { Image Format Functions }
  280. { Returns short information about given image format.}
  281. function GetImageFormatInfo(Format: TImageFormat; out Info: TImageFormatInfo): Boolean;
  282. { Returns size in bytes of Width x Height area of pixels. Works for all formats.}
  283. function GetPixelsSize(Format: TImageFormat; Width, Height: LongInt): LongInt;
  284. { IO Functions }
  285. { User can set his own file IO functions used when loading from/saving to
  286. files by this function.}
  287. procedure SetUserFileIO(OpenProc: TOpenProc; CloseProc: TCloseProc; EofProc: TEofProc; SeekProc:
  288. TSeekProc; TellProc: TTellProc; ReadProc: TReadProc; WriteProc: TWriteProc);
  289. { Sets file IO functions to Imaging default.}
  290. procedure ResetFileIO;
  291. { Raw Image IO Functions }
  292. procedure ReadRawImageFromFile(const FileName: string; Width, Height: Integer;
  293. Format: TImageFormat; var Image: TImageData; Offset: Integer = 0; RowLength: Integer = 0);
  294. procedure ReadRawImageFromStream(Stream: TStream; Width, Height: Integer;
  295. Format: TImageFormat; var Image: TImageData; Offset: Integer = 0; RowLength: Integer = 0);
  296. procedure ReadRawImageFromMemory(Data: Pointer; DataSize: Integer; Width, Height: Integer;
  297. Format: TImageFormat; var Image: TImageData; Offset: Integer = 0; RowLength: Integer = 0);
  298. procedure ReadRawImageRect(Data: Pointer; Left, Top, Width, Height: Integer;
  299. var Image: TImageData; Offset: Integer = 0; RowLength: Integer = 0);
  300. procedure WriteRawImageToFile(const FileName: string; const Image: TImageData;
  301. Offset: Integer = 0; RowLength: Integer = 0);
  302. procedure WriteRawImageToStream(Stream: TStream; const Image: TImageData;
  303. Offset: Integer = 0; RowLength: Integer = 0);
  304. procedure WriteRawImageToMemory(Data: Pointer; DataSize: Integer; const Image: TImageData;
  305. Offset: Integer = 0; RowLength: Integer = 0);
  306. procedure WriteRawImageRect(Data: Pointer; Left, Top, Width, Height: Integer;
  307. const Image: TImageData; Offset: Integer = 0; RowLength: Integer = 0);
  308. { Convenience/helper Functions }
  309. procedure ResizeImageToFit(const SrcImage: TImageData; FitWidth, FitHeight: Integer;
  310. Filter: TResizeFilter; var DestImage: TImageData);
  311. { Color functions }
  312. { Constructs TColor24Rec color.}
  313. function Color24(R, G, B: Byte): TColor24Rec; {$IFDEF USE_INLINE}inline;{$ENDIF}
  314. { Constructs TColor32Rec color.}
  315. function Color32(A, R, G, B: Byte): TColor32Rec; {$IFDEF USE_INLINE}inline;{$ENDIF}
  316. { Constructs TColor48Rec color.}
  317. function Color48(R, G, B: Word): TColor48Rec; {$IFDEF USE_INLINE}inline;{$ENDIF}
  318. { Constructs TColor64Rec color.}
  319. function Color64(A, R, G, B: Word): TColor64Rec; {$IFDEF USE_INLINE}inline;{$ENDIF}
  320. { Constructs TColorFPRec color.}
  321. function ColorFP(A, R, G, B: Single): TColorFPRec; {$IFDEF USE_INLINE}inline;{$ENDIF}
  322. { Constructs TColorHFRec color.}
  323. function ColorHF(A, R, G, B: THalfFloat): TColorHFRec; {$IFDEF USE_INLINE}inline;{$ENDIF}
  324. { Convenience function for getting alpha component of TColor32.}
  325. function GetAlphaValue(Color32: TColor32): Byte; {$IFDEF USE_INLINE}inline;{$ENDIF}
  326. { Convenience function for getting red component of TColor32.}
  327. function GetRedValue(Color32: TColor32): Byte; {$IFDEF USE_INLINE}inline;{$ENDIF}
  328. { Convenience function for getting green component of TColor32.}
  329. function GetGreenValue(Color32: TColor32): Byte; {$IFDEF USE_INLINE}inline;{$ENDIF}
  330. { Convenience function for getting blue component of TColor32.}
  331. function GetBlueValue(Color32: TColor32): Byte; {$IFDEF USE_INLINE}inline;{$ENDIF}
  332. { ------------------------------------------------------------------------
  333. Other Imaging Stuff
  334. ------------------------------------------------------------------------}
  335. type
  336. { Set of TImageFormat enum.}
  337. TImageFormats = set of TImageFormat;
  338. { Record containg set of IO functions internaly used by image loaders/savers.}
  339. TIOFunctions = record
  340. Open: TOpenProc;
  341. Close: TCloseProc;
  342. Eof: TEofProc;
  343. Seek: TSeekProc;
  344. Tell: TTellProc;
  345. Read: TReadProc;
  346. Write: TWriteProc;
  347. end;
  348. PIOFunctions = ^TIOFunctions;
  349. type
  350. TFileFormatFeature = (
  351. ffLoad,
  352. ffSave,
  353. ffMultiImage,
  354. ffReadOnSave,
  355. ffProgress,
  356. ffReadScanlines);
  357. TFileFormatFeatures = set of TFileFormatFeature;
  358. TMetadata = class;
  359. { Base class for various image file format loaders/savers which
  360. descend from this class. If you want to add support for new image file
  361. format the best way is probably to look at TImageFileFormat descendants'
  362. implementations that are already part of Imaging.}
  363. {$TYPEINFO ON}
  364. TImageFileFormat = class
  365. private
  366. FExtensions: TStringList;
  367. FMasks: TStringList;
  368. function GetCanLoad: Boolean;
  369. function GetCanSave: Boolean;
  370. function GetIsMultiImageFormat: Boolean;
  371. { Does various checks and actions before LoadData method is called.}
  372. function PrepareLoad(Handle: TImagingHandle; var Images: TDynImageDataArray;
  373. OnlyFirstFrame: Boolean): Boolean;
  374. { Processes some actions according to result of LoadData.}
  375. function PostLoadCheck(var Images: TDynImageDataArray; LoadResult: Boolean): Boolean;
  376. { Helper function to be called in SaveData methods of descendants (ensures proper
  377. index and sets FFirstIdx and FLastIdx for multi-images).}
  378. function PrepareSave(Handle: TImagingHandle; const Images: TDynImageDataArray;
  379. var Index: LongInt): Boolean;
  380. { Returns file open mode used for saving images. Depends on defined Features.}
  381. function GetSaveOpenMode: TOpenMode;
  382. protected
  383. FName: string;
  384. FFeatures: TFileFormatFeatures;
  385. FSupportedFormats: TImageFormats;
  386. FFirstIdx, FLastIdx: LongInt;
  387. FMetadata: TMetadata;
  388. { Descendants must override this method and define file format name and
  389. capabilities.}
  390. procedure Define; virtual;
  391. { Defines filename masks for this image file format. AMasks should be
  392. in format '*.ext1,*.ext2,umajo.*'.}
  393. procedure AddMasks(const AMasks: string);
  394. function GetFormatInfo(Format: TImageFormat): TImageFormatInfo;
  395. { Returns set of TImageData formats that can be saved in this file format
  396. without need for conversion.}
  397. function GetSupportedFormats: TImageFormats; virtual;
  398. { Method which must be overrided in descendants if they' are be capable
  399. of loading images. Images are already freed and length is set to zero
  400. whenever this method gets called. Also Handle is assured to be valid
  401. and contains data that passed TestFormat method's check.}
  402. function LoadData(Handle: TImagingHandle; var Images: TDynImageDataArray;
  403. OnlyFirstFrame: Boolean): Boolean; virtual;
  404. { Method which must be overriden in descendants if they are be capable
  405. of saving images. Images are checked to have length >0 and
  406. that they contain valid images. For single-image file formats
  407. Index contain valid index to Images array (to image which should be saved).
  408. Multi-image formats should use FFirstIdx and FLastIdx fields to
  409. to get all images that are to be saved.}
  410. function SaveData(Handle: TImagingHandle; const Images: TDynImageDataArray;
  411. Index: LongInt): Boolean; virtual;
  412. { This method is called internaly by MakeCompatible when input image
  413. is in format not supported by this file format. Image is clone of
  414. MakeCompatible's input and Info is its extended format info.}
  415. procedure ConvertToSupported(var Image: TImageData;
  416. const Info: TImageFormatInfo); virtual;
  417. { Returns True if given image is supported for saving by this file format.
  418. Most file formats don't need to override this method. It checks
  419. (in this base class) if Image's format is in SupportedFromats set.
  420. But you may override it if you want further checks
  421. (proper widht and height for example).}
  422. function IsSupported(const Image: TImageData): Boolean; virtual;
  423. public
  424. constructor Create(AMetadata: TMetadata = nil); virtual;
  425. destructor Destroy; override;
  426. { Loads images from file source.}
  427. function LoadFromFile(const FileName: string; var Images: TDynImageDataArray;
  428. OnlyFirstLevel: Boolean = False): Boolean;
  429. { Loads images from stream source.}
  430. function LoadFromStream(Stream: TStream; var Images: TDynImageDataArray;
  431. OnlyFirstLevel: Boolean = False): Boolean;
  432. { Loads images from memory source.}
  433. function LoadFromMemory(Data: Pointer; Size: LongInt;
  434. var Images: TDynImageDataArray; OnlyFirstLevel: Boolean = False): Boolean;
  435. { Saves images to file. If format supports only single level images and
  436. there are multiple images to be saved, they are saved as sequence of
  437. independent images (for example SaveToFile saves sequence of
  438. files img000.jpg, img001.jpg ....).}
  439. function SaveToFile(const FileName: string; const Images: TDynImageDataArray;
  440. OnlyFirstLevel: Boolean = False): Boolean;
  441. { Saves images to stream. If format supports only single level images and
  442. there are multiple images to be saved, they are saved as sequence of
  443. independent images.}
  444. function SaveToStream(Stream: TStream; const Images: TDynImageDataArray;
  445. OnlyFirstLevel: Boolean = False): Boolean;
  446. { Saves images to memory. If format supports only single level images and
  447. there are multiple images to be saved, they are saved as sequence of
  448. independent images. Data must be already allocated and their size passed
  449. as Size parameter, number of written bytes is then returned in the same
  450. parameter.}
  451. function SaveToMemory(Data: Pointer; var Size: LongInt;
  452. const Images: TDynImageDataArray; OnlyFirstLevel: Boolean = False): Boolean;
  453. { Makes Image compatible with this file format (that means it is in one
  454. of data formats in Supported formats set). If input is already
  455. in supported format then Compatible just use value from input
  456. (Compatible := Image) so must not free it after you are done with it
  457. (image bits pointer points to input image's bits).
  458. If input is not in supported format then it is cloned to Compatible
  459. and concerted to one of supported formats (which one dependeds on
  460. this file format). If image is cloned MustBeFreed is set to True
  461. to indicated that you must free Compatible after you are done with it.}
  462. function MakeCompatible(const Image: TImageData; var Compatible: TImageData;
  463. out MustBeFreed: Boolean): Boolean;
  464. { Returns True if data located in source identified by Handle
  465. represent valid image in current format.}
  466. function TestFormat(Handle: TImagingHandle): Boolean; virtual;
  467. { Resturns True if the given FileName matches filter for this file format.
  468. For most formats it just checks filename extensions.
  469. It uses filename masks in from Masks property so it can recognize
  470. filenames like this 'umajoXXXumajo.j0j' if one of themasks is
  471. 'umajo*umajo.j?j'.}
  472. function TestFileName(const FileName: string): Boolean;
  473. { Descendants use this method to check if their options (registered with
  474. constant Ids for SetOption/GetOption interface or accessible as properties
  475. of descendants) have valid values and make necessary changes.}
  476. procedure CheckOptionsValidity; virtual;
  477. { Description of this format.}
  478. property Name: string read FName;
  479. { Indicates whether images in this format can be loaded.}
  480. property CanLoad: Boolean read GetCanLoad;
  481. { Indicates whether images in this format can be saved.}
  482. property CanSave: Boolean read GetCanSave;
  483. { Indicates whether images in this format can contain multiple image levels.}
  484. property IsMultiImageFormat: Boolean read GetIsMultiImageFormat;
  485. { List of filename extensions for this format.}
  486. property Extensions: TStringList read FExtensions;
  487. { List of filename masks that are used to associate filenames
  488. with TImageFileFormat descendants. Typical mask looks like
  489. '*.bmp' or 'texture.*' (supports file formats which use filename instead
  490. of extension to identify image files).}
  491. property Masks: TStringList read FMasks;
  492. { Set of TImageFormats supported by saving functions of this format. Images
  493. can be saved only in one those formats.}
  494. property SupportedFormats: TImageFormats read GetSupportedFormats;
  495. end;
  496. {$TYPEINFO OFF}
  497. { Class reference for TImageFileFormat class}
  498. TImageFileFormatClass = class of TImageFileFormat;
  499. { Physical resolution unit.}
  500. TResolutionUnit = (
  501. ruSizeInMicroMeters, // value is pixel size in micrometers
  502. ruDpi, // value is pixels/dots per inch
  503. ruDpm, // value is pixels/dots per meter
  504. ruDpcm // value is pixels/dots per centimeter
  505. );
  506. { Class for storage of single metadata item.}
  507. TMetadataItem = class
  508. public
  509. Id: string;
  510. ImageIndex: Integer;
  511. Value: Variant;
  512. end;
  513. { Metadata manager class.}
  514. TMetadata = class
  515. private
  516. FLoadMetaItems: TStringList;
  517. FSaveMetaItems: TStringList;
  518. procedure AddMetaToList(List: TStringList; const Id: string; const Value: Variant; ImageIndex: Integer);
  519. procedure ClearMetaList(List: TStringList);
  520. function GetMetaById(const Id: string): Variant;
  521. function GetMetaByIdMulti(const Id: string; ImageIndex: Integer): Variant;
  522. function GetMetaCount: Integer;
  523. function GetMetaByIdx(Index: Integer): TMetadataItem;
  524. function GetSaveMetaById(const Id: string): Variant;
  525. function GetSaveMetaByIdMulti(const Id: string; ImageIndex: Integer): Variant;
  526. procedure TranslateUnits(ResolutionUnit: TResolutionUnit; var XRes, YRes: Single);
  527. public
  528. constructor Create;
  529. destructor Destroy; override;
  530. procedure SetMetaItem(const Id: string; const Value: Variant; ImageIndex: Integer = 0);
  531. procedure SetMetaItemForSaving(const Id: string; const Value: Variant; ImageIndex: Integer = 0);
  532. function HasMetaItem(const Id: string; ImageIndex: Integer = 0): Boolean;
  533. function HasMetaItemForSaving(const Id: string; ImageIndex: Integer = 0): Boolean;
  534. procedure ClearMetaItems;
  535. procedure ClearMetaItemsForSaving;
  536. function GetMetaItemName(const Id: string; ImageIndex: Integer): string;
  537. { Copies loaded meta items to items-for-save stack. Use this when you want to
  538. save metadata that have been just loaded (e.g. resaving image in
  539. different file format but keeping the metadata).}
  540. procedure CopyLoadedMetaItemsForSaving;
  541. function GetPhysicalPixelSize(ResUnit: TResolutionUnit; var XSize,
  542. YSize: Single; MetaForSave: Boolean = False; ImageIndex: Integer = 0): Boolean;
  543. procedure SetPhysicalPixelSize(ResUnit: TResolutionUnit; XSize, YSize: Single;
  544. MetaForSave: Boolean = False; ImageIndex: Integer = 0);
  545. property MetaItems[const Id: string]: Variant read GetMetaById;
  546. property MetaItemsMulti[const Id: string; ImageIndex: Integer]: Variant read GetMetaByIdMulti;
  547. { Number of loaded metadata items.}
  548. property MetaItemCount: Integer read GetMetaCount;
  549. property MetaItemsByIdx[Index: Integer]: TMetadataItem read GetMetaByIdx;
  550. property MetaItemsForSaving[const Id: string]: Variant read GetSaveMetaById;
  551. property MetaItemsForSavingMulti[const Id: string; ImageIndex: Integer]: Variant read GetSaveMetaByIdMulti;
  552. end;
  553. const
  554. { Metadata item id constants }
  555. { Physical size of one pixel in micrometers. Type of value is Float.}
  556. SMetaPhysicalPixelSizeX = 'PhysicalPixelSizeX';
  557. SMetaPhysicalPixelSizeY = 'PhysicalPixelSizeY';
  558. { Delay for frame of animation (how long it should stay visible) in milliseconds.
  559. Type of value is Integer.}
  560. SMetaFrameDelay = 'FrameDelay';
  561. { Number of times animation should be looped (0 = infinite looping). Type is Int. }
  562. SMetaAnimationLoops = 'AnimationLoops';
  563. { Gamma correction value. Type is Float.}
  564. SMetaGamma = 'Gamma';
  565. { Exposure value for HDR etc. Type is Float.}
  566. SMetaExposure = 'Exposure';
  567. { EXIF image metadata raw blob.}
  568. SMetaExifBlob = 'ExifBlob';
  569. { XMP image metadata raw blob.}
  570. SMetaXmpBlob = 'XmpBlob';
  571. { IPTC image metadata raw blob.}
  572. SMetaIptcBlob = 'IptcBlob';
  573. var
  574. GlobalMetadata: TMetadata;
  575. { Returns symbolic name of given format.}
  576. function GetFormatName(Format: TImageFormat): string;
  577. { Returns string with information about given Image.}
  578. function ImageToStr(const Image: TImageData): string;
  579. { Returns Imaging version string in format 'Major.Minor'.}
  580. function GetVersionStr: string;
  581. { If Condition is True then TruePart is retured, otherwise FalsePart is returned.}
  582. function IffFormat(Condition: Boolean; const TruePart, FalsePart: TImageFormat): TImageFormat;
  583. { Registers new option so it can be used by SetOption and GetOption functions.
  584. Returns True if registration was succesful - that is Id is valid and is
  585. not already taken by another option.}
  586. function RegisterOption(OptionId: LongInt; Variable: PLongInt): Boolean;
  587. { Registers new image loader/saver so it can be used by LoadFrom/SaveTo
  588. functions.}
  589. procedure RegisterImageFileFormat(AClass: TImageFileFormatClass);
  590. { Returns image format loader/saver according to given extension
  591. or nil if not found.}
  592. function FindImageFileFormatByExt(const Ext: string): TImageFileFormat;
  593. { Returns image format loader/saver according to given filename
  594. or nil if not found.}
  595. function FindImageFileFormatByName(const FileName: string): TImageFileFormat;
  596. { Returns image format loader/saver based on its class
  597. or nil if not found or not registered.}
  598. function FindImageFileFormatByClass(AClass: TImageFileFormatClass): TImageFileFormat;
  599. { Returns number of registered image file format loaders/saver.}
  600. function GetFileFormatCount: LongInt;
  601. { Returns image file format loader/saver at given index. Index must be
  602. in range [0..GetFileFormatCount - 1] otherwise nil is returned.}
  603. function GetFileFormatAtIndex(Index: LongInt): TImageFileFormat;
  604. { Returns filter string for usage with open and save picture dialogs
  605. which contains all registered image file formats.
  606. Set OpenFileFilter to True if you want filter for open dialog
  607. and to False if you want save dialog filter (formats that cannot save to files
  608. are not added then).
  609. For open dialog filter for all known graphic files
  610. (like All(*.jpg;*.png;....) is added too at the first index.}
  611. function GetImageFileFormatsFilter(OpenFileFilter: Boolean): string;
  612. { Returns file extension (without dot) of image format selected
  613. by given filter index. Used filter string is defined by GetImageFileFormatsFilter
  614. function. This function can be used with save dialogs (with filters created
  615. by GetImageFileFormatsFilter) to get the extension of file format selected
  616. in dialog quickly. Index is in range 1..N (as FilterIndex property
  617. of TOpenDialog/TSaveDialog)}
  618. function GetFilterIndexExtension(Index: LongInt; OpenFileFilter: Boolean): string;
  619. { Returns filter index of image file format of file specified by FileName. Used filter
  620. string is defined by GetImageFileFormatsFilter function.
  621. Returned index is in range 1..N (as FilterIndex property of TOpenDialog/TSaveDialog)}
  622. function GetFileNameFilterIndex(const FileName: string; OpenFileFilter: Boolean): LongInt;
  623. { Returns current IO functions.}
  624. function GetIO: TIOFunctions;
  625. { Raises EImagingError with given message.}
  626. procedure RaiseImaging(const Msg: string; const Args: array of const); overload;
  627. procedure RaiseImaging(const Msg: string); overload; {$IFDEF USE_INLINE}inline;{$ENDIF}
  628. const
  629. SImagingLibTitle = 'Vampyre Imaging Library';
  630. implementation
  631. uses
  632. {$IFNDEF DONT_LINK_FILE_FORMATS}
  633. {$IFNDEF DONT_LINK_BITMAP}
  634. ImagingBitmap,
  635. {$ENDIF}
  636. {$IFNDEF DONT_LINK_JPEG}
  637. ImagingJpeg,
  638. {$ENDIF}
  639. {$IF not Defined(DONT_LINK_PNG) or not Defined(DONT_LINK_MNG) or not Defined(DONT_LINK_JNG)}
  640. ImagingNetworkGraphics,
  641. {$IFEND}
  642. {$IFNDEF DONT_LINK_GIF}
  643. ImagingGif,
  644. {$ENDIF}
  645. {$IFNDEF DONT_LINK_DDS}
  646. ImagingDds,
  647. {$ENDIF}
  648. {$IFNDEF DONT_LINK_TARGA}
  649. ImagingTarga,
  650. {$ENDIF}
  651. {$IFNDEF DONT_LINK_PNM}
  652. ImagingPortableMaps,
  653. {$ENDIF}
  654. {$IFNDEF DONT_LINK_RADHDR}
  655. ImagingRadiance,
  656. {$ENDIF}
  657. {$IFNDEF DONT_LINK_EXTRAS}
  658. ImagingExtras,
  659. {$ENDIF}
  660. {$ENDIF}
  661. //ImagingDebug,
  662. ImagingFormats, ImagingUtility, ImagingIO, Variants;
  663. resourcestring
  664. SExceptMsg = 'Exception Message';
  665. SAllFilter = 'All Images';
  666. SUnknownFormat = 'Unknown and unsupported format';
  667. SErrorFreeImage = 'Error while freeing image. %s';
  668. SErrorCloneImage = 'Error while cloning image. %s';
  669. SErrorFlipImage = 'Error while flipping image. %s';
  670. SErrorMirrorImage = 'Error while mirroring image. %s';
  671. SErrorResizeImage = 'Error while resizing image. %s';
  672. SErrorSwapImage = 'Error while swapping channels of image. %s';
  673. SFileFormatCanNotLoad = 'Image Format "%s" does not support loading images.';
  674. SFileFormatCanNotSave = 'Image Format "%s" does not support saving images.';
  675. SErrorNewImage = 'Error while creating image data with params: Width=%d ' +
  676. 'Height=%d Format=%s.';
  677. SErrorConvertImage = 'Error while converting image to format "%s". %s';
  678. SImageInfo = 'Image @%p info: Width = %dpx, Height = %dpx, ' +
  679. 'Format = %s, Size = %.0n %s, Bits @%p, Palette @%p.';
  680. SImageInfoInvalid = 'Access violation encountered when getting info on ' +
  681. 'image at address %p.';
  682. SFileNotValid = 'File "%s" is not valid image in "%s" format.';
  683. SStreamNotValid = 'Stream %p does not contain valid image in "%s" format.';
  684. SMemoryNotValid = 'Memory %p (%d Bytes) does not contain valid image ' +
  685. 'in "%s" format.';
  686. SErrorLoadingFile = 'Error while loading images from file "%s" (file format: %s).';
  687. SErrorLoadingStream = 'Error while loading images from stream %p (file format: %s).';
  688. SErrorLoadingMemory = 'Error while loading images from memory %p (%d Bytes) (file format: %s).';
  689. SErrorSavingFile = 'Error while saving images to file "%s" (file format: %s).';
  690. SErrorSavingStream = 'Error while saving images to stream %p (file format: %s).';
  691. SErrorSavingMemory = 'Error while saving images to memory %p (%d Bytes) (file format: %s).';
  692. SErrorFindColor = 'Error while finding color in palette @%p with %d entries.';
  693. SErrorGrayscalePalette = 'Error while filling grayscale palette @%p with %d entries.';
  694. SErrorCustomPalette = 'Error while filling custom palette @%p with %d entries.';
  695. SErrorSwapPalette = 'Error while swapping channels of palette @%p with %d entries.';
  696. SErrorReduceColors = 'Error while reducing number of colors of image to %d. %s';
  697. SErrorGenerateMipMaps = 'Error while generating %d mipmap levels for image %s';
  698. SImagesNotValid = 'One or more images are not valid.';
  699. SErrorCopyRect = 'Error while copying rect from image %s to image %s.';
  700. SErrorMapImage = 'Error while mapping image %s to palette.';
  701. SErrorFillRect = 'Error while filling rectangle X:%d Y:%d W:%d H:%d in image %s';
  702. SErrorSplitImage = 'Error while splitting image %s to %dx%d sized chunks.';
  703. SErrorMakePaletteForImages = 'Error while making %d color palette for %d images.';
  704. SErrorNewPalette = 'Error while creating new palette with %d entries';
  705. SErrorFreePalette = 'Error while freeing palette @%p';
  706. SErrorCopyPalette = 'Error while copying %d entries from palette @%p to @%p';
  707. SErrorReplaceColor = 'Error while replacing colors in rectangle X:%d Y:%d W:%d H:%d of image %s';
  708. SErrorRotateImage = 'Error while rotating image %s by %.2n degrees';
  709. SErrorStretchRect = 'Error while stretching rect from image %s to image %s.';
  710. SErrorEmptyStream = 'Input stream has no data. Check Position property.';
  711. SErrorInvalidInputImage = 'Invalid input image.';
  712. SErrorBadImage = 'Bad image detected.';
  713. const
  714. // Initial size of array with options information
  715. InitialOptions = 256;
  716. // Max depth of the option stack
  717. OptionStackDepth = 8;
  718. // Do not change the default format now, its too late
  719. DefaultImageFormat: TImageFormat = ifA8R8G8B8;
  720. // Format used to create metadata IDs for frames loaded form multiimages.
  721. SMetaIdForSubImage = '%s/%d';
  722. type
  723. TOptionArray = array of PLongInt;
  724. TOptionValueArray = array of LongInt;
  725. TOptionStack = class(TObject)
  726. private
  727. FStack: array[0..OptionStackDepth - 1] of TOptionValueArray;
  728. FPosition: LongInt;
  729. public
  730. constructor Create;
  731. destructor Destroy; override;
  732. function Push: Boolean;
  733. function Pop: Boolean;
  734. end;
  735. var
  736. // Currently set IO functions
  737. IO: TIOFunctions;
  738. // List with all registered TImageFileFormat classes
  739. ImageFileFormats: TList = nil;
  740. // Aarray with registered options (pointers to their values)
  741. Options: TOptionArray = nil;
  742. // Array containing addional infomation about every image format
  743. ImageFormatInfos: TImageFormatInfoArray;
  744. // Stack used by PushOptions/PopOtions functions
  745. OptionStack: TOptionStack = nil;
  746. var
  747. // Variable for ImagingColorReduction option
  748. ColorReductionMask: LongInt = $FF;
  749. // Variable for ImagingLoadOverrideFormat option
  750. LoadOverrideFormat: TImageFormat = ifUnknown;
  751. // Variable for ImagingSaveOverrideFormat option
  752. SaveOverrideFormat: TImageFormat = ifUnknown;
  753. // Variable for ImagingSaveOverrideFormat option
  754. MipMapFilter: TSamplingFilter = sfLinear;
  755. // Variable for ImagingBinaryTreshold option
  756. BinaryTreshold: Integer = 128;
  757. { Exceptions }
  758. constructor EImagingBadImage.Create;
  759. begin
  760. inherited Create(SErrorBadImage);
  761. end;
  762. { Internal unit functions }
  763. { Modifies option value to be in the allowed range. Works only
  764. for options registered in this unit.}
  765. function CheckOptionValue(OptionId, Value: LongInt): LongInt; forward;
  766. { Sets IO functions to file IO.}
  767. procedure SetFileIO; forward;
  768. { Sets IO functions to stream IO.}
  769. procedure SetStreamIO; forward;
  770. { Sets IO functions to memory IO.}
  771. procedure SetMemoryIO; forward;
  772. { Inits image format infos array.}
  773. procedure InitImageFormats; forward;
  774. { Freew image format infos array.}
  775. procedure FreeImageFileFormats; forward;
  776. { Creates options array and stack.}
  777. procedure InitOptions; forward;
  778. { Frees options array and stack.}
  779. procedure FreeOptions; forward;
  780. function UpdateExceptMessage(E: Exception; const MsgToPrepend: string; const Args: array of const): Exception;
  781. begin
  782. Result := E;
  783. E.Message := Format(MsgToPrepend, Args) + ' ' + SExceptMsg + ': ' + E.Message
  784. end;
  785. { ------------------------------------------------------------------------
  786. Low Level Interface Functions
  787. ------------------------------------------------------------------------}
  788. { General Functions }
  789. procedure InitImage(var Image: TImageData);
  790. begin
  791. FillChar(Image, SizeOf(Image), 0);
  792. end;
  793. function NewImage(Width, Height: LongInt; Format: TImageFormat; var Image:
  794. TImageData): Boolean;
  795. var
  796. FInfo: PImageFormatInfo;
  797. begin
  798. Assert((Width > 0) and (Height >0));
  799. Assert(IsImageFormatValid(Format));
  800. Result := False;
  801. FreeImage(Image);
  802. try
  803. Image.Width := Width;
  804. Image.Height := Height;
  805. // Select default data format if selected
  806. if (Format = ifDefault) then
  807. Image.Format := DefaultImageFormat
  808. else
  809. Image.Format := Format;
  810. // Get extended format info
  811. FInfo := ImageFormatInfos[Image.Format];
  812. if FInfo = nil then
  813. begin
  814. InitImage(Image);
  815. Exit;
  816. end;
  817. // Check image dimensions and calculate its size in bytes
  818. FInfo.CheckDimensions(FInfo.Format, Image.Width, Image.Height);
  819. Image.Size := FInfo.GetPixelsSize(FInfo.Format, Image.Width, Image.Height);
  820. if Image.Size = 0 then
  821. begin
  822. InitImage(Image);
  823. Exit;
  824. end;
  825. // Image bits are allocated and set to zeroes
  826. GetMem(Image.Bits, Image.Size);
  827. FillChar(Image.Bits^, Image.Size, 0);
  828. // Palette is allocated and set to zeroes
  829. if FInfo.PaletteEntries > 0 then
  830. begin
  831. GetMem(Image.Palette, FInfo.PaletteEntries * SizeOf(TColor32Rec));
  832. FillChar(Image.Palette^, FInfo.PaletteEntries * SizeOf(TColor32Rec), 0);
  833. end;
  834. Result := TestImage(Image);
  835. except
  836. on E: Exception do
  837. begin
  838. FreeMem(Image.Bits);
  839. FreeMem(Image.Palette);
  840. InitImage(Image);
  841. raise UpdateExceptMessage(E, SErrorNewImage, [Width, Height, GetFormatName(Format)]);
  842. end;
  843. end;
  844. end;
  845. function TestImage(const Image: TImageData): Boolean;
  846. begin
  847. try
  848. Result := (LongInt(Image.Format) >= LongInt(Low(TImageFormat))) and
  849. (LongInt(Image.Format) <= LongInt(High(TImageFormat))) and
  850. (ImageFormatInfos[Image.Format] <> nil) and
  851. (Assigned(ImageFormatInfos[Image.Format].GetPixelsSize) and
  852. (ImageFormatInfos[Image.Format].GetPixelsSize(Image.Format,
  853. Image.Width, Image.Height) = Image.Size));
  854. except
  855. // Possible int overflows or other errors
  856. Result := False;
  857. end;
  858. end;
  859. procedure FreeImage(var Image: TImageData);
  860. begin
  861. try
  862. if TestImage(Image) then
  863. begin
  864. FreeMemNil(Image.Bits);
  865. FreeMemNil(Image.Palette);
  866. end;
  867. InitImage(Image);
  868. except
  869. raise UpdateExceptMessage(GetExceptObject, SErrorFreeImage, [ImageToStr(Image)]);
  870. end;
  871. end;
  872. procedure FreeImagesInArray(var Images: TDynImageDataArray);
  873. var
  874. I: LongInt;
  875. begin
  876. if Length(Images) > 0 then
  877. begin
  878. for I := 0 to Length(Images) - 1 do
  879. FreeImage(Images[I]);
  880. SetLength(Images, 0);
  881. end;
  882. end;
  883. function TestImagesInArray(const Images: TDynImageDataArray): Boolean;
  884. var
  885. I: LongInt;
  886. begin
  887. if Length(Images) > 0 then
  888. begin
  889. Result := True;
  890. for I := 0 to Length(Images) - 1 do
  891. begin
  892. Result := Result and TestImage(Images[I]);
  893. if not Result then
  894. Break;
  895. end;
  896. end
  897. else
  898. Result := False;
  899. end;
  900. function DetermineFileFormat(const FileName: string): string;
  901. var
  902. I: LongInt;
  903. Fmt: TImageFileFormat;
  904. Handle: TImagingHandle;
  905. begin
  906. Assert(FileName <> '');
  907. Result := '';
  908. SetFileIO;
  909. Handle := IO.Open(PChar(FileName), omReadOnly);
  910. try
  911. // First file format according to FileName and test if the data in
  912. // file is really in that format
  913. for I := 0 to ImageFileFormats.Count - 1 do
  914. begin
  915. Fmt := TImageFileFormat(ImageFileFormats[I]);
  916. if Fmt.TestFileName(FileName) and Fmt.TestFormat(Handle) then
  917. begin
  918. Result := Fmt.Extensions[0];
  919. Exit;
  920. end;
  921. end;
  922. // No file format was found with filename search so try data-based search
  923. for I := 0 to ImageFileFormats.Count - 1 do
  924. begin
  925. Fmt := TImageFileFormat(ImageFileFormats[I]);
  926. if Fmt.TestFormat(Handle) then
  927. begin
  928. Result := Fmt.Extensions[0];
  929. Exit;
  930. end;
  931. end;
  932. finally
  933. IO.Close(Handle);
  934. end;
  935. end;
  936. function DetermineStreamFormat(Stream: TStream): string;
  937. var
  938. I: LongInt;
  939. Fmt: TImageFileFormat;
  940. Handle: TImagingHandle;
  941. begin
  942. Assert(Stream <> nil);
  943. Result := '';
  944. SetStreamIO;
  945. Handle := IO.Open(Pointer(Stream), omReadOnly);
  946. try
  947. for I := 0 to ImageFileFormats.Count - 1 do
  948. begin
  949. Fmt := TImageFileFormat(ImageFileFormats[I]);
  950. if Fmt.TestFormat(Handle) then
  951. begin
  952. Result := Fmt.Extensions[0];
  953. Exit;
  954. end;
  955. end;
  956. finally
  957. IO.Close(Handle);
  958. end;
  959. end;
  960. function DetermineMemoryFormat(Data: Pointer; Size: LongInt): string;
  961. var
  962. I: LongInt;
  963. Fmt: TImageFileFormat;
  964. Handle: TImagingHandle;
  965. IORec: TMemoryIORec;
  966. begin
  967. Assert((Data <> nil) and (Size > 0));
  968. Result := '';
  969. SetMemoryIO;
  970. IORec.Data := Data;
  971. IORec.Position := 0;
  972. IORec.Size := Size;
  973. Handle := IO.Open(@IORec, omReadOnly);
  974. try
  975. for I := 0 to ImageFileFormats.Count - 1 do
  976. begin
  977. Fmt := TImageFileFormat(ImageFileFormats[I]);
  978. if Fmt.TestFormat(Handle) then
  979. begin
  980. Result := Fmt.Extensions[0];
  981. Exit;
  982. end;
  983. end;
  984. finally
  985. IO.Close(Handle);
  986. end;
  987. end;
  988. function IsFileFormatSupported(const FileName: string): Boolean;
  989. begin
  990. Result := FindImageFileFormatByName(FileName) <> nil;
  991. end;
  992. function EnumFileFormats(var Index: LongInt; var Name, DefaultExt, Masks: string;
  993. var CanSaveImages, IsMultiImageFormat: Boolean): Boolean;
  994. var
  995. FileFmt: TImageFileFormat;
  996. begin
  997. FileFmt := GetFileFormatAtIndex(Index);
  998. Result := FileFmt <> nil;
  999. if Result then
  1000. begin
  1001. Name := FileFmt.Name;
  1002. DefaultExt := FileFmt.Extensions[0];
  1003. Masks := FileFmt.Masks.DelimitedText;
  1004. CanSaveImages := FileFmt.CanSave;
  1005. IsMultiImageFormat := FileFmt.IsMultiImageFormat;
  1006. Inc(Index);
  1007. end
  1008. else
  1009. begin
  1010. Name := '';
  1011. DefaultExt := '';
  1012. Masks := '';
  1013. CanSaveImages := False;
  1014. IsMultiImageFormat := False;
  1015. end;
  1016. end;
  1017. { Loading Functions }
  1018. function LoadImageFromFile(const FileName: string; var Image: TImageData):
  1019. Boolean;
  1020. var
  1021. Format: TImageFileFormat;
  1022. IArray: TDynImageDataArray;
  1023. I: LongInt;
  1024. begin
  1025. Assert(FileName <> '');
  1026. Result := False;
  1027. Format := FindImageFileFormatByExt(DetermineFileFormat(FileName));
  1028. if Format <> nil then
  1029. begin
  1030. FreeImage(Image);
  1031. Result := Format.LoadFromFile(FileName, IArray, True);
  1032. if Result and (Length(IArray) > 0) then
  1033. begin
  1034. Image := IArray[0];
  1035. for I := 1 to Length(IArray) - 1 do
  1036. FreeImage(IArray[I]);
  1037. end
  1038. else
  1039. Result := False;
  1040. end;
  1041. end;
  1042. function LoadImageFromStream(Stream: TStream; var Image: TImageData): Boolean;
  1043. var
  1044. Format: TImageFileFormat;
  1045. IArray: TDynImageDataArray;
  1046. I: LongInt;
  1047. begin
  1048. Assert(Stream <> nil);
  1049. if Stream.Size - Stream.Position = 0 then
  1050. RaiseImaging(SErrorEmptyStream, []);
  1051. Result := False;
  1052. Format := FindImageFileFormatByExt(DetermineStreamFormat(Stream));
  1053. if Format <> nil then
  1054. begin
  1055. FreeImage(Image);
  1056. Result := Format.LoadFromStream(Stream, IArray, True);
  1057. if Result and (Length(IArray) > 0) then
  1058. begin
  1059. Image := IArray[0];
  1060. for I := 1 to Length(IArray) - 1 do
  1061. FreeImage(IArray[I]);
  1062. end
  1063. else
  1064. Result := False;
  1065. end;
  1066. end;
  1067. function LoadImageFromMemory(Data: Pointer; Size: LongInt; var Image: TImageData): Boolean;
  1068. var
  1069. Format: TImageFileFormat;
  1070. IArray: TDynImageDataArray;
  1071. I: LongInt;
  1072. begin
  1073. Assert((Data <> nil) and (Size > 0));
  1074. Result := False;
  1075. Format := FindImageFileFormatByExt(DetermineMemoryFormat(Data, Size));
  1076. if Format <> nil then
  1077. begin
  1078. FreeImage(Image);
  1079. Result := Format.LoadFromMemory(Data, Size, IArray, True);
  1080. if Result and (Length(IArray) > 0) then
  1081. begin
  1082. Image := IArray[0];
  1083. for I := 1 to Length(IArray) - 1 do
  1084. FreeImage(IArray[I]);
  1085. end
  1086. else
  1087. Result := False;
  1088. end;
  1089. end;
  1090. function LoadMultiImageFromFile(const FileName: string; var Images:
  1091. TDynImageDataArray): Boolean;
  1092. var
  1093. Format: TImageFileFormat;
  1094. begin
  1095. Assert(FileName <> '');
  1096. Result := False;
  1097. Format := FindImageFileFormatByExt(DetermineFileFormat(FileName));
  1098. if Format <> nil then
  1099. begin
  1100. FreeImagesInArray(Images);
  1101. Result := Format.LoadFromFile(FileName, Images);
  1102. end;
  1103. end;
  1104. function LoadMultiImageFromStream(Stream: TStream; var Images: TDynImageDataArray): Boolean;
  1105. var
  1106. Format: TImageFileFormat;
  1107. begin
  1108. Assert(Stream <> nil);
  1109. if Stream.Size - Stream.Position = 0 then
  1110. RaiseImaging(SErrorEmptyStream, []);
  1111. Result := False;
  1112. Format := FindImageFileFormatByExt(DetermineStreamFormat(Stream));
  1113. if Format <> nil then
  1114. begin
  1115. FreeImagesInArray(Images);
  1116. Result := Format.LoadFromStream(Stream, Images);
  1117. end;
  1118. end;
  1119. function LoadMultiImageFromMemory(Data: Pointer; Size: LongInt;
  1120. var Images: TDynImageDataArray): Boolean;
  1121. var
  1122. Format: TImageFileFormat;
  1123. begin
  1124. Assert((Data <> nil) and (Size > 0));
  1125. Result := False;
  1126. Format := FindImageFileFormatByExt(DetermineMemoryFormat(Data, Size));
  1127. if Format <> nil then
  1128. begin
  1129. FreeImagesInArray(Images);
  1130. Result := Format.LoadFromMemory(Data, Size, Images);
  1131. end;
  1132. end;
  1133. { Saving Functions }
  1134. function SaveImageToFile(const FileName: string; const Image: TImageData): Boolean;
  1135. var
  1136. Format: TImageFileFormat;
  1137. IArray: TDynImageDataArray;
  1138. begin
  1139. Assert(FileName <> '');
  1140. Result := False;
  1141. Format := FindImageFileFormatByName(FileName);
  1142. if Format <> nil then
  1143. begin
  1144. SetLength(IArray, 1);
  1145. IArray[0] := Image;
  1146. Result := Format.SaveToFile(FileName, IArray, True);
  1147. end;
  1148. end;
  1149. function SaveImageToStream(const Ext: string; Stream: TStream;
  1150. const Image: TImageData): Boolean;
  1151. var
  1152. Format: TImageFileFormat;
  1153. IArray: TDynImageDataArray;
  1154. begin
  1155. Assert((Ext <> '') and (Stream <> nil));
  1156. Result := False;
  1157. Format := FindImageFileFormatByExt(Ext);
  1158. if Format <> nil then
  1159. begin
  1160. SetLength(IArray, 1);
  1161. IArray[0] := Image;
  1162. Result := Format.SaveToStream(Stream, IArray, True);
  1163. end;
  1164. end;
  1165. function SaveImageToMemory(const Ext: string; Data: Pointer; var Size: LongInt;
  1166. const Image: TImageData): Boolean;
  1167. var
  1168. Format: TImageFileFormat;
  1169. IArray: TDynImageDataArray;
  1170. begin
  1171. Assert((Ext <> '') and (Data <> nil) and (Size > 0));
  1172. Result := False;
  1173. Format := FindImageFileFormatByExt(Ext);
  1174. if Format <> nil then
  1175. begin
  1176. SetLength(IArray, 1);
  1177. IArray[0] := Image;
  1178. Result := Format.SaveToMemory(Data, Size, IArray, True);
  1179. end;
  1180. end;
  1181. function SaveMultiImageToFile(const FileName: string;
  1182. const Images: TDynImageDataArray): Boolean;
  1183. var
  1184. Format: TImageFileFormat;
  1185. begin
  1186. Assert(FileName <> '');
  1187. Result := False;
  1188. Format := FindImageFileFormatByName(FileName);
  1189. if Format <> nil then
  1190. Result := Format.SaveToFile(FileName, Images);
  1191. end;
  1192. function SaveMultiImageToStream(const Ext: string; Stream: TStream;
  1193. const Images: TDynImageDataArray): Boolean;
  1194. var
  1195. Format: TImageFileFormat;
  1196. begin
  1197. Assert((Ext <> '') and (Stream <> nil));
  1198. Result := False;
  1199. Format := FindImageFileFormatByExt(Ext);
  1200. if Format <> nil then
  1201. Result := Format.SaveToStream(Stream, Images);
  1202. end;
  1203. function SaveMultiImageToMemory(const Ext: string; Data: Pointer;
  1204. var Size: LongInt; const Images: TDynImageDataArray): Boolean;
  1205. var
  1206. Format: TImageFileFormat;
  1207. begin
  1208. Assert((Ext <> '') and (Data <> nil) and (Size > 0));
  1209. Result := False;
  1210. Format := FindImageFileFormatByExt(Ext);
  1211. if Format <> nil then
  1212. Result := Format.SaveToMemory(Data, Size, Images);
  1213. end;
  1214. { Manipulation Functions }
  1215. function CloneImage(const Image: TImageData; var Clone: TImageData): Boolean;
  1216. var
  1217. Info: PImageFormatInfo;
  1218. begin
  1219. Result := False;
  1220. if TestImage(Image) then
  1221. try
  1222. if TestImage(Clone) and (Image.Bits <> Clone.Bits) then
  1223. FreeImage(Clone)
  1224. else
  1225. InitImage(Clone);
  1226. Info := ImageFormatInfos[Image.Format];
  1227. Clone.Width := Image.Width;
  1228. Clone.Height := Image.Height;
  1229. Clone.Format := Image.Format;
  1230. Clone.Size := Image.Size;
  1231. if Info.PaletteEntries > 0 then
  1232. begin
  1233. GetMem(Clone.Palette, Info.PaletteEntries * SizeOf(TColor32Rec));
  1234. Move(Image.Palette^, Clone.Palette^, Info.PaletteEntries *
  1235. SizeOf(TColor32Rec));
  1236. end;
  1237. GetMem(Clone.Bits, Clone.Size);
  1238. Move(Image.Bits^, Clone.Bits^, Clone.Size);
  1239. Result := True;
  1240. except
  1241. raise UpdateExceptMessage(GetExceptObject, SErrorCloneImage, [ImageToStr(Image)]);
  1242. end;
  1243. end;
  1244. function ConvertImage(var Image: TImageData; DestFormat: TImageFormat): Boolean;
  1245. var
  1246. NewData: Pointer;
  1247. NewPal: PPalette32;
  1248. NewSize, NumPixels: LongInt;
  1249. SrcInfo, DstInfo: PImageFormatInfo;
  1250. begin
  1251. Assert(IsImageFormatValid(DestFormat));
  1252. Result := False;
  1253. if TestImage(Image) then
  1254. with Image do
  1255. try
  1256. // If default format is set we use DefaultImageFormat
  1257. if DestFormat = ifDefault then
  1258. DestFormat := DefaultImageFormat;
  1259. SrcInfo := ImageFormatInfos[Format];
  1260. DstInfo := ImageFormatInfos[DestFormat];
  1261. if SrcInfo = DstInfo then
  1262. begin
  1263. // There is nothing to convert - src is alredy in dest format
  1264. Result := True;
  1265. Exit;
  1266. end;
  1267. // Exit Src or Dest format is invalid
  1268. if (SrcInfo = nil) or (DstInfo = nil) then Exit;
  1269. // If dest format is just src with swapped channels we call
  1270. // SwapChannels instead
  1271. if (SrcInfo.RBSwapFormat = DestFormat) and
  1272. (DstInfo.RBSwapFormat = SrcInfo.Format) then
  1273. begin
  1274. Result := SwapChannels(Image, ChannelRed, ChannelBlue);
  1275. Image.Format := SrcInfo.RBSwapFormat;
  1276. Exit;
  1277. end;
  1278. if (not SrcInfo.IsSpecial) and (not DstInfo.IsSpecial) then
  1279. begin
  1280. NumPixels := Width * Height;
  1281. NewSize := NumPixels * DstInfo.BytesPerPixel;
  1282. GetMem(NewData, NewSize);
  1283. FillChar(NewData^, NewSize, 0);
  1284. GetMem(NewPal, DstInfo.PaletteEntries * SizeOf(TColor32Rec));
  1285. FillChar(NewPal^, DstInfo.PaletteEntries * SizeOf(TColor32Rec), 0);
  1286. if SrcInfo.IsIndexed then
  1287. begin
  1288. // Source: indexed format
  1289. if DstInfo.IsIndexed then
  1290. IndexToIndex(NumPixels, Bits, NewData, SrcInfo, DstInfo, Palette, NewPal)
  1291. else if DstInfo.HasGrayChannel then
  1292. IndexToGray(NumPixels, Bits, NewData, SrcInfo, DstInfo, Palette)
  1293. else if DstInfo.IsFloatingPoint then
  1294. IndexToFloat(NumPixels, Bits, NewData, SrcInfo, DstInfo, Palette)
  1295. else
  1296. IndexToChannel(NumPixels, Bits, NewData, SrcInfo, DstInfo, Palette);
  1297. end
  1298. else if SrcInfo.HasGrayChannel then
  1299. begin
  1300. // Source: grayscale format
  1301. if DstInfo.IsIndexed then
  1302. GrayToIndex(NumPixels, Bits, NewData, SrcInfo, DstInfo, NewPal)
  1303. else if DstInfo.HasGrayChannel then
  1304. GrayToGray(NumPixels, Bits, NewData, SrcInfo, DstInfo)
  1305. else if DstInfo.IsFloatingPoint then
  1306. GrayToFloat(NumPixels, Bits, NewData, SrcInfo, DstInfo)
  1307. else
  1308. GrayToChannel(NumPixels, Bits, NewData, SrcInfo, DstInfo);
  1309. end
  1310. else if SrcInfo.IsFloatingPoint then
  1311. begin
  1312. // Source: floating point format
  1313. if DstInfo.IsIndexed then
  1314. FloatToIndex(NumPixels, Bits, NewData, SrcInfo, DstInfo, NewPal)
  1315. else if DstInfo.HasGrayChannel then
  1316. FloatToGray(NumPixels, Bits, NewData, SrcInfo, DstInfo)
  1317. else if DstInfo.IsFloatingPoint then
  1318. FloatToFloat(NumPixels, Bits, NewData, SrcInfo, DstInfo)
  1319. else
  1320. FloatToChannel(NumPixels, Bits, NewData, SrcInfo, DstInfo);
  1321. end
  1322. else
  1323. begin
  1324. // Source: standard multi channel image
  1325. if DstInfo.IsIndexed then
  1326. ChannelToIndex(NumPixels, Bits, NewData, SrcInfo, DstInfo, NewPal)
  1327. else if DstInfo.HasGrayChannel then
  1328. ChannelToGray(NumPixels, Bits, NewData, SrcInfo, DstInfo)
  1329. else if DstInfo.IsFloatingPoint then
  1330. ChannelToFloat(NumPixels, Bits, NewData, SrcInfo, DstInfo)
  1331. else
  1332. ChannelToChannel(NumPixels, Bits, NewData, SrcInfo, DstInfo);
  1333. end;
  1334. FreeMemNil(Bits);
  1335. FreeMemNil(Palette);
  1336. Format := DestFormat;
  1337. Bits := NewData;
  1338. Size := NewSize;
  1339. Palette := NewPal;
  1340. end
  1341. else
  1342. ConvertSpecial(Image, SrcInfo, DstInfo);
  1343. Assert(SrcInfo.Format <> Image.Format);
  1344. Result := True;
  1345. except
  1346. raise UpdateExceptMessage(GetExceptObject, SErrorConvertImage, [GetFormatName(DestFormat), ImageToStr(Image)]);
  1347. end;
  1348. end;
  1349. function FlipImage(var Image: TImageData): Boolean;
  1350. var
  1351. P1, P2, Buff: Pointer;
  1352. WidthBytes, I: LongInt;
  1353. OldFmt: TImageFormat;
  1354. begin
  1355. Result := False;
  1356. OldFmt := Image.Format;
  1357. if TestImage(Image) then
  1358. with Image do
  1359. try
  1360. if ImageFormatInfos[OldFmt].IsSpecial then
  1361. ConvertImage(Image, ifDefault);
  1362. WidthBytes := Width * ImageFormatInfos[Format].BytesPerPixel;
  1363. GetMem(Buff, WidthBytes);
  1364. try
  1365. // Swap all scanlines of image
  1366. for I := 0 to Height div 2 - 1 do
  1367. begin
  1368. P1 := @PByteArray(Bits)[I * WidthBytes];
  1369. P2 := @PByteArray(Bits)[(Height - I - 1) * WidthBytes];
  1370. Move(P1^, Buff^, WidthBytes);
  1371. Move(P2^, P1^, WidthBytes);
  1372. Move(Buff^, P2^, WidthBytes);
  1373. end;
  1374. finally
  1375. FreeMemNil(Buff);
  1376. end;
  1377. if OldFmt <> Format then
  1378. ConvertImage(Image, OldFmt);
  1379. Result := True;
  1380. except
  1381. RaiseImaging(SErrorFlipImage, [ImageToStr(Image)]);
  1382. end;
  1383. end;
  1384. function MirrorImage(var Image: TImageData): Boolean;
  1385. var
  1386. Scanline: PByte;
  1387. Buff: TColorFPRec;
  1388. Bpp, Y, X, WidthDiv2, WidthBytes, XLeft, XRight: LongInt;
  1389. OldFmt: TImageFormat;
  1390. begin
  1391. Result := False;
  1392. OldFmt := Image.Format;
  1393. if TestImage(Image) then
  1394. with Image do
  1395. try
  1396. if ImageFormatInfos[OldFmt].IsSpecial then
  1397. ConvertImage(Image, ifDefault);
  1398. Bpp := ImageFormatInfos[Format].BytesPerPixel;
  1399. WidthDiv2 := Width div 2;
  1400. WidthBytes := Width * Bpp;
  1401. // Mirror all pixels on each scanline of image
  1402. for Y := 0 to Height - 1 do
  1403. begin
  1404. Scanline := @PByteArray(Bits)[Y * WidthBytes];
  1405. XLeft := 0;
  1406. XRight := (Width - 1) * Bpp;
  1407. for X := 0 to WidthDiv2 - 1 do
  1408. begin
  1409. CopyPixel(@PByteArray(Scanline)[XLeft], @Buff, Bpp);
  1410. CopyPixel(@PByteArray(Scanline)[XRight],
  1411. @PByteArray(Scanline)[XLeft], Bpp);
  1412. CopyPixel(@Buff, @PByteArray(Scanline)[XRight], Bpp);
  1413. Inc(XLeft, Bpp);
  1414. Dec(XRight, Bpp);
  1415. end;
  1416. end;
  1417. if OldFmt <> Format then
  1418. ConvertImage(Image, OldFmt);
  1419. Result := True;
  1420. except
  1421. RaiseImaging(SErrorMirrorImage, [ImageToStr(Image)]);
  1422. end;
  1423. end;
  1424. function ResizeImage(var Image: TImageData; NewWidth, NewHeight: LongInt;
  1425. Filter: TResizeFilter): Boolean;
  1426. var
  1427. WorkImage: TImageData;
  1428. begin
  1429. Assert((NewWidth > 0) and (NewHeight > 0), 'New width or height is zero.');
  1430. Result := False;
  1431. if TestImage(Image) and ((Image.Width <> NewWidth) or (Image.Height <> NewHeight)) then
  1432. try
  1433. InitImage(WorkImage);
  1434. // Create new image with desired dimensions
  1435. NewImage(NewWidth, NewHeight, Image.Format, WorkImage);
  1436. // Stretch pixels from old image to new one
  1437. StretchRect(Image, 0, 0, Image.Width, Image.Height,
  1438. WorkImage, 0, 0, WorkImage.Width, WorkImage.Height, Filter);
  1439. // Free old image and assign new image to it
  1440. FreeMemNil(Image.Bits);
  1441. if Image.Palette <> nil then
  1442. begin
  1443. FreeMem(WorkImage.Palette);
  1444. WorkImage.Palette := Image.Palette;
  1445. end;
  1446. Image := WorkImage;
  1447. Result := True;
  1448. except
  1449. raise UpdateExceptMessage(GetExceptObject, SErrorResizeImage, [ImageToStr(Image)]);
  1450. end;
  1451. end;
  1452. function SwapChannels(var Image: TImageData; SrcChannel, DstChannel: LongInt): Boolean;
  1453. var
  1454. I, NumPixels: LongInt;
  1455. Info: PImageFormatInfo;
  1456. Swap, Alpha: Word;
  1457. Data: PByte;
  1458. Pix64: TColor64Rec;
  1459. PixF: TColorFPRec;
  1460. SwapF: Single;
  1461. begin
  1462. Assert((SrcChannel in [0..3]) and (DstChannel in [0..3]));
  1463. Result := False;
  1464. if TestImage(Image) and (SrcChannel <> DstChannel) then
  1465. with Image do
  1466. try
  1467. NumPixels := Width * Height;
  1468. Info := ImageFormatInfos[Format];
  1469. Data := Bits;
  1470. if (Info.Format = ifR8G8B8) or ((Info.Format = ifA8R8G8B8) and
  1471. (SrcChannel <> ChannelAlpha) and (DstChannel <> ChannelAlpha)) then
  1472. begin
  1473. // Swap channels of most common formats R8G8B8 and A8R8G8B8 (no alpha)
  1474. for I := 0 to NumPixels - 1 do
  1475. with PColor24Rec(Data)^ do
  1476. begin
  1477. Swap := Channels[SrcChannel];
  1478. Channels[SrcChannel] := Channels[DstChannel];
  1479. Channels[DstChannel] := Swap;
  1480. Inc(Data, Info.BytesPerPixel);
  1481. end;
  1482. end
  1483. else if Info.IsIndexed then
  1484. begin
  1485. // Swap palette channels of indexed images
  1486. SwapChannelsOfPalette(Palette, Info.PaletteEntries, SrcChannel, DstChannel)
  1487. end
  1488. else if Info.IsFloatingPoint then
  1489. begin
  1490. // Swap channels of floating point images
  1491. for I := 0 to NumPixels - 1 do
  1492. begin
  1493. FloatGetSrcPixel(Data, Info, PixF);
  1494. with PixF do
  1495. begin
  1496. SwapF := Channels[SrcChannel];
  1497. Channels[SrcChannel] := Channels[DstChannel];
  1498. Channels[DstChannel] := SwapF;
  1499. end;
  1500. FloatSetDstPixel(Data, Info, PixF);
  1501. Inc(Data, Info.BytesPerPixel);
  1502. end;
  1503. end
  1504. else if Info.IsSpecial then
  1505. begin
  1506. // Swap channels of special format images
  1507. ConvertImage(Image, ifDefault);
  1508. SwapChannels(Image, SrcChannel, DstChannel);
  1509. ConvertImage(Image, Info.Format);
  1510. end
  1511. else if Info.HasGrayChannel and Info.HasAlphaChannel and
  1512. ((SrcChannel = ChannelAlpha) or (DstChannel = ChannelAlpha)) then
  1513. begin
  1514. for I := 0 to NumPixels - 1 do
  1515. begin
  1516. // If we have grayscale image with alpha and alpha is channel
  1517. // to be swapped, we swap it. No other alternative for gray images,
  1518. // just alpha and something
  1519. GrayGetSrcPixel(Data, Info, Pix64, Alpha);
  1520. Swap := Alpha;
  1521. Alpha := Pix64.A;
  1522. Pix64.A := Swap;
  1523. GraySetDstPixel(Data, Info, Pix64, Alpha);
  1524. Inc(Data, Info.BytesPerPixel);
  1525. end;
  1526. end
  1527. else
  1528. begin
  1529. // Then do general swap on other channel image formats
  1530. for I := 0 to NumPixels - 1 do
  1531. begin
  1532. ChannelGetSrcPixel(Data, Info, Pix64);
  1533. with Pix64 do
  1534. begin
  1535. Swap := Channels[SrcChannel];
  1536. Channels[SrcChannel] := Channels[DstChannel];
  1537. Channels[DstChannel] := Swap;
  1538. end;
  1539. ChannelSetDstPixel(Data, Info, Pix64);
  1540. Inc(Data, Info.BytesPerPixel);
  1541. end;
  1542. end;
  1543. Result := True;
  1544. except
  1545. RaiseImaging(SErrorSwapImage, [ImageToStr(Image)]);
  1546. end;
  1547. end;
  1548. function ReduceColors(var Image: TImageData; MaxColors: LongInt): Boolean;
  1549. var
  1550. TmpInfo: TImageFormatInfo;
  1551. Data, Index: PWord;
  1552. I, NumPixels: LongInt;
  1553. Pal: PPalette32;
  1554. Col:PColor32Rec;
  1555. OldFmt: TImageFormat;
  1556. begin
  1557. Result := False;
  1558. if TestImage(Image) then
  1559. with Image do
  1560. try
  1561. // First create temp image info and allocate output bits and palette
  1562. MaxColors := ClampInt(MaxColors, 2, High(Word));
  1563. OldFmt := Format;
  1564. FillChar(TmpInfo, SizeOf(TmpInfo), 0);
  1565. TmpInfo.PaletteEntries := MaxColors;
  1566. TmpInfo.BytesPerPixel := 2;
  1567. NumPixels := Width * Height;
  1568. GetMem(Data, NumPixels * TmpInfo.BytesPerPixel);
  1569. GetMem(Pal, MaxColors * SizeOf(TColor32Rec));
  1570. ConvertImage(Image, ifA8R8G8B8);
  1571. // We use median cut algorithm to create reduced palette and to
  1572. // fill Data with indices to this palette
  1573. ReduceColorsMedianCut(NumPixels, Bits, PByte(Data),
  1574. ImageFormatInfos[Format], @TmpInfo, MaxColors, ColorReductionMask, Pal);
  1575. Col := Bits;
  1576. Index := Data;
  1577. // Then we write reduced colors to the input image
  1578. for I := 0 to NumPixels - 1 do
  1579. begin
  1580. Col.Color := Pal[Index^].Color;
  1581. Inc(Col);
  1582. Inc(Index);
  1583. end;
  1584. FreeMemNil(Data);
  1585. FreeMemNil(Pal);
  1586. // And convert it to its original format
  1587. ConvertImage(Image, OldFmt);
  1588. Result := True;
  1589. except
  1590. RaiseImaging(SErrorReduceColors, [MaxColors, ImageToStr(Image)]);
  1591. end;
  1592. end;
  1593. function GenerateMipMaps(const Image: TImageData; Levels: LongInt;
  1594. var MipMaps: TDynImageDataArray): Boolean;
  1595. var
  1596. Width, Height, I, Count: LongInt;
  1597. Info: TImageFormatInfo;
  1598. CompatibleCopy: TImageData;
  1599. begin
  1600. Result := False;
  1601. if TestImage(Image) then
  1602. try
  1603. Width := Image.Width;
  1604. Height := Image.Height;
  1605. // We compute number of possible mipmap levels and if
  1606. // the given levels are invalid or zero we use this value
  1607. Count := GetNumMipMapLevels(Width, Height);
  1608. if (Levels <= 0) or (Levels > Count) then
  1609. Levels := Count;
  1610. // If we have special format image we create copy to allow pixel access.
  1611. // This is also done in FillMipMapLevel which is called for each level
  1612. // but then the main big image would be converted to compatible
  1613. // for every level.
  1614. GetImageFormatInfo(Image.Format, Info);
  1615. if Info.IsSpecial then
  1616. begin
  1617. InitImage(CompatibleCopy);
  1618. CloneImage(Image, CompatibleCopy);
  1619. ConvertImage(CompatibleCopy, ifDefault);
  1620. end
  1621. else
  1622. CompatibleCopy := Image;
  1623. FreeImagesInArray(MipMaps);
  1624. SetLength(MipMaps, Levels);
  1625. CloneImage(Image, MipMaps[0]);
  1626. for I := 1 to Levels - 1 do
  1627. begin
  1628. Width := Width shr 1;
  1629. Height := Height shr 1;
  1630. if Width < 1 then Width := 1;
  1631. if Height < 1 then Height := 1;
  1632. FillMipMapLevel(CompatibleCopy, Width, Height, MipMaps[I]);
  1633. end;
  1634. if CompatibleCopy.Format <> MipMaps[0].Format then
  1635. begin
  1636. // Must convert smaller levels to proper format
  1637. for I := 1 to High(MipMaps) do
  1638. ConvertImage(MipMaps[I], MipMaps[0].Format);
  1639. FreeImage(CompatibleCopy);
  1640. end;
  1641. Result := True;
  1642. except
  1643. RaiseImaging(SErrorGenerateMipMaps, [Levels, ImageToStr(Image)]);
  1644. end;
  1645. end;
  1646. function MapImageToPalette(var Image: TImageData; Pal: PPalette32;
  1647. Entries: LongInt): Boolean;
  1648. function FindNearestColor(Pal: PPalette32; Entries: LongInt; Col: TColor32Rec): LongInt;
  1649. var
  1650. I, MinDif, Dif: LongInt;
  1651. begin
  1652. Result := 0;
  1653. MinDif := 1020;
  1654. for I := 0 to Entries - 1 do
  1655. with Pal[I] do
  1656. begin
  1657. Dif := Abs(R - Col.R);
  1658. if Dif > MinDif then Continue;
  1659. Dif := Dif + Abs(G - Col.G);
  1660. if Dif > MinDif then Continue;
  1661. Dif := Dif + Abs(B - Col.B);
  1662. if Dif > MinDif then Continue;
  1663. Dif := Dif + Abs(A - Col.A);
  1664. if Dif < MinDif then
  1665. begin
  1666. MinDif := Dif;
  1667. Result := I;
  1668. end;
  1669. end;
  1670. end;
  1671. var
  1672. I, MaxEntries: LongInt;
  1673. PIndex: PByte;
  1674. PColor: PColor32Rec;
  1675. CloneARGB: TImageData;
  1676. Info: PImageFormatInfo;
  1677. begin
  1678. Assert((Entries >= 2) and (Entries <= 256));
  1679. Result := False;
  1680. if TestImage(Image) then
  1681. try
  1682. // We create clone of source image in A8R8G8B8 and
  1683. // then recreate source image in ifIndex8 format
  1684. // with palette taken from Pal parameter
  1685. InitImage(CloneARGB);
  1686. CloneImage(Image, CloneARGB);
  1687. ConvertImage(CloneARGB, ifA8R8G8B8);
  1688. FreeImage(Image);
  1689. NewImage(CloneARGB.Width, CloneARGB.Height, ifIndex8, Image);
  1690. Info := ImageFormatInfos[Image.Format];
  1691. MaxEntries := Min(Info.PaletteEntries, Entries);
  1692. Move(Pal^, Image.Palette^, MaxEntries * SizeOf(TColor32Rec));
  1693. PIndex := Image.Bits;
  1694. PColor := CloneARGB.Bits;
  1695. // For every pixel of ARGB clone we find closest color in
  1696. // given palette and assign its index to resulting image's pixel
  1697. // procedure used here is very slow but simple and memory usage friendly
  1698. // (contrary to other methods)
  1699. for I := 0 to Image.Width * Image.Height - 1 do
  1700. begin
  1701. PIndex^ := Byte(FindNearestColor(Image.Palette, MaxEntries, PColor^));
  1702. Inc(PIndex);
  1703. Inc(PColor);
  1704. end;
  1705. FreeImage(CloneARGB);
  1706. Result := True;
  1707. except
  1708. raise UpdateExceptMessage(GetExceptObject, SErrorMapImage, [ImageToStr(Image)]);
  1709. end;
  1710. end;
  1711. function SplitImage(var Image: TImageData; var Chunks: TDynImageDataArray;
  1712. ChunkWidth, ChunkHeight: LongInt; var XChunks, YChunks: LongInt;
  1713. PreserveSize: Boolean; Fill: Pointer): Boolean;
  1714. var
  1715. X, Y, XTrunc, YTrunc: LongInt;
  1716. NotOnEdge: Boolean;
  1717. Info: PImageFormatInfo;
  1718. OldFmt: TImageFormat;
  1719. begin
  1720. Assert((ChunkWidth > 0) and (ChunkHeight > 0));
  1721. Result := False;
  1722. OldFmt := Image.Format;
  1723. FreeImagesInArray(Chunks);
  1724. if TestImage(Image) then
  1725. try
  1726. Info := ImageFormatInfos[Image.Format];
  1727. if Info.IsSpecial then
  1728. ConvertImage(Image, ifDefault);
  1729. // We compute make sure that chunks are not larger than source image or negative
  1730. ChunkWidth := ClampInt(ChunkWidth, 0, Image.Width);
  1731. ChunkHeight := ClampInt(ChunkHeight, 0, Image.Height);
  1732. // Number of chunks along X and Y axes is computed
  1733. XChunks := Trunc(Ceil(Image.Width / ChunkWidth));
  1734. YChunks := Trunc(Ceil(Image.Height / ChunkHeight));
  1735. SetLength(Chunks, XChunks * YChunks);
  1736. // For every chunk we create new image and copy a portion of
  1737. // the source image to it. If chunk is on the edge of the source image
  1738. // we fill enpty space with Fill pixel data if PreserveSize is set or
  1739. // make the chunk smaller if it is not set
  1740. for Y := 0 to YChunks - 1 do
  1741. for X := 0 to XChunks - 1 do
  1742. begin
  1743. // Determine if current chunk is on the edge of original image
  1744. NotOnEdge := ((X < XChunks - 1) and (Y < YChunks - 1)) or
  1745. ((Image.Width mod ChunkWidth = 0) and (Image.Height mod ChunkHeight = 0));
  1746. if PreserveSize or NotOnEdge then
  1747. begin
  1748. // We should preserve chunk sizes or we are somewhere inside original image
  1749. NewImage(ChunkWidth, ChunkHeight, Image.Format, Chunks[Y * XChunks + X]);
  1750. if (not NotOnEdge) and (Fill <> nil) then
  1751. FillRect(Chunks[Y * XChunks + X], 0, 0, ChunkWidth, ChunkHeight, Fill);
  1752. CopyRect(Image, X * ChunkWidth, Y * ChunkHeight, ChunkWidth, ChunkHeight,
  1753. Chunks[Y * XChunks + X], 0, 0);
  1754. end
  1755. else
  1756. begin
  1757. // Create smaller edge chunk
  1758. XTrunc := Image.Width - X * ChunkWidth;
  1759. YTrunc := Image.Height - Y * ChunkHeight;
  1760. NewImage(XTrunc, YTrunc, Image.Format, Chunks[Y * XChunks + X]);
  1761. CopyRect(Image, X * ChunkWidth, Y * ChunkHeight, XTrunc, YTrunc,
  1762. Chunks[Y * XChunks + X], 0, 0);
  1763. end;
  1764. // If source image is in indexed format we copy its palette to chunk
  1765. if Info.IsIndexed then
  1766. begin
  1767. Move(Image.Palette^, Chunks[Y * XChunks + X].Palette^,
  1768. Info.PaletteEntries * SizeOf(TColor32Rec));
  1769. end;
  1770. end;
  1771. if OldFmt <> Image.Format then
  1772. begin
  1773. ConvertImage(Image, OldFmt);
  1774. for X := 0 to Length(Chunks) - 1 do
  1775. ConvertImage(Chunks[X], OldFmt);
  1776. end;
  1777. Result := True;
  1778. except
  1779. raise UpdateExceptMessage(GetExceptObject, SErrorSplitImage,
  1780. [ImageToStr(Image), ChunkWidth, ChunkHeight]);
  1781. end;
  1782. end;
  1783. function MakePaletteForImages(var Images: TDynImageDataArray; Pal: PPalette32;
  1784. MaxColors: LongInt; ConvertImages: Boolean): Boolean;
  1785. var
  1786. I: Integer;
  1787. SrcInfo, DstInfo: PImageFormatInfo;
  1788. Target, TempImage: TImageData;
  1789. DstFormat: TImageFormat;
  1790. begin
  1791. Assert((Pal <> nil) and (MaxColors > 0));
  1792. Result := False;
  1793. InitImage(TempImage);
  1794. if TestImagesInArray(Images) then
  1795. try
  1796. // Null the color histogram
  1797. ReduceColorsMedianCut(0, nil, nil, nil, nil, 0, 0, nil, [raCreateHistogram]);
  1798. for I := 0 to Length(Images) - 1 do
  1799. begin
  1800. SrcInfo := ImageFormatInfos[Images[I].Format];
  1801. if SrcInfo.IsIndexed or SrcInfo.IsSpecial then
  1802. begin
  1803. // create temp image in supported format for updating histogram
  1804. CloneImage(Images[I], TempImage);
  1805. ConvertImage(TempImage, ifA8R8G8B8);
  1806. SrcInfo := ImageFormatInfos[TempImage.Format];
  1807. end
  1808. else
  1809. TempImage := Images[I];
  1810. // Update histogram with colors of each input image
  1811. ReduceColorsMedianCut(TempImage.Width * TempImage.Height, TempImage.Bits,
  1812. nil, SrcInfo, nil, MaxColors, ColorReductionMask, nil, [raUpdateHistogram]);
  1813. if Images[I].Bits <> TempImage.Bits then
  1814. FreeImage(TempImage);
  1815. end;
  1816. // Construct reduced color map from the histogram
  1817. ReduceColorsMedianCut(0, nil, nil, nil, nil, MaxColors, ColorReductionMask,
  1818. Pal, [raMakeColorMap]);
  1819. if ConvertImages then
  1820. begin
  1821. DstFormat := ifIndex8;
  1822. DstInfo := ImageFormatInfos[DstFormat];
  1823. MaxColors := Min(DstInfo.PaletteEntries, MaxColors);
  1824. for I := 0 to Length(Images) - 1 do
  1825. begin
  1826. SrcInfo := ImageFormatInfos[Images[I].Format];
  1827. if SrcInfo.IsIndexed or SrcInfo.IsSpecial then
  1828. begin
  1829. // If source image is in format not supported by ReduceColorsMedianCut
  1830. // we convert it
  1831. ConvertImage(Images[I], ifA8R8G8B8);
  1832. SrcInfo := ImageFormatInfos[Images[I].Format];
  1833. end;
  1834. InitImage(Target);
  1835. NewImage(Images[I].Width, Images[I].Height, DstFormat, Target);
  1836. // We map each input image to reduced palette and replace
  1837. // image in array with mapped image
  1838. ReduceColorsMedianCut(Images[I].Width * Images[I].Height, Images[I].Bits,
  1839. Target.Bits, SrcInfo, DstInfo, MaxColors, 0, nil, [raMapImage]);
  1840. Move(Pal^, Target.Palette^, MaxColors * SizeOf(TColor32Rec));
  1841. FreeImage(Images[I]);
  1842. Images[I] := Target;
  1843. end;
  1844. end;
  1845. Result := True;
  1846. except
  1847. RaiseImaging(SErrorMakePaletteForImages, [MaxColors, Length(Images)]);
  1848. end;
  1849. end;
  1850. procedure RotateImage(var Image: TImageData; Angle: Single);
  1851. var
  1852. OldFmt: TImageFormat;
  1853. procedure XShear(var Src, Dst: TImageData; Row, Offset, Weight, Bpp: Integer);
  1854. var
  1855. I, J, XPos: Integer;
  1856. PixSrc, PixLeft, PixOldLeft: TColor32Rec;
  1857. LineDst: PByteArray;
  1858. SrcPtr: PColor32;
  1859. begin
  1860. SrcPtr := @PByteArray(Src.Bits)[Row * Src.Width * Bpp];
  1861. LineDst := @PByteArray(Dst.Bits)[Row * Dst.Width * Bpp];
  1862. PixOldLeft.Color := 0;
  1863. for I := 0 to Src.Width - 1 do
  1864. begin
  1865. CopyPixel(SrcPtr, @PixSrc, Bpp);
  1866. for J := 0 to Bpp - 1 do
  1867. PixLeft.Channels[J] := MulDiv(PixSrc.Channels[J], Weight, 256);
  1868. XPos := I + Offset;
  1869. if (XPos >= 0) and (XPos < Dst.Width) then
  1870. begin
  1871. for J := 0 to Bpp - 1 do
  1872. PixSrc.Channels[J] := ClampToByte(PixSrc.Channels[J] - (PixLeft.Channels[J] - PixOldLeft.Channels[J]));
  1873. CopyPixel(@PixSrc, @LineDst[XPos * Bpp], Bpp);
  1874. end;
  1875. PixOldLeft := PixLeft;
  1876. Inc(PByte(SrcPtr), Bpp);
  1877. end;
  1878. XPos := Src.Width + Offset;
  1879. if XPos < Dst.Width then
  1880. CopyPixel(@PixOldLeft, @LineDst[XPos * Bpp], Bpp);
  1881. end;
  1882. procedure YShear(var Src, Dst: TImageData; Col, Offset, Weight, Bpp: Integer);
  1883. var
  1884. I, J, YPos: Integer;
  1885. PixSrc, PixLeft, PixOldLeft: TColor32Rec;
  1886. SrcPtr: PByte;
  1887. begin
  1888. SrcPtr := @PByteArray(Src.Bits)[Col * Bpp];
  1889. PixOldLeft.Color := 0;
  1890. for I := 0 to Src.Height - 1 do
  1891. begin
  1892. CopyPixel(SrcPtr, @PixSrc, Bpp);
  1893. for J := 0 to Bpp - 1 do
  1894. PixLeft.Channels[J] := MulDiv(PixSrc.Channels[J], Weight, 256);
  1895. YPos := I + Offset;
  1896. if (YPos >= 0) and (YPos < Dst.Height) then
  1897. begin
  1898. for J := 0 to Bpp - 1 do
  1899. PixSrc.Channels[J] := ClampToByte(PixSrc.Channels[J] - (PixLeft.Channels[J] - PixOldLeft.Channels[J]));
  1900. CopyPixel(@PixSrc, @PByteArray(Dst.Bits)[(YPos * Dst.Width + Col) * Bpp], Bpp);
  1901. end;
  1902. PixOldLeft := PixLeft;
  1903. Inc(SrcPtr, Src.Width * Bpp);
  1904. end;
  1905. YPos := Src.Height + Offset;
  1906. if YPos < Dst.Height then
  1907. CopyPixel(@PixOldLeft, @PByteArray(Dst.Bits)[(YPos * Dst.Width + Col) * Bpp], Bpp);
  1908. end;
  1909. procedure Rotate45(var Image: TImageData; Angle: Single);
  1910. var
  1911. TempImage1, TempImage2: TImageData;
  1912. AngleRad, AngleTan, AngleSin, AngleCos, Shear: Single;
  1913. I, DstWidth, DstHeight, SrcWidth, SrcHeight, Bpp: Integer;
  1914. SrcFmt, TempFormat: TImageFormat;
  1915. Info: TImageFormatInfo;
  1916. begin
  1917. AngleRad := Angle * Pi / 180;
  1918. AngleSin := Sin(AngleRad);
  1919. AngleCos := Cos(AngleRad);
  1920. AngleTan := Sin(AngleRad / 2) / Cos(AngleRad / 2);
  1921. SrcWidth := Image.Width;
  1922. SrcHeight := Image.Height;
  1923. SrcFmt := Image.Format;
  1924. if not (SrcFmt in [ifR8G8B8..ifX8R8G8B8, ifGray8..ifGray32, ifA16Gray16]) then
  1925. ConvertImage(Image, ifA8R8G8B8);
  1926. TempFormat := Image.Format;
  1927. GetImageFormatInfo(TempFormat, Info);
  1928. Bpp := Info.BytesPerPixel;
  1929. // 1st shear (horizontal)
  1930. DstWidth := Trunc(SrcWidth + SrcHeight * Abs(AngleTan) + 0.5);
  1931. DstHeight := SrcHeight;
  1932. InitImage(TempImage1);
  1933. NewImage(DstWidth, DstHeight, TempFormat, TempImage1);
  1934. for I := 0 to DstHeight - 1 do
  1935. begin
  1936. if AngleTan >= 0 then
  1937. Shear := (I + 0.5) * AngleTan
  1938. else
  1939. Shear := (I - DstHeight + 0.5) * AngleTan;
  1940. XShear(Image, TempImage1, I, Floor(Shear), Trunc(255 * (Shear - Floor(Shear)) + 1), Bpp);
  1941. end;
  1942. // 2nd shear (vertical)
  1943. FreeImage(Image);
  1944. DstHeight := Trunc(SrcWidth * Abs(AngleSin) + SrcHeight * AngleCos + 0.5) + 1;
  1945. InitImage(TempImage2);
  1946. NewImage(DstWidth, DstHeight, TempFormat, TempImage2);
  1947. if AngleSin >= 0 then
  1948. Shear := (SrcWidth - 1) * AngleSin
  1949. else
  1950. Shear := (SrcWidth - DstWidth) * -AngleSin;
  1951. for I := 0 to DstWidth - 1 do
  1952. begin
  1953. YShear(TempImage1, TempImage2, I, Floor(Shear), Trunc(255 * (Shear - Floor(Shear)) + 1), Bpp);
  1954. Shear := Shear - AngleSin;
  1955. end;
  1956. // 3rd shear (horizontal)
  1957. FreeImage(TempImage1);
  1958. DstWidth := Trunc(SrcHeight * Abs(AngleSin) + SrcWidth * AngleCos + 0.5) + 1;
  1959. NewImage(DstWidth, DstHeight, TempFormat, Image);
  1960. if AngleSin >= 0 then
  1961. Shear := (SrcWidth - 1) * AngleSin * -AngleTan
  1962. else
  1963. Shear := ((SrcWidth - 1) * -AngleSin + (1 - DstHeight)) * AngleTan;
  1964. for I := 0 to DstHeight - 1 do
  1965. begin
  1966. XShear(TempImage2, Image, I, Floor(Shear), Trunc(255 * (Shear - Floor(Shear)) + 1), Bpp);
  1967. Shear := Shear + AngleTan;
  1968. end;
  1969. FreeImage(TempImage2);
  1970. if Image.Format <> SrcFmt then
  1971. ConvertImage(Image, SrcFmt);
  1972. end;
  1973. procedure RotateMul90(var Image: TImageData; Angle: Integer);
  1974. var
  1975. RotImage: TImageData;
  1976. X, Y, BytesPerPixel: Integer;
  1977. RotPix, Pix: PByte;
  1978. begin
  1979. InitImage(RotImage);
  1980. BytesPerPixel := ImageFormatInfos[Image.Format].BytesPerPixel;
  1981. if ((Angle = 90) or (Angle = 270)) and (Image.Width <> Image.Height) then
  1982. NewImage(Image.Height, Image.Width, Image.Format, RotImage)
  1983. else
  1984. NewImage(Image.Width, Image.Height, Image.Format, RotImage);
  1985. RotPix := RotImage.Bits;
  1986. case Angle of
  1987. 90:
  1988. begin
  1989. for Y := 0 to RotImage.Height - 1 do
  1990. begin
  1991. Pix := @PByteArray(Image.Bits)[(Image.Width - Y - 1) * BytesPerPixel];
  1992. for X := 0 to RotImage.Width - 1 do
  1993. begin
  1994. CopyPixel(Pix, RotPix, BytesPerPixel);
  1995. Inc(RotPix, BytesPerPixel);
  1996. Inc(Pix, Image.Width * BytesPerPixel);
  1997. end;
  1998. end;
  1999. end;
  2000. 180:
  2001. begin
  2002. Pix := @PByteArray(Image.Bits)[((Image.Height - 1) * Image.Width +
  2003. (Image.Width - 1)) * BytesPerPixel];
  2004. for Y := 0 to RotImage.Height - 1 do
  2005. for X := 0 to RotImage.Width - 1 do
  2006. begin
  2007. CopyPixel(Pix, RotPix, BytesPerPixel);
  2008. Inc(RotPix, BytesPerPixel);
  2009. Dec(Pix, BytesPerPixel);
  2010. end;
  2011. end;
  2012. 270:
  2013. begin
  2014. for Y := 0 to RotImage.Height - 1 do
  2015. begin
  2016. Pix := @PByteArray(Image.Bits)[((Image.Height - 1) * Image.Width + Y) * BytesPerPixel];
  2017. for X := 0 to RotImage.Width - 1 do
  2018. begin
  2019. CopyPixel(Pix, RotPix, BytesPerPixel);
  2020. Inc(RotPix, BytesPerPixel);
  2021. Dec(Pix, Image.Width * BytesPerPixel);
  2022. end;
  2023. end;
  2024. end;
  2025. end;
  2026. FreeMemNil(Image.Bits);
  2027. RotImage.Palette := Image.Palette;
  2028. Image := RotImage;
  2029. end;
  2030. begin
  2031. if TestImage(Image) then
  2032. try
  2033. while Angle >= 360 do
  2034. Angle := Angle - 360;
  2035. while Angle < 0 do
  2036. Angle := Angle + 360;
  2037. if (Angle = 0) or (Abs(Angle) = 360) then
  2038. Exit;
  2039. OldFmt := Image.Format;
  2040. if ImageFormatInfos[Image.Format].IsSpecial then
  2041. ConvertImage(Image, ifDefault);
  2042. if (Angle > 45) and (Angle <= 135) then
  2043. begin
  2044. RotateMul90(Image, 90);
  2045. Angle := Angle - 90;
  2046. end
  2047. else if (Angle > 135) and (Angle <= 225) then
  2048. begin
  2049. RotateMul90(Image, 180);
  2050. Angle := Angle - 180;
  2051. end
  2052. else if (Angle > 225) and (Angle <= 315) then
  2053. begin
  2054. RotateMul90(Image, 270);
  2055. Angle := Angle - 270;
  2056. end;
  2057. if Angle <> 0 then
  2058. Rotate45(Image, Angle);
  2059. if OldFmt <> Image.Format then
  2060. ConvertImage(Image, OldFmt);
  2061. except
  2062. raise UpdateExceptMessage(GetExceptObject, SErrorRotateImage, [ImageToStr(Image), Angle]);
  2063. end;
  2064. end;
  2065. { Drawing/Pixel functions }
  2066. function CopyRect(const SrcImage: TImageData; SrcX, SrcY, Width, Height: LongInt;
  2067. var DstImage: TImageData; DstX, DstY: LongInt): Boolean;
  2068. var
  2069. Info: PImageFormatInfo;
  2070. I, SrcWidthBytes, DstWidthBytes, MoveBytes: LongInt;
  2071. SrcPointer, DstPointer: PByte;
  2072. WorkImage: TImageData;
  2073. OldFormat: TImageFormat;
  2074. begin
  2075. Result := False;
  2076. OldFormat := ifUnknown;
  2077. if TestImage(SrcImage) and TestImage(DstImage) then
  2078. try
  2079. // Make sure we are still copying image to image, not invalid pointer to protected memory
  2080. ClipCopyBounds(SrcX, SrcY, Width, Height, DstX, DstY, SrcImage.Width, SrcImage.Height,
  2081. Rect(0, 0, DstImage.Width, DstImage.Height));
  2082. if (Width > 0) and (Height > 0) then
  2083. begin
  2084. Info := ImageFormatInfos[DstImage.Format];
  2085. if Info.IsSpecial then
  2086. begin
  2087. // If dest image is in special format we convert it to default
  2088. OldFormat := Info.Format;
  2089. ConvertImage(DstImage, ifDefault);
  2090. Info := ImageFormatInfos[DstImage.Format];
  2091. end;
  2092. if SrcImage.Format <> DstImage.Format then
  2093. begin
  2094. // If images are in different format source is converted to dest's format
  2095. InitImage(WorkImage);
  2096. CloneImage(SrcImage, WorkImage);
  2097. ConvertImage(WorkImage, DstImage.Format);
  2098. end
  2099. else
  2100. WorkImage := SrcImage;
  2101. MoveBytes := Width * Info.BytesPerPixel;
  2102. DstWidthBytes := DstImage.Width * Info.BytesPerPixel;
  2103. DstPointer := @PByteArray(DstImage.Bits)[DstY * DstWidthBytes +
  2104. DstX * Info.BytesPerPixel];
  2105. SrcWidthBytes := WorkImage.Width * Info.BytesPerPixel;
  2106. SrcPointer := @PByteArray(WorkImage.Bits)[SrcY * SrcWidthBytes +
  2107. SrcX * Info.BytesPerPixel];
  2108. for I := 0 to Height - 1 do
  2109. begin
  2110. Move(SrcPointer^, DstPointer^, MoveBytes);
  2111. Inc(SrcPointer, SrcWidthBytes);
  2112. Inc(DstPointer, DstWidthBytes);
  2113. end;
  2114. // If dest image was in special format we convert it back
  2115. if OldFormat <> ifUnknown then
  2116. ConvertImage(DstImage, OldFormat);
  2117. // Working image must be freed if it is not the same as source image
  2118. if WorkImage.Bits <> SrcImage.Bits then
  2119. FreeImage(WorkImage);
  2120. Result := True;
  2121. end;
  2122. except
  2123. RaiseImaging(SErrorCopyRect, [ImageToStr(SrcImage), ImageToStr(DstImage)]);
  2124. end;
  2125. end;
  2126. function FillRect(var Image: TImageData; X, Y, Width, Height: LongInt;
  2127. FillColor: Pointer): Boolean;
  2128. var
  2129. Info: PImageFormatInfo;
  2130. I, J, ImageWidthBytes, RectWidthBytes, Bpp: Longint;
  2131. LinePointer, PixPointer: PByte;
  2132. OldFmt: TImageFormat;
  2133. begin
  2134. Result := False;
  2135. if TestImage(Image) then
  2136. try
  2137. ClipRectBounds(X, Y, Width, Height, Rect(0, 0, Image.Width, Image.Height));
  2138. if (Width > 0) and (Height > 0) then
  2139. begin
  2140. OldFmt := Image.Format;
  2141. if ImageFormatInfos[OldFmt].IsSpecial then
  2142. ConvertImage(Image, ifDefault);
  2143. Info := ImageFormatInfos[Image.Format];
  2144. Bpp := Info.BytesPerPixel;
  2145. ImageWidthBytes := Image.Width * Bpp;
  2146. RectWidthBytes := Width * Bpp;
  2147. LinePointer := @PByteArray(Image.Bits)[Y * ImageWidthBytes + X * Bpp];
  2148. for I := 0 to Height - 1 do
  2149. begin
  2150. case Bpp of
  2151. 1: FillMemoryByte(LinePointer, RectWidthBytes, PByte(FillColor)^);
  2152. 2: FillMemoryWord(LinePointer, RectWidthBytes, PWord(FillColor)^);
  2153. 4: FillMemoryLongWord(LinePointer, RectWidthBytes, PLongWord(FillColor)^);
  2154. else
  2155. PixPointer := LinePointer;
  2156. for J := 0 to Width - 1 do
  2157. begin
  2158. CopyPixel(FillColor, PixPointer, Bpp);
  2159. Inc(PixPointer, Bpp);
  2160. end;
  2161. end;
  2162. Inc(LinePointer, ImageWidthBytes);
  2163. end;
  2164. if OldFmt <> Image.Format then
  2165. ConvertImage(Image, OldFmt);
  2166. end;
  2167. Result := True;
  2168. except
  2169. RaiseImaging(SErrorFillRect, [X, Y, Width, Height, ImageToStr(Image)]);
  2170. end;
  2171. end;
  2172. function ReplaceColor(var Image: TImageData; X, Y, Width, Height: LongInt;
  2173. OldColor, NewColor: Pointer): Boolean;
  2174. var
  2175. Info: PImageFormatInfo;
  2176. I, J, WidthBytes, Bpp: Longint;
  2177. LinePointer, PixPointer: PByte;
  2178. OldFmt: TImageFormat;
  2179. begin
  2180. Assert((OldColor <> nil) and (NewColor <> nil));
  2181. Result := False;
  2182. if TestImage(Image) then
  2183. try
  2184. ClipRectBounds(X, Y, Width, Height, Rect(0, 0, Image.Width, Image.Height));
  2185. if (Width > 0) and (Height > 0) then
  2186. begin
  2187. OldFmt := Image.Format;
  2188. if ImageFormatInfos[OldFmt].IsSpecial then
  2189. ConvertImage(Image, ifDefault);
  2190. Info := ImageFormatInfos[Image.Format];
  2191. Bpp := Info.BytesPerPixel;
  2192. WidthBytes := Image.Width * Bpp;
  2193. LinePointer := @PByteArray(Image.Bits)[Y * WidthBytes + X * Bpp];
  2194. for I := 0 to Height - 1 do
  2195. begin
  2196. PixPointer := LinePointer;
  2197. for J := 0 to Width - 1 do
  2198. begin
  2199. if ComparePixels(PixPointer, OldColor, Bpp) then
  2200. CopyPixel(NewColor, PixPointer, Bpp);
  2201. Inc(PixPointer, Bpp);
  2202. end;
  2203. Inc(LinePointer, WidthBytes);
  2204. end;
  2205. if OldFmt <> Image.Format then
  2206. ConvertImage(Image, OldFmt);
  2207. end;
  2208. Result := True;
  2209. except
  2210. RaiseImaging(SErrorReplaceColor, [X, Y, Width, Height, ImageToStr(Image)]);
  2211. end;
  2212. end;
  2213. function StretchRect(const SrcImage: TImageData; SrcX, SrcY, SrcWidth,
  2214. SrcHeight: LongInt; var DstImage: TImageData; DstX, DstY, DstWidth,
  2215. DstHeight: LongInt; Filter: TResizeFilter): Boolean;
  2216. var
  2217. Info: PImageFormatInfo;
  2218. WorkImage: TImageData;
  2219. OldFormat: TImageFormat;
  2220. Resampling: TSamplingFilter;
  2221. begin
  2222. Result := False;
  2223. OldFormat := ifUnknown;
  2224. if TestImage(SrcImage) and TestImage(DstImage) then
  2225. try
  2226. // Make sure we are still copying image to image, not invalid pointer to protected memory
  2227. ClipStretchBounds(SrcX, SrcY, SrcWidth, SrcHeight, DstX, DstY, DstWidth, DstHeight,
  2228. SrcImage.Width, SrcImage.Height, Rect(0, 0, DstImage.Width, DstImage.Height));
  2229. if (SrcWidth = DstWidth) and (SrcHeight = DstHeight) then
  2230. begin
  2231. // If source and dest rectangles have the same size call CopyRect
  2232. Result := CopyRect(SrcImage, SrcX, SrcY, SrcWidth, SrcHeight, DstImage, DstX, DstY);
  2233. end
  2234. else if (SrcWidth > 0) and (SrcHeight > 0) and (DstWidth > 0) and (DstHeight > 0) then
  2235. begin
  2236. // If source and dest rectangles don't have the same size we do stretch
  2237. Info := ImageFormatInfos[DstImage.Format];
  2238. if Info.IsSpecial then
  2239. begin
  2240. // If dest image is in special format we convert it to default
  2241. OldFormat := Info.Format;
  2242. ConvertImage(DstImage, ifDefault);
  2243. Info := ImageFormatInfos[DstImage.Format];
  2244. end;
  2245. if SrcImage.Format <> DstImage.Format then
  2246. begin
  2247. // If images are in different format source is converted to dest's format
  2248. InitImage(WorkImage);
  2249. CloneImage(SrcImage, WorkImage);
  2250. ConvertImage(WorkImage, DstImage.Format);
  2251. end
  2252. else
  2253. WorkImage := SrcImage;
  2254. // Only pixel resize is supported for indexed images
  2255. if Info.IsIndexed then
  2256. Filter := rfNearest;
  2257. if Filter = rfNearest then
  2258. begin
  2259. StretchNearest(WorkImage, SrcX, SrcY, SrcWidth, SrcHeight,
  2260. DstImage, DstX, DstY, DstWidth, DstHeight);
  2261. end
  2262. else
  2263. begin
  2264. Resampling := sfNearest;
  2265. case Filter of
  2266. rfBilinear: Resampling := sfLinear;
  2267. rfBicubic: Resampling := DefaultCubicFilter;
  2268. rfLanczos: Resampling := sfLanczos;
  2269. end;
  2270. StretchResample(WorkImage, SrcX, SrcY, SrcWidth, SrcHeight,
  2271. DstImage, DstX, DstY, DstWidth, DstHeight, Resampling);
  2272. end;
  2273. // If dest image was in special format we convert it back
  2274. if OldFormat <> ifUnknown then
  2275. ConvertImage(DstImage, OldFormat);
  2276. // Working image must be freed if it is not the same as source image
  2277. if WorkImage.Bits <> SrcImage.Bits then
  2278. FreeImage(WorkImage);
  2279. Result := True;
  2280. end;
  2281. except
  2282. RaiseImaging(SErrorStretchRect, [ImageToStr(SrcImage), ImageToStr(DstImage)]);
  2283. end;
  2284. end;
  2285. procedure GetPixelDirect(const Image: TImageData; X, Y: LongInt; Pixel: Pointer);
  2286. var
  2287. BytesPerPixel: LongInt;
  2288. begin
  2289. Assert(Pixel <> nil);
  2290. BytesPerPixel := ImageFormatInfos[Image.Format].BytesPerPixel;
  2291. CopyPixel(@PByteArray(Image.Bits)[(Y * Image.Width + X) * BytesPerPixel],
  2292. Pixel, BytesPerPixel);
  2293. end;
  2294. procedure SetPixelDirect(const Image: TImageData; X, Y: LongInt; Pixel: Pointer);
  2295. var
  2296. BytesPerPixel: LongInt;
  2297. begin
  2298. Assert(Pixel <> nil);
  2299. BytesPerPixel := ImageFormatInfos[Image.Format].BytesPerPixel;
  2300. CopyPixel(Pixel, @PByteArray(Image.Bits)[(Y * Image.Width + X) * BytesPerPixel],
  2301. BytesPerPixel);
  2302. end;
  2303. function GetPixel32(const Image: TImageData; X, Y: LongInt): TColor32Rec;
  2304. var
  2305. Info: PImageFormatInfo;
  2306. Data: PByte;
  2307. begin
  2308. Info := ImageFormatInfos[Image.Format];
  2309. Data := @PByteArray(Image.Bits)[(Y * Image.Width + X) * Info.BytesPerPixel];
  2310. Result := GetPixel32Generic(Data, Info, Image.Palette);
  2311. end;
  2312. procedure SetPixel32(const Image: TImageData; X, Y: LongInt; const Color: TColor32Rec);
  2313. var
  2314. Info: PImageFormatInfo;
  2315. Data: PByte;
  2316. begin
  2317. Info := ImageFormatInfos[Image.Format];
  2318. Data := @PByteArray(Image.Bits)[(Y * Image.Width + X) * Info.BytesPerPixel];
  2319. SetPixel32Generic(Data, Info, Image.Palette, Color);
  2320. end;
  2321. function GetPixelFP(const Image: TImageData; X, Y: LongInt): TColorFPRec;
  2322. var
  2323. Info: PImageFormatInfo;
  2324. Data: PByte;
  2325. begin
  2326. Info := ImageFormatInfos[Image.Format];
  2327. Data := @PByteArray(Image.Bits)[(Y * Image.Width + X) * Info.BytesPerPixel];
  2328. Result := GetPixelFPGeneric(Data, Info, Image.Palette);
  2329. end;
  2330. procedure SetPixelFP(const Image: TImageData; X, Y: LongInt; const Color: TColorFPRec);
  2331. var
  2332. Info: PImageFormatInfo;
  2333. Data: PByte;
  2334. begin
  2335. Info := ImageFormatInfos[Image.Format];
  2336. Data := @PByteArray(Image.Bits)[(Y * Image.Width + X) * Info.BytesPerPixel];
  2337. SetPixelFPGeneric(Data, Info, Image.Palette, Color);
  2338. end;
  2339. { Palette Functions }
  2340. procedure NewPalette(Entries: LongInt; var Pal: PPalette32);
  2341. begin
  2342. Assert((Entries > 2) and (Entries <= 65535));
  2343. try
  2344. GetMem(Pal, Entries * SizeOf(TColor32Rec));
  2345. FillChar(Pal^, Entries * SizeOf(TColor32Rec), $FF);
  2346. except
  2347. RaiseImaging(SErrorNewPalette, [Entries]);
  2348. end;
  2349. end;
  2350. procedure FreePalette(var Pal: PPalette32);
  2351. begin
  2352. try
  2353. FreeMemNil(Pal);
  2354. except
  2355. RaiseImaging(SErrorFreePalette, [Pal]);
  2356. end;
  2357. end;
  2358. procedure CopyPalette(SrcPal, DstPal: PPalette32; SrcIdx, DstIdx, Count: LongInt);
  2359. begin
  2360. Assert((SrcPal <> nil) and (DstPal <> nil));
  2361. Assert((SrcIdx >= 0) and (DstIdx >= 0) and (Count >= 0));
  2362. try
  2363. Move(SrcPal[SrcIdx], DstPal[DstIdx], Count * SizeOf(TColor32Rec));
  2364. except
  2365. RaiseImaging(SErrorCopyPalette, [Count, SrcPal, DstPal]);
  2366. end;
  2367. end;
  2368. function FindColor(Pal: PPalette32; Entries: LongInt; Color: TColor32):
  2369. LongInt;
  2370. var
  2371. Col: TColor32Rec;
  2372. I, MinDif, Dif: LongInt;
  2373. begin
  2374. Assert(Pal <> nil);
  2375. Result := -1;
  2376. Col.Color := Color;
  2377. try
  2378. // First try to find exact match
  2379. for I := 0 to Entries - 1 do
  2380. with Pal[I] do
  2381. begin
  2382. if (A = Col.A) and (R = Col.R) and
  2383. (G = Col.G) and (B = Col.B) then
  2384. begin
  2385. Result := I;
  2386. Exit;
  2387. end;
  2388. end;
  2389. // If exact match was not found, find nearest color
  2390. MinDif := 1020;
  2391. for I := 0 to Entries - 1 do
  2392. with Pal[I] do
  2393. begin
  2394. Dif := Abs(R - Col.R);
  2395. if Dif > MinDif then Continue;
  2396. Dif := Dif + Abs(G - Col.G);
  2397. if Dif > MinDif then Continue;
  2398. Dif := Dif + Abs(B - Col.B);
  2399. if Dif > MinDif then Continue;
  2400. Dif := Dif + Abs(A - Col.A);
  2401. if Dif < MinDif then
  2402. begin
  2403. MinDif := Dif;
  2404. Result := I;
  2405. end;
  2406. end;
  2407. except
  2408. RaiseImaging(SErrorFindColor, [Pal, Entries]);
  2409. end;
  2410. end;
  2411. procedure FillGrayscalePalette(Pal: PPalette32; Entries: LongInt);
  2412. var
  2413. I: LongInt;
  2414. begin
  2415. Assert(Pal <> nil);
  2416. try
  2417. for I := 0 to Entries - 1 do
  2418. with Pal[I] do
  2419. begin
  2420. A := $FF;
  2421. R := Byte(I);
  2422. G := Byte(I);
  2423. B := Byte(I);
  2424. end;
  2425. except
  2426. RaiseImaging(SErrorGrayscalePalette, [Pal, Entries]);
  2427. end;
  2428. end;
  2429. procedure FillCustomPalette(Pal: PPalette32; Entries: LongInt; RBits, GBits,
  2430. BBits: Byte; Alpha: Byte = $FF);
  2431. var
  2432. I, TotalBits, MaxEntries: LongInt;
  2433. begin
  2434. Assert(Pal <> nil);
  2435. TotalBits := RBits + GBits + BBits;
  2436. MaxEntries := Min(Pow2Int(TotalBits), Entries);
  2437. FillChar(Pal^, Entries * SizeOf(TColor32Rec), 0);
  2438. try
  2439. for I := 0 to MaxEntries - 1 do
  2440. with Pal[I] do
  2441. begin
  2442. A := Alpha;
  2443. if RBits > 0 then
  2444. R := ((I shr Max(0, GBits + BBits - 1)) and (1 shl RBits - 1)) * 255 div (1 shl RBits - 1);
  2445. if GBits > 0 then
  2446. G := ((I shr Max(0, BBits - 1)) and (1 shl GBits - 1)) * 255 div (1 shl GBits - 1);
  2447. if BBits > 0 then
  2448. B := ((I shr 0) and (1 shl BBits - 1)) * 255 div (1 shl BBits - 1);
  2449. end;
  2450. except
  2451. RaiseImaging(SErrorCustomPalette, [Pal, Entries]);
  2452. end;
  2453. end;
  2454. procedure SwapChannelsOfPalette(Pal: PPalette32; Entries, SrcChannel,
  2455. DstChannel: LongInt);
  2456. var
  2457. I: LongInt;
  2458. Swap: Byte;
  2459. begin
  2460. Assert(Pal <> nil);
  2461. Assert((SrcChannel in [0..3]) and (DstChannel in [0..3]));
  2462. try
  2463. for I := 0 to Entries - 1 do
  2464. with Pal[I] do
  2465. begin
  2466. Swap := Channels[SrcChannel];
  2467. Channels[SrcChannel] := Channels[DstChannel];
  2468. Channels[DstChannel] := Swap;
  2469. end;
  2470. except
  2471. RaiseImaging(SErrorSwapPalette, [Pal, Entries]);
  2472. end;
  2473. end;
  2474. { Options Functions }
  2475. function SetOption(OptionId, Value: LongInt): Boolean;
  2476. begin
  2477. Result := False;
  2478. if (OptionId >= 0) and (OptionId < Length(Options)) and
  2479. (Options[OptionID] <> nil) then
  2480. begin
  2481. Options[OptionID]^ := CheckOptionValue(OptionId, Value);
  2482. Result := True;
  2483. end;
  2484. end;
  2485. function GetOption(OptionId: LongInt): LongInt;
  2486. begin
  2487. Result := InvalidOption;
  2488. if (OptionId >= 0) and (OptionId < Length(Options)) and
  2489. (Options[OptionID] <> nil) then
  2490. begin
  2491. Result := Options[OptionID]^;
  2492. end;
  2493. end;
  2494. function PushOptions: Boolean;
  2495. begin
  2496. Result := OptionStack.Push;
  2497. end;
  2498. function PopOptions: Boolean;
  2499. begin
  2500. Result := OptionStack.Pop;
  2501. end;
  2502. { Image Format Functions }
  2503. function GetImageFormatInfo(Format: TImageFormat; out Info: TImageFormatInfo): Boolean;
  2504. begin
  2505. FillChar(Info, SizeOf(Info), 0);
  2506. if ImageFormatInfos[Format] <> nil then
  2507. begin
  2508. Info := ImageFormatInfos[Format]^;
  2509. Result := True;
  2510. end
  2511. else
  2512. Result := False;
  2513. end;
  2514. function GetPixelsSize(Format: TImageFormat; Width, Height: LongInt): LongInt;
  2515. begin
  2516. if ImageFormatInfos[Format] <> nil then
  2517. Result := ImageFormatInfos[Format].GetPixelsSize(Format, Width, Height)
  2518. else
  2519. Result := 0;
  2520. end;
  2521. { IO Functions }
  2522. procedure SetUserFileIO(OpenProc: TOpenProc;
  2523. CloseProc: TCloseProc; EofProc: TEofProc; SeekProc: TSeekProc; TellProc:
  2524. TTellProc; ReadProc: TReadProc; WriteProc: TWriteProc);
  2525. begin
  2526. FileIO.Open := OpenProc;
  2527. FileIO.Close := CloseProc;
  2528. FileIO.Eof := EofProc;
  2529. FileIO.Seek := SeekProc;
  2530. FileIO.Tell := TellProc;
  2531. FileIO.Read := ReadProc;
  2532. FileIO.Write := WriteProc;
  2533. end;
  2534. procedure ResetFileIO;
  2535. begin
  2536. FileIO := OriginalFileIO;
  2537. end;
  2538. { Raw Image IO Functions }
  2539. procedure ReadRawImage(Handle: TImagingHandle; Width, Height: Integer;
  2540. Format: TImageFormat; var Image: TImageData; Offset, RowLength: Integer);
  2541. var
  2542. WidthBytes, I: Integer;
  2543. Info: PImageFormatInfo;
  2544. begin
  2545. Info := ImageFormatInfos[Format];
  2546. // Calc scanline size
  2547. WidthBytes := Info.GetPixelsSize(Format, Width, 1);
  2548. if RowLength = 0 then
  2549. RowLength := WidthBytes;
  2550. // Create new image if needed - don't need to allocate new one if there is already
  2551. // one with desired size and format
  2552. if (Image.Width <> Width) or (Image.Height <> Height) or (Image.Format <> Format) then
  2553. NewImage(Width, Height, Format, Image);
  2554. // Move past the header
  2555. IO.Seek(Handle, Offset, smFromCurrent);
  2556. // Read scanlines from input
  2557. for I := 0 to Height - 1 do
  2558. begin
  2559. IO.Read(Handle, @PByteArray(Image.Bits)[I * WidthBytes], WidthBytes);
  2560. IO.Seek(Handle, RowLength - WidthBytes, smFromCurrent);
  2561. end;
  2562. end;
  2563. procedure ReadRawImageFromFile(const FileName: string; Width, Height: Integer;
  2564. Format: TImageFormat; var Image: TImageData; Offset, RowLength: Integer);
  2565. var
  2566. Handle: TImagingHandle;
  2567. begin
  2568. Assert(FileName <> '');
  2569. // Set IO ops to file ops and open given file
  2570. SetFileIO;
  2571. Handle := IO.Open(PChar(FileName), omReadOnly);
  2572. try
  2573. ReadRawImage(Handle, Width, Height, Format, Image, Offset, RowLength);
  2574. finally
  2575. IO.Close(Handle);
  2576. end;
  2577. end;
  2578. procedure ReadRawImageFromStream(Stream: TStream; Width, Height: Integer;
  2579. Format: TImageFormat; var Image: TImageData; Offset, RowLength: Integer);
  2580. var
  2581. Handle: TImagingHandle;
  2582. begin
  2583. Assert(Stream <> nil);
  2584. if Stream.Size - Stream.Position = 0 then
  2585. RaiseImaging(SErrorEmptyStream, []);
  2586. // Set IO ops to stream ops and open given stream
  2587. SetStreamIO;
  2588. Handle := IO.Open(Pointer(Stream), omReadOnly);
  2589. try
  2590. ReadRawImage(Handle, Width, Height, Format, Image, Offset, RowLength);
  2591. finally
  2592. IO.Close(Handle);
  2593. end;
  2594. end;
  2595. procedure ReadRawImageFromMemory(Data: Pointer; DataSize: Integer; Width, Height: Integer;
  2596. Format: TImageFormat; var Image: TImageData; Offset, RowLength: Integer);
  2597. var
  2598. Handle: TImagingHandle;
  2599. MemRec: TMemoryIORec;
  2600. begin
  2601. Assert((Data <> nil) and (DataSize > 0));
  2602. // Set IO ops to memory ops and open given stream
  2603. SetMemoryIO;
  2604. MemRec := PrepareMemIO(Data, DataSize);
  2605. Handle := IO.Open(@MemRec, omReadOnly);
  2606. try
  2607. ReadRawImage(Handle, Width, Height, Format, Image, Offset, RowLength);
  2608. finally
  2609. IO.Close(Handle);
  2610. end;
  2611. end;
  2612. procedure ReadRawImageRect(Data: Pointer; Left, Top, Width, Height: Integer;
  2613. var Image: TImageData; Offset, RowLength: Integer);
  2614. var
  2615. DestScanBytes, RectBytes, I: Integer;
  2616. Info: PImageFormatInfo;
  2617. Src, Dest: PByte;
  2618. begin
  2619. Assert(Data <> nil);
  2620. Assert((Left + Width <= Image.Width) and (Top + Height <= Image.Height));
  2621. Info := ImageFormatInfos[Image.Format];
  2622. // Calc scanline size
  2623. DestScanBytes := Info.GetPixelsSize(Info.Format, Image.Width, 1);
  2624. RectBytes := Info.GetPixelsSize(Info.Format, Width, 1);
  2625. if RowLength = 0 then
  2626. RowLength := RectBytes;
  2627. Src := Data;
  2628. Dest := @PByteArray(Image.Bits)[Top * DestScanBytes + Info.GetPixelsSize(Info.Format, Left, 1)];
  2629. // Move past the header
  2630. Inc(Src, Offset);
  2631. // Read lines into rect in the existing image
  2632. for I := 0 to Height - 1 do
  2633. begin
  2634. Move(Src^, Dest^, RectBytes);
  2635. Inc(Src, RowLength);
  2636. Inc(Dest, DestScanBytes);
  2637. end;
  2638. end;
  2639. procedure WriteRawImage(Handle: TImagingHandle; const Image: TImageData;
  2640. Offset, RowLength: Integer);
  2641. var
  2642. WidthBytes, I: Integer;
  2643. Info: PImageFormatInfo;
  2644. begin
  2645. Info := ImageFormatInfos[Image.Format];
  2646. // Calc scanline size
  2647. WidthBytes := Info.GetPixelsSize(Image.Format, Image.Width, 1);
  2648. if RowLength = 0 then
  2649. RowLength := WidthBytes;
  2650. // Move past the header
  2651. IO.Seek(Handle, Offset, smFromCurrent);
  2652. // Write scanlines to output
  2653. for I := 0 to Image.Height - 1 do
  2654. begin
  2655. IO.Write(Handle, @PByteArray(Image.Bits)[I * WidthBytes], WidthBytes);
  2656. IO.Seek(Handle, RowLength - WidthBytes, smFromCurrent);
  2657. end;
  2658. end;
  2659. procedure WriteRawImageToFile(const FileName: string; const Image: TImageData;
  2660. Offset, RowLength: Integer);
  2661. var
  2662. Handle: TImagingHandle;
  2663. begin
  2664. Assert(FileName <> '');
  2665. // Set IO ops to file ops and open given file
  2666. SetFileIO;
  2667. Handle := IO.Open(PChar(FileName), omCreate);
  2668. try
  2669. WriteRawImage(Handle, Image, Offset, RowLength);
  2670. finally
  2671. IO.Close(Handle);
  2672. end;
  2673. end;
  2674. procedure WriteRawImageToStream(Stream: TStream; const Image: TImageData;
  2675. Offset, RowLength: Integer);
  2676. var
  2677. Handle: TImagingHandle;
  2678. begin
  2679. Assert(Stream <> nil);
  2680. // Set IO ops to stream ops and open given stream
  2681. SetStreamIO;
  2682. Handle := IO.Open(Pointer(Stream), omCreate);
  2683. try
  2684. WriteRawImage(Handle, Image, Offset, RowLength);
  2685. finally
  2686. IO.Close(Handle);
  2687. end;
  2688. end;
  2689. procedure WriteRawImageToMemory(Data: Pointer; DataSize: Integer; const Image: TImageData;
  2690. Offset, RowLength: Integer);
  2691. var
  2692. Handle: TImagingHandle;
  2693. MemRec: TMemoryIORec;
  2694. begin
  2695. Assert((Data <> nil) and (DataSize > 0));
  2696. // Set IO ops to memory ops and open given stream
  2697. SetMemoryIO;
  2698. MemRec := PrepareMemIO(Data, DataSize);
  2699. Handle := IO.Open(@MemRec, omCreate);
  2700. try
  2701. WriteRawImage(Handle, Image, Offset, RowLength);
  2702. finally
  2703. IO.Close(Handle);
  2704. end;
  2705. end;
  2706. procedure WriteRawImageRect(Data: Pointer; Left, Top, Width, Height: Integer;
  2707. const Image: TImageData; Offset, RowLength: Integer);
  2708. var
  2709. SrcScanBytes, RectBytes, I: Integer;
  2710. Info: PImageFormatInfo;
  2711. Src, Dest: PByte;
  2712. begin
  2713. Assert(Data <> nil);
  2714. Assert((Left + Width <= Image.Width) and (Top + Height <= Image.Height));
  2715. Info := ImageFormatInfos[Image.Format];
  2716. // Calc scanline size
  2717. SrcScanBytes := Info.GetPixelsSize(Info.Format, Image.Width, 1);
  2718. RectBytes := Info.GetPixelsSize(Info.Format, Width, 1);
  2719. if RowLength = 0 then
  2720. RowLength := RectBytes;
  2721. Src := @PByteArray(Image.Bits)[Top * SrcScanBytes + Info.GetPixelsSize(Info.Format, Left, 1)];
  2722. Dest := Data;
  2723. // Move past the header
  2724. Inc(Dest, Offset);
  2725. // Write lines from rect of the existing image
  2726. for I := 0 to Height - 1 do
  2727. begin
  2728. Move(Src^, Dest^, RectBytes);
  2729. Inc(Dest, RowLength);
  2730. Inc(Src, SrcScanBytes);
  2731. end;
  2732. end;
  2733. { Convenience/helper Functions }
  2734. procedure ResizeImageToFit(const SrcImage: TImageData; FitWidth, FitHeight: Integer;
  2735. Filter: TResizeFilter; var DestImage: TImageData);
  2736. var
  2737. CurSize, FitSize, DestSize: TSize;
  2738. begin
  2739. if not TestImage(SrcImage) then
  2740. raise EImagingError.Create(SErrorInvalidInputImage);
  2741. FitSize.CX := FitWidth;
  2742. FitSize.CY := FitHeight;
  2743. CurSize.CX := SrcImage.Width;
  2744. CurSize.CY := SrcImage.Height;
  2745. DestSize := ImagingUtility.ScaleSizeToFit(CurSize, FitSize);
  2746. NewImage(Max(DestSize.CX, 1), Max(DestSize.CY, 1), SrcImage.Format, DestImage);
  2747. if SrcImage.Palette <> nil then
  2748. CopyPalette(SrcImage.Palette, DestImage.Palette, 0, 0, ImageFormatInfos[SrcImage.Format].PaletteEntries);
  2749. StretchRect(SrcImage, 0, 0, CurSize.CX, CurSize.CY, DestImage, 0, 0,
  2750. DestSize.CX, DestSize.CY, Filter);
  2751. end;
  2752. { Color constructor functions }
  2753. function Color24(R, G, B: Byte): TColor24Rec;
  2754. begin
  2755. Result.R := R;
  2756. Result.G := G;
  2757. Result.B := B;
  2758. end;
  2759. function Color32(A, R, G, B: Byte): TColor32Rec;
  2760. begin
  2761. Result.A := A;
  2762. Result.R := R;
  2763. Result.G := G;
  2764. Result.B := B;
  2765. end;
  2766. function Color48(R, G, B: Word): TColor48Rec;
  2767. begin
  2768. Result.R := R;
  2769. Result.G := G;
  2770. Result.B := B;
  2771. end;
  2772. function Color64(A, R, G, B: Word): TColor64Rec;
  2773. begin
  2774. Result.A := A;
  2775. Result.R := R;
  2776. Result.G := G;
  2777. Result.B := B;
  2778. end;
  2779. function ColorFP(A, R, G, B: Single): TColorFPRec;
  2780. begin
  2781. Result.A := A;
  2782. Result.R := R;
  2783. Result.G := G;
  2784. Result.B := B;
  2785. end;
  2786. function ColorHF(A, R, G, B: THalfFloat): TColorHFRec;
  2787. begin
  2788. Result.A := A;
  2789. Result.R := R;
  2790. Result.G := G;
  2791. Result.B := B;
  2792. end;
  2793. function GetAlphaValue(Color32: TColor32): Byte;
  2794. begin
  2795. Result := Color32 shr 24;
  2796. end;
  2797. function GetRedValue(Color32: TColor32): Byte;
  2798. begin
  2799. Result := (Color32 shr 16) and $FF;
  2800. end;
  2801. function GetGreenValue(Color32: TColor32): Byte;
  2802. begin
  2803. Result := (Color32 shr 8) and $FF;
  2804. end;
  2805. function GetBlueValue(Color32: TColor32): Byte;
  2806. begin
  2807. Result := Color32 and $FF;
  2808. end;
  2809. { ------------------------------------------------------------------------
  2810. Other Imaging Stuff
  2811. ------------------------------------------------------------------------}
  2812. function GetFormatName(Format: TImageFormat): string;
  2813. begin
  2814. if ImageFormatInfos[Format] <> nil then
  2815. Result := ImageFormatInfos[Format].Name
  2816. else
  2817. Result := SUnknownFormat;
  2818. end;
  2819. function ImageToStr(const Image: TImageData): string;
  2820. var
  2821. ImgSize: Integer;
  2822. begin
  2823. if TestImage(Image) then
  2824. with Image do
  2825. begin
  2826. ImgSize := Size;
  2827. if ImgSize > 8192 then
  2828. ImgSize := ImgSize div 1024;
  2829. Result := SysUtils.Format(SImageInfo, [@Image, Width, Height,
  2830. GetFormatName(Format), ImgSize + 0.0, Iff(ImgSize = Size, 'B', 'KiB'), Bits,
  2831. Palette]);
  2832. end
  2833. else
  2834. Result := SysUtils.Format(SImageInfoInvalid, [@Image]);
  2835. end;
  2836. function GetVersionStr: string;
  2837. begin
  2838. Result := Format('%.1d.%.2d', [ImagingVersionMajor, ImagingVersionMinor]);
  2839. end;
  2840. function IffFormat(Condition: Boolean; const TruePart, FalsePart: TImageFormat): TImageFormat;
  2841. begin
  2842. if Condition then
  2843. Result := TruePart
  2844. else
  2845. Result := FalsePart;
  2846. end;
  2847. procedure RegisterImageFileFormat(AClass: TImageFileFormatClass);
  2848. begin
  2849. Assert(AClass <> nil);
  2850. if ImageFileFormats = nil then
  2851. ImageFileFormats := TList.Create;
  2852. if GlobalMetadata = nil then
  2853. GlobalMetadata := TMetadata.Create;
  2854. if ImageFileFormats <> nil then
  2855. ImageFileFormats.Add(AClass.Create);
  2856. end;
  2857. function RegisterOption(OptionId: LongInt; Variable: PLongInt): Boolean;
  2858. begin
  2859. Result := False;
  2860. if Options = nil then
  2861. InitOptions;
  2862. Assert(Variable <> nil);
  2863. if OptionId >= Length(Options) then
  2864. SetLength(Options, OptionId + InitialOptions);
  2865. if (OptionId >= 0) and (OptionId < Length(Options)) {and (Options[OptionId] = nil) - must be able to override existing } then
  2866. begin
  2867. Options[OptionId] := Variable;
  2868. Result := True;
  2869. end;
  2870. end;
  2871. function FindImageFileFormatByExt(const Ext: string): TImageFileFormat;
  2872. var
  2873. I: LongInt;
  2874. begin
  2875. Result := nil;
  2876. for I := ImageFileFormats.Count - 1 downto 0 do
  2877. if TImageFileFormat(ImageFileFormats[I]).Extensions.IndexOf(Ext) >= 0 then
  2878. begin
  2879. Result := TImageFileFormat(ImageFileFormats[I]);
  2880. Exit;
  2881. end;
  2882. end;
  2883. function FindImageFileFormatByName(const FileName: string): TImageFileFormat;
  2884. var
  2885. I: LongInt;
  2886. begin
  2887. Result := nil;
  2888. for I := ImageFileFormats.Count - 1 downto 0 do
  2889. if TImageFileFormat(ImageFileFormats[I]).TestFileName(FileName) then
  2890. begin
  2891. Result := TImageFileFormat(ImageFileFormats[I]);
  2892. Exit;
  2893. end;
  2894. end;
  2895. function FindImageFileFormatByClass(AClass: TImageFileFormatClass): TImageFileFormat;
  2896. var
  2897. I: LongInt;
  2898. begin
  2899. Result := nil;
  2900. for I := 0 to ImageFileFormats.Count - 1 do
  2901. if TImageFileFormat(ImageFileFormats[I]) is AClass then
  2902. begin
  2903. Result := TObject(ImageFileFormats[I]) as TImageFileFormat;
  2904. Break;
  2905. end;
  2906. end;
  2907. function GetFileFormatCount: LongInt;
  2908. begin
  2909. Result := ImageFileFormats.Count;
  2910. end;
  2911. function GetFileFormatAtIndex(Index: LongInt): TImageFileFormat;
  2912. begin
  2913. if (Index >= 0) and (Index < ImageFileFormats.Count) then
  2914. Result := TImageFileFormat(ImageFileFormats[Index])
  2915. else
  2916. Result := nil;
  2917. end;
  2918. function GetImageFileFormatsFilter(OpenFileFilter: Boolean): string;
  2919. var
  2920. I, J, Count: LongInt;
  2921. Descriptions: string;
  2922. Filters, CurFilter: string;
  2923. FileFormat: TImageFileFormat;
  2924. begin
  2925. Descriptions := '';
  2926. Filters := '';
  2927. Count := 0;
  2928. for I := 0 to ImageFileFormats.Count - 1 do
  2929. begin
  2930. FileFormat := TObject(ImageFileFormats[I]) as TImageFileFormat;
  2931. // If we are creating filter for save dialog and this format cannot save
  2932. // files the we skip it
  2933. if not OpenFileFilter and not FileFormat.CanSave then
  2934. Continue;
  2935. CurFilter := '';
  2936. for J := 0 to FileFormat.Masks.Count - 1 do
  2937. begin
  2938. CurFilter := CurFilter + FileFormat.Masks[J];
  2939. if J < FileFormat.Masks.Count - 1 then
  2940. CurFilter := CurFilter + ';';
  2941. end;
  2942. FmtStr(Descriptions, '%s%s (%s)|%2:s', [Descriptions, FileFormat.Name, CurFilter]);
  2943. if Filters <> '' then
  2944. FmtStr(Filters, '%s;%s', [Filters, CurFilter])
  2945. else
  2946. Filters := CurFilter;
  2947. if I < ImageFileFormats.Count - 1 then
  2948. Descriptions := Descriptions + '|';
  2949. Inc(Count);
  2950. end;
  2951. if (Count > 1) and OpenFileFilter then
  2952. FmtStr(Descriptions, '%s (%s)|%1:s|%s', [SAllFilter, Filters, Descriptions]);
  2953. Result := Descriptions;
  2954. end;
  2955. function GetFilterIndexExtension(Index: LongInt; OpenFileFilter: Boolean): string;
  2956. var
  2957. I, Count: LongInt;
  2958. FileFormat: TImageFileFormat;
  2959. begin
  2960. // -1 because filter indices are in 1..n range
  2961. Index := Index - 1;
  2962. Result := '';
  2963. if OpenFileFilter then
  2964. begin
  2965. if Index > 0 then
  2966. Index := Index - 1;
  2967. end;
  2968. if (Index >= 0) and (Index < ImageFileFormats.Count) then
  2969. begin
  2970. Count := 0;
  2971. for I := 0 to ImageFileFormats.Count - 1 do
  2972. begin
  2973. FileFormat := TObject(ImageFileFormats[I]) as TImageFileFormat;
  2974. if not OpenFileFilter and not FileFormat.CanSave then
  2975. Continue;
  2976. if Index = Count then
  2977. begin
  2978. if FileFormat.Extensions.Count > 0 then
  2979. Result := FileFormat.Extensions[0];
  2980. Exit;
  2981. end;
  2982. Inc(Count);
  2983. end;
  2984. end;
  2985. end;
  2986. function GetFileNameFilterIndex(const FileName: string; OpenFileFilter: Boolean): LongInt;
  2987. var
  2988. I: LongInt;
  2989. FileFormat: TImageFileFormat;
  2990. begin
  2991. Result := 0;
  2992. for I := 0 to ImageFileFormats.Count - 1 do
  2993. begin
  2994. FileFormat := TObject(ImageFileFormats[I]) as TImageFileFormat;
  2995. if not OpenFileFilter and not FileFormat.CanSave then
  2996. Continue;
  2997. if FileFormat.TestFileName(FileName) then
  2998. begin
  2999. // +1 because filter indices are in 1..n range
  3000. Inc(Result);
  3001. if OpenFileFilter then
  3002. Inc(Result);
  3003. Exit;
  3004. end;
  3005. Inc(Result);
  3006. end;
  3007. Result := -1;
  3008. end;
  3009. function GetIO: TIOFunctions;
  3010. begin
  3011. Result := IO;
  3012. end;
  3013. procedure RaiseImaging(const Msg: string; const Args: array of const);
  3014. var
  3015. WholeMsg: string;
  3016. begin
  3017. WholeMsg := Msg;
  3018. if GetExceptObject <> nil then
  3019. begin
  3020. WholeMsg := WholeMsg + ' ' + SExceptMsg + ': ' +
  3021. GetExceptObject.Message;
  3022. end;
  3023. raise EImagingError.CreateFmt(WholeMsg, Args);
  3024. end;
  3025. procedure RaiseImaging(const Msg: string);
  3026. begin
  3027. RaiseImaging(Msg, []);
  3028. end;
  3029. { Internal unit functions }
  3030. function CheckOptionValue(OptionId, Value: LongInt): LongInt;
  3031. begin
  3032. case OptionId of
  3033. ImagingColorReductionMask:
  3034. Result := ClampInt(Value, 0, $FF);
  3035. ImagingLoadOverrideFormat, ImagingSaveOverrideFormat:
  3036. Result := Iff(ImagingFormats.IsImageFormatValid(TImageFormat(Value)),
  3037. Value, LongInt(ifUnknown));
  3038. ImagingMipMapFilter: Result := ClampInt(Value, Ord(Low(TSamplingFilter)),
  3039. Ord(High(TSamplingFilter)));
  3040. else
  3041. Result := Value;
  3042. end;
  3043. end;
  3044. procedure SetFileIO;
  3045. begin
  3046. IO := FileIO;
  3047. end;
  3048. procedure SetStreamIO;
  3049. begin
  3050. IO := StreamIO;
  3051. end;
  3052. procedure SetMemoryIO;
  3053. begin
  3054. IO := MemoryIO;
  3055. end;
  3056. procedure InitImageFormats;
  3057. begin
  3058. ImagingFormats.InitImageFormats(ImageFormatInfos);
  3059. end;
  3060. procedure FreeImageFileFormats;
  3061. var
  3062. I: LongInt;
  3063. begin
  3064. if ImageFileFormats <> nil then
  3065. for I := 0 to ImageFileFormats.Count - 1 do
  3066. TImageFileFormat(ImageFileFormats[I]).Free;
  3067. FreeAndNil(ImageFileFormats);
  3068. end;
  3069. procedure InitOptions;
  3070. begin
  3071. SetLength(Options, InitialOptions);
  3072. OptionStack := TOptionStack.Create;
  3073. end;
  3074. procedure FreeOptions;
  3075. begin
  3076. SetLength(Options, 0);
  3077. FreeAndNil(OptionStack);
  3078. end;
  3079. {
  3080. TImageFileFormat class implementation
  3081. }
  3082. constructor TImageFileFormat.Create(AMetadata: TMetadata);
  3083. begin
  3084. inherited Create;
  3085. FName := SUnknownFormat;
  3086. FExtensions := TStringList.Create;
  3087. FMasks := TStringList.Create;
  3088. if AMetadata = nil then
  3089. FMetadata := GlobalMetadata
  3090. else
  3091. FMetadata := AMetadata;
  3092. Define;
  3093. end;
  3094. destructor TImageFileFormat.Destroy;
  3095. begin
  3096. FExtensions.Free;
  3097. FMasks.Free;
  3098. inherited Destroy;
  3099. end;
  3100. procedure TImageFileFormat.Define;
  3101. begin
  3102. end;
  3103. function TImageFileFormat.PrepareLoad(Handle: TImagingHandle;
  3104. var Images: TDynImageDataArray; OnlyFirstFrame: Boolean): Boolean;
  3105. begin
  3106. FMetadata.ClearMetaItems; // Clear old metadata
  3107. FreeImagesInArray(Images);
  3108. SetLength(Images, 0);
  3109. Result := Handle <> nil;
  3110. end;
  3111. function TImageFileFormat.PostLoadCheck(var Images: TDynImageDataArray;
  3112. LoadResult: Boolean): Boolean;
  3113. var
  3114. I: LongInt;
  3115. begin
  3116. if not LoadResult then
  3117. begin
  3118. FreeImagesInArray(Images);
  3119. SetLength(Images, 0);
  3120. Result := False;
  3121. end
  3122. else
  3123. begin
  3124. Result := (Length(Images) > 0) and TestImagesInArray(Images);
  3125. if Result then
  3126. begin
  3127. // Convert to overriden format if it is set
  3128. if LoadOverrideFormat <> ifUnknown then
  3129. for I := Low(Images) to High(Images) do
  3130. ConvertImage(Images[I], LoadOverrideFormat);
  3131. end;
  3132. end;
  3133. end;
  3134. function TImageFileFormat.PrepareSave(Handle: TImagingHandle;
  3135. const Images: TDynImageDataArray; var Index: Integer): Boolean;
  3136. var
  3137. Len, I: LongInt;
  3138. begin
  3139. CheckOptionsValidity;
  3140. Result := False;
  3141. if CanSave then
  3142. begin
  3143. Len := Length(Images);
  3144. Assert(Len > 0);
  3145. // If there are no images to be saved exit
  3146. if Len = 0 then Exit;
  3147. // Check index of image to be saved (-1 as index means save all images)
  3148. if IsMultiImageFormat then
  3149. begin
  3150. if (Index >= Len) then
  3151. Index := 0;
  3152. if Index < 0 then
  3153. begin
  3154. Index := 0;
  3155. FFirstIdx := 0;
  3156. FLastIdx := Len - 1;
  3157. end
  3158. else
  3159. begin
  3160. FFirstIdx := Index;
  3161. FLastIdx := Index;
  3162. end;
  3163. for I := FFirstIdx to FLastIdx - 1 do
  3164. begin
  3165. if not TestImage(Images[I]) then
  3166. Exit;
  3167. end;
  3168. end
  3169. else
  3170. begin
  3171. if (Index >= Len) or (Index < 0) then
  3172. Index := 0;
  3173. if not TestImage(Images[Index]) then
  3174. Exit;
  3175. end;
  3176. Result := True;
  3177. end;
  3178. end;
  3179. procedure TImageFileFormat.AddMasks(const AMasks: string);
  3180. var
  3181. I: LongInt;
  3182. Ext: string;
  3183. begin
  3184. FExtensions.Clear;
  3185. FMasks.CommaText := AMasks;
  3186. FMasks.Delimiter := ';';
  3187. for I := 0 to FMasks.Count - 1 do
  3188. begin
  3189. FMasks[I] := Trim(FMasks[I]);
  3190. Ext := GetFileExt(FMasks[I]);
  3191. if (Ext <> '') and (Ext <> '*') then
  3192. FExtensions.Add(Ext);
  3193. end;
  3194. end;
  3195. function TImageFileFormat.GetFormatInfo(Format: TImageFormat): TImageFormatInfo;
  3196. begin
  3197. Result := ImageFormatInfos[Format]^;
  3198. end;
  3199. function TImageFileFormat.GetSupportedFormats: TImageFormats;
  3200. begin
  3201. Result := FSupportedFormats;
  3202. end;
  3203. function TImageFileFormat.LoadData(Handle: TImagingHandle;
  3204. var Images: TDynImageDataArray; OnlyFirstFrame: Boolean): Boolean;
  3205. begin
  3206. Result := False;
  3207. RaiseImaging(SFileFormatCanNotLoad, [FName]);
  3208. end;
  3209. function TImageFileFormat.SaveData(Handle: TImagingHandle;
  3210. const Images: TDynImageDataArray; Index: LongInt): Boolean;
  3211. begin
  3212. Result := False;
  3213. RaiseImaging(SFileFormatCanNotSave, [FName]);
  3214. end;
  3215. procedure TImageFileFormat.ConvertToSupported(var Image: TImageData;
  3216. const Info: TImageFormatInfo);
  3217. begin
  3218. end;
  3219. function TImageFileFormat.IsSupported(const Image: TImageData): Boolean;
  3220. begin
  3221. Result := Image.Format in GetSupportedFormats;
  3222. end;
  3223. function TImageFileFormat.LoadFromFile(const FileName: string;
  3224. var Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean;
  3225. var
  3226. Handle: TImagingHandle;
  3227. begin
  3228. Result := False;
  3229. if CanLoad then
  3230. try
  3231. // Set IO ops to file ops and open given file
  3232. SetFileIO;
  3233. Handle := IO.Open(PChar(FileName), omReadOnly);
  3234. try
  3235. // Test if file contains valid image and if so then load it
  3236. if TestFormat(Handle) then
  3237. begin
  3238. Result := PrepareLoad(Handle, Images, OnlyFirstLevel) and
  3239. LoadData(Handle, Images, OnlyFirstlevel);
  3240. Result := PostLoadCheck(Images, Result);
  3241. end
  3242. else
  3243. RaiseImaging(SFileNotValid, [FileName, Name]);
  3244. finally
  3245. IO.Close(Handle);
  3246. end;
  3247. except
  3248. RaiseImaging(SErrorLoadingFile, [FileName, FExtensions[0]]);
  3249. end;
  3250. end;
  3251. function TImageFileFormat.LoadFromStream(Stream: TStream;
  3252. var Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean;
  3253. var
  3254. Handle: TImagingHandle;
  3255. OldPosition: Int64;
  3256. begin
  3257. Result := False;
  3258. OldPosition := Stream.Position;
  3259. if CanLoad then
  3260. try
  3261. // Set IO ops to stream ops and "open" given memory
  3262. SetStreamIO;
  3263. Handle := IO.Open(Pointer(Stream), omReadOnly);
  3264. try
  3265. // Test if stream contains valid image and if so then load it
  3266. if TestFormat(Handle) then
  3267. begin
  3268. Result := PrepareLoad(Handle, Images, OnlyFirstLevel) and
  3269. LoadData(Handle, Images, OnlyFirstlevel);
  3270. Result := PostLoadCheck(Images, Result);
  3271. end
  3272. else
  3273. RaiseImaging(SStreamNotValid, [@Stream, Name]);
  3274. finally
  3275. IO.Close(Handle);
  3276. end;
  3277. except
  3278. Stream.Position := OldPosition;
  3279. FreeImagesInArray(Images);
  3280. RaiseImaging(SErrorLoadingStream, [@Stream, FExtensions[0]]);
  3281. end;
  3282. end;
  3283. function TImageFileFormat.LoadFromMemory(Data: Pointer; Size: LongInt; var
  3284. Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean;
  3285. var
  3286. Handle: TImagingHandle;
  3287. IORec: TMemoryIORec;
  3288. begin
  3289. Result := False;
  3290. if CanLoad then
  3291. try
  3292. // Set IO ops to memory ops and "open" given memory
  3293. SetMemoryIO;
  3294. IORec := PrepareMemIO(Data, Size);
  3295. Handle := IO.Open(@IORec,omReadOnly);
  3296. try
  3297. // Test if memory contains valid image and if so then load it
  3298. if TestFormat(Handle) then
  3299. begin
  3300. Result := PrepareLoad(Handle, Images, OnlyFirstLevel) and
  3301. LoadData(Handle, Images, OnlyFirstlevel);
  3302. Result := PostLoadCheck(Images, Result);
  3303. end
  3304. else
  3305. RaiseImaging(SMemoryNotValid, [Data, Size, Name]);
  3306. finally
  3307. IO.Close(Handle);
  3308. end;
  3309. except
  3310. RaiseImaging(SErrorLoadingMemory, [Data, Size, FExtensions[0]]);
  3311. end;
  3312. end;
  3313. function TImageFileFormat.SaveToFile(const FileName: string;
  3314. const Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean;
  3315. var
  3316. Handle: TImagingHandle;
  3317. Len, Index, I: LongInt;
  3318. Ext, FName: string;
  3319. begin
  3320. Result := False;
  3321. if CanSave and TestImagesInArray(Images) then
  3322. try
  3323. SetFileIO;
  3324. Len := Length(Images);
  3325. if IsMultiImageFormat or
  3326. (not IsMultiImageFormat and (OnlyFirstLevel or (Len = 1))) then
  3327. begin
  3328. Handle := IO.Open(PChar(FileName), GetSaveOpenMode);
  3329. try
  3330. if OnlyFirstLevel then
  3331. Index := 0
  3332. else
  3333. Index := -1;
  3334. // Write multi image to one file
  3335. Result := PrepareSave(Handle, Images, Index) and SaveData(Handle, Images, Index);
  3336. finally
  3337. IO.Close(Handle);
  3338. end;
  3339. end
  3340. else
  3341. begin
  3342. // Write multi image to file sequence
  3343. Ext := ExtractFileExt(FileName);
  3344. FName := ChangeFileExt(FileName, '');
  3345. Result := True;
  3346. for I := 0 to Len - 1 do
  3347. begin
  3348. Handle := IO.Open(PChar(Format(FName + '%.3d' + Ext, [I])), GetSaveOpenMode);
  3349. try
  3350. Index := I;
  3351. Result := Result and PrepareSave(Handle, Images, Index) and
  3352. SaveData(Handle, Images, Index);
  3353. if not Result then
  3354. Break;
  3355. finally
  3356. IO.Close(Handle);
  3357. end;
  3358. end;
  3359. end;
  3360. except
  3361. raise UpdateExceptMessage(GetExceptObject, SErrorSavingFile, [FileName, FExtensions[0]]);
  3362. end;
  3363. end;
  3364. function TImageFileFormat.SaveToStream(Stream: TStream;
  3365. const Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean;
  3366. var
  3367. Handle: TImagingHandle;
  3368. Len, Index, I: LongInt;
  3369. OldPosition: Int64;
  3370. begin
  3371. Result := False;
  3372. OldPosition := Stream.Position;
  3373. if CanSave and TestImagesInArray(Images) then
  3374. try
  3375. SetStreamIO;
  3376. Handle := IO.Open(PChar(Stream), GetSaveOpenMode);
  3377. try
  3378. if IsMultiImageFormat or OnlyFirstLevel then
  3379. begin
  3380. if OnlyFirstLevel then
  3381. Index := 0
  3382. else
  3383. Index := -1;
  3384. // Write multi image in one run
  3385. Result := PrepareSave(Handle, Images, Index) and SaveData(Handle, Images, Index);
  3386. end
  3387. else
  3388. begin
  3389. // Write multi image to sequence
  3390. Result := True;
  3391. Len := Length(Images);
  3392. for I := 0 to Len - 1 do
  3393. begin
  3394. Index := I;
  3395. Result := Result and PrepareSave(Handle, Images, Index) and
  3396. SaveData(Handle, Images, Index);
  3397. if not Result then
  3398. Break;
  3399. end;
  3400. end;
  3401. finally
  3402. IO.Close(Handle);
  3403. end;
  3404. except
  3405. Stream.Position := OldPosition;
  3406. raise UpdateExceptMessage(GetExceptObject, SErrorSavingStream, [@Stream, FExtensions[0]]);
  3407. end;
  3408. end;
  3409. function TImageFileFormat.SaveToMemory(Data: Pointer; var Size: LongInt;
  3410. const Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean;
  3411. var
  3412. Handle: TImagingHandle;
  3413. Len, Index, I: LongInt;
  3414. IORec: TMemoryIORec;
  3415. begin
  3416. Result := False;
  3417. if CanSave and TestImagesInArray(Images) then
  3418. try
  3419. SetMemoryIO;
  3420. IORec := PrepareMemIO(Data, Size);
  3421. Handle := IO.Open(PChar(@IORec), GetSaveOpenMode);
  3422. try
  3423. if IsMultiImageFormat or OnlyFirstLevel then
  3424. begin
  3425. if OnlyFirstLevel then
  3426. Index := 0
  3427. else
  3428. Index := -1;
  3429. // Write multi image in one run
  3430. Result := PrepareSave(Handle, Images, Index) and SaveData(Handle, Images, Index);
  3431. end
  3432. else
  3433. begin
  3434. // Write multi image to sequence
  3435. Result := True;
  3436. Len := Length(Images);
  3437. for I := 0 to Len - 1 do
  3438. begin
  3439. Index := I;
  3440. Result := Result and PrepareSave(Handle, Images, Index) and
  3441. SaveData(Handle, Images, Index);
  3442. if not Result then
  3443. Break;
  3444. end;
  3445. end;
  3446. Size := IORec.Position;
  3447. finally
  3448. IO.Close(Handle);
  3449. end;
  3450. except
  3451. raise UpdateExceptMessage(GetExceptObject, SErrorSavingMemory, [Data, Size, FExtensions[0]]);
  3452. end;
  3453. end;
  3454. function TImageFileFormat.MakeCompatible(const Image: TImageData;
  3455. var Compatible: TImageData; out MustBeFreed: Boolean): Boolean;
  3456. begin
  3457. InitImage(Compatible);
  3458. if SaveOverrideFormat <> ifUnknown then
  3459. begin
  3460. // Save format override is active. Clone input and convert it to override format.
  3461. CloneImage(Image, Compatible);
  3462. ConvertImage(Compatible, SaveOverrideFormat);
  3463. // Now check if override format is supported by file format. If it is not
  3464. // then file format specific conversion (virtual method) is called.
  3465. Result := IsSupported(Compatible);
  3466. if not Result then
  3467. begin
  3468. ConvertToSupported(Compatible, GetFormatInfo(Compatible.Format));
  3469. Result := IsSupported(Compatible);
  3470. end;
  3471. end // Add IsCompatible function! not only checking by Format
  3472. else if IsSupported(Image) then
  3473. begin
  3474. // No save format override and input is in format supported by this
  3475. // file format. Just copy Image's fields to Compatible
  3476. Compatible := Image;
  3477. Result := True;
  3478. end
  3479. else
  3480. begin
  3481. // No override and input's format is not compatible with file format.
  3482. // Clone it and the call file format specific conversion (virtual method).
  3483. CloneImage(Image, Compatible);
  3484. ConvertToSupported(Compatible, GetFormatInfo(Compatible.Format));
  3485. Result := IsSupported(Compatible);
  3486. end;
  3487. // Tell the user that he must free Compatible after he's done with it
  3488. // (if necessary).
  3489. MustBeFreed := Image.Bits <> Compatible.Bits;
  3490. end;
  3491. function TImageFileFormat.TestFormat(Handle: TImagingHandle): Boolean;
  3492. begin
  3493. Result := False;
  3494. end;
  3495. function TImageFileFormat.TestFileName(const FileName: string): Boolean;
  3496. var
  3497. I: LongInt;
  3498. OnlyName: string;
  3499. begin
  3500. OnlyName := ExtractFileName(FileName);
  3501. // For each mask test if filename matches it
  3502. for I := 0 to FMasks.Count - 1 do
  3503. if StrMaskMatch(OnlyName, FMasks[I], False) then
  3504. begin
  3505. Result := True;
  3506. Exit;
  3507. end;
  3508. Result := False;
  3509. end;
  3510. procedure TImageFileFormat.CheckOptionsValidity;
  3511. begin
  3512. end;
  3513. function TImageFileFormat.GetCanLoad: Boolean;
  3514. begin
  3515. Result := ffLoad in FFeatures;
  3516. end;
  3517. function TImageFileFormat.GetCanSave: Boolean;
  3518. begin
  3519. Result := ffSave in FFeatures;
  3520. end;
  3521. function TImageFileFormat.GetIsMultiImageFormat: Boolean;
  3522. begin
  3523. Result := ffMultiImage in FFeatures;
  3524. end;
  3525. function TImageFileFormat.GetSaveOpenMode: TOpenMode;
  3526. begin
  3527. // TODO: fix
  3528. //if ffReadOnSave in FFeatures then
  3529. // Result := omReadWrite
  3530. //else
  3531. Result := omCreate;
  3532. end;
  3533. { TOptionStack class implementation }
  3534. constructor TOptionStack.Create;
  3535. begin
  3536. inherited Create;
  3537. FPosition := -1;
  3538. end;
  3539. destructor TOptionStack.Destroy;
  3540. var
  3541. I: LongInt;
  3542. begin
  3543. for I := 0 to OptionStackDepth - 1 do
  3544. SetLength(FStack[I], 0);
  3545. inherited Destroy;
  3546. end;
  3547. function TOptionStack.Pop: Boolean;
  3548. var
  3549. I: LongInt;
  3550. begin
  3551. Result := False;
  3552. if FPosition >= 0 then
  3553. begin
  3554. SetLength(Options, Length(FStack[FPosition]));
  3555. for I := 0 to Length(FStack[FPosition]) - 1 do
  3556. if Options[I] <> nil then
  3557. Options[I]^ := FStack[FPosition, I];
  3558. Dec(FPosition);
  3559. Result := True;
  3560. end;
  3561. end;
  3562. function TOptionStack.Push: Boolean;
  3563. var
  3564. I: LongInt;
  3565. begin
  3566. Result := False;
  3567. if FPosition < OptionStackDepth - 1 then
  3568. begin
  3569. Inc(FPosition);
  3570. SetLength(FStack[FPosition], Length(Options));
  3571. for I := 0 to Length(Options) - 1 do
  3572. if Options[I] <> nil then
  3573. FStack[FPosition, I] := Options[I]^;
  3574. Result := True;
  3575. end;
  3576. end;
  3577. { TMetadata }
  3578. procedure TMetadata.SetMetaItem(const Id: string; const Value: Variant;
  3579. ImageIndex: Integer);
  3580. begin
  3581. AddMetaToList(FLoadMetaItems, Id, Value, ImageIndex);
  3582. end;
  3583. procedure TMetadata.SetMetaItemForSaving(const Id: string; const Value: Variant;
  3584. ImageIndex: Integer);
  3585. begin
  3586. AddMetaToList(FSaveMetaItems, Id, Value, ImageIndex);
  3587. end;
  3588. procedure TMetadata.AddMetaToList(List: TStringList; const Id: string;
  3589. const Value: Variant; ImageIndex: Integer);
  3590. var
  3591. Item: TMetadataItem;
  3592. Idx: Integer;
  3593. FullId: string;
  3594. begin
  3595. FullId := GetMetaItemName(Id, ImageIndex);
  3596. if List.Find(FullId, Idx) then
  3597. (List.Objects[Idx] as TMetadataItem).Value := Value
  3598. else
  3599. begin
  3600. Item := TMetadataItem.Create;
  3601. Item.Id := Id;
  3602. Item.ImageIndex := ImageIndex;
  3603. Item.Value := Value;
  3604. List.AddObject(FullId, Item);
  3605. end;
  3606. end;
  3607. procedure TMetadata.ClearMetaItems;
  3608. begin
  3609. ClearMetaList(FLoadMetaItems);
  3610. end;
  3611. procedure TMetadata.ClearMetaItemsForSaving;
  3612. begin
  3613. ClearMetaList(FSaveMetaItems);
  3614. end;
  3615. procedure TMetadata.ClearMetaList(List: TStringList);
  3616. var
  3617. I: Integer;
  3618. begin
  3619. for I := 0 to List.Count - 1 do
  3620. List.Objects[I].Free;
  3621. List.Clear;
  3622. end;
  3623. procedure TMetadata.CopyLoadedMetaItemsForSaving;
  3624. var
  3625. I: Integer;
  3626. Copy, Orig: TMetadataItem;
  3627. begin
  3628. ClearMetaItemsForSaving;
  3629. for I := 0 to FLoadMetaItems.Count - 1 do
  3630. begin
  3631. Orig := TMetadataItem(FLoadMetaItems.Objects[I]);
  3632. Copy := TMetadataItem.Create;
  3633. Copy.Id := Orig.Id;
  3634. Copy.ImageIndex := Orig.ImageIndex;
  3635. Copy.Value := Orig.Value;
  3636. FSaveMetaItems.AddObject(GetMetaItemName(Copy.Id, Copy.ImageIndex), Copy);
  3637. end;
  3638. end;
  3639. constructor TMetadata.Create;
  3640. begin
  3641. inherited;
  3642. FLoadMetaItems := TStringList.Create;
  3643. FLoadMetaItems.Sorted := True;
  3644. FSaveMetaItems := TStringList.Create;
  3645. FSaveMetaItems.Sorted := True;
  3646. end;
  3647. destructor TMetadata.Destroy;
  3648. begin
  3649. ClearMetaItems;
  3650. ClearMetaItemsForSaving;
  3651. FLoadMetaItems.Free;
  3652. FSaveMetaItems.Free;
  3653. inherited;
  3654. end;
  3655. function TMetadata.GetMetaById(const Id: string): Variant;
  3656. var
  3657. Idx: Integer;
  3658. begin
  3659. if FLoadMetaItems.Find(Id, Idx) then
  3660. Result := (FLoadMetaItems.Objects[Idx] as TMetadataItem).Value
  3661. else
  3662. Result := Variants.Null;
  3663. end;
  3664. function TMetadata.GetMetaByIdMulti(const Id: string; ImageIndex: Integer): Variant;
  3665. begin
  3666. Result := GetMetaById(GetMetaItemName(Id, ImageIndex));
  3667. end;
  3668. function TMetadata.GetSaveMetaById(const Id: string): Variant;
  3669. var
  3670. Idx: Integer;
  3671. begin
  3672. if FSaveMetaItems.Find(Id, Idx) then
  3673. Result := (FSaveMetaItems.Objects[Idx] as TMetadataItem).Value
  3674. else
  3675. Result := Variants.Null;
  3676. end;
  3677. function TMetadata.GetSaveMetaByIdMulti(const Id: string;
  3678. ImageIndex: Integer): Variant;
  3679. begin
  3680. Result := GetSaveMetaById(GetMetaItemName(Id, ImageIndex));
  3681. end;
  3682. function TMetadata.GetMetaByIdx(Index: Integer): TMetadataItem;
  3683. begin
  3684. Result := FLoadMetaItems.Objects[Index] as TMetadataItem;
  3685. end;
  3686. function TMetadata.GetMetaCount: Integer;
  3687. begin
  3688. Result := FLoadMetaItems.Count;
  3689. end;
  3690. function TMetadata.GetMetaItemName(const Id: string;
  3691. ImageIndex: Integer): string;
  3692. begin
  3693. Result := Iff(ImageIndex = 0, Id, Format(SMetaIdForSubImage, [Id, ImageIndex]));
  3694. end;
  3695. function TMetadata.GetPhysicalPixelSize(ResUnit: TResolutionUnit; var XSize,
  3696. YSize: Single; MetaForSave: Boolean; ImageIndex: Integer): Boolean;
  3697. type
  3698. TGetter = function(const Id: string; ImageIndex: Integer): Variant of object;
  3699. var
  3700. Getter: TGetter;
  3701. XMeta, YMeta: Variant;
  3702. begin
  3703. if MetaForSave then
  3704. Getter := GetSaveMetaByIdMulti
  3705. else
  3706. Getter := GetMetaByIdMulti;
  3707. XMeta := Getter(SMetaPhysicalPixelSizeX, ImageIndex);
  3708. YMeta := Getter(SMetaPhysicalPixelSizeY, ImageIndex);
  3709. XSize := -1;
  3710. YSize := -1;
  3711. Result := not VarIsNull(XMeta) or not VarIsNull(YMeta);
  3712. if not Result then
  3713. Exit;
  3714. if not VarIsNull(XMeta) then
  3715. XSize := XMeta;
  3716. if not VarIsNull(YMeta) then
  3717. YSize := YMeta;
  3718. if XSize < 0 then
  3719. XSize := YSize;
  3720. if YSize < 0 then
  3721. YSize := XSize;
  3722. TranslateUnits(ResUnit, XSize, YSize);
  3723. end;
  3724. procedure TMetadata.SetPhysicalPixelSize(ResUnit: TResolutionUnit; XSize,
  3725. YSize: Single; MetaForSave: Boolean; ImageIndex: Integer);
  3726. type
  3727. TAdder = procedure(const Id: string; const Value: Variant; ImageIndex: Integer) of object;
  3728. var
  3729. Adder: TAdder;
  3730. begin
  3731. TranslateUnits(ResUnit, XSize, YSize);
  3732. if MetaForSave then
  3733. Adder := SetMetaItemForSaving
  3734. else
  3735. Adder := SetMetaItem;
  3736. Adder(SMetaPhysicalPixelSizeX, XSize, ImageIndex);
  3737. Adder(SMetaPhysicalPixelSizeY, YSize, ImageIndex);
  3738. end;
  3739. procedure TMetadata.TranslateUnits(ResolutionUnit: TResolutionUnit; var XRes,
  3740. YRes: Single);
  3741. var
  3742. UnitSize: Single;
  3743. begin
  3744. case ResolutionUnit of
  3745. ruDpi: UnitSize := 25400;
  3746. ruDpm: UnitSize := 1e06;
  3747. ruDpcm: UnitSize := 1e04;
  3748. else
  3749. UnitSize := 1;
  3750. end;
  3751. if ResolutionUnit <> ruSizeInMicroMeters then
  3752. begin
  3753. XRes := UnitSize / XRes;
  3754. YRes := UnitSize / YRes;
  3755. end;
  3756. end;
  3757. function TMetadata.HasMetaItem(const Id: string; ImageIndex: Integer): Boolean;
  3758. begin
  3759. Result := GetMetaByIdMulti(Id, ImageIndex) <> Variants.Null;
  3760. end;
  3761. function TMetadata.HasMetaItemForSaving(const Id: string; ImageIndex: Integer): Boolean;
  3762. begin
  3763. Result := GetSaveMetaByIdMulti(Id, ImageIndex) <> Variants.Null;
  3764. end;
  3765. initialization
  3766. {$IFDEF MEMCHECK}
  3767. {$IF CompilerVersion >= 18}
  3768. System.ReportMemoryLeaksOnShutdown := True;
  3769. {$IFEND}
  3770. {$ENDIF}
  3771. if GlobalMetadata = nil then
  3772. GlobalMetadata := TMetadata.Create;
  3773. if ImageFileFormats = nil then
  3774. ImageFileFormats := TList.Create;
  3775. InitImageFormats;
  3776. RegisterOption(ImagingColorReductionMask, @ColorReductionMask);
  3777. RegisterOption(ImagingLoadOverrideFormat, @LoadOverrideFormat);
  3778. RegisterOption(ImagingSaveOverrideFormat, @SaveOverrideFormat);
  3779. RegisterOption(ImagingMipMapFilter, @MipMapFilter);
  3780. RegisterOption(ImagingBinaryTreshold, @BinaryTreshold);
  3781. finalization
  3782. FreeOptions;
  3783. FreeImageFileFormats;
  3784. GlobalMetadata.Free;
  3785. {
  3786. File Notes:
  3787. -- TODOS ----------------------------------------------------
  3788. - nothing now
  3789. -- 0.80 ------------------------------------------------------
  3790. - Added new color records constructor functions (Color24(..), Color32(..)).
  3791. - Added convenience channel getters for TColor32 (GetGreenValue, ...).
  3792. -- 0.77.1 ---------------------------------------------------
  3793. - Updated IO Open functions according to changes in ImagingTypes.
  3794. - Fixed bug in SplitImage that could cause wrong size of edge chunks.
  3795. - Metadata support fixes and extensions (frame delays, animation loops).
  3796. -- 0.26.5 Changes/Bug Fixes ---------------------------------
  3797. - Started reworking exception raising to keep the original class type
  3798. (e.g. in NewImage EOutOfMemory could be raised but was hidden
  3799. by EImagingError raised afterwards in NewImage try/except).
  3800. - Fixed possible AV in Rotate45 subproc of RotateImage.
  3801. - Added ReadRawXXX and WriteRawXXX functions for raw image bits IO.
  3802. - Implemented ImagingBinaryTreshold option.
  3803. - Added support for simple image metadata loading/saving.
  3804. - Moved file format definition (name, exts, caps, ...) from
  3805. constructor to new Define method.
  3806. - Fixed some memory leaks caused by failures during image loading.
  3807. -- 0.26.3 Changes/Bug Fixes ---------------------------------
  3808. - Extended RotateImage to allow arbitrary angle rotations.
  3809. - Reversed the order file formats list is searched so
  3810. if you register a new one it will be found sooner than
  3811. built in formats.
  3812. - Fixed memory leak in ResizeImage ocurring when resizing
  3813. indexed images.
  3814. -- 0.26.1 Changes/Bug Fixes ---------------------------------
  3815. - Added position/size checks to LoadFromStream functions.
  3816. - Changed conditional compilation in impl. uses section to reflect changes
  3817. in LINK symbols.
  3818. -- 0.24.3 Changes/Bug Fixes ---------------------------------
  3819. - GenerateMipMaps now generates all smaller levels from
  3820. original big image (better results when using more advanced filters).
  3821. Also conversion to compatible image format is now done here not
  3822. in FillMipMapLevel (that is called for every mipmap level).
  3823. -- 0.23 Changes/Bug Fixes -----------------------------------
  3824. - MakePaletteForImages now works correctly for indexed and special format images
  3825. - Fixed bug in StretchRect: Image was not properly stretched if
  3826. src and dst dimensions differed only in height.
  3827. - ConvertImage now fills new image with zeroes to avoid random data in
  3828. some conversions (RGB->XRGB)
  3829. - Changed RegisterOption procedure to function
  3830. - Changed bunch of palette functions from low level interface to procedure
  3831. (there was no reason for them to be functions).
  3832. - Changed FreeImage and FreeImagesInArray functions to procedures.
  3833. - Added many assertions, come try-finally, other checks, and small code
  3834. and doc changes.
  3835. -- 0.21 Changes/Bug Fixes -----------------------------------
  3836. - GenerateMipMaps threw failed assertion when input was indexed or special,
  3837. fixed.
  3838. - Added CheckOptionsValidity to TImageFileFormat and its decendants.
  3839. - Unit ImagingExtras which registers file formats in Extras package
  3840. is now automatically added to uses clause if LINK_EXTRAS symbol is
  3841. defined in ImagingOptions.inc file.
  3842. - Added EnumFileFormats function to low level interface.
  3843. - Fixed bug in SwapChannels which could cause AV when swapping alpha
  3844. channel of A8R8G8B8 images.
  3845. - Converting loaded images to ImagingOverrideFormat is now done
  3846. in PostLoadCheck method to avoid code duplicity.
  3847. - Added GetFileFormatCount and GetFileFormatAtIndex functions
  3848. - Bug in ConvertImage: if some format was converted to similar format
  3849. only with swapped channels (R16G16B16<>B16G16R16) then channels were
  3850. swapped correctly but new data format (swapped one) was not set.
  3851. - Made TImageFileFormat.MakeCompatible public non-virtual method
  3852. (and modified its function). Created new virtual
  3853. ConvertToSupported which should be overriden by descendants.
  3854. Main reason for doint this is to avoid duplicate code that was in all
  3855. TImageFileFormat's descendants.
  3856. - Changed TImageFileFormat.GetFormatInfo's result type to TImageFormatInfo.
  3857. - Split overloaded FindImageFileFormat functions to
  3858. FindImageFileFormatByClass and FindImageFileFormatByExt and created new
  3859. FindImageFileFormatByName which operates on whole filenames.
  3860. - Function GetExtensionFilterIndex renamed to GetFileNameFilterIndex
  3861. (because it now works with filenames not extensions).
  3862. - DetermineFileFormat now first searches by filename and if not found
  3863. then by data.
  3864. - Added TestFileName method to TImageFileFormat.
  3865. - Updated GetImageFileFormatsFilter to uses Masks instead of Extensions
  3866. property of TImageFileFormat. Also you can now request
  3867. OpenDialog and SaveDialog type filters
  3868. - Added Masks property and AddMasks method to TImageFileFormat.
  3869. AddMasks replaces AddExtensions, it uses filename masks instead
  3870. of sime filename extensions to identify supported files.
  3871. - Changed TImageFileFormat.LoadData procedure to function and
  3872. moved varios duplicate code from its descandats (check index,...)
  3873. here to TImageFileFormat helper methods.
  3874. - Changed TImageFileFormat.SaveData procedure to function and
  3875. moved varios duplicate code from its descandats (check index,...)
  3876. here to TImageFileFormat helper methods.
  3877. - Removed RAISE_EXCEPTIONS define, exceptions are now raised everytime
  3878. - Added MustBeFreed parameter to TImageFileFormat.MakeComptible method
  3879. that indicates that compatible image returned by this method must be
  3880. freed after its usage.
  3881. -- 0.19 Changes/Bug Fixes -----------------------------------
  3882. - fixed bug in NewImage: if given format was ifDefault it wasn't
  3883. replaced with DefaultImageFormat constant which caused problems later
  3884. in other units
  3885. - fixed bug in RotateImage which caused that rotated special format
  3886. images were whole black
  3887. - LoadImageFromXXX and LoadMultiImageFromXXX now use DetermineXXXFormat
  3888. when choosing proper loader, this eliminated need for Ext parameter
  3889. in stream and memory loading functions
  3890. - added GetVersionStr function
  3891. - fixed bug in ResizeImage which caued indexed images to lose their
  3892. palette during process resulting in whole black image
  3893. - Clipping in ...Rect functions now uses clipping procs from ImagingUtility,
  3894. it also works better
  3895. - FillRect optimization for 8, 16, and 32 bit formats
  3896. - added pixel set/get functions to low level interface:
  3897. GetPixelDirect, SetPixelDirect, GetPixel32, SetPixel32,
  3898. GetPixelFP, SetPixelFP
  3899. - removed GetPixelBytes low level intf function - redundant
  3900. (same data can be obtained by GetImageFormatInfo)
  3901. - made small changes in many parts of library to compile
  3902. on AMD64 CPU (Linux with FPC)
  3903. - changed InitImage to procedure (function was pointless)
  3904. - Method TestFormat of TImageFileFormat class made public
  3905. (was protected)
  3906. - added function IsFileFormatSupported to low level interface
  3907. (contributed by Paul Michell)
  3908. - fixed some missing format arguments from error strings
  3909. which caused Format function to raise exception
  3910. - removed forgotten debug code that disabled filtered resizing of images with
  3911. channel bitcounts > 8
  3912. -- 0.17 Changes/Bug Fixes -----------------------------------
  3913. - changed order of parameters of CopyRect function
  3914. - GenerateMipMaps now filters mipmap levels
  3915. - ResizeImage functions was extended to allow bilinear and bicubic filtering
  3916. - added StretchRect function to low level interface
  3917. - added functions GetImageFileFormatsFilter, GetFilterIndexExtension,
  3918. and GetExtensionFilterIndex
  3919. -- 0.15 Changes/Bug Fixes -----------------------------------
  3920. - added function RotateImage to low level interface
  3921. - moved TImageFormatInfo record and types required by it to
  3922. ImagingTypes unit, changed GetImageFormatInfo low level
  3923. interface function to return TImageFormatInfo instead of short info
  3924. - added checking of options values validity before they are used
  3925. - fixed possible memory leak in CloneImage
  3926. - added ReplaceColor function to low level interface
  3927. - new function FindImageFileFormat by class added
  3928. -- 0.13 Changes/Bug Fixes -----------------------------------
  3929. - added DetermineFileFormat, DetermineStreamFormat, DetermineMemoryFormat,
  3930. GetPixelsSize functions to low level interface
  3931. - added NewPalette, CopyPalette, FreePalette functions
  3932. to low level interface
  3933. - added MapImageToPalette, FillRect, SplitImage, MakePaletteForImages
  3934. functions to low level interface
  3935. - fixed buggy FillCustomPalette function (possible div by zero and others)
  3936. - added CopyRect function to low level interface
  3937. - Member functions of TImageFormatInfo record implemented for all formats
  3938. - before saving images TestImagesInArray is called now
  3939. - added TestImagesInArray function to low level interface
  3940. - added GenerateMipMaps function to low level interface
  3941. - stream position in load/save from/to stream is now set to position before
  3942. function was called if error occurs
  3943. - when error occured during load/save from/to file file handle
  3944. was not released
  3945. - CloneImage returned always False
  3946. }
  3947. end.