From 0335ceedb4958d4f1bc78b74b99dce6b22e3aeca Mon Sep 17 00:00:00 2001 From: Cameron McCormack Date: Mon, 11 Feb 2013 17:22:20 +1100 Subject: [PATCH] Bug 655877 - Part 46: Paint SVG glyphs with new SVG text frame. r=roc,jwatt --- gfx/thebes/gfxFont.cpp | 279 +++++++++++------- gfx/thebes/gfxFont.h | 70 +++-- layout/generic/nsTextFrame.h | 18 +- layout/generic/nsTextFrameThebes.cpp | 27 +- layout/reftests/text-svgglyphs/reftest.list | 2 + .../text-svgglyphs/svg-glyph-direct-ref.svg | 27 ++ .../text-svgglyphs/svg-glyph-direct.svg | 19 ++ .../text-svgglyphs/svg-glyph-html-ref.svg | 11 + .../text-svgglyphs/svg-glyph-html.html | 12 + layout/svg/nsSVGGlyphFrame.cpp | 14 +- layout/svg/nsSVGGlyphFrame.h | 145 ++++----- layout/svg/nsSVGTextFrame2.cpp | 190 +++++++++++- layout/svg/nsSVGTextFrame2.h | 61 +++- 13 files changed, 653 insertions(+), 222 deletions(-) create mode 100644 layout/reftests/text-svgglyphs/svg-glyph-direct-ref.svg create mode 100644 layout/reftests/text-svgglyphs/svg-glyph-direct.svg create mode 100644 layout/reftests/text-svgglyphs/svg-glyph-html-ref.svg create mode 100644 layout/reftests/text-svgglyphs/svg-glyph-html.html diff --git a/gfx/thebes/gfxFont.cpp b/gfx/thebes/gfxFont.cpp index 73236c91c61..3052954222a 100644 --- a/gfx/thebes/gfxFont.cpp +++ b/gfx/thebes/gfxFont.cpp @@ -1667,40 +1667,46 @@ struct GlyphBufferAzure { nsRefPtr fillPattern; if (!aObjectPaint || !(fillPattern = aObjectPaint->GetFillPattern(aThebesContext->CurrentMatrix()))) { - pat = state.pattern->GetPattern(aDT, state.patternTransformChanged ? &state.patternTransform : nullptr); + if (state.pattern) { + pat = state.pattern->GetPattern(aDT, state.patternTransformChanged ? &state.patternTransform : nullptr); + } else { + pat = nullptr; + } } else { pat = fillPattern->GetPattern(aDT); } - Matrix saved; - Matrix *mat = nullptr; - if (aInvFontMatrix) { - // The brush matrix needs to be multiplied with the inverted matrix - // as well, to move the brush into the space of the glyphs. Before - // the render target transformation + if (pat) { + Matrix saved; + Matrix *mat = nullptr; + if (aInvFontMatrix) { + // The brush matrix needs to be multiplied with the inverted matrix + // as well, to move the brush into the space of the glyphs. Before + // the render target transformation - // This relies on the returned Pattern not to be reused by - // others, but regenerated on GetPattern calls. This is true! - if (pat->GetType() == PATTERN_LINEAR_GRADIENT) { - mat = &static_cast(pat)->mMatrix; - } else if (pat->GetType() == PATTERN_RADIAL_GRADIENT) { - mat = &static_cast(pat)->mMatrix; - } else if (pat->GetType() == PATTERN_SURFACE) { - mat = &static_cast(pat)->mMatrix; + // This relies on the returned Pattern not to be reused by + // others, but regenerated on GetPattern calls. This is true! + if (pat->GetType() == PATTERN_LINEAR_GRADIENT) { + mat = &static_cast(pat)->mMatrix; + } else if (pat->GetType() == PATTERN_RADIAL_GRADIENT) { + mat = &static_cast(pat)->mMatrix; + } else if (pat->GetType() == PATTERN_SURFACE) { + mat = &static_cast(pat)->mMatrix; + } + + if (mat) { + saved = *mat; + *mat = (*mat) * (*aInvFontMatrix); + } } + aDT->FillGlyphs(aFont, buf, *pat, + aDrawOptions, aOptions); + if (mat) { - saved = *mat; - *mat = (*mat) * (*aInvFontMatrix); + *mat = saved; } } - - aDT->FillGlyphs(aFont, buf, *pat, - aDrawOptions, aOptions); - - if (mat) { - *mat = saved; - } } else if (state.sourceSurface) { aDT->FillGlyphs(aFont, buf, SurfacePattern(state.sourceSurface, EXTEND_CLAMP, @@ -1768,10 +1774,19 @@ gfxFont::CalcXScale(gfxContext *aContext) return 1.0 / m; } +static gfxFont::DrawMode +ForcePaintingDrawMode(gfxFont::DrawMode aDrawMode) +{ + return aDrawMode == gfxFont::GLYPH_PATH ? + gfxFont::DrawMode(gfxFont::GLYPH_FILL | gfxFont::GLYPH_STROKE) : + aDrawMode; +} + void gfxFont::Draw(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd, gfxContext *aContext, DrawMode aDrawMode, gfxPoint *aPt, - Spacing *aSpacing, gfxTextObjectPaint *aObjectPaint) + Spacing *aSpacing, gfxTextObjectPaint *aObjectPaint, + gfxTextRunDrawCallbacks *aCallbacks) { NS_ASSERTION(aDrawMode == gfxFont::GLYPH_PATH || !(aDrawMode & gfxFont::GLYPH_PATH), "GLYPH_PATH cannot be used with GLYPH_FILL, GLYPH_STROKE or GLYPH_STROKE_UNDERNEATH"); @@ -1816,6 +1831,9 @@ gfxFont::Draw(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd, cairo_t *cr = aContext->GetCairo(); RefPtr dt = aContext->GetDrawTarget(); + bool paintSVGGlyphs = !aCallbacks || aCallbacks->mShouldPaintSVGGlyphs; + bool emittedGlyphs = false; + if (aContext->IsCairo()) { bool success = SetupCairoFont(aContext); if (MOZ_UNLIKELY(!success)) @@ -1841,11 +1859,15 @@ gfxFont::Draw(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd, } if (haveSVGGlyphs) { + if (!paintSVGGlyphs) { + continue; + } gfxPoint point(ToDeviceUnits(glyphX, devUnitsPerAppUnit), ToDeviceUnits(y, devUnitsPerAppUnit)); - if (RenderSVGGlyph(aContext, point, aDrawMode, - glyphData->GetSimpleGlyph(), - aObjectPaint)) { + gfxFont::DrawMode mode = ForcePaintingDrawMode(aDrawMode); + if (RenderSVGGlyph(aContext, point, mode, + glyphData->GetSimpleGlyph(), aObjectPaint, + aCallbacks, emittedGlyphs)) { continue; } } @@ -1878,14 +1900,16 @@ gfxFont::Draw(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd, glyphs.Flush(cr, aDrawMode, isRTL, aObjectPaint, globalMatrix); } while (--strikeCount > 0); } + emittedGlyphs = true; } else { uint32_t glyphCount = glyphData->GetGlyphCount(); if (glyphCount > 0) { const gfxTextRun::DetailedGlyph *details = aTextRun->GetDetailedGlyphs(i); NS_ASSERTION(details, "detailedGlyph should not be missing!"); - for (uint32_t j = 0; j < glyphCount; ++j, ++details) { - double advance = details->mAdvance; + double advance; + for (uint32_t j = 0; j < glyphCount; ++j, ++details, x += direction * advance) { + advance = details->mAdvance; if (glyphData->IsMissing()) { // default ignorable characters will have zero advance width. // we don't have to draw the hexbox for them @@ -1913,34 +1937,43 @@ gfxFont::Draw(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd, gfxPoint point(ToDeviceUnits(glyphX, devUnitsPerAppUnit), ToDeviceUnits(y, devUnitsPerAppUnit)); - if (!haveSVGGlyphs || - !RenderSVGGlyph(aContext, point, aDrawMode, - details->mGlyphID, aObjectPaint)) { - glyph = glyphs.AppendGlyph(); - glyph->index = details->mGlyphID; - glyph->x = ToDeviceUnits(glyphX, devUnitsPerAppUnit); - glyph->y = ToDeviceUnits(y + details->mYOffset, devUnitsPerAppUnit); - glyphs.Flush(cr, aDrawMode, isRTL, aObjectPaint, globalMatrix); - - if (IsSyntheticBold()) { - double strikeOffset = synBoldOnePixelOffset; - int32_t strikeCount = strikes; - do { - cairo_glyph_t *doubleglyph; - doubleglyph = glyphs.AppendGlyph(); - doubleglyph->index = glyph->index; - doubleglyph->x = - ToDeviceUnits(glyphX + strikeOffset * - appUnitsPerDevUnit, - devUnitsPerAppUnit); - doubleglyph->y = glyph->y; - strikeOffset += synBoldOnePixelOffset; - glyphs.Flush(cr, aDrawMode, isRTL, aObjectPaint, globalMatrix); - } while (--strikeCount > 0); + if (haveSVGGlyphs) { + if (!paintSVGGlyphs) { + continue; + } + gfxFont::DrawMode mode = ForcePaintingDrawMode(aDrawMode); + if (RenderSVGGlyph(aContext, point, mode, + details->mGlyphID, + aObjectPaint, aCallbacks, + emittedGlyphs)) { + continue; } } + + glyph = glyphs.AppendGlyph(); + glyph->index = details->mGlyphID; + glyph->x = ToDeviceUnits(glyphX, devUnitsPerAppUnit); + glyph->y = ToDeviceUnits(y + details->mYOffset, devUnitsPerAppUnit); + glyphs.Flush(cr, aDrawMode, isRTL, aObjectPaint, globalMatrix); + + if (IsSyntheticBold()) { + double strikeOffset = synBoldOnePixelOffset; + int32_t strikeCount = strikes; + do { + cairo_glyph_t *doubleglyph; + doubleglyph = glyphs.AppendGlyph(); + doubleglyph->index = glyph->index; + doubleglyph->x = + ToDeviceUnits(glyphX + strikeOffset * + appUnitsPerDevUnit, + devUnitsPerAppUnit); + doubleglyph->y = glyph->y; + strikeOffset += synBoldOnePixelOffset; + glyphs.Flush(cr, aDrawMode, isRTL, aObjectPaint, globalMatrix); + } while (--strikeCount > 0); + } + emittedGlyphs = true; } - x += direction*advance; } } } @@ -1966,6 +1999,9 @@ gfxFont::Draw(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd, // draw any remaining glyphs glyphs.Flush(cr, aDrawMode, isRTL, aObjectPaint, globalMatrix, true); + if (aCallbacks && emittedGlyphs) { + aCallbacks->NotifyGlyphPathEmitted(); + } } else { RefPtr scaledFont = GetScaledFont(dt); @@ -2037,11 +2073,15 @@ gfxFont::Draw(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd, } if (haveSVGGlyphs) { + if (!paintSVGGlyphs) { + continue; + } gfxPoint point(ToDeviceUnits(glyphX, devUnitsPerAppUnit), ToDeviceUnits(y, devUnitsPerAppUnit)); - if (RenderSVGGlyph(aContext, point, aDrawMode, - glyphData->GetSimpleGlyph(), - aObjectPaint)) { + gfxFont::DrawMode mode = ForcePaintingDrawMode(aDrawMode); + if (RenderSVGGlyph(aContext, point, mode, + glyphData->GetSimpleGlyph(), aObjectPaint, + aCallbacks, emittedGlyphs)) { continue; } } @@ -2082,14 +2122,16 @@ gfxFont::Draw(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd, drawOptions); } while (--strikeCount > 0); } + emittedGlyphs = true; } else { uint32_t glyphCount = glyphData->GetGlyphCount(); if (glyphCount > 0) { const gfxTextRun::DetailedGlyph *details = aTextRun->GetDetailedGlyphs(i); NS_ASSERTION(details, "detailedGlyph should not be missing!"); - for (uint32_t j = 0; j < glyphCount; ++j, ++details) { - double advance = details->mAdvance; + double advance; + for (uint32_t j = 0; j < glyphCount; ++j, ++details, x += direction * advance) { + advance = details->mAdvance; if (glyphData->IsMissing()) { // default ignorable characters will have zero advance width. // we don't have to draw the hexbox for them @@ -2117,40 +2159,49 @@ gfxFont::Draw(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd, gfxPoint point(ToDeviceUnits(glyphX, devUnitsPerAppUnit), ToDeviceUnits(y, devUnitsPerAppUnit)); - if (!haveSVGGlyphs || - !RenderSVGGlyph(aContext, point, aDrawMode, - details->mGlyphID, aObjectPaint)) { - glyph = glyphs.AppendGlyph(); - glyph->mIndex = details->mGlyphID; - glyph->mPosition.x = ToDeviceUnits(glyphX, devUnitsPerAppUnit); - glyph->mPosition.y = ToDeviceUnits(y + details->mYOffset, devUnitsPerAppUnit); - glyph->mPosition = matInv * glyph->mPosition; - glyphs.Flush(dt, aObjectPaint, scaledFont, aDrawMode, - isRTL, renderingOptions, aContext, passedInvMatrix, - drawOptions); - - if (IsSyntheticBold()) { - double strikeOffset = synBoldOnePixelOffset; - int32_t strikeCount = strikes; - do { - Glyph *doubleglyph; - doubleglyph = glyphs.AppendGlyph(); - doubleglyph->mIndex = glyph->mIndex; - doubleglyph->mPosition.x = - ToDeviceUnits(glyphX + strikeOffset * - appUnitsPerDevUnit, - devUnitsPerAppUnit); - doubleglyph->mPosition.y = glyph->mPosition.y; - strikeOffset += synBoldOnePixelOffset; - doubleglyph->mPosition = matInv * doubleglyph->mPosition; - glyphs.Flush(dt, aObjectPaint, scaledFont, - aDrawMode, isRTL, renderingOptions, - aContext, passedInvMatrix, drawOptions); - } while (--strikeCount > 0); + if (haveSVGGlyphs) { + if (!paintSVGGlyphs) { + continue; + } + gfxFont::DrawMode mode = ForcePaintingDrawMode(aDrawMode); + if (RenderSVGGlyph(aContext, point, mode, + details->mGlyphID, + aObjectPaint, aCallbacks, + emittedGlyphs)) { + continue; } } + + glyph = glyphs.AppendGlyph(); + glyph->mIndex = details->mGlyphID; + glyph->mPosition.x = ToDeviceUnits(glyphX, devUnitsPerAppUnit); + glyph->mPosition.y = ToDeviceUnits(y + details->mYOffset, devUnitsPerAppUnit); + glyph->mPosition = matInv * glyph->mPosition; + glyphs.Flush(dt, aObjectPaint, scaledFont, aDrawMode, + isRTL, renderingOptions, aContext, passedInvMatrix, + drawOptions); + + if (IsSyntheticBold()) { + double strikeOffset = synBoldOnePixelOffset; + int32_t strikeCount = strikes; + do { + Glyph *doubleglyph; + doubleglyph = glyphs.AppendGlyph(); + doubleglyph->mIndex = glyph->mIndex; + doubleglyph->mPosition.x = + ToDeviceUnits(glyphX + strikeOffset * + appUnitsPerDevUnit, + devUnitsPerAppUnit); + doubleglyph->mPosition.y = glyph->mPosition.y; + strikeOffset += synBoldOnePixelOffset; + doubleglyph->mPosition = matInv * doubleglyph->mPosition; + glyphs.Flush(dt, aObjectPaint, scaledFont, + aDrawMode, isRTL, renderingOptions, + aContext, passedInvMatrix, drawOptions); + } while (--strikeCount > 0); + } + emittedGlyphs = true; } - x += direction*advance; } } } @@ -2167,6 +2218,9 @@ gfxFont::Draw(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd, glyphs.Flush(dt, aObjectPaint, scaledFont, aDrawMode, isRTL, renderingOptions, aContext, passedInvMatrix, drawOptions, true); + if (aCallbacks && emittedGlyphs) { + aCallbacks->NotifyGlyphPathEmitted(); + } dt->SetTransform(oldMat); @@ -2196,6 +2250,27 @@ gfxFont::RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint, DrawMode aDrawMod aObjectPaint); } +bool +gfxFont::RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint, DrawMode aDrawMode, + uint32_t aGlyphId, gfxTextObjectPaint *aObjectPaint, + gfxTextRunDrawCallbacks *aCallbacks, + bool& aEmittedGlyphs) +{ + if (aCallbacks) { + if (aEmittedGlyphs) { + aCallbacks->NotifyGlyphPathEmitted(); + aEmittedGlyphs = false; + } + aCallbacks->NotifyBeforeSVGGlyphPainted(); + } + bool rendered = RenderSVGGlyph(aContext, aPoint, aDrawMode, aGlyphId, + aObjectPaint); + if (aCallbacks) { + aCallbacks->NotifyAfterSVGGlyphPainted(); + } + return rendered; +} + static void UnionRange(gfxFloat aX, gfxFloat* aDestMin, gfxFloat* aDestMax) { @@ -4945,13 +5020,15 @@ gfxTextRun::DrawGlyphs(gfxFont *aFont, gfxContext *aContext, gfxTextObjectPaint *aObjectPaint, uint32_t aStart, uint32_t aEnd, PropertyProvider *aProvider, - uint32_t aSpacingStart, uint32_t aSpacingEnd) + uint32_t aSpacingStart, uint32_t aSpacingEnd, + gfxTextRunDrawCallbacks *aCallbacks) { nsAutoTArray spacingBuffer; bool haveSpacing = GetAdjustedSpacingArray(aStart, aEnd, aProvider, aSpacingStart, aSpacingEnd, &spacingBuffer); aFont->Draw(this, aStart, aEnd, aContext, aDrawMode, aPt, - haveSpacing ? spacingBuffer.Elements() : nullptr, aObjectPaint); + haveSpacing ? spacingBuffer.Elements() : nullptr, aObjectPaint, + aCallbacks); } static void @@ -4980,7 +5057,7 @@ gfxTextRun::DrawPartialLigature(gfxFont *aFont, gfxContext *aCtx, uint32_t aStart, uint32_t aEnd, gfxPoint *aPt, PropertyProvider *aProvider, - gfxTextRun::DrawCallbacks *aCallbacks) + gfxTextRunDrawCallbacks *aCallbacks) { if (aStart >= aEnd) return; @@ -5015,10 +5092,7 @@ gfxTextRun::DrawPartialLigature(gfxFont *aFont, gfxContext *aCtx, DrawGlyphs(aFont, aCtx, aCallbacks ? gfxFont::GLYPH_PATH : gfxFont::GLYPH_FILL, &pt, nullptr, data.mLigatureStart, data.mLigatureEnd, aProvider, - aStart, aEnd); - if (aCallbacks) { - aCallbacks->NotifyGlyphPathEmitted(); - } + aStart, aEnd, aCallbacks); aCtx->Restore(); aPt->x += direction*data.mPartWidth; @@ -5095,7 +5169,7 @@ gfxTextRun::Draw(gfxContext *aContext, gfxPoint aPt, gfxFont::DrawMode aDrawMode uint32_t aStart, uint32_t aLength, PropertyProvider *aProvider, gfxFloat *aAdvanceWidth, gfxTextObjectPaint *aObjectPaint, - gfxTextRun::DrawCallbacks *aCallbacks) + gfxTextRunDrawCallbacks *aCallbacks) { NS_ASSERTION(aStart + aLength <= GetLength(), "Substring out of range"); NS_ASSERTION(aDrawMode == gfxFont::GLYPH_PATH || !(aDrawMode & gfxFont::GLYPH_PATH), @@ -5153,11 +5227,8 @@ gfxTextRun::Draw(gfxContext *aContext, gfxPoint aPt, gfxFont::DrawMode aDrawMode } DrawGlyphs(font, aContext, aDrawMode, &pt, aObjectPaint, ligatureRunStart, - ligatureRunEnd, aProvider, ligatureRunStart, ligatureRunEnd); - - if (aCallbacks) { - aCallbacks->NotifyGlyphPathEmitted(); - } + ligatureRunEnd, aProvider, ligatureRunStart, ligatureRunEnd, + aCallbacks); if (drawPartial) { DrawPartialLigature(font, aContext, ligatureRunEnd, end, &pt, diff --git a/gfx/thebes/gfxFont.h b/gfx/thebes/gfxFont.h index ed2cb8a3765..5c0e12c9b9e 100644 --- a/gfx/thebes/gfxFont.h +++ b/gfx/thebes/gfxFont.h @@ -56,6 +56,7 @@ typedef struct hb_blob_t hb_blob_t; #define NO_FONT_LANGUAGE_OVERRIDE 0 struct FontListSizes; +struct gfxTextRunDrawCallbacks; struct THEBES_API gfxFontStyle { gfxFontStyle(); @@ -1432,7 +1433,8 @@ public: */ virtual void Draw(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd, gfxContext *aContext, DrawMode aDrawMode, gfxPoint *aBaselineOrigin, - Spacing *aSpacing, gfxTextObjectPaint *aObjectPaint); + Spacing *aSpacing, gfxTextObjectPaint *aObjectPaint, + gfxTextRunDrawCallbacks *aCallbacks); /** * Measure a run of characters. See gfxTextRun::Metrics. @@ -1768,6 +1770,10 @@ protected: bool RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint, DrawMode aDrawMode, uint32_t aGlyphId, gfxTextObjectPaint *aObjectPaint); + bool RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint, DrawMode aDrawMode, + uint32_t aGlyphId, gfxTextObjectPaint *aObjectPaint, + gfxTextRunDrawCallbacks *aCallbacks, + bool& aEmittedGlyphs); // Bug 674909. When synthetic bolding text by drawing twice, need to // render using a pixel offset in device pixels, otherwise text @@ -2402,6 +2408,45 @@ private: CompressedGlyph mCharGlyphsStorage[1]; }; +/** + * Callback for Draw() to use when drawing text with mode + * gfxFont::GLYPH_PATH. + */ +struct gfxTextRunDrawCallbacks { + + /** + * Constructs a new DrawCallbacks object. + * + * @param aShouldPaintSVGGlyphs If true, SVG glyphs will be + * painted and the NotifyBeforeSVGGlyphPainted/NotifyAfterSVGGlyphPainted + * callbacks will be invoked for each SVG glyph. If false, SVG glyphs + * will not be painted; fallback plain glyphs are not emitted either. + */ + gfxTextRunDrawCallbacks(bool aShouldPaintSVGGlyphs = false) + : mShouldPaintSVGGlyphs(aShouldPaintSVGGlyphs) + { + } + + /** + * Called when a path has been emitted to the gfxContext when + * painting a text run. This can be called any number of times, + * due to partial ligatures and intervening SVG glyphs. + */ + virtual void NotifyGlyphPathEmitted() = 0; + + /** + * Called just before an SVG glyph has been painted to the gfxContext. + */ + virtual void NotifyBeforeSVGGlyphPainted() { } + + /** + * Called just after an SVG glyph has been painted to the gfxContext. + */ + virtual void NotifyAfterSVGGlyphPainted() { } + + bool mShouldPaintSVGGlyphs; +}; + /** * gfxTextRun is an abstraction for drawing and measuring substrings of a run * of text. It stores runs of positioned glyph data, each run having a single @@ -2555,22 +2600,6 @@ public: uint32_t mCurrentChar; }; - /** - * Callback for Draw() to use when drawing text with mode - * gfxFont::GLYPH_PATH. - */ - struct DrawCallbacks { - - /** - * Called when a path has been emitted to the gfxContext when - * painting a text run. This can be called up to three times: - * once for any partial ligature at the beginning of the text run, - * once for the main run of glyphs, and once for any partial ligature - * at the end of the text run. - */ - virtual void NotifyGlyphPathEmitted() = 0; - }; - /** * Draws a substring. Uses only GetSpacing from aBreakProvider. * The provided point is the baseline origin on the left of the string @@ -2597,7 +2626,7 @@ public: uint32_t aStart, uint32_t aLength, PropertyProvider *aProvider, gfxFloat *aAdvanceWidth, gfxTextObjectPaint *aObjectPaint, - DrawCallbacks *aCallbacks = nullptr); + gfxTextRunDrawCallbacks *aCallbacks = nullptr); /** * Computes the ReflowMetrics for a substring. @@ -2991,7 +3020,7 @@ private: void DrawPartialLigature(gfxFont *aFont, gfxContext *aCtx, uint32_t aStart, uint32_t aEnd, gfxPoint *aPt, PropertyProvider *aProvider, - DrawCallbacks *aCallbacks); + gfxTextRunDrawCallbacks *aCallbacks); // Advance aStart to the start of the nearest ligature; back up aEnd // to the nearest ligature end; may result in *aStart == *aEnd void ShrinkToLigatureBoundaries(uint32_t *aStart, uint32_t *aEnd); @@ -3017,7 +3046,8 @@ private: gfxFont::DrawMode aDrawMode, gfxPoint *aPt, gfxTextObjectPaint *aObjectPaint, uint32_t aStart, uint32_t aEnd, PropertyProvider *aProvider, - uint32_t aSpacingStart, uint32_t aSpacingEnd); + uint32_t aSpacingStart, uint32_t aSpacingEnd, + gfxTextRunDrawCallbacks *aCallbacks); // XXX this should be changed to a GlyphRun plus a maybe-null GlyphRun*, // for smaller size especially in the super-common one-glyphrun case diff --git a/layout/generic/nsTextFrame.h b/layout/generic/nsTextFrame.h index 518e1da8fc0..906719a563e 100644 --- a/layout/generic/nsTextFrame.h +++ b/layout/generic/nsTextFrame.h @@ -297,7 +297,8 @@ public: * (NotifyBeforeSelectionBackground NotifySelectionBackgroundPathEmitted)? * (NotifyBeforeDecorationLine NotifyDecorationLinePathEmitted)* * NotifyBeforeText - * NotifyGlyphPathEmitted* + * (NotifyGlyphPathEmitted | + * (NotifyBeforeSVGGlyphPainted NotifyAfterSVGGlyphPainted))* * NotifyAfterText * (NotifyBeforeDecorationLine NotifyDecorationLinePathEmitted)* * (NotifyBeforeSelectionDecorationLine NotifySelectionDecorationLinePathEmitted)* @@ -308,8 +309,16 @@ public: * NS_TRANSPARENT, NS_SAME_AS_FOREGROUND_COLOR and * NS_40PERCENT_FOREGROUND_COLOR. */ - struct DrawPathCallbacks : gfxTextRun::DrawCallbacks + struct DrawPathCallbacks : gfxTextRunDrawCallbacks { + /** + * @param aShouldPaintSVGGlyphs Whether SVG glyphs should be painted. + */ + DrawPathCallbacks(bool aShouldPaintSVGGlyphs = false) + : gfxTextRunDrawCallbacks(aShouldPaintSVGGlyphs) + { + } + /** * Called just before any paths have been emitted to the gfxContext * for the glyphs of the frame's text. @@ -365,6 +374,7 @@ public: // context. void PaintText(nsRenderingContext* aRenderingContext, nsPoint aPt, const nsRect& aDirtyRect, const nsCharClipDisplayItem& aItem, + gfxTextObjectPaint* aObjectPaint = nullptr, DrawPathCallbacks* aCallbacks = nullptr); // helper: paint text frame when we're impacted by at least one selection. // Return false if the text was not painted and we should continue with @@ -378,6 +388,7 @@ public: uint32_t aContentLength, nsTextPaintStyle& aTextPaintStyle, const nsCharClipDisplayItem::ClipEdges& aClipEdges, + gfxTextObjectPaint* aObjectPaint, DrawPathCallbacks* aCallbacks); // helper: paint text with foreground and background colors determined // by selection(s). Also computes a mask of all selection types applying to @@ -639,6 +650,7 @@ protected: nscolor aTextColor, gfxFloat& aAdvanceWidth, bool aDrawSoftHyphen, + gfxTextObjectPaint* aObjectPaint, DrawPathCallbacks* aCallbacks); void DrawTextRunAndDecorations(gfxContext* const aCtx, @@ -655,6 +667,7 @@ protected: bool aDrawSoftHyphen, const TextDecorations& aDecorations, const nscolor* const aDecorationOverrideColor, + gfxTextObjectPaint* aObjectPaint, DrawPathCallbacks* aCallbacks); void DrawText(gfxContext* const aCtx, @@ -670,6 +683,7 @@ protected: gfxFloat& aAdvanceWidth, bool aDrawSoftHyphen, const nscolor* const aDecorationOverrideColor = nullptr, + gfxTextObjectPaint* aObjectPaint = nullptr, DrawPathCallbacks* aCallbacks = nullptr); // Set non empty rect to aRect, it should be overflow rect or frame rect. diff --git a/layout/generic/nsTextFrameThebes.cpp b/layout/generic/nsTextFrameThebes.cpp index 61d6a24f3a4..46a60845a7a 100644 --- a/layout/generic/nsTextFrameThebes.cpp +++ b/layout/generic/nsTextFrameThebes.cpp @@ -5585,7 +5585,7 @@ nsTextFrame::PaintTextWithSelectionColors(gfxContext* aCtx, DrawText(aCtx, aDirtyRect, aFramePt, textBaselinePt, offset, length, aProvider, aTextPaintStyle, foreground, aClipEdges, - advance, hyphenWidth > 0, nullptr, aCallbacks); + advance, hyphenWidth > 0, nullptr, nullptr, aCallbacks); if (hyphenWidth) { advance += hyphenWidth; } @@ -5677,6 +5677,7 @@ nsTextFrame::PaintTextWithSelection(gfxContext* aCtx, uint32_t aContentOffset, uint32_t aContentLength, nsTextPaintStyle& aTextPaintStyle, const nsCharClipDisplayItem::ClipEdges& aClipEdges, + gfxTextObjectPaint* aObjectPaint, nsTextFrame::DrawPathCallbacks* aCallbacks) { NS_ASSERTION(GetContent()->IsSelectionDescendant(), "wrong paint path"); @@ -5890,6 +5891,7 @@ void nsTextFrame::PaintText(nsRenderingContext* aRenderingContext, nsPoint aPt, const nsRect& aDirtyRect, const nsCharClipDisplayItem& aItem, + gfxTextObjectPaint* aObjectPaint, nsTextFrame::DrawPathCallbacks* aCallbacks) { // Don't pass in aRenderingContext here, because we need a *reference* @@ -5932,7 +5934,8 @@ nsTextFrame::PaintText(nsRenderingContext* aRenderingContext, nsPoint aPt, tmp.ConvertSkippedToOriginal(startOffset + maxLength) - contentOffset; if (PaintTextWithSelection(ctx, framePt, textBaselinePt, dirtyRect, provider, contentOffset, contentLength, - textPaintStyle, clipEdges, aCallbacks)) { + textPaintStyle, clipEdges, aObjectPaint, + aCallbacks)) { return; } } @@ -5960,7 +5963,7 @@ nsTextFrame::PaintText(nsRenderingContext* aRenderingContext, nsPoint aPt, DrawText(ctx, dirtyRect, framePt, textBaselinePt, startOffset, maxLength, provider, textPaintStyle, foregroundColor, clipEdges, advanceWidth, (GetStateBits() & TEXT_HYPHEN_BREAK) != 0, - nullptr, aCallbacks); + nullptr, aObjectPaint, aCallbacks); } static void @@ -5971,6 +5974,7 @@ DrawTextRun(gfxTextRun* aTextRun, PropertyProvider* aProvider, nscolor aTextColor, gfxFloat* aAdvanceWidth, + gfxTextObjectPaint* aObjectPaint, nsTextFrame::DrawPathCallbacks* aCallbacks) { gfxFont::DrawMode drawMode = aCallbacks ? gfxFont::GLYPH_PATH : @@ -5978,12 +5982,12 @@ DrawTextRun(gfxTextRun* aTextRun, if (aCallbacks) { aCallbacks->NotifyBeforeText(aTextColor); aTextRun->Draw(aCtx, aTextBaselinePt, drawMode, aOffset, aLength, - aProvider, aAdvanceWidth, nullptr, aCallbacks); + aProvider, aAdvanceWidth, aObjectPaint, aCallbacks); aCallbacks->NotifyAfterText(); } else { aCtx->SetColor(gfxRGBA(aTextColor)); aTextRun->Draw(aCtx, aTextBaselinePt, drawMode, aOffset, aLength, - aProvider, aAdvanceWidth, nullptr); + aProvider, aAdvanceWidth, aObjectPaint); } } @@ -5995,10 +5999,11 @@ nsTextFrame::DrawTextRun(gfxContext* const aCtx, nscolor aTextColor, gfxFloat& aAdvanceWidth, bool aDrawSoftHyphen, + gfxTextObjectPaint* aObjectPaint, nsTextFrame::DrawPathCallbacks* aCallbacks) { ::DrawTextRun(mTextRun, aCtx, aTextBaselinePt, aOffset, aLength, &aProvider, - aTextColor, &aAdvanceWidth, aCallbacks); + aTextColor, &aAdvanceWidth, aObjectPaint, aCallbacks); if (aDrawSoftHyphen) { // Don't use ctx as the context, because we need a reference context here, @@ -6012,7 +6017,7 @@ nsTextFrame::DrawTextRun(gfxContext* const aCtx, ::DrawTextRun(hyphenTextRun.get(), aCtx, gfxPoint(hyphenBaselineX, aTextBaselinePt.y), 0, hyphenTextRun->GetLength(), - nullptr, aTextColor, nullptr, aCallbacks); + nullptr, aTextColor, nullptr, aObjectPaint, aCallbacks); } } } @@ -6030,6 +6035,7 @@ nsTextFrame::DrawTextRunAndDecorations( bool aDrawSoftHyphen, const TextDecorations& aDecorations, const nscolor* const aDecorationOverrideColor, + gfxTextObjectPaint* aObjectPaint, nsTextFrame::DrawPathCallbacks* aCallbacks) { const gfxFloat app = aTextStyle.PresContext()->AppUnitsPerDevPixel(); @@ -6094,7 +6100,7 @@ nsTextFrame::DrawTextRunAndDecorations( // CSS 2.1 mandates that text be painted after over/underlines, and *then* // line-throughs DrawTextRun(aCtx, aTextBaselinePt, aOffset, aLength, aProvider, aTextColor, - aAdvanceWidth, aDrawSoftHyphen, aCallbacks); + aAdvanceWidth, aDrawSoftHyphen, aObjectPaint, aCallbacks); // Line-throughs for (uint32_t i = aDecorations.mStrikes.Length(); i-- > 0; ) { @@ -6130,6 +6136,7 @@ nsTextFrame::DrawText( gfxFloat& aAdvanceWidth, bool aDrawSoftHyphen, const nscolor* const aDecorationOverrideColor, + gfxTextObjectPaint* aObjectPaint, nsTextFrame::DrawPathCallbacks* aCallbacks) { TextDecorations decorations; @@ -6144,10 +6151,10 @@ nsTextFrame::DrawText( DrawTextRunAndDecorations(aCtx, aDirtyRect, aFramePt, aTextBaselinePt, aOffset, aLength, aProvider, aTextStyle, aTextColor, aClipEdges, aAdvanceWidth, aDrawSoftHyphen, decorations, - aDecorationOverrideColor, aCallbacks); + aDecorationOverrideColor, aObjectPaint, aCallbacks); } else { DrawTextRun(aCtx, aTextBaselinePt, aOffset, aLength, aProvider, - aTextColor, aAdvanceWidth, aDrawSoftHyphen, aCallbacks); + aTextColor, aAdvanceWidth, aDrawSoftHyphen, aObjectPaint, aCallbacks); } } diff --git a/layout/reftests/text-svgglyphs/reftest.list b/layout/reftests/text-svgglyphs/reftest.list index 02d27a60766..3ed9c3b1f8d 100644 --- a/layout/reftests/text-svgglyphs/reftest.list +++ b/layout/reftests/text-svgglyphs/reftest.list @@ -2,6 +2,8 @@ pref(gfx.font_rendering.opentype_svg.enabled,false) != svg-glyph-basic.svg svg pref(gfx.font_rendering.opentype_svg.enabled,true) == svg-glyph-basic.svg svg-glyph-basic-ref.svg pref(gfx.font_rendering.opentype_svg.enabled,false) != svg-glyph-positioning.svg svg-glyph-positioning-ref.svg pref(gfx.font_rendering.opentype_svg.enabled,true) == svg-glyph-positioning.svg svg-glyph-positioning-ref.svg +pref(gfx.font_rendering.opentype_svg.enabled,true) == svg-glyph-html.html svg-glyph-html-ref.svg +pref(gfx.font_rendering.opentype_svg.enabled,true) == svg-glyph-direct.svg svg-glyph-direct-ref.svg pref(gfx.font_rendering.opentype_svg.enabled,true) == svg-glyph-invalid.html svg-glyph-invalid-ref.html pref(gfx.font_rendering.opentype_svg.enabled,true) == svg-glyph-objectfill-solid.svg svg-glyph-objectfill-solid-ref.svg pref(gfx.font_rendering.opentype_svg.enabled,true) == svg-glyph-objectstroke-solid.svg svg-glyph-objectstroke-solid-ref.svg diff --git a/layout/reftests/text-svgglyphs/svg-glyph-direct-ref.svg b/layout/reftests/text-svgglyphs/svg-glyph-direct-ref.svg new file mode 100644 index 00000000000..67a5fb8d23e --- /dev/null +++ b/layout/reftests/text-svgglyphs/svg-glyph-direct-ref.svg @@ -0,0 +1,27 @@ + + + Reference for SVG glyphs being painted when painting properties are simple + + + + + + + + + L + diff --git a/layout/reftests/text-svgglyphs/svg-glyph-direct.svg b/layout/reftests/text-svgglyphs/svg-glyph-direct.svg new file mode 100644 index 00000000000..c1e38a2cee6 --- /dev/null +++ b/layout/reftests/text-svgglyphs/svg-glyph-direct.svg @@ -0,0 +1,19 @@ + + + Test for SVG glyphs being painted when painting properties are simple + + + + L + diff --git a/layout/reftests/text-svgglyphs/svg-glyph-html-ref.svg b/layout/reftests/text-svgglyphs/svg-glyph-html-ref.svg new file mode 100644 index 00000000000..6360240ff89 --- /dev/null +++ b/layout/reftests/text-svgglyphs/svg-glyph-html-ref.svg @@ -0,0 +1,11 @@ + + Reference for SVG glyphs being used in HTML content + + b + diff --git a/layout/reftests/text-svgglyphs/svg-glyph-html.html b/layout/reftests/text-svgglyphs/svg-glyph-html.html new file mode 100644 index 00000000000..5d4298f6dfe --- /dev/null +++ b/layout/reftests/text-svgglyphs/svg-glyph-html.html @@ -0,0 +1,12 @@ + +Test for SVG glyphs being used in HTML content + +
b
diff --git a/layout/svg/nsSVGGlyphFrame.cpp b/layout/svg/nsSVGGlyphFrame.cpp index 38ddf06f46d..86f46ffa70e 100644 --- a/layout/svg/nsSVGGlyphFrame.cpp +++ b/layout/svg/nsSVGGlyphFrame.cpp @@ -1092,23 +1092,23 @@ nsSVGGlyphFrame::SetupObjectPaint(gfxContext *aContext, // SVGTextObjectPaint methods: already_AddRefed -nsSVGGlyphFrame::SVGTextObjectPaint::GetFillPattern(float aOpacity, - const gfxMatrix& aCTM) +mozilla::SVGTextObjectPaint::GetFillPattern(float aOpacity, + const gfxMatrix& aCTM) { return mFillPaint.GetPattern(aOpacity, &nsStyleSVG::mFill, aCTM); } already_AddRefed -nsSVGGlyphFrame::SVGTextObjectPaint::GetStrokePattern(float aOpacity, - const gfxMatrix& aCTM) +mozilla::SVGTextObjectPaint::GetStrokePattern(float aOpacity, + const gfxMatrix& aCTM) { return mStrokePaint.GetPattern(aOpacity, &nsStyleSVG::mStroke, aCTM); } already_AddRefed -nsSVGGlyphFrame::SVGTextObjectPaint::Paint::GetPattern(float aOpacity, - nsStyleSVGPaint nsStyleSVG::*aFillOrStroke, - const gfxMatrix& aCTM) +mozilla::SVGTextObjectPaint::Paint::GetPattern(float aOpacity, + nsStyleSVGPaint nsStyleSVG::*aFillOrStroke, + const gfxMatrix& aCTM) { nsRefPtr pattern; if (mPatternCache.Get(aOpacity, getter_AddRefs(pattern))) { diff --git a/layout/svg/nsSVGGlyphFrame.h b/layout/svg/nsSVGGlyphFrame.h index 0dd4db687c4..d20efa9eda3 100644 --- a/layout/svg/nsSVGGlyphFrame.h +++ b/layout/svg/nsSVGGlyphFrame.h @@ -8,12 +8,12 @@ #include "mozilla/Attributes.h" #include "gfxFont.h" -#include "nsISVGGlyphFragmentNode.h" +#include "gfxSVGGlyphs.h" #include "nsISVGChildFrame.h" +#include "nsISVGGlyphFragmentNode.h" #include "nsSVGGeometryFrame.h" #include "nsSVGUtils.h" #include "nsTextFragment.h" -#include "gfxSVGGlyphs.h" class CharacterIterator; class gfxContext; @@ -27,6 +27,79 @@ class gfxTextObjectPaint; struct CharacterPosition; +namespace mozilla { + +// Slightly horrible callback for deferring application of opacity +struct SVGTextObjectPaint : public gfxTextObjectPaint { + already_AddRefed GetFillPattern(float aOpacity, + const gfxMatrix& aCTM); + already_AddRefed GetStrokePattern(float aOpacity, + const gfxMatrix& aCTM); + + void SetFillOpacity(float aOpacity) { mFillOpacity = aOpacity; } + float GetFillOpacity() { return mFillOpacity; } + + void SetStrokeOpacity(float aOpacity) { mStrokeOpacity = aOpacity; } + float GetStrokeOpacity() { return mStrokeOpacity; } + + struct Paint { + Paint() { + mPatternCache.Init(); + } + + void SetPaintServer(nsIFrame *aFrame, const gfxMatrix& aContextMatrix, + nsSVGPaintServerFrame *aPaintServerFrame) { + mPaintType = eStyleSVGPaintType_Server; + mPaintDefinition.mPaintServerFrame = aPaintServerFrame; + mFrame = aFrame; + mContextMatrix = aContextMatrix; + } + + void SetColor(const nscolor &aColor) { + mPaintType = eStyleSVGPaintType_Color; + mPaintDefinition.mColor = aColor; + } + + void SetObjectPaint(gfxTextObjectPaint *aObjectPaint, + nsStyleSVGPaintType aPaintType) { + NS_ASSERTION(aPaintType == eStyleSVGPaintType_ObjectFill || + aPaintType == eStyleSVGPaintType_ObjectStroke, + "Invalid object paint type"); + mPaintType = aPaintType; + mPaintDefinition.mObjectPaint = aObjectPaint; + } + + union { + nsSVGPaintServerFrame *mPaintServerFrame; + gfxTextObjectPaint *mObjectPaint; + nscolor mColor; + } mPaintDefinition; + + nsIFrame *mFrame; + // CTM defining the user space for the pattern we will use. + gfxMatrix mContextMatrix; + nsStyleSVGPaintType mPaintType; + + // Device-space-to-pattern-space + gfxMatrix mPatternMatrix; + nsRefPtrHashtable mPatternCache; + + already_AddRefed GetPattern(float aOpacity, + nsStyleSVGPaint nsStyleSVG::*aFillOrStroke, + const gfxMatrix& aCTM); + }; + + Paint mFillPaint; + Paint mStrokePaint; + + float mFillOpacity; + float mStrokeOpacity; +}; + +} // namespace mozilla + +using namespace mozilla; + typedef gfxFont::DrawMode DrawMode; typedef nsSVGGeometryFrame nsSVGGlyphFrameBase; @@ -184,7 +257,6 @@ public: } private: - /** * This class exists purely because it would be too messy to pass the "for" * flag for GetCanvasTM through the call chains to the GetCanvasTM() call in @@ -271,73 +343,6 @@ private: gfxTextObjectPaint *aOuterObjectPaint, gfxTextObjectPaint **aThisObjectPaint); - // Slightly horrible callback for deferring application of opacity - struct SVGTextObjectPaint : public gfxTextObjectPaint { - already_AddRefed GetFillPattern(float aOpacity, - const gfxMatrix& aCTM); - already_AddRefed GetStrokePattern(float aOpacity, - const gfxMatrix& aCTM); - - void SetFillOpacity(float aOpacity) { mFillOpacity = aOpacity; } - float GetFillOpacity() { return mFillOpacity; } - - void SetStrokeOpacity(float aOpacity) { mStrokeOpacity = aOpacity; } - float GetStrokeOpacity() { return mStrokeOpacity; } - - struct Paint { - Paint() { - mPatternCache.Init(); - } - - void SetPaintServer(nsIFrame *aFrame, const gfxMatrix& aContextMatrix, - nsSVGPaintServerFrame *aPaintServerFrame) { - mPaintType = eStyleSVGPaintType_Server; - mPaintDefinition.mPaintServerFrame = aPaintServerFrame; - mFrame = aFrame; - mContextMatrix = aContextMatrix; - } - - void SetColor(const nscolor &aColor) { - mPaintType = eStyleSVGPaintType_Color; - mPaintDefinition.mColor = aColor; - } - - void SetObjectPaint(gfxTextObjectPaint *aObjectPaint, - nsStyleSVGPaintType aPaintType) { - NS_ASSERTION(aPaintType == eStyleSVGPaintType_ObjectFill || - aPaintType == eStyleSVGPaintType_ObjectStroke, - "Invalid object paint type"); - mPaintType = aPaintType; - mPaintDefinition.mObjectPaint = aObjectPaint; - } - - union { - nsSVGPaintServerFrame *mPaintServerFrame; - gfxTextObjectPaint *mObjectPaint; - nscolor mColor; - } mPaintDefinition; - - nsIFrame *mFrame; - // CTM defining the user space for the pattern we will use. - gfxMatrix mContextMatrix; - nsStyleSVGPaintType mPaintType; - - // Device-space-to-pattern-space - gfxMatrix mPatternMatrix; - nsRefPtrHashtable mPatternCache; - - already_AddRefed GetPattern(float aOpacity, - nsStyleSVGPaint nsStyleSVG::*aFillOrStroke, - const gfxMatrix& aCTM); - }; - - Paint mFillPaint; - Paint mStrokePaint; - - float mFillOpacity; - float mStrokeOpacity; - }; - /** * Sets up the stroke style in |aContext| and stores stroke pattern * information in |aThisObjectPaint|. diff --git a/layout/svg/nsSVGTextFrame2.cpp b/layout/svg/nsSVGTextFrame2.cpp index e678a020cd9..96e458a3515 100644 --- a/layout/svg/nsSVGTextFrame2.cpp +++ b/layout/svg/nsSVGTextFrame2.cpp @@ -27,6 +27,7 @@ #include "nsSVGEffects.h" #include "nsSVGGlyphFrame.h" #include "nsSVGOuterSVGFrame.h" +#include "nsSVGPaintServerFrame.h" #include "nsSVGRect.h" #include "nsSVGIntegrationUtils.h" #include "nsSVGTextFrame2.h" @@ -2471,11 +2472,14 @@ public: * @param aCanvasTM The transformation matrix to set when painting; this * should be the FOR_OUTERSVG_TM canvas TM of the text, so that * paint servers are painted correctly. + * @param aShouldPaintSVGGlyphs Whether SVG glyphs should be painted. */ SVGTextDrawPathCallbacks(nsRenderingContext* aContext, nsTextFrame* aFrame, - const gfxMatrix& aCanvasTM) - : gfx(aContext->ThebesContext()), + const gfxMatrix& aCanvasTM, + bool aShouldPaintSVGGlyphs) + : DrawPathCallbacks(aShouldPaintSVGGlyphs), + gfx(aContext->ThebesContext()), mRenderMode(SVGAutoRenderState::GetRenderMode(aContext)), mFrame(aFrame), mCanvasTM(aCanvasTM) @@ -2484,6 +2488,8 @@ public: void NotifyBeforeText(nscolor aColor); void NotifyGlyphPathEmitted(); + void NotifyBeforeSVGGlyphPainted(); + void NotifyAfterSVGGlyphPainted(); void NotifyAfterText(); void NotifyBeforeSelectionBackground(nscolor aColor); void NotifySelectionBackgroundPathEmitted(); @@ -2543,6 +2549,19 @@ SVGTextDrawPathCallbacks::NotifyGlyphPathEmitted() gfx->NewPath(); } +void +SVGTextDrawPathCallbacks::NotifyBeforeSVGGlyphPainted() +{ + gfx->Save(); +} + +void +SVGTextDrawPathCallbacks::NotifyAfterSVGGlyphPainted() +{ + gfx->Restore(); + gfx->NewPath(); +} + void SVGTextDrawPathCallbacks::NotifyAfterText() { @@ -3143,6 +3162,15 @@ nsSVGTextFrame2::PaintSVG(nsRenderingContext* aContext, // need to ignore. SVGCharClipDisplayItem item(run); + // Set up the fill and stroke so that SVG glyphs can get painted correctly + // when they use -moz-objectFill values etc. + gfx->SetMatrix(initialMatrix); + gfxTextObjectPaint *outerObjectPaint = + (gfxTextObjectPaint*)aContext->GetUserData(&gfxTextObjectPaint::sUserDataKey); + + nsAutoPtr objectPaint; + SetupCairoState(gfx, frame, outerObjectPaint, getter_Transfers(objectPaint)); + // Set up the transform for painting the text frame for the substring // indicated by the run. gfxMatrix runTransform = @@ -3151,11 +3179,15 @@ nsSVGTextFrame2::PaintSVG(nsRenderingContext* aContext, gfx->SetMatrix(runTransform); nsRect frameRect = frame->GetVisualOverflowRect(); - if (ShouldRenderAsPath(aContext, frame)) { - SVGTextDrawPathCallbacks callbacks(aContext, frame, matrixForPaintServers); - frame->PaintText(aContext, nsPoint(), frameRect, item, &callbacks); + bool paintSVGGlyphs; + if (ShouldRenderAsPath(aContext, frame, paintSVGGlyphs)) { + SVGTextDrawPathCallbacks callbacks(aContext, frame, matrixForPaintServers, + paintSVGGlyphs); + frame->PaintText(aContext, nsPoint(), frameRect, item, + objectPaint, &callbacks); } else { - frame->PaintText(aContext, nsPoint(), frameRect, item, nullptr); + frame->PaintText(aContext, nsPoint(), frameRect, item, + objectPaint, nullptr); } if (frame == caretFrame && ShouldPaintCaret(run, caret)) { @@ -3255,7 +3287,7 @@ nsSVGTextFrame2::ReflowSVG() uint16_t hitTestFlags = nsSVGUtils::GetGeometryHitTestFlags(run.mFrame); if ((hitTestFlags & SVG_HIT_TEST_FILL) || - run.mFrame->GetStyleSVG()->mFill.mType != eStyleSVGPaintType_None) { + run.mFrame->GetStyleSVG()->mFill.mType == eStyleSVGPaintType_None) { runFlags |= TextRenderedRun::eIncludeFill; } if ((hitTestFlags & SVG_HIT_TEST_STROKE) || @@ -4464,13 +4496,17 @@ nsSVGTextFrame2::DoGlyphPositioning() bool nsSVGTextFrame2::ShouldRenderAsPath(nsRenderingContext* aContext, - nsTextFrame* aFrame) + nsTextFrame* aFrame, + bool& aShouldPaintSVGGlyphs) { // Rendering to a clip path. if (SVGAutoRenderState::GetRenderMode(aContext) != SVGAutoRenderState::NORMAL) { + aShouldPaintSVGGlyphs = false; return true; } + aShouldPaintSVGGlyphs = true; + const nsStyleSVG* style = aFrame->GetStyleSVG(); // Fill is a non-solid paint, has a non-default fill-rule or has @@ -4878,3 +4914,141 @@ nsSVGTextFrame2::TransformFrameRectFromTextChild(const nsRect& aRect, return result - framePosition; } + +gfxFont::DrawMode +nsSVGTextFrame2::SetupCairoState(gfxContext* aContext, + nsIFrame* aFrame, + gfxTextObjectPaint* aOuterObjectPaint, + gfxTextObjectPaint** aThisObjectPaint) +{ + gfxFont::DrawMode toDraw = gfxFont::DrawMode(0); + SVGTextObjectPaint *thisObjectPaint = new SVGTextObjectPaint(); + + if (SetupCairoStroke(aContext, aFrame, aOuterObjectPaint, thisObjectPaint)) { + toDraw = gfxFont::DrawMode(toDraw | gfxFont::GLYPH_STROKE); + } + + if (SetupCairoFill(aContext, aFrame, aOuterObjectPaint, thisObjectPaint)) { + toDraw = gfxFont::DrawMode(toDraw | gfxFont::GLYPH_FILL); + } + + *aThisObjectPaint = thisObjectPaint; + + return toDraw; +} + +bool +nsSVGTextFrame2::SetupCairoStroke(gfxContext* aContext, + nsIFrame* aFrame, + gfxTextObjectPaint* aOuterObjectPaint, + SVGTextObjectPaint* aThisObjectPaint) +{ + const nsStyleSVG *style = aFrame->GetStyleSVG(); + if (style->mStroke.mType == eStyleSVGPaintType_None) { + aThisObjectPaint->SetStrokeOpacity(0.0f); + return false; + } + + gfxContextMatrixAutoSaveRestore matrixRestore(aContext); + aContext->IdentityMatrix(); + + nsSVGUtils::SetupCairoStrokeHitGeometry(aFrame, aContext, aOuterObjectPaint); + float opacity = nsSVGUtils::GetOpacity(style->mStrokeOpacitySource, + style->mStrokeOpacity, + aOuterObjectPaint); + + SetupInheritablePaint(aContext, aFrame, opacity, aOuterObjectPaint, + aThisObjectPaint->mStrokePaint, &nsStyleSVG::mStroke, + nsSVGEffects::StrokeProperty()); + + aThisObjectPaint->SetStrokeOpacity(opacity); + + return opacity != 0.0f; +} + +bool +nsSVGTextFrame2::SetupCairoFill(gfxContext* aContext, + nsIFrame* aFrame, + gfxTextObjectPaint* aOuterObjectPaint, + SVGTextObjectPaint* aThisObjectPaint) +{ + const nsStyleSVG *style = aFrame->GetStyleSVG(); + if (style->mFill.mType == eStyleSVGPaintType_None) { + aThisObjectPaint->SetFillOpacity(0.0f); + return false; + } + + float opacity = nsSVGUtils::GetOpacity(style->mFillOpacitySource, + style->mFillOpacity, + aOuterObjectPaint); + + SetupInheritablePaint(aContext, aFrame, opacity, aOuterObjectPaint, + aThisObjectPaint->mFillPaint, &nsStyleSVG::mFill, + nsSVGEffects::FillProperty()); + + aThisObjectPaint->SetFillOpacity(opacity); + + return true; +} + +void +nsSVGTextFrame2::SetupInheritablePaint(gfxContext* aContext, + nsIFrame* aFrame, + float& aOpacity, + gfxTextObjectPaint* aOuterObjectPaint, + SVGTextObjectPaint::Paint& aTargetPaint, + nsStyleSVGPaint nsStyleSVG::*aFillOrStroke, + const FramePropertyDescriptor* aProperty) +{ + const nsStyleSVG *style = aFrame->GetStyleSVG(); + nsSVGPaintServerFrame *ps = + nsSVGEffects::GetPaintServer(aFrame, &(style->*aFillOrStroke), aProperty); + + if (ps && ps->SetupPaintServer(aContext, aFrame, aFillOrStroke, aOpacity)) { + aTargetPaint.SetPaintServer(aFrame, aContext->CurrentMatrix(), ps); + } else if (SetupObjectPaint(aContext, aFrame, aFillOrStroke, aOpacity, aOuterObjectPaint)) { + aTargetPaint.SetObjectPaint(aOuterObjectPaint, (style->*aFillOrStroke).mType); + } else { + nscolor color = nsSVGUtils::GetFallbackOrPaintColor(aContext, + aFrame->GetStyleContext(), + aFillOrStroke); + aTargetPaint.SetColor(color); + + aContext->SetPattern(new gfxPattern(gfxRGBA(NS_GET_R(color) / 255.0, + NS_GET_G(color) / 255.0, + NS_GET_B(color) / 255.0, + NS_GET_A(color) / 255.0 * aOpacity))); + } +} + +bool +nsSVGTextFrame2::SetupObjectPaint(gfxContext* aContext, + nsIFrame* aFrame, + nsStyleSVGPaint nsStyleSVG::*aFillOrStroke, + float& aOpacity, + gfxTextObjectPaint* aOuterObjectPaint) +{ + if (!aOuterObjectPaint) { + return false; + } + + const nsStyleSVG *style = aFrame->GetStyleSVG(); + const nsStyleSVGPaint &paint = style->*aFillOrStroke; + + if (paint.mType != eStyleSVGPaintType_ObjectFill && + paint.mType != eStyleSVGPaintType_ObjectStroke) { + return false; + } + + gfxMatrix current = aContext->CurrentMatrix(); + nsRefPtr pattern = + paint.mType == eStyleSVGPaintType_ObjectFill ? + aOuterObjectPaint->GetFillPattern(aOpacity, current) : + aOuterObjectPaint->GetStrokePattern(aOpacity, current); + if (!pattern) { + return false; + } + + aContext->SetPattern(pattern); + return true; +} diff --git a/layout/svg/nsSVGTextFrame2.h b/layout/svg/nsSVGTextFrame2.h index e14d10f209c..caeae8f2a36 100644 --- a/layout/svg/nsSVGTextFrame2.h +++ b/layout/svg/nsSVGTextFrame2.h @@ -6,9 +6,12 @@ #ifndef NS_SVGTEXTFRAME2_H #define NS_SVGTEXTFRAME2_H +#include "gfxFont.h" #include "gfxMatrix.h" #include "gfxRect.h" +#include "gfxSVGGlyphs.h" #include "nsStubMutationObserver.h" +#include "nsSVGGlyphFrame.h" // for SVGTextObjectPaint #include "nsSVGTextContainerFrame.h" class nsDisplaySVGText; @@ -450,8 +453,12 @@ private: * Returns whether we need to render the text using * nsTextFrame::DrawPathCallbacks rather than directly painting * the text frames. + * + * @param aShouldPaintSVGGlyphs (out) Whether SVG glyphs in the text + * should be painted. */ - bool ShouldRenderAsPath(nsRenderingContext* aContext, nsTextFrame* aFrame); + bool ShouldRenderAsPath(nsRenderingContext* aContext, nsTextFrame* aFrame, + bool& aShouldPaintSVGGlyphs); // Methods to get information for a frame. nsIFrame* GetTextPathPathFrame(nsIFrame* aTextPathFrame); @@ -459,6 +466,58 @@ private: gfxFloat GetOffsetScale(nsIFrame* aTextPathFrame); gfxFloat GetStartOffset(nsIFrame* aTextPathFrame); + gfxFont::DrawMode SetupCairoState(gfxContext* aContext, + nsIFrame* aFrame, + gfxTextObjectPaint* aOuterObjectPaint, + gfxTextObjectPaint** aThisObjectPaint); + + /** + * Sets up the stroke style for |aFrame| in |aContext| and stores stroke + * pattern information in |aThisObjectPaint|. + */ + bool SetupCairoStroke(gfxContext* aContext, + nsIFrame* aFrame, + gfxTextObjectPaint* aOuterObjectPaint, + SVGTextObjectPaint* aThisObjectPaint); + + /** + * Sets up the fill style for |aFrame| in |aContext| and stores fill pattern + * information in |aThisObjectPaint|. + */ + bool SetupCairoFill(gfxContext* aContext, + nsIFrame* aFrame, + gfxTextObjectPaint* aOuterObjectPaint, + SVGTextObjectPaint* aThisObjectPaint); + + /** + * Sets the current pattern for |aFrame| to the fill or stroke style of the + * outer text object. Will also set the paint opacity to transparent if the + * paint is set to "none". + */ + bool SetupObjectPaint(gfxContext* aContext, + nsIFrame* aFrame, + nsStyleSVGPaint nsStyleSVG::*aFillOrStroke, + float& aOpacity, + gfxTextObjectPaint* aObjectPaint); + + /** + * Stores in |aTargetPaint| information on how to reconstruct the current + * fill or stroke pattern. Will also set the paint opacity to transparent if + * the paint is set to "none". + * @param aOuterObjectPaint pattern information from the outer text object + * @param aTargetPaint where to store the current pattern information + * @param aFillOrStroke member pointer to the paint we are setting up + * @param aProperty the frame property descriptor of the fill or stroke paint + * server frame + */ + void SetupInheritablePaint(gfxContext* aContext, + nsIFrame* aFrame, + float& aOpacity, + gfxTextObjectPaint* aOuterObjectPaint, + SVGTextObjectPaint::Paint& aTargetPaint, + nsStyleSVGPaint nsStyleSVG::*aFillOrStroke, + const FramePropertyDescriptor* aProperty); + /** * The MutationObserver we have registered for the element subtree. */