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