gecko-dev/layout/generic/TextDrawTarget.h

497 строки
17 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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"
#include "mozilla/layers/WebRenderLayerManager.h"
#include "mozilla/layers/WebRenderBridgeChild.h"
#include "mozilla/webrender/WebRenderAPI.h"
#include "mozilla/layers/StackingContextHelper.h"
namespace mozilla {
namespace layout {
using namespace gfx;
// 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.
//
// TextDrawTarget doesn't yet support all features. See mHasUnsupportedFeatures
// for details.
class TextDrawTarget : public DrawTarget
{
public:
explicit TextDrawTarget(wr::DisplayListBuilder& aBuilder,
const layers::StackingContextHelper& aSc,
layers::WebRenderLayerManager* aManager,
nsDisplayItem* aItem,
nsRect& aBounds)
: mBuilder(aBuilder), mSc(aSc), mManager(aManager)
{
SetPermitSubpixelAA(!aItem->IsSubpixelAADisabled());
// Compute clip/bounds
auto appUnitsPerDevPixel = aItem->Frame()->PresContext()->AppUnitsPerDevPixel();
LayoutDeviceRect layoutBoundsRect = LayoutDeviceRect::FromAppUnits(
aBounds, appUnitsPerDevPixel);
LayoutDeviceRect layoutClipRect = layoutBoundsRect;
auto clip = aItem->GetClip();
if (clip.HasClip()) {
layoutClipRect = LayoutDeviceRect::FromAppUnits(
clip.GetClipRect(), appUnitsPerDevPixel);
}
mBoundsRect = aSc.ToRelativeLayoutRect(layoutBoundsRect);
mClipRect = aSc.ToRelativeLayoutRect(layoutClipRect);
mBackfaceVisible = !aItem->BackfaceIsHidden();
mBuilder.Save();
}
// Prevent this from being copied
TextDrawTarget(const TextDrawTarget& src) = delete;
TextDrawTarget& operator=(const TextDrawTarget&) = delete;
~TextDrawTarget()
{
if (mHasUnsupportedFeatures) {
mBuilder.Restore();
} else {
mBuilder.ClearSave();
}
}
void FoundUnsupportedFeature() { mHasUnsupportedFeatures = true; }
bool HasUnsupportedFeatures() { return mHasUnsupportedFeatures; }
// This overload just stores the glyphs/font/color.
void
FillGlyphs(ScaledFont* aFont,
const GlyphBuffer& aBuffer,
const Pattern& aPattern,
const DrawOptions& aOptions) override
{
// Make sure we're only given boring color patterns
MOZ_RELEASE_ASSERT(aOptions.mCompositionOp == CompositionOp::OP_OVER);
MOZ_RELEASE_ASSERT(aOptions.mAlpha == 1.0f);
MOZ_RELEASE_ASSERT(aPattern.GetType() == PatternType::COLOR);
auto* colorPat = static_cast<const ColorPattern*>(&aPattern);
auto color = wr::ToColorF(colorPat->mColor);
// Make sure the font exists, and can be serialized
MOZ_RELEASE_ASSERT(aFont);
if (!aFont->CanSerialize()) {
FoundUnsupportedFeature();
return;
}
// 170 is the maximum size gfxFont is expected to hand us
AutoTArray<wr::GlyphInstance, 170> glyphs;
glyphs.SetLength(aBuffer.mNumGlyphs);
for (size_t i = 0; i < aBuffer.mNumGlyphs; i++) {
wr::GlyphInstance& targetGlyph = glyphs[i];
const gfx::Glyph& sourceGlyph = aBuffer.mGlyphs[i];
targetGlyph.index = sourceGlyph.mIndex;
targetGlyph.point = mSc.ToRelativeLayoutPoint(
LayoutDevicePoint::FromUnknownPoint(sourceGlyph.mPosition));
}
wr::GlyphOptions glyphOptions;
glyphOptions.render_mode = wr::ToFontRenderMode(aOptions.mAntialiasMode, GetPermitSubpixelAA());
mManager->WrBridge()->PushGlyphs(mBuilder, glyphs, aFont,
color, mSc, mBoundsRect, mClipRect,
mBackfaceVisible, &glyphOptions);
}
void
PushClipRect(const Rect &aRect) override {
auto rect = mSc.ToRelativeLayoutRect(LayoutDeviceRect::FromUnknownRect(aRect));
auto clipId = mBuilder.DefineClip(Nothing(), Nothing(), rect);
mBuilder.PushClip(clipId);
}
void
PopClip() override {
mBuilder.PopClip();
}
void
AppendShadow(const wr::Shadow& aShadow)
{
mBuilder.PushShadow(mBoundsRect, mClipRect, mBackfaceVisible, aShadow);
mHasShadows = true;
}
void
TerminateShadows()
{
if (mHasShadows) {
mBuilder.PopAllShadows();
mHasShadows = false;
}
}
void
AppendSelectionRect(const LayoutDeviceRect& aRect, const Color& aColor)
{
auto rect = wr::ToLayoutRect(aRect);
auto color = wr::ToColorF(aColor);
mBuilder.PushRect(rect, mClipRect, mBackfaceVisible, color);
}
// 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.
//
// NOTE: we assume the points are axis-aligned, and aStart should be used
// as the top-left corner of the rect.
void
AppendDecoration(const Point& aStart,
const Point& aEnd,
const float aThickness,
const bool aVertical,
const Color& aColor,
const uint8_t aStyle)
{
auto pos = LayoutDevicePoint::FromUnknownPoint(aStart);
LayoutDeviceSize size;
if (aVertical) {
pos.x -= aThickness / 2; // adjust from center to corner
size = LayoutDeviceSize(aThickness, aEnd.y - aStart.y);
} else {
pos.y -= aThickness / 2; // adjust from center to corner
size = LayoutDeviceSize(aEnd.x - aStart.x, aThickness);
}
wr::Line decoration;
decoration.bounds = mSc.ToRelativeLayoutRect(LayoutDeviceRect(pos, size));
decoration.wavyLineThickness = 0; // dummy value, unused
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;
// Wavy lines should go through AppendWavyDecoration
case NS_STYLE_TEXT_DECORATION_STYLE_WAVY:
// Double lines should be lowered to two solid lines
case NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE:
default:
MOZ_CRASH("TextDrawTarget received unsupported line style");
}
mBuilder.PushLine(mClipRect, mBackfaceVisible, decoration);
}
// Seperated out from AppendDecoration because Wavy Lines are completely
// different, and trying to merge the concept is more of a mess than it's
// worth.
void
AppendWavyDecoration(const Rect& aBounds,
const float aThickness,
const bool aVertical,
const Color& aColor)
{
wr::Line decoration;
decoration.bounds = mSc.ToRelativeLayoutRect(
LayoutDeviceRect::FromUnknownRect(aBounds));
decoration.wavyLineThickness = aThickness;
decoration.color = wr::ToColorF(aColor);
decoration.orientation = aVertical
? wr::LineOrientation::Vertical
: wr::LineOrientation::Horizontal;
decoration.style = wr::LineStyle::Wavy;
mBuilder.PushLine(mClipRect, mBackfaceVisible, decoration);
}
private:
// Whether anything unsupported was encountered. Currently:
//
// * Synthetic bold/italics
// * SVG fonts
// * Unserializable fonts
// * Tofu glyphs
// * Pratial ligatures
// * Text writing-mode
// * Text stroke
bool mHasUnsupportedFeatures = false;
// Whether PopAllShadows needs to be called
bool mHasShadows = false;
// Things used to push to webrender
wr::DisplayListBuilder& mBuilder;
const layers::StackingContextHelper& mSc;
layers::WebRenderLayerManager* mManager;
// Computed facts
wr::LayerRect mBoundsRect;
wr::LayerRect mClipRect;
bool mBackfaceVisible;
// The rest of this is dummy implementations of DrawTarget's API
public:
DrawTargetType GetType() const override {
return DrawTargetType::SOFTWARE_RASTER;
}
BackendType GetBackendType() const override {
return BackendType::WEBRENDER_TEXT;
}
bool IsRecording() const override { return true; }
bool IsCaptureDT() const override { return false; }
already_AddRefed<SourceSurface> Snapshot() override {
MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
return nullptr;
}
already_AddRefed<SourceSurface> IntoLuminanceSource(LuminanceType aLuminanceType,
float aOpacity) override {
MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
return nullptr;
}
IntSize GetSize() override {
MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
return IntSize(1, 1);
}
void Flush() override {
MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
}
void DrawCapturedDT(DrawTargetCapture *aCaptureDT,
const Matrix& aTransform) override {
MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
}
void DrawSurface(SourceSurface *aSurface,
const Rect &aDest,
const Rect &aSource,
const DrawSurfaceOptions &aSurfOptions,
const DrawOptions &aOptions) override {
MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
}
void DrawFilter(FilterNode *aNode,
const Rect &aSourceRect,
const Point &aDestPoint,
const DrawOptions &aOptions) override {
MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
}
void DrawSurfaceWithShadow(SourceSurface *aSurface,
const Point &aDest,
const Color &aColor,
const Point &aOffset,
Float aSigma,
CompositionOp aOperator) override {
MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
}
void ClearRect(const Rect &aRect) override {
MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
}
void CopySurface(SourceSurface *aSurface,
const IntRect &aSourceRect,
const IntPoint &aDestination) override {
MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
}
void FillRect(const Rect &aRect,
const Pattern &aPattern,
const DrawOptions &aOptions = DrawOptions()) override {
MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
}
void StrokeRect(const Rect &aRect,
const Pattern &aPattern,
const StrokeOptions &aStrokeOptions,
const DrawOptions &aOptions) override {
MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
}
void StrokeLine(const Point &aStart,
const Point &aEnd,
const Pattern &aPattern,
const StrokeOptions &aStrokeOptions,
const DrawOptions &aOptions) override {
MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
}
void Stroke(const Path *aPath,
const Pattern &aPattern,
const StrokeOptions &aStrokeOptions,
const DrawOptions &aOptions) override {
MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
}
void Fill(const Path *aPath,
const Pattern &aPattern,
const DrawOptions &aOptions) override {
MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
}
void StrokeGlyphs(ScaledFont* aFont,
const GlyphBuffer& aBuffer,
const Pattern& aPattern,
const StrokeOptions& aStrokeOptions,
const DrawOptions& aOptions) override {
MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
}
void Mask(const Pattern &aSource,
const Pattern &aMask,
const DrawOptions &aOptions) override {
MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
}
void MaskSurface(const Pattern &aSource,
SourceSurface *aMask,
Point aOffset,
const DrawOptions &aOptions) override {
MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
}
bool Draw3DTransformedSurface(SourceSurface* aSurface,
const Matrix4x4& aMatrix) override {
MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
}
void PushClip(const Path *aPath) override {
MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
}
void PushDeviceSpaceClipRects(const IntRect* aRects, uint32_t aCount) override {
MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
}
void PushLayer(bool aOpaque, Float aOpacity,
SourceSurface* aMask,
const Matrix& aMaskTransform,
const IntRect& aBounds,
bool aCopyBackground) override {
// Fine to pretend we do this
}
void PopLayer() override {
// Fine to pretend we do this
}
already_AddRefed<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
const IntSize &aSize,
int32_t aStride,
SurfaceFormat aFormat) const override {
MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
return nullptr;
}
already_AddRefed<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const override {
MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
return nullptr;
}
already_AddRefed<SourceSurface>
CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const override {
MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
return nullptr;
}
already_AddRefed<DrawTarget>
CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override {
MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
return nullptr;
}
already_AddRefed<PathBuilder> CreatePathBuilder(FillRule aFillRule) const override {
MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
return nullptr;
}
already_AddRefed<FilterNode> CreateFilter(FilterType aType) override {
MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
return nullptr;
}
already_AddRefed<GradientStops>
CreateGradientStops(GradientStop *aStops,
uint32_t aNumStops,
ExtendMode aExtendMode) const override {
MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
return nullptr;
}
void* GetNativeSurface(NativeSurfaceType aType) override {
MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
return nullptr;
}
void DetachAllSnapshots() override {
MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
}
};
}
}
#endif