ImagingCanvases.pas 72 KB

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