gfxContext.cpp 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257
  1. /* -*- Mode: C++; tab-width: 8; 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. #include <math.h>
  6. #include "mozilla/Alignment.h"
  7. #include "cairo.h"
  8. #include "gfxContext.h"
  9. #include "gfxMatrix.h"
  10. #include "gfxUtils.h"
  11. #include "gfxASurface.h"
  12. #include "gfxPattern.h"
  13. #include "gfxPlatform.h"
  14. #include "gfxPrefs.h"
  15. #include "GeckoProfiler.h"
  16. #include "gfx2DGlue.h"
  17. #include "mozilla/gfx/PathHelpers.h"
  18. #include "mozilla/gfx/DrawTargetTiled.h"
  19. #include <algorithm>
  20. #if XP_WIN
  21. #include "gfxWindowsPlatform.h"
  22. #include "mozilla/gfx/DeviceManagerDx.h"
  23. #endif
  24. using namespace mozilla;
  25. using namespace mozilla::gfx;
  26. UserDataKey gfxContext::sDontUseAsSourceKey;
  27. PatternFromState::operator mozilla::gfx::Pattern&()
  28. {
  29. gfxContext::AzureState &state = mContext->CurrentState();
  30. if (state.pattern) {
  31. return *state.pattern->GetPattern(mContext->mDT, state.patternTransformChanged ? &state.patternTransform : nullptr);
  32. }
  33. if (state.sourceSurface) {
  34. Matrix transform = state.surfTransform;
  35. if (state.patternTransformChanged) {
  36. Matrix mat = mContext->GetDTTransform();
  37. if (!mat.Invert()) {
  38. mPattern = new (mColorPattern.addr())
  39. ColorPattern(Color()); // transparent black to paint nothing
  40. return *mPattern;
  41. }
  42. transform = transform * state.patternTransform * mat;
  43. }
  44. mPattern = new (mSurfacePattern.addr())
  45. SurfacePattern(state.sourceSurface, ExtendMode::CLAMP, transform);
  46. return *mPattern;
  47. }
  48. mPattern = new (mColorPattern.addr())
  49. ColorPattern(state.color);
  50. return *mPattern;
  51. }
  52. gfxContext::gfxContext(DrawTarget *aTarget, const Point& aDeviceOffset)
  53. : mPathIsRect(false)
  54. , mTransformChanged(false)
  55. , mDT(aTarget)
  56. {
  57. if (!aTarget) {
  58. gfxCriticalError() << "Don't create a gfxContext without a DrawTarget";
  59. }
  60. MOZ_COUNT_CTOR(gfxContext);
  61. mStateStack.SetLength(1);
  62. CurrentState().drawTarget = mDT;
  63. CurrentState().deviceOffset = aDeviceOffset;
  64. mDT->SetTransform(GetDTTransform());
  65. }
  66. /* static */ already_AddRefed<gfxContext>
  67. gfxContext::CreateOrNull(DrawTarget* aTarget,
  68. const mozilla::gfx::Point& aDeviceOffset)
  69. {
  70. if (!aTarget || !aTarget->IsValid()) {
  71. gfxCriticalNote << "Invalid target in gfxContext::CreateOrNull " << hexa(aTarget);
  72. return nullptr;
  73. }
  74. RefPtr<gfxContext> result = new gfxContext(aTarget, aDeviceOffset);
  75. return result.forget();
  76. }
  77. /* static */ already_AddRefed<gfxContext>
  78. gfxContext::CreatePreservingTransformOrNull(DrawTarget* aTarget)
  79. {
  80. if (!aTarget || !aTarget->IsValid()) {
  81. gfxCriticalNote << "Invalid target in gfxContext::CreatePreservingTransformOrNull " << hexa(aTarget);
  82. return nullptr;
  83. }
  84. Matrix transform = aTarget->GetTransform();
  85. RefPtr<gfxContext> result = new gfxContext(aTarget);
  86. result->SetMatrix(ThebesMatrix(transform));
  87. return result.forget();
  88. }
  89. gfxContext::~gfxContext()
  90. {
  91. for (int i = mStateStack.Length() - 1; i >= 0; i--) {
  92. for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
  93. mStateStack[i].drawTarget->PopClip();
  94. }
  95. }
  96. MOZ_COUNT_DTOR(gfxContext);
  97. }
  98. void
  99. gfxContext::Save()
  100. {
  101. CurrentState().transform = mTransform;
  102. mStateStack.AppendElement(AzureState(CurrentState()));
  103. CurrentState().pushedClips.Clear();
  104. }
  105. void
  106. gfxContext::Restore()
  107. {
  108. for (unsigned int c = 0; c < CurrentState().pushedClips.Length(); c++) {
  109. mDT->PopClip();
  110. }
  111. mStateStack.RemoveElementAt(mStateStack.Length() - 1);
  112. mDT = CurrentState().drawTarget;
  113. ChangeTransform(CurrentState().transform, false);
  114. }
  115. // drawing
  116. void
  117. gfxContext::NewPath()
  118. {
  119. mPath = nullptr;
  120. mPathBuilder = nullptr;
  121. mPathIsRect = false;
  122. mTransformChanged = false;
  123. }
  124. void
  125. gfxContext::ClosePath()
  126. {
  127. EnsurePathBuilder();
  128. mPathBuilder->Close();
  129. }
  130. already_AddRefed<Path> gfxContext::GetPath()
  131. {
  132. EnsurePath();
  133. RefPtr<Path> path(mPath);
  134. return path.forget();
  135. }
  136. void gfxContext::SetPath(Path* path)
  137. {
  138. MOZ_ASSERT(path->GetBackendType() == mDT->GetBackendType() ||
  139. path->GetBackendType() == BackendType::RECORDING ||
  140. (mDT->GetBackendType() == BackendType::DIRECT2D1_1 && path->GetBackendType() == BackendType::DIRECT2D));
  141. mPath = path;
  142. mPathBuilder = nullptr;
  143. mPathIsRect = false;
  144. mTransformChanged = false;
  145. }
  146. gfxPoint
  147. gfxContext::CurrentPoint()
  148. {
  149. EnsurePathBuilder();
  150. return ThebesPoint(mPathBuilder->CurrentPoint());
  151. }
  152. void
  153. gfxContext::Fill()
  154. {
  155. Fill(PatternFromState(this));
  156. }
  157. void
  158. gfxContext::Fill(const Pattern& aPattern)
  159. {
  160. PROFILER_LABEL("gfxContext", "Fill",
  161. js::ProfileEntry::Category::GRAPHICS);
  162. FillAzure(aPattern, 1.0f);
  163. }
  164. void
  165. gfxContext::MoveTo(const gfxPoint& pt)
  166. {
  167. EnsurePathBuilder();
  168. mPathBuilder->MoveTo(ToPoint(pt));
  169. }
  170. void
  171. gfxContext::LineTo(const gfxPoint& pt)
  172. {
  173. EnsurePathBuilder();
  174. mPathBuilder->LineTo(ToPoint(pt));
  175. }
  176. void
  177. gfxContext::Line(const gfxPoint& start, const gfxPoint& end)
  178. {
  179. EnsurePathBuilder();
  180. mPathBuilder->MoveTo(ToPoint(start));
  181. mPathBuilder->LineTo(ToPoint(end));
  182. }
  183. // XXX snapToPixels is only valid when snapping for filled
  184. // rectangles and for even-width stroked rectangles.
  185. // For odd-width stroked rectangles, we need to offset x/y by
  186. // 0.5...
  187. void
  188. gfxContext::Rectangle(const gfxRect& rect, bool snapToPixels)
  189. {
  190. Rect rec = ToRect(rect);
  191. if (snapToPixels) {
  192. gfxRect newRect(rect);
  193. if (UserToDevicePixelSnapped(newRect, true)) {
  194. gfxMatrix mat = ThebesMatrix(mTransform);
  195. if (mat.Invert()) {
  196. // We need the user space rect.
  197. rec = ToRect(mat.TransformBounds(newRect));
  198. } else {
  199. rec = Rect();
  200. }
  201. }
  202. }
  203. if (!mPathBuilder && !mPathIsRect) {
  204. mPathIsRect = true;
  205. mRect = rec;
  206. return;
  207. }
  208. EnsurePathBuilder();
  209. mPathBuilder->MoveTo(rec.TopLeft());
  210. mPathBuilder->LineTo(rec.TopRight());
  211. mPathBuilder->LineTo(rec.BottomRight());
  212. mPathBuilder->LineTo(rec.BottomLeft());
  213. mPathBuilder->Close();
  214. }
  215. // transform stuff
  216. void
  217. gfxContext::Multiply(const gfxMatrix& matrix)
  218. {
  219. ChangeTransform(ToMatrix(matrix) * mTransform);
  220. }
  221. void
  222. gfxContext::SetMatrix(const gfxMatrix& matrix)
  223. {
  224. ChangeTransform(ToMatrix(matrix));
  225. }
  226. gfxMatrix
  227. gfxContext::CurrentMatrix() const
  228. {
  229. return ThebesMatrix(mTransform);
  230. }
  231. gfxPoint
  232. gfxContext::DeviceToUser(const gfxPoint& point) const
  233. {
  234. return ThebesPoint(mTransform.Inverse().TransformPoint(ToPoint(point)));
  235. }
  236. Size
  237. gfxContext::DeviceToUser(const Size& size) const
  238. {
  239. return mTransform.Inverse().TransformSize(size);
  240. }
  241. gfxRect
  242. gfxContext::DeviceToUser(const gfxRect& rect) const
  243. {
  244. return ThebesRect(mTransform.Inverse().TransformBounds(ToRect(rect)));
  245. }
  246. gfxPoint
  247. gfxContext::UserToDevice(const gfxPoint& point) const
  248. {
  249. return ThebesPoint(mTransform.TransformPoint(ToPoint(point)));
  250. }
  251. Size
  252. gfxContext::UserToDevice(const Size& size) const
  253. {
  254. const Matrix &matrix = mTransform;
  255. Size newSize;
  256. newSize.width = size.width * matrix._11 + size.height * matrix._12;
  257. newSize.height = size.width * matrix._21 + size.height * matrix._22;
  258. return newSize;
  259. }
  260. gfxRect
  261. gfxContext::UserToDevice(const gfxRect& rect) const
  262. {
  263. const Matrix &matrix = mTransform;
  264. return ThebesRect(matrix.TransformBounds(ToRect(rect)));
  265. }
  266. bool
  267. gfxContext::UserToDevicePixelSnapped(gfxRect& rect, bool ignoreScale) const
  268. {
  269. if (mDT->GetUserData(&sDisablePixelSnapping))
  270. return false;
  271. // if we're not at 1.0 scale, don't snap, unless we're
  272. // ignoring the scale. If we're not -just- a scale,
  273. // never snap.
  274. const gfxFloat epsilon = 0.0000001;
  275. #define WITHIN_E(a,b) (fabs((a)-(b)) < epsilon)
  276. Matrix mat = mTransform;
  277. if (!ignoreScale &&
  278. (!WITHIN_E(mat._11,1.0) || !WITHIN_E(mat._22,1.0) ||
  279. !WITHIN_E(mat._12,0.0) || !WITHIN_E(mat._21,0.0)))
  280. return false;
  281. #undef WITHIN_E
  282. gfxPoint p1 = UserToDevice(rect.TopLeft());
  283. gfxPoint p2 = UserToDevice(rect.TopRight());
  284. gfxPoint p3 = UserToDevice(rect.BottomRight());
  285. // Check that the rectangle is axis-aligned. For an axis-aligned rectangle,
  286. // two opposite corners define the entire rectangle. So check if
  287. // the axis-aligned rectangle with opposite corners p1 and p3
  288. // define an axis-aligned rectangle whose other corners are p2 and p4.
  289. // We actually only need to check one of p2 and p4, since an affine
  290. // transform maps parallelograms to parallelograms.
  291. if (p2 == gfxPoint(p1.x, p3.y) || p2 == gfxPoint(p3.x, p1.y)) {
  292. p1.Round();
  293. p3.Round();
  294. rect.MoveTo(gfxPoint(std::min(p1.x, p3.x), std::min(p1.y, p3.y)));
  295. rect.SizeTo(gfxSize(std::max(p1.x, p3.x) - rect.X(),
  296. std::max(p1.y, p3.y) - rect.Y()));
  297. return true;
  298. }
  299. return false;
  300. }
  301. bool
  302. gfxContext::UserToDevicePixelSnapped(gfxPoint& pt, bool ignoreScale) const
  303. {
  304. if (mDT->GetUserData(&sDisablePixelSnapping))
  305. return false;
  306. // if we're not at 1.0 scale, don't snap, unless we're
  307. // ignoring the scale. If we're not -just- a scale,
  308. // never snap.
  309. const gfxFloat epsilon = 0.0000001;
  310. #define WITHIN_E(a,b) (fabs((a)-(b)) < epsilon)
  311. Matrix mat = mTransform;
  312. if (!ignoreScale &&
  313. (!WITHIN_E(mat._11,1.0) || !WITHIN_E(mat._22,1.0) ||
  314. !WITHIN_E(mat._12,0.0) || !WITHIN_E(mat._21,0.0)))
  315. return false;
  316. #undef WITHIN_E
  317. pt = UserToDevice(pt);
  318. pt.Round();
  319. return true;
  320. }
  321. void
  322. gfxContext::SetAntialiasMode(AntialiasMode mode)
  323. {
  324. CurrentState().aaMode = mode;
  325. }
  326. AntialiasMode
  327. gfxContext::CurrentAntialiasMode() const
  328. {
  329. return CurrentState().aaMode;
  330. }
  331. void
  332. gfxContext::SetDash(gfxFloat *dashes, int ndash, gfxFloat offset)
  333. {
  334. AzureState &state = CurrentState();
  335. state.dashPattern.SetLength(ndash);
  336. for (int i = 0; i < ndash; i++) {
  337. state.dashPattern[i] = Float(dashes[i]);
  338. }
  339. state.strokeOptions.mDashLength = ndash;
  340. state.strokeOptions.mDashOffset = Float(offset);
  341. state.strokeOptions.mDashPattern = ndash ? state.dashPattern.Elements()
  342. : nullptr;
  343. }
  344. bool
  345. gfxContext::CurrentDash(FallibleTArray<gfxFloat>& dashes, gfxFloat* offset) const
  346. {
  347. const AzureState &state = CurrentState();
  348. int count = state.strokeOptions.mDashLength;
  349. if (count <= 0 || !dashes.SetLength(count, fallible)) {
  350. return false;
  351. }
  352. for (int i = 0; i < count; i++) {
  353. dashes[i] = state.dashPattern[i];
  354. }
  355. *offset = state.strokeOptions.mDashOffset;
  356. return true;
  357. }
  358. gfxFloat
  359. gfxContext::CurrentDashOffset() const
  360. {
  361. return CurrentState().strokeOptions.mDashOffset;
  362. }
  363. void
  364. gfxContext::SetLineWidth(gfxFloat width)
  365. {
  366. CurrentState().strokeOptions.mLineWidth = Float(width);
  367. }
  368. gfxFloat
  369. gfxContext::CurrentLineWidth() const
  370. {
  371. return CurrentState().strokeOptions.mLineWidth;
  372. }
  373. void
  374. gfxContext::SetOp(CompositionOp aOp)
  375. {
  376. CurrentState().op = aOp;
  377. }
  378. CompositionOp
  379. gfxContext::CurrentOp() const
  380. {
  381. return CurrentState().op;
  382. }
  383. void
  384. gfxContext::SetLineCap(CapStyle cap)
  385. {
  386. CurrentState().strokeOptions.mLineCap = cap;
  387. }
  388. CapStyle
  389. gfxContext::CurrentLineCap() const
  390. {
  391. return CurrentState().strokeOptions.mLineCap;
  392. }
  393. void
  394. gfxContext::SetLineJoin(JoinStyle join)
  395. {
  396. CurrentState().strokeOptions.mLineJoin = join;
  397. }
  398. JoinStyle
  399. gfxContext::CurrentLineJoin() const
  400. {
  401. return CurrentState().strokeOptions.mLineJoin;
  402. }
  403. void
  404. gfxContext::SetMiterLimit(gfxFloat limit)
  405. {
  406. CurrentState().strokeOptions.mMiterLimit = Float(limit);
  407. }
  408. gfxFloat
  409. gfxContext::CurrentMiterLimit() const
  410. {
  411. return CurrentState().strokeOptions.mMiterLimit;
  412. }
  413. // clipping
  414. void
  415. gfxContext::Clip(const Rect& rect)
  416. {
  417. AzureState::PushedClip clip = { nullptr, rect, mTransform };
  418. CurrentState().pushedClips.AppendElement(clip);
  419. mDT->PushClipRect(rect);
  420. NewPath();
  421. }
  422. void
  423. gfxContext::Clip(const gfxRect& rect)
  424. {
  425. Clip(ToRect(rect));
  426. }
  427. void
  428. gfxContext::Clip(Path* aPath)
  429. {
  430. mDT->PushClip(aPath);
  431. AzureState::PushedClip clip = { aPath, Rect(), mTransform };
  432. CurrentState().pushedClips.AppendElement(clip);
  433. }
  434. void
  435. gfxContext::Clip()
  436. {
  437. if (mPathIsRect) {
  438. MOZ_ASSERT(!mTransformChanged);
  439. AzureState::PushedClip clip = { nullptr, mRect, mTransform };
  440. CurrentState().pushedClips.AppendElement(clip);
  441. mDT->PushClipRect(mRect);
  442. } else {
  443. EnsurePath();
  444. mDT->PushClip(mPath);
  445. AzureState::PushedClip clip = { mPath, Rect(), mTransform };
  446. CurrentState().pushedClips.AppendElement(clip);
  447. }
  448. }
  449. void
  450. gfxContext::PopClip()
  451. {
  452. MOZ_ASSERT(CurrentState().pushedClips.Length() > 0);
  453. CurrentState().pushedClips.RemoveElementAt(CurrentState().pushedClips.Length() - 1);
  454. mDT->PopClip();
  455. }
  456. gfxRect
  457. gfxContext::GetClipExtents()
  458. {
  459. Rect rect = GetAzureDeviceSpaceClipBounds();
  460. if (rect.width == 0 || rect.height == 0) {
  461. return gfxRect(0, 0, 0, 0);
  462. }
  463. Matrix mat = mTransform;
  464. mat.Invert();
  465. rect = mat.TransformBounds(rect);
  466. return ThebesRect(rect);
  467. }
  468. bool
  469. gfxContext::HasComplexClip() const
  470. {
  471. for (int i = mStateStack.Length() - 1; i >= 0; i--) {
  472. for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
  473. const AzureState::PushedClip &clip = mStateStack[i].pushedClips[c];
  474. if (clip.path || !clip.transform.IsRectilinear()) {
  475. return true;
  476. }
  477. }
  478. }
  479. return false;
  480. }
  481. bool
  482. gfxContext::ExportClip(ClipExporter& aExporter)
  483. {
  484. for (unsigned int i = 0; i < mStateStack.Length(); i++) {
  485. for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
  486. AzureState::PushedClip &clip = mStateStack[i].pushedClips[c];
  487. gfx::Matrix transform = clip.transform;
  488. transform.PostTranslate(-GetDeviceOffset());
  489. aExporter.BeginClip(transform);
  490. if (clip.path) {
  491. clip.path->StreamToSink(&aExporter);
  492. } else {
  493. aExporter.MoveTo(clip.rect.TopLeft());
  494. aExporter.LineTo(clip.rect.TopRight());
  495. aExporter.LineTo(clip.rect.BottomRight());
  496. aExporter.LineTo(clip.rect.BottomLeft());
  497. aExporter.Close();
  498. }
  499. aExporter.EndClip();
  500. }
  501. }
  502. return true;
  503. }
  504. bool
  505. gfxContext::ClipContainsRect(const gfxRect& aRect)
  506. {
  507. // Since we always return false when the clip list contains a
  508. // non-rectangular clip or a non-rectilinear transform, our 'total' clip
  509. // is always a rectangle if we hit the end of this function.
  510. Rect clipBounds(0, 0, Float(mDT->GetSize().width), Float(mDT->GetSize().height));
  511. for (unsigned int i = 0; i < mStateStack.Length(); i++) {
  512. for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
  513. AzureState::PushedClip &clip = mStateStack[i].pushedClips[c];
  514. if (clip.path || !clip.transform.IsRectilinear()) {
  515. // Cairo behavior is we return false if the clip contains a non-
  516. // rectangle.
  517. return false;
  518. } else {
  519. Rect clipRect = mTransform.TransformBounds(clip.rect);
  520. clipBounds.IntersectRect(clipBounds, clipRect);
  521. }
  522. }
  523. }
  524. return clipBounds.Contains(ToRect(aRect));
  525. }
  526. // rendering sources
  527. void
  528. gfxContext::SetColor(const Color& aColor)
  529. {
  530. CurrentState().pattern = nullptr;
  531. CurrentState().sourceSurfCairo = nullptr;
  532. CurrentState().sourceSurface = nullptr;
  533. CurrentState().color = ToDeviceColor(aColor);
  534. }
  535. void
  536. gfxContext::SetDeviceColor(const Color& aColor)
  537. {
  538. CurrentState().pattern = nullptr;
  539. CurrentState().sourceSurfCairo = nullptr;
  540. CurrentState().sourceSurface = nullptr;
  541. CurrentState().color = aColor;
  542. }
  543. bool
  544. gfxContext::GetDeviceColor(Color& aColorOut)
  545. {
  546. if (CurrentState().sourceSurface) {
  547. return false;
  548. }
  549. if (CurrentState().pattern) {
  550. return CurrentState().pattern->GetSolidColor(aColorOut);
  551. }
  552. aColorOut = CurrentState().color;
  553. return true;
  554. }
  555. void
  556. gfxContext::SetSource(gfxASurface *surface, const gfxPoint& offset)
  557. {
  558. CurrentState().surfTransform = Matrix(1.0f, 0, 0, 1.0f, Float(offset.x), Float(offset.y));
  559. CurrentState().pattern = nullptr;
  560. CurrentState().patternTransformChanged = false;
  561. // Keep the underlying cairo surface around while we keep the
  562. // sourceSurface.
  563. CurrentState().sourceSurfCairo = surface;
  564. CurrentState().sourceSurface =
  565. gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mDT, surface);
  566. CurrentState().color = Color(0, 0, 0, 0);
  567. }
  568. void
  569. gfxContext::SetPattern(gfxPattern *pattern)
  570. {
  571. CurrentState().sourceSurfCairo = nullptr;
  572. CurrentState().sourceSurface = nullptr;
  573. CurrentState().patternTransformChanged = false;
  574. CurrentState().pattern = pattern;
  575. }
  576. already_AddRefed<gfxPattern>
  577. gfxContext::GetPattern()
  578. {
  579. RefPtr<gfxPattern> pat;
  580. AzureState &state = CurrentState();
  581. if (state.pattern) {
  582. pat = state.pattern;
  583. } else if (state.sourceSurface) {
  584. NS_ASSERTION(false, "Ugh, this isn't good.");
  585. } else {
  586. pat = new gfxPattern(state.color);
  587. }
  588. return pat.forget();
  589. }
  590. void
  591. gfxContext::SetFontSmoothingBackgroundColor(const Color& aColor)
  592. {
  593. CurrentState().fontSmoothingBackgroundColor = aColor;
  594. }
  595. Color
  596. gfxContext::GetFontSmoothingBackgroundColor()
  597. {
  598. return CurrentState().fontSmoothingBackgroundColor;
  599. }
  600. // masking
  601. void
  602. gfxContext::Mask(SourceSurface* aSurface, Float aAlpha, const Matrix& aTransform)
  603. {
  604. Matrix old = mTransform;
  605. Matrix mat = aTransform * mTransform;
  606. ChangeTransform(mat);
  607. mDT->MaskSurface(PatternFromState(this), aSurface, Point(),
  608. DrawOptions(aAlpha, CurrentState().op, CurrentState().aaMode));
  609. ChangeTransform(old);
  610. }
  611. void
  612. gfxContext::Mask(SourceSurface *surface, float alpha, const Point& offset)
  613. {
  614. // We clip here to bind to the mask surface bounds, see above.
  615. mDT->MaskSurface(PatternFromState(this),
  616. surface,
  617. offset,
  618. DrawOptions(alpha, CurrentState().op, CurrentState().aaMode));
  619. }
  620. void
  621. gfxContext::Paint(gfxFloat alpha)
  622. {
  623. PROFILER_LABEL("gfxContext", "Paint",
  624. js::ProfileEntry::Category::GRAPHICS);
  625. AzureState &state = CurrentState();
  626. if (state.sourceSurface && !state.sourceSurfCairo &&
  627. !state.patternTransformChanged)
  628. {
  629. // This is the case where a PopGroupToSource has been done and this
  630. // paint is executed without changing the transform or the source.
  631. Matrix oldMat = mDT->GetTransform();
  632. IntSize surfSize = state.sourceSurface->GetSize();
  633. mDT->SetTransform(Matrix::Translation(-state.deviceOffset.x,
  634. -state.deviceOffset.y));
  635. mDT->DrawSurface(state.sourceSurface,
  636. Rect(state.sourceSurfaceDeviceOffset, Size(surfSize.width, surfSize.height)),
  637. Rect(Point(), Size(surfSize.width, surfSize.height)),
  638. DrawSurfaceOptions(), DrawOptions(alpha, GetOp()));
  639. mDT->SetTransform(oldMat);
  640. return;
  641. }
  642. Matrix mat = mDT->GetTransform();
  643. mat.Invert();
  644. Rect paintRect = mat.TransformBounds(Rect(Point(0, 0), Size(mDT->GetSize())));
  645. mDT->FillRect(paintRect, PatternFromState(this),
  646. DrawOptions(Float(alpha), GetOp()));
  647. }
  648. void
  649. gfxContext::PushGroupForBlendBack(gfxContentType content, Float aOpacity, SourceSurface* aMask, const Matrix& aMaskTransform)
  650. {
  651. if (gfxPrefs::UseNativePushLayer()) {
  652. Save();
  653. mDT->PushLayer(content == gfxContentType::COLOR, aOpacity, aMask, aMaskTransform);
  654. } else {
  655. DrawTarget* oldDT = mDT;
  656. PushNewDT(content);
  657. if (oldDT != mDT) {
  658. PushClipsToDT(mDT);
  659. }
  660. mDT->SetTransform(GetDTTransform());
  661. CurrentState().mBlendOpacity = aOpacity;
  662. CurrentState().mBlendMask = aMask;
  663. #ifdef DEBUG
  664. CurrentState().mWasPushedForBlendBack = true;
  665. #endif
  666. CurrentState().mBlendMaskTransform = aMaskTransform;
  667. }
  668. }
  669. static gfxRect
  670. GetRoundOutDeviceClipExtents(gfxContext* aCtx)
  671. {
  672. gfxContextMatrixAutoSaveRestore save(aCtx);
  673. aCtx->SetMatrix(gfxMatrix());
  674. gfxRect r = aCtx->GetClipExtents();
  675. r.RoundOut();
  676. return r;
  677. }
  678. void
  679. gfxContext::PushGroupAndCopyBackground(gfxContentType content, Float aOpacity, SourceSurface* aMask, const Matrix& aMaskTransform)
  680. {
  681. IntRect clipExtents;
  682. if (mDT->GetFormat() != SurfaceFormat::B8G8R8X8) {
  683. gfxRect clipRect = GetRoundOutDeviceClipExtents(this);
  684. clipExtents = IntRect::Truncate(clipRect.x, clipRect.y, clipRect.width, clipRect.height);
  685. }
  686. bool pushOpaqueWithCopiedBG = (mDT->GetFormat() == SurfaceFormat::B8G8R8X8 ||
  687. mDT->GetOpaqueRect().Contains(clipExtents)) &&
  688. !mDT->GetUserData(&sDontUseAsSourceKey);
  689. if (gfxPrefs::UseNativePushLayer()) {
  690. Save();
  691. if (pushOpaqueWithCopiedBG) {
  692. mDT->PushLayer(true, aOpacity, aMask, aMaskTransform, IntRect(), true);
  693. } else {
  694. mDT->PushLayer(content == gfxContentType::COLOR, aOpacity, aMask, aMaskTransform, IntRect(), false);
  695. }
  696. } else {
  697. RefPtr<SourceSurface> source;
  698. // This snapshot can be nullptr if the DrawTarget is a cairo target that is currently
  699. // in an error state.
  700. if (pushOpaqueWithCopiedBG && (source = mDT->Snapshot())) {
  701. DrawTarget *oldDT = mDT;
  702. Point oldDeviceOffset = CurrentState().deviceOffset;
  703. PushNewDT(gfxContentType::COLOR);
  704. if (oldDT == mDT) {
  705. // Creating new DT failed.
  706. return;
  707. }
  708. CurrentState().mBlendOpacity = aOpacity;
  709. CurrentState().mBlendMask = aMask;
  710. #ifdef DEBUG
  711. CurrentState().mWasPushedForBlendBack = true;
  712. #endif
  713. CurrentState().mBlendMaskTransform = aMaskTransform;
  714. Point offset = CurrentState().deviceOffset - oldDeviceOffset;
  715. Rect surfRect(0, 0, Float(mDT->GetSize().width), Float(mDT->GetSize().height));
  716. Rect sourceRect = surfRect + offset;
  717. mDT->SetTransform(Matrix());
  718. // XXX: It's really sad that we have to do this (for performance).
  719. // Once DrawTarget gets a PushLayer API we can implement this within
  720. // DrawTargetTiled.
  721. if (source->GetType() == SurfaceType::TILED) {
  722. SnapshotTiled *sourceTiled = static_cast<SnapshotTiled*>(source.get());
  723. for (uint32_t i = 0; i < sourceTiled->mSnapshots.size(); i++) {
  724. Rect tileSourceRect = sourceRect.Intersect(Rect(sourceTiled->mOrigins[i].x,
  725. sourceTiled->mOrigins[i].y,
  726. sourceTiled->mSnapshots[i]->GetSize().width,
  727. sourceTiled->mSnapshots[i]->GetSize().height));
  728. if (tileSourceRect.IsEmpty()) {
  729. continue;
  730. }
  731. Rect tileDestRect = tileSourceRect - offset;
  732. tileSourceRect -= sourceTiled->mOrigins[i];
  733. mDT->DrawSurface(sourceTiled->mSnapshots[i], tileDestRect, tileSourceRect);
  734. }
  735. } else {
  736. mDT->DrawSurface(source, surfRect, sourceRect);
  737. }
  738. mDT->SetOpaqueRect(oldDT->GetOpaqueRect());
  739. PushClipsToDT(mDT);
  740. mDT->SetTransform(GetDTTransform());
  741. return;
  742. }
  743. DrawTarget* oldDT = mDT;
  744. PushNewDT(content);
  745. if (oldDT != mDT) {
  746. PushClipsToDT(mDT);
  747. }
  748. mDT->SetTransform(GetDTTransform());
  749. CurrentState().mBlendOpacity = aOpacity;
  750. CurrentState().mBlendMask = aMask;
  751. #ifdef DEBUG
  752. CurrentState().mWasPushedForBlendBack = true;
  753. #endif
  754. CurrentState().mBlendMaskTransform = aMaskTransform;
  755. }
  756. }
  757. void
  758. gfxContext::PopGroupAndBlend()
  759. {
  760. if (gfxPrefs::UseNativePushLayer()) {
  761. mDT->PopLayer();
  762. Restore();
  763. } else {
  764. MOZ_ASSERT(CurrentState().mWasPushedForBlendBack);
  765. Float opacity = CurrentState().mBlendOpacity;
  766. RefPtr<SourceSurface> mask = CurrentState().mBlendMask;
  767. Matrix maskTransform = CurrentState().mBlendMaskTransform;
  768. RefPtr<SourceSurface> src = mDT->Snapshot();
  769. Point deviceOffset = CurrentState().deviceOffset;
  770. Restore();
  771. CurrentState().sourceSurfCairo = nullptr;
  772. CurrentState().sourceSurface = src;
  773. CurrentState().sourceSurfaceDeviceOffset = deviceOffset;
  774. CurrentState().pattern = nullptr;
  775. CurrentState().patternTransformChanged = false;
  776. Matrix mat = mTransform;
  777. mat.Invert();
  778. mat.PreTranslate(deviceOffset.x, deviceOffset.y); // device offset translation
  779. CurrentState().surfTransform = mat;
  780. CompositionOp oldOp = GetOp();
  781. SetOp(CompositionOp::OP_OVER);
  782. if (mask) {
  783. if (!maskTransform.HasNonTranslation()) {
  784. Mask(mask, opacity, Point(maskTransform._31, maskTransform._32));
  785. } else {
  786. Mask(mask, opacity, maskTransform);
  787. }
  788. } else {
  789. Paint(opacity);
  790. }
  791. SetOp(oldOp);
  792. }
  793. }
  794. #ifdef MOZ_DUMP_PAINTING
  795. void
  796. gfxContext::WriteAsPNG(const char* aFile)
  797. {
  798. gfxUtils::WriteAsPNG(mDT, aFile);
  799. }
  800. void
  801. gfxContext::DumpAsDataURI()
  802. {
  803. gfxUtils::DumpAsDataURI(mDT);
  804. }
  805. void
  806. gfxContext::CopyAsDataURI()
  807. {
  808. gfxUtils::CopyAsDataURI(mDT);
  809. }
  810. #endif
  811. void
  812. gfxContext::EnsurePath()
  813. {
  814. if (mPathBuilder) {
  815. mPath = mPathBuilder->Finish();
  816. mPathBuilder = nullptr;
  817. }
  818. if (mPath) {
  819. if (mTransformChanged) {
  820. Matrix mat = mTransform;
  821. mat.Invert();
  822. mat = mPathTransform * mat;
  823. mPathBuilder = mPath->TransformedCopyToBuilder(mat);
  824. mPath = mPathBuilder->Finish();
  825. mPathBuilder = nullptr;
  826. mTransformChanged = false;
  827. }
  828. return;
  829. }
  830. EnsurePathBuilder();
  831. mPath = mPathBuilder->Finish();
  832. mPathBuilder = nullptr;
  833. }
  834. void
  835. gfxContext::EnsurePathBuilder()
  836. {
  837. if (mPathBuilder && !mTransformChanged) {
  838. return;
  839. }
  840. if (mPath) {
  841. if (!mTransformChanged) {
  842. mPathBuilder = mPath->CopyToBuilder();
  843. mPath = nullptr;
  844. } else {
  845. Matrix invTransform = mTransform;
  846. invTransform.Invert();
  847. Matrix toNewUS = mPathTransform * invTransform;
  848. mPathBuilder = mPath->TransformedCopyToBuilder(toNewUS);
  849. }
  850. return;
  851. }
  852. DebugOnly<PathBuilder*> oldPath = mPathBuilder.get();
  853. if (!mPathBuilder) {
  854. mPathBuilder = mDT->CreatePathBuilder(FillRule::FILL_WINDING);
  855. if (mPathIsRect) {
  856. mPathBuilder->MoveTo(mRect.TopLeft());
  857. mPathBuilder->LineTo(mRect.TopRight());
  858. mPathBuilder->LineTo(mRect.BottomRight());
  859. mPathBuilder->LineTo(mRect.BottomLeft());
  860. mPathBuilder->Close();
  861. }
  862. }
  863. if (mTransformChanged) {
  864. // This could be an else if since this should never happen when
  865. // mPathBuilder is nullptr and mPath is nullptr. But this way we can
  866. // assert if all the state is as expected.
  867. MOZ_ASSERT(oldPath);
  868. MOZ_ASSERT(!mPathIsRect);
  869. Matrix invTransform = mTransform;
  870. invTransform.Invert();
  871. Matrix toNewUS = mPathTransform * invTransform;
  872. RefPtr<Path> path = mPathBuilder->Finish();
  873. if (!path) {
  874. gfxCriticalError() << "gfxContext::EnsurePathBuilder failed in PathBuilder::Finish";
  875. }
  876. mPathBuilder = path->TransformedCopyToBuilder(toNewUS);
  877. }
  878. mPathIsRect = false;
  879. }
  880. void
  881. gfxContext::FillAzure(const Pattern& aPattern, Float aOpacity)
  882. {
  883. AzureState &state = CurrentState();
  884. CompositionOp op = GetOp();
  885. if (mPathIsRect) {
  886. MOZ_ASSERT(!mTransformChanged);
  887. if (op == CompositionOp::OP_SOURCE) {
  888. // Emulate cairo operator source which is bound by mask!
  889. mDT->ClearRect(mRect);
  890. mDT->FillRect(mRect, aPattern, DrawOptions(aOpacity));
  891. } else {
  892. mDT->FillRect(mRect, aPattern, DrawOptions(aOpacity, op, state.aaMode));
  893. }
  894. } else {
  895. EnsurePath();
  896. mDT->Fill(mPath, aPattern, DrawOptions(aOpacity, op, state.aaMode));
  897. }
  898. }
  899. void
  900. gfxContext::PushClipsToDT(DrawTarget *aDT)
  901. {
  902. // Don't need to save the old transform, we'll be setting a new one soon!
  903. // Push all clips from the bottom of the stack to the clip before ours.
  904. for (unsigned int i = 0; i < mStateStack.Length() - 1; i++) {
  905. for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
  906. aDT->SetTransform(mStateStack[i].pushedClips[c].transform * GetDeviceTransform());
  907. if (mStateStack[i].pushedClips[c].path) {
  908. aDT->PushClip(mStateStack[i].pushedClips[c].path);
  909. } else {
  910. aDT->PushClipRect(mStateStack[i].pushedClips[c].rect);
  911. }
  912. }
  913. }
  914. }
  915. CompositionOp
  916. gfxContext::GetOp()
  917. {
  918. if (CurrentState().op != CompositionOp::OP_SOURCE) {
  919. return CurrentState().op;
  920. }
  921. AzureState &state = CurrentState();
  922. if (state.pattern) {
  923. if (state.pattern->IsOpaque()) {
  924. return CompositionOp::OP_OVER;
  925. } else {
  926. return CompositionOp::OP_SOURCE;
  927. }
  928. } else if (state.sourceSurface) {
  929. if (state.sourceSurface->GetFormat() == SurfaceFormat::B8G8R8X8) {
  930. return CompositionOp::OP_OVER;
  931. } else {
  932. return CompositionOp::OP_SOURCE;
  933. }
  934. } else {
  935. if (state.color.a > 0.999) {
  936. return CompositionOp::OP_OVER;
  937. } else {
  938. return CompositionOp::OP_SOURCE;
  939. }
  940. }
  941. }
  942. /* SVG font code can change the transform after having set the pattern on the
  943. * context. When the pattern is set it is in user space, if the transform is
  944. * changed after doing so the pattern needs to be converted back into userspace.
  945. * We just store the old pattern transform here so that we only do the work
  946. * needed here if the pattern is actually used.
  947. * We need to avoid doing this when this ChangeTransform comes from a restore,
  948. * since the current pattern and the current transform are both part of the
  949. * state we know the new CurrentState()'s values are valid. But if we assume
  950. * a change they might become invalid since patternTransformChanged is part of
  951. * the state and might be false for the restored AzureState.
  952. */
  953. void
  954. gfxContext::ChangeTransform(const Matrix &aNewMatrix, bool aUpdatePatternTransform)
  955. {
  956. AzureState &state = CurrentState();
  957. if (aUpdatePatternTransform && (state.pattern || state.sourceSurface)
  958. && !state.patternTransformChanged) {
  959. state.patternTransform = GetDTTransform();
  960. state.patternTransformChanged = true;
  961. }
  962. if (mPathIsRect) {
  963. Matrix invMatrix = aNewMatrix;
  964. invMatrix.Invert();
  965. Matrix toNewUS = mTransform * invMatrix;
  966. if (toNewUS.IsRectilinear()) {
  967. mRect = toNewUS.TransformBounds(mRect);
  968. mRect.NudgeToIntegers();
  969. } else {
  970. mPathBuilder = mDT->CreatePathBuilder(FillRule::FILL_WINDING);
  971. mPathBuilder->MoveTo(toNewUS.TransformPoint(mRect.TopLeft()));
  972. mPathBuilder->LineTo(toNewUS.TransformPoint(mRect.TopRight()));
  973. mPathBuilder->LineTo(toNewUS.TransformPoint(mRect.BottomRight()));
  974. mPathBuilder->LineTo(toNewUS.TransformPoint(mRect.BottomLeft()));
  975. mPathBuilder->Close();
  976. mPathIsRect = false;
  977. }
  978. // No need to consider the transform changed now!
  979. mTransformChanged = false;
  980. } else if ((mPath || mPathBuilder) && !mTransformChanged) {
  981. mTransformChanged = true;
  982. mPathTransform = mTransform;
  983. }
  984. mTransform = aNewMatrix;
  985. mDT->SetTransform(GetDTTransform());
  986. }
  987. Rect
  988. gfxContext::GetAzureDeviceSpaceClipBounds()
  989. {
  990. Rect rect(CurrentState().deviceOffset.x, CurrentState().deviceOffset.y,
  991. Float(mDT->GetSize().width), Float(mDT->GetSize().height));
  992. for (unsigned int i = 0; i < mStateStack.Length(); i++) {
  993. for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
  994. AzureState::PushedClip &clip = mStateStack[i].pushedClips[c];
  995. if (clip.path) {
  996. Rect bounds = clip.path->GetBounds(clip.transform);
  997. rect.IntersectRect(rect, bounds);
  998. } else {
  999. rect.IntersectRect(rect, clip.transform.TransformBounds(clip.rect));
  1000. }
  1001. }
  1002. }
  1003. return rect;
  1004. }
  1005. Point
  1006. gfxContext::GetDeviceOffset() const
  1007. {
  1008. return CurrentState().deviceOffset;
  1009. }
  1010. Matrix
  1011. gfxContext::GetDeviceTransform() const
  1012. {
  1013. return Matrix::Translation(-CurrentState().deviceOffset.x,
  1014. -CurrentState().deviceOffset.y);
  1015. }
  1016. Matrix
  1017. gfxContext::GetDTTransform() const
  1018. {
  1019. Matrix mat = mTransform;
  1020. mat._31 -= CurrentState().deviceOffset.x;
  1021. mat._32 -= CurrentState().deviceOffset.y;
  1022. return mat;
  1023. }
  1024. void
  1025. gfxContext::PushNewDT(gfxContentType content)
  1026. {
  1027. Rect clipBounds = GetAzureDeviceSpaceClipBounds();
  1028. clipBounds.RoundOut();
  1029. clipBounds.width = std::max(1.0f, clipBounds.width);
  1030. clipBounds.height = std::max(1.0f, clipBounds.height);
  1031. SurfaceFormat format = gfxPlatform::GetPlatform()->Optimal2DFormatForContent(content);
  1032. RefPtr<DrawTarget> newDT =
  1033. mDT->CreateSimilarDrawTarget(IntSize(int32_t(clipBounds.width), int32_t(clipBounds.height)),
  1034. format);
  1035. if (!newDT) {
  1036. NS_WARNING("Failed to create DrawTarget of sufficient size.");
  1037. newDT = mDT->CreateSimilarDrawTarget(IntSize(64, 64), format);
  1038. if (!newDT) {
  1039. if (!gfxPlatform::GetPlatform()->DidRenderingDeviceReset()
  1040. #ifdef XP_WIN
  1041. && !(mDT->GetBackendType() == BackendType::DIRECT2D1_1 &&
  1042. !DeviceManagerDx::Get()->GetContentDevice())
  1043. #endif
  1044. ) {
  1045. // If even this fails.. we're most likely just out of memory!
  1046. NS_ABORT_OOM(BytesPerPixel(format) * 64 * 64);
  1047. }
  1048. newDT = CurrentState().drawTarget;
  1049. }
  1050. }
  1051. Save();
  1052. CurrentState().drawTarget = newDT;
  1053. CurrentState().deviceOffset = clipBounds.TopLeft();
  1054. mDT = newDT;
  1055. }