ImagingCanvases.pas 71 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096
  1. {
  2. Vampyre Imaging Library
  3. by Marek Mauder
  4. https://github.com/galfar/imaginglib
  5. https://imaginglib.sourceforge.io
  6. - - - - -
  7. This Source Code Form is subject to the terms of the Mozilla Public
  8. License, v. 2.0. If a copy of the MPL was not distributed with this
  9. file, You can obtain one at https://mozilla.org/MPL/2.0.
  10. }
  11. { This unit contains canvas classes for drawing and applying effects.}
  12. unit ImagingCanvases;
  13. {$I ImagingOptions.inc}
  14. interface
  15. uses
  16. SysUtils, Types, Classes, ImagingTypes, Imaging, ImagingClasses,
  17. ImagingFormats, ImagingUtility;
  18. const
  19. MaxPenWidth = 256;
  20. type
  21. EImagingCanvasError = class(EImagingError);
  22. EImagingCanvasBlendingError = class(EImagingError);
  23. { Fill mode used when drawing filled objects on canvas.}
  24. TFillMode = (
  25. fmSolid, // Solid fill using current fill color
  26. fmClear // No filling done
  27. );
  28. { Pen mode used when drawing lines, object outlines, and similar on canvas.}
  29. TPenMode = (
  30. pmSolid, // Draws solid lines using current pen color.
  31. pmClear // No drawing done
  32. );
  33. { Source and destination blending factors for drawing functions with blending.
  34. Blending formula: SrcColor * SrcFactor + DestColor * DestFactor }
  35. TBlendingFactor = (
  36. bfIgnore, // Don't care
  37. bfZero, // For Src and Dest, Factor = (0, 0, 0, 0)
  38. bfOne, // For Src and Dest, Factor = (1, 1, 1, 1)
  39. bfSrcAlpha, // For Src and Dest, Factor = (Src.A, Src.A, Src.A, Src.A)
  40. bfOneMinusSrcAlpha, // For Src and Dest, Factor = (1 - Src.A, 1 - Src.A, 1 - Src.A, 1 - Src.A)
  41. bfDstAlpha, // For Src and Dest, Factor = (Dest.A, Dest.A, Dest.A, Dest.A)
  42. bfOneMinusDstAlpha, // For Src and Dest, Factor = (1 - Dest.A, 1 - Dest.A, 1 - Dest.A, 1 - Dest.A)
  43. bfSrcColor, // For Dest, Factor = (Src.R, Src.R, Src.B, Src.A)
  44. bfOneMinusSrcColor, // For Dest, Factor = (1 - Src.R, 1 - Src.G, 1 - Src.B, 1 - Src.A)
  45. bfDstColor, // For Src, Factor = (Dest.R, Dest.G, Dest.B, Dest.A)
  46. bfOneMinusDstColor // For Src, Factor = (1 - Dest.R, 1 - Dest.G, 1 - Dest.B, 1 - Dest.A)
  47. );
  48. { Procedure for custom pixel write modes with blending.}
  49. TPixelWriteProc = procedure(const SrcPix: TColorFPRec; DestPtr: PByte;
  50. DestInfo: PImageFormatInfo; SrcFactor, DestFactor: TBlendingFactor);
  51. { Represents 3x3 convolution filter kernel.}
  52. TConvolutionFilter3x3 = record
  53. Kernel: array[0..2, 0..2] of LongInt;
  54. Divisor: LongInt;
  55. Bias: Single;
  56. end;
  57. { Represents 5x5 convolution filter kernel.}
  58. TConvolutionFilter5x5 = record
  59. Kernel: array[0..4, 0..4] of LongInt;
  60. Divisor: LongInt;
  61. Bias: Single;
  62. end;
  63. TPointTransformFunction = function(const Pixel: TColorFPRec;
  64. Param1, Param2, Param3: Single): TColorFPRec;
  65. TDynFPPixelArray = array of TColorFPRec;
  66. THistogramArray = array[Byte] of Integer;
  67. TSelectPixelFunction = function(var Pixels: TDynFPPixelArray): TColorFPRec;
  68. { Base canvas class for drawing objects, applying effects, and other.
  69. Constructor takes TBaseImage (or pointer to TImageData). Source image
  70. bits are not copied but referenced so all canvas functions affect
  71. source image and vice versa. When you change format or resolution of
  72. source image you must call UpdateCanvasState method (so canvas could
  73. recompute some data size related stuff).
  74. TImagingCanvas works for all image data formats except special ones
  75. (compressed). Because of this its methods are quite slow (they usually work
  76. with colors in ifA32R32G32B32F format). If you want fast drawing you
  77. can use one of fast canvas classes. These descendants of TImagingCanvas
  78. work only for few select formats (or only one) but they are optimized thus
  79. much faster.
  80. }
  81. TImagingCanvas = class(TObject)
  82. private
  83. FDataSizeOnUpdate: LongInt;
  84. FLineRecursion: Boolean;
  85. function GetPixel32(X, Y: LongInt): TColor32; virtual;
  86. function GetPixelFP(X, Y: LongInt): TColorFPRec; virtual;
  87. function GetValid: Boolean; {$IFDEF USE_INLINE}inline;{$ENDIF}
  88. procedure SetPixel32(X, Y: LongInt; const Value: TColor32); virtual;
  89. procedure SetPixelFP(X, Y: LongInt; const Value: TColorFPRec); virtual;
  90. procedure SetPenColor32(const Value: TColor32); {$IFDEF USE_INLINE}inline;{$ENDIF}
  91. procedure SetPenColorFP(const Value: TColorFPRec); {$IFDEF USE_INLINE}inline;{$ENDIF}
  92. procedure SetPenWidth(const Value: LongInt); {$IFDEF USE_INLINE}inline;{$ENDIF}
  93. procedure SetFillColor32(const Value: TColor32); {$IFDEF USE_INLINE}inline;{$ENDIF}
  94. procedure SetFillColorFP(const Value: TColorFPRec); {$IFDEF USE_INLINE}inline;{$ENDIF}
  95. procedure SetClipRect(const Value: TRect);
  96. procedure CheckBeforeBlending(SrcFactor, DestFactor: TBlendingFactor; DestCanvas: TImagingCanvas);
  97. protected
  98. FPData: PImageData;
  99. FClipRect: TRect;
  100. FPenColorFP: TColorFPRec;
  101. FPenColor32: TColor32;
  102. FPenMode: TPenMode;
  103. FPenWidth: LongInt;
  104. FFillColorFP: TColorFPRec;
  105. FFillColor32: TColor32;
  106. FFillMode: TFillMode;
  107. FNativeColor: TColorFPRec;
  108. FFormatInfo: TImageFormatInfo;
  109. { Returns pointer to pixel at given position.}
  110. function GetPixelPointer(X, Y: LongInt): Pointer; {$IFDEF USE_INLINE}inline;{$ENDIF}
  111. { Translates given FP color to native format of canvas and stores it
  112. in FNativeColor field (its bit copy) or user pointer (in overloaded method).}
  113. procedure TranslateFPToNative(const Color: TColorFPRec); overload; {$IFDEF USE_INLINE}inline;{$ENDIF}
  114. procedure TranslateFPToNative(const Color: TColorFPRec; Native: Pointer); overload; {$IFDEF USE_INLINE}inline;{$ENDIF}
  115. { Clipping function used by horizontal and vertical line drawing functions.}
  116. function ClipAxisParallelLine(var A1, A2, B: LongInt;
  117. AStart, AStop, BStart, BStop: LongInt): Boolean;
  118. { Internal horizontal line drawer used mainly for filling inside of objects
  119. like ellipses and circles.}
  120. procedure HorzLineInternal(X1, X2, Y: LongInt; Color: Pointer; Bpp: LongInt); virtual;
  121. procedure CopyPixelInternal(X, Y: LongInt; Pixel: Pointer; Bpp: LongInt); {$IFDEF USE_INLINE}inline;{$ENDIF}
  122. procedure DrawInternal(const SrcRect: TRect; DestCanvas: TImagingCanvas;
  123. DestX, DestY: LongInt; SrcFactor, DestFactor: TBlendingFactor; PixelWriteProc: TPixelWriteProc);
  124. procedure StretchDrawInternal(const SrcRect: TRect; DestCanvas: TImagingCanvas;
  125. const DestRect: TRect; SrcFactor, DestFactor: TBlendingFactor;
  126. Filter: TResizeFilter; PixelWriteProc: TPixelWriteProc);
  127. public
  128. constructor CreateForData(ImageDataPointer: PImageData);
  129. constructor CreateForImage(Image: TBaseImage);
  130. destructor Destroy; override;
  131. { Call this method when you change size or format of image this canvas
  132. operates on (like calling ResizeImage, ConvertImage, or changing Format
  133. property of TBaseImage descendants).}
  134. procedure UpdateCanvasState; virtual;
  135. { Resets clipping rectangle to Rect(0, 0, ImageWidth, ImageHeight).}
  136. procedure ResetClipRect;
  137. { Clears entire canvas with current fill color (ignores clipping rectangle
  138. and always uses fmSolid fill mode).}
  139. procedure Clear;
  140. { Draws horizontal line with current pen settings.}
  141. procedure HorzLine(X1, X2, Y: LongInt); virtual;
  142. { Draws vertical line with current pen settings.}
  143. procedure VertLine(X, Y1, Y2: LongInt); virtual;
  144. { Draws line from [X1, Y1] to [X2, Y2] with current pen settings.}
  145. procedure Line(X1, Y1, X2, Y2: LongInt); virtual;
  146. { Draws a rectangle using current pen settings.}
  147. procedure FrameRect(const Rect: TRect);
  148. { Fills given rectangle with current fill settings.}
  149. procedure FillRect(const Rect: TRect); virtual;
  150. { Fills given rectangle with current fill settings and pixel blending.}
  151. procedure FillRectBlend(const Rect: TRect; SrcFactor, DestFactor: TBlendingFactor);
  152. { Draws rectangle which is outlined by using the current pen settings and
  153. filled by using the current fill settings.}
  154. procedure Rectangle(const Rect: TRect);
  155. { Draws ellipse which is outlined by using the current pen settings and
  156. filled by using the current fill settings. Rect specifies bounding rectangle
  157. of ellipse to be drawn.}
  158. procedure Ellipse(const Rect: TRect);
  159. { Fills area of canvas with current fill color starting at point [X, Y] and
  160. coloring its neighbors. Default flood fill mode changes color of all
  161. neighbors with the same color as pixel [X, Y]. With BoundaryFillMode
  162. set to True neighbors are recolored regardless of their old color,
  163. but area which will be recolored has boundary (specified by current pen color).}
  164. procedure FloodFill(X, Y: Integer; BoundaryFillMode: Boolean = False);
  165. { Draws contents of this canvas onto another canvas with pixel blending.
  166. Blending factors are chosen using TBlendingFactor parameters.
  167. Resulting destination pixel color is:
  168. SrcColor * SrcFactor + DstColor * DstFactor}
  169. procedure DrawBlend(const SrcRect: TRect; DestCanvas: TImagingCanvas;
  170. DestX, DestY: LongInt; SrcFactor, DestFactor: TBlendingFactor);
  171. { Draws contents of this canvas onto another one with typical alpha
  172. blending (Src 'over' Dest, factors are bfSrcAlpha and bfOneMinusSrcAlpha.)}
  173. procedure DrawAlpha(const SrcRect: TRect; DestCanvas: TImagingCanvas; DestX, DestY: LongInt); virtual;
  174. { Draws contents of this canvas onto another one using additive blending
  175. (source and dest factors are bfOne).}
  176. procedure DrawAdd(const SrcRect: TRect; DestCanvas: TImagingCanvas; DestX, DestY: LongInt);
  177. { Draws stretched and filtered contents of this canvas onto another canvas
  178. with pixel blending. Blending factors are chosen using TBlendingFactor parameters.
  179. Resulting destination pixel color is:
  180. SrcColor * SrcFactor + DstColor * DstFactor}
  181. procedure StretchDrawBlend(const SrcRect: TRect; DestCanvas: TImagingCanvas;
  182. const DestRect: TRect; SrcFactor, DestFactor: TBlendingFactor;
  183. Filter: TResizeFilter = rfBilinear);
  184. { Draws contents of this canvas onto another one with typical alpha
  185. blending (Src 'over' Dest, factors are bfSrcAlpha and bfOneMinusSrcAlpha.)}
  186. procedure StretchDrawAlpha(const SrcRect: TRect; DestCanvas: TImagingCanvas;
  187. const DestRect: TRect; Filter: TResizeFilter = rfBilinear); virtual;
  188. { Draws contents of this canvas onto another one using additive blending
  189. (source and dest factors are bfOne).}
  190. procedure StretchDrawAdd(const SrcRect: TRect; DestCanvas: TImagingCanvas;
  191. const DestRect: TRect; Filter: TResizeFilter = rfBilinear);
  192. { Convolves canvas' image with given 3x3 filter kernel. You can use
  193. predefined filter kernels or define your own.}
  194. procedure ApplyConvolution3x3(const Filter: TConvolutionFilter3x3);
  195. { Convolves canvas' image with given 5x5 filter kernel. You can use
  196. predefined filter kernels or define your own.}
  197. procedure ApplyConvolution5x5(const Filter: TConvolutionFilter5x5);
  198. { Computes 2D convolution of canvas' image and given filter kernel.
  199. Kernel is in row format and KernelSize must be odd number >= 3. Divisor
  200. is normalizing value based on Kernel (usually sum of all kernel's cells).
  201. The Bias number shifts each color value by a fixed amount (color values
  202. are usually in range [0, 1] during processing). If ClampChannels
  203. is True all output color values are clamped to [0, 1]. You can use
  204. predefined filter kernels or define your own.}
  205. procedure ApplyConvolution(Kernel: PLongInt; KernelSize, Divisor: LongInt;
  206. Bias: Single = 0.0; ClampChannels: Boolean = True); virtual;
  207. { Applies custom non-linear filter. Filter size is diameter of pixel
  208. neighborhood. Typical values are 3, 5, or 7. }
  209. procedure ApplyNonLinearFilter(FilterSize: Integer; SelectFunc: TSelectPixelFunction);
  210. { Applies median non-linear filter with user defined pixel neighborhood.
  211. Selects median pixel from the neighborhood as new pixel
  212. (current implementation is quite slow).}
  213. procedure ApplyMedianFilter(FilterSize: Integer);
  214. { Applies min non-linear filter with user defined pixel neighborhood.
  215. Selects min pixel from the neighborhood as new pixel.}
  216. procedure ApplyMinFilter(FilterSize: Integer);
  217. { Applies max non-linear filter with user defined pixel neighborhood.
  218. Selects max pixel from the neighborhood as new pixel.}
  219. procedure ApplyMaxFilter(FilterSize: Integer);
  220. { Transforms pixels one by one by given function. Pixel neighbors are
  221. not taken into account. Param 1-3 are optional parameters
  222. for transform function.}
  223. procedure PointTransform(Transform: TPointTransformFunction;
  224. Param1, Param2, Param3: Single);
  225. { Modifies image contrast and brightness. Parameters should be
  226. in range <-100; 100>.}
  227. procedure ModifyContrastBrightness(Contrast, Brightness: Single);
  228. { Gamma correction of individual color channels. Range is (0, +inf),
  229. 1.0 means no change.}
  230. procedure GammaCorrection(Red, Green, Blue: Single);
  231. { Inverts colors of all image pixels, makes negative image. Ignores alpha channel.}
  232. procedure InvertColors; virtual;
  233. { Simple single level thresholding with threshold level (in range [0, 1])
  234. for each color channel.}
  235. procedure Threshold(Red, Green, Blue: Single);
  236. { Adjusts the color levels of the image by scaling the
  237. colors falling between specified white and black points to full [0, 1] range.
  238. The black point specifies the darkest color in the image, white point
  239. specifies the lightest color, and mid point is gamma aplied to image.
  240. Black and white point must be in range [0, 1].}
  241. procedure AdjustColorLevels(BlackPoint, WhitePoint: Single; MidPoint: Single = 1.0);
  242. { Premultiplies color channel values by alpha. Needed for some platforms/APIs
  243. to display images with alpha properly.}
  244. procedure PremultiplyAlpha;
  245. { Reverses PremultiplyAlpha operation.}
  246. procedure UnPremultiplyAlpha;
  247. { Calculates image histogram for each channel and also gray values. Each
  248. channel has 256 values available. Channel values of data formats with higher
  249. precision are scaled and rounded. Example: Red[126] specifies number of pixels
  250. in image with red channel = 126.}
  251. procedure GetHistogram(out Red, Green, Blue, Alpha, Gray: THistogramArray);
  252. { Fills image channel with given value leaving other channels intact.
  253. Use ChannelAlpha, ChannelRed, etc. constants from ImagingTypes as
  254. channel identifier.}
  255. procedure FillChannel(ChannelId: Integer; NewChannelValue: Byte); overload;
  256. { Fills image channel with given value leaving other channels intact.
  257. Use ChannelAlpha, ChannelRed, etc. constants from ImagingTypes as
  258. channel identifier.}
  259. procedure FillChannelFP(ChannelId: Integer; NewChannelValue: Single); overload;
  260. { Color used when drawing lines, frames, and outlines of objects.}
  261. property PenColor32: TColor32 read FPenColor32 write SetPenColor32;
  262. { Color used when drawing lines, frames, and outlines of objects.}
  263. property PenColorFP: TColorFPRec read FPenColorFP write SetPenColorFP;
  264. { Pen mode used when drawing lines, object outlines, and similar on canvas.}
  265. property PenMode: TPenMode read FPenMode write FPenMode;
  266. { Width with which objects like lines, frames, etc. (everything which uses
  267. PenColor) are drawn.}
  268. property PenWidth: LongInt read FPenWidth write SetPenWidth;
  269. { Color used for filling when drawing various objects.}
  270. property FillColor32: TColor32 read FFillColor32 write SetFillColor32;
  271. { Color used for filling when drawing various objects.}
  272. property FillColorFP: TColorFPRec read FFillColorFP write SetFillColorFP;
  273. { Fill mode used when drawing filled objects on canvas.}
  274. property FillMode: TFillMode read FFillMode write FFillMode;
  275. { Specifies the current color of the pixels of canvas. Native pixel is
  276. read from canvas and then translated to 32bit ARGB. Reverse operation
  277. is made when setting pixel color.}
  278. property Pixels32[X, Y: LongInt]: TColor32 read GetPixel32 write SetPixel32;
  279. { Specifies the current color of the pixels of canvas. Native pixel is
  280. read from canvas and then translated to FP ARGB. Reverse operation
  281. is made when setting pixel color.}
  282. property PixelsFP[X, Y: LongInt]: TColorFPRec read GetPixelFP write SetPixelFP;
  283. { Clipping rectangle of this canvas. No pixels outside this rectangle are
  284. altered by canvas methods if Clipping property is True. Clip rect gets
  285. reset when UpdateCanvasState is called.}
  286. property ClipRect: TRect read FClipRect write SetClipRect;
  287. { Extended format information.}
  288. property FormatInfo: TImageFormatInfo read FFormatInfo;
  289. { Indicates that this canvas is in valid state. If False canvas operations
  290. may crash.}
  291. property Valid: Boolean read GetValid;
  292. { Returns all formats supported by this canvas class.}
  293. class function GetSupportedFormats: TImageFormats; virtual;
  294. end;
  295. TImagingCanvasClass = class of TImagingCanvas;
  296. TScanlineArray = array[0..MaxInt div SizeOf(Pointer) - 1] of PColor32RecArray;
  297. PScanlineArray = ^TScanlineArray;
  298. { Fast canvas class for ifA8R8G8B8 format images.}
  299. TFastARGB32Canvas = class(TImagingCanvas)
  300. protected
  301. FScanlines: PScanlineArray;
  302. procedure AlphaBlendPixels(SrcPix, DestPix: PColor32Rec); {$IFDEF USE_INLINE}inline;{$ENDIF}
  303. function GetPixel32(X, Y: LongInt): TColor32; override;
  304. procedure SetPixel32(X, Y: LongInt; const Value: TColor32); override;
  305. public
  306. destructor Destroy; override;
  307. procedure UpdateCanvasState; override;
  308. procedure DrawAlpha(const SrcRect: TRect; DestCanvas: TImagingCanvas; DestX, DestY: LongInt); override;
  309. procedure StretchDrawAlpha(const SrcRect: TRect; DestCanvas: TImagingCanvas;
  310. const DestRect: TRect; Filter: TResizeFilter = rfBilinear); override;
  311. procedure InvertColors; override;
  312. property Scanlines: PScanlineArray read FScanlines;
  313. class function GetSupportedFormats: TImageFormats; override;
  314. end;
  315. const
  316. { Kernel for 3x3 average smoothing filter.}
  317. FilterAverage3x3: TConvolutionFilter3x3 = (
  318. Kernel: ((1, 1, 1),
  319. (1, 1, 1),
  320. (1, 1, 1));
  321. Divisor: 9;
  322. Bias: 0);
  323. { Kernel for 5x5 average smoothing filter.}
  324. FilterAverage5x5: TConvolutionFilter5x5 = (
  325. Kernel: ((1, 1, 1, 1, 1),
  326. (1, 1, 1, 1, 1),
  327. (1, 1, 1, 1, 1),
  328. (1, 1, 1, 1, 1),
  329. (1, 1, 1, 1, 1));
  330. Divisor: 25;
  331. Bias: 0);
  332. { Kernel for 3x3 Gaussian smoothing filter.}
  333. FilterGaussian3x3: TConvolutionFilter3x3 = (
  334. Kernel: ((1, 2, 1),
  335. (2, 4, 2),
  336. (1, 2, 1));
  337. Divisor: 16;
  338. Bias: 0);
  339. { Kernel for 5x5 Gaussian smoothing filter.}
  340. FilterGaussian5x5: TConvolutionFilter5x5 = (
  341. Kernel: ((1, 4, 6, 4, 1),
  342. (4, 16, 24, 16, 4),
  343. (6, 24, 36, 24, 6),
  344. (4, 16, 24, 16, 4),
  345. (1, 4, 6, 4, 1));
  346. Divisor: 256;
  347. Bias: 0);
  348. { Kernel for 3x3 Sobel horizontal edge detection filter (1st derivative approximation).}
  349. FilterSobelHorz3x3: TConvolutionFilter3x3 = (
  350. Kernel: (( 1, 2, 1),
  351. ( 0, 0, 0),
  352. (-1, -2, -1));
  353. Divisor: 1;
  354. Bias: 0);
  355. { Kernel for 3x3 Sobel vertical edge detection filter (1st derivative approximation).}
  356. FilterSobelVert3x3: TConvolutionFilter3x3 = (
  357. Kernel: ((-1, 0, 1),
  358. (-2, 0, 2),
  359. (-1, 0, 1));
  360. Divisor: 1;
  361. Bias: 0);
  362. { Kernel for 3x3 Prewitt horizontal edge detection filter.}
  363. FilterPrewittHorz3x3: TConvolutionFilter3x3 = (
  364. Kernel: (( 1, 1, 1),
  365. ( 0, 0, 0),
  366. (-1, -1, -1));
  367. Divisor: 1;
  368. Bias: 0);
  369. { Kernel for 3x3 Prewitt vertical edge detection filter.}
  370. FilterPrewittVert3x3: TConvolutionFilter3x3 = (
  371. Kernel: ((-1, 0, 1),
  372. (-1, 0, 1),
  373. (-1, 0, 1));
  374. Divisor: 1;
  375. Bias: 0);
  376. { Kernel for 3x3 Kirsh horizontal edge detection filter.}
  377. FilterKirshHorz3x3: TConvolutionFilter3x3 = (
  378. Kernel: (( 5, 5, 5),
  379. (-3, 0, -3),
  380. (-3, -3, -3));
  381. Divisor: 1;
  382. Bias: 0);
  383. { Kernel for 3x3 Kirsh vertical edge detection filter.}
  384. FilterKirshVert3x3: TConvolutionFilter3x3 = (
  385. Kernel: ((5, -3, -3),
  386. (5, 0, -3),
  387. (5, -3, -3));
  388. Divisor: 1;
  389. Bias: 0);
  390. { Kernel for 3x3 Laplace omni-directional edge detection filter
  391. (2nd derivative approximation).}
  392. FilterLaplace3x3: TConvolutionFilter3x3 = (
  393. Kernel: ((-1, -1, -1),
  394. (-1, 8, -1),
  395. (-1, -1, -1));
  396. Divisor: 1;
  397. Bias: 0);
  398. { Kernel for 5x5 Laplace omni-directional edge detection filter
  399. (2nd derivative approximation).}
  400. FilterLaplace5x5: TConvolutionFilter5x5 = (
  401. Kernel: ((-1, -1, -1, -1, -1),
  402. (-1, -1, -1, -1, -1),
  403. (-1, -1, 24, -1, -1),
  404. (-1, -1, -1, -1, -1),
  405. (-1, -1, -1, -1, -1));
  406. Divisor: 1;
  407. Bias: 0);
  408. { Kernel for 3x3 sharpening filter (Laplacian + original color).}
  409. FilterSharpen3x3: TConvolutionFilter3x3 = (
  410. Kernel: ((-1, -1, -1),
  411. (-1, 9, -1),
  412. (-1, -1, -1));
  413. Divisor: 1;
  414. Bias: 0);
  415. { Kernel for 5x5 sharpening filter (Laplacian + original color).}
  416. FilterSharpen5x5: TConvolutionFilter5x5 = (
  417. Kernel: ((-1, -1, -1, -1, -1),
  418. (-1, -1, -1, -1, -1),
  419. (-1, -1, 25, -1, -1),
  420. (-1, -1, -1, -1, -1),
  421. (-1, -1, -1, -1, -1));
  422. Divisor: 1;
  423. Bias: 0);
  424. { Kernel for 5x5 glow filter.}
  425. FilterGlow5x5: TConvolutionFilter5x5 = (
  426. Kernel: (( 1, 2, 2, 2, 1),
  427. ( 2, 0, 0, 0, 2),
  428. ( 2, 0, -20, 0, 2),
  429. ( 2, 0, 0, 0, 2),
  430. ( 1, 2, 2, 2, 1));
  431. Divisor: 8;
  432. Bias: 0);
  433. { Kernel for 3x3 edge enhancement filter.}
  434. FilterEdgeEnhance3x3: TConvolutionFilter3x3 = (
  435. Kernel: ((-1, -2, -1),
  436. (-2, 16, -2),
  437. (-1, -2, -1));
  438. Divisor: 4;
  439. Bias: 0);
  440. { Kernel for 3x3 contour enhancement filter.}
  441. FilterTraceContour3x3: TConvolutionFilter3x3 = (
  442. Kernel: ((-6, -6, -2),
  443. (-1, 32, -1),
  444. (-6, -2, -6));
  445. Divisor: 4;
  446. Bias: 240/255);
  447. { Kernel for filter that negates all images pixels.}
  448. FilterNegative3x3: TConvolutionFilter3x3 = (
  449. Kernel: ((0, 0, 0),
  450. (0, -1, 0),
  451. (0, 0, 0));
  452. Divisor: 1;
  453. Bias: 1);
  454. { Kernel for 3x3 horz/vert embossing filter.}
  455. FilterEmboss3x3: TConvolutionFilter3x3 = (
  456. Kernel: ((2, 0, 0),
  457. (0, -1, 0),
  458. (0, 0, -1));
  459. Divisor: 1;
  460. Bias: 0.5);
  461. { You can register your own canvas class. List of registered canvases is used
  462. by FindBestCanvasForImage functions to find best canvas for given image.
  463. If two different canvases which support the same image data format are
  464. registered then the one that was registered later is returned (so you can
  465. override builtin Imaging canvases).}
  466. procedure RegisterCanvas(CanvasClass: TImagingCanvasClass);
  467. { Returns best canvas for given TImageFormat.}
  468. function FindBestCanvasForImage(ImageFormat: TImageFormat): TImagingCanvasClass; overload;
  469. { Returns best canvas for given TImageData.}
  470. function FindBestCanvasForImage(const ImageData: TImageData): TImagingCanvasClass; overload;
  471. { Returns best canvas for given TBaseImage.}
  472. function FindBestCanvasForImage(Image: TBaseImage): TImagingCanvasClass; overload;
  473. implementation
  474. uses
  475. ImagingColors;
  476. resourcestring
  477. SConstructorInvalidPointer = 'Invalid pointer (%p) to TImageData passed to TImagingCanvas constructor.';
  478. SConstructorInvalidImage = 'Invalid image data passed to TImagingCanvas constructor (%s).';
  479. SConstructorUnsupportedFormat = 'Image passed to TImagingCanvas constructor is in unsupported format (%s)';
  480. var
  481. // list with all registered TImagingCanvas classes
  482. CanvasClasses: TList = nil;
  483. procedure RegisterCanvas(CanvasClass: TImagingCanvasClass);
  484. begin
  485. Assert(CanvasClass <> nil);
  486. if CanvasClasses = nil then
  487. CanvasClasses := TList.Create;
  488. if CanvasClasses.IndexOf(CanvasClass) < 0 then
  489. CanvasClasses.Add(CanvasClass);
  490. end;
  491. function FindBestCanvasForImage(ImageFormat: TImageFormat): TImagingCanvasClass; overload;
  492. var
  493. I: LongInt;
  494. begin
  495. for I := CanvasClasses.Count - 1 downto 0 do
  496. begin
  497. if ImageFormat in TImagingCanvasClass(CanvasClasses[I]).GetSupportedFormats then
  498. begin
  499. Result := TImagingCanvasClass(CanvasClasses[I]);
  500. Exit;
  501. end;
  502. end;
  503. Result := TImagingCanvas;
  504. end;
  505. function FindBestCanvasForImage(const ImageData: TImageData): TImagingCanvasClass;
  506. begin
  507. Result := FindBestCanvasForImage(ImageData.Format);
  508. end;
  509. function FindBestCanvasForImage(Image: TBaseImage): TImagingCanvasClass;
  510. begin
  511. Result := FindBestCanvasForImage(Image.Format);
  512. end;
  513. { Canvas helper functions }
  514. procedure PixelBlendProc(const SrcPix: TColorFPRec; DestPtr: PByte;
  515. DestInfo: PImageFormatInfo; SrcFactor, DestFactor: TBlendingFactor);
  516. var
  517. DestPix, FSrc, FDst: TColorFPRec;
  518. begin
  519. // Get set pixel color
  520. DestPix := DestInfo.GetPixelFP(DestPtr, DestInfo, nil);
  521. // Determine current blending factors
  522. case SrcFactor of
  523. bfZero: FSrc := ColorFP(0, 0, 0, 0);
  524. bfOne: FSrc := ColorFP(1, 1, 1, 1);
  525. bfSrcAlpha: FSrc := ColorFP(SrcPix.A, SrcPix.A, SrcPix.A, SrcPix.A);
  526. bfOneMinusSrcAlpha: FSrc := ColorFP(1 - SrcPix.A, 1 - SrcPix.A, 1 - SrcPix.A, 1 - SrcPix.A);
  527. bfDstAlpha: FSrc := ColorFP(DestPix.A, DestPix.A, DestPix.A, DestPix.A);
  528. bfOneMinusDstAlpha: FSrc := ColorFP(1 - DestPix.A, 1 - DestPix.A, 1 - DestPix.A, 1 - DestPix.A);
  529. bfDstColor: FSrc := ColorFP(DestPix.A, DestPix.R, DestPix.G, DestPix.B);
  530. bfOneMinusDstColor: FSrc := ColorFP(1 - DestPix.A, 1 - DestPix.R, 1 - DestPix.G, 1 - DestPix.B);
  531. else
  532. Assert(False);
  533. end;
  534. case DestFactor of
  535. bfZero: FDst := ColorFP(0, 0, 0, 0);
  536. bfOne: FDst := ColorFP(1, 1, 1, 1);
  537. bfSrcAlpha: FDst := ColorFP(SrcPix.A, SrcPix.A, SrcPix.A, SrcPix.A);
  538. bfOneMinusSrcAlpha: FDst := ColorFP(1 - SrcPix.A, 1 - SrcPix.A, 1 - SrcPix.A, 1 - SrcPix.A);
  539. bfDstAlpha: FDst := ColorFP(DestPix.A, DestPix.A, DestPix.A, DestPix.A);
  540. bfOneMinusDstAlpha: FDst := ColorFP(1 - DestPix.A, 1 - DestPix.A, 1 - DestPix.A, 1 - DestPix.A);
  541. bfSrcColor: FDst := ColorFP(SrcPix.A, SrcPix.R, SrcPix.G, SrcPix.B);
  542. bfOneMinusSrcColor: FDst := ColorFP(1 - SrcPix.A, 1 - SrcPix.R, 1 - SrcPix.G, 1 - SrcPix.B);
  543. else
  544. Assert(False);
  545. end;
  546. // Compute blending formula
  547. DestPix.R := SrcPix.R * FSrc.R + DestPix.R * FDst.R;
  548. DestPix.G := SrcPix.G * FSrc.G + DestPix.G * FDst.G;
  549. DestPix.B := SrcPix.B * FSrc.B + DestPix.B * FDst.B;
  550. DestPix.A := SrcPix.A * FSrc.A + DestPix.A * FDst.A;
  551. // Write blended pixel
  552. DestInfo.SetPixelFP(DestPtr, DestInfo, nil, DestPix);
  553. end;
  554. procedure PixelAlphaProc(const SrcPix: TColorFPRec; DestPtr: PByte;
  555. DestInfo: PImageFormatInfo; SrcFactor, DestFactor: TBlendingFactor);
  556. var
  557. DestPix: TColorFPRec;
  558. SrcAlpha, DestAlpha: Single;
  559. begin
  560. DestPix := DestInfo.GetPixelFP(DestPtr, DestInfo, nil);
  561. // Blend the two pixels (Src 'over' Dest alpha composition operation)
  562. DestPix.A := SrcPix.A + DestPix.A - SrcPix.A * DestPix.A;
  563. if DestPix.A = 0 then
  564. SrcAlpha := 0
  565. else
  566. SrcAlpha := SrcPix.A / DestPix.A;
  567. DestAlpha := 1.0 - SrcAlpha;
  568. DestPix.R := SrcPix.R * SrcAlpha + DestPix.R * DestAlpha;
  569. DestPix.G := SrcPix.G * SrcAlpha + DestPix.G * DestAlpha;
  570. DestPix.B := SrcPix.B * SrcAlpha + DestPix.B * DestAlpha;
  571. // Write blended pixel
  572. DestInfo.SetPixelFP(DestPtr, DestInfo, nil, DestPix);
  573. end;
  574. procedure PixelAddProc(const SrcPix: TColorFPRec; DestPtr: PByte;
  575. DestInfo: PImageFormatInfo; SrcFactor, DestFactor: TBlendingFactor);
  576. var
  577. DestPix: TColorFPRec;
  578. begin
  579. // Just add Src and Dest
  580. DestPix := DestInfo.GetPixelFP(DestPtr, DestInfo, nil);
  581. DestPix.R := SrcPix.R + DestPix.R;
  582. DestPix.G := SrcPix.G + DestPix.G;
  583. DestPix.B := SrcPix.B + DestPix.B;
  584. DestPix.A := SrcPix.A + DestPix.A;
  585. DestInfo.SetPixelFP(DestPtr, DestInfo, nil, DestPix);
  586. end;
  587. function CompareColors(const C1, C2: TColorFPRec): Single; {$IFDEF USE_INLINE}inline;{$ENDIF}
  588. begin
  589. Result := (C1.R * GrayConv.R + C1.G * GrayConv.G + C1.B * GrayConv.B) -
  590. (C2.R * GrayConv.R + C2.G * GrayConv.G + C2.B * GrayConv.B);
  591. end;
  592. function MedianSelect(var Pixels: TDynFPPixelArray): TColorFPRec;
  593. procedure QuickSort(L, R: Integer);
  594. var
  595. I, J: Integer;
  596. P, Temp: TColorFPRec;
  597. begin
  598. repeat
  599. I := L;
  600. J := R;
  601. P := Pixels[(L + R) shr 1];
  602. repeat
  603. while CompareColors(Pixels[I], P) < 0 do Inc(I);
  604. while CompareColors(Pixels[J], P) > 0 do Dec(J);
  605. if I <= J then
  606. begin
  607. Temp := Pixels[I];
  608. Pixels[I] := Pixels[J];
  609. Pixels[J] := Temp;
  610. Inc(I);
  611. Dec(J);
  612. end;
  613. until I > J;
  614. if L < J then
  615. QuickSort(L, J);
  616. L := I;
  617. until I >= R;
  618. end;
  619. begin
  620. // First sort pixels
  621. QuickSort(0, High(Pixels));
  622. // Select middle pixel
  623. Result := Pixels[Length(Pixels) div 2];
  624. end;
  625. function MinSelect(var Pixels: TDynFPPixelArray): TColorFPRec;
  626. var
  627. I: Integer;
  628. begin
  629. Result := Pixels[0];
  630. for I := 1 to High(Pixels) do
  631. begin
  632. if CompareColors(Pixels[I], Result) < 0 then
  633. Result := Pixels[I];
  634. end;
  635. end;
  636. function MaxSelect(var Pixels: TDynFPPixelArray): TColorFPRec;
  637. var
  638. I: Integer;
  639. begin
  640. Result := Pixels[0];
  641. for I := 1 to High(Pixels) do
  642. begin
  643. if CompareColors(Pixels[I], Result) > 0 then
  644. Result := Pixels[I];
  645. end;
  646. end;
  647. function TransformContrastBrightness(const Pixel: TColorFPRec; C, B, P3: Single): TColorFPRec;
  648. begin
  649. Result.A := Pixel.A;
  650. Result.R := Pixel.R * C + B;
  651. Result.G := Pixel.G * C + B;
  652. Result.B := Pixel.B * C + B;
  653. end;
  654. function TransformGamma(const Pixel: TColorFPRec; R, G, B: Single): TColorFPRec;
  655. begin
  656. Result.A := Pixel.A;
  657. Result.R := Power(Pixel.R, 1.0 / R);
  658. Result.G := Power(Pixel.G, 1.0 / G);
  659. Result.B := Power(Pixel.B, 1.0 / B);
  660. end;
  661. function TransformInvert(const Pixel: TColorFPRec; P1, P2, P3: Single): TColorFPRec;
  662. begin
  663. Result.A := Pixel.A;
  664. Result.R := 1.0 - Pixel.R;
  665. Result.G := 1.0 - Pixel.G;
  666. Result.B := 1.0 - Pixel.B;
  667. end;
  668. function TransformThreshold(const Pixel: TColorFPRec; R, G, B: Single): TColorFPRec;
  669. begin
  670. Result.A := Pixel.A;
  671. Result.R := IffFloat(Pixel.R >= R, 1.0, 0.0);
  672. Result.G := IffFloat(Pixel.G >= G, 1.0, 0.0);
  673. Result.B := IffFloat(Pixel.B >= B, 1.0, 0.0);
  674. end;
  675. function TransformLevels(const Pixel: TColorFPRec; BlackPoint, WhitePoint, Exp: Single): TColorFPRec;
  676. begin
  677. Result.A := Pixel.A;
  678. if Pixel.R > BlackPoint then
  679. Result.R := Power((Pixel.R - BlackPoint) / (WhitePoint - BlackPoint), Exp)
  680. else
  681. Result.R := 0.0;
  682. if Pixel.G > BlackPoint then
  683. Result.G := Power((Pixel.G - BlackPoint) / (WhitePoint - BlackPoint), Exp)
  684. else
  685. Result.G := 0.0;
  686. if Pixel.B > BlackPoint then
  687. Result.B := Power((Pixel.B - BlackPoint) / (WhitePoint - BlackPoint), Exp)
  688. else
  689. Result.B := 0.0;
  690. end;
  691. function TransformPremultiplyAlpha(const Pixel: TColorFPRec; P1, P2, P3: Single): TColorFPRec;
  692. begin
  693. Result.A := Pixel.A;
  694. Result.R := Pixel.R * Pixel.A;
  695. Result.G := Pixel.G * Pixel.A;
  696. Result.B := Pixel.B * Pixel.A;
  697. end;
  698. function TransformUnPremultiplyAlpha(const Pixel: TColorFPRec; P1, P2, P3: Single): TColorFPRec;
  699. begin
  700. Result.A := Pixel.A;
  701. if Pixel.A <> 0.0 then
  702. begin
  703. Result.R := Pixel.R / Pixel.A;
  704. Result.G := Pixel.G / Pixel.A;
  705. Result.B := Pixel.B / Pixel.A;
  706. end
  707. else
  708. begin
  709. Result.R := 0;
  710. Result.G := 0;
  711. Result.B := 0;
  712. end;
  713. end;
  714. { TImagingCanvas class implementation }
  715. constructor TImagingCanvas.CreateForData(ImageDataPointer: PImageData);
  716. begin
  717. if ImageDataPointer = nil then
  718. raise EImagingCanvasError.CreateFmt(SConstructorInvalidPointer, [ImageDataPointer]);
  719. if not TestImage(ImageDataPointer^) then
  720. raise EImagingCanvasError.CreateFmt(SConstructorInvalidImage, [Imaging.ImageToStr(ImageDataPointer^)]);
  721. if not (ImageDataPointer.Format in GetSupportedFormats) then
  722. raise EImagingCanvasError.CreateFmt(SConstructorUnsupportedFormat, [Imaging.ImageToStr(ImageDataPointer^)]);
  723. FPData := ImageDataPointer;
  724. FPenWidth := 1;
  725. SetPenColor32(pcWhite);
  726. SetFillColor32(pcBlack);
  727. FFillMode := fmSolid;
  728. UpdateCanvasState;
  729. end;
  730. constructor TImagingCanvas.CreateForImage(Image: TBaseImage);
  731. begin
  732. CreateForData(Image.ImageDataPointer);
  733. end;
  734. destructor TImagingCanvas.Destroy;
  735. begin
  736. inherited Destroy;
  737. end;
  738. function TImagingCanvas.GetPixel32(X, Y: LongInt): TColor32;
  739. begin
  740. Result := Imaging.GetPixel32(FPData^, X, Y).Color;
  741. end;
  742. function TImagingCanvas.GetPixelFP(X, Y: LongInt): TColorFPRec;
  743. begin
  744. Result := Imaging.GetPixelFP(FPData^, X, Y);
  745. end;
  746. function TImagingCanvas.GetValid: Boolean;
  747. begin
  748. Result := (FPData <> nil) and (FDataSizeOnUpdate = FPData.Size);
  749. end;
  750. procedure TImagingCanvas.SetPixel32(X, Y: LongInt; const Value: TColor32);
  751. begin
  752. if (X >= FClipRect.Left) and (Y >= FClipRect.Top) and
  753. (X < FClipRect.Right) and (Y < FClipRect.Bottom) then
  754. begin
  755. Imaging.SetPixel32(FPData^, X, Y, TColor32Rec(Value));
  756. end;
  757. end;
  758. procedure TImagingCanvas.SetPixelFP(X, Y: LongInt; const Value: TColorFPRec);
  759. begin
  760. if (X >= FClipRect.Left) and (Y >= FClipRect.Top) and
  761. (X < FClipRect.Right) and (Y < FClipRect.Bottom) then
  762. begin
  763. Imaging.SetPixelFP(FPData^, X, Y, TColorFPRec(Value));
  764. end;
  765. end;
  766. procedure TImagingCanvas.SetPenColor32(const Value: TColor32);
  767. begin
  768. FPenColor32 := Value;
  769. TranslatePixel(@FPenColor32, @FPenColorFP, ifA8R8G8B8, ifA32R32G32B32F, nil, nil);
  770. end;
  771. procedure TImagingCanvas.SetPenColorFP(const Value: TColorFPRec);
  772. begin
  773. FPenColorFP := Value;
  774. TranslatePixel(@FPenColorFP, @FPenColor32, ifA32R32G32B32F, ifA8R8G8B8, nil, nil);
  775. end;
  776. procedure TImagingCanvas.SetPenWidth(const Value: LongInt);
  777. begin
  778. FPenWidth := ClampInt(Value, 0, MaxPenWidth);
  779. end;
  780. procedure TImagingCanvas.SetFillColor32(const Value: TColor32);
  781. begin
  782. FFillColor32 := Value;
  783. TranslatePixel(@FFillColor32, @FFillColorFP, ifA8R8G8B8, ifA32R32G32B32F, nil, nil);
  784. end;
  785. procedure TImagingCanvas.SetFillColorFP(const Value: TColorFPRec);
  786. begin
  787. FFillColorFP := Value;
  788. TranslatePixel(@FFillColorFP, @FFillColor32, ifA32R32G32B32F, ifA8R8G8B8, nil, nil);
  789. end;
  790. procedure TImagingCanvas.SetClipRect(const Value: TRect);
  791. begin
  792. FClipRect := Value;
  793. NormalizeRect(FClipRect);
  794. IntersectRect(FClipRect, FClipRect, Rect(0, 0, FPData.Width, FPData.Height));
  795. end;
  796. procedure TImagingCanvas.CheckBeforeBlending(SrcFactor,
  797. DestFactor: TBlendingFactor; DestCanvas: TImagingCanvas);
  798. begin
  799. if SrcFactor in [bfSrcColor, bfOneMinusSrcColor] then
  800. raise EImagingCanvasBlendingError.Create('Invalid source blending factor. Check the documentation for TBlendingFactor.');
  801. if DestFactor in [bfDstColor, bfOneMinusDstColor] then
  802. raise EImagingCanvasBlendingError.Create('Invalid destination blending factor. Check the documentation for TBlendingFactor.');
  803. if DestCanvas.FormatInfo.IsIndexed then
  804. raise EImagingCanvasBlendingError.Create('Blending destination canvas cannot be in indexed mode.');
  805. end;
  806. function TImagingCanvas.GetPixelPointer(X, Y: LongInt): Pointer;
  807. begin
  808. Result := @PByteArray(FPData.Bits)[(Y * FPData.Width + X) * FFormatInfo.BytesPerPixel]
  809. end;
  810. procedure TImagingCanvas.TranslateFPToNative(const Color: TColorFPRec);
  811. begin
  812. TranslateFPToNative(Color, @FNativeColor);
  813. end;
  814. procedure TImagingCanvas.TranslateFPToNative(const Color: TColorFPRec;
  815. Native: Pointer);
  816. begin
  817. ImagingFormats.TranslatePixel(@Color, Native, ifA32R32G32B32F,
  818. FPData.Format, nil, FPData.Palette);
  819. end;
  820. procedure TImagingCanvas.UpdateCanvasState;
  821. begin
  822. FDataSizeOnUpdate := FPData.Size;
  823. ResetClipRect;
  824. Imaging.GetImageFormatInfo(FPData.Format, FFormatInfo)
  825. end;
  826. procedure TImagingCanvas.ResetClipRect;
  827. begin
  828. FClipRect := Rect(0, 0, FPData.Width, FPData.Height)
  829. end;
  830. procedure TImagingCanvas.Clear;
  831. begin
  832. TranslateFPToNative(FFillColorFP);
  833. Imaging.FillRect(FPData^, 0, 0, FPData.Width, FPData.Height, @FNativeColor);
  834. end;
  835. function TImagingCanvas.ClipAxisParallelLine(var A1, A2, B: LongInt;
  836. AStart, AStop, BStart, BStop: LongInt): Boolean;
  837. begin
  838. if (B >= BStart) and (B < BStop) then
  839. begin
  840. SwapMin(A1, A2);
  841. if A1 < AStart then A1 := AStart;
  842. if A2 >= AStop then A2 := AStop - 1;
  843. Result := True;
  844. end
  845. else
  846. Result := False;
  847. end;
  848. procedure TImagingCanvas.HorzLineInternal(X1, X2, Y: LongInt; Color: Pointer;
  849. Bpp: LongInt);
  850. var
  851. I, WidthBytes: LongInt;
  852. PixelPtr: PByte;
  853. begin
  854. if (Y >= FClipRect.Top) and (Y < FClipRect.Bottom) then
  855. begin
  856. SwapMin(X1, X2);
  857. X1 := Max(X1, FClipRect.Left);
  858. X2 := Min(X2, FClipRect.Right);
  859. PixelPtr := GetPixelPointer(X1, Y);
  860. WidthBytes := (X2 - X1) * Bpp;
  861. case Bpp of
  862. 1: FillMemoryByte(PixelPtr, WidthBytes, PByte(Color)^);
  863. 2: FillMemoryWord(PixelPtr, WidthBytes, PWord(Color)^);
  864. 4: FillMemoryUInt32(PixelPtr, WidthBytes, PUInt32(Color)^);
  865. else
  866. for I := X1 to X2 do
  867. begin
  868. ImagingFormats.CopyPixel(Color, PixelPtr, Bpp);
  869. Inc(PixelPtr, Bpp);
  870. end;
  871. end;
  872. end;
  873. end;
  874. procedure TImagingCanvas.CopyPixelInternal(X, Y: LongInt; Pixel: Pointer;
  875. Bpp: LongInt);
  876. begin
  877. if (X >= FClipRect.Left) and (Y >= FClipRect.Top) and
  878. (X < FClipRect.Right) and (Y < FClipRect.Bottom) then
  879. begin
  880. ImagingFormats.CopyPixel(Pixel, GetPixelPointer(X, Y), Bpp);
  881. end;
  882. end;
  883. procedure TImagingCanvas.HorzLine(X1, X2, Y: LongInt);
  884. var
  885. DstRect: TRect;
  886. begin
  887. if FPenMode = pmClear then Exit;
  888. SwapMin(X1, X2);
  889. if IntersectRect(DstRect, Rect(X1, Y - FPenWidth div 2, X2,
  890. Y + FPenWidth div 2 + FPenWidth mod 2), FClipRect) then
  891. begin
  892. TranslateFPToNative(FPenColorFP);
  893. Imaging.FillRect(FPData^, DstRect.Left, DstRect.Top, DstRect.Right - DstRect.Left,
  894. DstRect.Bottom - DstRect.Top, @FNativeColor);
  895. end;
  896. end;
  897. procedure TImagingCanvas.VertLine(X, Y1, Y2: LongInt);
  898. var
  899. DstRect: TRect;
  900. begin
  901. if FPenMode = pmClear then Exit;
  902. SwapMin(Y1, Y2);
  903. if IntersectRect(DstRect, Rect(X - FPenWidth div 2, Y1,
  904. X + FPenWidth div 2 + FPenWidth mod 2, Y2), FClipRect) then
  905. begin
  906. TranslateFPToNative(FPenColorFP);
  907. Imaging.FillRect(FPData^, DstRect.Left, DstRect.Top, DstRect.Right - DstRect.Left,
  908. DstRect.Bottom - DstRect.Top, @FNativeColor);
  909. end;
  910. end;
  911. procedure TImagingCanvas.Line(X1, Y1, X2, Y2: LongInt);
  912. var
  913. Steep: Boolean;
  914. Error, YStep, DeltaX, DeltaY, X, Y, I, Bpp, W1, W2, Code1, Code2: LongInt;
  915. begin
  916. if FPenMode = pmClear then Exit;
  917. // If line is vertical or horizontal just call appropriate method
  918. if X2 = X1 then
  919. begin
  920. VertLine(X1, Y1, Y2);
  921. Exit;
  922. end;
  923. if Y2 = Y1 then
  924. begin
  925. HorzLine(X1, X2, Y1);
  926. Exit;
  927. end;
  928. // Determine if line is steep (angle with X-axis > 45 degrees)
  929. Steep := Abs(Y2 - Y1) > Abs(X2 - X1);
  930. // If we need to draw thick line we just draw more 1 pixel lines around
  931. // the one we already drawn. Setting FLineRecursion assures that we
  932. // won't be doing recursions till the end of the world.
  933. if (FPenWidth > 1) and not FLineRecursion then
  934. begin
  935. FLineRecursion := True;
  936. W1 := FPenWidth div 2;
  937. W2 := W1;
  938. if FPenWidth mod 2 = 0 then
  939. Dec(W1);
  940. if Steep then
  941. begin
  942. // Add lines left/right
  943. for I := 1 to W1 do
  944. Line(X1, Y1 - I, X2, Y2 - I);
  945. for I := 1 to W2 do
  946. Line(X1, Y1 + I, X2, Y2 + I);
  947. end
  948. else
  949. begin
  950. // Add lines above/under
  951. for I := 1 to W1 do
  952. Line(X1 - I, Y1, X2 - I, Y2);
  953. for I := 1 to W2 do
  954. Line(X1 + I, Y1, X2 + I, Y2);
  955. end;
  956. FLineRecursion := False;
  957. end;
  958. with FClipRect do
  959. begin
  960. // Use part of Cohen-Sutherland line clipping to determine if any part of line
  961. // is in ClipRect
  962. Code1 := Ord(X1 < Left) + Ord(X1 > Right) shl 1 + Ord(Y1 < Top) shl 2 + Ord(Y1 > Bottom) shl 3;
  963. Code2 := Ord(X2 < Left) + Ord(X2 > Right) shl 1 + Ord(Y2 < Top) shl 2 + Ord(Y2 > Bottom) shl 3;
  964. end;
  965. if (Code1 and Code2) = 0 then
  966. begin
  967. TranslateFPToNative(FPenColorFP);
  968. Bpp := FFormatInfo.BytesPerPixel;
  969. // If line is steep swap X and Y coordinates so later we just have one loop
  970. // of two (where only one is used according to steepness).
  971. if Steep then
  972. begin
  973. SwapValues(X1, Y1);
  974. SwapValues(X2, Y2);
  975. end;
  976. if X1 > X2 then
  977. begin
  978. SwapValues(X1, X2);
  979. SwapValues(Y1, Y2);
  980. end;
  981. DeltaX := X2 - X1;
  982. DeltaY := Abs(Y2 - Y1);
  983. YStep := Iff(Y2 > Y1, 1, -1);
  984. Error := 0;
  985. Y := Y1;
  986. // Draw line using Bresenham algorithm. No real line clipping here,
  987. // just don't draw pixels outsize clip rect.
  988. for X := X1 to X2 do
  989. begin
  990. if Steep then
  991. CopyPixelInternal(Y, X, @FNativeColor, Bpp)
  992. else
  993. CopyPixelInternal(X, Y, @FNativeColor, Bpp);
  994. Error := Error + DeltaY;
  995. if Error * 2 >= DeltaX then
  996. begin
  997. Inc(Y, YStep);
  998. Dec(Error, DeltaX);
  999. end;
  1000. end;
  1001. end;
  1002. end;
  1003. procedure TImagingCanvas.FrameRect(const Rect: TRect);
  1004. var
  1005. HalfPen, PenMod: LongInt;
  1006. begin
  1007. if FPenMode = pmClear then Exit;
  1008. HalfPen := FPenWidth div 2;
  1009. PenMod := FPenWidth mod 2;
  1010. HorzLine(Rect.Left - HalfPen, Rect.Right + HalfPen + PenMod - 1, Rect.Top);
  1011. HorzLine(Rect.Left - HalfPen, Rect.Right + HalfPen + PenMod - 1, Rect.Bottom - 1);
  1012. VertLine(Rect.Left, Rect.Top, Rect.Bottom);
  1013. VertLine(Rect.Right - 1, Rect.Top, Rect.Bottom);
  1014. end;
  1015. procedure TImagingCanvas.FillRect(const Rect: TRect);
  1016. var
  1017. DstRect: TRect;
  1018. begin
  1019. if (FFillMode <> fmClear) and IntersectRect(DstRect, Rect, FClipRect) then
  1020. begin
  1021. TranslateFPToNative(FFillColorFP);
  1022. Imaging.FillRect(FPData^, DstRect.Left, DstRect.Top, DstRect.Right - DstRect.Left,
  1023. DstRect.Bottom - DstRect.Top, @FNativeColor);
  1024. end;
  1025. end;
  1026. procedure TImagingCanvas.FillRectBlend(const Rect: TRect; SrcFactor,
  1027. DestFactor: TBlendingFactor);
  1028. var
  1029. DstRect: TRect;
  1030. X, Y: Integer;
  1031. Line: PByte;
  1032. begin
  1033. if (FFillMode <> fmClear) and IntersectRect(DstRect, Rect, FClipRect) then
  1034. begin
  1035. CheckBeforeBlending(SrcFactor, DestFactor, Self);
  1036. for Y := DstRect.Top to DstRect.Bottom - 1 do
  1037. begin
  1038. Line := @PByteArray(FPData.Bits)[(Y * FPData.Width + DstRect.Left) * FFormatInfo.BytesPerPixel];
  1039. for X := DstRect.Left to DstRect.Right - 1 do
  1040. begin
  1041. PixelBlendProc(FFillColorFP, Line, @FFormatInfo, SrcFactor, DestFactor);
  1042. Inc(Line, FFormatInfo.BytesPerPixel);
  1043. end;
  1044. end;
  1045. end;
  1046. end;
  1047. procedure TImagingCanvas.Rectangle(const Rect: TRect);
  1048. begin
  1049. FillRect(Rect);
  1050. FrameRect(Rect);
  1051. end;
  1052. procedure TImagingCanvas.Ellipse(const Rect: TRect);
  1053. var
  1054. RadX, RadY, DeltaX, DeltaY, R, RX, RY: LongInt;
  1055. X1, X2, Y1, Y2, Bpp, OldY: LongInt;
  1056. Fill, Pen: TColorFPRec;
  1057. begin
  1058. // TODO: Use PenWidth
  1059. X1 := Rect.Left;
  1060. X2 := Rect.Right;
  1061. Y1 := Rect.Top;
  1062. Y2 := Rect.Bottom;
  1063. TranslateFPToNative(FPenColorFP, @Pen);
  1064. TranslateFPToNative(FFillColorFP, @Fill);
  1065. Bpp := FFormatInfo.BytesPerPixel;
  1066. SwapMin(X1, X2);
  1067. SwapMin(Y1, Y2);
  1068. RadX := (X2 - X1) div 2;
  1069. RadY := (Y2 - Y1) div 2;
  1070. Y1 := Y1 + RadY;
  1071. Y2 := Y1;
  1072. OldY := Y1;
  1073. DeltaX := (RadX * RadX);
  1074. DeltaY := (RadY * RadY);
  1075. R := RadX * RadY * RadY;
  1076. RX := R;
  1077. RY := 0;
  1078. if (FFillMode <> fmClear) then
  1079. HorzLineInternal(X1, X2, Y1, @Fill, Bpp);
  1080. CopyPixelInternal(X1, Y1, @Pen, Bpp);
  1081. CopyPixelInternal(X2, Y1, @Pen, Bpp);
  1082. while RadX > 0 do
  1083. begin
  1084. if R > 0 then
  1085. begin
  1086. Inc(Y1);
  1087. Dec(Y2);
  1088. Inc(RY, DeltaX);
  1089. Dec(R, RY);
  1090. end;
  1091. if R <= 0 then
  1092. begin
  1093. Dec(RadX);
  1094. Inc(X1);
  1095. Dec(X2);
  1096. Dec(RX, DeltaY);
  1097. Inc(R, RX);
  1098. end;
  1099. if (OldY <> Y1) and (FFillMode <> fmClear) then
  1100. begin
  1101. HorzLineInternal(X1, X2, Y1, @Fill, Bpp);
  1102. HorzLineInternal(X1, X2, Y2, @Fill, Bpp);
  1103. end;
  1104. OldY := Y1;
  1105. CopyPixelInternal(X1, Y1, @Pen, Bpp);
  1106. CopyPixelInternal(X2, Y1, @Pen, Bpp);
  1107. CopyPixelInternal(X1, Y2, @Pen, Bpp);
  1108. CopyPixelInternal(X2, Y2, @Pen, Bpp);
  1109. end;
  1110. end;
  1111. procedure TImagingCanvas.FloodFill(X, Y: Integer; BoundaryFillMode: Boolean);
  1112. var
  1113. Stack: array of TPoint;
  1114. StackPos, Y1: Integer;
  1115. OldColor: TColor32;
  1116. SpanLeft, SpanRight: Boolean;
  1117. procedure Push(AX, AY: Integer);
  1118. begin
  1119. if StackPos < High(Stack) then
  1120. begin
  1121. Inc(StackPos);
  1122. Stack[StackPos].X := AX;
  1123. Stack[StackPos].Y := AY;
  1124. end
  1125. else
  1126. begin
  1127. SetLength(Stack, Length(Stack) + FPData.Width);
  1128. Push(AX, AY);
  1129. end;
  1130. end;
  1131. function Pop(out AX, AY: Integer): Boolean;
  1132. begin
  1133. if StackPos > 0 then
  1134. begin
  1135. AX := Stack[StackPos].X;
  1136. AY := Stack[StackPos].Y;
  1137. Dec(StackPos);
  1138. Result := True;
  1139. end
  1140. else
  1141. Result := False;
  1142. end;
  1143. function Compare(AX, AY: Integer): Boolean;
  1144. var
  1145. Color: TColor32;
  1146. begin
  1147. Color := GetPixel32(AX, AY);
  1148. if BoundaryFillMode then
  1149. Result := (Color <> FFillColor32) and (Color <> FPenColor32)
  1150. else
  1151. Result := Color = OldColor;
  1152. end;
  1153. begin
  1154. // Scanline Floodfill Algorithm With Stack
  1155. // http://student.kuleuven.be/~m0216922/CG/floodfill.html
  1156. if not PtInRect(FClipRect, Point(X, Y)) then Exit;
  1157. SetLength(Stack, FPData.Width * 4);
  1158. StackPos := 0;
  1159. OldColor := GetPixel32(X, Y);
  1160. Push(X, Y);
  1161. while Pop(X, Y) do
  1162. begin
  1163. Y1 := Y;
  1164. while (Y1 >= FClipRect.Top) and Compare(X, Y1) do
  1165. Dec(Y1);
  1166. Inc(Y1);
  1167. SpanLeft := False;
  1168. SpanRight := False;
  1169. while (Y1 < FClipRect.Bottom) and Compare(X, Y1) do
  1170. begin
  1171. SetPixel32(X, Y1, FFillColor32);
  1172. if not SpanLeft and (X > FClipRect.Left) and Compare(X - 1, Y1) then
  1173. begin
  1174. Push(X - 1, Y1);
  1175. SpanLeft := True;
  1176. end
  1177. else if SpanLeft and (X > FClipRect.Left) and not Compare(X - 1, Y1) then
  1178. SpanLeft := False
  1179. else if not SpanRight and (X < FClipRect.Right - 1) and Compare(X + 1, Y1)then
  1180. begin
  1181. Push(X + 1, Y1);
  1182. SpanRight := True;
  1183. end
  1184. else if SpanRight and (X < FClipRect.Right - 1) and not Compare(X + 1, Y1) then
  1185. SpanRight := False;
  1186. Inc(Y1);
  1187. end;
  1188. end;
  1189. end;
  1190. procedure TImagingCanvas.DrawInternal(const SrcRect: TRect;
  1191. DestCanvas: TImagingCanvas; DestX, DestY: LongInt; SrcFactor,
  1192. DestFactor: TBlendingFactor; PixelWriteProc: TPixelWriteProc);
  1193. var
  1194. X, Y, SrcX, SrcY, Width, Height, SrcBpp, DestBpp: LongInt;
  1195. PSrc: TColorFPRec;
  1196. SrcPointer, DestPointer: PByte;
  1197. begin
  1198. CheckBeforeBlending(SrcFactor, DestFactor, DestCanvas);
  1199. SrcX := SrcRect.Left;
  1200. SrcY := SrcRect.Top;
  1201. Width := SrcRect.Right - SrcRect.Left;
  1202. Height := SrcRect.Bottom - SrcRect.Top;
  1203. SrcBpp := FFormatInfo.BytesPerPixel;
  1204. DestBpp := DestCanvas.FFormatInfo.BytesPerPixel;
  1205. // Clip src and dst rects
  1206. ClipCopyBounds(SrcX, SrcY, Width, Height, DestX, DestY,
  1207. FPData.Width, FPData.Height, DestCanvas.ClipRect);
  1208. for Y := 0 to Height - 1 do
  1209. begin
  1210. // Get src and dst scanlines
  1211. SrcPointer := @PByteArray(FPData.Bits)[((SrcY + Y) * FPData.Width + SrcX) * SrcBpp];
  1212. DestPointer := @PByteArray(DestCanvas.FPData.Bits)[((DestY + Y) * DestCanvas.FPData.Width + DestX) * DestBpp];
  1213. for X := 0 to Width - 1 do
  1214. begin
  1215. PSrc := FFormatInfo.GetPixelFP(SrcPointer, @FFormatInfo, FPData.Palette);
  1216. // Call pixel writer procedure - combine source and dest pixels
  1217. PixelWriteProc(PSrc, DestPointer, @DestCanvas.FFormatInfo, SrcFactor, DestFactor);
  1218. // Increment pixel pointers
  1219. Inc(SrcPointer, SrcBpp);
  1220. Inc(DestPointer, DestBpp);
  1221. end;
  1222. end;
  1223. end;
  1224. procedure TImagingCanvas.DrawBlend(const SrcRect: TRect; DestCanvas: TImagingCanvas;
  1225. DestX, DestY: LongInt; SrcFactor, DestFactor: TBlendingFactor);
  1226. begin
  1227. DrawInternal(SrcRect, DestCanvas, DestX, DestY, SrcFactor, DestFactor, PixelBlendProc);
  1228. end;
  1229. procedure TImagingCanvas.DrawAlpha(const SrcRect: TRect; DestCanvas: TImagingCanvas;
  1230. DestX, DestY: LongInt);
  1231. begin
  1232. DrawInternal(SrcRect, DestCanvas, DestX, DestY, bfIgnore, bfIgnore, PixelAlphaProc);
  1233. end;
  1234. procedure TImagingCanvas.DrawAdd(const SrcRect: TRect;
  1235. DestCanvas: TImagingCanvas; DestX, DestY: LongInt);
  1236. begin
  1237. DrawInternal(SrcRect, DestCanvas, DestX, DestY, bfIgnore, bfIgnore, PixelAddProc);
  1238. end;
  1239. procedure TImagingCanvas.StretchDrawInternal(const SrcRect: TRect;
  1240. DestCanvas: TImagingCanvas; const DestRect: TRect;
  1241. SrcFactor, DestFactor: TBlendingFactor; Filter: TResizeFilter;
  1242. PixelWriteProc: TPixelWriteProc);
  1243. const
  1244. FilterMapping: array[TResizeFilter] of TSamplingFilter =
  1245. (sfNearest, sfLinear, DefaultCubicFilter, sfLanczos);
  1246. var
  1247. X, Y, I, J, SrcX, SrcY, SrcWidth, SrcHeight: LongInt;
  1248. DestX, DestY, DestWidth, DestHeight, SrcBpp, DestBpp: LongInt;
  1249. SrcPix: TColorFPRec;
  1250. MapX, MapY: TMappingTable;
  1251. XMinimum, XMaximum: LongInt;
  1252. LineBuffer: array of TColorFPRec;
  1253. ClusterX, ClusterY: TCluster;
  1254. Weight, AccumA, AccumR, AccumG, AccumB: Single;
  1255. DestLine: PByte;
  1256. FilterFunction: TFilterFunction;
  1257. Radius: Single;
  1258. begin
  1259. CheckBeforeBlending(SrcFactor, DestFactor, DestCanvas);
  1260. SrcX := SrcRect.Left;
  1261. SrcY := SrcRect.Top;
  1262. SrcWidth := SrcRect.Right - SrcRect.Left;
  1263. SrcHeight := SrcRect.Bottom - SrcRect.Top;
  1264. DestX := DestRect.Left;
  1265. DestY := DestRect.Top;
  1266. DestWidth := DestRect.Right - DestRect.Left;
  1267. DestHeight := DestRect.Bottom - DestRect.Top;
  1268. SrcBpp := FFormatInfo.BytesPerPixel;
  1269. DestBpp := DestCanvas.FFormatInfo.BytesPerPixel;
  1270. // Get actual resampling filter and radius
  1271. FilterFunction := SamplingFilterFunctions[FilterMapping[Filter]];
  1272. Radius := SamplingFilterRadii[FilterMapping[Filter]];
  1273. // Clip src and dst rects
  1274. ClipStretchBounds(SrcX, SrcY, SrcWidth, SrcHeight, DestX, DestY, DestWidth, DestHeight,
  1275. FPData.Width, FPData.Height, DestCanvas.ClipRect);
  1276. // Generate mapping tables
  1277. MapX := BuildMappingTable(DestX, DestX + DestWidth, SrcX, SrcX + SrcWidth,
  1278. FPData.Width, FilterFunction, Radius, False);
  1279. MapY := BuildMappingTable(DestY, DestY + DestHeight, SrcY, SrcY + SrcHeight,
  1280. FPData.Height, FilterFunction, Radius, False);
  1281. FindExtremes(MapX, XMinimum, XMaximum);
  1282. SetLength(LineBuffer, XMaximum - XMinimum + 1);
  1283. for J := 0 to DestHeight - 1 do
  1284. begin
  1285. ClusterY := MapY[J];
  1286. for X := XMinimum to XMaximum do
  1287. begin
  1288. AccumA := 0.0;
  1289. AccumR := 0.0;
  1290. AccumG := 0.0;
  1291. AccumB := 0.0;
  1292. for Y := 0 to Length(ClusterY) - 1 do
  1293. begin
  1294. Weight := ClusterY[Y].Weight;
  1295. SrcPix := FFormatInfo.GetPixelFP(@PByteArray(FPData.Bits)[(ClusterY[Y].Pos * FPData.Width + X) * SrcBpp],
  1296. @FFormatInfo, FPData.Palette);
  1297. AccumB := AccumB + SrcPix.B * Weight;
  1298. AccumG := AccumG + SrcPix.G * Weight;
  1299. AccumR := AccumR + SrcPix.R * Weight;
  1300. AccumA := AccumA + SrcPix.A * Weight;
  1301. end;
  1302. with LineBuffer[X - XMinimum] do
  1303. begin
  1304. A := AccumA;
  1305. R := AccumR;
  1306. G := AccumG;
  1307. B := AccumB;
  1308. end;
  1309. end;
  1310. DestLine := @PByteArray(DestCanvas.FPData.Bits)[((J + DestY) * DestCanvas.FPData.Width + DestX) * DestBpp];
  1311. for I := 0 to DestWidth - 1 do
  1312. begin
  1313. ClusterX := MapX[I];
  1314. AccumA := 0.0;
  1315. AccumR := 0.0;
  1316. AccumG := 0.0;
  1317. AccumB := 0.0;
  1318. for X := 0 to Length(ClusterX) - 1 do
  1319. begin
  1320. Weight := ClusterX[X].Weight;
  1321. with LineBuffer[ClusterX[X].Pos - XMinimum] do
  1322. begin
  1323. AccumB := AccumB + B * Weight;
  1324. AccumG := AccumG + G * Weight;
  1325. AccumR := AccumR + R * Weight;
  1326. AccumA := AccumA + A * Weight;
  1327. end;
  1328. end;
  1329. SrcPix.A := AccumA;
  1330. SrcPix.R := AccumR;
  1331. SrcPix.G := AccumG;
  1332. SrcPix.B := AccumB;
  1333. // Write resulting blended pixel
  1334. PixelWriteProc(SrcPix, DestLine, @DestCanvas.FFormatInfo, SrcFactor, DestFactor);
  1335. Inc(DestLine, DestBpp);
  1336. end;
  1337. end;
  1338. end;
  1339. procedure TImagingCanvas.StretchDrawBlend(const SrcRect: TRect;
  1340. DestCanvas: TImagingCanvas; const DestRect: TRect;
  1341. SrcFactor, DestFactor: TBlendingFactor; Filter: TResizeFilter);
  1342. begin
  1343. StretchDrawInternal(SrcRect, DestCanvas, DestRect, SrcFactor, DestFactor, Filter, PixelBlendProc);
  1344. end;
  1345. procedure TImagingCanvas.StretchDrawAlpha(const SrcRect: TRect;
  1346. DestCanvas: TImagingCanvas; const DestRect: TRect; Filter: TResizeFilter);
  1347. begin
  1348. StretchDrawInternal(SrcRect, DestCanvas, DestRect, bfIgnore, bfIgnore, Filter, PixelAlphaProc);
  1349. end;
  1350. procedure TImagingCanvas.StretchDrawAdd(const SrcRect: TRect;
  1351. DestCanvas: TImagingCanvas; const DestRect: TRect; Filter: TResizeFilter);
  1352. begin
  1353. StretchDrawInternal(SrcRect, DestCanvas, DestRect, bfIgnore, bfIgnore, Filter, PixelAddProc);
  1354. end;
  1355. procedure TImagingCanvas.ApplyConvolution(Kernel: PLongInt; KernelSize,
  1356. Divisor: LongInt; Bias: Single; ClampChannels: Boolean);
  1357. var
  1358. X, Y, I, J, PosY, PosX, SizeDiv2, KernelValue, WidthBytes, Bpp: LongInt;
  1359. R, G, B, DivFloat: Single;
  1360. Pixel: TColorFPRec;
  1361. TempImage: TImageData;
  1362. DstPointer, SrcPointer: PByte;
  1363. begin
  1364. SizeDiv2 := KernelSize div 2;
  1365. DivFloat := IffFloat(Divisor > 1, 1.0 / Divisor, 1.0);
  1366. Bpp := FFormatInfo.BytesPerPixel;
  1367. WidthBytes := FPData.Width * Bpp;
  1368. InitImage(TempImage);
  1369. CloneImage(FPData^, TempImage);
  1370. try
  1371. // For every pixel in clip rect
  1372. for Y := FClipRect.Top to FClipRect.Bottom - 1 do
  1373. begin
  1374. DstPointer := @PByteArray(FPData.Bits)[Y * WidthBytes + FClipRect.Left * Bpp];
  1375. for X := FClipRect.Left to FClipRect.Right - 1 do
  1376. begin
  1377. // Reset accumulators
  1378. R := 0.0;
  1379. G := 0.0;
  1380. B := 0.0;
  1381. for J := 0 to KernelSize - 1 do
  1382. begin
  1383. PosY := ClampInt(Y + J - SizeDiv2, FClipRect.Top, FClipRect.Bottom - 1);
  1384. for I := 0 to KernelSize - 1 do
  1385. begin
  1386. PosX := ClampInt(X + I - SizeDiv2, FClipRect.Left, FClipRect.Right - 1);
  1387. SrcPointer := @PByteArray(TempImage.Bits)[PosY * WidthBytes + PosX * Bpp];
  1388. // Get pixels from neighborhood of current pixel and add their
  1389. // colors to accumulators weighted by filter kernel values
  1390. Pixel := FFormatInfo.GetPixelFP(SrcPointer, @FFormatInfo, TempImage.Palette);
  1391. KernelValue := PUInt32Array(Kernel)[J * KernelSize + I];
  1392. R := R + Pixel.R * KernelValue;
  1393. G := G + Pixel.G * KernelValue;
  1394. B := B + Pixel.B * KernelValue;
  1395. end;
  1396. end;
  1397. Pixel := FFormatInfo.GetPixelFP(DstPointer, @FFormatInfo, FPData.Palette);
  1398. Pixel.R := R * DivFloat + Bias;
  1399. Pixel.G := G * DivFloat + Bias;
  1400. Pixel.B := B * DivFloat + Bias;
  1401. if ClampChannels then
  1402. ClampFloatPixel(Pixel);
  1403. // Set resulting pixel color
  1404. FFormatInfo.SetPixelFP(DstPointer, @FFormatInfo, FPData.Palette, Pixel);
  1405. Inc(DstPointer, Bpp);
  1406. end;
  1407. end;
  1408. finally
  1409. FreeImage(TempImage);
  1410. end;
  1411. end;
  1412. procedure TImagingCanvas.ApplyConvolution3x3(const Filter: TConvolutionFilter3x3);
  1413. begin
  1414. ApplyConvolution(@Filter.Kernel, 3, Filter.Divisor, Filter.Bias, True);
  1415. end;
  1416. procedure TImagingCanvas.ApplyConvolution5x5(const Filter: TConvolutionFilter5x5);
  1417. begin
  1418. ApplyConvolution(@Filter.Kernel, 5, Filter.Divisor, Filter.Bias, True);
  1419. end;
  1420. procedure TImagingCanvas.ApplyNonLinearFilter(FilterSize: Integer; SelectFunc: TSelectPixelFunction);
  1421. var
  1422. X, Y, I, J, PosY, PosX, SizeDiv2, WidthBytes, Bpp: LongInt;
  1423. Pixel: TColorFPRec;
  1424. TempImage: TImageData;
  1425. DstPointer, SrcPointer: PByte;
  1426. NeighPixels: TDynFPPixelArray;
  1427. begin
  1428. SizeDiv2 := FilterSize div 2;
  1429. Bpp := FFormatInfo.BytesPerPixel;
  1430. WidthBytes := FPData.Width * Bpp;
  1431. SetLength(NeighPixels, FilterSize * FilterSize);
  1432. InitImage(TempImage);
  1433. CloneImage(FPData^, TempImage);
  1434. try
  1435. // For every pixel in clip rect
  1436. for Y := FClipRect.Top to FClipRect.Bottom - 1 do
  1437. begin
  1438. DstPointer := @PByteArray(FPData.Bits)[Y * WidthBytes + FClipRect.Left * Bpp];
  1439. for X := FClipRect.Left to FClipRect.Right - 1 do
  1440. begin
  1441. for J := 0 to FilterSize - 1 do
  1442. begin
  1443. PosY := ClampInt(Y + J - SizeDiv2, FClipRect.Top, FClipRect.Bottom - 1);
  1444. for I := 0 to FilterSize - 1 do
  1445. begin
  1446. PosX := ClampInt(X + I - SizeDiv2, FClipRect.Left, FClipRect.Right - 1);
  1447. SrcPointer := @PByteArray(TempImage.Bits)[PosY * WidthBytes + PosX * Bpp];
  1448. // Get pixels from neighbourhood of current pixel and store them
  1449. Pixel := FFormatInfo.GetPixelFP(SrcPointer, @FFormatInfo, TempImage.Palette);
  1450. NeighPixels[J * FilterSize + I] := Pixel;
  1451. end;
  1452. end;
  1453. // Choose pixel using custom function
  1454. Pixel := SelectFunc(NeighPixels);
  1455. // Set resulting pixel color
  1456. FFormatInfo.SetPixelFP(DstPointer, @FFormatInfo, FPData.Palette, Pixel);
  1457. Inc(DstPointer, Bpp);
  1458. end;
  1459. end;
  1460. finally
  1461. FreeImage(TempImage);
  1462. end;
  1463. end;
  1464. procedure TImagingCanvas.ApplyMedianFilter(FilterSize: Integer);
  1465. begin
  1466. ApplyNonLinearFilter(FilterSize, MedianSelect);
  1467. end;
  1468. procedure TImagingCanvas.ApplyMinFilter(FilterSize: Integer);
  1469. begin
  1470. ApplyNonLinearFilter(FilterSize, MinSelect);
  1471. end;
  1472. procedure TImagingCanvas.ApplyMaxFilter(FilterSize: Integer);
  1473. begin
  1474. ApplyNonLinearFilter(FilterSize, MaxSelect);
  1475. end;
  1476. procedure TImagingCanvas.PointTransform(Transform: TPointTransformFunction;
  1477. Param1, Param2, Param3: Single);
  1478. var
  1479. X, Y, Bpp, WidthBytes: Integer;
  1480. PixPointer: PByte;
  1481. Pixel: TColorFPRec;
  1482. begin
  1483. Bpp := FFormatInfo.BytesPerPixel;
  1484. WidthBytes := FPData.Width * Bpp;
  1485. // For every pixel in clip rect
  1486. for Y := FClipRect.Top to FClipRect.Bottom - 1 do
  1487. begin
  1488. PixPointer := @PByteArray(FPData.Bits)[Y * WidthBytes + FClipRect.Left * Bpp];
  1489. for X := FClipRect.Left to FClipRect.Right - 1 do
  1490. begin
  1491. Pixel := FFormatInfo.GetPixelFP(PixPointer, @FFormatInfo, FPData.Palette);
  1492. FFormatInfo.SetPixelFP(PixPointer, @FFormatInfo, FPData.Palette,
  1493. Transform(Pixel, Param1, Param2, Param3));
  1494. Inc(PixPointer, Bpp);
  1495. end;
  1496. end;
  1497. end;
  1498. procedure TImagingCanvas.ModifyContrastBrightness(Contrast, Brightness: Single);
  1499. begin
  1500. PointTransform(TransformContrastBrightness, 1.0 + Contrast / 100,
  1501. Brightness / 100, 0);
  1502. end;
  1503. procedure TImagingCanvas.GammaCorrection(Red, Green, Blue: Single);
  1504. begin
  1505. PointTransform(TransformGamma, Red, Green, Blue);
  1506. end;
  1507. procedure TImagingCanvas.InvertColors;
  1508. begin
  1509. PointTransform(TransformInvert, 0, 0, 0);
  1510. end;
  1511. procedure TImagingCanvas.Threshold(Red, Green, Blue: Single);
  1512. begin
  1513. PointTransform(TransformThreshold, Red, Green, Blue);
  1514. end;
  1515. procedure TImagingCanvas.AdjustColorLevels(BlackPoint, WhitePoint, MidPoint: Single);
  1516. begin
  1517. PointTransform(TransformLevels, BlackPoint, WhitePoint, 1.0 / MidPoint);
  1518. end;
  1519. procedure TImagingCanvas.PremultiplyAlpha;
  1520. begin
  1521. PointTransform(TransformPremultiplyAlpha, 0, 0, 0);
  1522. end;
  1523. procedure TImagingCanvas.UnPremultiplyAlpha;
  1524. begin
  1525. PointTransform(TransformUnPremultiplyAlpha, 0, 0, 0);
  1526. end;
  1527. procedure TImagingCanvas.GetHistogram(out Red, Green, Blue, Alpha,
  1528. Gray: THistogramArray);
  1529. var
  1530. X, Y, Bpp: Integer;
  1531. PixPointer: PByte;
  1532. Color32: TColor32Rec;
  1533. begin
  1534. FillChar(Red, SizeOf(Red), 0);
  1535. FillChar(Green, SizeOf(Green), 0);
  1536. FillChar(Blue, SizeOf(Blue), 0);
  1537. FillChar(Alpha, SizeOf(Alpha), 0);
  1538. FillChar(Gray, SizeOf(Gray), 0);
  1539. Bpp := FFormatInfo.BytesPerPixel;
  1540. for Y := FClipRect.Top to FClipRect.Bottom - 1 do
  1541. begin
  1542. PixPointer := @PByteArray(FPData.Bits)[Y * FPData.Width * Bpp + FClipRect.Left * Bpp];
  1543. for X := FClipRect.Left to FClipRect.Right - 1 do
  1544. begin
  1545. Color32 := FFormatInfo.GetPixel32(PixPointer, @FFormatInfo, FPData.Palette);
  1546. Inc(Red[Color32.R]);
  1547. Inc(Green[Color32.G]);
  1548. Inc(Blue[Color32.B]);
  1549. Inc(Alpha[Color32.A]);
  1550. Inc(Gray[Round(GrayConv.R * Color32.R + GrayConv.G * Color32.G + GrayConv.B * Color32.B)]);
  1551. Inc(PixPointer, Bpp);
  1552. end;
  1553. end;
  1554. end;
  1555. procedure TImagingCanvas.FillChannel(ChannelId: Integer; NewChannelValue: Byte);
  1556. var
  1557. X, Y, Bpp: Integer;
  1558. PixPointer: PByte;
  1559. Color32: TColor32Rec;
  1560. begin
  1561. Bpp := FFormatInfo.BytesPerPixel;
  1562. for Y := FClipRect.Top to FClipRect.Bottom - 1 do
  1563. begin
  1564. PixPointer := @PByteArray(FPData.Bits)[Y * FPData.Width * Bpp + FClipRect.Left * Bpp];
  1565. for X := FClipRect.Left to FClipRect.Right - 1 do
  1566. begin
  1567. Color32 := FFormatInfo.GetPixel32(PixPointer, @FFormatInfo, FPData.Palette);
  1568. Color32.Channels[ChannelId] := NewChannelValue;
  1569. FFormatInfo.SetPixel32(PixPointer, @FFormatInfo, FPData.Palette, Color32);
  1570. Inc(PixPointer, Bpp);
  1571. end;
  1572. end;
  1573. end;
  1574. procedure TImagingCanvas.FillChannelFP(ChannelId: Integer; NewChannelValue: Single);
  1575. var
  1576. X, Y, Bpp: Integer;
  1577. PixPointer: PByte;
  1578. ColorFP: TColorFPRec;
  1579. begin
  1580. Bpp := FFormatInfo.BytesPerPixel;
  1581. for Y := FClipRect.Top to FClipRect.Bottom - 1 do
  1582. begin
  1583. PixPointer := @PByteArray(FPData.Bits)[Y * FPData.Width * Bpp + FClipRect.Left * Bpp];
  1584. for X := FClipRect.Left to FClipRect.Right - 1 do
  1585. begin
  1586. ColorFP := FFormatInfo.GetPixelFP(PixPointer, @FFormatInfo, FPData.Palette);
  1587. ColorFP.Channels[ChannelId] := NewChannelValue;
  1588. FFormatInfo.SetPixelFP(PixPointer, @FFormatInfo, FPData.Palette, ColorFP);
  1589. Inc(PixPointer, Bpp);
  1590. end;
  1591. end;
  1592. end;
  1593. class function TImagingCanvas.GetSupportedFormats: TImageFormats;
  1594. begin
  1595. Result := [ifIndex8..Pred(ifDXT1)];
  1596. end;
  1597. { TFastARGB32Canvas }
  1598. destructor TFastARGB32Canvas.Destroy;
  1599. begin
  1600. FreeMem(FScanlines);
  1601. inherited Destroy;
  1602. end;
  1603. procedure TFastARGB32Canvas.AlphaBlendPixels(SrcPix, DestPix: PColor32Rec);
  1604. var
  1605. SrcAlpha, DestAlpha, FinalAlpha: Integer;
  1606. begin
  1607. FinalAlpha := SrcPix.A + 1 + (DestPix.A * (256 - SrcPix.A)) shr 8;
  1608. if FinalAlpha = 0 then
  1609. SrcAlpha := 0
  1610. else
  1611. SrcAlpha := (SrcPix.A shl 8) div FinalAlpha;
  1612. DestAlpha := 256 - SrcAlpha;
  1613. DestPix.A := ClampToByte(FinalAlpha);
  1614. DestPix.R := (SrcPix.R * SrcAlpha + DestPix.R * DestAlpha) shr 8;
  1615. DestPix.G := (SrcPix.G * SrcAlpha + DestPix.G * DestAlpha) shr 8;
  1616. DestPix.B := (SrcPix.B * SrcAlpha + DestPix.B * DestAlpha) shr 8;
  1617. end;
  1618. procedure TFastARGB32Canvas.DrawAlpha(const SrcRect: TRect;
  1619. DestCanvas: TImagingCanvas; DestX, DestY: LongInt);
  1620. var
  1621. X, Y, SrcX, SrcY, Width, Height: LongInt;
  1622. SrcPix, DestPix: PColor32Rec;
  1623. begin
  1624. if DestCanvas.ClassType <> Self.ClassType then
  1625. begin
  1626. inherited;
  1627. Exit;
  1628. end;
  1629. SrcX := SrcRect.Left;
  1630. SrcY := SrcRect.Top;
  1631. Width := SrcRect.Right - SrcRect.Left;
  1632. Height := SrcRect.Bottom - SrcRect.Top;
  1633. ClipCopyBounds(SrcX, SrcY, Width, Height, DestX, DestY,
  1634. FPData.Width, FPData.Height, DestCanvas.ClipRect);
  1635. for Y := 0 to Height - 1 do
  1636. begin
  1637. SrcPix := @FScanlines[SrcY + Y, SrcX];
  1638. DestPix := @TFastARGB32Canvas(DestCanvas).FScanlines[DestY + Y, DestX];
  1639. for X := 0 to Width - 1 do
  1640. begin
  1641. AlphaBlendPixels(SrcPix, DestPix);
  1642. Inc(SrcPix);
  1643. Inc(DestPix);
  1644. end;
  1645. end;
  1646. end;
  1647. function TFastARGB32Canvas.GetPixel32(X, Y: LongInt): TColor32;
  1648. begin
  1649. Result := FScanlines[Y, X].Color;
  1650. end;
  1651. procedure TFastARGB32Canvas.SetPixel32(X, Y: LongInt; const Value: TColor32);
  1652. begin
  1653. if (X >= FClipRect.Left) and (Y >= FClipRect.Top) and
  1654. (X < FClipRect.Right) and (Y < FClipRect.Bottom) then
  1655. begin
  1656. FScanlines[Y, X].Color := Value;
  1657. end;
  1658. end;
  1659. procedure TFastARGB32Canvas.StretchDrawAlpha(const SrcRect: TRect;
  1660. DestCanvas: TImagingCanvas; const DestRect: TRect; Filter: TResizeFilter);
  1661. var
  1662. X, Y, ScaleX, ScaleY, Yp, Xp, Weight1, Weight2, Weight3, Weight4, InvFracY, T1, T2: Integer;
  1663. FracX, FracY: Cardinal;
  1664. SrcX, SrcY, SrcWidth, SrcHeight: LongInt;
  1665. DestX, DestY, DestWidth, DestHeight: LongInt;
  1666. SrcLine, SrcLine2: PColor32RecArray;
  1667. DestPix: PColor32Rec;
  1668. Accum: TColor32Rec;
  1669. begin
  1670. if (Filter = rfBicubic) or (DestCanvas.ClassType <> Self.ClassType) then
  1671. begin
  1672. inherited;
  1673. Exit;
  1674. end;
  1675. SrcX := SrcRect.Left;
  1676. SrcY := SrcRect.Top;
  1677. SrcWidth := SrcRect.Right - SrcRect.Left;
  1678. SrcHeight := SrcRect.Bottom - SrcRect.Top;
  1679. DestX := DestRect.Left;
  1680. DestY := DestRect.Top;
  1681. DestWidth := DestRect.Right - DestRect.Left;
  1682. DestHeight := DestRect.Bottom - DestRect.Top;
  1683. // Clip src and dst rects
  1684. ClipStretchBounds(SrcX, SrcY, SrcWidth, SrcHeight, DestX, DestY, DestWidth, DestHeight,
  1685. FPData.Width, FPData.Height, DestCanvas.ClipRect);
  1686. ScaleX := (SrcWidth shl 16) div DestWidth;
  1687. ScaleY := (SrcHeight shl 16) div DestHeight;
  1688. // Nearest and linear filtering using fixed point math
  1689. if Filter = rfNearest then
  1690. begin
  1691. Yp := 0;
  1692. for Y := DestY to DestY + DestHeight - 1 do
  1693. begin
  1694. Xp := 0;
  1695. SrcLine := @FScanlines[SrcY + Yp shr 16, SrcX];
  1696. DestPix := @TFastARGB32Canvas(DestCanvas).FScanlines[Y, DestX];
  1697. for X := 0 to DestWidth - 1 do
  1698. begin
  1699. AlphaBlendPixels(@SrcLine[Xp shr 16], DestPix);
  1700. Inc(DestPix);
  1701. Inc(Xp, ScaleX);
  1702. end;
  1703. Inc(Yp, ScaleY);
  1704. end;
  1705. end
  1706. else
  1707. begin
  1708. Yp := (ScaleY shr 1) - $8000;
  1709. for Y := DestY to DestY + DestHeight - 1 do
  1710. begin
  1711. DestPix := @TFastARGB32Canvas(DestCanvas).FScanlines[Y, DestX];
  1712. if Yp < 0 then
  1713. begin
  1714. T1 := 0;
  1715. FracY := 0;
  1716. InvFracY := $10000;
  1717. end
  1718. else
  1719. begin
  1720. T1 := Yp shr 16;
  1721. FracY := Yp and $FFFF;
  1722. InvFracY := (not Yp and $FFFF) + 1;
  1723. end;
  1724. T2 := Iff(T1 < SrcHeight - 1, T1 + 1, T1);
  1725. SrcLine := @Scanlines[T1 + SrcY, SrcX];
  1726. SrcLine2 := @Scanlines[T2 + SrcY, SrcX];
  1727. Xp := (ScaleX shr 1) - $8000;
  1728. for X := 0 to DestWidth - 1 do
  1729. begin
  1730. if Xp < 0 then
  1731. begin
  1732. T1 := 0;
  1733. FracX := 0;
  1734. end
  1735. else
  1736. begin
  1737. T1 := Xp shr 16;
  1738. FracX := Xp and $FFFF;
  1739. end;
  1740. T2 := Iff(T1 < SrcWidth - 1, T1 + 1, T1);
  1741. Weight2:= Integer((Cardinal(InvFracY) * FracX) shr 16); // cast to Card, Int can overflow here
  1742. Weight1:= InvFracY - Weight2;
  1743. Weight4:= Integer((Cardinal(FracY) * FracX) shr 16);
  1744. Weight3:= FracY - Weight4;
  1745. Accum.B := (SrcLine[T1].B * Weight1 + SrcLine[T2].B * Weight2 +
  1746. SrcLine2[T1].B * Weight3 + SrcLine2[T2].B * Weight4 + $8000) shr 16;
  1747. Accum.G := (SrcLine[T1].G * Weight1 + SrcLine[T2].G * Weight2 +
  1748. SrcLine2[T1].G * Weight3 + SrcLine2[T2].G * Weight4 + $8000) shr 16;
  1749. Accum.R := (SrcLine[T1].R * Weight1 + SrcLine[T2].R * Weight2 +
  1750. SrcLine2[T1].R * Weight3 + SrcLine2[T2].R * Weight4 + $8000) shr 16;
  1751. Accum.A := (SrcLine[T1].A * Weight1 + SrcLine[T2].A * Weight2 +
  1752. SrcLine2[T1].A * Weight3 + SrcLine2[T2].A * Weight4 + $8000) shr 16;
  1753. AlphaBlendPixels(@Accum, DestPix);
  1754. Inc(Xp, ScaleX);
  1755. Inc(DestPix);
  1756. end;
  1757. Inc(Yp, ScaleY);
  1758. end;
  1759. end;
  1760. end;
  1761. procedure TFastARGB32Canvas.UpdateCanvasState;
  1762. var
  1763. I: LongInt;
  1764. ScanPos: PUInt32;
  1765. begin
  1766. inherited UpdateCanvasState;
  1767. // Realloc and update scanline array
  1768. ReallocMem(FScanlines, FPData.Height * SizeOf(PColor32RecArray));
  1769. ScanPos := FPData.Bits;
  1770. for I := 0 to FPData.Height - 1 do
  1771. begin
  1772. FScanlines[I] := PColor32RecArray(ScanPos);
  1773. Inc(ScanPos, FPData.Width);
  1774. end;
  1775. end;
  1776. class function TFastARGB32Canvas.GetSupportedFormats: TImageFormats;
  1777. begin
  1778. Result := [ifA8R8G8B8];
  1779. end;
  1780. procedure TFastARGB32Canvas.InvertColors;
  1781. var
  1782. X, Y: Integer;
  1783. PixPtr: PColor32Rec;
  1784. begin
  1785. for Y := FClipRect.Top to FClipRect.Bottom - 1 do
  1786. begin
  1787. PixPtr := @FScanlines[Y, FClipRect.Left];
  1788. for X := FClipRect.Left to FClipRect.Right - 1 do
  1789. begin
  1790. PixPtr.R := not PixPtr.R;
  1791. PixPtr.G := not PixPtr.G;
  1792. PixPtr.B := not PixPtr.B;
  1793. Inc(PixPtr);
  1794. end;
  1795. end;
  1796. end;
  1797. initialization
  1798. RegisterCanvas(TFastARGB32Canvas);
  1799. finalization
  1800. FreeAndNil(CanvasClasses);
  1801. {
  1802. File Notes:
  1803. -- TODOS ----------------------------------------------------
  1804. - more more more ...
  1805. - implement pen width everywhere
  1806. - more objects (arc, polygon)
  1807. -- 0.26.5 Changes/Bug Fixes ---------------------------------
  1808. - Fixed bug that could raise floating point error in DrawAlpha
  1809. and StretchDrawAlpha.
  1810. - Fixed bug in TImagingCanvas.Line that caused not drawing
  1811. of horz or vert lines.
  1812. -- 0.26.3 Changes/Bug Fixes ---------------------------------
  1813. - Added some methods to TFastARGB32Canvas (InvertColors, DrawAlpha/StretchDrawAlpha)
  1814. - Fixed DrawAlpha/StretchDrawAlpha destination alpha calculation.
  1815. - Added PremultiplyAlpha and UnPremultiplyAlpha methods.
  1816. -- 0.26.1 Changes/Bug Fixes ---------------------------------
  1817. - Added FillChannel methods.
  1818. - Added FloodFill method.
  1819. - Added GetHistogram method.
  1820. - Fixed "Invalid FP operation" in AdjustColorLevels in FPC compiled exes
  1821. (thanks to Carlos Gonzalez).
  1822. - Added TImagingCanvas.AdjustColorLevels method.
  1823. -- 0.25.0 Changes/Bug Fixes ---------------------------------
  1824. - Fixed error that could cause AV in linear and nonlinear filters.
  1825. - Added blended rect filling function FillRectBlend.
  1826. - Added drawing function with blending (DrawAlpha, StretchDrawAlpha,
  1827. StretchDrawAdd, DrawBlend, StretchDrawBlend, ...)
  1828. - Added non-linear filters (min, max, median).
  1829. - Added point transforms (invert, contrast, gamma, brightness).
  1830. -- 0.21 Changes/Bug Fixes -----------------------------------
  1831. - Added some new filter kernels for convolution.
  1832. - Added FillMode and PenMode properties.
  1833. - Added FrameRect, Rectangle, Ellipse, and Line methods.
  1834. - Removed HorzLine and VertLine from TFastARGB32Canvas - new versions
  1835. in general canvas is now as fast as those in TFastARGB32Canvas
  1836. (only in case of A8R8G8B8 images of course).
  1837. - Added PenWidth property, updated HorzLine and VertLine to use it.
  1838. -- 0.19 Changes/Bug Fixes -----------------------------------
  1839. - added TFastARGB32Canvas
  1840. - added convolutions, hline, vline
  1841. - unit created, initial stuff added
  1842. }
  1843. end.