diff options
Diffstat (limited to 'gfx/thebes/gfxContext.h')
-rw-r--r-- | gfx/thebes/gfxContext.h | 690 |
1 files changed, 690 insertions, 0 deletions
diff --git a/gfx/thebes/gfxContext.h b/gfx/thebes/gfxContext.h new file mode 100644 index 0000000000..8d1db02db1 --- /dev/null +++ b/gfx/thebes/gfxContext.h @@ -0,0 +1,690 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef GFX_CONTEXT_H +#define GFX_CONTEXT_H + +#include "gfxTypes.h" + +#include "gfxASurface.h" +#include "gfxPoint.h" +#include "gfxRect.h" +#include "gfxMatrix.h" +#include "gfxPattern.h" +#include "nsTArray.h" + +#include "mozilla/gfx/2D.h" + +typedef struct _cairo cairo_t; +class GlyphBufferAzure; + +namespace mozilla { +namespace gfx { +struct RectCornerRadii; +} // namespace gfx +namespace layout { +class TextDrawTarget; +} // namespace layout +} // namespace mozilla + +class ClipExporter; + +/** + * This is the main class for doing actual drawing. It is initialized using + * a surface and can be drawn on. It manages various state information like + * a current transformation matrix (CTM), a current path, current color, + * etc. + * + * All drawing happens by creating a path and then stroking or filling it. + * The functions like Rectangle and Arc do not do any drawing themselves. + * When a path is drawn (stroked or filled), it is filled/stroked with a + * pattern set by SetPattern or SetColor. + * + * Note that the gfxContext takes coordinates in device pixels, + * as opposed to app units. + */ +class gfxContext final { + typedef mozilla::gfx::CapStyle CapStyle; + typedef mozilla::gfx::CompositionOp CompositionOp; + typedef mozilla::gfx::JoinStyle JoinStyle; + typedef mozilla::gfx::FillRule FillRule; + typedef mozilla::gfx::Float Float; + typedef mozilla::gfx::Path Path; + typedef mozilla::gfx::Pattern Pattern; + typedef mozilla::gfx::Rect Rect; + typedef mozilla::gfx::RectCornerRadii RectCornerRadii; + typedef mozilla::gfx::Size Size; + + NS_INLINE_DECL_REFCOUNTING(gfxContext) + + public: + /** + * Initialize this context from a DrawTarget. + * Strips any transform from aTarget. + * aTarget will be flushed in the gfxContext's destructor. + * If aTarget is null or invalid, nullptr is returned. The caller + * is responsible for handling this scenario as appropriate. + */ + static already_AddRefed<gfxContext> CreateOrNull( + mozilla::gfx::DrawTarget* aTarget, + const mozilla::gfx::Point& aDeviceOffset = mozilla::gfx::Point()); + + /** + * Create a new gfxContext wrapping aTarget and preserving aTarget's + * transform. Note that the transform is moved from aTarget to the resulting + * gfxContext, aTarget will no longer have its transform. + * If aTarget is null or invalid, nullptr is returned. The caller + * is responsible for handling this scenario as appropriate. + */ + static already_AddRefed<gfxContext> CreatePreservingTransformOrNull( + mozilla::gfx::DrawTarget* aTarget); + + mozilla::gfx::DrawTarget* GetDrawTarget() { return mDT; } + + /** + * Returns the DrawTarget if it's actually a TextDrawTarget. + */ + mozilla::layout::TextDrawTarget* GetTextDrawer(); + + /** + ** State + **/ + // XXX document exactly what bits are saved + void Save(); + void Restore(); + + /** + ** Paths & Drawing + **/ + + /** + * Fill the current path according to the current settings. + * + * Does not consume the current path. + */ + void Fill(); + void Fill(const Pattern& aPattern); + + /** + * Forgets the current path. + */ + void NewPath(); + + /** + * Closes the path, i.e. connects the last drawn point to the first one. + * + * Filling a path will implicitly close it. + */ + void ClosePath(); + + /** + * Returns the current path. + */ + already_AddRefed<Path> GetPath(); + + /** + * Sets the given path as the current path. + */ + void SetPath(Path* path); + + /** + * Moves the pen to a new point without drawing a line. + */ + void MoveTo(const gfxPoint& pt); + + /** + * Draws a line from the current point to pt. + * + * @see MoveTo + */ + void LineTo(const gfxPoint& pt); + + // path helpers + /** + * Draws a line from start to end. + */ + void Line(const gfxPoint& start, + const gfxPoint& end); // XXX snapToPixels option? + + /** + * Draws the rectangle given by rect. + */ + void Rectangle(const gfxRect& rect) { return Rectangle(rect, false); } + void SnappedRectangle(const gfxRect& rect) { return Rectangle(rect, true); } + + private: + void Rectangle(const gfxRect& rect, bool snapToPixels); + + public: + /** + ** Transformation Matrix manipulation + **/ + + /** + * Post-multiplies 'other' onto the current CTM, i.e. this + * matrix's transformation will take place before the previously set + * transformations. + */ + void Multiply(const gfxMatrix& other); + void Multiply(const mozilla::gfx::Matrix& other); + + /** + * Replaces the current transformation matrix with matrix. + */ + void SetMatrix(const mozilla::gfx::Matrix& matrix); + void SetMatrixDouble(const gfxMatrix& matrix); + + /** + * Returns the current transformation matrix. + */ + mozilla::gfx::Matrix CurrentMatrix() const; + gfxMatrix CurrentMatrixDouble() const; + + /** + * Converts a point from device to user coordinates using the inverse + * transformation matrix. + */ + gfxPoint DeviceToUser(const gfxPoint& point) const; + + /** + * Converts a size from device to user coordinates. This does not apply + * translation components of the matrix. + */ + Size DeviceToUser(const Size& size) const; + + /** + * Converts a rectangle from device to user coordinates; this has the + * same effect as using DeviceToUser on both the rectangle's point and + * size. + */ + gfxRect DeviceToUser(const gfxRect& rect) const; + + /** + * Converts a point from user to device coordinates using the transformation + * matrix. + */ + gfxPoint UserToDevice(const gfxPoint& point) const; + + /** + * Converts a size from user to device coordinates. This does not apply + * translation components of the matrix. + */ + Size UserToDevice(const Size& size) const; + + /** + * Converts a rectangle from user to device coordinates. The + * resulting rectangle is the minimum device-space rectangle that + * encloses the user-space rectangle given. + */ + gfxRect UserToDevice(const gfxRect& rect) const; + + /** + * Takes the given rect and tries to align it to device pixels. If + * this succeeds, the method will return true, and the rect will + * be in device coordinates (already transformed by the CTM). If it + * fails, the method will return false, and the rect will not be + * changed. + * + * If ignoreScale is true, then snapping will take place even if + * the CTM has a scale applied. Snapping never takes place if + * there is a rotation in the CTM. + */ + bool UserToDevicePixelSnapped(gfxRect& rect, bool ignoreScale = false) const; + + /** + * Takes the given point and tries to align it to device pixels. If + * this succeeds, the method will return true, and the point will + * be in device coordinates (already transformed by the CTM). If it + * fails, the method will return false, and the point will not be + * changed. + * + * If ignoreScale is true, then snapping will take place even if + * the CTM has a scale applied. Snapping never takes place if + * there is a rotation in the CTM. + */ + bool UserToDevicePixelSnapped(gfxPoint& pt, bool ignoreScale = false) const; + + /** + ** Painting sources + **/ + + /** + * Set a solid color to use for drawing. This color is in the device color + * space and is not transformed. + */ + void SetDeviceColor(const mozilla::gfx::DeviceColor& aColor); + + /** + * Gets the current color. It's returned in the device color space. + * returns false if there is something other than a color + * set as the current source (pattern, surface, etc) + */ + bool GetDeviceColor(mozilla::gfx::DeviceColor& aColorOut); + + /** + * Returns true if color is neither opaque nor transparent (i.e. alpha is not + * 0 or 1), and false otherwise. If true, aColorOut is set on output. + */ + bool HasNonOpaqueNonTransparentColor(mozilla::gfx::DeviceColor& aColorOut) { + return GetDeviceColor(aColorOut) && 0.f < aColorOut.a && aColorOut.a < 1.f; + } + + /** + * Set a solid color in the sRGB color space to use for drawing. + * If CMS is not enabled, the color is treated as a device-space color + * and this call is identical to SetDeviceColor(). + */ + void SetColor(const mozilla::gfx::sRGBColor& aColor); + + /** + * Uses a pattern for drawing. + */ + void SetPattern(gfxPattern* pattern); + + /** + * Get the source pattern (solid color, normal pattern, surface, etc) + */ + already_AddRefed<gfxPattern> GetPattern(); + + /** + ** Painting + **/ + /** + * Paints the current source surface/pattern everywhere in the current + * clip region. + */ + void Paint(Float alpha = 1.0); + + /** + ** Painting with a Mask + **/ + /** + * Like Paint, except that it only draws the source where pattern is + * non-transparent. + */ + void Mask(mozilla::gfx::SourceSurface* aSurface, mozilla::gfx::Float aAlpha, + const mozilla::gfx::Matrix& aTransform); + void Mask(mozilla::gfx::SourceSurface* aSurface, + const mozilla::gfx::Matrix& aTransform) { + Mask(aSurface, 1.0f, aTransform); + } + void Mask(mozilla::gfx::SourceSurface* surface, float alpha = 1.0f, + const mozilla::gfx::Point& offset = mozilla::gfx::Point()); + + /** + ** Line Properties + **/ + + void SetDash(const Float* dashes, int ndash, Float offset); + // Return true if dashing is set, false if it's not enabled or the + // context is in an error state. |offset| can be nullptr to mean + // "don't care". + bool CurrentDash(FallibleTArray<Float>& dashes, Float* offset) const; + + /** + * Sets the line width that's used for line drawing. + */ + void SetLineWidth(Float width); + + /** + * Returns the currently set line width. + * + * @see SetLineWidth + */ + Float CurrentLineWidth() const; + + /** + * Sets the line caps, i.e. how line endings are drawn. + */ + void SetLineCap(CapStyle cap); + CapStyle CurrentLineCap() const; + + /** + * Sets the line join, i.e. how the connection between two lines is + * drawn. + */ + void SetLineJoin(JoinStyle join); + JoinStyle CurrentLineJoin() const; + + void SetMiterLimit(Float limit); + Float CurrentMiterLimit() const; + + /** + * Sets the operator used for all further drawing. The operator affects + * how drawing something will modify the destination. For example, the + * OVER operator will do alpha blending of source and destination, while + * SOURCE will replace the destination with the source. + */ + void SetOp(CompositionOp op); + CompositionOp CurrentOp() const; + + void SetAntialiasMode(mozilla::gfx::AntialiasMode mode); + mozilla::gfx::AntialiasMode CurrentAntialiasMode() const; + + /** + ** Clipping + **/ + + /** + * Clips all further drawing to the current path. + * This does not consume the current path. + */ + void Clip(); + + /** + * Helper functions that will create a rect path and call Clip(). + * Any current path will be destroyed by these functions! + */ + void Clip(const Rect& rect); + void Clip(const gfxRect& rect); // will clip to a rect + void SnappedClip(const gfxRect& rect); // snap rect and clip to the result + void Clip(Path* aPath); + + void PopClip(); + + enum ClipExtentsSpace { + eUserSpace = 0, + eDeviceSpace = 1, + }; + + /** + * According to aSpace, this function will return the current bounds of + * the clip region in user space or device space. + */ + gfxRect GetClipExtents(ClipExtentsSpace aSpace = eUserSpace) const; + + /** + * Returns true if the given rectangle is fully contained in the current clip. + * This is conservative; it may return false even when the given rectangle is + * fully contained by the current clip. + */ + bool ClipContainsRect(const gfxRect& aRect); + + /** + * Exports the current clip using the provided exporter. + */ + bool ExportClip(ClipExporter& aExporter); + + /** + * Groups + */ + void PushGroupForBlendBack( + gfxContentType content, mozilla::gfx::Float aOpacity = 1.0f, + mozilla::gfx::SourceSurface* aMask = nullptr, + const mozilla::gfx::Matrix& aMaskTransform = mozilla::gfx::Matrix()); + + /** + * Like PushGroupForBlendBack, but if the current surface is + * gfxContentType::COLOR and content is gfxContentType::COLOR_ALPHA, makes the + * pushed surface gfxContentType::COLOR instead and copies the contents of the + * current surface to the pushed surface. This is good for pushing opacity + * groups, since blending the group back to the current surface with some + * alpha applied will give the correct results and using an opaque pushed + * surface gives better quality and performance. + */ + void PushGroupAndCopyBackground( + gfxContentType content = gfxContentType::COLOR, + mozilla::gfx::Float aOpacity = 1.0f, + mozilla::gfx::SourceSurface* aMask = nullptr, + const mozilla::gfx::Matrix& aMaskTransform = mozilla::gfx::Matrix()); + void PopGroupAndBlend(); + + mozilla::gfx::Point GetDeviceOffset() const; + void SetDeviceOffset(const mozilla::gfx::Point& aOffset); + +#ifdef MOZ_DUMP_PAINTING + /** + * Debug functions to encode the current surface as a PNG and export it. + */ + + /** + * Writes a binary PNG file. + */ + void WriteAsPNG(const char* aFile); + + /** + * Write as a PNG encoded Data URL to stdout. + */ + void DumpAsDataURI(); + + /** + * Copy a PNG encoded Data URL to the clipboard. + */ + void CopyAsDataURI(); +#endif + + static mozilla::gfx::UserDataKey sDontUseAsSourceKey; + + private: + /** + * Initialize this context from a DrawTarget. + * Strips any transform from aTarget. + * aTarget will be flushed in the gfxContext's destructor. Use the static + * ContextForDrawTargetNoTransform() when you want this behavior, as that + * version deals with null DrawTarget better. + */ + explicit gfxContext( + mozilla::gfx::DrawTarget* aTarget, + const mozilla::gfx::Point& aDeviceOffset = mozilla::gfx::Point()); + ~gfxContext(); + + friend class PatternFromState; + friend class GlyphBufferAzure; + + typedef mozilla::gfx::Matrix Matrix; + typedef mozilla::gfx::DrawTarget DrawTarget; + typedef mozilla::gfx::sRGBColor sRGBColor; + typedef mozilla::gfx::DeviceColor DeviceColor; + typedef mozilla::gfx::StrokeOptions StrokeOptions; + typedef mozilla::gfx::PathBuilder PathBuilder; + typedef mozilla::gfx::SourceSurface SourceSurface; + + struct AzureState { + AzureState() + : op(mozilla::gfx::CompositionOp::OP_OVER), + color(0, 0, 0, 1.0f), + aaMode(mozilla::gfx::AntialiasMode::SUBPIXEL), + patternTransformChanged(false) +#ifdef DEBUG + , + mContentChanged(false) +#endif + { + } + + mozilla::gfx::CompositionOp op; + DeviceColor color; + RefPtr<gfxPattern> pattern; + Matrix transform; + struct PushedClip { + RefPtr<Path> path; + Rect rect; + Matrix transform; + }; + CopyableTArray<PushedClip> pushedClips; + CopyableTArray<Float> dashPattern; + StrokeOptions strokeOptions; + RefPtr<DrawTarget> drawTarget; + mozilla::gfx::AntialiasMode aaMode; + bool patternTransformChanged; + Matrix patternTransform; + DeviceColor fontSmoothingBackgroundColor; + // This is used solely for using minimal intermediate surface size. + mozilla::gfx::Point deviceOffset; +#ifdef DEBUG + // Whether the content of this AzureState changed after construction. + bool mContentChanged; +#endif + }; + + // This ensures mPath contains a valid path (in user space!) + void EnsurePath(); + // This ensures mPathBuilder contains a valid PathBuilder (in user space!) + void EnsurePathBuilder(); + CompositionOp GetOp(); + void ChangeTransform(const mozilla::gfx::Matrix& aNewMatrix, + bool aUpdatePatternTransform = true); + Rect GetAzureDeviceSpaceClipBounds() const; + Matrix GetDTTransform() const; + + bool mPathIsRect; + bool mTransformChanged; + Matrix mPathTransform; + Rect mRect; + RefPtr<PathBuilder> mPathBuilder; + RefPtr<Path> mPath; + Matrix mTransform; + nsTArray<AzureState> mStateStack; + + AzureState& CurrentState() { return mStateStack[mStateStack.Length() - 1]; } + const AzureState& CurrentState() const { + return mStateStack[mStateStack.Length() - 1]; + } + + RefPtr<DrawTarget> mDT; +}; + +/** + * Sentry helper class for functions with multiple return points that need to + * call Save() on a gfxContext and have Restore() called automatically on the + * gfxContext before they return. + */ +class gfxContextAutoSaveRestore { + public: + gfxContextAutoSaveRestore() : mContext(nullptr) {} + + explicit gfxContextAutoSaveRestore(gfxContext* aContext) + : mContext(aContext) { + mContext->Save(); + } + + ~gfxContextAutoSaveRestore() { Restore(); } + + void SetContext(gfxContext* aContext) { + NS_ASSERTION(!mContext, "Not going to call Restore() on some context!!!"); + mContext = aContext; + mContext->Save(); + } + + void EnsureSaved(gfxContext* aContext) { + MOZ_ASSERT(!mContext || mContext == aContext, "wrong context"); + if (!mContext) { + mContext = aContext; + mContext->Save(); + } + } + + void Restore() { + if (mContext) { + mContext->Restore(); + mContext = nullptr; + } + } + + private: + gfxContext* mContext; +}; + +/** + * Sentry helper class for functions with multiple return points that need to + * back up the current matrix of a context and have it automatically restored + * before they return. + */ +class gfxContextMatrixAutoSaveRestore { + public: + gfxContextMatrixAutoSaveRestore() : mContext(nullptr) {} + + explicit gfxContextMatrixAutoSaveRestore(gfxContext* aContext) + : mContext(aContext), mMatrix(aContext->CurrentMatrix()) {} + + ~gfxContextMatrixAutoSaveRestore() { + if (mContext) { + mContext->SetMatrix(mMatrix); + } + } + + void SetContext(gfxContext* aContext) { + NS_ASSERTION(!mContext, "Not going to restore the matrix on some context!"); + mContext = aContext; + mMatrix = aContext->CurrentMatrix(); + } + + void Restore() { + if (mContext) { + mContext->SetMatrix(mMatrix); + mContext = nullptr; + } + } + + const mozilla::gfx::Matrix& Matrix() { + MOZ_ASSERT(mContext, "mMatrix doesn't contain a useful matrix"); + return mMatrix; + } + + bool HasMatrix() const { return !!mContext; } + + private: + gfxContext* mContext; + mozilla::gfx::Matrix mMatrix; +}; + +class DrawTargetAutoDisableSubpixelAntialiasing { + public: + typedef mozilla::gfx::DrawTarget DrawTarget; + + DrawTargetAutoDisableSubpixelAntialiasing(DrawTarget* aDT, bool aDisable) + : mSubpixelAntialiasingEnabled(false) { + if (aDisable) { + mDT = aDT; + mSubpixelAntialiasingEnabled = mDT->GetPermitSubpixelAA(); + mDT->SetPermitSubpixelAA(false); + } + } + ~DrawTargetAutoDisableSubpixelAntialiasing() { + if (mDT) { + mDT->SetPermitSubpixelAA(mSubpixelAntialiasingEnabled); + } + } + + private: + RefPtr<DrawTarget> mDT; + bool mSubpixelAntialiasingEnabled; +}; + +/* This class lives on the stack and allows gfxContext users to easily, and + * performantly get a gfx::Pattern to use for drawing in their current context. + */ +class PatternFromState { + public: + explicit PatternFromState(gfxContext* aContext) + : mContext(aContext), mPattern(nullptr) {} + ~PatternFromState() { + if (mPattern) { + mPattern->~Pattern(); + } + } + + operator mozilla::gfx::Pattern&(); + + private: + union { + mozilla::AlignedStorage2<mozilla::gfx::ColorPattern> mColorPattern; + mozilla::AlignedStorage2<mozilla::gfx::SurfacePattern> mSurfacePattern; + }; + + gfxContext* mContext; + mozilla::gfx::Pattern* mPattern; +}; + +/* This interface should be implemented to handle exporting the clip from a + * context. + */ +class ClipExporter : public mozilla::gfx::PathSink { + public: + virtual void BeginClip(const mozilla::gfx::Matrix& aMatrix) = 0; + virtual void EndClip() = 0; +}; + +#endif /* GFX_CONTEXT_H */ |