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.

4464 lines
137 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 manages information about all image data formats and contains
  24. low level format conversion, manipulation, and other related functions.}
  25. unit ImagingFormats;
  26. {$I ImagingOptions.inc}
  27. interface
  28. uses
  29. ImagingTypes, Imaging, ImagingUtility;
  30. type
  31. TImageFormatInfoArray = array[TImageFormat] of PImageFormatInfo;
  32. PImageFormatInfoArray = ^TImageFormatInfoArray;
  33. { Additional image manipulation functions (usually used internally by Imaging unit) }
  34. type
  35. { Color reduction operations.}
  36. TReduceColorsAction = (raCreateHistogram, raUpdateHistogram, raMakeColorMap,
  37. raMapImage);
  38. TReduceColorsActions = set of TReduceColorsAction;
  39. const
  40. AllReduceColorsActions = [raCreateHistogram, raUpdateHistogram,
  41. raMakeColorMap, raMapImage];
  42. { Reduces the number of colors of source. Src is bits of source image
  43. (ARGB or floating point) and Dst is in some indexed format. MaxColors
  44. is the number of colors to which reduce and DstPal is palette to which
  45. the resulting colors are written and it must be allocated to at least
  46. MaxColors entries. ChannelMask is 'anded' with every pixel's channel value
  47. when creating color histogram. If $FF is used all 8bits of color channels
  48. are used which can be slow for large images with many colors so you can
  49. use lower masks to speed it up.}
  50. procedure ReduceColorsMedianCut(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
  51. DstInfo: PImageFormatInfo; MaxColors: LongInt; ChannelMask: Byte;
  52. DstPal: PPalette32; Actions: TReduceColorsActions = AllReduceColorsActions);
  53. { Stretches rectangle in source image to rectangle in destination image
  54. using nearest neighbor filtering. It is fast but results look blocky
  55. because there is no interpolation used. SrcImage and DstImage must be
  56. in the same data format. Works for all data formats except special formats.}
  57. procedure StretchNearest(const SrcImage: TImageData; SrcX, SrcY, SrcWidth,
  58. SrcHeight: LongInt; var DstImage: TImageData; DstX, DstY, DstWidth,
  59. DstHeight: LongInt);
  60. type
  61. { Built-in sampling filters.}
  62. TSamplingFilter = (sfNearest, sfLinear, sfCosine, sfHermite, sfQuadratic,
  63. sfGaussian, sfSpline, sfLanczos, sfMitchell, sfCatmullRom);
  64. { Type of custom sampling function}
  65. TFilterFunction = function(Value: Single): Single;
  66. const
  67. { Default resampling filter used for bicubic resizing.}
  68. DefaultCubicFilter = sfCatmullRom;
  69. var
  70. { Built-in filter functions.}
  71. SamplingFilterFunctions: array[TSamplingFilter] of TFilterFunction;
  72. { Default radii of built-in filter functions.}
  73. SamplingFilterRadii: array[TSamplingFilter] of Single;
  74. { Stretches rectangle in source image to rectangle in destination image
  75. with resampling. One of built-in resampling filters defined by
  76. Filter is used. Set WrapEdges to True for seamlessly tileable images.
  77. SrcImage and DstImage must be in the same data format.
  78. Works for all data formats except special and indexed formats.}
  79. procedure StretchResample(const SrcImage: TImageData; SrcX, SrcY, SrcWidth,
  80. SrcHeight: LongInt; var DstImage: TImageData; DstX, DstY, DstWidth,
  81. DstHeight: LongInt; Filter: TSamplingFilter; WrapEdges: Boolean = False); overload;
  82. { Stretches rectangle in source image to rectangle in destination image
  83. with resampling. You can use custom sampling function and filter radius.
  84. Set WrapEdges to True for seamlessly tileable images. SrcImage and DstImage
  85. must be in the same data format.
  86. Works for all data formats except special and indexed formats.}
  87. procedure StretchResample(const SrcImage: TImageData; SrcX, SrcY, SrcWidth,
  88. SrcHeight: LongInt; var DstImage: TImageData; DstX, DstY, DstWidth,
  89. DstHeight: LongInt; Filter: TFilterFunction; Radius: Single;
  90. WrapEdges: Boolean = False); overload;
  91. { Helper for functions that create mipmap levels. BiggerLevel is
  92. valid image and SmallerLevel is empty zeroed image. SmallerLevel is created
  93. with Width and Height dimensions and it is filled with pixels of BiggerLevel
  94. using resampling filter specified by ImagingMipMapFilter option.
  95. Uses StretchNearest and StretchResample internally so the same image data format
  96. limitations apply.}
  97. procedure FillMipMapLevel(const BiggerLevel: TImageData; Width, Height: LongInt;
  98. var SmallerLevel: TImageData);
  99. { Various helper & support functions }
  100. { Copies Src pixel to Dest pixel. It is faster than System.Move procedure.}
  101. procedure CopyPixel(Src, Dest: Pointer; BytesPerPixel: LongInt); {$IFDEF USE_INLINE}inline;{$ENDIF}
  102. { Compares Src pixel and Dest pixel. It is faster than SysUtils.CompareMem function.}
  103. function ComparePixels(PixelA, PixelB: Pointer; BytesPerPixel: LongInt): Boolean; {$IFDEF USE_INLINE}inline;{$ENDIF}
  104. { Translates pixel color in SrcFormat to DstFormat.}
  105. procedure TranslatePixel(SrcPixel, DstPixel: Pointer; SrcFormat,
  106. DstFormat: TImageFormat; SrcPalette, DstPalette: PPalette32);
  107. { Clamps floating point pixel channel values to [0.0, 1.0] range.}
  108. procedure ClampFloatPixel(var PixF: TColorFPRec); {$IFDEF USE_INLINE}inline;{$ENDIF}
  109. { Helper function that converts pixel in any format to 32bit ARGB pixel.
  110. For common formats it's faster than calling GetPixel32 etc.}
  111. procedure ConvertToPixel32(SrcPix: PByte; DestPix: PColor32Rec;
  112. const SrcInfo: TImageFormatInfo; SrcPalette: PPalette32 = nil); {$IFDEF USE_INLINE}inline;{$ENDIF}
  113. { Adds padding bytes at the ends of scanlines. Bpp is the number of bytes per
  114. pixel of source and WidthBytes is the number of bytes per scanlines of dest.}
  115. procedure AddPadBytes(DataIn: Pointer; DataOut: Pointer; Width, Height,
  116. Bpp, WidthBytes: LongInt);
  117. { Removes padding from image with scanlines that have aligned sizes. Bpp is
  118. the number of bytes per pixel of dest and WidthBytes is the number of bytes
  119. per scanlines of source.}
  120. procedure RemovePadBytes(DataIn: Pointer; DataOut: Pointer; Width, Height,
  121. Bpp, WidthBytes: LongInt);
  122. { Converts 1bit image data to 8bit. Used mostly by file loaders for formats
  123. supporting 1bit images. Scaling of pixel values to 8bits is optional
  124. (indexed formats don't need this).}
  125. procedure Convert1To8(DataIn, DataOut: PByte; Width, Height,
  126. WidthBytes: LongInt; ScaleTo8Bits: Boolean);
  127. { Converts 2bit image data to 8bit. Used mostly by file loaders for formats
  128. supporting 2bit images. Scaling of pixel values to 8bits is optional
  129. (indexed formats don't need this).}
  130. procedure Convert2To8(DataIn, DataOut: PByte; Width, Height,
  131. WidthBytes: LongInt; ScaleTo8Bits: Boolean);
  132. { Converts 4bit image data to 8bit. Used mostly by file loaders for formats
  133. supporting 4bit images. Scaling of pixel values to 8bits is optional
  134. (indexed formats don't need this).}
  135. procedure Convert4To8(DataIn, DataOut: PByte; Width, Height,
  136. WidthBytes: LongInt; ScaleTo8Bits: Boolean);
  137. { Helper function for image file loaders. Some 15 bit images (targas, bitmaps)
  138. may contain 1 bit alpha but there is no indication of it. This function checks
  139. all 16 bit(should be X1R5G5B5 or A1R5G5B5 format) pixels and some of them have
  140. alpha bit set it returns True, otherwise False.}
  141. function Has16BitImageAlpha(NumPixels: LongInt; Data: PWord): Boolean;
  142. { Helper function for image file loaders. This function checks is similar
  143. to Has16BitImageAlpha but works with A8R8G8B8/X8R8G8B8 format.}
  144. function Has32BitImageAlpha(NumPixels: LongInt; Data: PLongWord): Boolean;
  145. { Checks if there is any relevant alpha data (any entry has alpha <> 255)
  146. in the given palette.}
  147. function PaletteHasAlpha(Palette: PPalette32; PaletteEntries: Integer): Boolean;
  148. { Checks if given palette has only grayscale entries.}
  149. function PaletteIsGrayScale(Palette: PPalette32; PaletteEntries: Integer): Boolean;
  150. { Provides indexed access to each line of pixels. Does not work with special
  151. format images.}
  152. function GetScanLine(ImageBits: Pointer; const FormatInfo: TImageFormatInfo;
  153. LineWidth, Index: LongInt): Pointer; {$IFDEF USE_INLINE}inline;{$ENDIF}
  154. { Returns True if Format is valid image data format identifier.}
  155. function IsImageFormatValid(Format: TImageFormat): Boolean;
  156. { Converts 16bit half floating point value to 32bit Single.}
  157. function HalfToFloat(Half: THalfFloat): Single;
  158. { Converts 32bit Single to 16bit half floating point.}
  159. function FloatToHalf(Float: Single): THalfFloat;
  160. { Converts half float color value to single-precision floating point color.}
  161. function ColorHalfToFloat(ColorHF: TColorHFRec): TColorFPRec; {$IFDEF USE_INLINE}inline;{$ENDIF}
  162. { Converts single-precision floating point color to half float color.}
  163. function ColorFloatToHalf(ColorFP: TColorFPRec): TColorHFRec; {$IFDEF USE_INLINE}inline;{$ENDIF}
  164. { Converts ARGB color to grayscale. }
  165. function Color32ToGray(Color32: TColor32): Byte; {$IFDEF USE_INLINE}inline;{$ENDIF}
  166. { Makes image PalEntries x 1 big where each pixel has color of one pal entry.}
  167. procedure VisualizePalette(Pal: PPalette32; Entries: Integer; out PalImage: TImageData);
  168. type
  169. TPointRec = record
  170. Pos: LongInt;
  171. Weight: Single;
  172. end;
  173. TCluster = array of TPointRec;
  174. TMappingTable = array of TCluster;
  175. { Helper function for resampling.}
  176. function BuildMappingTable(DstLow, DstHigh, SrcLow, SrcHigh, SrcImageWidth: LongInt;
  177. Filter: TFilterFunction; Radius: Single; WrapEdges: Boolean): TMappingTable;
  178. { Helper function for resampling.}
  179. procedure FindExtremes(const Map: TMappingTable; var MinPos, MaxPos: LongInt);
  180. { Pixel readers/writers for different image formats }
  181. { Returns pixel of image in any ARGB format. Channel values are scaled to 16 bits.}
  182. procedure ChannelGetSrcPixel(Src: PByte; SrcInfo: PImageFormatInfo;
  183. var Pix: TColor64Rec);
  184. { Sets pixel of image in any ARGB format. Channel values must be scaled to 16 bits.}
  185. procedure ChannelSetDstPixel(Dst: PByte; DstInfo: PImageFormatInfo;
  186. const Pix: TColor64Rec);
  187. { Returns pixel of image in any grayscale format. Gray value is scaled to 64 bits
  188. and alpha to 16 bits.}
  189. procedure GrayGetSrcPixel(Src: PByte; SrcInfo: PImageFormatInfo;
  190. var Gray: TColor64Rec; var Alpha: Word);
  191. { Sets pixel of image in any grayscale format. Gray value must be scaled to 64 bits
  192. and alpha to 16 bits.}
  193. procedure GraySetDstPixel(Dst: PByte; DstInfo: PImageFormatInfo;
  194. const Gray: TColor64Rec; Alpha: Word);
  195. { Returns pixel of image in any floating point format. Channel values are
  196. in range <0.0, 1.0>.}
  197. procedure FloatGetSrcPixel(Src: PByte; SrcInfo: PImageFormatInfo;
  198. var Pix: TColorFPRec);
  199. { Sets pixel of image in any floating point format. Channel values must be
  200. in range <0.0, 1.0>.}
  201. procedure FloatSetDstPixel(Dst: PByte; DstInfo: PImageFormatInfo;
  202. const Pix: TColorFPRec);
  203. { Returns pixel of image in any indexed format. Returned value is index to
  204. the palette.}
  205. procedure IndexGetSrcPixel(Src: PByte; SrcInfo: PImageFormatInfo;
  206. var Index: LongWord);
  207. { Sets pixel of image in any indexed format. Index is index to the palette.}
  208. procedure IndexSetDstPixel(Dst: PByte; DstInfo: PImageFormatInfo;
  209. Index: LongWord);
  210. { Pixel readers/writers for 32bit and FP colors}
  211. { Function for getting pixel colors. Native pixel is read from Image and
  212. then translated to 32 bit ARGB.}
  213. function GetPixel32Generic(Bits: Pointer; Info: PImageFormatInfo;
  214. Palette: PPalette32): TColor32Rec;
  215. { Procedure for setting pixel colors. Input 32 bit ARGB color is translated to
  216. native format and then written to Image.}
  217. procedure SetPixel32Generic(Bits: Pointer; Info: PImageFormatInfo;
  218. Palette: PPalette32; const Color: TColor32Rec);
  219. { Function for getting pixel colors. Native pixel is read from Image and
  220. then translated to FP ARGB.}
  221. function GetPixelFPGeneric(Bits: Pointer; Info: PImageFormatInfo;
  222. Palette: PPalette32): TColorFPRec;
  223. { Procedure for setting pixel colors. Input FP ARGB color is translated to
  224. native format and then written to Image.}
  225. procedure SetPixelFPGeneric(Bits: Pointer; Info: PImageFormatInfo;
  226. Palette: PPalette32; const Color: TColorFPRec);
  227. { Image format conversion functions }
  228. { Converts any ARGB format to any ARGB format.}
  229. procedure ChannelToChannel(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
  230. DstInfo: PImageFormatInfo);
  231. { Converts any ARGB format to any grayscale format.}
  232. procedure ChannelToGray(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
  233. DstInfo: PImageFormatInfo);
  234. { Converts any ARGB format to any floating point format.}
  235. procedure ChannelToFloat(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
  236. DstInfo: PImageFormatInfo);
  237. { Converts any ARGB format to any indexed format.}
  238. procedure ChannelToIndex(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
  239. DstInfo: PImageFormatInfo; DstPal: PPalette32);
  240. { Converts any grayscale format to any grayscale format.}
  241. procedure GrayToGray(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
  242. DstInfo: PImageFormatInfo);
  243. { Converts any grayscale format to any ARGB format.}
  244. procedure GrayToChannel(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
  245. DstInfo: PImageFormatInfo);
  246. { Converts any grayscale format to any floating point format.}
  247. procedure GrayToFloat(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
  248. DstInfo: PImageFormatInfo);
  249. { Converts any grayscale format to any indexed format.}
  250. procedure GrayToIndex(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
  251. DstInfo: PImageFormatInfo; DstPal: PPalette32);
  252. { Converts any floating point format to any floating point format.}
  253. procedure FloatToFloat(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
  254. DstInfo: PImageFormatInfo);
  255. { Converts any floating point format to any ARGB format.}
  256. procedure FloatToChannel(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
  257. DstInfo: PImageFormatInfo);
  258. { Converts any floating point format to any grayscale format.}
  259. procedure FloatToGray(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
  260. DstInfo: PImageFormatInfo);
  261. { Converts any floating point format to any indexed format.}
  262. procedure FloatToIndex(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
  263. DstInfo: PImageFormatInfo; DstPal: PPalette32);
  264. { Converts any indexed format to any indexed format.}
  265. procedure IndexToIndex(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
  266. DstInfo: PImageFormatInfo; SrcPal, DstPal: PPalette32);
  267. { Converts any indexed format to any ARGB format.}
  268. procedure IndexToChannel(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
  269. DstInfo: PImageFormatInfo; SrcPal: PPalette32);
  270. { Converts any indexed format to any grayscale format.}
  271. procedure IndexToGray(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
  272. DstInfo: PImageFormatInfo; SrcPal: PPalette32);
  273. { Converts any indexed format to any floating point format.}
  274. procedure IndexToFloat(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
  275. DstInfo: PImageFormatInfo; SrcPal: PPalette32);
  276. { Special formats conversion functions }
  277. { Converts image to/from/between special image formats (dxtc, ...).}
  278. procedure ConvertSpecial(var Image: TImageData; SrcInfo,
  279. DstInfo: PImageFormatInfo);
  280. { Inits all image format information. Called internally on startup.}
  281. procedure InitImageFormats(var Infos: TImageFormatInfoArray);
  282. const
  283. // Grayscale conversion channel weights
  284. GrayConv: TColorFPRec = (B: 0.114; G: 0.587; R: 0.299; A: 0.0);
  285. // Contants for converting integer colors to floating point
  286. OneDiv8Bit: Single = 1.0 / 255.0;
  287. OneDiv16Bit: Single = 1.0 / 65535.0;
  288. implementation
  289. { TImageFormatInfo member functions }
  290. { Returns size in bytes of image in given standard format where
  291. Size = Width * Height * Bpp.}
  292. function GetStdPixelsSize(Format: TImageFormat; Width, Height: LongInt): LongInt; forward;
  293. { Checks if Width and Height are valid for given standard format.}
  294. procedure CheckStdDimensions(Format: TImageFormat; var Width, Height: LongInt); forward;
  295. { Returns size in bytes of image in given DXT format.}
  296. function GetDXTPixelsSize(Format: TImageFormat; Width, Height: LongInt): LongInt; forward;
  297. { Checks if Width and Height are valid for given DXT format. If they are
  298. not valid, they are changed to pass the check.}
  299. procedure CheckDXTDimensions(Format: TImageFormat; var Width, Height: LongInt); forward;
  300. { Returns size in bytes of image in BTC format.}
  301. function GetBTCPixelsSize(Format: TImageFormat; Width, Height: LongInt): LongInt; forward;
  302. { Returns size in bytes of image in binary format (1bit image).}
  303. function GetBinaryPixelsSize(Format: TImageFormat; Width, Height: LongInt): LongInt; forward;
  304. function GetBCPixelsSize(Format: TImageFormat; Width, Height: LongInt): LongInt; forward;
  305. procedure CheckBCDimensions(Format: TImageFormat; var Width, Height: LongInt); forward;
  306. { Optimized pixel readers/writers for 32bit and FP colors to be stored in TImageFormatInfo }
  307. function GetPixel32ifA8R8G8B8(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32): TColor32Rec; forward;
  308. procedure SetPixel32ifA8R8G8B8(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32; const Color: TColor32Rec); forward;
  309. function GetPixelFPifA8R8G8B8(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32): TColorFPRec; forward;
  310. procedure SetPixelFPifA8R8G8B8(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32; const Color: TColorFPRec); forward;
  311. function GetPixel32Channel8Bit(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32): TColor32Rec; forward;
  312. procedure SetPixel32Channel8Bit(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32; const Color: TColor32Rec); forward;
  313. function GetPixelFPChannel8Bit(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32): TColorFPRec; forward;
  314. procedure SetPixelFPChannel8Bit(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32; const Color: TColorFPRec); forward;
  315. function GetPixelFPFloat32(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32): TColorFPRec; forward;
  316. procedure SetPixelFPFloat32(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32; const Color: TColorFPRec); forward;
  317. var
  318. PFR3G3B2: TPixelFormatInfo;
  319. PFX5R1G1B1: TPixelFormatInfo;
  320. PFR5G6B5: TPixelFormatInfo;
  321. PFA1R5G5B5: TPixelFormatInfo;
  322. PFA4R4G4B4: TPixelFormatInfo;
  323. PFX1R5G5B5: TPixelFormatInfo;
  324. PFX4R4G4B4: TPixelFormatInfo;
  325. FInfos: PImageFormatInfoArray;
  326. var
  327. // Free Pascal generates hundreds of warnings here
  328. {$WARNINGS OFF}
  329. // indexed formats
  330. Index8Info: TImageFormatInfo = (
  331. Format: ifIndex8;
  332. Name: 'Index8';
  333. BytesPerPixel: 1;
  334. ChannelCount: 1;
  335. PaletteEntries: 256;
  336. HasAlphaChannel: True;
  337. IsIndexed: True;
  338. GetPixelsSize: GetStdPixelsSize;
  339. CheckDimensions: CheckStdDimensions;
  340. GetPixel32: GetPixel32Generic;
  341. GetPixelFP: GetPixelFPGeneric;
  342. SetPixel32: SetPixel32Generic;
  343. SetPixelFP: SetPixelFPGeneric);
  344. // grayscale formats
  345. Gray8Info: TImageFormatInfo = (
  346. Format: ifGray8;
  347. Name: 'Gray8';
  348. BytesPerPixel: 1;
  349. ChannelCount: 1;
  350. HasGrayChannel: True;
  351. GetPixelsSize: GetStdPixelsSize;
  352. CheckDimensions: CheckStdDimensions;
  353. GetPixel32: GetPixel32Channel8Bit;
  354. GetPixelFP: GetPixelFPChannel8Bit;
  355. SetPixel32: SetPixel32Channel8Bit;
  356. SetPixelFP: SetPixelFPChannel8Bit);
  357. A8Gray8Info: TImageFormatInfo = (
  358. Format: ifA8Gray8;
  359. Name: 'A8Gray8';
  360. BytesPerPixel: 2;
  361. ChannelCount: 2;
  362. HasGrayChannel: True;
  363. HasAlphaChannel: True;
  364. GetPixelsSize: GetStdPixelsSize;
  365. CheckDimensions: CheckStdDimensions;
  366. GetPixel32: GetPixel32Channel8Bit;
  367. GetPixelFP: GetPixelFPChannel8Bit;
  368. SetPixel32: SetPixel32Channel8Bit;
  369. SetPixelFP: SetPixelFPChannel8Bit);
  370. Gray16Info: TImageFormatInfo = (
  371. Format: ifGray16;
  372. Name: 'Gray16';
  373. BytesPerPixel: 2;
  374. ChannelCount: 1;
  375. HasGrayChannel: True;
  376. GetPixelsSize: GetStdPixelsSize;
  377. CheckDimensions: CheckStdDimensions;
  378. GetPixel32: GetPixel32Generic;
  379. GetPixelFP: GetPixelFPGeneric;
  380. SetPixel32: SetPixel32Generic;
  381. SetPixelFP: SetPixelFPGeneric);
  382. Gray32Info: TImageFormatInfo = (
  383. Format: ifGray32;
  384. Name: 'Gray32';
  385. BytesPerPixel: 4;
  386. ChannelCount: 1;
  387. HasGrayChannel: True;
  388. GetPixelsSize: GetStdPixelsSize;
  389. CheckDimensions: CheckStdDimensions;
  390. GetPixel32: GetPixel32Generic;
  391. GetPixelFP: GetPixelFPGeneric;
  392. SetPixel32: SetPixel32Generic;
  393. SetPixelFP: SetPixelFPGeneric);
  394. Gray64Info: TImageFormatInfo = (
  395. Format: ifGray64;
  396. Name: 'Gray64';
  397. BytesPerPixel: 8;
  398. ChannelCount: 1;
  399. HasGrayChannel: True;
  400. GetPixelsSize: GetStdPixelsSize;
  401. CheckDimensions: CheckStdDimensions;
  402. GetPixel32: GetPixel32Generic;
  403. GetPixelFP: GetPixelFPGeneric;
  404. SetPixel32: SetPixel32Generic;
  405. SetPixelFP: SetPixelFPGeneric);
  406. A16Gray16Info: TImageFormatInfo = (
  407. Format: ifA16Gray16;
  408. Name: 'A16Gray16';
  409. BytesPerPixel: 4;
  410. ChannelCount: 2;
  411. HasGrayChannel: True;
  412. HasAlphaChannel: True;
  413. GetPixelsSize: GetStdPixelsSize;
  414. CheckDimensions: CheckStdDimensions;
  415. GetPixel32: GetPixel32Generic;
  416. GetPixelFP: GetPixelFPGeneric;
  417. SetPixel32: SetPixel32Generic;
  418. SetPixelFP: SetPixelFPGeneric);
  419. // ARGB formats
  420. X5R1G1B1Info: TImageFormatInfo = (
  421. Format: ifX5R1G1B1;
  422. Name: 'X5R1G1B1';
  423. BytesPerPixel: 1;
  424. ChannelCount: 3;
  425. UsePixelFormat: True;
  426. PixelFormat: @PFX5R1G1B1;
  427. GetPixelsSize: GetStdPixelsSize;
  428. CheckDimensions: CheckStdDimensions;
  429. GetPixel32: GetPixel32Generic;
  430. GetPixelFP: GetPixelFPGeneric;
  431. SetPixel32: SetPixel32Generic;
  432. SetPixelFP: SetPixelFPGeneric);
  433. R3G3B2Info: TImageFormatInfo = (
  434. Format: ifR3G3B2;
  435. Name: 'R3G3B2';
  436. BytesPerPixel: 1;
  437. ChannelCount: 3;
  438. UsePixelFormat: True;
  439. PixelFormat: @PFR3G3B2;
  440. GetPixelsSize: GetStdPixelsSize;
  441. CheckDimensions: CheckStdDimensions;
  442. GetPixel32: GetPixel32Generic;
  443. GetPixelFP: GetPixelFPGeneric;
  444. SetPixel32: SetPixel32Generic;
  445. SetPixelFP: SetPixelFPGeneric);
  446. R5G6B5Info: TImageFormatInfo = (
  447. Format: ifR5G6B5;
  448. Name: 'R5G6B5';
  449. BytesPerPixel: 2;
  450. ChannelCount: 3;
  451. UsePixelFormat: True;
  452. PixelFormat: @PFR5G6B5;
  453. GetPixelsSize: GetStdPixelsSize;
  454. CheckDimensions: CheckStdDimensions;
  455. GetPixel32: GetPixel32Generic;
  456. GetPixelFP: GetPixelFPGeneric;
  457. SetPixel32: SetPixel32Generic;
  458. SetPixelFP: SetPixelFPGeneric);
  459. A1R5G5B5Info: TImageFormatInfo = (
  460. Format: ifA1R5G5B5;
  461. Name: 'A1R5G5B5';
  462. BytesPerPixel: 2;
  463. ChannelCount: 4;
  464. HasAlphaChannel: True;
  465. UsePixelFormat: True;
  466. PixelFormat: @PFA1R5G5B5;
  467. GetPixelsSize: GetStdPixelsSize;
  468. CheckDimensions: CheckStdDimensions;
  469. GetPixel32: GetPixel32Generic;
  470. GetPixelFP: GetPixelFPGeneric;
  471. SetPixel32: SetPixel32Generic;
  472. SetPixelFP: SetPixelFPGeneric);
  473. A4R4G4B4Info: TImageFormatInfo = (
  474. Format: ifA4R4G4B4;
  475. Name: 'A4R4G4B4';
  476. BytesPerPixel: 2;
  477. ChannelCount: 4;
  478. HasAlphaChannel: True;
  479. UsePixelFormat: True;
  480. PixelFormat: @PFA4R4G4B4;
  481. GetPixelsSize: GetStdPixelsSize;
  482. CheckDimensions: CheckStdDimensions;
  483. GetPixel32: GetPixel32Generic;
  484. GetPixelFP: GetPixelFPGeneric;
  485. SetPixel32: SetPixel32Generic;
  486. SetPixelFP: SetPixelFPGeneric);
  487. X1R5G5B5Info: TImageFormatInfo = (
  488. Format: ifX1R5G5B5;
  489. Name: 'X1R5G5B5';
  490. BytesPerPixel: 2;
  491. ChannelCount: 3;
  492. UsePixelFormat: True;
  493. PixelFormat: @PFX1R5G5B5;
  494. GetPixelsSize: GetStdPixelsSize;
  495. CheckDimensions: CheckStdDimensions;
  496. GetPixel32: GetPixel32Generic;
  497. GetPixelFP: GetPixelFPGeneric;
  498. SetPixel32: SetPixel32Generic;
  499. SetPixelFP: SetPixelFPGeneric);
  500. X4R4G4B4Info: TImageFormatInfo = (
  501. Format: ifX4R4G4B4;
  502. Name: 'X4R4G4B4';
  503. BytesPerPixel: 2;
  504. ChannelCount: 3;
  505. UsePixelFormat: True;
  506. PixelFormat: @PFX4R4G4B4;
  507. GetPixelsSize: GetStdPixelsSize;
  508. CheckDimensions: CheckStdDimensions;
  509. GetPixel32: GetPixel32Generic;
  510. GetPixelFP: GetPixelFPGeneric;
  511. SetPixel32: SetPixel32Generic;
  512. SetPixelFP: SetPixelFPGeneric);
  513. R8G8B8Info: TImageFormatInfo = (
  514. Format: ifR8G8B8;
  515. Name: 'R8G8B8';
  516. BytesPerPixel: 3;
  517. ChannelCount: 3;
  518. GetPixelsSize: GetStdPixelsSize;
  519. CheckDimensions: CheckStdDimensions;
  520. GetPixel32: GetPixel32Channel8Bit;
  521. GetPixelFP: GetPixelFPChannel8Bit;
  522. SetPixel32: SetPixel32Channel8Bit;
  523. SetPixelFP: SetPixelFPChannel8Bit);
  524. A8R8G8B8Info: TImageFormatInfo = (
  525. Format: ifA8R8G8B8;
  526. Name: 'A8R8G8B8';
  527. BytesPerPixel: 4;
  528. ChannelCount: 4;
  529. HasAlphaChannel: True;
  530. GetPixelsSize: GetStdPixelsSize;
  531. CheckDimensions: CheckStdDimensions;
  532. GetPixel32: GetPixel32ifA8R8G8B8;
  533. GetPixelFP: GetPixelFPifA8R8G8B8;
  534. SetPixel32: SetPixel32ifA8R8G8B8;
  535. SetPixelFP: SetPixelFPifA8R8G8B8);
  536. X8R8G8B8Info: TImageFormatInfo = (
  537. Format: ifX8R8G8B8;
  538. Name: 'X8R8G8B8';
  539. BytesPerPixel: 4;
  540. ChannelCount: 3;
  541. GetPixelsSize: GetStdPixelsSize;
  542. CheckDimensions: CheckStdDimensions;
  543. GetPixel32: GetPixel32Channel8Bit;
  544. GetPixelFP: GetPixelFPChannel8Bit;
  545. SetPixel32: SetPixel32Channel8Bit;
  546. SetPixelFP: SetPixelFPChannel8Bit);
  547. R16G16B16Info: TImageFormatInfo = (
  548. Format: ifR16G16B16;
  549. Name: 'R16G16B16';
  550. BytesPerPixel: 6;
  551. ChannelCount: 3;
  552. RBSwapFormat: ifB16G16R16;
  553. GetPixelsSize: GetStdPixelsSize;
  554. CheckDimensions: CheckStdDimensions;
  555. GetPixel32: GetPixel32Generic;
  556. GetPixelFP: GetPixelFPGeneric;
  557. SetPixel32: SetPixel32Generic;
  558. SetPixelFP: SetPixelFPGeneric);
  559. A16R16G16B16Info: TImageFormatInfo = (
  560. Format: ifA16R16G16B16;
  561. Name: 'A16R16G16B16';
  562. BytesPerPixel: 8;
  563. ChannelCount: 4;
  564. HasAlphaChannel: True;
  565. RBSwapFormat: ifA16B16G16R16;
  566. GetPixelsSize: GetStdPixelsSize;
  567. CheckDimensions: CheckStdDimensions;
  568. GetPixel32: GetPixel32Generic;
  569. GetPixelFP: GetPixelFPGeneric;
  570. SetPixel32: SetPixel32Generic;
  571. SetPixelFP: SetPixelFPGeneric);
  572. B16G16R16Info: TImageFormatInfo = (
  573. Format: ifB16G16R16;
  574. Name: 'B16G16R16';
  575. BytesPerPixel: 6;
  576. ChannelCount: 3;
  577. IsRBSwapped: True;
  578. RBSwapFormat: ifR16G16B16;
  579. GetPixelsSize: GetStdPixelsSize;
  580. CheckDimensions: CheckStdDimensions;
  581. GetPixel32: GetPixel32Generic;
  582. GetPixelFP: GetPixelFPGeneric;
  583. SetPixel32: SetPixel32Generic;
  584. SetPixelFP: SetPixelFPGeneric);
  585. A16B16G16R16Info: TImageFormatInfo = (
  586. Format: ifA16B16G16R16;
  587. Name: 'A16B16G16R16';
  588. BytesPerPixel: 8;
  589. ChannelCount: 4;
  590. HasAlphaChannel: True;
  591. IsRBSwapped: True;
  592. RBSwapFormat: ifA16R16G16B16;
  593. GetPixelsSize: GetStdPixelsSize;
  594. CheckDimensions: CheckStdDimensions;
  595. GetPixel32: GetPixel32Generic;
  596. GetPixelFP: GetPixelFPGeneric;
  597. SetPixel32: SetPixel32Generic;
  598. SetPixelFP: SetPixelFPGeneric);
  599. // floating point formats
  600. R32FInfo: TImageFormatInfo = (
  601. Format: ifR32F;
  602. Name: 'R32F';
  603. BytesPerPixel: 4;
  604. ChannelCount: 1;
  605. IsFloatingPoint: True;
  606. GetPixelsSize: GetStdPixelsSize;
  607. CheckDimensions: CheckStdDimensions;
  608. GetPixel32: GetPixel32Generic;
  609. GetPixelFP: GetPixelFPFloat32;
  610. SetPixel32: SetPixel32Generic;
  611. SetPixelFP: SetPixelFPFloat32);
  612. A32R32G32B32FInfo: TImageFormatInfo = (
  613. Format: ifA32R32G32B32F;
  614. Name: 'A32R32G32B32F';
  615. BytesPerPixel: 16;
  616. ChannelCount: 4;
  617. HasAlphaChannel: True;
  618. IsFloatingPoint: True;
  619. RBSwapFormat: ifA32B32G32R32F;
  620. GetPixelsSize: GetStdPixelsSize;
  621. CheckDimensions: CheckStdDimensions;
  622. GetPixel32: GetPixel32Generic;
  623. GetPixelFP: GetPixelFPFloat32;
  624. SetPixel32: SetPixel32Generic;
  625. SetPixelFP: SetPixelFPFloat32);
  626. A32B32G32R32FInfo: TImageFormatInfo = (
  627. Format: ifA32B32G32R32F;
  628. Name: 'A32B32G32R32F';
  629. BytesPerPixel: 16;
  630. ChannelCount: 4;
  631. HasAlphaChannel: True;
  632. IsFloatingPoint: True;
  633. IsRBSwapped: True;
  634. RBSwapFormat: ifA32R32G32B32F;
  635. GetPixelsSize: GetStdPixelsSize;
  636. CheckDimensions: CheckStdDimensions;
  637. GetPixel32: GetPixel32Generic;
  638. GetPixelFP: GetPixelFPFloat32;
  639. SetPixel32: SetPixel32Generic;
  640. SetPixelFP: SetPixelFPFloat32);
  641. R16FInfo: TImageFormatInfo = (
  642. Format: ifR16F;
  643. Name: 'R16F';
  644. BytesPerPixel: 2;
  645. ChannelCount: 1;
  646. IsFloatingPoint: True;
  647. GetPixelsSize: GetStdPixelsSize;
  648. CheckDimensions: CheckStdDimensions;
  649. GetPixel32: GetPixel32Generic;
  650. GetPixelFP: GetPixelFPGeneric;
  651. SetPixel32: SetPixel32Generic;
  652. SetPixelFP: SetPixelFPGeneric);
  653. A16R16G16B16FInfo: TImageFormatInfo = (
  654. Format: ifA16R16G16B16F;
  655. Name: 'A16R16G16B16F';
  656. BytesPerPixel: 8;
  657. ChannelCount: 4;
  658. HasAlphaChannel: True;
  659. IsFloatingPoint: True;
  660. RBSwapFormat: ifA16B16G16R16F;
  661. GetPixelsSize: GetStdPixelsSize;
  662. CheckDimensions: CheckStdDimensions;
  663. GetPixel32: GetPixel32Generic;
  664. GetPixelFP: GetPixelFPGeneric;
  665. SetPixel32: SetPixel32Generic;
  666. SetPixelFP: SetPixelFPGeneric);
  667. A16B16G16R16FInfo: TImageFormatInfo = (
  668. Format: ifA16B16G16R16F;
  669. Name: 'A16B16G16R16F';
  670. BytesPerPixel: 8;
  671. ChannelCount: 4;
  672. HasAlphaChannel: True;
  673. IsFloatingPoint: True;
  674. IsRBSwapped: True;
  675. RBSwapFormat: ifA16R16G16B16F;
  676. GetPixelsSize: GetStdPixelsSize;
  677. CheckDimensions: CheckStdDimensions;
  678. GetPixel32: GetPixel32Generic;
  679. GetPixelFP: GetPixelFPGeneric;
  680. SetPixel32: SetPixel32Generic;
  681. SetPixelFP: SetPixelFPGeneric);
  682. R32G32B32FInfo: TImageFormatInfo = (
  683. Format: ifR32G32B32F;
  684. Name: 'R32G32B32F';
  685. BytesPerPixel: 12;
  686. ChannelCount: 3;
  687. IsFloatingPoint: True;
  688. RBSwapFormat: ifB32G32R32F;
  689. GetPixelsSize: GetStdPixelsSize;
  690. CheckDimensions: CheckStdDimensions;
  691. GetPixel32: GetPixel32Generic;
  692. GetPixelFP: GetPixelFPFloat32;
  693. SetPixel32: SetPixel32Generic;
  694. SetPixelFP: SetPixelFPFloat32);
  695. B32G32R32FInfo: TImageFormatInfo = (
  696. Format: ifB32G32R32F;
  697. Name: 'B32G32R32F';
  698. BytesPerPixel: 12;
  699. ChannelCount: 3;
  700. IsFloatingPoint: True;
  701. IsRBSwapped: True;
  702. RBSwapFormat: ifR32G32B32F;
  703. GetPixelsSize: GetStdPixelsSize;
  704. CheckDimensions: CheckStdDimensions;
  705. GetPixel32: GetPixel32Generic;
  706. GetPixelFP: GetPixelFPFloat32;
  707. SetPixel32: SetPixel32Generic;
  708. SetPixelFP: SetPixelFPFloat32);
  709. // special formats
  710. DXT1Info: TImageFormatInfo = (
  711. Format: ifDXT1;
  712. Name: 'DXT1';
  713. ChannelCount: 4;
  714. HasAlphaChannel: True;
  715. IsSpecial: True;
  716. GetPixelsSize: GetDXTPixelsSize;
  717. CheckDimensions: CheckDXTDimensions;
  718. SpecialNearestFormat: ifA8R8G8B8);
  719. DXT3Info: TImageFormatInfo = (
  720. Format: ifDXT3;
  721. Name: 'DXT3';
  722. ChannelCount: 4;
  723. HasAlphaChannel: True;
  724. IsSpecial: True;
  725. GetPixelsSize: GetDXTPixelsSize;
  726. CheckDimensions: CheckDXTDimensions;
  727. SpecialNearestFormat: ifA8R8G8B8);
  728. DXT5Info: TImageFormatInfo = (
  729. Format: ifDXT5;
  730. Name: 'DXT5';
  731. ChannelCount: 4;
  732. HasAlphaChannel: True;
  733. IsSpecial: True;
  734. GetPixelsSize: GetDXTPixelsSize;
  735. CheckDimensions: CheckDXTDimensions;
  736. SpecialNearestFormat: ifA8R8G8B8);
  737. BTCInfo: TImageFormatInfo = (
  738. Format: ifBTC;
  739. Name: 'BTC';
  740. ChannelCount: 1;
  741. HasAlphaChannel: False;
  742. IsSpecial: True;
  743. GetPixelsSize: GetBTCPixelsSize;
  744. CheckDimensions: CheckDXTDimensions;
  745. SpecialNearestFormat: ifGray8);
  746. ATI1NInfo: TImageFormatInfo = (
  747. Format: ifATI1N;
  748. Name: 'ATI1N';
  749. ChannelCount: 1;
  750. HasAlphaChannel: False;
  751. IsSpecial: True;
  752. GetPixelsSize: GetDXTPixelsSize;
  753. CheckDimensions: CheckDXTDimensions;
  754. SpecialNearestFormat: ifGray8);
  755. ATI2NInfo: TImageFormatInfo = (
  756. Format: ifATI2N;
  757. Name: 'ATI2N';
  758. ChannelCount: 2;
  759. HasAlphaChannel: False;
  760. IsSpecial: True;
  761. GetPixelsSize: GetDXTPixelsSize;
  762. CheckDimensions: CheckDXTDimensions;
  763. SpecialNearestFormat: ifA8R8G8B8);
  764. BinaryInfo: TImageFormatInfo = (
  765. Format: ifBinary;
  766. Name: 'Binary';
  767. ChannelCount: 1;
  768. HasAlphaChannel: False;
  769. IsSpecial: True;
  770. GetPixelsSize: GetBinaryPixelsSize;
  771. CheckDimensions: CheckStdDimensions;
  772. SpecialNearestFormat: ifGray8);
  773. { Passtrough formats }
  774. {ETC1Info: TImageFormatInfo = (
  775. Format: ifETC1;
  776. Name: 'ETC1';
  777. ChannelCount: 3;
  778. HasAlphaChannel: False;
  779. IsSpecial: True;
  780. IsPasstrough: True;
  781. GetPixelsSize: GetBCPixelsSize;
  782. CheckDimensions: CheckBCDimensions;
  783. SpecialNearestFormat: ifR8G8B8);
  784. ETC2RGBInfo: TImageFormatInfo = (
  785. Format: ifETC2RGB;
  786. Name: 'ETC2RGB';
  787. ChannelCount: 3;
  788. HasAlphaChannel: False;
  789. IsSpecial: True;
  790. IsPasstrough: True;
  791. GetPixelsSize: GetBCPixelsSize;
  792. CheckDimensions: CheckBCDimensions;
  793. SpecialNearestFormat: ifR8G8B8);
  794. ETC2RGBAInfo: TImageFormatInfo = (
  795. Format: ifETC2RGBA;
  796. Name: 'ETC2RGBA';
  797. ChannelCount: 4;
  798. HasAlphaChannel: True;
  799. IsSpecial: True;
  800. IsPasstrough: True;
  801. GetPixelsSize: GetBCPixelsSize;
  802. CheckDimensions: CheckBCDimensions;
  803. SpecialNearestFormat: ifA8R8G8B8);
  804. ETC2PAInfo: TImageFormatInfo = (
  805. Format: ifETC2PA;
  806. Name: 'ETC2PA';
  807. ChannelCount: 4;
  808. HasAlphaChannel: True;
  809. IsSpecial: True;
  810. IsPasstrough: True;
  811. GetPixelsSize: GetBCPixelsSize;
  812. CheckDimensions: CheckBCDimensions;
  813. SpecialNearestFormat: ifA8R8G8B8);
  814. DXBC6Info: TImageFormatInfo = (
  815. Format: ifDXBC6;
  816. Name: 'DXBC6';
  817. ChannelCount: 4;
  818. HasAlphaChannel: True;
  819. IsSpecial: True;
  820. IsPasstrough: True;
  821. GetPixelsSize: GetBCPixelsSize;
  822. CheckDimensions: CheckBCDimensions;
  823. SpecialNearestFormat: ifA8R8G8B8);
  824. DXBC7Info: TImageFormatInfo = (
  825. Format: ifDXBC6;
  826. Name: 'DXBC7';
  827. ChannelCount: 4;
  828. HasAlphaChannel: True;
  829. IsSpecial: True;
  830. IsPasstrough: True;
  831. GetPixelsSize: GetBCPixelsSize;
  832. CheckDimensions: CheckBCDimensions;
  833. SpecialNearestFormat: ifA8R8G8B8);
  834. PVRTCInfo: TImageFormatInfo = (
  835. Format: ifPVRTC;
  836. Name: 'PVRTC';
  837. ChannelCount: 4;
  838. HasAlphaChannel: True;
  839. IsSpecial: True;
  840. IsPasstrough: True;
  841. GetPixelsSize: GetBCPixelsSize;
  842. CheckDimensions: CheckBCDimensions;
  843. SpecialNearestFormat: ifA8R8G8B8);}
  844. {$WARNINGS ON}
  845. function PixelFormat(ABitCount, RBitCount, GBitCount, BBitCount: Byte): TPixelFormatInfo; forward;
  846. procedure InitImageFormats(var Infos: TImageFormatInfoArray);
  847. begin
  848. FInfos := @Infos;
  849. Infos[ifDefault] := @A8R8G8B8Info;
  850. // indexed formats
  851. Infos[ifIndex8] := @Index8Info;
  852. // grayscale formats
  853. Infos[ifGray8] := @Gray8Info;
  854. Infos[ifA8Gray8] := @A8Gray8Info;
  855. Infos[ifGray16] := @Gray16Info;
  856. Infos[ifGray32] := @Gray32Info;
  857. Infos[ifGray64] := @Gray64Info;
  858. Infos[ifA16Gray16] := @A16Gray16Info;
  859. // ARGB formats
  860. Infos[ifX5R1G1B1] := @X5R1G1B1Info;
  861. Infos[ifR3G3B2] := @R3G3B2Info;
  862. Infos[ifR5G6B5] := @R5G6B5Info;
  863. Infos[ifA1R5G5B5] := @A1R5G5B5Info;
  864. Infos[ifA4R4G4B4] := @A4R4G4B4Info;
  865. Infos[ifX1R5G5B5] := @X1R5G5B5Info;
  866. Infos[ifX4R4G4B4] := @X4R4G4B4Info;
  867. Infos[ifR8G8B8] := @R8G8B8Info;
  868. Infos[ifA8R8G8B8] := @A8R8G8B8Info;
  869. Infos[ifX8R8G8B8] := @X8R8G8B8Info;
  870. Infos[ifR16G16B16] := @R16G16B16Info;
  871. Infos[ifA16R16G16B16] := @A16R16G16B16Info;
  872. Infos[ifB16G16R16] := @B16G16R16Info;
  873. Infos[ifA16B16G16R16] := @A16B16G16R16Info;
  874. // floating point formats
  875. Infos[ifR32F] := @R32FInfo;
  876. Infos[ifA32R32G32B32F] := @A32R32G32B32FInfo;
  877. Infos[ifA32B32G32R32F] := @A32B32G32R32FInfo;
  878. Infos[ifR16F] := @R16FInfo;
  879. Infos[ifA16R16G16B16F] := @A16R16G16B16FInfo;
  880. Infos[ifA16B16G16R16F] := @A16B16G16R16FInfo;
  881. Infos[ifR32G32B32F] := @R32G32B32FInfo;
  882. Infos[ifB32G32R32F] := @B32G32R32FInfo;
  883. // special formats
  884. Infos[ifDXT1] := @DXT1Info;
  885. Infos[ifDXT3] := @DXT3Info;
  886. Infos[ifDXT5] := @DXT5Info;
  887. Infos[ifBTC] := @BTCInfo;
  888. Infos[ifATI1N] := @ATI1NInfo;
  889. Infos[ifATI2N] := @ATI2NInfo;
  890. Infos[ifBinary] := @BinaryInfo;
  891. PFR3G3B2 := PixelFormat(0, 3, 3, 2);
  892. PFX5R1G1B1 := PixelFormat(0, 1, 1, 1);
  893. PFR5G6B5 := PixelFormat(0, 5, 6, 5);
  894. PFA1R5G5B5 := PixelFormat(1, 5, 5, 5);
  895. PFA4R4G4B4 := PixelFormat(4, 4, 4, 4);
  896. PFX1R5G5B5 := PixelFormat(0, 5, 5, 5);
  897. PFX4R4G4B4 := PixelFormat(0, 4, 4, 4);
  898. end;
  899. { Internal unit helper functions }
  900. function PixelFormat(ABitCount, RBitCount, GBitCount, BBitCount: Byte): TPixelFormatInfo;
  901. begin
  902. Result.ABitMask := ((1 shl ABitCount) - 1) shl (RBitCount + GBitCount +
  903. BBitCount);
  904. Result.RBitMask := ((1 shl RBitCount) - 1) shl (GBitCount + BBitCount);
  905. Result.GBitMask := ((1 shl GBitCount) - 1) shl (BBitCount);
  906. Result.BBitMask := (1 shl BBitCount) - 1;
  907. Result.ABitCount := ABitCount;
  908. Result.RBitCount := RBitCount;
  909. Result.GBitCount := GBitCount;
  910. Result.BBitCount := BBitCount;
  911. Result.AShift := RBitCount + GBitCount + BBitCount;
  912. Result.RShift := GBitCount + BBitCount;
  913. Result.GShift := BBitCount;
  914. Result.BShift := 0;
  915. Result.ARecDiv := Max(1, Pow2Int(Result.ABitCount) - 1);
  916. Result.RRecDiv := Max(1, Pow2Int(Result.RBitCount) - 1);
  917. Result.GRecDiv := Max(1, Pow2Int(Result.GBitCount) - 1);
  918. Result.BRecDiv := Max(1, Pow2Int(Result.BBitCount) - 1);
  919. end;
  920. function PixelFormatMask(ABitMask, RBitMask, GBitMask, BBitMask: LongWord): TPixelFormatInfo;
  921. function GetBitCount(B: LongWord): LongWord;
  922. var
  923. I: LongWord;
  924. begin
  925. I := 0;
  926. while (I < 31) and (((1 shl I) and B) = 0) do
  927. Inc(I);
  928. Result := 0;
  929. while ((1 shl I) and B) <> 0 do
  930. begin
  931. Inc(I);
  932. Inc(Result);
  933. end;
  934. end;
  935. begin
  936. Result := PixelFormat(GetBitCount(ABitMask), GetBitCount(RBitMask),
  937. GetBitCount(GBitMask), GetBitCount(BBitMask));
  938. end;
  939. function PFSetARGB(const PF: TPixelFormatInfo; A, R, G, B: Byte): TColor32;
  940. {$IFDEF USE_INLINE}inline;{$ENDIF}
  941. begin
  942. with PF do
  943. Result :=
  944. (A shl ABitCount shr 8 shl AShift) or
  945. (R shl RBitCount shr 8 shl RShift) or
  946. (G shl GBitCount shr 8 shl GShift) or
  947. (B shl BBitCount shr 8 shl BShift);
  948. end;
  949. procedure PFGetARGB(const PF: TPixelFormatInfo; Color: LongWord;
  950. var A, R, G, B: Byte); {$IFDEF USE_INLINE}inline;{$ENDIF}
  951. begin
  952. with PF do
  953. begin
  954. A := (Color and ABitMask shr AShift) * 255 div ARecDiv;
  955. R := (Color and RBitMask shr RShift) * 255 div RRecDiv;
  956. G := (Color and GBitMask shr GShift) * 255 div GRecDiv;
  957. B := (Color and BBitMask shl BShift) * 255 div BRecDiv;
  958. end;
  959. end;
  960. function PFSetColor(const PF: TPixelFormatInfo; ARGB: TColor32): LongWord;
  961. {$IFDEF USE_INLINE}inline;{$ENDIF}
  962. begin
  963. with PF do
  964. Result :=
  965. (Byte(ARGB shr 24) shl ABitCount shr 8 shl AShift) or
  966. (Byte(ARGB shr 16) shl RBitCount shr 8 shl RShift) or
  967. (Byte(ARGB shr 8) shl GBitCount shr 8 shl GShift) or
  968. (Byte(ARGB) shl BBitCount shr 8 shl BShift);
  969. end;
  970. function PFGetColor(const PF: TPixelFormatInfo; Color: LongWord): TColor32;
  971. {$IFDEF USE_INLINE}inline;{$ENDIF}
  972. begin
  973. with PF, TColor32Rec(Result) do
  974. begin
  975. A := (Color and ABitMask shr AShift) * 255 div ARecDiv;
  976. R := (Color and RBitMask shr RShift) * 255 div RRecDiv;
  977. G := (Color and GBitMask shr GShift) * 255 div GRecDiv;
  978. B := (Color and BBitMask shl BShift) * 255 div BRecDiv;
  979. end;
  980. end;
  981. { Additional image manipulation functions (usually used internally by Imaging unit) }
  982. const
  983. MaxPossibleColors = 4096;
  984. HashSize = 32768;
  985. AlphaWeight = 1024;
  986. RedWeight = 612;
  987. GreenWeight = 1202;
  988. BlueWeight = 234;
  989. type
  990. PColorBin = ^TColorBin;
  991. TColorBin = record
  992. Color: TColor32Rec;
  993. Number: LongInt;
  994. Next: PColorBin;
  995. end;
  996. THashTable = array[0..HashSize - 1] of PColorBin;
  997. TColorBox = record
  998. AMin, AMax,
  999. RMin, RMax,
  1000. GMin, GMax,
  1001. BMin, BMax: LongInt;
  1002. Total: LongInt;
  1003. Represented: TColor32Rec;
  1004. List: PColorBin;
  1005. end;
  1006. var
  1007. Table: THashTable;
  1008. Box: array[0..MaxPossibleColors - 1] of TColorBox;
  1009. Boxes: LongInt;
  1010. procedure ReduceColorsMedianCut(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
  1011. DstInfo: PImageFormatInfo; MaxColors: LongInt; ChannelMask: Byte;
  1012. DstPal: PPalette32; Actions: TReduceColorsActions);
  1013. procedure CreateHistogram (Src: PByte; SrcInfo: PImageFormatInfo;
  1014. ChannelMask: Byte);
  1015. var
  1016. A, R, G, B: Byte;
  1017. I, Addr: LongInt;
  1018. PC: PColorBin;
  1019. Col: TColor32Rec;
  1020. begin
  1021. for I := 0 to NumPixels - 1 do
  1022. begin
  1023. Col := GetPixel32Generic(Src, SrcInfo, nil);
  1024. A := Col.A and ChannelMask;
  1025. R := Col.R and ChannelMask;
  1026. G := Col.G and ChannelMask;
  1027. B := Col.B and ChannelMask;
  1028. Addr := (A + 11 * B + 59 * R + 119 * G) mod HashSize;
  1029. PC := Table[Addr];
  1030. while (PC <> nil) and ((PC.Color.R <> R) or (PC.Color.G <> G) or
  1031. (PC.Color.B <> B) or (PC.Color.A <> A)) do
  1032. PC := PC.Next;
  1033. if PC = nil then
  1034. begin
  1035. New(PC);
  1036. PC.Color.R := R;
  1037. PC.Color.G := G;
  1038. PC.Color.B := B;
  1039. PC.Color.A := A;
  1040. PC.Number := 1;
  1041. PC.Next := Table[Addr];
  1042. Table[Addr] := PC;
  1043. end
  1044. else
  1045. Inc(PC^.Number);
  1046. Inc(Src, SrcInfo.BytesPerPixel);
  1047. end;
  1048. end;
  1049. procedure InitBox (var Box : TColorBox);
  1050. begin
  1051. Box.AMin := 256;
  1052. Box.RMin := 256;
  1053. Box.GMin := 256;
  1054. Box.BMin := 256;
  1055. Box.AMax := -1;
  1056. Box.RMax := -1;
  1057. Box.GMax := -1;
  1058. Box.BMax := -1;
  1059. Box.Total := 0;
  1060. Box.List := nil;
  1061. end;
  1062. procedure ChangeBox (var Box: TColorBox; const C: TColorBin);
  1063. begin
  1064. with C.Color do
  1065. begin
  1066. if A < Box.AMin then Box.AMin := A;
  1067. if A > Box.AMax then Box.AMax := A;
  1068. if B < Box.BMin then Box.BMin := B;
  1069. if B > Box.BMax then Box.BMax := B;
  1070. if G < Box.GMin then Box.GMin := G;
  1071. if G > Box.GMax then Box.GMax := G;
  1072. if R < Box.RMin then Box.RMin := R;
  1073. if R > Box.RMax then Box.RMax := R;
  1074. end;
  1075. Inc(Box.Total, C.Number);
  1076. end;
  1077. procedure MakeColormap;
  1078. var
  1079. I, J: LongInt;
  1080. CP, Pom: PColorBin;
  1081. Cut, LargestIdx, Largest, Size, S: LongInt;
  1082. CutA, CutR, CutG, CutB: Boolean;
  1083. SumA, SumR, SumG, SumB: LongInt;
  1084. Temp: TColorBox;
  1085. begin
  1086. I := 0;
  1087. Boxes := 1;
  1088. LargestIdx := 0;
  1089. while (I < HashSize) and (Table[I] = nil) do
  1090. Inc(i);
  1091. if I < HashSize then
  1092. begin
  1093. // put all colors into Box[0]
  1094. InitBox(Box[0]);
  1095. repeat
  1096. CP := Table[I];
  1097. while CP.Next <> nil do
  1098. begin
  1099. ChangeBox(Box[0], CP^);
  1100. CP := CP.Next;
  1101. end;
  1102. ChangeBox(Box[0], CP^);
  1103. CP.Next := Box[0].List;
  1104. Box[0].List := Table[I];
  1105. Table[I] := nil;
  1106. repeat
  1107. Inc(I)
  1108. until (I = HashSize) or (Table[I] <> nil);
  1109. until I = HashSize;
  1110. // now all colors are in Box[0]
  1111. repeat
  1112. // cut one color box
  1113. Largest := 0;
  1114. for I := 0 to Boxes - 1 do
  1115. with Box[I] do
  1116. begin
  1117. Size := (AMax - AMin) * AlphaWeight;
  1118. S := (RMax - RMin) * RedWeight;
  1119. if S > Size then
  1120. Size := S;
  1121. S := (GMax - GMin) * GreenWeight;
  1122. if S > Size then
  1123. Size := S;
  1124. S := (BMax - BMin) * BlueWeight;
  1125. if S > Size then
  1126. Size := S;
  1127. if Size > Largest then
  1128. begin
  1129. Largest := Size;
  1130. LargestIdx := I;
  1131. end;
  1132. end;
  1133. if Largest > 0 then
  1134. begin
  1135. // cutting Box[LargestIdx] into Box[LargestIdx] and Box[Boxes]
  1136. CutR := False;
  1137. CutG := False;
  1138. CutB := False;
  1139. CutA := False;
  1140. with Box[LargestIdx] do
  1141. begin
  1142. if (AMax - AMin) * AlphaWeight = Largest then
  1143. begin
  1144. Cut := (AMax + AMin) shr 1;
  1145. CutA := True;
  1146. end
  1147. else
  1148. if (RMax - RMin) * RedWeight = Largest then
  1149. begin
  1150. Cut := (RMax + RMin) shr 1;
  1151. CutR := True;
  1152. end
  1153. else
  1154. if (GMax - GMin) * GreenWeight = Largest then
  1155. begin
  1156. Cut := (GMax + GMin) shr 1;
  1157. CutG := True;
  1158. end
  1159. else
  1160. begin
  1161. Cut := (BMax + BMin) shr 1;
  1162. CutB := True;
  1163. end;
  1164. CP := List;
  1165. end;
  1166. InitBox(Box[LargestIdx]);
  1167. InitBox(Box[Boxes]);
  1168. repeat
  1169. // distribute one color
  1170. Pom := CP.Next;
  1171. with CP.Color do
  1172. begin
  1173. if (CutA and (A <= Cut)) or (CutR and (R <= Cut)) or
  1174. (CutG and (G <= Cut)) or (CutB and (B <= Cut)) then
  1175. I := LargestIdx
  1176. else
  1177. I := Boxes;
  1178. end;
  1179. CP.Next := Box[i].List;
  1180. Box[i].List := CP;
  1181. ChangeBox(Box[i], CP^);
  1182. CP := Pom;
  1183. until CP = nil;
  1184. Inc(Boxes);
  1185. end;
  1186. until (Boxes = MaxColors) or (Largest = 0);
  1187. // compute box representation
  1188. for I := 0 to Boxes - 1 do
  1189. begin
  1190. SumR := 0;
  1191. SumG := 0;
  1192. SumB := 0;
  1193. SumA := 0;
  1194. repeat
  1195. CP := Box[I].List;
  1196. Inc(SumR, CP.Color.R * CP.Number);
  1197. Inc(SumG, CP.Color.G * CP.Number);
  1198. Inc(SumB, CP.Color.B * CP.Number);
  1199. Inc(SumA, CP.Color.A * CP.Number);
  1200. Box[I].List := CP.Next;
  1201. Dispose(CP);
  1202. until Box[I].List = nil;
  1203. with Box[I] do
  1204. begin
  1205. Represented.A := SumA div Total;
  1206. Represented.R := SumR div Total;
  1207. Represented.G := SumG div Total;
  1208. Represented.B := SumB div Total;
  1209. AMin := AMin and ChannelMask;
  1210. RMin := RMin and ChannelMask;
  1211. GMin := GMin and ChannelMask;
  1212. BMin := BMin and ChannelMask;
  1213. AMax := (AMax and ChannelMask) + (not ChannelMask);
  1214. RMax := (RMax and ChannelMask) + (not ChannelMask);
  1215. GMax := (GMax and ChannelMask) + (not ChannelMask);
  1216. BMax := (BMax and ChannelMask) + (not ChannelMask);
  1217. end;
  1218. end;
  1219. // sort color boxes
  1220. for I := 0 to Boxes - 2 do
  1221. begin
  1222. Largest := 0;
  1223. for J := I to Boxes - 1 do
  1224. if Box[J].Total > Largest then
  1225. begin
  1226. Largest := Box[J].Total;
  1227. LargestIdx := J;
  1228. end;
  1229. if LargestIdx <> I then
  1230. begin
  1231. Temp := Box[I];
  1232. Box[I] := Box[LargestIdx];
  1233. Box[LargestIdx] := Temp;
  1234. end;
  1235. end;
  1236. end;
  1237. end;
  1238. procedure FillOutputPalette;
  1239. var
  1240. I: LongInt;
  1241. begin
  1242. FillChar(DstPal^, SizeOf(TColor32Rec) * MaxColors, $FF);
  1243. for I := 0 to MaxColors - 1 do
  1244. begin
  1245. if I < Boxes then
  1246. with Box[I].Represented do
  1247. begin
  1248. DstPal[I].A := A;
  1249. DstPal[I].R := R;
  1250. DstPal[I].G := G;
  1251. DstPal[I].B := B;
  1252. end
  1253. else
  1254. DstPal[I].Color := $FF000000;
  1255. end;
  1256. end;
  1257. function MapColor(const Col: TColor32Rec) : LongInt;
  1258. var
  1259. I: LongInt;
  1260. begin
  1261. I := 0;
  1262. with Col do
  1263. while (I < Boxes) and ((Box[I].AMin > A) or (Box[I].AMax < A) or
  1264. (Box[I].RMin > R) or (Box[I].RMax < R) or (Box[I].GMin > G) or
  1265. (Box[I].GMax < G) or (Box[I].BMin > B) or (Box[I].BMax < B)) do
  1266. Inc(I);
  1267. if I = Boxes then
  1268. MapColor := 0
  1269. else
  1270. MapColor := I;
  1271. end;
  1272. procedure MapImage(Src, Dst: PByte; SrcInfo, DstInfo: PImageFormatInfo);
  1273. var
  1274. I: LongInt;
  1275. Col: TColor32Rec;
  1276. begin
  1277. for I := 0 to NumPixels - 1 do
  1278. begin
  1279. Col := GetPixel32Generic(Src, SrcInfo, nil);
  1280. IndexSetDstPixel(Dst, DstInfo, MapColor(Col));
  1281. Inc(Src, SrcInfo.BytesPerPixel);
  1282. Inc(Dst, DstInfo.BytesPerPixel);
  1283. end;
  1284. end;
  1285. begin
  1286. MaxColors := ClampInt(MaxColors, 2, MaxPossibleColors);
  1287. if (raUpdateHistogram in Actions) or (raMapImage in Actions) then
  1288. begin
  1289. Assert(not SrcInfo.IsSpecial);
  1290. Assert(not SrcInfo.IsIndexed);
  1291. end;
  1292. if raCreateHistogram in Actions then
  1293. FillChar(Table, SizeOf(Table), 0);
  1294. if raUpdateHistogram in Actions then
  1295. CreateHistogram(Src, SrcInfo, ChannelMask);
  1296. if raMakeColorMap in Actions then
  1297. begin
  1298. MakeColorMap;
  1299. FillOutputPalette;
  1300. end;
  1301. if raMapImage in Actions then
  1302. MapImage(Src, Dst, SrcInfo, DstInfo);
  1303. end;
  1304. procedure StretchNearest(const SrcImage: TImageData; SrcX, SrcY, SrcWidth,
  1305. SrcHeight: LongInt; var DstImage: TImageData; DstX, DstY, DstWidth,
  1306. DstHeight: LongInt);
  1307. var
  1308. Info: TImageFormatInfo;
  1309. ScaleX, ScaleY, X, Y, Xp, Yp: LongInt;
  1310. DstPixel, SrcLine: PByte;
  1311. begin
  1312. GetImageFormatInfo(SrcImage.Format, Info);
  1313. Assert(SrcImage.Format = DstImage.Format);
  1314. Assert(not Info.IsSpecial);
  1315. // Use integers instead of floats for source image pixel coords
  1316. // Xp and Yp coords must be shifted right to get read source image coords
  1317. ScaleX := (SrcWidth shl 16) div DstWidth;
  1318. ScaleY := (SrcHeight shl 16) div DstHeight;
  1319. Yp := 0;
  1320. for Y := 0 to DstHeight - 1 do
  1321. begin
  1322. Xp := 0;
  1323. SrcLine := @PByteArray(SrcImage.Bits)[((SrcY + Yp shr 16) * SrcImage.Width + SrcX) * Info.BytesPerPixel];
  1324. DstPixel := @PByteArray(DstImage.Bits)[((DstY + Y) * DstImage.Width + DstX) * Info.BytesPerPixel];
  1325. for X := 0 to DstWidth - 1 do
  1326. begin
  1327. case Info.BytesPerPixel of
  1328. 1: PByte(DstPixel)^ := PByteArray(SrcLine)[Xp shr 16];
  1329. 2: PWord(DstPixel)^ := PWordArray(SrcLine)[Xp shr 16];
  1330. 3: PColor24Rec(DstPixel)^ := PPalette24(SrcLine)[Xp shr 16];
  1331. 4: PColor32(DstPixel)^ := PLongWordArray(SrcLine)[Xp shr 16];
  1332. 6: PColor48Rec(DstPixel)^ := PColor48RecArray(SrcLine)[Xp shr 16];
  1333. 8: PColor64(DstPixel)^ := PInt64Array(SrcLine)[Xp shr 16];
  1334. 16: PColorFPRec(DstPixel)^ := PColorFPRecArray(SrcLine)[Xp shr 16];
  1335. end;
  1336. Inc(DstPixel, Info.BytesPerPixel);
  1337. Inc(Xp, ScaleX);
  1338. end;
  1339. Inc(Yp, ScaleY);
  1340. end;
  1341. end;
  1342. { Filter function for nearest filtering. Also known as box filter.}
  1343. function FilterNearest(Value: Single): Single;
  1344. begin
  1345. if (Value > -0.5) and (Value <= 0.5) then
  1346. Result := 1
  1347. else
  1348. Result := 0;
  1349. end;
  1350. { Filter function for linear filtering. Also known as triangle or Bartlett filter.}
  1351. function FilterLinear(Value: Single): Single;
  1352. begin
  1353. if Value < 0.0 then
  1354. Value := -Value;
  1355. if Value < 1.0 then
  1356. Result := 1.0 - Value
  1357. else
  1358. Result := 0.0;
  1359. end;
  1360. { Cosine filter.}
  1361. function FilterCosine(Value: Single): Single;
  1362. begin
  1363. Result := 0;
  1364. if Abs(Value) < 1 then
  1365. Result := (Cos(Value * Pi) + 1) / 2;
  1366. end;
  1367. { f(t) = 2|t|^3 - 3|t|^2 + 1, -1 <= t <= 1 }
  1368. function FilterHermite(Value: Single): Single;
  1369. begin
  1370. if Value < 0.0 then
  1371. Value := -Value;
  1372. if Value < 1 then
  1373. Result := (2 * Value - 3) * Sqr(Value) + 1
  1374. else
  1375. Result := 0;
  1376. end;
  1377. { Quadratic filter. Also known as Bell.}
  1378. function FilterQuadratic(Value: Single): Single;
  1379. begin
  1380. if Value < 0.0 then
  1381. Value := -Value;
  1382. if Value < 0.5 then
  1383. Result := 0.75 - Sqr(Value)
  1384. else
  1385. if Value < 1.5 then
  1386. begin
  1387. Value := Value - 1.5;
  1388. Result := 0.5 * Sqr(Value);
  1389. end
  1390. else
  1391. Result := 0.0;
  1392. end;
  1393. { Gaussian filter.}
  1394. function FilterGaussian(Value: Single): Single;
  1395. begin
  1396. Result := Exp(-2.0 * Sqr(Value)) * Sqrt(2.0 / Pi);
  1397. end;
  1398. { 4th order (cubic) b-spline filter.}
  1399. function FilterSpline(Value: Single): Single;
  1400. var
  1401. Temp: Single;
  1402. begin
  1403. if Value < 0.0 then
  1404. Value := -Value;
  1405. if Value < 1.0 then
  1406. begin
  1407. Temp := Sqr(Value);
  1408. Result := 0.5 * Temp * Value - Temp + 2.0 / 3.0;
  1409. end
  1410. else
  1411. if Value < 2.0 then
  1412. begin
  1413. Value := 2.0 - Value;
  1414. Result := Sqr(Value) * Value / 6.0;
  1415. end
  1416. else
  1417. Result := 0.0;
  1418. end;
  1419. { Lanczos-windowed sinc filter.}
  1420. function FilterLanczos(Value: Single): Single;
  1421. function SinC(Value: Single): Single;
  1422. begin
  1423. if Value <> 0.0 then
  1424. begin
  1425. Value := Value * Pi;
  1426. Result := Sin(Value) / Value;
  1427. end
  1428. else
  1429. Result := 1.0;
  1430. end;
  1431. begin
  1432. if Value < 0.0 then
  1433. Value := -Value;
  1434. if Value < 3.0 then
  1435. Result := SinC(Value) * SinC(Value / 3.0)
  1436. else
  1437. Result := 0.0;
  1438. end;
  1439. { Micthell cubic filter.}
  1440. function FilterMitchell(Value: Single): Single;
  1441. const
  1442. B = 1.0 / 3.0;
  1443. C = 1.0 / 3.0;
  1444. var
  1445. Temp: Single;
  1446. begin
  1447. if Value < 0.0 then
  1448. Value := -Value;
  1449. Temp := Sqr(Value);
  1450. if Value < 1.0 then
  1451. begin
  1452. Value := (((12.0 - 9.0 * B - 6.0 * C) * (Value * Temp)) +
  1453. ((-18.0 + 12.0 * B + 6.0 * C) * Temp) +
  1454. (6.0 - 2.0 * B));
  1455. Result := Value / 6.0;
  1456. end
  1457. else
  1458. if Value < 2.0 then
  1459. begin
  1460. Value := (((-B - 6.0 * C) * (Value * Temp)) +
  1461. ((6.0 * B + 30.0 * C) * Temp) +
  1462. ((-12.0 * B - 48.0 * C) * Value) +
  1463. (8.0 * B + 24.0 * C));
  1464. Result := Value / 6.0;
  1465. end
  1466. else
  1467. Result := 0.0;
  1468. end;
  1469. { CatmullRom spline filter.}
  1470. function FilterCatmullRom(Value: Single): Single;
  1471. begin
  1472. if Value < 0.0 then
  1473. Value := -Value;
  1474. if Value < 1.0 then
  1475. Result := 0.5 * (2.0 + Sqr(Value) * (-5.0 + 3.0 * Value))
  1476. else
  1477. if Value < 2.0 then
  1478. Result := 0.5 * (4.0 + Value * (-8.0 + Value * (5.0 - Value)))
  1479. else
  1480. Result := 0.0;
  1481. end;
  1482. procedure StretchResample(const SrcImage: TImageData; SrcX, SrcY, SrcWidth,
  1483. SrcHeight: LongInt; var DstImage: TImageData; DstX, DstY, DstWidth,
  1484. DstHeight: LongInt; Filter: TSamplingFilter; WrapEdges: Boolean);
  1485. begin
  1486. // Calls the other function with filter function and radius defined by Filter
  1487. StretchResample(SrcImage, SrcX, SrcY, SrcWidth, SrcHeight, DstImage, DstX, DstY,
  1488. DstWidth, DstHeight, SamplingFilterFunctions[Filter], SamplingFilterRadii[Filter],
  1489. WrapEdges);
  1490. end;
  1491. var
  1492. FullEdge: Boolean = True;
  1493. { The following resampling code is modified and extended code from Graphics32
  1494. library by Alex A. Denisov.}
  1495. function BuildMappingTable(DstLow, DstHigh, SrcLow, SrcHigh, SrcImageWidth: LongInt;
  1496. Filter: TFilterFunction; Radius: Single; WrapEdges: Boolean): TMappingTable;
  1497. var
  1498. I, J, K, N: LongInt;
  1499. Left, Right, SrcWidth, DstWidth: LongInt;
  1500. Weight, Scale, Center, Count: Single;
  1501. begin
  1502. Result := nil;
  1503. K := 0;
  1504. SrcWidth := SrcHigh - SrcLow;
  1505. DstWidth := DstHigh - DstLow;
  1506. // Check some special cases
  1507. if SrcWidth = 1 then
  1508. begin
  1509. SetLength(Result, DstWidth);
  1510. for I := 0 to DstWidth - 1 do
  1511. begin
  1512. SetLength(Result[I], 1);
  1513. Result[I][0].Pos := 0;
  1514. Result[I][0].Weight := 1.0;
  1515. end;
  1516. Exit;
  1517. end
  1518. else
  1519. if (SrcWidth = 0) or (DstWidth = 0) then
  1520. Exit;
  1521. if FullEdge then
  1522. Scale := DstWidth / SrcWidth
  1523. else
  1524. Scale := (DstWidth - 1) / (SrcWidth - 1);
  1525. SetLength(Result, DstWidth);
  1526. // Pre-calculate filter contributions for a row or column
  1527. if Scale = 0.0 then
  1528. begin
  1529. Assert(Length(Result) = 1);
  1530. SetLength(Result[0], 1);
  1531. Result[0][0].Pos := (SrcLow + SrcHigh) div 2;
  1532. Result[0][0].Weight := 1.0;
  1533. end
  1534. else if Scale < 1.0 then
  1535. begin
  1536. // Sub-sampling - scales from bigger to smaller
  1537. Radius := Radius / Scale;
  1538. for I := 0 to DstWidth - 1 do
  1539. begin
  1540. if FullEdge then
  1541. Center := SrcLow - 0.5 + (I + 0.5) / Scale
  1542. else
  1543. Center := SrcLow + I / Scale;
  1544. Left := Floor(Center - Radius);
  1545. Right := Ceil(Center + Radius);
  1546. Count := -1.0;
  1547. for J := Left to Right do
  1548. begin
  1549. Weight := Filter((Center - J) * Scale) * Scale;
  1550. if Weight <> 0.0 then
  1551. begin
  1552. Count := Count + Weight;
  1553. K := Length(Result[I]);
  1554. SetLength(Result[I], K + 1);
  1555. Result[I][K].Pos := ClampInt(J, SrcLow, SrcHigh - 1);
  1556. Result[I][K].Weight := Weight;
  1557. end;
  1558. end;
  1559. if Length(Result[I]) = 0 then
  1560. begin
  1561. SetLength(Result[I], 1);
  1562. Result[I][0].Pos := Floor(Center);
  1563. Result[I][0].Weight := 1.0;
  1564. end
  1565. else if Count <> 0.0 then
  1566. Result[I][K div 2].Weight := Result[I][K div 2].Weight - Count;
  1567. end;
  1568. end
  1569. else // if Scale > 1.0 then
  1570. begin
  1571. // Super-sampling - scales from smaller to bigger
  1572. Scale := 1.0 / Scale;
  1573. for I := 0 to DstWidth - 1 do
  1574. begin
  1575. if FullEdge then
  1576. Center := SrcLow - 0.5 + (I + 0.5) * Scale
  1577. else
  1578. Center := SrcLow + I * Scale;
  1579. Left := Floor(Center - Radius);
  1580. Right := Ceil(Center + Radius);
  1581. Count := -1.0;
  1582. for J := Left to Right do
  1583. begin
  1584. Weight := Filter(Center - J);
  1585. if Weight <> 0.0 then
  1586. begin
  1587. Count := Count + Weight;
  1588. K := Length(Result[I]);
  1589. SetLength(Result[I], K + 1);
  1590. if WrapEdges then
  1591. begin
  1592. if J < 0 then
  1593. N := SrcImageWidth + J
  1594. else if J >= SrcImageWidth then
  1595. N := J - SrcImageWidth
  1596. else
  1597. N := ClampInt(J, SrcLow, SrcHigh - 1);
  1598. end
  1599. else
  1600. N := ClampInt(J, SrcLow, SrcHigh - 1);
  1601. Result[I][K].Pos := N;
  1602. Result[I][K].Weight := Weight;
  1603. end;
  1604. end;
  1605. if Count <> 0.0 then
  1606. Result[I][K div 2].Weight := Result[I][K div 2].Weight - Count;
  1607. end;
  1608. end;
  1609. end;
  1610. procedure FindExtremes(const Map: TMappingTable; var MinPos, MaxPos: LongInt);
  1611. var
  1612. I, J: LongInt;
  1613. begin
  1614. if Length(Map) > 0 then
  1615. begin
  1616. MinPos := Map[0][0].Pos;
  1617. MaxPos := MinPos;
  1618. for I := 0 to Length(Map) - 1 do
  1619. for J := 0 to Length(Map[I]) - 1 do
  1620. begin
  1621. if MinPos > Map[I][J].Pos then
  1622. MinPos := Map[I][J].Pos;
  1623. if MaxPos < Map[I][J].Pos then
  1624. MaxPos := Map[I][J].Pos;
  1625. end;
  1626. end;
  1627. end;
  1628. procedure StretchResample(const SrcImage: TImageData; SrcX, SrcY, SrcWidth,
  1629. SrcHeight: LongInt; var DstImage: TImageData; DstX, DstY, DstWidth,
  1630. DstHeight: LongInt; Filter: TFilterFunction; Radius: Single; WrapEdges: Boolean);
  1631. var
  1632. MapX, MapY: TMappingTable;
  1633. I, J, X, Y: LongInt;
  1634. XMinimum, XMaximum: LongInt;
  1635. LineBufferFP: array of TColorFPRec;
  1636. ClusterX, ClusterY: TCluster;
  1637. Weight, AccumA, AccumR, AccumG, AccumB: Single;
  1638. DstLine: PByte;
  1639. SrcFloat: TColorFPRec;
  1640. Info: TImageFormatInfo;
  1641. BytesPerChannel: Integer;
  1642. begin
  1643. GetImageFormatInfo(SrcImage.Format, Info);
  1644. Assert(SrcImage.Format = DstImage.Format);
  1645. Assert(not Info.IsSpecial and not Info.IsIndexed);
  1646. BytesPerChannel := Info.BytesPerPixel div Info.ChannelCount;
  1647. // Create horizontal and vertical mapping tables
  1648. MapX := BuildMappingTable(DstX, DstX + DstWidth, SrcX, SrcX + SrcWidth,
  1649. SrcImage.Width, Filter, Radius, WrapEdges);
  1650. MapY := BuildMappingTable(DstY, DstY + DstHeight, SrcY, SrcY + SrcHeight,
  1651. SrcImage.Height, Filter, Radius, WrapEdges);
  1652. if (MapX = nil) or (MapY = nil) then
  1653. Exit;
  1654. ClusterX := nil;
  1655. ClusterY := nil;
  1656. try
  1657. // Find min and max X coords of pixels that will contribute to target image
  1658. FindExtremes(MapX, XMinimum, XMaximum);
  1659. SetLength(LineBufferFP, XMaximum - XMinimum + 1);
  1660. // Following code works for the rest of data formats
  1661. for J := 0 to DstHeight - 1 do
  1662. begin
  1663. // First for each pixel in the current line sample vertically
  1664. // and store results in LineBuffer. Then sample horizontally
  1665. // using values in LineBuffer.
  1666. ClusterY := MapY[J];
  1667. for X := XMinimum to XMaximum do
  1668. begin
  1669. // Clear accumulators
  1670. AccumA := 0;
  1671. AccumR := 0;
  1672. AccumG := 0;
  1673. AccumB := 0;
  1674. // For each pixel in line compute weighted sum of pixels
  1675. // in source column that will contribute to this pixel
  1676. for Y := 0 to Length(ClusterY) - 1 do
  1677. begin
  1678. // Accumulate this pixel's weighted value
  1679. Weight := ClusterY[Y].Weight;
  1680. SrcFloat := Info.GetPixelFP(@PByteArray(SrcImage.Bits)[(ClusterY[Y].Pos * SrcImage.Width + X) * Info.BytesPerPixel], @Info, nil);
  1681. AccumB := AccumB + SrcFloat.B * Weight;
  1682. AccumG := AccumG + SrcFloat.G * Weight;
  1683. AccumR := AccumR + SrcFloat.R * Weight;
  1684. AccumA := AccumA + SrcFloat.A * Weight;
  1685. end;
  1686. // Store accumulated value for this pixel in buffer
  1687. with LineBufferFP[X - XMinimum] do
  1688. begin
  1689. A := AccumA;
  1690. R := AccumR;
  1691. G := AccumG;
  1692. B := AccumB;
  1693. end;
  1694. end;
  1695. DstLine := @PByteArray(DstImage.Bits)[((J + DstY) * DstImage.Width + DstX) * Info.BytesPerPixel];
  1696. // Now compute final colors for targte pixels in the current row
  1697. // by sampling horizontally
  1698. for I := 0 to DstWidth - 1 do
  1699. begin
  1700. ClusterX := MapX[I];
  1701. // Clear accumulator
  1702. AccumA := 0;
  1703. AccumR := 0;
  1704. AccumG := 0;
  1705. AccumB := 0;
  1706. // Compute weighted sum of values (which are already
  1707. // computed weighted sums of pixels in source columns stored in LineBuffer)
  1708. // that will contribute to the current target pixel
  1709. for X := 0 to Length(ClusterX) - 1 do
  1710. begin
  1711. Weight := ClusterX[X].Weight;
  1712. with LineBufferFP[ClusterX[X].Pos - XMinimum] do
  1713. begin
  1714. AccumB := AccumB + B * Weight;
  1715. AccumG := AccumG + G * Weight;
  1716. AccumR := AccumR + R * Weight;
  1717. AccumA := AccumA + A * Weight;
  1718. end;
  1719. end;
  1720. // Now compute final color to be written to dest image
  1721. SrcFloat.A := AccumA;
  1722. SrcFloat.R := AccumR;
  1723. SrcFloat.G := AccumG;
  1724. SrcFloat.B := AccumB;
  1725. Info.SetPixelFP(DstLine, @Info, nil, SrcFloat);
  1726. Inc(DstLine, Info.BytesPerPixel);
  1727. end;
  1728. end;
  1729. finally
  1730. MapX := nil;
  1731. MapY := nil;
  1732. end;
  1733. end;
  1734. procedure FillMipMapLevel(const BiggerLevel: TImageData; Width, Height: LongInt;
  1735. var SmallerLevel: TImageData);
  1736. var
  1737. Filter: TSamplingFilter;
  1738. Info: TImageFormatInfo;
  1739. CompatibleCopy: TImageData;
  1740. begin
  1741. Assert(TestImage(BiggerLevel));
  1742. Filter := TSamplingFilter(GetOption(ImagingMipMapFilter));
  1743. // If we have special format image we must create copy to allow pixel access
  1744. GetImageFormatInfo(BiggerLevel.Format, Info);
  1745. if Info.IsSpecial then
  1746. begin
  1747. InitImage(CompatibleCopy);
  1748. CloneImage(BiggerLevel, CompatibleCopy);
  1749. ConvertImage(CompatibleCopy, ifDefault);
  1750. end
  1751. else
  1752. CompatibleCopy := BiggerLevel;
  1753. // Create new smaller image
  1754. NewImage(Width, Height, CompatibleCopy.Format, SmallerLevel);
  1755. GetImageFormatInfo(CompatibleCopy.Format, Info);
  1756. // If input is indexed we must copy its palette
  1757. if Info.IsIndexed then
  1758. CopyPalette(CompatibleCopy.Palette, SmallerLevel.Palette, 0, 0, Info.PaletteEntries);
  1759. if (Filter = sfNearest) or Info.IsIndexed then
  1760. begin
  1761. StretchNearest(CompatibleCopy, 0, 0, CompatibleCopy.Width, CompatibleCopy.Height,
  1762. SmallerLevel, 0, 0, Width, Height);
  1763. end
  1764. else
  1765. begin
  1766. StretchResample(CompatibleCopy, 0, 0, CompatibleCopy.Width, CompatibleCopy.Height,
  1767. SmallerLevel, 0, 0, Width, Height, Filter);
  1768. end;
  1769. // Free copy and convert result to special format if necessary
  1770. if CompatibleCopy.Format <> BiggerLevel.Format then
  1771. begin
  1772. ConvertImage(SmallerLevel, BiggerLevel.Format);
  1773. FreeImage(CompatibleCopy);
  1774. end;
  1775. end;
  1776. { Various format support functions }
  1777. procedure CopyPixel(Src, Dest: Pointer; BytesPerPixel: LongInt);
  1778. begin
  1779. case BytesPerPixel of
  1780. 1: PByte(Dest)^ := PByte(Src)^;
  1781. 2: PWord(Dest)^ := PWord(Src)^;
  1782. 3: PColor24Rec(Dest)^ := PColor24Rec(Src)^;
  1783. 4: PLongWord(Dest)^ := PLongWord(Src)^;
  1784. 6: PColor48Rec(Dest)^ := PColor48Rec(Src)^;
  1785. 8: PInt64(Dest)^ := PInt64(Src)^;
  1786. 12: PColor96FPRec(Dest)^ := PColor96FPRec(Src)^;
  1787. 16: PColorFPRec(Dest)^ := PColorFPRec(Src)^;
  1788. end;
  1789. end;
  1790. function ComparePixels(PixelA, PixelB: Pointer; BytesPerPixel: LongInt): Boolean;
  1791. begin
  1792. case BytesPerPixel of
  1793. 1: Result := PByte(PixelA)^ = PByte(PixelB)^;
  1794. 2: Result := PWord(PixelA)^ = PWord(PixelB)^;
  1795. 3: Result := (PWord(PixelA)^ = PWord(PixelB)^) and (PColor24Rec(PixelA).R = PColor24Rec(PixelB).R);
  1796. 4: Result := PLongWord(PixelA)^ = PLongWord(PixelB)^;
  1797. 6: Result := (PLongWord(PixelA)^ = PLongWord(PixelB)^) and (PColor48Rec(PixelA).R = PColor48Rec(PixelB).R);
  1798. 8: Result := PInt64(PixelA)^ = PInt64(PixelB)^;
  1799. 12: Result := (PFloatHelper(PixelA).Data = PFloatHelper(PixelB).Data) and
  1800. (PFloatHelper(PixelA).Data32 = PFloatHelper(PixelB).Data32);
  1801. 16: Result := (PFloatHelper(PixelA).Data = PFloatHelper(PixelB).Data) and
  1802. (PFloatHelper(PixelA).Data64 = PFloatHelper(PixelB).Data64);
  1803. else
  1804. Result := False;
  1805. end;
  1806. end;
  1807. procedure TranslatePixel(SrcPixel, DstPixel: Pointer; SrcFormat,
  1808. DstFormat: TImageFormat; SrcPalette, DstPalette: PPalette32);
  1809. var
  1810. SrcInfo, DstInfo: PImageFormatInfo;
  1811. PixFP: TColorFPRec;
  1812. begin
  1813. SrcInfo := FInfos[SrcFormat];
  1814. DstInfo := FInfos[DstFormat];
  1815. PixFP := GetPixelFPGeneric(SrcPixel, SrcInfo, SrcPalette);
  1816. SetPixelFPGeneric(DstPixel, DstInfo, DstPalette, PixFP);
  1817. end;
  1818. procedure ClampFloatPixel(var PixF: TColorFPRec);
  1819. begin
  1820. if PixF.A > 1.0 then
  1821. PixF.A := 1.0;
  1822. if PixF.R > 1.0 then
  1823. PixF.R := 1.0;
  1824. if PixF.G > 1.0 then
  1825. PixF.G := 1.0;
  1826. if PixF.B > 1.0 then
  1827. PixF.B := 1.0;
  1828. if PixF.A < 0.0 then
  1829. PixF.A := 0.0;
  1830. if PixF.R < 0.0 then
  1831. PixF.R := 0.0;
  1832. if PixF.G < 0.0 then
  1833. PixF.G := 0.0;
  1834. if PixF.B < 0.0 then
  1835. PixF.B := 0.0;
  1836. end;
  1837. procedure ConvertToPixel32(SrcPix: PByte; DestPix: PColor32Rec;
  1838. const SrcInfo: TImageFormatInfo; SrcPalette: PPalette32);
  1839. begin
  1840. case SrcInfo.Format of
  1841. ifIndex8:
  1842. begin
  1843. DestPix^ := SrcPalette[SrcPix^];
  1844. end;
  1845. ifGray8:
  1846. begin
  1847. DestPix.R := SrcPix^;
  1848. DestPix.G := SrcPix^;
  1849. DestPix.B := SrcPix^;
  1850. DestPix.A := 255;
  1851. end;
  1852. ifA8Gray8:
  1853. begin
  1854. DestPix.R := SrcPix^;
  1855. DestPix.G := SrcPix^;
  1856. DestPix.B := SrcPix^;
  1857. DestPix.A := PWordRec(SrcPix).High;
  1858. end;
  1859. ifGray16:
  1860. begin
  1861. DestPix.R := PWord(SrcPix)^ shr 8;
  1862. DestPix.G := DestPix.R;
  1863. DestPix.B := DestPix.R;
  1864. DestPix.A := 255;
  1865. end;
  1866. ifR8G8B8:
  1867. begin
  1868. DestPix.Color24Rec := PColor24Rec(SrcPix)^;
  1869. DestPix.A := 255;
  1870. end;
  1871. ifA8R8G8B8:
  1872. begin
  1873. DestPix^ := PColor32Rec(SrcPix)^;
  1874. end;
  1875. ifR16G16B16:
  1876. begin
  1877. DestPix.R := PColor48Rec(SrcPix).R shr 8;
  1878. DestPix.G := PColor48Rec(SrcPix).G shr 8;
  1879. DestPix.B := PColor48Rec(SrcPix).B shr 8;
  1880. DestPix.A := 255;
  1881. end;
  1882. ifA16R16G16B16:
  1883. begin
  1884. DestPix.R := PColor64Rec(SrcPix).R shr 8;
  1885. DestPix.G := PColor64Rec(SrcPix).G shr 8;
  1886. DestPix.B := PColor64Rec(SrcPix).B shr 8;
  1887. DestPix.A := PColor64Rec(SrcPix).A shr 8;
  1888. end;
  1889. else
  1890. DestPix^ := SrcInfo.GetPixel32(SrcPix, @SrcInfo, SrcPalette);
  1891. end;
  1892. end;
  1893. procedure AddPadBytes(DataIn: Pointer; DataOut: Pointer; Width, Height,
  1894. Bpp, WidthBytes: LongInt);
  1895. var
  1896. I, W: LongInt;
  1897. begin
  1898. W := Width * Bpp;
  1899. for I := 0 to Height - 1 do
  1900. Move(PByteArray(DataIn)[I * W], PByteArray(DataOut)[I * WidthBytes], W);
  1901. end;
  1902. procedure RemovePadBytes(DataIn: Pointer; DataOut: Pointer; Width, Height,
  1903. Bpp, WidthBytes: LongInt);
  1904. var
  1905. I, W: LongInt;
  1906. begin
  1907. W := Width * Bpp;
  1908. for I := 0 to Height - 1 do
  1909. Move(PByteArray(DataIn)[I * WidthBytes], PByteArray(DataOut)[I * W], W);
  1910. end;
  1911. procedure Convert1To8(DataIn, DataOut: PByte; Width, Height,
  1912. WidthBytes: LongInt; ScaleTo8Bits: Boolean);
  1913. const
  1914. Mask1: array[0..7] of Byte = ($80, $40, $20, $10, $08, $04, $02, $01);
  1915. Shift1: array[0..7] of Byte = (7, 6, 5, 4, 3, 2, 1, 0);
  1916. Scaling: Byte = 255;
  1917. var
  1918. X, Y: LongInt;
  1919. InArray: PByteArray absolute DataIn;
  1920. begin
  1921. for Y := 0 to Height - 1 do
  1922. for X := 0 to Width - 1 do
  1923. begin
  1924. DataOut^ := (InArray[Y * WidthBytes + X shr 3] and Mask1[X and 7]) shr Shift1[X and 7];
  1925. if ScaleTo8Bits then
  1926. DataOut^ := DataOut^ * Scaling;
  1927. Inc(DataOut);
  1928. end;
  1929. end;
  1930. procedure Convert2To8(DataIn, DataOut: PByte; Width, Height,
  1931. WidthBytes: LongInt; ScaleTo8Bits: Boolean);
  1932. const
  1933. Mask2: array[0..3] of Byte = ($C0, $30, $0C, $03);
  1934. Shift2: array[0..3] of Byte = (6, 4, 2, 0);
  1935. Scaling: Byte = 85;
  1936. var
  1937. X, Y: LongInt;
  1938. InArray: PByteArray absolute DataIn;
  1939. begin
  1940. for Y := 0 to Height - 1 do
  1941. for X := 0 to Width - 1 do
  1942. begin
  1943. DataOut^ := (InArray[Y * WidthBytes + X shr 2] and Mask2[X and 3]) shr Shift2[X and 3];
  1944. if ScaleTo8Bits then
  1945. DataOut^ := DataOut^ * Scaling;
  1946. Inc(DataOut);
  1947. end;
  1948. end;
  1949. procedure Convert4To8(DataIn, DataOut: PByte; Width, Height,
  1950. WidthBytes: LongInt; ScaleTo8Bits: Boolean);
  1951. const
  1952. Mask4: array[0..1] of Byte = ($F0, $0F);
  1953. Shift4: array[0..1] of Byte = (4, 0);
  1954. Scaling: Byte = 17;
  1955. var
  1956. X, Y: LongInt;
  1957. InArray: PByteArray absolute DataIn;
  1958. begin
  1959. for Y := 0 to Height - 1 do
  1960. for X := 0 to Width - 1 do
  1961. begin
  1962. DataOut^ := (InArray[Y * WidthBytes + X shr 1] and Mask4[X and 1]) shr Shift4[X and 1];
  1963. if ScaleTo8Bits then
  1964. DataOut^ := DataOut^ * Scaling;
  1965. Inc(DataOut);
  1966. end;
  1967. end;
  1968. function Has16BitImageAlpha(NumPixels: LongInt; Data: PWord): Boolean;
  1969. var
  1970. I: LongInt;
  1971. begin
  1972. Result := False;
  1973. for I := 0 to NumPixels - 1 do
  1974. begin
  1975. if Data^ >= 1 shl 15 then
  1976. begin
  1977. Result := True;
  1978. Exit;
  1979. end;
  1980. Inc(Data);
  1981. end;
  1982. end;
  1983. function Has32BitImageAlpha(NumPixels: LongInt; Data: PLongWord): Boolean;
  1984. var
  1985. I: LongInt;
  1986. begin
  1987. Result := False;
  1988. for I := 0 to NumPixels - 1 do
  1989. begin
  1990. if Data^ >= 1 shl 24 then
  1991. begin
  1992. Result := True;
  1993. Exit;
  1994. end;
  1995. Inc(Data);
  1996. end;
  1997. end;
  1998. function PaletteHasAlpha(Palette: PPalette32; PaletteEntries: Integer): Boolean;
  1999. var
  2000. I: Integer;
  2001. begin
  2002. for I := 0 to PaletteEntries - 1 do
  2003. begin
  2004. if Palette[I].A <> 255 then
  2005. begin
  2006. Result := True;
  2007. Exit;
  2008. end;
  2009. end;
  2010. Result := False;
  2011. end;
  2012. function PaletteIsGrayScale(Palette: PPalette32; PaletteEntries: Integer): Boolean;
  2013. var
  2014. I: Integer;
  2015. begin
  2016. for I := 0 to PaletteEntries - 1 do
  2017. begin
  2018. if (Palette[I].R <> Palette[I].G) or (Palette[I].R <> Palette[I].B) then
  2019. begin
  2020. Result := False;
  2021. Exit;
  2022. end;
  2023. end;
  2024. Result := True;
  2025. end;
  2026. function GetScanLine(ImageBits: Pointer; const FormatInfo: TImageFormatInfo;
  2027. LineWidth, Index: LongInt): Pointer;
  2028. var
  2029. LineBytes: LongInt;
  2030. begin
  2031. Assert(not FormatInfo.IsSpecial);
  2032. LineBytes := FormatInfo.GetPixelsSize(FormatInfo.Format, LineWidth, 1);
  2033. Result := @PByteArray(ImageBits)[Index * LineBytes];
  2034. end;
  2035. function IsImageFormatValid(Format: TImageFormat): Boolean;
  2036. begin
  2037. Result := FInfos[Format] <> nil;
  2038. end;
  2039. const
  2040. HalfMin: Single = 5.96046448e-08; // Smallest positive half
  2041. HalfMinNorm: Single = 6.10351562e-05; // Smallest positive normalized half
  2042. HalfMax: Single = 65504.0; // Largest positive half
  2043. HalfEpsilon: Single = 0.00097656; // Smallest positive e for which half (1.0 + e) != half (1.0)
  2044. HalfNaN: THalfFloat = 65535;
  2045. HalfPosInf: THalfFloat = 31744;
  2046. HalfNegInf: THalfFloat = 64512;
  2047. {
  2048. Half/Float conversions inspired by half class from OpenEXR library.
  2049. Float (Pascal Single type) is an IEEE 754 single-precision
  2050. floating point number.
  2051. Bit layout of Single:
  2052. 31 (msb)
  2053. |
  2054. | 30 23
  2055. | | |
  2056. | | | 22 0 (lsb)
  2057. | | | | |
  2058. X XXXXXXXX XXXXXXXXXXXXXXXXXXXXXXX
  2059. s e m
  2060. Bit layout of half:
  2061. 15 (msb)
  2062. |
  2063. | 14 10
  2064. | | |
  2065. | | | 9 0 (lsb)
  2066. | | | | |
  2067. X XXXXX XXXXXXXXXX
  2068. s e m
  2069. S is the sign-bit, e is the exponent and m is the significand (mantissa).
  2070. }
  2071. function HalfToFloat(Half: THalfFloat): Single;
  2072. var
  2073. Dst, Sign, Mantissa: LongWord;
  2074. Exp: LongInt;
  2075. begin
  2076. // Extract sign, exponent, and mantissa from half number
  2077. Sign := Half shr 15;
  2078. Exp := (Half and $7C00) shr 10;
  2079. Mantissa := Half and 1023;
  2080. if (Exp > 0) and (Exp < 31) then
  2081. begin
  2082. // Common normalized number
  2083. Exp := Exp + (127 - 15);
  2084. Mantissa := Mantissa shl 13;
  2085. Dst := (Sign shl 31) or (LongWord(Exp) shl 23) or Mantissa;
  2086. // Result := Power(-1, Sign) * Power(2, Exp - 15) * (1 + Mantissa / 1024);
  2087. end
  2088. else if (Exp = 0) and (Mantissa = 0) then
  2089. begin
  2090. // Zero - preserve sign
  2091. Dst := Sign shl 31;
  2092. end
  2093. else if (Exp = 0) and (Mantissa <> 0) then
  2094. begin
  2095. // Denormalized number - renormalize it
  2096. while (Mantissa and $00000400) = 0 do
  2097. begin
  2098. Mantissa := Mantissa shl 1;
  2099. Dec(Exp);
  2100. end;
  2101. Inc(Exp);
  2102. Mantissa := Mantissa and not $00000400;
  2103. // Now assemble normalized number
  2104. Exp := Exp + (127 - 15);
  2105. Mantissa := Mantissa shl 13;
  2106. Dst := (Sign shl 31) or (LongWord(Exp) shl 23) or Mantissa;
  2107. // Result := Power(-1, Sign) * Power(2, -14) * (Mantissa / 1024);
  2108. end
  2109. else if (Exp = 31) and (Mantissa = 0) then
  2110. begin
  2111. // +/- infinity
  2112. Dst := (Sign shl 31) or $7F800000;
  2113. end
  2114. else //if (Exp = 31) and (Mantisa <> 0) then
  2115. begin
  2116. // Not a number - preserve sign and mantissa
  2117. Dst := (Sign shl 31) or $7F800000 or (Mantissa shl 13);
  2118. end;
  2119. // Reinterpret LongWord as Single
  2120. Result := PSingle(@Dst)^;
  2121. end;
  2122. function FloatToHalf(Float: Single): THalfFloat;
  2123. var
  2124. Src: LongWord;
  2125. Sign, Exp, Mantissa: LongInt;
  2126. begin
  2127. Src := PLongWord(@Float)^;
  2128. // Extract sign, exponent, and mantissa from Single number
  2129. Sign := Src shr 31;
  2130. Exp := LongInt((Src and $7F800000) shr 23) - 127 + 15;
  2131. Mantissa := Src and $007FFFFF;
  2132. if (Exp > 0) and (Exp < 30) then
  2133. begin
  2134. // Simple case - round the significand and combine it with the sign and exponent
  2135. Result := (Sign shl 15) or (Exp shl 10) or ((Mantissa + $00001000) shr 13);
  2136. end
  2137. else if Src = 0 then
  2138. begin
  2139. // Input float is zero - return zero
  2140. Result := 0;
  2141. end
  2142. else
  2143. begin
  2144. // Difficult case - lengthy conversion
  2145. if Exp <= 0 then
  2146. begin
  2147. if Exp < -10 then
  2148. begin
  2149. // Input float's value is less than HalfMin, return zero
  2150. Result := 0;
  2151. end
  2152. else
  2153. begin
  2154. // Float is a normalized Single whose magnitude is less than HalfNormMin.
  2155. // We convert it to denormalized half.
  2156. Mantissa := (Mantissa or $00800000) shr (1 - Exp);
  2157. // Round to nearest
  2158. if (Mantissa and $00001000) > 0 then
  2159. Mantissa := Mantissa + $00002000;
  2160. // Assemble Sign and Mantissa (Exp is zero to get denormalized number)
  2161. Result := (Sign shl 15) or (Mantissa shr 13);
  2162. end;
  2163. end
  2164. else if Exp = 255 - 127 + 15 then
  2165. begin
  2166. if Mantissa = 0 then
  2167. begin
  2168. // Input float is infinity, create infinity half with original sign
  2169. Result := (Sign shl 15) or $7C00;
  2170. end
  2171. else
  2172. begin
  2173. // Input float is NaN, create half NaN with original sign and mantissa
  2174. Result := (Sign shl 15) or $7C00 or (Mantissa shr 13);
  2175. end;
  2176. end
  2177. else
  2178. begin
  2179. // Exp is > 0 so input float is normalized Single
  2180. // Round to nearest
  2181. if (Mantissa and $00001000) > 0 then
  2182. begin
  2183. Mantissa := Mantissa + $00002000;
  2184. if (Mantissa and $00800000) > 0 then
  2185. begin
  2186. Mantissa := 0;
  2187. Exp := Exp + 1;
  2188. end;
  2189. end;
  2190. if Exp > 30 then
  2191. begin
  2192. // Exponent overflow - return infinity half
  2193. Result := (Sign shl 15) or $7C00;
  2194. end
  2195. else
  2196. // Assemble normalized half
  2197. Result := (Sign shl 15) or (Exp shl 10) or (Mantissa shr 13);
  2198. end;
  2199. end;
  2200. end;
  2201. function ColorHalfToFloat(ColorHF: TColorHFRec): TColorFPRec;
  2202. begin
  2203. Result.A := HalfToFloat(ColorHF.A);
  2204. Result.R := HalfToFloat(ColorHF.R);
  2205. Result.G := HalfToFloat(ColorHF.G);
  2206. Result.B := HalfToFloat(ColorHF.B);
  2207. end;
  2208. function ColorFloatToHalf(ColorFP: TColorFPRec): TColorHFRec;
  2209. begin
  2210. Result.A := FloatToHalf(ColorFP.A);
  2211. Result.R := FloatToHalf(ColorFP.R);
  2212. Result.G := FloatToHalf(ColorFP.G);
  2213. Result.B := FloatToHalf(ColorFP.B);
  2214. end;
  2215. function Color32ToGray(Color32: TColor32): Byte;
  2216. begin
  2217. Result := Round(GrayConv.R * TColor32Rec(Color32).R +
  2218. GrayConv.G * TColor32Rec(Color32).G +
  2219. GrayConv.B * TColor32Rec(Color32).B);
  2220. end;
  2221. procedure VisualizePalette(Pal: PPalette32; Entries: Integer; out PalImage: TImageData);
  2222. var
  2223. I: Integer;
  2224. Pix: PColor32;
  2225. begin
  2226. InitImage(PalImage);
  2227. NewImage(Entries, 1, ifA8R8G8B8, PalImage);
  2228. Pix := PalImage.Bits;
  2229. for I := 0 to Entries - 1 do
  2230. begin
  2231. Pix^ := Pal[I].Color;
  2232. Inc(Pix);
  2233. end;
  2234. end;
  2235. { Pixel readers/writers for different image formats }
  2236. procedure ChannelGetSrcPixel(Src: PByte; SrcInfo: PImageFormatInfo;
  2237. var Pix: TColor64Rec);
  2238. var
  2239. A, R, G, B: Byte;
  2240. begin
  2241. FillChar(Pix, SizeOf(Pix), 0);
  2242. // returns 64 bit color value with 16 bits for each channel
  2243. case SrcInfo.BytesPerPixel of
  2244. 1:
  2245. begin
  2246. PFGetARGB(SrcInfo.PixelFormat^, Src^, A, R, G, B);
  2247. Pix.A := A shl 8;
  2248. Pix.R := R shl 8;
  2249. Pix.G := G shl 8;
  2250. Pix.B := B shl 8;
  2251. end;
  2252. 2:
  2253. begin
  2254. PFGetARGB(SrcInfo.PixelFormat^, PWord(Src)^, A, R, G, B);
  2255. Pix.A := A shl 8;
  2256. Pix.R := R shl 8;
  2257. Pix.G := G shl 8;
  2258. Pix.B := B shl 8;
  2259. end;
  2260. 3:
  2261. with Pix do
  2262. begin
  2263. R := MulDiv(PColor24Rec(Src).R, 65535, 255);
  2264. G := MulDiv(PColor24Rec(Src).G, 65535, 255);
  2265. B := MulDiv(PColor24Rec(Src).B, 65535, 255);
  2266. end;
  2267. 4:
  2268. with Pix do
  2269. begin
  2270. A := MulDiv(PColor32Rec(Src).A, 65535, 255);
  2271. R := MulDiv(PColor32Rec(Src).R, 65535, 255);
  2272. G := MulDiv(PColor32Rec(Src).G, 65535, 255);
  2273. B := MulDiv(PColor32Rec(Src).B, 65535, 255);
  2274. end;
  2275. 6:
  2276. with Pix do
  2277. begin
  2278. R := PColor48Rec(Src).R;
  2279. G := PColor48Rec(Src).G;
  2280. B := PColor48Rec(Src).B;
  2281. end;
  2282. 8: Pix.Color := PColor64(Src)^;
  2283. end;
  2284. // if src has no alpha, we set it to max (otherwise we would have to
  2285. // test if dest has alpha or not in each ChannelToXXX function)
  2286. if not SrcInfo.HasAlphaChannel then
  2287. Pix.A := 65535;
  2288. if SrcInfo.IsRBSwapped then
  2289. SwapValues(Pix.R, Pix.B);
  2290. end;
  2291. procedure ChannelSetDstPixel(Dst: PByte; DstInfo: PImageFormatInfo;
  2292. const Pix: TColor64Rec);
  2293. var
  2294. PixW: TColor64Rec;
  2295. begin
  2296. PixW := Pix;
  2297. if DstInfo.IsRBSwapped then
  2298. SwapValues(PixW.R, PixW.B);
  2299. // Pix contains 64 bit color value with 16 bit for each channel
  2300. case DstInfo.BytesPerPixel of
  2301. 1: Dst^ := PFSetARGB(DstInfo.PixelFormat^, PixW.A shr 8,
  2302. PixW.R shr 8, PixW.G shr 8, PixW.B shr 8);
  2303. 2: PWord(Dst)^ := PFSetARGB(DstInfo.PixelFormat^, PixW.A shr 8,
  2304. PixW.R shr 8, PixW.G shr 8, PixW.B shr 8);
  2305. 3:
  2306. with PColor24Rec(Dst)^ do
  2307. begin
  2308. R := MulDiv(PixW.R, 255, 65535);
  2309. G := MulDiv(PixW.G, 255, 65535);
  2310. B := MulDiv(PixW.B, 255, 65535);
  2311. end;
  2312. 4:
  2313. with PColor32Rec(Dst)^ do
  2314. begin
  2315. A := MulDiv(PixW.A, 255, 65535);
  2316. R := MulDiv(PixW.R, 255, 65535);
  2317. G := MulDiv(PixW.G, 255, 65535);
  2318. B := MulDiv(PixW.B, 255, 65535);
  2319. end;
  2320. 6:
  2321. with PColor48Rec(Dst)^ do
  2322. begin
  2323. R := PixW.R;
  2324. G := PixW.G;
  2325. B := PixW.B;
  2326. end;
  2327. 8: PColor64(Dst)^ := PixW.Color;
  2328. end;
  2329. end;
  2330. procedure GrayGetSrcPixel(Src: PByte; SrcInfo: PImageFormatInfo;
  2331. var Gray: TColor64Rec; var Alpha: Word);
  2332. begin
  2333. FillChar(Gray, SizeOf(Gray), 0);
  2334. // Source alpha is scaled to 16 bits and stored in Alpha,
  2335. // grayscale value is scaled to 64 bits and stored in Gray
  2336. case SrcInfo.BytesPerPixel of
  2337. 1: Gray.A := MulDiv(Src^, 65535, 255);
  2338. 2:
  2339. if SrcInfo.HasAlphaChannel then
  2340. with PWordRec(Src)^ do
  2341. begin
  2342. Alpha := MulDiv(High, 65535, 255);
  2343. Gray.A := MulDiv(Low, 65535, 255);
  2344. end
  2345. else
  2346. Gray.A := PWord(Src)^;
  2347. 4:
  2348. if SrcInfo.HasAlphaChannel then
  2349. with PLongWordRec(Src)^ do
  2350. begin
  2351. Alpha := High;
  2352. Gray.A := Low;
  2353. end
  2354. else
  2355. with PLongWordRec(Src)^ do
  2356. begin
  2357. Gray.A := High;
  2358. Gray.R := Low;
  2359. end;
  2360. 8: Gray.Color := PColor64(Src)^;
  2361. end;
  2362. // if src has no alpha, we set it to max (otherwise we would have to
  2363. // test if dest has alpha or not in each GrayToXXX function)
  2364. if not SrcInfo.HasAlphaChannel then
  2365. Alpha := 65535;
  2366. end;
  2367. procedure GraySetDstPixel(Dst: PByte; DstInfo: PImageFormatInfo;
  2368. const Gray: TColor64Rec; Alpha: Word);
  2369. begin
  2370. // Gray contains grayscale value scaled to 64 bits, Alpha contains
  2371. // alpha value scaled to 16 bits
  2372. case DstInfo.BytesPerPixel of
  2373. 1: Dst^ := MulDiv(Gray.A, 255, 65535);
  2374. 2:
  2375. if DstInfo.HasAlphaChannel then
  2376. with PWordRec(Dst)^ do
  2377. begin
  2378. High := MulDiv(Alpha, 255, 65535);
  2379. Low := MulDiv(Gray.A, 255, 65535);
  2380. end
  2381. else
  2382. PWord(Dst)^ := Gray.A;
  2383. 4:
  2384. if DstInfo.HasAlphaChannel then
  2385. with PLongWordRec(Dst)^ do
  2386. begin
  2387. High := Alpha;
  2388. Low := Gray.A;
  2389. end
  2390. else
  2391. with PLongWordRec(Dst)^ do
  2392. begin
  2393. High := Gray.A;
  2394. Low := Gray.R;
  2395. end;
  2396. 8: PColor64(Dst)^ := Gray.Color;
  2397. end;
  2398. end;
  2399. procedure FloatGetSrcPixel(Src: PByte; SrcInfo: PImageFormatInfo;
  2400. var Pix: TColorFPRec);
  2401. var
  2402. PixHF: TColorHFRec;
  2403. begin
  2404. Assert(SrcInfo.BytesPerPixel in [2, 4, 8, 12, 16]);
  2405. if SrcInfo.BytesPerPixel in [4, 12, 16] then
  2406. begin
  2407. // IEEE 754 single-precision channels
  2408. FillChar(Pix, SizeOf(Pix), 0);
  2409. case SrcInfo.BytesPerPixel of
  2410. 4: Pix.R := PSingle(Src)^;
  2411. 12: Pix.Color96Rec := PColor96FPRec(Src)^;
  2412. 16: Pix := PColorFPRec(Src)^;
  2413. end;
  2414. end
  2415. else
  2416. begin
  2417. // Half float channels
  2418. FillChar(PixHF, SizeOf(PixHF), 0);
  2419. case SrcInfo.BytesPerPixel of
  2420. 2: PixHF.R := PHalfFloat(Src)^;
  2421. 8: PixHF := PColorHFRec(Src)^;
  2422. end;
  2423. Pix := ColorHalfToFloat(PixHF);
  2424. end;
  2425. // If src has no alpha, we set it to max (otherwise we would have to
  2426. // test if dest has alpha or not in each FloatToXXX function)
  2427. if not SrcInfo.HasAlphaChannel then
  2428. Pix.A := 1.0;
  2429. if SrcInfo.IsRBSwapped then
  2430. SwapValues(Pix.R, Pix.B);
  2431. end;
  2432. procedure FloatSetDstPixel(Dst: PByte; DstInfo: PImageFormatInfo;
  2433. const Pix: TColorFPRec);
  2434. var
  2435. PixW: TColorFPRec;
  2436. PixHF: TColorHFRec;
  2437. begin
  2438. Assert(DstInfo.BytesPerPixel in [2, 4, 8, 12, 16]);
  2439. PixW := Pix;
  2440. if DstInfo.IsRBSwapped then
  2441. SwapValues(PixW.R, PixW.B);
  2442. if DstInfo.BytesPerPixel in [4, 12, 16] then
  2443. begin
  2444. case DstInfo.BytesPerPixel of
  2445. 4: PSingle(Dst)^ := PixW.R;
  2446. 12: PColor96FPRec(Dst)^:= PixW.Color96Rec;
  2447. 16: PColorFPRec(Dst)^ := PixW;
  2448. end;
  2449. end
  2450. else
  2451. begin
  2452. PixHF := ColorFloatToHalf(PixW);
  2453. case DstInfo.BytesPerPixel of
  2454. 2: PHalfFloat(Dst)^ := PixHF.R;
  2455. 8: PColorHFRec(Dst)^ := PixHF;
  2456. end;
  2457. end;
  2458. end;
  2459. procedure IndexGetSrcPixel(Src: PByte; SrcInfo: PImageFormatInfo;
  2460. var Index: LongWord);
  2461. begin
  2462. case SrcInfo.BytesPerPixel of
  2463. 1: Index := Src^;
  2464. end;
  2465. end;
  2466. procedure IndexSetDstPixel(Dst: PByte; DstInfo: PImageFormatInfo;
  2467. Index: LongWord);
  2468. begin
  2469. case DstInfo.BytesPerPixel of
  2470. 1: Dst^ := Byte(Index);
  2471. 2: PWord(Dst)^ := Word(Index);
  2472. 4: PLongWord(Dst)^ := Index;
  2473. end;
  2474. end;
  2475. { Pixel readers/writers for 32bit and FP colors}
  2476. function GetPixel32Generic(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32): TColor32Rec;
  2477. var
  2478. Pix64: TColor64Rec;
  2479. PixF: TColorFPRec;
  2480. Alpha: Word;
  2481. Index: LongWord;
  2482. begin
  2483. if Info.Format = ifA8R8G8B8 then
  2484. begin
  2485. Result := PColor32Rec(Bits)^
  2486. end
  2487. else if Info.Format = ifR8G8B8 then
  2488. begin
  2489. PColor24Rec(@Result)^ := PColor24Rec(Bits)^;
  2490. Result.A := $FF;
  2491. end
  2492. else if Info.IsFloatingPoint then
  2493. begin
  2494. FloatGetSrcPixel(Bits, Info, PixF);
  2495. Result.A := ClampToByte(Round(PixF.A * 255.0));
  2496. Result.R := ClampToByte(Round(PixF.R * 255.0));
  2497. Result.G := ClampToByte(Round(PixF.G * 255.0));
  2498. Result.B := ClampToByte(Round(PixF.B * 255.0));
  2499. end
  2500. else if Info.HasGrayChannel then
  2501. begin
  2502. GrayGetSrcPixel(Bits, Info, Pix64, Alpha);
  2503. Result.A := MulDiv(Alpha, 255, 65535);
  2504. Result.R := MulDiv(Pix64.A, 255, 65535);
  2505. Result.G := MulDiv(Pix64.A, 255, 65535);
  2506. Result.B := MulDiv(Pix64.A, 255, 65535);
  2507. end
  2508. else if Info.IsIndexed then
  2509. begin
  2510. IndexGetSrcPixel(Bits, Info, Index);
  2511. Result := Palette[Index];
  2512. end
  2513. else
  2514. begin
  2515. ChannelGetSrcPixel(Bits, Info, Pix64);
  2516. Result.A := MulDiv(Pix64.A, 255, 65535);
  2517. Result.R := MulDiv(Pix64.R, 255, 65535);
  2518. Result.G := MulDiv(Pix64.G, 255, 65535);
  2519. Result.B := MulDiv(Pix64.B, 255, 65535);
  2520. end;
  2521. end;
  2522. procedure SetPixel32Generic(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32; const Color: TColor32Rec);
  2523. var
  2524. Pix64: TColor64Rec;
  2525. PixF: TColorFPRec;
  2526. Alpha: Word;
  2527. Index: LongWord;
  2528. begin
  2529. if Info.Format = ifA8R8G8B8 then
  2530. begin
  2531. PColor32Rec(Bits)^ := Color
  2532. end
  2533. else if Info.Format = ifR8G8B8 then
  2534. begin
  2535. PColor24Rec(Bits)^ := Color.Color24Rec;
  2536. end
  2537. else if Info.IsFloatingPoint then
  2538. begin
  2539. PixF.A := Color.A * OneDiv8Bit;
  2540. PixF.R := Color.R * OneDiv8Bit;
  2541. PixF.G := Color.G * OneDiv8Bit;
  2542. PixF.B := Color.B * OneDiv8Bit;
  2543. FloatSetDstPixel(Bits, Info, PixF);
  2544. end
  2545. else if Info.HasGrayChannel then
  2546. begin
  2547. Alpha := MulDiv(Color.A, 65535, 255);
  2548. Pix64.Color := 0;
  2549. Pix64.A := MulDiv(Round(GrayConv.R * Color.R + GrayConv.G * Color.G +
  2550. GrayConv.B * Color.B), 65535, 255);
  2551. GraySetDstPixel(Bits, Info, Pix64, Alpha);
  2552. end
  2553. else if Info.IsIndexed then
  2554. begin
  2555. Index := FindColor(Palette, Info.PaletteEntries, Color.Color);
  2556. IndexSetDstPixel(Bits, Info, Index);
  2557. end
  2558. else
  2559. begin
  2560. Pix64.A := MulDiv(Color.A, 65535, 255);
  2561. Pix64.R := MulDiv(Color.R, 65535, 255);
  2562. Pix64.G := MulDiv(Color.G, 65535, 255);
  2563. Pix64.B := MulDiv(Color.B, 65535, 255);
  2564. ChannelSetDstPixel(Bits, Info, Pix64);
  2565. end;
  2566. end;
  2567. function GetPixelFPGeneric(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32): TColorFPRec;
  2568. var
  2569. Pix32: TColor32Rec;
  2570. Pix64: TColor64Rec;
  2571. Alpha: Word;
  2572. Index: LongWord;
  2573. begin
  2574. if Info.IsFloatingPoint then
  2575. begin
  2576. FloatGetSrcPixel(Bits, Info, Result);
  2577. end
  2578. else if Info.HasGrayChannel then
  2579. begin
  2580. GrayGetSrcPixel(Bits, Info, Pix64, Alpha);
  2581. Result.A := Alpha * OneDiv16Bit;
  2582. Result.R := Pix64.A * OneDiv16Bit;
  2583. Result.G := Pix64.A * OneDiv16Bit;
  2584. Result.B := Pix64.A * OneDiv16Bit;
  2585. end
  2586. else if Info.IsIndexed then
  2587. begin
  2588. IndexGetSrcPixel(Bits, Info, Index);
  2589. Pix32 := Palette[Index];
  2590. Result.A := Pix32.A * OneDiv8Bit;
  2591. Result.R := Pix32.R * OneDiv8Bit;
  2592. Result.G := Pix32.G * OneDiv8Bit;
  2593. Result.B := Pix32.B * OneDiv8Bit;
  2594. end
  2595. else
  2596. begin
  2597. ChannelGetSrcPixel(Bits, Info, Pix64);
  2598. Result.A := Pix64.A * OneDiv16Bit;
  2599. Result.R := Pix64.R * OneDiv16Bit;
  2600. Result.G := Pix64.G * OneDiv16Bit;
  2601. Result.B := Pix64.B * OneDiv16Bit;
  2602. end;
  2603. end;
  2604. procedure SetPixelFPGeneric(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32; const Color: TColorFPRec);
  2605. var
  2606. Pix32: TColor32Rec;
  2607. Pix64: TColor64Rec;
  2608. Alpha: Word;
  2609. Index: LongWord;
  2610. begin
  2611. if Info.IsFloatingPoint then
  2612. begin
  2613. FloatSetDstPixel(Bits, Info, Color);
  2614. end
  2615. else if Info.HasGrayChannel then
  2616. begin
  2617. Alpha := ClampToWord(Round(Color.A * 65535.0));
  2618. Pix64.Color := 0;
  2619. Pix64.A := ClampToWord(Round((GrayConv.R * Color.R + GrayConv.G * Color.G +
  2620. GrayConv.B * Color.B) * 65535.0));
  2621. GraySetDstPixel(Bits, Info, Pix64, Alpha);
  2622. end
  2623. else if Info.IsIndexed then
  2624. begin
  2625. Pix32.A := ClampToByte(Round(Color.A * 255.0));
  2626. Pix32.R := ClampToByte(Round(Color.R * 255.0));
  2627. Pix32.G := ClampToByte(Round(Color.G * 255.0));
  2628. Pix32.B := ClampToByte(Round(Color.B * 255.0));
  2629. Index := FindColor(Palette, Info.PaletteEntries, Pix32.Color);
  2630. IndexSetDstPixel(Bits, Info, Index);
  2631. end
  2632. else
  2633. begin
  2634. Pix64.A := ClampToWord(Round(Color.A * 65535.0));
  2635. Pix64.R := ClampToWord(Round(Color.R * 65535.0));
  2636. Pix64.G := ClampToWord(Round(Color.G * 65535.0));
  2637. Pix64.B := ClampToWord(Round(Color.B * 65535.0));
  2638. ChannelSetDstPixel(Bits, Info, Pix64);
  2639. end;
  2640. end;
  2641. { Image format conversion functions }
  2642. procedure ChannelToChannel(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
  2643. DstInfo: PImageFormatInfo);
  2644. var
  2645. I: LongInt;
  2646. Pix64: TColor64Rec;
  2647. begin
  2648. // two most common conversions (RGB->ARGB and ARGB->RGB for 24/32 bit
  2649. // images) are made separately from general ARGB conversion to
  2650. // make them faster
  2651. if (SrcInfo.BytesPerPixel = 3) and (DstInfo.BytesPerPixel = 4) then
  2652. for I := 0 to NumPixels - 1 do
  2653. begin
  2654. PColor24Rec(Dst)^ := PColor24Rec(Src)^;
  2655. if DstInfo.HasAlphaChannel then
  2656. PColor32Rec(Dst).A := 255;
  2657. Inc(Src, SrcInfo.BytesPerPixel);
  2658. Inc(Dst, DstInfo.BytesPerPixel);
  2659. end
  2660. else
  2661. if (SrcInfo.BytesPerPixel = 4) and (DstInfo.BytesPerPixel = 3) then
  2662. for I := 0 to NumPixels - 1 do
  2663. begin
  2664. PColor24Rec(Dst)^ := PColor24Rec(Src)^;
  2665. Inc(Src, SrcInfo.BytesPerPixel);
  2666. Inc(Dst, DstInfo.BytesPerPixel);
  2667. end
  2668. else
  2669. for I := 0 to NumPixels - 1 do
  2670. begin
  2671. // general ARGB conversion
  2672. ChannelGetSrcPixel(Src, SrcInfo, Pix64);
  2673. ChannelSetDstPixel(Dst, DstInfo, Pix64);
  2674. Inc(Src, SrcInfo.BytesPerPixel);
  2675. Inc(Dst, DstInfo.BytesPerPixel);
  2676. end;
  2677. end;
  2678. procedure ChannelToGray(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
  2679. DstInfo: PImageFormatInfo);
  2680. var
  2681. I: LongInt;
  2682. Pix64: TColor64Rec;
  2683. Alpha: Word;
  2684. begin
  2685. // two most common conversions (R8G8B8->Gray8 nad A8R8G8B8->Gray8)
  2686. // are made separately from general conversions to make them faster
  2687. if (SrcInfo.BytesPerPixel in [3, 4]) and (DstInfo.Format = ifGray8) then
  2688. for I := 0 to NumPixels - 1 do
  2689. begin
  2690. Dst^ := Round(GrayConv.R * PColor24Rec(Src).R + GrayConv.G * PColor24Rec(Src).G +
  2691. GrayConv.B * PColor24Rec(Src).B);
  2692. Inc(Src, SrcInfo.BytesPerPixel);
  2693. Inc(Dst, DstInfo.BytesPerPixel);
  2694. end
  2695. else
  2696. for I := 0 to NumPixels - 1 do
  2697. begin
  2698. ChannelGetSrcPixel(Src, SrcInfo, Pix64);
  2699. // alpha is saved from source pixel to Alpha,
  2700. // Gray value is computed and set to highest word of Pix64 so
  2701. // Pix64.Color contains grayscale value scaled to 64 bits
  2702. Alpha := Pix64.A;
  2703. with GrayConv do
  2704. Pix64.A := Round(R * Pix64.R + G * Pix64.G + B * Pix64.B);
  2705. GraySetDstPixel(Dst, DstInfo, Pix64, Alpha);
  2706. Inc(Src, SrcInfo.BytesPerPixel);
  2707. Inc(Dst, DstInfo.BytesPerPixel);
  2708. end;
  2709. end;
  2710. procedure ChannelToFloat(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
  2711. DstInfo: PImageFormatInfo);
  2712. var
  2713. I: LongInt;
  2714. Pix64: TColor64Rec;
  2715. PixF: TColorFPRec;
  2716. begin
  2717. for I := 0 to NumPixels - 1 do
  2718. begin
  2719. ChannelGetSrcPixel(Src, SrcInfo, Pix64);
  2720. // floating point channel values are scaled to 1.0
  2721. PixF.A := Pix64.A * OneDiv16Bit;
  2722. PixF.R := Pix64.R * OneDiv16Bit;
  2723. PixF.G := Pix64.G * OneDiv16Bit;
  2724. PixF.B := Pix64.B * OneDiv16Bit;
  2725. FloatSetDstPixel(Dst, DstInfo, PixF);
  2726. Inc(Src, SrcInfo.BytesPerPixel);
  2727. Inc(Dst, DstInfo.BytesPerPixel);
  2728. end;
  2729. end;
  2730. procedure ChannelToIndex(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
  2731. DstInfo: PImageFormatInfo; DstPal: PPalette32);
  2732. begin
  2733. ReduceColorsMedianCut(NumPixels, Src, Dst, SrcInfo, DstInfo, DstInfo.PaletteEntries,
  2734. GetOption(ImagingColorReductionMask), DstPal);
  2735. end;
  2736. procedure GrayToGray(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
  2737. DstInfo: PImageFormatInfo);
  2738. var
  2739. I: LongInt;
  2740. Gray: TColor64Rec;
  2741. Alpha: Word;
  2742. begin
  2743. // two most common conversions (Gray8->Gray16 nad Gray16->Gray8)
  2744. // are made separately from general conversions to make them faster
  2745. if (SrcInfo.Format = ifGray8) and (DstInfo.Format = ifGray16) then
  2746. begin
  2747. for I := 0 to NumPixels - 1 do
  2748. PWordArray(Dst)[I] := PByteArray(Src)[I] shl 8;
  2749. end
  2750. else
  2751. begin
  2752. if (DstInfo.Format = ifGray8) and (SrcInfo.Format = ifGray16) then
  2753. begin
  2754. for I := 0 to NumPixels - 1 do
  2755. PByteArray(Dst)[I] := PWordArray(Src)[I] shr 8;
  2756. end
  2757. else
  2758. for I := 0 to NumPixels - 1 do
  2759. begin
  2760. // general grayscale conversion
  2761. GrayGetSrcPixel(Src, SrcInfo, Gray, Alpha);
  2762. GraySetDstPixel(Dst, DstInfo, Gray, Alpha);
  2763. Inc(Src, SrcInfo.BytesPerPixel);
  2764. Inc(Dst, DstInfo.BytesPerPixel);
  2765. end;
  2766. end;
  2767. end;
  2768. procedure GrayToChannel(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
  2769. DstInfo: PImageFormatInfo);
  2770. var
  2771. I: LongInt;
  2772. Pix64: TColor64Rec;
  2773. Alpha: Word;
  2774. begin
  2775. // two most common conversions (Gray8->R8G8B8 nad Gray8->A8R8G8B8)
  2776. // are made separately from general conversions to make them faster
  2777. if (DstInfo.BytesPerPixel in [3, 4]) and (SrcInfo.Format = ifGray8) then
  2778. for I := 0 to NumPixels - 1 do
  2779. begin
  2780. PColor24Rec(Dst).R := Src^;
  2781. PColor24Rec(Dst).G := Src^;
  2782. PColor24Rec(Dst).B := Src^;
  2783. if DstInfo.HasAlphaChannel then
  2784. PColor32Rec(Dst).A := $FF;
  2785. Inc(Src, SrcInfo.BytesPerPixel);
  2786. Inc(Dst, DstInfo.BytesPerPixel);
  2787. end
  2788. else
  2789. for I := 0 to NumPixels - 1 do
  2790. begin
  2791. GrayGetSrcPixel(Src, SrcInfo, Pix64, Alpha);
  2792. // most significant word of grayscale value is used for
  2793. // each channel and alpha channel is set to Alpha
  2794. Pix64.R := Pix64.A;
  2795. Pix64.G := Pix64.A;
  2796. Pix64.B := Pix64.A;
  2797. Pix64.A := Alpha;
  2798. ChannelSetDstPixel(Dst, DstInfo, Pix64);
  2799. Inc(Src, SrcInfo.BytesPerPixel);
  2800. Inc(Dst, DstInfo.BytesPerPixel);
  2801. end;
  2802. end;
  2803. procedure GrayToFloat(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
  2804. DstInfo: PImageFormatInfo);
  2805. var
  2806. I: LongInt;
  2807. Gray: TColor64Rec;
  2808. PixF: TColorFPRec;
  2809. Alpha: Word;
  2810. begin
  2811. for I := 0 to NumPixels - 1 do
  2812. begin
  2813. GrayGetSrcPixel(Src, SrcInfo, Gray, Alpha);
  2814. // most significant word of grayscale value is used for
  2815. // each channel and alpha channel is set to Alpha
  2816. // then all is scaled to 0..1
  2817. PixF.R := Gray.A * OneDiv16Bit;
  2818. PixF.G := Gray.A * OneDiv16Bit;
  2819. PixF.B := Gray.A * OneDiv16Bit;
  2820. PixF.A := Alpha * OneDiv16Bit;
  2821. FloatSetDstPixel(Dst, DstInfo, PixF);
  2822. Inc(Src, SrcInfo.BytesPerPixel);
  2823. Inc(Dst, DstInfo.BytesPerPixel);
  2824. end;
  2825. end;
  2826. procedure GrayToIndex(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
  2827. DstInfo: PImageFormatInfo; DstPal: PPalette32);
  2828. var
  2829. I: LongInt;
  2830. Idx: LongWord;
  2831. Gray: TColor64Rec;
  2832. Alpha, Shift: Word;
  2833. begin
  2834. FillGrayscalePalette(DstPal, DstInfo.PaletteEntries);
  2835. Shift := Log2Int(DstInfo.PaletteEntries);
  2836. // most common conversion (Gray8->Index8)
  2837. // is made separately from general conversions to make it faster
  2838. if (SrcInfo.Format = ifGray8) and (DstInfo.Format = ifIndex8) then
  2839. for I := 0 to NumPixels - 1 do
  2840. begin
  2841. Dst^ := Src^;
  2842. Inc(Src, SrcInfo.BytesPerPixel);
  2843. Inc(Dst, DstInfo.BytesPerPixel);
  2844. end
  2845. else
  2846. for I := 0 to NumPixels - 1 do
  2847. begin
  2848. // gray value is read from src and index to precomputed
  2849. // grayscale palette is computed and written to dst
  2850. // (we assume here that there will be no more than 65536 palette
  2851. // entries in dst format, gray value is shifted so the highest
  2852. // gray value match the highest possible index in palette)
  2853. GrayGetSrcPixel(Src, SrcInfo, Gray, Alpha);
  2854. Idx := Gray.A shr (16 - Shift);
  2855. IndexSetDstPixel(Dst, DstInfo, Idx);
  2856. Inc(Src, SrcInfo.BytesPerPixel);
  2857. Inc(Dst, DstInfo.BytesPerPixel);
  2858. end;
  2859. end;
  2860. procedure FloatToFloat(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
  2861. DstInfo: PImageFormatInfo);
  2862. var
  2863. I: LongInt;
  2864. PixF: TColorFPRec;
  2865. begin
  2866. for I := 0 to NumPixels - 1 do
  2867. begin
  2868. // general floating point conversion
  2869. FloatGetSrcPixel(Src, SrcInfo, PixF);
  2870. FloatSetDstPixel(Dst, DstInfo, PixF);
  2871. Inc(Src, SrcInfo.BytesPerPixel);
  2872. Inc(Dst, DstInfo.BytesPerPixel);
  2873. end;
  2874. end;
  2875. procedure FloatToChannel(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
  2876. DstInfo: PImageFormatInfo);
  2877. var
  2878. I: LongInt;
  2879. Pix64: TColor64Rec;
  2880. PixF: TColorFPRec;
  2881. begin
  2882. for I := 0 to NumPixels - 1 do
  2883. begin
  2884. FloatGetSrcPixel(Src, SrcInfo, PixF);
  2885. ClampFloatPixel(PixF);
  2886. // floating point channel values are scaled to 1.0
  2887. Pix64.A := ClampToWord(Round(PixF.A * 65535));
  2888. Pix64.R := ClampToWord(Round(PixF.R * 65535));
  2889. Pix64.G := ClampToWord(Round(PixF.G * 65535));
  2890. Pix64.B := ClampToWord(Round(PixF.B * 65535));
  2891. ChannelSetDstPixel(Dst, DstInfo, Pix64);
  2892. Inc(Src, SrcInfo.BytesPerPixel);
  2893. Inc(Dst, DstInfo.BytesPerPixel);
  2894. end;
  2895. end;
  2896. procedure FloatToGray(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
  2897. DstInfo: PImageFormatInfo);
  2898. var
  2899. I: LongInt;
  2900. PixF: TColorFPRec;
  2901. Gray: TColor64Rec;
  2902. Alpha: Word;
  2903. begin
  2904. for I := 0 to NumPixels - 1 do
  2905. begin
  2906. FloatGetSrcPixel(Src, SrcInfo, PixF);
  2907. ClampFloatPixel(PixF);
  2908. // alpha is saved from source pixel to Alpha,
  2909. // Gray value is computed and set to highest word of Pix64 so
  2910. // Pix64.Color contains grayscale value scaled to 64 bits
  2911. Alpha := ClampToWord(Round(PixF.A * 65535.0));
  2912. Gray.A := ClampToWord(Round((GrayConv.R * PixF.R + GrayConv.G * PixF.G +
  2913. GrayConv.B * PixF.B) * 65535.0));
  2914. GraySetDstPixel(Dst, DstInfo, Gray, Alpha);
  2915. Inc(Src, SrcInfo.BytesPerPixel);
  2916. Inc(Dst, DstInfo.BytesPerPixel);
  2917. end;
  2918. end;
  2919. procedure FloatToIndex(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
  2920. DstInfo: PImageFormatInfo; DstPal: PPalette32);
  2921. begin
  2922. ReduceColorsMedianCut(NumPixels, Src, Dst, SrcInfo, DstInfo, DstInfo.PaletteEntries,
  2923. GetOption(ImagingColorReductionMask), DstPal);
  2924. end;
  2925. procedure IndexToIndex(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
  2926. DstInfo: PImageFormatInfo; SrcPal, DstPal: PPalette32);
  2927. var
  2928. I: LongInt;
  2929. begin
  2930. // there is only one indexed format now, so it is just a copy
  2931. for I := 0 to NumPixels - 1 do
  2932. begin
  2933. Dst^ := Src^;
  2934. Inc(Src, SrcInfo.BytesPerPixel);
  2935. Inc(Dst, DstInfo.BytesPerPixel);
  2936. end;
  2937. for I := 0 to SrcInfo.PaletteEntries - 1 do
  2938. DstPal[I] := SrcPal[I];
  2939. end;
  2940. procedure IndexToChannel(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
  2941. DstInfo: PImageFormatInfo; SrcPal: PPalette32);
  2942. var
  2943. I: LongInt;
  2944. Pix64: TColor64Rec;
  2945. Idx: LongWord;
  2946. begin
  2947. // two most common conversions (Index8->R8G8B8 nad Index8->A8R8G8B8)
  2948. // are made separately from general conversions to make them faster
  2949. if (SrcInfo.Format = ifIndex8) and (DstInfo.Format in [ifR8G8B8, ifA8R8G8B8]) then
  2950. for I := 0 to NumPixels - 1 do
  2951. begin
  2952. with PColor24Rec(Dst)^ do
  2953. begin
  2954. R := SrcPal[Src^].R;
  2955. G := SrcPal[Src^].G;
  2956. B := SrcPal[Src^].B;
  2957. end;
  2958. if DstInfo.Format = ifA8R8G8B8 then
  2959. PColor32Rec(Dst).A := SrcPal[Src^].A;
  2960. Inc(Src, SrcInfo.BytesPerPixel);
  2961. Inc(Dst, DstInfo.BytesPerPixel);
  2962. end
  2963. else
  2964. for I := 0 to NumPixels - 1 do
  2965. begin
  2966. // index to palette is read from source and color
  2967. // is retrieved from palette entry. Color is then
  2968. // scaled to 16bits and written to dest
  2969. IndexGetSrcPixel(Src, SrcInfo, Idx);
  2970. with Pix64 do
  2971. begin
  2972. A := SrcPal[Idx].A shl 8;
  2973. R := SrcPal[Idx].R shl 8;
  2974. G := SrcPal[Idx].G shl 8;
  2975. B := SrcPal[Idx].B shl 8;
  2976. end;
  2977. ChannelSetDstPixel(Dst, DstInfo, Pix64);
  2978. Inc(Src, SrcInfo.BytesPerPixel);
  2979. Inc(Dst, DstInfo.BytesPerPixel);
  2980. end;
  2981. end;
  2982. procedure IndexToGray(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
  2983. DstInfo: PImageFormatInfo; SrcPal: PPalette32);
  2984. var
  2985. I: LongInt;
  2986. Gray: TColor64Rec;
  2987. Alpha: Word;
  2988. Idx: LongWord;
  2989. begin
  2990. // most common conversion (Index8->Gray8)
  2991. // is made separately from general conversions to make it faster
  2992. if (SrcInfo.Format = ifIndex8) and (DstInfo.Format = ifGray8) then
  2993. begin
  2994. for I := 0 to NumPixels - 1 do
  2995. begin
  2996. Dst^ := Round(GrayConv.R * SrcPal[Src^].R + GrayConv.G * SrcPal[Src^].G +
  2997. GrayConv.B * SrcPal[Src^].B);
  2998. Inc(Src, SrcInfo.BytesPerPixel);
  2999. Inc(Dst, DstInfo.BytesPerPixel);
  3000. end
  3001. end
  3002. else
  3003. for I := 0 to NumPixels - 1 do
  3004. begin
  3005. // index to palette is read from source and color
  3006. // is retrieved from palette entry. Color is then
  3007. // transformed to grayscale and assigned to the highest
  3008. // byte of Gray value
  3009. IndexGetSrcPixel(Src, SrcInfo, Idx);
  3010. Alpha := SrcPal[Idx].A shl 8;
  3011. Gray.A := MulDiv(Round(GrayConv.R * SrcPal[Idx].R + GrayConv.G * SrcPal[Idx].G +
  3012. GrayConv.B * SrcPal[Idx].B), 65535, 255);
  3013. GraySetDstPixel(Dst, DstInfo, Gray, Alpha);
  3014. Inc(Src, SrcInfo.BytesPerPixel);
  3015. Inc(Dst, DstInfo.BytesPerPixel);
  3016. end;
  3017. end;
  3018. procedure IndexToFloat(NumPixels: LongInt; Src, Dst: PByte; SrcInfo,
  3019. DstInfo: PImageFormatInfo; SrcPal: PPalette32);
  3020. var
  3021. I: LongInt;
  3022. Idx: LongWord;
  3023. PixF: TColorFPRec;
  3024. begin
  3025. for I := 0 to NumPixels - 1 do
  3026. begin
  3027. // index to palette is read from source and color
  3028. // is retrieved from palette entry. Color is then
  3029. // scaled to 0..1 and written to dest
  3030. IndexGetSrcPixel(Src, SrcInfo, Idx);
  3031. with PixF do
  3032. begin
  3033. A := SrcPal[Idx].A * OneDiv8Bit;
  3034. R := SrcPal[Idx].R * OneDiv8Bit;
  3035. G := SrcPal[Idx].G * OneDiv8Bit;
  3036. B := SrcPal[Idx].B * OneDiv8Bit;
  3037. end;
  3038. FloatSetDstPixel(Dst, DstInfo, PixF);
  3039. Inc(Src, SrcInfo.BytesPerPixel);
  3040. Inc(Dst, DstInfo.BytesPerPixel);
  3041. end;
  3042. end;
  3043. { Special formats conversion functions }
  3044. type
  3045. // DXT RGB color block
  3046. TDXTColorBlock = packed record
  3047. Color0, Color1: Word;
  3048. Mask: LongWord;
  3049. end;
  3050. PDXTColorBlock = ^TDXTColorBlock;
  3051. // DXT explicit alpha for a block
  3052. TDXTAlphaBlockExp = packed record
  3053. Alphas: array[0..3] of Word;
  3054. end;
  3055. PDXTAlphaBlockExp = ^TDXTAlphaBlockExp;
  3056. // DXT interpolated alpha for a block
  3057. TDXTAlphaBlockInt = packed record
  3058. Alphas: array[0..7] of Byte;
  3059. end;
  3060. PDXTAlphaBlockInt = ^TDXTAlphaBlockInt;
  3061. TPixelInfo = record
  3062. Color: Word;
  3063. Alpha: Byte;
  3064. Orig: TColor32Rec;
  3065. end;
  3066. TPixelBlock = array[0..15] of TPixelInfo;
  3067. function DecodeCol(Color: Word): TColor32Rec;
  3068. {$IFDEF USE_INLINE} inline; {$ENDIF}
  3069. begin
  3070. Result.A := $FF;
  3071. { Result.R := ((Color and $F800) shr 11) shl 3;
  3072. Result.G := ((Color and $07E0) shr 5) shl 2;
  3073. Result.B := (Color and $001F) shl 3;}
  3074. // this color expansion is slower but gives better results
  3075. Result.R := (Color shr 11) * 255 div 31;
  3076. Result.G := ((Color shr 5) and $3F) * 255 div 63;
  3077. Result.B := (Color and $1F) * 255 div 31;
  3078. end;
  3079. procedure DecodeDXT1(SrcBits, DestBits: PByte; Width, Height: LongInt);
  3080. var
  3081. Sel, X, Y, I, J, K: LongInt;
  3082. Block: TDXTColorBlock;
  3083. Colors: array[0..3] of TColor32Rec;
  3084. begin
  3085. for Y := 0 to Height div 4 - 1 do
  3086. for X := 0 to Width div 4 - 1 do
  3087. begin
  3088. Block := PDXTColorBlock(SrcBits)^;
  3089. Inc(SrcBits, SizeOf(Block));
  3090. // we read and decode endpoint colors
  3091. Colors[0] := DecodeCol(Block.Color0);
  3092. Colors[1] := DecodeCol(Block.Color1);
  3093. // and interpolate between them
  3094. if Block.Color0 > Block.Color1 then
  3095. begin
  3096. // interpolation for block without alpha
  3097. Colors[2].A := $FF;
  3098. Colors[2].R := (Colors[0].R shl 1 + Colors[1].R + 1) div 3;
  3099. Colors[2].G := (Colors[0].G shl 1 + Colors[1].G + 1) div 3;
  3100. Colors[2].B := (Colors[0].B shl 1 + Colors[1].B + 1) div 3;
  3101. Colors[3].A := $FF;
  3102. Colors[3].R := (Colors[0].R + Colors[1].R shl 1 + 1) div 3;
  3103. Colors[3].G := (Colors[0].G + Colors[1].G shl 1 + 1) div 3;
  3104. Colors[3].B := (Colors[0].B + Colors[1].B shl 1 + 1) div 3;
  3105. end
  3106. else
  3107. begin
  3108. // interpolation for block with alpha
  3109. Colors[2].A := $FF;
  3110. Colors[2].R := (Colors[0].R + Colors[1].R) shr 1;
  3111. Colors[2].G := (Colors[0].G + Colors[1].G) shr 1;
  3112. Colors[2].B := (Colors[0].B + Colors[1].B) shr 1;
  3113. Colors[3].A := 0;
  3114. Colors[3].R := (Colors[0].R + Colors[1].R shl 1 + 1) div 3;
  3115. Colors[3].G := (Colors[0].G + Colors[1].G shl 1 + 1) div 3;
  3116. Colors[3].B := (Colors[0].B + Colors[1].B shl 1 + 1) div 3;
  3117. end;
  3118. // we distribute the dxt block colors across the 4x4 block of the
  3119. // destination image accroding to the dxt block mask
  3120. K := 0;
  3121. for J := 0 to 3 do
  3122. for I := 0 to 3 do
  3123. begin
  3124. Sel := (Block.Mask and (3 shl (K shl 1))) shr (K shl 1);
  3125. if ((X shl 2 + I) < Width) and ((Y shl 2 + J) < Height) then
  3126. PPalette32(DestBits)[(Y shl 2 + J) * Width + X shl 2 + I] :=
  3127. Colors[Sel];
  3128. Inc(K);
  3129. end;
  3130. end;
  3131. end;
  3132. procedure DecodeDXT3(SrcBits, DestBits: PByte; Width, Height: LongInt);
  3133. var
  3134. Sel, X, Y, I, J, K: LongInt;
  3135. Block: TDXTColorBlock;
  3136. AlphaBlock: TDXTAlphaBlockExp;
  3137. Colors: array[0..3] of TColor32Rec;
  3138. AWord: Word;
  3139. begin
  3140. for Y := 0 to Height div 4 - 1 do
  3141. for X := 0 to Width div 4 - 1 do
  3142. begin
  3143. AlphaBlock := PDXTAlphaBlockExp(SrcBits)^;
  3144. Inc(SrcBits, SizeOf(AlphaBlock));
  3145. Block := PDXTColorBlock(SrcBits)^;
  3146. Inc(SrcBits, SizeOf(Block));
  3147. // we read and decode endpoint colors
  3148. Colors[0] := DecodeCol(Block.Color0);
  3149. Colors[1] := DecodeCol(Block.Color1);
  3150. // and interpolate between them
  3151. Colors[2].R := (Colors[0].R shl 1 + Colors[1].R + 1) div 3;
  3152. Colors[2].G := (Colors[0].G shl 1 + Colors[1].G + 1) div 3;
  3153. Colors[2].B := (Colors[0].B shl 1 + Colors[1].B + 1) div 3;
  3154. Colors[3].R := (Colors[0].R + Colors[1].R shl 1 + 1) div 3;
  3155. Colors[3].G := (Colors[0].G + Colors[1].G shl 1 + 1) div 3;
  3156. Colors[3].B := (Colors[0].B + Colors[1].B shl 1 + 1) div 3;
  3157. // we distribute the dxt block colors and alphas
  3158. // across the 4x4 block of the destination image
  3159. // accroding to the dxt block mask and alpha block
  3160. K := 0;
  3161. for J := 0 to 3 do
  3162. begin
  3163. AWord := AlphaBlock.Alphas[J];
  3164. for I := 0 to 3 do
  3165. begin
  3166. Sel := (Block.Mask and (3 shl (K shl 1))) shr (K shl 1);
  3167. if (X shl 2 + I < Width) and (Y shl 2 + J < Height) then
  3168. begin
  3169. Colors[Sel].A := AWord and $0F;
  3170. Colors[Sel].A := Colors[Sel].A or (Colors[Sel].A shl 4);
  3171. PPalette32(DestBits)[(Y shl 2 + J) * Width + X shl 2 + I] :=
  3172. Colors[Sel];
  3173. end;
  3174. Inc(K);
  3175. AWord := AWord shr 4;
  3176. end;
  3177. end;
  3178. end;
  3179. end;
  3180. procedure GetInterpolatedAlphas(var AlphaBlock: TDXTAlphaBlockInt);
  3181. begin
  3182. with AlphaBlock do
  3183. if Alphas[0] > Alphas[1] then
  3184. begin
  3185. // Interpolation of six alphas
  3186. Alphas[2] := (6 * Alphas[0] + 1 * Alphas[1] + 3) div 7;
  3187. Alphas[3] := (5 * Alphas[0] + 2 * Alphas[1] + 3) div 7;
  3188. Alphas[4] := (4 * Alphas[0] + 3 * Alphas[1] + 3) div 7;
  3189. Alphas[5] := (3 * Alphas[0] + 4 * Alphas[1] + 3) div 7;
  3190. Alphas[6] := (2 * Alphas[0] + 5 * Alphas[1] + 3) div 7;
  3191. Alphas[7] := (1 * Alphas[0] + 6 * Alphas[1] + 3) div 7;
  3192. end
  3193. else
  3194. begin
  3195. // Interpolation of four alphas, two alphas are set directly
  3196. Alphas[2] := (4 * Alphas[0] + 1 * Alphas[1] + 2) div 5;
  3197. Alphas[3] := (3 * Alphas[0] + 2 * Alphas[1] + 2) div 5;
  3198. Alphas[4] := (2 * Alphas[0] + 3 * Alphas[1] + 2) div 5;
  3199. Alphas[5] := (1 * Alphas[0] + 4 * Alphas[1] + 2) div 5;
  3200. Alphas[6] := 0;
  3201. Alphas[7] := $FF;
  3202. end;
  3203. end;
  3204. procedure DecodeDXT5(SrcBits, DestBits: PByte; Width, Height: LongInt);
  3205. var
  3206. Sel, X, Y, I, J, K: LongInt;
  3207. Block: TDXTColorBlock;
  3208. AlphaBlock: TDXTAlphaBlockInt;
  3209. Colors: array[0..3] of TColor32Rec;
  3210. AMask: array[0..1] of LongWord;
  3211. begin
  3212. for Y := 0 to Height div 4 - 1 do
  3213. for X := 0 to Width div 4 - 1 do
  3214. begin
  3215. AlphaBlock := PDXTAlphaBlockInt(SrcBits)^;
  3216. Inc(SrcBits, SizeOf(AlphaBlock));
  3217. Block := PDXTColorBlock(SrcBits)^;
  3218. Inc(SrcBits, SizeOf(Block));
  3219. // we read and decode endpoint colors
  3220. Colors[0] := DecodeCol(Block.Color0);
  3221. Colors[1] := DecodeCol(Block.Color1);
  3222. // and interpolate between them
  3223. Colors[2].R := (Colors[0].R shl 1 + Colors[1].R + 1) div 3;
  3224. Colors[2].G := (Colors[0].G shl 1 + Colors[1].G + 1) div 3;
  3225. Colors[2].B := (Colors[0].B shl 1 + Colors[1].B + 1) div 3;
  3226. Colors[3].R := (Colors[0].R + Colors[1].R shl 1 + 1) div 3;
  3227. Colors[3].G := (Colors[0].G + Colors[1].G shl 1 + 1) div 3;
  3228. Colors[3].B := (Colors[0].B + Colors[1].B shl 1 + 1) div 3;
  3229. // 6 bit alpha mask is copied into two long words for
  3230. // easier usage
  3231. AMask[0] := PLongWord(@AlphaBlock.Alphas[2])^ and $00FFFFFF;
  3232. AMask[1] := PLongWord(@AlphaBlock.Alphas[5])^ and $00FFFFFF;
  3233. // alpha interpolation between two endpoint alphas
  3234. GetInterpolatedAlphas(AlphaBlock);
  3235. // we distribute the dxt block colors and alphas
  3236. // across the 4x4 block of the destination image
  3237. // accroding to the dxt block mask and alpha block mask
  3238. K := 0;
  3239. for J := 0 to 3 do
  3240. for I := 0 to 3 do
  3241. begin
  3242. Sel := (Block.Mask and (3 shl (K shl 1))) shr (K shl 1);
  3243. if ((X shl 2 + I) < Width) and ((Y shl 2 + J) < Height) then
  3244. begin
  3245. Colors[Sel].A := AlphaBlock.Alphas[AMask[J shr 1] and 7];
  3246. PPalette32(DestBits)[(Y shl 2 + J) * Width + (X shl 2 + I)] :=
  3247. Colors[Sel];
  3248. end;
  3249. Inc(K);
  3250. AMask[J shr 1] := AMask[J shr 1] shr 3;
  3251. end;
  3252. end;
  3253. end;
  3254. procedure GetBlock(var Block: TPixelBlock; SrcBits: Pointer; XPos, YPos,
  3255. Width, Height: LongInt);
  3256. var
  3257. X, Y, I: LongInt;
  3258. Src: PColor32Rec;
  3259. begin
  3260. I := 0;
  3261. // 4x4 pixel block is filled with information about every
  3262. // pixel in the block: alpha, original color, 565 color
  3263. for Y := 0 to 3 do
  3264. for X := 0 to 3 do
  3265. begin
  3266. Src := @PPalette32(SrcBits)[(YPos shl 2 + Y) * Width + XPos shl 2 + X];
  3267. Block[I].Color := ((Src.R shr 3) shl 11) or ((Src.G shr 2) shl 5) or
  3268. (Src.B shr 3);
  3269. Block[I].Alpha := Src.A;
  3270. Block[I].Orig := Src^;
  3271. Inc(I);
  3272. end;
  3273. end;
  3274. function ColorDistance(const C1, C2: TColor32Rec): LongInt;
  3275. {$IFDEF USE_INLINE} inline;{$ENDIF}
  3276. begin
  3277. Result := (C1.R - C2.R) * (C1.R - C2.R) +
  3278. (C1.G - C2.G) * (C1.G - C2.G) + (C1.B - C2.B) * (C1.B - C2.B);
  3279. end;
  3280. procedure GetEndpoints(const Block: TPixelBlock; var Ep0, Ep1: Word);
  3281. var
  3282. I, J, Farthest, Dist: LongInt;
  3283. Colors: array[0..15] of TColor32Rec;
  3284. begin
  3285. // we choose two colors from the pixel block which has the
  3286. // largest distance between them
  3287. for I := 0 to 15 do
  3288. Colors[I] := Block[I].Orig;
  3289. Farthest := -1;
  3290. for I := 0 to 15 do
  3291. for J := I + 1 to 15 do
  3292. begin
  3293. Dist := ColorDistance(Colors[I], Colors[J]);
  3294. if Dist > Farthest then
  3295. begin
  3296. Farthest := Dist;
  3297. Ep0 := Block[I].Color;
  3298. Ep1 := Block[J].Color;
  3299. end;
  3300. end;
  3301. end;
  3302. procedure GetAlphaEndpoints(const Block: TPixelBlock; var Min, Max: Byte);
  3303. var
  3304. I: LongInt;
  3305. begin
  3306. Min := 255;
  3307. Max := 0;
  3308. // we choose the lowest and the highest alpha values
  3309. for I := 0 to 15 do
  3310. begin
  3311. if Block[I].Alpha < Min then
  3312. Min := Block[I].Alpha;
  3313. if Block[I].Alpha > Max then
  3314. Max := Block[I].Alpha;
  3315. end;
  3316. end;
  3317. procedure FixEndpoints(var Ep0, Ep1: Word; HasAlpha: Boolean);
  3318. var
  3319. Temp: Word;
  3320. begin
  3321. // if dxt block has alpha information, Ep0 must be smaller
  3322. // than Ep1, if the block has no alpha Ep1 must be smaller
  3323. if HasAlpha then
  3324. begin
  3325. if Ep0 > Ep1 then
  3326. begin
  3327. Temp := Ep0;
  3328. Ep0 := Ep1;
  3329. Ep1 := Temp;
  3330. end;
  3331. end
  3332. else
  3333. if Ep0 < Ep1 then
  3334. begin
  3335. Temp := Ep0;
  3336. Ep0 := Ep1;
  3337. Ep1 := Temp;
  3338. end;
  3339. end;
  3340. function GetColorMask(Ep0, Ep1: Word; NumCols: LongInt;
  3341. const Block: TPixelBlock): LongWord;
  3342. var
  3343. I, J, Closest, Dist: LongInt;
  3344. Colors: array[0..3] of TColor32Rec;
  3345. Mask: array[0..15] of Byte;
  3346. begin
  3347. // we decode endpoint colors
  3348. Colors[0] := DecodeCol(Ep0);
  3349. Colors[1] := DecodeCol(Ep1);
  3350. // and interpolate colors between (3 for DXT1 with alpha, 4 for the others)
  3351. if NumCols = 3 then
  3352. begin
  3353. Colors[2].R := (Colors[0].R + Colors[1].R) shr 1;
  3354. Colors[2].G := (Colors[0].G + Colors[1].G) shr 1;
  3355. Colors[2].B := (Colors[0].B + Colors[1].B) shr 1;
  3356. Colors[3].R := (Colors[0].R + Colors[1].R) shr 1;
  3357. Colors[3].G := (Colors[0].G + Colors[1].G) shr 1;
  3358. Colors[3].B := (Colors[0].B + Colors[1].B) shr 1;
  3359. end
  3360. else
  3361. begin
  3362. Colors[2].R := (Colors[0].R shl 1 + Colors[1].R + 1) div 3;
  3363. Colors[2].G := (Colors[0].G shl 1 + Colors[1].G + 1) div 3;
  3364. Colors[2].B := (Colors[0].B shl 1 + Colors[1].B + 1) div 3;
  3365. Colors[3].R := (Colors[0].R + Colors[1].R shl 1 + 1) div 3;
  3366. Colors[3].G := (Colors[0].G + Colors[1].G shl 1 + 1) div 3;
  3367. Colors[3].B := (Colors[0].B + Colors[1].B shl 1 + 1) div 3;
  3368. end;
  3369. for I := 0 to 15 do
  3370. begin
  3371. // this is only for DXT1 with alpha
  3372. if (Block[I].Alpha < 128) and (NumCols = 3) then
  3373. begin
  3374. Mask[I] := 3;
  3375. Continue;
  3376. end;
  3377. // for each of the 16 input pixels the nearest color in the
  3378. // 4 dxt colors is found
  3379. Closest := MaxInt;
  3380. for J := 0 to NumCols - 1 do
  3381. begin
  3382. Dist := ColorDistance(Block[I].Orig, Colors[J]);
  3383. if Dist < Closest then
  3384. begin
  3385. Closest := Dist;
  3386. Mask[I] := J;
  3387. end;
  3388. end;
  3389. end;
  3390. Result := 0;
  3391. for I := 0 to 15 do
  3392. Result := Result or (Mask[I] shl (I shl 1));
  3393. end;
  3394. procedure GetAlphaMask(Ep0, Ep1: Byte; var Block: TPixelBlock; Mask: PByteArray);
  3395. var
  3396. Alphas: array[0..7] of Byte;
  3397. M: array[0..15] of Byte;
  3398. I, J, Closest, Dist: LongInt;
  3399. begin
  3400. Alphas[0] := Ep0;
  3401. Alphas[1] := Ep1;
  3402. // interpolation between two given alpha endpoints
  3403. // (I use 6 interpolated values mode)
  3404. Alphas[2] := (6 * Alphas[0] + 1 * Alphas[1] + 3) div 7;
  3405. Alphas[3] := (5 * Alphas[0] + 2 * Alphas[1] + 3) div 7;
  3406. Alphas[4] := (4 * Alphas[0] + 3 * Alphas[1] + 3) div 7;
  3407. Alphas[5] := (3 * Alphas[0] + 4 * Alphas[1] + 3) div 7;
  3408. Alphas[6] := (2 * Alphas[0] + 5 * Alphas[1] + 3) div 7;
  3409. Alphas[7] := (1 * Alphas[0] + 6 * Alphas[1] + 3) div 7;
  3410. // the closest interpolated values for each of the input alpha
  3411. // is found
  3412. for I := 0 to 15 do
  3413. begin
  3414. Closest := MaxInt;
  3415. for J := 0 to 7 do
  3416. begin
  3417. Dist := Abs(Alphas[J] - Block[I].Alpha);
  3418. if Dist < Closest then
  3419. begin
  3420. Closest := Dist;
  3421. M[I] := J;
  3422. end;
  3423. end;
  3424. end;
  3425. Mask[0] := M[0] or (M[1] shl 3) or ((M[2] and 3) shl 6);
  3426. Mask[1] := ((M[2] and 4) shr 2) or (M[3] shl 1) or (M[4] shl 4) or
  3427. ((M[5] and 1) shl 7);
  3428. Mask[2] := ((M[5] and 6) shr 1) or (M[6] shl 2) or (M[7] shl 5);
  3429. Mask[3] := M[8] or (M[9] shl 3) or ((M[10] and 3) shl 6);
  3430. Mask[4] := ((M[10] and 4) shr 2) or (M[11] shl 1) or (M[12] shl 4) or
  3431. ((M[13] and 1) shl 7);
  3432. Mask[5] := ((M[13] and 6) shr 1) or (M[14] shl 2) or (M[15] shl 5);
  3433. end;
  3434. procedure EncodeDXT1(SrcBits: PByte; DestBits: PByte; Width, Height: LongInt);
  3435. var
  3436. X, Y, I: LongInt;
  3437. HasAlpha: Boolean;
  3438. Block: TDXTColorBlock;
  3439. Pixels: TPixelBlock;
  3440. begin
  3441. for Y := 0 to Height div 4 - 1 do
  3442. for X := 0 to Width div 4 - 1 do
  3443. begin
  3444. GetBlock(Pixels, SrcBits, X, Y, Width, Height);
  3445. HasAlpha := False;
  3446. for I := 0 to 15 do
  3447. if Pixels[I].Alpha < 128 then
  3448. begin
  3449. HasAlpha := True;
  3450. Break;
  3451. end;
  3452. GetEndpoints(Pixels, Block.Color0, Block.Color1);
  3453. FixEndpoints(Block.Color0, Block.Color1, HasAlpha);
  3454. if HasAlpha then
  3455. Block.Mask := GetColorMask(Block.Color0, Block.Color1, 3, Pixels)
  3456. else
  3457. Block.Mask := GetColorMask(Block.Color0, Block.Color1, 4, Pixels);
  3458. PDXTColorBlock(DestBits)^ := Block;
  3459. Inc(DestBits, SizeOf(Block));
  3460. end;
  3461. end;
  3462. procedure EncodeDXT3(SrcBits: Pointer; DestBits: PByte; Width, Height: LongInt);
  3463. var
  3464. X, Y, I: LongInt;
  3465. Block: TDXTColorBlock;
  3466. AlphaBlock: TDXTAlphaBlockExp;
  3467. Pixels: TPixelBlock;
  3468. begin
  3469. for Y := 0 to Height div 4 - 1 do
  3470. for X := 0 to Width div 4 - 1 do
  3471. begin
  3472. GetBlock(Pixels, SrcBits, X, Y, Width, Height);
  3473. for I := 0 to 7 do
  3474. PByteArray(@AlphaBlock.Alphas)[I] :=
  3475. (Pixels[I shl 1].Alpha shr 4) or ((Pixels[I shl 1 + 1].Alpha shr 4) shl 4);
  3476. GetEndpoints(Pixels, Block.Color0, Block.Color1);
  3477. FixEndpoints(Block.Color0, Block.Color1, False);
  3478. Block.Mask := GetColorMask(Block.Color0, Block.Color1, 4, Pixels);
  3479. PDXTAlphaBlockExp(DestBits)^ := AlphaBlock;
  3480. Inc(DestBits, SizeOf(AlphaBlock));
  3481. PDXTColorBlock(DestBits)^ := Block;
  3482. Inc(DestBits, SizeOf(Block));
  3483. end;
  3484. end;
  3485. procedure EncodeDXT5(SrcBits: Pointer; DestBits: PByte; Width, Height: LongInt);
  3486. var
  3487. X, Y: LongInt;
  3488. Block: TDXTColorBlock;
  3489. AlphaBlock: TDXTAlphaBlockInt;
  3490. Pixels: TPixelBlock;
  3491. begin
  3492. for Y := 0 to Height div 4 - 1 do
  3493. for X := 0 to Width div 4 - 1 do
  3494. begin
  3495. GetBlock(Pixels, SrcBits, X, Y, Width, Height);
  3496. GetEndpoints(Pixels, Block.Color0, Block.Color1);
  3497. FixEndpoints(Block.Color0, Block.Color1, False);
  3498. Block.Mask := GetColorMask(Block.Color0, Block.Color1, 4, Pixels);
  3499. GetAlphaEndPoints(Pixels, AlphaBlock.Alphas[1], AlphaBlock.Alphas[0]);
  3500. GetAlphaMask(AlphaBlock.Alphas[0], AlphaBlock.Alphas[1], Pixels,
  3501. PByteArray(@AlphaBlock.Alphas[2]));
  3502. PDXTAlphaBlockInt(DestBits)^ := AlphaBlock;
  3503. Inc(DestBits, SizeOf(AlphaBlock));
  3504. PDXTColorBlock(DestBits)^ := Block;
  3505. Inc(DestBits, SizeOf(Block));
  3506. end;
  3507. end;
  3508. type
  3509. TBTCBlock = packed record
  3510. MLower, MUpper: Byte;
  3511. BitField: Word;
  3512. end;
  3513. PBTCBlock = ^TBTCBlock;
  3514. procedure EncodeBTC(SrcBits: Pointer; DestBits: PByte; Width, Height: Integer);
  3515. var
  3516. X, Y, I, J: Integer;
  3517. Block: TBTCBlock;
  3518. M, MLower, MUpper, K: Integer;
  3519. Pixels: array[0..15] of Byte;
  3520. begin
  3521. for Y := 0 to Height div 4 - 1 do
  3522. for X := 0 to Width div 4 - 1 do
  3523. begin
  3524. M := 0;
  3525. MLower := 0;
  3526. MUpper := 0;
  3527. FillChar(Block, SizeOf(Block), 0);
  3528. K := 0;
  3529. // Store 4x4 pixels and compute average, lower, and upper intensity levels
  3530. for I := 0 to 3 do
  3531. for J := 0 to 3 do
  3532. begin
  3533. Pixels[K] := PByteArray(SrcBits)[(Y shl 2 + I) * Width + X shl 2 + J];
  3534. Inc(M, Pixels[K]);
  3535. Inc(K);
  3536. end;
  3537. M := M div 16;
  3538. K := 0;
  3539. // Now compute upper and lower levels, number of upper pixels,
  3540. // and update bit field (1 when pixel is above avg. level M)
  3541. for I := 0 to 15 do
  3542. begin
  3543. if Pixels[I] > M then
  3544. begin
  3545. Inc(MUpper, Pixels[I]);
  3546. Inc(K);
  3547. Block.BitField := Block.BitField or (1 shl I);
  3548. end
  3549. else
  3550. Inc(MLower, Pixels[I]);
  3551. end;
  3552. // Scale levels and save them to block
  3553. if K > 0 then
  3554. Block.MUpper := ClampToByte(MUpper div K)
  3555. else
  3556. Block.MUpper := 0;
  3557. Block.MLower := ClampToByte(MLower div (16 - K));
  3558. // Finally save block to dest data
  3559. PBTCBlock(DestBits)^ := Block;
  3560. Inc(DestBits, SizeOf(Block));
  3561. end;
  3562. end;
  3563. procedure GetOneChannelBlock(var Block: TPixelBlock; SrcBits: Pointer; XPos, YPos,
  3564. Width, Height, BytesPP, ChannelIdx: Integer);
  3565. var
  3566. X, Y, I: Integer;
  3567. Src: PByte;
  3568. begin
  3569. I := 0;
  3570. // 4x4 pixel block is filled with information about every pixel in the block,
  3571. // but only one channel value is stored in Alpha field
  3572. for Y := 0 to 3 do
  3573. for X := 0 to 3 do
  3574. begin
  3575. Src := @PByteArray(SrcBits)[(YPos * 4 + Y) * Width * BytesPP +
  3576. (XPos * 4 + X) * BytesPP + ChannelIdx];
  3577. Block[I].Alpha := Src^;
  3578. Inc(I);
  3579. end;
  3580. end;
  3581. procedure EncodeATI1N(SrcBits: Pointer; DestBits: PByte; Width, Height: Integer);
  3582. var
  3583. X, Y: Integer;
  3584. AlphaBlock: TDXTAlphaBlockInt;
  3585. Pixels: TPixelBlock;
  3586. begin
  3587. for Y := 0 to Height div 4 - 1 do
  3588. for X := 0 to Width div 4 - 1 do
  3589. begin
  3590. // Encode one channel
  3591. GetOneChannelBlock(Pixels, SrcBits, X, Y, Width, Height, 1, 0);
  3592. GetAlphaEndPoints(Pixels, AlphaBlock.Alphas[1], AlphaBlock.Alphas[0]);
  3593. GetAlphaMask(AlphaBlock.Alphas[0], AlphaBlock.Alphas[1], Pixels,
  3594. PByteArray(@AlphaBlock.Alphas[2]));
  3595. PDXTAlphaBlockInt(DestBits)^ := AlphaBlock;
  3596. Inc(DestBits, SizeOf(AlphaBlock));
  3597. end;
  3598. end;
  3599. procedure EncodeATI2N(SrcBits: Pointer; DestBits: PByte; Width, Height: Integer);
  3600. var
  3601. X, Y: Integer;
  3602. AlphaBlock: TDXTAlphaBlockInt;
  3603. Pixels: TPixelBlock;
  3604. begin
  3605. for Y := 0 to Height div 4 - 1 do
  3606. for X := 0 to Width div 4 - 1 do
  3607. begin
  3608. // Encode Red/X channel
  3609. GetOneChannelBlock(Pixels, SrcBits, X, Y, Width, Height, 4, ChannelRed);
  3610. GetAlphaEndPoints(Pixels, AlphaBlock.Alphas[1], AlphaBlock.Alphas[0]);
  3611. GetAlphaMask(AlphaBlock.Alphas[0], AlphaBlock.Alphas[1], Pixels,
  3612. PByteArray(@AlphaBlock.Alphas[2]));
  3613. PDXTAlphaBlockInt(DestBits)^ := AlphaBlock;
  3614. Inc(DestBits, SizeOf(AlphaBlock));
  3615. // Encode Green/Y channel
  3616. GetOneChannelBlock(Pixels, SrcBits, X, Y, Width, Height, 4, ChannelGreen);
  3617. GetAlphaEndPoints(Pixels, AlphaBlock.Alphas[1], AlphaBlock.Alphas[0]);
  3618. GetAlphaMask(AlphaBlock.Alphas[0], AlphaBlock.Alphas[1], Pixels,
  3619. PByteArray(@AlphaBlock.Alphas[2]));
  3620. PDXTAlphaBlockInt(DestBits)^ := AlphaBlock;
  3621. Inc(DestBits, SizeOf(AlphaBlock));
  3622. end;
  3623. end;
  3624. procedure EncodeBinary(SrcBits: Pointer; DestBits: PByte; Width, Height: Integer);
  3625. var
  3626. Src: PByte absolute SrcBits;
  3627. Bitmap: PByteArray absolute DestBits;
  3628. X, Y, WidthBytes: Integer;
  3629. PixelTresholded, Treshold: Byte;
  3630. begin
  3631. Treshold := ClampToByte(GetOption(ImagingBinaryTreshold));
  3632. WidthBytes := (Width + 7) div 8;
  3633. for Y := 0 to Height - 1 do
  3634. for X := 0 to Width - 1 do
  3635. begin
  3636. if Src^ > Treshold then
  3637. PixelTresholded := 255
  3638. else
  3639. PixelTresholded := 0;
  3640. Bitmap[Y * WidthBytes + X div 8] := Bitmap[Y * WidthBytes + X div 8] or // OR current value of byte with following:
  3641. (PixelTresholded and 1) // To make 1 from 255, 0 remains 0
  3642. shl (7 - (X mod 8)); // Put current bit to proper place in byte
  3643. Inc(Src);
  3644. end;
  3645. end;
  3646. procedure DecodeBTC(SrcBits, DestBits: PByte; Width, Height: Integer);
  3647. var
  3648. X, Y, I, J, K: Integer;
  3649. Block: TBTCBlock;
  3650. Dest: PByte;
  3651. begin
  3652. for Y := 0 to Height div 4 - 1 do
  3653. for X := 0 to Width div 4 - 1 do
  3654. begin
  3655. Block := PBTCBlock(SrcBits)^;
  3656. Inc(SrcBits, SizeOf(Block));
  3657. K := 0;
  3658. // Just write MUpper when there is '1' in bit field and MLower
  3659. // when there is '0'
  3660. for I := 0 to 3 do
  3661. for J := 0 to 3 do
  3662. begin
  3663. Dest := @PByteArray(DestBits)[(Y shl 2 + I) * Width + X shl 2 + J];
  3664. if Block.BitField and (1 shl K) <> 0 then
  3665. Dest^ := Block.MUpper
  3666. else
  3667. Dest^ := Block.MLower;
  3668. Inc(K);
  3669. end;
  3670. end;
  3671. end;
  3672. procedure DecodeATI1N(SrcBits, DestBits: PByte; Width, Height: Integer);
  3673. var
  3674. X, Y, I, J: Integer;
  3675. AlphaBlock: TDXTAlphaBlockInt;
  3676. AMask: array[0..1] of LongWord;
  3677. begin
  3678. for Y := 0 to Height div 4 - 1 do
  3679. for X := 0 to Width div 4 - 1 do
  3680. begin
  3681. AlphaBlock := PDXTAlphaBlockInt(SrcBits)^;
  3682. Inc(SrcBits, SizeOf(AlphaBlock));
  3683. // 6 bit alpha mask is copied into two long words for
  3684. // easier usage
  3685. AMask[0] := PLongWord(@AlphaBlock.Alphas[2])^ and $00FFFFFF;
  3686. AMask[1] := PLongWord(@AlphaBlock.Alphas[5])^ and $00FFFFFF;
  3687. // alpha interpolation between two endpoint alphas
  3688. GetInterpolatedAlphas(AlphaBlock);
  3689. // we distribute the dxt block alphas
  3690. // across the 4x4 block of the destination image
  3691. for J := 0 to 3 do
  3692. for I := 0 to 3 do
  3693. begin
  3694. PByteArray(DestBits)[(Y shl 2 + J) * Width + (X shl 2 + I)] :=
  3695. AlphaBlock.Alphas[AMask[J shr 1] and 7];
  3696. AMask[J shr 1] := AMask[J shr 1] shr 3;
  3697. end;
  3698. end;
  3699. end;
  3700. procedure DecodeATI2N(SrcBits, DestBits: PByte; Width, Height: Integer);
  3701. var
  3702. X, Y, I, J: Integer;
  3703. Color: TColor32Rec;
  3704. AlphaBlock1, AlphaBlock2: TDXTAlphaBlockInt;
  3705. AMask1: array[0..1] of LongWord;
  3706. AMask2: array[0..1] of LongWord;
  3707. begin
  3708. for Y := 0 to Height div 4 - 1 do
  3709. for X := 0 to Width div 4 - 1 do
  3710. begin
  3711. // Read the first alpha block and get masks
  3712. AlphaBlock1 := PDXTAlphaBlockInt(SrcBits)^;
  3713. Inc(SrcBits, SizeOf(AlphaBlock1));
  3714. AMask1[0] := PLongWord(@AlphaBlock1.Alphas[2])^ and $00FFFFFF;
  3715. AMask1[1] := PLongWord(@AlphaBlock1.Alphas[5])^ and $00FFFFFF;
  3716. // Read the secind alpha block and get masks
  3717. AlphaBlock2 := PDXTAlphaBlockInt(SrcBits)^;
  3718. Inc(SrcBits, SizeOf(AlphaBlock2));
  3719. AMask2[0] := PLongWord(@AlphaBlock2.Alphas[2])^ and $00FFFFFF;
  3720. AMask2[1] := PLongWord(@AlphaBlock2.Alphas[5])^ and $00FFFFFF;
  3721. // alpha interpolation between two endpoint alphas
  3722. GetInterpolatedAlphas(AlphaBlock1);
  3723. GetInterpolatedAlphas(AlphaBlock2);
  3724. Color.A := $FF;
  3725. Color.B := 0;
  3726. // Distribute alpha block values across 4x4 pixel block,
  3727. // first alpha block represents Red channel, second is Green.
  3728. for J := 0 to 3 do
  3729. for I := 0 to 3 do
  3730. begin
  3731. Color.R := AlphaBlock1.Alphas[AMask1[J shr 1] and 7];
  3732. Color.G := AlphaBlock2.Alphas[AMask2[J shr 1] and 7];
  3733. PColor32RecArray(DestBits)[(Y shl 2 + J) * Width + (X shl 2 + I)] := Color;
  3734. AMask1[J shr 1] := AMask1[J shr 1] shr 3;
  3735. AMask2[J shr 1] := AMask2[J shr 1] shr 3;
  3736. end;
  3737. end;
  3738. end;
  3739. procedure DecodeBinary(SrcBits, DestBits: PByte; Width, Height: Integer); {$IFDEF USE_INLINE}inline;{$ENDIF}
  3740. begin
  3741. Convert1To8(SrcBits, DestBits, Width, Height, (Width + 7) div 8, True);
  3742. end;
  3743. procedure SpecialToUnSpecial(const SrcImage: TImageData; DestBits: Pointer;
  3744. SpecialFormat: TImageFormat);
  3745. begin
  3746. case SpecialFormat of
  3747. ifDXT1: DecodeDXT1(SrcImage.Bits, DestBits, SrcImage.Width, SrcImage.Height);
  3748. ifDXT3: DecodeDXT3(SrcImage.Bits, DestBits, SrcImage.Width, SrcImage.Height);
  3749. ifDXT5: DecodeDXT5(SrcImage.Bits, DestBits, SrcImage.Width, SrcImage.Height);
  3750. ifBTC: DecodeBTC (SrcImage.Bits, DestBits, SrcImage.Width, SrcImage.Height);
  3751. ifATI1N: DecodeATI1N(SrcImage.Bits, DestBits, SrcImage.Width, SrcImage.Height);
  3752. ifATI2N: DecodeATI2N(SrcImage.Bits, DestBits, SrcImage.Width, SrcImage.Height);
  3753. ifBinary: DecodeBinary(SrcImage.Bits, DestBits, SrcImage.Width, SrcImage.Height);
  3754. end;
  3755. end;
  3756. procedure UnSpecialToSpecial(SrcBits: Pointer; const DestImage: TImageData;
  3757. SpecialFormat: TImageFormat);
  3758. begin
  3759. case SpecialFormat of
  3760. ifDXT1: EncodeDXT1(SrcBits, DestImage.Bits, DestImage.Width, DestImage.Height);
  3761. ifDXT3: EncodeDXT3(SrcBits, DestImage.Bits, DestImage.Width, DestImage.Height);
  3762. ifDXT5: EncodeDXT5(SrcBits, DestImage.Bits, DestImage.Width, DestImage.Height);
  3763. ifBTC: EncodeBTC (SrcBits, DestImage.Bits, DestImage.Width, DestImage.Height);
  3764. ifATI1N: EncodeATI1N(SrcBits, DestImage.Bits, DestImage.Width, DestImage.Height);
  3765. ifATI2N: EncodeATI2N(SrcBits, DestImage.Bits, DestImage.Width, DestImage.Height);
  3766. ifBinary: EncodeBinary(SrcBits, DestImage.Bits, DestImage.Width, DestImage.Height);
  3767. end;
  3768. end;
  3769. procedure ConvertSpecial(var Image: TImageData;
  3770. SrcInfo, DstInfo: PImageFormatInfo);
  3771. var
  3772. WorkImage: TImageData;
  3773. procedure CheckSize(var Img: TImageData; Info: PImageFormatInfo);
  3774. var
  3775. Width, Height: Integer;
  3776. begin
  3777. Width := Img.Width;
  3778. Height := Img.Height;
  3779. DstInfo.CheckDimensions(Info.Format, Width, Height);
  3780. ResizeImage(Img, Width, Height, rfNearest);
  3781. end;
  3782. begin
  3783. if SrcInfo.IsSpecial and DstInfo.IsSpecial then
  3784. begin
  3785. // Convert source to nearest 'normal' format
  3786. InitImage(WorkImage);
  3787. NewImage(Image.Width, Image.Height, SrcInfo.SpecialNearestFormat, WorkImage);
  3788. SpecialToUnSpecial(Image, WorkImage.Bits, SrcInfo.Format);
  3789. FreeImage(Image);
  3790. // Make sure output of SpecialToUnSpecial is the same as input of
  3791. // UnSpecialToSpecial
  3792. if SrcInfo.SpecialNearestFormat <> DstInfo.SpecialNearestFormat then
  3793. ConvertImage(WorkImage, DstInfo.SpecialNearestFormat);
  3794. // Convert work image to dest special format
  3795. CheckSize(WorkImage, DstInfo);
  3796. NewImage(WorkImage.Width, WorkImage.Height, DstInfo.Format, Image);
  3797. UnSpecialToSpecial(WorkImage.Bits, Image, DstInfo.Format);
  3798. FreeImage(WorkImage);
  3799. end
  3800. else if SrcInfo.IsSpecial and not DstInfo.IsSpecial then
  3801. begin
  3802. // Convert source to nearest 'normal' format
  3803. InitImage(WorkImage);
  3804. NewImage(Image.Width, Image.Height, SrcInfo.SpecialNearestFormat, WorkImage);
  3805. SpecialToUnSpecial(Image, WorkImage.Bits, SrcInfo.Format);
  3806. FreeImage(Image);
  3807. // Now convert to dest format
  3808. ConvertImage(WorkImage, DstInfo.Format);
  3809. Image := WorkImage;
  3810. end
  3811. else if not SrcInfo.IsSpecial and DstInfo.IsSpecial then
  3812. begin
  3813. // Convert source to nearest format
  3814. WorkImage := Image;
  3815. ConvertImage(WorkImage, DstInfo.SpecialNearestFormat);
  3816. // Now convert from nearest to dest
  3817. CheckSize(WorkImage, DstInfo);
  3818. InitImage(Image);
  3819. NewImage(WorkImage.Width, WorkImage.Height, DstInfo.Format, Image);
  3820. UnSpecialToSpecial(WorkImage.Bits, Image, DstInfo.Format);
  3821. FreeImage(WorkImage);
  3822. end;
  3823. end;
  3824. function GetStdPixelsSize(Format: TImageFormat; Width, Height: LongInt): LongInt;
  3825. begin
  3826. if FInfos[Format] <> nil then
  3827. Result := Width * Height * FInfos[Format].BytesPerPixel
  3828. else
  3829. Result := 0;
  3830. end;
  3831. procedure CheckStdDimensions(Format: TImageFormat; var Width, Height: LongInt);
  3832. begin
  3833. end;
  3834. function GetDXTPixelsSize(Format: TImageFormat; Width, Height: LongInt): LongInt;
  3835. begin
  3836. // DXT can be used only for images with dimensions that are
  3837. // multiples of four
  3838. CheckDXTDimensions(Format, Width, Height);
  3839. Result := Width * Height;
  3840. if Format in [ifDXT1, ifATI1N] then
  3841. Result := Result div 2;
  3842. end;
  3843. procedure CheckDXTDimensions(Format: TImageFormat; var Width, Height: LongInt);
  3844. begin
  3845. // DXT image dimensions must be multiples of four
  3846. Width := (Width + 3) and not 3; // div 4 * 4;
  3847. Height := (Height + 3) and not 3; // div 4 * 4;
  3848. end;
  3849. function GetBTCPixelsSize(Format: TImageFormat; Width, Height: LongInt): LongInt;
  3850. begin
  3851. // BTC can be used only for images with dimensions that are
  3852. // multiples of four
  3853. CheckDXTDimensions(Format, Width, Height);
  3854. Result := Width * Height div 4; // 2bits/pixel
  3855. end;
  3856. function GetBCPixelsSize(Format: TImageFormat; Width, Height: LongInt): LongInt;
  3857. begin
  3858. raise ENotImplemented.Create();
  3859. end;
  3860. procedure CheckBCDimensions(Format: TImageFormat; var Width, Height: LongInt);
  3861. begin
  3862. raise ENotImplemented.Create();
  3863. end;
  3864. function GetBinaryPixelsSize(Format: TImageFormat; Width, Height: LongInt): LongInt;
  3865. begin
  3866. // Binary images are aligned on BYTE boundary
  3867. Result := ((Width + 7) div 8) * Height; // 1bit/pixel
  3868. end;
  3869. { Optimized pixel readers/writers for 32bit and FP colors to be stored in TImageFormatInfo }
  3870. function GetPixel32ifA8R8G8B8(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32): TColor32Rec;
  3871. begin
  3872. Result.Color := PLongWord(Bits)^;
  3873. end;
  3874. procedure SetPixel32ifA8R8G8B8(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32; const Color: TColor32Rec);
  3875. begin
  3876. PLongWord(Bits)^ := Color.Color;
  3877. end;
  3878. function GetPixelFPifA8R8G8B8(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32): TColorFPRec;
  3879. begin
  3880. Result.A := PColor32Rec(Bits).A * OneDiv8Bit;
  3881. Result.R := PColor32Rec(Bits).R * OneDiv8Bit;
  3882. Result.G := PColor32Rec(Bits).G * OneDiv8Bit;
  3883. Result.B := PColor32Rec(Bits).B * OneDiv8Bit;
  3884. end;
  3885. procedure SetPixelFPifA8R8G8B8(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32; const Color: TColorFPRec);
  3886. begin
  3887. PColor32Rec(Bits).A := ClampToByte(Round(Color.A * 255.0));
  3888. PColor32Rec(Bits).R := ClampToByte(Round(Color.R * 255.0));
  3889. PColor32Rec(Bits).G := ClampToByte(Round(Color.G * 255.0));
  3890. PColor32Rec(Bits).B := ClampToByte(Round(Color.B * 255.0));
  3891. end;
  3892. function GetPixel32Channel8Bit(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32): TColor32Rec;
  3893. begin
  3894. case Info.Format of
  3895. ifR8G8B8, ifX8R8G8B8:
  3896. begin
  3897. Result.A := $FF;
  3898. PColor24Rec(@Result)^ := PColor24Rec(Bits)^;
  3899. end;
  3900. ifGray8, ifA8Gray8:
  3901. begin
  3902. if Info.HasAlphaChannel then
  3903. Result.A := PWordRec(Bits).High
  3904. else
  3905. Result.A := $FF;
  3906. Result.R := PWordRec(Bits).Low;
  3907. Result.G := PWordRec(Bits).Low;
  3908. Result.B := PWordRec(Bits).Low;
  3909. end;
  3910. end;
  3911. end;
  3912. procedure SetPixel32Channel8Bit(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32; const Color: TColor32Rec);
  3913. begin
  3914. case Info.Format of
  3915. ifR8G8B8, ifX8R8G8B8:
  3916. begin
  3917. PColor24Rec(Bits)^ := PColor24Rec(@Color)^;
  3918. end;
  3919. ifGray8, ifA8Gray8:
  3920. begin
  3921. if Info.HasAlphaChannel then
  3922. PWordRec(Bits).High := Color.A;
  3923. PWordRec(Bits).Low := Round(GrayConv.R * Color.R + GrayConv.G * Color.G +
  3924. GrayConv.B * Color.B);
  3925. end;
  3926. end;
  3927. end;
  3928. function GetPixelFPChannel8Bit(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32): TColorFPRec;
  3929. begin
  3930. case Info.Format of
  3931. ifR8G8B8, ifX8R8G8B8:
  3932. begin
  3933. Result.A := 1.0;
  3934. Result.R := PColor24Rec(Bits).R * OneDiv8Bit;
  3935. Result.G := PColor24Rec(Bits).G * OneDiv8Bit;
  3936. Result.B := PColor24Rec(Bits).B * OneDiv8Bit;
  3937. end;
  3938. ifGray8, ifA8Gray8:
  3939. begin
  3940. if Info.HasAlphaChannel then
  3941. Result.A := PWordRec(Bits).High * OneDiv8Bit
  3942. else
  3943. Result.A := 1.0;
  3944. Result.R := PWordRec(Bits).Low * OneDiv8Bit;
  3945. Result.G := PWordRec(Bits).Low * OneDiv8Bit;
  3946. Result.B := PWordRec(Bits).Low * OneDiv8Bit;
  3947. end;
  3948. end;
  3949. end;
  3950. procedure SetPixelFPChannel8Bit(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32; const Color: TColorFPRec);
  3951. begin
  3952. case Info.Format of
  3953. ifR8G8B8, ifX8R8G8B8:
  3954. begin
  3955. PColor24Rec(Bits).R := ClampToByte(Round(Color.R * 255.0));
  3956. PColor24Rec(Bits).G := ClampToByte(Round(Color.G * 255.0));
  3957. PColor24Rec(Bits).B := ClampToByte(Round(Color.B * 255.0));
  3958. end;
  3959. ifGray8, ifA8Gray8:
  3960. begin
  3961. if Info.HasAlphaChannel then
  3962. PWordRec(Bits).High := ClampToByte(Round(Color.A * 255.0));
  3963. PWordRec(Bits).Low := ClampToByte(Round((GrayConv.R * Color.R + GrayConv.G * Color.G +
  3964. GrayConv.B * Color.B) * 255.0));
  3965. end;
  3966. end;
  3967. end;
  3968. function GetPixelFPFloat32(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32): TColorFPRec;
  3969. begin
  3970. case Info.Format of
  3971. ifA32R32G32B32F, ifA32B32G32R32F:
  3972. begin
  3973. Result := PColorFPRec(Bits)^;
  3974. end;
  3975. ifR32G32B32F, ifB32G32R32F:
  3976. begin
  3977. Result.A := 1.0;
  3978. Result.Color96Rec := PColor96FPRec(Bits)^;
  3979. end;
  3980. ifR32F:
  3981. begin
  3982. Result.A := 1.0;
  3983. Result.R := PSingle(Bits)^;
  3984. Result.G := 0.0;
  3985. Result.B := 0.0;
  3986. end;
  3987. end;
  3988. if Info.IsRBSwapped then
  3989. SwapValues(Result.R, Result.B);
  3990. end;
  3991. procedure SetPixelFPFloat32(Bits: Pointer; Info: PImageFormatInfo; Palette: PPalette32; const Color: TColorFPRec);
  3992. begin
  3993. case Info.Format of
  3994. ifA32R32G32B32F, ifA32B32G32R32F:
  3995. begin
  3996. PColorFPRec(Bits)^ := Color;
  3997. end;
  3998. ifR32G32B32F, ifB32G32R32F:
  3999. begin
  4000. PColor96FPRec(Bits)^ := Color.Color96Rec;
  4001. end;
  4002. ifR32F:
  4003. begin
  4004. PSingle(Bits)^ := Color.R;
  4005. end;
  4006. end;
  4007. if Info.IsRBSwapped then
  4008. SwapValues(PColor96FPRec(Bits).R, PColor96FPRec(Bits).B);
  4009. end;
  4010. initialization
  4011. // Initialize default sampling filter function pointers and radii
  4012. SamplingFilterFunctions[sfNearest] := FilterNearest;
  4013. SamplingFilterFunctions[sfLinear] := FilterLinear;
  4014. SamplingFilterFunctions[sfCosine] := FilterCosine;
  4015. SamplingFilterFunctions[sfHermite] := FilterHermite;
  4016. SamplingFilterFunctions[sfQuadratic] := FilterQuadratic;
  4017. SamplingFilterFunctions[sfGaussian] := FilterGaussian;
  4018. SamplingFilterFunctions[sfSpline] := FilterSpline;
  4019. SamplingFilterFunctions[sfLanczos] := FilterLanczos;
  4020. SamplingFilterFunctions[sfMitchell] := FilterMitchell;
  4021. SamplingFilterFunctions[sfCatmullRom] := FilterCatmullRom;
  4022. SamplingFilterRadii[sfNearest] := 1.0;
  4023. SamplingFilterRadii[sfLinear] := 1.0;
  4024. SamplingFilterRadii[sfCosine] := 1.0;
  4025. SamplingFilterRadii[sfHermite] := 1.0;
  4026. SamplingFilterRadii[sfQuadratic] := 1.5;
  4027. SamplingFilterRadii[sfGaussian] := 1.25;
  4028. SamplingFilterRadii[sfSpline] := 2.0;
  4029. SamplingFilterRadii[sfLanczos] := 3.0;
  4030. SamplingFilterRadii[sfMitchell] := 2.0;
  4031. SamplingFilterRadii[sfCatmullRom] := 2.0;
  4032. {
  4033. File Notes:
  4034. -- TODOS ----------------------------------------------------
  4035. - nothing now
  4036. -- 0.80 -------------------------------------------------------
  4037. - Added PaletteIsGrayScale and Color32ToGray functions.
  4038. -- 0.77 Changes/Bug Fixes -------------------------------------
  4039. - NOT YET: Added support for Passtrough image data formats.
  4040. - Added ConvertToPixel32 helper function.
  4041. -- 0.26.5 Changes/Bug Fixes -----------------------------------
  4042. - Removed optimized codepatch for few data formats from StretchResample
  4043. function. It was quite buggy and not so much faster anyway.
  4044. - Added PaletteHasAlpha function.
  4045. - Added support functions for ifBinary data format.
  4046. - Added optional pixel scaling to Convert1To8, Convert2To8,
  4047. abd Convert4To8 functions.
  4048. -- 0.26.3 Changes/Bug Fixes -----------------------------------
  4049. - Filtered resampling ~10% faster now.
  4050. - Fixed DXT3 alpha encoding.
  4051. - ifIndex8 format now has HasAlphaChannel=True.
  4052. -- 0.25.0 Changes/Bug Fixes -----------------------------------
  4053. - Made some resampling stuff public so that it can be used in canvas class.
  4054. - Added some color constructors.
  4055. - Added VisualizePalette helper function.
  4056. - Fixed ConvertSpecial, not very readable before and error when
  4057. converting special->special.
  4058. -- 0.24.3 Changes/Bug Fixes -----------------------------------
  4059. - Some refactorings a changes to DXT based formats.
  4060. - Added ifATI1N and ifATI2N image data formats support structures and functions.
  4061. -- 0.23 Changes/Bug Fixes -----------------------------------
  4062. - Added ifBTC image format support structures and functions.
  4063. -- 0.21 Changes/Bug Fixes -----------------------------------
  4064. - FillMipMapLevel now works well with indexed and special formats too.
  4065. - Moved Convert1To8 and Convert4To8 functions from ImagingBitmaps here
  4066. and created new Convert2To8 function. They are now used by more than one
  4067. file format loader.
  4068. -- 0.19 Changes/Bug Fixes -----------------------------------
  4069. - StretchResample now uses pixel get/set functions stored in
  4070. TImageFormatInfo so it is much faster for formats that override
  4071. them with optimized ones
  4072. - added pixel set/get functions optimized for various image formats
  4073. (to be stored in TImageFormatInfo)
  4074. - bug in ConvertSpecial caused problems when converting DXTC images
  4075. to bitmaps in ImagingCoponents
  4076. - bug in StretchRect caused that it didn't work with ifR32F and
  4077. ifR16F formats
  4078. - removed leftover code in FillMipMapLevel which disabled
  4079. filtered resizing of images witch ChannelSize <> 8bits
  4080. - added half float converting functions and support for half based
  4081. image formats where needed
  4082. - added TranslatePixel and IsImageFormatValid functions
  4083. - fixed possible range overflows when converting from FP to integer images
  4084. - added pixel set/get functions: GetPixel32Generic, GetPixelFPGeneric,
  4085. SetPixel32Generic, SetPixelFPGeneric
  4086. - fixed occasional range overflows in StretchResample
  4087. -- 0.17 Changes/Bug Fixes -----------------------------------
  4088. - added StretchNearest, StretchResample and some sampling functions
  4089. - added ChannelCount values to TImageFormatInfo constants
  4090. - added resolution validity check to GetDXTPixelsSize
  4091. -- 0.15 Changes/Bug Fixes -----------------------------------
  4092. - added RBSwapFormat values to some TImageFromatInfo definitions
  4093. - fixed bug in ConvertSpecial (causing DXT images to convert only to 32bit)
  4094. - added CopyPixel, ComparePixels helper functions
  4095. -- 0.13 Changes/Bug Fixes -----------------------------------
  4096. - replaced pixel format conversions for colors not to be
  4097. darkened when converting from low bit counts
  4098. - ReduceColorsMedianCut was updated to support creating one
  4099. optimal palette for more images and it is somewhat faster
  4100. now too
  4101. - there was ugly bug in DXTC dimensions checking
  4102. }
  4103. end.