зеркало из https://github.com/mozilla/gecko-dev.git
Implement text-shadow rendering (bug 10713). r+sr=roc. Relanding with fixes to make tests pass on Mac
This commit is contained in:
Родитель
e5412538bd
Коммит
129d4dc805
|
@ -25,6 +25,7 @@
|
||||||
* Takeshi Ichimaru <ayakawa.m@gmail.com>
|
* Takeshi Ichimaru <ayakawa.m@gmail.com>
|
||||||
* Masayuki Nakano <masayuki@d-toybox.com>
|
* Masayuki Nakano <masayuki@d-toybox.com>
|
||||||
* L. David Baron <dbaron@dbaron.org>, Mozilla Corporation
|
* 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
|
* 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"),
|
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||||
|
@ -4707,3 +4708,155 @@ nsCSSRendering::GetTextDecorationRectInternal(const gfxPoint& aPt,
|
||||||
r.pos.y = baseline - NS_floor(offset + 0.5);
|
r.pos.y = baseline - NS_floor(offset + 0.5);
|
||||||
return r;
|
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,
|
||||||
|
gfxContext* aDestinationCtx)
|
||||||
|
{
|
||||||
|
mBlurRadius = aBlurRadius / aAppUnitsPerDevPixel;
|
||||||
|
|
||||||
|
if (mBlurRadius <= 0) {
|
||||||
|
mContext = aDestinationCtx;
|
||||||
|
return mContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
mDestinationCtx = aDestinationCtx;
|
||||||
|
|
||||||
|
// 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()
|
||||||
|
{
|
||||||
|
if (mBlurRadius <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
mDestinationCtx->Mask(mImageSurface);
|
||||||
|
}
|
||||||
|
|
||||||
|
gfxContext*
|
||||||
|
nsContextBoxBlur::GetContext()
|
||||||
|
{
|
||||||
|
return mContext;
|
||||||
|
}
|
||||||
|
|
|
@ -43,6 +43,7 @@
|
||||||
#include "nsIRenderingContext.h"
|
#include "nsIRenderingContext.h"
|
||||||
#include "nsStyleConsts.h"
|
#include "nsStyleConsts.h"
|
||||||
#include "gfxContext.h"
|
#include "gfxContext.h"
|
||||||
|
#include "gfxImageSurface.h"
|
||||||
struct nsPoint;
|
struct nsPoint;
|
||||||
class nsStyleContext;
|
class nsStyleContext;
|
||||||
class nsPresContext;
|
class nsPresContext;
|
||||||
|
@ -306,5 +307,85 @@ protected:
|
||||||
const PRUint8 aStyle);
|
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.
|
||||||
|
*
|
||||||
|
* @param aDestinationCtx The graphics context to apply the blurred mask to
|
||||||
|
* when you call DoPaint(). Make sure it is not destroyed
|
||||||
|
* before you call DoPaint(). To set the color of the resulting
|
||||||
|
* blurred graphic mask, you must set the color on this
|
||||||
|
* context before calling Init().
|
||||||
|
*
|
||||||
|
* @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.
|
||||||
|
*
|
||||||
|
* If aBlurRadius is 0, the returned context is aDestinationCtx, because no blurring is required.
|
||||||
|
*/
|
||||||
|
gfxContext* Init(const gfxRect& aRect, nscoord aBlurRadius, PRInt32 aAppUnitsPerDevPixel,
|
||||||
|
gfxContext* aDestinationCtx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
void DoPaint();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
gfxContext* mDestinationCtx;
|
||||||
|
|
||||||
|
// Contrary to what is passed as parameters, these are in device pixels
|
||||||
|
gfxRect mRect;
|
||||||
|
PRInt32 mBlurRadius;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
#endif /* nsCSSRendering_h___ */
|
#endif /* nsCSSRendering_h___ */
|
||||||
|
|
|
@ -1286,6 +1286,30 @@ nsLayoutUtils::GetAllInFlowRectsUnion(nsIFrame* aFrame, nsIFrame* aRelativeTo) {
|
||||||
: accumulator.mResultRect;
|
: 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
|
nsresult
|
||||||
nsLayoutUtils::GetFontMetricsForFrame(nsIFrame* aFrame,
|
nsLayoutUtils::GetFontMetricsForFrame(nsIFrame* aFrame,
|
||||||
nsIFontMetrics** aFontMetrics)
|
nsIFontMetrics** aFontMetrics)
|
||||||
|
|
|
@ -502,6 +502,14 @@ public:
|
||||||
*/
|
*/
|
||||||
static nsRect GetAllInFlowRectsUnion(nsIFrame* aFrame, nsIFrame* aRelativeTo);
|
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.
|
* Get the font metrics corresponding to the frame's style data.
|
||||||
* @param aFrame the frame
|
* @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
|
// XXX_perf: This can be done incrementally. It is currently one of
|
||||||
// the things that makes incremental reflow O(N^2).
|
// the things that makes incremental reflow O(N^2).
|
||||||
nsRect area(0, 0, aMetrics.width, aMetrics.height);
|
nsRect area(0, 0, aMetrics.width, aMetrics.height);
|
||||||
|
|
||||||
if (NS_STYLE_OVERFLOW_CLIP != aReflowState.mStyleDisplay->mOverflowX) {
|
if (NS_STYLE_OVERFLOW_CLIP != aReflowState.mStyleDisplay->mOverflowX) {
|
||||||
|
PRBool inQuirks = (PresContext()->CompatibilityMode() == eCompatibility_NavQuirks);
|
||||||
for (line_iterator line = begin_lines(), line_end = end_lines();
|
for (line_iterator line = begin_lines(), line_end = end_lines();
|
||||||
line != line_end;
|
line != line_end;
|
||||||
++line) {
|
++line) {
|
||||||
|
|
||||||
|
// Text-shadow overflows
|
||||||
|
if (!inQuirks && line->IsInline()) {
|
||||||
|
nsRect shadowRect = nsLayoutUtils::GetTextShadowRectsUnion(line->GetCombinedArea(),
|
||||||
|
this);
|
||||||
|
area.UnionRect(area, shadowRect);
|
||||||
|
}
|
||||||
|
|
||||||
area.UnionRect(area, line->GetCombinedArea());
|
area.UnionRect(area, line->GetCombinedArea());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5904,7 +5914,7 @@ nsBlockFrame::IsVisibleInSelection(nsISelection* aSelection)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* virtual */ void
|
/* virtual */ void
|
||||||
nsBlockFrame::PaintTextDecorationLine(nsIRenderingContext& aRenderingContext,
|
nsBlockFrame::PaintTextDecorationLine(gfxContext* aCtx,
|
||||||
const nsPoint& aPt,
|
const nsPoint& aPt,
|
||||||
nsLineBox* aLine,
|
nsLineBox* aLine,
|
||||||
nscolor aColor,
|
nscolor aColor,
|
||||||
|
@ -5945,12 +5955,11 @@ nsBlockFrame::PaintTextDecorationLine(nsIRenderingContext& aRenderingContext,
|
||||||
|
|
||||||
// Only paint if we have a positive width
|
// Only paint if we have a positive width
|
||||||
if (width > 0) {
|
if (width > 0) {
|
||||||
gfxContext *ctx = aRenderingContext.ThebesContext();
|
|
||||||
gfxPoint pt(PresContext()->AppUnitsToGfxUnits(start + aPt.x),
|
gfxPoint pt(PresContext()->AppUnitsToGfxUnits(start + aPt.x),
|
||||||
PresContext()->AppUnitsToGfxUnits(aLine->mBounds.y + aPt.y));
|
PresContext()->AppUnitsToGfxUnits(aLine->mBounds.y + aPt.y));
|
||||||
gfxSize size(PresContext()->AppUnitsToGfxUnits(width), aSize);
|
gfxSize size(PresContext()->AppUnitsToGfxUnits(width), aSize);
|
||||||
nsCSSRendering::PaintDecorationLine(
|
nsCSSRendering::PaintDecorationLine(
|
||||||
ctx, aColor, pt, size,
|
aCtx, aColor, pt, size,
|
||||||
PresContext()->AppUnitsToGfxUnits(aLine->GetAscent()),
|
PresContext()->AppUnitsToGfxUnits(aLine->GetAscent()),
|
||||||
aOffset, aDecoration, NS_STYLE_BORDER_STYLE_SOLID);
|
aOffset, aDecoration, NS_STYLE_BORDER_STYLE_SOLID);
|
||||||
}
|
}
|
||||||
|
|
|
@ -341,7 +341,7 @@ protected:
|
||||||
* Overides member function of nsHTMLContainerFrame. Needed to handle the
|
* Overides member function of nsHTMLContainerFrame. Needed to handle the
|
||||||
* lines in a nsBlockFrame properly.
|
* lines in a nsBlockFrame properly.
|
||||||
*/
|
*/
|
||||||
virtual void PaintTextDecorationLine(nsIRenderingContext& aRenderingContext,
|
virtual void PaintTextDecorationLine(gfxContext* aCtx,
|
||||||
const nsPoint& aPt,
|
const nsPoint& aPt,
|
||||||
nsLineBox* aLine,
|
nsLineBox* aLine,
|
||||||
nscolor aColor,
|
nscolor aColor,
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
* the Initial Developer. All Rights Reserved.
|
* the Initial Developer. All Rights Reserved.
|
||||||
*
|
*
|
||||||
* Contributor(s):
|
* Contributor(s):
|
||||||
|
* Michael Ventnor <m.ventnor@gmail.com>
|
||||||
*
|
*
|
||||||
* Alternatively, the contents of this file may be used under the terms of
|
* 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"),
|
* 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);
|
nsHTMLContainerFrame* f = static_cast<nsHTMLContainerFrame*>(mFrame);
|
||||||
if (mDecoration == NS_STYLE_TEXT_DECORATION_UNDERLINE) {
|
if (mDecoration == NS_STYLE_TEXT_DECORATION_UNDERLINE) {
|
||||||
gfxFloat underlineOffset = fontGroup->GetUnderlineOffset();
|
gfxFloat underlineOffset = fontGroup->GetUnderlineOffset();
|
||||||
f->PaintTextDecorationLine(*aCtx, pt, mLine, mColor,
|
f->PaintTextDecorationLine(aCtx->ThebesContext(), pt, mLine, mColor,
|
||||||
underlineOffset, ascent,
|
underlineOffset, ascent,
|
||||||
metrics.underlineSize, mDecoration);
|
metrics.underlineSize, mDecoration);
|
||||||
} else if (mDecoration == NS_STYLE_TEXT_DECORATION_OVERLINE) {
|
} 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.maxAscent, ascent,
|
||||||
metrics.underlineSize, mDecoration);
|
metrics.underlineSize, mDecoration);
|
||||||
} else {
|
} else {
|
||||||
f->PaintTextDecorationLine(*aCtx, pt, mLine, mColor,
|
f->PaintTextDecorationLine(aCtx->ThebesContext(), pt, mLine, mColor,
|
||||||
metrics.strikeoutOffset, ascent,
|
metrics.strikeoutOffset, ascent,
|
||||||
metrics.strikeoutSize, mDecoration);
|
metrics.strikeoutSize, mDecoration);
|
||||||
}
|
}
|
||||||
|
@ -151,6 +152,95 @@ nsDisplayTextDecoration::GetBounds(nsDisplayListBuilder* aBuilder)
|
||||||
return mFrame->GetOverflowRect() + aBuilder->ToReferenceFrame(mFrame);
|
return mFrame->GetOverflowRect() + aBuilder->ToReferenceFrame(mFrame);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class nsDisplayTextShadow : public nsDisplayItem {
|
||||||
|
public:
|
||||||
|
nsDisplayTextShadow(nsHTMLContainerFrame* aFrame, const PRUint8 aDecoration,
|
||||||
|
const nscolor& 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;
|
||||||
|
nscolor 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);
|
||||||
|
gfxContext* thebesCtx = aCtx->ThebesContext();
|
||||||
|
|
||||||
|
nsContextBoxBlur contextBoxBlur;
|
||||||
|
gfxContext* shadowCtx = contextBoxBlur.Init(shadowRect, mBlurRadius,
|
||||||
|
mFrame->PresContext()->AppUnitsPerDevPixel(),
|
||||||
|
thebesCtx);
|
||||||
|
if (!shadowCtx)
|
||||||
|
return;
|
||||||
|
|
||||||
|
thebesCtx->Save();
|
||||||
|
thebesCtx->NewPath();
|
||||||
|
thebesCtx->SetColor(gfxRGBA(mColor));
|
||||||
|
|
||||||
|
if (mDecorationFlags & NS_STYLE_TEXT_DECORATION_UNDERLINE) {
|
||||||
|
gfxFloat underlineOffset = fontGroup->GetUnderlineOffset();
|
||||||
|
f->PaintTextDecorationLine(shadowCtx, pt, mLine, mColor,
|
||||||
|
underlineOffset, metrics.maxAscent,
|
||||||
|
metrics.underlineSize, NS_STYLE_TEXT_DECORATION_UNDERLINE);
|
||||||
|
}
|
||||||
|
if (mDecorationFlags & NS_STYLE_TEXT_DECORATION_OVERLINE) {
|
||||||
|
f->PaintTextDecorationLine(shadowCtx, pt, mLine, mColor,
|
||||||
|
metrics.maxAscent, metrics.maxAscent,
|
||||||
|
metrics.underlineSize, NS_STYLE_TEXT_DECORATION_OVERLINE);
|
||||||
|
}
|
||||||
|
if (mDecorationFlags & NS_STYLE_TEXT_DECORATION_LINE_THROUGH) {
|
||||||
|
f->PaintTextDecorationLine(shadowCtx, pt, mLine, mColor,
|
||||||
|
metrics.strikeoutOffset, metrics.maxAscent,
|
||||||
|
metrics.strikeoutSize, NS_STYLE_TEXT_DECORATION_LINE_THROUGH);
|
||||||
|
}
|
||||||
|
|
||||||
|
contextBoxBlur.DoPaint();
|
||||||
|
thebesCtx->Restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
nsRect
|
||||||
|
nsDisplayTextShadow::GetBounds(nsDisplayListBuilder* aBuilder)
|
||||||
|
{
|
||||||
|
// Shadows are always painted in the overflow rect
|
||||||
|
return mFrame->GetOverflowRect() + aBuilder->ToReferenceFrame(mFrame);
|
||||||
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
nsHTMLContainerFrame::DisplayTextDecorations(nsDisplayListBuilder* aBuilder,
|
nsHTMLContainerFrame::DisplayTextDecorations(nsDisplayListBuilder* aBuilder,
|
||||||
nsDisplayList* aBelowTextDecorations,
|
nsDisplayList* aBelowTextDecorations,
|
||||||
|
@ -170,6 +260,34 @@ nsHTMLContainerFrame::DisplayTextDecorations(nsDisplayListBuilder* aBuilder,
|
||||||
GetTextDecorations(PresContext(), aLine != nsnull, decorations, underColor,
|
GetTextDecorations(PresContext(), aLine != nsnull, decorations, underColor,
|
||||||
overColor, strikeColor);
|
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();
|
||||||
|
nscolor shadowColor;
|
||||||
|
|
||||||
|
if (shadow->mHasColor)
|
||||||
|
shadowColor = 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) {
|
if (decorations & NS_STYLE_TEXT_DECORATION_UNDERLINE) {
|
||||||
nsresult rv = aBelowTextDecorations->AppendNewToTop(new (aBuilder)
|
nsresult rv = aBelowTextDecorations->AppendNewToTop(new (aBuilder)
|
||||||
nsDisplayTextDecoration(this, NS_STYLE_TEXT_DECORATION_UNDERLINE, underColor, aLine));
|
nsDisplayTextDecoration(this, NS_STYLE_TEXT_DECORATION_UNDERLINE, underColor, aLine));
|
||||||
|
@ -221,7 +339,7 @@ HasTextFrameDescendantOrInFlow(nsIFrame* aFrame);
|
||||||
|
|
||||||
/*virtual*/ void
|
/*virtual*/ void
|
||||||
nsHTMLContainerFrame::PaintTextDecorationLine(
|
nsHTMLContainerFrame::PaintTextDecorationLine(
|
||||||
nsIRenderingContext& aRenderingContext,
|
gfxContext* aCtx,
|
||||||
const nsPoint& aPt,
|
const nsPoint& aPt,
|
||||||
nsLineBox* aLine,
|
nsLineBox* aLine,
|
||||||
nscolor aColor,
|
nscolor aColor,
|
||||||
|
@ -239,11 +357,10 @@ nsHTMLContainerFrame::PaintTextDecorationLine(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
nscoord innerWidth = mRect.width - bp.left - bp.right;
|
nscoord innerWidth = mRect.width - bp.left - bp.right;
|
||||||
gfxContext *ctx = aRenderingContext.ThebesContext();
|
|
||||||
gfxPoint pt(PresContext()->AppUnitsToGfxUnits(bp.left + aPt.x),
|
gfxPoint pt(PresContext()->AppUnitsToGfxUnits(bp.left + aPt.x),
|
||||||
PresContext()->AppUnitsToGfxUnits(bp.top + aPt.y));
|
PresContext()->AppUnitsToGfxUnits(bp.top + aPt.y));
|
||||||
gfxSize size(PresContext()->AppUnitsToGfxUnits(innerWidth), aSize);
|
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);
|
aDecoration, NS_STYLE_BORDER_STYLE_SOLID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -161,7 +161,7 @@ protected:
|
||||||
/**
|
/**
|
||||||
* Function that does the actual drawing of the textdecoration.
|
* Function that does the actual drawing of the textdecoration.
|
||||||
* input:
|
* 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 aLine the line, or nsnull if this is an inline frame
|
||||||
* @param aColor the color of the text-decoration
|
* @param aColor the color of the text-decoration
|
||||||
* @param aAscent ascent of the font from which the
|
* @param aAscent ascent of the font from which the
|
||||||
|
@ -176,7 +176,7 @@ protected:
|
||||||
* NS_STYLE_TEXT_DECORATION_OVERLINE or
|
* NS_STYLE_TEXT_DECORATION_OVERLINE or
|
||||||
* NS_STYLE_TEXT_DECORATION_LINE_THROUGH.
|
* NS_STYLE_TEXT_DECORATION_LINE_THROUGH.
|
||||||
*/
|
*/
|
||||||
virtual void PaintTextDecorationLine(nsIRenderingContext& aRenderingContext,
|
virtual void PaintTextDecorationLine(gfxContext* aCtx,
|
||||||
const nsPoint& aPt,
|
const nsPoint& aPt,
|
||||||
nsLineBox* aLine,
|
nsLineBox* aLine,
|
||||||
nscolor aColor,
|
nscolor aColor,
|
||||||
|
@ -186,6 +186,7 @@ protected:
|
||||||
const PRUint8 aDecoration);
|
const PRUint8 aDecoration);
|
||||||
|
|
||||||
friend class nsDisplayTextDecoration;
|
friend class nsDisplayTextDecoration;
|
||||||
|
friend class nsDisplayTextShadow;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* nsHTMLContainerFrame_h___ */
|
#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
|
// bidi reordering can move and resize the frames. So use the frame's
|
||||||
// rect instead of mBounds.
|
// rect instead of mBounds.
|
||||||
nsRect adjustedBounds(nsPoint(0, 0), psd->mFrame->mFrame->GetSize());
|
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);
|
combinedAreaResult.UnionRect(psd->mFrame->mCombinedArea, adjustedBounds);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -54,6 +54,7 @@
|
||||||
#include "nsLineBox.h"
|
#include "nsLineBox.h"
|
||||||
#include "gfxFont.h"
|
#include "gfxFont.h"
|
||||||
#include "gfxSkipChars.h"
|
#include "gfxSkipChars.h"
|
||||||
|
#include "gfxContext.h"
|
||||||
|
|
||||||
class nsTextPaintStyle;
|
class nsTextPaintStyle;
|
||||||
class PropertyProvider;
|
class PropertyProvider;
|
||||||
|
@ -253,6 +254,7 @@ public:
|
||||||
gfxFloat GetSnappedBaselineY(gfxContext* aContext, gfxFloat aY);
|
gfxFloat GetSnappedBaselineY(gfxContext* aContext, gfxFloat aY);
|
||||||
|
|
||||||
// primary frame paint method called from nsDisplayText
|
// 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,
|
void PaintText(nsIRenderingContext* aRenderingContext, nsPoint aPt,
|
||||||
const nsRect& aDirtyRect);
|
const nsRect& aDirtyRect);
|
||||||
// helper: paint quirks-mode CSS text decorations
|
// helper: paint quirks-mode CSS text decorations
|
||||||
|
@ -260,7 +262,8 @@ public:
|
||||||
const gfxPoint& aFramePt,
|
const gfxPoint& aFramePt,
|
||||||
const gfxPoint& aTextBaselinePt,
|
const gfxPoint& aTextBaselinePt,
|
||||||
nsTextPaintStyle& aTextStyle,
|
nsTextPaintStyle& aTextStyle,
|
||||||
PropertyProvider& aProvider);
|
PropertyProvider& aProvider,
|
||||||
|
const nscolor& aOverrideColor = 0);
|
||||||
// helper: paint text frame when we're impacted by at least one selection.
|
// helper: paint text frame when we're impacted by at least one selection.
|
||||||
// Return PR_FALSE if the text was not painted and we should continue with
|
// Return PR_FALSE if the text was not painted and we should continue with
|
||||||
// the fast path.
|
// the fast path.
|
||||||
|
@ -382,6 +385,25 @@ protected:
|
||||||
PropertyProvider& aProvider,
|
PropertyProvider& aProvider,
|
||||||
nsRect* aOverflowRect);
|
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 nscolor& aForegroundColor);
|
||||||
|
|
||||||
struct TextDecorations {
|
struct TextDecorations {
|
||||||
PRUint8 mDecorations;
|
PRUint8 mDecorations;
|
||||||
nscolor mOverColor;
|
nscolor mOverColor;
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
* Mats Palmgren <mats.palmgren@bredband.net>
|
* Mats Palmgren <mats.palmgren@bredband.net>
|
||||||
* Uri Bernstein <uriber@gmail.com>
|
* Uri Bernstein <uriber@gmail.com>
|
||||||
* Stephen Blackheath <entangled.mooched.stephen@blacksapphire.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
|
* 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"),
|
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||||
|
@ -115,6 +116,7 @@
|
||||||
#include "gfxFont.h"
|
#include "gfxFont.h"
|
||||||
#include "gfxContext.h"
|
#include "gfxContext.h"
|
||||||
#include "gfxTextRunWordCache.h"
|
#include "gfxTextRunWordCache.h"
|
||||||
|
#include "gfxImageSurface.h"
|
||||||
|
|
||||||
#ifdef NS_DEBUG
|
#ifdef NS_DEBUG
|
||||||
#undef NOISY_BLINK
|
#undef NOISY_BLINK
|
||||||
|
@ -3675,6 +3677,10 @@ nsTextFrame::UnionTextDecorationOverflow(nsPresContext* aPresContext,
|
||||||
PropertyProvider& aProvider,
|
PropertyProvider& aProvider,
|
||||||
nsRect* aOverflowRect)
|
nsRect* aOverflowRect)
|
||||||
{
|
{
|
||||||
|
// Text-shadow overflows
|
||||||
|
nsRect shadowRect = nsLayoutUtils::GetTextShadowRectsUnion(*aOverflowRect, this);
|
||||||
|
aOverflowRect->UnionRect(*aOverflowRect, shadowRect);
|
||||||
|
|
||||||
if (IsFloatingFirstLetterChild()) {
|
if (IsFloatingFirstLetterChild()) {
|
||||||
// The underline/overline drawable area must be contained in the overflow
|
// The underline/overline drawable area must be contained in the overflow
|
||||||
// rect when this is in floating first letter frame at *both* modes.
|
// rect when this is in floating first letter frame at *both* modes.
|
||||||
|
@ -3704,7 +3710,8 @@ nsTextFrame::PaintTextDecorations(gfxContext* aCtx, const gfxRect& aDirtyRect,
|
||||||
const gfxPoint& aFramePt,
|
const gfxPoint& aFramePt,
|
||||||
const gfxPoint& aTextBaselinePt,
|
const gfxPoint& aTextBaselinePt,
|
||||||
nsTextPaintStyle& aTextPaintStyle,
|
nsTextPaintStyle& aTextPaintStyle,
|
||||||
PropertyProvider& aProvider)
|
PropertyProvider& aProvider,
|
||||||
|
const nscolor& aOverrideColor)
|
||||||
{
|
{
|
||||||
TextDecorations decorations =
|
TextDecorations decorations =
|
||||||
GetTextDecorations(aTextPaintStyle.PresContext());
|
GetTextDecorations(aTextPaintStyle.PresContext());
|
||||||
|
@ -3722,24 +3729,28 @@ nsTextFrame::PaintTextDecorations(gfxContext* aCtx, const gfxRect& aDirtyRect,
|
||||||
gfxSize size(GetRect().width / app, 0);
|
gfxSize size(GetRect().width / app, 0);
|
||||||
gfxFloat ascent = gfxFloat(mAscent) / app;
|
gfxFloat ascent = gfxFloat(mAscent) / app;
|
||||||
|
|
||||||
|
nscolor lineColor;
|
||||||
if (decorations.HasOverline()) {
|
if (decorations.HasOverline()) {
|
||||||
|
lineColor = aOverrideColor ? aOverrideColor : decorations.mOverColor;
|
||||||
size.height = fontMetrics.underlineSize;
|
size.height = fontMetrics.underlineSize;
|
||||||
nsCSSRendering::PaintDecorationLine(
|
nsCSSRendering::PaintDecorationLine(
|
||||||
aCtx, decorations.mOverColor, pt, size, ascent, fontMetrics.maxAscent,
|
aCtx, lineColor, pt, size, ascent, fontMetrics.maxAscent,
|
||||||
NS_STYLE_TEXT_DECORATION_OVERLINE, NS_STYLE_BORDER_STYLE_SOLID);
|
NS_STYLE_TEXT_DECORATION_OVERLINE, NS_STYLE_BORDER_STYLE_SOLID);
|
||||||
}
|
}
|
||||||
if (decorations.HasUnderline()) {
|
if (decorations.HasUnderline()) {
|
||||||
|
lineColor = aOverrideColor ? aOverrideColor : decorations.mUnderColor;
|
||||||
size.height = fontMetrics.underlineSize;
|
size.height = fontMetrics.underlineSize;
|
||||||
gfxFloat offset = aProvider.GetFontGroup()->GetUnderlineOffset();
|
gfxFloat offset = aProvider.GetFontGroup()->GetUnderlineOffset();
|
||||||
nsCSSRendering::PaintDecorationLine(
|
nsCSSRendering::PaintDecorationLine(
|
||||||
aCtx, decorations.mUnderColor, pt, size, ascent, offset,
|
aCtx, lineColor, pt, size, ascent, offset,
|
||||||
NS_STYLE_TEXT_DECORATION_UNDERLINE, NS_STYLE_BORDER_STYLE_SOLID);
|
NS_STYLE_TEXT_DECORATION_UNDERLINE, NS_STYLE_BORDER_STYLE_SOLID);
|
||||||
}
|
}
|
||||||
if (decorations.HasStrikeout()) {
|
if (decorations.HasStrikeout()) {
|
||||||
|
lineColor = aOverrideColor ? aOverrideColor : decorations.mStrikeColor;
|
||||||
size.height = fontMetrics.strikeoutSize;
|
size.height = fontMetrics.strikeoutSize;
|
||||||
gfxFloat offset = fontMetrics.strikeoutOffset;
|
gfxFloat offset = fontMetrics.strikeoutOffset;
|
||||||
nsCSSRendering::PaintDecorationLine(
|
nsCSSRendering::PaintDecorationLine(
|
||||||
aCtx, decorations.mStrikeColor, pt, size, ascent, offset,
|
aCtx, lineColor, pt, size, ascent, offset,
|
||||||
NS_STYLE_TEXT_DECORATION_LINE_THROUGH, NS_STYLE_BORDER_STYLE_SOLID);
|
NS_STYLE_TEXT_DECORATION_LINE_THROUGH, NS_STYLE_BORDER_STYLE_SOLID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3948,6 +3959,72 @@ PRBool SelectionIterator::GetNextSegment(gfxFloat* aXOffset,
|
||||||
return PR_TRUE;
|
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 nscolor& 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(),
|
||||||
|
aCtx);
|
||||||
|
if (!shadowContext)
|
||||||
|
return;
|
||||||
|
|
||||||
|
nscolor shadowColor;
|
||||||
|
if (aShadowDetails->mHasColor)
|
||||||
|
shadowColor = aShadowDetails->mColor;
|
||||||
|
else
|
||||||
|
shadowColor = aForegroundColor;
|
||||||
|
|
||||||
|
aCtx->Save();
|
||||||
|
aCtx->NewPath();
|
||||||
|
aCtx->SetColor(gfxRGBA(shadowColor));
|
||||||
|
|
||||||
|
// 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, shadowColor);
|
||||||
|
|
||||||
|
contextBoxBlur.DoPaint();
|
||||||
|
aCtx->Restore();
|
||||||
|
}
|
||||||
|
|
||||||
// Paints selection backgrounds and text in the correct colors. Also computes
|
// Paints selection backgrounds and text in the correct colors. Also computes
|
||||||
// aAllTypes, the union of all selection types that are applying to this text.
|
// aAllTypes, the union of all selection types that are applying to this text.
|
||||||
void
|
void
|
||||||
|
@ -4031,18 +4108,11 @@ nsTextFrame::PaintTextWithSelectionColors(gfxContext* aCtx,
|
||||||
// Draw text segment
|
// Draw text segment
|
||||||
aCtx->SetColor(gfxRGBA(foreground));
|
aCtx->SetColor(gfxRGBA(foreground));
|
||||||
gfxFloat advance;
|
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) {
|
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;
|
advance += hyphenWidth;
|
||||||
}
|
}
|
||||||
iterator.UpdateWithAdvance(advance);
|
iterator.UpdateWithAdvance(advance);
|
||||||
|
@ -4193,6 +4263,23 @@ nsTextFrame::PaintText(nsIRenderingContext* aRenderingContext, nsPoint aPt,
|
||||||
gfxRect dirtyRect(aDirtyRect.x, aDirtyRect.y,
|
gfxRect dirtyRect(aDirtyRect.x, aDirtyRect.y,
|
||||||
aDirtyRect.width, aDirtyRect.height);
|
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,
|
||||||
|
textPaintStyle.GetTextColor());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Fork off to the (slower) paint-with-selection path if necessary.
|
// Fork off to the (slower) paint-with-selection path if necessary.
|
||||||
if (GetNonGeneratedAncestor(this)->GetStateBits() & NS_FRAME_SELECTED_CONTENT) {
|
if (GetNonGeneratedAncestor(this)->GetStateBits() & NS_FRAME_SELECTED_CONTENT) {
|
||||||
if (PaintTextWithSelection(ctx, framePt, textBaselinePt,
|
if (PaintTextWithSelection(ctx, framePt, textBaselinePt,
|
||||||
|
@ -4200,27 +4287,36 @@ nsTextFrame::PaintText(nsIRenderingContext* aRenderingContext, nsPoint aPt,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
gfxFloat advanceWidth;
|
ctx->SetColor(foregroundColor);
|
||||||
gfxFloat* needAdvanceWidth =
|
|
||||||
(GetStateBits() & TEXT_HYPHEN_BREAK) ? &advanceWidth : nsnull;
|
|
||||||
ctx->SetColor(gfxRGBA(textPaintStyle.GetTextColor()));
|
|
||||||
|
|
||||||
mTextRun->Draw(ctx, textBaselinePt,
|
DrawText(ctx, textBaselinePt, provider.GetStart().GetSkippedOffset(),
|
||||||
provider.GetStart().GetSkippedOffset(),
|
ComputeTransformedLength(provider), &dirtyRect,
|
||||||
ComputeTransformedLength(provider),
|
&provider, advanceWidth,
|
||||||
&dirtyRect, &provider, needAdvanceWidth);
|
(GetStateBits() & TEXT_HYPHEN_BREAK) != 0);
|
||||||
if (GetStateBits() & TEXT_HYPHEN_BREAK) {
|
PaintTextDecorations(ctx, dirtyRect, framePt, textBaselinePt,
|
||||||
gfxFloat hyphenBaselineX = textBaselinePt.x + mTextRun->GetDirection()*advanceWidth;
|
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,
|
// Don't use ctx as the context, because we need a reference context here,
|
||||||
// ctx may be transformed.
|
// ctx may be transformed.
|
||||||
gfxTextRunCache::AutoTextRun hyphenTextRun(GetHyphenTextRun(mTextRun, nsnull, this));
|
gfxTextRunCache::AutoTextRun hyphenTextRun(GetHyphenTextRun(mTextRun, nsnull, this));
|
||||||
if (hyphenTextRun.get()) {
|
if (hyphenTextRun.get()) {
|
||||||
hyphenTextRun->Draw(ctx, gfxPoint(hyphenBaselineX, textBaselinePt.y),
|
hyphenTextRun->Draw(aCtx, gfxPoint(hyphenBaselineX, aTextBaselinePt.y),
|
||||||
0, hyphenTextRun->GetLength(), &dirtyRect, nsnull, nsnull);
|
0, hyphenTextRun->GetLength(), aDirtyRect, nsnull, nsnull);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PaintTextDecorations(ctx, dirtyRect, framePt, textBaselinePt,
|
|
||||||
textPaintStyle, provider);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PRInt16
|
PRInt16
|
||||||
|
|
|
@ -856,6 +856,12 @@ nsMathMLContainerFrame::GatherAndStoreOverflow(nsHTMLReflowMetrics* aMetrics)
|
||||||
// frame rectangle.
|
// frame rectangle.
|
||||||
nsRect frameRect(0, 0, aMetrics->width, aMetrics->height);
|
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
|
// All non-child-frame content such as nsMathMLChars (and most child-frame
|
||||||
// content) is included in mBoundingMetrics.
|
// content) is included in mBoundingMetrics.
|
||||||
nsRect boundingBox(mBoundingMetrics.leftBearing,
|
nsRect boundingBox(mBoundingMetrics.leftBearing,
|
||||||
|
|
|
@ -55,7 +55,7 @@ include text/reftest.list
|
||||||
include text-decoration/reftest.list
|
include text-decoration/reftest.list
|
||||||
|
|
||||||
# text-shadow/
|
# text-shadow/
|
||||||
# include text-shadow/reftest.list
|
include text-shadow/reftest.list
|
||||||
|
|
||||||
# text-indent/
|
# text-indent/
|
||||||
include text-indent/reftest.list
|
include text-indent/reftest.list
|
||||||
|
|
|
@ -1,13 +1,25 @@
|
||||||
<!DOCTYPE HTML>
|
<!DOCTYPE HTML>
|
||||||
|
|
||||||
<!-- blue/green underline -->
|
<!-- blue underline -->
|
||||||
<div style="position: absolute; top: 22px; left: 22px; color: blue; text-decoration: underline;"><span style="color: rgba(0, 0, 0, 0);">testfor</span><span style="text-decoration: underline; color: green;"><span style="color: rgba(0, 0, 0, 0);">quirks</span></span></div>
|
<div style="position: absolute; top: 22px; left: 22px; color: blue; text-decoration: underline;"><span style="color: rgba(0, 0, 0, 0);">testforquirks</span></div>
|
||||||
<div style="position: absolute; top: 20px; left: 20px; color: blue; text-decoration: underline;"><span style="color: rgba(0, 0, 0, 0);">testfor</span><span style="text-decoration: underline; color: green;"><span style="color: rgba(0, 0, 0, 0);">quirks</span></span></div>
|
<div style="position: absolute; top: 20px; left: 20px; color: blue; text-decoration: underline;"><span style="color: rgba(0, 0, 0, 0);">testforquirks</span></div>
|
||||||
|
|
||||||
|
<!-- blue text -->
|
||||||
|
<div style="position: absolute; top: 22px; left: 22px;"><span style="color: blue;">test</span></div>
|
||||||
|
<div style="position: absolute; top: 20px; left: 20px;"><span style="color: blue;">test</span></div>
|
||||||
|
|
||||||
<!-- red overline -->
|
<!-- red overline -->
|
||||||
<div style="position: absolute; top: 22px; left: 22px; color: rgba(0, 0, 0, 0);">test<span style="text-decoration: overline; color: red;"><span style="color: rgba(0, 0, 0, 0);">forquirks</span></span></div>
|
<div style="position: absolute; top: 22px; left: 22px; color: rgba(0, 0, 0, 0);">test<span style="text-decoration: overline; color: red;"><span style="color: rgba(0, 0, 0, 0);">forquirks</span></span></div>
|
||||||
<div style="position: absolute; top: 20px; left: 20px; color: rgba(0, 0, 0, 0);">test<span style="text-decoration: overline; color: red;"><span style="color: rgba(0, 0, 0, 0);">forquirks</span></span></div>
|
<div style="position: absolute; top: 20px; left: 20px; color: rgba(0, 0, 0, 0);">test<span style="text-decoration: overline; color: red;"><span style="color: rgba(0, 0, 0, 0);">forquirks</span></span></div>
|
||||||
|
|
||||||
<!-- the actual text -->
|
<!-- red text -->
|
||||||
<div style="position: absolute; top: 22px; left: 22px;"><span style="color: blue;">test</span><span style="color: red;">for</span><span style="color: green;">quirks</span></div>
|
<div style="position: absolute; top: 22px; left: 22px;"><span style="color: rgba(0, 0, 0, 0);">test</span><span style="color: red;">for</span></div>
|
||||||
<div style="position: absolute; top: 20px; left: 20px;"><span style="color: blue;">test</span><span style="color: red;">for</span><span style="color: green;">quirks</span></div>
|
<div style="position: absolute; top: 20px; left: 20px;"><span style="color: rgba(0, 0, 0, 0);">test</span><span style="color: red;">for</span></div>
|
||||||
|
|
||||||
|
<!-- green underline -->
|
||||||
|
<div style="position: absolute; top: 22px; left: 22px;"><span style="color: rgba(0, 0, 0, 0);">testfor</span><span style="text-decoration: underline; color: green;"><span style="color: rgba(0, 0, 0, 0);">quirks</span></span></div>
|
||||||
|
<div style="position: absolute; top: 20px; left: 20px;"><span style="color: rgba(0, 0, 0, 0);">testfor</span><span style="text-decoration: underline; color: green;"><span style="color: rgba(0, 0, 0, 0);">quirks</span></span></div>
|
||||||
|
|
||||||
|
<!-- green text -->
|
||||||
|
<div style="position: absolute; top: 22px; left: 22px;"><span style="color: rgba(0, 0, 0, 0);">testfor</span><span style="color: green;">quirks</span></div>
|
||||||
|
<div style="position: absolute; top: 20px; left: 20px;"><span style="color: rgba(0, 0, 0, 0);">testfor</span><span style="color: green;">quirks</span></div>
|
||||||
|
|
Загрузка…
Ссылка в новой задаче