DisplayListItems.cpp   [plain text]


/*
 * Copyright (C) 2015 Apple Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 */

#include "config.h"
#include "DisplayListItems.h"

#include "FontCascade.h"
#include <wtf/text/TextStream.h>

#if USE(CG)
#include "GraphicsContextPlatformPrivateCG.h"
#endif

namespace WebCore {
namespace DisplayList {

// Should match RenderTheme::platformFocusRingWidth()
static const float platformFocusRingWidth = 3;

#if !defined(NDEBUG) || !LOG_DISABLED
WTF::CString Item::description() const
{
    TextStream ts;
    ts << *this;
    return ts.release().utf8();
}
#endif

Item::Item(ItemType type)
    : m_type(type)
{
}

Item::~Item() = default;

size_t Item::sizeInBytes(const Item& item)
{
    switch (item.type()) {
    case ItemType::Save:
        return sizeof(downcast<Save>(item));
    case ItemType::Restore:
        return sizeof(downcast<Restore>(item));
    case ItemType::Translate:
        return sizeof(downcast<Translate>(item));
    case ItemType::Rotate:
        return sizeof(downcast<Rotate>(item));
    case ItemType::Scale:
        return sizeof(downcast<Scale>(item));
    case ItemType::SetCTM:
        return sizeof(downcast<SetCTM>(item));
    case ItemType::ConcatenateCTM:
        return sizeof(downcast<ConcatenateCTM>(item));
    case ItemType::SetState:
        return sizeof(downcast<SetState>(item));
    case ItemType::SetLineCap:
        return sizeof(downcast<SetLineCap>(item));
    case ItemType::SetLineDash:
        return sizeof(downcast<SetLineDash>(item));
    case ItemType::SetLineJoin:
        return sizeof(downcast<SetLineJoin>(item));
    case ItemType::SetMiterLimit:
        return sizeof(downcast<SetMiterLimit>(item));
    case ItemType::ClearShadow:
        return sizeof(downcast<ClearShadow>(item));
    case ItemType::Clip:
        return sizeof(downcast<Clip>(item));
    case ItemType::ClipOut:
        return sizeof(downcast<ClipOut>(item));
    case ItemType::ClipOutToPath:
        return sizeof(downcast<ClipOutToPath>(item));
    case ItemType::ClipPath:
        return sizeof(downcast<ClipPath>(item));
    case ItemType::DrawGlyphs:
        return sizeof(downcast<DrawGlyphs>(item));
    case ItemType::DrawImage:
        return sizeof(downcast<DrawImage>(item));
    case ItemType::DrawTiledImage:
        return sizeof(downcast<DrawTiledImage>(item));
    case ItemType::DrawTiledScaledImage:
        return sizeof(downcast<DrawTiledScaledImage>(item));
#if USE(CG) || USE(CAIRO)
    case ItemType::DrawNativeImage:
        return sizeof(downcast<DrawNativeImage>(item));
#endif
    case ItemType::DrawPattern:
        return sizeof(downcast<DrawPattern>(item));
    case ItemType::DrawRect:
        return sizeof(downcast<DrawRect>(item));
    case ItemType::DrawLine:
        return sizeof(downcast<DrawLine>(item));
    case ItemType::DrawLinesForText:
        return sizeof(downcast<DrawLinesForText>(item));
    case ItemType::DrawDotsForDocumentMarker:
        return sizeof(downcast<DrawDotsForDocumentMarker>(item));
    case ItemType::DrawEllipse:
        return sizeof(downcast<DrawEllipse>(item));
    case ItemType::DrawPath:
        return sizeof(downcast<DrawPath>(item));
    case ItemType::DrawFocusRingPath:
        return sizeof(downcast<DrawFocusRingPath>(item));
    case ItemType::DrawFocusRingRects:
        return sizeof(downcast<DrawFocusRingRects>(item));
    case ItemType::FillRect:
        return sizeof(downcast<FillRect>(item));
    case ItemType::FillRectWithColor:
        return sizeof(downcast<FillRectWithColor>(item));
    case ItemType::FillRectWithGradient:
        return sizeof(downcast<FillRectWithGradient>(item));
    case ItemType::FillCompositedRect:
        return sizeof(downcast<FillCompositedRect>(item));
    case ItemType::FillRoundedRect:
        return sizeof(downcast<FillRoundedRect>(item));
    case ItemType::FillRectWithRoundedHole:
        return sizeof(downcast<FillRectWithRoundedHole>(item));
    case ItemType::FillPath:
        return sizeof(downcast<FillPath>(item));
    case ItemType::FillEllipse:
        return sizeof(downcast<FillEllipse>(item));
    case ItemType::StrokeRect:
        return sizeof(downcast<StrokeRect>(item));
    case ItemType::StrokePath:
        return sizeof(downcast<StrokePath>(item));
    case ItemType::StrokeEllipse:
        return sizeof(downcast<StrokeEllipse>(item));
    case ItemType::ClearRect:
        return sizeof(downcast<ClearRect>(item));
    case ItemType::BeginTransparencyLayer:
        return sizeof(downcast<BeginTransparencyLayer>(item));
    case ItemType::EndTransparencyLayer:
        return sizeof(downcast<EndTransparencyLayer>(item));
#if USE(CG)
    case ItemType::ApplyStrokePattern:
        return sizeof(downcast<ApplyStrokePattern>(item));
    case ItemType::ApplyFillPattern:
        return sizeof(downcast<ApplyFillPattern>(item));
#endif
    case ItemType::ApplyDeviceScaleFactor:
        return sizeof(downcast<ApplyDeviceScaleFactor>(item));
    }
    return 0;
}

static TextStream& operator<<(TextStream& ts, const DrawingItem& item)
{
    ts.startGroup();
    ts << "extent ";
    if (item.extentKnown())
        ts << item.extent();
    else
        ts << "unknown";
    
    ts.endGroup();
    return ts;
}

DrawingItem::DrawingItem(ItemType type)
    : Item(type)
{
}

DrawingItem::~DrawingItem() = default;

Save::Save()
    : Item(ItemType::Save)
{
}

Save::~Save() = default;

void Save::apply(GraphicsContext& context) const
{
    context.save();
}

static TextStream& operator<<(TextStream& ts, const Save& item)
{
    ts.dumpProperty("restore-index", item.restoreIndex());
    return ts;
}

Restore::Restore()
    : Item(ItemType::Restore)
{
}

Restore::~Restore() = default;

void Restore::apply(GraphicsContext& context) const
{
    context.restore();
}

Translate::Translate(float x, float y)
    : Item(ItemType::Translate)
    , m_x(x)
    , m_y(y)
{
}

Translate::~Translate() = default;

void Translate::apply(GraphicsContext& context) const
{
    context.translate(m_x, m_y);
}

static TextStream& operator<<(TextStream& ts, const Translate& item)
{
    ts.dumpProperty("x", item.x());
    ts.dumpProperty("y", item.y());

    return ts;
}

Rotate::Rotate(float angle)
    : Item(ItemType::Rotate)
    , m_angle(angle)
{
}

Rotate::~Rotate() = default;

void Rotate::apply(GraphicsContext& context) const
{
    context.rotate(m_angle);
}

static TextStream& operator<<(TextStream& ts, const Rotate& item)
{
    ts.dumpProperty("angle", item.angle());

    return ts;
}

Scale::Scale(const FloatSize& size)
    : Item(ItemType::Scale)
    , m_size(size)
{
}

Scale::~Scale() = default;

void Scale::apply(GraphicsContext& context) const
{
    context.scale(m_size);
}

static TextStream& operator<<(TextStream& ts, const Scale& item)
{
    ts.dumpProperty("size", item.amount());

    return ts;
}

SetCTM::SetCTM(const AffineTransform& transform)
    : Item(ItemType::SetCTM)
    , m_transform(transform)
{
}

SetCTM::~SetCTM() = default;

void SetCTM::apply(GraphicsContext& context) const
{
    context.setCTM(m_transform);
}

static TextStream& operator<<(TextStream& ts, const SetCTM& item)
{
    ts.dumpProperty("set-ctm", item.transform());

    return ts;
}

ConcatenateCTM::ConcatenateCTM(const AffineTransform& transform)
    : Item(ItemType::ConcatenateCTM)
    , m_transform(transform)
{
}

ConcatenateCTM::~ConcatenateCTM() = default;

void ConcatenateCTM::apply(GraphicsContext& context) const
{
    context.concatCTM(m_transform);
}

static TextStream& operator<<(TextStream& ts, const ConcatenateCTM& item)
{
    ts.dumpProperty("ctm", item.transform());

    return ts;
}

SetState::SetState(const GraphicsContextState& state, GraphicsContextState::StateChangeFlags flags)
    : Item(ItemType::SetState)
    , m_state(state, flags)
{
}

SetState::SetState(const GraphicsContextStateChange& stateChange)
    : Item(ItemType::SetState)
    , m_state(stateChange)
{
}

SetState::~SetState() = default;

void SetState::apply(GraphicsContext& context) const
{
    m_state.apply(context);
}

void SetState::accumulate(const GraphicsContextState& state, GraphicsContextState::StateChangeFlags flags)
{
    m_state.accumulate(state, flags);
}

static TextStream& operator<<(TextStream& ts, const SetState& state)
{
    ts << state.state();
    return ts;
}

SetLineCap::SetLineCap(LineCap lineCap)
    : Item(ItemType::SetLineCap)
    , m_lineCap(lineCap)
{
}

SetLineCap::~SetLineCap() = default;

void SetLineCap::apply(GraphicsContext& context) const
{
    context.setLineCap(m_lineCap);
}

static TextStream& operator<<(TextStream& ts, const SetLineCap& lineCap)
{
    ts.dumpProperty("line-cap", lineCap.lineCap());
    return ts;
}

SetLineDash::SetLineDash(const DashArray& dashArray, float dashOffset)
    : Item(ItemType::SetLineDash)
    , m_dashArray(dashArray)
    , m_dashOffset(dashOffset)
{
}

SetLineDash::~SetLineDash() = default;

void SetLineDash::apply(GraphicsContext& context) const
{
    context.setLineDash(m_dashArray, m_dashOffset);
}

static TextStream& operator<<(TextStream& ts, const SetLineDash& lineDash)
{
    ts.dumpProperty("dash-array", lineDash.dashArray());
    ts.dumpProperty("dash-offset", lineDash.dashOffset());
    return ts;
}

SetLineJoin::SetLineJoin(LineJoin lineJoin)
    : Item(ItemType::SetLineJoin)
    , m_lineJoin(lineJoin)
{
}

SetLineJoin::~SetLineJoin() = default;

void SetLineJoin::apply(GraphicsContext& context) const
{
    context.setLineJoin(m_lineJoin);
}

static TextStream& operator<<(TextStream& ts, const SetLineJoin& lineJoin)
{
    ts.dumpProperty("line-join", lineJoin.lineJoin());
    return ts;
}

SetMiterLimit::SetMiterLimit(float miterLimit)
    : Item(ItemType::SetMiterLimit)
    , m_miterLimit(miterLimit)
{
}

SetMiterLimit::~SetMiterLimit() = default;

void SetMiterLimit::apply(GraphicsContext& context) const
{
    context.setMiterLimit(m_miterLimit);
}

static TextStream& operator<<(TextStream& ts, const SetMiterLimit& miterLimit)
{
    ts.dumpProperty("mitre-limit", miterLimit.miterLimit());
    return ts;
}

ClearShadow::ClearShadow()
    : Item(ItemType::ClearShadow)
{
}

ClearShadow::~ClearShadow() = default;

void ClearShadow::apply(GraphicsContext& context) const
{
    context.clearShadow();
}

Clip::Clip(const FloatRect& rect)
    : Item(ItemType::Clip)
    , m_rect(rect)
{
}

Clip::~Clip() = default;

void Clip::apply(GraphicsContext& context) const
{
    context.clip(m_rect);
}

static TextStream& operator<<(TextStream& ts, const Clip& item)
{
    ts.dumpProperty("rect", item.rect());
    return ts;
}

ClipOut::ClipOut(const FloatRect& rect)
    : Item(ItemType::ClipOut)
    , m_rect(rect)
{
}

ClipOut::~ClipOut() = default;

void ClipOut::apply(GraphicsContext& context) const
{
    context.clipOut(m_rect);
}

static TextStream& operator<<(TextStream& ts, const ClipOut& item)
{
    ts.dumpProperty("rect", item.rect());
    return ts;
}

ClipOutToPath::ClipOutToPath(const Path& path)
    : Item(ItemType::ClipOutToPath)
    , m_path(path)
{
}

ClipOutToPath::~ClipOutToPath() = default;

void ClipOutToPath::apply(GraphicsContext& context) const
{
    context.clipOut(m_path);
}

static TextStream& operator<<(TextStream& ts, const ClipOutToPath& item)
{
    ts.dumpProperty("path", item.path());
    return ts;
}

ClipPath::ClipPath(const Path& path, WindRule windRule)
    : Item(ItemType::ClipPath)
    , m_path(path)
    , m_windRule(windRule)
{
}

ClipPath::~ClipPath() = default;

void ClipPath::apply(GraphicsContext& context) const
{
    context.clipPath(m_path, m_windRule);
}

static TextStream& operator<<(TextStream& ts, const ClipPath& item)
{
    ts.dumpProperty("path", item.path());
    ts.dumpProperty("wind-rule", item.windRule());
    return ts;
}

DrawGlyphs::DrawGlyphs(const Font& font, const GlyphBufferGlyph* glyphs, const GlyphBufferAdvance* advances, unsigned count, const FloatPoint& blockLocation, const FloatSize& localAnchor, FontSmoothingMode smoothingMode)
    : DrawingItem(ItemType::DrawGlyphs)
    , m_font(const_cast<Font&>(font))
    , m_blockLocation(blockLocation)
    , m_localAnchor(localAnchor)
    , m_smoothingMode(smoothingMode)
{
    m_glyphs.reserveInitialCapacity(count);
    m_advances.reserveInitialCapacity(count);
    for (unsigned i = 0; i < count; ++i) {
        m_glyphs.uncheckedAppend(glyphs[i]);
        m_advances.uncheckedAppend(advances[i]);
    }
    computeBounds();
}

inline GlyphBuffer DrawGlyphs::generateGlyphBuffer() const
{
    GlyphBuffer result;
    for (size_t i = 0; i < m_glyphs.size(); ++i) {
        result.add(m_glyphs[i], &m_font.get(), m_advances[i], GlyphBuffer::noOffset);
    }
    return result;
}

void DrawGlyphs::apply(GraphicsContext& context) const
{
    context.drawGlyphs(m_font, generateGlyphBuffer(), 0, m_glyphs.size(), anchorPoint(), m_smoothingMode);
}

void DrawGlyphs::computeBounds()
{
    // FIXME: This code doesn't actually take the extents of the glyphs into consideration. It assumes that
    // the glyph lies entirely within its [(ascent + descent), advance] rect.
    float ascent = m_font->fontMetrics().floatAscent();
    float descent = m_font->fontMetrics().floatDescent();
    FloatPoint current = toFloatPoint(localAnchor());
    size_t numGlyphs = m_glyphs.size();
    for (size_t i = 0; i < numGlyphs; ++i) {
        GlyphBufferAdvance advance = m_advances[i];
        FloatRect glyphRect = FloatRect(current.x(), current.y() - ascent, advance.width(), ascent + descent);
        m_bounds.unite(glyphRect);

        current.move(advance.width(), advance.height());
    }
}

Optional<FloatRect> DrawGlyphs::localBounds(const GraphicsContext&) const
{
    FloatRect localBounds = m_bounds;
    localBounds.move(m_blockLocation.x(), m_blockLocation.y());
    return localBounds;
}

static TextStream& operator<<(TextStream& ts, const DrawGlyphs& item)
{
    ts << static_cast<const DrawingItem&>(item);
    // FIXME: dump more stuff.
    ts.dumpProperty("block-location", item.blockLocation());
    ts.dumpProperty("local-anchor", item.localAnchor());
    ts.dumpProperty("anchor-point", item.anchorPoint());
    ts.dumpProperty("length", item.glyphs().size());

    return ts;
}

DrawImage::DrawImage(Image& image, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions& imagePaintingOptions)
    : DrawingItem(ItemType::DrawImage)
    , m_image(image)
    , m_destination(destination)
    , m_source(source)
    , m_imagePaintingOptions(imagePaintingOptions)
{
}

DrawImage::~DrawImage() = default;

void DrawImage::apply(GraphicsContext& context) const
{
    context.drawImage(m_image.get(), m_destination, m_source, m_imagePaintingOptions);
}

static TextStream& operator<<(TextStream& ts, const DrawImage& item)
{
    ts << static_cast<const DrawingItem&>(item);
    ts.dumpProperty("image", item.image());
    ts.dumpProperty("source-rect", item.source());
    ts.dumpProperty("dest-rect", item.destination());
    return ts;
}

DrawTiledImage::DrawTiledImage(Image& image, const FloatRect& destination, const FloatPoint& source, const FloatSize& tileSize, const FloatSize& spacing, const ImagePaintingOptions& imagePaintingOptions)
    : DrawingItem(ItemType::DrawTiledImage)
    , m_image(image)
    , m_destination(destination)
    , m_source(source)
    , m_tileSize(tileSize)
    , m_spacing(spacing)
    , m_imagePaintingOptions(imagePaintingOptions)
{
}

DrawTiledImage::~DrawTiledImage() = default;

void DrawTiledImage::apply(GraphicsContext& context) const
{
    context.drawTiledImage(m_image.get(), m_destination, m_source, m_tileSize, m_spacing, m_imagePaintingOptions);
}

static TextStream& operator<<(TextStream& ts, const DrawTiledImage& item)
{
    ts << static_cast<const DrawingItem&>(item);
    ts.dumpProperty("image", item.image());
    ts.dumpProperty("source-point", item.source());
    ts.dumpProperty("dest-rect", item.destination());
    ts.dumpProperty("tile-size", item.tileSize());
    ts.dumpProperty("spacing", item.spacing());
    return ts;
}

DrawTiledScaledImage::DrawTiledScaledImage(Image& image, const FloatRect& destination, const FloatRect& source, const FloatSize& tileScaleFactor, Image::TileRule hRule, Image::TileRule vRule, const ImagePaintingOptions& imagePaintingOptions)
    : DrawingItem(ItemType::DrawTiledScaledImage)
    , m_image(image)
    , m_destination(destination)
    , m_source(source)
    , m_tileScaleFactor(tileScaleFactor)
    , m_hRule(hRule)
    , m_vRule(vRule)
    , m_imagePaintingOptions(imagePaintingOptions)
{
}

DrawTiledScaledImage::~DrawTiledScaledImage() = default;

void DrawTiledScaledImage::apply(GraphicsContext& context) const
{
    context.drawTiledImage(m_image.get(), m_destination, m_source, m_tileScaleFactor, m_hRule, m_vRule, m_imagePaintingOptions);
}

static TextStream& operator<<(TextStream& ts, const DrawTiledScaledImage& item)
{
    ts << static_cast<const DrawingItem&>(item);
    ts.dumpProperty("image", item.image());
    ts.dumpProperty("source-rect", item.source());
    ts.dumpProperty("dest-rect", item.destination());
    return ts;
}

#if USE(CG) || USE(CAIRO) || USE(DIRECT2D)
DrawNativeImage::DrawNativeImage(const NativeImagePtr& image, const FloatSize& imageSize, const FloatRect& destRect, const FloatRect& srcRect, const ImagePaintingOptions& options)
    : DrawingItem(ItemType::DrawNativeImage)
#if USE(CG)
    // FIXME: Need to store an image for Cairo.
    , m_image(image)
#endif
    , m_imageSize(imageSize)
    , m_destinationRect(destRect)
    , m_srcRect(srcRect)
    , m_options(options)
{
#if !USE(CG)
    UNUSED_PARAM(image);
#endif
}

DrawNativeImage::~DrawNativeImage() = default;

void DrawNativeImage::apply(GraphicsContext& context) const
{
#if USE(CG)
    context.drawNativeImage(m_image, m_imageSize, m_destinationRect, m_srcRect, m_options);
#else
    UNUSED_PARAM(context);
#endif
}

static TextStream& operator<<(TextStream& ts, const DrawNativeImage& item)
{
    ts << static_cast<const DrawingItem&>(item);
    // FIXME: dump more stuff.
    ts.dumpProperty("source-rect", item.source());
    ts.dumpProperty("dest-rect", item.destinationRect());
    return ts;
}
#endif

DrawPattern::DrawPattern(Image& image, const FloatRect& destRect, const FloatRect& tileRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, const ImagePaintingOptions& options)
    : DrawingItem(ItemType::DrawPattern)
    , m_image(image)
    , m_patternTransform(patternTransform)
    , m_tileRect(tileRect)
    , m_destination(destRect)
    , m_phase(phase)
    , m_spacing(spacing)
    , m_options(options)
{
}

DrawPattern::~DrawPattern() = default;

void DrawPattern::apply(GraphicsContext& context) const
{
    context.drawPattern(m_image.get(), m_destination, m_tileRect, m_patternTransform, m_phase, m_spacing, m_options);
}

static TextStream& operator<<(TextStream& ts, const DrawPattern& item)
{
    ts << static_cast<const DrawingItem&>(item);
    ts.dumpProperty("image", item.image());
    ts.dumpProperty("pattern-transform", item.patternTransform());
    ts.dumpProperty("tile-rect", item.tileRect());
    ts.dumpProperty("dest-rect", item.destRect());
    ts.dumpProperty("phase", item.phase());
    ts.dumpProperty("spacing", item.spacing());
    return ts;
}

DrawRect::DrawRect(const FloatRect& rect, float borderThickness)
    : DrawingItem(ItemType::DrawRect)
    , m_rect(rect)
    , m_borderThickness(borderThickness)
{
}

DrawRect::~DrawRect() = default;

void DrawRect::apply(GraphicsContext& context) const
{
    context.drawRect(m_rect, m_borderThickness);
}

static TextStream& operator<<(TextStream& ts, const DrawRect& item)
{
    ts << static_cast<const DrawingItem&>(item);
    ts.dumpProperty("rect", item.rect());
    ts.dumpProperty("border-thickness", item.borderThickness());
    return ts;
}

DrawLine::DrawLine(const FloatPoint& point1, const FloatPoint& point2)
    : DrawingItem(ItemType::DrawLine)
    , m_point1(point1)
    , m_point2(point2)
{
}

DrawLine::~DrawLine() = default;

Optional<FloatRect> DrawLine::localBounds(const GraphicsContext&) const
{
    FloatRect bounds;
    bounds.fitToPoints(m_point1, m_point2);
    return bounds;
}

void DrawLine::apply(GraphicsContext& context) const
{
    context.drawLine(m_point1, m_point2);
}

static TextStream& operator<<(TextStream& ts, const DrawLine& item)
{
    ts << static_cast<const DrawingItem&>(item);
    ts.dumpProperty("point-1", item.point1());
    ts.dumpProperty("point-2", item.point2());
    return ts;
}

DrawLinesForText::DrawLinesForText(const FloatPoint& blockLocation, const FloatSize& localAnchor, float thickness, const DashArray& widths, bool printing, bool doubleLines)
    : DrawingItem(ItemType::DrawLinesForText)
    , m_blockLocation(blockLocation)
    , m_localAnchor(localAnchor)
    , m_widths(widths)
    , m_thickness(thickness)
    , m_printing(printing)
    , m_doubleLines(doubleLines)
{
}

DrawLinesForText::~DrawLinesForText() = default;

void DrawLinesForText::apply(GraphicsContext& context) const
{
    context.drawLinesForText(point(), m_thickness, m_widths, m_printing, m_doubleLines);
}

Optional<FloatRect> DrawLinesForText::localBounds(const GraphicsContext&) const
{
    // This function needs to return a value equal to or enclosing what GraphicsContext::computeLineBoundsAndAntialiasingModeForText() returns.

    if (!m_widths.size())
        return FloatRect();

    FloatRect result(point(), FloatSize(m_widths.last(), m_thickness));
    result.inflate(1); // Account for pixel snapping. FIXME: This isn't perfect, as it doesn't take the CTM into account.
    return result;
}

static TextStream& operator<<(TextStream& ts, const DrawLinesForText& item)
{
    ts << static_cast<const DrawingItem&>(item);
    ts.dumpProperty("block-location", item.blockLocation());
    ts.dumpProperty("local-anchor", item.localAnchor());
    ts.dumpProperty("point", item.point());
    ts.dumpProperty("thickness", item.thickness());
    ts.dumpProperty("double", item.doubleLines());
    ts.dumpProperty("widths", item.widths());
    ts.dumpProperty("is-printing", item.isPrinting());
    ts.dumpProperty("double", item.doubleLines());
    return ts;
}

DrawDotsForDocumentMarker::DrawDotsForDocumentMarker(const FloatRect& rect, DocumentMarkerLineStyle style)
    : DrawingItem(ItemType::DrawDotsForDocumentMarker)
    , m_rect(rect)
    , m_style(style)
{
}

DrawDotsForDocumentMarker::~DrawDotsForDocumentMarker() = default;

void DrawDotsForDocumentMarker::apply(GraphicsContext& context) const
{
    context.drawDotsForDocumentMarker(m_rect, m_style);
}

Optional<FloatRect> DrawDotsForDocumentMarker::localBounds(const GraphicsContext&) const
{
    return m_rect;
}

static TextStream& operator<<(TextStream& ts, const DrawDotsForDocumentMarker& item)
{
    ts << static_cast<const DrawingItem&>(item);
    ts.dumpProperty("rect", item.rect());
    return ts;
}

DrawEllipse::DrawEllipse(const FloatRect& rect)
    : DrawingItem(ItemType::DrawEllipse)
    , m_rect(rect)
{
}

DrawEllipse::~DrawEllipse() = default;

void DrawEllipse::apply(GraphicsContext& context) const
{
    context.drawEllipse(m_rect);
}

static TextStream& operator<<(TextStream& ts, const DrawEllipse& item)
{
    ts.dumpProperty("rect", item.rect());
    return ts;
}

DrawPath::DrawPath(const Path& path)
    : DrawingItem(ItemType::DrawPath)
    , m_path(path)
{
}

DrawPath::~DrawPath() = default;

void DrawPath::apply(GraphicsContext& context) const
{
#if USE(CG)
    context.drawPath(m_path);
#else
    UNUSED_PARAM(context);
#endif
}

static TextStream& operator<<(TextStream& ts, const DrawPath& item)
{
    ts << static_cast<const DrawingItem&>(item);
//    ts.dumpProperty("path", item.path()); // FIXME: add logging for paths.
    return ts;
}

DrawFocusRingPath::DrawFocusRingPath(const Path& path, float width, float offset, const Color& color)
    : DrawingItem(ItemType::DrawFocusRingPath)
    , m_path(path)
    , m_width(width)
    , m_offset(offset)
    , m_color(color)
{
}

DrawFocusRingPath::~DrawFocusRingPath() = default;

void DrawFocusRingPath::apply(GraphicsContext& context) const
{
    context.drawFocusRing(m_path, m_width, m_offset, m_color);
}

Optional<FloatRect> DrawFocusRingPath::localBounds(const GraphicsContext&) const
{
    FloatRect result = m_path.fastBoundingRect();
    result.inflate(platformFocusRingWidth);
    return result;
}

static TextStream& operator<<(TextStream& ts, const DrawFocusRingPath& item)
{
    ts << static_cast<const DrawingItem&>(item);
//    ts.dumpProperty("path", item.path()); // FIXME: add logging for paths.
    ts.dumpProperty("width", item.width());
    ts.dumpProperty("offset", item.offset());
    ts.dumpProperty("color", item.color());
    return ts;
}

DrawFocusRingRects::DrawFocusRingRects(const Vector<FloatRect>& rects, float width, float offset, const Color& color)
    : DrawingItem(ItemType::DrawFocusRingRects)
    , m_rects(rects)
    , m_width(width)
    , m_offset(offset)
    , m_color(color)
{
}

DrawFocusRingRects::~DrawFocusRingRects() = default;

void DrawFocusRingRects::apply(GraphicsContext& context) const
{
    context.drawFocusRing(m_rects, m_width, m_offset, m_color);
}

Optional<FloatRect> DrawFocusRingRects::localBounds(const GraphicsContext&) const
{
    FloatRect result;
    for (auto& rect : m_rects)
        result.unite(rect);
    result.inflate(platformFocusRingWidth);
    return result;
}

static TextStream& operator<<(TextStream& ts, const DrawFocusRingRects& item)
{
    ts << static_cast<const DrawingItem&>(item);
    ts.dumpProperty("rects", item.rects());
    ts.dumpProperty("width", item.width());
    ts.dumpProperty("offset", item.offset());
    ts.dumpProperty("color", item.color());
    return ts;
}

FillRect::FillRect(const FloatRect& rect)
    : DrawingItem(ItemType::FillRect)
    , m_rect(rect)
{
}

FillRect::~FillRect() = default;

void FillRect::apply(GraphicsContext& context) const
{
    context.fillRect(m_rect);
}

static TextStream& operator<<(TextStream& ts, const FillRect& item)
{
    ts << static_cast<const DrawingItem&>(item);
    ts.dumpProperty("rect", item.rect());
    return ts;
}

FillRectWithColor::FillRectWithColor(const FloatRect& rect, const Color& color)
    : DrawingItem(ItemType::FillRectWithColor)
    , m_rect(rect)
    , m_color(color)
{
}

FillRectWithColor::~FillRectWithColor() = default;

void FillRectWithColor::apply(GraphicsContext& context) const
{
    context.fillRect(m_rect, m_color);
}

static TextStream& operator<<(TextStream& ts, const FillRectWithColor& item)
{
    ts << static_cast<const DrawingItem&>(item);
    ts.dumpProperty("rect", item.rect());
    ts.dumpProperty("color", item.color());
    return ts;
}

FillRectWithGradient::FillRectWithGradient(const FloatRect& rect, Gradient& gradient)
    : DrawingItem(ItemType::FillRectWithGradient)
    , m_rect(rect)
    , m_gradient(gradient)
{
}

FillRectWithGradient::~FillRectWithGradient() = default;

void FillRectWithGradient::apply(GraphicsContext& context) const
{
    context.fillRect(m_rect, m_gradient.get());
}

static TextStream& operator<<(TextStream& ts, const FillRectWithGradient& item)
{
    ts << static_cast<const DrawingItem&>(item);
    // FIXME: log gradient.
    ts.dumpProperty("rect", item.rect());
    return ts;
}

FillCompositedRect::FillCompositedRect(const FloatRect& rect, const Color& color, CompositeOperator op, BlendMode blendMode)
    : DrawingItem(ItemType::FillCompositedRect)
    , m_rect(rect)
    , m_color(color)
    , m_op(op)
    , m_blendMode(blendMode)
{
}

FillCompositedRect::~FillCompositedRect() = default;

void FillCompositedRect::apply(GraphicsContext& context) const
{
    context.fillRect(m_rect, m_color, m_op, m_blendMode);
}

static TextStream& operator<<(TextStream& ts, const FillCompositedRect& item)
{
    ts << static_cast<const DrawingItem&>(item);
    ts.dumpProperty("rect", item.rect());
    ts.dumpProperty("color", item.color());
    ts.dumpProperty("composite-operation", item.compositeOperator());
    ts.dumpProperty("blend-mode", item.blendMode());
    return ts;
}

FillRoundedRect::FillRoundedRect(const FloatRoundedRect& rect, const Color& color, BlendMode blendMode)
    : DrawingItem(ItemType::FillRoundedRect)
    , m_rect(rect)
    , m_color(color)
    , m_blendMode(blendMode)
{
}

FillRoundedRect::~FillRoundedRect() = default;

void FillRoundedRect::apply(GraphicsContext& context) const
{
    context.fillRoundedRect(m_rect, m_color, m_blendMode);
}

static TextStream& operator<<(TextStream& ts, const FillRoundedRect& item)
{
    ts << static_cast<const DrawingItem&>(item);
    ts.dumpProperty("rect", item.roundedRect());
    ts.dumpProperty("color", item.color());
    ts.dumpProperty("blend-mode", item.blendMode());
    return ts;
}

FillRectWithRoundedHole::FillRectWithRoundedHole(const FloatRect& rect, const FloatRoundedRect& roundedHoleRect, const Color& color)
    : DrawingItem(ItemType::FillRectWithRoundedHole)
    , m_rect(rect)
    , m_roundedHoleRect(roundedHoleRect)
    , m_color(color)
{
}

FillRectWithRoundedHole::~FillRectWithRoundedHole() = default;

void FillRectWithRoundedHole::apply(GraphicsContext& context) const
{
    context.fillRectWithRoundedHole(m_rect, m_roundedHoleRect, m_color);
}

static TextStream& operator<<(TextStream& ts, const FillRectWithRoundedHole& item)
{
    ts << static_cast<const DrawingItem&>(item);
    ts.dumpProperty("rect", item.rect());
    ts.dumpProperty("rounded-hole-rect", item.roundedHoleRect());
    ts.dumpProperty("color", item.color());
    return ts;
}

FillPath::FillPath(const Path& path)
    : DrawingItem(ItemType::FillPath)
    , m_path(path)
{
}

FillPath::~FillPath() = default;

void FillPath::apply(GraphicsContext& context) const
{
    context.fillPath(m_path);
}

static TextStream& operator<<(TextStream& ts, const FillPath& item)
{
    ts << static_cast<const DrawingItem&>(item);
    ts.dumpProperty("path", item.path());
    return ts;
}

FillEllipse::FillEllipse(const FloatRect& rect)
    : DrawingItem(ItemType::FillEllipse)
    , m_rect(rect)
{
}

FillEllipse::~FillEllipse() = default;

void FillEllipse::apply(GraphicsContext& context) const
{
    context.fillEllipse(m_rect);
}

static TextStream& operator<<(TextStream& ts, const FillEllipse& item)
{
    ts << static_cast<const DrawingItem&>(item);
    ts.dumpProperty("rect", item.rect());
    return ts;
}

StrokeRect::StrokeRect(const FloatRect& rect, float lineWidth)
    : DrawingItem(ItemType::StrokeRect)
    , m_rect(rect)
    , m_lineWidth(lineWidth)
{
}

StrokeRect::~StrokeRect() = default;

Optional<FloatRect> StrokeRect::localBounds(const GraphicsContext&) const
{
    FloatRect bounds = m_rect;
    bounds.expand(m_lineWidth, m_lineWidth);
    return bounds;
}

void StrokeRect::apply(GraphicsContext& context) const
{
    context.strokeRect(m_rect, m_lineWidth);
}

static TextStream& operator<<(TextStream& ts, const StrokeRect& item)
{
    ts << static_cast<const DrawingItem&>(item);
    ts.dumpProperty("rect", item.rect());
    ts.dumpProperty("line-width", item.lineWidth());
    return ts;
}

Optional<FloatRect> StrokePath::localBounds(const GraphicsContext& context) const
{
    // FIXME: Need to take stroke thickness into account correctly, via CGPathByStrokingPath().
    float strokeThickness = context.strokeThickness();

    FloatRect bounds = m_path.boundingRect();
    bounds.expand(strokeThickness, strokeThickness);
    return bounds;
}

void StrokePath::apply(GraphicsContext& context) const
{
    context.strokePath(m_path);
}

static TextStream& operator<<(TextStream& ts, const StrokePath& item)
{
    ts << static_cast<const DrawingItem&>(item);
    ts.dumpProperty("path", item.path());
    return ts;
}

StrokeEllipse::StrokeEllipse(const FloatRect& rect)
    : DrawingItem(ItemType::StrokeEllipse)
    , m_rect(rect)
{
}

StrokeEllipse::~StrokeEllipse() = default;

Optional<FloatRect> StrokeEllipse::localBounds(const GraphicsContext& context) const
{
    float strokeThickness = context.strokeThickness();

    FloatRect bounds = m_rect;
    bounds.expand(strokeThickness, strokeThickness);
    return bounds;
}

void StrokeEllipse::apply(GraphicsContext& context) const
{
    context.strokeEllipse(m_rect);
}

static TextStream& operator<<(TextStream& ts, const StrokeEllipse& item)
{
    ts << static_cast<const DrawingItem&>(item);
    ts.dumpProperty("rect", item.rect());
    return ts;
}

StrokePath::StrokePath(const Path& path)
    : DrawingItem(ItemType::StrokePath)
    , m_path(path)
{
}

StrokePath::~StrokePath() = default;

ClearRect::ClearRect(const FloatRect& rect)
    : DrawingItem(ItemType::ClearRect)
    , m_rect(rect)
{
}

ClearRect::~ClearRect() = default;

void ClearRect::apply(GraphicsContext& context) const
{
    context.clearRect(m_rect);
}

static TextStream& operator<<(TextStream& ts, const ClearRect& item)
{
    ts << static_cast<const DrawingItem&>(item);
    ts.dumpProperty("rect", item.rect());
    return ts;
}

BeginTransparencyLayer::BeginTransparencyLayer(float opacity)
    : DrawingItem(ItemType::BeginTransparencyLayer)
    , m_opacity(opacity)
{
}

BeginTransparencyLayer::~BeginTransparencyLayer() = default;

void BeginTransparencyLayer::apply(GraphicsContext& context) const
{
    context.beginTransparencyLayer(m_opacity);
}

static TextStream& operator<<(TextStream& ts, const BeginTransparencyLayer& item)
{
    ts << static_cast<const DrawingItem&>(item);
    ts.dumpProperty("opacity", item.opacity());
    return ts;
}

EndTransparencyLayer::EndTransparencyLayer()
    : DrawingItem(ItemType::EndTransparencyLayer)
{
}

EndTransparencyLayer::~EndTransparencyLayer() = default;

void EndTransparencyLayer::apply(GraphicsContext& context) const
{
    context.endTransparencyLayer();
}

#if USE(CG)
ApplyStrokePattern::ApplyStrokePattern()
    : Item(ItemType::ApplyStrokePattern)
{
}

ApplyStrokePattern::~ApplyStrokePattern() = default;

void ApplyStrokePattern::apply(GraphicsContext& context) const
{
    context.applyStrokePattern();
}

ApplyFillPattern::ApplyFillPattern()
    : Item(ItemType::ApplyFillPattern)
{
}

ApplyFillPattern::~ApplyFillPattern() = default;

void ApplyFillPattern::apply(GraphicsContext& context) const
{
    context.applyFillPattern();
}
#endif

ApplyDeviceScaleFactor::ApplyDeviceScaleFactor(float scaleFactor)
    : Item(ItemType::ApplyDeviceScaleFactor)
    , m_scaleFactor(scaleFactor)
{
}

ApplyDeviceScaleFactor::~ApplyDeviceScaleFactor() = default;

void ApplyDeviceScaleFactor::apply(GraphicsContext& context) const
{
    context.applyDeviceScaleFactor(m_scaleFactor);
}

static TextStream& operator<<(TextStream& ts, const ApplyDeviceScaleFactor& item)
{
    ts.dumpProperty("scale-factor", item.scaleFactor());
    return ts;
}

static TextStream& operator<<(TextStream& ts, const ItemType& type)
{
    switch (type) {
    case ItemType::Save: ts << "save"; break;
    case ItemType::Restore: ts << "restore"; break;
    case ItemType::Translate: ts << "translate"; break;
    case ItemType::Rotate: ts << "rotate"; break;
    case ItemType::Scale: ts << "scale"; break;
    case ItemType::SetCTM: ts << "set-ctm"; break;
    case ItemType::ConcatenateCTM: ts << "concatentate-ctm"; break;
    case ItemType::SetState: ts << "set-state"; break;
    case ItemType::SetLineCap: ts << "set-line-cap"; break;
    case ItemType::SetLineDash: ts << "set-line-dash"; break;
    case ItemType::SetLineJoin: ts << "set-line-join"; break;
    case ItemType::SetMiterLimit: ts << "set-miter-limit"; break;
    case ItemType::Clip: ts << "clip"; break;
    case ItemType::ClipOut: ts << "clip-out"; break;
    case ItemType::ClipOutToPath: ts << "clip-out-to-path"; break;
    case ItemType::ClipPath: ts << "clip-path"; break;
    case ItemType::DrawGlyphs: ts << "draw-glyphs"; break;
    case ItemType::DrawImage: ts << "draw-image"; break;
    case ItemType::DrawTiledImage: ts << "draw-tiled-image"; break;
    case ItemType::DrawTiledScaledImage: ts << "draw-tiled-scaled-image"; break;
#if USE(CG) || USE(CAIRO)
    case ItemType::DrawNativeImage: ts << "draw-native-image"; break;
#endif
    case ItemType::DrawPattern: ts << "draw-pattern"; break;
    case ItemType::DrawRect: ts << "draw-rect"; break;
    case ItemType::DrawLine: ts << "draw-line"; break;
    case ItemType::DrawLinesForText: ts << "draw-lines-for-text"; break;
    case ItemType::DrawDotsForDocumentMarker: ts << "draw-dots-for-document-marker"; break;
    case ItemType::DrawEllipse: ts << "draw-ellipse"; break;
    case ItemType::DrawPath: ts << "draw-path"; break;
    case ItemType::DrawFocusRingPath: ts << "draw-focus-ring-path"; break;
    case ItemType::DrawFocusRingRects: ts << "draw-focus-ring-rects"; break;
    case ItemType::FillRect: ts << "fill-rect"; break;
    case ItemType::FillRectWithColor: ts << "fill-rect-with-color"; break;
    case ItemType::FillRectWithGradient: ts << "fill-rect-with-gradient"; break;
    case ItemType::FillCompositedRect: ts << "fill-composited-rect"; break;
    case ItemType::FillRoundedRect: ts << "fill-rounded-rect"; break;
    case ItemType::FillRectWithRoundedHole: ts << "fill-rect-with-rounded-hole"; break;
    case ItemType::FillPath: ts << "fill-path"; break;
    case ItemType::FillEllipse: ts << "fill-ellipse"; break;
    case ItemType::StrokeRect: ts << "stroke-rect"; break;
    case ItemType::StrokePath: ts << "stroke-path"; break;
    case ItemType::StrokeEllipse: ts << "stroke-ellipse"; break;
    case ItemType::ClearRect: ts << "clear-rect"; break;
    case ItemType::BeginTransparencyLayer: ts << "begin-transparency-layer"; break;
    case ItemType::EndTransparencyLayer: ts << "end-transparency-layer"; break;
#if USE(CG)
    case ItemType::ApplyStrokePattern: ts << "apply-stroke-pattern"; break;
    case ItemType::ApplyFillPattern: ts << "apply-fill-pattern"; break;
#endif
    case ItemType::ApplyDeviceScaleFactor: ts << "apply-device-scale-factor"; break;
    case ItemType::ClearShadow: ts << "clear-shadow"; break;
    }
    return ts;
}

TextStream& operator<<(TextStream& ts, const Item& item)
{
    TextStream::GroupScope group(ts);
    ts << item.type();

    // FIXME: Make a macro which takes a macro for all these enumeration switches
    switch (item.type()) {
    case ItemType::Save:
        ts << downcast<Save>(item);
        break;
    case ItemType::Translate:
        ts << downcast<Translate>(item);
        break;
    case ItemType::Rotate:
        ts << downcast<Rotate>(item);
        break;
    case ItemType::Scale:
        ts << downcast<Scale>(item);
        break;
    case ItemType::SetCTM:
        ts << downcast<SetCTM>(item);
        break;
    case ItemType::ConcatenateCTM:
        ts << downcast<ConcatenateCTM>(item);
        break;
    case ItemType::SetState:
        ts << downcast<SetState>(item);
        break;
    case ItemType::SetLineCap:
        ts << downcast<SetLineCap>(item);
        break;
    case ItemType::SetLineDash:
        ts << downcast<SetLineDash>(item);
        break;
    case ItemType::SetLineJoin:
        ts << downcast<SetLineJoin>(item);
        break;
    case ItemType::SetMiterLimit:
        ts << downcast<SetMiterLimit>(item);
        break;
    case ItemType::Clip:
        ts << downcast<Clip>(item);
        break;
    case ItemType::ClipOut:
        ts << downcast<ClipOut>(item);
        break;
    case ItemType::ClipOutToPath:
        ts << downcast<ClipOutToPath>(item);
        break;
    case ItemType::ClipPath:
        ts << downcast<ClipPath>(item);
        break;
    case ItemType::DrawGlyphs:
        ts << downcast<DrawGlyphs>(item);
        break;
    case ItemType::DrawImage:
        ts << downcast<DrawImage>(item);
        break;
    case ItemType::DrawTiledImage:
        ts << downcast<DrawTiledImage>(item);
        break;
    case ItemType::DrawTiledScaledImage:
        ts << downcast<DrawTiledScaledImage>(item);
        break;
#if USE(CG) || USE(CAIRO)
    case ItemType::DrawNativeImage:
        ts << downcast<DrawNativeImage>(item);
        break;
#endif
    case ItemType::DrawPattern:
        ts << downcast<DrawPattern>(item);
        break;
    case ItemType::DrawRect:
        ts << downcast<DrawRect>(item);
        break;
    case ItemType::DrawLine:
        ts << downcast<DrawLine>(item);
        break;
    case ItemType::DrawLinesForText:
        ts << downcast<DrawLinesForText>(item);
        break;
    case ItemType::DrawDotsForDocumentMarker:
        ts << downcast<DrawDotsForDocumentMarker>(item);
        break;
    case ItemType::DrawEllipse:
        ts << downcast<DrawEllipse>(item);
        break;
    case ItemType::DrawPath:
        ts << downcast<DrawPath>(item);
        break;
    case ItemType::DrawFocusRingPath:
        ts << downcast<DrawFocusRingPath>(item);
        break;
    case ItemType::DrawFocusRingRects:
        ts << downcast<DrawFocusRingRects>(item);
        break;
    case ItemType::FillRect:
        ts << downcast<FillRect>(item);
        break;
    case ItemType::FillRectWithColor:
        ts << downcast<FillRectWithColor>(item);
        break;
    case ItemType::FillRectWithGradient:
        ts << downcast<FillRectWithGradient>(item);
        break;
    case ItemType::FillCompositedRect:
        ts << downcast<FillCompositedRect>(item);
        break;
    case ItemType::FillRoundedRect:
        ts << downcast<FillRoundedRect>(item);
        break;
    case ItemType::FillRectWithRoundedHole:
        ts << downcast<FillRectWithRoundedHole>(item);
        break;
    case ItemType::FillPath:
        ts << downcast<FillPath>(item);
        break;
    case ItemType::FillEllipse:
        ts << downcast<FillEllipse>(item);
        break;
    case ItemType::StrokeRect:
        ts << downcast<StrokeRect>(item);
        break;
    case ItemType::StrokePath:
        ts << downcast<StrokePath>(item);
        break;
    case ItemType::StrokeEllipse:
        ts << downcast<StrokeEllipse>(item);
        break;
    case ItemType::ClearRect:
        ts << downcast<ClearRect>(item);
        break;
    case ItemType::BeginTransparencyLayer:
        ts << downcast<BeginTransparencyLayer>(item);
        break;
    case ItemType::ApplyDeviceScaleFactor:
        ts << downcast<ApplyDeviceScaleFactor>(item);
        break;

    // Items with no additional data.
    case ItemType::Restore:
    case ItemType::EndTransparencyLayer:
#if USE(CG)
    case ItemType::ApplyStrokePattern:
    case ItemType::ApplyFillPattern:
#endif
    case ItemType::ClearShadow:
        break;
    }
    return ts;
}

}
}