Statistics
| Branch: | Tag: | Revision:

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