1 // SPDX-License-Identifier: GPL-3.0-only
2 unit UStateType;
3
4 {$mode objfpc}
5
6 interface
7
8 uses
9 Types, Classes, SysUtils, BGRABitmap, BGRABitmapTypes, BGRALayers,
10 BGRALayerOriginal, fgl, UImageBackup;
11
12 const MinSizeToCompress = 512; //set to 1 if you want always compression
13 const MinSerializedSize = 16384;
14
15 type
16 TState = class;
17
18 { TStateDifference }
19
20 TStateDifference = class
TryCompressnull21 function TryCompress: boolean; virtual;
22 procedure ApplyTo(AState: TState); virtual; abstract;
23 procedure UnapplyTo(AState: TState); virtual; abstract;
UsedMemorynull24 function UsedMemory: int64; virtual;
ToStringnull25 function ToString: ansistring; override;
26 end;
27
28 TState = class
29 saved: boolean;
30 procedure ApplyDifference(ADifference: TStateDifference); virtual; abstract;
31 procedure ReverseDifference(ADifference: TStateDifference); virtual; abstract;
Duplicatenull32 function Duplicate: TState; virtual; abstract;
33 end;
34
35 TImageDifferenceKind = (idkChangeImageAndSelection, idkChangeStack, idkChangeSelection,
36 idkChangeImage);
37
38 { TCustomImageDifference }
39
40 TCustomImageDifference = class(TStateDifference)
41 protected
42 FSavedBefore, FSavedAfter: boolean;
GetIsIdentitynull43 function GetIsIdentity: boolean; virtual;
GetImageDifferenceKindnull44 function GetImageDifferenceKind: TImageDifferenceKind; virtual;
GetChangingBoundsnull45 function GetChangingBounds: TRect; virtual;
GetChangingBoundsDefinednull46 function GetChangingBoundsDefined: boolean; virtual;
GetCostnull47 function GetCost: integer; virtual;
48 public
49 constructor Create(AFromState: TState; AToState: TState);
50 constructor Create(AFromState: TState);
51 constructor Create(ASavedBefore : boolean; ASavedAfter: boolean);
52 procedure ApplyTo(AState: TState); override;
53 procedure UnapplyTo(AState: TState); override;
54 property SavedBefore: boolean read FSavedBefore write FSavedBefore;
55 property SavedAfter: boolean read FSavedAfter write FSavedAfter;
56 property Kind: TImageDifferenceKind read GetImageDifferenceKind;
57 property ChangingBounds: TRect read GetChangingBounds;
58 property ChangingBoundsDefined: boolean read GetChangingBoundsDefined;
59 property IsIdentity: boolean read GetIsIdentity;
60 end;
61
62 TImageDifferenceList = specialize TFPGObjectList<TCustomImageDifference>;
63
64 { TComposedImageDifference }
65
66 TComposedImageDifference = class(TCustomImageDifference)
67 private
GetCountnull68 function GetCount: integer;
GetItemnull69 function GetItem(AIndex: integer): TCustomImageDifference;
GetTotalCountnull70 function GetTotalCount: integer;
71 protected
72 FDiffs: TImageDifferenceList;
73 FAgglutinate: boolean;
GetIsIdentitynull74 function GetIsIdentity: boolean; override;
GetImageDifferenceKindnull75 function GetImageDifferenceKind: TImageDifferenceKind; override;
GetChangingBoundsnull76 function GetChangingBounds: TRect; override;
GetChangingBoundsDefinednull77 function GetChangingBoundsDefined: boolean; override;
78 public
79 constructor Create(AAgglutinate: boolean = false);
80 procedure ReleaseDiffs;
81 procedure StopAgglutinate;
82 destructor Destroy; override;
TryCompressnull83 function TryCompress: boolean; override;
UsedMemorynull84 function UsedMemory: int64; override;
85 procedure Add(ADiff: TCustomImageDifference);
86 procedure AddRange(AComposed: TComposedImageDifference);
87 procedure ApplyTo(AState: TState); override;
88 procedure UnapplyTo(AState: TState); override;
89 procedure Clear;
90 procedure Delete(AIndex: integer);
91 procedure DeleteFrom(AIndex: integer);
GetLastnull92 function GetLast: TCustomImageDifference;
ToStringnull93 function ToString: ansistring; override;
94 property Count: integer read GetCount;
95 property TotalCount: integer read GetTotalCount;
96 property Agglutinate: boolean read FAgglutinate;
97 property Item[AIndex: integer]: TCustomImageDifference read GetItem; default;
98 end;
99
100 {*********** Layer info *************}
101
102 type
103 TLayerInfo = record
104 Id: integer;
105 Visible: boolean;
106 BlendOp: TBlendOperation;
107 Name: string;
108 Opactiy: byte;
109 Offset: TPoint;
110 end;
111
112 procedure ApplyLayerInfo(AInfo: TLayerInfo; ALayeredBitmap: TBGRALayeredBitmap; AIndex: integer);
GetLayerInfonull113 function GetLayerInfo(ALayeredBitmap: TBGRALayeredBitmap; AIndex: integer): TLayerInfo;
114
115 {*********** Inversible **************}
116
117 type
118 TInversibleAction = (iaHorizontalFlip, iaHorizontalFlipLayer, iaVerticalFlip, iaVerticalFlipLayer, iaRotateCW, iaRotateCCW, iaRotate180, iaSwapRedBlue, iaLinearNegative);
119
120 const
121 InversibleActionStr : array[TInversibleAction] of string =
122 ('HorizontalFlip', 'HorizontalFlipLayer', 'VerticalFlip', 'VerticalFlipLayer', 'RotateCW', 'RotateCCW', 'Rotate180', 'SwapRedBlue', 'LinearNegative');
123
GetInverseActionnull124 function GetInverseAction(AAction: TInversibleAction): TInversibleAction;
CanCombineInversibleActionnull125 function CanCombineInversibleAction(AAction1, AAction2: TInversibleAction; out
126 ACombined: TInversibleAction): boolean;
127
128 type
129 { TImageDiff }
130
131 TImageDiff = class
132 private
133 FChangeRect: TRect;
134 FGrayscale: boolean;
135 FSavedFilename: string;
136 FImageBackup: TImageBackup;
137 FSizeBefore, FSizeAfter: TSize;
GetIsIdentitynull138 function GetIsIdentity: boolean;
139 procedure DiscardFile;
140 procedure Decompress;
141 procedure Init(Image1,Image2: TBGRABitmap; AGrayscale: boolean); overload;
142 procedure Init(Image1,Image2: TBGRABitmap; AGrayscale: boolean; AChangeRect: TRect); overload;
143 protected
CreateNewnull144 function CreateNew(AWidth, AHeight: integer): TBGRABitmap;
145 public
146 constructor Create(Image1: TBGRABitmap; AGrayscale: boolean);
147 constructor Create(Image1: TBGRABitmap; AGrayscale: boolean; AChangeRect: TRect);
148 constructor Create(Image1,Image2: TBGRABitmap; AGrayscale: boolean);
149 constructor Create(Image1,Image2: TBGRABitmap; AGrayscale: boolean; AChangeRect: TRect);
150 procedure ApplyInPlace(ADest: TBGRABitmap; AXor: boolean);
ApplyInNewnull151 function ApplyInNew(ASource: TBGRABitmap; AReverse: boolean; AXor: boolean): TBGRABitmap;
ApplyCanCreateNewnull152 function ApplyCanCreateNew(ASource: TBGRABitmap; AReverse: boolean; AXor: boolean): TBGRABitmap;
Compressnull153 function Compress: boolean;
154 destructor Destroy; override;
UsedMemorynull155 function UsedMemory: int64;
156 property ChangeRect: TRect read FChangeRect;
157 property IsIdentity: boolean read GetIsIdentity;
158 property SizeBefore: TSize read FSizeBefore;
159 property SizeAfter: TSize read FSizeAfter;
160 end;
161
162 type
163 { TStoredImage }
164
165 TStoredImage = class(TImageDiff)
166 public
GetBitmapnull167 function GetBitmap: TBGRABitmap;
168 end;
169
170 { TStoredLayer }
171
172 TStoredLayer = class(TStoredImage)
173 private
GetIdnull174 function GetId: integer;
GetOffsetnull175 function GetOffset: TPoint;
176 protected
177 FInfo: TLayerInfo;
178 FIndex: integer;
179 FOriginalData: TMemoryStream;
180 FOriginalBitmapStored: boolean;
181 FOriginalRenderStatus: TOriginalRenderStatus;
182 FOriginalMatrix: TAffineMatrix;
183 FOriginalDraft: boolean;
184 FOriginalGuid: TGuid;
185 FRegistryData: TMemoryStream;
186 public
187 constructor Create(ALayeredImage: TBGRALayeredBitmap; AIndex: integer);
188 constructor Create(ALayeredImage: TBGRALayeredBitmap; AIndex: integer;
189 AAlwaysStoreBitmap: boolean);
190 destructor Destroy; override;
191 procedure Restore(ALayeredImage: TBGRALayeredBitmap);
192 procedure Replace(ALayeredImage: TBGRALayeredBitmap);
193 property LayerIndex: integer read FIndex;
194 property LayerId: integer read GetId;
195 property Offset: TPoint read GetOffset;
196 end;
197
198 implementation
199
200 uses Math, BGRALzpCommon, UFileSystem;
201
202 { TComposedImageDifference }
203
TComposedImageDifference.GetCountnull204 function TComposedImageDifference.GetCount: integer;
205 begin
206 result := FDiffs.Count;
207 end;
208
GetItemnull209 function TComposedImageDifference.GetItem(AIndex: integer): TCustomImageDifference;
210 begin
211 result := FDiffs[AIndex];
212 end;
213
GetTotalCountnull214 function TComposedImageDifference.GetTotalCount: integer;
215 var
216 i: Integer;
217 begin
218 result := 0;
219 for i := 0 to FDiffs.Count-1 do
220 if FDiffs[i] is TComposedImageDifference then
221 inc(result, TComposedImageDifference(FDiffs[i]).TotalCount)
222 else
223 inc(result, FDiffs[i].GetCost);
224 end;
225
GetIsIdentitynull226 function TComposedImageDifference.GetIsIdentity: boolean;
227 var
228 i: Integer;
229 begin
230 if FAgglutinate then exit(false);
231 for i := 0 to FDiffs.Count-1 do
232 if not FDiffs[i].GetIsIdentity then exit(false);
233 exit(true);
234 end;
235
TComposedImageDifference.GetImageDifferenceKindnull236 function TComposedImageDifference.GetImageDifferenceKind: TImageDifferenceKind;
237 var
238 i: Integer;
239 begin
240 result := idkChangeStack;
241 for i := 0 to FDiffs.Count-1 do
242 case FDiffs[i].GetImageDifferenceKind of
243 idkChangeImageAndSelection: result := idkChangeImageAndSelection;
244 idkChangeSelection: if result in[idkChangeImage,idkChangeImageAndSelection] then
245 result := idkChangeImageAndSelection
246 else result := idkChangeSelection;
247 idkChangeImage: if result in[idkChangeImageAndSelection,idkChangeSelection] then
248 result := idkChangeImageAndSelection
249 else result := idkChangeImage;
250 end;
251 end;
252
TComposedImageDifference.GetChangingBoundsnull253 function TComposedImageDifference.GetChangingBounds: TRect;
254 var
255 i: Integer;
256 r: TRect;
257 begin
258 result:= EmptyRect;
259 for i := 0 to FDiffs.Count-1 do
260 begin
261 r := FDiffs[i].GetChangingBounds;
262 if not IsRectEmpty(r) then
263 begin
264 if IsRectEmpty(result) then result:= r
265 else UnionRect(result, result,r);
266 end;
267 end;
268 end;
269
GetChangingBoundsDefinednull270 function TComposedImageDifference.GetChangingBoundsDefined: boolean;
271 var
272 i: Integer;
273 begin
274 for i := 0 to FDiffs.Count-1 do
275 if not FDiffs[i].GetChangingBoundsDefined then exit(false);
276 exit(true);
277 end;
278
279 constructor TComposedImageDifference.Create(AAgglutinate: boolean);
280 begin
281 FDiffs := TImageDifferenceList.Create;
282 FAgglutinate:= AAgglutinate;
283 end;
284
285 procedure TComposedImageDifference.ReleaseDiffs;
286 begin
287 FDiffs.FreeObjects:= false;
288 FDiffs.Clear;
289 FDiffs.FreeObjects:= true;
290 end;
291
292 procedure TComposedImageDifference.StopAgglutinate;
293 begin
294 FAgglutinate:= false;
295 end;
296
297 destructor TComposedImageDifference.Destroy;
298 begin
299 FDiffs.Free;
300 inherited Destroy;
301 end;
302
TryCompressnull303 function TComposedImageDifference.TryCompress: boolean;
304 var
305 i: Integer;
306 begin
307 for i := 0 to FDiffs.Count-1 do
308 if FDiffs[i].TryCompress then exit(true);
309 exit(false);
310 end;
311
UsedMemorynull312 function TComposedImageDifference.UsedMemory: int64;
313 var
314 i: Integer;
315 begin
316 result := 0;
317 for i := 0 to FDiffs.Count-1 do
318 inc(result, FDiffs[i].UsedMemory);
319 end;
320
321 procedure TComposedImageDifference.Add(ADiff: TCustomImageDifference);
322 begin
323 FDiffs.Add(ADiff);
324 end;
325
326 procedure TComposedImageDifference.AddRange(AComposed: TComposedImageDifference);
327 var
328 i: Integer;
329 begin
330 for i:= 0 to AComposed.Count-1 do
331 Add(AComposed.FDiffs[i]);
332 end;
333
334 procedure TComposedImageDifference.ApplyTo(AState: TState);
335 var
336 i: Integer;
337 begin
338 for i := 0 to FDiffs.Count-1 do
339 FDiffs[i].ApplyTo(AState);
340 end;
341
342 procedure TComposedImageDifference.UnapplyTo(AState: TState);
343 var
344 i: Integer;
345 begin
346 for i := FDiffs.Count-1 downto 0 do
347 FDiffs[i].UnapplyTo(AState);
348 end;
349
350 procedure TComposedImageDifference.Clear;
351 begin
352 FDiffs.Clear;
353 end;
354
355 procedure TComposedImageDifference.Delete(AIndex: integer);
356 begin
357 FDiffs.Delete(AIndex);
358 end;
359
360 procedure TComposedImageDifference.DeleteFrom(AIndex: integer);
361 var
362 i: Integer;
363 begin
364 for i := Count-1 downto AIndex do
365 Delete(i);
366 end;
367
GetLastnull368 function TComposedImageDifference.GetLast: TCustomImageDifference;
369 begin
370 if Count = 0 then result := nil
371 else result := FDiffs[Count-1];
372 end;
373
TComposedImageDifference.ToStringnull374 function TComposedImageDifference.ToString: ansistring;
375 var
376 i: Integer;
377 begin
378 Result:= '[';
379 for i := 0 to Count-1 do
380 begin
381 if i <> 0 then result += ', ';
382 result += FDiffs[i].ToString;
383 end;
384 result += ']';
385 end;
386
387 { TStateDifference }
388
TryCompressnull389 function TStateDifference.TryCompress: boolean;
390 begin
391 result := false;
392 end;
393
UsedMemorynull394 function TStateDifference.UsedMemory: int64;
395 begin
396 result := 0;
397 end;
398
ToStringnull399 function TStateDifference.ToString: ansistring;
400 begin
401 Result:= ClassName;
402 end;
403
404 { TCustomImageDifference }
405
GetIsIdentitynull406 function TCustomImageDifference.GetIsIdentity: boolean;
407 begin
408 result := false;
409 end;
410
GetImageDifferenceKindnull411 function TCustomImageDifference.GetImageDifferenceKind: TImageDifferenceKind;
412 begin
413 result := idkChangeImageAndSelection;
414 end;
415
TCustomImageDifference.GetChangingBoundsnull416 function TCustomImageDifference.GetChangingBounds: TRect;
417 begin
418 result := rect(0,0,0,0);
419 end;
420
GetChangingBoundsDefinednull421 function TCustomImageDifference.GetChangingBoundsDefined: boolean;
422 begin
423 result := false;
424 end;
425
GetCostnull426 function TCustomImageDifference.GetCost: integer;
427 begin
428 result := 1;
429 end;
430
431 constructor TCustomImageDifference.Create(AFromState: TState; AToState: TState);
432 begin
433 FSavedBefore:= AFromState.saved;
434 FSavedAfter:= AToState.saved;
435 end;
436
437 constructor TCustomImageDifference.Create(AFromState: TState);
438 begin
439 FSavedBefore:= AFromState.saved;
440 FSavedAfter:= false;
441 end;
442
443 constructor TCustomImageDifference.Create(ASavedBefore: boolean;
444 ASavedAfter: boolean);
445 begin
446 FSavedBefore := ASavedBefore;
447 FSavedAfter := ASavedAfter;
448 end;
449
450 procedure TCustomImageDifference.ApplyTo(AState: TState);
451 begin
452 AState.saved:= FSavedAfter;
453 end;
454
455 procedure TCustomImageDifference.UnapplyTo(AState: TState);
456 begin
457 AState.saved:= FSavedBefore;
458 end;
459
460 {*********** Layer info *************}
461
462 procedure ApplyLayerInfo(AInfo: TLayerInfo; ALayeredBitmap: TBGRALayeredBitmap; AIndex: integer);
463 begin
464 if (AIndex < 0) or (AIndex >= ALayeredBitmap.NbLayers) then
465 raise exception.Create('Out of bounds');
466 ALayeredBitmap.LayerUniqueId[AIndex] := AInfo.Id;
467 ALayeredBitmap.LayerVisible[AIndex] := AInfo.Visible;
468 ALayeredBitmap.BlendOperation[AIndex] := AInfo.BlendOp;
469 ALayeredBitmap.LayerName[AIndex] := AInfo.Name;
470 ALayeredBitmap.LayerOpacity[AIndex] := AInfo.Opactiy;
471 if ALayeredBitmap.LayerOriginalGuid[AIndex] = GUID_NULL then
472 ALayeredBitmap.LayerOffset[AIndex] := AInfo.Offset;
473 end;
474
GetLayerInfonull475 function GetLayerInfo(ALayeredBitmap: TBGRALayeredBitmap; AIndex: integer): TLayerInfo;
476 begin
477 if (AIndex < 0) or (AIndex >= ALayeredBitmap.NbLayers) then
478 raise exception.Create('Out of bounds');
479 result.Id := ALayeredBitmap.LayerUniqueId[AIndex];
480 result.Visible := ALayeredBitmap.LayerVisible[AIndex];
481 result.BlendOp := ALayeredBitmap.BlendOperation[AIndex];
482 result.Name := ALayeredBitmap.LayerName[AIndex];
483 result.Opactiy := ALayeredBitmap.LayerOpacity[AIndex];
484 result.Offset := ALayeredBitmap.LayerOffset[AIndex];
485 end;
486
487 {*********** Inversible **************}
488
GetInverseActionnull489 function GetInverseAction(AAction: TInversibleAction): TInversibleAction;
490 begin
491 case AAction of
492 iaRotateCW: result := iaRotateCCW;
493 iaRotateCCW: result := iaRotateCW;
494 else
495 result := AAction;
496 end;
497 end;
498
CanCombineInversibleActionnull499 function CanCombineInversibleAction(AAction1, AAction2: TInversibleAction; out
500 ACombined: TInversibleAction): boolean;
501 begin
502 result := true;
503 ACombined:= iaHorizontalFlip; //some default value
504 case AAction1 of
505 iaSwapRedBlue: result := false;
506 iaRotate180: case AAction2 of
507 iaRotateCW: ACombined:= iaRotateCCW;
508 iaRotateCCW: ACombined := iaRotateCW;
509 iaHorizontalFlip: ACombined := iaVerticalFlip;
510 iaVerticalFlip: ACombined:= iaHorizontalFlip;
511 else result := false;
512 end;
513 iaHorizontalFlip: case AAction2 of
514 iaVerticalFlip: ACombined:= iaRotate180;
515 else result := false;
516 end;
517 iaVerticalFlip: case AAction2 of
518 iaHorizontalFlip: ACombined:= iaRotate180;
519 else result := false;
520 end;
521 iaRotateCW: case AAction2 of
522 iaRotateCW: ACombined:= iaRotate180;
523 iaRotate180: ACombined:= iaRotateCCW;
524 else result := false;
525 end;
526 iaRotateCCW: case AAction2 of
527 iaRotateCCW: ACombined:= iaRotate180;
528 iaRotate180: ACombined:= iaRotateCW;
529 else result := false;
530 end;
531 else
532 result := false;
533 end;
534 end;
535
536 { TImageDiff }
537
GetIsIdentitynull538 function TImageDiff.GetIsIdentity: boolean;
539 begin
540 result := (FSizeBefore.cx = FSizeAfter.cx) and (FSizeBefore.cy = FSizeAfter.cy)
541 and ChangeRect.IsEmpty;
542 end;
543
544 procedure TImageDiff.DiscardFile;
545 begin
546 if FSavedFilename <> '' then
547 begin
548 try
549 if FileManager.FileExists(FSavedFilename) then
550 FileManager.DeleteFile(FSavedFilename);
551 except on ex:exception do begin end;
552 end;
553 FSavedFilename:= '';
554 end;
555 end;
556
557 procedure TImageDiff.Decompress;
558 var
559 stream: TStream;
560 begin
561 if (FImageBackup = nil) and (FSavedFilename <> '') then
562 begin
563 stream := nil;
564 FImageBackup := TImageBackup.Create;
565 try
566 stream := FileManager.CreateFileStream(FSavedFilename,fmOpenRead or fmShareDenyWrite);
567 FImageBackup.LoadFromStream(stream);
568 except
569 end;
570 stream.free;
571 end;
572 end;
573
574 procedure TImageDiff.Init(Image1, Image2: TBGRABitmap; AGrayscale: boolean);
575 var r: TRect;
576 begin
577 r := rect(0,0,0,0);
578 if Assigned(image1) then
579 begin
580 r.Right := image1.Width;
581 r.Bottom := image1.Height;
582 end;
583 if Assigned(image2) then
584 begin
585 r.Right := max(r.Right, image2.Width);
586 r.Bottom := max(r.Bottom, image2.Height);
587 end;
588 Init(Image1, Image2, AGrayscale, r);
589 end;
590
591 procedure TImageDiff.Init(Image1, Image2: TBGRABitmap; AGrayscale: boolean;
592 AChangeRect: TRect);
593 begin
594 if Image1 = nil then FSizeBefore := Size(0, 0) else
595 FSizeBefore := Size(Image1.Width, Image1.Height);
596 if Image2 = nil then FSizeAfter := Size(0, 0) else
597 FSizeAfter := Size(Image2.Width, Image2.Height);
598
599 FImageBackup := TImageBackup.Create(Image1, Image2, AGrayscale, AChangeRect);
600 FChangeRect := FImageBackup.Bounds;
601 FGrayscale := AGrayscale;
602 end;
603
CreateNewnull604 function TImageDiff.CreateNew(AWidth, AHeight: integer): TBGRABitmap;
605 begin
606 if FGrayscale then
607 result := TBGRABitmap.Create(AWidth, AHeight, BGRABlack)
608 else
609 result := TBGRABitmap.Create(AWidth, AHeight);
610 end;
611
612 constructor TImageDiff.Create(Image1: TBGRABitmap; AGrayscale: boolean);
613 begin
614 Init(Image1, nil, AGrayscale);
615 end;
616
617 constructor TImageDiff.Create(Image1: TBGRABitmap; AGrayscale: boolean;
618 AChangeRect: TRect);
619 begin
620 Init(Image1, nil, AGrayscale, AChangeRect);
621 end;
622
623 constructor TImageDiff.Create(Image1, Image2: TBGRABitmap; AGrayscale: boolean);
624 begin
625 Init(Image1, Image2, AGrayscale);
626 end;
627
628 constructor TImageDiff.Create(Image1, Image2: TBGRABitmap; AGrayscale: boolean;
629 AChangeRect: TRect);
630 begin
631 Init(Image1, Image2, AGrayscale, AChangeRect);
632 end;
633
634 procedure TImageDiff.ApplyInPlace(ADest: TBGRABitmap; AXor: boolean);
635 begin
636 if ADest = nil then raise exception.Create('Unexpected nil reference');
637 Decompress;
638 if Assigned(FImageBackup) then
639 FImageBackup.Restore(ADest, ChangeRect, AXor)
640 else
641 begin
642 if not AXor then
643 ADest.EraseRect(ChangeRect, 255);
644 end;
645 end;
646
ApplyInNewnull647 function TImageDiff.ApplyInNew(ASource: TBGRABitmap; AReverse: boolean; AXor: boolean): TBGRABitmap;
648 var
649 destSize: TSize;
650 begin
651 if (self = nil) or IsIdentity then
652 begin
653 if ASource = nil then
654 result := nil
655 else
656 result := ASource.Duplicate as TBGRABitmap
657 end
658 else
659 begin
660 if AReverse then destSize := FSizeBefore else
661 destSize := FSizeAfter;
662
663 if (destSize.cx = 0) or (destSize.cy = 0) then
664 result := nil
665 else
666 begin
667 result := CreateNew(destSize.cx, destSize.cy);
668 if ASource <> nil then
669 result.PutImage(0, 0, ASource, dmSet);
670 ApplyInPlace(result, AXor);
671 end;
672 end;
673 end;
674
ApplyCanCreateNewnull675 function TImageDiff.ApplyCanCreateNew(ASource: TBGRABitmap; AReverse: boolean; AXor: boolean): TBGRABitmap;
676 begin
677 if (self = nil) or IsIdentity then exit(ASource); //keep
678
679 if (ASource = nil) or
680 ((FSizeAfter.cx <> FSizeBefore.cx) or
681 (FSizeAfter.cy <> FSizeBefore.cy)) then
682 exit(ApplyInNew(ASource, AReverse, AXor))
683 else
684 begin
685 ApplyInPlace(ASource, AXor);
686 exit(ASource);
687 end;
688 end;
689
Compressnull690 function TImageDiff.Compress: boolean;
691 var
692 savedFile: TStream;
693 begin
694 if Assigned(FImageBackup) and (FSavedFilename = '') then
695 begin
696 FSavedFilename := GetTempFileName;
697 try
698 savedFile := FileManager.CreateFileStream(FSavedFilename,fmCreate);
699 try
700 FImageBackup.SaveToStream(savedFile);
701 FreeAndNil(FImageBackup);
702 result := true;
703 finally
704 savedFile.Free;
705 end;
706 except
707 on ex: exception do
708 begin
709 if FileManager.FileExists(FSavedFilename) then FileManager.DeleteFile(FSavedFilename);
710 FSavedFilename := '';
711 result := false;
712 end;
713 end;
714 end else
715 if Assigned(FImageBackup) then
716 begin
717 FreeAndNil(FImageBackup);
718 result := true;
719 end else
720 result := false;
721 end;
722
723 destructor TImageDiff.Destroy;
724 begin
725 FImageBackup.Free;
726 DiscardFile;
727 inherited Destroy;
728 end;
729
TImageDiff.UsedMemorynull730 function TImageDiff.UsedMemory: int64;
731 begin
732 if Assigned(FImageBackup) then result := FImageBackup.UsedMemory
733 else result := 0;
734 end;
735
736 { TStoredImage }
737
GetBitmapnull738 function TStoredImage.GetBitmap: TBGRABitmap;
739 begin
740 result := ApplyInNew(nil, true, false);
741 end;
742
743 { TStoredLayer }
744
TStoredLayer.GetIdnull745 function TStoredLayer.GetId: integer;
746 begin
747 result := FInfo.Id;
748 end;
749
TStoredLayer.GetOffsetnull750 function TStoredLayer.GetOffset: TPoint;
751 begin
752 result := FInfo.Offset;
753 end;
754
755 constructor TStoredLayer.Create(ALayeredImage: TBGRALayeredBitmap;
756 AIndex: integer);
757 var
758 {%H-}orig: TBGRALayerCustomOriginal;
759 alwaysStoreBitmap: Boolean;
760 begin
761 alwaysStoreBitmap := false;
762 if (ALayeredImage.LayerOriginalGuid[AIndex]<>GUID_NULL) and
763 ALayeredImage.LayerOriginalKnown[AIndex] then
764 begin
765 try
766 orig := ALayeredImage.LayerOriginal[AIndex];
767 except
768 on ex:exception do
769 alwaysStoreBitmap:= true;
770 end;
771 end;
772 Create(ALayeredImage, AIndex, alwaysStoreBitmap);
773 end;
774
775 constructor TStoredLayer.Create(ALayeredImage: TBGRALayeredBitmap;
776 AIndex: integer; AAlwaysStoreBitmap: boolean);
777 begin
778 FIndex := AIndex;
779 FInfo := GetLayerInfo(ALayeredImage, AIndex);
780 if ALayeredImage.LayerOriginalGuid[AIndex]<>GUID_NULL then
781 begin
782 FOriginalBitmapStored := AAlwaysStoreBitmap or not ALayeredImage.LayerOriginalKnown[AIndex];
783 FOriginalRenderStatus:= ALayeredImage.LayerOriginalRenderStatus[AIndex];
784
785 if not FOriginalBitmapStored then
786 inherited Create(nil, False)
787 else
788 inherited Create(ALayeredImage.LayerBitmap[AIndex], False);
789
790 FOriginalData := TMemoryStream.Create;
791 FOriginalGuid := ALayeredImage.LayerOriginalGuid[AIndex];
792 ALayeredImage.SaveOriginalToStream(FOriginalGuid, FOriginalData);
793 FOriginalMatrix := ALayeredImage.LayerOriginalMatrix[AIndex];
794 FOriginalDraft := ALayeredImage.LayerOriginalRenderStatus[AIndex] in[orsDraft,orsPartialDraft];
795 end else
796 begin
797 inherited Create(ALayeredImage.LayerBitmap[AIndex], False);
798 FOriginalData := nil;
799 end;
800 FRegistryData := TMemoryStream.Create;
801 ALayeredImage.SaveLayerRegistryToStream(AIndex, FRegistryData);
802 end;
803
804 destructor TStoredLayer.Destroy;
805 begin
806 FRegistryData.Free;
807 FOriginalData.Free;
808 inherited Destroy;
809 end;
810
811 procedure TStoredLayer.Restore(ALayeredImage: TBGRALayeredBitmap);
812 var
813 tempIdx, idxOrig: Integer;
814 begin
815 if Assigned(FOriginalData) then
816 begin
817 FOriginalData.Position:= 0;
818 idxOrig := ALayeredImage.IndexOfOriginal(FOriginalGuid);
819 if idxOrig = -1 then
820 idxOrig := ALayeredImage.AddOriginalFromStream(FOriginalData, FOriginalGuid, true);
821
822 if not FOriginalBitmapStored then
823 begin
824 tempIdx := ALayeredImage.AddLayerFromOriginal(ALayeredImage.Original[idxOrig].Guid, FOriginalMatrix);
825 ALayeredImage.RenderLayerFromOriginal(tempIdx, FOriginalDraft);
826 end else
827 begin
828 tempIdx := ALayeredImage.AddOwnedLayer(GetBitmap);
829 ALayeredImage.LayerOffset[tempIdx] := FInfo.Offset;
830 ALayeredImage.LayerOriginalGuid[tempIdx] := ALayeredImage.OriginalGuid[idxOrig];
831 ALayeredImage.LayerOriginalMatrix[tempIdx] := FOriginalMatrix;
832 ALayeredImage.LayerOriginalRenderStatus[tempIdx] := FOriginalRenderStatus;
833 end;
834 end else
835 tempIdx := ALayeredImage.AddOwnedLayer(GetBitmap);
836
837 ApplyLayerInfo(FInfo,ALayeredImage,tempIdx);
838 FRegistryData.Position := 0;
839 ALayeredImage.LoadLayerRegistryFromStream(tempIdx, FRegistryData);
840 ALayeredImage.InsertLayer(FIndex,tempIdx);
841 end;
842
843 procedure TStoredLayer.Replace(ALayeredImage: TBGRALayeredBitmap);
844 var
845 idxOrig: Integer;
846 begin
847 if Assigned(FOriginalData) then
848 begin
849 FOriginalData.Position:= 0;
850 idxOrig := ALayeredImage.IndexOfOriginal(FOriginalGuid);
851 if idxOrig = -1 then
852 idxOrig := ALayeredImage.AddOriginalFromStream(FOriginalData, FOriginalGuid, true);
853 if not FOriginalBitmapStored then
854 begin
855 ALayeredImage.LayerOriginalGuid[FIndex] := ALayeredImage.OriginalGuid[idxOrig];
856 ALayeredImage.LayerOriginalMatrix[FIndex] := FOriginalMatrix;
857 ALayeredImage.RenderLayerFromOriginal(FIndex, FOriginalDraft);
858 end else
859 begin
860 ALayeredImage.SetLayerBitmap(FIndex, GetBitmap, True);
861 ALayeredImage.LayerOffset[FIndex] := FInfo.Offset;
862 ALayeredImage.LayerOriginalGuid[FIndex] := ALayeredImage.OriginalGuid[idxOrig];
863 ALayeredImage.LayerOriginalMatrix[FIndex] := FOriginalMatrix;
864 ALayeredImage.LayerOriginalRenderStatus[FIndex] := FOriginalRenderStatus;
865 end;
866 end else
867 ALayeredImage.SetLayerBitmap(FIndex, GetBitmap, True);
868 ALayeredImage.RemoveUnusedOriginals;
869
870 ApplyLayerInfo(FInfo,ALayeredImage,FIndex);
871 FRegistryData.Position := 0;
872 ALayeredImage.LoadLayerRegistryFromStream(FIndex, FRegistryData);
873 end;
874
875 end.
876
877