nsSVGUtils.cpp 58 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813
  1. /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. // Main header first:
  6. // This is also necessary to ensure our definition of M_SQRT1_2 is picked up
  7. #include "nsSVGUtils.h"
  8. #include <algorithm>
  9. // Keep others in (case-insensitive) order:
  10. #include "gfx2DGlue.h"
  11. #include "gfxContext.h"
  12. #include "gfxMatrix.h"
  13. #include "gfxPlatform.h"
  14. #include "gfxRect.h"
  15. #include "gfxUtils.h"
  16. #include "mozilla/gfx/2D.h"
  17. #include "mozilla/gfx/PatternHelpers.h"
  18. #include "mozilla/Preferences.h"
  19. #include "mozilla/SVGContextPaint.h"
  20. #include "nsCSSClipPathInstance.h"
  21. #include "nsCSSFrameConstructor.h"
  22. #include "nsDisplayList.h"
  23. #include "nsFilterInstance.h"
  24. #include "nsFrameList.h"
  25. #include "nsGkAtoms.h"
  26. #include "nsIContent.h"
  27. #include "nsIDocument.h"
  28. #include "nsIFrame.h"
  29. #include "nsIPresShell.h"
  30. #include "nsISVGChildFrame.h"
  31. #include "nsLayoutUtils.h"
  32. #include "nsPresContext.h"
  33. #include "nsStyleCoord.h"
  34. #include "nsStyleStruct.h"
  35. #include "nsSVGClipPathFrame.h"
  36. #include "nsSVGContainerFrame.h"
  37. #include "nsSVGEffects.h"
  38. #include "nsSVGFilterPaintCallback.h"
  39. #include "nsSVGForeignObjectFrame.h"
  40. #include "nsSVGInnerSVGFrame.h"
  41. #include "nsSVGIntegrationUtils.h"
  42. #include "nsSVGLength2.h"
  43. #include "nsSVGMaskFrame.h"
  44. #include "nsSVGOuterSVGFrame.h"
  45. #include "mozilla/dom/SVGClipPathElement.h"
  46. #include "mozilla/dom/SVGPathElement.h"
  47. #include "nsSVGPathGeometryElement.h"
  48. #include "nsSVGPathGeometryFrame.h"
  49. #include "nsSVGPaintServerFrame.h"
  50. #include "mozilla/dom/SVGSVGElement.h"
  51. #include "nsTextFrame.h"
  52. #include "SVGContentUtils.h"
  53. #include "SVGTextFrame.h"
  54. #include "mozilla/Unused.h"
  55. using namespace mozilla;
  56. using namespace mozilla::dom;
  57. using namespace mozilla::gfx;
  58. using namespace mozilla::image;
  59. static bool sSVGPathCachingEnabled;
  60. static bool sSVGDisplayListHitTestingEnabled;
  61. static bool sSVGDisplayListPaintingEnabled;
  62. static bool sSVGNewGetBBoxEnabled;
  63. bool
  64. NS_SVGPathCachingEnabled()
  65. {
  66. return sSVGPathCachingEnabled;
  67. }
  68. bool
  69. NS_SVGDisplayListHitTestingEnabled()
  70. {
  71. return sSVGDisplayListHitTestingEnabled;
  72. }
  73. bool
  74. NS_SVGDisplayListPaintingEnabled()
  75. {
  76. return sSVGDisplayListPaintingEnabled;
  77. }
  78. bool
  79. NS_SVGNewGetBBoxEnabled()
  80. {
  81. return sSVGNewGetBBoxEnabled;
  82. }
  83. // we only take the address of this:
  84. static mozilla::gfx::UserDataKey sSVGAutoRenderStateKey;
  85. SVGAutoRenderState::SVGAutoRenderState(DrawTarget* aDrawTarget
  86. MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
  87. : mDrawTarget(aDrawTarget)
  88. , mOriginalRenderState(nullptr)
  89. , mPaintingToWindow(false)
  90. {
  91. MOZ_GUARD_OBJECT_NOTIFIER_INIT;
  92. mOriginalRenderState =
  93. aDrawTarget->RemoveUserData(&sSVGAutoRenderStateKey);
  94. // We always remove ourselves from aContext before it dies, so
  95. // passing nullptr as the destroy function is okay.
  96. aDrawTarget->AddUserData(&sSVGAutoRenderStateKey, this, nullptr);
  97. }
  98. SVGAutoRenderState::~SVGAutoRenderState()
  99. {
  100. mDrawTarget->RemoveUserData(&sSVGAutoRenderStateKey);
  101. if (mOriginalRenderState) {
  102. mDrawTarget->AddUserData(&sSVGAutoRenderStateKey,
  103. mOriginalRenderState, nullptr);
  104. }
  105. }
  106. void
  107. SVGAutoRenderState::SetPaintingToWindow(bool aPaintingToWindow)
  108. {
  109. mPaintingToWindow = aPaintingToWindow;
  110. }
  111. /* static */ bool
  112. SVGAutoRenderState::IsPaintingToWindow(DrawTarget* aDrawTarget)
  113. {
  114. void *state = aDrawTarget->GetUserData(&sSVGAutoRenderStateKey);
  115. if (state) {
  116. return static_cast<SVGAutoRenderState*>(state)->mPaintingToWindow;
  117. }
  118. return false;
  119. }
  120. void
  121. nsSVGUtils::Init()
  122. {
  123. Preferences::AddBoolVarCache(&sSVGPathCachingEnabled,
  124. "svg.path-caching.enabled");
  125. Preferences::AddBoolVarCache(&sSVGDisplayListHitTestingEnabled,
  126. "svg.display-lists.hit-testing.enabled");
  127. Preferences::AddBoolVarCache(&sSVGDisplayListPaintingEnabled,
  128. "svg.display-lists.painting.enabled");
  129. Preferences::AddBoolVarCache(&sSVGNewGetBBoxEnabled,
  130. "svg.new-getBBox.enabled");
  131. }
  132. nsSVGDisplayContainerFrame*
  133. nsSVGUtils::GetNearestSVGViewport(nsIFrame *aFrame)
  134. {
  135. NS_ASSERTION(aFrame->IsFrameOfType(nsIFrame::eSVG), "SVG frame expected");
  136. if (aFrame->GetType() == nsGkAtoms::svgOuterSVGFrame) {
  137. return nullptr;
  138. }
  139. while ((aFrame = aFrame->GetParent())) {
  140. NS_ASSERTION(aFrame->IsFrameOfType(nsIFrame::eSVG), "SVG frame expected");
  141. if (aFrame->GetType() == nsGkAtoms::svgInnerSVGFrame ||
  142. aFrame->GetType() == nsGkAtoms::svgOuterSVGFrame) {
  143. return do_QueryFrame(aFrame);
  144. }
  145. }
  146. NS_NOTREACHED("This is not reached. It's only needed to compile.");
  147. return nullptr;
  148. }
  149. nsRect
  150. nsSVGUtils::GetPostFilterVisualOverflowRect(nsIFrame *aFrame,
  151. const nsRect &aPreFilterRect)
  152. {
  153. MOZ_ASSERT(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT,
  154. "Called on invalid frame type");
  155. nsSVGFilterProperty *property = nsSVGEffects::GetFilterProperty(aFrame);
  156. if (!property || !property->ReferencesValidResources()) {
  157. return aPreFilterRect;
  158. }
  159. return nsFilterInstance::GetPostFilterBounds(aFrame, nullptr, &aPreFilterRect);
  160. }
  161. bool
  162. nsSVGUtils::OuterSVGIsCallingReflowSVG(nsIFrame *aFrame)
  163. {
  164. return GetOuterSVGFrame(aFrame)->IsCallingReflowSVG();
  165. }
  166. bool
  167. nsSVGUtils::AnyOuterSVGIsCallingReflowSVG(nsIFrame* aFrame)
  168. {
  169. nsSVGOuterSVGFrame* outer = GetOuterSVGFrame(aFrame);
  170. do {
  171. if (outer->IsCallingReflowSVG()) {
  172. return true;
  173. }
  174. outer = GetOuterSVGFrame(outer->GetParent());
  175. } while (outer);
  176. return false;
  177. }
  178. void
  179. nsSVGUtils::ScheduleReflowSVG(nsIFrame *aFrame)
  180. {
  181. MOZ_ASSERT(aFrame->IsFrameOfType(nsIFrame::eSVG),
  182. "Passed bad frame!");
  183. // If this is triggered, the callers should be fixed to call us before
  184. // ReflowSVG is called. If we try to mark dirty bits on frames while we're
  185. // in the process of removing them, things will get messed up.
  186. NS_ASSERTION(!OuterSVGIsCallingReflowSVG(aFrame),
  187. "Do not call under nsISVGChildFrame::ReflowSVG!");
  188. // We don't call nsSVGEffects::InvalidateRenderingObservers here because
  189. // we should only be called under InvalidateAndScheduleReflowSVG (which
  190. // calls InvalidateBounds) or nsSVGDisplayContainerFrame::InsertFrames
  191. // (at which point the frame has no observers).
  192. if (aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY) {
  193. return;
  194. }
  195. if (aFrame->GetStateBits() &
  196. (NS_FRAME_IS_DIRTY | NS_FRAME_FIRST_REFLOW)) {
  197. // Nothing to do if we're already dirty, or if the outer-<svg>
  198. // hasn't yet had its initial reflow.
  199. return;
  200. }
  201. nsSVGOuterSVGFrame *outerSVGFrame = nullptr;
  202. // We must not add dirty bits to the nsSVGOuterSVGFrame or else
  203. // PresShell::FrameNeedsReflow won't work when we pass it in below.
  204. if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG) {
  205. outerSVGFrame = static_cast<nsSVGOuterSVGFrame*>(aFrame);
  206. } else {
  207. aFrame->AddStateBits(NS_FRAME_IS_DIRTY);
  208. nsIFrame *f = aFrame->GetParent();
  209. while (f && !(f->GetStateBits() & NS_STATE_IS_OUTER_SVG)) {
  210. if (f->GetStateBits() &
  211. (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN)) {
  212. return;
  213. }
  214. f->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
  215. f = f->GetParent();
  216. MOZ_ASSERT(f->IsFrameOfType(nsIFrame::eSVG),
  217. "NS_STATE_IS_OUTER_SVG check above not valid!");
  218. }
  219. outerSVGFrame = static_cast<nsSVGOuterSVGFrame*>(f);
  220. MOZ_ASSERT(outerSVGFrame &&
  221. outerSVGFrame->GetType() == nsGkAtoms::svgOuterSVGFrame,
  222. "Did not find nsSVGOuterSVGFrame!");
  223. }
  224. if (outerSVGFrame->GetStateBits() & NS_FRAME_IN_REFLOW) {
  225. // We're currently under an nsSVGOuterSVGFrame::Reflow call so there is no
  226. // need to call PresShell::FrameNeedsReflow, since we have an
  227. // nsSVGOuterSVGFrame::DidReflow call pending.
  228. return;
  229. }
  230. nsFrameState dirtyBit =
  231. (outerSVGFrame == aFrame ? NS_FRAME_IS_DIRTY : NS_FRAME_HAS_DIRTY_CHILDREN);
  232. aFrame->PresContext()->PresShell()->FrameNeedsReflow(
  233. outerSVGFrame, nsIPresShell::eResize, dirtyBit);
  234. }
  235. bool
  236. nsSVGUtils::NeedsReflowSVG(nsIFrame *aFrame)
  237. {
  238. MOZ_ASSERT(aFrame->IsFrameOfType(nsIFrame::eSVG),
  239. "SVG uses bits differently!");
  240. // The flags we test here may change, hence why we have this separate
  241. // function.
  242. return NS_SUBTREE_DIRTY(aFrame);
  243. }
  244. void
  245. nsSVGUtils::NotifyAncestorsOfFilterRegionChange(nsIFrame *aFrame)
  246. {
  247. MOZ_ASSERT(!(aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG),
  248. "Not expecting to be called on the outer SVG Frame");
  249. aFrame = aFrame->GetParent();
  250. while (aFrame) {
  251. if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG)
  252. return;
  253. nsSVGFilterProperty *property = nsSVGEffects::GetFilterProperty(aFrame);
  254. if (property) {
  255. property->Invalidate();
  256. }
  257. aFrame = aFrame->GetParent();
  258. }
  259. }
  260. Size
  261. nsSVGUtils::GetContextSize(const nsIFrame* aFrame)
  262. {
  263. Size size;
  264. MOZ_ASSERT(aFrame->GetContent()->IsSVGElement(), "bad cast");
  265. const nsSVGElement* element = static_cast<nsSVGElement*>(aFrame->GetContent());
  266. SVGSVGElement* ctx = element->GetCtx();
  267. if (ctx) {
  268. size.width = ctx->GetLength(SVGContentUtils::X);
  269. size.height = ctx->GetLength(SVGContentUtils::Y);
  270. }
  271. return size;
  272. }
  273. float
  274. nsSVGUtils::ObjectSpace(const gfxRect &aRect, const nsSVGLength2 *aLength)
  275. {
  276. float axis;
  277. switch (aLength->GetCtxType()) {
  278. case SVGContentUtils::X:
  279. axis = aRect.Width();
  280. break;
  281. case SVGContentUtils::Y:
  282. axis = aRect.Height();
  283. break;
  284. case SVGContentUtils::XY:
  285. axis = float(SVGContentUtils::ComputeNormalizedHypotenuse(
  286. aRect.Width(), aRect.Height()));
  287. break;
  288. default:
  289. NS_NOTREACHED("unexpected ctx type");
  290. axis = 0.0f;
  291. break;
  292. }
  293. if (aLength->IsPercentage()) {
  294. // Multiply first to avoid precision errors:
  295. return axis * aLength->GetAnimValInSpecifiedUnits() / 100;
  296. }
  297. return aLength->GetAnimValue(static_cast<SVGSVGElement*>(nullptr)) * axis;
  298. }
  299. float
  300. nsSVGUtils::UserSpace(nsSVGElement *aSVGElement, const nsSVGLength2 *aLength)
  301. {
  302. return aLength->GetAnimValue(aSVGElement);
  303. }
  304. float
  305. nsSVGUtils::UserSpace(nsIFrame *aNonSVGContext, const nsSVGLength2 *aLength)
  306. {
  307. return aLength->GetAnimValue(aNonSVGContext);
  308. }
  309. float
  310. nsSVGUtils::UserSpace(const UserSpaceMetrics& aMetrics, const nsSVGLength2 *aLength)
  311. {
  312. return aLength->GetAnimValue(aMetrics);
  313. }
  314. nsSVGOuterSVGFrame *
  315. nsSVGUtils::GetOuterSVGFrame(nsIFrame *aFrame)
  316. {
  317. while (aFrame) {
  318. if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG) {
  319. return static_cast<nsSVGOuterSVGFrame*>(aFrame);
  320. }
  321. aFrame = aFrame->GetParent();
  322. }
  323. return nullptr;
  324. }
  325. nsIFrame*
  326. nsSVGUtils::GetOuterSVGFrameAndCoveredRegion(nsIFrame* aFrame, nsRect* aRect)
  327. {
  328. nsISVGChildFrame* svg = do_QueryFrame(aFrame);
  329. if (!svg)
  330. return nullptr;
  331. nsSVGOuterSVGFrame* outer = GetOuterSVGFrame(aFrame);
  332. if (outer == svg) {
  333. return nullptr;
  334. }
  335. nsMargin bp = outer->GetUsedBorderAndPadding();
  336. *aRect = ((aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY) ?
  337. nsRect(0, 0, 0, 0) : svg->GetCoveredRegion()) +
  338. nsPoint(bp.left, bp.top);
  339. return outer;
  340. }
  341. gfxMatrix
  342. nsSVGUtils::GetCanvasTM(nsIFrame *aFrame)
  343. {
  344. // XXX yuck, we really need a common interface for GetCanvasTM
  345. if (!aFrame->IsFrameOfType(nsIFrame::eSVG)) {
  346. return nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(aFrame);
  347. }
  348. nsIAtom* type = aFrame->GetType();
  349. if (type == nsGkAtoms::svgForeignObjectFrame) {
  350. return static_cast<nsSVGForeignObjectFrame*>(aFrame)->GetCanvasTM();
  351. }
  352. if (type == nsGkAtoms::svgOuterSVGFrame) {
  353. return nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(aFrame);
  354. }
  355. nsSVGContainerFrame *containerFrame = do_QueryFrame(aFrame);
  356. if (containerFrame) {
  357. return containerFrame->GetCanvasTM();
  358. }
  359. return static_cast<nsSVGPathGeometryFrame*>(aFrame)->GetCanvasTM();
  360. }
  361. gfxMatrix
  362. nsSVGUtils::GetUserToCanvasTM(nsIFrame *aFrame)
  363. {
  364. nsISVGChildFrame* svgFrame = do_QueryFrame(aFrame);
  365. NS_ASSERTION(svgFrame, "bad frame");
  366. gfxMatrix tm;
  367. if (svgFrame) {
  368. nsSVGElement *content = static_cast<nsSVGElement*>(aFrame->GetContent());
  369. tm = content->PrependLocalTransformsTo(
  370. GetCanvasTM(aFrame->GetParent()),
  371. eUserSpaceToParent);
  372. }
  373. return tm;
  374. }
  375. void
  376. nsSVGUtils::NotifyChildrenOfSVGChange(nsIFrame *aFrame, uint32_t aFlags)
  377. {
  378. for (nsIFrame* kid : aFrame->PrincipalChildList()) {
  379. nsISVGChildFrame* SVGFrame = do_QueryFrame(kid);
  380. if (SVGFrame) {
  381. SVGFrame->NotifySVGChanged(aFlags);
  382. } else {
  383. NS_ASSERTION(kid->IsFrameOfType(nsIFrame::eSVG) || kid->IsSVGText(),
  384. "SVG frame expected");
  385. // recurse into the children of container frames e.g. <clipPath>, <mask>
  386. // in case they have child frames with transformation matrices
  387. if (kid->IsFrameOfType(nsIFrame::eSVG)) {
  388. NotifyChildrenOfSVGChange(kid, aFlags);
  389. }
  390. }
  391. }
  392. }
  393. // ************************************************************
  394. class SVGPaintCallback : public nsSVGFilterPaintCallback
  395. {
  396. public:
  397. virtual DrawResult Paint(gfxContext& aContext, nsIFrame *aTarget,
  398. const gfxMatrix& aTransform,
  399. const nsIntRect* aDirtyRect) override
  400. {
  401. nsISVGChildFrame *svgChildFrame = do_QueryFrame(aTarget);
  402. NS_ASSERTION(svgChildFrame, "Expected SVG frame here");
  403. nsIntRect* dirtyRect = nullptr;
  404. nsIntRect tmpDirtyRect;
  405. // aDirtyRect is in user-space pixels, we need to convert to
  406. // outer-SVG-frame-relative device pixels.
  407. if (aDirtyRect) {
  408. gfxMatrix userToDeviceSpace = aTransform;
  409. if (userToDeviceSpace.IsSingular()) {
  410. return DrawResult::SUCCESS;
  411. }
  412. gfxRect dirtyBounds = userToDeviceSpace.TransformBounds(
  413. gfxRect(aDirtyRect->x, aDirtyRect->y, aDirtyRect->width, aDirtyRect->height));
  414. dirtyBounds.RoundOut();
  415. if (gfxUtils::GfxRectToIntRect(dirtyBounds, &tmpDirtyRect)) {
  416. dirtyRect = &tmpDirtyRect;
  417. }
  418. }
  419. return svgChildFrame->PaintSVG(aContext, aTransform, dirtyRect);
  420. }
  421. };
  422. float
  423. nsSVGUtils::ComputeOpacity(nsIFrame* aFrame, bool aHandleOpacity)
  424. {
  425. float opacity = aFrame->StyleEffects()->mOpacity;
  426. if (opacity != 1.0f &&
  427. (nsSVGUtils::CanOptimizeOpacity(aFrame) || !aHandleOpacity)) {
  428. return 1.0f;
  429. }
  430. return opacity;
  431. }
  432. void
  433. nsSVGUtils::DetermineMaskUsage(nsIFrame* aFrame, bool aHandleOpacity,
  434. MaskUsage& aUsage)
  435. {
  436. aUsage.opacity = ComputeOpacity(aFrame, aHandleOpacity);
  437. nsIFrame* firstFrame =
  438. nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
  439. nsSVGEffects::EffectProperties effectProperties =
  440. nsSVGEffects::GetEffectProperties(firstFrame);
  441. const nsStyleSVGReset *svgReset = firstFrame->StyleSVGReset();
  442. nsTArray<nsSVGMaskFrame*> maskFrames = effectProperties.GetMaskFrames();
  443. aUsage.shouldGenerateMaskLayer = (maskFrames.Length() > 0);
  444. bool isOK = effectProperties.HasNoFilterOrHasValidFilter();
  445. nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame(&isOK);
  446. MOZ_ASSERT_IF(clipPathFrame,
  447. svgReset->mClipPath.GetType() == StyleShapeSourceType::URL);
  448. switch (svgReset->mClipPath.GetType()) {
  449. case StyleShapeSourceType::URL:
  450. if (clipPathFrame) {
  451. if (clipPathFrame->IsTrivial()) {
  452. aUsage.shouldApplyClipPath = true;
  453. } else {
  454. aUsage.shouldGenerateClipMaskLayer = true;
  455. }
  456. }
  457. break;
  458. case StyleShapeSourceType::Shape:
  459. case StyleShapeSourceType::Box:
  460. aUsage.shouldApplyBasicShape = true;
  461. break;
  462. case StyleShapeSourceType::None:
  463. MOZ_ASSERT(!aUsage.shouldGenerateClipMaskLayer &&
  464. !aUsage.shouldApplyClipPath && !aUsage.shouldApplyBasicShape);
  465. break;
  466. default:
  467. MOZ_ASSERT_UNREACHABLE("Unsupported clip-path type.");
  468. break;
  469. }
  470. }
  471. static IntRect
  472. ComputeClipExtsInDeviceSpace(gfxContext& aCtx)
  473. {
  474. gfxContextMatrixAutoSaveRestore matRestore(&aCtx);
  475. // Get the clip extents in device space.
  476. aCtx.SetMatrix(gfxMatrix());
  477. gfxRect clippedFrameSurfaceRect = aCtx.GetClipExtents();
  478. clippedFrameSurfaceRect.RoundOut();
  479. IntRect result;
  480. ToRect(clippedFrameSurfaceRect).ToIntRect(&result);
  481. return mozilla::gfx::Factory::CheckSurfaceSize(result.Size()) ? result
  482. : IntRect();
  483. }
  484. static already_AddRefed<gfxContext>
  485. CreateBlendTarget(gfxContext* aContext, IntPoint& aTargetOffset)
  486. {
  487. // Create a temporary context to draw to so we can blend it back with
  488. // another operator.
  489. IntRect drawRect = ComputeClipExtsInDeviceSpace(*aContext);
  490. RefPtr<DrawTarget> targetDT =
  491. aContext->GetDrawTarget()->CreateSimilarDrawTarget(drawRect.Size(),
  492. SurfaceFormat::B8G8R8A8);
  493. if (!targetDT || !targetDT->IsValid()) {
  494. return nullptr;
  495. }
  496. RefPtr<gfxContext> target = gfxContext::CreateOrNull(targetDT);
  497. MOZ_ASSERT(target); // already checked the draw target above
  498. target->SetMatrix(aContext->CurrentMatrix() *
  499. gfxMatrix::Translation(-drawRect.TopLeft()));
  500. aTargetOffset = drawRect.TopLeft();
  501. return target.forget();
  502. }
  503. static void
  504. BlendToTarget(nsIFrame* aFrame, gfxContext* aSource, gfxContext* aTarget,
  505. const IntPoint& aTargetOffset)
  506. {
  507. MOZ_ASSERT(aFrame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL);
  508. RefPtr<DrawTarget> targetDT = aTarget->GetDrawTarget();
  509. RefPtr<SourceSurface> targetSurf = targetDT->Snapshot();
  510. gfxContextAutoSaveRestore save(aSource);
  511. aSource->SetMatrix(gfxMatrix()); // This will be restored right after.
  512. RefPtr<gfxPattern> pattern = new gfxPattern(targetSurf, Matrix::Translation(aTargetOffset.x, aTargetOffset.y));
  513. aSource->SetPattern(pattern);
  514. aSource->Paint();
  515. }
  516. DrawResult
  517. nsSVGUtils::PaintFrameWithEffects(nsIFrame *aFrame,
  518. gfxContext& aContext,
  519. const gfxMatrix& aTransform,
  520. const nsIntRect *aDirtyRect)
  521. {
  522. NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() ||
  523. (aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY) ||
  524. aFrame->PresContext()->IsGlyph(),
  525. "If display lists are enabled, only painting of non-display "
  526. "SVG should take this code path");
  527. nsISVGChildFrame *svgChildFrame = do_QueryFrame(aFrame);
  528. if (!svgChildFrame)
  529. return DrawResult::SUCCESS;
  530. MaskUsage maskUsage;
  531. DetermineMaskUsage(aFrame, true, maskUsage);
  532. if (maskUsage.opacity == 0.0f) {
  533. return DrawResult::SUCCESS;
  534. }
  535. const nsIContent* content = aFrame->GetContent();
  536. if (content->IsSVGElement() &&
  537. !static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
  538. return DrawResult::SUCCESS;
  539. }
  540. if (aDirtyRect &&
  541. !(aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)) {
  542. // Here we convert aFrame's paint bounds to outer-<svg> device space,
  543. // compare it to aDirtyRect, and return early if they don't intersect.
  544. // We don't do this optimization for nondisplay SVG since nondisplay
  545. // SVG doesn't maintain bounds/overflow rects.
  546. nsRect overflowRect = aFrame->GetVisualOverflowRectRelativeToSelf();
  547. if (aFrame->IsFrameOfType(nsIFrame::eSVGGeometry) ||
  548. aFrame->IsSVGText()) {
  549. // Unlike containers, leaf frames do not include GetPosition() in
  550. // GetCanvasTM().
  551. overflowRect = overflowRect + aFrame->GetPosition();
  552. }
  553. int32_t appUnitsPerDevPx = aFrame->PresContext()->AppUnitsPerDevPixel();
  554. gfxMatrix tm = aTransform;
  555. if (aFrame->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer)) {
  556. gfx::Matrix childrenOnlyTM;
  557. if (static_cast<nsSVGContainerFrame*>(aFrame)->
  558. HasChildrenOnlyTransform(&childrenOnlyTM)) {
  559. // Undo the children-only transform:
  560. if (!childrenOnlyTM.Invert()) {
  561. return DrawResult::SUCCESS;
  562. }
  563. tm = ThebesMatrix(childrenOnlyTM) * tm;
  564. }
  565. }
  566. nsIntRect bounds = TransformFrameRectToOuterSVG(overflowRect,
  567. tm, aFrame->PresContext()).
  568. ToOutsidePixels(appUnitsPerDevPx);
  569. if (!aDirtyRect->Intersects(bounds)) {
  570. return DrawResult::SUCCESS;
  571. }
  572. }
  573. /* SVG defines the following rendering model:
  574. *
  575. * 1. Render fill
  576. * 2. Render stroke
  577. * 3. Render markers
  578. * 4. Apply filter
  579. * 5. Apply clipping, masking, group opacity
  580. *
  581. * We follow this, but perform a couple of optimizations:
  582. *
  583. * + Use cairo's clipPath when representable natively (single object
  584. * clip region).
  585. *f
  586. * + Merge opacity and masking if both used together.
  587. */
  588. /* Properties are added lazily and may have been removed by a restyle,
  589. so make sure all applicable ones are set again. */
  590. nsSVGEffects::EffectProperties effectProperties =
  591. nsSVGEffects::GetEffectProperties(aFrame);
  592. bool isOK = effectProperties.HasNoFilterOrHasValidFilter();
  593. nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame(&isOK);
  594. nsSVGMaskFrame *maskFrame = effectProperties.GetFirstMaskFrame(&isOK);
  595. if (!isOK) {
  596. // Some resource is invalid. We shouldn't paint anything.
  597. return DrawResult::SUCCESS;
  598. }
  599. // These are used if we require a temporary surface for a custom blend mode.
  600. // Clip the source context first, so that we can generate a smaller temporary
  601. // surface. (Since we will clip this context in SetupContextMatrix, a pair
  602. // of save/restore is needed.)
  603. aContext.Save();
  604. if (!(aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)) {
  605. // aFrame has a valid visual overflow rect, so clip to it before calling
  606. // PushGroup() to minimize the size of the surfaces we'll composite:
  607. gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(&aContext);
  608. aContext.Multiply(aTransform);
  609. nsRect overflowRect = aFrame->GetVisualOverflowRectRelativeToSelf();
  610. if (aFrame->IsFrameOfType(nsIFrame::eSVGGeometry) ||
  611. aFrame->IsSVGText()) {
  612. // Unlike containers, leaf frames do not include GetPosition() in
  613. // GetCanvasTM().
  614. overflowRect = overflowRect + aFrame->GetPosition();
  615. }
  616. aContext.Clip(NSRectToSnappedRect(overflowRect,
  617. aFrame->PresContext()->AppUnitsPerDevPixel(),
  618. *aContext.GetDrawTarget()));
  619. }
  620. IntPoint targetOffset;
  621. RefPtr<gfxContext> target =
  622. (aFrame->StyleEffects()->mMixBlendMode == NS_STYLE_BLEND_NORMAL)
  623. ? RefPtr<gfxContext>(&aContext).forget()
  624. : CreateBlendTarget(&aContext, targetOffset);
  625. aContext.Restore();
  626. if (!target) {
  627. return DrawResult::TEMPORARY_ERROR;
  628. }
  629. /* Check if we need to do additional operations on this child's
  630. * rendering, which necessitates rendering into another surface. */
  631. bool shouldGenerateMask = (maskUsage.opacity != 1.0f ||
  632. maskUsage.shouldGenerateClipMaskLayer ||
  633. maskUsage.shouldGenerateMaskLayer ||
  634. aFrame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL);
  635. if (shouldGenerateMask) {
  636. Matrix maskTransform;
  637. RefPtr<SourceSurface> maskSurface;
  638. if (maskUsage.shouldGenerateMaskLayer) {
  639. // Make sure we have a mask frame.
  640. if (maskFrame) {
  641. maskSurface =
  642. maskFrame->GetMaskForMaskedFrame(&aContext, aFrame, aTransform,
  643. maskUsage.opacity, &maskTransform);
  644. }
  645. if (!maskSurface) {
  646. // Entire surface is clipped out.
  647. return DrawResult::SUCCESS;
  648. }
  649. }
  650. if (maskUsage.shouldGenerateClipMaskLayer) {
  651. Matrix clippedMaskTransform;
  652. RefPtr<SourceSurface> clipMaskSurface =
  653. clipPathFrame->GetClipMask(aContext, aFrame, aTransform,
  654. &clippedMaskTransform, maskSurface,
  655. maskTransform);
  656. if (clipMaskSurface) {
  657. maskSurface = clipMaskSurface;
  658. maskTransform = clippedMaskTransform;
  659. }
  660. }
  661. // SVG mask multiply opacity into maskSurface already, so we do not bother
  662. // to apply opacity again.
  663. float opacity = maskFrame ? 1.0 : maskUsage.opacity;
  664. target->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, opacity,
  665. maskSurface, maskTransform);
  666. }
  667. /* If this frame has only a trivial clipPath, set up cairo's clipping now so
  668. * we can just do normal painting and get it clipped appropriately.
  669. */
  670. if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShape) {
  671. if (maskUsage.shouldApplyClipPath) {
  672. clipPathFrame->ApplyClipPath(aContext, aFrame, aTransform);
  673. } else {
  674. nsCSSClipPathInstance::ApplyBasicShapeClip(aContext, aFrame);
  675. }
  676. }
  677. DrawResult result = DrawResult::SUCCESS;
  678. /* Paint the child */
  679. if (effectProperties.HasValidFilter()) {
  680. nsRegion* dirtyRegion = nullptr;
  681. nsRegion tmpDirtyRegion;
  682. if (aDirtyRect) {
  683. // aDirtyRect is in outer-<svg> device pixels, but the filter code needs
  684. // it in frame space.
  685. gfxMatrix userToDeviceSpace = GetUserToCanvasTM(aFrame);
  686. if (userToDeviceSpace.IsSingular()) {
  687. return DrawResult::SUCCESS;
  688. }
  689. gfxMatrix deviceToUserSpace = userToDeviceSpace;
  690. deviceToUserSpace.Invert();
  691. gfxRect dirtyBounds = deviceToUserSpace.TransformBounds(
  692. gfxRect(aDirtyRect->x, aDirtyRect->y,
  693. aDirtyRect->width, aDirtyRect->height));
  694. tmpDirtyRegion =
  695. nsLayoutUtils::RoundGfxRectToAppRect(
  696. dirtyBounds, aFrame->PresContext()->AppUnitsPerCSSPixel()) -
  697. aFrame->GetPosition();
  698. dirtyRegion = &tmpDirtyRegion;
  699. }
  700. SVGPaintCallback paintCallback;
  701. nsFilterInstance::PaintFilteredFrame(aFrame, target->GetDrawTarget(),
  702. aTransform, &paintCallback,
  703. dirtyRegion);
  704. } else {
  705. result = svgChildFrame->PaintSVG(*target, aTransform, aDirtyRect);
  706. }
  707. if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShape) {
  708. aContext.PopClip();
  709. }
  710. if (shouldGenerateMask) {
  711. target->PopGroupAndBlend();
  712. }
  713. if (aFrame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
  714. MOZ_ASSERT(target != &aContext);
  715. BlendToTarget(aFrame, &aContext, target, targetOffset);
  716. }
  717. return result;
  718. }
  719. bool
  720. nsSVGUtils::HitTestClip(nsIFrame *aFrame, const gfxPoint &aPoint)
  721. {
  722. nsSVGEffects::EffectProperties props =
  723. nsSVGEffects::GetEffectProperties(aFrame);
  724. if (!props.mClipPath) {
  725. const nsStyleSVGReset *style = aFrame->StyleSVGReset();
  726. if (style->HasClipPath()) {
  727. return nsCSSClipPathInstance::HitTestBasicShapeClip(aFrame, aPoint);
  728. }
  729. return true;
  730. }
  731. bool isOK = true;
  732. nsSVGClipPathFrame *clipPathFrame = props.GetClipPathFrame(&isOK);
  733. if (!isOK) {
  734. // clipPath is not a valid resource, so nothing gets painted, so
  735. // hit-testing must fail.
  736. return false;
  737. }
  738. if (!clipPathFrame) {
  739. // clipPath doesn't exist, ignore it.
  740. return true;
  741. }
  742. return clipPathFrame->PointIsInsideClipPath(aFrame, aPoint);
  743. }
  744. nsIFrame *
  745. nsSVGUtils::HitTestChildren(nsSVGDisplayContainerFrame* aFrame,
  746. const gfxPoint& aPoint)
  747. {
  748. // First we transform aPoint into the coordinate space established by aFrame
  749. // for its children (e.g. take account of any 'viewBox' attribute):
  750. gfxPoint point = aPoint;
  751. if (aFrame->GetContent()->IsSVGElement()) { // must check before cast
  752. gfxMatrix m = static_cast<const nsSVGElement*>(aFrame->GetContent())->
  753. PrependLocalTransformsTo(gfxMatrix(),
  754. eChildToUserSpace);
  755. if (!m.IsIdentity()) {
  756. if (!m.Invert()) {
  757. return nullptr;
  758. }
  759. point = m.Transform(point);
  760. }
  761. }
  762. // Traverse the list in reverse order, so that if we get a hit we know that's
  763. // the topmost frame that intersects the point; then we can just return it.
  764. nsIFrame* result = nullptr;
  765. for (nsIFrame* current = aFrame->PrincipalChildList().LastChild();
  766. current;
  767. current = current->GetPrevSibling()) {
  768. nsISVGChildFrame* SVGFrame = do_QueryFrame(current);
  769. if (SVGFrame) {
  770. const nsIContent* content = current->GetContent();
  771. if (content->IsSVGElement() &&
  772. !static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
  773. continue;
  774. }
  775. // GetFrameForPoint() expects a point in its frame's SVG user space, so
  776. // we need to convert to that space:
  777. gfxPoint p = point;
  778. if (content->IsSVGElement()) { // must check before cast
  779. gfxMatrix m = static_cast<const nsSVGElement*>(content)->
  780. PrependLocalTransformsTo(gfxMatrix(),
  781. eUserSpaceToParent);
  782. if (!m.IsIdentity()) {
  783. if (!m.Invert()) {
  784. continue;
  785. }
  786. p = m.Transform(p);
  787. }
  788. }
  789. result = SVGFrame->GetFrameForPoint(p);
  790. if (result)
  791. break;
  792. }
  793. }
  794. if (result && !HitTestClip(aFrame, aPoint))
  795. result = nullptr;
  796. return result;
  797. }
  798. nsRect
  799. nsSVGUtils::GetCoveredRegion(const nsFrameList &aFrames)
  800. {
  801. nsRect rect;
  802. for (nsIFrame* kid = aFrames.FirstChild();
  803. kid;
  804. kid = kid->GetNextSibling()) {
  805. nsISVGChildFrame* child = do_QueryFrame(kid);
  806. if (child) {
  807. nsRect childRect = child->GetCoveredRegion();
  808. rect.UnionRect(rect, childRect);
  809. }
  810. }
  811. return rect;
  812. }
  813. nsRect
  814. nsSVGUtils::TransformFrameRectToOuterSVG(const nsRect& aRect,
  815. const gfxMatrix& aMatrix,
  816. nsPresContext* aPresContext)
  817. {
  818. gfxRect r(aRect.x, aRect.y, aRect.width, aRect.height);
  819. r.Scale(1.0 / nsPresContext::AppUnitsPerCSSPixel());
  820. return nsLayoutUtils::RoundGfxRectToAppRect(
  821. aMatrix.TransformBounds(r), aPresContext->AppUnitsPerDevPixel());
  822. }
  823. IntSize
  824. nsSVGUtils::ConvertToSurfaceSize(const gfxSize& aSize,
  825. bool *aResultOverflows)
  826. {
  827. IntSize surfaceSize(ClampToInt(ceil(aSize.width)), ClampToInt(ceil(aSize.height)));
  828. *aResultOverflows = surfaceSize.width != ceil(aSize.width) ||
  829. surfaceSize.height != ceil(aSize.height);
  830. if (!Factory::CheckSurfaceSize(surfaceSize)) {
  831. surfaceSize.width = std::min(NS_SVG_OFFSCREEN_MAX_DIMENSION,
  832. surfaceSize.width);
  833. surfaceSize.height = std::min(NS_SVG_OFFSCREEN_MAX_DIMENSION,
  834. surfaceSize.height);
  835. *aResultOverflows = true;
  836. }
  837. return surfaceSize;
  838. }
  839. bool
  840. nsSVGUtils::HitTestRect(const gfx::Matrix &aMatrix,
  841. float aRX, float aRY, float aRWidth, float aRHeight,
  842. float aX, float aY)
  843. {
  844. gfx::Rect rect(aRX, aRY, aRWidth, aRHeight);
  845. if (rect.IsEmpty() || aMatrix.IsSingular()) {
  846. return false;
  847. }
  848. gfx::Matrix toRectSpace = aMatrix;
  849. toRectSpace.Invert();
  850. gfx::Point p = toRectSpace.TransformPoint(gfx::Point(aX, aY));
  851. return rect.x <= p.x && p.x <= rect.XMost() &&
  852. rect.y <= p.y && p.y <= rect.YMost();
  853. }
  854. gfxRect
  855. nsSVGUtils::GetClipRectForFrame(nsIFrame *aFrame,
  856. float aX, float aY, float aWidth, float aHeight)
  857. {
  858. const nsStyleDisplay* disp = aFrame->StyleDisplay();
  859. const nsStyleEffects* effects = aFrame->StyleEffects();
  860. if (!(effects->mClipFlags & NS_STYLE_CLIP_RECT)) {
  861. NS_ASSERTION(effects->mClipFlags == NS_STYLE_CLIP_AUTO,
  862. "We don't know about this type of clip.");
  863. return gfxRect(aX, aY, aWidth, aHeight);
  864. }
  865. if (disp->mOverflowX == NS_STYLE_OVERFLOW_HIDDEN ||
  866. disp->mOverflowY == NS_STYLE_OVERFLOW_HIDDEN) {
  867. nsIntRect clipPxRect =
  868. effects->mClip.ToOutsidePixels(aFrame->PresContext()->AppUnitsPerDevPixel());
  869. gfxRect clipRect =
  870. gfxRect(clipPxRect.x, clipPxRect.y, clipPxRect.width, clipPxRect.height);
  871. if (NS_STYLE_CLIP_RIGHT_AUTO & effects->mClipFlags) {
  872. clipRect.width = aWidth - clipRect.X();
  873. }
  874. if (NS_STYLE_CLIP_BOTTOM_AUTO & effects->mClipFlags) {
  875. clipRect.height = aHeight - clipRect.Y();
  876. }
  877. if (disp->mOverflowX != NS_STYLE_OVERFLOW_HIDDEN) {
  878. clipRect.x = aX;
  879. clipRect.width = aWidth;
  880. }
  881. if (disp->mOverflowY != NS_STYLE_OVERFLOW_HIDDEN) {
  882. clipRect.y = aY;
  883. clipRect.height = aHeight;
  884. }
  885. return clipRect;
  886. }
  887. return gfxRect(aX, aY, aWidth, aHeight);
  888. }
  889. void
  890. nsSVGUtils::SetClipRect(gfxContext *aContext,
  891. const gfxMatrix &aCTM,
  892. const gfxRect &aRect)
  893. {
  894. if (aCTM.IsSingular())
  895. return;
  896. gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(aContext);
  897. aContext->Multiply(aCTM);
  898. aContext->Clip(aRect);
  899. }
  900. gfxRect
  901. nsSVGUtils::GetBBox(nsIFrame *aFrame, uint32_t aFlags)
  902. {
  903. if (aFrame->GetContent()->IsNodeOfType(nsINode::eTEXT)) {
  904. aFrame = aFrame->GetParent();
  905. }
  906. gfxRect bbox;
  907. nsISVGChildFrame *svg = do_QueryFrame(aFrame);
  908. if (svg || aFrame->IsSVGText()) {
  909. // It is possible to apply a gradient, pattern, clipping path, mask or
  910. // filter to text. When one of these facilities is applied to text
  911. // the bounding box is the entire text element in all
  912. // cases.
  913. if (aFrame->IsSVGText()) {
  914. nsIFrame* ancestor = GetFirstNonAAncestorFrame(aFrame);
  915. if (ancestor && ancestor->IsSVGText()) {
  916. while (ancestor->GetType() != nsGkAtoms::svgTextFrame) {
  917. ancestor = ancestor->GetParent();
  918. }
  919. }
  920. svg = do_QueryFrame(ancestor);
  921. }
  922. nsIContent* content = aFrame->GetContent();
  923. if (content->IsSVGElement() &&
  924. !static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
  925. return bbox;
  926. }
  927. if (aFlags == eBBoxIncludeFillGeometry) {
  928. gfxRect* prop = aFrame->GetProperty(ObjectBoundingBoxProperty());
  929. if (prop) {
  930. return *prop;
  931. }
  932. }
  933. gfxMatrix matrix;
  934. if (aFrame->GetType() == nsGkAtoms::svgForeignObjectFrame ||
  935. aFrame->GetType() == nsGkAtoms::svgUseFrame) {
  936. // The spec says getBBox "Returns the tight bounding box in *current user
  937. // space*". So we should really be doing this for all elements, but that
  938. // needs investigation to check that we won't break too much content.
  939. // NOTE: When changing this to apply to other frame types, make sure to
  940. // also update nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset.
  941. MOZ_ASSERT(content->IsSVGElement(), "bad cast");
  942. nsSVGElement *element = static_cast<nsSVGElement*>(content);
  943. matrix = element->PrependLocalTransformsTo(matrix, eChildToUserSpace);
  944. }
  945. bbox = svg->GetBBoxContribution(ToMatrix(matrix), aFlags).ToThebesRect();
  946. // Account for 'clipped'.
  947. if (aFlags & nsSVGUtils::eBBoxIncludeClipped) {
  948. gfxRect clipRect(0, 0, 0, 0);
  949. float x, y, width, height;
  950. gfxMatrix tm;
  951. gfxRect fillBBox =
  952. svg->GetBBoxContribution(ToMatrix(tm),
  953. nsSVGUtils::eBBoxIncludeFill).ToThebesRect();
  954. x = fillBBox.x;
  955. y = fillBBox.y;
  956. width = fillBBox.width;
  957. height = fillBBox.height;
  958. bool hasClip = aFrame->StyleDisplay()->IsScrollableOverflow();
  959. if (hasClip) {
  960. clipRect =
  961. nsSVGUtils::GetClipRectForFrame(aFrame, x, y, width, height);
  962. if (aFrame->GetType() == nsGkAtoms::svgForeignObjectFrame ||
  963. aFrame->GetType() == nsGkAtoms::svgUseFrame) {
  964. clipRect = matrix.TransformBounds(clipRect);
  965. }
  966. }
  967. nsSVGEffects::EffectProperties effectProperties =
  968. nsSVGEffects::GetEffectProperties(aFrame);
  969. bool isOK = true;
  970. nsSVGClipPathFrame *clipPathFrame =
  971. effectProperties.GetClipPathFrame(&isOK);
  972. if (clipPathFrame && isOK) {
  973. SVGClipPathElement *clipContent =
  974. static_cast<SVGClipPathElement*>(clipPathFrame->GetContent());
  975. if (clipContent->IsUnitsObjectBoundingBox()) {
  976. matrix.Translate(gfxPoint(x, y));
  977. matrix.Scale(width, height);
  978. } else if (aFrame->GetType() == nsGkAtoms::svgForeignObjectFrame) {
  979. matrix.Reset();
  980. }
  981. bbox =
  982. clipPathFrame->GetBBoxForClipPathFrame(bbox, matrix).ToThebesRect();
  983. if (hasClip) {
  984. bbox = bbox.Intersect(clipRect);
  985. }
  986. } else {
  987. if (!isOK) {
  988. bbox = gfxRect(0, 0, 0, 0);
  989. } else {
  990. if (hasClip) {
  991. bbox = bbox.Intersect(clipRect);
  992. }
  993. }
  994. }
  995. if (bbox.IsEmpty()) {
  996. bbox = gfxRect(0, 0, 0, 0);
  997. }
  998. }
  999. if (aFlags == eBBoxIncludeFillGeometry) {
  1000. // Obtaining the bbox for objectBoundingBox calculations is common so we
  1001. // cache the result for future calls, since calculation can be expensive:
  1002. aFrame->SetProperty(ObjectBoundingBoxProperty(), new gfxRect(bbox));
  1003. }
  1004. return bbox;
  1005. }
  1006. return nsSVGIntegrationUtils::GetSVGBBoxForNonSVGFrame(aFrame);
  1007. }
  1008. gfxPoint
  1009. nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(nsIFrame *aFrame)
  1010. {
  1011. if (!(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
  1012. // The user space for non-SVG frames is defined as the bounding box of the
  1013. // frame's border-box rects over all continuations.
  1014. return gfxPoint();
  1015. }
  1016. // Leaf frames apply their own offset inside their user space.
  1017. if (aFrame->IsFrameOfType(nsIFrame::eSVGGeometry) ||
  1018. aFrame->IsSVGText()) {
  1019. return nsLayoutUtils::RectToGfxRect(aFrame->GetRect(),
  1020. nsPresContext::AppUnitsPerCSSPixel()).TopLeft();
  1021. }
  1022. // For foreignObject frames, nsSVGUtils::GetBBox applies their local
  1023. // transform, so we need to do the same here.
  1024. if (aFrame->GetType() == nsGkAtoms::svgForeignObjectFrame ||
  1025. aFrame->GetType() == nsGkAtoms::svgUseFrame) {
  1026. gfxMatrix transform = static_cast<nsSVGElement*>(aFrame->GetContent())->
  1027. PrependLocalTransformsTo(gfxMatrix(), eChildToUserSpace);
  1028. NS_ASSERTION(!transform.HasNonTranslation(), "we're relying on this being an offset-only transform");
  1029. return transform.GetTranslation();
  1030. }
  1031. return gfxPoint();
  1032. }
  1033. static gfxRect
  1034. GetBoundingBoxRelativeRect(const nsSVGLength2 *aXYWH,
  1035. const gfxRect& aBBox)
  1036. {
  1037. return gfxRect(aBBox.x + nsSVGUtils::ObjectSpace(aBBox, &aXYWH[0]),
  1038. aBBox.y + nsSVGUtils::ObjectSpace(aBBox, &aXYWH[1]),
  1039. nsSVGUtils::ObjectSpace(aBBox, &aXYWH[2]),
  1040. nsSVGUtils::ObjectSpace(aBBox, &aXYWH[3]));
  1041. }
  1042. gfxRect
  1043. nsSVGUtils::GetRelativeRect(uint16_t aUnits, const nsSVGLength2 *aXYWH,
  1044. const gfxRect& aBBox,
  1045. const UserSpaceMetrics& aMetrics)
  1046. {
  1047. if (aUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
  1048. return GetBoundingBoxRelativeRect(aXYWH, aBBox);
  1049. }
  1050. return gfxRect(UserSpace(aMetrics, &aXYWH[0]),
  1051. UserSpace(aMetrics, &aXYWH[1]),
  1052. UserSpace(aMetrics, &aXYWH[2]),
  1053. UserSpace(aMetrics, &aXYWH[3]));
  1054. }
  1055. gfxRect
  1056. nsSVGUtils::GetRelativeRect(uint16_t aUnits, const nsSVGLength2 *aXYWH,
  1057. const gfxRect& aBBox, nsIFrame *aFrame)
  1058. {
  1059. if (aUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
  1060. return GetBoundingBoxRelativeRect(aXYWH, aBBox);
  1061. }
  1062. nsIContent* content = aFrame->GetContent();
  1063. if (content->IsSVGElement()) {
  1064. nsSVGElement* svgElement = static_cast<nsSVGElement*>(content);
  1065. return GetRelativeRect(aUnits, aXYWH, aBBox, SVGElementMetrics(svgElement));
  1066. }
  1067. return GetRelativeRect(aUnits, aXYWH, aBBox, NonSVGFrameUserSpaceMetrics(aFrame));
  1068. }
  1069. bool
  1070. nsSVGUtils::CanOptimizeOpacity(nsIFrame *aFrame)
  1071. {
  1072. if (!(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
  1073. return false;
  1074. }
  1075. nsIAtom *type = aFrame->GetType();
  1076. if (type != nsGkAtoms::svgImageFrame &&
  1077. type != nsGkAtoms::svgPathGeometryFrame) {
  1078. return false;
  1079. }
  1080. if (aFrame->StyleEffects()->HasFilters()) {
  1081. return false;
  1082. }
  1083. // XXX The SVG WG is intending to allow fill, stroke and markers on <image>
  1084. if (type == nsGkAtoms::svgImageFrame) {
  1085. return true;
  1086. }
  1087. const nsStyleSVG *style = aFrame->StyleSVG();
  1088. if (style->HasMarker()) {
  1089. return false;
  1090. }
  1091. if (!style->HasFill() || !HasStroke(aFrame)) {
  1092. return true;
  1093. }
  1094. return false;
  1095. }
  1096. gfxMatrix
  1097. nsSVGUtils::AdjustMatrixForUnits(const gfxMatrix &aMatrix,
  1098. nsSVGEnum *aUnits,
  1099. nsIFrame *aFrame)
  1100. {
  1101. if (aFrame &&
  1102. aUnits->GetAnimValue() == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
  1103. gfxRect bbox = GetBBox(aFrame);
  1104. gfxMatrix tm = aMatrix;
  1105. tm.Translate(gfxPoint(bbox.X(), bbox.Y()));
  1106. tm.Scale(bbox.Width(), bbox.Height());
  1107. return tm;
  1108. }
  1109. return aMatrix;
  1110. }
  1111. nsIFrame*
  1112. nsSVGUtils::GetFirstNonAAncestorFrame(nsIFrame* aStartFrame)
  1113. {
  1114. for (nsIFrame *ancestorFrame = aStartFrame; ancestorFrame;
  1115. ancestorFrame = ancestorFrame->GetParent()) {
  1116. if (ancestorFrame->GetType() != nsGkAtoms::svgAFrame) {
  1117. return ancestorFrame;
  1118. }
  1119. }
  1120. return nullptr;
  1121. }
  1122. bool
  1123. nsSVGUtils::GetNonScalingStrokeTransform(nsIFrame *aFrame,
  1124. gfxMatrix* aUserToOuterSVG)
  1125. {
  1126. if (aFrame->GetContent()->IsNodeOfType(nsINode::eTEXT)) {
  1127. aFrame = aFrame->GetParent();
  1128. }
  1129. if (!aFrame->StyleSVGReset()->HasNonScalingStroke()) {
  1130. return false;
  1131. }
  1132. nsIContent *content = aFrame->GetContent();
  1133. MOZ_ASSERT(content->IsSVGElement(), "bad cast");
  1134. *aUserToOuterSVG = ThebesMatrix(SVGContentUtils::GetCTM(
  1135. static_cast<nsSVGElement*>(content), true));
  1136. return !aUserToOuterSVG->IsIdentity();
  1137. }
  1138. // The logic here comes from _cairo_stroke_style_max_distance_from_path
  1139. static gfxRect
  1140. PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents,
  1141. nsIFrame* aFrame,
  1142. double aStyleExpansionFactor,
  1143. const gfxMatrix& aMatrix)
  1144. {
  1145. double style_expansion =
  1146. aStyleExpansionFactor * nsSVGUtils::GetStrokeWidth(aFrame);
  1147. gfxMatrix matrix = aMatrix;
  1148. gfxMatrix outerSVGToUser;
  1149. if (nsSVGUtils::GetNonScalingStrokeTransform(aFrame, &outerSVGToUser)) {
  1150. outerSVGToUser.Invert();
  1151. matrix.PreMultiply(outerSVGToUser);
  1152. }
  1153. double dx = style_expansion * (fabs(matrix._11) + fabs(matrix._21));
  1154. double dy = style_expansion * (fabs(matrix._22) + fabs(matrix._12));
  1155. gfxRect strokeExtents = aPathExtents;
  1156. strokeExtents.Inflate(dx, dy);
  1157. return strokeExtents;
  1158. }
  1159. /*static*/ gfxRect
  1160. nsSVGUtils::PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents,
  1161. nsTextFrame* aFrame,
  1162. const gfxMatrix& aMatrix)
  1163. {
  1164. NS_ASSERTION(aFrame->IsSVGText(), "expected an nsTextFrame for SVG text");
  1165. return ::PathExtentsToMaxStrokeExtents(aPathExtents, aFrame, 0.5, aMatrix);
  1166. }
  1167. /*static*/ gfxRect
  1168. nsSVGUtils::PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents,
  1169. nsSVGPathGeometryFrame* aFrame,
  1170. const gfxMatrix& aMatrix)
  1171. {
  1172. bool strokeMayHaveCorners =
  1173. !SVGContentUtils::ShapeTypeHasNoCorners(aFrame->GetContent());
  1174. // For a shape without corners the stroke can only extend half the stroke
  1175. // width from the path in the x/y-axis directions. For shapes with corners
  1176. // the stroke can extend by sqrt(1/2) (think 45 degree rotated rect, or line
  1177. // with stroke-linecaps="square").
  1178. double styleExpansionFactor = strokeMayHaveCorners ? M_SQRT1_2 : 0.5;
  1179. // The stroke can extend even further for paths that can be affected by
  1180. // stroke-miterlimit.
  1181. bool affectedByMiterlimit =
  1182. aFrame->GetContent()->IsAnyOfSVGElements(nsGkAtoms::path,
  1183. nsGkAtoms::polyline,
  1184. nsGkAtoms::polygon);
  1185. if (affectedByMiterlimit) {
  1186. const nsStyleSVG* style = aFrame->StyleSVG();
  1187. if (style->mStrokeLinejoin == NS_STYLE_STROKE_LINEJOIN_MITER &&
  1188. styleExpansionFactor < style->mStrokeMiterlimit / 2.0) {
  1189. styleExpansionFactor = style->mStrokeMiterlimit / 2.0;
  1190. }
  1191. }
  1192. return ::PathExtentsToMaxStrokeExtents(aPathExtents,
  1193. aFrame,
  1194. styleExpansionFactor,
  1195. aMatrix);
  1196. }
  1197. // ----------------------------------------------------------------------
  1198. /* static */ nscolor
  1199. nsSVGUtils::GetFallbackOrPaintColor(nsStyleContext *aStyleContext,
  1200. nsStyleSVGPaint nsStyleSVG::*aFillOrStroke)
  1201. {
  1202. const nsStyleSVGPaint &paint = aStyleContext->StyleSVG()->*aFillOrStroke;
  1203. nsStyleContext *styleIfVisited = aStyleContext->GetStyleIfVisited();
  1204. bool isServer = paint.Type() == eStyleSVGPaintType_Server ||
  1205. paint.Type() == eStyleSVGPaintType_ContextFill ||
  1206. paint.Type() == eStyleSVGPaintType_ContextStroke;
  1207. nscolor color = isServer ? paint.GetFallbackColor() : paint.GetColor();
  1208. if (styleIfVisited) {
  1209. const nsStyleSVGPaint &paintIfVisited =
  1210. styleIfVisited->StyleSVG()->*aFillOrStroke;
  1211. // To prevent Web content from detecting if a user has visited a URL
  1212. // (via URL loading triggered by paint servers or performance
  1213. // differences between paint servers or between a paint server and a
  1214. // color), we do not allow whether links are visited to change which
  1215. // paint server is used or switch between paint servers and simple
  1216. // colors. A :visited style may only override a simple color with
  1217. // another simple color.
  1218. if (paintIfVisited.Type() == eStyleSVGPaintType_Color &&
  1219. paint.Type() == eStyleSVGPaintType_Color) {
  1220. nscolor colors[2] = { color, paintIfVisited.GetColor() };
  1221. return nsStyleContext::CombineVisitedColors(
  1222. colors, aStyleContext->RelevantLinkVisited());
  1223. }
  1224. }
  1225. return color;
  1226. }
  1227. /* static */ void
  1228. nsSVGUtils::MakeFillPatternFor(nsIFrame* aFrame,
  1229. gfxContext* aContext,
  1230. GeneralPattern* aOutPattern,
  1231. SVGContextPaint* aContextPaint)
  1232. {
  1233. const nsStyleSVG* style = aFrame->StyleSVG();
  1234. if (style->mFill.Type() == eStyleSVGPaintType_None) {
  1235. return;
  1236. }
  1237. const float opacity = aFrame->StyleEffects()->mOpacity;
  1238. float fillOpacity = GetOpacity(style->FillOpacitySource(),
  1239. style->mFillOpacity,
  1240. aContextPaint);
  1241. if (opacity < 1.0f &&
  1242. nsSVGUtils::CanOptimizeOpacity(aFrame)) {
  1243. // Combine the group opacity into the fill opacity (we will have skipped
  1244. // creating an offscreen surface to apply the group opacity).
  1245. fillOpacity *= opacity;
  1246. }
  1247. const DrawTarget* dt = aContext->GetDrawTarget();
  1248. nsSVGPaintServerFrame *ps =
  1249. nsSVGEffects::GetPaintServer(aFrame, &nsStyleSVG::mFill,
  1250. nsSVGEffects::FillProperty());
  1251. if (ps) {
  1252. RefPtr<gfxPattern> pattern =
  1253. ps->GetPaintServerPattern(aFrame, dt, aContext->CurrentMatrix(),
  1254. &nsStyleSVG::mFill, fillOpacity);
  1255. if (pattern) {
  1256. pattern->CacheColorStops(dt);
  1257. aOutPattern->Init(*pattern->GetPattern(dt));
  1258. return;
  1259. }
  1260. }
  1261. if (aContextPaint) {
  1262. RefPtr<gfxPattern> pattern;
  1263. switch (style->mFill.Type()) {
  1264. case eStyleSVGPaintType_ContextFill:
  1265. pattern = aContextPaint->GetFillPattern(dt, fillOpacity,
  1266. aContext->CurrentMatrix());
  1267. break;
  1268. case eStyleSVGPaintType_ContextStroke:
  1269. pattern = aContextPaint->GetStrokePattern(dt, fillOpacity,
  1270. aContext->CurrentMatrix());
  1271. break;
  1272. default:
  1273. ;
  1274. }
  1275. if (pattern) {
  1276. aOutPattern->Init(*pattern->GetPattern(dt));
  1277. return;
  1278. }
  1279. }
  1280. // On failure, use the fallback colour in case we have an
  1281. // objectBoundingBox where the width or height of the object is zero.
  1282. // See http://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBox
  1283. Color color(Color::FromABGR(GetFallbackOrPaintColor(aFrame->StyleContext(),
  1284. &nsStyleSVG::mFill)));
  1285. color.a *= fillOpacity;
  1286. aOutPattern->InitColorPattern(ToDeviceColor(color));
  1287. }
  1288. /* static */ void
  1289. nsSVGUtils::MakeStrokePatternFor(nsIFrame* aFrame,
  1290. gfxContext* aContext,
  1291. GeneralPattern* aOutPattern,
  1292. SVGContextPaint* aContextPaint)
  1293. {
  1294. const nsStyleSVG* style = aFrame->StyleSVG();
  1295. if (style->mStroke.Type() == eStyleSVGPaintType_None) {
  1296. return;
  1297. }
  1298. const float opacity = aFrame->StyleEffects()->mOpacity;
  1299. float strokeOpacity = GetOpacity(style->StrokeOpacitySource(),
  1300. style->mStrokeOpacity,
  1301. aContextPaint);
  1302. if (opacity < 1.0f &&
  1303. nsSVGUtils::CanOptimizeOpacity(aFrame)) {
  1304. // Combine the group opacity into the stroke opacity (we will have skipped
  1305. // creating an offscreen surface to apply the group opacity).
  1306. strokeOpacity *= opacity;
  1307. }
  1308. const DrawTarget* dt = aContext->GetDrawTarget();
  1309. nsSVGPaintServerFrame *ps =
  1310. nsSVGEffects::GetPaintServer(aFrame, &nsStyleSVG::mStroke,
  1311. nsSVGEffects::StrokeProperty());
  1312. if (ps) {
  1313. RefPtr<gfxPattern> pattern =
  1314. ps->GetPaintServerPattern(aFrame, dt, aContext->CurrentMatrix(),
  1315. &nsStyleSVG::mStroke, strokeOpacity);
  1316. if (pattern) {
  1317. pattern->CacheColorStops(dt);
  1318. aOutPattern->Init(*pattern->GetPattern(dt));
  1319. return;
  1320. }
  1321. }
  1322. if (aContextPaint) {
  1323. RefPtr<gfxPattern> pattern;
  1324. switch (style->mStroke.Type()) {
  1325. case eStyleSVGPaintType_ContextFill:
  1326. pattern = aContextPaint->GetFillPattern(dt, strokeOpacity,
  1327. aContext->CurrentMatrix());
  1328. break;
  1329. case eStyleSVGPaintType_ContextStroke:
  1330. pattern = aContextPaint->GetStrokePattern(dt, strokeOpacity,
  1331. aContext->CurrentMatrix());
  1332. break;
  1333. default:
  1334. ;
  1335. }
  1336. if (pattern) {
  1337. aOutPattern->Init(*pattern->GetPattern(dt));
  1338. return;
  1339. }
  1340. }
  1341. // On failure, use the fallback colour in case we have an
  1342. // objectBoundingBox where the width or height of the object is zero.
  1343. // See http://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBox
  1344. Color color(Color::FromABGR(GetFallbackOrPaintColor(aFrame->StyleContext(),
  1345. &nsStyleSVG::mStroke)));
  1346. color.a *= strokeOpacity;
  1347. aOutPattern->InitColorPattern(ToDeviceColor(color));
  1348. }
  1349. /* static */ float
  1350. nsSVGUtils::GetOpacity(nsStyleSVGOpacitySource aOpacityType,
  1351. const float& aOpacity,
  1352. SVGContextPaint *aContextPaint)
  1353. {
  1354. float opacity = 1.0f;
  1355. switch (aOpacityType) {
  1356. case eStyleSVGOpacitySource_Normal:
  1357. opacity = aOpacity;
  1358. break;
  1359. case eStyleSVGOpacitySource_ContextFillOpacity:
  1360. if (aContextPaint) {
  1361. opacity = aContextPaint->GetFillOpacity();
  1362. } else {
  1363. NS_WARNING("Content used context-fill-opacity when not in a context element");
  1364. }
  1365. break;
  1366. case eStyleSVGOpacitySource_ContextStrokeOpacity:
  1367. if (aContextPaint) {
  1368. opacity = aContextPaint->GetStrokeOpacity();
  1369. } else {
  1370. NS_WARNING("Content used context-stroke-opacity when not in a context element");
  1371. }
  1372. break;
  1373. default:
  1374. NS_NOTREACHED("Unknown object opacity inheritance type for SVG glyph");
  1375. }
  1376. return opacity;
  1377. }
  1378. bool
  1379. nsSVGUtils::HasStroke(nsIFrame* aFrame, SVGContextPaint* aContextPaint)
  1380. {
  1381. const nsStyleSVG *style = aFrame->StyleSVG();
  1382. return style->HasStroke() && GetStrokeWidth(aFrame, aContextPaint) > 0;
  1383. }
  1384. float
  1385. nsSVGUtils::GetStrokeWidth(nsIFrame* aFrame, SVGContextPaint* aContextPaint)
  1386. {
  1387. const nsStyleSVG *style = aFrame->StyleSVG();
  1388. if (aContextPaint && style->StrokeWidthFromObject()) {
  1389. return aContextPaint->GetStrokeWidth();
  1390. }
  1391. nsIContent* content = aFrame->GetContent();
  1392. if (content->IsNodeOfType(nsINode::eTEXT)) {
  1393. content = content->GetParent();
  1394. }
  1395. nsSVGElement *ctx = static_cast<nsSVGElement*>(content);
  1396. return SVGContentUtils::CoordToFloat(ctx, style->mStrokeWidth);
  1397. }
  1398. static bool
  1399. GetStrokeDashData(nsIFrame* aFrame,
  1400. nsTArray<gfxFloat>& aDashes,
  1401. gfxFloat* aDashOffset,
  1402. SVGContextPaint* aContextPaint)
  1403. {
  1404. const nsStyleSVG* style = aFrame->StyleSVG();
  1405. nsIContent *content = aFrame->GetContent();
  1406. nsSVGElement *ctx = static_cast<nsSVGElement*>
  1407. (content->IsNodeOfType(nsINode::eTEXT) ?
  1408. content->GetParent() : content);
  1409. gfxFloat totalLength = 0.0;
  1410. if (aContextPaint && style->StrokeDasharrayFromObject()) {
  1411. aDashes = aContextPaint->GetStrokeDashArray();
  1412. for (uint32_t i = 0; i < aDashes.Length(); i++) {
  1413. if (aDashes[i] < 0.0) {
  1414. return false;
  1415. }
  1416. totalLength += aDashes[i];
  1417. }
  1418. } else {
  1419. uint32_t count = style->mStrokeDasharray.Length();
  1420. if (!count || !aDashes.SetLength(count, fallible)) {
  1421. return false;
  1422. }
  1423. gfxFloat pathScale = 1.0;
  1424. if (content->IsSVGElement(nsGkAtoms::path)) {
  1425. pathScale = static_cast<SVGPathElement*>(content)->
  1426. GetPathLengthScale(SVGPathElement::eForStroking);
  1427. if (pathScale <= 0) {
  1428. return false;
  1429. }
  1430. }
  1431. const nsTArray<nsStyleCoord>& dasharray = style->mStrokeDasharray;
  1432. for (uint32_t i = 0; i < count; i++) {
  1433. aDashes[i] = SVGContentUtils::CoordToFloat(ctx,
  1434. dasharray[i]) * pathScale;
  1435. if (aDashes[i] < 0.0) {
  1436. return false;
  1437. }
  1438. totalLength += aDashes[i];
  1439. }
  1440. }
  1441. if (aContextPaint && style->StrokeDashoffsetFromObject()) {
  1442. *aDashOffset = aContextPaint->GetStrokeDashOffset();
  1443. } else {
  1444. *aDashOffset = SVGContentUtils::CoordToFloat(ctx,
  1445. style->mStrokeDashoffset);
  1446. }
  1447. return (totalLength > 0.0);
  1448. }
  1449. void
  1450. nsSVGUtils::SetupCairoStrokeGeometry(nsIFrame* aFrame,
  1451. gfxContext *aContext,
  1452. SVGContextPaint* aContextPaint)
  1453. {
  1454. float width = GetStrokeWidth(aFrame, aContextPaint);
  1455. if (width <= 0)
  1456. return;
  1457. aContext->SetLineWidth(width);
  1458. // Apply any stroke-specific transform
  1459. gfxMatrix outerSVGToUser;
  1460. if (GetNonScalingStrokeTransform(aFrame, &outerSVGToUser) &&
  1461. outerSVGToUser.Invert()) {
  1462. aContext->Multiply(outerSVGToUser);
  1463. }
  1464. const nsStyleSVG* style = aFrame->StyleSVG();
  1465. switch (style->mStrokeLinecap) {
  1466. case NS_STYLE_STROKE_LINECAP_BUTT:
  1467. aContext->SetLineCap(CapStyle::BUTT);
  1468. break;
  1469. case NS_STYLE_STROKE_LINECAP_ROUND:
  1470. aContext->SetLineCap(CapStyle::ROUND);
  1471. break;
  1472. case NS_STYLE_STROKE_LINECAP_SQUARE:
  1473. aContext->SetLineCap(CapStyle::SQUARE);
  1474. break;
  1475. }
  1476. aContext->SetMiterLimit(style->mStrokeMiterlimit);
  1477. switch (style->mStrokeLinejoin) {
  1478. case NS_STYLE_STROKE_LINEJOIN_MITER:
  1479. aContext->SetLineJoin(JoinStyle::MITER_OR_BEVEL);
  1480. break;
  1481. case NS_STYLE_STROKE_LINEJOIN_ROUND:
  1482. aContext->SetLineJoin(JoinStyle::ROUND);
  1483. break;
  1484. case NS_STYLE_STROKE_LINEJOIN_BEVEL:
  1485. aContext->SetLineJoin(JoinStyle::BEVEL);
  1486. break;
  1487. }
  1488. AutoTArray<gfxFloat, 10> dashes;
  1489. gfxFloat dashOffset;
  1490. if (GetStrokeDashData(aFrame, dashes, &dashOffset, aContextPaint)) {
  1491. aContext->SetDash(dashes.Elements(), dashes.Length(), dashOffset);
  1492. }
  1493. }
  1494. uint16_t
  1495. nsSVGUtils::GetGeometryHitTestFlags(nsIFrame* aFrame)
  1496. {
  1497. uint16_t flags = 0;
  1498. switch (aFrame->StyleUserInterface()->mPointerEvents) {
  1499. case NS_STYLE_POINTER_EVENTS_NONE:
  1500. break;
  1501. case NS_STYLE_POINTER_EVENTS_AUTO:
  1502. case NS_STYLE_POINTER_EVENTS_VISIBLEPAINTED:
  1503. if (aFrame->StyleVisibility()->IsVisible()) {
  1504. if (aFrame->StyleSVG()->mFill.Type() != eStyleSVGPaintType_None)
  1505. flags |= SVG_HIT_TEST_FILL;
  1506. if (aFrame->StyleSVG()->mStroke.Type() != eStyleSVGPaintType_None)
  1507. flags |= SVG_HIT_TEST_STROKE;
  1508. if (aFrame->StyleSVG()->mStrokeOpacity > 0)
  1509. flags |= SVG_HIT_TEST_CHECK_MRECT;
  1510. }
  1511. break;
  1512. case NS_STYLE_POINTER_EVENTS_VISIBLEFILL:
  1513. if (aFrame->StyleVisibility()->IsVisible()) {
  1514. flags |= SVG_HIT_TEST_FILL;
  1515. }
  1516. break;
  1517. case NS_STYLE_POINTER_EVENTS_VISIBLESTROKE:
  1518. if (aFrame->StyleVisibility()->IsVisible()) {
  1519. flags |= SVG_HIT_TEST_STROKE;
  1520. }
  1521. break;
  1522. case NS_STYLE_POINTER_EVENTS_VISIBLE:
  1523. if (aFrame->StyleVisibility()->IsVisible()) {
  1524. flags |= SVG_HIT_TEST_FILL | SVG_HIT_TEST_STROKE;
  1525. }
  1526. break;
  1527. case NS_STYLE_POINTER_EVENTS_PAINTED:
  1528. if (aFrame->StyleSVG()->mFill.Type() != eStyleSVGPaintType_None)
  1529. flags |= SVG_HIT_TEST_FILL;
  1530. if (aFrame->StyleSVG()->mStroke.Type() != eStyleSVGPaintType_None)
  1531. flags |= SVG_HIT_TEST_STROKE;
  1532. if (aFrame->StyleSVG()->mStrokeOpacity)
  1533. flags |= SVG_HIT_TEST_CHECK_MRECT;
  1534. break;
  1535. case NS_STYLE_POINTER_EVENTS_FILL:
  1536. flags |= SVG_HIT_TEST_FILL;
  1537. break;
  1538. case NS_STYLE_POINTER_EVENTS_STROKE:
  1539. flags |= SVG_HIT_TEST_STROKE;
  1540. break;
  1541. case NS_STYLE_POINTER_EVENTS_ALL:
  1542. flags |= SVG_HIT_TEST_FILL | SVG_HIT_TEST_STROKE;
  1543. break;
  1544. default:
  1545. NS_ERROR("not reached");
  1546. break;
  1547. }
  1548. return flags;
  1549. }
  1550. bool
  1551. nsSVGUtils::PaintSVGGlyph(Element* aElement, gfxContext* aContext)
  1552. {
  1553. nsIFrame* frame = aElement->GetPrimaryFrame();
  1554. nsISVGChildFrame* svgFrame = do_QueryFrame(frame);
  1555. if (!svgFrame) {
  1556. return false;
  1557. }
  1558. gfxMatrix m;
  1559. if (frame->GetContent()->IsSVGElement()) {
  1560. // PaintSVG() expects the passed transform to be the transform to its own
  1561. // SVG user space, so we need to account for any 'transform' attribute:
  1562. m = static_cast<nsSVGElement*>(frame->GetContent())->
  1563. PrependLocalTransformsTo(gfxMatrix(), eUserSpaceToParent);
  1564. }
  1565. DrawResult result = svgFrame->PaintSVG(*aContext, m);
  1566. return (result == DrawResult::SUCCESS);
  1567. }
  1568. bool
  1569. nsSVGUtils::GetSVGGlyphExtents(Element* aElement,
  1570. const gfxMatrix& aSVGToAppSpace,
  1571. gfxRect* aResult)
  1572. {
  1573. nsIFrame* frame = aElement->GetPrimaryFrame();
  1574. nsISVGChildFrame* svgFrame = do_QueryFrame(frame);
  1575. if (!svgFrame) {
  1576. return false;
  1577. }
  1578. gfxMatrix transform(aSVGToAppSpace);
  1579. nsIContent* content = frame->GetContent();
  1580. if (content->IsSVGElement()) {
  1581. transform = static_cast<nsSVGElement*>(content)->
  1582. PrependLocalTransformsTo(aSVGToAppSpace);
  1583. }
  1584. *aResult = svgFrame->GetBBoxContribution(gfx::ToMatrix(transform),
  1585. nsSVGUtils::eBBoxIncludeFill | nsSVGUtils::eBBoxIncludeFillGeometry |
  1586. nsSVGUtils::eBBoxIncludeStroke | nsSVGUtils::eBBoxIncludeStrokeGeometry |
  1587. nsSVGUtils::eBBoxIncludeMarkers).ToThebesRect();
  1588. return true;
  1589. }
  1590. nsRect
  1591. nsSVGUtils::ToCanvasBounds(const gfxRect &aUserspaceRect,
  1592. const gfxMatrix &aToCanvas,
  1593. const nsPresContext *presContext)
  1594. {
  1595. return nsLayoutUtils::RoundGfxRectToAppRect(
  1596. aToCanvas.TransformBounds(aUserspaceRect),
  1597. presContext->AppUnitsPerDevPixel());
  1598. }