Statistics
| Branch: | Tag: | Revision:

root / Imaging / ImagingClasses.pas @ 0:95bd93c28625

History | View | Annotate | Download (32.3 kB)

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