uimage.pas 79 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492
  1. // SPDX-License-Identifier: GPL-3.0-only
  2. unit UImage;
  3. {$mode objfpc}{$H+}
  4. interface
  5. uses
  6. Classes, SysUtils, BGRABitmap, BGRABitmapTypes, types,
  7. UImageState, UStateType, Graphics, BGRALayers, UImageObservation, FPWriteBMP,
  8. UImageType, UZoom, BGRATransform, BGRALayerOriginal, ULayerAction;
  9. const
  10. MaxLayersToAdd = 99;
  11. MaxImageWidth = 8192;
  12. MaxImageHeight = 8192;
  13. MaxLayerNameLength = 255;
  14. MaxUndoCount = 200;
  15. MaxUsedMemoryWithoutCompression = 512*1024*1024;
  16. type
  17. TLayeredBitmapAndSelection = record
  18. layeredBitmap: TBGRALayeredBitmap;
  19. selection: TBGRABitmap;
  20. selectionLayer: TBGRABitmap;
  21. end;
  22. TLazPaintImage = class;
  23. TOnSelectionMaskChanged = procedure(ASender: TLazPaintImage; const ARect: TRect) of object;
  24. TOnCurrentLayerIndexChanged = procedure(ASender: TLazPaintImage) of object;
  25. TOnStackChanged = procedure(ASender: TLazPaintImage; AScrollIntoView: boolean) of object;
  26. TImageExceptionHandler = procedure(AFunctionName: string; AException: Exception) of object;
  27. TOnCurrentFilenameChanged = procedure(ASender: TLazPaintImage) of object;
  28. TOnRenderChanged = procedure(ASender: TLazPaintImage; AInvalidateAll: boolean) of object;
  29. TOnQueryExitToolHandler = procedure(sender: TLazPaintImage) of object;
  30. { TLazPaintImage }
  31. TLazPaintImage = class
  32. private
  33. FLazPaintInstance: TObject;
  34. FZoom: TZoom;
  35. FActionInProgress: TCustomLayerAction;
  36. FOnActionProgress: TLayeredActionProgressEvent;
  37. FOnSelectedLayerIndexChanging: TOnCurrentLayerIndexChanged;
  38. FOnSelectionMaskChanged: TOnSelectionMaskChanged;
  39. FOnSelectedLayerIndexChanged: TOnCurrentLayerIndexChanged;
  40. FOnSizeChanged: TNotifyEvent;
  41. FOnStackChanged: TOnStackChanged;
  42. FOnQueryExitToolHandler: TOnQueryExitToolHandler;
  43. FCurrentState: TImageState;
  44. FRenderedImage: TBGRABitmap;
  45. FRenderedImageInvalidated: TRect;
  46. FOnImageChanged, FOnImageSaving, FOnImageExport: TLazPaintImageObservable;
  47. FOnImageRenderChanged: TOnRenderChanged;
  48. FUndoList: TComposedImageDifference;
  49. FUndoPos: integer;
  50. FRenderUpdateRectInPicCoord, FRenderUpdateRectInVSCoord: TRect;
  51. FOnCurrentFilenameChanged: TOnCurrentFilenameChanged;
  52. FSelectionLayerAfterMask: TBGRABitmap;
  53. FSelectionLayerAfterMaskOffset: TPoint;
  54. FSelectionLayerAfterMaskDefined: boolean;
  55. FDraftOriginal: boolean;
  56. procedure DiscardSelectionLayerAfterMask;
  57. function GetDPI: integer;
  58. function GetIsCursor: boolean;
  59. function GetIsIconCursor: boolean;
  60. function GetIsTiff: boolean;
  61. function GetIsGif: boolean;
  62. function GetLayerBitmapById(AId: integer): TBGRABitmap;
  63. function GetLayerGuid(AIndex: integer): TGuid;
  64. function GetLayerId(AIndex: integer): integer;
  65. function GetLayerOriginal(AIndex: integer): TBGRALayerCustomOriginal;
  66. function GetLayerOriginalClass(AIndex: integer): TBGRALayerOriginalAny;
  67. function GetLayerOriginalDefined(AIndex: integer): boolean;
  68. function GetLayerOriginalKnown(AIndex: integer): boolean;
  69. function GetLayerOriginalMatrix(AIndex: integer): TAffineMatrix;
  70. function GetSelectionLayerEmpty: boolean;
  71. function GetSelectionMaskBounds: TRect;
  72. function GetSelectionMaskEmpty: boolean;
  73. function GetSelectionTransform: TAffineMatrix;
  74. procedure LayeredActionDone(Sender: TObject);
  75. procedure LayeredActionProgress({%H-}ASender: TObject; AProgressPercent: integer);
  76. procedure LayeredSizeChanged(Sender: TObject);
  77. procedure NeedSelectionLayerAfterMask;
  78. function GetBlendOperation(AIndex: integer): TBlendOperation;
  79. function GetCurrentFilenameUTF8: string;
  80. function GetCurrentLayerVisible: boolean;
  81. function GetCurrentLayerIndex:integer;
  82. function GetEmpty: boolean;
  83. function GetHeight: integer;
  84. function GetSelectionMask: TBGRABitmap;
  85. function GetSelectedImageLayer: TBGRABitmap;
  86. function GetLayerBitmap(AIndex: integer): TBGRABitmap;
  87. function GetLayerName(AIndex: integer): string;
  88. function GetLayerOffset(AIndex: integer): TPoint;
  89. function GetLayerOpacity(AIndex: integer): byte;
  90. function GetLayerVisible(AIndex: integer): boolean;
  91. function GetNbLayers: integer;
  92. function GetRenderedImage: TBGRABitmap;
  93. function GetSelectedLayerPixel(X, Y: Integer): TBGRAPixel;
  94. function GetSelectionLayerBounds: TRect;
  95. function GetWidth: integer;
  96. function GetZoomFactor: single;
  97. procedure InvalidateImageDifference(ADiff: TCustomImageDifference);
  98. procedure OriginalChange({%H-}ASender: TObject;
  99. AOriginal: TBGRALayerCustomOriginal; var ADiff: TBGRAOriginalDiff);
  100. procedure OriginalEditingChange({%H-}ASender: TObject;
  101. {%H-}AOriginal: TBGRALayerCustomOriginal);
  102. procedure OriginalLoadError({%H-}ASender: TObject; {%H-}AError: string;
  103. var ARaise: boolean);
  104. procedure SetBlendOperation(AIndex: integer; AValue: TBlendOperation);
  105. procedure SetCurrentFilenameUTF8(AValue: string);
  106. procedure LayeredBitmapReplaced;
  107. procedure SetDraftOriginal(AValue: boolean);
  108. procedure SetLayerName(AIndex: integer; AValue: string);
  109. procedure SetLayerOffset(AIndex: integer; AValue: TPoint);
  110. procedure SetLayerOpacity(AIndex: integer; AValue: byte);
  111. procedure SetLayerOriginalMatrix(AIndex: integer; AValue: TAffineMatrix);
  112. procedure SetLayerVisible(AIndex: integer; AValue: boolean);
  113. procedure LayerBlendMayChange(AIndex: integer);
  114. function GetDrawingLayer: TBGRABitmap;
  115. procedure CompressUndoIfNecessary;
  116. procedure NotifyException(AFunctionName: string; AException: Exception);
  117. procedure SetOnActionProgress(AValue: TLayeredActionProgressEvent);
  118. procedure SetOnSizeChanged(AValue: TNotifyEvent);
  119. procedure SetSelectionTransform(ATransform: TAffineMatrix);
  120. procedure SetZoom(AValue: TZoom);
  121. procedure UpdateIconFileUTF8(AFilename: string; AOutputFilename: string = ''; AExport: boolean = false);
  122. procedure UpdateTiffFileUTF8(AFilename: string; AOutputFilename: string = ''; AExport: boolean = false);
  123. procedure UpdateGifFileUTF8(AFilename: string; AOutputFilename: string = ''; AExport: boolean = false);
  124. procedure ReplaceCurrentSelectionWithoutUndo(const AValue: TBGRABitmap);
  125. procedure LayerActionNotifyChange({%H-}ASender: TObject; ALayer: TBGRABitmap; ARect: TRect);
  126. procedure LayerActionDestroy(Sender: TObject);
  127. procedure LayerActionNotifyUndo({%H-}ASender: TObject; AUndo: TCustomImageDifference; var Owned: boolean);
  128. procedure ZoomOnCenterQuery(Sender: TObject);
  129. public
  130. OnException: TImageExceptionHandler;
  131. ImageOffset: TPoint;
  132. CursorHotSpot: TPoint;
  133. BPP, FrameIndex, FrameCount: integer;
  134. VisibleArea: TRectF;
  135. // make copy
  136. function MakeLayeredBitmapCopy: TBGRALayeredBitmap;
  137. function MakeLayeredBitmapAndSelectionCopy: TLayeredBitmapAndSelection;
  138. function MakeBitmapCopy(backgroundColor: TColor): TBitmap;
  139. function MakeCroppedLayer: TBGRABitmap;
  140. // undo/redo
  141. procedure AddUndo(AUndoAction: TCustomImageDifference);
  142. function CanUndo: boolean;
  143. function CanRedo: boolean;
  144. procedure Undo;
  145. procedure Redo;
  146. function DoBegin: TComposedImageDifference;
  147. procedure DoEnd(out ADoFound: boolean; out ASomethingDone: boolean);
  148. procedure DoEnd(var ACompose: TComposedImageDifference);
  149. procedure ClearUndo;
  150. procedure CompressUndo;
  151. function UsedMemory: int64;
  152. function CreateAction(AApplyOfsBefore: boolean=false; AApplySelTransformBefore: boolean=false): TLayerAction;
  153. // invalidating
  154. procedure ImageMayChange(ARect: TRect; ADiscardSelectionLayerAfterMask: boolean = true);
  155. procedure ImageMayChangeCompletely;
  156. procedure LayerMayChange(ALayer: TBGRABitmap; ARect: TRect);
  157. procedure LayerMayChangeCompletely(ALayer: TBGRABitmap);
  158. procedure SelectionMaskMayChange(ARect: TRect);
  159. procedure SelectionMaskMayChangeCompletely;
  160. procedure RenderMayChange(ARect: TRect; APicCoords: boolean = false; ANotify: boolean = true);
  161. procedure RenderMayChangeCompletely(ANotify: boolean = true);
  162. procedure ResetRenderUpdateRect;
  163. // selection mask
  164. function SelectionMaskNil: boolean;
  165. function GetSelectionMaskCenter: TPointF;
  166. procedure SaveSelectionMaskToFileUTF8(AFilename: string);
  167. function SelectionMaskReadonly: TBGRABitmap;
  168. procedure ReleaseEmptySelection;
  169. // selection layer
  170. function SelectionLayerReadonly: TBGRABitmap;
  171. // image layer
  172. function SetCurrentLayerByIndex(AValue: integer): boolean;
  173. function SelectLayerContainingPixelAt(APicturePos: TPoint): boolean;
  174. function CurrentLayerEmpty: boolean;
  175. function CurrentLayerTransparent: boolean;
  176. function CurrentLayerEquals(AColor: TBGRAPixel): boolean;
  177. property CurrentLayerPixel[X,Y: Integer]: TBGRAPixel read GetSelectedLayerPixel;
  178. procedure SetLayerOffset(AIndex: integer; AValue: TPoint; APrecomputedLayerBounds: TRect);
  179. function CurrentLayerReadOnly: TBGRABitmap;
  180. procedure SetLayerRegistry(ALayerIndex: integer; AIdentifier: string; AValue: RawByteString);
  181. function GetLayerRegistry(ALayerIndex: integer; AIdentifier: string): RawByteString;
  182. procedure SetRegistry(AIdentifier: string; AValue: RawByteString);
  183. function GetRegistry(AIdentifier: string): RawByteString;
  184. function GetLayerIndexById(AId: integer): integer;
  185. function GetLayerIndexByGuid(AGuid: TGuid): integer;
  186. procedure AddNewLayer;
  187. procedure AddNewLayer(AOriginal: TBGRALayerCustomOriginal; AName: string; ABlendOp: TBlendOperation; AMatrix: TAffineMatrix; AOpacity: byte = 255);
  188. procedure AddNewLayer(ALayer: TBGRABitmap; AName: string; ABlendOp: TBlendOperation; AOpacity: byte = 255);
  189. procedure AddNewLayer(ALayer: TBGRABitmap; AName: string; AOffset: TPoint; ABlendOp: TBlendOperation; AOpacity: byte = 255);
  190. procedure DuplicateLayer;
  191. procedure RasterizeLayer;
  192. procedure MergeLayerOver;
  193. procedure MoveLayer(AFromIndex,AToIndex: integer);
  194. procedure RemoveLayer;
  195. procedure ClearLayer;
  196. procedure HorizontalFlip(ALayerIndex: integer); overload;
  197. procedure VerticalFlip(ALayerIndex: integer); overload;
  198. // whole image
  199. procedure Assign(const AValue: TBGRABitmap; AOwned: boolean; AUndoable: boolean;
  200. ACaption: string = ''; AOpacity: byte = 255); overload;
  201. procedure Assign(const AValue: TBGRACustomLayeredBitmap; AOwned: boolean; AUndoable: boolean); overload;
  202. procedure Assign(const AValue: TLayeredBitmapAndSelection; AOwned: boolean; AUndoable: boolean); overload;
  203. procedure SwapRedBlue;
  204. procedure LinearNegativeAll;
  205. procedure NegativeAll;
  206. procedure HorizontalFlip; overload;
  207. procedure VerticalFlip; overload;
  208. procedure RotateCW;
  209. procedure RotateCCW;
  210. procedure Rotate180;
  211. procedure Resample(AWidth, AHeight: integer; filter: TResampleFilter);
  212. function ApplySmartZoom3: boolean;
  213. procedure Flatten;
  214. function FlatImageEquals(ABitmap: TBGRABitmap): boolean;
  215. function ComputeFlatImage(AFromLayer,AToLayer: integer; ASeparateXorMask: boolean): TBGRABitmap;
  216. procedure PrepareForRendering;
  217. procedure Draw(ADest: TBGRABitmap; x,y: integer);
  218. // input/output
  219. function DetectImageFormat(AFilename: string): TBGRAImageFormat;
  220. procedure LoadFromFileUTF8(AFilename: string);
  221. function AbleToSaveAsUTF8(AFilename: string): boolean;
  222. function AbleToSaveSelectionAsUTF8(AFilename: string): boolean;
  223. procedure SaveToFileUTF8(AFilename: string; AExport: boolean = false);
  224. procedure UpdateMultiImage(AOutputFilename: string = ''; AExport: boolean = false);
  225. procedure SetSavedFlag(ASavedBPP: integer = 0;
  226. ASavedFrameIndex: integer = 0;
  227. ASavedFrameCount: integer = 1;
  228. AOpening: boolean = false);
  229. function IsFileModified: boolean;
  230. procedure SaveOriginalToStream(AStream: TStream);
  231. function CheckCurrentLayerVisible: boolean;
  232. function CheckNoAction(ASilent: boolean = false): boolean;
  233. function CanDuplicateFrame: boolean;
  234. function CanHaveFrames: boolean;
  235. procedure ZoomFit;
  236. property CurrentState: TImageState read FCurrentState;
  237. property currentFilenameUTF8: string read GetCurrentFilenameUTF8 write SetCurrentFilenameUTF8;
  238. property CurrentLayerIndex: integer read GetCurrentLayerIndex;
  239. property SelectionMask: TBGRABitmap read GetSelectionMask;
  240. property RenderedImage: TBGRABitmap read GetRenderedImage;
  241. property Width: integer read GetWidth;
  242. property Height: integer read GetHeight;
  243. property OnSelectionChanged: TOnSelectionMaskChanged read FOnSelectionMaskChanged write FOnSelectionMaskChanged;
  244. property OnSelectedLayerIndexChanging: TOnCurrentLayerIndexChanged read FOnSelectedLayerIndexChanging write FOnSelectedLayerIndexChanging;
  245. property OnSelectedLayerIndexChanged: TOnCurrentLayerIndexChanged read FOnSelectedLayerIndexChanged write FOnSelectedLayerIndexChanged;
  246. property OnStackChanged: TOnStackChanged read FOnStackChanged write FOnStackChanged;
  247. property OnImageChanged: TLazPaintImageObservable read FOnImageChanged;
  248. property OnImageRenderChanged: TOnRenderChanged read FOnImageRenderChanged write FOnImageRenderChanged;
  249. property OnImageSaving: TLazPaintImageObservable read FOnImageSaving;
  250. property OnImageExport: TLazPaintImageObservable read FOnImageExport;
  251. property OnSizeChanged: TNotifyEvent read FOnSizeChanged write SetOnSizeChanged;
  252. property OnActionProgress: TLayeredActionProgressEvent read FOnActionProgress write SetOnActionProgress;
  253. property NbLayers: integer read GetNbLayers;
  254. property Empty: boolean read GetEmpty;
  255. property SelectionLayerBounds: TRect read GetSelectionLayerBounds;
  256. property SelectionLayerIsEmpty: boolean read GetSelectionLayerEmpty;
  257. property SelectionMaskBounds: TRect read GetSelectionMaskBounds;
  258. property SelectionMaskEmpty: boolean read GetSelectionMaskEmpty;
  259. property LayerName[AIndex: integer]: string read GetLayerName write SetLayerName;
  260. property LayerBitmap[AIndex: integer]: TBGRABitmap read GetLayerBitmap;
  261. property LayerBitmapById[AIndex: integer]: TBGRABitmap read GetLayerBitmapById;
  262. property LayerOriginal[AIndex: integer]: TBGRALayerCustomOriginal read GetLayerOriginal;
  263. property LayerOriginalDefined[AIndex: integer]: boolean read GetLayerOriginalDefined;
  264. property LayerOriginalKnown[AIndex: integer]: boolean read GetLayerOriginalKnown;
  265. property LayerOriginalClass[AIndex: integer]: TBGRALayerOriginalAny read GetLayerOriginalClass;
  266. property LayerOriginalMatrix[AIndex: integer]: TAffineMatrix read GetLayerOriginalMatrix write SetLayerOriginalMatrix;
  267. property LayerId[AIndex: integer]: integer read GetLayerId;
  268. property LayerGuid[AIndex: integer]: TGuid read GetLayerGuid;
  269. property LayerVisible[AIndex: integer]: boolean read GetLayerVisible write SetLayerVisible;
  270. property LayerOpacity[AIndex: integer]: byte read GetLayerOpacity write SetLayerOpacity;
  271. property LayerOffset[AIndex: integer]: TPoint read GetLayerOffset write SetLayerOffset;
  272. property BlendOperation[AIndex: integer]: TBlendOperation read GetBlendOperation write SetBlendOperation;
  273. property CurrentLayerVisible: boolean read GetCurrentLayerVisible;
  274. property OnQueryExitToolHandler: TOnQueryExitToolHandler read FOnQueryExitToolHandler write FOnQueryExitToolHandler;
  275. property OnCurrentFilenameChanged: TOnCurrentFilenameChanged read FOnCurrentFilenameChanged write FOnCurrentFilenameChanged;
  276. property RenderUpdateRectInPicCoord: TRect read FRenderUpdateRectInPicCoord;
  277. property RenderUpdateRectInVSCoord: TRect read FRenderUpdateRectInVSCoord;
  278. property SelectionTransform: TAffineMatrix read GetSelectionTransform write SetSelectionTransform;
  279. property Zoom: TZoom read FZoom write SetZoom;
  280. property ZoomFactor: single read GetZoomFactor;
  281. property DraftOriginal: boolean read FDraftOriginal write SetDraftOriginal;
  282. property IsIconCursor: boolean read GetIsIconCursor;
  283. property IsCursor: boolean read GetIsCursor;
  284. property IsTiff: boolean read GetIsTiff;
  285. property IsGif: boolean read GetIsGif;
  286. property DPI: integer read GetDPI;
  287. constructor Create(ALazPaintInstance: TObject);
  288. destructor Destroy; override;
  289. end;
  290. function ComputeAcceptableImageSize(AWidth,AHeight: integer): TSize;
  291. implementation
  292. uses UGraph, UResourceStrings, Dialogs,
  293. BGRAOpenRaster, BGRAPhoxo, BGRAPaintNet, UImageDiff, ULoading,
  294. BGRAWriteLzp, BGRAUTF8,
  295. BGRAPalette, BGRAColorQuantization, UFileSystem,
  296. BGRAThumbnail, BGRAIconCursor, UTiff, LazPaintType,
  297. BGRALazPaint, BGRAAnimatedGif,
  298. BGRAGradientScanner, BGRASVGOriginal, Forms;
  299. function ComputeAcceptableImageSize(AWidth, AHeight: integer): TSize;
  300. var ratio,newRatio: single;
  301. begin
  302. ratio := 1;
  303. if AWidth > MaxImageWidth then ratio := MaxImageWidth/AWidth;
  304. if AHeight > MaxImageHeight then
  305. begin
  306. newRatio := MaxImageHeight/AHeight;
  307. if newRatio < ratio then ratio := newRatio;
  308. end;
  309. if ratio < 1 then
  310. begin
  311. result.cx := round(AWidth*ratio);
  312. result.cy := round(AHeight*ratio);
  313. end else
  314. begin
  315. result.cx := AWidth;
  316. result.cy := AHeight;
  317. end;
  318. end;
  319. { TLazPaintImage }
  320. procedure TLazPaintImage.LayerActionNotifyUndo(ASender: TObject; AUndo: TCustomImageDifference;
  321. var Owned: boolean);
  322. begin
  323. AddUndo(AUndo);
  324. Owned := true;
  325. OnImageChanged.NotifyObservers;
  326. end;
  327. procedure TLazPaintImage.ZoomOnCenterQuery(Sender: TObject);
  328. begin
  329. ImageOffset := Point(0,0);
  330. end;
  331. function TLazPaintImage.MakeCroppedLayer: TBGRABitmap;
  332. var r: TRect;
  333. cropped: TBGRABitmap;
  334. ofs: TPoint;
  335. begin
  336. ofs := Point(0,0);
  337. result := DuplicateBitmap(FCurrentState.SelectionLayer);
  338. if (result <> nil) and (SelectionMask <> nil) then result.ApplyMask(SelectionMask);
  339. if (result <> nil) and result.Empty then FreeAndNil(result);
  340. if result = nil then
  341. begin
  342. ofs := LayerOffset[CurrentLayerIndex];
  343. result := DuplicateBitmap(GetSelectedImageLayer);
  344. if (result <> nil) and (SelectionMask <> nil) then
  345. result.ApplyMask(SelectionMask, rect(0,0,result.Width,result.Height),
  346. Point(ofs.X,ofs.Y));
  347. end;
  348. if result <> nil then
  349. begin
  350. if SelectionMask = nil then
  351. r := result.GetImageBounds
  352. else
  353. begin
  354. r := SelectionMaskBounds;
  355. OffsetRect(r, -ofs.x, -ofs.y);
  356. end;
  357. if IsRectEmpty(r) then
  358. FreeAndNil(result)
  359. else
  360. begin
  361. if (r.left <> 0) or (r.top <> 0) or (r.right <> result.Width) or (r.bottom <> result.Height) then
  362. begin
  363. cropped := TBGRABitmap.Create(r.Width,r.Height);
  364. cropped.PutImage(-r.Left, -r.Top, result, dmSet);
  365. BGRAReplace(result, cropped);
  366. end;
  367. end;
  368. end;
  369. end;
  370. function TLazPaintImage.ApplySmartZoom3: boolean;
  371. var i, idx: integer;
  372. zoomed: TLayeredBitmapAndSelection;
  373. ofs: TPoint;
  374. withOfs: TBGRABitmap;
  375. begin
  376. result := false;
  377. if not CheckNoAction then exit;
  378. try
  379. zoomed.layeredBitmap := TBGRALayeredBitmap.Create(Width*3,Height*3);
  380. for i := 0 to NbLayers-1 do
  381. begin
  382. idx := zoomed.layeredBitmap.AddOwnedLayer(FCurrentState.LayerBitmap[i].FilterSmartZoom3(moMediumSmooth) as TBGRABitmap,
  383. FCurrentState.BlendOperation[i], FCurrentState.LayerOpacity[i]);
  384. ofs := FCurrentState.LayerOffset[i];
  385. if (ofs.x <> 0) or (ofs.y <> 0) or (zoomed.layeredBitmap.LayerBitmap[idx].Width <> zoomed.layeredBitmap.Width)
  386. or (zoomed.layeredBitmap.LayerBitmap[idx].Height <> zoomed.layeredBitmap.Height) then
  387. begin
  388. withOfs := TBGRABitmap.Create(zoomed.layeredBitmap.Width, zoomed.layeredBitmap.Height);
  389. withOfs.PutImage(ofs.x*3,ofs.y*3, zoomed.layeredBitmap.LayerBitmap[idx], dmSet);
  390. zoomed.layeredBitmap.SetLayerBitmap(idx, withOfs, true);
  391. end;
  392. end;
  393. if SelectionMask <> nil then
  394. zoomed.selection:= SelectionMask.FilterSmartZoom3(moMediumSmooth) as TBGRABitmap
  395. else zoomed.Selection := nil;
  396. if FCurrentState.SelectionLayer <> nil then
  397. zoomed.selectionLayer := FCurrentState.SelectionLayer.FilterSmartZoom3(moMediumSmooth) as TBGRABitmap
  398. else
  399. zoomed.selectionLayer := nil;
  400. AddUndo(FCurrentState.AssignWithUndo(zoomed.layeredBitmap,true, FCurrentState.SelectedImageLayerIndex, zoomed.selection, zoomed.selectionLayer));
  401. result := true;
  402. except on ex: exception do NotifyException('ApplySmartZoom3',ex);
  403. end;
  404. ImageMayChangeCompletely;
  405. SelectionMaskMayChangeCompletely;
  406. end;
  407. procedure TLazPaintImage.Resample(AWidth, AHeight: integer; filter: TResampleFilter);
  408. var quality : TResampleMode;
  409. backup: TImageState;
  410. begin
  411. if not CheckNoAction then exit;
  412. try
  413. backup := FCurrentState.Duplicate as TImageState;
  414. if filter = rfBox then
  415. quality := rmSimpleStretch
  416. else
  417. quality := rmFineResample;
  418. FCurrentState.Resample(AWidth,AHeight,quality,filter);
  419. LayeredBitmapReplaced;
  420. AddUndo(FCurrentState.GetUndoAfterAssign(backup));
  421. SelectionMaskMayChangeCompletely;
  422. backup.Free;
  423. except on ex: exception do NotifyException(RemoveTrail(rsResamplingImage),ex);
  424. end;
  425. end;
  426. function TLazPaintImage.DetectImageFormat(AFilename: string): TBGRAImageFormat;
  427. var
  428. s: TStream;
  429. begin
  430. s := FileManager.CreateFileStream(AFilename, fmOpenRead);
  431. try
  432. result := DetectFileFormat(s, ExtractFileExt(AFilename));
  433. finally
  434. s.Free;
  435. end;
  436. end;
  437. function TLazPaintImage.AbleToSaveAsUTF8(AFilename: string): boolean;
  438. var format: TBGRAImageFormat;
  439. begin
  440. format := SuggestImageFormat(AFilename);
  441. result := (DefaultBGRAImageWriter[format] <> nil) or
  442. (format in [ifIco,ifCur,ifSvg]);
  443. if result and (format = ifXPixMap) then
  444. begin
  445. if (Width > 256) or (Height > 256) then
  446. begin
  447. ShowMessage(rsNotReasonableFormat + ' (> 256x256)');
  448. result := false;
  449. end;
  450. end;
  451. end;
  452. function TLazPaintImage.AbleToSaveSelectionAsUTF8(AFilename: string): boolean;
  453. var ext: string;
  454. begin
  455. ext := UTF8LowerCase(ExtractFileExt(AFilename));
  456. if (ext='.bmp') or (ext='.jpg') or (ext='.jpeg')
  457. or (ext='.png') or (ext='.pcx') or (ext='.tga') or (ext='.lzp') then
  458. result := true else
  459. result := false;
  460. end;
  461. procedure TLazPaintImage.SaveToFileUTF8(AFilename: string; AExport: boolean);
  462. var s: TStream;
  463. format: TBGRAImageFormat;
  464. begin
  465. format := SuggestImageFormat(AFilename);
  466. if format in[ifOpenRaster,ifPhoxo,ifLazPaint,ifSvg] then
  467. begin
  468. s := FileManager.CreateFileStream(AFilename, fmCreate);
  469. try
  470. FCurrentState.SaveToStreamAs(s, format);
  471. finally
  472. s.Free;
  473. end;
  474. if not AExport then SetSavedFlag else OnImageExport.NotifyObservers;
  475. end else
  476. begin
  477. if RenderedImage = nil then exit;
  478. s := FileManager.CreateFileStream(AFilename, fmCreate);
  479. try
  480. RenderedImage.SaveToStreamAs(s, SuggestImageFormat(AFilename));
  481. finally
  482. s.Free;
  483. end;
  484. if not AExport then
  485. begin
  486. if NbLayers = 1 then SetSavedFlag
  487. else OnImageSaving.NotifyObservers;
  488. end
  489. else OnImageExport.NotifyObservers;
  490. end;
  491. end;
  492. procedure TLazPaintImage.UpdateMultiImage(AOutputFilename: string; AExport: boolean);
  493. begin
  494. if not FileManager.FileExists(currentFilenameUTF8) then
  495. begin
  496. ShowMessage(rsFileNotFound + LineEnding + LineEnding + currentFilenameUTF8);
  497. exit;
  498. end;
  499. if IsIconCursor then
  500. UpdateIconFileUTF8(currentFilenameUTF8, AOutputFilename, AExport)
  501. else if IsTiff then
  502. UpdateTiffFileUTF8(currentFilenameUTF8, AOutputFilename, AExport)
  503. else if IsGif then
  504. UpdateGifFileUTF8(currentFilenameUTF8, AOutputFilename, AExport)
  505. else
  506. ShowMessage(rsFileExtensionNotSupported);
  507. end;
  508. procedure TLazPaintImage.UpdateIconFileUTF8(AFilename: string; AOutputFilename: string; AExport: boolean);
  509. var
  510. s: TStream;
  511. icoCur: TBGRAIconCursor;
  512. frame: TBGRABitmap;
  513. newFrameIndex: integer;
  514. begin
  515. if bpp = 0 then
  516. begin
  517. if RenderedImage.HasTransparentPixels then
  518. bpp := 32
  519. else
  520. bpp := 24;
  521. end;
  522. if AOutputFilename = '' then AOutputFilename := AFilename;
  523. frame := BGRADitherIconCursor(RenderedImage, bpp, daFloydSteinberg) as TBGRABitmap;
  524. icoCur := TBGRAIconCursor.Create;
  525. try
  526. if FileManager.FileExists(AFilename) then
  527. begin
  528. s := FileManager.CreateFileStream(AFilename,fmOpenRead or fmShareDenyWrite);
  529. try
  530. icoCur.LoadFromStream(s);
  531. finally
  532. s.Free;
  533. end;
  534. end;
  535. newFrameIndex := icoCur.Add(frame, bpp, true);
  536. icoCur.FileType:= SuggestImageFormat(AOutputFilename);
  537. s := FileManager.CreateFileStream(AOutputFilename,fmCreate);
  538. try
  539. icoCur.SaveToStream(s);
  540. if not AExport then
  541. SetSavedFlag(bpp, newFrameIndex, icoCur.Count)
  542. else OnImageExport.NotifyObservers;
  543. finally
  544. s.Free;
  545. end;
  546. finally
  547. frame.free;
  548. icoCur.Free;
  549. end;
  550. end;
  551. procedure TLazPaintImage.UpdateTiffFileUTF8(AFilename: string;
  552. AOutputFilename: string; AExport: boolean);
  553. var
  554. s, sAdded: TStream;
  555. tiff, addedTiff: TTiff;
  556. newFrameIndex: integer;
  557. begin
  558. if AOutputFilename = '' then AOutputFilename := AFilename;
  559. tiff := TTiff.Create;
  560. addedTiff := TTiff.Create;
  561. sAdded := nil;
  562. s := nil;
  563. try
  564. if FileManager.FileExists(AFilename) then
  565. begin
  566. s := FileManager.CreateFileStream(AFilename,fmOpenRead or fmShareDenyWrite);
  567. if tiff.LoadFromStream(s) <> teNone then
  568. raise Exception.Create(StringReplace(rsErrorOnOpeningFile,'%1', AFilename, []));
  569. FreeAndNil(s);
  570. end;
  571. sAdded := TMemoryStream.Create;
  572. RenderedImage.SaveToStreamAs(sAdded, ifTiff);
  573. sAdded.Position:= 0;
  574. if addedTiff.LoadFromStream(sAdded) <> teNone then
  575. raise Exception.Create(rsInternalError);
  576. FreeAndNil(sAdded);
  577. if FrameIndex = TImageEntry.NewFrameIndex then
  578. newFrameIndex := tiff.Move(addedTiff,0)
  579. else
  580. begin
  581. newFrameIndex := FrameIndex;
  582. if newFrameIndex >= tiff.Count then
  583. newFrameIndex := tiff.Count
  584. else
  585. tiff.Delete(newFrameIndex);
  586. tiff.Move(addedTiff,0,newFrameIndex);
  587. end;
  588. s := FileManager.CreateFileStream(AOutputFilename,fmCreate);
  589. try
  590. tiff.SaveToStream(s);
  591. if not AExport then
  592. SetSavedFlag(bpp, newFrameIndex, tiff.Count)
  593. else OnImageExport.NotifyObservers;
  594. finally
  595. FreeAndNil(s);
  596. end;
  597. finally
  598. addedTiff.Free;
  599. sAdded.Free;
  600. tiff.Free;
  601. s.Free;
  602. end;
  603. end;
  604. procedure TLazPaintImage.UpdateGifFileUTF8(AFilename: string;
  605. AOutputFilename: string; AExport: boolean);
  606. var
  607. s: TStream;
  608. gif: TBGRAAnimatedGif;
  609. newFrameIndex: integer;
  610. begin
  611. if AOutputFilename = '' then AOutputFilename := AFilename;
  612. gif := TBGRAAnimatedGif.Create;
  613. s := nil;
  614. try
  615. if FileManager.FileExists(AFilename) then
  616. begin
  617. s := FileManager.CreateFileStream(AFilename,fmOpenRead or fmShareDenyWrite);
  618. gif.LoadFromStream(s);
  619. FreeAndNil(s);
  620. end;
  621. if FrameIndex = TImageEntry.NewFrameIndex then
  622. newFrameIndex := gif.AddFullFrame(RenderedImage, gif.AverageDelayMs)
  623. else
  624. begin
  625. newFrameIndex := FrameIndex;
  626. gif.ReplaceFullFrame(newFrameIndex, RenderedImage, gif.FrameDelayMs[newFrameIndex]);
  627. end;
  628. gif.OptimizeFrames;
  629. s := FileManager.CreateFileStream(AOutputFilename,fmCreate);
  630. try
  631. gif.SaveToStream(s);
  632. if not AExport then
  633. SetSavedFlag(bpp, newFrameIndex, gif.Count)
  634. else OnImageExport.NotifyObservers;
  635. finally
  636. FreeAndNil(s);
  637. end;
  638. finally
  639. gif.Free;
  640. s.Free;
  641. end;
  642. end;
  643. procedure TLazPaintImage.LoadFromFileUTF8(AFilename: string);
  644. var s: TStream;
  645. ext: string;
  646. bmp: TBGRABitmap;
  647. layeredBmp: TBGRACustomLayeredBitmap;
  648. temp: TBGRALayeredBitmap;
  649. selIndex: Integer;
  650. begin
  651. if not CheckNoAction then exit;
  652. ext := UTF8LowerCase(ExtractFileExt(AFilename));
  653. bmp := nil;
  654. s := nil;
  655. try
  656. s := FileManager.CreateFileStream(AFilename, fmOpenRead or fmShareDenyWrite);
  657. layeredBmp := TryCreateLayeredBitmapReader(ext);
  658. if Assigned(layeredBmp) then
  659. begin
  660. if layeredBmp is TBGRALayeredSVG then
  661. with TBGRALayeredSVG(layeredBmp) do
  662. begin
  663. ContainerWidth := Screen.Width;
  664. ContainerHeight := Screen.Height;
  665. DefaultLayerName:= rsLayer;
  666. end;
  667. layeredBmp.LoadFromStream(s);
  668. with ComputeAcceptableImageSize(layeredBmp.Width,layeredBmp.Height) do
  669. if (cx < layeredBmp.Width) or (cy < layeredBmp.Height) then
  670. begin
  671. if not (layeredBmp is TBGRALayeredBitmap) then
  672. begin
  673. temp := TBGRALayeredBitmap.Create;
  674. temp.Assign(layeredBmp, true, true);
  675. layeredBmp.Free;
  676. layeredBmp := temp;
  677. end;
  678. MessagePopupForever(rsResamplingImage);
  679. (FLazPaintInstance as TLazPaintCustomInstance).UpdateWindows;
  680. (layeredBmp as TBGRALayeredBitmap).Resample(cx, cy, rmFineResample);
  681. MessagePopupHide;
  682. end;
  683. CursorHotSpot := Point(0,0);
  684. if layeredBmp is TBGRALazPaintImage then
  685. selIndex := TBGRALazPaintImage(layeredBmp).SelectedLayerIndex
  686. else selIndex := -1;
  687. Assign(layeredBmp, true, false);
  688. if selIndex <> -1 then SetCurrentLayerByIndex(selIndex);
  689. layeredBmp := nil;
  690. end else
  691. begin
  692. bmp := TBGRABitmap.Create;
  693. bmp.LoadFromStream(s, [lobmpAutoOpaque]);
  694. Assign(bmp,true,false);
  695. bmp := nil;
  696. end;
  697. finally
  698. bmp.Free;
  699. s.Free;
  700. end;
  701. end;
  702. procedure TLazPaintImage.SetSavedFlag(ASavedBPP: integer; ASavedFrameIndex: integer;
  703. ASavedFrameCount: integer; AOpening: boolean);
  704. var i: integer;
  705. begin
  706. FCurrentState.saved := true;
  707. self.BPP := ASavedBPP;
  708. self.FrameIndex := ASavedFrameIndex;
  709. self.FrameCount := ASavedFrameCount;
  710. for i := 0 to FUndoList.Count-1 do
  711. begin
  712. FUndoList[i].SavedBefore := (i = FUndoPos+1);
  713. FUndoList[i].SavedAfter := (i = FUndoPos);
  714. end;
  715. OnImageChanged.NotifyObservers;
  716. if (currentFilenameUTF8 <> '') and not AOpening then
  717. OnImageSaving.NotifyObservers;
  718. end;
  719. function TLazPaintImage.IsFileModified: boolean;
  720. begin
  721. result := not FCurrentState.saved;
  722. end;
  723. function TLazPaintImage.FlatImageEquals(ABitmap: TBGRABitmap): boolean;
  724. begin
  725. if ABitmap = nil then result := RenderedImage = nil
  726. else
  727. result := ABitmap.Equals(RenderedImage);
  728. end;
  729. procedure TLazPaintImage.Flatten;
  730. begin
  731. Assign(RenderedImage,False,True);
  732. end;
  733. function TLazPaintImage.GetDrawingLayer: TBGRABitmap;
  734. begin
  735. if SelectionMaskEmpty then result := GetSelectedImageLayer else
  736. result := FCurrentState.GetOrCreateSelectionLayer;
  737. end;
  738. procedure TLazPaintImage.LayeredBitmapReplaced;
  739. begin
  740. FreeAndNil(FRenderedImage);
  741. if FCurrentState.NbLayers = 0 then
  742. raise Exception.Create('No layer')
  743. else
  744. if FCurrentState.SelectedImageLayerIndex = -1 then
  745. FCurrentState.SelectedImageLayerIndex := 0;
  746. if Assigned(FOnStackChanged)then FOnStackChanged(self,True);
  747. OnImageChanged.NotifyObservers;
  748. ImageMayChangeCompletely;
  749. end;
  750. procedure TLazPaintImage.SetDraftOriginal(AValue: boolean);
  751. var
  752. r: TRect;
  753. begin
  754. if FDraftOriginal=AValue then Exit;
  755. FDraftOriginal:=AValue;
  756. if not FDraftOriginal then
  757. begin
  758. r := FCurrentState.LayeredBitmap.RenderOriginalsIfNecessary(FDraftOriginal);
  759. ImageMayChange(r, false);
  760. end;
  761. end;
  762. procedure TLazPaintImage.AddUndo(AUndoAction: TCustomImageDifference);
  763. var
  764. prevAction: TCustomImageDifference;
  765. prevGroup: TComposedImageDifference;
  766. prevActionIndex: Integer;
  767. begin
  768. if AUndoAction <> nil then
  769. begin
  770. if AUndoAction.IsIdentity then
  771. begin
  772. AUndoAction.Free;
  773. exit;
  774. end;
  775. prevGroup := FUndoList;
  776. prevActionIndex := FUndoPos;
  777. if prevActionIndex > -1 then
  778. begin
  779. prevAction := prevGroup[prevActionIndex];
  780. while (prevAction is TComposedImageDifference) and
  781. TComposedImageDifference(prevAction).Agglutinate do
  782. begin
  783. prevGroup := TComposedImageDifference(prevAction);
  784. prevActionIndex := prevGroup.Count-1;
  785. if prevActionIndex>=0 then
  786. prevAction := prevGroup[prevActionIndex]
  787. else
  788. prevAction := nil;
  789. end;
  790. end else
  791. prevAction := nil;
  792. if assigned(prevAction) then
  793. begin
  794. if IsInverseImageDiff(AUndoAction,prevAction) then
  795. begin
  796. //writeln('Inverse');
  797. AUndoAction.Free;
  798. FCurrentState.saved := prevAction.SavedBefore;
  799. prevGroup.DeleteFrom(prevActionIndex);
  800. if prevGroup = FUndoList then FUndoPos := prevActionIndex-1;
  801. exit;
  802. end else
  803. if not prevAction.savedAfter and TryCombineImageDiff(AUndoAction,prevAction) then
  804. begin
  805. AUndoAction.Free;
  806. If prevAction.IsIdentity then
  807. begin
  808. //writeln('Inverse (combine)');
  809. FCurrentState.saved := prevAction.SavedBefore;
  810. prevGroup.DeleteFrom(prevActionIndex);
  811. if prevGroup = FUndoList then FUndoPos := prevActionIndex-1;
  812. end;
  813. exit;
  814. end;
  815. end;
  816. prevGroup.DeleteFrom(prevActionIndex+1);
  817. if prevGroup.TotalCount >= MaxUndoCount then
  818. begin
  819. if prevGroup = FUndoList then
  820. begin
  821. FUndoList.Delete(0);
  822. FUndoList.Add(AUndoAction);
  823. end else
  824. begin
  825. MessagePopup(rsTooManyActions, 4000);
  826. AUndoAction.UnapplyTo(FCurrentState);
  827. InvalidateImageDifference(AUndoAction);
  828. exit;
  829. end;
  830. end else
  831. begin
  832. prevGroup.Add(AUndoAction);
  833. if prevGroup = FUndoList then inc(FUndoPos);
  834. end;
  835. //writeln(AUndoAction.ToString);
  836. FCurrentState.saved := AUndoAction.SavedAfter;
  837. CompressUndoIfNecessary;
  838. end;
  839. end;
  840. procedure TLazPaintImage.CompressUndoIfNecessary;
  841. var i: integer;
  842. begin
  843. for i := 0 to FUndoList.Count-1 do
  844. if UsedMemory <= MaxUsedMemoryWithoutCompression then break else
  845. repeat
  846. if not FUndoList[i].TryCompress then break;
  847. until UsedMemory <= MaxUsedMemoryWithoutCompression;
  848. end;
  849. procedure TLazPaintImage.NotifyException(AFunctionName: string;
  850. AException: Exception);
  851. begin
  852. if Assigned(OnException) then
  853. OnException(AFunctionName,AException)
  854. else
  855. MessageDlg(AFunctionName,AException.Message,mtError,[mbOk],0);
  856. end;
  857. procedure TLazPaintImage.SetOnActionProgress(AValue: TLayeredActionProgressEvent);
  858. begin
  859. if FOnActionProgress=AValue then Exit;
  860. FOnActionProgress:=AValue;
  861. end;
  862. procedure TLazPaintImage.SetOnSizeChanged(AValue: TNotifyEvent);
  863. begin
  864. if FOnSizeChanged=AValue then Exit;
  865. FOnSizeChanged:=AValue;
  866. end;
  867. procedure TLazPaintImage.SetSelectionTransform(ATransform: TAffineMatrix);
  868. procedure InvalidateTransformedSelection;
  869. var selectionChangeRect: TRect;
  870. begin
  871. selectionChangeRect := FCurrentState.GetTransformedSelectionMaskBounds;
  872. if not SelectionLayerIsEmpty then
  873. ImageMayChange(selectionChangeRect,False);
  874. if not IsRectEmpty(selectionChangeRect) then
  875. begin
  876. InflateRect(selectionChangeRect,1,1);
  877. RenderMayChange(selectionChangeRect,true);
  878. end;
  879. end;
  880. var
  881. diff: TSetSelectionTransformDifference;
  882. begin
  883. if ATransform <> CurrentState.SelectionTransform then
  884. begin
  885. InvalidateTransformedSelection;
  886. diff := TSetSelectionTransformDifference.Create(FCurrentState, ATransform);
  887. diff.ApplyTo(FCurrentState);
  888. InvalidateTransformedSelection;
  889. AddUndo(diff);
  890. end;
  891. end;
  892. procedure TLazPaintImage.SetZoom(AValue: TZoom);
  893. begin
  894. if FZoom=AValue then Exit;
  895. if Assigned(FZoom) then FZoom.OnCenterQuery:= nil;
  896. FZoom:=AValue;
  897. if Assigned(FZoom) then FZoom.OnCenterQuery:=@ZoomOnCenterQuery;
  898. end;
  899. procedure TLazPaintImage.SetLayerName(AIndex: integer; AValue: string);
  900. begin
  901. AddUndo(FCurrentState.SetLayerName(AIndex,Avalue));
  902. OnImageChanged.NotifyObservers;
  903. end;
  904. procedure TLazPaintImage.SetLayerOffset(AIndex: integer; AValue: TPoint);
  905. var bounds: TRect;
  906. begin
  907. bounds := FCurrentState.LayerBitmap[AIndex].GetImageBounds;
  908. SetLayerOffset(AIndex,AValue,bounds);
  909. end;
  910. procedure TLazPaintImage.SetLayerOpacity(AIndex: integer; AValue: byte);
  911. begin
  912. AddUndo(FCurrentState.SetLayerOpacity(AIndex,AValue));
  913. LayerBlendMayChange(AIndex);
  914. end;
  915. procedure TLazPaintImage.SetLayerOriginalMatrix(AIndex: integer;
  916. AValue: TAffineMatrix);
  917. var
  918. prevMatrix: TAffineMatrix;
  919. r: TRect;
  920. begin
  921. if LayerOriginalDefined[AIndex] then
  922. begin
  923. if not LayerOriginalKnown[AIndex] then
  924. raise exception.Create('Unknown original cannot be transformed');
  925. prevMatrix := LayerOriginalMatrix[AIndex];
  926. FCurrentState.LayeredBitmap.LayerOriginalMatrix[AIndex] := AValue;
  927. r := FCurrentState.LayeredBitmap.RenderOriginalsIfNecessary(FDraftOriginal);
  928. ImageMayChange(r, false);
  929. AddUndo(FCurrentState.ComputeLayerMatrixDifference(AIndex, prevMatrix, AValue));
  930. end else
  931. if not IsAffineMatrixIdentity(AValue) then
  932. raise exception.Create('Raster layer cannot have a matrix transform');
  933. end;
  934. procedure TLazPaintImage.SetLayerVisible(AIndex: integer; AValue: boolean);
  935. begin
  936. if not CheckNoAction then exit;
  937. if not SelectionLayerIsEmpty then
  938. begin
  939. MessagePopup(rsMustReleaseSelection,2000);
  940. exit;
  941. end;
  942. AddUndo(FCurrentState.SetLayerVisible(AIndex,AValue));
  943. LayerBlendMayChange(AIndex);
  944. OnImageChanged.NotifyObservers; //to show/hide tools
  945. end;
  946. function TLazPaintImage.MakeBitmapCopy(backgroundColor: TColor): TBitmap;
  947. begin
  948. result := RenderedImage.MakeBitmapCopy(backgroundColor);
  949. end;
  950. function TLazPaintImage.CanUndo: boolean;
  951. begin
  952. result := FUndoPos >= 0;
  953. end;
  954. function TLazPaintImage.CanRedo: boolean;
  955. begin
  956. result := FUndoPos < (FUndoList.Count-1);
  957. end;
  958. procedure TLazPaintImage.Undo;
  959. var prevAction: TCustomImageDifference;
  960. prevGroup: TComposedImageDifference;
  961. prevActionIndex: Integer;
  962. begin
  963. if CanUndo then
  964. begin
  965. if not CheckNoAction then exit;
  966. try
  967. prevGroup := FUndoList;
  968. prevActionIndex := FUndoPos;
  969. prevAction := prevGroup[prevActionIndex];
  970. while (prevAction is TComposedImageDifference) and
  971. TComposedImageDifference(prevAction).Agglutinate and
  972. (TComposedImageDifference(prevAction).Count > 0) do
  973. begin
  974. prevGroup := TComposedImageDifference(prevAction);
  975. prevActionIndex := prevGroup.Count-1;
  976. prevAction := prevGroup[prevActionIndex];
  977. end;
  978. prevAction.UnapplyTo(FCurrentState);
  979. InvalidateImageDifference(prevAction);
  980. if prevGroup = FUndoList then
  981. Dec(FUndoPos)
  982. else
  983. prevGroup.Delete(prevActionIndex);
  984. except
  985. on ex:Exception do
  986. begin
  987. NotifyException('Undo',ex);
  988. ClearUndo;
  989. ImageMayChangeCompletely;
  990. SelectionMaskMayChangeCompletely;
  991. end;
  992. end;
  993. CompressUndoIfNecessary;
  994. end;
  995. end;
  996. procedure TLazPaintImage.InvalidateImageDifference(ADiff: TCustomImageDifference);
  997. var kind:TImageDifferenceKind;
  998. begin
  999. kind := ADiff.Kind;
  1000. case kind of
  1001. idkChangeStack: OnImageChanged.NotifyObservers;
  1002. idkChangeImageAndSelection: begin
  1003. if ADiff.ChangingBoundsDefined then
  1004. begin
  1005. ImageMayChange(ADiff.ChangingBounds);
  1006. SelectionMaskMayChange(ADiff.ChangingBounds);
  1007. end else
  1008. begin
  1009. ImageMayChangeCompletely;
  1010. SelectionMaskMayChangeCompletely;
  1011. end;
  1012. end;
  1013. idkChangeImage:
  1014. if ADiff.ChangingBoundsDefined then
  1015. ImageMayChange(ADiff.ChangingBounds)
  1016. else
  1017. ImageMayChangeCompletely;
  1018. idkChangeSelection:
  1019. if ADiff.ChangingBoundsDefined then
  1020. SelectionMaskMayChange(ADiff.ChangingBounds)
  1021. else
  1022. SelectionMaskMayChangeCompletely;
  1023. end;
  1024. end;
  1025. procedure TLazPaintImage.OriginalChange(ASender: TObject;
  1026. AOriginal: TBGRALayerCustomOriginal; var ADiff: TBGRAOriginalDiff);
  1027. var
  1028. r: TRect;
  1029. begin
  1030. r := FCurrentState.LayeredBitmap.RenderOriginalIfNecessary(AOriginal.Guid, FDraftOriginal);
  1031. if r.IsEmpty then OnImageChanged.NotifyObservers
  1032. else ImageMayChange(r, false);
  1033. if Assigned(ADiff) then
  1034. begin
  1035. AddUndo(TVectorOriginalEmbeddedDifference.Create(CurrentState,AOriginal.Guid,ADiff,r));
  1036. ADiff := nil;
  1037. end;
  1038. end;
  1039. procedure TLazPaintImage.OriginalEditingChange(ASender: TObject;
  1040. AOriginal: TBGRALayerCustomOriginal);
  1041. begin
  1042. OnImageChanged.NotifyObservers;
  1043. end;
  1044. procedure TLazPaintImage.OriginalLoadError(ASender: TObject; AError: string;
  1045. var ARaise: boolean);
  1046. begin
  1047. MessagePopup(rsErrorLoadingOriginal, 4000);
  1048. ARaise := false;
  1049. end;
  1050. procedure TLazPaintImage.Redo;
  1051. var diff: TCustomImageDifference;
  1052. begin
  1053. if CanRedo then
  1054. begin
  1055. if not CheckNoAction then exit;
  1056. try
  1057. inc(FUndoPos);
  1058. diff := FUndoList[FUndoPos];
  1059. diff.ApplyTo(FCurrentState);
  1060. InvalidateImageDifference(diff);
  1061. except
  1062. on ex:Exception do
  1063. begin
  1064. NotifyException('Redo',ex);
  1065. ClearUndo;
  1066. ImageMayChangeCompletely;
  1067. SelectionMaskMayChangeCompletely;
  1068. end;
  1069. end;
  1070. CompressUndoIfNecessary;
  1071. end;
  1072. end;
  1073. function TLazPaintImage.DoBegin: TComposedImageDifference;
  1074. begin
  1075. result := TComposedImageDifference.Create(True);
  1076. AddUndo(result);
  1077. end;
  1078. procedure TLazPaintImage.DoEnd(out ADoFound: boolean; out ASomethingDone: boolean);
  1079. var
  1080. curDiff, insideDiff: TCustomImageDifference;
  1081. curGroup: TComposedImageDifference;
  1082. curIndex: Integer;
  1083. begin
  1084. ADoFound := false;
  1085. ASomethingDone := false;
  1086. if FUndoPos >= 0 then
  1087. begin
  1088. curGroup := FUndoList;
  1089. curIndex := FUndoPos;
  1090. curDiff := curGroup[curIndex];
  1091. if not ((curDiff is TComposedImageDifference) and
  1092. TComposedImageDifference(curDiff).Agglutinate and
  1093. not TComposedImageDifference(curDiff).LockAgglutinate) then
  1094. exit;
  1095. ADoFound:= true;
  1096. ASomethingDone := true;
  1097. repeat
  1098. insideDiff := TComposedImageDifference(curDiff).GetLast;
  1099. if (insideDiff <> nil) and (insideDiff is TComposedImageDifference) and
  1100. TComposedImageDifference(insideDiff).Agglutinate and
  1101. not TComposedImageDifference(insideDiff).LockAgglutinate then
  1102. begin
  1103. curGroup := TComposedImageDifference(curDiff);
  1104. curIndex := curGroup.Count-1;
  1105. curDiff := insideDiff;
  1106. end
  1107. else
  1108. break;
  1109. until false;
  1110. TComposedImageDifference(curDiff).StopAgglutinate;
  1111. if TComposedImageDifference(curDiff).Count = 0 then
  1112. begin
  1113. curGroup.Delete(curIndex);
  1114. if (curGroup = FUndoList) and (FUndoPos >= curIndex) then dec(FUndoPos);
  1115. ASomethingDone := false;
  1116. end;
  1117. end;
  1118. end;
  1119. procedure TLazPaintImage.DoEnd(var ACompose: TComposedImageDifference);
  1120. var
  1121. index: Integer;
  1122. begin
  1123. ACompose.StopAgglutinate;
  1124. if ACompose.Count = 0 then
  1125. begin
  1126. index := FUndoList.IndexOf(ACompose);
  1127. if index <> -1 then
  1128. begin
  1129. FUndoList.Delete(index);
  1130. if FUndoPos >= index then dec(FUndoPos);
  1131. ACompose := nil;
  1132. end;
  1133. end;
  1134. end;
  1135. procedure TLazPaintImage.ClearUndo;
  1136. begin
  1137. try
  1138. FUndoList.Clear;
  1139. FUndoPos := -1;
  1140. except on ex:exception do
  1141. MessagePopup(ex.Message, 4000);
  1142. end;
  1143. end;
  1144. procedure TLazPaintImage.CompressUndo;
  1145. var i: integer;
  1146. begin
  1147. for i := 0 to FUndoList.Count-1 do
  1148. if FUndoList[i].TryCompress then exit;
  1149. end;
  1150. function TLazPaintImage.UsedMemory: int64;
  1151. var i: integer;
  1152. begin
  1153. result := 0;
  1154. if Assigned(FUndoList) then
  1155. for i := 0 to FUndoList.Count-1 do
  1156. result += FUndoList[i].UsedMemory;
  1157. end;
  1158. function TLazPaintImage.CreateAction(AApplyOfsBefore: boolean;
  1159. AApplySelTransformBefore: boolean): TLayerAction;
  1160. begin
  1161. if not CheckNoAction(True) then
  1162. raise exception.Create(rsConflictingActions);
  1163. result := TLayerAction.Create(FCurrentState, AApplyOfsBefore, AApplySelTransformBefore);
  1164. result.OnNotifyChange:= @LayerActionNotifyChange;
  1165. result.OnDestroy:=@LayerActionDestroy;
  1166. result.OnNotifyUndo:=@LayerActionNotifyUndo;
  1167. FActionInProgress := result;
  1168. if Assigned(result.Prediff) then
  1169. InvalidateImageDifference(result.Prediff);
  1170. end;
  1171. procedure TLazPaintImage.ImageMayChange(ARect: TRect;
  1172. ADiscardSelectionLayerAfterMask: boolean);
  1173. begin
  1174. IntersectRect(ARect, ARect, rect(0,0,Width,Height));
  1175. if IsRectEmpty(ARect) then exit;
  1176. if ADiscardSelectionLayerAfterMask then DiscardSelectionLayerAfterMask;
  1177. FRenderUpdateRectInPicCoord := RectUnion(FRenderUpdateRectInPicCoord,ARect);
  1178. FRenderedImageInvalidated := RectUnion(FRenderedImageInvalidated, ARect);
  1179. FCurrentState.DiscardSelectionLayerBounds(ARect);
  1180. OnImageChanged.NotifyObservers;
  1181. end;
  1182. procedure TLazPaintImage.ImageMayChangeCompletely;
  1183. begin
  1184. ImageMayChange(rect(0,0,Width,Height));
  1185. RenderMayChangeCompletely;
  1186. end;
  1187. procedure TLazPaintImage.LayerMayChange(ALayer: TBGRABitmap; ARect: TRect);
  1188. var
  1189. ab: TAffineBox;
  1190. begin
  1191. If ALayer = nil then exit;
  1192. if ALayer = SelectionMask then
  1193. begin
  1194. SelectionMaskMayChange(ARect);
  1195. exit;
  1196. end;
  1197. if ALayer = SelectionLayerReadonly then
  1198. begin
  1199. DiscardSelectionLayerAfterMask;
  1200. ARect.Intersect(SelectionMaskBounds);
  1201. ab := SelectionTransform*TAffineBox.AffineBox(rectF(ARect.Left,ARect.Top,ARect.Right,ARect.Bottom));
  1202. ARect := ab.RectBounds;
  1203. end;
  1204. if ALayer = CurrentLayerReadOnly then
  1205. with LayerOffset[CurrentLayerIndex] do
  1206. OffsetRect(ARect,X,Y);
  1207. ImageMayChange(ARect);
  1208. end;
  1209. procedure TLazPaintImage.LayerMayChangeCompletely(ALayer: TBGRABitmap);
  1210. begin
  1211. If ALayer = nil then exit;
  1212. LayerMayChange(ALayer,rect(0,0,ALayer.Width,ALayer.Height));
  1213. end;
  1214. procedure TLazPaintImage.SelectionMaskMayChange(ARect: TRect);
  1215. var transfRect: TRect;
  1216. ab: TAffineBox;
  1217. begin
  1218. IntersectRect(ARect, ARect, rect(0,0,Width,Height));
  1219. if IsRectEmpty(ARect) then exit;
  1220. DiscardSelectionLayerAfterMask;
  1221. ab := SelectionTransform*TAffineBox.AffineBox(rectF(ARect.Left,ARect.Top,ARect.Right,ARect.Bottom));
  1222. transfRect := ab.RectBounds;
  1223. InflateRect(transfRect,1,1);
  1224. FRenderUpdateRectInPicCoord := RectUnion(FRenderUpdateRectInPicCoord,transfRect);
  1225. FCurrentState.DiscardSelectionMaskBounds(ARect);
  1226. if Assigned(FOnSelectionMaskChanged) then FOnSelectionMaskChanged(self, ARect);
  1227. if FCurrentState.SelectionLayer <> nil then
  1228. ImageMayChange(transfRect, False)
  1229. else
  1230. OnImageChanged.NotifyObservers;
  1231. end;
  1232. procedure TLazPaintImage.SelectionMaskMayChangeCompletely;
  1233. begin
  1234. DiscardSelectionLayerAfterMask;
  1235. FRenderUpdateRectInPicCoord := rect(0,0,Width,Height);
  1236. FCurrentState.DiscardSelectionMaskBoundsCompletely;
  1237. if Assigned(FOnSelectionMaskChanged) then FOnSelectionMaskChanged(self, rect(0,0,Width,Height));
  1238. if FCurrentState.SelectionLayer <> nil then
  1239. LayerMayChange(FCurrentState.SelectionLayer, rect(0,0,Width,Height))
  1240. else
  1241. OnImageChanged.NotifyObservers;
  1242. end;
  1243. procedure TLazPaintImage.RenderMayChange(ARect: TRect; APicCoords: boolean; ANotify: boolean);
  1244. begin
  1245. if APicCoords then
  1246. FRenderUpdateRectInPicCoord := RectUnion(FRenderUpdateRectInPicCoord,ARect)
  1247. else
  1248. FRenderUpdateRectInVSCoord := RectUnion(FRenderUpdateRectInVSCoord,ARect);
  1249. if ANotify and Assigned(OnImageRenderChanged) then
  1250. OnImageRenderChanged(self, false);
  1251. end;
  1252. procedure TLazPaintImage.RenderMayChangeCompletely(ANotify: boolean);
  1253. begin
  1254. FRenderUpdateRectInPicCoord := rect(-MaxLongint div 2,-MaxLongint div 2,MaxLongint div 2,MaxLongint div 2);
  1255. if ANotify and Assigned(OnImageRenderChanged) then
  1256. OnImageRenderChanged(self, true);
  1257. end;
  1258. procedure TLazPaintImage.LayerBlendMayChange(AIndex: integer);
  1259. var r, rSel: TRect;
  1260. begin
  1261. r := FCurrentState.LayerBitmap[AIndex].GetImageBounds;
  1262. with LayerOffset[AIndex] do OffsetRect(r, x,y);
  1263. if (AIndex = CurrentLayerIndex) and not SelectionMaskEmpty then
  1264. begin
  1265. rSel := TRect.Intersect(SelectionMaskBounds, SelectionLayerBounds);
  1266. rSel := SelectionMask.GetImageAffineBounds(SelectionTransform, rSel, false);
  1267. if not rSel.IsEmpty then
  1268. begin
  1269. if r.IsEmpty then r := rSel
  1270. else r := TRect.Union(r, rSel);
  1271. end;
  1272. end;
  1273. ImageMayChange(r);
  1274. end;
  1275. function TLazPaintImage.MakeLayeredBitmapAndSelectionCopy: TLayeredBitmapAndSelection;
  1276. begin
  1277. result.layeredBitmap := FCurrentState.GetLayeredBitmapCopy;
  1278. result.selection := DuplicateBitmap(SelectionMask);
  1279. result.selectionLayer := DuplicateBitmap(FCurrentState.SelectionLayer);
  1280. end;
  1281. {--------------------- Selection --------------------------------------}
  1282. function TLazPaintImage.SelectionMaskNil: boolean;
  1283. begin
  1284. result := (SelectionMask = nil);
  1285. end;
  1286. function TLazPaintImage.GetHeight: integer;
  1287. begin
  1288. result := FCurrentState.Height;
  1289. end;
  1290. function TLazPaintImage.GetSelectedImageLayer: TBGRABitmap;
  1291. begin
  1292. result := FCurrentState.SelectedImageLayer;
  1293. if (result = nil) and (NbLayers > 0) then
  1294. begin
  1295. SetCurrentLayerByIndex(0);
  1296. result := FCurrentState.SelectedImageLayer;
  1297. end;
  1298. end;
  1299. function TLazPaintImage.GetCurrentLayerIndex: integer;
  1300. begin
  1301. result := FCurrentState.SelectedImageLayerIndex;
  1302. if (result = -1) and (NbLayers > 0) then
  1303. begin
  1304. SetCurrentLayerByIndex(0);
  1305. result := 0;
  1306. end;
  1307. end;
  1308. function TLazPaintImage.GetCurrentFilenameUTF8: string;
  1309. begin
  1310. result := FCurrentState.filenameUTF8;
  1311. end;
  1312. function TLazPaintImage.GetCurrentLayerVisible: boolean;
  1313. var idx: integer;
  1314. begin
  1315. idx := CurrentLayerIndex;
  1316. if (idx < 0) or (idx >= NbLayers) then
  1317. result := false
  1318. else
  1319. result := LayerVisible[CurrentLayerIndex];
  1320. end;
  1321. procedure TLazPaintImage.DiscardSelectionLayerAfterMask;
  1322. begin
  1323. if FSelectionLayerAfterMaskDefined then
  1324. begin
  1325. FreeAndNil(FSelectionLayerAfterMask);
  1326. FSelectionLayerAfterMaskOffset := Point(0,0);
  1327. FSelectionLayerAfterMaskDefined := false;
  1328. end;
  1329. end;
  1330. function TLazPaintImage.GetDPI: integer;
  1331. begin
  1332. result := ScreenInfo.PixelsPerInchY;
  1333. end;
  1334. function TLazPaintImage.GetIsCursor: boolean;
  1335. begin
  1336. result := UTF8CompareText(ExtractFileExt(currentFilenameUTF8),'.cur')=0;
  1337. end;
  1338. function TLazPaintImage.GetIsIconCursor: boolean;
  1339. begin
  1340. result := SuggestImageFormat(currentFilenameUTF8) in [ifIco,ifCur];
  1341. end;
  1342. function TLazPaintImage.GetIsTiff: boolean;
  1343. begin
  1344. result := SuggestImageFormat(currentFilenameUTF8) = ifTiff;
  1345. end;
  1346. function TLazPaintImage.GetIsGif: boolean;
  1347. begin
  1348. result := SuggestImageFormat(currentFilenameUTF8) = ifGif;
  1349. end;
  1350. function TLazPaintImage.GetLayerBitmapById(AId: integer): TBGRABitmap;
  1351. begin
  1352. result := FCurrentState.LayerBitmapById[AId];
  1353. end;
  1354. function TLazPaintImage.GetLayerGuid(AIndex: integer): TGuid;
  1355. var
  1356. guidStr: RawByteString;
  1357. begin
  1358. guidStr := GetLayerRegistry(AIndex, 'guid');
  1359. if guidStr<>'' then
  1360. result := StringToGUID(guidStr)
  1361. else
  1362. begin
  1363. CreateGUID(result);
  1364. SetLayerRegistry(AIndex, 'guid', GUIDToString(result));
  1365. end;
  1366. end;
  1367. function TLazPaintImage.GetLayerId(AIndex: integer): integer;
  1368. begin
  1369. result := FCurrentState.LayerId[AIndex];
  1370. end;
  1371. function TLazPaintImage.GetLayerOriginal(AIndex: integer): TBGRALayerCustomOriginal;
  1372. begin
  1373. try
  1374. result := FCurrentState.LayerOriginal[AIndex];
  1375. except
  1376. on ex:exception do
  1377. begin
  1378. MessagePopup(rsErrorLoadingOriginal, 4000);
  1379. result := nil;
  1380. end;
  1381. end;
  1382. end;
  1383. function TLazPaintImage.GetLayerOriginalClass(AIndex: integer): TBGRALayerOriginalAny;
  1384. begin
  1385. result := FCurrentState.LayerOriginalClass[AIndex];
  1386. end;
  1387. function TLazPaintImage.GetLayerOriginalDefined(AIndex: integer): boolean;
  1388. begin
  1389. result := FCurrentState.LayerOriginalDefined[AIndex];
  1390. end;
  1391. function TLazPaintImage.GetLayerOriginalKnown(AIndex: integer): boolean;
  1392. begin
  1393. result := FCurrentState.LayerOriginalKnown[AIndex];
  1394. end;
  1395. function TLazPaintImage.GetLayerOriginalMatrix(AIndex: integer): TAffineMatrix;
  1396. begin
  1397. result := FCurrentState.LayerOriginalMatrix[AIndex];
  1398. end;
  1399. function TLazPaintImage.GetSelectionLayerEmpty: boolean;
  1400. begin
  1401. result := FCurrentState.SelectionLayerEmpty;
  1402. end;
  1403. function TLazPaintImage.GetSelectionMaskBounds: TRect;
  1404. begin
  1405. result := FCurrentState.GetSelectionMaskBounds;
  1406. end;
  1407. function TLazPaintImage.GetSelectionMaskEmpty: boolean;
  1408. begin
  1409. result := FCurrentState.SelectionMaskEmpty;
  1410. end;
  1411. function TLazPaintImage.GetSelectionTransform: TAffineMatrix;
  1412. begin
  1413. result := FCurrentState.SelectionTransform;
  1414. end;
  1415. procedure TLazPaintImage.LayeredActionDone(Sender: TObject);
  1416. begin
  1417. if Assigned(OnActionProgress) then
  1418. OnActionProgress(self, 100);
  1419. end;
  1420. procedure TLazPaintImage.LayeredActionProgress(ASender: TObject;
  1421. AProgressPercent: integer);
  1422. begin
  1423. if Assigned(OnActionProgress) then
  1424. OnActionProgress(self, AProgressPercent);
  1425. end;
  1426. procedure TLazPaintImage.LayeredSizeChanged(Sender: TObject);
  1427. begin
  1428. if Assigned(FOnSizeChanged) then
  1429. FOnSizeChanged(self);
  1430. end;
  1431. procedure TLazPaintImage.NeedSelectionLayerAfterMask;
  1432. var
  1433. bounds,
  1434. boundsAfter: TRect;
  1435. begin
  1436. if not FSelectionLayerAfterMaskDefined then
  1437. begin
  1438. if SelectionMaskEmpty or SelectionLayerIsEmpty then
  1439. FreeAndNil(FSelectionLayerAfterMask)
  1440. else
  1441. begin
  1442. bounds := SelectionLayerBounds;
  1443. FSelectionLayerAfterMask := SelectionLayerReadonly.GetPart(bounds) as TBGRABitmap;
  1444. FSelectionLayerAfterMask.ApplyMask(SelectionMask,
  1445. Rect(0,0,FSelectionLayerAfterMask.Width,FSelectionLayerAfterMask.Height),
  1446. bounds.TopLeft);
  1447. FSelectionLayerAfterMaskOffset := bounds.TopLeft;
  1448. boundsAfter := FSelectionLayerAfterMask.GetImageBounds;
  1449. if IsRectEmpty(boundsAfter) then FreeAndNil(FSelectionLayerAfterMask) else
  1450. if (boundsAfter.left > FSelectionLayerAfterMask.Width div 10) or (boundsAfter.right < FSelectionLayerAfterMask.Width*9 div 10) or
  1451. (boundsAfter.top > FSelectionLayerAfterMask.Height div 10) or (boundsAfter.bottom < FSelectionLayerAfterMask.Height*9 div 10) then
  1452. begin
  1453. BGRAReplace(FSelectionLayerAfterMask, FSelectionLayerAfterMask.GetPart(boundsAfter));
  1454. FSelectionLayerAfterMaskOffset.x += boundsAfter.Left;
  1455. FSelectionLayerAfterMaskOffset.y += boundsAfter.Top;
  1456. end;
  1457. end;
  1458. FSelectionLayerAfterMaskDefined := true;
  1459. end;
  1460. end;
  1461. function TLazPaintImage.GetBlendOperation(AIndex: integer): TBlendOperation;
  1462. begin
  1463. result := FCurrentState.BlendOperation[AIndex];
  1464. end;
  1465. function TLazPaintImage.GetEmpty: boolean;
  1466. begin
  1467. result := (NbLayers = 0) or ((NbLayers = 1) and FCurrentState.LayerBitmap[0].Empty);
  1468. end;
  1469. procedure TLazPaintImage.SetBlendOperation(AIndex: integer;
  1470. AValue: TBlendOperation);
  1471. begin
  1472. AddUndo(FCurrentState.SetBlendOp(AIndex,AValue));
  1473. LayerBlendMayChange(AIndex);
  1474. end;
  1475. procedure TLazPaintImage.SetCurrentFilenameUTF8(AValue: string);
  1476. var oldIsIco: boolean;
  1477. begin
  1478. oldIsIco := IsIconCursor;
  1479. FCurrentState.filenameUTF8 := AValue;
  1480. if oldIsIco <> IsIconCursor then ImageMayChangeCompletely;
  1481. if Assigned(FOnCurrentFilenameChanged) then
  1482. FOnCurrentFilenameChanged(self);
  1483. end;
  1484. function TLazPaintImage.SetCurrentLayerByIndex(AValue: integer): boolean;
  1485. begin
  1486. if AValue = FCurrentState.SelectedImageLayerIndex then exit(true);
  1487. if (AValue < 0) or (AValue >= NbLayers) then exit(false);
  1488. if not CheckNoAction then
  1489. begin
  1490. result := false;
  1491. exit;
  1492. end;
  1493. if assigned(OnSelectedLayerIndexChanging) then OnSelectedLayerIndexChanging(self);
  1494. FCurrentState.SelectedImageLayerIndex := AValue;
  1495. if assigned(OnSelectedLayerIndexChanged) then OnSelectedLayerIndexChanged(self);
  1496. ImageMayChangeCompletely;
  1497. result := true;
  1498. end;
  1499. function TLazPaintImage.SelectLayerContainingPixelAt(APicturePos: TPoint): boolean;
  1500. var
  1501. i: Integer;
  1502. ofs: TPoint;
  1503. begin
  1504. for i := NbLayers-1 downto 0 do
  1505. begin
  1506. ofs := LayerOffset[i];
  1507. if LayerBitmap[i].GetPixel(APicturePos.x - ofs.x, APicturePos.y - ofs.y).alpha > 0 then
  1508. begin
  1509. result := SetCurrentLayerByIndex(i);
  1510. exit;
  1511. end;
  1512. end;
  1513. result := false;
  1514. end;
  1515. procedure TLazPaintImage.SetLayerOffset(AIndex: integer; AValue: TPoint;
  1516. APrecomputedLayerBounds: TRect);
  1517. var
  1518. discardOrig: TDiscardOriginalStateDifference;
  1519. comb: TComposedImageDifference;
  1520. begin
  1521. OffsetRect(APrecomputedLayerBounds, LayerOffset[AIndex].x,LayerOffset[AIndex].y);
  1522. ImageMayChange(APrecomputedLayerBounds);
  1523. OffsetRect(APrecomputedLayerBounds, -LayerOffset[AIndex].x,-LayerOffset[AIndex].y);
  1524. if FCurrentState.LayerOriginalDefined[AIndex] then
  1525. begin
  1526. discardOrig := TDiscardOriginalStateDifference.Create(FCurrentState,AIndex);
  1527. discardOrig.ApplyTo(FCurrentState);
  1528. comb := TComposedImageDifference.Create;
  1529. comb.Add(discardOrig);
  1530. comb.Add(FCurrentState.SetLayerOffset(AIndex,AValue));
  1531. AddUndo(comb);
  1532. end else
  1533. AddUndo(FCurrentState.SetLayerOffset(AIndex,AValue));
  1534. OffsetRect(APrecomputedLayerBounds, LayerOffset[AIndex].x,LayerOffset[AIndex].y);
  1535. ImageMayChange(APrecomputedLayerBounds);
  1536. OffsetRect(APrecomputedLayerBounds, -LayerOffset[AIndex].x,-LayerOffset[AIndex].y);
  1537. end;
  1538. function TLazPaintImage.CheckNoAction(ASilent: boolean): boolean;
  1539. begin
  1540. result := true;
  1541. if FActionInProgress <> nil then
  1542. begin
  1543. FActionInProgress.TryStop;
  1544. if FActionInProgress <> nil then
  1545. begin
  1546. if Assigned(FOnQueryExitToolHandler) then
  1547. FOnQueryExitToolHandler(self);
  1548. if FActionInProgress <> nil then
  1549. begin
  1550. if not ASilent then MessagePopup(rsActionInProgress,2000);
  1551. result := false;
  1552. end;
  1553. end;
  1554. end;
  1555. end;
  1556. function TLazPaintImage.CanDuplicateFrame: boolean;
  1557. begin
  1558. result := IsGif or IsTiff;
  1559. end;
  1560. function TLazPaintImage.CanHaveFrames: boolean;
  1561. begin
  1562. result := IsGif or IsTiff or IsIconCursor;
  1563. end;
  1564. procedure TLazPaintImage.ZoomFit;
  1565. begin
  1566. if Assigned(Zoom) then Zoom.ZoomFit(Width,Height);
  1567. end;
  1568. procedure TLazPaintImage.ResetRenderUpdateRect;
  1569. begin
  1570. FRenderUpdateRectInPicCoord := rect(0,0,0,0);
  1571. FRenderUpdateRectInVSCoord := rect(0,0,0,0);
  1572. end;
  1573. function TLazPaintImage.GetSelectionMask: TBGRABitmap;
  1574. begin
  1575. result := FCurrentState.SelectionMask;
  1576. end;
  1577. function TLazPaintImage.GetLayerBitmap(AIndex: integer): TBGRABitmap;
  1578. begin
  1579. result := FCurrentState.LayerBitmap[AIndex];
  1580. end;
  1581. function TLazPaintImage.GetLayerName(AIndex: integer): string;
  1582. begin
  1583. result := FCurrentState.LayerName[AIndex];
  1584. end;
  1585. function TLazPaintImage.GetLayerOffset(AIndex: integer): TPoint;
  1586. begin
  1587. result := FCurrentState.LayerOffset[AIndex];
  1588. end;
  1589. function TLazPaintImage.GetLayerOpacity(AIndex: integer): byte;
  1590. begin
  1591. result := FCurrentState.LayerOpacity[AIndex];
  1592. end;
  1593. function TLazPaintImage.GetLayerVisible(AIndex: integer): boolean;
  1594. begin
  1595. result := FCurrentState.LayerVisible[AIndex];
  1596. end;
  1597. function TLazPaintImage.GetNbLayers: integer;
  1598. begin
  1599. result := FCurrentState.NbLayers;
  1600. end;
  1601. function TLazPaintImage.GetRenderedImage: TBGRABitmap;
  1602. var
  1603. ofs: TPoint;
  1604. temp: TBGRABitmap;
  1605. rectOutput, rLayer: TRect;
  1606. actualTransformation: TAffineMatrix;
  1607. selectionScanner: TBGRACustomScanner;
  1608. selFilter: TResampleFilter;
  1609. begin
  1610. if (NbLayers = 1) and (LayerOpacity[CurrentLayerIndex] = 255) and
  1611. (LayerOffset[CurrentLayerIndex].X = 0) and (LayerOffset[CurrentLayerIndex].Y = 0) and
  1612. (LayerBitmap[CurrentLayerIndex].Width = Width) and (LayerBitmap[CurrentLayerIndex].Height = Height) and
  1613. LayerVisible[CurrentLayerIndex] and ((SelectionMask = nil) or (SelectionLayerReadonly = nil)) then
  1614. exit(LayerBitmap[CurrentLayerIndex])
  1615. else
  1616. if (FRenderedImage = nil) or ((FRenderedImageInvalidated.Right > FRenderedImageInvalidated.Left) and
  1617. (FRenderedImageInvalidated.Bottom > FRenderedImageInvalidated.Top)) then
  1618. begin
  1619. if FCurrentState = nil then
  1620. begin
  1621. FreeAndNil(FRenderedImage);
  1622. result := nil;
  1623. exit;
  1624. end;
  1625. PrepareForRendering;
  1626. selectionScanner := nil;
  1627. //if there is an overlapping selection, then we must draw it on current layer
  1628. if LayerVisible[CurrentLayerIndex] and (LayerOpacity[CurrentLayerIndex] > 0) and
  1629. (SelectionMask <> nil) and (SelectionLayerReadonly <> nil) then
  1630. begin
  1631. if not SelectionMaskEmpty and not SelectionLayerIsEmpty then
  1632. begin
  1633. if not TBGRABitmap.IsAffineRoughlyTranslation(SelectionTransform, SelectionMaskBounds) then
  1634. begin
  1635. NeedSelectionLayerAfterMask;
  1636. actualTransformation := SelectionTransform*AffineMatrixTranslation(FSelectionLayerAfterMaskOffset.X,FSelectionLayerAfterMaskOffset.Y);
  1637. rectOutput := SelectionMask.GetImageAffineBounds(actualTransformation, FSelectionLayerAfterMask.ClipRect);
  1638. rectOutput.Intersect(rect(0,0,self.Width,self.Height));
  1639. if not rectOutput.IsEmpty then
  1640. begin
  1641. if rectOutput.Width*rectOutput.Height > 640*480 then
  1642. selFilter := rfBox else selFilter := rfCosine;
  1643. selectionScanner := TBGRAAffineBitmapTransform.Create(
  1644. FSelectionLayerAfterMask, false, selFilter);
  1645. TBGRAAffineBitmapTransform(selectionScanner).ViewMatrix := actualTransformation;
  1646. FCurrentState.LayeredBitmap.SelectionScanner := selectionScanner;
  1647. FCurrentState.LayeredBitmap.SelectionRect:= rectOutput;
  1648. FCurrentState.LayeredBitmap.SelectionScannerOffset:= Point(0, 0);
  1649. FCurrentState.LayeredBitmap.SelectionLayerIndex:= CurrentLayerIndex;
  1650. end;
  1651. end else
  1652. begin
  1653. DiscardSelectionLayerAfterMask;
  1654. rectOutput := TRect.Intersect(SelectionLayerBounds, SelectionMaskBounds);
  1655. ofs := Point(round(SelectionTransform[1, 3]), round(SelectionTransform[2, 3]));
  1656. rectOutput.Offset(ofs.x, ofs.y);
  1657. rectOutput.Intersect(rect(0,0,self.Width,self.Height));
  1658. if not IsRectEmpty(rectOutput) then
  1659. begin
  1660. selectionScanner := TBGRATextureMaskScanner.Create(SelectionMask,
  1661. Point(0,0), FCurrentState.SelectionLayer);
  1662. FCurrentState.LayeredBitmap.SelectionScanner := selectionScanner;
  1663. FCurrentState.LayeredBitmap.SelectionRect:= rectOutput;
  1664. FCurrentState.LayeredBitmap.SelectionScannerOffset:= Point(-ofs.x, -ofs.y);
  1665. FCurrentState.LayeredBitmap.SelectionLayerIndex:= CurrentLayerIndex;
  1666. end;
  1667. end;
  1668. end;
  1669. end;
  1670. if (FRenderedImage <> nil) and ((FRenderedImage.Width <> Width) or (FRenderedImage.Height <> Height)) then
  1671. FreeAndNil(FRenderedImage);
  1672. if FRenderedImage = nil then FRenderedImage := TBGRABitmap.Create(Width,Height);
  1673. if IsIconCursor then
  1674. begin
  1675. temp := FCurrentState.ComputeFlatImage(FRenderedImageInvalidated,0,NbLayers-1,True);
  1676. FRenderedImage.PutImage(FRenderedImageInvalidated.Left,FRenderedImageInvalidated.Top, temp, dmSet);
  1677. if temp.XorMask <> nil then
  1678. begin
  1679. FRenderedImage.NeedXorMask;
  1680. FRenderedImage.XorMask.PutImage(FRenderedImageInvalidated.Left,FRenderedImageInvalidated.Top, temp.XorMask, dmSet);
  1681. end else
  1682. FRenderedImage.DiscardXorMask;
  1683. temp.Free;
  1684. end else
  1685. begin
  1686. FRenderedImage.ClipRect := FRenderedImageInvalidated;
  1687. FRenderedImage.DiscardXorMask;
  1688. if (NbLayers = 1) and (FCurrentState.LayeredBitmap.SelectionScanner = nil) then
  1689. begin
  1690. if (LayerOpacity[0] > 0) and LayerVisible[0] then
  1691. begin
  1692. rLayer := RectWithSize(LayerOffset[0].X, LayerOffset[0].Y, LayerBitmap[0].Width, LayerBitmap[0].Height);
  1693. if rLayer.Top > FRenderedImageInvalidated.Top then
  1694. FRenderedImage.EraseRect(FRenderedImageInvalidated.Left, FRenderedImageInvalidated.Top,
  1695. FRenderedImageInvalidated.Right, rLayer.Top, 255);
  1696. if rLayer.Left > FRenderedImageInvalidated.Left then
  1697. FRenderedImage.EraseRect(FRenderedImageInvalidated.Left, rLayer.Top,
  1698. rLayer.Left, rLayer.Bottom, 255);
  1699. FRenderedImage.PutImage(rLayer.Left, rLayer.Top, LayerBitmap[0], dmSet);
  1700. FRenderedImage.ApplyGlobalOpacity(rLayer, LayerOpacity[0]);
  1701. if rLayer.Right < FRenderedImageInvalidated.Right then
  1702. FRenderedImage.EraseRect(rLayer.Right, rLayer.Top,
  1703. FRenderedImageInvalidated.Right, rLayer.Bottom, 255);
  1704. if rLayer.Bottom < FRenderedImageInvalidated.Bottom then
  1705. FRenderedImage.EraseRect(FRenderedImageInvalidated.Left, rLayer.Bottom,
  1706. FRenderedImageInvalidated.Right, FRenderedImageInvalidated.Bottom, 255);
  1707. end else
  1708. FRenderedImage.EraseRect(FRenderedImageInvalidated, 255);
  1709. end else
  1710. begin
  1711. FRenderedImage.FillRect(FRenderedImageInvalidated, BGRAPixelTransparent, dmSet);
  1712. FCurrentState.DrawLayers(FRenderedImage, 0, 0, False, true);
  1713. end;
  1714. FRenderedImage.NoClip;
  1715. end;
  1716. FCurrentState.LayeredBitmap.DiscardSelection;
  1717. selectionScanner.Free;
  1718. FRenderedImageInvalidated := EmptyRect; //up to date
  1719. end;
  1720. result := FRenderedImage;
  1721. end;
  1722. function TLazPaintImage.GetSelectedLayerPixel(X, Y: Integer): TBGRAPixel;
  1723. begin
  1724. result := GetSelectedImageLayer.GetPixel(X,Y);
  1725. end;
  1726. function TLazPaintImage.GetSelectionLayerBounds: TRect;
  1727. begin
  1728. result := FCurrentState.GetSelectionLayerBounds;
  1729. end;
  1730. function TLazPaintImage.GetWidth: integer;
  1731. begin
  1732. result := FCurrentState.Width;
  1733. end;
  1734. function TLazPaintImage.GetZoomFactor: single;
  1735. begin
  1736. if Assigned(Zoom) then result := Zoom.Factor else result := 1;
  1737. end;
  1738. procedure TLazPaintImage.Assign(const AValue: TBGRABitmap; AOwned: boolean; AUndoable: boolean;
  1739. ACaption: string; AOpacity: byte);
  1740. var layeredBmp: TBGRALayeredBitmap;
  1741. mask: TBGRABitmap;
  1742. begin
  1743. if not CheckNoAction then exit;
  1744. CursorHotSpot := AValue.HotSpot;
  1745. layeredBmp := TBGRALayeredBitmap.Create(AValue.Width,AValue.Height);
  1746. if AOwned then
  1747. begin
  1748. layeredBmp.AddOwnedLayer(AValue);
  1749. if Assigned(AValue.XorMask) then
  1750. begin
  1751. mask := AValue.XorMask.Duplicate as TBGRABitmap;
  1752. mask.AlphaFill(255);
  1753. mask.ReplaceColor(BGRABlack,BGRAPixelTransparent);
  1754. layeredBmp.LayerName[layeredBmp.AddOwnedLayer(mask,boXor)] := 'Xor';
  1755. AValue.DiscardXorMask;
  1756. end;
  1757. end
  1758. else
  1759. begin
  1760. layeredBmp.AddLayer(AValue);
  1761. if Assigned(AValue.XorMask) then
  1762. begin
  1763. mask := AValue.XorMask.Duplicate as TBGRABitmap;
  1764. mask.AlphaFill(255);
  1765. mask.ReplaceColor(BGRABlack,BGRAPixelTransparent);
  1766. layeredBmp.LayerName[layeredBmp.AddOwnedLayer(mask,boXor)] := 'Xor';
  1767. end;
  1768. end;
  1769. if ACaption = '' then ACaption := rsLayer+'1';
  1770. layeredBmp.LayerName[0] := ACaption;
  1771. layeredBmp.LayerOpacity[0] := AOpacity;
  1772. Assign(layeredBmp,True,AUndoable);
  1773. end;
  1774. procedure TLazPaintImage.Assign(const AValue: TBGRACustomLayeredBitmap;
  1775. AOwned: boolean; AUndoable: boolean);
  1776. var idx: integer;
  1777. begin
  1778. if not CheckNoAction then exit;
  1779. if AValue.NbLayers = 0 then
  1780. begin
  1781. Assign(TBGRABitmap.Create(AValue.Width,AValue.Height),True,AUndoable);
  1782. if AOwned then AValue.Free;
  1783. exit;
  1784. end;
  1785. if AUndoable then
  1786. begin
  1787. idx := FCurrentState.SelectedImageLayerIndex;
  1788. if idx > AValue.NbLayers-1 then idx := 0;
  1789. AddUndo(FCurrentState.AssignWithUndo(AValue, AOwned, idx, nil, nil));
  1790. ImageMayChangeCompletely;
  1791. SelectionMaskMayChangeCompletely;
  1792. end else
  1793. begin
  1794. FCurrentState.Assign(AValue, AOwned);
  1795. FCurrentState.RemoveSelection;
  1796. FCurrentState.saved := false;
  1797. LayeredBitmapReplaced;
  1798. ImageMayChangeCompletely;
  1799. SelectionMaskMayChangeCompletely;
  1800. ClearUndo;
  1801. end;
  1802. end;
  1803. procedure TLazPaintImage.Assign(const AValue: TLayeredBitmapAndSelection;
  1804. AOwned: boolean; AUndoable: boolean);
  1805. begin
  1806. if not CheckNoAction then exit;
  1807. if AUndoable then
  1808. begin
  1809. AddUndo(FCurrentState.AssignWithUndo(AValue.layeredBitmap,AOwned,FCurrentState.SelectedImageLayerIndex,AValue.selection,AValue.selectionLayer));
  1810. ImageMayChangeCompletely;
  1811. SelectionMaskMayChangeCompletely;
  1812. end
  1813. else
  1814. begin
  1815. with AValue do
  1816. begin
  1817. Assign(layeredBitmap,AOwned,False);
  1818. if not AOwned then
  1819. ReplaceCurrentSelectionWithoutUndo(selection.Duplicate(True) as TBGRABitmap)
  1820. else
  1821. ReplaceCurrentSelectionWithoutUndo(selection);
  1822. FCurrentState.ReplaceSelectionLayer(selectionLayer,AOwned);
  1823. end;
  1824. end;
  1825. OnImageChanged.NotifyObservers;
  1826. end;
  1827. procedure TLazPaintImage.Draw(ADest: TBGRABitmap; x, y: integer);
  1828. var bmp: TBGRABitmap;
  1829. begin
  1830. if (NbLayers = 1) and ((SelectionMask = nil) or (GetSelectedImageLayer = nil)) then
  1831. begin
  1832. if FCurrentState <> nil then
  1833. FCurrentState.DrawLayers(ADest,x,y,IsIconCursor);
  1834. end else
  1835. begin
  1836. bmp := RenderedImage;
  1837. if bmp <> nil then
  1838. if FCurrentState.LinearBlend then
  1839. ADest.PutImage(x,y,bmp,dmLinearBlend)
  1840. else
  1841. ADest.PutImage(x,y,bmp,dmDrawWithTransparency);
  1842. end;
  1843. end;
  1844. procedure TLazPaintImage.AddNewLayer;
  1845. begin
  1846. if not CheckNoAction then exit;
  1847. try
  1848. AddUndo(FCurrentState.AddNewLayer(TBGRABitmap.Create(1,1), '', Point(0,0), boTransparent));
  1849. LayerBlendMayChange(CurrentLayerIndex);
  1850. except on ex: exception do NotifyException('AddNewLayer',ex);
  1851. end;
  1852. OnImageChanged.NotifyObservers;
  1853. end;
  1854. procedure TLazPaintImage.AddNewLayer(AOriginal: TBGRALayerCustomOriginal;
  1855. AName: string; ABlendOp: TBlendOperation; AMatrix: TAffineMatrix; AOpacity: byte);
  1856. begin
  1857. if not CheckNoAction then exit;
  1858. try
  1859. AddUndo(FCurrentState.AddNewLayer(AOriginal, AName, ABlendOp, AMatrix, AOpacity));
  1860. ImageMayChangeCompletely;
  1861. except on ex: exception do NotifyException('AddNewLayer',ex);
  1862. end;
  1863. OnImageChanged.NotifyObservers;
  1864. end;
  1865. procedure TLazPaintImage.AddNewLayer(ALayer: TBGRABitmap; AName: string; ABlendOp: TBlendOperation; AOpacity: byte);
  1866. var temp: TBGRAbitmap;
  1867. begin
  1868. if not CheckNoAction then exit;
  1869. try
  1870. If (ALayer.Width > Width) or (ALayer.Height > Height) then
  1871. begin
  1872. temp := TBGRABitmap.Create(Width,Height);
  1873. temp.PutImage((Width-ALayer.Width) div 2, (Height-ALayer.Height) div 2,ALayer,dmSet);
  1874. ALayer.Free;
  1875. ALayer := temp;
  1876. end;
  1877. AddUndo(FCurrentState.AddNewLayer(ALayer, AName,
  1878. Point((Width - ALayer.Width) div 2, (Height - ALayer.Height) div 2),
  1879. ABlendOp, AOpacity));
  1880. ImageMayChangeCompletely;
  1881. except on ex: exception do NotifyException('AddNewLayer',ex);
  1882. end;
  1883. OnImageChanged.NotifyObservers;
  1884. end;
  1885. procedure TLazPaintImage.AddNewLayer(ALayer: TBGRABitmap; AName: string;
  1886. AOffset: TPoint; ABlendOp: TBlendOperation; AOpacity: byte);
  1887. begin
  1888. if not CheckNoAction then exit;
  1889. try
  1890. AddUndo(FCurrentState.AddNewLayer(ALayer, AName, AOffset, ABlendOp, AOpacity));
  1891. ImageMayChangeCompletely;
  1892. except on ex: exception do NotifyException('AddNewLayer',ex);
  1893. end;
  1894. OnImageChanged.NotifyObservers;
  1895. end;
  1896. procedure TLazPaintImage.DuplicateLayer;
  1897. begin
  1898. if not CheckNoAction then exit;
  1899. try
  1900. AddUndo(FCurrentState.DuplicateLayer);
  1901. LayerBlendMayChange(CurrentLayerIndex);
  1902. OnImageChanged.NotifyObservers;
  1903. except on ex: exception do
  1904. begin
  1905. NotifyException('DuplicateLayer',ex);
  1906. ImageMayChangeCompletely;
  1907. end;
  1908. end;
  1909. end;
  1910. procedure TLazPaintImage.RasterizeLayer;
  1911. begin
  1912. if LayerOriginalDefined[CurrentLayerIndex] then
  1913. try
  1914. AddUndo(FCurrentState.DiscardOriginal(True));
  1915. OnImageChanged.NotifyObservers;
  1916. except on ex: exception do NotifyException('RasterizeLayer',ex);
  1917. end;
  1918. end;
  1919. procedure TLazPaintImage.MergeLayerOver;
  1920. var
  1921. remove: TCustomImageDifference;
  1922. nextId: LongInt;
  1923. begin
  1924. if CurrentLayerIndex = 0 then exit;
  1925. if not CheckNoAction then exit;
  1926. try
  1927. if LayerBitmap[CurrentLayerIndex].Empty then
  1928. begin
  1929. nextId := LayerId[CurrentLayerIndex-1];
  1930. remove := FCurrentState.RemoveLayer;
  1931. if remove is TRemoveLayerStateDifference then
  1932. TRemoveLayerStateDifference(remove).nextActiveLayerId:= nextId;
  1933. AddUndo(remove);
  1934. end else
  1935. AddUndo(FCurrentState.MergerLayerOver(CurrentLayerIndex));
  1936. except on ex: exception do NotifyException('MergeLayerOver',ex);
  1937. end;
  1938. ImageMayChangeCompletely;
  1939. end;
  1940. procedure TLazPaintImage.PrepareForRendering;
  1941. begin
  1942. if FCurrentState <> nil then FCurrentState.PrepareForRendering;
  1943. end;
  1944. function TLazPaintImage.MakeLayeredBitmapCopy: TBGRALayeredBitmap;
  1945. begin
  1946. result := FCurrentState.GetLayeredBitmapCopy;
  1947. end;
  1948. function TLazPaintImage.ComputeFlatImage(AFromLayer, AToLayer: integer;
  1949. ASeparateXorMask: boolean): TBGRABitmap;
  1950. begin
  1951. result := FCurrentState.ComputeFlatImage(AFromLayer,AToLayer,ASeparateXorMask);
  1952. end;
  1953. procedure TLazPaintImage.MoveLayer(AFromIndex, AToIndex: integer);
  1954. begin
  1955. if (AFromIndex < 0) or (AFromIndex >= NbLayers) then
  1956. raise exception.Create('Index out of bounds');
  1957. if AToIndex < 0 then AToIndex := 0;
  1958. if AToIndex >= NbLayers then AToIndex := NbLayers-1;
  1959. if AToIndex = AFromIndex then exit;
  1960. if not CheckNoAction then exit;
  1961. try
  1962. LayerBlendMayChange(AToIndex);
  1963. AddUndo(FCurrentState.MoveLayer(AFromIndex,AToIndex));
  1964. LayerBlendMayChange(AToIndex);
  1965. except on ex: exception do
  1966. begin
  1967. NotifyException('MoveLayer',ex);
  1968. ImageMayChangeCompletely;
  1969. end;
  1970. end;
  1971. end;
  1972. procedure TLazPaintImage.RemoveLayer;
  1973. begin
  1974. if not CheckNoAction then exit;
  1975. try
  1976. AddUndo(FCurrentState.RemoveLayer);
  1977. except on ex: exception do NotifyException('RemoveLayer',ex);
  1978. end;
  1979. ImageMayChangeCompletely;
  1980. end;
  1981. procedure TLazPaintImage.ClearLayer;
  1982. begin
  1983. if not CheckNoAction then exit;
  1984. try
  1985. AddUndo(FCurrentState.ClearLayer);
  1986. except on ex: exception do NotifyException('ClearLayer',ex);
  1987. end;
  1988. ImageMayChangeCompletely;
  1989. end;
  1990. procedure TLazPaintImage.SaveOriginalToStream(AStream: TStream);
  1991. begin
  1992. FCurrentState.LayeredBitmap.SaveOriginalToStream(
  1993. FCurrentState.LayeredBitmap.LayerOriginalGuid[CurrentLayerIndex],
  1994. AStream);
  1995. end;
  1996. procedure TLazPaintImage.SwapRedBlue;
  1997. begin
  1998. if not CheckNoAction then exit;
  1999. try
  2000. AddUndo(FCurrentState.SwapRedBlue);
  2001. except on ex: exception do NotifyException('SwapRedBlue',ex);
  2002. end;
  2003. ImageMayChangeCompletely;
  2004. end;
  2005. procedure TLazPaintImage.LinearNegativeAll;
  2006. begin
  2007. if not CheckNoAction then exit;
  2008. try
  2009. AddUndo(FCurrentState.LinearNegative);
  2010. except on ex: exception do NotifyException('LinearNegativeAll',ex);
  2011. end;
  2012. ImageMayChangeCompletely;
  2013. end;
  2014. procedure TLazPaintImage.NegativeAll;
  2015. begin
  2016. if not CheckNoAction then exit;
  2017. try
  2018. AddUndo(FCurrentState.Negative);
  2019. except on ex: exception do NotifyException('NegativeAll',ex);
  2020. end;
  2021. ImageMayChangeCompletely;
  2022. end;
  2023. procedure TLazPaintImage.HorizontalFlip;
  2024. begin
  2025. if not CheckNoAction then exit;
  2026. try
  2027. AddUndo(FCurrentState.HorizontalFlip);
  2028. except on ex: exception do NotifyException('HorizontalFlip',ex);
  2029. end;
  2030. ImageMayChangeCompletely;
  2031. end;
  2032. procedure TLazPaintImage.HorizontalFlip(ALayerIndex: integer);
  2033. begin
  2034. if not CheckNoAction then exit;
  2035. try
  2036. AddUndo(FCurrentState.HorizontalFlip(ALayerIndex));
  2037. except on ex: exception do NotifyException('HorizontalFlip',ex);
  2038. end;
  2039. ImageMayChangeCompletely;
  2040. end;
  2041. procedure TLazPaintImage.VerticalFlip;
  2042. begin
  2043. if not CheckNoAction then exit;
  2044. try
  2045. AddUndo(FCurrentState.VerticalFlip);
  2046. except on ex: exception do NotifyException('VerticalFlip',ex);
  2047. end;
  2048. ImageMayChangeCompletely;
  2049. end;
  2050. procedure TLazPaintImage.VerticalFlip(ALayerIndex: integer);
  2051. begin
  2052. if not CheckNoAction then exit;
  2053. try
  2054. AddUndo(FCurrentState.VerticalFlip(ALayerIndex));
  2055. except on ex: exception do NotifyException('VerticalFlip',ex);
  2056. end;
  2057. ImageMayChangeCompletely;
  2058. end;
  2059. procedure TLazPaintImage.RotateCW;
  2060. begin
  2061. if not CheckNoAction then exit;
  2062. try
  2063. AddUndo(FCurrentState.RotateCW);
  2064. except on ex: exception do NotifyException('RotateCW',ex);
  2065. end;
  2066. ImageMayChangeCompletely;
  2067. SelectionMaskMayChangeCompletely;
  2068. end;
  2069. procedure TLazPaintImage.RotateCCW;
  2070. begin
  2071. if not CheckNoAction then exit;
  2072. try
  2073. AddUndo(FCurrentState.RotateCCW);
  2074. except on ex: exception do NotifyException('RotateCCW',ex);
  2075. end;
  2076. ImageMayChangeCompletely;
  2077. SelectionMaskMayChangeCompletely;
  2078. end;
  2079. procedure TLazPaintImage.Rotate180;
  2080. begin
  2081. if not CheckNoAction then exit;
  2082. try
  2083. AddUndo(FCurrentState.Rotate180);
  2084. except on ex: exception do NotifyException('Rotate180',ex);
  2085. end;
  2086. ImageMayChangeCompletely;
  2087. SelectionMaskMayChangeCompletely;
  2088. end;
  2089. function TLazPaintImage.CheckCurrentLayerVisible: boolean;
  2090. begin
  2091. result := CurrentLayerVisible;
  2092. if not result then
  2093. MessagePopup(rsMustShowLayer,2000);
  2094. end;
  2095. procedure TLazPaintImage.ReplaceCurrentSelectionWithoutUndo(const AValue: TBGRABitmap);
  2096. begin
  2097. if FCurrentState.SelectionMask = AValue then exit;
  2098. FCurrentState.SelectionMask.Free;
  2099. FCurrentState.SelectionMask := AValue;
  2100. SelectionMaskMayChangeCompletely;
  2101. end;
  2102. procedure TLazPaintImage.LayerActionNotifyChange(ASender: TObject;
  2103. ALayer: TBGRABitmap; ARect: TRect);
  2104. begin
  2105. LayerMayChange(ALayer, ARect);
  2106. end;
  2107. procedure TLazPaintImage.LayerActionDestroy(Sender: TObject);
  2108. begin
  2109. if FActionInProgress = Sender then
  2110. FActionInProgress := nil;
  2111. end;
  2112. procedure TLazPaintImage.ReleaseEmptySelection;
  2113. begin
  2114. if SelectionMaskEmpty and SelectionLayerIsEmpty then
  2115. FCurrentState.ReplaceSelection(nil,nil);
  2116. end;
  2117. function TLazPaintImage.CurrentLayerEmpty: boolean;
  2118. var
  2119. selLayer: TBGRABitmap;
  2120. begin
  2121. selLayer := GetSelectedImageLayer;
  2122. result := not Assigned(selLayer) or selLayer.Empty;
  2123. end;
  2124. function TLazPaintImage.CurrentLayerTransparent: boolean;
  2125. var
  2126. r: TRect;
  2127. idx: Integer;
  2128. y, x: LongInt;
  2129. p: PBGRAPixel;
  2130. begin
  2131. r := rect(0,0, Width, height);
  2132. idx := CurrentLayerIndex;
  2133. if RectWithSize(LayerOffset[idx].x, LayerOffset[idx].y,
  2134. LayerBitmap[idx].Width, LayerBitmap[idx].Height).Contains(r) then
  2135. begin
  2136. r.Offset(-LayerOffset[idx].x, -LayerOffset[idx].y);
  2137. for y := r.Top to r.Bottom-1 do
  2138. begin
  2139. p := LayerBitmap[idx].ScanLine[y] + r.Left;
  2140. for x := r.Left to r.Right-1 do
  2141. begin
  2142. if p^.alpha <> 255 then exit(true);
  2143. inc(p);
  2144. end;
  2145. end;
  2146. result := false;
  2147. end else
  2148. result := true;
  2149. end;
  2150. function TLazPaintImage.CurrentLayerEquals(AColor: TBGRAPixel): boolean;
  2151. begin
  2152. result := GetSelectedImageLayer.Equals(AColor);
  2153. end;
  2154. function TLazPaintImage.GetSelectionMaskCenter: TPointF;
  2155. begin
  2156. result := ugraph.GetSelectionCenter(SelectionMask);
  2157. end;
  2158. procedure TLazPaintImage.SaveSelectionMaskToFileUTF8(AFilename: string);
  2159. var s: TStream;
  2160. begin
  2161. if SelectionMask = nil then exit;
  2162. try
  2163. s := FileManager.CreateFileStream(AFilename, fmCreate);
  2164. try
  2165. SelectionMask.SaveToStreamAs(s, SuggestImageFormat(AFilename));
  2166. finally
  2167. s.Free;
  2168. end;
  2169. except on ex: exception do NotifyException('SaveSelectionToFile',ex);
  2170. end;
  2171. end;
  2172. function TLazPaintImage.SelectionMaskReadonly: TBGRABitmap;
  2173. begin
  2174. result := SelectionMask;
  2175. end;
  2176. function TLazPaintImage.SelectionLayerReadonly: TBGRABitmap;
  2177. begin
  2178. result := FCurrentState.SelectionLayer;
  2179. end;
  2180. function TLazPaintImage.CurrentLayerReadOnly: TBGRABitmap;
  2181. begin
  2182. result := GetSelectedImageLayer;
  2183. end;
  2184. procedure TLazPaintImage.SetLayerRegistry(ALayerIndex: integer;
  2185. AIdentifier: string; AValue: RawByteString);
  2186. begin
  2187. AddUndo(TSetLayerRegistryDifference.Create(FCurrentState, LayerId[ALayerIndex], AIdentifier, AValue, true));
  2188. end;
  2189. function TLazPaintImage.GetLayerRegistry(ALayerIndex: integer;
  2190. AIdentifier: string): RawByteString;
  2191. begin
  2192. result := FCurrentState.LayeredBitmap.GetLayerRegistry(ALayerIndex, AIdentifier);
  2193. end;
  2194. procedure TLazPaintImage.SetRegistry(AIdentifier: string;
  2195. AValue: RawByteString);
  2196. begin
  2197. AddUndo(TSetImageRegistryDifference.Create(FCurrentState, AIdentifier, AValue, true));
  2198. end;
  2199. function TLazPaintImage.GetRegistry(AIdentifier: string): RawByteString;
  2200. begin
  2201. result := FCurrentState.LayeredBitmap.GetGlobalRegistry(AIdentifier);
  2202. end;
  2203. function TLazPaintImage.GetLayerIndexById(AId: integer): integer;
  2204. begin
  2205. result := FCurrentState.LayeredBitmap.GetLayerIndexFromId(AId);
  2206. end;
  2207. function TLazPaintImage.GetLayerIndexByGuid(AGuid: TGuid): integer;
  2208. var
  2209. guidStr: String;
  2210. i: Integer;
  2211. begin
  2212. guidStr := GUIDToString(AGuid);
  2213. for i := 0 to NbLayers-1 do
  2214. if CompareText(GetLayerRegistry(i, 'guid'),guidStr)=0 then exit(i);
  2215. exit(-1);
  2216. end;
  2217. constructor TLazPaintImage.Create(ALazPaintInstance: TObject);
  2218. begin
  2219. FLazPaintInstance := ALazPaintInstance;
  2220. FCurrentState := TImageState.Create;
  2221. FCurrentState.OnOriginalChange:= @OriginalChange;
  2222. FCurrentState.OnOriginalEditingChange:= @OriginalEditingChange;
  2223. FCurrentState.OnOriginalLoadError:=@OriginalLoadError;
  2224. FCurrentState.OnActionProgress:= @LayeredActionProgress;
  2225. FCurrentState.OnActionDone:=@LayeredActionDone;
  2226. FCurrentState.OnSizeChanged:=@LayeredSizeChanged;
  2227. FRenderUpdateRectInPicCoord := rect(0,0,0,0);
  2228. FRenderUpdateRectInVSCoord := rect(0,0,0,0);
  2229. FOnSelectionMaskChanged := nil;
  2230. FOnSelectedLayerIndexChanged := nil;
  2231. FOnStackChanged := nil;
  2232. FOnImageChanged := TLazPaintImageObservable.Create(self);
  2233. FOnImageSaving := TLazPaintImageObservable.Create(self);
  2234. FOnImageExport := TLazPaintImageObservable.Create(self);
  2235. FUndoList := TComposedImageDifference.Create;
  2236. FUndoPos := -1;
  2237. ImageOffset := Point(0,0);
  2238. FrameIndex := -1;
  2239. FrameCount := 0;
  2240. end;
  2241. destructor TLazPaintImage.Destroy;
  2242. begin
  2243. ClearUndo;
  2244. FUndoList.Free;
  2245. FreeAndNil(FRenderedImage);
  2246. FCurrentState.Free;
  2247. FOnImageChanged.Free;
  2248. FOnImageSaving.Free;
  2249. FOnImageExport.Free;
  2250. FSelectionLayerAfterMask.Free;
  2251. inherited Destroy;
  2252. end;
  2253. initialization
  2254. RegisterPaintNetFormat;
  2255. RegisterOpenRasterFormat;
  2256. RegisterPhoxoFormat;
  2257. RegisterLazPaintFormat;
  2258. BGRAColorQuantizerFactory := TBGRAColorQuantizer;
  2259. end.