| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492 |
- // SPDX-License-Identifier: GPL-3.0-only
- unit UImage;
- {$mode objfpc}{$H+}
- interface
- uses
- Classes, SysUtils, BGRABitmap, BGRABitmapTypes, types,
- UImageState, UStateType, Graphics, BGRALayers, UImageObservation, FPWriteBMP,
- UImageType, UZoom, BGRATransform, BGRALayerOriginal, ULayerAction;
- const
- MaxLayersToAdd = 99;
- MaxImageWidth = 8192;
- MaxImageHeight = 8192;
- MaxLayerNameLength = 255;
- MaxUndoCount = 200;
- MaxUsedMemoryWithoutCompression = 512*1024*1024;
- type
- TLayeredBitmapAndSelection = record
- layeredBitmap: TBGRALayeredBitmap;
- selection: TBGRABitmap;
- selectionLayer: TBGRABitmap;
- end;
- TLazPaintImage = class;
- TOnSelectionMaskChanged = procedure(ASender: TLazPaintImage; const ARect: TRect) of object;
- TOnCurrentLayerIndexChanged = procedure(ASender: TLazPaintImage) of object;
- TOnStackChanged = procedure(ASender: TLazPaintImage; AScrollIntoView: boolean) of object;
- TImageExceptionHandler = procedure(AFunctionName: string; AException: Exception) of object;
- TOnCurrentFilenameChanged = procedure(ASender: TLazPaintImage) of object;
- TOnRenderChanged = procedure(ASender: TLazPaintImage; AInvalidateAll: boolean) of object;
- TOnQueryExitToolHandler = procedure(sender: TLazPaintImage) of object;
- { TLazPaintImage }
- TLazPaintImage = class
- private
- FLazPaintInstance: TObject;
- FZoom: TZoom;
- FActionInProgress: TCustomLayerAction;
- FOnActionProgress: TLayeredActionProgressEvent;
- FOnSelectedLayerIndexChanging: TOnCurrentLayerIndexChanged;
- FOnSelectionMaskChanged: TOnSelectionMaskChanged;
- FOnSelectedLayerIndexChanged: TOnCurrentLayerIndexChanged;
- FOnSizeChanged: TNotifyEvent;
- FOnStackChanged: TOnStackChanged;
- FOnQueryExitToolHandler: TOnQueryExitToolHandler;
- FCurrentState: TImageState;
- FRenderedImage: TBGRABitmap;
- FRenderedImageInvalidated: TRect;
- FOnImageChanged, FOnImageSaving, FOnImageExport: TLazPaintImageObservable;
- FOnImageRenderChanged: TOnRenderChanged;
- FUndoList: TComposedImageDifference;
- FUndoPos: integer;
- FRenderUpdateRectInPicCoord, FRenderUpdateRectInVSCoord: TRect;
- FOnCurrentFilenameChanged: TOnCurrentFilenameChanged;
- FSelectionLayerAfterMask: TBGRABitmap;
- FSelectionLayerAfterMaskOffset: TPoint;
- FSelectionLayerAfterMaskDefined: boolean;
- FDraftOriginal: boolean;
- procedure DiscardSelectionLayerAfterMask;
- function GetDPI: integer;
- function GetIsCursor: boolean;
- function GetIsIconCursor: boolean;
- function GetIsTiff: boolean;
- function GetIsGif: boolean;
- function GetLayerBitmapById(AId: integer): TBGRABitmap;
- function GetLayerGuid(AIndex: integer): TGuid;
- function GetLayerId(AIndex: integer): integer;
- function GetLayerOriginal(AIndex: integer): TBGRALayerCustomOriginal;
- function GetLayerOriginalClass(AIndex: integer): TBGRALayerOriginalAny;
- function GetLayerOriginalDefined(AIndex: integer): boolean;
- function GetLayerOriginalKnown(AIndex: integer): boolean;
- function GetLayerOriginalMatrix(AIndex: integer): TAffineMatrix;
- function GetSelectionLayerEmpty: boolean;
- function GetSelectionMaskBounds: TRect;
- function GetSelectionMaskEmpty: boolean;
- function GetSelectionTransform: TAffineMatrix;
- procedure LayeredActionDone(Sender: TObject);
- procedure LayeredActionProgress({%H-}ASender: TObject; AProgressPercent: integer);
- procedure LayeredSizeChanged(Sender: TObject);
- procedure NeedSelectionLayerAfterMask;
- function GetBlendOperation(AIndex: integer): TBlendOperation;
- function GetCurrentFilenameUTF8: string;
- function GetCurrentLayerVisible: boolean;
- function GetCurrentLayerIndex:integer;
- function GetEmpty: boolean;
- function GetHeight: integer;
- function GetSelectionMask: TBGRABitmap;
- function GetSelectedImageLayer: TBGRABitmap;
- function GetLayerBitmap(AIndex: integer): TBGRABitmap;
- function GetLayerName(AIndex: integer): string;
- function GetLayerOffset(AIndex: integer): TPoint;
- function GetLayerOpacity(AIndex: integer): byte;
- function GetLayerVisible(AIndex: integer): boolean;
- function GetNbLayers: integer;
- function GetRenderedImage: TBGRABitmap;
- function GetSelectedLayerPixel(X, Y: Integer): TBGRAPixel;
- function GetSelectionLayerBounds: TRect;
- function GetWidth: integer;
- function GetZoomFactor: single;
- procedure InvalidateImageDifference(ADiff: TCustomImageDifference);
- procedure OriginalChange({%H-}ASender: TObject;
- AOriginal: TBGRALayerCustomOriginal; var ADiff: TBGRAOriginalDiff);
- procedure OriginalEditingChange({%H-}ASender: TObject;
- {%H-}AOriginal: TBGRALayerCustomOriginal);
- procedure OriginalLoadError({%H-}ASender: TObject; {%H-}AError: string;
- var ARaise: boolean);
- procedure SetBlendOperation(AIndex: integer; AValue: TBlendOperation);
- procedure SetCurrentFilenameUTF8(AValue: string);
- procedure LayeredBitmapReplaced;
- procedure SetDraftOriginal(AValue: boolean);
- procedure SetLayerName(AIndex: integer; AValue: string);
- procedure SetLayerOffset(AIndex: integer; AValue: TPoint);
- procedure SetLayerOpacity(AIndex: integer; AValue: byte);
- procedure SetLayerOriginalMatrix(AIndex: integer; AValue: TAffineMatrix);
- procedure SetLayerVisible(AIndex: integer; AValue: boolean);
- procedure LayerBlendMayChange(AIndex: integer);
- function GetDrawingLayer: TBGRABitmap;
- procedure CompressUndoIfNecessary;
- procedure NotifyException(AFunctionName: string; AException: Exception);
- procedure SetOnActionProgress(AValue: TLayeredActionProgressEvent);
- procedure SetOnSizeChanged(AValue: TNotifyEvent);
- procedure SetSelectionTransform(ATransform: TAffineMatrix);
- procedure SetZoom(AValue: TZoom);
- procedure UpdateIconFileUTF8(AFilename: string; AOutputFilename: string = ''; AExport: boolean = false);
- procedure UpdateTiffFileUTF8(AFilename: string; AOutputFilename: string = ''; AExport: boolean = false);
- procedure UpdateGifFileUTF8(AFilename: string; AOutputFilename: string = ''; AExport: boolean = false);
- procedure ReplaceCurrentSelectionWithoutUndo(const AValue: TBGRABitmap);
- procedure LayerActionNotifyChange({%H-}ASender: TObject; ALayer: TBGRABitmap; ARect: TRect);
- procedure LayerActionDestroy(Sender: TObject);
- procedure LayerActionNotifyUndo({%H-}ASender: TObject; AUndo: TCustomImageDifference; var Owned: boolean);
- procedure ZoomOnCenterQuery(Sender: TObject);
- public
- OnException: TImageExceptionHandler;
- ImageOffset: TPoint;
- CursorHotSpot: TPoint;
- BPP, FrameIndex, FrameCount: integer;
- VisibleArea: TRectF;
- // make copy
- function MakeLayeredBitmapCopy: TBGRALayeredBitmap;
- function MakeLayeredBitmapAndSelectionCopy: TLayeredBitmapAndSelection;
- function MakeBitmapCopy(backgroundColor: TColor): TBitmap;
- function MakeCroppedLayer: TBGRABitmap;
- // undo/redo
- procedure AddUndo(AUndoAction: TCustomImageDifference);
- function CanUndo: boolean;
- function CanRedo: boolean;
- procedure Undo;
- procedure Redo;
- function DoBegin: TComposedImageDifference;
- procedure DoEnd(out ADoFound: boolean; out ASomethingDone: boolean);
- procedure DoEnd(var ACompose: TComposedImageDifference);
- procedure ClearUndo;
- procedure CompressUndo;
- function UsedMemory: int64;
- function CreateAction(AApplyOfsBefore: boolean=false; AApplySelTransformBefore: boolean=false): TLayerAction;
- // invalidating
- procedure ImageMayChange(ARect: TRect; ADiscardSelectionLayerAfterMask: boolean = true);
- procedure ImageMayChangeCompletely;
- procedure LayerMayChange(ALayer: TBGRABitmap; ARect: TRect);
- procedure LayerMayChangeCompletely(ALayer: TBGRABitmap);
- procedure SelectionMaskMayChange(ARect: TRect);
- procedure SelectionMaskMayChangeCompletely;
- procedure RenderMayChange(ARect: TRect; APicCoords: boolean = false; ANotify: boolean = true);
- procedure RenderMayChangeCompletely(ANotify: boolean = true);
- procedure ResetRenderUpdateRect;
- // selection mask
- function SelectionMaskNil: boolean;
- function GetSelectionMaskCenter: TPointF;
- procedure SaveSelectionMaskToFileUTF8(AFilename: string);
- function SelectionMaskReadonly: TBGRABitmap;
- procedure ReleaseEmptySelection;
- // selection layer
- function SelectionLayerReadonly: TBGRABitmap;
- // image layer
- function SetCurrentLayerByIndex(AValue: integer): boolean;
- function SelectLayerContainingPixelAt(APicturePos: TPoint): boolean;
- function CurrentLayerEmpty: boolean;
- function CurrentLayerTransparent: boolean;
- function CurrentLayerEquals(AColor: TBGRAPixel): boolean;
- property CurrentLayerPixel[X,Y: Integer]: TBGRAPixel read GetSelectedLayerPixel;
- procedure SetLayerOffset(AIndex: integer; AValue: TPoint; APrecomputedLayerBounds: TRect);
- function CurrentLayerReadOnly: TBGRABitmap;
- procedure SetLayerRegistry(ALayerIndex: integer; AIdentifier: string; AValue: RawByteString);
- function GetLayerRegistry(ALayerIndex: integer; AIdentifier: string): RawByteString;
- procedure SetRegistry(AIdentifier: string; AValue: RawByteString);
- function GetRegistry(AIdentifier: string): RawByteString;
- function GetLayerIndexById(AId: integer): integer;
- function GetLayerIndexByGuid(AGuid: TGuid): integer;
- procedure AddNewLayer;
- procedure AddNewLayer(AOriginal: TBGRALayerCustomOriginal; AName: string; ABlendOp: TBlendOperation; AMatrix: TAffineMatrix; AOpacity: byte = 255);
- procedure AddNewLayer(ALayer: TBGRABitmap; AName: string; ABlendOp: TBlendOperation; AOpacity: byte = 255);
- procedure AddNewLayer(ALayer: TBGRABitmap; AName: string; AOffset: TPoint; ABlendOp: TBlendOperation; AOpacity: byte = 255);
- procedure DuplicateLayer;
- procedure RasterizeLayer;
- procedure MergeLayerOver;
- procedure MoveLayer(AFromIndex,AToIndex: integer);
- procedure RemoveLayer;
- procedure ClearLayer;
- procedure HorizontalFlip(ALayerIndex: integer); overload;
- procedure VerticalFlip(ALayerIndex: integer); overload;
- // whole image
- procedure Assign(const AValue: TBGRABitmap; AOwned: boolean; AUndoable: boolean;
- ACaption: string = ''; AOpacity: byte = 255); overload;
- procedure Assign(const AValue: TBGRACustomLayeredBitmap; AOwned: boolean; AUndoable: boolean); overload;
- procedure Assign(const AValue: TLayeredBitmapAndSelection; AOwned: boolean; AUndoable: boolean); overload;
- procedure SwapRedBlue;
- procedure LinearNegativeAll;
- procedure NegativeAll;
- procedure HorizontalFlip; overload;
- procedure VerticalFlip; overload;
- procedure RotateCW;
- procedure RotateCCW;
- procedure Rotate180;
- procedure Resample(AWidth, AHeight: integer; filter: TResampleFilter);
- function ApplySmartZoom3: boolean;
- procedure Flatten;
- function FlatImageEquals(ABitmap: TBGRABitmap): boolean;
- function ComputeFlatImage(AFromLayer,AToLayer: integer; ASeparateXorMask: boolean): TBGRABitmap;
- procedure PrepareForRendering;
- procedure Draw(ADest: TBGRABitmap; x,y: integer);
- // input/output
- function DetectImageFormat(AFilename: string): TBGRAImageFormat;
- procedure LoadFromFileUTF8(AFilename: string);
- function AbleToSaveAsUTF8(AFilename: string): boolean;
- function AbleToSaveSelectionAsUTF8(AFilename: string): boolean;
- procedure SaveToFileUTF8(AFilename: string; AExport: boolean = false);
- procedure UpdateMultiImage(AOutputFilename: string = ''; AExport: boolean = false);
- procedure SetSavedFlag(ASavedBPP: integer = 0;
- ASavedFrameIndex: integer = 0;
- ASavedFrameCount: integer = 1;
- AOpening: boolean = false);
- function IsFileModified: boolean;
- procedure SaveOriginalToStream(AStream: TStream);
- function CheckCurrentLayerVisible: boolean;
- function CheckNoAction(ASilent: boolean = false): boolean;
- function CanDuplicateFrame: boolean;
- function CanHaveFrames: boolean;
- procedure ZoomFit;
- property CurrentState: TImageState read FCurrentState;
- property currentFilenameUTF8: string read GetCurrentFilenameUTF8 write SetCurrentFilenameUTF8;
- property CurrentLayerIndex: integer read GetCurrentLayerIndex;
- property SelectionMask: TBGRABitmap read GetSelectionMask;
- property RenderedImage: TBGRABitmap read GetRenderedImage;
- property Width: integer read GetWidth;
- property Height: integer read GetHeight;
- property OnSelectionChanged: TOnSelectionMaskChanged read FOnSelectionMaskChanged write FOnSelectionMaskChanged;
- property OnSelectedLayerIndexChanging: TOnCurrentLayerIndexChanged read FOnSelectedLayerIndexChanging write FOnSelectedLayerIndexChanging;
- property OnSelectedLayerIndexChanged: TOnCurrentLayerIndexChanged read FOnSelectedLayerIndexChanged write FOnSelectedLayerIndexChanged;
- property OnStackChanged: TOnStackChanged read FOnStackChanged write FOnStackChanged;
- property OnImageChanged: TLazPaintImageObservable read FOnImageChanged;
- property OnImageRenderChanged: TOnRenderChanged read FOnImageRenderChanged write FOnImageRenderChanged;
- property OnImageSaving: TLazPaintImageObservable read FOnImageSaving;
- property OnImageExport: TLazPaintImageObservable read FOnImageExport;
- property OnSizeChanged: TNotifyEvent read FOnSizeChanged write SetOnSizeChanged;
- property OnActionProgress: TLayeredActionProgressEvent read FOnActionProgress write SetOnActionProgress;
- property NbLayers: integer read GetNbLayers;
- property Empty: boolean read GetEmpty;
- property SelectionLayerBounds: TRect read GetSelectionLayerBounds;
- property SelectionLayerIsEmpty: boolean read GetSelectionLayerEmpty;
- property SelectionMaskBounds: TRect read GetSelectionMaskBounds;
- property SelectionMaskEmpty: boolean read GetSelectionMaskEmpty;
- property LayerName[AIndex: integer]: string read GetLayerName write SetLayerName;
- property LayerBitmap[AIndex: integer]: TBGRABitmap read GetLayerBitmap;
- property LayerBitmapById[AIndex: integer]: TBGRABitmap read GetLayerBitmapById;
- property LayerOriginal[AIndex: integer]: TBGRALayerCustomOriginal read GetLayerOriginal;
- property LayerOriginalDefined[AIndex: integer]: boolean read GetLayerOriginalDefined;
- property LayerOriginalKnown[AIndex: integer]: boolean read GetLayerOriginalKnown;
- property LayerOriginalClass[AIndex: integer]: TBGRALayerOriginalAny read GetLayerOriginalClass;
- property LayerOriginalMatrix[AIndex: integer]: TAffineMatrix read GetLayerOriginalMatrix write SetLayerOriginalMatrix;
- property LayerId[AIndex: integer]: integer read GetLayerId;
- property LayerGuid[AIndex: integer]: TGuid read GetLayerGuid;
- property LayerVisible[AIndex: integer]: boolean read GetLayerVisible write SetLayerVisible;
- property LayerOpacity[AIndex: integer]: byte read GetLayerOpacity write SetLayerOpacity;
- property LayerOffset[AIndex: integer]: TPoint read GetLayerOffset write SetLayerOffset;
- property BlendOperation[AIndex: integer]: TBlendOperation read GetBlendOperation write SetBlendOperation;
- property CurrentLayerVisible: boolean read GetCurrentLayerVisible;
- property OnQueryExitToolHandler: TOnQueryExitToolHandler read FOnQueryExitToolHandler write FOnQueryExitToolHandler;
- property OnCurrentFilenameChanged: TOnCurrentFilenameChanged read FOnCurrentFilenameChanged write FOnCurrentFilenameChanged;
- property RenderUpdateRectInPicCoord: TRect read FRenderUpdateRectInPicCoord;
- property RenderUpdateRectInVSCoord: TRect read FRenderUpdateRectInVSCoord;
- property SelectionTransform: TAffineMatrix read GetSelectionTransform write SetSelectionTransform;
- property Zoom: TZoom read FZoom write SetZoom;
- property ZoomFactor: single read GetZoomFactor;
- property DraftOriginal: boolean read FDraftOriginal write SetDraftOriginal;
- property IsIconCursor: boolean read GetIsIconCursor;
- property IsCursor: boolean read GetIsCursor;
- property IsTiff: boolean read GetIsTiff;
- property IsGif: boolean read GetIsGif;
- property DPI: integer read GetDPI;
- constructor Create(ALazPaintInstance: TObject);
- destructor Destroy; override;
- end;
- function ComputeAcceptableImageSize(AWidth,AHeight: integer): TSize;
- implementation
- uses UGraph, UResourceStrings, Dialogs,
- BGRAOpenRaster, BGRAPhoxo, BGRAPaintNet, UImageDiff, ULoading,
- BGRAWriteLzp, BGRAUTF8,
- BGRAPalette, BGRAColorQuantization, UFileSystem,
- BGRAThumbnail, BGRAIconCursor, UTiff, LazPaintType,
- BGRALazPaint, BGRAAnimatedGif,
- BGRAGradientScanner, BGRASVGOriginal, Forms;
- function ComputeAcceptableImageSize(AWidth, AHeight: integer): TSize;
- var ratio,newRatio: single;
- begin
- ratio := 1;
- if AWidth > MaxImageWidth then ratio := MaxImageWidth/AWidth;
- if AHeight > MaxImageHeight then
- begin
- newRatio := MaxImageHeight/AHeight;
- if newRatio < ratio then ratio := newRatio;
- end;
- if ratio < 1 then
- begin
- result.cx := round(AWidth*ratio);
- result.cy := round(AHeight*ratio);
- end else
- begin
- result.cx := AWidth;
- result.cy := AHeight;
- end;
- end;
- { TLazPaintImage }
- procedure TLazPaintImage.LayerActionNotifyUndo(ASender: TObject; AUndo: TCustomImageDifference;
- var Owned: boolean);
- begin
- AddUndo(AUndo);
- Owned := true;
- OnImageChanged.NotifyObservers;
- end;
- procedure TLazPaintImage.ZoomOnCenterQuery(Sender: TObject);
- begin
- ImageOffset := Point(0,0);
- end;
- function TLazPaintImage.MakeCroppedLayer: TBGRABitmap;
- var r: TRect;
- cropped: TBGRABitmap;
- ofs: TPoint;
- begin
- ofs := Point(0,0);
- result := DuplicateBitmap(FCurrentState.SelectionLayer);
- if (result <> nil) and (SelectionMask <> nil) then result.ApplyMask(SelectionMask);
- if (result <> nil) and result.Empty then FreeAndNil(result);
- if result = nil then
- begin
- ofs := LayerOffset[CurrentLayerIndex];
- result := DuplicateBitmap(GetSelectedImageLayer);
- if (result <> nil) and (SelectionMask <> nil) then
- result.ApplyMask(SelectionMask, rect(0,0,result.Width,result.Height),
- Point(ofs.X,ofs.Y));
- end;
- if result <> nil then
- begin
- if SelectionMask = nil then
- r := result.GetImageBounds
- else
- begin
- r := SelectionMaskBounds;
- OffsetRect(r, -ofs.x, -ofs.y);
- end;
- if IsRectEmpty(r) then
- FreeAndNil(result)
- else
- begin
- if (r.left <> 0) or (r.top <> 0) or (r.right <> result.Width) or (r.bottom <> result.Height) then
- begin
- cropped := TBGRABitmap.Create(r.Width,r.Height);
- cropped.PutImage(-r.Left, -r.Top, result, dmSet);
- BGRAReplace(result, cropped);
- end;
- end;
- end;
- end;
- function TLazPaintImage.ApplySmartZoom3: boolean;
- var i, idx: integer;
- zoomed: TLayeredBitmapAndSelection;
- ofs: TPoint;
- withOfs: TBGRABitmap;
- begin
- result := false;
- if not CheckNoAction then exit;
- try
- zoomed.layeredBitmap := TBGRALayeredBitmap.Create(Width*3,Height*3);
- for i := 0 to NbLayers-1 do
- begin
- idx := zoomed.layeredBitmap.AddOwnedLayer(FCurrentState.LayerBitmap[i].FilterSmartZoom3(moMediumSmooth) as TBGRABitmap,
- FCurrentState.BlendOperation[i], FCurrentState.LayerOpacity[i]);
- ofs := FCurrentState.LayerOffset[i];
- if (ofs.x <> 0) or (ofs.y <> 0) or (zoomed.layeredBitmap.LayerBitmap[idx].Width <> zoomed.layeredBitmap.Width)
- or (zoomed.layeredBitmap.LayerBitmap[idx].Height <> zoomed.layeredBitmap.Height) then
- begin
- withOfs := TBGRABitmap.Create(zoomed.layeredBitmap.Width, zoomed.layeredBitmap.Height);
- withOfs.PutImage(ofs.x*3,ofs.y*3, zoomed.layeredBitmap.LayerBitmap[idx], dmSet);
- zoomed.layeredBitmap.SetLayerBitmap(idx, withOfs, true);
- end;
- end;
- if SelectionMask <> nil then
- zoomed.selection:= SelectionMask.FilterSmartZoom3(moMediumSmooth) as TBGRABitmap
- else zoomed.Selection := nil;
- if FCurrentState.SelectionLayer <> nil then
- zoomed.selectionLayer := FCurrentState.SelectionLayer.FilterSmartZoom3(moMediumSmooth) as TBGRABitmap
- else
- zoomed.selectionLayer := nil;
- AddUndo(FCurrentState.AssignWithUndo(zoomed.layeredBitmap,true, FCurrentState.SelectedImageLayerIndex, zoomed.selection, zoomed.selectionLayer));
- result := true;
- except on ex: exception do NotifyException('ApplySmartZoom3',ex);
- end;
- ImageMayChangeCompletely;
- SelectionMaskMayChangeCompletely;
- end;
- procedure TLazPaintImage.Resample(AWidth, AHeight: integer; filter: TResampleFilter);
- var quality : TResampleMode;
- backup: TImageState;
- begin
- if not CheckNoAction then exit;
- try
- backup := FCurrentState.Duplicate as TImageState;
- if filter = rfBox then
- quality := rmSimpleStretch
- else
- quality := rmFineResample;
- FCurrentState.Resample(AWidth,AHeight,quality,filter);
- LayeredBitmapReplaced;
- AddUndo(FCurrentState.GetUndoAfterAssign(backup));
- SelectionMaskMayChangeCompletely;
- backup.Free;
- except on ex: exception do NotifyException(RemoveTrail(rsResamplingImage),ex);
- end;
- end;
- function TLazPaintImage.DetectImageFormat(AFilename: string): TBGRAImageFormat;
- var
- s: TStream;
- begin
- s := FileManager.CreateFileStream(AFilename, fmOpenRead);
- try
- result := DetectFileFormat(s, ExtractFileExt(AFilename));
- finally
- s.Free;
- end;
- end;
- function TLazPaintImage.AbleToSaveAsUTF8(AFilename: string): boolean;
- var format: TBGRAImageFormat;
- begin
- format := SuggestImageFormat(AFilename);
- result := (DefaultBGRAImageWriter[format] <> nil) or
- (format in [ifIco,ifCur,ifSvg]);
- if result and (format = ifXPixMap) then
- begin
- if (Width > 256) or (Height > 256) then
- begin
- ShowMessage(rsNotReasonableFormat + ' (> 256x256)');
- result := false;
- end;
- end;
- end;
- function TLazPaintImage.AbleToSaveSelectionAsUTF8(AFilename: string): boolean;
- var ext: string;
- begin
- ext := UTF8LowerCase(ExtractFileExt(AFilename));
- if (ext='.bmp') or (ext='.jpg') or (ext='.jpeg')
- or (ext='.png') or (ext='.pcx') or (ext='.tga') or (ext='.lzp') then
- result := true else
- result := false;
- end;
- procedure TLazPaintImage.SaveToFileUTF8(AFilename: string; AExport: boolean);
- var s: TStream;
- format: TBGRAImageFormat;
- begin
- format := SuggestImageFormat(AFilename);
- if format in[ifOpenRaster,ifPhoxo,ifLazPaint,ifSvg] then
- begin
- s := FileManager.CreateFileStream(AFilename, fmCreate);
- try
- FCurrentState.SaveToStreamAs(s, format);
- finally
- s.Free;
- end;
- if not AExport then SetSavedFlag else OnImageExport.NotifyObservers;
- end else
- begin
- if RenderedImage = nil then exit;
- s := FileManager.CreateFileStream(AFilename, fmCreate);
- try
- RenderedImage.SaveToStreamAs(s, SuggestImageFormat(AFilename));
- finally
- s.Free;
- end;
- if not AExport then
- begin
- if NbLayers = 1 then SetSavedFlag
- else OnImageSaving.NotifyObservers;
- end
- else OnImageExport.NotifyObservers;
- end;
- end;
- procedure TLazPaintImage.UpdateMultiImage(AOutputFilename: string; AExport: boolean);
- begin
- if not FileManager.FileExists(currentFilenameUTF8) then
- begin
- ShowMessage(rsFileNotFound + LineEnding + LineEnding + currentFilenameUTF8);
- exit;
- end;
- if IsIconCursor then
- UpdateIconFileUTF8(currentFilenameUTF8, AOutputFilename, AExport)
- else if IsTiff then
- UpdateTiffFileUTF8(currentFilenameUTF8, AOutputFilename, AExport)
- else if IsGif then
- UpdateGifFileUTF8(currentFilenameUTF8, AOutputFilename, AExport)
- else
- ShowMessage(rsFileExtensionNotSupported);
- end;
- procedure TLazPaintImage.UpdateIconFileUTF8(AFilename: string; AOutputFilename: string; AExport: boolean);
- var
- s: TStream;
- icoCur: TBGRAIconCursor;
- frame: TBGRABitmap;
- newFrameIndex: integer;
- begin
- if bpp = 0 then
- begin
- if RenderedImage.HasTransparentPixels then
- bpp := 32
- else
- bpp := 24;
- end;
- if AOutputFilename = '' then AOutputFilename := AFilename;
- frame := BGRADitherIconCursor(RenderedImage, bpp, daFloydSteinberg) as TBGRABitmap;
- icoCur := TBGRAIconCursor.Create;
- try
- if FileManager.FileExists(AFilename) then
- begin
- s := FileManager.CreateFileStream(AFilename,fmOpenRead or fmShareDenyWrite);
- try
- icoCur.LoadFromStream(s);
- finally
- s.Free;
- end;
- end;
- newFrameIndex := icoCur.Add(frame, bpp, true);
- icoCur.FileType:= SuggestImageFormat(AOutputFilename);
- s := FileManager.CreateFileStream(AOutputFilename,fmCreate);
- try
- icoCur.SaveToStream(s);
- if not AExport then
- SetSavedFlag(bpp, newFrameIndex, icoCur.Count)
- else OnImageExport.NotifyObservers;
- finally
- s.Free;
- end;
- finally
- frame.free;
- icoCur.Free;
- end;
- end;
- procedure TLazPaintImage.UpdateTiffFileUTF8(AFilename: string;
- AOutputFilename: string; AExport: boolean);
- var
- s, sAdded: TStream;
- tiff, addedTiff: TTiff;
- newFrameIndex: integer;
- begin
- if AOutputFilename = '' then AOutputFilename := AFilename;
- tiff := TTiff.Create;
- addedTiff := TTiff.Create;
- sAdded := nil;
- s := nil;
- try
- if FileManager.FileExists(AFilename) then
- begin
- s := FileManager.CreateFileStream(AFilename,fmOpenRead or fmShareDenyWrite);
- if tiff.LoadFromStream(s) <> teNone then
- raise Exception.Create(StringReplace(rsErrorOnOpeningFile,'%1', AFilename, []));
- FreeAndNil(s);
- end;
- sAdded := TMemoryStream.Create;
- RenderedImage.SaveToStreamAs(sAdded, ifTiff);
- sAdded.Position:= 0;
- if addedTiff.LoadFromStream(sAdded) <> teNone then
- raise Exception.Create(rsInternalError);
- FreeAndNil(sAdded);
- if FrameIndex = TImageEntry.NewFrameIndex then
- newFrameIndex := tiff.Move(addedTiff,0)
- else
- begin
- newFrameIndex := FrameIndex;
- if newFrameIndex >= tiff.Count then
- newFrameIndex := tiff.Count
- else
- tiff.Delete(newFrameIndex);
- tiff.Move(addedTiff,0,newFrameIndex);
- end;
- s := FileManager.CreateFileStream(AOutputFilename,fmCreate);
- try
- tiff.SaveToStream(s);
- if not AExport then
- SetSavedFlag(bpp, newFrameIndex, tiff.Count)
- else OnImageExport.NotifyObservers;
- finally
- FreeAndNil(s);
- end;
- finally
- addedTiff.Free;
- sAdded.Free;
- tiff.Free;
- s.Free;
- end;
- end;
- procedure TLazPaintImage.UpdateGifFileUTF8(AFilename: string;
- AOutputFilename: string; AExport: boolean);
- var
- s: TStream;
- gif: TBGRAAnimatedGif;
- newFrameIndex: integer;
- begin
- if AOutputFilename = '' then AOutputFilename := AFilename;
- gif := TBGRAAnimatedGif.Create;
- s := nil;
- try
- if FileManager.FileExists(AFilename) then
- begin
- s := FileManager.CreateFileStream(AFilename,fmOpenRead or fmShareDenyWrite);
- gif.LoadFromStream(s);
- FreeAndNil(s);
- end;
- if FrameIndex = TImageEntry.NewFrameIndex then
- newFrameIndex := gif.AddFullFrame(RenderedImage, gif.AverageDelayMs)
- else
- begin
- newFrameIndex := FrameIndex;
- gif.ReplaceFullFrame(newFrameIndex, RenderedImage, gif.FrameDelayMs[newFrameIndex]);
- end;
- gif.OptimizeFrames;
- s := FileManager.CreateFileStream(AOutputFilename,fmCreate);
- try
- gif.SaveToStream(s);
- if not AExport then
- SetSavedFlag(bpp, newFrameIndex, gif.Count)
- else OnImageExport.NotifyObservers;
- finally
- FreeAndNil(s);
- end;
- finally
- gif.Free;
- s.Free;
- end;
- end;
- procedure TLazPaintImage.LoadFromFileUTF8(AFilename: string);
- var s: TStream;
- ext: string;
- bmp: TBGRABitmap;
- layeredBmp: TBGRACustomLayeredBitmap;
- temp: TBGRALayeredBitmap;
- selIndex: Integer;
- begin
- if not CheckNoAction then exit;
- ext := UTF8LowerCase(ExtractFileExt(AFilename));
- bmp := nil;
- s := nil;
- try
- s := FileManager.CreateFileStream(AFilename, fmOpenRead or fmShareDenyWrite);
- layeredBmp := TryCreateLayeredBitmapReader(ext);
- if Assigned(layeredBmp) then
- begin
- if layeredBmp is TBGRALayeredSVG then
- with TBGRALayeredSVG(layeredBmp) do
- begin
- ContainerWidth := Screen.Width;
- ContainerHeight := Screen.Height;
- DefaultLayerName:= rsLayer;
- end;
- layeredBmp.LoadFromStream(s);
- with ComputeAcceptableImageSize(layeredBmp.Width,layeredBmp.Height) do
- if (cx < layeredBmp.Width) or (cy < layeredBmp.Height) then
- begin
- if not (layeredBmp is TBGRALayeredBitmap) then
- begin
- temp := TBGRALayeredBitmap.Create;
- temp.Assign(layeredBmp, true, true);
- layeredBmp.Free;
- layeredBmp := temp;
- end;
- MessagePopupForever(rsResamplingImage);
- (FLazPaintInstance as TLazPaintCustomInstance).UpdateWindows;
- (layeredBmp as TBGRALayeredBitmap).Resample(cx, cy, rmFineResample);
- MessagePopupHide;
- end;
- CursorHotSpot := Point(0,0);
- if layeredBmp is TBGRALazPaintImage then
- selIndex := TBGRALazPaintImage(layeredBmp).SelectedLayerIndex
- else selIndex := -1;
- Assign(layeredBmp, true, false);
- if selIndex <> -1 then SetCurrentLayerByIndex(selIndex);
- layeredBmp := nil;
- end else
- begin
- bmp := TBGRABitmap.Create;
- bmp.LoadFromStream(s, [lobmpAutoOpaque]);
- Assign(bmp,true,false);
- bmp := nil;
- end;
- finally
- bmp.Free;
- s.Free;
- end;
- end;
- procedure TLazPaintImage.SetSavedFlag(ASavedBPP: integer; ASavedFrameIndex: integer;
- ASavedFrameCount: integer; AOpening: boolean);
- var i: integer;
- begin
- FCurrentState.saved := true;
- self.BPP := ASavedBPP;
- self.FrameIndex := ASavedFrameIndex;
- self.FrameCount := ASavedFrameCount;
- for i := 0 to FUndoList.Count-1 do
- begin
- FUndoList[i].SavedBefore := (i = FUndoPos+1);
- FUndoList[i].SavedAfter := (i = FUndoPos);
- end;
- OnImageChanged.NotifyObservers;
- if (currentFilenameUTF8 <> '') and not AOpening then
- OnImageSaving.NotifyObservers;
- end;
- function TLazPaintImage.IsFileModified: boolean;
- begin
- result := not FCurrentState.saved;
- end;
- function TLazPaintImage.FlatImageEquals(ABitmap: TBGRABitmap): boolean;
- begin
- if ABitmap = nil then result := RenderedImage = nil
- else
- result := ABitmap.Equals(RenderedImage);
- end;
- procedure TLazPaintImage.Flatten;
- begin
- Assign(RenderedImage,False,True);
- end;
- function TLazPaintImage.GetDrawingLayer: TBGRABitmap;
- begin
- if SelectionMaskEmpty then result := GetSelectedImageLayer else
- result := FCurrentState.GetOrCreateSelectionLayer;
- end;
- procedure TLazPaintImage.LayeredBitmapReplaced;
- begin
- FreeAndNil(FRenderedImage);
- if FCurrentState.NbLayers = 0 then
- raise Exception.Create('No layer')
- else
- if FCurrentState.SelectedImageLayerIndex = -1 then
- FCurrentState.SelectedImageLayerIndex := 0;
- if Assigned(FOnStackChanged)then FOnStackChanged(self,True);
- OnImageChanged.NotifyObservers;
- ImageMayChangeCompletely;
- end;
- procedure TLazPaintImage.SetDraftOriginal(AValue: boolean);
- var
- r: TRect;
- begin
- if FDraftOriginal=AValue then Exit;
- FDraftOriginal:=AValue;
- if not FDraftOriginal then
- begin
- r := FCurrentState.LayeredBitmap.RenderOriginalsIfNecessary(FDraftOriginal);
- ImageMayChange(r, false);
- end;
- end;
- procedure TLazPaintImage.AddUndo(AUndoAction: TCustomImageDifference);
- var
- prevAction: TCustomImageDifference;
- prevGroup: TComposedImageDifference;
- prevActionIndex: Integer;
- begin
- if AUndoAction <> nil then
- begin
- if AUndoAction.IsIdentity then
- begin
- AUndoAction.Free;
- exit;
- end;
- prevGroup := FUndoList;
- prevActionIndex := FUndoPos;
- if prevActionIndex > -1 then
- begin
- prevAction := prevGroup[prevActionIndex];
- while (prevAction is TComposedImageDifference) and
- TComposedImageDifference(prevAction).Agglutinate do
- begin
- prevGroup := TComposedImageDifference(prevAction);
- prevActionIndex := prevGroup.Count-1;
- if prevActionIndex>=0 then
- prevAction := prevGroup[prevActionIndex]
- else
- prevAction := nil;
- end;
- end else
- prevAction := nil;
- if assigned(prevAction) then
- begin
- if IsInverseImageDiff(AUndoAction,prevAction) then
- begin
- //writeln('Inverse');
- AUndoAction.Free;
- FCurrentState.saved := prevAction.SavedBefore;
- prevGroup.DeleteFrom(prevActionIndex);
- if prevGroup = FUndoList then FUndoPos := prevActionIndex-1;
- exit;
- end else
- if not prevAction.savedAfter and TryCombineImageDiff(AUndoAction,prevAction) then
- begin
- AUndoAction.Free;
- If prevAction.IsIdentity then
- begin
- //writeln('Inverse (combine)');
- FCurrentState.saved := prevAction.SavedBefore;
- prevGroup.DeleteFrom(prevActionIndex);
- if prevGroup = FUndoList then FUndoPos := prevActionIndex-1;
- end;
- exit;
- end;
- end;
- prevGroup.DeleteFrom(prevActionIndex+1);
- if prevGroup.TotalCount >= MaxUndoCount then
- begin
- if prevGroup = FUndoList then
- begin
- FUndoList.Delete(0);
- FUndoList.Add(AUndoAction);
- end else
- begin
- MessagePopup(rsTooManyActions, 4000);
- AUndoAction.UnapplyTo(FCurrentState);
- InvalidateImageDifference(AUndoAction);
- exit;
- end;
- end else
- begin
- prevGroup.Add(AUndoAction);
- if prevGroup = FUndoList then inc(FUndoPos);
- end;
- //writeln(AUndoAction.ToString);
- FCurrentState.saved := AUndoAction.SavedAfter;
- CompressUndoIfNecessary;
- end;
- end;
- procedure TLazPaintImage.CompressUndoIfNecessary;
- var i: integer;
- begin
- for i := 0 to FUndoList.Count-1 do
- if UsedMemory <= MaxUsedMemoryWithoutCompression then break else
- repeat
- if not FUndoList[i].TryCompress then break;
- until UsedMemory <= MaxUsedMemoryWithoutCompression;
- end;
- procedure TLazPaintImage.NotifyException(AFunctionName: string;
- AException: Exception);
- begin
- if Assigned(OnException) then
- OnException(AFunctionName,AException)
- else
- MessageDlg(AFunctionName,AException.Message,mtError,[mbOk],0);
- end;
- procedure TLazPaintImage.SetOnActionProgress(AValue: TLayeredActionProgressEvent);
- begin
- if FOnActionProgress=AValue then Exit;
- FOnActionProgress:=AValue;
- end;
- procedure TLazPaintImage.SetOnSizeChanged(AValue: TNotifyEvent);
- begin
- if FOnSizeChanged=AValue then Exit;
- FOnSizeChanged:=AValue;
- end;
- procedure TLazPaintImage.SetSelectionTransform(ATransform: TAffineMatrix);
- procedure InvalidateTransformedSelection;
- var selectionChangeRect: TRect;
- begin
- selectionChangeRect := FCurrentState.GetTransformedSelectionMaskBounds;
- if not SelectionLayerIsEmpty then
- ImageMayChange(selectionChangeRect,False);
- if not IsRectEmpty(selectionChangeRect) then
- begin
- InflateRect(selectionChangeRect,1,1);
- RenderMayChange(selectionChangeRect,true);
- end;
- end;
- var
- diff: TSetSelectionTransformDifference;
- begin
- if ATransform <> CurrentState.SelectionTransform then
- begin
- InvalidateTransformedSelection;
- diff := TSetSelectionTransformDifference.Create(FCurrentState, ATransform);
- diff.ApplyTo(FCurrentState);
- InvalidateTransformedSelection;
- AddUndo(diff);
- end;
- end;
- procedure TLazPaintImage.SetZoom(AValue: TZoom);
- begin
- if FZoom=AValue then Exit;
- if Assigned(FZoom) then FZoom.OnCenterQuery:= nil;
- FZoom:=AValue;
- if Assigned(FZoom) then FZoom.OnCenterQuery:=@ZoomOnCenterQuery;
- end;
- procedure TLazPaintImage.SetLayerName(AIndex: integer; AValue: string);
- begin
- AddUndo(FCurrentState.SetLayerName(AIndex,Avalue));
- OnImageChanged.NotifyObservers;
- end;
- procedure TLazPaintImage.SetLayerOffset(AIndex: integer; AValue: TPoint);
- var bounds: TRect;
- begin
- bounds := FCurrentState.LayerBitmap[AIndex].GetImageBounds;
- SetLayerOffset(AIndex,AValue,bounds);
- end;
- procedure TLazPaintImage.SetLayerOpacity(AIndex: integer; AValue: byte);
- begin
- AddUndo(FCurrentState.SetLayerOpacity(AIndex,AValue));
- LayerBlendMayChange(AIndex);
- end;
- procedure TLazPaintImage.SetLayerOriginalMatrix(AIndex: integer;
- AValue: TAffineMatrix);
- var
- prevMatrix: TAffineMatrix;
- r: TRect;
- begin
- if LayerOriginalDefined[AIndex] then
- begin
- if not LayerOriginalKnown[AIndex] then
- raise exception.Create('Unknown original cannot be transformed');
- prevMatrix := LayerOriginalMatrix[AIndex];
- FCurrentState.LayeredBitmap.LayerOriginalMatrix[AIndex] := AValue;
- r := FCurrentState.LayeredBitmap.RenderOriginalsIfNecessary(FDraftOriginal);
- ImageMayChange(r, false);
- AddUndo(FCurrentState.ComputeLayerMatrixDifference(AIndex, prevMatrix, AValue));
- end else
- if not IsAffineMatrixIdentity(AValue) then
- raise exception.Create('Raster layer cannot have a matrix transform');
- end;
- procedure TLazPaintImage.SetLayerVisible(AIndex: integer; AValue: boolean);
- begin
- if not CheckNoAction then exit;
- if not SelectionLayerIsEmpty then
- begin
- MessagePopup(rsMustReleaseSelection,2000);
- exit;
- end;
- AddUndo(FCurrentState.SetLayerVisible(AIndex,AValue));
- LayerBlendMayChange(AIndex);
- OnImageChanged.NotifyObservers; //to show/hide tools
- end;
- function TLazPaintImage.MakeBitmapCopy(backgroundColor: TColor): TBitmap;
- begin
- result := RenderedImage.MakeBitmapCopy(backgroundColor);
- end;
- function TLazPaintImage.CanUndo: boolean;
- begin
- result := FUndoPos >= 0;
- end;
- function TLazPaintImage.CanRedo: boolean;
- begin
- result := FUndoPos < (FUndoList.Count-1);
- end;
- procedure TLazPaintImage.Undo;
- var prevAction: TCustomImageDifference;
- prevGroup: TComposedImageDifference;
- prevActionIndex: Integer;
- begin
- if CanUndo then
- begin
- if not CheckNoAction then exit;
- try
- prevGroup := FUndoList;
- prevActionIndex := FUndoPos;
- prevAction := prevGroup[prevActionIndex];
- while (prevAction is TComposedImageDifference) and
- TComposedImageDifference(prevAction).Agglutinate and
- (TComposedImageDifference(prevAction).Count > 0) do
- begin
- prevGroup := TComposedImageDifference(prevAction);
- prevActionIndex := prevGroup.Count-1;
- prevAction := prevGroup[prevActionIndex];
- end;
- prevAction.UnapplyTo(FCurrentState);
- InvalidateImageDifference(prevAction);
- if prevGroup = FUndoList then
- Dec(FUndoPos)
- else
- prevGroup.Delete(prevActionIndex);
- except
- on ex:Exception do
- begin
- NotifyException('Undo',ex);
- ClearUndo;
- ImageMayChangeCompletely;
- SelectionMaskMayChangeCompletely;
- end;
- end;
- CompressUndoIfNecessary;
- end;
- end;
- procedure TLazPaintImage.InvalidateImageDifference(ADiff: TCustomImageDifference);
- var kind:TImageDifferenceKind;
- begin
- kind := ADiff.Kind;
- case kind of
- idkChangeStack: OnImageChanged.NotifyObservers;
- idkChangeImageAndSelection: begin
- if ADiff.ChangingBoundsDefined then
- begin
- ImageMayChange(ADiff.ChangingBounds);
- SelectionMaskMayChange(ADiff.ChangingBounds);
- end else
- begin
- ImageMayChangeCompletely;
- SelectionMaskMayChangeCompletely;
- end;
- end;
- idkChangeImage:
- if ADiff.ChangingBoundsDefined then
- ImageMayChange(ADiff.ChangingBounds)
- else
- ImageMayChangeCompletely;
- idkChangeSelection:
- if ADiff.ChangingBoundsDefined then
- SelectionMaskMayChange(ADiff.ChangingBounds)
- else
- SelectionMaskMayChangeCompletely;
- end;
- end;
- procedure TLazPaintImage.OriginalChange(ASender: TObject;
- AOriginal: TBGRALayerCustomOriginal; var ADiff: TBGRAOriginalDiff);
- var
- r: TRect;
- begin
- r := FCurrentState.LayeredBitmap.RenderOriginalIfNecessary(AOriginal.Guid, FDraftOriginal);
- if r.IsEmpty then OnImageChanged.NotifyObservers
- else ImageMayChange(r, false);
- if Assigned(ADiff) then
- begin
- AddUndo(TVectorOriginalEmbeddedDifference.Create(CurrentState,AOriginal.Guid,ADiff,r));
- ADiff := nil;
- end;
- end;
- procedure TLazPaintImage.OriginalEditingChange(ASender: TObject;
- AOriginal: TBGRALayerCustomOriginal);
- begin
- OnImageChanged.NotifyObservers;
- end;
- procedure TLazPaintImage.OriginalLoadError(ASender: TObject; AError: string;
- var ARaise: boolean);
- begin
- MessagePopup(rsErrorLoadingOriginal, 4000);
- ARaise := false;
- end;
- procedure TLazPaintImage.Redo;
- var diff: TCustomImageDifference;
- begin
- if CanRedo then
- begin
- if not CheckNoAction then exit;
- try
- inc(FUndoPos);
- diff := FUndoList[FUndoPos];
- diff.ApplyTo(FCurrentState);
- InvalidateImageDifference(diff);
- except
- on ex:Exception do
- begin
- NotifyException('Redo',ex);
- ClearUndo;
- ImageMayChangeCompletely;
- SelectionMaskMayChangeCompletely;
- end;
- end;
- CompressUndoIfNecessary;
- end;
- end;
- function TLazPaintImage.DoBegin: TComposedImageDifference;
- begin
- result := TComposedImageDifference.Create(True);
- AddUndo(result);
- end;
- procedure TLazPaintImage.DoEnd(out ADoFound: boolean; out ASomethingDone: boolean);
- var
- curDiff, insideDiff: TCustomImageDifference;
- curGroup: TComposedImageDifference;
- curIndex: Integer;
- begin
- ADoFound := false;
- ASomethingDone := false;
- if FUndoPos >= 0 then
- begin
- curGroup := FUndoList;
- curIndex := FUndoPos;
- curDiff := curGroup[curIndex];
- if not ((curDiff is TComposedImageDifference) and
- TComposedImageDifference(curDiff).Agglutinate and
- not TComposedImageDifference(curDiff).LockAgglutinate) then
- exit;
- ADoFound:= true;
- ASomethingDone := true;
- repeat
- insideDiff := TComposedImageDifference(curDiff).GetLast;
- if (insideDiff <> nil) and (insideDiff is TComposedImageDifference) and
- TComposedImageDifference(insideDiff).Agglutinate and
- not TComposedImageDifference(insideDiff).LockAgglutinate then
- begin
- curGroup := TComposedImageDifference(curDiff);
- curIndex := curGroup.Count-1;
- curDiff := insideDiff;
- end
- else
- break;
- until false;
- TComposedImageDifference(curDiff).StopAgglutinate;
- if TComposedImageDifference(curDiff).Count = 0 then
- begin
- curGroup.Delete(curIndex);
- if (curGroup = FUndoList) and (FUndoPos >= curIndex) then dec(FUndoPos);
- ASomethingDone := false;
- end;
- end;
- end;
- procedure TLazPaintImage.DoEnd(var ACompose: TComposedImageDifference);
- var
- index: Integer;
- begin
- ACompose.StopAgglutinate;
- if ACompose.Count = 0 then
- begin
- index := FUndoList.IndexOf(ACompose);
- if index <> -1 then
- begin
- FUndoList.Delete(index);
- if FUndoPos >= index then dec(FUndoPos);
- ACompose := nil;
- end;
- end;
- end;
- procedure TLazPaintImage.ClearUndo;
- begin
- try
- FUndoList.Clear;
- FUndoPos := -1;
- except on ex:exception do
- MessagePopup(ex.Message, 4000);
- end;
- end;
- procedure TLazPaintImage.CompressUndo;
- var i: integer;
- begin
- for i := 0 to FUndoList.Count-1 do
- if FUndoList[i].TryCompress then exit;
- end;
- function TLazPaintImage.UsedMemory: int64;
- var i: integer;
- begin
- result := 0;
- if Assigned(FUndoList) then
- for i := 0 to FUndoList.Count-1 do
- result += FUndoList[i].UsedMemory;
- end;
- function TLazPaintImage.CreateAction(AApplyOfsBefore: boolean;
- AApplySelTransformBefore: boolean): TLayerAction;
- begin
- if not CheckNoAction(True) then
- raise exception.Create(rsConflictingActions);
- result := TLayerAction.Create(FCurrentState, AApplyOfsBefore, AApplySelTransformBefore);
- result.OnNotifyChange:= @LayerActionNotifyChange;
- result.OnDestroy:=@LayerActionDestroy;
- result.OnNotifyUndo:=@LayerActionNotifyUndo;
- FActionInProgress := result;
- if Assigned(result.Prediff) then
- InvalidateImageDifference(result.Prediff);
- end;
- procedure TLazPaintImage.ImageMayChange(ARect: TRect;
- ADiscardSelectionLayerAfterMask: boolean);
- begin
- IntersectRect(ARect, ARect, rect(0,0,Width,Height));
- if IsRectEmpty(ARect) then exit;
- if ADiscardSelectionLayerAfterMask then DiscardSelectionLayerAfterMask;
- FRenderUpdateRectInPicCoord := RectUnion(FRenderUpdateRectInPicCoord,ARect);
- FRenderedImageInvalidated := RectUnion(FRenderedImageInvalidated, ARect);
- FCurrentState.DiscardSelectionLayerBounds(ARect);
- OnImageChanged.NotifyObservers;
- end;
- procedure TLazPaintImage.ImageMayChangeCompletely;
- begin
- ImageMayChange(rect(0,0,Width,Height));
- RenderMayChangeCompletely;
- end;
- procedure TLazPaintImage.LayerMayChange(ALayer: TBGRABitmap; ARect: TRect);
- var
- ab: TAffineBox;
- begin
- If ALayer = nil then exit;
- if ALayer = SelectionMask then
- begin
- SelectionMaskMayChange(ARect);
- exit;
- end;
- if ALayer = SelectionLayerReadonly then
- begin
- DiscardSelectionLayerAfterMask;
- ARect.Intersect(SelectionMaskBounds);
- ab := SelectionTransform*TAffineBox.AffineBox(rectF(ARect.Left,ARect.Top,ARect.Right,ARect.Bottom));
- ARect := ab.RectBounds;
- end;
- if ALayer = CurrentLayerReadOnly then
- with LayerOffset[CurrentLayerIndex] do
- OffsetRect(ARect,X,Y);
- ImageMayChange(ARect);
- end;
- procedure TLazPaintImage.LayerMayChangeCompletely(ALayer: TBGRABitmap);
- begin
- If ALayer = nil then exit;
- LayerMayChange(ALayer,rect(0,0,ALayer.Width,ALayer.Height));
- end;
- procedure TLazPaintImage.SelectionMaskMayChange(ARect: TRect);
- var transfRect: TRect;
- ab: TAffineBox;
- begin
- IntersectRect(ARect, ARect, rect(0,0,Width,Height));
- if IsRectEmpty(ARect) then exit;
- DiscardSelectionLayerAfterMask;
- ab := SelectionTransform*TAffineBox.AffineBox(rectF(ARect.Left,ARect.Top,ARect.Right,ARect.Bottom));
- transfRect := ab.RectBounds;
- InflateRect(transfRect,1,1);
- FRenderUpdateRectInPicCoord := RectUnion(FRenderUpdateRectInPicCoord,transfRect);
- FCurrentState.DiscardSelectionMaskBounds(ARect);
- if Assigned(FOnSelectionMaskChanged) then FOnSelectionMaskChanged(self, ARect);
- if FCurrentState.SelectionLayer <> nil then
- ImageMayChange(transfRect, False)
- else
- OnImageChanged.NotifyObservers;
- end;
- procedure TLazPaintImage.SelectionMaskMayChangeCompletely;
- begin
- DiscardSelectionLayerAfterMask;
- FRenderUpdateRectInPicCoord := rect(0,0,Width,Height);
- FCurrentState.DiscardSelectionMaskBoundsCompletely;
- if Assigned(FOnSelectionMaskChanged) then FOnSelectionMaskChanged(self, rect(0,0,Width,Height));
- if FCurrentState.SelectionLayer <> nil then
- LayerMayChange(FCurrentState.SelectionLayer, rect(0,0,Width,Height))
- else
- OnImageChanged.NotifyObservers;
- end;
- procedure TLazPaintImage.RenderMayChange(ARect: TRect; APicCoords: boolean; ANotify: boolean);
- begin
- if APicCoords then
- FRenderUpdateRectInPicCoord := RectUnion(FRenderUpdateRectInPicCoord,ARect)
- else
- FRenderUpdateRectInVSCoord := RectUnion(FRenderUpdateRectInVSCoord,ARect);
- if ANotify and Assigned(OnImageRenderChanged) then
- OnImageRenderChanged(self, false);
- end;
- procedure TLazPaintImage.RenderMayChangeCompletely(ANotify: boolean);
- begin
- FRenderUpdateRectInPicCoord := rect(-MaxLongint div 2,-MaxLongint div 2,MaxLongint div 2,MaxLongint div 2);
- if ANotify and Assigned(OnImageRenderChanged) then
- OnImageRenderChanged(self, true);
- end;
- procedure TLazPaintImage.LayerBlendMayChange(AIndex: integer);
- var r, rSel: TRect;
- begin
- r := FCurrentState.LayerBitmap[AIndex].GetImageBounds;
- with LayerOffset[AIndex] do OffsetRect(r, x,y);
- if (AIndex = CurrentLayerIndex) and not SelectionMaskEmpty then
- begin
- rSel := TRect.Intersect(SelectionMaskBounds, SelectionLayerBounds);
- rSel := SelectionMask.GetImageAffineBounds(SelectionTransform, rSel, false);
- if not rSel.IsEmpty then
- begin
- if r.IsEmpty then r := rSel
- else r := TRect.Union(r, rSel);
- end;
- end;
- ImageMayChange(r);
- end;
- function TLazPaintImage.MakeLayeredBitmapAndSelectionCopy: TLayeredBitmapAndSelection;
- begin
- result.layeredBitmap := FCurrentState.GetLayeredBitmapCopy;
- result.selection := DuplicateBitmap(SelectionMask);
- result.selectionLayer := DuplicateBitmap(FCurrentState.SelectionLayer);
- end;
- {--------------------- Selection --------------------------------------}
- function TLazPaintImage.SelectionMaskNil: boolean;
- begin
- result := (SelectionMask = nil);
- end;
- function TLazPaintImage.GetHeight: integer;
- begin
- result := FCurrentState.Height;
- end;
- function TLazPaintImage.GetSelectedImageLayer: TBGRABitmap;
- begin
- result := FCurrentState.SelectedImageLayer;
- if (result = nil) and (NbLayers > 0) then
- begin
- SetCurrentLayerByIndex(0);
- result := FCurrentState.SelectedImageLayer;
- end;
- end;
- function TLazPaintImage.GetCurrentLayerIndex: integer;
- begin
- result := FCurrentState.SelectedImageLayerIndex;
- if (result = -1) and (NbLayers > 0) then
- begin
- SetCurrentLayerByIndex(0);
- result := 0;
- end;
- end;
- function TLazPaintImage.GetCurrentFilenameUTF8: string;
- begin
- result := FCurrentState.filenameUTF8;
- end;
- function TLazPaintImage.GetCurrentLayerVisible: boolean;
- var idx: integer;
- begin
- idx := CurrentLayerIndex;
- if (idx < 0) or (idx >= NbLayers) then
- result := false
- else
- result := LayerVisible[CurrentLayerIndex];
- end;
- procedure TLazPaintImage.DiscardSelectionLayerAfterMask;
- begin
- if FSelectionLayerAfterMaskDefined then
- begin
- FreeAndNil(FSelectionLayerAfterMask);
- FSelectionLayerAfterMaskOffset := Point(0,0);
- FSelectionLayerAfterMaskDefined := false;
- end;
- end;
- function TLazPaintImage.GetDPI: integer;
- begin
- result := ScreenInfo.PixelsPerInchY;
- end;
- function TLazPaintImage.GetIsCursor: boolean;
- begin
- result := UTF8CompareText(ExtractFileExt(currentFilenameUTF8),'.cur')=0;
- end;
- function TLazPaintImage.GetIsIconCursor: boolean;
- begin
- result := SuggestImageFormat(currentFilenameUTF8) in [ifIco,ifCur];
- end;
- function TLazPaintImage.GetIsTiff: boolean;
- begin
- result := SuggestImageFormat(currentFilenameUTF8) = ifTiff;
- end;
- function TLazPaintImage.GetIsGif: boolean;
- begin
- result := SuggestImageFormat(currentFilenameUTF8) = ifGif;
- end;
- function TLazPaintImage.GetLayerBitmapById(AId: integer): TBGRABitmap;
- begin
- result := FCurrentState.LayerBitmapById[AId];
- end;
- function TLazPaintImage.GetLayerGuid(AIndex: integer): TGuid;
- var
- guidStr: RawByteString;
- begin
- guidStr := GetLayerRegistry(AIndex, 'guid');
- if guidStr<>'' then
- result := StringToGUID(guidStr)
- else
- begin
- CreateGUID(result);
- SetLayerRegistry(AIndex, 'guid', GUIDToString(result));
- end;
- end;
- function TLazPaintImage.GetLayerId(AIndex: integer): integer;
- begin
- result := FCurrentState.LayerId[AIndex];
- end;
- function TLazPaintImage.GetLayerOriginal(AIndex: integer): TBGRALayerCustomOriginal;
- begin
- try
- result := FCurrentState.LayerOriginal[AIndex];
- except
- on ex:exception do
- begin
- MessagePopup(rsErrorLoadingOriginal, 4000);
- result := nil;
- end;
- end;
- end;
- function TLazPaintImage.GetLayerOriginalClass(AIndex: integer): TBGRALayerOriginalAny;
- begin
- result := FCurrentState.LayerOriginalClass[AIndex];
- end;
- function TLazPaintImage.GetLayerOriginalDefined(AIndex: integer): boolean;
- begin
- result := FCurrentState.LayerOriginalDefined[AIndex];
- end;
- function TLazPaintImage.GetLayerOriginalKnown(AIndex: integer): boolean;
- begin
- result := FCurrentState.LayerOriginalKnown[AIndex];
- end;
- function TLazPaintImage.GetLayerOriginalMatrix(AIndex: integer): TAffineMatrix;
- begin
- result := FCurrentState.LayerOriginalMatrix[AIndex];
- end;
- function TLazPaintImage.GetSelectionLayerEmpty: boolean;
- begin
- result := FCurrentState.SelectionLayerEmpty;
- end;
- function TLazPaintImage.GetSelectionMaskBounds: TRect;
- begin
- result := FCurrentState.GetSelectionMaskBounds;
- end;
- function TLazPaintImage.GetSelectionMaskEmpty: boolean;
- begin
- result := FCurrentState.SelectionMaskEmpty;
- end;
- function TLazPaintImage.GetSelectionTransform: TAffineMatrix;
- begin
- result := FCurrentState.SelectionTransform;
- end;
- procedure TLazPaintImage.LayeredActionDone(Sender: TObject);
- begin
- if Assigned(OnActionProgress) then
- OnActionProgress(self, 100);
- end;
- procedure TLazPaintImage.LayeredActionProgress(ASender: TObject;
- AProgressPercent: integer);
- begin
- if Assigned(OnActionProgress) then
- OnActionProgress(self, AProgressPercent);
- end;
- procedure TLazPaintImage.LayeredSizeChanged(Sender: TObject);
- begin
- if Assigned(FOnSizeChanged) then
- FOnSizeChanged(self);
- end;
- procedure TLazPaintImage.NeedSelectionLayerAfterMask;
- var
- bounds,
- boundsAfter: TRect;
- begin
- if not FSelectionLayerAfterMaskDefined then
- begin
- if SelectionMaskEmpty or SelectionLayerIsEmpty then
- FreeAndNil(FSelectionLayerAfterMask)
- else
- begin
- bounds := SelectionLayerBounds;
- FSelectionLayerAfterMask := SelectionLayerReadonly.GetPart(bounds) as TBGRABitmap;
- FSelectionLayerAfterMask.ApplyMask(SelectionMask,
- Rect(0,0,FSelectionLayerAfterMask.Width,FSelectionLayerAfterMask.Height),
- bounds.TopLeft);
- FSelectionLayerAfterMaskOffset := bounds.TopLeft;
- boundsAfter := FSelectionLayerAfterMask.GetImageBounds;
- if IsRectEmpty(boundsAfter) then FreeAndNil(FSelectionLayerAfterMask) else
- if (boundsAfter.left > FSelectionLayerAfterMask.Width div 10) or (boundsAfter.right < FSelectionLayerAfterMask.Width*9 div 10) or
- (boundsAfter.top > FSelectionLayerAfterMask.Height div 10) or (boundsAfter.bottom < FSelectionLayerAfterMask.Height*9 div 10) then
- begin
- BGRAReplace(FSelectionLayerAfterMask, FSelectionLayerAfterMask.GetPart(boundsAfter));
- FSelectionLayerAfterMaskOffset.x += boundsAfter.Left;
- FSelectionLayerAfterMaskOffset.y += boundsAfter.Top;
- end;
- end;
- FSelectionLayerAfterMaskDefined := true;
- end;
- end;
- function TLazPaintImage.GetBlendOperation(AIndex: integer): TBlendOperation;
- begin
- result := FCurrentState.BlendOperation[AIndex];
- end;
- function TLazPaintImage.GetEmpty: boolean;
- begin
- result := (NbLayers = 0) or ((NbLayers = 1) and FCurrentState.LayerBitmap[0].Empty);
- end;
- procedure TLazPaintImage.SetBlendOperation(AIndex: integer;
- AValue: TBlendOperation);
- begin
- AddUndo(FCurrentState.SetBlendOp(AIndex,AValue));
- LayerBlendMayChange(AIndex);
- end;
- procedure TLazPaintImage.SetCurrentFilenameUTF8(AValue: string);
- var oldIsIco: boolean;
- begin
- oldIsIco := IsIconCursor;
- FCurrentState.filenameUTF8 := AValue;
- if oldIsIco <> IsIconCursor then ImageMayChangeCompletely;
- if Assigned(FOnCurrentFilenameChanged) then
- FOnCurrentFilenameChanged(self);
- end;
- function TLazPaintImage.SetCurrentLayerByIndex(AValue: integer): boolean;
- begin
- if AValue = FCurrentState.SelectedImageLayerIndex then exit(true);
- if (AValue < 0) or (AValue >= NbLayers) then exit(false);
- if not CheckNoAction then
- begin
- result := false;
- exit;
- end;
- if assigned(OnSelectedLayerIndexChanging) then OnSelectedLayerIndexChanging(self);
- FCurrentState.SelectedImageLayerIndex := AValue;
- if assigned(OnSelectedLayerIndexChanged) then OnSelectedLayerIndexChanged(self);
- ImageMayChangeCompletely;
- result := true;
- end;
- function TLazPaintImage.SelectLayerContainingPixelAt(APicturePos: TPoint): boolean;
- var
- i: Integer;
- ofs: TPoint;
- begin
- for i := NbLayers-1 downto 0 do
- begin
- ofs := LayerOffset[i];
- if LayerBitmap[i].GetPixel(APicturePos.x - ofs.x, APicturePos.y - ofs.y).alpha > 0 then
- begin
- result := SetCurrentLayerByIndex(i);
- exit;
- end;
- end;
- result := false;
- end;
- procedure TLazPaintImage.SetLayerOffset(AIndex: integer; AValue: TPoint;
- APrecomputedLayerBounds: TRect);
- var
- discardOrig: TDiscardOriginalStateDifference;
- comb: TComposedImageDifference;
- begin
- OffsetRect(APrecomputedLayerBounds, LayerOffset[AIndex].x,LayerOffset[AIndex].y);
- ImageMayChange(APrecomputedLayerBounds);
- OffsetRect(APrecomputedLayerBounds, -LayerOffset[AIndex].x,-LayerOffset[AIndex].y);
- if FCurrentState.LayerOriginalDefined[AIndex] then
- begin
- discardOrig := TDiscardOriginalStateDifference.Create(FCurrentState,AIndex);
- discardOrig.ApplyTo(FCurrentState);
- comb := TComposedImageDifference.Create;
- comb.Add(discardOrig);
- comb.Add(FCurrentState.SetLayerOffset(AIndex,AValue));
- AddUndo(comb);
- end else
- AddUndo(FCurrentState.SetLayerOffset(AIndex,AValue));
- OffsetRect(APrecomputedLayerBounds, LayerOffset[AIndex].x,LayerOffset[AIndex].y);
- ImageMayChange(APrecomputedLayerBounds);
- OffsetRect(APrecomputedLayerBounds, -LayerOffset[AIndex].x,-LayerOffset[AIndex].y);
- end;
- function TLazPaintImage.CheckNoAction(ASilent: boolean): boolean;
- begin
- result := true;
- if FActionInProgress <> nil then
- begin
- FActionInProgress.TryStop;
- if FActionInProgress <> nil then
- begin
- if Assigned(FOnQueryExitToolHandler) then
- FOnQueryExitToolHandler(self);
- if FActionInProgress <> nil then
- begin
- if not ASilent then MessagePopup(rsActionInProgress,2000);
- result := false;
- end;
- end;
- end;
- end;
- function TLazPaintImage.CanDuplicateFrame: boolean;
- begin
- result := IsGif or IsTiff;
- end;
- function TLazPaintImage.CanHaveFrames: boolean;
- begin
- result := IsGif or IsTiff or IsIconCursor;
- end;
- procedure TLazPaintImage.ZoomFit;
- begin
- if Assigned(Zoom) then Zoom.ZoomFit(Width,Height);
- end;
- procedure TLazPaintImage.ResetRenderUpdateRect;
- begin
- FRenderUpdateRectInPicCoord := rect(0,0,0,0);
- FRenderUpdateRectInVSCoord := rect(0,0,0,0);
- end;
- function TLazPaintImage.GetSelectionMask: TBGRABitmap;
- begin
- result := FCurrentState.SelectionMask;
- end;
- function TLazPaintImage.GetLayerBitmap(AIndex: integer): TBGRABitmap;
- begin
- result := FCurrentState.LayerBitmap[AIndex];
- end;
- function TLazPaintImage.GetLayerName(AIndex: integer): string;
- begin
- result := FCurrentState.LayerName[AIndex];
- end;
- function TLazPaintImage.GetLayerOffset(AIndex: integer): TPoint;
- begin
- result := FCurrentState.LayerOffset[AIndex];
- end;
- function TLazPaintImage.GetLayerOpacity(AIndex: integer): byte;
- begin
- result := FCurrentState.LayerOpacity[AIndex];
- end;
- function TLazPaintImage.GetLayerVisible(AIndex: integer): boolean;
- begin
- result := FCurrentState.LayerVisible[AIndex];
- end;
- function TLazPaintImage.GetNbLayers: integer;
- begin
- result := FCurrentState.NbLayers;
- end;
- function TLazPaintImage.GetRenderedImage: TBGRABitmap;
- var
- ofs: TPoint;
- temp: TBGRABitmap;
- rectOutput, rLayer: TRect;
- actualTransformation: TAffineMatrix;
- selectionScanner: TBGRACustomScanner;
- selFilter: TResampleFilter;
- begin
- if (NbLayers = 1) and (LayerOpacity[CurrentLayerIndex] = 255) and
- (LayerOffset[CurrentLayerIndex].X = 0) and (LayerOffset[CurrentLayerIndex].Y = 0) and
- (LayerBitmap[CurrentLayerIndex].Width = Width) and (LayerBitmap[CurrentLayerIndex].Height = Height) and
- LayerVisible[CurrentLayerIndex] and ((SelectionMask = nil) or (SelectionLayerReadonly = nil)) then
- exit(LayerBitmap[CurrentLayerIndex])
- else
- if (FRenderedImage = nil) or ((FRenderedImageInvalidated.Right > FRenderedImageInvalidated.Left) and
- (FRenderedImageInvalidated.Bottom > FRenderedImageInvalidated.Top)) then
- begin
- if FCurrentState = nil then
- begin
- FreeAndNil(FRenderedImage);
- result := nil;
- exit;
- end;
- PrepareForRendering;
- selectionScanner := nil;
- //if there is an overlapping selection, then we must draw it on current layer
- if LayerVisible[CurrentLayerIndex] and (LayerOpacity[CurrentLayerIndex] > 0) and
- (SelectionMask <> nil) and (SelectionLayerReadonly <> nil) then
- begin
- if not SelectionMaskEmpty and not SelectionLayerIsEmpty then
- begin
- if not TBGRABitmap.IsAffineRoughlyTranslation(SelectionTransform, SelectionMaskBounds) then
- begin
- NeedSelectionLayerAfterMask;
- actualTransformation := SelectionTransform*AffineMatrixTranslation(FSelectionLayerAfterMaskOffset.X,FSelectionLayerAfterMaskOffset.Y);
- rectOutput := SelectionMask.GetImageAffineBounds(actualTransformation, FSelectionLayerAfterMask.ClipRect);
- rectOutput.Intersect(rect(0,0,self.Width,self.Height));
- if not rectOutput.IsEmpty then
- begin
- if rectOutput.Width*rectOutput.Height > 640*480 then
- selFilter := rfBox else selFilter := rfCosine;
- selectionScanner := TBGRAAffineBitmapTransform.Create(
- FSelectionLayerAfterMask, false, selFilter);
- TBGRAAffineBitmapTransform(selectionScanner).ViewMatrix := actualTransformation;
- FCurrentState.LayeredBitmap.SelectionScanner := selectionScanner;
- FCurrentState.LayeredBitmap.SelectionRect:= rectOutput;
- FCurrentState.LayeredBitmap.SelectionScannerOffset:= Point(0, 0);
- FCurrentState.LayeredBitmap.SelectionLayerIndex:= CurrentLayerIndex;
- end;
- end else
- begin
- DiscardSelectionLayerAfterMask;
- rectOutput := TRect.Intersect(SelectionLayerBounds, SelectionMaskBounds);
- ofs := Point(round(SelectionTransform[1, 3]), round(SelectionTransform[2, 3]));
- rectOutput.Offset(ofs.x, ofs.y);
- rectOutput.Intersect(rect(0,0,self.Width,self.Height));
- if not IsRectEmpty(rectOutput) then
- begin
- selectionScanner := TBGRATextureMaskScanner.Create(SelectionMask,
- Point(0,0), FCurrentState.SelectionLayer);
- FCurrentState.LayeredBitmap.SelectionScanner := selectionScanner;
- FCurrentState.LayeredBitmap.SelectionRect:= rectOutput;
- FCurrentState.LayeredBitmap.SelectionScannerOffset:= Point(-ofs.x, -ofs.y);
- FCurrentState.LayeredBitmap.SelectionLayerIndex:= CurrentLayerIndex;
- end;
- end;
- end;
- end;
- if (FRenderedImage <> nil) and ((FRenderedImage.Width <> Width) or (FRenderedImage.Height <> Height)) then
- FreeAndNil(FRenderedImage);
- if FRenderedImage = nil then FRenderedImage := TBGRABitmap.Create(Width,Height);
- if IsIconCursor then
- begin
- temp := FCurrentState.ComputeFlatImage(FRenderedImageInvalidated,0,NbLayers-1,True);
- FRenderedImage.PutImage(FRenderedImageInvalidated.Left,FRenderedImageInvalidated.Top, temp, dmSet);
- if temp.XorMask <> nil then
- begin
- FRenderedImage.NeedXorMask;
- FRenderedImage.XorMask.PutImage(FRenderedImageInvalidated.Left,FRenderedImageInvalidated.Top, temp.XorMask, dmSet);
- end else
- FRenderedImage.DiscardXorMask;
- temp.Free;
- end else
- begin
- FRenderedImage.ClipRect := FRenderedImageInvalidated;
- FRenderedImage.DiscardXorMask;
- if (NbLayers = 1) and (FCurrentState.LayeredBitmap.SelectionScanner = nil) then
- begin
- if (LayerOpacity[0] > 0) and LayerVisible[0] then
- begin
- rLayer := RectWithSize(LayerOffset[0].X, LayerOffset[0].Y, LayerBitmap[0].Width, LayerBitmap[0].Height);
- if rLayer.Top > FRenderedImageInvalidated.Top then
- FRenderedImage.EraseRect(FRenderedImageInvalidated.Left, FRenderedImageInvalidated.Top,
- FRenderedImageInvalidated.Right, rLayer.Top, 255);
- if rLayer.Left > FRenderedImageInvalidated.Left then
- FRenderedImage.EraseRect(FRenderedImageInvalidated.Left, rLayer.Top,
- rLayer.Left, rLayer.Bottom, 255);
- FRenderedImage.PutImage(rLayer.Left, rLayer.Top, LayerBitmap[0], dmSet);
- FRenderedImage.ApplyGlobalOpacity(rLayer, LayerOpacity[0]);
- if rLayer.Right < FRenderedImageInvalidated.Right then
- FRenderedImage.EraseRect(rLayer.Right, rLayer.Top,
- FRenderedImageInvalidated.Right, rLayer.Bottom, 255);
- if rLayer.Bottom < FRenderedImageInvalidated.Bottom then
- FRenderedImage.EraseRect(FRenderedImageInvalidated.Left, rLayer.Bottom,
- FRenderedImageInvalidated.Right, FRenderedImageInvalidated.Bottom, 255);
- end else
- FRenderedImage.EraseRect(FRenderedImageInvalidated, 255);
- end else
- begin
- FRenderedImage.FillRect(FRenderedImageInvalidated, BGRAPixelTransparent, dmSet);
- FCurrentState.DrawLayers(FRenderedImage, 0, 0, False, true);
- end;
- FRenderedImage.NoClip;
- end;
- FCurrentState.LayeredBitmap.DiscardSelection;
- selectionScanner.Free;
- FRenderedImageInvalidated := EmptyRect; //up to date
- end;
- result := FRenderedImage;
- end;
- function TLazPaintImage.GetSelectedLayerPixel(X, Y: Integer): TBGRAPixel;
- begin
- result := GetSelectedImageLayer.GetPixel(X,Y);
- end;
- function TLazPaintImage.GetSelectionLayerBounds: TRect;
- begin
- result := FCurrentState.GetSelectionLayerBounds;
- end;
- function TLazPaintImage.GetWidth: integer;
- begin
- result := FCurrentState.Width;
- end;
- function TLazPaintImage.GetZoomFactor: single;
- begin
- if Assigned(Zoom) then result := Zoom.Factor else result := 1;
- end;
- procedure TLazPaintImage.Assign(const AValue: TBGRABitmap; AOwned: boolean; AUndoable: boolean;
- ACaption: string; AOpacity: byte);
- var layeredBmp: TBGRALayeredBitmap;
- mask: TBGRABitmap;
- begin
- if not CheckNoAction then exit;
- CursorHotSpot := AValue.HotSpot;
- layeredBmp := TBGRALayeredBitmap.Create(AValue.Width,AValue.Height);
- if AOwned then
- begin
- layeredBmp.AddOwnedLayer(AValue);
- if Assigned(AValue.XorMask) then
- begin
- mask := AValue.XorMask.Duplicate as TBGRABitmap;
- mask.AlphaFill(255);
- mask.ReplaceColor(BGRABlack,BGRAPixelTransparent);
- layeredBmp.LayerName[layeredBmp.AddOwnedLayer(mask,boXor)] := 'Xor';
- AValue.DiscardXorMask;
- end;
- end
- else
- begin
- layeredBmp.AddLayer(AValue);
- if Assigned(AValue.XorMask) then
- begin
- mask := AValue.XorMask.Duplicate as TBGRABitmap;
- mask.AlphaFill(255);
- mask.ReplaceColor(BGRABlack,BGRAPixelTransparent);
- layeredBmp.LayerName[layeredBmp.AddOwnedLayer(mask,boXor)] := 'Xor';
- end;
- end;
- if ACaption = '' then ACaption := rsLayer+'1';
- layeredBmp.LayerName[0] := ACaption;
- layeredBmp.LayerOpacity[0] := AOpacity;
- Assign(layeredBmp,True,AUndoable);
- end;
- procedure TLazPaintImage.Assign(const AValue: TBGRACustomLayeredBitmap;
- AOwned: boolean; AUndoable: boolean);
- var idx: integer;
- begin
- if not CheckNoAction then exit;
- if AValue.NbLayers = 0 then
- begin
- Assign(TBGRABitmap.Create(AValue.Width,AValue.Height),True,AUndoable);
- if AOwned then AValue.Free;
- exit;
- end;
- if AUndoable then
- begin
- idx := FCurrentState.SelectedImageLayerIndex;
- if idx > AValue.NbLayers-1 then idx := 0;
- AddUndo(FCurrentState.AssignWithUndo(AValue, AOwned, idx, nil, nil));
- ImageMayChangeCompletely;
- SelectionMaskMayChangeCompletely;
- end else
- begin
- FCurrentState.Assign(AValue, AOwned);
- FCurrentState.RemoveSelection;
- FCurrentState.saved := false;
- LayeredBitmapReplaced;
- ImageMayChangeCompletely;
- SelectionMaskMayChangeCompletely;
- ClearUndo;
- end;
- end;
- procedure TLazPaintImage.Assign(const AValue: TLayeredBitmapAndSelection;
- AOwned: boolean; AUndoable: boolean);
- begin
- if not CheckNoAction then exit;
- if AUndoable then
- begin
- AddUndo(FCurrentState.AssignWithUndo(AValue.layeredBitmap,AOwned,FCurrentState.SelectedImageLayerIndex,AValue.selection,AValue.selectionLayer));
- ImageMayChangeCompletely;
- SelectionMaskMayChangeCompletely;
- end
- else
- begin
- with AValue do
- begin
- Assign(layeredBitmap,AOwned,False);
- if not AOwned then
- ReplaceCurrentSelectionWithoutUndo(selection.Duplicate(True) as TBGRABitmap)
- else
- ReplaceCurrentSelectionWithoutUndo(selection);
- FCurrentState.ReplaceSelectionLayer(selectionLayer,AOwned);
- end;
- end;
- OnImageChanged.NotifyObservers;
- end;
- procedure TLazPaintImage.Draw(ADest: TBGRABitmap; x, y: integer);
- var bmp: TBGRABitmap;
- begin
- if (NbLayers = 1) and ((SelectionMask = nil) or (GetSelectedImageLayer = nil)) then
- begin
- if FCurrentState <> nil then
- FCurrentState.DrawLayers(ADest,x,y,IsIconCursor);
- end else
- begin
- bmp := RenderedImage;
- if bmp <> nil then
- if FCurrentState.LinearBlend then
- ADest.PutImage(x,y,bmp,dmLinearBlend)
- else
- ADest.PutImage(x,y,bmp,dmDrawWithTransparency);
- end;
- end;
- procedure TLazPaintImage.AddNewLayer;
- begin
- if not CheckNoAction then exit;
- try
- AddUndo(FCurrentState.AddNewLayer(TBGRABitmap.Create(1,1), '', Point(0,0), boTransparent));
- LayerBlendMayChange(CurrentLayerIndex);
- except on ex: exception do NotifyException('AddNewLayer',ex);
- end;
- OnImageChanged.NotifyObservers;
- end;
- procedure TLazPaintImage.AddNewLayer(AOriginal: TBGRALayerCustomOriginal;
- AName: string; ABlendOp: TBlendOperation; AMatrix: TAffineMatrix; AOpacity: byte);
- begin
- if not CheckNoAction then exit;
- try
- AddUndo(FCurrentState.AddNewLayer(AOriginal, AName, ABlendOp, AMatrix, AOpacity));
- ImageMayChangeCompletely;
- except on ex: exception do NotifyException('AddNewLayer',ex);
- end;
- OnImageChanged.NotifyObservers;
- end;
- procedure TLazPaintImage.AddNewLayer(ALayer: TBGRABitmap; AName: string; ABlendOp: TBlendOperation; AOpacity: byte);
- var temp: TBGRAbitmap;
- begin
- if not CheckNoAction then exit;
- try
- If (ALayer.Width > Width) or (ALayer.Height > Height) then
- begin
- temp := TBGRABitmap.Create(Width,Height);
- temp.PutImage((Width-ALayer.Width) div 2, (Height-ALayer.Height) div 2,ALayer,dmSet);
- ALayer.Free;
- ALayer := temp;
- end;
- AddUndo(FCurrentState.AddNewLayer(ALayer, AName,
- Point((Width - ALayer.Width) div 2, (Height - ALayer.Height) div 2),
- ABlendOp, AOpacity));
- ImageMayChangeCompletely;
- except on ex: exception do NotifyException('AddNewLayer',ex);
- end;
- OnImageChanged.NotifyObservers;
- end;
- procedure TLazPaintImage.AddNewLayer(ALayer: TBGRABitmap; AName: string;
- AOffset: TPoint; ABlendOp: TBlendOperation; AOpacity: byte);
- begin
- if not CheckNoAction then exit;
- try
- AddUndo(FCurrentState.AddNewLayer(ALayer, AName, AOffset, ABlendOp, AOpacity));
- ImageMayChangeCompletely;
- except on ex: exception do NotifyException('AddNewLayer',ex);
- end;
- OnImageChanged.NotifyObservers;
- end;
- procedure TLazPaintImage.DuplicateLayer;
- begin
- if not CheckNoAction then exit;
- try
- AddUndo(FCurrentState.DuplicateLayer);
- LayerBlendMayChange(CurrentLayerIndex);
- OnImageChanged.NotifyObservers;
- except on ex: exception do
- begin
- NotifyException('DuplicateLayer',ex);
- ImageMayChangeCompletely;
- end;
- end;
- end;
- procedure TLazPaintImage.RasterizeLayer;
- begin
- if LayerOriginalDefined[CurrentLayerIndex] then
- try
- AddUndo(FCurrentState.DiscardOriginal(True));
- OnImageChanged.NotifyObservers;
- except on ex: exception do NotifyException('RasterizeLayer',ex);
- end;
- end;
- procedure TLazPaintImage.MergeLayerOver;
- var
- remove: TCustomImageDifference;
- nextId: LongInt;
- begin
- if CurrentLayerIndex = 0 then exit;
- if not CheckNoAction then exit;
- try
- if LayerBitmap[CurrentLayerIndex].Empty then
- begin
- nextId := LayerId[CurrentLayerIndex-1];
- remove := FCurrentState.RemoveLayer;
- if remove is TRemoveLayerStateDifference then
- TRemoveLayerStateDifference(remove).nextActiveLayerId:= nextId;
- AddUndo(remove);
- end else
- AddUndo(FCurrentState.MergerLayerOver(CurrentLayerIndex));
- except on ex: exception do NotifyException('MergeLayerOver',ex);
- end;
- ImageMayChangeCompletely;
- end;
- procedure TLazPaintImage.PrepareForRendering;
- begin
- if FCurrentState <> nil then FCurrentState.PrepareForRendering;
- end;
- function TLazPaintImage.MakeLayeredBitmapCopy: TBGRALayeredBitmap;
- begin
- result := FCurrentState.GetLayeredBitmapCopy;
- end;
- function TLazPaintImage.ComputeFlatImage(AFromLayer, AToLayer: integer;
- ASeparateXorMask: boolean): TBGRABitmap;
- begin
- result := FCurrentState.ComputeFlatImage(AFromLayer,AToLayer,ASeparateXorMask);
- end;
- procedure TLazPaintImage.MoveLayer(AFromIndex, AToIndex: integer);
- begin
- if (AFromIndex < 0) or (AFromIndex >= NbLayers) then
- raise exception.Create('Index out of bounds');
- if AToIndex < 0 then AToIndex := 0;
- if AToIndex >= NbLayers then AToIndex := NbLayers-1;
- if AToIndex = AFromIndex then exit;
- if not CheckNoAction then exit;
- try
- LayerBlendMayChange(AToIndex);
- AddUndo(FCurrentState.MoveLayer(AFromIndex,AToIndex));
- LayerBlendMayChange(AToIndex);
- except on ex: exception do
- begin
- NotifyException('MoveLayer',ex);
- ImageMayChangeCompletely;
- end;
- end;
- end;
- procedure TLazPaintImage.RemoveLayer;
- begin
- if not CheckNoAction then exit;
- try
- AddUndo(FCurrentState.RemoveLayer);
- except on ex: exception do NotifyException('RemoveLayer',ex);
- end;
- ImageMayChangeCompletely;
- end;
- procedure TLazPaintImage.ClearLayer;
- begin
- if not CheckNoAction then exit;
- try
- AddUndo(FCurrentState.ClearLayer);
- except on ex: exception do NotifyException('ClearLayer',ex);
- end;
- ImageMayChangeCompletely;
- end;
- procedure TLazPaintImage.SaveOriginalToStream(AStream: TStream);
- begin
- FCurrentState.LayeredBitmap.SaveOriginalToStream(
- FCurrentState.LayeredBitmap.LayerOriginalGuid[CurrentLayerIndex],
- AStream);
- end;
- procedure TLazPaintImage.SwapRedBlue;
- begin
- if not CheckNoAction then exit;
- try
- AddUndo(FCurrentState.SwapRedBlue);
- except on ex: exception do NotifyException('SwapRedBlue',ex);
- end;
- ImageMayChangeCompletely;
- end;
- procedure TLazPaintImage.LinearNegativeAll;
- begin
- if not CheckNoAction then exit;
- try
- AddUndo(FCurrentState.LinearNegative);
- except on ex: exception do NotifyException('LinearNegativeAll',ex);
- end;
- ImageMayChangeCompletely;
- end;
- procedure TLazPaintImage.NegativeAll;
- begin
- if not CheckNoAction then exit;
- try
- AddUndo(FCurrentState.Negative);
- except on ex: exception do NotifyException('NegativeAll',ex);
- end;
- ImageMayChangeCompletely;
- end;
- procedure TLazPaintImage.HorizontalFlip;
- begin
- if not CheckNoAction then exit;
- try
- AddUndo(FCurrentState.HorizontalFlip);
- except on ex: exception do NotifyException('HorizontalFlip',ex);
- end;
- ImageMayChangeCompletely;
- end;
- procedure TLazPaintImage.HorizontalFlip(ALayerIndex: integer);
- begin
- if not CheckNoAction then exit;
- try
- AddUndo(FCurrentState.HorizontalFlip(ALayerIndex));
- except on ex: exception do NotifyException('HorizontalFlip',ex);
- end;
- ImageMayChangeCompletely;
- end;
- procedure TLazPaintImage.VerticalFlip;
- begin
- if not CheckNoAction then exit;
- try
- AddUndo(FCurrentState.VerticalFlip);
- except on ex: exception do NotifyException('VerticalFlip',ex);
- end;
- ImageMayChangeCompletely;
- end;
- procedure TLazPaintImage.VerticalFlip(ALayerIndex: integer);
- begin
- if not CheckNoAction then exit;
- try
- AddUndo(FCurrentState.VerticalFlip(ALayerIndex));
- except on ex: exception do NotifyException('VerticalFlip',ex);
- end;
- ImageMayChangeCompletely;
- end;
- procedure TLazPaintImage.RotateCW;
- begin
- if not CheckNoAction then exit;
- try
- AddUndo(FCurrentState.RotateCW);
- except on ex: exception do NotifyException('RotateCW',ex);
- end;
- ImageMayChangeCompletely;
- SelectionMaskMayChangeCompletely;
- end;
- procedure TLazPaintImage.RotateCCW;
- begin
- if not CheckNoAction then exit;
- try
- AddUndo(FCurrentState.RotateCCW);
- except on ex: exception do NotifyException('RotateCCW',ex);
- end;
- ImageMayChangeCompletely;
- SelectionMaskMayChangeCompletely;
- end;
- procedure TLazPaintImage.Rotate180;
- begin
- if not CheckNoAction then exit;
- try
- AddUndo(FCurrentState.Rotate180);
- except on ex: exception do NotifyException('Rotate180',ex);
- end;
- ImageMayChangeCompletely;
- SelectionMaskMayChangeCompletely;
- end;
- function TLazPaintImage.CheckCurrentLayerVisible: boolean;
- begin
- result := CurrentLayerVisible;
- if not result then
- MessagePopup(rsMustShowLayer,2000);
- end;
- procedure TLazPaintImage.ReplaceCurrentSelectionWithoutUndo(const AValue: TBGRABitmap);
- begin
- if FCurrentState.SelectionMask = AValue then exit;
- FCurrentState.SelectionMask.Free;
- FCurrentState.SelectionMask := AValue;
- SelectionMaskMayChangeCompletely;
- end;
- procedure TLazPaintImage.LayerActionNotifyChange(ASender: TObject;
- ALayer: TBGRABitmap; ARect: TRect);
- begin
- LayerMayChange(ALayer, ARect);
- end;
- procedure TLazPaintImage.LayerActionDestroy(Sender: TObject);
- begin
- if FActionInProgress = Sender then
- FActionInProgress := nil;
- end;
- procedure TLazPaintImage.ReleaseEmptySelection;
- begin
- if SelectionMaskEmpty and SelectionLayerIsEmpty then
- FCurrentState.ReplaceSelection(nil,nil);
- end;
- function TLazPaintImage.CurrentLayerEmpty: boolean;
- var
- selLayer: TBGRABitmap;
- begin
- selLayer := GetSelectedImageLayer;
- result := not Assigned(selLayer) or selLayer.Empty;
- end;
- function TLazPaintImage.CurrentLayerTransparent: boolean;
- var
- r: TRect;
- idx: Integer;
- y, x: LongInt;
- p: PBGRAPixel;
- begin
- r := rect(0,0, Width, height);
- idx := CurrentLayerIndex;
- if RectWithSize(LayerOffset[idx].x, LayerOffset[idx].y,
- LayerBitmap[idx].Width, LayerBitmap[idx].Height).Contains(r) then
- begin
- r.Offset(-LayerOffset[idx].x, -LayerOffset[idx].y);
- for y := r.Top to r.Bottom-1 do
- begin
- p := LayerBitmap[idx].ScanLine[y] + r.Left;
- for x := r.Left to r.Right-1 do
- begin
- if p^.alpha <> 255 then exit(true);
- inc(p);
- end;
- end;
- result := false;
- end else
- result := true;
- end;
- function TLazPaintImage.CurrentLayerEquals(AColor: TBGRAPixel): boolean;
- begin
- result := GetSelectedImageLayer.Equals(AColor);
- end;
- function TLazPaintImage.GetSelectionMaskCenter: TPointF;
- begin
- result := ugraph.GetSelectionCenter(SelectionMask);
- end;
- procedure TLazPaintImage.SaveSelectionMaskToFileUTF8(AFilename: string);
- var s: TStream;
- begin
- if SelectionMask = nil then exit;
- try
- s := FileManager.CreateFileStream(AFilename, fmCreate);
- try
- SelectionMask.SaveToStreamAs(s, SuggestImageFormat(AFilename));
- finally
- s.Free;
- end;
- except on ex: exception do NotifyException('SaveSelectionToFile',ex);
- end;
- end;
- function TLazPaintImage.SelectionMaskReadonly: TBGRABitmap;
- begin
- result := SelectionMask;
- end;
- function TLazPaintImage.SelectionLayerReadonly: TBGRABitmap;
- begin
- result := FCurrentState.SelectionLayer;
- end;
- function TLazPaintImage.CurrentLayerReadOnly: TBGRABitmap;
- begin
- result := GetSelectedImageLayer;
- end;
- procedure TLazPaintImage.SetLayerRegistry(ALayerIndex: integer;
- AIdentifier: string; AValue: RawByteString);
- begin
- AddUndo(TSetLayerRegistryDifference.Create(FCurrentState, LayerId[ALayerIndex], AIdentifier, AValue, true));
- end;
- function TLazPaintImage.GetLayerRegistry(ALayerIndex: integer;
- AIdentifier: string): RawByteString;
- begin
- result := FCurrentState.LayeredBitmap.GetLayerRegistry(ALayerIndex, AIdentifier);
- end;
- procedure TLazPaintImage.SetRegistry(AIdentifier: string;
- AValue: RawByteString);
- begin
- AddUndo(TSetImageRegistryDifference.Create(FCurrentState, AIdentifier, AValue, true));
- end;
- function TLazPaintImage.GetRegistry(AIdentifier: string): RawByteString;
- begin
- result := FCurrentState.LayeredBitmap.GetGlobalRegistry(AIdentifier);
- end;
- function TLazPaintImage.GetLayerIndexById(AId: integer): integer;
- begin
- result := FCurrentState.LayeredBitmap.GetLayerIndexFromId(AId);
- end;
- function TLazPaintImage.GetLayerIndexByGuid(AGuid: TGuid): integer;
- var
- guidStr: String;
- i: Integer;
- begin
- guidStr := GUIDToString(AGuid);
- for i := 0 to NbLayers-1 do
- if CompareText(GetLayerRegistry(i, 'guid'),guidStr)=0 then exit(i);
- exit(-1);
- end;
- constructor TLazPaintImage.Create(ALazPaintInstance: TObject);
- begin
- FLazPaintInstance := ALazPaintInstance;
- FCurrentState := TImageState.Create;
- FCurrentState.OnOriginalChange:= @OriginalChange;
- FCurrentState.OnOriginalEditingChange:= @OriginalEditingChange;
- FCurrentState.OnOriginalLoadError:=@OriginalLoadError;
- FCurrentState.OnActionProgress:= @LayeredActionProgress;
- FCurrentState.OnActionDone:=@LayeredActionDone;
- FCurrentState.OnSizeChanged:=@LayeredSizeChanged;
- FRenderUpdateRectInPicCoord := rect(0,0,0,0);
- FRenderUpdateRectInVSCoord := rect(0,0,0,0);
- FOnSelectionMaskChanged := nil;
- FOnSelectedLayerIndexChanged := nil;
- FOnStackChanged := nil;
- FOnImageChanged := TLazPaintImageObservable.Create(self);
- FOnImageSaving := TLazPaintImageObservable.Create(self);
- FOnImageExport := TLazPaintImageObservable.Create(self);
- FUndoList := TComposedImageDifference.Create;
- FUndoPos := -1;
- ImageOffset := Point(0,0);
- FrameIndex := -1;
- FrameCount := 0;
- end;
- destructor TLazPaintImage.Destroy;
- begin
- ClearUndo;
- FUndoList.Free;
- FreeAndNil(FRenderedImage);
- FCurrentState.Free;
- FOnImageChanged.Free;
- FOnImageSaving.Free;
- FOnImageExport.Free;
- FSelectionLayerAfterMask.Free;
- inherited Destroy;
- end;
- initialization
- RegisterPaintNetFormat;
- RegisterOpenRasterFormat;
- RegisterPhoxoFormat;
- RegisterLazPaintFormat;
- BGRAColorQuantizerFactory := TBGRAColorQuantizer;
- end.
|