2
0

ImagingCanvases.pas 71 KB

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