diff options
Diffstat (limited to 'gfx/thebes/gfxContext.h')
-rw-r--r-- | gfx/thebes/gfxContext.h | 694 |
1 files changed, 694 insertions, 0 deletions
diff --git a/gfx/thebes/gfxContext.h b/gfx/thebes/gfxContext.h new file mode 100644 index 000000000..49f6a7dca --- /dev/null +++ b/gfx/thebes/gfxContext.h @@ -0,0 +1,694 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * 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 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, SetColor or SetSource. + * + * 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::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; } + + /** + ** 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); + + /** + * Returns the current point in the current path. + */ + gfxPoint CurrentPoint(); + + /** + * 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. + * @param snapToPixels ? + */ + void Rectangle(const gfxRect& rect, bool snapToPixels = false); + void SnappedRectangle(const gfxRect& rect) { return Rectangle(rect, true); } + + /** + ** 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); + + /** + * Replaces the current transformation matrix with matrix. + */ + void SetMatrix(const gfxMatrix& matrix); + + /** + * Returns the current transformation matrix. + */ + gfxMatrix CurrentMatrix() 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::Color& 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::Color& aColorOut); + + /** + * 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::Color& aColor); + + /** + * Uses a surface for drawing. This is a shorthand for creating a + * pattern and setting it. + * + * @param offset from the source surface, to use only part of it. + * May need to make it negative. + */ + void SetSource(gfxASurface *surface, const gfxPoint& offset = gfxPoint(0.0, 0.0)); + + /** + * Uses a pattern for drawing. + */ + void SetPattern(gfxPattern *pattern); + + /** + * Set the color that text drawn on top of transparent pixels should be + * anti-aliased into. + */ + void SetFontSmoothingBackgroundColor(const mozilla::gfx::Color& aColor); + mozilla::gfx::Color GetFontSmoothingBackgroundColor(); + + /** + * 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(gfxFloat 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(gfxFloat *dashes, int ndash, gfxFloat 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<gfxFloat>& dashes, gfxFloat* offset) const; + // Returns 0.0 if dashing isn't enabled. + gfxFloat CurrentDashOffset() const; + + /** + * Sets the line width that's used for line drawing. + */ + void SetLineWidth(gfxFloat width); + + /** + * Returns the currently set line width. + * + * @see SetLineWidth + */ + gfxFloat 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(gfxFloat limit); + gfxFloat 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 Clip(Path* aPath); + + void PopClip(); + + /** + * This will return the current bounds of the clip region in user + * space. + */ + gfxRect GetClipExtents(); + + /** + * Whether the current clip is not a simple rectangle. + */ + bool HasComplexClip() 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; + +#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::Color Color; + typedef mozilla::gfx::StrokeOptions StrokeOptions; + typedef mozilla::gfx::Float Float; + 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) + , mBlendOpacity(0.0f) + {} + + mozilla::gfx::CompositionOp op; + Color color; + RefPtr<gfxPattern> pattern; + RefPtr<gfxASurface> sourceSurfCairo; + RefPtr<SourceSurface> sourceSurface; + mozilla::gfx::Point sourceSurfaceDeviceOffset; + Matrix surfTransform; + Matrix transform; + struct PushedClip { + RefPtr<Path> path; + Rect rect; + Matrix transform; + }; + nsTArray<PushedClip> pushedClips; + nsTArray<Float> dashPattern; + StrokeOptions strokeOptions; + RefPtr<DrawTarget> drawTarget; + mozilla::gfx::AntialiasMode aaMode; + bool patternTransformChanged; + Matrix patternTransform; + Color fontSmoothingBackgroundColor; + // This is used solely for using minimal intermediate surface size. + mozilla::gfx::Point deviceOffset; + // Support groups + mozilla::gfx::Float mBlendOpacity; + RefPtr<SourceSurface> mBlendMask; + Matrix mBlendMaskTransform; +#ifdef DEBUG + bool mWasPushedForBlendBack; +#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(); + void FillAzure(const Pattern& aPattern, mozilla::gfx::Float aOpacity); + void PushClipsToDT(mozilla::gfx::DrawTarget *aDT); + CompositionOp GetOp(); + void ChangeTransform(const mozilla::gfx::Matrix &aNewMatrix, bool aUpdatePatternTransform = true); + Rect GetAzureDeviceSpaceClipBounds(); + Matrix GetDeviceTransform() const; + Matrix GetDTTransform() const; + void PushNewDT(gfxContentType content); + + 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 gfxMatrix& Matrix() + { + MOZ_ASSERT(mContext, "mMatrix doesn't contain a useful matrix"); + return mMatrix; + } + + bool HasMatrix() const { return !!mContext; } + +private: + gfxContext *mContext; + gfxMatrix mMatrix; +}; + + +class DrawTargetAutoDisableSubpixelAntialiasing { +public: + typedef mozilla::gfx::DrawTarget DrawTarget; + + DrawTargetAutoDisableSubpixelAntialiasing(DrawTarget *aDT, bool aDisable) + { + 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 */ |