michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* utility functions for drawing borders and backgrounds */ michael@0: michael@0: #ifndef nsCSSRendering_h___ michael@0: #define nsCSSRendering_h___ michael@0: michael@0: #include "gfxBlur.h" michael@0: #include "gfxContext.h" michael@0: #include "nsLayoutUtils.h" michael@0: #include "nsStyleStruct.h" michael@0: #include "nsIFrame.h" michael@0: michael@0: class nsStyleContext; michael@0: class nsPresContext; michael@0: class nsRenderingContext; michael@0: michael@0: namespace mozilla { michael@0: michael@0: namespace layers { michael@0: class ImageContainer; michael@0: } michael@0: michael@0: // A CSSSizeOrRatio represents a (possibly partially specified) size for use michael@0: // in computing image sizes. Either or both of the width and height might be michael@0: // given. A ratio of width to height may also be given. If we at least two michael@0: // of these then we can compute a concrete size, that is a width and height. michael@0: struct CSSSizeOrRatio michael@0: { michael@0: CSSSizeOrRatio() michael@0: : mRatio(0, 0) michael@0: , mHasWidth(false) michael@0: , mHasHeight(false) {} michael@0: michael@0: bool CanComputeConcreteSize() const michael@0: { michael@0: return mHasWidth + mHasHeight + HasRatio() >= 2; michael@0: } michael@0: bool IsConcrete() const { return mHasWidth && mHasHeight; } michael@0: bool HasRatio() const { return mRatio.width > 0 && mRatio.height > 0; } michael@0: bool IsEmpty() const michael@0: { michael@0: return (mHasWidth && mWidth <= 0) || michael@0: (mHasHeight && mHeight <= 0) || michael@0: mRatio.width <= 0 || mRatio.height <= 0; michael@0: } michael@0: michael@0: // CanComputeConcreteSize must return true when ComputeConcreteSize is michael@0: // called. michael@0: nsSize ComputeConcreteSize() const; michael@0: michael@0: void SetWidth(nscoord aWidth) michael@0: { michael@0: mWidth = aWidth; michael@0: mHasWidth = true; michael@0: if (mHasHeight) { michael@0: mRatio = nsSize(mWidth, mHeight); michael@0: } michael@0: } michael@0: void SetHeight(nscoord aHeight) michael@0: { michael@0: mHeight = aHeight; michael@0: mHasHeight = true; michael@0: if (mHasWidth) { michael@0: mRatio = nsSize(mWidth, mHeight); michael@0: } michael@0: } michael@0: void SetSize(const nsSize& aSize) michael@0: { michael@0: mWidth = aSize.width; michael@0: mHeight = aSize.height; michael@0: mHasWidth = true; michael@0: mHasHeight = true; michael@0: mRatio = aSize; michael@0: } michael@0: void SetRatio(const nsSize& aRatio) michael@0: { michael@0: MOZ_ASSERT(!mHasWidth || !mHasHeight, michael@0: "Probably shouldn't be setting a ratio if we have a concrete size"); michael@0: mRatio = aRatio; michael@0: } michael@0: michael@0: nsSize mRatio; michael@0: nscoord mWidth; michael@0: nscoord mHeight; michael@0: bool mHasWidth; michael@0: bool mHasHeight; michael@0: }; michael@0: michael@0: } michael@0: michael@0: /** michael@0: * This is a small wrapper class to encapsulate image drawing that can draw an michael@0: * nsStyleImage image, which may internally be a real image, a sub image, or a michael@0: * CSS gradient. michael@0: * michael@0: * @note Always call the member functions in the order of PrepareImage(), michael@0: * SetSize(), and Draw*(). michael@0: */ michael@0: class nsImageRenderer { michael@0: public: michael@0: typedef mozilla::layers::LayerManager LayerManager; michael@0: typedef mozilla::layers::ImageContainer ImageContainer; michael@0: michael@0: enum { michael@0: FLAG_SYNC_DECODE_IMAGES = 0x01, michael@0: FLAG_PAINTING_TO_WINDOW = 0x02 michael@0: }; michael@0: enum FitType michael@0: { michael@0: CONTAIN, michael@0: COVER michael@0: }; michael@0: michael@0: nsImageRenderer(nsIFrame* aForFrame, const nsStyleImage* aImage, uint32_t aFlags); michael@0: ~nsImageRenderer(); michael@0: /** michael@0: * Populates member variables to get ready for rendering. michael@0: * @return true iff the image is ready, and there is at least a pixel to michael@0: * draw. michael@0: */ michael@0: bool PrepareImage(); michael@0: michael@0: /** michael@0: * The three Compute*Size functions correspond to the sizing algorthms and michael@0: * definitions from the CSS Image Values and Replaced Content spec. See michael@0: * http://dev.w3.org/csswg/css-images-3/#sizing . michael@0: */ michael@0: michael@0: /** michael@0: * Compute the intrinsic size of the image as defined in the CSS Image Values michael@0: * spec. The intrinsic size is the unscaled size which the image would ideally michael@0: * like to be in app units. michael@0: */ michael@0: mozilla::CSSSizeOrRatio ComputeIntrinsicSize(); michael@0: michael@0: /** michael@0: * Compute the size of the rendered image using either the 'cover' or michael@0: * 'contain' constraints (aFitType). michael@0: * aIntrinsicRatio may be an invalid ratio, that is one or both of its michael@0: * dimensions can be less than or equal to zero. michael@0: */ michael@0: static nsSize ComputeConstrainedSize(const nsSize& aConstrainingSize, michael@0: const nsSize& aIntrinsicRatio, michael@0: FitType aFitType); michael@0: /** michael@0: * Compute the size of the rendered image (the concrete size) where no cover/ michael@0: * contain constraints are given. The 'default algorithm' from the CSS Image michael@0: * Values spec. michael@0: */ michael@0: static nsSize ComputeConcreteSize(const mozilla::CSSSizeOrRatio& aSpecifiedSize, michael@0: const mozilla::CSSSizeOrRatio& aIntrinsicSize, michael@0: const nsSize& aDefaultSize); michael@0: michael@0: /** michael@0: * Set this image's preferred size. This will be its intrinsic size where michael@0: * specified and the default size where it is not. Used as the unscaled size michael@0: * when rendering the image. michael@0: */ michael@0: void SetPreferredSize(const mozilla::CSSSizeOrRatio& aIntrinsicSize, michael@0: const nsSize& aDefaultSize); michael@0: michael@0: /** michael@0: * Draws the image to the target rendering context. michael@0: * aSrc is a rect on the source image which will be mapped to aDest. michael@0: * @see nsLayoutUtils::DrawImage() for other parameters. michael@0: */ michael@0: void Draw(nsPresContext* aPresContext, michael@0: nsRenderingContext& aRenderingContext, michael@0: const nsRect& aDirtyRect, michael@0: const nsRect& aFill, michael@0: const nsRect& aDest, michael@0: const mozilla::CSSIntRect& aSrc); michael@0: /** michael@0: * Draws the image to the target rendering context using background-specific michael@0: * arguments. michael@0: * @see nsLayoutUtils::DrawImage() for parameters. michael@0: */ michael@0: void DrawBackground(nsPresContext* aPresContext, michael@0: nsRenderingContext& aRenderingContext, michael@0: const nsRect& aDest, michael@0: const nsRect& aFill, michael@0: const nsPoint& aAnchor, michael@0: const nsRect& aDirty); michael@0: michael@0: /** michael@0: * Draw the image to a single component of a border-image style rendering. michael@0: * aFill The destination rect to be drawn into michael@0: * aSrc is the part of the image to be rendered into a tile (aUnitSize in michael@0: * aFill), if aSrc and the dest tile are different sizes, the image will be michael@0: * scaled to map aSrc onto the dest tile. michael@0: * aHFill and aVFill are the repeat patterns for the component - michael@0: * NS_STYLE_BORDER_IMAGE_REPEAT_* michael@0: * aUnitSize The scaled size of a single source rect (in destination coords) michael@0: * aIndex identifies the component: 0 1 2 michael@0: * 3 4 5 michael@0: * 6 7 8 michael@0: */ michael@0: void michael@0: DrawBorderImageComponent(nsPresContext* aPresContext, michael@0: nsRenderingContext& aRenderingContext, michael@0: const nsRect& aDirtyRect, michael@0: const nsRect& aFill, michael@0: const mozilla::CSSIntRect& aSrc, michael@0: uint8_t aHFill, michael@0: uint8_t aVFill, michael@0: const nsSize& aUnitSize, michael@0: uint8_t aIndex); michael@0: michael@0: bool IsRasterImage(); michael@0: bool IsAnimatedImage(); michael@0: already_AddRefed GetContainer(LayerManager* aManager); michael@0: michael@0: bool IsReady() { return mIsReady; } michael@0: michael@0: private: michael@0: /** michael@0: * Helper method for creating a gfxDrawable from mPaintServerFrame or michael@0: * mImageElementSurface. michael@0: * Requires mType is eStyleImageType_Element. michael@0: * Returns null if we cannot create the drawable. michael@0: */ michael@0: already_AddRefed DrawableForElement(const nsRect& aImageRect, michael@0: nsRenderingContext& aRenderingContext); michael@0: michael@0: nsIFrame* mForFrame; michael@0: const nsStyleImage* mImage; michael@0: nsStyleImageType mType; michael@0: nsCOMPtr mImageContainer; michael@0: nsRefPtr mGradientData; michael@0: nsIFrame* mPaintServerFrame; michael@0: nsLayoutUtils::SurfaceFromElementResult mImageElementSurface; michael@0: bool mIsReady; michael@0: nsSize mSize; // unscaled size of the image, in app units michael@0: uint32_t mFlags; michael@0: }; michael@0: michael@0: /** michael@0: * A struct representing all the information needed to paint a background michael@0: * image to some target, taking into account all CSS background-* properties. michael@0: * See PrepareBackgroundLayer. michael@0: */ michael@0: struct nsBackgroundLayerState { michael@0: /** michael@0: * @param aFlags some combination of nsCSSRendering::PAINTBG_* flags michael@0: */ michael@0: nsBackgroundLayerState(nsIFrame* aForFrame, const nsStyleImage* aImage, uint32_t aFlags) michael@0: : mImageRenderer(aForFrame, aImage, aFlags), mCompositingOp(gfxContext::OPERATOR_OVER) {} michael@0: michael@0: /** michael@0: * The nsImageRenderer that will be used to draw the background. michael@0: */ michael@0: nsImageRenderer mImageRenderer; michael@0: /** michael@0: * A rectangle that one copy of the image tile is mapped onto. Same michael@0: * coordinate system as aBorderArea/aBGClipRect passed into michael@0: * PrepareBackgroundLayer. michael@0: */ michael@0: nsRect mDestArea; michael@0: /** michael@0: * The actual rectangle that should be filled with (complete or partial) michael@0: * image tiles. Same coordinate system as aBorderArea/aBGClipRect passed into michael@0: * PrepareBackgroundLayer. michael@0: */ michael@0: nsRect mFillArea; michael@0: /** michael@0: * The anchor point that should be snapped to a pixel corner. Same michael@0: * coordinate system as aBorderArea/aBGClipRect passed into michael@0: * PrepareBackgroundLayer. michael@0: */ michael@0: nsPoint mAnchor; michael@0: /** michael@0: * The compositing operation that the image should use michael@0: */ michael@0: gfxContext::GraphicsOperator mCompositingOp; michael@0: }; michael@0: michael@0: struct nsCSSRendering { michael@0: /** michael@0: * Initialize any static variables used by nsCSSRendering. michael@0: */ michael@0: static void Init(); michael@0: michael@0: /** michael@0: * Clean up any static variables used by nsCSSRendering. michael@0: */ michael@0: static void Shutdown(); michael@0: michael@0: static void PaintBoxShadowInner(nsPresContext* aPresContext, michael@0: nsRenderingContext& aRenderingContext, michael@0: nsIFrame* aForFrame, michael@0: const nsRect& aFrameArea, michael@0: const nsRect& aDirtyRect); michael@0: michael@0: static void PaintBoxShadowOuter(nsPresContext* aPresContext, michael@0: nsRenderingContext& aRenderingContext, michael@0: nsIFrame* aForFrame, michael@0: const nsRect& aFrameArea, michael@0: const nsRect& aDirtyRect, michael@0: float aOpacity = 1.0); michael@0: michael@0: static void ComputePixelRadii(const nscoord *aAppUnitsRadii, michael@0: nscoord aAppUnitsPerPixel, michael@0: gfxCornerSizes *oBorderRadii); michael@0: michael@0: /** michael@0: * Render the border for an element using css rendering rules michael@0: * for borders. aSkipSides is a bitmask of the sides to skip michael@0: * when rendering. If 0 then no sides are skipped. michael@0: */ michael@0: static void PaintBorder(nsPresContext* aPresContext, michael@0: nsRenderingContext& aRenderingContext, michael@0: nsIFrame* aForFrame, michael@0: const nsRect& aDirtyRect, michael@0: const nsRect& aBorderArea, michael@0: nsStyleContext* aStyleContext, michael@0: int aSkipSides = 0); michael@0: michael@0: /** michael@0: * Like PaintBorder, but taking an nsStyleBorder argument instead of michael@0: * getting it from aStyleContext. michael@0: */ michael@0: static void PaintBorderWithStyleBorder(nsPresContext* aPresContext, michael@0: nsRenderingContext& aRenderingContext, michael@0: nsIFrame* aForFrame, michael@0: const nsRect& aDirtyRect, michael@0: const nsRect& aBorderArea, michael@0: const nsStyleBorder& aBorderStyle, michael@0: nsStyleContext* aStyleContext, michael@0: int aSkipSides = 0); michael@0: michael@0: michael@0: /** michael@0: * Render the outline for an element using css rendering rules michael@0: * for borders. aSkipSides is a bitmask of the sides to skip michael@0: * when rendering. If 0 then no sides are skipped. michael@0: */ michael@0: static void PaintOutline(nsPresContext* aPresContext, michael@0: nsRenderingContext& aRenderingContext, michael@0: nsIFrame* aForFrame, michael@0: const nsRect& aDirtyRect, michael@0: const nsRect& aBorderArea, michael@0: nsStyleContext* aStyleContext); michael@0: michael@0: /** michael@0: * Render keyboard focus on an element. michael@0: * |aFocusRect| is the outer rectangle of the focused element. michael@0: * Uses a fixed style equivalent to "1px dotted |aColor|". michael@0: * Not used for controls, because the native theme may differ. michael@0: */ michael@0: static void PaintFocus(nsPresContext* aPresContext, michael@0: nsRenderingContext& aRenderingContext, michael@0: const nsRect& aFocusRect, michael@0: nscolor aColor); michael@0: michael@0: /** michael@0: * Render a gradient for an element. michael@0: * aDest is the rect for a single tile of the gradient on the destination. michael@0: * aFill is the rect on the destination to be covered by repeated tiling of michael@0: * the gradient. michael@0: * aSrc is the part of the gradient to be rendered into a tile (aDest), if michael@0: * aSrc and aDest are different sizes, the image will be scaled to map aSrc michael@0: * onto aDest. michael@0: * aIntrinsicSize is the size of the source gradient. michael@0: */ michael@0: static void PaintGradient(nsPresContext* aPresContext, michael@0: nsRenderingContext& aRenderingContext, michael@0: nsStyleGradient* aGradient, michael@0: const nsRect& aDirtyRect, michael@0: const nsRect& aDest, michael@0: const nsRect& aFill, michael@0: const mozilla::CSSIntRect& aSrc, michael@0: const nsSize& aIntrinsiceSize); michael@0: michael@0: /** michael@0: * Find the frame whose background style should be used to draw the michael@0: * canvas background. aForFrame must be the frame for the root element michael@0: * whose background style should be used. This function will return michael@0: * aForFrame unless the background should be propagated, in michael@0: * which case we return the frame associated with the 's background. michael@0: */ michael@0: static nsIFrame* FindBackgroundStyleFrame(nsIFrame* aForFrame); michael@0: michael@0: /** michael@0: * @return true if |aFrame| is a canvas frame, in the CSS sense. michael@0: */ michael@0: static bool IsCanvasFrame(nsIFrame* aFrame); michael@0: michael@0: /** michael@0: * Fill in an aBackgroundSC to be used to paint the background michael@0: * for an element. This applies the rules for propagating michael@0: * backgrounds between BODY, the root element, and the canvas. michael@0: * @return true if there is some meaningful background. michael@0: */ michael@0: static bool FindBackground(nsIFrame* aForFrame, michael@0: nsStyleContext** aBackgroundSC); michael@0: michael@0: /** michael@0: * As FindBackground, but the passed-in frame is known to be a root frame michael@0: * (returned from nsCSSFrameConstructor::GetRootElementStyleFrame()) michael@0: * and there is always some meaningful background returned. michael@0: */ michael@0: static nsStyleContext* FindRootFrameBackground(nsIFrame* aForFrame); michael@0: michael@0: /** michael@0: * Returns background style information for the canvas. michael@0: * michael@0: * @param aForFrame michael@0: * the frame used to represent the canvas, in the CSS sense (i.e. michael@0: * nsCSSRendering::IsCanvasFrame(aForFrame) must be true) michael@0: * @param aRootElementFrame michael@0: * the frame representing the root element of the document michael@0: * @param aBackground michael@0: * contains background style information for the canvas on return michael@0: */ michael@0: static nsStyleContext* michael@0: FindCanvasBackground(nsIFrame* aForFrame, nsIFrame* aRootElementFrame) michael@0: { michael@0: NS_ABORT_IF_FALSE(IsCanvasFrame(aForFrame), "not a canvas frame"); michael@0: if (aRootElementFrame) michael@0: return FindRootFrameBackground(aRootElementFrame); michael@0: michael@0: // This should always give transparent, so we'll fill it in with the michael@0: // default color if needed. This seems to happen a bit while a page is michael@0: // being loaded. michael@0: return aForFrame->StyleContext(); michael@0: } michael@0: michael@0: /** michael@0: * Find a frame which draws a non-transparent background, michael@0: * for various table-related and HR-related backwards-compatibility hacks. michael@0: * This function will also stop if it finds themed frame which might draw michael@0: * background. michael@0: * michael@0: * Be very hesitant if you're considering calling this function -- it's michael@0: * usually not what you want. michael@0: */ michael@0: static nsIFrame* michael@0: FindNonTransparentBackgroundFrame(nsIFrame* aFrame, michael@0: bool aStartAtParent = false); michael@0: michael@0: /** michael@0: * Determine the background color to draw taking into account print settings. michael@0: */ michael@0: static nscolor michael@0: DetermineBackgroundColor(nsPresContext* aPresContext, michael@0: nsStyleContext* aStyleContext, michael@0: nsIFrame* aFrame, michael@0: bool& aDrawBackgroundImage, michael@0: bool& aDrawBackgroundColor); michael@0: michael@0: static nsRect michael@0: ComputeBackgroundPositioningArea(nsPresContext* aPresContext, michael@0: nsIFrame* aForFrame, michael@0: const nsRect& aBorderArea, michael@0: const nsStyleBackground& aBackground, michael@0: const nsStyleBackground::Layer& aLayer, michael@0: nsIFrame** aAttachedToFrame); michael@0: michael@0: static nsBackgroundLayerState michael@0: PrepareBackgroundLayer(nsPresContext* aPresContext, michael@0: nsIFrame* aForFrame, michael@0: uint32_t aFlags, michael@0: const nsRect& aBorderArea, michael@0: const nsRect& aBGClipRect, michael@0: const nsStyleBackground& aBackground, michael@0: const nsStyleBackground::Layer& aLayer); michael@0: michael@0: /** michael@0: * Render the background for an element using css rendering rules michael@0: * for backgrounds. michael@0: */ michael@0: enum { michael@0: /** michael@0: * When this flag is passed, the element's nsDisplayBorder will be michael@0: * painted immediately on top of this background. michael@0: */ michael@0: PAINTBG_WILL_PAINT_BORDER = 0x01, michael@0: /** michael@0: * When this flag is passed, images are synchronously decoded. michael@0: */ michael@0: PAINTBG_SYNC_DECODE_IMAGES = 0x02, michael@0: /** michael@0: * When this flag is passed, painting will go to the screen so we can michael@0: * take advantage of the fact that it will be clipped to the viewport. michael@0: */ michael@0: PAINTBG_TO_WINDOW = 0x04 michael@0: }; michael@0: static void PaintBackground(nsPresContext* aPresContext, michael@0: nsRenderingContext& aRenderingContext, michael@0: nsIFrame* aForFrame, michael@0: const nsRect& aDirtyRect, michael@0: const nsRect& aBorderArea, michael@0: uint32_t aFlags, michael@0: nsRect* aBGClipRect = nullptr, michael@0: int32_t aLayer = -1); michael@0: michael@0: static void PaintBackgroundColor(nsPresContext* aPresContext, michael@0: nsRenderingContext& aRenderingContext, michael@0: nsIFrame* aForFrame, michael@0: const nsRect& aDirtyRect, michael@0: const nsRect& aBorderArea, michael@0: uint32_t aFlags); michael@0: michael@0: /** michael@0: * Same as |PaintBackground|, except using the provided style structs. michael@0: * This short-circuits the code that ensures that the root element's michael@0: * background is drawn on the canvas. michael@0: * The aLayer parameter allows you to paint a single layer of the background. michael@0: * The default value for aLayer, -1, means that all layers will be painted. michael@0: * The background color will only be painted if the back-most layer is also michael@0: * being painted. michael@0: */ michael@0: static void PaintBackgroundWithSC(nsPresContext* aPresContext, michael@0: nsRenderingContext& aRenderingContext, michael@0: nsIFrame* aForFrame, michael@0: const nsRect& aDirtyRect, michael@0: const nsRect& aBorderArea, michael@0: nsStyleContext *aStyleContext, michael@0: const nsStyleBorder& aBorder, michael@0: uint32_t aFlags, michael@0: nsRect* aBGClipRect = nullptr, michael@0: int32_t aLayer = -1); michael@0: michael@0: static void PaintBackgroundColorWithSC(nsPresContext* aPresContext, michael@0: nsRenderingContext& aRenderingContext, michael@0: nsIFrame* aForFrame, michael@0: const nsRect& aDirtyRect, michael@0: const nsRect& aBorderArea, michael@0: nsStyleContext *aStyleContext, michael@0: const nsStyleBorder& aBorder, michael@0: uint32_t aFlags); michael@0: /** michael@0: * Returns the rectangle covered by the given background layer image, taking michael@0: * into account background positioning, sizing, and repetition, but not michael@0: * clipping. michael@0: */ michael@0: static nsRect GetBackgroundLayerRect(nsPresContext* aPresContext, michael@0: nsIFrame* aForFrame, michael@0: const nsRect& aBorderArea, michael@0: const nsRect& aClipRect, michael@0: const nsStyleBackground& aBackground, michael@0: const nsStyleBackground::Layer& aLayer, michael@0: uint32_t aFlags); michael@0: michael@0: /** michael@0: * Checks if image in layer aLayer of aBackground is currently decoded. michael@0: */ michael@0: static bool IsBackgroundImageDecodedForStyleContextAndLayer( michael@0: const nsStyleBackground *aBackground, uint32_t aLayer); michael@0: michael@0: /** michael@0: * Checks if all images that are part of the background for aFrame are michael@0: * currently decoded. michael@0: */ michael@0: static bool AreAllBackgroundImagesDecodedForFrame(nsIFrame* aFrame); michael@0: michael@0: /** michael@0: * Called when we start creating a display list. The frame tree will not michael@0: * change until a matching EndFrameTreeLocked is called. michael@0: */ michael@0: static void BeginFrameTreesLocked(); michael@0: /** michael@0: * Called when we've finished using a display list. When all michael@0: * BeginFrameTreeLocked calls have been balanced by an EndFrameTreeLocked, michael@0: * the frame tree may start changing again. michael@0: */ michael@0: static void EndFrameTreesLocked(); michael@0: michael@0: // Draw a border segment in the table collapsing border model without michael@0: // beveling corners michael@0: static void DrawTableBorderSegment(nsRenderingContext& aContext, michael@0: uint8_t aBorderStyle, michael@0: nscolor aBorderColor, michael@0: const nsStyleBackground* aBGColor, michael@0: const nsRect& aBorderRect, michael@0: int32_t aAppUnitsPerCSSPixel, michael@0: uint8_t aStartBevelSide = 0, michael@0: nscoord aStartBevelOffset = 0, michael@0: uint8_t aEndBevelSide = 0, michael@0: nscoord aEndBevelOffset = 0); michael@0: michael@0: /** michael@0: * Function for painting the decoration lines for the text. michael@0: * NOTE: aPt, aLineSize, aAscent and aOffset are non-rounded device pixels, michael@0: * not app units. michael@0: * input: michael@0: * @param aFrame the frame which needs the decoration line michael@0: * @param aGfxContext michael@0: * @param aDirtyRect no need to paint outside this rect michael@0: * @param aColor the color of the decoration line michael@0: * @param aPt the top/left edge of the text michael@0: * @param aXInFrame the distance between aPt.x and left edge of michael@0: * aFrame. If the decoration line is for shadow, michael@0: * set the distance between the left edge of michael@0: * the aFrame and the position of the text as michael@0: * positioned without offset of the shadow. michael@0: * @param aLineSize the width and the height of the decoration michael@0: * line michael@0: * @param aAscent the ascent of the text michael@0: * @param aOffset the offset of the decoration line from michael@0: * the baseline of the text (if the value is michael@0: * positive, the line is lifted up) michael@0: * @param aDecoration which line will be painted. The value can be michael@0: * NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE or michael@0: * NS_STYLE_TEXT_DECORATION_LINE_OVERLINE or michael@0: * NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH. michael@0: * @param aStyle the style of the decoration line such as michael@0: * NS_STYLE_TEXT_DECORATION_STYLE_*. michael@0: * @param aDescentLimit If aDescentLimit is zero or larger and the michael@0: * underline overflows from the descent space, michael@0: * the underline should be lifted up as far as michael@0: * possible. Note that this does not mean the michael@0: * underline never overflows from this michael@0: * limitation. Because if the underline is michael@0: * positioned to the baseline or upper, it causes michael@0: * unreadability. Note that if this is zero michael@0: * or larger, the underline rect may be shrunken michael@0: * if it's possible. Therefore, this value is michael@0: * used for strikeout line and overline too. michael@0: */ michael@0: static void PaintDecorationLine(nsIFrame* aFrame, michael@0: gfxContext* aGfxContext, michael@0: const gfxRect& aDirtyRect, michael@0: const nscolor aColor, michael@0: const gfxPoint& aPt, michael@0: const gfxFloat aXInFrame, michael@0: const gfxSize& aLineSize, michael@0: const gfxFloat aAscent, michael@0: const gfxFloat aOffset, michael@0: const uint8_t aDecoration, michael@0: const uint8_t aStyle, michael@0: const gfxFloat aDescentLimit = -1.0); michael@0: michael@0: /** michael@0: * Adds a path corresponding to the outline of the decoration line to michael@0: * the specified context. Arguments have the same meaning as for michael@0: * PaintDecorationLine. Currently this only works for solid michael@0: * decorations; for other decoration styles, an empty path is added michael@0: * to the context. michael@0: */ michael@0: static void DecorationLineToPath(nsIFrame* aFrame, michael@0: gfxContext* aGfxContext, michael@0: const gfxRect& aDirtyRect, michael@0: const nscolor aColor, michael@0: const gfxPoint& aPt, michael@0: const gfxFloat aXInFrame, michael@0: const gfxSize& aLineSize, michael@0: const gfxFloat aAscent, michael@0: const gfxFloat aOffset, michael@0: const uint8_t aDecoration, michael@0: const uint8_t aStyle, michael@0: const gfxFloat aDescentLimit = -1.0); michael@0: michael@0: /** michael@0: * Function for getting the decoration line rect for the text. michael@0: * NOTE: aLineSize, aAscent and aOffset are non-rounded device pixels, michael@0: * not app units. michael@0: * input: michael@0: * @param aPresContext michael@0: * @param aLineSize the width and the height of the decoration michael@0: * line michael@0: * @param aAscent the ascent of the text michael@0: * @param aOffset the offset of the decoration line from michael@0: * the baseline of the text (if the value is michael@0: * positive, the line is lifted up) michael@0: * @param aDecoration which line will be painted. The value can be michael@0: * NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE or michael@0: * NS_STYLE_TEXT_DECORATION_LINE_OVERLINE or michael@0: * NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH. michael@0: * @param aStyle the style of the decoration line such as michael@0: * NS_STYLE_TEXT_DECORATION_STYLE_*. michael@0: * @param aDescentLimit If aDescentLimit is zero or larger and the michael@0: * underline overflows from the descent space, michael@0: * the underline should be lifted up as far as michael@0: * possible. Note that this does not mean the michael@0: * underline never overflows from this michael@0: * limitation. Because if the underline is michael@0: * positioned to the baseline or upper, it causes michael@0: * unreadability. Note that if this is zero michael@0: * or larger, the underline rect may be shrunken michael@0: * if it's possible. Therefore, this value is michael@0: * used for strikeout line and overline too. michael@0: * output: michael@0: * @return the decoration line rect for the input, michael@0: * the each values are app units. michael@0: */ michael@0: static nsRect GetTextDecorationRect(nsPresContext* aPresContext, michael@0: const gfxSize& aLineSize, michael@0: const gfxFloat aAscent, michael@0: const gfxFloat aOffset, michael@0: const uint8_t aDecoration, michael@0: const uint8_t aStyle, michael@0: const gfxFloat aDescentLimit = -1.0); michael@0: michael@0: static gfxContext::GraphicsOperator GetGFXBlendMode(uint8_t mBlendMode) { michael@0: switch (mBlendMode) { michael@0: case NS_STYLE_BLEND_NORMAL: return gfxContext::OPERATOR_OVER; michael@0: case NS_STYLE_BLEND_MULTIPLY: return gfxContext::OPERATOR_MULTIPLY; michael@0: case NS_STYLE_BLEND_SCREEN: return gfxContext::OPERATOR_SCREEN; michael@0: case NS_STYLE_BLEND_OVERLAY: return gfxContext::OPERATOR_OVERLAY; michael@0: case NS_STYLE_BLEND_DARKEN: return gfxContext::OPERATOR_DARKEN; michael@0: case NS_STYLE_BLEND_LIGHTEN: return gfxContext::OPERATOR_LIGHTEN; michael@0: case NS_STYLE_BLEND_COLOR_DODGE: return gfxContext::OPERATOR_COLOR_DODGE; michael@0: case NS_STYLE_BLEND_COLOR_BURN: return gfxContext::OPERATOR_COLOR_BURN; michael@0: case NS_STYLE_BLEND_HARD_LIGHT: return gfxContext::OPERATOR_HARD_LIGHT; michael@0: case NS_STYLE_BLEND_SOFT_LIGHT: return gfxContext::OPERATOR_SOFT_LIGHT; michael@0: case NS_STYLE_BLEND_DIFFERENCE: return gfxContext::OPERATOR_DIFFERENCE; michael@0: case NS_STYLE_BLEND_EXCLUSION: return gfxContext::OPERATOR_EXCLUSION; michael@0: case NS_STYLE_BLEND_HUE: return gfxContext::OPERATOR_HUE; michael@0: case NS_STYLE_BLEND_SATURATION: return gfxContext::OPERATOR_SATURATION; michael@0: case NS_STYLE_BLEND_COLOR: return gfxContext::OPERATOR_COLOR; michael@0: case NS_STYLE_BLEND_LUMINOSITY: return gfxContext::OPERATOR_LUMINOSITY; michael@0: default: MOZ_ASSERT(false); return gfxContext::OPERATOR_OVER; michael@0: } michael@0: michael@0: return gfxContext::OPERATOR_OVER; michael@0: } michael@0: michael@0: protected: michael@0: static gfxRect GetTextDecorationRectInternal(const gfxPoint& aPt, michael@0: const gfxSize& aLineSize, michael@0: const gfxFloat aAscent, michael@0: const gfxFloat aOffset, michael@0: const uint8_t aDecoration, michael@0: const uint8_t aStyle, michael@0: const gfxFloat aDscentLimit); michael@0: michael@0: /** michael@0: * Returns inflated rect for painting a decoration line. michael@0: * Complex style decoration lines should be painted from leftmost of nearest michael@0: * ancestor block box because that makes better look of connection of lines michael@0: * for different nodes. ExpandPaintingRectForDecorationLine() returns michael@0: * a rect for actual painting rect for the clipped rect. michael@0: * michael@0: * input: michael@0: * @param aFrame the frame which needs the decoration line. michael@0: * @param aStyle the style of the complex decoration line michael@0: * NS_STYLE_TEXT_DECORATION_STYLE_DOTTED or michael@0: * NS_STYLE_TEXT_DECORATION_STYLE_DASHED or michael@0: * NS_STYLE_TEXT_DECORATION_STYLE_WAVY. michael@0: * @param aClippedRect the clipped rect for the decoration line. michael@0: * in other words, visible area of the line. michael@0: * @param aXInFrame the distance between left edge of aFrame and michael@0: * aClippedRect.pos.x. michael@0: * @param aCycleLength the width of one cycle of the line style. michael@0: */ michael@0: static gfxRect ExpandPaintingRectForDecorationLine( michael@0: nsIFrame* aFrame, michael@0: const uint8_t aStyle, michael@0: const gfxRect &aClippedRect, michael@0: const gfxFloat aXInFrame, michael@0: const gfxFloat aCycleLength); michael@0: }; michael@0: michael@0: /* michael@0: * nsContextBoxBlur michael@0: * Creates an 8-bit alpha channel context for callers to draw in, blurs the michael@0: * contents of that context and applies it as a 1-color mask on a michael@0: * different existing context. Uses gfxAlphaBoxBlur as its back end. michael@0: * michael@0: * You must call Init() first to create a suitable temporary surface to draw michael@0: * on. You must then draw any desired content onto the given context, then michael@0: * call DoPaint() to apply the blurred content as a single-color mask. You michael@0: * can only call Init() once, so objects cannot be reused. michael@0: * michael@0: * This is very useful for creating drop shadows or silhouettes. michael@0: */ michael@0: class nsContextBoxBlur { michael@0: public: michael@0: enum { michael@0: FORCE_MASK = 0x01 michael@0: }; michael@0: /** michael@0: * Prepares a gfxContext to draw on. Do not call this twice; if you want michael@0: * to get the gfxContext again use GetContext(). michael@0: * michael@0: * @param aRect The coordinates of the surface to create. michael@0: * All coordinates must be in app units. michael@0: * This must not include the blur radius, pass michael@0: * it as the second parameter and everything michael@0: * is taken care of. michael@0: * michael@0: * @param aBlurRadius The blur radius in app units. michael@0: * michael@0: * @param aAppUnitsPerDevPixel The number of app units in a device pixel, michael@0: * for conversion. Most of the time you'll michael@0: * pass this from the current PresContext if michael@0: * available. michael@0: * michael@0: * @param aDestinationCtx The graphics context to apply the blurred michael@0: * mask to when you call DoPaint(). Make sure michael@0: * it is not destroyed before you call michael@0: * DoPaint(). To set the color of the michael@0: * resulting blurred graphic mask, you must michael@0: * set the color on this context before michael@0: * calling Init(). michael@0: * michael@0: * @param aDirtyRect The absolute dirty rect in app units. Used to michael@0: * optimize the temporary surface size and speed up blur. michael@0: * michael@0: * @param aSkipRect An area in device pixels (NOT app units!) to avoid michael@0: * blurring over, to prevent unnecessary work. michael@0: * michael@0: * @param aFlags FORCE_MASK to ensure that the content drawn to the michael@0: * returned gfxContext is used as a mask, and not michael@0: * drawn directly to aDestinationCtx. michael@0: * michael@0: * @return A blank 8-bit alpha-channel-only graphics context to michael@0: * draw on, or null on error. Must not be freed. The michael@0: * context has a device offset applied to it given by michael@0: * aRect. This means you can use coordinates as if it michael@0: * were at the desired position at aRect and you don't michael@0: * need to worry about translating any coordinates to michael@0: * draw on this temporary surface. michael@0: * michael@0: * If aBlurRadius is 0, the returned context is aDestinationCtx and michael@0: * DoPaint() does nothing, because no blurring is required. Therefore, you michael@0: * should prepare the destination context as if you were going to draw michael@0: * directly on it instead of any temporary surface created in this class. michael@0: */ michael@0: gfxContext* Init(const nsRect& aRect, nscoord aSpreadRadius, michael@0: nscoord aBlurRadius, michael@0: int32_t aAppUnitsPerDevPixel, gfxContext* aDestinationCtx, michael@0: const nsRect& aDirtyRect, const gfxRect* aSkipRect, michael@0: uint32_t aFlags = 0); michael@0: michael@0: /** michael@0: * Does the actual blurring and mask applying. Users of this object *must* michael@0: * have called Init() first, then have drawn whatever they want to be michael@0: * blurred onto the internal gfxContext before calling this. michael@0: */ michael@0: void DoPaint(); michael@0: michael@0: /** michael@0: * Gets the internal gfxContext at any time. Must not be freed. Avoid michael@0: * calling this before calling Init() since the context would not be michael@0: * constructed at that point. michael@0: */ michael@0: gfxContext* GetContext(); michael@0: michael@0: michael@0: /** michael@0: * Get the margin associated with the given blur radius, i.e., the michael@0: * additional area that might be painted as a result of it. (The michael@0: * margin for a spread radius is itself, on all sides.) michael@0: */ michael@0: static nsMargin GetBlurRadiusMargin(nscoord aBlurRadius, michael@0: int32_t aAppUnitsPerDevPixel); michael@0: michael@0: /** michael@0: * Blurs a coloured rectangle onto aDestinationCtx. This is equivalent michael@0: * to calling Init(), drawing a rectangle onto the returned surface michael@0: * and then calling DoPaint, but may let us optimize better in the michael@0: * backend. michael@0: * michael@0: * @param aDestinationCtx The destination to blur to. michael@0: * @param aRect The rectangle to blur in app units. michael@0: * @param aAppUnitsPerDevPixel The number of app units in a device pixel, michael@0: * for conversion. Most of the time you'll michael@0: * pass this from the current PresContext if michael@0: * available. michael@0: * @param aCornerRadii Corner radii for aRect, if it is a rounded michael@0: * rectangle. michael@0: * @param aBlurRadius The blur radius in app units. michael@0: * @param aShadowColor The color to draw the blurred shadow. michael@0: * @param aDirtyRect The absolute dirty rect in app units. Used to michael@0: * optimize the temporary surface size and speed up blur. michael@0: * @param aSkipRect An area in device pixels (NOT app units!) to avoid michael@0: * blurring over, to prevent unnecessary work. michael@0: */ michael@0: static void BlurRectangle(gfxContext* aDestinationCtx, michael@0: const nsRect& aRect, michael@0: int32_t aAppUnitsPerDevPixel, michael@0: gfxCornerSizes* aCornerRadii, michael@0: nscoord aBlurRadius, michael@0: const gfxRGBA& aShadowColor, michael@0: const nsRect& aDirtyRect, michael@0: const gfxRect& aSkipRect); michael@0: michael@0: protected: michael@0: gfxAlphaBoxBlur blur; michael@0: nsRefPtr mContext; michael@0: gfxContext* mDestinationCtx; michael@0: michael@0: /* This is true if the blur already has it's content transformed michael@0: * by mDestinationCtx's transform */ michael@0: bool mPreTransformed; michael@0: michael@0: }; michael@0: michael@0: #endif /* nsCSSRendering_h___ */