1 /* -*- Mode: C++; tab-width: 20; 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/. */
10 #include "gfxPattern.h"
14 #include "mozilla/EnumSet.h"
15 #include "mozilla/gfx/2D.h"
17 typedef struct _cairo cairo_t
;
18 class GlyphBufferAzure
;
22 struct RectCornerRadii
;
27 } // namespace mozilla
31 /* This class lives on the stack and allows gfxContext users to easily, and
32 * performantly get a gfx::Pattern to use for drawing in their current context.
34 class PatternFromState
{
36 explicit PatternFromState(const gfxContext
* aContext
)
37 : mContext(aContext
), mPattern(nullptr) {}
44 operator mozilla::gfx::Pattern
&();
47 mozilla::AlignedStorage2
<mozilla::gfx::ColorPattern
> mColorPattern
;
49 const gfxContext
* mContext
;
50 mozilla::gfx::Pattern
* mPattern
;
54 * This is the main class for doing actual drawing. It is initialized using
55 * a surface and can be drawn on. It manages various state information like
56 * a current transformation matrix (CTM), a current path, current color,
59 * All drawing happens by creating a path and then stroking or filling it.
60 * The functions like Rectangle and Arc do not do any drawing themselves.
61 * When a path is drawn (stroked or filled), it is filled/stroked with a
62 * pattern set by SetPattern or SetColor.
64 * Note that the gfxContext takes coordinates in device pixels,
65 * as opposed to app units.
67 class gfxContext final
{
69 # define CURRENTSTATE_CHANGED() mAzureState.mContentChanged = true;
71 # define CURRENTSTATE_CHANGED()
74 typedef mozilla::gfx::BackendType BackendType
;
75 typedef mozilla::gfx::CapStyle CapStyle
;
76 typedef mozilla::gfx::CompositionOp CompositionOp
;
77 typedef mozilla::gfx::DeviceColor DeviceColor
;
78 typedef mozilla::gfx::DrawOptions DrawOptions
;
79 typedef mozilla::gfx::DrawTarget DrawTarget
;
80 typedef mozilla::gfx::JoinStyle JoinStyle
;
81 typedef mozilla::gfx::FillRule FillRule
;
82 typedef mozilla::gfx::Float Float
;
83 typedef mozilla::gfx::Matrix Matrix
;
84 typedef mozilla::gfx::Path Path
;
85 typedef mozilla::gfx::Pattern Pattern
;
86 typedef mozilla::gfx::Point Point
;
87 typedef mozilla::gfx::Rect Rect
;
88 typedef mozilla::gfx::RectCornerRadii RectCornerRadii
;
89 typedef mozilla::gfx::Size Size
;
93 * Initialize this context from a DrawTarget, which must be non-null.
94 * Strips any transform from aTarget, unless aPreserveTransform is true.
95 * aTarget will be flushed in the gfxContext's destructor.
98 explicit gfxContext(DrawTarget
* aTarget
, const Point
& aDeviceOffset
= Point())
100 mAzureState
.deviceOffset
= aDeviceOffset
;
101 mDT
->SetTransform(GetDTTransform());
105 gfxContext(DrawTarget
* aTarget
, bool aPreserveTransform
) : mDT(aTarget
) {
106 if (aPreserveTransform
) {
107 SetMatrix(aTarget
->GetTransform());
109 mDT
->SetTransform(GetDTTransform());
116 * Initialize this context from a DrawTarget.
117 * Strips any transform from aTarget.
118 * aTarget will be flushed in the gfxContext's destructor.
119 * If aTarget is null or invalid, nullptr is returned. The caller
120 * is responsible for handling this scenario as appropriate.
122 static mozilla::UniquePtr
<gfxContext
> CreateOrNull(DrawTarget
* aTarget
);
124 DrawTarget
* GetDrawTarget() const { return mDT
; }
127 * Returns the DrawTarget if it's actually a TextDrawTarget.
129 mozilla::layout::TextDrawTarget
* GetTextDrawer() const;
134 // XXX document exactly what bits are saved
143 * Fill the current path according to the current settings.
145 * Does not consume the current path.
147 void Fill() { Fill(PatternFromState(this)); }
148 void Fill(const Pattern
& aPattern
);
151 * Forgets the current path.
155 mPathBuilder
= nullptr;
157 mTransformChanged
= false;
161 * Returns the current path.
163 already_AddRefed
<Path
> GetPath() {
165 RefPtr
<Path
> path(mPath
);
166 return path
.forget();
170 * Sets the given path as the current path.
172 void SetPath(Path
* path
) {
173 MOZ_ASSERT(path
->GetBackendType() == mDT
->GetBackendType() ||
174 path
->GetBackendType() == BackendType::RECORDING
||
175 (mDT
->GetBackendType() == BackendType::DIRECT2D1_1
&&
176 path
->GetBackendType() == BackendType::DIRECT2D
));
178 mPathBuilder
= nullptr;
180 mTransformChanged
= false;
184 * Draws the rectangle given by rect.
186 void Rectangle(const gfxRect
& rect
) { return Rectangle(rect
, false); }
187 void SnappedRectangle(const gfxRect
& rect
) { return Rectangle(rect
, true); }
190 void Rectangle(const gfxRect
& rect
, bool snapToPixels
);
194 ** Transformation Matrix manipulation
198 * Post-multiplies 'other' onto the current CTM, i.e. this
199 * matrix's transformation will take place before the previously set
202 void Multiply(const gfxMatrix
& aMatrix
) { Multiply(ToMatrix(aMatrix
)); }
203 void Multiply(const Matrix
& aOther
) {
204 CURRENTSTATE_CHANGED()
205 ChangeTransform(aOther
* mAzureState
.transform
);
209 * Replaces the current transformation matrix with matrix.
211 void SetMatrix(const Matrix
& aMatrix
) {
212 CURRENTSTATE_CHANGED()
213 ChangeTransform(aMatrix
);
215 void SetMatrixDouble(const gfxMatrix
& aMatrix
) {
216 SetMatrix(ToMatrix(aMatrix
));
219 void SetCrossProcessPaintScale(float aScale
) {
220 MOZ_ASSERT(mCrossProcessPaintScale
== 1.0f
,
221 "Should only be initialized once");
222 mCrossProcessPaintScale
= aScale
;
225 float GetCrossProcessPaintScale() const { return mCrossProcessPaintScale
; }
228 * Returns the current transformation matrix.
230 Matrix
CurrentMatrix() const { return mAzureState
.transform
; }
231 gfxMatrix
CurrentMatrixDouble() const {
232 return ThebesMatrix(CurrentMatrix());
236 * Converts a point from device to user coordinates using the inverse
237 * transformation matrix.
239 gfxPoint
DeviceToUser(const gfxPoint
& aPoint
) const {
241 mAzureState
.transform
.Inverse().TransformPoint(ToPoint(aPoint
)));
245 * Converts a size from device to user coordinates. This does not apply
246 * translation components of the matrix.
248 Size
DeviceToUser(const Size
& aSize
) const {
249 return mAzureState
.transform
.Inverse().TransformSize(aSize
);
253 * Converts a rectangle from device to user coordinates; this has the
254 * same effect as using DeviceToUser on both the rectangle's point and
257 gfxRect
DeviceToUser(const gfxRect
& aRect
) const {
259 mAzureState
.transform
.Inverse().TransformBounds(ToRect(aRect
)));
263 * Converts a point from user to device coordinates using the transformation
266 gfxPoint
UserToDevice(const gfxPoint
& aPoint
) const {
267 return ThebesPoint(mAzureState
.transform
.TransformPoint(ToPoint(aPoint
)));
271 * Converts a size from user to device coordinates. This does not apply
272 * translation components of the matrix.
274 Size
UserToDevice(const Size
& aSize
) const {
275 const auto& mtx
= mAzureState
.transform
;
276 return Size(aSize
.width
* mtx
._11
+ aSize
.height
* mtx
._12
,
277 aSize
.width
* mtx
._21
+ aSize
.height
* mtx
._22
);
281 * Converts a rectangle from user to device coordinates. The
282 * resulting rectangle is the minimum device-space rectangle that
283 * encloses the user-space rectangle given.
285 gfxRect
UserToDevice(const gfxRect
& rect
) const {
286 return ThebesRect(mAzureState
.transform
.TransformBounds(ToRect(rect
)));
290 * Takes the given rect and tries to align it to device pixels. If
291 * this succeeds, the method will return true, and the rect will
292 * be in device coordinates (already transformed by the CTM). If it
293 * fails, the method will return false, and the rect will not be
296 * aOptions parameter:
297 * If IgnoreScale is set, then snapping will take place even if the CTM
298 * has a scale applied. Snapping never takes place if there is a rotation
301 * If PrioritizeSize is set, the rect's dimensions will first be snapped
302 * and then its position aligned to device pixels, rather than snapping
303 * the position of each edge independently.
305 enum class SnapOption
: uint8_t {
309 using SnapOptions
= mozilla::EnumSet
<SnapOption
>;
310 bool UserToDevicePixelSnapped(gfxRect
& rect
, SnapOptions aOptions
= {}) const;
313 * Takes the given point and tries to align it to device pixels. If
314 * this succeeds, the method will return true, and the point will
315 * be in device coordinates (already transformed by the CTM). If it
316 * fails, the method will return false, and the point will not be
319 * If ignoreScale is true, then snapping will take place even if
320 * the CTM has a scale applied. Snapping never takes place if
321 * there is a rotation in the CTM.
323 bool UserToDevicePixelSnapped(gfxPoint
& pt
, bool ignoreScale
= false) const;
330 * Set a solid color to use for drawing. This color is in the device color
331 * space and is not transformed.
333 void SetDeviceColor(const DeviceColor
& aColor
) {
334 CURRENTSTATE_CHANGED()
335 mAzureState
.pattern
= nullptr;
336 mAzureState
.color
= aColor
;
340 * Gets the current color. It's returned in the device color space.
341 * returns false if there is something other than a color
342 * set as the current source (pattern, surface, etc)
344 bool GetDeviceColor(DeviceColor
& aColorOut
) const;
347 * Returns true if color is neither opaque nor transparent (i.e. alpha is not
348 * 0 or 1), and false otherwise. If true, aColorOut is set on output.
350 bool HasNonOpaqueNonTransparentColor(DeviceColor
& aColorOut
) const {
351 return GetDeviceColor(aColorOut
) && 0.f
< aColorOut
.a
&& aColorOut
.a
< 1.f
;
355 * Set a solid color in the sRGB color space to use for drawing.
356 * If CMS is not enabled, the color is treated as a device-space color
357 * and this call is identical to SetDeviceColor().
359 void SetColor(const mozilla::gfx::sRGBColor
& aColor
) {
360 CURRENTSTATE_CHANGED()
361 mAzureState
.pattern
= nullptr;
362 mAzureState
.color
= ToDeviceColor(aColor
);
366 * Uses a pattern for drawing.
368 void SetPattern(gfxPattern
* pattern
) {
369 CURRENTSTATE_CHANGED()
370 mAzureState
.patternTransformChanged
= false;
371 mAzureState
.pattern
= pattern
;
375 * Get the source pattern (solid color, normal pattern, surface, etc)
377 already_AddRefed
<gfxPattern
> GetPattern() const;
383 * Paints the current source surface/pattern everywhere in the current
386 void Paint(Float alpha
= 1.0) const;
392 // Set the dash pattern, applying devPxScale to convert passed-in lengths
393 // to device pixels (used by the SVGUtils::SetupStrokeGeometry caller,
394 // which has the desired dash pattern in CSS px).
395 void SetDash(const Float
* dashes
, int ndash
, Float offset
, Float devPxScale
);
397 // Return true if dashing is set, false if it's not enabled or the
398 // context is in an error state. |offset| can be nullptr to mean
400 bool CurrentDash(FallibleTArray
<Float
>& dashes
, Float
* offset
) const;
403 * Sets the line width that's used for line drawing.
405 void SetLineWidth(Float width
) {
406 CURRENTSTATE_CHANGED()
407 mAzureState
.strokeOptions
.mLineWidth
= width
;
411 * Returns the currently set line width.
415 Float
CurrentLineWidth() const {
416 return mAzureState
.strokeOptions
.mLineWidth
;
420 * Sets the line caps, i.e. how line endings are drawn.
422 void SetLineCap(CapStyle cap
) {
423 CURRENTSTATE_CHANGED()
424 mAzureState
.strokeOptions
.mLineCap
= cap
;
426 CapStyle
CurrentLineCap() const { return mAzureState
.strokeOptions
.mLineCap
; }
429 * Sets the line join, i.e. how the connection between two lines is
432 void SetLineJoin(JoinStyle join
) {
433 CURRENTSTATE_CHANGED()
434 mAzureState
.strokeOptions
.mLineJoin
= join
;
436 JoinStyle
CurrentLineJoin() const {
437 return mAzureState
.strokeOptions
.mLineJoin
;
440 void SetMiterLimit(Float limit
) {
441 CURRENTSTATE_CHANGED()
442 mAzureState
.strokeOptions
.mMiterLimit
= limit
;
444 Float
CurrentMiterLimit() const {
445 return mAzureState
.strokeOptions
.mMiterLimit
;
449 * Sets the operator used for all further drawing. The operator affects
450 * how drawing something will modify the destination. For example, the
451 * OVER operator will do alpha blending of source and destination, while
452 * SOURCE will replace the destination with the source.
454 void SetOp(CompositionOp aOp
) {
455 CURRENTSTATE_CHANGED()
456 mAzureState
.op
= aOp
;
458 CompositionOp
CurrentOp() const { return mAzureState
.op
; }
460 void SetAntialiasMode(mozilla::gfx::AntialiasMode aMode
) {
461 CURRENTSTATE_CHANGED()
462 mAzureState
.aaMode
= aMode
;
464 mozilla::gfx::AntialiasMode
CurrentAntialiasMode() const {
465 return mAzureState
.aaMode
;
473 * Clips all further drawing to the current path.
474 * This does not consume the current path.
479 * Helper functions that will create a rect path and call Clip().
480 * Any current path will be destroyed by these functions!
482 void Clip(const gfxRect
& aRect
) { Clip(ToRect(aRect
)); }
483 void Clip(const Rect
& rect
); // will clip to a rect
484 void SnappedClip(const gfxRect
& rect
); // snap rect and clip to the result
485 void Clip(Path
* aPath
);
488 MOZ_ASSERT(!mAzureState
.pushedClips
.IsEmpty());
489 mAzureState
.pushedClips
.RemoveLastElement();
493 enum ClipExtentsSpace
{
499 * According to aSpace, this function will return the current bounds of
500 * the clip region in user space or device space.
502 gfxRect
GetClipExtents(ClipExtentsSpace aSpace
= eUserSpace
) const;
505 * Exports the current clip using the provided exporter.
507 bool ExportClip(ClipExporter
& aExporter
) const;
512 void PushGroupForBlendBack(gfxContentType content
, Float aOpacity
= 1.0f
,
513 mozilla::gfx::SourceSurface
* aMask
= nullptr,
514 const Matrix
& aMaskTransform
= Matrix()) const {
515 mDT
->PushLayer(content
== gfxContentType::COLOR
, aOpacity
, aMask
,
519 void PopGroupAndBlend() const { mDT
->PopLayer(); }
521 Point
GetDeviceOffset() const { return mAzureState
.deviceOffset
; }
522 void SetDeviceOffset(const Point
& aOffset
) {
523 mAzureState
.deviceOffset
= aOffset
;
526 #ifdef MOZ_DUMP_PAINTING
528 * Debug functions to encode the current surface as a PNG and export it.
532 * Writes a binary PNG file.
534 void WriteAsPNG(const char* aFile
);
537 * Write as a PNG encoded Data URL to stdout.
539 void DumpAsDataURI();
542 * Copy a PNG encoded Data URL to the clipboard.
544 void CopyAsDataURI();
548 friend class PatternFromState
;
549 friend class GlyphBufferAzure
;
551 typedef mozilla::gfx::sRGBColor sRGBColor
;
552 typedef mozilla::gfx::StrokeOptions StrokeOptions
;
553 typedef mozilla::gfx::PathBuilder PathBuilder
;
554 typedef mozilla::gfx::SourceSurface SourceSurface
;
558 : op(CompositionOp::OP_OVER
),
559 color(0, 0, 0, 1.0f
),
560 aaMode(mozilla::gfx::AntialiasMode::SUBPIXEL
),
561 patternTransformChanged(false)
564 mContentChanged(false)
571 RefPtr
<gfxPattern
> pattern
;
578 CopyableTArray
<PushedClip
> pushedClips
;
579 CopyableTArray
<Float
> dashPattern
;
580 StrokeOptions strokeOptions
;
581 mozilla::gfx::AntialiasMode aaMode
;
582 bool patternTransformChanged
;
583 Matrix patternTransform
;
584 // This is used solely for using minimal intermediate surface size.
587 // Whether the content of this AzureState changed after construction.
588 bool mContentChanged
;
592 // This ensures mPath contains a valid path (in user space!)
594 // This ensures mPathBuilder contains a valid PathBuilder (in user space!)
595 void EnsurePathBuilder();
596 CompositionOp
GetOp() const;
597 void ChangeTransform(const Matrix
& aNewMatrix
,
598 bool aUpdatePatternTransform
= true);
599 Rect
GetAzureDeviceSpaceClipBounds() const;
600 Matrix
GetDTTransform() const {
601 Matrix mat
= mAzureState
.transform
;
602 mat
.PostTranslate(-mAzureState
.deviceOffset
);
606 bool mPathIsRect
= false;
607 bool mTransformChanged
= false;
608 Matrix mPathTransform
;
610 RefPtr
<PathBuilder
> mPathBuilder
;
612 AzureState mAzureState
;
613 nsTArray
<AzureState
> mSavedStates
;
615 // Iterate over all clips in the saved and current states, calling aLambda
616 // with each of them.
617 template <typename F
>
618 void ForAllClips(F
&& aLambda
) const;
620 const AzureState
& CurrentState() const { return mAzureState
; }
622 RefPtr
<DrawTarget
> const mDT
;
623 float mCrossProcessPaintScale
= 1.0f
;
626 # undef CURRENTSTATE_CHANGED
631 * Sentry helper class for functions with multiple return points that need to
632 * call Save() on a gfxContext and have Restore() called automatically on the
633 * gfxContext before they return.
635 class MOZ_STACK_CLASS gfxContextAutoSaveRestore final
{
637 gfxContextAutoSaveRestore() : mContext(nullptr) {}
639 explicit gfxContextAutoSaveRestore(gfxContext
* aContext
)
640 : mContext(aContext
) {
644 ~gfxContextAutoSaveRestore() { Restore(); }
646 void SetContext(gfxContext
* aContext
) {
647 MOZ_ASSERT(!mContext
, "no context?");
652 void EnsureSaved(gfxContext
* aContext
) {
653 MOZ_ASSERT(!mContext
|| mContext
== aContext
, "wrong context");
668 gfxContext
* mContext
;
672 * Sentry helper class for functions with multiple return points that need to
673 * back up the current matrix of a context and have it automatically restored
674 * before they return.
676 class MOZ_STACK_CLASS gfxContextMatrixAutoSaveRestore final
{
678 gfxContextMatrixAutoSaveRestore() : mContext(nullptr) {}
680 explicit gfxContextMatrixAutoSaveRestore(gfxContext
* aContext
)
681 : mContext(aContext
), mMatrix(aContext
->CurrentMatrix()) {}
683 ~gfxContextMatrixAutoSaveRestore() {
685 mContext
->SetMatrix(mMatrix
);
689 void SetContext(gfxContext
* aContext
) {
690 NS_ASSERTION(!mContext
, "Not going to restore the matrix on some context!");
692 mMatrix
= aContext
->CurrentMatrix();
697 mContext
->SetMatrix(mMatrix
);
702 const mozilla::gfx::Matrix
& Matrix() {
703 MOZ_ASSERT(mContext
, "mMatrix doesn't contain a useful matrix");
707 bool HasMatrix() const { return !!mContext
; }
710 gfxContext
* mContext
;
711 mozilla::gfx::Matrix mMatrix
;
714 class MOZ_STACK_CLASS gfxGroupForBlendAutoSaveRestore final
{
716 using Float
= mozilla::gfx::Float
;
717 using Matrix
= mozilla::gfx::Matrix
;
719 explicit gfxGroupForBlendAutoSaveRestore(gfxContext
* aContext
)
720 : mContext(aContext
) {}
722 ~gfxGroupForBlendAutoSaveRestore() {
724 mContext
->PopGroupAndBlend();
728 void PushGroupForBlendBack(gfxContentType aContent
, Float aOpacity
= 1.0f
,
729 mozilla::gfx::SourceSurface
* aMask
= nullptr,
730 const Matrix
& aMaskTransform
= Matrix()) {
731 MOZ_ASSERT(!mPushedGroup
, "Already called PushGroupForBlendBack once");
732 mContext
->PushGroupForBlendBack(aContent
, aOpacity
, aMask
, aMaskTransform
);
737 gfxContext
* mContext
;
738 bool mPushedGroup
= false;
741 class MOZ_STACK_CLASS gfxClipAutoSaveRestore final
{
743 using Rect
= mozilla::gfx::Rect
;
745 explicit gfxClipAutoSaveRestore(gfxContext
* aContext
) : mContext(aContext
) {}
747 void Clip(const gfxRect
& aRect
) { Clip(ToRect(aRect
)); }
749 void Clip(const Rect
& aRect
) {
750 MOZ_ASSERT(!mClipped
, "Already called Clip once");
751 mContext
->Clip(aRect
);
755 void TransformedClip(const gfxMatrix
& aTransform
, const gfxRect
& aRect
) {
756 MOZ_ASSERT(!mClipped
, "Already called Clip once");
757 if (aTransform
.IsSingular()) {
760 gfxContextMatrixAutoSaveRestore
matrixAutoSaveRestore(mContext
);
761 mContext
->Multiply(aTransform
);
762 mContext
->Clip(aRect
);
766 ~gfxClipAutoSaveRestore() {
773 gfxContext
* mContext
;
774 bool mClipped
= false;
777 class MOZ_STACK_CLASS DrawTargetAutoDisableSubpixelAntialiasing final
{
779 typedef mozilla::gfx::DrawTarget DrawTarget
;
781 DrawTargetAutoDisableSubpixelAntialiasing(DrawTarget
* aDT
, bool aDisable
)
782 : mSubpixelAntialiasingEnabled(false) {
785 mSubpixelAntialiasingEnabled
= mDT
->GetPermitSubpixelAA();
786 mDT
->SetPermitSubpixelAA(false);
789 ~DrawTargetAutoDisableSubpixelAntialiasing() {
791 mDT
->SetPermitSubpixelAA(mSubpixelAntialiasingEnabled
);
796 RefPtr
<DrawTarget
> mDT
;
797 bool mSubpixelAntialiasingEnabled
;
800 /* This interface should be implemented to handle exporting the clip from a
803 class ClipExporter
: public mozilla::gfx::PathSink
{
805 virtual void BeginClip(const mozilla::gfx::Matrix
& aMatrix
) = 0;
806 virtual void EndClip() = 0;
809 #endif /* GFX_CONTEXT_H */