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.

1107 lines
35 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 contains class based wrapper to Imaging library.}
  24. unit ImagingClasses;
  25. {$I ImagingOptions.inc}
  26. interface
  27. uses
  28. Types, Classes, ImagingTypes, Imaging, ImagingFormats, ImagingUtility;
  29. type
  30. { Base abstract high level class wrapper to low level Imaging structures and
  31. functions.}
  32. TBaseImage = class(TPersistent)
  33. private
  34. function GetEmpty: Boolean;
  35. protected
  36. FPData: PImageData;
  37. FOnDataSizeChanged: TNotifyEvent;
  38. FOnPixelsChanged: TNotifyEvent;
  39. function GetFormat: TImageFormat; {$IFDEF USE_INLINE}inline;{$ENDIF}
  40. function GetHeight: Integer; {$IFDEF USE_INLINE}inline;{$ENDIF}
  41. function GetSize: Integer; {$IFDEF USE_INLINE}inline;{$ENDIF}
  42. function GetWidth: Integer; {$IFDEF USE_INLINE}inline;{$ENDIF}
  43. function GetBits: Pointer; {$IFDEF USE_INLINE}inline;{$ENDIF}
  44. function GetPalette: PPalette32; {$IFDEF USE_INLINE}inline;{$ENDIF}
  45. function GetPaletteEntries: Integer; {$IFDEF USE_INLINE}inline;{$ENDIF}
  46. function GetScanline(Index: Integer): Pointer;
  47. function GetPixelPointer(X, Y: Integer): Pointer; {$IFDEF USE_INLINE}inline;{$ENDIF}
  48. function GetScanlineSize: Integer; {$IFDEF USE_INLINE}inline;{$ENDIF}
  49. function GetFormatInfo: TImageFormatInfo; {$IFDEF USE_INLINE}inline;{$ENDIF}
  50. function GetValid: Boolean; {$IFDEF USE_INLINE}inline;{$ENDIF}
  51. function GetBoundsRect: TRect;
  52. procedure SetFormat(const Value: TImageFormat); {$IFDEF USE_INLINE}inline;{$ENDIF}
  53. procedure SetHeight(const Value: Integer); {$IFDEF USE_INLINE}inline;{$ENDIF}
  54. procedure SetWidth(const Value: Integer); {$IFDEF USE_INLINE}inline;{$ENDIF}
  55. procedure SetPointer; virtual; abstract;
  56. procedure DoDataSizeChanged; virtual;
  57. procedure DoPixelsChanged; virtual;
  58. public
  59. constructor Create; virtual;
  60. constructor CreateFromImage(AImage: TBaseImage);
  61. destructor Destroy; override;
  62. { Returns info about current image.}
  63. function ToString: string; {$IF (Defined(DCC) and (CompilerVersion >= 20.0)) or Defined(FPC)}override;{$IFEND}
  64. { Creates a new image data with the given size and format. Old image
  65. data is lost. Works only for the current image of TMultiImage.}
  66. procedure RecreateImageData(AWidth, AHeight: Integer; AFormat: TImageFormat);
  67. { Maps underlying image data to given TImageData record. Both TBaseImage and
  68. TImageData now share some image memory (bits). So don't call FreeImage
  69. on TImageData afterwards since this TBaseImage would get really broken.}
  70. procedure MapImageData(const ImageData: TImageData);
  71. { Deletes current image.}
  72. procedure Clear;
  73. { Resizes current image with optional resampling.}
  74. procedure Resize(NewWidth, NewHeight: Integer; Filter: TResizeFilter);
  75. procedure ResizeToFit(FitWidth, FitHeight: Integer; Filter: TResizeFilter; DstImage: TBaseImage);
  76. { Flips current image. Reverses the image along its horizontal axis the top
  77. becomes the bottom and vice versa.}
  78. procedure Flip;
  79. { Mirrors current image. Reverses the image along its vertical axis the left
  80. side becomes the right and vice versa.}
  81. procedure Mirror;
  82. { Rotates image by Angle degrees counterclockwise.}
  83. procedure Rotate(Angle: Single);
  84. { Copies rectangular part of SrcImage to DstImage. No blending is performed -
  85. alpha is simply copied to destination image. Operates also with
  86. negative X and Y coordinates.
  87. Note that copying is fastest for images in the same data format
  88. (and slowest for images in special formats).}
  89. procedure CopyTo(SrcX, SrcY, Width, Height: Integer; DstImage: TBaseImage; DstX, DstY: Integer); overload;
  90. { Copies whole image to DstImage. No blending is performed -
  91. alpha is simply copied to destination image. Operates also with
  92. negative X and Y coordinates.
  93. Note that copying is fastest for images in the same data format
  94. (and slowest for images in special formats).}
  95. procedure CopyTo(DstImage: TBaseImage; DstX, DstY: Integer); overload;
  96. { Stretches the contents of the source rectangle to the destination rectangle
  97. with optional resampling. No blending is performed - alpha is
  98. simply copied/resampled to destination image. Note that stretching is
  99. fastest for images in the same data format (and slowest for
  100. images in special formats).}
  101. procedure StretchTo(SrcX, SrcY, SrcWidth, SrcHeight: Integer; DstImage: TBaseImage; DstX, DstY, DstWidth, DstHeight: Integer; Filter: TResizeFilter);
  102. { Replaces pixels with OldPixel in the given rectangle by NewPixel.
  103. OldPixel and NewPixel should point to the pixels in the same format
  104. as the given image is in.}
  105. procedure ReplaceColor(X, Y, Width, Height: Integer; OldColor, NewColor: Pointer);
  106. { Swaps SrcChannel and DstChannel color or alpha channels of image.
  107. Use ChannelRed, ChannelBlue, ChannelGreen, ChannelAlpha constants to
  108. identify channels.}
  109. procedure SwapChannels(SrcChannel, DstChannel: Integer);
  110. { Loads current image data from file.}
  111. procedure LoadFromFile(const FileName: string); virtual;
  112. { Loads current image data from stream.}
  113. procedure LoadFromStream(Stream: TStream); virtual;
  114. { Saves current image data to file.}
  115. procedure SaveToFile(const FileName: string);
  116. { Saves current image data to stream. Ext identifies desired image file
  117. format (jpg, png, dds, ...)}
  118. procedure SaveToStream(const Ext: string; Stream: TStream);
  119. { Width of current image in pixels.}
  120. property Width: Integer read GetWidth write SetWidth;
  121. { Height of current image in pixels.}
  122. property Height: Integer read GetHeight write SetHeight;
  123. { Image data format of current image.}
  124. property Format: TImageFormat read GetFormat write SetFormat;
  125. { Size in bytes of current image's data.}
  126. property Size: Integer read GetSize;
  127. { Pointer to memory containing image bits.}
  128. property Bits: Pointer read GetBits;
  129. { Pointer to palette for indexed format images. It is nil for others.
  130. Max palette entry is at index [PaletteEntries - 1].}
  131. property Palette: PPalette32 read GetPalette;
  132. { Number of entries in image's palette}
  133. property PaletteEntries: Integer read GetPaletteEntries;
  134. { Provides indexed access to each line of pixels. Does not work with special
  135. format images (like DXT).}
  136. property Scanline[Index: Integer]: Pointer read GetScanline;
  137. { Returns pointer to image pixel at [X, Y] coordinates.}
  138. property PixelPointer[X, Y: Integer]: Pointer read GetPixelPointer;
  139. { Size/length of one image scanline in bytes.}
  140. property ScanlineSize: Integer read GetScanlineSize;
  141. { Extended image format information.}
  142. property FormatInfo: TImageFormatInfo read GetFormatInfo;
  143. { This gives complete access to underlying TImageData record.
  144. It can be used in functions that take TImageData as parameter
  145. (for example: ReduceColors(SingleImageInstance.ImageData^, 64)).}
  146. property ImageDataPointer: PImageData read FPData;
  147. { Indicates whether the current image is valid (proper format,
  148. allowed dimensions, right size, ...).}
  149. property Valid: Boolean read GetValid;
  150. { Indicates whether image containst any data (size in bytes > 0).}
  151. property Empty: Boolean read GetEmpty;
  152. { Specifies the bounding rectangle of the image.}
  153. property BoundsRect: TRect read GetBoundsRect;
  154. { This event occurs when the image data size has just changed. That means
  155. image width, height, or format has been changed.}
  156. property OnDataSizeChanged: TNotifyEvent read FOnDataSizeChanged write FOnDataSizeChanged;
  157. { This event occurs when some pixels of the image have just changed.}
  158. property OnPixelsChanged: TNotifyEvent read FOnPixelsChanged write FOnPixelsChanged;
  159. end;
  160. { Extension of TBaseImage which uses single TImageData record to
  161. store image. All methods inherited from TBaseImage work with this record.}
  162. TSingleImage = class(TBaseImage)
  163. protected
  164. FImageData: TImageData;
  165. procedure SetPointer; override;
  166. public
  167. constructor Create; override;
  168. constructor CreateFromParams(AWidth, AHeight: Integer; AFormat: TImageFormat = ifDefault);
  169. constructor CreateFromData(const AData: TImageData);
  170. constructor CreateFromFile(const FileName: string);
  171. constructor CreateFromStream(Stream: TStream);
  172. destructor Destroy; override;
  173. { Assigns single image from another single image or multi image.}
  174. procedure Assign(Source: TPersistent); override;
  175. { Assigns single image from image data record.}
  176. procedure AssignFromImageData(const AImageData: TImageData);
  177. end;
  178. { Extension of TBaseImage which uses array of TImageData records to
  179. store multiple images. Images are independent on each other and they don't
  180. share any common characteristic. Each can have different size, format, and
  181. palette. All methods inherited from TBaseImage work only with
  182. active image (it could represent mipmap level, animation frame, or whatever).
  183. Methods whose names contain word 'Multi' work with all images in array
  184. (as well as other methods with obvious names).}
  185. TMultiImage = class(TBaseImage)
  186. protected
  187. FDataArray: TDynImageDataArray;
  188. FActiveImage: Integer;
  189. procedure SetActiveImage(Value: Integer); {$IFDEF USE_INLINE}inline;{$ENDIF}
  190. function GetImageCount: Integer; {$IFDEF USE_INLINE}inline;{$ENDIF}
  191. procedure SetImageCount(Value: Integer);
  192. function GetAllImagesValid: Boolean; {$IFDEF USE_INLINE}inline;{$ENDIF}
  193. function GetImage(Index: Integer): TImageData; {$IFDEF USE_INLINE}inline;{$ENDIF}
  194. procedure SetImage(Index: Integer; Value: TImageData); {$IFDEF USE_INLINE}inline;{$ENDIF}
  195. procedure SetPointer; override;
  196. function PrepareInsert(Index, Count: Integer): Boolean;
  197. procedure DoInsertImages(Index: Integer; const Images: TDynImageDataArray);
  198. procedure DoInsertNew(Index: Integer; AWidth, AHeight: Integer; AFormat: TImageFormat);
  199. public
  200. constructor Create; override;
  201. constructor CreateFromParams(AWidth, AHeight: Integer; AFormat: TImageFormat; ImageCount: Integer);
  202. constructor CreateFromArray(const ADataArray: TDynImageDataArray);
  203. constructor CreateFromFile(const FileName: string);
  204. constructor CreateFromStream(Stream: TStream);
  205. destructor Destroy; override;
  206. { Assigns multi image from another multi image or single image.}
  207. procedure Assign(Source: TPersistent); override;
  208. { Assigns multi image from array of image data records.}
  209. procedure AssignFromArray(const ADataArray: TDynImageDataArray);
  210. { Adds new image at the end of the image array. }
  211. function AddImage(AWidth, AHeight: Integer; AFormat: TImageFormat = ifDefault): Integer; overload;
  212. { Adds existing image at the end of the image array. }
  213. function AddImage(const Image: TImageData): Integer; overload;
  214. { Adds existing image (Active image of a TmultiImage)
  215. at the end of the image array. }
  216. function AddImage(Image: TBaseImage): Integer; overload;
  217. { Adds existing image array ((all images of a multi image))
  218. at the end of the image array. }
  219. procedure AddImages(const Images: TDynImageDataArray); overload;
  220. { Adds existing MultiImage images at the end of the image array. }
  221. procedure AddImages(Images: TMultiImage); overload;
  222. { Inserts new image image at the given position in the image array. }
  223. procedure InsertImage(Index, AWidth, AHeight: Integer; AFormat: TImageFormat = ifDefault); overload;
  224. { Inserts existing image at the given position in the image array. }
  225. procedure InsertImage(Index: Integer; const Image: TImageData); overload;
  226. { Inserts existing image (Active image of a TmultiImage)
  227. at the given position in the image array. }
  228. procedure InsertImage(Index: Integer; Image: TBaseImage); overload;
  229. { Inserts existing image at the given position in the image array. }
  230. procedure InsertImages(Index: Integer; const Images: TDynImageDataArray); overload;
  231. { Inserts existing images (all images of a TmultiImage) at
  232. the given position in the image array. }
  233. procedure InsertImages(Index: Integer; Images: TMultiImage); overload;
  234. { Exchanges two images at the given positions in the image array. }
  235. procedure ExchangeImages(Index1, Index2: Integer);
  236. { Deletes image at the given position in the image array.}
  237. procedure DeleteImage(Index: Integer);
  238. { Rearranges images so that the first image will become last and vice versa.}
  239. procedure ReverseImages;
  240. { Deletes all images.}
  241. procedure ClearAll;
  242. { Converts all images to another image data format.}
  243. procedure ConvertImages(Format: TImageFormat);
  244. { Resizes all images.}
  245. procedure ResizeImages(NewWidth, NewHeight: Integer; Filter: TResizeFilter);
  246. { Overloaded loading method that will add new image to multiimage if
  247. image array is empty bero loading. }
  248. procedure LoadFromFile(const FileName: string); override;
  249. { Overloaded loading method that will add new image to multiimage if
  250. image array is empty bero loading. }
  251. procedure LoadFromStream(Stream: TStream); override;
  252. { Loads whole multi image from file.}
  253. procedure LoadMultiFromFile(const FileName: string);
  254. { Loads whole multi image from stream.}
  255. procedure LoadMultiFromStream(Stream: TStream);
  256. { Saves whole multi image to file.}
  257. procedure SaveMultiToFile(const FileName: string);
  258. { Saves whole multi image to stream. Ext identifies desired
  259. image file format (jpg, png, dds, ...).}
  260. procedure SaveMultiToStream(const Ext: string; Stream: TStream);
  261. { Indicates active image of this multi image. All methods inherited
  262. from TBaseImage operate on this image only.}
  263. property ActiveImage: Integer read FActiveImage write SetActiveImage;
  264. { Number of images of this multi image.}
  265. property ImageCount: Integer read GetImageCount write SetImageCount;
  266. { This value is True if all images of this TMultiImage are valid.}
  267. property AllImagesValid: Boolean read GetAllImagesValid;
  268. { This gives complete access to underlying TDynImageDataArray.
  269. It can be used in functions that take TDynImageDataArray
  270. as parameter.}
  271. property DataArray: TDynImageDataArray read FDataArray;
  272. { Array property for accessing individual images of TMultiImage. When you
  273. set image at given index the old image is freed and the source is cloned.}
  274. property Images[Index: Integer]: TImageData read GetImage write SetImage; default;
  275. end;
  276. implementation
  277. const
  278. DefaultWidth = 16;
  279. Defaultheight = 16;
  280. function GetArrayFromImageData(const ImageData: TImageData): TDynImageDataArray;
  281. begin
  282. SetLength(Result, 1);
  283. Result[0] := ImageData;
  284. end;
  285. { TBaseImage class implementation }
  286. constructor TBaseImage.Create;
  287. begin
  288. SetPointer;
  289. end;
  290. constructor TBaseImage.CreateFromImage(AImage: TBaseImage);
  291. begin
  292. Create;
  293. Assign(AImage);
  294. end;
  295. destructor TBaseImage.Destroy;
  296. begin
  297. inherited Destroy;
  298. end;
  299. function TBaseImage.GetWidth: Integer;
  300. begin
  301. if Valid then
  302. Result := FPData.Width
  303. else
  304. Result := 0;
  305. end;
  306. function TBaseImage.GetHeight: Integer;
  307. begin
  308. if Valid then
  309. Result := FPData.Height
  310. else
  311. Result := 0;
  312. end;
  313. function TBaseImage.GetFormat: TImageFormat;
  314. begin
  315. if Valid then
  316. Result := FPData.Format
  317. else
  318. Result := ifUnknown;
  319. end;
  320. function TBaseImage.GetScanline(Index: Integer): Pointer;
  321. var
  322. Info: TImageFormatInfo;
  323. begin
  324. if Valid then
  325. begin
  326. Info := GetFormatInfo;
  327. if not Info.IsSpecial then
  328. Result := ImagingFormats.GetScanLine(FPData.Bits, Info, FPData.Width, Index)
  329. else
  330. Result := FPData.Bits;
  331. end
  332. else
  333. Result := nil;
  334. end;
  335. function TBaseImage.GetScanlineSize: Integer;
  336. begin
  337. if Valid then
  338. Result := FormatInfo.GetPixelsSize(Format, Width, 1)
  339. else
  340. Result := 0;
  341. end;
  342. function TBaseImage.GetPixelPointer(X, Y: Integer): Pointer;
  343. begin
  344. if Valid then
  345. Result := @PByteArray(FPData.Bits)[(Y * FPData.Width + X) * GetFormatInfo.BytesPerPixel]
  346. else
  347. Result := nil;
  348. end;
  349. function TBaseImage.GetSize: Integer;
  350. begin
  351. if Valid then
  352. Result := FPData.Size
  353. else
  354. Result := 0;
  355. end;
  356. function TBaseImage.GetBits: Pointer;
  357. begin
  358. if Valid then
  359. Result := FPData.Bits
  360. else
  361. Result := nil;
  362. end;
  363. function TBaseImage.GetPalette: PPalette32;
  364. begin
  365. if Valid then
  366. Result := FPData.Palette
  367. else
  368. Result := nil;
  369. end;
  370. function TBaseImage.GetPaletteEntries: Integer;
  371. begin
  372. Result := GetFormatInfo.PaletteEntries;
  373. end;
  374. function TBaseImage.GetFormatInfo: TImageFormatInfo;
  375. begin
  376. if Valid then
  377. Imaging.GetImageFormatInfo(FPData.Format, Result)
  378. else
  379. FillChar(Result, SizeOf(Result), 0);
  380. end;
  381. function TBaseImage.GetValid: Boolean;
  382. begin
  383. Result := Assigned(FPData) and Imaging.TestImage(FPData^);
  384. end;
  385. function TBaseImage.GetBoundsRect: TRect;
  386. begin
  387. Result := Rect(0, 0, GetWidth, GetHeight);
  388. end;
  389. function TBaseImage.GetEmpty: Boolean;
  390. begin
  391. Result := FPData.Size = 0;
  392. end;
  393. procedure TBaseImage.SetWidth(const Value: Integer);
  394. begin
  395. Resize(Value, GetHeight, rfNearest);
  396. end;
  397. procedure TBaseImage.SetHeight(const Value: Integer);
  398. begin
  399. Resize(GetWidth, Value, rfNearest);
  400. end;
  401. procedure TBaseImage.SetFormat(const Value: TImageFormat);
  402. begin
  403. if Valid and Imaging.ConvertImage(FPData^, Value) then
  404. DoDataSizeChanged;
  405. end;
  406. procedure TBaseImage.DoDataSizeChanged;
  407. begin
  408. if Assigned(FOnDataSizeChanged) then
  409. FOnDataSizeChanged(Self);
  410. DoPixelsChanged;
  411. end;
  412. procedure TBaseImage.DoPixelsChanged;
  413. begin
  414. if Assigned(FOnPixelsChanged) then
  415. FOnPixelsChanged(Self);
  416. end;
  417. procedure TBaseImage.RecreateImageData(AWidth, AHeight: Integer; AFormat: TImageFormat);
  418. begin
  419. if Assigned(FPData) and Imaging.NewImage(AWidth, AHeight, AFormat, FPData^) then
  420. DoDataSizeChanged;
  421. end;
  422. procedure TBaseImage.MapImageData(const ImageData: TImageData);
  423. begin
  424. Clear;
  425. FPData.Width := ImageData.Width;
  426. FPData.Height := ImageData.Height;
  427. FPData.Format := ImageData.Format;
  428. FPData.Size := ImageData.Size;
  429. FPData.Bits := ImageData.Bits;
  430. FPData.Palette := ImageData.Palette;
  431. end;
  432. procedure TBaseImage.Clear;
  433. begin
  434. FreeImage(FPData^);
  435. end;
  436. procedure TBaseImage.Resize(NewWidth, NewHeight: Integer; Filter: TResizeFilter);
  437. begin
  438. if Valid and Imaging.ResizeImage(FPData^, NewWidth, NewHeight, Filter) then
  439. DoDataSizeChanged;
  440. end;
  441. procedure TBaseImage.ResizeToFit(FitWidth, FitHeight: Integer;
  442. Filter: TResizeFilter; DstImage: TBaseImage);
  443. begin
  444. if Valid and Assigned(DstImage) then
  445. begin
  446. Imaging.ResizeImageToFit(FPData^, FitWidth, FitHeight, Filter,
  447. DstImage.FPData^);
  448. DstImage.DoDataSizeChanged;
  449. end;
  450. end;
  451. procedure TBaseImage.Flip;
  452. begin
  453. if Valid and Imaging.FlipImage(FPData^) then
  454. DoPixelsChanged;
  455. end;
  456. procedure TBaseImage.Mirror;
  457. begin
  458. if Valid and Imaging.MirrorImage(FPData^) then
  459. DoPixelsChanged;
  460. end;
  461. procedure TBaseImage.Rotate(Angle: Single);
  462. begin
  463. if Valid then
  464. begin
  465. Imaging.RotateImage(FPData^, Angle);
  466. DoPixelsChanged;
  467. end;
  468. end;
  469. procedure TBaseImage.CopyTo(SrcX, SrcY, Width, Height: Integer;
  470. DstImage: TBaseImage; DstX, DstY: Integer);
  471. begin
  472. if Valid and Assigned(DstImage) and DstImage.Valid then
  473. begin
  474. Imaging.CopyRect(FPData^, SrcX, SrcY, Width, Height, DstImage.FPData^, DstX, DstY);
  475. DstImage.DoPixelsChanged;
  476. end;
  477. end;
  478. procedure TBaseImage.CopyTo(DstImage: TBaseImage; DstX, DstY: Integer);
  479. begin
  480. if Valid and Assigned(DstImage) and DstImage.Valid then
  481. begin
  482. Imaging.CopyRect(FPData^, 0, 0, Width, Height, DstImage.FPData^, DstX, DstY);
  483. DstImage.DoPixelsChanged;
  484. end;
  485. end;
  486. procedure TBaseImage.StretchTo(SrcX, SrcY, SrcWidth, SrcHeight: Integer;
  487. DstImage: TBaseImage; DstX, DstY, DstWidth, DstHeight: Integer; Filter: TResizeFilter);
  488. begin
  489. if Valid and Assigned(DstImage) and DstImage.Valid then
  490. begin
  491. Imaging.StretchRect(FPData^, SrcX, SrcY, SrcWidth, SrcHeight,
  492. DstImage.FPData^, DstX, DstY, DstWidth, DstHeight, Filter);
  493. DstImage.DoPixelsChanged;
  494. end;
  495. end;
  496. procedure TBaseImage.ReplaceColor(X, Y, Width, Height: Integer; OldColor,
  497. NewColor: Pointer);
  498. begin
  499. if Valid then
  500. begin
  501. Imaging.ReplaceColor(FPData^, X, Y, Width, Height, OldColor, NewColor);
  502. DoPixelsChanged;
  503. end;
  504. end;
  505. procedure TBaseImage.SwapChannels(SrcChannel, DstChannel: Integer);
  506. begin
  507. if Valid then
  508. begin
  509. Imaging.SwapChannels(FPData^, SrcChannel, DstChannel);
  510. DoPixelsChanged;
  511. end;
  512. end;
  513. function TBaseImage.ToString: string;
  514. begin
  515. Result := Iff(Valid, Imaging.ImageToStr(FPData^), 'empty image');
  516. end;
  517. procedure TBaseImage.LoadFromFile(const FileName: string);
  518. begin
  519. if Assigned(FPData) and Imaging.LoadImageFromFile(FileName, FPData^) then
  520. DoDataSizeChanged;
  521. end;
  522. procedure TBaseImage.LoadFromStream(Stream: TStream);
  523. begin
  524. if Assigned(FPData) and Imaging.LoadImageFromStream(Stream, FPData^) then
  525. DoDataSizeChanged;
  526. end;
  527. procedure TBaseImage.SaveToFile(const FileName: string);
  528. begin
  529. if Valid then
  530. Imaging.SaveImageToFile(FileName, FPData^);
  531. end;
  532. procedure TBaseImage.SaveToStream(const Ext: string; Stream: TStream);
  533. begin
  534. if Valid then
  535. Imaging.SaveImageToStream(Ext, Stream, FPData^);
  536. end;
  537. { TSingleImage class implementation }
  538. constructor TSingleImage.Create;
  539. begin
  540. inherited Create;
  541. Clear;
  542. end;
  543. constructor TSingleImage.CreateFromParams(AWidth, AHeight: Integer; AFormat: TImageFormat);
  544. begin
  545. inherited Create;
  546. RecreateImageData(AWidth, AHeight, AFormat);
  547. end;
  548. constructor TSingleImage.CreateFromData(const AData: TImageData);
  549. begin
  550. inherited Create;
  551. AssignFromImageData(AData);
  552. end;
  553. constructor TSingleImage.CreateFromFile(const FileName: string);
  554. begin
  555. inherited Create;
  556. LoadFromFile(FileName);
  557. end;
  558. constructor TSingleImage.CreateFromStream(Stream: TStream);
  559. begin
  560. inherited Create;
  561. LoadFromStream(Stream);
  562. end;
  563. destructor TSingleImage.Destroy;
  564. begin
  565. Imaging.FreeImage(FImageData);
  566. inherited Destroy;
  567. end;
  568. procedure TSingleImage.SetPointer;
  569. begin
  570. FPData := @FImageData;
  571. end;
  572. procedure TSingleImage.Assign(Source: TPersistent);
  573. begin
  574. if Source = nil then
  575. begin
  576. Clear;
  577. end
  578. else if Source is TSingleImage then
  579. begin
  580. AssignFromImageData(TSingleImage(Source).FImageData);
  581. end
  582. else if Source is TMultiImage then
  583. begin
  584. if TMultiImage(Source).Valid then
  585. AssignFromImageData(TMultiImage(Source).FPData^)
  586. else
  587. Clear;
  588. end
  589. else
  590. inherited Assign(Source);
  591. end;
  592. procedure TSingleImage.AssignFromImageData(const AImageData: TImageData);
  593. begin
  594. if Imaging.TestImage(AImageData) then
  595. begin
  596. Imaging.CloneImage(AImageData, FImageData);
  597. DoDataSizeChanged;
  598. end
  599. else
  600. Clear;
  601. end;
  602. { TMultiImage class implementation }
  603. constructor TMultiImage.Create;
  604. begin
  605. inherited Create;
  606. end;
  607. constructor TMultiImage.CreateFromParams(AWidth, AHeight: Integer;
  608. AFormat: TImageFormat; ImageCount: Integer);
  609. var
  610. I: Integer;
  611. begin
  612. Imaging.FreeImagesInArray(FDataArray);
  613. SetLength(FDataArray, ImageCount);
  614. for I := 0 to GetImageCount - 1 do
  615. Imaging.NewImage(AWidth, AHeight, AFormat, FDataArray[I]);
  616. if GetImageCount > 0 then
  617. SetActiveImage(0);
  618. end;
  619. constructor TMultiImage.CreateFromArray(const ADataArray: TDynImageDataArray);
  620. begin
  621. AssignFromArray(ADataArray);
  622. end;
  623. constructor TMultiImage.CreateFromFile(const FileName: string);
  624. begin
  625. LoadMultiFromFile(FileName);
  626. end;
  627. constructor TMultiImage.CreateFromStream(Stream: TStream);
  628. begin
  629. LoadMultiFromStream(Stream);
  630. end;
  631. destructor TMultiImage.Destroy;
  632. begin
  633. Imaging.FreeImagesInArray(FDataArray);
  634. inherited Destroy;
  635. end;
  636. procedure TMultiImage.SetActiveImage(Value: Integer);
  637. begin
  638. FActiveImage := Value;
  639. SetPointer;
  640. end;
  641. function TMultiImage.GetImageCount: Integer;
  642. begin
  643. Result := Length(FDataArray);
  644. end;
  645. procedure TMultiImage.SetImageCount(Value: Integer);
  646. var
  647. I, OldCount: Integer;
  648. begin
  649. if Value > GetImageCount then
  650. begin
  651. // Create new empty images if array will be enlarged
  652. OldCount := GetImageCount;
  653. SetLength(FDataArray, Value);
  654. for I := OldCount to Value - 1 do
  655. Imaging.NewImage(DefaultWidth, DefaultHeight, ifDefault, FDataArray[I]);
  656. end
  657. else
  658. begin
  659. // Free images that exceed desired count and shrink array
  660. for I := Value to GetImageCount - 1 do
  661. Imaging.FreeImage(FDataArray[I]);
  662. SetLength(FDataArray, Value);
  663. end;
  664. SetPointer;
  665. end;
  666. function TMultiImage.GetAllImagesValid: Boolean;
  667. begin
  668. Result := (GetImageCount > 0) and TestImagesInArray(FDataArray);
  669. end;
  670. function TMultiImage.GetImage(Index: Integer): TImageData;
  671. begin
  672. if (Index >= 0) and (Index < GetImageCount) then
  673. Result := FDataArray[Index];
  674. end;
  675. procedure TMultiImage.SetImage(Index: Integer; Value: TImageData);
  676. begin
  677. if (Index >= 0) and (Index < GetImageCount) then
  678. Imaging.CloneImage(Value, FDataArray[Index]);
  679. end;
  680. procedure TMultiImage.SetPointer;
  681. begin
  682. if GetImageCount > 0 then
  683. begin
  684. FActiveImage := ClampInt(FActiveImage, 0, GetImageCount - 1);
  685. FPData := @FDataArray[FActiveImage];
  686. end
  687. else
  688. begin
  689. FActiveImage := -1;
  690. FPData := nil
  691. end;
  692. end;
  693. function TMultiImage.PrepareInsert(Index, Count: Integer): Boolean;
  694. var
  695. I: Integer;
  696. begin
  697. // Inserting to empty image will add image at index 0
  698. if GetImageCount = 0 then
  699. Index := 0;
  700. if (Index >= 0) and (Index <= GetImageCount) and (Count > 0) then
  701. begin
  702. SetLength(FDataArray, GetImageCount + Count);
  703. if Index < GetImageCount - 1 then
  704. begin
  705. // Move imges to new position
  706. System.Move(FDataArray[Index], FDataArray[Index + Count],
  707. (GetImageCount - Count - Index) * SizeOf(TImageData));
  708. // Null old images, not free them!
  709. for I := Index to Index + Count - 1 do
  710. InitImage(FDataArray[I]);
  711. end;
  712. Result := True;
  713. end
  714. else
  715. Result := False;
  716. end;
  717. procedure TMultiImage.DoInsertImages(Index: Integer; const Images: TDynImageDataArray);
  718. var
  719. I, Len: Integer;
  720. begin
  721. Len := Length(Images);
  722. if PrepareInsert(Index, Len) then
  723. begin
  724. for I := 0 to Len - 1 do
  725. Imaging.CloneImage(Images[I], FDataArray[Index + I]);
  726. end;
  727. end;
  728. procedure TMultiImage.DoInsertNew(Index, AWidth, AHeight: Integer;
  729. AFormat: TImageFormat);
  730. begin
  731. if PrepareInsert(Index, 1) then
  732. Imaging.NewImage(AWidth, AHeight, AFormat, FDataArray[Index]);
  733. end;
  734. procedure TMultiImage.Assign(Source: TPersistent);
  735. var
  736. Arr: TDynImageDataArray;
  737. begin
  738. if Source = nil then
  739. begin
  740. ClearAll;
  741. end
  742. else if Source is TMultiImage then
  743. begin
  744. AssignFromArray(TMultiImage(Source).FDataArray);
  745. SetActiveImage(TMultiImage(Source).ActiveImage);
  746. end
  747. else if Source is TSingleImage then
  748. begin
  749. SetLength(Arr, 1);
  750. Arr[0] := TSingleImage(Source).FImageData;
  751. AssignFromArray(Arr);
  752. end
  753. else
  754. inherited Assign(Source);
  755. end;
  756. procedure TMultiImage.AssignFromArray(const ADataArray: TDynImageDataArray);
  757. var
  758. I: Integer;
  759. begin
  760. Imaging.FreeImagesInArray(FDataArray);
  761. SetLength(FDataArray, Length(ADataArray));
  762. for I := 0 to GetImageCount - 1 do
  763. begin
  764. // Clone only valid images
  765. if Imaging.TestImage(ADataArray[I]) then
  766. Imaging.CloneImage(ADataArray[I], FDataArray[I])
  767. else
  768. Imaging.NewImage(DefaultWidth, DefaultHeight, ifDefault, FDataArray[I]);
  769. end;
  770. if GetImageCount > 0 then
  771. SetActiveImage(0);
  772. end;
  773. function TMultiImage.AddImage(AWidth, AHeight: Integer; AFormat: TImageFormat): Integer;
  774. begin
  775. Result := GetImageCount;
  776. DoInsertNew(Result, AWidth, AHeight, AFormat);
  777. end;
  778. function TMultiImage.AddImage(const Image: TImageData): Integer;
  779. begin
  780. Result := GetImageCount;
  781. DoInsertImages(Result, GetArrayFromImageData(Image));
  782. end;
  783. function TMultiImage.AddImage(Image: TBaseImage): Integer;
  784. begin
  785. if Assigned(Image) and Image.Valid then
  786. begin
  787. Result := GetImageCount;
  788. DoInsertImages(Result, GetArrayFromImageData(Image.FPData^));
  789. end
  790. else
  791. Result := -1;
  792. end;
  793. procedure TMultiImage.AddImages(const Images: TDynImageDataArray);
  794. begin
  795. DoInsertImages(GetImageCount, Images);
  796. end;
  797. procedure TMultiImage.AddImages(Images: TMultiImage);
  798. begin
  799. DoInsertImages(GetImageCount, Images.FDataArray);
  800. end;
  801. procedure TMultiImage.InsertImage(Index, AWidth, AHeight: Integer;
  802. AFormat: TImageFormat);
  803. begin
  804. DoInsertNew(Index, AWidth, AHeight, AFormat);
  805. end;
  806. procedure TMultiImage.InsertImage(Index: Integer; const Image: TImageData);
  807. begin
  808. DoInsertImages(Index, GetArrayFromImageData(Image));
  809. end;
  810. procedure TMultiImage.InsertImage(Index: Integer; Image: TBaseImage);
  811. begin
  812. if Assigned(Image) and Image.Valid then
  813. DoInsertImages(Index, GetArrayFromImageData(Image.FPData^));
  814. end;
  815. procedure TMultiImage.InsertImages(Index: Integer;
  816. const Images: TDynImageDataArray);
  817. begin
  818. DoInsertImages(Index, FDataArray);
  819. end;
  820. procedure TMultiImage.InsertImages(Index: Integer; Images: TMultiImage);
  821. begin
  822. DoInsertImages(Index, Images.FDataArray);
  823. end;
  824. procedure TMultiImage.ExchangeImages(Index1, Index2: Integer);
  825. var
  826. TempData: TImageData;
  827. begin
  828. if (Index1 >= 0) and (Index1 < GetImageCount) and
  829. (Index2 >= 0) and (Index2 < GetImageCount) then
  830. begin
  831. TempData := FDataArray[Index1];
  832. FDataArray[Index1] := FDataArray[Index2];
  833. FDataArray[Index2] := TempData;
  834. end;
  835. end;
  836. procedure TMultiImage.DeleteImage(Index: Integer);
  837. var
  838. I: Integer;
  839. begin
  840. if (Index >= 0) and (Index < GetImageCount) then
  841. begin
  842. // Free image at index to be deleted
  843. Imaging.FreeImage(FDataArray[Index]);
  844. if Index < GetImageCount - 1 then
  845. begin
  846. // Move images to new indices if necessary
  847. for I := Index to GetImageCount - 2 do
  848. FDataArray[I] := FDataArray[I + 1];
  849. end;
  850. // Set new array length and update pointer to active image
  851. SetLength(FDataArray, GetImageCount - 1);
  852. SetPointer;
  853. end;
  854. end;
  855. procedure TMultiImage.ClearAll;
  856. begin
  857. ImageCount := 0;
  858. end;
  859. procedure TMultiImage.ConvertImages(Format: TImageFormat);
  860. var
  861. I: Integer;
  862. begin
  863. for I := 0 to GetImageCount - 1 do
  864. Imaging.ConvertImage(FDataArray[I], Format);
  865. end;
  866. procedure TMultiImage.ResizeImages(NewWidth, NewHeight: Integer;
  867. Filter: TResizeFilter);
  868. var
  869. I: Integer;
  870. begin
  871. for I := 0 to GetImageCount - 1 do
  872. Imaging.ResizeImage(FDataArray[I], NewWidth, NewHeight, Filter);
  873. end;
  874. procedure TMultiImage.ReverseImages;
  875. var
  876. I: Integer;
  877. begin
  878. for I := 0 to GetImageCount div 2 do
  879. ExchangeImages(I, GetImageCount - 1 - I);
  880. end;
  881. procedure TMultiImage.LoadFromFile(const FileName: string);
  882. begin
  883. if GetImageCount = 0 then
  884. ImageCount := 1;
  885. inherited LoadFromFile(FileName);
  886. end;
  887. procedure TMultiImage.LoadFromStream(Stream: TStream);
  888. begin
  889. if GetImageCount = 0 then
  890. ImageCount := 1;
  891. inherited LoadFromStream(Stream);
  892. end;
  893. procedure TMultiImage.LoadMultiFromFile(const FileName: string);
  894. begin
  895. Imaging.LoadMultiImageFromFile(FileName, FDataArray);
  896. SetActiveImage(0);
  897. end;
  898. procedure TMultiImage.LoadMultiFromStream(Stream: TStream);
  899. begin
  900. Imaging.LoadMultiImageFromStream(Stream, FDataArray);
  901. SetActiveImage(0);
  902. end;
  903. procedure TMultiImage.SaveMultiToFile(const FileName: string);
  904. begin
  905. Imaging.SaveMultiImageToFile(FileName, FDataArray);
  906. end;
  907. procedure TMultiImage.SaveMultiToStream(const Ext: string; Stream: TStream);
  908. begin
  909. Imaging.SaveMultiImageToStream(Ext, Stream, FDataArray);
  910. end;
  911. {
  912. File Notes:
  913. -- TODOS ----------------------------------------------------
  914. - nothing now
  915. -- 0.77.1 ---------------------------------------------------
  916. - Added TSingleImage.AssignFromData and TMultiImage.AssigntFromArray
  917. as a replacement for constructors used as methods (that is
  918. compiler error in Delphi XE3).
  919. - Added TBaseImage.ResizeToFit method.
  920. - Changed TMultiImage to have default state with no images.
  921. - TMultiImage.AddImage now returns index of newly added image.
  922. - Fixed img index bug in TMultiImage.ResizeImages
  923. -- 0.26.5 Changes/Bug Fixes ---------------------------------
  924. - Added MapImageData method to TBaseImage
  925. - Added Empty property to TBaseImage.
  926. - Added Clear method to TBaseImage.
  927. - Added ScanlineSize property to TBaseImage.
  928. -- 0.24.3 Changes/Bug Fixes ---------------------------------
  929. - Added TMultiImage.ReverseImages method.
  930. -- 0.23 Changes/Bug Fixes -----------------------------------
  931. - Added SwapChannels method to TBaseImage.
  932. - Added ReplaceColor method to TBaseImage.
  933. - Added ToString method to TBaseImage.
  934. -- 0.21 Changes/Bug Fixes -----------------------------------
  935. - Inserting images to empty MultiImage will act as Add method.
  936. - MultiImages with empty arrays will now create one image when
  937. LoadFromFile or LoadFromStream is called.
  938. - Fixed bug that caused AVs when getting props like Width, Height, asn Size
  939. and when inlining was off. There was call to Iff but with inlining disabled
  940. params like FPData.Size were evaluated and when FPData was nil => AV.
  941. - Added many FPData validity checks to many methods. There were AVs
  942. when calling most methods on empty TMultiImage.
  943. - Added AllImagesValid property to TMultiImage.
  944. - Fixed memory leak in TMultiImage.CreateFromParams.
  945. -- 0.19 Changes/Bug Fixes -----------------------------------
  946. - added ResizeImages method to TMultiImage
  947. - removed Ext parameter from various LoadFromStream methods, no
  948. longer needed
  949. - fixed various issues concerning ActiveImage of TMultiImage
  950. (it pointed to invalid location after some operations)
  951. - most of property set/get methods are now inline
  952. - added PixelPointers property to TBaseImage
  953. - added Images default array property to TMultiImage
  954. - renamed methods in TMultiImage to contain 'Image' instead of 'Level'
  955. - added canvas support
  956. - added OnDataSizeChanged and OnPixelsChanged event to TBaseImage
  957. - renamed TSingleImage.NewImage to RecreateImageData, made public, and
  958. moved to TBaseImage
  959. -- 0.17 Changes/Bug Fixes -----------------------------------
  960. - added props PaletteEntries and ScanLine to TBaseImage
  961. - aded new constructor to TBaseImage that take TBaseImage source
  962. - TMultiImage levels adding and inserting rewritten internally
  963. - added some new functions to TMultiImage: AddLevels, InsertLevels
  964. - added some new functions to TBaseImage: Flip, Mirror, Rotate,
  965. CopyRect, StretchRect
  966. - TBasicImage.Resize has now filter parameter
  967. - new stuff added to TMultiImage (DataArray prop, ConvertLevels)
  968. -- 0.13 Changes/Bug Fixes -----------------------------------
  969. - added AddLevel, InsertLevel, ExchangeLevels and DeleteLevel
  970. methods to TMultiImage
  971. - added TBaseImage, TSingleImage and TMultiImage with initial
  972. members
  973. }
  974. end.