ImagingSquishLib.pas 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. {
  2. Vampyre Imaging Library
  3. by Marek Mauder
  4. https://github.com/galfar/imaginglib
  5. https://imaginglib.sourceforge.io
  6. - - - - -
  7. This Source Code Form is subject to the terms of the Mozilla Public
  8. License, v. 2.0. If a copy of the MPL was not distributed with this
  9. file, You can obtain one at https://mozilla.org/MPL/2.0.
  10. }
  11. { High quality DXTC compressor using Squish library (dynamically linked).}
  12. unit ImagingSquishLib;
  13. interface
  14. {$I ImagingOptions.inc}
  15. uses
  16. ImagingTypes, Imaging, ImagingFormats;
  17. type
  18. TDXTCompressor = (
  19. dcClusterFit, // Use a slow but high quality colour compressor (the default).
  20. dcRangeFit, // Use a fast but low quality colour compressor.
  21. dcClusterFitAlphaWeighted // Cluster fit that weights the colour by alpha.
  22. // For images that are rendered using alpha blending,
  23. // this can significantly increase the perceived quality.
  24. );
  25. TColorMetric = (
  26. cmPerceptual, // Use a perceptual metric for colour error (the default).
  27. cmUniform // Use a uniform metric for colour error.
  28. );
  29. { Compresses SrcImage using selected DXTn compression into DestImage.
  30. DestImage should be cleared before calling.}
  31. procedure DXTCompressImage(const SrcImage: TImageData; var DestImage: TImageData;
  32. DXTFormat: TImageFormat; Compressor: TDXTCompressor = dcClusterFit;
  33. Metric: TColorMetric = cmPerceptual);
  34. implementation
  35. const
  36. FlagDXT1 = 1 shl 0;
  37. FlagDXT3 = 1 shl 1;
  38. FlagDXT5 = 1 shl 2;
  39. FlagColourClusterFit = 1 shl 3;
  40. FlagColourRangeFit = 1 shl 4;
  41. FlagColourMetricPerceptual = 1 shl 5;
  42. FlagColourMetricUniform = 1 shl 6;
  43. FlagWeightColourByAlpha = 1 shl 7;
  44. (* @brief Compresses an image in memory.
  45. @param rgba The pixels of the source.
  46. @param width The width of the source image.
  47. @param height The height of the source image.
  48. @param blocks Storage for the compressed output.
  49. @param flags Compression flags.
  50. The source pixels should be presented as a contiguous array of width*height
  51. rgba values, with each component as 1 byte each. In memory this should be:
  52. { r1, g1, b1, a1, .... , rn, gn, bn, an } for n = width*height
  53. The flags parameter should specify either kDxt1, kDxt3 or kDxt5 compression,
  54. however, DXT1 will be used by default if none is specified. When using DXT1
  55. compression, 8 bytes of storage are required for each compressed DXT block.
  56. DXT3 and DXT5 compression require 16 bytes of storage per block.
  57. The flags parameter can also specify a preferred colour compressor and
  58. colour error metric to use when fitting the RGB components of the data.
  59. Possible colour compressors are: kColourClusterFit (the default) or
  60. kColourRangeFit. Possible colour error metrics are: kColourMetricPerceptual
  61. (the default) or kColourMetricUniform. If no flags are specified in any
  62. particular category then the default will be used. Unknown flags are
  63. ignored.
  64. When using kColourClusterFit, an additional flag can be specified to
  65. weight the colour of each pixel by its alpha value. For images that are
  66. rendered using alpha blending, this can significantly increase the
  67. perceived quality.
  68. Internally this function calls squish::Compress for each block. To see how
  69. much memory is required in the compressed image, use
  70. squish::GetStorageRequirements.
  71. *)
  72. procedure CompressImage(RGBA: PByte; Width, Height: Integer; Blocks: Pointer;
  73. Flags: Integer); cdecl; external 'libsquish.dll';
  74. procedure DXTCompressImage(const SrcImage: TImageData; var DestImage: TImageData;
  75. DXTFormat: TImageFormat; Compressor: TDXTCompressor = dcClusterFit;
  76. Metric: TColorMetric = cmPerceptual);
  77. var
  78. Width, Height: Integer;
  79. Info: TImageFormatInfo;
  80. TempImage: TImageData;
  81. Flags: Integer;
  82. function GetSquishFlags: Integer;
  83. begin
  84. Result := 0;
  85. case DXTFormat of
  86. ifDXT1: Result := FlagDXT1;
  87. ifDXT3: Result := FlagDXT3;
  88. ifDXT5: Result := FlagDXT5;
  89. end;
  90. case Compressor of
  91. dcClusterFit: Result := Result or FlagColourClusterFit;
  92. dcRangeFit: Result := Result or FlagColourRangeFit;
  93. dcClusterFitAlphaWeighted: Result := Result or FlagColourClusterFit or FlagWeightColourByAlpha;
  94. end;
  95. case Metric of
  96. cmPerceptual: Result := Result or FlagColourMetricPerceptual;
  97. cmUniform: Result := Result or FlagColourMetricUniform;
  98. end;
  99. end;
  100. begin
  101. Assert(DXTFormat in [ifDXT1, ifDXT3, ifDXT5]);
  102. Width := SrcImage.Width;
  103. Height := SrcImage.Height;
  104. Flags := GetSquishFlags;
  105. // Check if input has correct dimensions and change them if needed
  106. GetImageFormatInfo(DXTFormat, Info);
  107. Info.CheckDimensions(DXTFormat, Width, Height);
  108. try
  109. // Create temp image as input for squish (must be ABGR order with
  110. // dimensions being multiples of 4)
  111. NewImage(Width, Height, ifA8R8G8B8, TempImage);
  112. CopyRect(SrcImage, 0, 0, SrcImage.Width, SrcImage.Height, TempImage, 0, 0);
  113. SwapChannels(TempImage, ChannelRed, ChannelBlue);
  114. // Init and create out image
  115. InitImage(DestImage);
  116. NewImage(Width, Height, DXTFormat, DestImage);
  117. // Finally call Squish
  118. CompressImage(TempImage.Bits, Width, Height, DestImage.Bits, Flags);
  119. finally
  120. FreeImage(TempImage);
  121. end;
  122. end;
  123. end.