--- a/gfx/layers/wr/WebRenderBridgeChild.cpp
+++ b/gfx/layers/wr/WebRenderBridgeChild.cpp
@@ -207,47 +207,41 @@ WriteFontFileData(const uint8_t* aData,
return;
}
memcpy(data->mFontBuffer.mData, aData, aLength);
data->mFontIndex = aIndex;
}
void
-WebRenderBridgeChild::PushGlyphs(wr::DisplayListBuilder& aBuilder, const nsTArray<GlyphArray>& aGlyphs,
- gfx::ScaledFont* aFont, const StackingContextHelper& aSc,
+WebRenderBridgeChild::PushGlyphs(wr::DisplayListBuilder& aBuilder, const nsTArray<gfx::Glyph>& aGlyphs,
+ gfx::ScaledFont* aFont, const gfx::Color& aColor, const StackingContextHelper& aSc,
const LayerRect& aBounds, const LayerRect& aClip)
{
MOZ_ASSERT(aFont);
MOZ_ASSERT(!aGlyphs.IsEmpty());
wr::WrFontKey key = GetFontKeyForScaledFont(aFont);
MOZ_ASSERT(key.mNamespace.mHandle && key.mHandle);
- for (size_t i = 0; i < aGlyphs.Length(); i++) {
- GlyphArray glyph_array = aGlyphs[i];
- nsTArray<gfx::Glyph>& glyphs = glyph_array.glyphs();
-
- nsTArray<wr::GlyphInstance> wr_glyph_instances;
- wr_glyph_instances.SetLength(glyphs.Length());
+ nsTArray<wr::GlyphInstance> wr_glyph_instances;
+ wr_glyph_instances.SetLength(aGlyphs.Length());
- for (size_t j = 0; j < glyphs.Length(); j++) {
- wr_glyph_instances[j].index = glyphs[j].mIndex;
- wr_glyph_instances[j].point = aSc.ToRelativeLayoutPoint(
- LayerPoint::FromUnknownPoint(glyphs[j].mPosition));
- }
+ for (size_t j = 0; j < aGlyphs.Length(); j++) {
+ wr_glyph_instances[j].index = aGlyphs[j].mIndex;
+ wr_glyph_instances[j].point = aSc.ToRelativeLayoutPoint(
+ LayerPoint::FromUnknownPoint(aGlyphs[j].mPosition));
+ }
- aBuilder.PushText(aSc.ToRelativeLayoutRect(aBounds),
- aSc.ToRelativeLayoutRect(aClip),
- glyph_array.color().value(),
- key,
- Range<const wr::GlyphInstance>(wr_glyph_instances.Elements(), wr_glyph_instances.Length()),
- aFont->GetSize());
-
- }
+ aBuilder.PushText(aSc.ToRelativeLayoutRect(aBounds),
+ aSc.ToRelativeLayoutRect(aClip),
+ aColor,
+ key,
+ Range<const wr::GlyphInstance>(wr_glyph_instances.Elements(), wr_glyph_instances.Length()),
+ aFont->GetSize());
}
wr::FontKey
WebRenderBridgeChild::GetFontKeyForScaledFont(gfx::ScaledFont* aScaledFont)
{
MOZ_ASSERT(!mDestroyed);
MOZ_ASSERT(aScaledFont);
MOZ_ASSERT((aScaledFont->GetType() == gfx::FontType::DWRITE) ||
--- a/gfx/layers/wr/WebRenderBridgeChild.h
+++ b/gfx/layers/wr/WebRenderBridgeChild.h
@@ -99,18 +99,19 @@ public:
mIdNamespace = aIdNamespace;
}
wr::WrImageKey GetNextImageKey()
{
return wr::WrImageKey{ GetNamespace(), GetNextResourceId() };
}
- void PushGlyphs(wr::DisplayListBuilder& aBuilder, const nsTArray<GlyphArray>& aGlyphs,
- gfx::ScaledFont* aFont, const StackingContextHelper& aSc,
+ void PushGlyphs(wr::DisplayListBuilder& aBuilder, const nsTArray<gfx::Glyph>& aGlyphs,
+ gfx::ScaledFont* aFont, const gfx::Color& aColor,
+ const StackingContextHelper& aSc,
const LayerRect& aBounds, const LayerRect& aClip);
wr::FontKey GetFontKeyForScaledFont(gfx::ScaledFont* aScaledFont);
void RemoveExpiredFontKeys();
void ClearReadLocks();
void BeginClearCachedResources();
--- a/gfx/layers/wr/WebRenderTextLayer.cpp
+++ b/gfx/layers/wr/WebRenderTextLayer.cpp
@@ -35,13 +35,16 @@ WebRenderTextLayer::RenderLayer(wr::Disp
// WebRender compositing, we don't pass the transform on this layer to
// WR, so WR has no way of knowing about the transformed bounds unless
// we apply it here. The glyphs that we push to WR should already be
// taking the transform into account.
GetTransform().TransformBounds(IntRectToRect(mBounds))
);
DumpLayerInfo("TextLayer", rect);
- WrBridge()->PushGlyphs(aBuilder, mGlyphs, mFont, aSc, rect, rect);
+ for (GlyphArray& glyphs : mGlyphs) {
+ WrBridge()->PushGlyphs(aBuilder, glyphs.glyphs(), mFont,
+ glyphs.color().value(), aSc, rect, rect);
+ }
}
} // namespace layers
} // namespace mozilla
--- a/gfx/webrender_bindings/WebRenderAPI.cpp
+++ b/gfx/webrender_bindings/WebRenderAPI.cpp
@@ -958,16 +958,55 @@ DisplayListBuilder::PushText(const wr::L
wr_dp_push_text(mWrState, aBounds, aClip,
ToColorF(aColor),
aFontKey,
&aGlyphBuffer[0], aGlyphBuffer.length(),
aGlyphSize);
}
void
+DisplayListBuilder::PushLine(const wr::LayoutRect& aClip,
+ const wr::Line& aLine)
+{
+ wr_dp_push_line(mWrState, aClip, aLine.baseline, aLine.start, aLine.end,
+ aLine.orientation, aLine.width, aLine.color, aLine.style);
+
+/* TODO(Gankro): remove this
+ LayoutRect rect;
+ if (aLine.orientation == wr::LineOrientation::Horizontal) {
+ rect.origin.x = aLine.start;
+ rect.origin.y = aLine.baseline;
+ rect.size.width = aLine.end - aLine.start;
+ rect.size.height = aLine.width;
+ } else {
+ rect.origin.x = aLine.baseline;
+ rect.origin.y = aLine.start;
+ rect.size.width = aLine.width;
+ rect.size.height = aLine.end - aLine.start;
+ }
+
+ PushRect(rect, aClip, aLine.color);
+*/
+}
+
+void
+DisplayListBuilder::PushTextShadow(const wr::LayoutRect& aRect,
+ const wr::LayoutRect& aClip,
+ const wr::TextShadow& aShadow)
+{
+ wr_dp_push_text_shadow(mWrState, aRect, aClip, aShadow);
+}
+
+void
+DisplayListBuilder::PopTextShadow()
+{
+ wr_dp_pop_text_shadow(mWrState);
+}
+
+void
DisplayListBuilder::PushBoxShadow(const wr::LayoutRect& aRect,
const wr::LayoutRect& aClip,
const wr::LayoutRect& aBoxBounds,
const wr::LayoutVector2D& aOffset,
const wr::ColorF& aColor,
const float& aBlurRadius,
const float& aSpreadRadius,
const float& aBorderRadius,
--- a/gfx/webrender_bindings/WebRenderAPI.h
+++ b/gfx/webrender_bindings/WebRenderAPI.h
@@ -31,16 +31,28 @@ class WebRenderBridgeParent;
}
namespace wr {
class DisplayListBuilder;
class RendererOGL;
class RendererEvent;
+// This isn't part of WR's API, but we define it here to simplify layout's
+// logic and data plumbing.
+struct Line {
+ float baseline;
+ float start;
+ float end;
+ float width;
+ wr::ColorF color;
+ wr::LineOrientation orientation;
+ wr::LineStyle style;
+};
+
class WebRenderAPI
{
NS_INLINE_DECL_REFCOUNTING(WebRenderAPI);
public:
/// This can be called on the compositor thread only.
static already_AddRefed<WebRenderAPI> Create(bool aEnableProfiler,
layers::CompositorBridgeParentBase* aBridge,
@@ -290,16 +302,27 @@ public:
void PushText(const wr::LayoutRect& aBounds,
const wr::LayoutRect& aClip,
const gfx::Color& aColor,
wr::FontKey aFontKey,
Range<const wr::GlyphInstance> aGlyphBuffer,
float aGlyphSize);
+ void PushLine(const wr::LayoutRect& aClip,
+ const wr::Line& aLine);
+
+ void PushTextShadow(const wr::LayoutRect& aBounds,
+ const wr::LayoutRect& aClip,
+ const wr::TextShadow& aShadow);
+
+ void PopTextShadow();
+
+
+
void PushBoxShadow(const wr::LayoutRect& aRect,
const wr::LayoutRect& aClip,
const wr::LayoutRect& aBoxBounds,
const wr::LayoutVector2D& aOffset,
const wr::ColorF& aColor,
const float& aBlurRadius,
const float& aSpreadRadius,
const float& aBorderRadius,
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -527,17 +527,17 @@ pub struct WrThreadPool(Arc<rayon::Threa
#[no_mangle]
pub unsafe extern "C" fn wr_thread_pool_new() -> *mut WrThreadPool {
let worker_config = rayon::Configuration::new()
.thread_name(|idx|{ format!("WebRender:Worker#{}", idx) })
.start_handler(|idx| {
register_thread_with_profiler(format!("WebRender:Worker#{}", idx));
});
- let workers = Arc::new(rayon::ThreadPool::new(worker_config).unwrap());
+ let workers = Arc::new(rayon::ThreadPool::new(worker_config).unwrap());
Box::into_raw(Box::new(WrThreadPool(workers)))
}
/// cbindgen:postfix=WR_DESTRUCTOR_SAFE_FUNC
#[no_mangle]
pub unsafe extern "C" fn wr_thread_pool_delete(thread_pool: *mut WrThreadPool) {
Box::from_raw(thread_pool);
@@ -1277,16 +1277,58 @@ pub extern "C" fn wr_dp_push_text(state:
&glyph_slice,
font_key,
colorf,
Au::from_f32_px(glyph_size),
glyph_options);
}
#[no_mangle]
+pub extern "C" fn wr_dp_push_text_shadow(state: &mut WrState,
+ bounds: LayoutRect,
+ clip: LayoutRect,
+ shadow: TextShadow) {
+ assert!(unsafe { is_in_main_thread() });
+
+ state.frame_builder.dl_builder.push_text_shadow(bounds, Some(LocalClip::Rect(clip.into())), shadow.into());
+}
+
+#[no_mangle]
+pub extern "C" fn wr_dp_pop_text_shadow(state: &mut WrState) {
+ assert!(unsafe { is_in_main_thread() });
+
+ state.frame_builder.dl_builder.pop_text_shadow();
+}
+
+#[no_mangle]
+pub extern "C" fn wr_dp_push_line(state: &mut WrState,
+ clip: LayoutRect,
+ baseline: f32,
+ start: f32,
+ end: f32,
+ orientation: LineOrientation,
+ width: f32,
+ color: ColorF,
+ style: LineStyle) {
+ assert!(unsafe { is_in_main_thread() });
+
+ state.frame_builder
+ .dl_builder
+ .push_line(Some(LocalClip::Rect(clip.into())),
+ baseline,
+ start,
+ end,
+ orientation,
+ width,
+ color,
+ style);
+
+}
+
+#[no_mangle]
pub extern "C" fn wr_dp_push_border(state: &mut WrState,
rect: LayoutRect,
clip: LayoutRect,
widths: BorderWidths,
top: BorderSide,
right: BorderSide,
bottom: BorderSide,
left: BorderSide,
--- a/gfx/webrender_bindings/webrender_ffi_generated.h
+++ b/gfx/webrender_bindings/webrender_ffi_generated.h
@@ -72,16 +72,32 @@ enum class ImageFormat : uint32_t {
enum class ImageRendering : uint32_t {
Auto = 0,
CrispEdges = 1,
Pixelated = 2,
Sentinel /* this must be last for serialization purposes. */
};
+enum class LineOrientation : uint8_t {
+ Vertical = 0,
+ Horizontal = 1,
+
+ Sentinel /* this must be last for serialization purposes. */
+};
+
+enum class LineStyle : uint8_t {
+ Solid = 0,
+ Dotted = 1,
+ Dashed = 2,
+ Wavy = 3,
+
+ Sentinel /* this must be last for serialization purposes. */
+};
+
enum class MixBlendMode : uint32_t {
Normal = 0,
Multiply = 1,
Screen = 2,
Overlay = 3,
Darken = 4,
Lighten = 5,
ColorDodge = 6,
@@ -551,16 +567,28 @@ struct GlyphInstance {
LayoutPoint point;
bool operator==(const GlyphInstance& aOther) const {
return index == aOther.index &&
point == aOther.point;
}
};
+struct TextShadow {
+ LayoutVector2D offset;
+ ColorF color;
+ float blur_radius;
+
+ bool operator==(const TextShadow& aOther) const {
+ return offset == aOther.offset &&
+ color == aOther.color &&
+ blur_radius == aOther.blur_radius;
+ }
+};
+
typedef YuvColorSpace WrYuvColorSpace;
struct MutByteSlice {
uint8_t *buffer;
size_t len;
bool operator==(const MutByteSlice& aOther) const {
return buffer == aOther.buffer &&
@@ -796,16 +824,20 @@ WR_INLINE
void wr_dp_pop_scroll_layer(WrState *aState)
WR_FUNC;
WR_INLINE
void wr_dp_pop_stacking_context(WrState *aState)
WR_FUNC;
WR_INLINE
+void wr_dp_pop_text_shadow(WrState *aState)
+WR_FUNC;
+
+WR_INLINE
void wr_dp_push_border(WrState *aState,
LayoutRect aRect,
LayoutRect aClip,
BorderWidths aWidths,
BorderSide aTop,
BorderSide aRight,
BorderSide aBottom,
BorderSide aLeft,
@@ -892,16 +924,28 @@ void wr_dp_push_image(WrState *aState,
LayoutRect aClip,
LayoutSize aStretchSize,
LayoutSize aTileSpacing,
ImageRendering aImageRendering,
WrImageKey aKey)
WR_FUNC;
WR_INLINE
+void wr_dp_push_line(WrState *aState,
+ LayoutRect aClip,
+ float aBaseline,
+ float aStart,
+ float aEnd,
+ LineOrientation aOrientation,
+ float aWidth,
+ ColorF aColor,
+ LineStyle aStyle)
+WR_FUNC;
+
+WR_INLINE
void wr_dp_push_linear_gradient(WrState *aState,
LayoutRect aRect,
LayoutRect aClip,
LayoutPoint aStartPoint,
LayoutPoint aEndPoint,
const GradientStop *aStops,
size_t aStopsCount,
ExtendMode aExtendMode,
@@ -956,16 +1000,23 @@ void wr_dp_push_text(WrState *aState,
ColorF aColor,
WrFontKey aFontKey,
const GlyphInstance *aGlyphs,
uint32_t aGlyphCount,
float aGlyphSize)
WR_FUNC;
WR_INLINE
+void wr_dp_push_text_shadow(WrState *aState,
+ LayoutRect aBounds,
+ LayoutRect aClip,
+ TextShadow aShadow)
+WR_FUNC;
+
+WR_INLINE
void wr_dp_push_yuv_NV12_image(WrState *aState,
LayoutRect aBounds,
LayoutRect aClip,
WrImageKey aImageKey0,
WrImageKey aImageKey1,
WrYuvColorSpace aColorSpace,
ImageRendering aImageRendering)
WR_FUNC;
new file mode 100644
--- /dev/null
+++ b/layout/generic/TextDrawTarget.h
@@ -0,0 +1,458 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef TextDrawTarget_h
+#define TextDrawTarget_h
+
+#include "mozilla/gfx/2D.h"
+
+namespace mozilla {
+namespace layout {
+
+using namespace gfx;
+
+// This is used by all Advanced Layers users, so we use plain gfx types
+struct TextRunFragment {
+ ScaledFont* font;
+ Color color;
+ nsTArray<gfx::Glyph> glyphs;
+};
+
+// Only webrender handles this, so we use webrender types
+struct SelectionFragment {
+ wr::ColorF color;
+ wr::LayoutRect rect;
+};
+
+// This class is fake DrawTarget, used to intercept text draw calls, while
+// also collecting up the other aspects of text natively.
+//
+// When using advanced-layers in nsDisplayText's constructor, we construct this
+// and run the full painting algorithm with this as the DrawTarget. This is
+// done to avoid having to massively refactor gecko's text painting code (which
+// has lots of components shared between other rendering algorithms).
+//
+// In some phases of the painting algorithm, we can grab the relevant values
+// and feed them directly into TextDrawTarget. For instance, selections,
+// decorations, and shadows are handled in this manner. In those cases we can
+// also short-circuit the painting algorithm to save work.
+//
+// In other phases, the computed values are sufficiently buried in complex
+// code that it's best for us to just intercept the final draw calls. This
+// is how we handle computing the glyphs of the main text and text-emphasis
+// (see our overloaded FillGlyphs implementation).
+//
+// To be clear: this is a big hack. With time we hope to refactor the codebase
+// so that all the elements of text are handled directly by TextDrawTarget,
+// which is to say everything is done like we do selections and shadows now.
+// This design is a good step for doing this work incrementally.
+//
+// This is also likely to be a bit buggy (missing or misinterpreted info)
+// while we further develop the design.
+//
+// This does not currently support SVG text effects.
+class TextDrawTarget : public DrawTarget
+{
+public:
+ // The different phases of drawing the text we're in
+ // Each should only happen once, and in the given order.
+ enum class Phase : uint8_t {
+ eSelection, eUnderline, eOverline, eGlyphs, eEmphasisMarks, eLineThrough
+ };
+
+ explicit TextDrawTarget()
+ : mCurrentlyDrawing(Phase::eSelection)
+ {
+ mCurrentTarget = gfx::Factory::CreateDrawTarget(gfx::BackendType::SKIA, IntSize(1, 1), gfx::SurfaceFormat::B8G8R8A8);
+ }
+
+ // Prevent this from being copied
+ TextDrawTarget(const TextDrawTarget& src) = delete;
+ TextDrawTarget& operator=(const TextDrawTarget&) = delete;
+
+ // Change the phase of text we're drawing.
+ void StartDrawing(Phase aPhase) { mCurrentlyDrawing = aPhase; }
+
+ // This overload just stores the glyphs/font/color.
+ void
+ FillGlyphs(ScaledFont* aFont,
+ const GlyphBuffer& aBuffer,
+ const Pattern& aPattern,
+ const DrawOptions& aOptions,
+ const GlyphRenderingOptions* aRenderingOptions) override
+ {
+ // FIXME: figure out which of these asserts are real
+ MOZ_RELEASE_ASSERT(aOptions.mCompositionOp == CompositionOp::OP_OVER);
+ MOZ_RELEASE_ASSERT(aOptions.mAlpha == 1.0f);
+
+ // Make sure we're only given color patterns
+ MOZ_RELEASE_ASSERT(aPattern.GetType() == PatternType::COLOR);
+ const ColorPattern* colorPat = static_cast<const ColorPattern*>(&aPattern);
+
+ // Make sure the font exists
+ MOZ_RELEASE_ASSERT(aFont);
+
+ // FIXME(?): Deal with AA on the DrawOptions, and the GlyphRenderingOptions
+
+ if (mCurrentlyDrawing != Phase::eGlyphs &&
+ mCurrentlyDrawing != Phase::eEmphasisMarks) {
+ MOZ_CRASH("TextDrawTarget received glyphs in wrong phase");
+ }
+
+ // We need to push a new TextRunFragment whenever the font/color changes
+ // (usually this implies some font fallback from mixing languages/emoji)
+ TextRunFragment* fragment;
+ if (mText.IsEmpty() ||
+ mText.LastElement().font != aFont ||
+ mText.LastElement().color != colorPat->mColor) {
+ fragment = mText.AppendElement();
+ fragment->font = aFont;
+ fragment->color = colorPat->mColor;
+ } else {
+ fragment = &mText.LastElement();
+ }
+
+ nsTArray<Glyph>& glyphs = fragment->glyphs;
+
+ size_t oldLength = glyphs.Length();
+ glyphs.SetLength(oldLength + aBuffer.mNumGlyphs);
+ PodCopy(glyphs.Elements() + oldLength, aBuffer.mGlyphs, aBuffer.mNumGlyphs);
+ }
+
+ void AppendShadow(const wr::TextShadow& aShadow) { mShadows.AppendElement(aShadow); }
+
+ void
+ AppendSelection(const LayoutDeviceRect& aRect, const Color& aColor)
+ {
+ SelectionFragment frag;
+ frag.rect = wr::ToLayoutRect(aRect);
+ frag.color = wr::ToColorF(aColor);
+ mSelections.AppendElement(frag);
+ }
+
+ void
+ AppendDecoration(const Point& aStart,
+ const Point& aEnd,
+ const float aThickness,
+ const bool aVertical,
+ const Color& aColor,
+ const uint8_t aStyle)
+ {
+ wr::Line* decoration;
+
+ switch (mCurrentlyDrawing) {
+ case Phase::eUnderline:
+ case Phase::eOverline:
+ decoration = mBeforeDecorations.AppendElement();
+ break;
+ case Phase::eLineThrough:
+ decoration = mAfterDecorations.AppendElement();
+ break;
+ default:
+ MOZ_CRASH("TextDrawTarget received Decoration in wrong phase");
+ }
+
+ // This function is basically designed to slide into the decoration drawing
+ // code of nsCSSRendering with minimum disruption, to minimize the
+ // chances of implementation drift. As such, it mostly looks like a call
+ // to a skia-style StrokeLine method: two end-points, with a thickness
+ // and style. Notably the end-points are *centered* in the block direction,
+ // even though webrender wants a rect-like representation, where the points
+ // are on corners.
+ //
+ // So we mangle the format here in a single centralized place, where neither
+ // webrender nor nsCSSRendering has to care about this mismatch.
+ decoration->baseline = (aVertical ? aStart.x : aStart.y) - aThickness / 2;
+ decoration->start = aVertical ? aStart.y : aStart.x;
+ decoration->end = aVertical ? aEnd.y : aEnd.x;
+ decoration->width = aThickness;
+ decoration->color = wr::ToColorF(aColor);
+ decoration->orientation = aVertical
+ ? wr::LineOrientation::Vertical
+ : wr::LineOrientation::Horizontal;
+
+ switch (aStyle) {
+ case NS_STYLE_TEXT_DECORATION_STYLE_SOLID:
+ decoration->style = wr::LineStyle::Solid;
+ break;
+ case NS_STYLE_TEXT_DECORATION_STYLE_DOTTED:
+ decoration->style = wr::LineStyle::Dotted;
+ break;
+ case NS_STYLE_TEXT_DECORATION_STYLE_DASHED:
+ decoration->style = wr::LineStyle::Dashed;
+ break;
+ case NS_STYLE_TEXT_DECORATION_STYLE_WAVY:
+ decoration->style = wr::LineStyle::Wavy;
+ break;
+ // Double lines should be lowered to two solid lines
+ case NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE:
+ default:
+ MOZ_CRASH("TextDrawTarget received unsupported line style");
+ }
+
+
+ }
+
+ const nsTArray<wr::TextShadow>& GetShadows() { return mShadows; }
+ const nsTArray<TextRunFragment>& GetText() { return mText; }
+ const nsTArray<SelectionFragment>& GetSelections() { return mSelections; }
+ const nsTArray<wr::Line>& GetBeforeDecorations() { return mBeforeDecorations; }
+ const nsTArray<wr::Line>& GetAfterDecorations() { return mAfterDecorations; }
+
+ bool
+ CanSerializeFonts()
+ {
+ for (const TextRunFragment& frag : GetText()) {
+ if (!frag.font->CanSerialize()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ bool
+ TryMerge(const TextDrawTarget& other) {
+ if (mShadows != other.mShadows) {
+ return false;
+ }
+
+ mText.AppendElements(other.mText);
+ mSelections.AppendElements(other.mSelections);
+ mBeforeDecorations.AppendElements(other.mBeforeDecorations);
+ mAfterDecorations.AppendElements(other.mAfterDecorations);
+
+ return true;
+ }
+
+
+private:
+ // The part of the text we're currently drawing (glyphs, underlines, etc.)
+ Phase mCurrentlyDrawing;
+
+ // Properties of the whole text
+ nsTArray<wr::TextShadow> mShadows;
+ nsTArray<TextRunFragment> mText;
+ nsTArray<SelectionFragment> mSelections;
+ nsTArray<wr::Line> mBeforeDecorations;
+ nsTArray<wr::Line> mAfterDecorations;
+
+ // A dummy to handle parts of the DrawTarget impl we don't care for
+ RefPtr<DrawTarget> mCurrentTarget;
+
+ // The rest of this is dummy implementations of DrawTarget's API
+public:
+ DrawTargetType GetType() const override {
+ return mCurrentTarget->GetType();
+ }
+
+ BackendType GetBackendType() const override {
+ return mCurrentTarget->GetBackendType();
+ }
+
+ bool IsRecording() const override { return true; }
+ bool IsCaptureDT() const override { return false; }
+
+ already_AddRefed<SourceSurface> Snapshot() override {
+ return mCurrentTarget->Snapshot();
+ }
+
+ already_AddRefed<SourceSurface> IntoLuminanceSource(LuminanceType aLuminanceType,
+ float aOpacity) override {
+ return mCurrentTarget->IntoLuminanceSource(aLuminanceType, aOpacity);
+ }
+
+ IntSize GetSize() override {
+ return mCurrentTarget->GetSize();
+ }
+
+ void Flush() override {
+ mCurrentTarget->Flush();
+ }
+
+ void DrawCapturedDT(DrawTargetCapture *aCaptureDT,
+ const Matrix& aTransform) override {
+ mCurrentTarget->DrawCapturedDT(aCaptureDT, aTransform);
+ }
+
+ void DrawSurface(SourceSurface *aSurface,
+ const Rect &aDest,
+ const Rect &aSource,
+ const DrawSurfaceOptions &aSurfOptions,
+ const DrawOptions &aOptions) override {
+ mCurrentTarget->DrawSurface(aSurface, aDest, aSource, aSurfOptions, aOptions);
+ }
+
+ void DrawFilter(FilterNode *aNode,
+ const Rect &aSourceRect,
+ const Point &aDestPoint,
+ const DrawOptions &aOptions) override {
+ mCurrentTarget->DrawFilter(aNode, aSourceRect, aDestPoint, aOptions);
+ }
+
+ void DrawSurfaceWithShadow(SourceSurface *aSurface,
+ const Point &aDest,
+ const Color &aColor,
+ const Point &aOffset,
+ Float aSigma,
+ CompositionOp aOperator) override {
+ mCurrentTarget->DrawSurfaceWithShadow(aSurface, aDest, aColor, aOffset, aSigma, aOperator);
+ }
+
+ void ClearRect(const Rect &aRect) override {
+ mCurrentTarget->ClearRect(aRect);
+ }
+
+ void CopySurface(SourceSurface *aSurface,
+ const IntRect &aSourceRect,
+ const IntPoint &aDestination) override {
+ mCurrentTarget->CopySurface(aSurface, aSourceRect, aDestination);
+ }
+
+ void FillRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions = DrawOptions()) override {
+ mCurrentTarget->FillRect(aRect, aPattern, aOptions);
+ }
+
+ void StrokeRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions,
+ const DrawOptions &aOptions) override {
+ mCurrentTarget->StrokeRect(aRect, aPattern, aStrokeOptions, aOptions);
+ }
+
+ void StrokeLine(const Point &aStart,
+ const Point &aEnd,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions,
+ const DrawOptions &aOptions) override {
+ mCurrentTarget->StrokeLine(aStart, aEnd, aPattern, aStrokeOptions, aOptions);
+ }
+
+
+ void Stroke(const Path *aPath,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions,
+ const DrawOptions &aOptions) override {
+ mCurrentTarget->Stroke(aPath, aPattern, aStrokeOptions, aOptions);
+ }
+
+ void Fill(const Path *aPath,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions) override {
+ mCurrentTarget->Fill(aPath, aPattern, aOptions);
+ }
+
+ void StrokeGlyphs(ScaledFont* aFont,
+ const GlyphBuffer& aBuffer,
+ const Pattern& aPattern,
+ const StrokeOptions& aStrokeOptions,
+ const DrawOptions& aOptions,
+ const GlyphRenderingOptions* aRenderingOptions) override {
+ MOZ_ASSERT(mCurrentlyDrawing == Phase::eGlyphs);
+ mCurrentTarget->StrokeGlyphs(aFont, aBuffer, aPattern,
+ aStrokeOptions, aOptions, aRenderingOptions);
+ }
+
+ void Mask(const Pattern &aSource,
+ const Pattern &aMask,
+ const DrawOptions &aOptions) override {
+ return mCurrentTarget->Mask(aSource, aMask, aOptions);
+ }
+
+ void MaskSurface(const Pattern &aSource,
+ SourceSurface *aMask,
+ Point aOffset,
+ const DrawOptions &aOptions) override {
+ return mCurrentTarget->MaskSurface(aSource, aMask, aOffset, aOptions);
+ }
+
+ bool Draw3DTransformedSurface(SourceSurface* aSurface,
+ const Matrix4x4& aMatrix) override {
+ return mCurrentTarget->Draw3DTransformedSurface(aSurface, aMatrix);
+ }
+
+ void PushClip(const Path *aPath) override {
+ mCurrentTarget->PushClip(aPath);
+ }
+
+ void PushClipRect(const Rect &aRect) override {
+ mCurrentTarget->PushClipRect(aRect);
+ }
+
+ void PushDeviceSpaceClipRects(const IntRect* aRects, uint32_t aCount) override {
+ mCurrentTarget->PushDeviceSpaceClipRects(aRects, aCount);
+ }
+
+ void PopClip() override {
+ mCurrentTarget->PopClip();
+ }
+
+ void PushLayer(bool aOpaque, Float aOpacity,
+ SourceSurface* aMask,
+ const Matrix& aMaskTransform,
+ const IntRect& aBounds,
+ bool aCopyBackground) override {
+ mCurrentTarget->PushLayer(aOpaque, aOpacity, aMask, aMaskTransform, aBounds, aCopyBackground);
+ }
+
+ void PopLayer() override {
+ mCurrentTarget->PopLayer();
+ }
+
+
+ already_AddRefed<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
+ const IntSize &aSize,
+ int32_t aStride,
+ SurfaceFormat aFormat) const override {
+ return mCurrentTarget->CreateSourceSurfaceFromData(aData, aSize, aStride, aFormat);
+ }
+
+ already_AddRefed<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const override {
+ return mCurrentTarget->OptimizeSourceSurface(aSurface);
+ }
+
+ already_AddRefed<SourceSurface>
+ CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const override {
+ return mCurrentTarget->CreateSourceSurfaceFromNativeSurface(aSurface);
+ }
+
+ already_AddRefed<DrawTarget>
+ CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override {
+ return mCurrentTarget->CreateSimilarDrawTarget(aSize, aFormat);
+ }
+
+ already_AddRefed<PathBuilder> CreatePathBuilder(FillRule aFillRule) const override {
+ return mCurrentTarget->CreatePathBuilder(aFillRule);
+ }
+
+ already_AddRefed<FilterNode> CreateFilter(FilterType aType) override {
+ return mCurrentTarget->CreateFilter(aType);
+ }
+
+ already_AddRefed<GradientStops>
+ CreateGradientStops(GradientStop *aStops,
+ uint32_t aNumStops,
+ ExtendMode aExtendMode) const override {
+ return mCurrentTarget->CreateGradientStops(aStops, aNumStops, aExtendMode);
+ }
+
+ void SetTransform(const Matrix &aTransform) override {
+ mCurrentTarget->SetTransform(aTransform);
+ // Need to do this to make inherited GetTransform to work
+ DrawTarget::SetTransform(aTransform);
+ }
+
+ void* GetNativeSurface(NativeSurfaceType aType) override {
+ return mCurrentTarget->GetNativeSurface(aType);
+ }
+
+ void DetachAllSnapshots() override { mCurrentTarget->DetachAllSnapshots(); }
+};
+
+}
+}
+
+#endif
--- a/layout/generic/moz.build
+++ b/layout/generic/moz.build
@@ -98,16 +98,17 @@ EXPORTS += [
'nsRubyTextFrame.h',
'nsSplittableFrame.h',
'nsSubDocumentFrame.h',
'nsTextFrameUtils.h',
'nsTextRunTransformations.h',
'RubyUtils.h',
'ScrollbarActivity.h',
'ScrollSnap.h',
+ 'TextDrawTarget.h',
'Visibility.h',
]
EXPORTS.mozilla += [
'CSSAlignUtils.h',
'CSSOrderAwareFrameIterator.h',
'FrameTypeList.h',
'ReflowInput.h',
--- a/layout/generic/nsBulletFrame.cpp
+++ b/layout/generic/nsBulletFrame.cpp
@@ -514,17 +514,21 @@ BulletRenderer::CreateWebRenderCommandsF
const int32_t appUnitsPerDevPixel = aItem->Frame()->PresContext()->AppUnitsPerDevPixel();
bool dummy;
LayerRect destRect = ViewAs<LayerPixel>(
LayoutDeviceRect::FromAppUnits(
aItem->GetBounds(aDisplayListBuilder, &dummy), appUnitsPerDevPixel),
PixelCastJustification::WebRenderHasUnitResolution);
- aManager->WrBridge()->PushGlyphs(aBuilder, mGlyphs, mFont, aSc, destRect, destRect);
+ for (layers::GlyphArray& glyphs : mGlyphs) {
+ aManager->WrBridge()->PushGlyphs(aBuilder, glyphs.glyphs(), mFont,
+ glyphs.color().value(),
+ aSc, destRect, destRect);
+ }
}
class nsDisplayBullet final : public nsDisplayItem {
public:
nsDisplayBullet(nsDisplayListBuilder* aBuilder, nsBulletFrame* aFrame)
: nsDisplayItem(aBuilder, aFrame)
, mDisableSubpixelAA(false)
{
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -50,16 +50,17 @@
#include "nsIMathMLFrame.h"
#include "nsPlaceholderFrame.h"
#include "nsTextFrameUtils.h"
#include "nsTextRunTransformations.h"
#include "MathMLTextRunFactory.h"
#include "nsUnicodeProperties.h"
#include "nsStyleUtil.h"
#include "nsRubyFrame.h"
+#include "TextDrawTarget.h"
#include "nsTextFragment.h"
#include "nsGkAtoms.h"
#include "nsFrameSelection.h"
#include "nsRange.h"
#include "nsCSSRendering.h"
#include "nsContentUtils.h"
#include "nsLineBreaker.h"
@@ -77,16 +78,17 @@
#include <limits>
#ifdef ACCESSIBILITY
#include "nsAccessibilityService.h"
#endif
#include "nsPrintfCString.h"
#include "gfxContext.h"
+#include "mozilla/gfx/DrawTargetRecording.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/dom/Element.h"
#include "mozilla/LookAndFeel.h"
#include "GeckoProfiler.h"
#ifdef DEBUG
@@ -101,16 +103,18 @@
#undef DrawText
#endif
using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::gfx;
using namespace mozilla::layers;
+typedef mozilla::layout::TextDrawTarget TextDrawTarget;
+
struct TabWidth {
TabWidth(uint32_t aOffset, uint32_t aWidth)
: mOffset(aOffset), mWidth(float(aWidth))
{ }
uint32_t mOffset; // DOM offset relative to the current frame's offset.
float mWidth; // extra space to be added at this position (in app units)
};
@@ -4970,17 +4974,17 @@ public:
virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
const nsDisplayItemGeometry* aGeometry,
nsRegion *aInvalidRegion) override;
virtual void DisableComponentAlpha() override {
mDisableSubpixelAA = true;
}
- void RenderToContext(gfxContext* aCtx, nsDisplayListBuilder* aBuilder, bool aIsRecording = false);
+ void RenderToContext(gfxContext* aCtx, TextDrawTarget* aTextDrawer, nsDisplayListBuilder* aBuilder, bool aIsRecording = false);
bool CanApplyOpacity() const override
{
nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
if (f->IsSelected()) {
return false;
}
@@ -5025,44 +5029,47 @@ public:
}
void GetMergedFrames(nsTArray<nsIFrame*>* aFrames) override
{
aFrames->AppendElements(mMergedFrames);
}
bool TryMerge(nsDisplayItem* aItem) override {
- if (aItem->GetType() != DisplayItemType::TYPE_TEXT)
+ if (aItem->GetType() != DisplayItemType::TYPE_TEXT) {
return false;
- if (aItem->GetClipChain() != GetClipChain())
+ }
+
+ if (aItem->GetClipChain() != GetClipChain()) {
return false;
+ }
nsDisplayText* other = static_cast<nsDisplayText*>(aItem);
- if (!mFont || !other->mFont || mFont != other->mFont) {
+ if (!mTextDrawer || !other->mTextDrawer) {
return false;
}
+
if (mOpacity != other->mOpacity) {
return false;
}
+ if (!mTextDrawer->TryMerge(*other->mTextDrawer)) {
+ return false;
+ }
+
mBounds.UnionRect(mBounds, other->mBounds);
mVisibleRect.UnionRect(mVisibleRect, other->mVisibleRect);
mMergedFrames.AppendElement(static_cast<nsTextFrame*>(other->mFrame));
mMergedFrames.AppendElements(mozilla::Move(other->mMergedFrames));
- for (GlyphArray& g : other->mGlyphs) {
- GlyphArray* append = mGlyphs.AppendElement();
- append->color() = g.color();
- append->glyphs().SwapElements(g.glyphs());
- }
return true;
}
- RefPtr<ScaledFont> mFont;
- nsTArray<GlyphArray> mGlyphs;
+ RefPtr<TextDrawTarget> mTextDrawer;
+
nsTArray<nsTextFrame*> mMergedFrames;
nsRect mBounds;
float mOpacity;
bool mDisableSubpixelAA;
};
class nsDisplayTextGeometry : public nsCharClipGeometry
@@ -5133,68 +5140,82 @@ nsDisplayText::nsDisplayText(nsDisplayLi
MOZ_COUNT_CTOR(nsDisplayText);
mIsFrameSelected = aIsSelected;
mBounds = mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
// Bug 748228
mBounds.Inflate(mFrame->PresContext()->AppUnitsPerDevPixel());
if (gfxPrefs::LayersAllowTextLayers() &&
CanUseAdvancedLayer(aBuilder->GetWidgetLayerManager())) {
- RefPtr<DrawTarget> screenTarget = gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
- RefPtr<DrawTargetCapture> capture =
- Factory::CreateCaptureDrawTarget(screenTarget->GetBackendType(),
- IntSize(),
- screenTarget->GetFormat());
- RefPtr<gfxContext> captureCtx = gfxContext::CreateOrNull(capture);
+ mTextDrawer = new TextDrawTarget();
+ RefPtr<gfxContext> captureCtx = gfxContext::CreateOrNull(mTextDrawer);
// TODO: Paint() checks mDisableSubpixelAA, we should too.
- RenderToContext(captureCtx, aBuilder, true);
-
- // TODO: Ideally we'd re-use captureCtx in Paint() if we couldn't build
- // a layer here. We have to deal with the problem that the ScreenReferenceDrawTarget
- // might not be compatible with the DT used for layer rendering.
-
- GlyphArray* g = mGlyphs.AppendElement();
- std::vector<Glyph> glyphs;
- Color color;
- if (!capture->ContainsOnlyColoredGlyphs(mFont, color, glyphs)
- || !mFont
- || !mFont->CanSerialize()) {
- mFont = nullptr;
- mGlyphs.Clear();
- } else {
- g->glyphs().SetLength(glyphs.size());
- PodCopy(g->glyphs().Elements(), glyphs.data(), glyphs.size());
- g->color() = color;
- }
+ RenderToContext(captureCtx, mTextDrawer, aBuilder, true);
}
}
LayerState
nsDisplayText::GetLayerState(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
const ContainerLayerParameters& aParameters)
{
- if (mFont) {
+ // Basic things that all advanced backends need
+ if (!mTextDrawer ||
+ !mTextDrawer->CanSerializeFonts() ||
+ XRE_IsParentProcess()) {
+ return mozilla::LAYER_NONE;
+ }
+
+ // If we're using the webrender backend, then we're good to go!
+ if (aManager->GetBackendType() == layers::LayersBackend::LAYERS_WR) {
return mozilla::LAYER_ACTIVE;
}
- MOZ_ASSERT(mMergedFrames.IsEmpty());
- return mozilla::LAYER_NONE;
+
+ // If we're using the TextLayer backend, then we need to make sure
+ // the input is plain enough for it to handle
+
+ // Can't handle shadows, selections, or decorations
+ if (mTextDrawer->GetShadows().Length() > 0 ||
+ mTextDrawer->GetSelections().Length() > 0 ||
+ mTextDrawer->GetBeforeDecorations().Length() > 0 ||
+ mTextDrawer->GetAfterDecorations().Length() > 0) {
+ return mozilla::LAYER_NONE;
+ }
+
+ // Must only have one font (multiple colors is fine)
+ ScaledFont* font = nullptr;
+
+ for (const mozilla::layout::TextRunFragment& text : mTextDrawer->GetText()) {
+ if (!font) {
+ font = text.font;
+ }
+ if (font != text.font) {
+ return mozilla::LAYER_NONE;
+ }
+ }
+
+ // Must have an actual font (i.e. actual text)
+ if (!font) {
+ return mozilla::LAYER_NONE;
+ }
+
+ return mozilla::LAYER_ACTIVE;
}
void
nsDisplayText::Paint(nsDisplayListBuilder* aBuilder,
gfxContext* aCtx) {
AUTO_PROFILER_LABEL("nsDisplayText::Paint", GRAPHICS);
MOZ_ASSERT(mMergedFrames.IsEmpty());
DrawTargetAutoDisableSubpixelAntialiasing disable(aCtx->GetDrawTarget(),
mDisableSubpixelAA);
- RenderToContext(aCtx, aBuilder);
+ RenderToContext(aCtx, nullptr, aBuilder);
}
bool
nsDisplayText::CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
const StackingContextHelper& aSc,
nsTArray<WebRenderParentCommand>& aParentCommands,
WebRenderLayerManager* aManager,
nsDisplayListBuilder* aDisplayListBuilder)
@@ -5206,59 +5227,118 @@ nsDisplayText::CreateWebRenderCommands(m
}
}
if (mBounds.IsEmpty()) {
return true;
}
auto appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
- LayoutDeviceRect rect = LayoutDeviceRect::FromAppUnits(
+ LayoutDeviceRect layoutBoundsRect = LayoutDeviceRect::FromAppUnits(
mBounds, appUnitsPerDevPixel);
- LayoutDeviceRect clipRect = rect;
+ LayoutDeviceRect layoutClipRect = layoutBoundsRect;
if (GetClip().HasClip()) {
- clipRect = LayoutDeviceRect::FromAppUnits(
+ layoutClipRect = LayoutDeviceRect::FromAppUnits(
GetClip().GetClipRect(), appUnitsPerDevPixel);
}
- aManager->WrBridge()->PushGlyphs(aBuilder, mGlyphs, mFont, aSc,
- LayerRect::FromUnknownRect(rect.ToUnknownRect()),
- LayerRect::FromUnknownRect(clipRect.ToUnknownRect()));
+
+ LayerRect boundsRect = LayerRect::FromUnknownRect(layoutBoundsRect.ToUnknownRect());
+ LayerRect clipRect = LayerRect::FromUnknownRect(layoutClipRect.ToUnknownRect());
+ wr::LayoutRect wrClipRect = aSc.ToRelativeLayoutRect(clipRect); // wr::ToLayoutRect(clipRect);
+ wr::LayoutRect wrBoundsRect = aSc.ToRelativeLayoutRect(boundsRect); //wr::ToLayoutRect(boundsRect);
+
+ // Drawing order: selections, shadows,
+ // underline, overline, [grouped in one array]
+ // text, emphasisText, [grouped in one array]
+ // lineThrough
+
+ for (const mozilla::layout::SelectionFragment& selection:
+ mTextDrawer->GetSelections()) {
+ aBuilder.PushRect(selection.rect, wrClipRect, selection.color);
+ }
+
+ // WR takes the shadows in CSS-order (reverse of rendering order),
+ // because the drawing of a shadow actually occurs when it's popped.
+ for (const wr::TextShadow& shadow : mTextDrawer->GetShadows()) {
+ aBuilder.PushTextShadow(wrBoundsRect, wrClipRect, shadow);
+ }
+
+ for (const wr::Line& decoration: mTextDrawer->GetBeforeDecorations()) {
+ aBuilder.PushLine(wrClipRect, decoration);
+ }
+
+ for (const mozilla::layout::TextRunFragment& text: mTextDrawer->GetText()) {
+ aManager->WrBridge()->PushGlyphs(aBuilder, text.glyphs, text.font,
+ text.color, aSc, boundsRect, clipRect);
+ }
+
+ for (const wr::Line& decoration: mTextDrawer->GetAfterDecorations()) {
+ aBuilder.PushLine(wrClipRect, decoration);
+ }
+
+ for (size_t i = 0; i < mTextDrawer->GetShadows().Length(); ++i) {
+ aBuilder.PopTextShadow();
+ }
return true;
}
already_AddRefed<layers::Layer>
nsDisplayText::BuildLayer(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
const ContainerLayerParameters& aContainerParameters)
{
+ // If we're using webrender, we want layerless rendering, so emit a dummy.
+ // See CreateWebRenderCommands for actual drawing code.
+ if (aManager->GetBackendType() == layers::LayersBackend::LAYERS_WR) {
+ return BuildDisplayItemLayer(aBuilder, aManager, aContainerParameters);
+ }
+
// We should have all the glyphs recorded now, build
// the TextLayer.
RefPtr<layers::TextLayer> layer = static_cast<layers::TextLayer*>
(aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, this));
if (!layer) {
layer = aManager->CreateTextLayer();
}
- layer->SetGlyphs(Move(mGlyphs));
- layer->SetScaledFont(mFont);
+ // GetLayerState has guaranteed to us that we have exactly one font
+ // so this will be overwritten by the time we use it.
+ ScaledFont* font = nullptr;
+
+ nsTArray<GlyphArray> allGlyphs;
+ allGlyphs.SetCapacity(mTextDrawer->GetText().Length());
+ for (const mozilla::layout::TextRunFragment& text : mTextDrawer->GetText()) {
+ if (!font) {
+ font = text.font;
+ }
+
+ GlyphArray* glyphs = allGlyphs.AppendElement();
+ glyphs->glyphs() = text.glyphs;
+ glyphs->color() = text.color;
+ }
+
+ MOZ_ASSERT(font);
+
+ layer->SetGlyphs(Move(allGlyphs));
+ layer->SetScaledFont(font);
auto A2D = mFrame->PresContext()->AppUnitsPerDevPixel();
bool dummy;
const LayoutDeviceIntRect destBounds =
LayoutDeviceIntRect::FromAppUnitsToOutside(GetBounds(aBuilder, &dummy), A2D);
layer->SetBounds(IntRect(destBounds.x, destBounds.y, destBounds.width, destBounds.height));
layer->SetBaseTransform(gfx::Matrix4x4::Translation(aContainerParameters.mOffset.x,
aContainerParameters.mOffset.y, 0));
return layer.forget();
}
void
-nsDisplayText::RenderToContext(gfxContext* aCtx, nsDisplayListBuilder* aBuilder, bool aIsRecording)
+nsDisplayText::RenderToContext(gfxContext* aCtx, TextDrawTarget* aTextDrawer, nsDisplayListBuilder* aBuilder, bool aIsRecording)
{
nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
// Add 1 pixel of dirty area around mVisibleRect to allow us to paint
// antialiased pixels beyond the measured text extents.
// This is temporary until we do this in the actual calculation of text extents.
auto A2D = mFrame->PresContext()->AppUnitsPerDevPixel();
LayoutDeviceRect extraVisible =
@@ -5296,16 +5376,17 @@ nsDisplayText::RenderToContext(gfxContex
gfxMatrix mat = aCtx->CurrentMatrix()
.PreTranslate(pt).PreScale(scaleFactor, 1.0).PreTranslate(-pt);
aCtx->SetMatrix (mat);
}
}
nsTextFrame::PaintTextParams params(aCtx);
params.framePt = gfxPoint(framePt.x, framePt.y);
params.dirtyRect = extraVisible;
+ params.textDrawer = aTextDrawer;
if (aBuilder->IsForGenerateGlyphMask()) {
MOZ_ASSERT(!aBuilder->IsForPaintingSelectionBG());
params.state = nsTextFrame::PaintTextParams::GenerateTextMask;
} else if (aBuilder->IsForPaintingSelectionBG()) {
params.state = nsTextFrame::PaintTextParams::PaintTextBGColor;
} else {
params.state = nsTextFrame::PaintTextParams::PaintText;
@@ -5987,16 +6068,17 @@ nsTextFrame::ComputeSelectionUnderlineHe
enum class DecorationType
{
Normal, Selection
};
struct nsTextFrame::PaintDecorationLineParams
: nsCSSRendering::DecorationRectParams
{
gfxContext* context = nullptr;
+ TextDrawTarget* textDrawer = nullptr;
LayoutDeviceRect dirtyRect;
Point pt;
const nscolor* overrideColor = nullptr;
nscolor color = NS_RGBA(0, 0, 0, 0);
gfxFloat icoordInFrame = 0.0f;
DecorationType decorationType = DecorationType::Normal;
DrawPathCallbacks* callbacks = nullptr;
};
@@ -6005,16 +6087,17 @@ void
nsTextFrame::PaintDecorationLine(const PaintDecorationLineParams& aParams)
{
nsCSSRendering::PaintDecorationLineParams params;
static_cast<nsCSSRendering::DecorationRectParams&>(params) = aParams;
params.dirtyRect = aParams.dirtyRect.ToUnknownRect();
params.pt = aParams.pt;
params.color = aParams.overrideColor ? *aParams.overrideColor : aParams.color;
params.icoordInFrame = Float(aParams.icoordInFrame);
+ params.textDrawer = aParams.textDrawer;
if (aParams.callbacks) {
Rect path = nsCSSRendering::DecorationLineToPath(params);
if (aParams.decorationType == DecorationType::Normal) {
aParams.callbacks->PaintDecorationLine(path, params.color);
} else {
aParams.callbacks->PaintSelectionDecorationLine(path, params.color);
}
} else {
@@ -6420,25 +6503,41 @@ nsTextFrame::PaintOneShadow(const PaintS
if (aShadowDetails->mHasColor) {
shadowColor = aShadowDetails->mColor;
decorationOverrideColor = &shadowColor;
} else {
shadowColor = aParams.foregroundColor;
decorationOverrideColor = nullptr;
}
+ if (aParams.textDrawer) {
+ wr::TextShadow wrShadow;
+
+ wrShadow.offset = {
+ PresContext()->AppUnitsToFloatDevPixels(aShadowDetails->mXOffset),
+ PresContext()->AppUnitsToFloatDevPixels(aShadowDetails->mYOffset)
+ };
+
+ wrShadow.blur_radius = PresContext()->AppUnitsToFloatDevPixels(aShadowDetails->mRadius);
+ wrShadow.color = wr::ToColorF(ToDeviceColor(shadowColor));
+
+ aParams.textDrawer->AppendShadow(wrShadow);
+ return;
+ }
+
aParams.context->Save();
aParams.context->SetColor(Color::FromABGR(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.
gfxFloat advanceWidth;
nsTextPaintStyle textPaintStyle(this);
DrawTextParams params(shadowContext);
+ params.textDrawer = nullptr; // Don't record anything that happens here
params.advanceWidth = &advanceWidth;
params.dirtyRect = aParams.dirtyRect;
params.framePt = aParams.framePt + shadowOffset;
params.provider = aParams.provider;
params.textStyle = &textPaintStyle;
params.textColor =
aParams.context == shadowContext ? shadowColor : NS_RGB(0, 0, 0);
params.clipEdges = aParams.clipEdges;
@@ -6537,31 +6636,40 @@ nsTextFrame::PaintTextWithSelectionColor
gfxFloat offs = iOffset - (mTextRun->IsInlineReversed() ? advance : 0);
if (vertical) {
bgRect = nsRect(aParams.framePt.x, aParams.framePt.y + offs,
GetSize().width, advance);
} else {
bgRect = nsRect(aParams.framePt.x + offs, aParams.framePt.y,
advance, GetSize().height);
}
- PaintSelectionBackground(
- *aParams.context->GetDrawTarget(), background, aParams.dirtyRect,
- LayoutDeviceRect::FromAppUnits(bgRect, appUnitsPerDevPixel),
- aParams.callbacks);
+
+ LayoutDeviceRect selectionRect =
+ LayoutDeviceRect::FromAppUnits(bgRect, appUnitsPerDevPixel);
+
+ if (aParams.textDrawer) {
+ aParams.textDrawer->AppendSelection(selectionRect,
+ ToDeviceColor(background));
+ } else {
+ PaintSelectionBackground(
+ *aParams.context->GetDrawTarget(), background, aParams.dirtyRect,
+ selectionRect, aParams.callbacks);
+ }
}
iterator.UpdateWithAdvance(advance);
}
}
if (aParams.IsPaintBGColor()) {
return true;
}
gfxFloat advance;
DrawTextParams params(aParams.context);
+ params.textDrawer = aParams.textDrawer;
params.dirtyRect = aParams.dirtyRect;
params.framePt = aParams.framePt;
params.provider = aParams.provider;
params.textStyle = aParams.textPaintStyle;
params.clipEdges = &aClipEdges;
params.advanceWidth = &advance;
params.callbacks = aParams.callbacks;
@@ -7157,16 +7265,17 @@ nsTextFrame::PaintText(const PaintTextPa
shadowParams.provider = &provider;
shadowParams.foregroundColor = foregroundColor;
shadowParams.clipEdges = &clipEdges;
PaintShadows(textStyle->mTextShadow, shadowParams);
}
gfxFloat advanceWidth;
DrawTextParams params(aParams.context);
+ params.textDrawer = aParams.textDrawer;
params.dirtyRect = aParams.dirtyRect;
params.framePt = aParams.framePt;
params.provider = &provider;
params.advanceWidth = &advanceWidth;
params.textStyle = &textPaintStyle;
params.textColor = foregroundColor;
params.textStrokeColor = textStrokeColor;
params.textStrokeWidth = textPaintStyle.GetWebkitTextStrokeWidth();
@@ -7190,17 +7299,16 @@ DrawTextRun(const gfxTextRun* aTextRun,
params.callbacks = aParams.callbacks;
if (aParams.callbacks) {
aParams.callbacks->NotifyBeforeText(aParams.textColor);
params.drawMode = DrawMode::GLYPH_PATH;
aTextRun->Draw(aRange, aTextBaselinePt, params);
aParams.callbacks->NotifyAfterText();
} else {
if (NS_GET_A(aParams.textColor) != 0) {
- // Default drawMode is DrawMode::GLYPH_FILL
aParams.context->SetColor(Color::FromABGR(aParams.textColor));
} else {
params.drawMode = DrawMode::GLYPH_STROKE;
}
if (NS_GET_A(aParams.textStrokeColor) != 0 &&
aParams.textStrokeWidth != 0.0f) {
StrokeOptions strokeOpts;
@@ -7215,16 +7323,21 @@ DrawTextRun(const gfxTextRun* aTextRun,
}
}
void
nsTextFrame::DrawTextRun(Range aRange, const gfxPoint& aTextBaselinePt,
const DrawTextRunParams& aParams)
{
MOZ_ASSERT(aParams.advanceWidth, "Must provide advanceWidth");
+
+ if (aParams.textDrawer) {
+ aParams.textDrawer->StartDrawing(TextDrawTarget::Phase::eGlyphs);
+ }
+
::DrawTextRun(mTextRun, aTextBaselinePt, aRange, aParams);
if (aParams.drawSoftHyphen) {
// Don't use ctx as the context, because we need a reference context here,
// ctx may be transformed.
RefPtr<gfxTextRun> hyphenTextRun =
GetHyphenTextRun(mTextRun, nullptr, this);
if (hyphenTextRun) {
@@ -7299,16 +7412,17 @@ nsTextFrame::DrawTextRunAndDecorations(R
// so we will multiply the values from metrics by this factor.
gfxFloat decorationOffsetDir = mTextRun->IsSidewaysLeft() ? -1.0 : 1.0;
PaintDecorationLineParams params;
params.context = aParams.context;
params.dirtyRect = aParams.dirtyRect;
params.overrideColor = aParams.decorationOverrideColor;
params.callbacks = aParams.callbacks;
+ params.textDrawer = aParams.textDrawer;
// pt is the physical point where the decoration is to be drawn,
// relative to the frame; one of its coordinates will be updated below.
params.pt = Point(x / app, y / app);
Float& bCoord = verticalDec ? params.pt.x : params.pt.y;
params.lineSize = Size(measure / app, 0);
params.ascent = ascent;
params.vertical = verticalDec;
@@ -7346,22 +7460,30 @@ nsTextFrame::DrawTextRunAndDecorations(R
params.color = dec.mColor;
params.offset = decorationOffsetDir * metrics.*lineOffset;
params.style = dec.mStyle;
PaintDecorationLine(params);
};
// Underlines
+ if (aParams.textDrawer && aDecorations.mUnderlines.Length() > 0) {
+ aParams.textDrawer->StartDrawing(TextDrawTarget::Phase::eUnderline);
+ }
+ //
params.decoration = NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE;
for (const LineDecoration& dec : Reversed(aDecorations.mUnderlines)) {
paintDecorationLine(dec, &Metrics::underlineSize,
&Metrics::underlineOffset);
}
+
// Overlines
+ if (aParams.textDrawer && aDecorations.mOverlines.Length() > 0) {
+ aParams.textDrawer->StartDrawing(TextDrawTarget::Phase::eOverline);
+ }
params.decoration = NS_STYLE_TEXT_DECORATION_LINE_OVERLINE;
for (const LineDecoration& dec : Reversed(aDecorations.mOverlines)) {
paintDecorationLine(dec, &Metrics::underlineSize, &Metrics::maxAscent);
}
{
gfxContextMatrixAutoSaveRestore unscaledRestorer;
if (scaledRestorer.HasMatrix()) {
@@ -7370,21 +7492,27 @@ nsTextFrame::DrawTextRunAndDecorations(R
}
// CSS 2.1 mandates that text be painted after over/underlines,
// and *then* line-throughs
DrawTextRun(aRange, aTextBaselinePt, aParams);
}
// Emphasis marks
+ if (aParams.textDrawer) {
+ aParams.textDrawer->StartDrawing(TextDrawTarget::Phase::eEmphasisMarks);
+ }
DrawEmphasisMarks(aParams.context, wm,
aTextBaselinePt, aParams.framePt, aRange,
aParams.decorationOverrideColor, aParams.provider);
// Line-throughs
+ if (aParams.textDrawer && aDecorations.mStrikes.Length() > 0) {
+ aParams.textDrawer->StartDrawing(TextDrawTarget::Phase::eLineThrough);
+ }
params.decoration = NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH;
for (const LineDecoration& dec : Reversed(aDecorations.mStrikes)) {
paintDecorationLine(dec, &Metrics::strikeoutSize,
&Metrics::strikeoutOffset);
}
}
void
--- a/layout/generic/nsTextFrame.h
+++ b/layout/generic/nsTextFrame.h
@@ -15,16 +15,17 @@
#include "nsGenericDOMDataNode.h"
#include "nsSplittableFrame.h"
#include "nsLineBox.h"
#include "gfxSkipChars.h"
#include "gfxTextRun.h"
#include "nsDisplayList.h"
#include "JustificationUtils.h"
#include "RubyUtils.h"
+#include "TextDrawTarget.h"
// Undo the windows.h damage
#if defined(XP_WIN) && defined(DrawText)
#undef DrawText
#endif
class nsTextPaintStyle;
class PropertyProvider;
@@ -43,16 +44,17 @@ class nsTextFrame : public nsFrame
typedef mozilla::LayoutDeviceRect LayoutDeviceRect;
typedef mozilla::RawSelectionType RawSelectionType;
typedef mozilla::SelectionType SelectionType;
typedef mozilla::TextRangeStyle TextRangeStyle;
typedef mozilla::gfx::DrawTarget DrawTarget;
typedef mozilla::gfx::Point Point;
typedef mozilla::gfx::Rect Rect;
typedef mozilla::gfx::Size Size;
+ typedef mozilla::layout::TextDrawTarget TextDrawTarget;
typedef gfxTextRun::Range Range;
public:
explicit nsTextFrame(nsStyleContext* aContext, ClassID aID = kClassID)
: nsFrame(aContext, aID)
, mNextContinuation(nullptr)
, mContentOffset(0)
, mContentLengthHint(0)
@@ -436,33 +438,34 @@ public:
* has been emitted to the gfxContext.
*/
virtual void NotifySelectionDecorationLinePathEmitted() {}
};
struct PaintTextParams
{
gfxContext* context;
+ TextDrawTarget* textDrawer;
gfxPoint framePt;
LayoutDeviceRect dirtyRect;
mozilla::SVGContextPaint* contextPaint = nullptr;
DrawPathCallbacks* callbacks = nullptr;
enum
{
PaintText, // Normal text painting.
PaintTextBGColor, // Only paint background color of the selected text
// range in this state.
GenerateTextMask // To generate a mask from a text frame. Should
// only paint text itself with opaque color.
// Text shadow, text selection color and text
// decoration are all discarded in this state.
};
uint8_t state = PaintText;
explicit PaintTextParams(gfxContext* aContext)
- : context(aContext)
+ : context(aContext), textDrawer(nullptr)
{
}
bool IsPaintText() const { return state == PaintText; }
bool IsGenerateTextMask() const { return state == GenerateTextMask; }
bool IsPaintBGColor() const { return state == PaintTextBGColor; }
};
@@ -475,26 +478,27 @@ public:
explicit PaintTextSelectionParams(const PaintTextParams& aParams)
: PaintTextParams(aParams)
{}
};
struct DrawTextRunParams
{
gfxContext* context;
+ TextDrawTarget* textDrawer;
PropertyProvider* provider = nullptr;
gfxFloat* advanceWidth = nullptr;
mozilla::SVGContextPaint* contextPaint = nullptr;
DrawPathCallbacks* callbacks = nullptr;
nscolor textColor = NS_RGBA(0, 0, 0, 0);
nscolor textStrokeColor = NS_RGBA(0, 0, 0, 0);
float textStrokeWidth = 0.0f;
bool drawSoftHyphen = false;
explicit DrawTextRunParams(gfxContext* aContext)
- : context(aContext)
+ : context(aContext), textDrawer(nullptr)
{}
};
struct DrawTextParams : DrawTextRunParams
{
gfxPoint framePt;
LayoutDeviceRect dirtyRect;
const nsTextPaintStyle* textStyle = nullptr;
@@ -701,24 +705,26 @@ protected:
struct PaintShadowParams
{
gfxTextRun::Range range;
LayoutDeviceRect dirtyRect;
gfxPoint framePt;
gfxPoint textBaselinePt;
gfxContext* context;
+ TextDrawTarget* textDrawer;
nscolor foregroundColor = NS_RGBA(0, 0, 0, 0);
const nsCharClipDisplayItem::ClipEdges* clipEdges = nullptr;
PropertyProvider* provider = nullptr;
nscoord leftSideOffset = 0;
explicit PaintShadowParams(const PaintTextParams& aParams)
: dirtyRect(aParams.dirtyRect)
, framePt(aParams.framePt)
, context(aParams.context)
+ , textDrawer(aParams.textDrawer)
{
}
};
void PaintOneShadow(const PaintShadowParams& aParams,
nsCSSShadowItem* aShadowDetails,
gfxRect& aBoundingBox,
uint32_t aBlurFlags);
--- a/layout/painting/nsCSSRendering.cpp
+++ b/layout/painting/nsCSSRendering.cpp
@@ -56,16 +56,17 @@
#include "mozilla/Telemetry.h"
#include "gfxUtils.h"
#include "gfxGradientCache.h"
#include "nsInlineFrame.h"
#include "nsRubyTextContainerFrame.h"
#include <algorithm>
#include "SVGImageContext.h"
#include "mozilla/layers/WebRenderDisplayItemLayer.h"
+#include "TextDrawTarget.h"
using namespace mozilla;
using namespace mozilla::css;
using namespace mozilla::gfx;
using namespace mozilla::image;
using mozilla::CSSSizeOrRatio;
static int gFrameTreeLockCount = 0;
@@ -3798,17 +3799,18 @@ nsCSSRendering::PaintDecorationLine(nsIF
aParams.decoration != NS_STYLE_TEXT_DECORATION_LINE_OVERLINE &&
aParams.decoration != NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH) {
NS_ERROR("Invalid decoration value!");
return;
}
Float lineThickness = std::max(NS_round(aParams.lineSize.height), 1.0);
- ColorPattern color(ToDeviceColor(aParams.color));
+ Color color = ToDeviceColor(aParams.color);
+ ColorPattern colorPat(color);
StrokeOptions strokeOptions(lineThickness);
DrawOptions drawOptions;
Float dash[2];
AutoPopClips autoPopClips(&aDrawTarget);
switch (aParams.style) {
@@ -3876,17 +3878,22 @@ nsCSSRendering::PaintDecorationLine(nsIF
}
switch (aParams.style) {
case NS_STYLE_TEXT_DECORATION_STYLE_SOLID:
case NS_STYLE_TEXT_DECORATION_STYLE_DOTTED:
case NS_STYLE_TEXT_DECORATION_STYLE_DASHED: {
Point p1 = rect.TopLeft();
Point p2 = aParams.vertical ? rect.BottomLeft() : rect.TopRight();
- aDrawTarget.StrokeLine(p1, p2, color, strokeOptions, drawOptions);
+ if (aParams.textDrawer) {
+ aParams.textDrawer->AppendDecoration(
+ p1, p2, lineThickness, aParams.vertical, color, aParams.style);
+ } else {
+ aDrawTarget.StrokeLine(p1, p2, colorPat, strokeOptions, drawOptions);
+ }
return;
}
case NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE: {
/**
* We are drawing double line as:
*
* +-------------------------------------------+
* |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^
@@ -3894,29 +3901,39 @@ nsCSSRendering::PaintDecorationLine(nsIF
* |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v
* | |
* | |
* |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^
* |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineThickness
* |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v
* +-------------------------------------------+
*/
- Point p1 = rect.TopLeft();
- Point p2 = aParams.vertical ? rect.BottomLeft() : rect.TopRight();
- aDrawTarget.StrokeLine(p1, p2, color, strokeOptions, drawOptions);
+ Point p1a = rect.TopLeft();
+ Point p2a = aParams.vertical ? rect.BottomLeft() : rect.TopRight();
if (aParams.vertical) {
rect.width -= lineThickness;
} else {
rect.height -= lineThickness;
}
- p1 = aParams.vertical ? rect.TopRight() : rect.BottomLeft();
- p2 = rect.BottomRight();
- aDrawTarget.StrokeLine(p1, p2, color, strokeOptions, drawOptions);
+ Point p1b = aParams.vertical ? rect.TopRight() : rect.BottomLeft();
+ Point p2b = rect.BottomRight();
+
+ if (aParams.textDrawer) {
+ aParams.textDrawer->AppendDecoration(
+ p1a, p2a, lineThickness, aParams.vertical, color,
+ NS_STYLE_TEXT_DECORATION_STYLE_SOLID);
+ aParams.textDrawer->AppendDecoration(
+ p1b, p2b, lineThickness, aParams.vertical, color,
+ NS_STYLE_TEXT_DECORATION_STYLE_SOLID);
+ } else {
+ aDrawTarget.StrokeLine(p1a, p2a, colorPat, strokeOptions, drawOptions);
+ aDrawTarget.StrokeLine(p1b, p2b, colorPat, strokeOptions, drawOptions);
+ }
return;
}
case NS_STYLE_TEXT_DECORATION_STYLE_WAVY: {
/**
* We are drawing wavy line as:
*
* P: Path, X: Painted pixel
*
@@ -3942,16 +3959,18 @@ nsCSSRendering::PaintDecorationLine(nsIF
* 5. Goes up to top of the area at 45 degrees.
* 6. Slides to right horizontaly.
* 7. Repeat from 2 until reached to right-most edge of the area.
*
* In the vertical case, swap horizontal and vertical coordinates and
* directions in the above description.
*/
+ // TODO(gankro)
+
Float& rectICoord = aParams.vertical ? rect.y : rect.x;
Float& rectISize = aParams.vertical ? rect.height : rect.width;
const Float rectBSize = aParams.vertical ? rect.width : rect.height;
const Float adv = rectBSize - lineThickness;
const Float flatLengthAtVertex =
std::max((lineThickness - 1.0) * 2.0, 1.0);
@@ -3967,16 +3986,27 @@ nsCSSRendering::PaintDecorationLine(nsIF
: aParams.dirtyRect.x;
int32_t skipCycles = floor((dirtyRectICoord - rectICoord) / cycleLength);
if (skipCycles > 0) {
rectICoord += skipCycles * cycleLength;
rectISize -= skipCycles * cycleLength;
}
rectICoord += lineThickness / 2.0;
+
+ if (aParams.textDrawer) {
+ Point p1 = rect.TopLeft();
+ Point p2 = aParams.vertical ? rect.BottomLeft() : rect.TopRight();
+
+ aParams.textDrawer->AppendDecoration(
+ p1, p2, adv, aParams.vertical, color,
+ NS_STYLE_TEXT_DECORATION_STYLE_WAVY);
+ return;
+ }
+
Point pt(rect.TopLeft());
Float& ptICoord = aParams.vertical ? pt.y : pt.x;
Float& ptBCoord = aParams.vertical ? pt.x : pt.y;
if (aParams.vertical) {
ptBCoord += adv + lineThickness / 2.0;
}
Float iCoordLimit = ptICoord + rectISize + lineThickness;
@@ -4001,33 +4031,33 @@ nsCSSRendering::PaintDecorationLine(nsIF
// it. So the sense of this flag is effectively inverted.
bool goDown = aParams.vertical ? false : true;
uint32_t iter = 0;
while (ptICoord < iCoordLimit) {
if (++iter > 1000) {
// stroke the current path and start again, to avoid pathological
// behavior in cairo with huge numbers of path segments
path = builder->Finish();
- aDrawTarget.Stroke(path, color, strokeOptions, drawOptions);
+ aDrawTarget.Stroke(path, colorPat, strokeOptions, drawOptions);
builder = aDrawTarget.CreatePathBuilder();
builder->MoveTo(pt);
iter = 0;
}
ptICoord += adv;
ptBCoord += goDown ? adv : -adv;
builder->LineTo(pt); // 3 and 5
ptICoord += flatLengthAtVertex;
builder->LineTo(pt); // 4 and 6
goDown = !goDown;
}
path = builder->Finish();
- aDrawTarget.Stroke(path, color, strokeOptions, drawOptions);
+ aDrawTarget.Stroke(path, colorPat, strokeOptions, drawOptions);
return;
}
default:
NS_ERROR("Invalid style value!");
}
}
Rect
--- a/layout/painting/nsCSSRendering.h
+++ b/layout/painting/nsCSSRendering.h
@@ -14,16 +14,17 @@
#include "mozilla/gfx/PathHelpers.h"
#include "mozilla/gfx/Rect.h"
#include "mozilla/TypedEnumBits.h"
#include "nsLayoutUtils.h"
#include "nsStyleStruct.h"
#include "nsIFrame.h"
#include "nsImageRenderer.h"
#include "nsCSSRenderingBorders.h"
+#include "TextDrawTarget.h"
class gfxContext;
class nsStyleContext;
class nsPresContext;
namespace mozilla {
namespace gfx {
@@ -583,27 +584,29 @@ struct nsCSSRendering {
// NS_STYLE_TEXT_DECORATION_LINE_OVERLINE or
// NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH.
uint8_t decoration = NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE;
// The style of the decoration line such as
// NS_STYLE_TEXT_DECORATION_STYLE_*.
uint8_t style = NS_STYLE_TEXT_DECORATION_STYLE_NONE;
bool vertical = false;
};
+
struct PaintDecorationLineParams : DecorationRectParams
{
// No need to paint outside this rect.
Rect dirtyRect;
// The top/left edge of the text.
Point pt;
// The color of the decoration line.
nscolor color = NS_RGBA(0, 0, 0, 0);
// The distance between the left edge of the given frame and the
// position of the text as positioned without offset of the shadow.
Float icoordInFrame = 0.0f;
+ mozilla::layout::TextDrawTarget* textDrawer = nullptr;
};
/**
* Function for painting the decoration lines for the text.
*
* input:
* @param aFrame the frame which needs the decoration line
* @param aGfxContext