зеркало из https://github.com/mozilla/gecko-dev.git
597 строки
20 KiB
C++
597 строки
20 KiB
C++
/* -*- 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"
|
|
#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 is used by all Advanced Layers users, so we use plain gfx types
|
|
struct TextRunFragment {
|
|
ScaledFont* font;
|
|
wr::ColorF color;
|
|
nsTArray<wr::GlyphInstance> glyphs;
|
|
};
|
|
|
|
// Only webrender handles this, so we use webrender types
|
|
struct SelectionFragment {
|
|
wr::ColorF color;
|
|
wr::LayoutRect rect;
|
|
};
|
|
|
|
// Selections are used in nsTextFrame to hack in sub-frame style changes.
|
|
// Most notably text-shadows can be changed by selections, and so we need to
|
|
// group all the glyphs and decorations attached to a shadow. We do this by
|
|
// having shadows apply to an entire SelectedTextRunFragment, and creating
|
|
// one for each "piece" of selection.
|
|
//
|
|
// For instance, this text:
|
|
//
|
|
// Hello [there] my name [is Mega]man
|
|
// ^ ^
|
|
// normal selection Ctrl+F highlight selection (yeah it's very overloaded)
|
|
//
|
|
// Would be broken up into 5 SelectedTextRunFragments
|
|
//
|
|
// ["Hello ", "there", " my name ", "is Mega", "man"]
|
|
//
|
|
// For almost all nsTextFrames, there will be only one SelectedTextRunFragment.
|
|
struct SelectedTextRunFragment {
|
|
Maybe<SelectionFragment> selection;
|
|
AutoTArray<wr::Shadow, 1> shadows;
|
|
AutoTArray<TextRunFragment, 1> text;
|
|
AutoTArray<wr::Line, 1> beforeDecorations;
|
|
AutoTArray<wr::Line, 1> afterDecorations;
|
|
};
|
|
|
|
}
|
|
}
|
|
|
|
// AutoTArray is bad
|
|
template<>
|
|
struct nsTArray_CopyChooser<mozilla::layout::SelectedTextRunFragment>
|
|
{
|
|
typedef nsTArray_CopyWithConstructors<mozilla::layout::SelectedTextRunFragment> Type;
|
|
};
|
|
|
|
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.
|
|
//
|
|
// 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(const layers::StackingContextHelper& aSc)
|
|
: mCurrentlyDrawing(Phase::eSelection),
|
|
mHasUnsupportedFeatures(false),
|
|
mSc(aSc)
|
|
{
|
|
SetSelectionIndex(0);
|
|
}
|
|
|
|
// 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; }
|
|
void FoundUnsupportedFeature() { mHasUnsupportedFeatures = true; }
|
|
|
|
void SetSelectionIndex(size_t i) {
|
|
// i should only be accessed if i-1 has already been
|
|
MOZ_ASSERT(i <= mParts.Length());
|
|
|
|
if (mParts.Length() == i){
|
|
mParts.AppendElement();
|
|
}
|
|
|
|
mCurrentPart = &mParts[i];
|
|
}
|
|
|
|
// 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 (mCurrentPart->text.IsEmpty() ||
|
|
mCurrentPart->text.LastElement().font != aFont ||
|
|
!(mCurrentPart->text.LastElement().color == wr::ToColorF(colorPat->mColor))) {
|
|
fragment = mCurrentPart->text.AppendElement();
|
|
fragment->font = aFont;
|
|
fragment->color = wr::ToColorF(colorPat->mColor);
|
|
} else {
|
|
fragment = &mCurrentPart->text.LastElement();
|
|
}
|
|
|
|
nsTArray<wr::GlyphInstance>& glyphs = fragment->glyphs;
|
|
|
|
size_t oldLength = glyphs.Length();
|
|
glyphs.SetLength(oldLength + aBuffer.mNumGlyphs);
|
|
|
|
for (size_t i = 0; i < aBuffer.mNumGlyphs; i++) {
|
|
wr::GlyphInstance& targetGlyph = glyphs[oldLength + i];
|
|
const gfx::Glyph& sourceGlyph = aBuffer.mGlyphs[i];
|
|
targetGlyph.index = sourceGlyph.mIndex;
|
|
targetGlyph.point = mSc.ToRelativeLayoutPoint(
|
|
LayerPoint::FromUnknownPoint(sourceGlyph.mPosition));
|
|
}
|
|
}
|
|
|
|
void
|
|
AppendShadow(const wr::Shadow& aShadow) {
|
|
mCurrentPart->shadows.AppendElement(aShadow);
|
|
}
|
|
|
|
void
|
|
SetSelectionRect(const LayoutDeviceRect& aRect, const Color& aColor)
|
|
{
|
|
SelectionFragment frag;
|
|
frag.rect = wr::ToLayoutRect(aRect);
|
|
frag.color = wr::ToColorF(aColor);
|
|
mCurrentPart->selection = Some(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 = mCurrentPart->beforeDecorations.AppendElement();
|
|
break;
|
|
case Phase::eLineThrough:
|
|
decoration = mCurrentPart->afterDecorations.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<SelectedTextRunFragment>& GetParts() { return mParts; }
|
|
|
|
bool
|
|
CanSerializeFonts()
|
|
{
|
|
if (mHasUnsupportedFeatures) {
|
|
return false;
|
|
}
|
|
|
|
for (const SelectedTextRunFragment& part : GetParts()) {
|
|
for (const TextRunFragment& frag : part.text) {
|
|
if (!frag.font->CanSerialize()) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
|
|
const layers::StackingContextHelper& aSc,
|
|
layers::WebRenderLayerManager* aManager,
|
|
nsDisplayItem* aItem,
|
|
nsRect& aBounds) {
|
|
|
|
if (!CanSerializeFonts()) {
|
|
return false;
|
|
}
|
|
|
|
// Drawing order: selections,
|
|
// shadows,
|
|
// underline, overline, [grouped in one array]
|
|
// text, emphasisText, [grouped in one array]
|
|
// lineThrough
|
|
|
|
// 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);
|
|
}
|
|
|
|
LayerRect boundsRect = LayerRect::FromUnknownRect(layoutBoundsRect.ToUnknownRect());
|
|
LayerRect clipRect = LayerRect::FromUnknownRect(layoutClipRect.ToUnknownRect());
|
|
|
|
bool backfaceVisible = !aItem->BackfaceIsHidden();
|
|
|
|
wr::LayoutRect wrBoundsRect = aSc.ToRelativeLayoutRect(boundsRect);
|
|
wr::LayoutRect wrClipRect = aSc.ToRelativeLayoutRect(clipRect);
|
|
|
|
|
|
// Create commands
|
|
for (auto& part : GetParts()) {
|
|
if (part.selection) {
|
|
auto selection = part.selection.value();
|
|
aBuilder.PushRect(selection.rect, wrClipRect, backfaceVisible, selection.color);
|
|
}
|
|
}
|
|
|
|
for (auto& part : GetParts()) {
|
|
// 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::Shadow& shadow : part.shadows) {
|
|
aBuilder.PushShadow(wrBoundsRect, wrClipRect, backfaceVisible, shadow);
|
|
}
|
|
|
|
for (const wr::Line& decoration : part.beforeDecorations) {
|
|
aBuilder.PushLine(wrClipRect, backfaceVisible, decoration);
|
|
}
|
|
|
|
for (const mozilla::layout::TextRunFragment& text : part.text) {
|
|
aManager->WrBridge()->PushGlyphs(aBuilder, text.glyphs, text.font,
|
|
text.color, aSc, wrBoundsRect, wrClipRect,
|
|
backfaceVisible);
|
|
}
|
|
|
|
for (const wr::Line& decoration : part.afterDecorations) {
|
|
aBuilder.PushLine(wrClipRect, backfaceVisible, decoration);
|
|
}
|
|
|
|
for (size_t i = 0; i < part.shadows.Length(); ++i) {
|
|
aBuilder.PopShadow();
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
// The part of the text we're currently drawing (glyphs, underlines, etc.)
|
|
Phase mCurrentlyDrawing;
|
|
|
|
// Which chunk of mParts is actively being populated
|
|
SelectedTextRunFragment* mCurrentPart;
|
|
|
|
// Chunks of the text, grouped by selection
|
|
AutoTArray<SelectedTextRunFragment, 1> mParts;
|
|
|
|
// Whether Tofu or SVG fonts were encountered
|
|
bool mHasUnsupportedFeatures;
|
|
|
|
// Needs to be saved so FillGlyphs can use this to offset glyphs to
|
|
// relative space. Shouldn't be used otherwise (may dangle if we move
|
|
// to retaining TextDrawTargets)
|
|
const layers::StackingContextHelper& mSc;
|
|
|
|
// 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,
|
|
const GlyphRenderingOptions* aRenderingOptions) 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 {
|
|
// Fine to pretend we do this
|
|
}
|
|
|
|
void PushClipRect(const Rect &aRect) override {
|
|
// Fine to pretend we do this
|
|
}
|
|
|
|
void PushDeviceSpaceClipRects(const IntRect* aRects, uint32_t aCount) override {
|
|
MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
|
|
}
|
|
|
|
void PopClip() override {
|
|
// Fine to pretend we do this
|
|
}
|
|
|
|
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
|