Bug 710521. Small refactor of gfxFont to separate drawing to paths and drawing strokes. r=roc

This commit is contained in:
Edwin Flores 2011-12-18 21:53:03 +13:00
parent dfdcd3935b
commit a9f4f6c3c4
7 changed files with 111 additions and 133 deletions

View File

@ -2792,22 +2792,14 @@ struct NS_STACK_CLASS nsCanvasBidiProcessor : public nsBidiPresUtils::BidiProces
// throughout the text layout process
}
// stroke or fill the text depending on operation
if (mOp == nsCanvasRenderingContext2D::TEXT_DRAW_OPERATION_STROKE)
mTextRun->DrawToPath(mThebes,
point,
0,
mTextRun->GetLength(),
nsnull,
nsnull);
else
// mOp == TEXT_DRAW_OPERATION_FILL
mTextRun->Draw(mThebes,
point,
0,
mTextRun->GetLength(),
nsnull,
nsnull);
mTextRun->Draw(mThebes,
point,
mOp == nsCanvasRenderingContext2D::TEXT_DRAW_OPERATION_STROKE ?
gfxFont::GLYPH_STROKE : gfxFont::GLYPH_FILL,
0,
mTextRun->GetLength(),
nsnull,
nsnull);
}
// current text run
@ -2889,10 +2881,11 @@ nsCanvasRenderingContext2D::DrawOrMeasureText(const nsAString& aRawText,
isRTL = GET_BIDI_OPTION_DIRECTION(document->GetBidiOptions()) == IBMBIDI_TEXTDIRECTION_RTL;
}
// don't need to take care of these with stroke since Stroke() does that
bool doDrawShadow = aOp == TEXT_DRAW_OPERATION_FILL && NeedToDrawShadow();
bool doUseIntermediateSurface = aOp == TEXT_DRAW_OPERATION_FILL &&
(NeedToUseIntermediateSurface() || NeedIntermediateSurfaceToHandleGlobalAlpha(STYLE_FILL));
Style style = aOp == TEXT_DRAW_OPERATION_FILL ? STYLE_FILL : STYLE_STROKE;
bool doDrawShadow = NeedToDrawShadow();
bool doUseIntermediateSurface = NeedToUseIntermediateSurface()
|| NeedIntermediateSurfaceToHandleGlobalAlpha(style);
// Clear the surface if we need to simulate unbounded SOURCE operator
ClearSurfaceForUnboundedSource();
@ -3029,6 +3022,7 @@ nsCanvasRenderingContext2D::DrawOrMeasureText(const nsAString& aRawText,
gfxContext* ctx = ShadowInitialize(drawExtents, blur);
if (ctx) {
ApplyStyle(style, false);
CopyContext(ctx, mThebes);
ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
processor.mThebes = ctx;
@ -3054,23 +3048,15 @@ nsCanvasRenderingContext2D::DrawOrMeasureText(const nsAString& aRawText,
gfxContextPathAutoSaveRestore pathSR(mThebes, false);
// back up and clear path if stroking
if (aOp == nsCanvasRenderingContext2D::TEXT_DRAW_OPERATION_STROKE) {
pathSR.Save();
mThebes->NewPath();
}
// doUseIntermediateSurface is mutually exclusive to op == STROKE
else {
if (doUseIntermediateSurface) {
mThebes->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
if (doUseIntermediateSurface) {
mThebes->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
// don't want operators to be applied twice
mThebes->SetOperator(gfxContext::OPERATOR_SOURCE);
}
ApplyStyle(STYLE_FILL);
// don't want operators to be applied twice
mThebes->SetOperator(gfxContext::OPERATOR_SOURCE);
}
ApplyStyle(style);
rv = nsBidiPresUtils::ProcessText(textToDraw.get(),
textToDraw.Length(),
isRTL ? NSBIDI_RTL : NSBIDI_LTR,
@ -3091,13 +3077,8 @@ nsCanvasRenderingContext2D::DrawOrMeasureText(const nsAString& aRawText,
if (NS_FAILED(rv))
return rv;
if (aOp == nsCanvasRenderingContext2D::TEXT_DRAW_OPERATION_STROKE) {
// DrawPath takes care of all shadows and composite oddities
rv = DrawPath(STYLE_STROKE);
if (NS_FAILED(rv))
return rv;
} else if (doUseIntermediateSurface)
mThebes->Paint(CurrentState().StyleIsColor(STYLE_FILL) ? 1.0 : CurrentState().globalAlpha);
if (doUseIntermediateSurface)
mThebes->Paint(CurrentState().StyleIsColor(style) ? 1.0 : CurrentState().globalAlpha);
if (aOp == nsCanvasRenderingContext2D::TEXT_DRAW_OPERATION_FILL && !doDrawShadow)
return RedrawUser(boundingBox);

View File

@ -327,7 +327,7 @@ nsFontMetrics::DrawString(const char *aString, PRUint32 aLength,
if (mTextRunRTL) {
pt.x += textRun->GetAdvanceWidth(0, aLength, &provider);
}
textRun->Draw(aContext->ThebesContext(), pt, 0, aLength, &provider, nsnull);
textRun->Draw(aContext->ThebesContext(), pt, gfxFont::GLYPH_FILL, 0, aLength, &provider, nsnull);
}
void
@ -345,7 +345,7 @@ nsFontMetrics::DrawString(const PRUnichar* aString, PRUint32 aLength,
if (mTextRunRTL) {
pt.x += textRun->GetAdvanceWidth(0, aLength, &provider);
}
textRun->Draw(aContext->ThebesContext(), pt, 0, aLength, &provider, nsnull);
textRun->Draw(aContext->ThebesContext(), pt, gfxFont::GLYPH_FILL, 0, aLength, &provider, nsnull);
}
nsBoundingMetrics

View File

@ -1125,7 +1125,7 @@ struct GlyphBuffer {
return &mGlyphBuffer[mNumGlyphs++];
}
void Flush(cairo_t *aCR, bool aDrawToPath, bool aReverse,
void Flush(cairo_t *aCR, gfxFont::DrawMode aDrawMode, bool aReverse,
bool aFinish = false) {
// Ensure there's enough room for a glyph to be added to the buffer
if (!aFinish && mNumGlyphs < GLYPH_BUFFER_SIZE) {
@ -1139,10 +1139,20 @@ struct GlyphBuffer {
mGlyphBuffer[mNumGlyphs - 1 - i] = tmp;
}
}
if (aDrawToPath)
if (aDrawMode == gfxFont::GLYPH_PATH) {
cairo_glyph_path(aCR, mGlyphBuffer, mNumGlyphs);
else
cairo_show_glyphs(aCR, mGlyphBuffer, mNumGlyphs);
} else {
if (aDrawMode & gfxFont::GLYPH_FILL) {
cairo_show_glyphs(aCR, mGlyphBuffer, mNumGlyphs);
}
if (aDrawMode & gfxFont::GLYPH_STROKE) {
cairo_new_path(aCR);
cairo_glyph_path(aCR, mGlyphBuffer, mNumGlyphs);
cairo_stroke(aCR);
}
}
mNumGlyphs = 0;
}
@ -1178,9 +1188,11 @@ CalcXScale(gfxContext *aContext)
void
gfxFont::Draw(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aEnd,
gfxContext *aContext, bool aDrawToPath, gfxPoint *aPt,
gfxContext *aContext, DrawMode aDrawMode, gfxPoint *aPt,
Spacing *aSpacing)
{
NS_ASSERTION(aDrawMode <= gfxFont::GLYPH_PATH, "GLYPH_PATH cannot be used with GLYPH_FILL or GLYPH_STROKE");
if (aStart >= aEnd)
return;
@ -1238,7 +1250,7 @@ gfxFont::Draw(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aEnd,
}
glyph->x = ToDeviceUnits(glyphX, devUnitsPerAppUnit);
glyph->y = ToDeviceUnits(y, devUnitsPerAppUnit);
glyphs.Flush(cr, aDrawToPath, isRTL);
glyphs.Flush(cr, aDrawMode, isRTL);
// synthetic bolding by multi-striking with 1-pixel offsets
// at least once, more if there's room (large font sizes)
@ -1254,7 +1266,7 @@ gfxFont::Draw(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aEnd,
devUnitsPerAppUnit);
doubleglyph->y = glyph->y;
strikeOffset += synBoldOnePixelOffset;
glyphs.Flush(cr, aDrawToPath, isRTL);
glyphs.Flush(cr, aDrawMode, isRTL);
} while (--strikeCount > 0);
}
} else {
@ -1268,7 +1280,7 @@ gfxFont::Draw(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aEnd,
if (glyphData->IsMissing()) {
// default ignorable characters will have zero advance width.
// we don't have to draw the hexbox for them
if (!aDrawToPath && advance > 0) {
if (aDrawMode != gfxFont::GLYPH_PATH && advance > 0) {
double glyphX = x;
if (isRTL) {
glyphX -= advance;
@ -1291,7 +1303,7 @@ gfxFont::Draw(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aEnd,
}
glyph->x = ToDeviceUnits(glyphX, devUnitsPerAppUnit);
glyph->y = ToDeviceUnits(y + details->mYOffset, devUnitsPerAppUnit);
glyphs.Flush(cr, aDrawToPath, isRTL);
glyphs.Flush(cr, aDrawMode, isRTL);
if (IsSyntheticBold()) {
double strikeOffset = synBoldOnePixelOffset;
@ -1306,7 +1318,7 @@ gfxFont::Draw(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aEnd,
devUnitsPerAppUnit);
doubleglyph->y = glyph->y;
strikeOffset += synBoldOnePixelOffset;
glyphs.Flush(cr, aDrawToPath, isRTL);
glyphs.Flush(cr, aDrawMode, isRTL);
} while (--strikeCount > 0);
}
}
@ -1335,7 +1347,7 @@ gfxFont::Draw(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aEnd,
}
// draw any remaining glyphs
glyphs.Flush(cr, aDrawToPath, isRTL, true);
glyphs.Flush(cr, aDrawMode, isRTL, true);
*aPt = gfxPoint(x, y);
}
@ -3450,7 +3462,7 @@ gfxTextRun::ShrinkToLigatureBoundaries(PRUint32 *aStart, PRUint32 *aEnd)
void
gfxTextRun::DrawGlyphs(gfxFont *aFont, gfxContext *aContext,
bool aDrawToPath, gfxPoint *aPt,
gfxFont::DrawMode aDrawMode, gfxPoint *aPt,
PRUint32 aStart, PRUint32 aEnd,
PropertyProvider *aProvider,
PRUint32 aSpacingStart, PRUint32 aSpacingEnd)
@ -3458,7 +3470,7 @@ gfxTextRun::DrawGlyphs(gfxFont *aFont, gfxContext *aContext,
nsAutoTArray<PropertyProvider::Spacing,200> spacingBuffer;
bool haveSpacing = GetAdjustedSpacingArray(aStart, aEnd, aProvider,
aSpacingStart, aSpacingEnd, &spacingBuffer);
aFont->Draw(this, aStart, aEnd, aContext, aDrawToPath, aPt,
aFont->Draw(this, aStart, aEnd, aContext, aDrawMode, aPt,
haveSpacing ? spacingBuffer.Elements() : nsnull);
}
@ -3516,7 +3528,7 @@ gfxTextRun::DrawPartialLigature(gfxFont *aFont, gfxContext *aCtx,
aCtx->Clip();
gfxFloat direction = GetDirection();
gfxPoint pt(aPt->x - direction*data.mPartAdvance, aPt->y);
DrawGlyphs(aFont, aCtx, false, &pt, data.mLigatureStart,
DrawGlyphs(aFont, aCtx, gfxFont::GLYPH_FILL, &pt, data.mLigatureStart,
data.mLigatureEnd, aProvider, aStart, aEnd);
aCtx->Restore();
@ -3645,11 +3657,12 @@ gfxTextRun::AdjustAdvancesForSyntheticBold(gfxContext *aContext,
}
void
gfxTextRun::Draw(gfxContext *aContext, gfxPoint aPt,
gfxTextRun::Draw(gfxContext *aContext, gfxPoint aPt, gfxFont::DrawMode aDrawMode,
PRUint32 aStart, PRUint32 aLength,
PropertyProvider *aProvider, gfxFloat *aAdvanceWidth)
{
NS_ASSERTION(aStart + aLength <= mCharacterCount, "Substring out of range");
NS_ASSERTION(aDrawMode <= gfxFont::GLYPH_PATH, "GLYPH_PATH cannot be used with GLYPH_FILL or GLYPH_STROKE");
gfxFloat direction = GetDirection();
@ -3674,7 +3687,8 @@ gfxTextRun::Draw(gfxContext *aContext, gfxPoint aPt,
gfxRGBA currentColor;
bool needToRestore = false;
if (HasNonOpaqueColor(aContext, currentColor) && HasSyntheticBold(this, aStart, aLength)) {
if (aDrawMode == gfxFont::GLYPH_FILL && HasNonOpaqueColor(aContext, currentColor)
&& HasSyntheticBold(this, aStart, aLength)) {
needToRestore = true;
// measure text, use the bounding box
gfxTextRun::Metrics metrics = MeasureText(aStart, aLength, gfxFont::LOOSE_INK_EXTENTS,
@ -3692,10 +3706,16 @@ gfxTextRun::Draw(gfxContext *aContext, gfxPoint aPt,
PRUint32 ligatureRunEnd = end;
ShrinkToLigatureBoundaries(&ligatureRunStart, &ligatureRunEnd);
DrawPartialLigature(font, aContext, start, ligatureRunStart, &pt, aProvider);
DrawGlyphs(font, aContext, false, &pt, ligatureRunStart,
if (aDrawMode == gfxFont::GLYPH_FILL) {
DrawPartialLigature(font, aContext, start, ligatureRunStart, &pt, aProvider);
}
DrawGlyphs(font, aContext, aDrawMode, &pt, ligatureRunStart,
ligatureRunEnd, aProvider, ligatureRunStart, ligatureRunEnd);
DrawPartialLigature(font, aContext, ligatureRunEnd, end, &pt, aProvider);
if (aDrawMode == gfxFont::GLYPH_FILL) {
DrawPartialLigature(font, aContext, ligatureRunEnd, end, &pt, aProvider);
}
}
// composite result when synthetic bolding used
@ -3708,38 +3728,6 @@ gfxTextRun::Draw(gfxContext *aContext, gfxPoint aPt,
}
}
void
gfxTextRun::DrawToPath(gfxContext *aContext, gfxPoint aPt,
PRUint32 aStart, PRUint32 aLength,
PropertyProvider *aProvider, gfxFloat *aAdvanceWidth)
{
NS_ASSERTION(aStart + aLength <= mCharacterCount, "Substring out of range");
gfxFloat direction = GetDirection();
gfxPoint pt = aPt;
GlyphRunIterator iter(this, aStart, aLength);
while (iter.NextRun()) {
gfxFont *font = iter.GetGlyphRun()->mFont;
PRUint32 start = iter.GetStringStart();
PRUint32 end = iter.GetStringEnd();
PRUint32 ligatureRunStart = start;
PRUint32 ligatureRunEnd = end;
ShrinkToLigatureBoundaries(&ligatureRunStart, &ligatureRunEnd);
NS_ASSERTION(ligatureRunStart == start,
"Can't draw path starting inside ligature");
NS_ASSERTION(ligatureRunEnd == end,
"Can't end drawing path inside ligature");
DrawGlyphs(font, aContext, true, &pt, ligatureRunStart, ligatureRunEnd, aProvider,
ligatureRunStart, ligatureRunEnd);
}
if (aAdvanceWidth) {
*aAdvanceWidth = (pt.x - aPt.x)*direction;
}
}
void
gfxTextRun::AccumulateMetricsForRun(gfxFont *aFont,
PRUint32 aStart, PRUint32 aEnd,

View File

@ -955,6 +955,18 @@ public:
kAntialiasSubpixel
} AntialiasOption;
// Options for how the text should be drawn
typedef enum {
// GLYPH_FILL and GLYPH_STROKE draw into the current context
// and may be used together with bitwise OR.
GLYPH_FILL = 1,
// Note: using GLYPH_STROKE will destroy the current path.
GLYPH_STROKE = 2,
// Appends glyphs to the current path. Can NOT be used with
// GLYPH_FILL or GLYPH_STROKE.
GLYPH_PATH = 4
} DrawMode;
protected:
nsAutoRefCnt mRefCnt;
cairo_scaled_font_t *mScaledFont;
@ -1165,8 +1177,8 @@ public:
* glyphs, before-spacing is inserted to the right of characters). There
* are aEnd - aStart elements in this array, unless it's null to indicate
* that there is no spacing.
* @param aDrawToPath when true, add the glyph outlines to the current path
* instead of drawing the glyphs
* @param aDrawMode specifies whether the fill or stroke of the glyph should be
* drawn, or if it should be drawn into the current path
*
* Callers guarantee:
* -- aStart and aEnd are aligned to cluster and ligature boundaries
@ -1176,7 +1188,7 @@ public:
* calls cairo_show_glyphs or cairo_glyph_path.
*/
virtual void Draw(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aEnd,
gfxContext *aContext, bool aDrawToPath, gfxPoint *aBaselineOrigin,
gfxContext *aContext, DrawMode aDrawMode, gfxPoint *aBaselineOrigin,
Spacing *aSpacing);
/**
* Measure a run of characters. See gfxTextRun::Metrics.
@ -1593,29 +1605,11 @@ public:
* if they overlap (perhaps due to negative spacing).
*/
void Draw(gfxContext *aContext, gfxPoint aPt,
gfxFont::DrawMode aDrawMode,
PRUint32 aStart, PRUint32 aLength,
PropertyProvider *aProvider,
gfxFloat *aAdvanceWidth);
/**
* Renders a substring to a path. Uses only GetSpacing from aBreakProvider.
* The provided point is the baseline origin on the left of the string
* for LTR, on the right of the string for RTL.
* @param aAdvanceWidth if non-null, the advance width of the substring
* is returned here.
*
* Drawing should respect advance widths in the way that Draw above does.
*
* Glyphs should be drawn in logical content order.
*
* UNLIKE Draw above, this cannot be used to render substrings that start or
* end inside a ligature.
*/
void DrawToPath(gfxContext *aContext, gfxPoint aPt,
PRUint32 aStart, PRUint32 aLength,
PropertyProvider *aBreakProvider,
gfxFloat *aAdvanceWidth);
/**
* Computes the ReflowMetrics for a substring.
* Uses GetSpacing from aBreakProvider.
@ -2189,7 +2183,7 @@ private:
Metrics *aMetrics);
// **** drawing helper ****
void DrawGlyphs(gfxFont *aFont, gfxContext *aContext, bool aDrawToPath,
void DrawGlyphs(gfxFont *aFont, gfxContext *aContext, gfxFont::DrawMode aDrawMode,
gfxPoint *aPt, PRUint32 aStart, PRUint32 aEnd,
PropertyProvider *aProvider,
PRUint32 aSpacingStart, PRUint32 aSpacingEnd);

View File

@ -5452,7 +5452,7 @@ nsTextFrame::DrawTextRun(gfxContext* const aCtx,
gfxFloat& aAdvanceWidth,
bool aDrawSoftHyphen)
{
mTextRun->Draw(aCtx, aTextBaselinePt, aOffset, aLength,
mTextRun->Draw(aCtx, aTextBaselinePt, gfxFont::GLYPH_FILL, aOffset, aLength,
&aProvider, &aAdvanceWidth);
if (aDrawSoftHyphen) {
@ -5465,7 +5465,7 @@ nsTextFrame::DrawTextRun(gfxContext* const aCtx,
gfxFloat hyphenBaselineX = aTextBaselinePt.x + mTextRun->GetDirection() * aAdvanceWidth -
(mTextRun->IsRightToLeft() ? hyphenTextRun->GetAdvanceWidth(0, hyphenTextRun->GetLength(), nsnull) : 0);
hyphenTextRun->Draw(aCtx, gfxPoint(hyphenBaselineX, aTextBaselinePt.y),
0, hyphenTextRun->GetLength(), nsnull, nsnull);
gfxFont::GLYPH_FILL, 0, hyphenTextRun->GetLength(), nsnull, nsnull);
}
}
}

View File

@ -411,12 +411,7 @@ nsSVGGlyphFrame::PaintSVG(nsSVGRenderState *aContext,
// there is a pattern or gradient on the text
iter.Reset();
gfx->NewPath();
AddCharactersToPath(&iter, gfx);
gfx->Stroke();
// We need to clear the context's path so state doesn't leak
// out. See bug 337753.
gfx->NewPath();
StrokeCharacters(&iter, gfx);
}
gfx->Restore();
@ -585,18 +580,17 @@ void
nsSVGGlyphFrame::AddCharactersToPath(CharacterIterator *aIter,
gfxContext *aContext)
{
aIter->SetLineWidthForDrawing(aContext);
if (aIter->SetupForDirectTextRunDrawing(aContext)) {
mTextRun->DrawToPath(aContext, gfxPoint(0, 0), 0,
mTextRun->GetLength(), nsnull, nsnull);
mTextRun->Draw(aContext, gfxPoint(0, 0), gfxFont::GLYPH_PATH, 0,
mTextRun->GetLength(), nsnull, nsnull);
return;
}
PRUint32 i;
while ((i = aIter->NextCluster()) != aIter->InvalidCluster()) {
aIter->SetupForDrawing(aContext);
mTextRun->DrawToPath(aContext, gfxPoint(0, 0), i, aIter->ClusterLength(),
nsnull, nsnull);
mTextRun->Draw(aContext, gfxPoint(0, 0), gfxFont::GLYPH_PATH, i,
aIter->ClusterLength(), nsnull, nsnull);
}
}
@ -627,7 +621,7 @@ nsSVGGlyphFrame::FillCharacters(CharacterIterator *aIter,
gfxContext *aContext)
{
if (aIter->SetupForDirectTextRunDrawing(aContext)) {
mTextRun->Draw(aContext, gfxPoint(0, 0), 0,
mTextRun->Draw(aContext, gfxPoint(0, 0), gfxFont::GLYPH_FILL, 0,
mTextRun->GetLength(), nsnull, nsnull);
return;
}
@ -635,8 +629,27 @@ nsSVGGlyphFrame::FillCharacters(CharacterIterator *aIter,
PRUint32 i;
while ((i = aIter->NextCluster()) != aIter->InvalidCluster()) {
aIter->SetupForDrawing(aContext);
mTextRun->Draw(aContext, gfxPoint(0, 0), i, aIter->ClusterLength(),
nsnull, nsnull);
mTextRun->Draw(aContext, gfxPoint(0, 0), gfxFont::GLYPH_FILL, i,
aIter->ClusterLength(), nsnull, nsnull);
}
}
void
nsSVGGlyphFrame::StrokeCharacters(CharacterIterator *aIter,
gfxContext *aContext)
{
aIter->SetLineWidthForDrawing(aContext);
if (aIter->SetupForDirectTextRunDrawing(aContext)) {
mTextRun->Draw(aContext, gfxPoint(0, 0), gfxFont::GLYPH_STROKE, 0,
mTextRun->GetLength(), nsnull, nsnull);
return;
}
PRUint32 i;
while ((i = aIter->NextCluster()) != aIter->InvalidCluster()) {
aIter->SetupForDrawing(aContext);
mTextRun->Draw(aContext, gfxPoint(0, 0), gfxFont::GLYPH_STROKE, i,
aIter->ClusterLength(), nsnull, nsnull);
}
}

View File

@ -232,6 +232,8 @@ protected:
gfxContext *aContext);
void FillCharacters(CharacterIterator *aIter,
gfxContext *aContext);
void StrokeCharacters(CharacterIterator *aIter,
gfxContext *aContext);
void NotifyGlyphMetricsChange();
bool GetGlobalTransform(gfxMatrix *aMatrix);