Img32.Draw.pas 88 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863
  1. unit Img32.Draw;
  2. (*******************************************************************************
  3. * Author : Angus Johnson *
  4. * Version : 4.8 *
  5. * Date : 10 January 2025 *
  6. * Website : http://www.angusj.com *
  7. * Copyright : Angus Johnson 2019-2025 *
  8. * *
  9. * Purpose : Polygon renderer for TImage32 *
  10. * *
  11. * License : Use, modification & distribution is subject to *
  12. * Boost Software License Ver 1 *
  13. * http://www.boost.org/LICENSE_1_0.txt *
  14. *******************************************************************************)
  15. interface
  16. {$I Img32.inc}
  17. uses
  18. SysUtils, Classes, Types, Math, Img32, Img32.Vector;
  19. type
  20. TFillRule = Img32.Vector.TFillRule;
  21. // TGradientColor: used internally by both
  22. // TLinearGradientRenderer and TRadialGradientRenderer
  23. TGradientColor = record
  24. offset: double;
  25. color: TColor32;
  26. end;
  27. TArrayOfGradientColor = array of TGradientColor;
  28. TGradientFillStyle = (gfsClamp, gfsMirror, gfsRepeat);
  29. // TBoundsProc: Function template for TCustomRenderer.
  30. TBoundsProc = function(dist, colorsCnt: integer): integer;
  31. TBoundsProcD = function(dist: double; colorsCnt: integer): integer;
  32. TImage32ChangeProc = procedure of object;
  33. // TCustomRenderer: can accommodate pixels of any size
  34. TCustomRenderer = class {$IFDEF ABSTRACT_CLASSES} abstract {$ENDIF}
  35. private
  36. fImgWidth : integer;
  37. fImgHeight : integer;
  38. fImgBase : Pointer;
  39. fCurrY : integer;
  40. fCurrLinePtr : Pointer;
  41. fPixelSize : integer;
  42. fChangeProc : TImage32ChangeProc;
  43. fOpacity : Byte;
  44. protected
  45. procedure NotifyChange;
  46. function Initialize(imgBase: Pointer;
  47. imgWidth, imgHeight, pixelSize: integer): Boolean; overload; virtual;
  48. function Initialize(targetImage: TImage32): Boolean; overload; virtual;
  49. function GetDstPixel(x,y: integer): Pointer;
  50. // RenderProc: x & y refer to pixel coords in the destination image and
  51. // where x1 is the start (and left) and x2 is the end of the render
  52. procedure RenderProc(x1, x2, y: integer; alpha: PByte); virtual; abstract;
  53. // RenderProcSkip: is called for every skipped line block if
  54. // SupportsRenderProcSkip=True and the Rasterize() function skips scanlines.
  55. procedure RenderProcSkip(const skippedRect: TRect); virtual;
  56. // SetClipRect is called by the Rasterize() function with the
  57. // rasterization clipRect. The default implementation does nothing.
  58. procedure SetClipRect(const clipRect: TRect); virtual;
  59. // If SupportsRenderProcSkip returns True the Rasterize() function
  60. // will call RenderProcSkip() for every scanline where it didn't have
  61. // anything to rasterize.
  62. function SupportsRenderProcSkip: Boolean; virtual;
  63. public
  64. constructor Create; virtual;
  65. property ImgWidth: integer read fImgWidth;
  66. property ImgHeight: integer read fImgHeight;
  67. property ImgBase: Pointer read fImgBase;
  68. property PixelSize: integer read fPixelSize;
  69. property Opacity: Byte read fOpacity write fOpacity;
  70. end;
  71. TCustomColorRenderer = class(TCustomRenderer)
  72. private
  73. fColor: TColor32;
  74. protected
  75. property Color: TColor32 read fColor write fColor;
  76. public
  77. procedure SetColor(value: TColor32); virtual;
  78. end;
  79. TColorRenderer = class(TCustomColorRenderer)
  80. private
  81. fAlpha: Byte;
  82. protected
  83. procedure RenderProc(x1, x2, y: integer; alpha: PByte); override;
  84. function Initialize(targetImage: TImage32): Boolean; override;
  85. public
  86. constructor Create(color: TColor32 = clNone32); reintroduce;
  87. procedure SetColor(value: TColor32); override;
  88. end;
  89. TAliasedColorRenderer = class(TCustomColorRenderer)
  90. protected
  91. function Initialize(targetImage: TImage32): Boolean; override;
  92. procedure RenderProc(x1, x2, y: integer; alpha: PByte); override;
  93. public
  94. constructor Create(color: TColor32 = clNone32); reintroduce;
  95. end;
  96. // TMaskRenderer masks all pixels inside the clipRect area
  97. // where the alpha[]-array is zero.
  98. TMaskRenderer = class(TCustomRenderer)
  99. private
  100. fClipRect: TRect;
  101. protected
  102. procedure SetClipRect(const clipRect: TRect); override;
  103. procedure RenderProc(x1, x2, y: integer; alpha: PByte); override;
  104. procedure RenderProcSkip(const skippedRect: TRect); override;
  105. function SupportsRenderProcSkip: Boolean; override;
  106. end;
  107. // TCustomRendererCache is used to not create Renderer
  108. // objects for every DrawPolygon/DrawLine function call. The color
  109. // of the TCustomColorRenderer will be changed by the DrawPolygon/
  110. // DrawLine method.
  111. TCustomRendererCache = class(TObject)
  112. private
  113. fColorRenderer: TColorRenderer;
  114. fAliasedColorRenderer: TAliasedColorRenderer;
  115. fMaskRenderer: TMaskRenderer;
  116. public
  117. constructor Create;
  118. destructor Destroy; override;
  119. function GetColorRenderer(color: TColor32): TColorRenderer;
  120. property ColorRenderer: TColorRenderer read fColorRenderer;
  121. property AliasedColorRenderer: TAliasedColorRenderer read fAliasedColorRenderer;
  122. property MaskRenderer: TMaskRenderer read fMaskRenderer;
  123. end;
  124. TEraseRenderer = class(TCustomRenderer)
  125. protected
  126. procedure RenderProc(x1, x2, y: integer; alpha: PByte); override;
  127. end;
  128. TInverseRenderer = class(TCustomRenderer)
  129. protected
  130. procedure RenderProc(x1, x2, y: integer; alpha: PByte); override;
  131. end;
  132. TImageRenderer = class(TCustomRenderer)
  133. private
  134. fImage : TImage32;
  135. fOffset : TPoint;
  136. fBrushPixel : PARGB;
  137. fLastYY : integer;
  138. fMirrorY : Boolean;
  139. fBoundsProc : TBoundsProc;
  140. function GetFirstBrushPixel(x, y: integer): PColor32;
  141. protected
  142. procedure RenderProc(x1, x2, y: integer; alpha: PByte); override;
  143. function Initialize(targetImage: TImage32): Boolean; override;
  144. public
  145. constructor Create(tileFillStyle: TTileFillStyle = tfsRepeat;
  146. brushImage: TImage32 = nil); reintroduce;
  147. destructor Destroy; override;
  148. procedure SetTileFillStyle(value: TTileFillStyle);
  149. property Image: TImage32 read fImage;
  150. property Offset: TPoint read fOffset write fOffset;
  151. end;
  152. // TCustomGradientRenderer is also an abstract class
  153. TCustomGradientRenderer = class(TCustomRenderer)
  154. private
  155. fBoundsProc : TBoundsProc;
  156. fGradientColors : TArrayOfGradientColor;
  157. protected
  158. fColors : TArrayOfColor32;
  159. fColorsCnt : integer;
  160. procedure SetGradientFillStyle(value: TGradientFillStyle); virtual;
  161. public
  162. constructor Create; override;
  163. procedure SetParameters(startColor, endColor: TColor32;
  164. gradFillStyle: TGradientFillStyle = gfsClamp); virtual;
  165. procedure InsertColorStop(offsetFrac: double; color: TColor32);
  166. procedure Clear;
  167. end;
  168. TLinearGradientRenderer = class(TCustomGradientRenderer)
  169. private
  170. fStartPt : TPointD;
  171. fEndPt : TPointD;
  172. fPerpendicOffsets: TArrayOfInteger;
  173. fIsVert : Boolean;
  174. protected
  175. procedure RenderProc(x1, x2, y: integer; alpha: PByte); override;
  176. function Initialize(targetImage: TImage32): Boolean; override;
  177. public
  178. procedure SetParameters(const startPt, endPt: TPointD;
  179. startColor, endColor: TColor32;
  180. gradFillStyle: TGradientFillStyle = gfsClamp); reintroduce;
  181. end;
  182. TRadialGradientRenderer = class(TCustomGradientRenderer)
  183. private
  184. fCenterPt : TPointD;
  185. fScaleX : double;
  186. fScaleY : double;
  187. fColors : TArrayOfColor32;
  188. protected
  189. procedure RenderProc(x1, x2, y: integer; alpha: PByte); override;
  190. function Initialize(targetImage: TImage32): Boolean; override;
  191. public
  192. procedure SetParameters(const focalRect: TRect;
  193. innerColor, outerColor: TColor32;
  194. gradientFillStyle: TGradientFillStyle = gfsClamp); reintroduce;
  195. end;
  196. TSvgRadialGradientRenderer = class(TCustomGradientRenderer)
  197. private
  198. fA, fB : double;
  199. fAA, fBB : double;
  200. fCenterPt : TPointD;
  201. fFocusPt : TPointD;
  202. fBoundsProcD : TBoundsProcD;
  203. protected
  204. procedure RenderProc(x1, x2, y: integer; alpha: PByte); override;
  205. function Initialize(targetImage: TImage32): Boolean; override;
  206. public
  207. procedure SetParameters(const ellipseRect: TRect;
  208. const focus: TPoint; innerColor, outerColor: TColor32;
  209. gradientFillStyle: TGradientFillStyle = gfsClamp); reintroduce;
  210. end;
  211. // Barycentric rendering colorizes inside triangles
  212. TBarycentricRenderer = class(TCustomRenderer)
  213. private
  214. a: TPointD;
  215. c1, c2, c3: TARGB;
  216. v0, v1: TPointD;
  217. d00, d01, d11, invDenom: double;
  218. function GetColor(const pt: TPointD): TColor32;
  219. protected
  220. procedure RenderProc(x1, x2, y: integer; alpha: PByte); override;
  221. public
  222. procedure SetParameters(const a, b, c: TPointD; c1, c2, c3: TColor32);
  223. end;
  224. // /////////////////////////////////////////////////////////////////////////
  225. // DRAWING FUNCTIONS
  226. // /////////////////////////////////////////////////////////////////////////
  227. procedure DrawPoint(img: TImage32; const pt: TPointD;
  228. radius: double; color: TColor32); overload;
  229. procedure DrawPoint(img: TImage32; const pt: TPointD;
  230. radius: double; renderer: TCustomRenderer); overload;
  231. procedure DrawPoint(img: TImage32; const points: TPathD;
  232. radius: double; color: TColor32); overload;
  233. procedure DrawPoint(img: TImage32; const paths: TPathsD;
  234. radius: double; color: TColor32); overload;
  235. procedure DrawInvertedPoint(img: TImage32; const pt: TPointD; radius: double);
  236. procedure DrawLine(img: TImage32;
  237. const pt1, pt2: TPointD; lineWidth: double; color: TColor32); overload;
  238. procedure DrawLine(img: TImage32;
  239. const line: TPathD; lineWidth: double; color: TColor32;
  240. endStyle: TEndStyle; joinStyle: TJoinStyle = jsAuto;
  241. miterLimit: double = 2); overload;
  242. procedure DrawLine(img: TImage32;
  243. const line: TPathD; lineWidth: double; color: TColor32;
  244. rendererCache: TCustomRendererCache;
  245. endStyle: TEndStyle; joinStyle: TJoinStyle = jsAuto;
  246. miterLimit: double = 2); overload;
  247. procedure DrawLine(img: TImage32;
  248. const line: TPathD; lineWidth: double; renderer: TCustomRenderer;
  249. endStyle: TEndStyle; joinStyle: TJoinStyle = jsAuto;
  250. miterLimit: double = 2); overload;
  251. procedure DrawLine(img: TImage32; const lines: TPathsD;
  252. lineWidth: double; color: TColor32;
  253. endStyle: TEndStyle; joinStyle: TJoinStyle = jsAuto;
  254. miterLimit: double = 2); overload;
  255. procedure DrawLine(img: TImage32; const lines: TPathsD;
  256. lineWidth: double; color: TColor32; rendererCache: TCustomRendererCache;
  257. endStyle: TEndStyle; joinStyle: TJoinStyle = jsAuto;
  258. miterLimit: double = 2); overload;
  259. procedure DrawLine(img: TImage32; const lines: TPathsD;
  260. lineWidth: double; renderer: TCustomRenderer;
  261. endStyle: TEndStyle; joinStyle: TJoinStyle = jsAuto;
  262. miterLimit: double = 2); overload;
  263. procedure DrawInvertedLine(img: TImage32;
  264. const line: TPathD; lineWidth: double;
  265. endStyle: TEndStyle; joinStyle: TJoinStyle = jsAuto); overload;
  266. procedure DrawInvertedLine(img: TImage32;
  267. const lines: TPathsD; lineWidth: double;
  268. endStyle: TEndStyle; joinStyle: TJoinStyle = jsAuto); overload;
  269. procedure DrawDashedLine(img: TImage32; const line: TPathD;
  270. dashPattern: TArrayOfDouble; patternOffset: PDouble;
  271. lineWidth: double; color: TColor32;
  272. endStyle: TEndStyle; joinStyle: TJoinStyle = jsAuto;
  273. rendererCache: TCustomRendererCache = nil); overload;
  274. procedure DrawDashedLine(img: TImage32; const lines: TPathsD;
  275. dashPattern: TArrayOfDouble; patternOffset: PDouble;
  276. lineWidth: double; color: TColor32; endStyle: TEndStyle;
  277. joinStyle: TJoinStyle = jsAuto;
  278. rendererCache: TCustomRendererCache = nil); overload;
  279. procedure DrawDashedLine(img: TImage32; const line: TPathD;
  280. dashPattern: TArrayOfDouble; patternOffset: PDouble;
  281. lineWidth: double; renderer: TCustomRenderer; endStyle: TEndStyle;
  282. joinStyle: TJoinStyle = jsAuto); overload;
  283. procedure DrawDashedLine(img: TImage32; const lines: TPathsD;
  284. dashPattern: TArrayOfDouble; patternOffset: PDouble;
  285. lineWidth: double; renderer: TCustomRenderer;
  286. endStyle: TEndStyle; joinStyle: TJoinStyle = jsAuto); overload;
  287. procedure DrawInvertedDashedLine(img: TImage32;
  288. const line: TPathD; dashPattern: TArrayOfDouble;
  289. patternOffset: PDouble; lineWidth: double; endStyle: TEndStyle;
  290. joinStyle: TJoinStyle = jsAuto); overload;
  291. procedure DrawInvertedDashedLine(img: TImage32;
  292. const lines: TPathsD; dashPattern: TArrayOfDouble;
  293. patternOffset: PDouble; lineWidth: double;
  294. endStyle: TEndStyle; joinStyle: TJoinStyle = jsAuto); overload;
  295. procedure DrawPolygon(img: TImage32; const polygon: TPathD;
  296. fillRule: TFillRule; color: TColor32); overload;
  297. procedure DrawPolygon(img: TImage32; const polygon: TPathD;
  298. fillRule: TFillRule; renderer: TCustomRenderer); overload;
  299. procedure DrawPolygon(img: TImage32; const polygons: TPathsD;
  300. fillRule: TFillRule; color: TColor32); overload;
  301. procedure DrawPolygon(img: TImage32; const polygons: TPathsD;
  302. fillRule: TFillRule; color: TColor32;
  303. rendererCache: TCustomRendererCache); overload;
  304. procedure DrawPolygon(img: TImage32; const polygons: TPathsD;
  305. fillRule: TFillRule; renderer: TCustomRenderer); overload;
  306. procedure DrawInvertedPolygon(img: TImage32; const polygon: TPathD;
  307. fillRule: TFillRule); overload;
  308. procedure DrawInvertedPolygon(img: TImage32; const polygons: TPathsD;
  309. fillRule: TFillRule); overload;
  310. // 'Clear Type' text rendering is quite useful for low resolution
  311. // displays (96 ppi). However it's of little to no benefit on higher
  312. // resolution displays and becomes unnecessary overhead. See also:
  313. // https://en.wikipedia.org/wiki/Subpixel_rendering
  314. // https://www.grc.com/ctwhat.htm
  315. // https://www.grc.com/cttech.htm
  316. procedure DrawPolygon_ClearType(img: TImage32; const polygons: TPathsD;
  317. fillRule: TFillRule; color: TColor32; backColor: TColor32 = clWhite32);
  318. // /////////////////////////////////////////////////////////////////////////
  319. // MISCELLANEOUS FUNCTIONS
  320. // /////////////////////////////////////////////////////////////////////////
  321. procedure ErasePolygon(img: TImage32; const polygon: TPathD;
  322. fillRule: TFillRule); overload;
  323. procedure ErasePolygon(img: TImage32; const polygons: TPathsD;
  324. fillRule: TFillRule); overload;
  325. // Both DrawBoolMask and DrawAlphaMask require
  326. // 'mask' length to equal 'img' width * height
  327. procedure DrawBoolMask(img: TImage32;
  328. const mask: TArrayOfByte; color: TColor32 = clBlack32);
  329. procedure DrawAlphaMask(img: TImage32;
  330. const mask: TArrayOfByte; color: TColor32 = clBlack32);
  331. procedure Rasterize(const paths: TPathsD;
  332. const clipRec: TRect; fillRule: TFillRule; renderer: TCustomRenderer); overload;
  333. procedure Rasterize(img: TImage32; const paths: TPathsD;
  334. const clipRec: TRect; fillRule: TFillRule; renderer: TCustomRenderer); overload;
  335. implementation
  336. {$IFDEF CPUX86}
  337. const
  338. // Use faster Trunc for x86 code in this unit.
  339. Trunc: function(Value: Double): Integer = __Trunc;
  340. {$ENDIF CPUX86}
  341. type
  342. // A horizontal scanline contains any number of line fragments. A fragment
  343. // can be a number of pixels wide but it can't be more than one pixel high.
  344. PFragment = ^TFragment;
  345. TFragment = record
  346. botX, topX, dy, dydx: double; // ie x at bottom and top of scanline
  347. end;
  348. TScanLine = record
  349. Y: integer;
  350. minX, maxX: integer;
  351. fragCnt: integer;
  352. fragOffset: integer;
  353. end;
  354. PScanline = ^TScanline;
  355. TArrayOfScanline = array of TScanline;
  356. // ------------------------------------------------------------------------------
  357. // ApplyClearType (see DrawPolygon_ClearType below)
  358. // ------------------------------------------------------------------------------
  359. type
  360. PArgbs = ^TArgbs;
  361. TArgbs = array [0.. (Maxint div SizeOf(TARGB)) -1] of TARGB;
  362. procedure ApplyClearType(img: TImage32; textColor: TColor32 = clBlack32;
  363. bkColor: TColor32 = clWhite32);
  364. const
  365. centerWeighting = 5; //0 <= centerWeighting <= 25
  366. var
  367. h, w: integer;
  368. src, dst: PARGB;
  369. srcArr: PArgbs;
  370. fgColor: TARGB absolute textColor;
  371. bgColor: TARGB absolute bkColor;
  372. diff_R, diff_G, diff_B: integer;
  373. bg8_R, bg8_G, bg8_B: integer;
  374. rowBuffer: TArrayOfARGB;
  375. primeTbl, nearTbl, FarTbl: PByteArray;
  376. begin
  377. // Precondition: the background to text drawn onto 'img' must be transparent
  378. // 85 + (2 * 57) + (2 * 28) == 255
  379. primeTbl := PByteArray(@MulTable[85 + centerWeighting *2]);
  380. nearTbl := PByteArray(@MulTable[57]);
  381. farTbl := PByteArray(@MulTable[28 - centerWeighting]);
  382. SetLength(rowBuffer, img.Width +4);
  383. for h := 0 to img.Height -1 do
  384. begin
  385. // each row of the image is copied into a temporary buffer ...
  386. // noting that while 'dst' (img.Pixels) is initially the source
  387. // it will later be destination (during image compression).
  388. dst := PARGB(@img.Pixels[h * img.Width]);
  389. src := PARGB(@rowBuffer[2]);
  390. Move(dst^, src^, img.Width * SizeOf(TColor32));
  391. srcArr := PArgbs(rowBuffer);
  392. // using this buffer compress the image ...
  393. w := 2;
  394. while w < img.Width do
  395. begin
  396. dst.R := primeTbl[srcArr[w].A] +
  397. nearTbl[srcArr[w-1].A] + farTbl[srcArr[w-2].A] +
  398. nearTbl[srcArr[w+1].A] + farTbl[srcArr[w+2].A];
  399. inc(w);
  400. dst.G := primeTbl[srcArr[w].A] +
  401. nearTbl[srcArr[w-1].A] + farTbl[srcArr[w-2].A] +
  402. nearTbl[srcArr[w+1].A] + farTbl[srcArr[w+2].A];
  403. inc(w);
  404. dst.B := primeTbl[srcArr[w].A] +
  405. nearTbl[srcArr[w-1].A] + farTbl[srcArr[w-2].A] +
  406. nearTbl[srcArr[w+1].A] + farTbl[srcArr[w+2].A];
  407. inc(w);
  408. dst.A := 255;
  409. inc(dst);
  410. end;
  411. end;
  412. // Following compression the right 2/3 of the image is redundant
  413. img.Crop(Types.Rect(0,0, img.Width div 3, img.Height));
  414. // currently text is white and the background is black
  415. // so blend in the text and background colors ...
  416. diff_R := fgColor.R - bgColor.R;
  417. diff_G := fgColor.G - bgColor.G;
  418. diff_B := fgColor.B - bgColor.B;
  419. bg8_R := bgColor.R shl 8;
  420. bg8_G := bgColor.G shl 8;
  421. bg8_B := bgColor.B shl 8;
  422. dst := PARGB(img.PixelBase);
  423. for h := 0 to img.Width * img.Height -1 do
  424. begin
  425. if dst.R = 0 then
  426. dst.Color := bkColor
  427. else
  428. begin
  429. // blend front (text) and background colors ...
  430. dst.R := (bg8_R + diff_R * dst.R) shr 8;
  431. dst.G := (bg8_G + diff_G * dst.G) shr 8;
  432. dst.B := (bg8_B + diff_B * dst.B) shr 8;
  433. end;
  434. inc(dst);
  435. end;
  436. end;
  437. // ------------------------------------------------------------------------------
  438. // Other miscellaneous functions
  439. // ------------------------------------------------------------------------------
  440. function ClampByte(val: double): byte; {$IFDEF INLINE} inline; {$ENDIF}
  441. begin
  442. if val < 0 then result := 0
  443. else if val > 255 then result := 255
  444. else result := Round(val);
  445. end;
  446. // ------------------------------------------------------------------------------
  447. function GetPixel(current: PARGB; delta: integer): PARGB;
  448. {$IFDEF INLINE} inline; {$ENDIF}
  449. begin
  450. Result := current;
  451. inc(Result, delta);
  452. end;
  453. // ------------------------------------------------------------------------------
  454. // Here "const" is used for opimization reasons, to skip the
  455. // dyn-array reference counting. "const" for dyn-arrays doesn't
  456. // prevent one from changing the array's content.
  457. procedure ReverseColors(const colors: TArrayOfGradientColor);
  458. var
  459. highI: integer;
  460. dst, src: ^TGradientColor;
  461. // Not using a TGradientColor record for the temporary value
  462. // allows the 64-bit compiler to use an XMM register for it.
  463. tmpOffset: double;
  464. tmpColor: TColor32;
  465. begin
  466. highI := High(colors);
  467. dst := @colors[0];
  468. src := @colors[highI];
  469. while PByte(dst) < PByte(src) do
  470. begin
  471. tmpColor := dst.color;
  472. tmpOffset := dst.offset;
  473. dst.color := src.color;
  474. dst.offset := 1 - src.offset;
  475. src.color := tmpColor;
  476. src.offset := 1 - tmpOffset;
  477. inc(dst);
  478. dec(src);
  479. end;
  480. end;
  481. // ------------------------------------------------------------------------------
  482. procedure SwapColors(var color1, color2: TColor32);
  483. var
  484. c: TColor32;
  485. begin
  486. c := color1;
  487. color1 := color2;
  488. color2 := c;
  489. end;
  490. // ------------------------------------------------------------------------------
  491. procedure SwapPoints(var point1, point2: TPoint); overload;
  492. var
  493. pt: TPoint;
  494. begin
  495. pt := point1;
  496. point1 := point2;
  497. point2 := pt;
  498. end;
  499. // ------------------------------------------------------------------------------
  500. procedure SwapPoints(var point1, point2: TPointD); overload;
  501. var
  502. pt: TPointD;
  503. begin
  504. pt := point1;
  505. point1 := point2;
  506. point2 := pt;
  507. end;
  508. // ------------------------------------------------------------------------------
  509. function ClampQ(q, endQ: integer): integer;
  510. begin
  511. if q < 0 then result := 0
  512. else if q >= endQ then result := endQ -1
  513. else result := q;
  514. end;
  515. // ------------------------------------------------------------------------------
  516. function ClampD(d: double; colorCnt: integer): integer;
  517. begin
  518. dec(colorCnt);
  519. if d < 0 then result := 0
  520. else if d >= 1 then result := colorCnt
  521. else result := Round(d * colorCnt);
  522. end;
  523. // ------------------------------------------------------------------------------
  524. function MirrorQ(q, endQ: integer): integer;
  525. begin
  526. result := q mod endQ;
  527. if (result < 0) then result := -result;
  528. if Odd(q div endQ) then
  529. result := (endQ -1) - result;
  530. end;
  531. // ------------------------------------------------------------------------------
  532. function MirrorD(d: double; colorCnt: integer): integer;
  533. begin
  534. dec(colorCnt);
  535. if Odd(Trunc(d)) then
  536. result := Trunc((1 - frac(d)) * colorCnt) else
  537. result := Trunc(frac(d) * colorCnt);
  538. end;
  539. // ------------------------------------------------------------------------------
  540. function RepeatQ(q, endQ: integer): integer;
  541. begin
  542. if (q < 0) or (q >= endQ) then
  543. begin
  544. endQ := Abs(endQ);
  545. result := q mod endQ;
  546. if result < 0 then inc(result, endQ);
  547. end
  548. else result := q;
  549. end;
  550. // ------------------------------------------------------------------------------
  551. function SoftRptQ(q, endQ: integer): integer;
  552. begin
  553. if (q < 0) then
  554. result := endQ + (q mod endQ) else
  555. result := (q mod endQ);
  556. if result = 0 then result := endQ div 2;
  557. end;
  558. // ------------------------------------------------------------------------------
  559. function RepeatD(d: double; colorCnt: integer): integer;
  560. begin
  561. dec(colorCnt);
  562. if (d < 0) then
  563. result := Trunc((1 + frac(d)) * colorCnt) else
  564. result := Trunc(frac(d) * colorCnt);
  565. end;
  566. // ------------------------------------------------------------------------------
  567. function BlendColorUsingMask(bgColor, fgColor: TColor32; mask: Byte): TColor32;
  568. var
  569. bg: TARGB absolute bgColor;
  570. fg: TARGB absolute fgColor;
  571. res: TARGB absolute Result;
  572. R, invR: PByteArray;
  573. begin
  574. if fg.A = 0 then
  575. begin
  576. Result := bgColor;
  577. res.A := MulTable[res.A, not mask];
  578. end
  579. else if bg.A = 0 then
  580. begin
  581. Result := fgColor;
  582. res.A := MulTable[res.A, mask];
  583. end
  584. else if (mask = 0) then
  585. Result := bgColor
  586. else if (mask = 255) then
  587. Result := fgColor
  588. else
  589. begin
  590. R := PByteArray(@MulTable[mask]);
  591. InvR := PByteArray(@MulTable[not mask]);
  592. res.A := R[fg.A] + InvR[bg.A];
  593. res.R := R[fg.R] + InvR[bg.R];
  594. res.G := R[fg.G] + InvR[bg.G];
  595. res.B := R[fg.B] + InvR[bg.B];
  596. end;
  597. end;
  598. // ------------------------------------------------------------------------------
  599. // MakeColorGradient: using the supplied array of TGradientColor,
  600. // create an array of TColor32 of the specified length
  601. procedure MakeColorGradient(const gradColors: TArrayOfGradientColor;
  602. len: integer; var result: TArrayOfColor32);
  603. var
  604. i,j, lenC: integer;
  605. dist, offset1, offset2, step, pos, reciprocalDistTimes255: double;
  606. color1, color2: TColor32;
  607. begin
  608. lenC := length(gradColors);
  609. if (len = 0) or (lenC < 2) then Exit;
  610. if Length(result) <> len then // we can reuse the array
  611. SetLength(result, len);
  612. color2 := gradColors[0].color;
  613. result[0] := color2;
  614. if len = 1 then Exit;
  615. reciprocalDistTimes255 := 0;
  616. step := 1/(len-1);
  617. pos := step;
  618. offset2 := 0;
  619. i := 1; j := 1;
  620. repeat
  621. offset1 := offset2;
  622. offset2 := gradColors[i].offset;
  623. dist := offset2 - offset1;
  624. color1 := color2;
  625. color2 := gradColors[i].color;
  626. if dist > 0 then
  627. reciprocalDistTimes255 := 255/dist; // 1/dist*255
  628. while (pos <= dist) and (j < len) do
  629. begin
  630. result[j] := BlendColorUsingMask(color1, color2, Round(pos * reciprocalDistTimes255));
  631. inc(j);
  632. pos := pos + step;
  633. end;
  634. pos := pos - dist;
  635. inc(i);
  636. until i = lenC;
  637. if j < len then result[j] := result[j-1];
  638. end;
  639. // ------------------------------------------------------------------------------
  640. // Rasterize() support functions
  641. // ------------------------------------------------------------------------------
  642. procedure AllocateScanlines(const polygons: TPathsD;
  643. const scanlines: TArrayOfScanline; var fragments: PFragment; clipBottom, clipRight: integer);
  644. var
  645. i,j, highI, highJ: integer;
  646. y1, y2: integer;
  647. fragOff: Cardinal;
  648. psl: PScanline;
  649. begin
  650. // first count how often each edge intersects with each horizontal scanline
  651. for i := 0 to high(polygons) do
  652. begin
  653. highJ := high(polygons[i]);
  654. if highJ < 2 then continue;
  655. y1 := Trunc(polygons[i][highJ].Y);
  656. for j := 0 to highJ do
  657. begin
  658. y2 := Trunc(polygons[i][j].Y);
  659. if y1 < y2 then
  660. begin
  661. // descending (but ignore edges outside the clipping range)
  662. if (y2 >= 0) and (y1 <= clipBottom) then
  663. begin
  664. if (y1 > 0) then
  665. dec(scanlines[y1 -1].fragCnt);
  666. if y2 >= clipBottom then
  667. inc(scanlines[clipBottom].fragCnt) else
  668. inc(scanlines[y2].fragCnt);
  669. end;
  670. end else
  671. begin
  672. // ascending (but ignore edges outside the clipping range)
  673. if (y1 >= 0) and (y2 <= clipBottom) then
  674. begin
  675. if (y2 > 0) then
  676. dec(scanlines[y2 -1].fragCnt);
  677. if y1 >= clipBottom then
  678. inc(scanlines[clipBottom].fragCnt) else
  679. inc(scanlines[y1].fragCnt);
  680. end;
  681. end;
  682. y1 := y2;
  683. end;
  684. end;
  685. // convert 'count' accumulators into real counts and allocate storage
  686. j := 0;
  687. fragOff := 0;
  688. highI := high(scanlines);
  689. psl := @scanlines[highI];
  690. // 'fragments' is a pointer and not a dynamic array because
  691. // dynamic arrays are zero initialized (hence slower than GetMem).
  692. for i := highI downto 0 do
  693. begin
  694. inc(j, psl.fragCnt); // nb: psl.fragCnt may be < 0 here!
  695. if j > 0 then
  696. begin
  697. psl.fragOffset := fragOff;
  698. inc(fragOff, j);
  699. end else
  700. psl.fragOffset := -1;
  701. psl.fragCnt := 0; // reset for later
  702. psl.minX := clipRight;
  703. psl.maxX := 0;
  704. psl.Y := i;
  705. dec(psl);
  706. end;
  707. // allocate fragments as a single block of memory
  708. GetMem(fragments, fragOff * sizeOf(TFragment));
  709. end;
  710. // ------------------------------------------------------------------------------
  711. procedure SplitEdgeIntoFragments(const pt1, pt2: TPointD;
  712. const scanlines: TArrayOfScanline; fragments: PFragment; const clipRec: TRect);
  713. var
  714. x,y, dx,dy, absDx, dydx, dxdy: double;
  715. i, scanlineY, maxY, maxX: integer;
  716. psl: PScanLine;
  717. pFrag: PFragment;
  718. bot, top: TPointD;
  719. begin
  720. dy := pt1.Y - pt2.Y;
  721. if dy > 0 then
  722. begin
  723. // ASCENDING EDGE (+VE WINDING DIR)
  724. if dy < 0.0001 then Exit; //ignore near horizontals
  725. bot := pt1; top := pt2;
  726. end else
  727. begin
  728. // DESCENDING EDGE (-VE WINDING DIR)
  729. if dy > -0.0001 then Exit; //ignore near horizontals
  730. bot := pt2; top := pt1;
  731. end;
  732. // exclude edges that are completely outside the top or bottom clip region
  733. RectWidthHeight(clipRec, maxX, maxY);
  734. if (top.Y >= maxY) or (bot.Y <= 0) then Exit;
  735. dx := pt2.X - pt1.X;
  736. absDx := abs(dx);
  737. if absDx < 0.000001 then
  738. begin
  739. // VERTICAL EDGE
  740. top.X := bot.X; //this circumvents v. rare rounding issues.
  741. // exclude vertical edges that are outside the right clip region
  742. // but still update maxX for each scanline the edge passes
  743. if bot.X > maxX then
  744. begin
  745. for i := Min(maxY, Trunc(bot.Y)) downto Max(0, Trunc(top.Y)) do
  746. scanlines[i].maxX := maxX;
  747. Exit;
  748. end;
  749. dxdy := 0;
  750. if dy > 0 then dydx := 1 else dydx := -1;
  751. end else
  752. begin
  753. dxdy := dx/dy;
  754. dydx := dy/absDx;
  755. end;
  756. // TRIM EDGES THAT CROSS CLIPPING BOUNDARIES (EXCEPT THE LEFT BOUNDARY)
  757. if bot.X >= maxX then
  758. begin
  759. if top.X >= maxX then
  760. begin
  761. for i := Min(maxY, Trunc(bot.Y)) downto Max(0, Trunc(top.Y)) do
  762. scanlines[i].maxX := maxX;
  763. Exit;
  764. end;
  765. // here the edge must be oriented bottom-right to top-left
  766. y := bot.Y - (bot.X - maxX) * Abs(dydx);
  767. for i := Min(maxY, Trunc(bot.Y)) downto Max(0, Trunc(y)) do
  768. scanlines[i].maxX := maxX;
  769. bot.Y := y;
  770. if bot.Y <= 0 then Exit;
  771. bot.X := maxX;
  772. end
  773. else if top.X > maxX then
  774. begin
  775. // here the edge must be oriented bottom-left to top-right
  776. y := top.Y + (top.X - maxX) * Abs(dydx);
  777. for i := Min(maxY, Trunc(y)) downto Max(0, Trunc(top.Y)) do
  778. scanlines[i].maxX := maxX;
  779. top.Y := y;
  780. if top.Y >= maxY then Exit;
  781. top.X := maxX;
  782. end;
  783. if bot.Y > maxY then
  784. begin
  785. bot.X := bot.X + dxdy * (bot.Y - maxY);
  786. if (bot.X > maxX) then Exit; //nb: no clipping on the left
  787. bot.Y := maxY;
  788. end;
  789. if top.Y < 0 then
  790. begin
  791. top.X := top.X + (dxdy * top.Y);
  792. if (top.X > maxX) then Exit; //nb: no clipping on the left
  793. top.Y := 0;
  794. end;
  795. // SPLIT THE EDGE INTO MULTIPLE SCANLINE FRAGMENTS
  796. scanlineY := Trunc(bot.Y);
  797. if bot.Y = scanlineY then dec(scanlineY);
  798. // at the lower-most extent of the edge 'split' the first fragment
  799. if scanlineY < 0 then Exit;
  800. psl := @scanlines[scanlineY];
  801. if psl.fragOffset < 0 then Exit; //a very rare event
  802. pFrag := fragments;
  803. inc(pFrag, psl.fragOffset + psl.fragCnt);
  804. inc(psl.fragCnt);
  805. pFrag.botX := bot.X;
  806. if scanlineY <= top.Y then
  807. begin
  808. // the whole edge is within 1 scanline
  809. pFrag.topX := top.X;
  810. pFrag.dy := bot.Y - top.Y;
  811. pFrag.dydx := dydx;
  812. Exit;
  813. end;
  814. x := bot.X + (bot.Y - scanlineY) * dxdy;
  815. pFrag.topX := x;
  816. pFrag.dy := bot.Y - scanlineY;
  817. pFrag.dydx := dydx;
  818. // 'split' subsequent fragments until the top fragment
  819. dec(psl);
  820. while psl.Y > top.Y do
  821. begin
  822. pFrag := fragments;
  823. inc(pFrag, psl.fragOffset + psl.fragCnt);
  824. inc(psl.fragCnt);
  825. pFrag.botX := x;
  826. x := x + dxdy;
  827. pFrag.topX := x;
  828. pFrag.dy := 1;
  829. pFrag.dydx := dydx;
  830. dec(psl);
  831. end;
  832. // and finally the top fragment
  833. pFrag := fragments;
  834. inc(pFrag, psl.fragOffset + psl.fragCnt);
  835. inc(psl.fragCnt);
  836. pFrag.botX := x;
  837. pFrag.topX := top.X;
  838. pFrag.dy := psl.Y + 1 - top.Y;
  839. pFrag.dydx := dydx;
  840. end;
  841. // ------------------------------------------------------------------------------
  842. procedure InitializeScanlines(const polygons: TPathsD;
  843. const scanlines: TArrayOfScanline; fragments: PFragment; const clipRec: TRect);
  844. var
  845. i,j, highJ: integer;
  846. pt1, pt2: PPointD;
  847. begin
  848. for i := 0 to high(polygons) do
  849. begin
  850. highJ := high(polygons[i]);
  851. if highJ < 2 then continue;
  852. pt1 := @polygons[i][highJ];
  853. pt2 := @polygons[i][0];
  854. for j := 0 to highJ do
  855. begin
  856. SplitEdgeIntoFragments(pt1^, pt2^, scanlines, fragments, clipRec);
  857. pt1 := pt2;
  858. inc(pt2);
  859. end;
  860. end;
  861. end;
  862. // ------------------------------------------------------------------------------
  863. procedure ProcessScanlineFragments(var scanline: TScanLine;
  864. fragments: PFragment; const buffer: TArrayOfDouble);
  865. var
  866. i,j, leftXi,rightXi: integer;
  867. fracX, yy, q{, windDir}: double;
  868. left, right, dy, dydx: double;
  869. frag: PFragment;
  870. pd: PDouble;
  871. begin
  872. frag := fragments;
  873. inc(frag, scanline.fragOffset);
  874. for i := 1 to scanline.fragCnt do
  875. begin
  876. left := frag.botX;
  877. right := frag.topX;
  878. dy := frag.dy;
  879. dydx := frag.dydx;
  880. inc(frag);
  881. // converting botX & topX to left & right simplifies code
  882. if {botX > topX} left > right then
  883. begin
  884. q := left;
  885. left := right;
  886. right := q;
  887. end;
  888. leftXi := Max(0, Trunc(left));
  889. rightXi := Max(0, Trunc(right));
  890. if (leftXi = rightXi) then
  891. begin
  892. // the fragment is only one pixel wide
  893. //if dydx < 0 then windDir := -1.0 else windDir := 1.0;
  894. if dydx < 0 then dy := -dy;
  895. if leftXi < scanline.minX then
  896. scanline.minX := leftXi;
  897. if rightXi > scanline.maxX then
  898. scanline.maxX := rightXi;
  899. pd := @buffer[leftXi];
  900. if (left <= 0) then
  901. begin
  902. pd^ := pd^ + dy {* windDir};
  903. end else
  904. begin
  905. q := (left + right) * 0.5 - leftXi;
  906. pd^ := pd^ + (1-q) * dy {* windDir};
  907. inc(pd);
  908. pd^ := pd^ + q * dy {* windDir};
  909. end;
  910. end else
  911. begin
  912. if leftXi < scanline.minX then
  913. scanline.minX := leftXi;
  914. if rightXi > scanline.maxX then
  915. scanline.maxX := rightXi;
  916. pd := @buffer[leftXi];
  917. // left pixel
  918. fracX := leftXi + 1 - left;
  919. yy := dydx * fracX;
  920. q := fracX * yy * 0.5;
  921. pd^ := pd^ + q;
  922. q := yy - q;
  923. inc(pd);
  924. // middle pixels
  925. for j := leftXi +1 to rightXi -1 do
  926. begin
  927. pd^ := pd^ + q + dydx * 0.5;
  928. q := dydx * 0.5;
  929. inc(pd);
  930. end;
  931. // right pixel
  932. fracX := right - rightXi;
  933. yy := fracX * dydx;
  934. pd^ := pd^ + q + (1 - fracX * 0.5) * yy;
  935. inc(pd);
  936. // overflow
  937. pd^ := pd^ + fracX * 0.5 * yy;
  938. end;
  939. end;
  940. end;
  941. // ------------------------------------------------------------------------------
  942. {$RANGECHECKS OFF} // negative array index is used
  943. { CPU register optimized implementations. Every data type must be exactly the one used. }
  944. procedure FillByteBufferEvenOdd(byteBuffer: PByte;
  945. windingAccum: PDouble; count: nativeint);
  946. var
  947. accum: double;
  948. lastValue: integer;
  949. start: nativeint;
  950. buf: PByteArray;
  951. begin
  952. accum := 0; //winding count accumulator
  953. lastValue := 0;
  954. // Copy byteBuffer to a local variable, so Delphi's 32bit compiler
  955. // can put buf into a CPU register.
  956. buf := PByteArray(byteBuffer);
  957. // Use the negative offset trick to only increment "count"
  958. // until it reaches zero. And by offsetting the arrays, "count"
  959. // also becomes the index for those.
  960. inc(PByte(buf), count);
  961. inc(windingAccum, count);
  962. count := -count;
  963. while count < 0 do
  964. begin
  965. // lastValue can be used if accum doesn't change
  966. if PInt64Array(windingAccum)[count] = 0 then
  967. begin
  968. start := count;
  969. repeat
  970. inc(count);
  971. until (count = 0) or (PInt64Array(windingAccum)[count] <> 0);
  972. FillChar(buf[start], count - start, Byte(lastValue));
  973. if count = 0 then break;
  974. end;
  975. accum := accum + PDoubleArray(windingAccum)[count];
  976. // EvenOdd
  977. lastValue := Trunc(Abs(accum) * 1275) mod 2550; // mul 5
  978. if lastValue > 1275 then
  979. lastValue := (2550 - lastValue) shr 2 else // div 4
  980. lastValue := lastValue shr 2; // div 4
  981. if lastValue > 255 then lastValue := 255;
  982. buf[count] := Byte(lastValue);
  983. PDoubleArray(windingAccum)[count] := 0;
  984. inc(count); // walk towards zero
  985. end;
  986. end;
  987. procedure FillByteBufferNonZero(byteBuffer: PByte;
  988. windingAccum: PDouble; count: nativeint);
  989. var
  990. accum: double;
  991. lastValue: integer;
  992. start: nativeint;
  993. buf: PByteArray;
  994. begin
  995. accum := 0; //winding count accumulator
  996. lastValue := 0;
  997. // Copy byteBuffer to a local variable, so Delphi's 32bit compiler
  998. // can put buf into a CPU register.
  999. buf := PByteArray(byteBuffer);
  1000. // Use the negative offset trick to only increment "count"
  1001. // until it reaches zero. And by offsetting the arrays, "count"
  1002. // also becomes the index for those.
  1003. inc(PByte(buf), count);
  1004. inc(windingAccum, count);
  1005. count := -count;
  1006. while count < 0 do
  1007. begin
  1008. // lastValue can be used if accum doesn't change
  1009. if PInt64Array(windingAccum)[count] = 0 then
  1010. begin
  1011. start := count;
  1012. repeat
  1013. inc(count);
  1014. until (count = 0) or (PInt64Array(windingAccum)[count] <> 0);
  1015. FillChar(buf[start], count - start, Byte(lastValue));
  1016. if count = 0 then break;
  1017. end;
  1018. accum := accum + PDoubleArray(windingAccum)[count];
  1019. // NonZero
  1020. lastValue := Trunc(Abs(accum) * 318);
  1021. if lastValue > 255 then lastValue := 255;
  1022. buf[count] := Byte(lastValue);
  1023. PDoubleArray(windingAccum)[count] := 0;
  1024. inc(count); // walk towards zero
  1025. end;
  1026. end;
  1027. procedure FillByteBufferPositive(byteBuffer: PByte;
  1028. windingAccum: PDouble; count: nativeint);
  1029. var
  1030. accum: double;
  1031. lastValue: integer;
  1032. start: nativeint;
  1033. buf: PByteArray;
  1034. begin
  1035. accum := 0; //winding count accumulator
  1036. lastValue := 0;
  1037. // Copy byteBuffer to a local variable, so Delphi's 32bit compiler
  1038. // can put buf into a CPU register.
  1039. buf := PByteArray(byteBuffer);
  1040. // Use the negative offset trick to only increment "count"
  1041. // until it reaches zero. And by offsetting the arrays, "count"
  1042. // also becomes the index for those.
  1043. inc(PByte(buf), count);
  1044. inc(windingAccum, count);
  1045. count := -count;
  1046. while count < 0 do
  1047. begin
  1048. // lastValue can be used if accum doesn't change
  1049. if PInt64Array(windingAccum)[count] = 0 then
  1050. begin
  1051. start := count;
  1052. repeat
  1053. inc(count);
  1054. until (count = 0) or (PInt64Array(windingAccum)[count] <> 0);
  1055. FillChar(buf[start], count - start, Byte(lastValue));
  1056. if count = 0 then break;
  1057. end;
  1058. accum := accum + PDoubleArray(windingAccum)[count];
  1059. // Positive
  1060. lastValue := 0;
  1061. if accum > 0.002 then
  1062. begin
  1063. lastValue := Trunc(accum * 318);
  1064. if lastValue > 255 then lastValue := 255;
  1065. end;
  1066. buf[count] := Byte(lastValue);
  1067. PDoubleArray(windingAccum)[count] := 0;
  1068. inc(count); // walk towards zero
  1069. end;
  1070. end;
  1071. procedure FillByteBufferNegative(byteBuffer: PByte;
  1072. windingAccum: PDouble; count: nativeint);
  1073. var
  1074. accum: double;
  1075. lastValue: integer;
  1076. start: nativeint;
  1077. buf: PByteArray;
  1078. begin
  1079. accum := 0; //winding count accumulator
  1080. lastValue := 0;
  1081. // Copy byteBuffer to a local variable, so Delphi's 32bit compiler
  1082. // can put buf into a CPU register.
  1083. buf := PByteArray(byteBuffer);
  1084. // Use the negative offset trick to only increment "count"
  1085. // until it reaches zero. And by offsetting the arrays, "count"
  1086. // also becomes the index for those.
  1087. inc(PByte(buf), count);
  1088. inc(windingAccum, count);
  1089. count := -count;
  1090. while count < 0 do
  1091. begin
  1092. // lastValue can be used if accum doesn't change
  1093. if PInt64Array(windingAccum)[count] = 0 then
  1094. begin
  1095. start := count;
  1096. repeat
  1097. inc(count);
  1098. until (count = 0) or (PInt64Array(windingAccum)[count] <> 0);
  1099. FillChar(buf[start], count - start, Byte(lastValue));
  1100. if count = 0 then break;
  1101. end;
  1102. accum := accum + PDoubleArray(windingAccum)[count];
  1103. // Negative
  1104. lastValue := 0;
  1105. if accum < -0.002 then
  1106. begin
  1107. lastValue := Trunc(accum * -318);
  1108. if lastValue > 255 then lastValue := 255;
  1109. end;
  1110. buf[count] := Byte(lastValue);
  1111. PDoubleArray(windingAccum)[count] := 0;
  1112. inc(count); // walk towards zero
  1113. end;
  1114. end;
  1115. {$IFDEF RANGECHECKS_ENABLED}
  1116. {$RANGECHECKS ON}
  1117. {$ENDIF}
  1118. procedure Rasterize(const paths: TPathsD; const clipRec: TRect;
  1119. fillRule: TFillRule; renderer: TCustomRenderer);
  1120. var
  1121. i, xli,xri, maxW, maxH: integer;
  1122. clipRec2: TRect;
  1123. paths2: TPathsD;
  1124. windingAccum: TArrayOfDouble;
  1125. byteBuffer: PByteArray;
  1126. scanlines: TArrayOfScanline;
  1127. fragments: PFragment;
  1128. scanline: PScanline;
  1129. skippedScanlines: integer;
  1130. skipRenderer: boolean;
  1131. // FPC generates wrong code if "count" isn't NativeInt
  1132. FillByteBuffer: procedure(byteBuffer: PByte; windingAccum: PDouble; count: nativeint);
  1133. begin
  1134. // See also https://nothings.org/gamedev/rasterize/
  1135. if not assigned(renderer) then Exit;
  1136. renderer.SetClipRect(clipRec);
  1137. skipRenderer := renderer.SupportsRenderProcSkip;
  1138. Types.IntersectRect(clipRec2, clipRec, GetBounds(paths));
  1139. if IsEmptyRect(clipRec2) then
  1140. begin
  1141. if skipRenderer then renderer.RenderProcSkip(clipRec);
  1142. Exit;
  1143. end;
  1144. if (clipRec2.Left = 0) and (clipRec2.Top = 0) then
  1145. paths2 := paths
  1146. else
  1147. paths2 := TranslatePath(paths, -clipRec2.Left, -clipRec2.Top);
  1148. // Delphi's Round() function is *much* faster than Trunc(),
  1149. // and even a little faster than Trunc() above (except
  1150. // when the FastMM4 memory manager is enabled.)
  1151. fragments := nil;
  1152. byteBuffer := nil;
  1153. try
  1154. RectWidthHeight(clipRec2, maxW, maxH);
  1155. if maxW <= 0 then Exit;
  1156. GetMem(byteBuffer, maxW); // no need for dyn. array zero initialize
  1157. SetLength(scanlines, maxH +1);
  1158. SetLength(windingAccum, maxW +2);
  1159. AllocateScanlines(paths2, scanlines, fragments, maxH, maxW-1);
  1160. InitializeScanlines(paths2, scanlines, fragments, clipRec2);
  1161. case fillRule of
  1162. frEvenOdd:
  1163. FillByteBuffer := FillByteBufferEvenOdd;
  1164. frNonZero:
  1165. FillByteBuffer := FillByteBufferNonZero;
  1166. {$IFDEF REVERSE_ORIENTATION}
  1167. frPositive:
  1168. {$ELSE}
  1169. frNegative:
  1170. {$ENDIF}
  1171. FillByteBuffer := FillByteBufferPositive;
  1172. {$IFDEF REVERSE_ORIENTATION}
  1173. frNegative:
  1174. {$ELSE}
  1175. frPositive:
  1176. {$ENDIF}
  1177. FillByteBuffer := FillByteBufferNegative;
  1178. else
  1179. if skipRenderer then renderer.RenderProcSkip(clipRec);
  1180. Exit;
  1181. end;
  1182. // Notify the renderer about the parts at the top
  1183. // that we didn't touch.
  1184. if skipRenderer and (clipRec2.Top > clipRec.Top) then
  1185. begin
  1186. renderer.RenderProcSkip(Rect(clipRec.Left, clipRec.Top,
  1187. clipRec.Right, clipRec2.Top - 1));
  1188. end;
  1189. skippedScanlines := 0;
  1190. scanline := @scanlines[0];
  1191. for i := 0 to high(scanlines) do
  1192. begin
  1193. if scanline.fragCnt = 0 then
  1194. begin
  1195. inc(scanline);
  1196. if skipRenderer then inc(skippedScanlines);
  1197. Continue;
  1198. end;
  1199. // If we have skipped some scanlines, we must notify the renderer.
  1200. if skipRenderer and (skippedScanlines > 0) then
  1201. begin
  1202. renderer.RenderProcSkip(Rect(clipRec.Left, clipRec2.Top + i - skippedScanlines,
  1203. clipRec.Right, clipRec2.Top + i - 1));
  1204. skippedScanlines := 0;
  1205. end;
  1206. // process each scanline to fill the winding count accumulation buffer
  1207. ProcessScanlineFragments(scanline^, fragments, windingAccum);
  1208. // it's faster to process only the modified sub-array of windingAccum
  1209. xli := scanline.minX;
  1210. xri := Min(maxW -1, scanline.maxX +1);
  1211. // a 25% weighting has been added to the alpha channel to minimize any
  1212. // background bleed-through where polygons join with a common edge.
  1213. // FillByteBuffer overwrites every byte in byteBuffer[xli..xri] and also resets
  1214. // windingAccum[xli..xri] to 0.
  1215. FillByteBuffer(@byteBuffer[xli], @windingAccum[xli], xri - xli +1);
  1216. renderer.RenderProc(clipRec2.Left + xli, clipRec2.Left + xri,
  1217. clipRec2.Top + i, @byteBuffer[xli]);
  1218. inc(scanline);
  1219. end;
  1220. // Notify the renderer about the last skipped scanlines
  1221. if skipRenderer then
  1222. begin
  1223. clipRec2.Bottom := clipRec2.top + High(scanlines) - skippedScanlines;
  1224. if clipRec2.Bottom < clipRec.Bottom then
  1225. begin
  1226. renderer.RenderProcSkip(Rect(clipRec.Left, clipRec2.Bottom + 1,
  1227. clipRec.Right, clipRec.Bottom));
  1228. end;
  1229. end;
  1230. finally
  1231. // cleanup and deallocate memory
  1232. FreeMem(fragments);
  1233. FreeMem(byteBuffer);
  1234. end;
  1235. end;
  1236. // ------------------------------------------------------------------------------
  1237. procedure Rasterize(img: TImage32; const paths: TPathsD;
  1238. const clipRec: TRect; fillRule: TFillRule; renderer: TCustomRenderer);
  1239. begin
  1240. if renderer.Initialize(img) then
  1241. begin
  1242. Rasterize(paths, clipRec, fillRule, renderer);
  1243. renderer.NotifyChange;
  1244. end;
  1245. end;
  1246. // ------------------------------------------------------------------------------
  1247. // TAbstractRenderer
  1248. // ------------------------------------------------------------------------------
  1249. constructor TCustomRenderer.Create;
  1250. begin
  1251. inherited;
  1252. fOpacity := 255;
  1253. end;
  1254. // ------------------------------------------------------------------------------
  1255. function TCustomRenderer.Initialize(imgBase: Pointer;
  1256. imgWidth, imgHeight, pixelSize: integer): Boolean;
  1257. begin
  1258. fImgBase := imgBase;
  1259. fImgWidth := ImgWidth;
  1260. fImgHeight := ImgHeight;
  1261. fPixelSize := pixelSize;
  1262. fCurrLinePtr := fImgBase;
  1263. fCurrY := 0;
  1264. result := true;
  1265. end;
  1266. // ------------------------------------------------------------------------------
  1267. procedure TCustomRenderer.NotifyChange;
  1268. begin
  1269. if assigned(fChangeProc) then fChangeProc;
  1270. end;
  1271. // ------------------------------------------------------------------------------
  1272. type THackedImage32 = class(TImage32); //exposes protected Changed method.
  1273. function TCustomRenderer.Initialize(targetImage: TImage32): Boolean;
  1274. begin
  1275. fChangeProc := THackedImage32(targetImage).Changed;
  1276. with targetImage do
  1277. result := Initialize(PixelBase, Width, Height, SizeOf(TColor32));
  1278. end;
  1279. // ------------------------------------------------------------------------------
  1280. function TCustomRenderer.GetDstPixel(x, y: integer): Pointer;
  1281. begin
  1282. if (y <> fCurrY) then
  1283. begin
  1284. fCurrY := y;
  1285. fCurrLinePtr := fImgBase;
  1286. inc(PByte(fCurrLinePtr), fCurrY * fImgWidth * fPixelSize);
  1287. end;
  1288. Result := fCurrLinePtr;
  1289. inc(PByte(Result), x * fPixelSize);
  1290. end;
  1291. // ------------------------------------------------------------------------------
  1292. procedure TCustomRenderer.SetClipRect(const clipRect: TRect);
  1293. begin
  1294. // default: do nothing
  1295. end;
  1296. // ------------------------------------------------------------------------------
  1297. procedure TCustomRenderer.RenderProcSkip(const skippedRect: TRect);
  1298. begin
  1299. // default: do nothing
  1300. end;
  1301. // ------------------------------------------------------------------------------
  1302. function TCustomRenderer.SupportsRenderProcSkip: Boolean;
  1303. begin
  1304. Result := False;
  1305. end;
  1306. // ------------------------------------------------------------------------------
  1307. // TCustomColorRenderer
  1308. // ------------------------------------------------------------------------------
  1309. procedure TCustomColorRenderer.SetColor(value: TColor32);
  1310. begin
  1311. fColor := value;
  1312. end;
  1313. // ------------------------------------------------------------------------------
  1314. // TColorRenderer
  1315. // ------------------------------------------------------------------------------
  1316. constructor TColorRenderer.Create(color: TColor32 = clNone32);
  1317. begin
  1318. inherited Create;
  1319. if color <> clNone32 then SetColor(color);
  1320. end;
  1321. // ------------------------------------------------------------------------------
  1322. function TColorRenderer.Initialize(targetImage: TImage32): Boolean;
  1323. begin
  1324. // there's no point rendering if the color is fully transparent
  1325. result := (fAlpha > 0) and inherited Initialize(targetImage);
  1326. end;
  1327. // ------------------------------------------------------------------------------
  1328. procedure TColorRenderer.SetColor(value: TColor32);
  1329. begin
  1330. fColor := value and $FFFFFF;
  1331. fAlpha := GetAlpha(value);
  1332. end;
  1333. // ------------------------------------------------------------------------------
  1334. {$RANGECHECKS OFF} // negative array index usage (Delphi 7-2007 have no pointer math)
  1335. type
  1336. // Used to reduce the number of parameters to help the compiler's
  1337. // optimizer.
  1338. TRenderProcData = record
  1339. dst: PColor32Array;
  1340. alpha: PByteArray;
  1341. end;
  1342. function RenderProcBlendToAlpha255(count: nativeint; dstColor: TColor32;
  1343. var data: TRenderProcData): nativeint;
  1344. // CPU register optimized
  1345. var
  1346. a: byte;
  1347. dst: PColor32Array;
  1348. alpha: PByteArray;
  1349. begin
  1350. Result := count;
  1351. dst := data.dst;
  1352. alpha := data.alpha;
  1353. a := alpha[Result];
  1354. dst[Result] := dstColor;
  1355. inc(Result);
  1356. while (Result < 0) and (alpha[Result] = a) do
  1357. begin
  1358. dst[Result] := dstColor;
  1359. inc(Result);
  1360. end;
  1361. end;
  1362. procedure RenderProcBlendToAlpha(dst: PColor32Array; alpha: PByteArray;
  1363. count: nativeint; color: TColor32; alphaTable: PByteArray);
  1364. var
  1365. a: byte;
  1366. lastDst, dstColor: TColor32;
  1367. data: TRenderProcData;
  1368. begin
  1369. // Use negative offset trick.
  1370. alpha := @alpha[count];
  1371. dst := @dst[count];
  1372. count := -count;
  1373. // store pointers for RenderProcBlendToAlpha255
  1374. data.dst := dst;
  1375. data.alpha := alpha;
  1376. while count < 0 do
  1377. begin
  1378. a := alpha[count];
  1379. if a > 1 then
  1380. begin
  1381. a := alphaTable[a];
  1382. dstColor := (a shl 24) or color;
  1383. // Special handling for alpha channel 255 (copy dstColor into dst)
  1384. if a = 255 then
  1385. count := RenderProcBlendToAlpha255(count, dstColor, data)
  1386. else
  1387. begin
  1388. lastDst := dst[count];
  1389. dstColor := BlendToAlpha(lastDst, dstColor);
  1390. a := alpha[count];
  1391. dst[count] := dstColor;
  1392. inc(count);
  1393. // if we have the same dst-pixel and the same alpha channel, we can
  1394. // just copy the already calculated BlendToAlpha color.
  1395. while (count < 0) and (a = alpha[count]) and (dst[count] = lastDst) do
  1396. begin
  1397. dst[count] := dstColor;
  1398. inc(count);
  1399. end;
  1400. end;
  1401. end
  1402. else
  1403. inc(count);
  1404. end;
  1405. end;
  1406. {$IFDEF RANGECHECKS_ENABLED}
  1407. {$RANGECHECKS ON}
  1408. {$ENDIF}
  1409. procedure TColorRenderer.RenderProc(x1, x2, y: integer; alpha: PByte);
  1410. begin
  1411. // Help the compiler to get better CPU register allocation.
  1412. // Without the hidden Self parameter the compiler optimizes
  1413. // better.
  1414. RenderProcBlendToAlpha(PColor32Array(GetDstPixel(x1, y)),
  1415. PByteArray(alpha), x2 - x1 + 1, fColor,
  1416. PByteArray(@MulTable[fAlpha]));
  1417. end;
  1418. // ------------------------------------------------------------------------------
  1419. // TAliasedColorRenderer
  1420. // ------------------------------------------------------------------------------
  1421. constructor TAliasedColorRenderer.Create(color: TColor32 = clNone32);
  1422. begin
  1423. inherited Create;
  1424. fColor := color;
  1425. end;
  1426. // ------------------------------------------------------------------------------
  1427. function TAliasedColorRenderer.Initialize(targetImage: TImage32): Boolean;
  1428. begin
  1429. // there's no point rendering if the color is fully transparent
  1430. result := (GetAlpha(fColor) > 0) and
  1431. inherited Initialize(targetImage);
  1432. end;
  1433. // ------------------------------------------------------------------------------
  1434. procedure TAliasedColorRenderer.RenderProc(x1, x2, y: integer; alpha: PByte);
  1435. var
  1436. i: integer;
  1437. dst: PColor32;
  1438. c: TColor32;
  1439. begin
  1440. dst := GetDstPixel(x1,y);
  1441. c := fColor; // copy fColor to local variable
  1442. for i := x1 to x2 do
  1443. begin
  1444. if Ord(alpha^) > 127 then dst^ := c; //ie no blending
  1445. inc(dst); inc(alpha);
  1446. end;
  1447. end;
  1448. // ------------------------------------------------------------------------------
  1449. // TMaskRenderer
  1450. // ------------------------------------------------------------------------------
  1451. procedure TMaskRenderer.SetClipRect(const clipRect: TRect);
  1452. begin
  1453. fClipRect := clipRect;
  1454. // clipping to the image size
  1455. if fClipRect.Left < 0 then fClipRect.Left := 0;
  1456. if fClipRect.Top < 0 then fClipRect.Top := 0;
  1457. if fClipRect.Right > fImgWidth then fClipRect.Right := fImgWidth;
  1458. if fClipRect.Bottom > fImgHeight then fClipRect.Bottom := fImgHeight;
  1459. end;
  1460. // ------------------------------------------------------------------------------
  1461. procedure TMaskRenderer.RenderProc(x1, x2, y: integer; alpha: PByte);
  1462. var
  1463. p: PColor32;
  1464. i: integer;
  1465. begin
  1466. // CopyBlend excludes ClipRect.Right/Bottom, so we also
  1467. // need to exclude it.
  1468. if (y < fClipRect.Top) or (y >= fClipRect.Bottom) then Exit;
  1469. if x2 >= fClipRect.Right then x2 := fClipRect.Right - 1;
  1470. if x1 < fClipRect.Left then
  1471. begin
  1472. inc(alpha, fClipRect.Left - x1);
  1473. x1 := fClipRect.Left;
  1474. end;
  1475. p := GetDstPixel(fClipRect.Left, y);
  1476. // Clear the area before x1 (inside OutsideBounds)
  1477. FillChar(p^, (x1 - fClipRect.Left) * SizeOf(TColor32), 0);
  1478. inc(p, x1 - fClipRect.Left);
  1479. // Fill the area between x1 and x2
  1480. for i := x1 to x2 do
  1481. begin
  1482. if p^ <> 0 then
  1483. begin
  1484. if Ord(alpha^) = 0 then
  1485. p^ := 0
  1486. else if Ord(alpha^) <> 255 then
  1487. p^ := BlendMask(p^, Ord(alpha^) shl 24);
  1488. end;
  1489. inc(p);
  1490. inc(alpha);
  1491. end;
  1492. // Clear the area after x2 (inside OutsideBounds)
  1493. FillChar(p^, (fClipRect.Right - (x2 + 1)) * SizeOf(TColor32), 0);
  1494. end;
  1495. // ------------------------------------------------------------------------------
  1496. procedure TMaskRenderer.RenderProcSkip(const skippedRect: TRect);
  1497. var
  1498. i, h, w: integer;
  1499. p: PColor32;
  1500. r: TRect;
  1501. begin
  1502. r := skippedRect;
  1503. if r.Left < fClipRect.Left then r.Left := fClipRect.Left;
  1504. if r.Top < fClipRect.Top then r.Top := fClipRect.Top;
  1505. // CopyBlend excludes ClipRect.Right/Bottom, so we also
  1506. // need to exclude it.
  1507. if r.Right >= fClipRect.Right then r.Right := fClipRect.Right - 1;
  1508. if r.Bottom >= fClipRect.Bottom then r.Bottom := fClipRect.Bottom - 1;
  1509. if r.Right < r.Left then Exit;
  1510. if r.Bottom < r.Top then Exit;
  1511. w := r.Right - r.Left + 1;
  1512. h := r.Bottom - r.Top + 1;
  1513. p := GetDstPixel(r.Left, r.Top);
  1514. if w = fImgWidth then
  1515. FillChar(p^, w * h * SizeOf(TColor32), 0)
  1516. else
  1517. begin
  1518. for i := 1 to h do
  1519. begin
  1520. FillChar(p^, w * SizeOf(TColor32), 0);
  1521. inc(p, fImgWidth);
  1522. end;
  1523. end;
  1524. end;
  1525. // ------------------------------------------------------------------------------
  1526. function TMaskRenderer.SupportsRenderProcSkip: Boolean;
  1527. begin
  1528. Result := True;
  1529. end;
  1530. // ------------------------------------------------------------------------------
  1531. // TCustomRendererCache
  1532. // ------------------------------------------------------------------------------
  1533. constructor TCustomRendererCache.Create;
  1534. begin
  1535. inherited Create;
  1536. fColorRenderer := TColorRenderer.Create;
  1537. fAliasedColorRenderer := TAliasedColorRenderer.Create;
  1538. fMaskRenderer := TMaskRenderer.Create;
  1539. end;
  1540. // ------------------------------------------------------------------------------
  1541. destructor TCustomRendererCache.Destroy;
  1542. begin
  1543. fColorRenderer.Free;
  1544. fAliasedColorRenderer.Free;
  1545. fMaskRenderer.Free;
  1546. end;
  1547. // ------------------------------------------------------------------------------
  1548. function TCustomRendererCache.GetColorRenderer(color: TColor32): TColorRenderer;
  1549. begin
  1550. Result := fColorRenderer;
  1551. Result.SetColor(color);
  1552. end;
  1553. // ------------------------------------------------------------------------------
  1554. // TBrushImageRenderer
  1555. // ------------------------------------------------------------------------------
  1556. constructor TImageRenderer.Create(tileFillStyle: TTileFillStyle;
  1557. brushImage: TImage32);
  1558. begin
  1559. inherited Create;
  1560. fImage := TImage32.Create(brushImage);
  1561. SetTileFillStyle(tileFillStyle);
  1562. end;
  1563. // ------------------------------------------------------------------------------
  1564. destructor TImageRenderer.Destroy;
  1565. begin
  1566. fImage.Free;
  1567. inherited;
  1568. end;
  1569. // ------------------------------------------------------------------------------
  1570. procedure TImageRenderer.SetTileFillStyle(value: TTileFillStyle);
  1571. begin
  1572. case value of
  1573. tfsRepeat: fBoundsProc := RepeatQ;
  1574. tfsMirrorHorz: fBoundsProc := MirrorQ;
  1575. tfsMirrorVert: fBoundsProc := RepeatQ;
  1576. tfsRotate180 : fBoundsProc := MirrorQ;
  1577. end;
  1578. fMirrorY := value in [tfsMirrorVert, tfsRotate180];
  1579. end;
  1580. // ------------------------------------------------------------------------------
  1581. function TImageRenderer.Initialize(targetImage: TImage32): Boolean;
  1582. begin
  1583. result := inherited Initialize(targetImage) and (not fImage.IsEmpty);
  1584. if not result then Exit;
  1585. fLastYY := 0;
  1586. fBrushPixel := PARGB(fImage.PixelBase);
  1587. end;
  1588. // ------------------------------------------------------------------------------
  1589. procedure TImageRenderer.RenderProc(x1, x2, y: integer; alpha: PByte);
  1590. var
  1591. i: integer;
  1592. pDst: PColor32;
  1593. pImg: PColor32;
  1594. opacityTable: PByteArray;
  1595. begin
  1596. pDst := GetDstPixel(x1,y);
  1597. dec(x1, fOffset.X);
  1598. dec(x2, fOffset.X);
  1599. dec(y, fOffset.Y);
  1600. pImg := GetFirstBrushPixel(x1, y);
  1601. if Opacity < 255 then
  1602. begin
  1603. opacityTable := PByteArray(@MulTable[Opacity]);
  1604. for i := x1 to x2 do
  1605. begin
  1606. pDst^ := BlendToAlpha3(pDst^, pImg^, opacityTable[Ord(alpha^)]);
  1607. inc(pDst); inc(alpha);
  1608. pImg := PColor32(GetPixel(fBrushPixel, fBoundsProc(i, fImage.Width)));
  1609. end;
  1610. end else
  1611. for i := x1 to x2 do
  1612. begin
  1613. pDst^ := BlendToAlpha3(pDst^, pImg^, Ord(alpha^));
  1614. inc(pDst); inc(alpha);
  1615. pImg := PColor32(GetPixel(fBrushPixel, fBoundsProc(i, fImage.Width)));
  1616. end;
  1617. end;
  1618. // ------------------------------------------------------------------------------
  1619. function TImageRenderer.GetFirstBrushPixel(x, y: integer): PColor32;
  1620. begin
  1621. if fMirrorY then
  1622. y := MirrorQ(y, fImage.Height) else
  1623. y := RepeatQ(y, fImage.Height);
  1624. if y <> fLastYY then
  1625. begin
  1626. fBrushPixel := PARGB(fImage.PixelRow[y]);
  1627. fLastYY := y;
  1628. end;
  1629. x := fBoundsProc(x, fImage.Width);
  1630. result := PColor32(GetPixel(fBrushPixel, x));
  1631. end;
  1632. // ------------------------------------------------------------------------------
  1633. // TGradientRenderer
  1634. // ------------------------------------------------------------------------------
  1635. constructor TCustomGradientRenderer.Create;
  1636. begin
  1637. inherited Create;
  1638. fBoundsProc := ClampQ; //default proc
  1639. end;
  1640. // ------------------------------------------------------------------------------
  1641. procedure TCustomGradientRenderer.Clear;
  1642. begin
  1643. fGradientColors := nil;
  1644. fColors := nil;
  1645. end;
  1646. // ------------------------------------------------------------------------------
  1647. procedure TCustomGradientRenderer.SetGradientFillStyle(value: TGradientFillStyle);
  1648. begin
  1649. case value of
  1650. gfsClamp: fBoundsProc := ClampQ;
  1651. gfsMirror: fBoundsProc := MirrorQ;
  1652. else fBoundsProc := RepeatQ;
  1653. end;
  1654. end;
  1655. // ------------------------------------------------------------------------------
  1656. procedure TCustomGradientRenderer.SetParameters(startColor, endColor: TColor32;
  1657. gradFillStyle: TGradientFillStyle = gfsClamp);
  1658. begin
  1659. SetGradientFillStyle(gradFillStyle);
  1660. // reset gradient colors if perviously set
  1661. SetLength(fGradientColors, 2);
  1662. fGradientColors[0].offset := 0;
  1663. fGradientColors[0].color := startColor;
  1664. fGradientColors[1].offset := 1;
  1665. fGradientColors[1].color := endColor;
  1666. end;
  1667. // ------------------------------------------------------------------------------
  1668. procedure TCustomGradientRenderer.InsertColorStop(offsetFrac: double; color: TColor32);
  1669. var
  1670. i, len: integer;
  1671. gradColor: TGradientColor;
  1672. begin
  1673. len := Length(fGradientColors);
  1674. // colorstops can only be inserted after calling SetParameters
  1675. if len = 0 then Exit;
  1676. if offsetFrac < 0 then offsetFrac := 0
  1677. else if offsetFrac > 1 then offsetFrac := 1;
  1678. if offsetFrac = 0 then
  1679. begin
  1680. fGradientColors[0].color := color;
  1681. Exit;
  1682. end
  1683. else if offsetFrac = 1 then
  1684. begin
  1685. fGradientColors[len -1].color := color;
  1686. Exit;
  1687. end;
  1688. gradColor.offset := offsetFrac;
  1689. gradColor.color := color;
  1690. i := 1;
  1691. while (i < len-1) and
  1692. (fGradientColors[i].offset <= offsetFrac) do inc(i);
  1693. SetLength(fGradientColors, len +1);
  1694. Move(fGradientColors[i],
  1695. fGradientColors[i+1], (len -i) * SizeOf(TGradientColor));
  1696. fGradientColors[i] := gradColor;
  1697. end;
  1698. // ------------------------------------------------------------------------------
  1699. // TLinearGradientRenderer
  1700. // ------------------------------------------------------------------------------
  1701. procedure TLinearGradientRenderer.SetParameters(const startPt, endPt: TPointD;
  1702. startColor, endColor: TColor32; gradFillStyle: TGradientFillStyle);
  1703. begin
  1704. inherited SetParameters(startColor, endColor, gradFillStyle);
  1705. fStartPt := startPt;
  1706. fEndPt := endPt;
  1707. end;
  1708. // ------------------------------------------------------------------------------
  1709. function TLinearGradientRenderer.Initialize(targetImage: TImage32): Boolean;
  1710. var
  1711. i: integer;
  1712. dx,dy, dxdy,dydx: double;
  1713. begin
  1714. result := inherited Initialize(targetImage) and assigned(fGradientColors);
  1715. if not result then Exit;
  1716. if abs(fEndPt.Y - fStartPt.Y) > abs(fEndPt.X - fStartPt.X) then
  1717. begin
  1718. // gradient > 45 degrees
  1719. if (fEndPt.Y < fStartPt.Y) then
  1720. begin
  1721. ReverseColors(fGradientColors);
  1722. SwapPoints(fStartPt, fEndPt);
  1723. end;
  1724. fIsVert := true;
  1725. dx := (fEndPt.X - fStartPt.X);
  1726. dy := (fEndPt.Y - fStartPt.Y);
  1727. dxdy := dx/dy;
  1728. fColorsCnt := Ceil(dy + dxdy * (fEndPt.X - fStartPt.X));
  1729. MakeColorGradient(fGradientColors, fColorsCnt, fColors);
  1730. // get a list of perpendicular offsets for each
  1731. NewIntegerArray(fPerpendicOffsets, ImgWidth, True);
  1732. // from an imaginary line that's through fStartPt and perpendicular to
  1733. // the gradient line, get a list of Y offsets for each X in image width
  1734. for i := 0 to ImgWidth -1 do
  1735. fPerpendicOffsets[i] := Round(dxdy * (fStartPt.X - i) + fStartPt.Y);
  1736. end
  1737. else //gradient <= 45 degrees
  1738. begin
  1739. if (fEndPt.X = fStartPt.X) then
  1740. begin
  1741. Result := false;
  1742. Exit;
  1743. end;
  1744. if (fEndPt.X < fStartPt.X) then
  1745. begin
  1746. ReverseColors(fGradientColors);
  1747. SwapPoints(fStartPt, fEndPt);
  1748. end;
  1749. fIsVert := false;
  1750. dx := (fEndPt.X - fStartPt.X);
  1751. dy := (fEndPt.Y - fStartPt.Y);
  1752. dydx := dy/dx; //perpendicular slope
  1753. fColorsCnt := Ceil(dx + dydx * (fEndPt.Y - fStartPt.Y));
  1754. MakeColorGradient(fGradientColors, fColorsCnt, fColors);
  1755. NewIntegerArray(fPerpendicOffsets, ImgHeight, True);
  1756. // from an imaginary line that's through fStartPt and perpendicular to
  1757. // the gradient line, get a list of X offsets for each Y in image height
  1758. for i := 0 to ImgHeight -1 do
  1759. fPerpendicOffsets[i] := Round(dydx * (fStartPt.Y - i) + fStartPt.X);
  1760. end;
  1761. end;
  1762. // ------------------------------------------------------------------------------
  1763. procedure TLinearGradientRenderer.RenderProc(x1, x2, y: integer; alpha: PByte);
  1764. var
  1765. i, colorsCnt: integer;
  1766. pDst: PColor32;
  1767. color: TColor32;
  1768. boundsProc: TBoundsProc;
  1769. offset: Integer;
  1770. colors: PColor32Array;
  1771. perpendicOffsets: PIntegerArray;
  1772. opacityTable: PByteArray;
  1773. begin
  1774. pDst := GetDstPixel(x1,y);
  1775. // optimize self fields access
  1776. colorsCnt := fColorsCnt;
  1777. colors := @fColors[0];
  1778. boundsProc := fBoundsProc;
  1779. if fIsVert then
  1780. begin
  1781. perpendicOffsets := @fPerpendicOffsets[0]; // optimize self field access
  1782. if Opacity < 255 then
  1783. begin
  1784. opacityTable := PByteArray(@MulTable[Opacity]);
  1785. for i := x1 to x2 do
  1786. begin
  1787. // when fIsVert = true, fPerpendicOffsets is an array of Y for each X
  1788. color := colors[boundsProc(y - perpendicOffsets[i], colorsCnt)];
  1789. pDst^ := BlendToAlpha3(pDst^, color, opacityTable[Ord(alpha^)]);
  1790. inc(pDst); inc(alpha);
  1791. end;
  1792. end else
  1793. begin
  1794. for i := x1 to x2 do
  1795. begin
  1796. // when fIsVert = true, fPerpendicOffsets is an array of Y for each X
  1797. color := colors[boundsProc(y - perpendicOffsets[i], colorsCnt)];
  1798. pDst^ := BlendToAlpha3(pDst^, color, Ord(alpha^));
  1799. inc(pDst); inc(alpha);
  1800. end;
  1801. end;
  1802. end
  1803. else
  1804. begin
  1805. // when fIsVert = false, fPerpendicOffsets is an array of X for each Y
  1806. offset := fPerpendicOffsets[y];
  1807. if Opacity < 255 then
  1808. begin
  1809. opacityTable := PByteArray(@MulTable[Opacity]);
  1810. for i := x1 to x2 do
  1811. begin
  1812. color := colors[boundsProc(i - offset, colorsCnt)];
  1813. pDst^ := BlendToAlpha3(pDst^, color, opacityTable[Ord(alpha^)]);
  1814. inc(pDst); inc(alpha);
  1815. end;
  1816. end else
  1817. begin
  1818. for i := x1 to x2 do
  1819. begin
  1820. color := colors[boundsProc(i - offset, colorsCnt)];
  1821. pDst^ := BlendToAlpha3(pDst^, color, Ord(alpha^));
  1822. inc(pDst); inc(alpha);
  1823. end;
  1824. end;
  1825. end;
  1826. end;
  1827. // ------------------------------------------------------------------------------
  1828. // TRadialGradientRenderer
  1829. // ------------------------------------------------------------------------------
  1830. function TRadialGradientRenderer.Initialize(targetImage: TImage32): Boolean;
  1831. begin
  1832. result := inherited Initialize(targetImage) and (fColorsCnt > 1);
  1833. if result then
  1834. MakeColorGradient(fGradientColors, fColorsCnt, fColors);
  1835. end;
  1836. // ------------------------------------------------------------------------------
  1837. procedure TRadialGradientRenderer.SetParameters(const focalRect: TRect;
  1838. innerColor, outerColor: TColor32;
  1839. gradientFillStyle: TGradientFillStyle);
  1840. var
  1841. w,h: integer;
  1842. radX,radY: double;
  1843. begin
  1844. inherited SetParameters(innerColor, outerColor, gradientFillStyle);
  1845. fColorsCnt := 0;
  1846. if IsEmptyRect(focalRect) then Exit;
  1847. fCenterPt.X := (focalRect.Left + focalRect.Right) * 0.5;
  1848. fCenterPt.Y := (focalRect.Top + focalRect.Bottom) * 0.5;
  1849. RectWidthHeight(focalRect, w, h);
  1850. radX := w * 0.5;
  1851. radY := h * 0.5;
  1852. if radX >= radY then
  1853. begin
  1854. fScaleX := 1;
  1855. fScaleY := radX/radY;
  1856. fColorsCnt := Ceil(radX) +1;
  1857. end else
  1858. begin
  1859. fScaleX := radY/radX;
  1860. fScaleY := 1;
  1861. fColorsCnt := Ceil(radY) +1;
  1862. end;
  1863. end;
  1864. // ------------------------------------------------------------------------------
  1865. procedure TRadialGradientRenderer.RenderProc(x1, x2, y: integer; alpha: PByte);
  1866. var
  1867. i: integer;
  1868. dist: double;
  1869. color: TColor32;
  1870. pDst: PColor32;
  1871. opacityTable: PByteArray;
  1872. begin
  1873. pDst := GetDstPixel(x1,y);
  1874. if Opacity < 255 then
  1875. begin
  1876. opacityTable := PByteArray(@MulTable[Opacity]);
  1877. for i := x1 to x2 do
  1878. begin
  1879. dist := Hypot((y - fCenterPt.Y) *fScaleY, (i - fCenterPt.X) *fScaleX);
  1880. color := fColors[fBoundsProc(Trunc(dist), fColorsCnt)];
  1881. pDst^ := BlendToAlpha3(pDst^, color, opacityTable[Ord(alpha^)]);
  1882. inc(pDst); inc(alpha);
  1883. end;
  1884. end else
  1885. begin
  1886. for i := x1 to x2 do
  1887. begin
  1888. dist := Hypot((y - fCenterPt.Y) *fScaleY, (i - fCenterPt.X) *fScaleX);
  1889. color := fColors[fBoundsProc(Trunc(dist), fColorsCnt)];
  1890. pDst^ := BlendToAlpha3(pDst^, color, Ord(alpha^));
  1891. inc(pDst); inc(alpha);
  1892. end;
  1893. end;
  1894. end;
  1895. // ------------------------------------------------------------------------------
  1896. // TSvgRadialGradientRenderer
  1897. // ------------------------------------------------------------------------------
  1898. function TSvgRadialGradientRenderer.Initialize(targetImage: TImage32): Boolean;
  1899. begin
  1900. result := inherited Initialize(targetImage) and (fColorsCnt > 1);
  1901. if result then
  1902. MakeColorGradient(fGradientColors, fColorsCnt, fColors);
  1903. end;
  1904. // ------------------------------------------------------------------------------
  1905. procedure TSvgRadialGradientRenderer.SetParameters(const ellipseRect: TRect;
  1906. const focus: TPoint; innerColor, outerColor: TColor32;
  1907. gradientFillStyle: TGradientFillStyle = gfsClamp);
  1908. var
  1909. w, h : integer;
  1910. begin
  1911. inherited SetParameters(innerColor, outerColor);
  1912. case gradientFillStyle of
  1913. gfsMirror: fBoundsProcD := MirrorD;
  1914. gfsRepeat: fBoundsProcD := RepeatD;
  1915. else fBoundsProcD := ClampD;
  1916. end;
  1917. fColorsCnt := 0;
  1918. if IsEmptyRect(ellipseRect) then Exit;
  1919. fCenterPt := RectD(ellipseRect).MidPoint;
  1920. RectWidthHeight(ellipseRect, w, h);
  1921. fA := w * 0.5;
  1922. fB := h * 0.5;
  1923. fFocusPt.X := focus.X - fCenterPt.X;
  1924. fFocusPt.Y := focus.Y - fCenterPt.Y;
  1925. fColorsCnt := Ceil(Hypot(fA*2, fB*2)) +1;
  1926. fAA := fA * fA;
  1927. fBB := fB * fB;
  1928. end;
  1929. // ------------------------------------------------------------------------------
  1930. procedure TSvgRadialGradientRenderer.RenderProc(x1, x2, y: integer; alpha: PByte);
  1931. var
  1932. i: integer;
  1933. q,qq, m,c, qa,qb,qc,qs: double;
  1934. dist, dist2: double;
  1935. color: TColor32;
  1936. pDst: PColor32;
  1937. pt, ellipsePt: TPointD;
  1938. opacityTable: PByteArray;
  1939. begin
  1940. opacityTable := PByteArray(@MulTable[Opacity]);
  1941. // get the left-most pixel to render
  1942. pDst := GetDstPixel(x1,y);
  1943. pt.X := x1 - fCenterPt.X; pt.Y := y - fCenterPt.Y;
  1944. for i := x1 to x2 do
  1945. begin
  1946. // equation of ellipse = (x*x)/aa + (y*y)/bb = 1
  1947. // equation of line = y = mx + c;
  1948. if (pt.X = fFocusPt.X) then //vertical line
  1949. begin
  1950. // let x = pt.X, then y*y = b*b(1 - Sqr(pt.X)/aa)
  1951. qq := (1 - Sqr(pt.X)/fAA);
  1952. if (qq > 1) then qq := 1
  1953. else if (qq < 0) then qq := 0;
  1954. q := Sqrt(fBB*qq);
  1955. ellipsePt.X := pt.X;
  1956. if pt.Y >= fFocusPt.Y then
  1957. ellipsePt.Y := q else
  1958. ellipsePt.Y := -q;
  1959. dist := abs(pt.Y - fFocusPt.Y);
  1960. dist2 := abs(ellipsePt.Y - fFocusPt.Y);
  1961. if dist2 = 0 then
  1962. q := 1 else
  1963. q := dist/ dist2;
  1964. end else
  1965. begin
  1966. // using simultaneous equations and substitution
  1967. // given y = mx + c
  1968. m := (pt.Y - fFocusPt.Y)/(pt.X - fFocusPt.X);
  1969. c := pt.Y - m * pt.X;
  1970. // given (x*x)/aa + (y*y)/bb = 1
  1971. // (x*x)/aa*bb + (y*y) = bb
  1972. // bb/aa *(x*x) + Sqr(m*x +c) = bb
  1973. // bb/aa *(x*x) + (m*m)*(x*x) + 2*m*x*c +c*c = b*b
  1974. // (bb/aa +(m*m)) *(x*x) + 2*m*c*(x) + (c*c) - bb = 0
  1975. // solving quadratic equation
  1976. qa := (fBB/fAA +(m*m));
  1977. qb := 2*m*c;
  1978. qc := (c*c) - fBB;
  1979. qs := (qb*qb) - 4*qa*qc;
  1980. if qs >= 0 then
  1981. begin
  1982. qs := Sqrt(qs);
  1983. if pt.X <= fFocusPt.X then
  1984. ellipsePt.X := (-qb -qs)/(2 * qa) else
  1985. ellipsePt.X := (-qb +qs)/(2 * qa);
  1986. ellipsePt.Y := m * ellipsePt.X + c;
  1987. // Use sqr'ed distances (Sqrt(a^2+b^2)/Sqrt(x^2+y^2) => Sqrt((a^2+b^2)/(x^2+y^2))
  1988. dist := Sqr(pt.X - fFocusPt.X) + Sqr(pt.Y - fFocusPt.Y);
  1989. dist2 := Sqr(ellipsePt.X - fFocusPt.X) + Sqr(ellipsePt.Y - fFocusPt.Y);
  1990. if dist2 = 0 then
  1991. q := 1 else
  1992. q := Sqrt(dist/dist2);
  1993. end else
  1994. q := 1; //shouldn't happen :)
  1995. end;
  1996. color := fColors[fBoundsProcD(Abs(q), fColorsCnt)];
  1997. pDst^ := BlendToAlpha3(pDst^, color, opacityTable[Ord(alpha^)]);
  1998. inc(pDst); pt.X := pt.X + 1; inc(alpha);
  1999. end;
  2000. end;
  2001. // ------------------------------------------------------------------------------
  2002. // TEraseRenderer
  2003. // ------------------------------------------------------------------------------
  2004. procedure TEraseRenderer.RenderProc(x1, x2, y: integer; alpha: PByte);
  2005. var
  2006. i: integer;
  2007. dst: PARGB;
  2008. begin
  2009. dst := PARGB(GetDstPixel(x1,y));
  2010. for i := x1 to x2 do
  2011. begin
  2012. {$IFDEF PBYTE}
  2013. dst.A := MulTable[dst.A, not alpha^];
  2014. {$ELSE}
  2015. dst.A := MulTable[dst.A, not Ord(alpha^)];
  2016. {$ENDIF}
  2017. inc(dst); inc(alpha);
  2018. end;
  2019. end;
  2020. // ------------------------------------------------------------------------------
  2021. // TInverseRenderer
  2022. // ------------------------------------------------------------------------------
  2023. procedure TInverseRenderer.RenderProc(x1, x2, y: integer; alpha: PByte);
  2024. var
  2025. i: integer;
  2026. dst: PARGB;
  2027. c: TARGB;
  2028. begin
  2029. dst := PARGB(GetDstPixel(x1,y));
  2030. for i := x1 to x2 do
  2031. begin
  2032. c.Color := not dst.Color;
  2033. c.A := MulTable[dst.A, Ord(alpha^)];
  2034. dst.Color := BlendToAlpha(dst.Color, c.Color);
  2035. inc(dst); inc(alpha);
  2036. end;
  2037. end;
  2038. // ------------------------------------------------------------------------------
  2039. procedure TBarycentricRenderer.SetParameters(const a, b, c: TPointD;
  2040. c1, c2, c3: TColor32);
  2041. begin
  2042. self.a := a;
  2043. self.c1.Color := c1;
  2044. self.c2.Color := c2;
  2045. self.c3.Color := c3;
  2046. v0.X := b.X - a.X;
  2047. v0.Y := b.Y - a.Y;
  2048. v1.X := c.X - a.X;
  2049. v1.Y := c.Y - a.Y;
  2050. d00 := (v0.X * v0.X + v0.Y * v0.Y);
  2051. d01 := (v0.X * v1.X + v0.Y * v1.Y);
  2052. d11 := (v1.X * v1.X + v1.Y * v1.Y);
  2053. invDenom := 1/(d00 * d11 - d01 * d01);
  2054. end;
  2055. // ------------------------------------------------------------------------------
  2056. function TBarycentricRenderer.GetColor(const pt: TPointD): TColor32;
  2057. var
  2058. v2: TPointD;
  2059. d20, d21, v, w, u: Double;
  2060. res: TARGB absolute Result;
  2061. begin
  2062. Result := 0;
  2063. v2.X := pt.X - a.X;
  2064. v2.Y := pt.Y - a.Y;
  2065. d20 := (v2.X * v0.X + v2.Y * v0.Y);
  2066. d21 := (v2.X * v1.X + v2.Y * v1.Y);
  2067. v := (d11 * d20 - d01 * d21) * invDenom;
  2068. w := (d00 * d21 - d01 * d20) * invDenom;
  2069. u := 1.0 - v - w;
  2070. Res.A := ClampByte(c1.A * u + c2.A * v + c3.A * w);
  2071. Res.R := ClampByte(c1.R * u + c2.R * v + c3.R * w);
  2072. Res.G := ClampByte(c1.G * u + c2.G * v + c3.G * w);
  2073. Res.B := ClampByte(c1.B * u + c2.B * v + c3.B * w);
  2074. end;
  2075. // ------------------------------------------------------------------------------
  2076. procedure TBarycentricRenderer.RenderProc(x1, x2, y: integer; alpha: PByte);
  2077. var
  2078. x: integer;
  2079. p: PARGB;
  2080. c: TARGB;
  2081. opacityTable: PByteArray;
  2082. begin
  2083. p := PARGB(fImgBase);
  2084. inc(p, y * ImgWidth + x1);
  2085. if Opacity < 255 then
  2086. begin
  2087. opacityTable := PByteArray(@MulTable[Opacity]);
  2088. for x := x1 to x2 do
  2089. begin
  2090. c.Color := GetColor(PointD(x, y));
  2091. c.A := opacityTable[MulTable[c.A, Ord(alpha^)]];
  2092. p.Color := BlendToAlpha(p.Color, c.Color);
  2093. inc(p); inc(alpha);
  2094. end
  2095. end
  2096. else
  2097. for x := x1 to x2 do
  2098. begin
  2099. c.Color := GetColor(PointD(x, y));
  2100. c.A := MulTable[c.A, Ord(alpha^)];
  2101. p.Color := BlendToAlpha(p.Color, c.Color);
  2102. inc(p); inc(alpha);
  2103. end
  2104. end;
  2105. // ------------------------------------------------------------------------------
  2106. // Draw functions
  2107. // ------------------------------------------------------------------------------
  2108. procedure DrawPoint(img: TImage32;
  2109. const pt: TPointD; radius: double; color: TColor32);
  2110. var
  2111. path: TPathD;
  2112. begin
  2113. if radius <= 1 then
  2114. path := Rectangle(pt.X-radius, pt.Y-radius, pt.X+radius, pt.Y+radius) else
  2115. path := Ellipse(RectD(pt.X-radius, pt.Y-radius, pt.X+radius, pt.Y+radius));
  2116. DrawPolygon(img, path, frEvenOdd, color);
  2117. end;
  2118. // ------------------------------------------------------------------------------
  2119. procedure DrawPoint(img: TImage32; const pt: TPointD;
  2120. radius: double; renderer: TCustomRenderer);
  2121. var
  2122. path: TPathD;
  2123. begin
  2124. path := Ellipse(RectD(pt.X -radius, pt.Y -radius, pt.X +radius, pt.Y +radius));
  2125. DrawPolygon(img, path, frEvenOdd, renderer);
  2126. end;
  2127. // ------------------------------------------------------------------------------
  2128. procedure DrawInvertedPoint(img: TImage32; const pt: TPointD; radius: double);
  2129. var
  2130. cr: TCustomRenderer;
  2131. begin
  2132. cr := TInverseRenderer.Create;
  2133. try
  2134. DrawPoint(img, pt, radius, cr);
  2135. finally
  2136. cr.Free;
  2137. end;
  2138. end;
  2139. // ------------------------------------------------------------------------------
  2140. procedure DrawPoint(img: TImage32; const points: TPathD;
  2141. radius: double; color: TColor32);
  2142. var
  2143. i: integer;
  2144. begin
  2145. for i := 0 to high(points) do
  2146. DrawPoint(img, points[i], radius, color);
  2147. end;
  2148. // ------------------------------------------------------------------------------
  2149. procedure DrawPoint(img: TImage32; const paths: TPathsD;
  2150. radius: double; color: TColor32);
  2151. var
  2152. i: integer;
  2153. begin
  2154. for i := 0 to high(paths) do
  2155. DrawPoint(img, paths[i], radius, color);
  2156. end;
  2157. // ------------------------------------------------------------------------------
  2158. procedure DrawLine(img: TImage32;
  2159. const pt1, pt2: TPointD; lineWidth: double; color: TColor32);
  2160. var
  2161. lines: TPathsD;
  2162. begin
  2163. setLength(lines, 1);
  2164. NewPointDArray(lines[0], 2, True);
  2165. lines[0][0] := pt1;
  2166. lines[0][1] := pt2;
  2167. DrawLine(img, lines, lineWidth, color, esRound);
  2168. end;
  2169. // ------------------------------------------------------------------------------
  2170. procedure DrawLine(img: TImage32; const line: TPathD; lineWidth: double;
  2171. color: TColor32; endStyle: TEndStyle; joinStyle: TJoinStyle;
  2172. miterLimit: double);
  2173. var
  2174. lines: TPathsD;
  2175. begin
  2176. setLength(lines, 1);
  2177. lines[0] := line;
  2178. DrawLine(img, lines, lineWidth, color, endStyle, joinStyle, miterLimit);
  2179. end;
  2180. // ------------------------------------------------------------------------------
  2181. procedure DrawLine(img: TImage32; const line: TPathD; lineWidth: double;
  2182. color: TColor32; rendererCache: TCustomRendererCache;
  2183. endStyle: TEndStyle; joinStyle: TJoinStyle; miterLimit: double);
  2184. var
  2185. lines: TPathsD;
  2186. begin
  2187. setLength(lines, 1);
  2188. lines[0] := line;
  2189. DrawLine(img, lines, lineWidth, color, rendererCache, endStyle, joinStyle,
  2190. miterLimit);
  2191. end;
  2192. // ------------------------------------------------------------------------------
  2193. procedure DrawLine(img: TImage32; const line: TPathD; lineWidth: double;
  2194. renderer: TCustomRenderer; endStyle: TEndStyle; joinStyle: TJoinStyle;
  2195. miterLimit: double);
  2196. var
  2197. lines: TPathsD;
  2198. begin
  2199. setLength(lines, 1);
  2200. lines[0] := line;
  2201. DrawLine(img, lines, lineWidth, renderer, endStyle, joinStyle, miterLimit);
  2202. end;
  2203. // ------------------------------------------------------------------------------
  2204. procedure DrawInvertedLine(img: TImage32; const line: TPathD;
  2205. lineWidth: double; endStyle: TEndStyle; joinStyle: TJoinStyle = jsAuto);
  2206. var
  2207. lines: TPathsD;
  2208. begin
  2209. setLength(lines, 1);
  2210. lines[0] := line;
  2211. DrawInvertedLine(img, lines, lineWidth, endStyle, joinStyle);
  2212. end;
  2213. // ------------------------------------------------------------------------------
  2214. procedure DrawLine(img: TImage32; const lines: TPathsD;
  2215. lineWidth: double; color: TColor32;
  2216. endStyle: TEndStyle; joinStyle: TJoinStyle; miterLimit: double);
  2217. var
  2218. cr: TCustomColorRenderer;
  2219. begin
  2220. if not assigned(lines) then exit;
  2221. if img.AntiAliased then
  2222. cr := TColorRenderer.Create(color) else
  2223. cr := TAliasedColorRenderer.Create(color);
  2224. try
  2225. DrawLine(img, lines, lineWidth, cr, endStyle, joinStyle, miterLimit);
  2226. finally
  2227. cr.free;
  2228. end;
  2229. end;
  2230. // ------------------------------------------------------------------------------
  2231. procedure DrawLine(img: TImage32; const lines: TPathsD;
  2232. lineWidth: double; color: TColor32; rendererCache: TCustomRendererCache;
  2233. endStyle: TEndStyle; joinStyle: TJoinStyle; miterLimit: double);
  2234. var
  2235. cr: TCustomColorRenderer;
  2236. begin
  2237. if not assigned(lines) then exit;
  2238. if rendererCache = nil then
  2239. DrawLine(img, lines, lineWidth, color, endStyle, joinStyle, miterLimit)
  2240. else
  2241. begin
  2242. if img.AntiAliased then
  2243. cr := rendererCache.ColorRenderer else
  2244. cr := rendererCache.AliasedColorRenderer;
  2245. DrawLine(img, lines, lineWidth, cr, endStyle, joinStyle, miterLimit);
  2246. end;
  2247. end;
  2248. // ------------------------------------------------------------------------------
  2249. procedure DrawLine(img: TImage32; const lines: TPathsD;
  2250. lineWidth: double; renderer: TCustomRenderer;
  2251. endStyle: TEndStyle; joinStyle: TJoinStyle;
  2252. miterLimit: double);
  2253. var
  2254. lines2: TPathsD;
  2255. begin
  2256. if (not assigned(lines)) or (not assigned(renderer)) then exit;
  2257. if (lineWidth < MinStrokeWidth) then lineWidth := MinStrokeWidth;
  2258. lines2 := RoughOutline(lines, lineWidth, joinStyle, endStyle, miterLimit);
  2259. Rasterize(img, lines2, img.bounds, frNonZero, renderer);
  2260. end;
  2261. // ------------------------------------------------------------------------------
  2262. procedure DrawInvertedLine(img: TImage32;
  2263. const lines: TPathsD; lineWidth: double;
  2264. endStyle: TEndStyle; joinStyle: TJoinStyle = jsAuto);
  2265. var
  2266. lines2: TPathsD;
  2267. ir: TInverseRenderer;
  2268. begin
  2269. if not assigned(lines) then exit;
  2270. if (lineWidth < MinStrokeWidth) then lineWidth := MinStrokeWidth;
  2271. lines2 := RoughOutline(lines, lineWidth, joinStyle, endStyle, 2);
  2272. ir := TInverseRenderer.Create;
  2273. try
  2274. Rasterize(img, lines2, img.bounds, frNonZero, ir);
  2275. finally
  2276. ir.free;
  2277. end;
  2278. end;
  2279. // ------------------------------------------------------------------------------
  2280. procedure DrawDashedLine(img: TImage32; const line: TPathD;
  2281. dashPattern: TArrayOfDouble; patternOffset: PDouble; lineWidth: double;
  2282. color: TColor32; endStyle: TEndStyle; joinStyle: TJoinStyle;
  2283. rendererCache: TCustomRendererCache);
  2284. var
  2285. lines: TPathsD;
  2286. cr: TColorRenderer;
  2287. i: integer;
  2288. begin
  2289. if (lineWidth < MinStrokeWidth) then lineWidth := MinStrokeWidth;
  2290. if not assigned(line) then exit;
  2291. for i := 0 to High(dashPattern) do
  2292. if dashPattern[i] <= 0 then dashPattern[i] := 1;
  2293. lines := GetDashedPath(line, endStyle = esPolygon, dashPattern, patternOffset);
  2294. if Length(lines) = 0 then Exit;
  2295. case joinStyle of
  2296. jsAuto:
  2297. if endStyle = esRound then
  2298. joinStyle := jsRound else
  2299. joinStyle := jsSquare;
  2300. jsSquare, jsMiter:
  2301. endStyle := esSquare;
  2302. jsRound:
  2303. endStyle := esRound;
  2304. jsButt:
  2305. endStyle := esButt;
  2306. end;
  2307. lines := RoughOutline(lines, lineWidth, joinStyle, endStyle);
  2308. if rendererCache = nil then
  2309. cr := TColorRenderer.Create(color) else
  2310. cr := rendererCache.GetColorRenderer(color);
  2311. try
  2312. Rasterize(img, lines, img.bounds, frNonZero, cr);
  2313. finally
  2314. if rendererCache = nil then
  2315. cr.free;
  2316. end;
  2317. end;
  2318. // ------------------------------------------------------------------------------
  2319. procedure DrawDashedLine(img: TImage32; const lines: TPathsD;
  2320. dashPattern: TArrayOfDouble; patternOffset: PDouble; lineWidth: double;
  2321. color: TColor32; endStyle: TEndStyle; joinStyle: TJoinStyle;
  2322. rendererCache: TCustomRendererCache);
  2323. var
  2324. i: integer;
  2325. begin
  2326. if not assigned(lines) then exit;
  2327. for i := 0 to high(lines) do
  2328. DrawDashedLine(img, lines[i],
  2329. dashPattern, patternOffset, lineWidth, color, endStyle, joinStyle,
  2330. rendererCache);
  2331. end;
  2332. // ------------------------------------------------------------------------------
  2333. procedure DrawDashedLine(img: TImage32; const line: TPathD;
  2334. dashPattern: TArrayOfDouble; patternOffset: PDouble; lineWidth: double;
  2335. renderer: TCustomRenderer; endStyle: TEndStyle; joinStyle: TJoinStyle);
  2336. var
  2337. i: integer;
  2338. lines: TPathsD;
  2339. begin
  2340. if (not assigned(line)) or (not assigned(renderer)) then exit;
  2341. if (lineWidth < MinStrokeWidth) then lineWidth := MinStrokeWidth;
  2342. for i := 0 to High(dashPattern) do
  2343. if dashPattern[i] <= 0 then dashPattern[i] := 1;
  2344. lines := GetDashedPath(line, endStyle = esPolygon, dashPattern, patternOffset);
  2345. if Length(lines) = 0 then Exit;
  2346. lines := RoughOutline(lines, lineWidth, joinStyle, endStyle);
  2347. Rasterize(img, lines, img.bounds, frNonZero, renderer);
  2348. end;
  2349. // ------------------------------------------------------------------------------
  2350. procedure DrawDashedLine(img: TImage32; const lines: TPathsD;
  2351. dashPattern: TArrayOfDouble; patternOffset: PDouble; lineWidth: double;
  2352. renderer: TCustomRenderer; endStyle: TEndStyle; joinStyle: TJoinStyle);
  2353. var
  2354. i: integer;
  2355. begin
  2356. if not assigned(lines) then exit;
  2357. for i := 0 to high(lines) do
  2358. DrawDashedLine(img, lines[i],
  2359. dashPattern, patternOffset, lineWidth, renderer, endStyle, joinStyle);
  2360. end;
  2361. // ------------------------------------------------------------------------------
  2362. procedure DrawInvertedDashedLine(img: TImage32;
  2363. const line: TPathD; dashPattern: TArrayOfDouble;
  2364. patternOffset: PDouble; lineWidth: double; endStyle: TEndStyle;
  2365. joinStyle: TJoinStyle = jsAuto);
  2366. var
  2367. i: integer;
  2368. lines: TPathsD;
  2369. renderer: TInverseRenderer;
  2370. begin
  2371. if not assigned(line) then exit;
  2372. if (lineWidth < MinStrokeWidth) then lineWidth := MinStrokeWidth;
  2373. for i := 0 to High(dashPattern) do
  2374. if dashPattern[i] <= 0 then dashPattern[i] := 1;
  2375. lines := GetDashedPath(line, endStyle = esPolygon, dashPattern, patternOffset);
  2376. if Length(lines) = 0 then Exit;
  2377. lines := RoughOutline(lines, lineWidth, joinStyle, endStyle);
  2378. renderer := TInverseRenderer.Create;
  2379. try
  2380. Rasterize(img, lines, img.bounds, frNonZero, renderer);
  2381. finally
  2382. renderer.Free;
  2383. end;
  2384. end;
  2385. // ------------------------------------------------------------------------------
  2386. procedure DrawInvertedDashedLine(img: TImage32;
  2387. const lines: TPathsD; dashPattern: TArrayOfDouble;
  2388. patternOffset: PDouble; lineWidth: double;
  2389. endStyle: TEndStyle; joinStyle: TJoinStyle = jsAuto);
  2390. var
  2391. i: integer;
  2392. begin
  2393. if not assigned(lines) then exit;
  2394. for i := 0 to high(lines) do
  2395. DrawInvertedDashedLine(img, lines[i],
  2396. dashPattern, patternOffset, lineWidth, endStyle, joinStyle);
  2397. end;
  2398. // ------------------------------------------------------------------------------
  2399. procedure DrawPolygon(img: TImage32; const polygon: TPathD;
  2400. fillRule: TFillRule; color: TColor32);
  2401. var
  2402. polygons: TPathsD;
  2403. begin
  2404. if not assigned(polygon) then exit;
  2405. setLength(polygons, 1);
  2406. polygons[0] := polygon;
  2407. DrawPolygon(img, polygons, fillRule, color);
  2408. end;
  2409. // ------------------------------------------------------------------------------
  2410. procedure DrawPolygon(img: TImage32; const polygon: TPathD;
  2411. fillRule: TFillRule; renderer: TCustomRenderer);
  2412. var
  2413. polygons: TPathsD;
  2414. begin
  2415. if (not assigned(polygon)) or (not assigned(renderer)) then exit;
  2416. setLength(polygons, 1);
  2417. polygons[0] := polygon;
  2418. Rasterize(img, polygons, img.Bounds, fillRule, renderer);
  2419. end;
  2420. // ------------------------------------------------------------------------------
  2421. procedure DrawPolygon(img: TImage32; const polygons: TPathsD;
  2422. fillRule: TFillRule; color: TColor32);
  2423. var
  2424. cr: TCustomRenderer;
  2425. begin
  2426. if not assigned(polygons) then exit;
  2427. if img.AntiAliased then
  2428. cr := TColorRenderer.Create(color) else
  2429. cr := TAliasedColorRenderer.Create(color);
  2430. try
  2431. Rasterize(img, polygons, img.bounds, fillRule, cr);
  2432. finally
  2433. cr.free;
  2434. end;
  2435. end;
  2436. // ------------------------------------------------------------------------------
  2437. procedure DrawPolygon(img: TImage32; const polygons: TPathsD;
  2438. fillRule: TFillRule; color: TColor32;
  2439. rendererCache: TCustomRendererCache);
  2440. var
  2441. cr: TCustomColorRenderer;
  2442. begin
  2443. if not assigned(polygons) then exit;
  2444. if rendererCache = nil then
  2445. DrawPolygon(img, polygons, fillRule, color)
  2446. else
  2447. begin
  2448. if img.AntiAliased then
  2449. cr := rendererCache.ColorRenderer else
  2450. cr := rendererCache.AliasedColorRenderer;
  2451. cr.SetColor(color);
  2452. Rasterize(img, polygons, img.bounds, fillRule, cr);
  2453. end;
  2454. end;
  2455. // ------------------------------------------------------------------------------
  2456. procedure DrawPolygon(img: TImage32; const polygons: TPathsD;
  2457. fillRule: TFillRule; renderer: TCustomRenderer);
  2458. begin
  2459. if (not assigned(polygons)) or (not assigned(renderer)) then exit;
  2460. Rasterize(img, polygons, img.bounds, fillRule, renderer);
  2461. end;
  2462. // ------------------------------------------------------------------------------
  2463. procedure DrawInvertedPolygon(img: TImage32; const polygon: TPathD;
  2464. fillRule: TFillRule);
  2465. var
  2466. polygons: TPathsD;
  2467. begin
  2468. if not assigned(polygon) then exit;
  2469. setLength(polygons, 1);
  2470. polygons[0] := polygon;
  2471. DrawInvertedPolygon(img, polygons, fillRule);
  2472. end;
  2473. // ------------------------------------------------------------------------------
  2474. procedure DrawInvertedPolygon(img: TImage32; const polygons: TPathsD;
  2475. fillRule: TFillRule);
  2476. var
  2477. cr: TCustomRenderer;
  2478. begin
  2479. if not assigned(polygons) then exit;
  2480. cr := TInverseRenderer.Create;
  2481. try
  2482. Rasterize(img, polygons, img.bounds, fillRule, cr);
  2483. finally
  2484. cr.free;
  2485. end;
  2486. end;
  2487. // ------------------------------------------------------------------------------
  2488. procedure DrawPolygon_ClearType(img: TImage32; const polygons: TPathsD;
  2489. fillRule: TFillRule; color: TColor32; backColor: TColor32);
  2490. var
  2491. w, h: integer;
  2492. tmpImg: TImage32;
  2493. rec: TRect;
  2494. tmpPolygons: TPathsD;
  2495. cr: TColorRenderer;
  2496. begin
  2497. if not assigned(polygons) then exit;
  2498. rec := GetBounds(polygons);
  2499. RectWidthHeight(rec, w, h);
  2500. tmpImg := TImage32.Create(w *3, h);
  2501. try
  2502. tmpPolygons := TranslatePath(polygons, -rec.Left, -rec.Top);
  2503. tmpPolygons := ScalePath(tmpPolygons, 3, 1);
  2504. cr := TColorRenderer.Create(clBlack32);
  2505. try
  2506. Rasterize(tmpImg, tmpPolygons, tmpImg.bounds, fillRule, cr);
  2507. finally
  2508. cr.Free;
  2509. end;
  2510. ApplyClearType(tmpImg, color, backColor);
  2511. img.CopyBlend(tmpImg, tmpImg.Bounds, rec, BlendToAlphaLine);
  2512. finally
  2513. tmpImg.Free;
  2514. end;
  2515. end;
  2516. // ------------------------------------------------------------------------------
  2517. procedure ErasePolygon(img: TImage32; const polygon: TPathD;
  2518. fillRule: TFillRule);
  2519. var
  2520. polygons: TPathsD;
  2521. begin
  2522. if not assigned(polygon) then exit;
  2523. setLength(polygons, 1);
  2524. polygons[0] := polygon;
  2525. ErasePolygon(img, polygons, fillRule);
  2526. end;
  2527. // ------------------------------------------------------------------------------
  2528. procedure ErasePolygon(img: TImage32; const polygons: TPathsD;
  2529. fillRule: TFillRule);
  2530. var
  2531. er: TEraseRenderer;
  2532. begin
  2533. er := TEraseRenderer.Create;
  2534. try
  2535. Rasterize(img, polygons, img.bounds, fillRule, er);
  2536. finally
  2537. er.Free;
  2538. end;
  2539. end;
  2540. // ------------------------------------------------------------------------------
  2541. procedure DrawBoolMask(img: TImage32; const mask: TArrayOfByte; color: TColor32);
  2542. var
  2543. i, len: integer;
  2544. pc: PColor32;
  2545. pb: PByte;
  2546. begin
  2547. len := Length(mask);
  2548. if (len = 0) or (len <> img.Width * img.Height) then Exit;
  2549. pc := img.PixelBase;
  2550. pb := @mask[0];
  2551. for i := 0 to len -1 do
  2552. begin
  2553. {$IFDEF PBYTE}
  2554. if pb^ > 0 then
  2555. {$ELSE}
  2556. if pb^ > #0 then
  2557. {$ENDIF}
  2558. pc^ := color else
  2559. pc^ := clNone32;
  2560. inc(pc); inc(pb);
  2561. end;
  2562. end;
  2563. // ------------------------------------------------------------------------------
  2564. procedure DrawAlphaMask(img: TImage32; const mask: TArrayOfByte; color: TColor32);
  2565. var
  2566. i, len: integer;
  2567. pc: PColor32;
  2568. pb: PByte;
  2569. begin
  2570. len := Length(mask);
  2571. if (len = 0) or (len <> img.Width * img.Height) then Exit;
  2572. color := color and $FFFFFF; //strip alpha value
  2573. pc := img.PixelBase;
  2574. pb := @mask[0];
  2575. for i := 0 to len -1 do
  2576. begin
  2577. {$IFDEF PBYTE}
  2578. if pb^ > 0 then
  2579. pc^ := color or pb^ shl 24 else
  2580. pc^ := clNone32;
  2581. {$ELSE}
  2582. if pb^ > #0 then
  2583. pc^ := color or Ord(pb^) shl 24 else
  2584. pc^ := clNone32;
  2585. {$ENDIF}
  2586. inc(pc); inc(pb);
  2587. end;
  2588. end;
  2589. // ------------------------------------------------------------------------------
  2590. end.