зеркало из https://github.com/mozilla/gecko-dev.git
Implement text-shadow rendering.
This commit is contained in:
Родитель
124b7ecaa6
Коммит
159e13c0ea
|
@ -25,6 +25,7 @@
|
|||
* Takeshi Ichimaru <ayakawa.m@gmail.com>
|
||||
* Masayuki Nakano <masayuki@d-toybox.com>
|
||||
* L. David Baron <dbaron@dbaron.org>, Mozilla Corporation
|
||||
* Michael Ventnor <m.ventnor@gmail.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
|
@ -4707,3 +4708,151 @@ nsCSSRendering::GetTextDecorationRectInternal(const gfxPoint& aPt,
|
|||
r.pos.y = baseline - NS_floor(offset + 0.5);
|
||||
return r;
|
||||
}
|
||||
|
||||
// -----
|
||||
// nsContextBoxBlur
|
||||
// -----
|
||||
void
|
||||
nsContextBoxBlur::BoxBlurHorizontal(unsigned char* aInput,
|
||||
unsigned char* aOutput,
|
||||
PRUint32 aLeftLobe,
|
||||
PRUint32 aRightLobe)
|
||||
{
|
||||
// Box blur involves looking at one pixel, and setting its value to the average of
|
||||
// its neighbouring pixels. leftLobe is how many pixels to the left to include
|
||||
// in the average, rightLobe is to the right.
|
||||
// boxSize is how many pixels total will be averaged when looking at each pixel.
|
||||
PRUint32 boxSize = aLeftLobe + aRightLobe + 1;
|
||||
|
||||
long stride = mImageSurface->Stride();
|
||||
PRUint32 rows = mRect.Height();
|
||||
|
||||
for (PRUint32 y = 0; y < rows; y++) {
|
||||
PRUint32 alphaSum = 0;
|
||||
for (PRUint32 i = 0; i < boxSize; i++) {
|
||||
PRInt32 pos = i - aLeftLobe;
|
||||
pos = PR_MAX(pos, 0);
|
||||
pos = PR_MIN(pos, stride - 1);
|
||||
alphaSum += aInput[stride * y + pos];
|
||||
}
|
||||
for (PRInt32 x = 0; x < stride; x++) {
|
||||
PRInt32 tmp = x - aLeftLobe;
|
||||
PRInt32 last = PR_MAX(tmp, 0);
|
||||
PRInt32 next = PR_MIN(tmp + boxSize, stride - 1);
|
||||
|
||||
aOutput[stride * y + x] = alphaSum/boxSize;
|
||||
|
||||
alphaSum += aInput[stride * y + next] -
|
||||
aInput[stride * y + last];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsContextBoxBlur::BoxBlurVertical(unsigned char* aInput,
|
||||
unsigned char* aOutput,
|
||||
PRUint32 aTopLobe,
|
||||
PRUint32 aBottomLobe)
|
||||
{
|
||||
PRUint32 boxSize = aTopLobe + aBottomLobe + 1;
|
||||
|
||||
long stride = mImageSurface->Stride();
|
||||
PRUint32 rows = mRect.Height();
|
||||
|
||||
for (PRInt32 x = 0; x < stride; x++) {
|
||||
PRUint32 alphaSum = 0;
|
||||
for (PRUint32 i = 0; i < boxSize; i++) {
|
||||
PRInt32 pos = i - aTopLobe;
|
||||
pos = PR_MAX(pos, 0);
|
||||
pos = PR_MIN(pos, stride - 1);
|
||||
alphaSum += aInput[stride * pos + x];
|
||||
}
|
||||
for (PRUint32 y = 0; y < rows; y++) {
|
||||
PRInt32 tmp = y - aTopLobe;
|
||||
PRInt32 last = PR_MAX(tmp, 0);
|
||||
PRInt32 next = PR_MIN(tmp + boxSize, rows - 1);
|
||||
|
||||
aOutput[stride * y + x] = alphaSum/boxSize;
|
||||
|
||||
alphaSum += aInput[stride * next + x] -
|
||||
aInput[stride * last + x];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gfxContext*
|
||||
nsContextBoxBlur::Init(const gfxRect& aRect, nscoord aBlurRadius,
|
||||
PRInt32 aAppUnitsPerDevPixel)
|
||||
{
|
||||
mBlurRadius = aBlurRadius / aAppUnitsPerDevPixel;
|
||||
|
||||
// Convert from app units to device pixels
|
||||
mRect = aRect;
|
||||
mRect.Outset(aBlurRadius);
|
||||
mRect.ScaleInverse(aAppUnitsPerDevPixel);
|
||||
mRect.RoundOut();
|
||||
|
||||
// Make an alpha-only surface to draw on. We will play with the data after everything is drawn
|
||||
// to create a blur effect.
|
||||
mImageSurface = new gfxImageSurface(gfxIntSize(mRect.Width(), mRect.Height()),
|
||||
gfxASurface::ImageFormatA8);
|
||||
if (!mImageSurface)
|
||||
return nsnull;
|
||||
|
||||
// Use a device offset so callers don't need to worry about translating coordinates,
|
||||
// they can draw as if this was part of the destination context at the coordinates
|
||||
// of mRect.
|
||||
mImageSurface->SetDeviceOffset(-mRect.TopLeft());
|
||||
|
||||
mContext = new gfxContext(mImageSurface);
|
||||
return mContext;
|
||||
}
|
||||
|
||||
void
|
||||
nsContextBoxBlur::DoPaint(gfxContext* aDestinationCtx,
|
||||
const gfxRGBA& aColor)
|
||||
{
|
||||
if (mBlurRadius > 0) {
|
||||
unsigned char* boxData = mImageSurface->Data();
|
||||
|
||||
// A blur radius of 1 achieves nothing (1/2 = 0 in int terms),
|
||||
// but we still want a blur!
|
||||
mBlurRadius = PR_MAX(mBlurRadius, 2);
|
||||
|
||||
nsTArray<unsigned char> tempAlphaDataBuf;
|
||||
if (!tempAlphaDataBuf.SetLength(mImageSurface->GetDataSize()))
|
||||
return; // OOM
|
||||
|
||||
// Here we do like what the SVG gaussian blur filter does in calculating
|
||||
// the lobes.
|
||||
if (mBlurRadius & 1) {
|
||||
// blur radius is odd
|
||||
BoxBlurHorizontal(boxData, tempAlphaDataBuf.Elements(), mBlurRadius/2, mBlurRadius/2);
|
||||
BoxBlurHorizontal(tempAlphaDataBuf.Elements(), boxData, mBlurRadius/2, mBlurRadius/2);
|
||||
BoxBlurHorizontal(boxData, tempAlphaDataBuf.Elements(), mBlurRadius/2, mBlurRadius/2);
|
||||
BoxBlurVertical(tempAlphaDataBuf.Elements(), boxData, mBlurRadius/2, mBlurRadius/2);
|
||||
BoxBlurVertical(boxData, tempAlphaDataBuf.Elements(), mBlurRadius/2, mBlurRadius/2);
|
||||
BoxBlurVertical(tempAlphaDataBuf.Elements(), boxData, mBlurRadius/2, mBlurRadius/2);
|
||||
} else {
|
||||
// blur radius is even
|
||||
BoxBlurHorizontal(boxData, tempAlphaDataBuf.Elements(), mBlurRadius/2, mBlurRadius/2 - 1);
|
||||
BoxBlurHorizontal(tempAlphaDataBuf.Elements(), boxData, mBlurRadius/2 - 1, mBlurRadius/2);
|
||||
BoxBlurHorizontal(boxData, tempAlphaDataBuf.Elements(), mBlurRadius/2, mBlurRadius/2);
|
||||
BoxBlurVertical(tempAlphaDataBuf.Elements(), boxData, mBlurRadius/2, mBlurRadius/2 - 1);
|
||||
BoxBlurVertical(boxData, tempAlphaDataBuf.Elements(), mBlurRadius/2 - 1, mBlurRadius/2);
|
||||
BoxBlurVertical(tempAlphaDataBuf.Elements(), boxData, mBlurRadius/2, mBlurRadius/2);
|
||||
}
|
||||
}
|
||||
|
||||
aDestinationCtx->Save();
|
||||
aDestinationCtx->NewPath();
|
||||
aDestinationCtx->SetColor(aColor);
|
||||
aDestinationCtx->Mask(mImageSurface);
|
||||
aDestinationCtx->Restore();
|
||||
}
|
||||
|
||||
gfxContext*
|
||||
nsContextBoxBlur::GetContext()
|
||||
{
|
||||
return mContext;
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include "nsIRenderingContext.h"
|
||||
#include "nsStyleConsts.h"
|
||||
#include "gfxContext.h"
|
||||
#include "gfxImageSurface.h"
|
||||
struct nsPoint;
|
||||
class nsStyleContext;
|
||||
class nsPresContext;
|
||||
|
@ -306,5 +307,82 @@ protected:
|
|||
const PRUint8 aStyle);
|
||||
};
|
||||
|
||||
/*
|
||||
* nsContextBoxBlur
|
||||
* Creates an 8-bit alpha channel context for callers to draw in, blurs the
|
||||
* contents of that context and applies it as a 1-color mask on a
|
||||
* different existing context.
|
||||
*
|
||||
* You must call Init() first to create a suitable temporary surface to draw on.
|
||||
* You must then draw any desired content onto the given context, then call DoPaint()
|
||||
* to apply the blurred content as a single-color mask. You can only call Init() once,
|
||||
* so objects cannot be reused.
|
||||
*
|
||||
* This is very useful for creating drop shadows or silhouettes.
|
||||
*/
|
||||
class nsContextBoxBlur {
|
||||
public:
|
||||
/**
|
||||
* Prepares a gfxContext to draw on. Do not call this twice; if you want to
|
||||
* get the gfxContext again use GetContext().
|
||||
*
|
||||
* @param aRect The coordinates of the surface to create.
|
||||
* All coordinates must be in app units.
|
||||
* This must not include the blur radius, pass it as the
|
||||
* second parameter and everything is taken care of.
|
||||
*
|
||||
* @param aBlurRadius The blur radius in app units.
|
||||
*
|
||||
* @param aAppUnitsPerDevPixel The number of app units in a device pixel, for conversion.
|
||||
* Most of the time you'll pass this from the current
|
||||
* PresContext if available.
|
||||
*
|
||||
* @return A blank 8-bit alpha-channel-only graphics context to draw on, or null on
|
||||
* error. Must not be freed. The context has a device offset applied to it given
|
||||
* by aRect. This means you can use coordinates as if it were at the desired position
|
||||
* at aRect and you don't need to worry about translating any coordinates to draw
|
||||
* on this temporary surface.
|
||||
*/
|
||||
gfxContext* Init(const gfxRect& aRect, nscoord aBlurRadius, PRInt32 aAppUnitsPerDevPixel);
|
||||
|
||||
/**
|
||||
* Does the actual blurring and mask applying. Users of this object *must*
|
||||
* have called Init() first, then have drawn whatever they want to be
|
||||
* blurred onto the internal gfxContext before calling this.
|
||||
*
|
||||
* @param aDestinationCtx The graphics context to apply the blurred mask onto.
|
||||
* The X and Y coordinates that the blurred graphic will be placed
|
||||
* at is determined by the X and Y you passed in aRect in Begin().
|
||||
*
|
||||
* @param aColor Since the graphics context is an 8-bit alpha-only mask, this
|
||||
* determines what base color the blurred graphic will have.
|
||||
*/
|
||||
void DoPaint(gfxContext* aDestinationCtx, const gfxRGBA& aColor);
|
||||
|
||||
/**
|
||||
* Gets the internal gfxContext at any time. Must not be freed. Avoid calling
|
||||
* this before calling Init() since the context would not be constructed at that
|
||||
* point.
|
||||
*/
|
||||
gfxContext* GetContext();
|
||||
|
||||
protected:
|
||||
void BoxBlurHorizontal(unsigned char* aInput,
|
||||
unsigned char* aOutput,
|
||||
PRUint32 aLeftLobe,
|
||||
PRUint32 aRightLobe);
|
||||
void BoxBlurVertical(unsigned char* aInput,
|
||||
unsigned char* aOutput,
|
||||
PRUint32 aTopLobe,
|
||||
PRUint32 aBottomLobe);
|
||||
|
||||
nsRefPtr<gfxContext> mContext;
|
||||
nsRefPtr<gfxImageSurface> mImageSurface;
|
||||
|
||||
// Contrary to what is passed as parameters, these are in device pixels
|
||||
gfxRect mRect;
|
||||
PRInt32 mBlurRadius;
|
||||
|
||||
};
|
||||
|
||||
#endif /* nsCSSRendering_h___ */
|
||||
|
|
|
@ -1286,6 +1286,30 @@ nsLayoutUtils::GetAllInFlowRectsUnion(nsIFrame* aFrame, nsIFrame* aRelativeTo) {
|
|||
: accumulator.mResultRect;
|
||||
}
|
||||
|
||||
nsRect
|
||||
nsLayoutUtils::GetTextShadowRectsUnion(const nsRect& aTextAndDecorationsRect,
|
||||
nsIFrame* aFrame)
|
||||
{
|
||||
const nsStyleText* textStyle = aFrame->GetStyleText();
|
||||
if (!textStyle->mShadowArray)
|
||||
return aTextAndDecorationsRect;
|
||||
|
||||
nsRect resultRect = aTextAndDecorationsRect;
|
||||
for (PRUint32 i = 0; i < textStyle->mShadowArray->Length(); ++i) {
|
||||
nsRect tmpRect(aTextAndDecorationsRect);
|
||||
nsTextShadowItem* shadow = textStyle->mShadowArray->ShadowAt(i);
|
||||
nscoord xOffset = shadow->mXOffset.GetCoordValue();
|
||||
nscoord yOffset = shadow->mYOffset.GetCoordValue();
|
||||
nscoord blurRadius = shadow->mRadius.GetCoordValue();
|
||||
|
||||
tmpRect.MoveBy(nsPoint(xOffset, yOffset));
|
||||
tmpRect.Inflate(blurRadius, blurRadius);
|
||||
|
||||
resultRect.UnionRect(resultRect, tmpRect);
|
||||
}
|
||||
return resultRect;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsLayoutUtils::GetFontMetricsForFrame(nsIFrame* aFrame,
|
||||
nsIFontMetrics** aFontMetrics)
|
||||
|
|
|
@ -502,6 +502,14 @@ public:
|
|||
*/
|
||||
static nsRect GetAllInFlowRectsUnion(nsIFrame* aFrame, nsIFrame* aRelativeTo);
|
||||
|
||||
/**
|
||||
* Takes a text-shadow array from the style properties of a given nsIFrame and
|
||||
* computes the union of those shadows along with the given initial rect.
|
||||
* If there are no shadows, the initial rect is returned.
|
||||
*/
|
||||
static nsRect GetTextShadowRectsUnion(const nsRect& aTextAndDecorationsRect,
|
||||
nsIFrame* aFrame);
|
||||
|
||||
/**
|
||||
* Get the font metrics corresponding to the frame's style data.
|
||||
* @param aFrame the frame
|
||||
|
|
|
@ -1439,10 +1439,20 @@ nsBlockFrame::ComputeCombinedArea(const nsHTMLReflowState& aReflowState,
|
|||
// XXX_perf: This can be done incrementally. It is currently one of
|
||||
// the things that makes incremental reflow O(N^2).
|
||||
nsRect area(0, 0, aMetrics.width, aMetrics.height);
|
||||
|
||||
if (NS_STYLE_OVERFLOW_CLIP != aReflowState.mStyleDisplay->mOverflowX) {
|
||||
PRBool inQuirks = (PresContext()->CompatibilityMode() == eCompatibility_NavQuirks);
|
||||
for (line_iterator line = begin_lines(), line_end = end_lines();
|
||||
line != line_end;
|
||||
++line) {
|
||||
|
||||
// Text-shadow overflows
|
||||
if (!inQuirks && line->IsInline()) {
|
||||
nsRect shadowRect = nsLayoutUtils::GetTextShadowRectsUnion(line->GetCombinedArea(),
|
||||
this);
|
||||
area.UnionRect(area, shadowRect);
|
||||
}
|
||||
|
||||
area.UnionRect(area, line->GetCombinedArea());
|
||||
}
|
||||
|
||||
|
@ -5904,7 +5914,7 @@ nsBlockFrame::IsVisibleInSelection(nsISelection* aSelection)
|
|||
}
|
||||
|
||||
/* virtual */ void
|
||||
nsBlockFrame::PaintTextDecorationLine(nsIRenderingContext& aRenderingContext,
|
||||
nsBlockFrame::PaintTextDecorationLine(gfxContext* aCtx,
|
||||
const nsPoint& aPt,
|
||||
nsLineBox* aLine,
|
||||
nscolor aColor,
|
||||
|
@ -5945,12 +5955,11 @@ nsBlockFrame::PaintTextDecorationLine(nsIRenderingContext& aRenderingContext,
|
|||
|
||||
// Only paint if we have a positive width
|
||||
if (width > 0) {
|
||||
gfxContext *ctx = aRenderingContext.ThebesContext();
|
||||
gfxPoint pt(PresContext()->AppUnitsToGfxUnits(start + aPt.x),
|
||||
PresContext()->AppUnitsToGfxUnits(aLine->mBounds.y + aPt.y));
|
||||
gfxSize size(PresContext()->AppUnitsToGfxUnits(width), aSize);
|
||||
nsCSSRendering::PaintDecorationLine(
|
||||
ctx, aColor, pt, size,
|
||||
aCtx, aColor, pt, size,
|
||||
PresContext()->AppUnitsToGfxUnits(aLine->GetAscent()),
|
||||
aOffset, aDecoration, NS_STYLE_BORDER_STYLE_SOLID);
|
||||
}
|
||||
|
|
|
@ -341,7 +341,7 @@ protected:
|
|||
* Overides member function of nsHTMLContainerFrame. Needed to handle the
|
||||
* lines in a nsBlockFrame properly.
|
||||
*/
|
||||
virtual void PaintTextDecorationLine(nsIRenderingContext& aRenderingContext,
|
||||
virtual void PaintTextDecorationLine(gfxContext* aCtx,
|
||||
const nsPoint& aPt,
|
||||
nsLineBox* aLine,
|
||||
nscolor aColor,
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Michael Ventnor <m.ventnor@gmail.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
|
@ -131,15 +132,15 @@ nsDisplayTextDecoration::Paint(nsDisplayListBuilder* aBuilder,
|
|||
nsHTMLContainerFrame* f = static_cast<nsHTMLContainerFrame*>(mFrame);
|
||||
if (mDecoration == NS_STYLE_TEXT_DECORATION_UNDERLINE) {
|
||||
gfxFloat underlineOffset = fontGroup->GetUnderlineOffset();
|
||||
f->PaintTextDecorationLine(*aCtx, pt, mLine, mColor,
|
||||
f->PaintTextDecorationLine(aCtx->ThebesContext(), pt, mLine, mColor,
|
||||
underlineOffset, ascent,
|
||||
metrics.underlineSize, mDecoration);
|
||||
} else if (mDecoration == NS_STYLE_TEXT_DECORATION_OVERLINE) {
|
||||
f->PaintTextDecorationLine(*aCtx, pt, mLine, mColor,
|
||||
f->PaintTextDecorationLine(aCtx->ThebesContext(), pt, mLine, mColor,
|
||||
metrics.maxAscent, ascent,
|
||||
metrics.underlineSize, mDecoration);
|
||||
} else {
|
||||
f->PaintTextDecorationLine(*aCtx, pt, mLine, mColor,
|
||||
f->PaintTextDecorationLine(aCtx->ThebesContext(), pt, mLine, mColor,
|
||||
metrics.strikeoutOffset, ascent,
|
||||
metrics.strikeoutSize, mDecoration);
|
||||
}
|
||||
|
@ -151,6 +152,90 @@ nsDisplayTextDecoration::GetBounds(nsDisplayListBuilder* aBuilder)
|
|||
return mFrame->GetOverflowRect() + aBuilder->ToReferenceFrame(mFrame);
|
||||
}
|
||||
|
||||
class nsDisplayTextShadow : public nsDisplayItem {
|
||||
public:
|
||||
nsDisplayTextShadow(nsHTMLContainerFrame* aFrame, const PRUint8 aDecoration,
|
||||
const gfxRGBA& aColor, nsLineBox* aLine,
|
||||
const nscoord& aBlurRadius, const gfxPoint& aOffset)
|
||||
: nsDisplayItem(aFrame), mLine(aLine), mColor(aColor),
|
||||
mDecorationFlags(aDecoration),
|
||||
mBlurRadius(aBlurRadius), mOffset(aOffset) {
|
||||
MOZ_COUNT_CTOR(nsDisplayTextShadow);
|
||||
}
|
||||
virtual ~nsDisplayTextShadow() {
|
||||
MOZ_COUNT_DTOR(nsDisplayTextShadow);
|
||||
}
|
||||
|
||||
virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx,
|
||||
const nsRect& aDirtyRect);
|
||||
virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder);
|
||||
NS_DISPLAY_DECL_NAME("TextShadow")
|
||||
private:
|
||||
nsLineBox* mLine;
|
||||
gfxRGBA mColor;
|
||||
PRUint8 mDecorationFlags;
|
||||
nscoord mBlurRadius; // App units
|
||||
gfxPoint mOffset; // App units
|
||||
};
|
||||
|
||||
void
|
||||
nsDisplayTextShadow::Paint(nsDisplayListBuilder* aBuilder,
|
||||
nsIRenderingContext* aCtx,
|
||||
const nsRect& aDirtyRect)
|
||||
{
|
||||
mBlurRadius = PR_MAX(mBlurRadius, 0);
|
||||
|
||||
nsCOMPtr<nsIFontMetrics> fm;
|
||||
nsLayoutUtils::GetFontMetricsForFrame(mFrame, getter_AddRefs(fm));
|
||||
nsIThebesFontMetrics* tfm = static_cast<nsIThebesFontMetrics*>(fm.get());
|
||||
gfxFontGroup* fontGroup = tfm->GetThebesFontGroup();
|
||||
gfxFont* firstFont = fontGroup->GetFontAt(0);
|
||||
if (!firstFont)
|
||||
return; // OOM
|
||||
const gfxFont::Metrics& metrics = firstFont->GetMetrics();
|
||||
nsPoint pt = aBuilder->ToReferenceFrame(mFrame) + nsPoint(mOffset.x, mOffset.y);
|
||||
|
||||
nsHTMLContainerFrame* f = static_cast<nsHTMLContainerFrame*>(mFrame);
|
||||
nsMargin bp = f->GetUsedBorderAndPadding();
|
||||
nscoord innerWidthInAppUnits = (mFrame->GetSize().width - bp.LeftRight());
|
||||
|
||||
gfxRect shadowRect = gfxRect(pt.x, pt.y, innerWidthInAppUnits, mFrame->GetSize().height);
|
||||
|
||||
nsContextBoxBlur contextBoxBlur;
|
||||
gfxContext* shadowCtx = contextBoxBlur.Init(shadowRect, mBlurRadius,
|
||||
mFrame->PresContext()->AppUnitsPerDevPixel());
|
||||
if (!shadowCtx)
|
||||
return;
|
||||
|
||||
nscolor dummyColor(NS_RGB(0xFF, 0xFF, 0xFF));
|
||||
|
||||
if (mDecorationFlags & NS_STYLE_TEXT_DECORATION_UNDERLINE) {
|
||||
gfxFloat underlineOffset = fontGroup->GetUnderlineOffset();
|
||||
f->PaintTextDecorationLine(shadowCtx, pt, mLine, dummyColor,
|
||||
underlineOffset, metrics.maxAscent,
|
||||
metrics.underlineSize, NS_STYLE_TEXT_DECORATION_UNDERLINE);
|
||||
}
|
||||
if (mDecorationFlags & NS_STYLE_TEXT_DECORATION_OVERLINE) {
|
||||
f->PaintTextDecorationLine(shadowCtx, pt, mLine, dummyColor,
|
||||
metrics.maxAscent, metrics.maxAscent,
|
||||
metrics.underlineSize, NS_STYLE_TEXT_DECORATION_OVERLINE);
|
||||
}
|
||||
if (mDecorationFlags & NS_STYLE_TEXT_DECORATION_LINE_THROUGH) {
|
||||
f->PaintTextDecorationLine(shadowCtx, pt, mLine, dummyColor,
|
||||
metrics.strikeoutOffset, metrics.maxAscent,
|
||||
metrics.strikeoutSize, NS_STYLE_TEXT_DECORATION_LINE_THROUGH);
|
||||
}
|
||||
|
||||
contextBoxBlur.DoPaint(aCtx->ThebesContext(), mColor);
|
||||
}
|
||||
|
||||
nsRect
|
||||
nsDisplayTextShadow::GetBounds(nsDisplayListBuilder* aBuilder)
|
||||
{
|
||||
// Shadows are always painted in the overflow rect
|
||||
return mFrame->GetOverflowRect() + aBuilder->ToReferenceFrame(mFrame);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHTMLContainerFrame::DisplayTextDecorations(nsDisplayListBuilder* aBuilder,
|
||||
nsDisplayList* aBelowTextDecorations,
|
||||
|
@ -170,6 +255,34 @@ nsHTMLContainerFrame::DisplayTextDecorations(nsDisplayListBuilder* aBuilder,
|
|||
GetTextDecorations(PresContext(), aLine != nsnull, decorations, underColor,
|
||||
overColor, strikeColor);
|
||||
|
||||
if (decorations == NS_STYLE_TEXT_DECORATION_NONE)
|
||||
return NS_OK;
|
||||
|
||||
// The text-shadow spec says that any text decorations must also have a shadow applied to
|
||||
// it. So draw the shadows as part of the display list.
|
||||
const nsStyleText* textStyle = GetStyleText();
|
||||
|
||||
if (textStyle->mShadowArray) {
|
||||
for (PRUint32 i = textStyle->mShadowArray->Length(); i > 0; --i) {
|
||||
nsTextShadowItem* shadow = textStyle->mShadowArray->ShadowAt(i - 1);
|
||||
nscoord blurRadius = shadow->mRadius.GetCoordValue();
|
||||
gfxRGBA shadowColor;
|
||||
|
||||
if (shadow->mHasColor)
|
||||
shadowColor = gfxRGBA(shadow->mColor);
|
||||
else
|
||||
shadowColor = GetStyleColor()->mColor;
|
||||
|
||||
gfxPoint offset = gfxPoint(shadow->mXOffset.GetCoordValue(),
|
||||
shadow->mYOffset.GetCoordValue());
|
||||
|
||||
// Add it to the display list so it is painted underneath the text and all decorations
|
||||
nsresult rv = aBelowTextDecorations->AppendNewToTop(new (aBuilder)
|
||||
nsDisplayTextShadow(this, decorations, shadowColor, aLine, blurRadius, offset));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
}
|
||||
|
||||
if (decorations & NS_STYLE_TEXT_DECORATION_UNDERLINE) {
|
||||
nsresult rv = aBelowTextDecorations->AppendNewToTop(new (aBuilder)
|
||||
nsDisplayTextDecoration(this, NS_STYLE_TEXT_DECORATION_UNDERLINE, underColor, aLine));
|
||||
|
@ -221,7 +334,7 @@ HasTextFrameDescendantOrInFlow(nsIFrame* aFrame);
|
|||
|
||||
/*virtual*/ void
|
||||
nsHTMLContainerFrame::PaintTextDecorationLine(
|
||||
nsIRenderingContext& aRenderingContext,
|
||||
gfxContext* aCtx,
|
||||
const nsPoint& aPt,
|
||||
nsLineBox* aLine,
|
||||
nscolor aColor,
|
||||
|
@ -239,11 +352,10 @@ nsHTMLContainerFrame::PaintTextDecorationLine(
|
|||
}
|
||||
}
|
||||
nscoord innerWidth = mRect.width - bp.left - bp.right;
|
||||
gfxContext *ctx = aRenderingContext.ThebesContext();
|
||||
gfxPoint pt(PresContext()->AppUnitsToGfxUnits(bp.left + aPt.x),
|
||||
PresContext()->AppUnitsToGfxUnits(bp.top + aPt.y));
|
||||
gfxSize size(PresContext()->AppUnitsToGfxUnits(innerWidth), aSize);
|
||||
nsCSSRendering::PaintDecorationLine(ctx, aColor, pt, size, aAscent, aOffset,
|
||||
nsCSSRendering::PaintDecorationLine(aCtx, aColor, pt, size, aAscent, aOffset,
|
||||
aDecoration, NS_STYLE_BORDER_STYLE_SOLID);
|
||||
}
|
||||
|
||||
|
|
|
@ -161,7 +161,7 @@ protected:
|
|||
/**
|
||||
* Function that does the actual drawing of the textdecoration.
|
||||
* input:
|
||||
* @param aRenderingContext
|
||||
* @param aCtx the Thebes graphics context to draw on
|
||||
* @param aLine the line, or nsnull if this is an inline frame
|
||||
* @param aColor the color of the text-decoration
|
||||
* @param aAscent ascent of the font from which the
|
||||
|
@ -176,7 +176,7 @@ protected:
|
|||
* NS_STYLE_TEXT_DECORATION_OVERLINE or
|
||||
* NS_STYLE_TEXT_DECORATION_LINE_THROUGH.
|
||||
*/
|
||||
virtual void PaintTextDecorationLine(nsIRenderingContext& aRenderingContext,
|
||||
virtual void PaintTextDecorationLine(gfxContext* aCtx,
|
||||
const nsPoint& aPt,
|
||||
nsLineBox* aLine,
|
||||
nscolor aColor,
|
||||
|
@ -186,6 +186,7 @@ protected:
|
|||
const PRUint8 aDecoration);
|
||||
|
||||
friend class nsDisplayTextDecoration;
|
||||
friend class nsDisplayTextShadow;
|
||||
};
|
||||
|
||||
#endif /* nsHTMLContainerFrame_h___ */
|
||||
|
|
|
@ -2556,6 +2556,14 @@ nsLineLayout::RelativePositionFrames(PerSpanData* psd, nsRect& aCombinedArea)
|
|||
// bidi reordering can move and resize the frames. So use the frame's
|
||||
// rect instead of mBounds.
|
||||
nsRect adjustedBounds(nsPoint(0, 0), psd->mFrame->mFrame->GetSize());
|
||||
|
||||
// Text-shadow overflow
|
||||
if (mPresContext->CompatibilityMode() != eCompatibility_NavQuirks) {
|
||||
nsRect shadowRect = nsLayoutUtils::GetTextShadowRectsUnion(adjustedBounds,
|
||||
psd->mFrame->mFrame);
|
||||
adjustedBounds.UnionRect(adjustedBounds, shadowRect);
|
||||
}
|
||||
|
||||
combinedAreaResult.UnionRect(psd->mFrame->mCombinedArea, adjustedBounds);
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -54,6 +54,7 @@
|
|||
#include "nsLineBox.h"
|
||||
#include "gfxFont.h"
|
||||
#include "gfxSkipChars.h"
|
||||
#include "gfxContext.h"
|
||||
|
||||
class nsTextPaintStyle;
|
||||
class PropertyProvider;
|
||||
|
@ -253,6 +254,7 @@ public:
|
|||
gfxFloat GetSnappedBaselineY(gfxContext* aContext, gfxFloat aY);
|
||||
|
||||
// primary frame paint method called from nsDisplayText
|
||||
// The private DrawText() is what applies the text to a graphics context
|
||||
void PaintText(nsIRenderingContext* aRenderingContext, nsPoint aPt,
|
||||
const nsRect& aDirtyRect);
|
||||
// helper: paint quirks-mode CSS text decorations
|
||||
|
@ -382,6 +384,25 @@ protected:
|
|||
PropertyProvider& aProvider,
|
||||
nsRect* aOverflowRect);
|
||||
|
||||
void DrawText(gfxContext* aCtx,
|
||||
const gfxPoint& aTextBaselinePt,
|
||||
PRUint32 aOffset,
|
||||
PRUint32 aLength,
|
||||
const gfxRect* aDirtyRect,
|
||||
PropertyProvider* aProvider,
|
||||
gfxFloat& aAdvanceWidth,
|
||||
PRBool aDrawSoftHyphen);
|
||||
|
||||
void PaintOneShadow(PRUint32 aOffset,
|
||||
PRUint32 aLength,
|
||||
nsTextShadowItem* aShadowDetails,
|
||||
PropertyProvider* aProvider,
|
||||
const gfxRect& aDirtyRect,
|
||||
const gfxPoint& aFramePt,
|
||||
const gfxPoint& aTextBaselinePt,
|
||||
gfxContext* aCtx,
|
||||
const gfxRGBA& aForegroundColor);
|
||||
|
||||
struct TextDecorations {
|
||||
PRUint8 mDecorations;
|
||||
nscolor mOverColor;
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
* Mats Palmgren <mats.palmgren@bredband.net>
|
||||
* Uri Bernstein <uriber@gmail.com>
|
||||
* Stephen Blackheath <entangled.mooched.stephen@blacksapphire.com>
|
||||
* Michael Ventnor <m.ventnor@gmail.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
|
@ -115,6 +116,7 @@
|
|||
#include "gfxFont.h"
|
||||
#include "gfxContext.h"
|
||||
#include "gfxTextRunWordCache.h"
|
||||
#include "gfxImageSurface.h"
|
||||
|
||||
#ifdef NS_DEBUG
|
||||
#undef NOISY_BLINK
|
||||
|
@ -3675,6 +3677,10 @@ nsTextFrame::UnionTextDecorationOverflow(nsPresContext* aPresContext,
|
|||
PropertyProvider& aProvider,
|
||||
nsRect* aOverflowRect)
|
||||
{
|
||||
// Text-shadow overflows
|
||||
nsRect shadowRect = nsLayoutUtils::GetTextShadowRectsUnion(*aOverflowRect, this);
|
||||
aOverflowRect->UnionRect(*aOverflowRect, shadowRect);
|
||||
|
||||
if (IsFloatingFirstLetterChild()) {
|
||||
// The underline/overline drawable area must be contained in the overflow
|
||||
// rect when this is in floating first letter frame at *both* modes.
|
||||
|
@ -3948,6 +3954,65 @@ PRBool SelectionIterator::GetNextSegment(gfxFloat* aXOffset,
|
|||
return PR_TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
nsTextFrame::PaintOneShadow(PRUint32 aOffset, PRUint32 aLength,
|
||||
nsTextShadowItem* aShadowDetails,
|
||||
PropertyProvider* aProvider, const gfxRect& aDirtyRect,
|
||||
const gfxPoint& aFramePt, const gfxPoint& aTextBaselinePt,
|
||||
gfxContext* aCtx, const gfxRGBA& aForegroundColor)
|
||||
{
|
||||
nscoord xOffset = aShadowDetails->mXOffset.GetCoordValue();
|
||||
nscoord yOffset = aShadowDetails->mYOffset.GetCoordValue();
|
||||
nscoord blurRadius = PR_MAX(aShadowDetails->mRadius.GetCoordValue(), 0);
|
||||
|
||||
nsTextPaintStyle textPaintStyle(this);
|
||||
gfxFloat advanceWidth;
|
||||
|
||||
gfxTextRun::Metrics shadowMetrics =
|
||||
mTextRun->MeasureText(aOffset, aLength, PR_FALSE,
|
||||
nsnull, aProvider);
|
||||
|
||||
// This rect is the box which is equivalent to where the shadow will be painted.
|
||||
// X and Y are significant because they can affect the rounding.
|
||||
// The origin of mBoundingBox is the text baseline point, so we must translate it by
|
||||
// that much in order to make the origin the top-left corner of the text bounding box.
|
||||
gfxRect shadowRect = shadowMetrics.mBoundingBox + aTextBaselinePt;
|
||||
shadowRect.MoveBy(gfxPoint(xOffset, yOffset));
|
||||
|
||||
if (GetStateBits() & TEXT_HYPHEN_BREAK) {
|
||||
// Add the width of the soft hyphen so it isn't cut off
|
||||
shadowRect.size.width += aProvider->GetHyphenWidth();
|
||||
}
|
||||
|
||||
nsContextBoxBlur contextBoxBlur;
|
||||
gfxContext* shadowContext = contextBoxBlur.Init(shadowRect, blurRadius,
|
||||
PresContext()->AppUnitsPerDevPixel());
|
||||
if (!shadowContext)
|
||||
return;
|
||||
|
||||
// Draw the text onto our alpha-only surface to capture the alpha values.
|
||||
// Remember that the box blur context has a device offset on it, so we don't need to
|
||||
// translate any coordinates to fit on the surface.
|
||||
DrawText(shadowContext,
|
||||
aTextBaselinePt + gfxPoint(xOffset, yOffset),
|
||||
aOffset, aLength, &aDirtyRect, aProvider, advanceWidth,
|
||||
(GetStateBits() & TEXT_HYPHEN_BREAK) != 0);
|
||||
|
||||
// This will only have an effect in quirks mode. Standards mode text-decoration shadow painting
|
||||
// is handled in nsHTMLContainerFrame.cpp, so you must remember to consider that if you change
|
||||
// any code behaviour here.
|
||||
PaintTextDecorations(shadowContext, aDirtyRect, aFramePt + gfxPoint(xOffset, yOffset),
|
||||
aTextBaselinePt + gfxPoint(xOffset, yOffset),
|
||||
textPaintStyle, *aProvider);
|
||||
|
||||
gfxRGBA maskColor;
|
||||
if (aShadowDetails->mHasColor)
|
||||
maskColor = gfxRGBA(aShadowDetails->mColor);
|
||||
else
|
||||
maskColor = aForegroundColor;
|
||||
contextBoxBlur.DoPaint(aCtx, maskColor);
|
||||
}
|
||||
|
||||
// Paints selection backgrounds and text in the correct colors. Also computes
|
||||
// aAllTypes, the union of all selection types that are applying to this text.
|
||||
void
|
||||
|
@ -4031,18 +4096,11 @@ nsTextFrame::PaintTextWithSelectionColors(gfxContext* aCtx,
|
|||
// Draw text segment
|
||||
aCtx->SetColor(gfxRGBA(foreground));
|
||||
gfxFloat advance;
|
||||
mTextRun->Draw(aCtx, gfxPoint(aFramePt.x + xOffset, aTextBaselinePt.y), offset, length,
|
||||
&aDirtyRect, &aProvider, &advance);
|
||||
|
||||
DrawText(aCtx, gfxPoint(aFramePt.x + xOffset, aTextBaselinePt.y),
|
||||
offset, length, &aDirtyRect, &aProvider,
|
||||
advance, hyphenWidth > 0);
|
||||
if (hyphenWidth) {
|
||||
// Draw the hyphen
|
||||
gfxFloat hyphenBaselineX = aFramePt.x + xOffset + mTextRun->GetDirection()*advance;
|
||||
// Get a reference rendering context because aCtx might not have the
|
||||
// reference matrix currently set
|
||||
gfxTextRunCache::AutoTextRun hyphenTextRun(GetHyphenTextRun(mTextRun, nsnull, this));
|
||||
if (hyphenTextRun.get()) {
|
||||
hyphenTextRun->Draw(aCtx, gfxPoint(hyphenBaselineX, aTextBaselinePt.y),
|
||||
0, hyphenTextRun->GetLength(), &aDirtyRect, nsnull, nsnull);
|
||||
}
|
||||
advance += hyphenWidth;
|
||||
}
|
||||
iterator.UpdateWithAdvance(advance);
|
||||
|
@ -4193,6 +4251,22 @@ nsTextFrame::PaintText(nsIRenderingContext* aRenderingContext, nsPoint aPt,
|
|||
gfxRect dirtyRect(aDirtyRect.x, aDirtyRect.y,
|
||||
aDirtyRect.width, aDirtyRect.height);
|
||||
|
||||
gfxFloat advanceWidth;
|
||||
gfxRGBA foregroundColor = gfxRGBA(textPaintStyle.GetTextColor());
|
||||
|
||||
// Paint the text shadow before doing any foreground stuff
|
||||
const nsStyleText* textStyle = GetStyleText();
|
||||
if (textStyle->mShadowArray) {
|
||||
// Text shadow happens with the last value being painted at the back,
|
||||
// ie. it is painted first.
|
||||
for (PRUint32 i = textStyle->mShadowArray->Length(); i > 0; --i) {
|
||||
PaintOneShadow(provider.GetStart().GetSkippedOffset(),
|
||||
ComputeTransformedLength(provider),
|
||||
textStyle->mShadowArray->ShadowAt(i - 1), &provider,
|
||||
dirtyRect, framePt, textBaselinePt, ctx, foregroundColor);
|
||||
}
|
||||
}
|
||||
|
||||
// Fork off to the (slower) paint-with-selection path if necessary.
|
||||
if (GetNonGeneratedAncestor(this)->GetStateBits() & NS_FRAME_SELECTED_CONTENT) {
|
||||
if (PaintTextWithSelection(ctx, framePt, textBaselinePt,
|
||||
|
@ -4200,27 +4274,36 @@ nsTextFrame::PaintText(nsIRenderingContext* aRenderingContext, nsPoint aPt,
|
|||
return;
|
||||
}
|
||||
|
||||
gfxFloat advanceWidth;
|
||||
gfxFloat* needAdvanceWidth =
|
||||
(GetStateBits() & TEXT_HYPHEN_BREAK) ? &advanceWidth : nsnull;
|
||||
ctx->SetColor(gfxRGBA(textPaintStyle.GetTextColor()));
|
||||
ctx->SetColor(foregroundColor);
|
||||
|
||||
mTextRun->Draw(ctx, textBaselinePt,
|
||||
provider.GetStart().GetSkippedOffset(),
|
||||
ComputeTransformedLength(provider),
|
||||
&dirtyRect, &provider, needAdvanceWidth);
|
||||
if (GetStateBits() & TEXT_HYPHEN_BREAK) {
|
||||
gfxFloat hyphenBaselineX = textBaselinePt.x + mTextRun->GetDirection()*advanceWidth;
|
||||
DrawText(ctx, textBaselinePt, provider.GetStart().GetSkippedOffset(),
|
||||
ComputeTransformedLength(provider), &dirtyRect,
|
||||
&provider, advanceWidth,
|
||||
(GetStateBits() & TEXT_HYPHEN_BREAK) != 0);
|
||||
PaintTextDecorations(ctx, dirtyRect, framePt, textBaselinePt,
|
||||
textPaintStyle, provider);
|
||||
}
|
||||
|
||||
void
|
||||
nsTextFrame::DrawText(gfxContext* aCtx, const gfxPoint& aTextBaselinePt,
|
||||
PRUint32 aOffset, PRUint32 aLength,
|
||||
const gfxRect* aDirtyRect, PropertyProvider* aProvider,
|
||||
gfxFloat& aAdvanceWidth, PRBool aDrawSoftHyphen)
|
||||
{
|
||||
// Paint the text and soft-hyphen (if any) onto the given graphics context
|
||||
mTextRun->Draw(aCtx, aTextBaselinePt, aOffset, aLength,
|
||||
aDirtyRect, aProvider, &aAdvanceWidth);
|
||||
|
||||
if (aDrawSoftHyphen) {
|
||||
gfxFloat hyphenBaselineX = aTextBaselinePt.x + mTextRun->GetDirection() * aAdvanceWidth;
|
||||
// Don't use ctx as the context, because we need a reference context here,
|
||||
// ctx may be transformed.
|
||||
gfxTextRunCache::AutoTextRun hyphenTextRun(GetHyphenTextRun(mTextRun, nsnull, this));
|
||||
if (hyphenTextRun.get()) {
|
||||
hyphenTextRun->Draw(ctx, gfxPoint(hyphenBaselineX, textBaselinePt.y),
|
||||
0, hyphenTextRun->GetLength(), &dirtyRect, nsnull, nsnull);
|
||||
hyphenTextRun->Draw(aCtx, gfxPoint(hyphenBaselineX, aTextBaselinePt.y),
|
||||
0, hyphenTextRun->GetLength(), aDirtyRect, nsnull, nsnull);
|
||||
}
|
||||
}
|
||||
PaintTextDecorations(ctx, dirtyRect, framePt, textBaselinePt,
|
||||
textPaintStyle, provider);
|
||||
}
|
||||
|
||||
PRInt16
|
||||
|
|
|
@ -856,6 +856,12 @@ nsMathMLContainerFrame::GatherAndStoreOverflow(nsHTMLReflowMetrics* aMetrics)
|
|||
// frame rectangle.
|
||||
nsRect frameRect(0, 0, aMetrics->width, aMetrics->height);
|
||||
|
||||
// Text-shadow overflows.
|
||||
if (PresContext()->CompatibilityMode() != eCompatibility_NavQuirks) {
|
||||
nsRect shadowRect = nsLayoutUtils::GetTextShadowRectsUnion(frameRect, this);
|
||||
frameRect.UnionRect(frameRect, shadowRect);
|
||||
}
|
||||
|
||||
// All non-child-frame content such as nsMathMLChars (and most child-frame
|
||||
// content) is included in mBoundingMetrics.
|
||||
nsRect boundingBox(mBoundingMetrics.leftBearing,
|
||||
|
|
Загрузка…
Ссылка в новой задаче