diff options
author | Samuel Rødal <sroedal@trolltech.com> | 2009-09-02 12:33:57 (GMT) |
---|---|---|
committer | Samuel Rødal <sroedal@trolltech.com> | 2009-09-03 12:13:09 (GMT) |
commit | 4feb152405e96125b6be2807d515293e3f25ab1a (patch) | |
tree | 52b0be39e7f435db7f7e41ed73a2bc53e01f4c8e /src/gui/painting | |
parent | d4a4b01f2f08a9031d692344d0d264de472da25e (diff) | |
download | Qt-4feb152405e96125b6be2807d515293e3f25ab1a.zip Qt-4feb152405e96125b6be2807d515293e3f25ab1a.tar.gz Qt-4feb152405e96125b6be2807d515293e3f25ab1a.tar.bz2 |
Added trace graphics system for painting performance profiling.
When running an application with graphics system trace everything that
gets painted to the window surface is proxied through a QPaintBuffer,
which is then both streamed to a trace file and replayed on a raster
window surface. The trace file can then be replayed with
tools/qttracereplay to measure pure painting performance.
Reviewed-by: Gunnar Sletta
Diffstat (limited to 'src/gui/painting')
-rw-r--r-- | src/gui/painting/painting.pri | 2 | ||||
-rw-r--r-- | src/gui/painting/qemulationpaintengine_p.h | 2 | ||||
-rw-r--r-- | src/gui/painting/qpaintbuffer.cpp | 1745 | ||||
-rw-r--r-- | src/gui/painting/qpaintbuffer_p.h | 439 | ||||
-rw-r--r-- | src/gui/painting/qpaintengine.h | 2 | ||||
-rw-r--r-- | src/gui/painting/qpaintengineex.cpp | 34 | ||||
-rw-r--r-- | src/gui/painting/qpaintengineex_p.h | 8 | ||||
-rw-r--r-- | src/gui/painting/qpainter.cpp | 13 | ||||
-rw-r--r-- | src/gui/painting/qpainterpath.h | 2 | ||||
-rw-r--r-- | src/gui/painting/qpainterpath_p.h | 36 | ||||
-rw-r--r-- | src/gui/painting/qvectorpath_p.h | 3 |
11 files changed, 2245 insertions, 41 deletions
diff --git a/src/gui/painting/painting.pri b/src/gui/painting/painting.pri index feef15a..5abac2f 100644 --- a/src/gui/painting/painting.pri +++ b/src/gui/painting/painting.pri @@ -42,6 +42,7 @@ HEADERS += \ painting/qtransform.h \ painting/qwindowsurface_p.h \ painting/qwmatrix.h \ + painting/qpaintbuffer_p.h SOURCES += \ @@ -79,6 +80,7 @@ SOURCES += \ painting/qtextureglyphcache.cpp \ painting/qtransform.cpp \ painting/qwindowsurface.cpp \ + painting/qpaintbuffer.cpp SOURCES += \ painting/qpaintengine_raster.cpp \ diff --git a/src/gui/painting/qemulationpaintengine_p.h b/src/gui/painting/qemulationpaintengine_p.h index 1c3445c..358066e 100644 --- a/src/gui/painting/qemulationpaintengine_p.h +++ b/src/gui/painting/qemulationpaintengine_p.h @@ -92,6 +92,8 @@ public: virtual void setState(QPainterState *s); + virtual uint flags() const {return QPaintEngineEx::IsEmulationEngine | QPaintEngineEx::DoNotEmulate;} + inline QPainterState *state() { return (QPainterState *)QPaintEngine::state; } inline const QPainterState *state() const { return (const QPainterState *)QPaintEngine::state; } diff --git a/src/gui/painting/qpaintbuffer.cpp b/src/gui/painting/qpaintbuffer.cpp new file mode 100644 index 0000000..04ddd7d --- /dev/null +++ b/src/gui/painting/qpaintbuffer.cpp @@ -0,0 +1,1745 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.1, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <qmath.h> +#include <private/qpainterpath_p.h> +#include <private/qpaintbuffer_p.h> +//#include <private/qtextengine_p.h> +#include <private/qfontengine_p.h> +#include <private/qemulationpaintengine_p.h> + +#include <QDebug> + +//#define QPAINTBUFFER_DEBUG_DRAW + +extern int qt_defaultDpiX(); +extern int qt_defaultDpiY(); +extern void qt_format_text(const QFont &font, + const QRectF &_r, int tf, const QTextOption *option, const QString& str, QRectF *brect, + int tabstops, int* tabarray, int tabarraylen, + QPainter *painter); + +QTextItemIntCopy::QTextItemIntCopy(const QTextItem &item) + : m_item(static_cast<const QTextItemInt &>(item)) +{ + QChar *chars = new QChar[m_item.num_chars]; + unsigned short *logClusters = new unsigned short[m_item.num_chars]; + memcpy(chars, m_item.chars, m_item.num_chars * sizeof(QChar)); + memcpy(logClusters, m_item.logClusters, m_item.num_chars * sizeof(unsigned short)); + m_item.chars = chars; + m_item.logClusters = logClusters; + + const int size = QGlyphLayout::spaceNeededForGlyphLayout(m_item.glyphs.numGlyphs); + char *glyphLayoutData = new char[size]; + QGlyphLayout glyphs(glyphLayoutData, m_item.glyphs.numGlyphs); + memcpy(glyphs.offsets, m_item.glyphs.offsets, m_item.glyphs.numGlyphs * sizeof(QFixedPoint)); + memcpy(glyphs.glyphs, m_item.glyphs.glyphs, m_item.glyphs.numGlyphs * sizeof(HB_Glyph)); + memcpy(glyphs.advances_x, m_item.glyphs.advances_x, m_item.glyphs.numGlyphs * sizeof(QFixed)); + memcpy(glyphs.advances_y, m_item.glyphs.advances_y, m_item.glyphs.numGlyphs * sizeof(QFixed)); + memcpy(glyphs.justifications, m_item.glyphs.justifications, m_item.glyphs.numGlyphs * sizeof(QGlyphJustification)); + memcpy(glyphs.attributes, m_item.glyphs.attributes, m_item.glyphs.numGlyphs * sizeof(HB_GlyphAttributes)); + m_item.glyphs = glyphs; + + m_font = *m_item.f; + m_item.f = &m_font; + + m_item.fontEngine->ref.ref(); // Increment reference count. +} + +QTextItemIntCopy::~QTextItemIntCopy() +{ + delete m_item.chars; + delete m_item.logClusters; + delete m_item.glyphs.data(); + if (!m_item.fontEngine->ref.deref()) + delete m_item.fontEngine; +} + +/************************************************************************ + * + * QPaintBufferSignalProxy + * + ************************************************************************/ + +Q_GLOBAL_STATIC(QPaintBufferSignalProxy, theSignalProxy) + +QPaintBufferSignalProxy *QPaintBufferSignalProxy::instance() +{ + return theSignalProxy(); +} + +/************************************************************************ + * + * QPaintBufferPrivate + * + ************************************************************************/ + +QPaintBufferPrivate::QPaintBufferPrivate() + : ref(1), engine(0), penWidthAdjustment(0) + , calculateBoundingRect(true) + , cache(0) +{ +} + +QPaintBufferPrivate::~QPaintBufferPrivate() +{ + QPaintBufferSignalProxy::instance()->emitAboutToDestroy(this); + + for (int i = 0; i < commands.size(); ++i) { + const QPaintBufferCommand &cmd = commands.at(i); + if (cmd.id == QPaintBufferPrivate::Cmd_DrawTextItem) + delete reinterpret_cast<QTextItemIntCopy *>(qVariantValue<void *>(variants.at(cmd.offset))); + } +} + + +inline void QPaintBufferPrivate::updateBoundingRect(const QRectF &br) +{ + // transform to device coords and adjust for pen width + Q_ASSERT(engine && engine->painter()); + QPainter *painter = engine->painter(); + const QTransform transform = painter->transform(); + QRectF devRect = transform.mapRect(br); + if (penWidthAdjustment > 0) { + devRect = devRect.adjusted(-penWidthAdjustment, -penWidthAdjustment, + penWidthAdjustment, penWidthAdjustment); + } + + if (boundingRect.isEmpty()) { + boundingRect = devRect; + } else { + qreal min_x = qMin(devRect.left(), boundingRect.left()); + qreal min_y = qMin(devRect.top(), boundingRect.top()); + qreal max_x = qMax(devRect.right(), boundingRect.right()); + qreal max_y = qMax(devRect.bottom(), boundingRect.bottom()); + boundingRect = QRectF(min_x, min_y, max_x - min_x, max_y - min_y); + } + if (painter->hasClipping()) + boundingRect &= transform.mapRect(painter->clipRegion().boundingRect()); +} + + +/************************************************************************ + * + * QPaintBuffer + * + ************************************************************************/ + + + +QPaintBuffer::QPaintBuffer() + : d_ptr(new QPaintBufferPrivate) +{ +} + +QPaintBuffer::~QPaintBuffer() +{ + if (!d_ptr->ref.deref()) + delete d_ptr; +} + +QPaintBuffer::QPaintBuffer(const QPaintBuffer &other) + : QPaintDevice(), d_ptr(other.d_ptr) +{ + d_ptr->ref.ref(); +} + +QPaintEngine *QPaintBuffer::paintEngine() const +{ + QPaintBufferPrivate *d = const_cast<QPaintBuffer *>(this)->d_ptr; + if (!d->engine) + d->engine = new QPaintBufferEngine(d); + return d->engine; +} + + +int QPaintBuffer::metric(PaintDeviceMetric metric) const +{ + int val = 0; + switch (metric) { + case PdmWidth: + val = qCeil(d_ptr->boundingRect.width()); + break; + case PdmHeight: + val = qCeil(d_ptr->boundingRect.height()); + break; + case PdmDpiX: + case PdmPhysicalDpiX: + val = qt_defaultDpiX(); + break; + case PdmDpiY: + case PdmPhysicalDpiY: + val = qt_defaultDpiY(); + break; + default: + val = QPaintDevice::metric(metric); + } + + return val; +} + +int QPaintBuffer::devType() const +{ + return QInternal::PaintBuffer; +} + +QPaintBuffer &QPaintBuffer::operator=(const QPaintBuffer &other) +{ + if (other.d_ptr != d_ptr) { + QPaintBufferPrivate *data = other.d_ptr; + data->ref.ref(); + if (d_ptr->ref.deref()) + delete d_ptr; + d_ptr = data; + } + return *this; +} + +bool QPaintBuffer::isEmpty() const +{ + return d_ptr->commands.isEmpty(); +} + + + +void QPaintBuffer::draw(QPainter *painter) const +{ +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << "QPaintBuffer::draw() --------------------------------"; + +// printf("Float buffer:"); +// for (int i=0; i<d->floats.size(); i++) { +// if ((i % 10) == 0) { +// printf("\n%4d-%4d: ", i, i+9); +// } +// printf("%4.2f ", d->floats[i]); +// } +// printf("\n"); + +// printf("Int Buffer:"); +// for (int i=0; i<d->ints.size(); i++) { +// if ((i % 10) == 0) { +// printf("\n%4d-%4d: ", i, i+10); +// } +// printf("%5d", d->ints[i]); +// } +// printf("\n"); +#endif + + if (painter && !painter->isActive()) + return; + + QPaintEngineEx *xengine = painter->paintEngine()->isExtended() + ? (QPaintEngineEx *) painter->paintEngine() : 0; + if (xengine) { + QPaintEngineExReplayer player; + player.draw(*this, painter); + } else { + QPainterReplayer player; + player.draw(*this, painter); + } + +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << "QPaintBuffer::draw() -------------------------------- DONE!"; +#endif +} + + +QRectF QPaintBuffer::boundingRect() const +{ + return d_ptr->boundingRect; +} + +void QPaintBuffer::setBoundingRect(const QRectF &rect) +{ + d_ptr->boundingRect = rect; + d_ptr->calculateBoundingRect = false; +} + + +class QPaintBufferEnginePrivate : public QPaintEngineExPrivate +{ + Q_DECLARE_PUBLIC(QPaintBufferEngine) +public: + void systemStateChanged() { + Q_Q(QPaintBufferEngine); + q->buffer->addCommand(QPaintBufferPrivate::Cmd_SystemStateChanged, QVariant(systemClip)); + } +}; + + +/************************************************************************ + * + * QPaintBufferEngine + * + ************************************************************************/ + +QPaintBufferEngine::QPaintBufferEngine(QPaintBufferPrivate *b) + : QPaintEngineEx(*(new QPaintBufferEnginePrivate)) + , buffer(b) + , m_begin_detected(false) + , m_save_detected(false) + , m_stream_raw_text_items(false) +{ +} + +bool QPaintBufferEngine::begin(QPaintDevice *) +{ + Q_D(QPaintBufferEngine); + painter()->save(); + d->systemStateChanged(); + return true; +} + +bool QPaintBufferEngine::end() +{ + painter()->restore(); + m_created_state = 0; + return true; +} + +QPainterState *QPaintBufferEngine::createState(QPainterState *orig) const +{ +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << "QPaintBufferEngine: createState, orig=" << orig << ", current=" << state(); +#endif + + Q_ASSERT(!m_begin_detected); + Q_ASSERT(!m_save_detected); + + if (orig == 0) { + m_begin_detected = true; + return new QPainterState(); + } else { + m_save_detected = true; + return new QPainterState(orig); + } +} + +void QPaintBufferEngine::clip(const QVectorPath &path, Qt::ClipOperation op) +{ +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << "QPaintBufferEngine: clip vpath:" << path.elementCount() << "op:" << op; +#endif + QPaintBufferCommand *cmd = + buffer->addCommand(QPaintBufferPrivate::Cmd_ClipVectorPath, path); + cmd->extra = op; +} + +void QPaintBufferEngine::clip(const QRect &rect, Qt::ClipOperation op) +{ +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << "QPaintBufferEngine: clip rect:" << rect << "op:" << op; +#endif + QPaintBufferCommand *cmd = + buffer->addCommand(QPaintBufferPrivate::Cmd_ClipRect, (int *) &rect, 4, 1); + cmd->extra = op; +} + +void QPaintBufferEngine::clip(const QRegion ®ion, Qt::ClipOperation op) +{ +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << "QPaintBufferEngine: clip region br:" << region.boundingRect() << "op:" << op; +#endif + QPaintBufferCommand *cmd = + buffer->addCommand(QPaintBufferPrivate::Cmd_ClipRegion, QVariant(region)); + cmd->extra = op; +} + +void QPaintBufferEngine::clip(const QPainterPath &path, Qt::ClipOperation op) +{ + // ### TODO +// QPaintBufferCommand *cmd = +// buffer->addCommand(QPaintBufferPrivate::Cmd_ClipPath, QVariant(path)); +// cmd->extra = op; + QPaintEngineEx::clip(path, op); +} + +void QPaintBufferEngine::clipEnabledChanged() +{ +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << "QPaintBufferEngine: clip enable change" << state()->clipEnabled; +#endif + + buffer->addCommand(QPaintBufferPrivate::Cmd_SetClipEnabled, state()->clipEnabled); +} + +void QPaintBufferEngine::penChanged() +{ +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << "QPaintBufferEngine:" << state()->pen; +#endif + const QPen &pen = state()->pen; + + if (!buffer->commands.isEmpty() + && buffer->commands.last().id == QPaintBufferPrivate::Cmd_SetPen) { + buffer->variants[buffer->commands.last().offset] = pen; + return; + } + if (buffer->calculateBoundingRect) { + if (pen.style() == Qt::NoPen) { + buffer->penWidthAdjustment = 0; + } else { + qreal penWidth = (pen.widthF() == 0) ? 1 : pen.widthF(); + QPointF transformedWidth(penWidth, penWidth); + if (!pen.isCosmetic()) + transformedWidth = painter()->transform().map(transformedWidth); + buffer->penWidthAdjustment = transformedWidth.x() / 2.0; + } + } + buffer->addCommand(QPaintBufferPrivate::Cmd_SetPen, pen); +} + +void QPaintBufferEngine::brushChanged() +{ +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << "QPaintBufferEngine:" << state()->brush; +#endif + const QBrush &brush = state()->brush; + + if (!buffer->commands.isEmpty() + && buffer->commands.last().id == QPaintBufferPrivate::Cmd_SetBrush) { + buffer->variants[buffer->commands.last().offset] = brush; + return; + } + + buffer->addCommand(QPaintBufferPrivate::Cmd_SetBrush, brush); +} + +void QPaintBufferEngine::brushOriginChanged() +{ +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << "QPaintBufferEngine: brush origin changed" << state()->brushOrigin; +#endif + buffer->addCommand(QPaintBufferPrivate::Cmd_SetBrushOrigin, state()->brushOrigin); +} + +void QPaintBufferEngine::opacityChanged() +{ +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << "QPaintBufferEngine: opacity changed" << state()->opacity; +#endif + buffer->addCommand(QPaintBufferPrivate::Cmd_SetOpacity, state()->opacity); +} + +void QPaintBufferEngine::compositionModeChanged() +{ +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << "QPaintBufferEngine: composition mode" << state()->composition_mode; +#endif + QPaintBufferCommand *cmd = + buffer->addCommand(QPaintBufferPrivate::Cmd_SetCompositionMode); + cmd->extra = state()->composition_mode; +} + +void QPaintBufferEngine::renderHintsChanged() +{ +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << "QPaintBufferEngine: render hints changed" << state()->renderHints; +#endif + QPaintBufferCommand *cmd = + buffer->addCommand(QPaintBufferPrivate::Cmd_SetRenderHints); + cmd->extra = state()->renderHints; +} + +void QPaintBufferEngine::transformChanged() +{ + // ### accumulate, like in QBrush case... + if (!buffer->commands.isEmpty() + && buffer->commands.last().id == QPaintBufferPrivate::Cmd_SetTransform) { +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << "QPaintBufferEngine: compressing " << state()->matrix; +#endif + buffer->variants[buffer->commands.last().offset] = state()->matrix; + return; + } + +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << "QPaintBufferEngine: " << state()->matrix; +#endif + buffer->addCommand(QPaintBufferPrivate::Cmd_SetTransform, state()->matrix); +} + +void QPaintBufferEngine::backgroundModeChanged() +{ +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << "QPaintEngineBuffer: background mode changed" << state()->bgMode; +#endif + QPaintBufferCommand *cmd = buffer->addCommand(QPaintBufferPrivate::Cmd_SetBackgroundMode); + cmd->extra = state()->bgMode; +} + +void QPaintBufferEngine::draw(const QVectorPath &path) +{ +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << "QPaintBufferEngine: draw vpath:" << path.elementCount(); +#endif + buffer->addCommand(QPaintBufferPrivate::Cmd_DrawVectorPath, path); +// if (buffer->calculateBoundingRect) { +// QRealRect r = path.controlPointRect(); +// buffer->updateBoundingRect(QRectF(r.x1, r.y1, r.x2 - r.x1, r.y2 - r.y1)); +// } +} + +void QPaintBufferEngine::fill(const QVectorPath &path, const QBrush &brush) +{ +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << "QPaintBufferEngine: fill vpath:" << path.elementCount() << brush; +#endif + QPaintBufferCommand *cmd = + buffer->addCommand(QPaintBufferPrivate::Cmd_FillVectorPath, path); + cmd->extra = buffer->addData(QVariant(brush)); +// if (buffer->calculateBoundingRect) { +// QRealRect r = path.controlPointRect(); +// buffer->updateBoundingRect(QRectF(r.x1, r.y1, r.x2 - r.x1, r.y2 - r.y1)); +// } +} + +void QPaintBufferEngine::stroke(const QVectorPath &path, const QPen &pen) +{ +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << "QPaintBufferEngine: stroke vpath:" << path.elementCount() << pen; +#endif + QPaintBufferCommand *cmd = + buffer->addCommand(QPaintBufferPrivate::Cmd_StrokeVectorPath, path); + cmd->extra = buffer->addData(QVariant(pen)); +// if (buffer->calculateBoundingRect) { +// QRealRect r = path.controlPointRect(); +// buffer->updateBoundingRect(QRectF(r.x1, r.y1, r.x2 - r.x1, r.y2 - r.y1)); +// } +} + +void QPaintBufferEngine::fillRect(const QRectF &rect, const QBrush &brush) +{ +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << "QPaintBufferEngine: fillRect brush:" << rect << brush; +#endif + QPaintBufferCommand *cmd = + buffer->addCommand(QPaintBufferPrivate::Cmd_FillRectBrush, (qreal *) &rect, 4, 1); + cmd->extra = buffer->addData(brush); + if (buffer->calculateBoundingRect) + buffer->updateBoundingRect(rect); +} + +void QPaintBufferEngine::fillRect(const QRectF &rect, const QColor &color) +{ +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << "QPaintBufferEngine: fillRect color:" << rect << color; +#endif + QPaintBufferCommand *cmd = + buffer->addCommand(QPaintBufferPrivate::Cmd_FillRectColor, (qreal *) &rect, 4, 1); + cmd->extra = buffer->addData(color); + if (buffer->calculateBoundingRect) + buffer->updateBoundingRect(rect); +} + +void QPaintBufferEngine::drawRects(const QRect *rects, int rectCount) +{ +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << "QPaintBufferEngine: drawRectsI:" << rectCount; +#endif + QPaintBufferCommand *cmd = + buffer->addCommand(QPaintBufferPrivate::Cmd_DrawRectI, (int *) rects, 4 * rectCount, rectCount); + cmd->extra = rectCount; + + if (buffer->calculateBoundingRect) { + if (rectCount == 1) { + buffer->updateBoundingRect(rects[0]); + } else { + int min_x = rects[0].left(); + int min_y = rects[0].top(); + int max_x = rects[0].left() + rects[0].width(); + int max_y = rects[0].top() + rects[0].height(); + for (int i=1; i< rectCount; ++i) { + if (rects[i].left() < min_x) + min_x = rects[i].left(); + if (rects[i].top() < min_y) + min_y = rects[i].top(); + if (rects[i].right() > max_x) + max_x = rects[i].left() + rects[i].width(); + if (rects[i].bottom() > max_y) + max_y = rects[i].top() + rects[i].height(); + + } + buffer->updateBoundingRect(QRectF(min_x, min_y, max_x - min_x, max_y - min_y)); + } + } +} + +void QPaintBufferEngine::drawRects(const QRectF *rects, int rectCount) +{ +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << "QPaintBufferEngine: drawRectsF:" << rectCount; +#endif + QPaintBufferCommand *cmd = + buffer->addCommand(QPaintBufferPrivate::Cmd_DrawRectF, (qreal *) rects, 4 * rectCount, rectCount); + cmd->extra = rectCount; + + if (buffer->calculateBoundingRect) { + if (rectCount == 1) { + buffer->updateBoundingRect(rects[0]); + } else { + qreal min_x = rects[0].left(); + qreal min_y = rects[0].top(); + qreal max_x = rects[0].right(); + qreal max_y = rects[0].bottom(); + for (int i=1; i< rectCount; ++i) { + if (rects[i].left() < min_x) + min_x = rects[i].left(); + if (rects[i].top() < min_y) + min_y = rects[i].top(); + if (rects[i].right() > max_x) + max_x = rects[i].right(); + if (rects[i].bottom() > max_y) + max_y = rects[i].bottom(); + + } + buffer->updateBoundingRect(QRectF(min_x, min_y, max_x - min_x, max_y - min_y)); + } + } +} + +void QPaintBufferEngine::drawLines(const QLine *lines, int lineCount) +{ +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << "QPaintBufferEngine: drawLinesI:" << lineCount; +#endif + QPaintBufferCommand *cmd = + buffer->addCommand(QPaintBufferPrivate::Cmd_DrawLineI, (int *) lines, 4 * lineCount, lineCount); + cmd->extra = lineCount; + + if (buffer->calculateBoundingRect) { + int min_x = lines[0].p1().x(); + int min_y = lines[0].p1().y(); + int max_x = lines[0].p2().x(); + int max_y = lines[0].p2().y(); + if (min_x > max_x) + qSwap(min_x, max_x); + if (min_y > max_y) + qSwap(min_y, max_y); + for (int i=1; i < lineCount; ++i) { + int p1_x = lines[i].p1().x(); + int p1_y = lines[i].p1().y(); + int p2_x = lines[i].p2().x(); + int p2_y = lines[i].p2().y(); + if (p1_x > p2_x) { + min_x = qMin(p2_x, min_x); + max_x = qMax(p1_x, max_x); + } else { + min_x = qMin(p1_x, min_x); + max_x = qMax(p2_x, max_x); + } + if (p1_y > p2_y) { + min_y = qMin(p2_y, min_y); + max_y = qMax(p1_y, max_y); + } else { + min_y = qMin(p1_y, min_y); + max_y = qMax(p2_y, max_y); + } + } + buffer->updateBoundingRect(QRectF(min_x, min_y, max_x - min_x, max_y - min_y)); + } +} + +void QPaintBufferEngine::drawLines(const QLineF *lines, int lineCount) +{ +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << "QPaintBufferEngine: drawLinesF:" << lineCount; +#endif + QPaintBufferCommand *cmd = + buffer->addCommand(QPaintBufferPrivate::Cmd_DrawLineF, (qreal *) lines, 4 * lineCount, lineCount); + cmd->extra = lineCount; + + if (buffer->calculateBoundingRect) { + qreal min_x = lines[0].p1().x(); + qreal min_y = lines[0].p1().y(); + qreal max_x = lines[0].p2().x(); + qreal max_y = lines[0].p2().y(); + if (min_x > max_x) + qSwap(min_x, max_x); + if (min_y > max_y) + qSwap(min_y, max_y); + for (int i=1; i < lineCount; ++i) { + qreal p1_x = lines[i].p1().x(); + qreal p1_y = lines[i].p1().y(); + qreal p2_x = lines[i].p2().x(); + qreal p2_y = lines[i].p2().y(); + if (p1_x > p2_x) { + min_x = qMin(p2_x, min_x); + max_x = qMax(p1_x, max_x); + } else { + min_x = qMin(p1_x, min_x); + max_x = qMax(p2_x, max_x); + } + if (p1_y > p2_y) { + min_y = qMin(p2_y, min_y); + max_y = qMax(p1_y, max_y); + } else { + min_y = qMin(p1_y, min_y); + max_y = qMax(p2_y, max_y); + } + } + buffer->updateBoundingRect(QRectF(min_x, min_y, max_x - min_x, max_y - min_y)); + } +} + +void QPaintBufferEngine::drawEllipse(const QRectF &r) +{ +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << "QPaintBufferEngine: drawEllipseF:" << r; +#endif + buffer->addCommand(QPaintBufferPrivate::Cmd_DrawEllipseF, (qreal *) &r, 4, 1); + if (buffer->calculateBoundingRect) + buffer->updateBoundingRect(r); +} + +void QPaintBufferEngine::drawEllipse(const QRect &r) +{ +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << "QPaintBufferEngine: drawEllipseI:" << r; +#endif + buffer->addCommand(QPaintBufferPrivate::Cmd_DrawEllipseI, (int *) &r, 4, 1); + if (buffer->calculateBoundingRect) + buffer->updateBoundingRect(r); +} + +void QPaintBufferEngine::drawPath(const QPainterPath &path) +{ +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << "QPaintBufferEngine: drawPath: element count:" << path.elementCount(); +#endif + // ### Path -> QVariant + // buffer->addCommand(QPaintBufferPrivate::Cmd_DrawPath, QVariant(path)); + QPaintEngineEx::drawPath(path); + + if (buffer->calculateBoundingRect) + buffer->updateBoundingRect(path.boundingRect()); +} + +void QPaintBufferEngine::drawPoints(const QPoint *points, int pointCount) +{ +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << "QPaintBufferEngine: drawPointsI: " << pointCount; +#endif + buffer->addCommand(QPaintBufferPrivate::Cmd_DrawPointsI, (int *) points, 2 * pointCount, pointCount); + + if (buffer->calculateBoundingRect) { + int min_x = points[0].x(); + int min_y = points[0].y(); + int max_x = points[0].x()+1; + int max_y = points[0].y()+1; + for (int i=1; i<pointCount; ++i) { + int x = points[i].x(); + int y = points[i].y(); + min_x = qMin(min_x, x); + min_y = qMin(min_y, y); + max_x = qMax(max_x, x+1); + max_y = qMax(max_y, y+1); + } + buffer->updateBoundingRect(QRectF(min_x, min_y, max_x - min_x, max_y - min_y)); + } +} + +void QPaintBufferEngine::drawPoints(const QPointF *points, int pointCount) +{ +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << "QPaintBufferEngine: drawPointsF: " << pointCount; +#endif + buffer->addCommand(QPaintBufferPrivate::Cmd_DrawPointsF, (qreal *) points, 2 * pointCount, pointCount); + + if (buffer->calculateBoundingRect) { + qreal min_x = points[0].x(); + qreal min_y = points[0].y(); + qreal max_x = points[0].x()+1; + qreal max_y = points[0].y()+1; + for (int i=1; i<pointCount; ++i) { + qreal x = points[i].x(); + qreal y = points[i].y(); + min_x = qMin(min_x, x); + min_y = qMin(min_y, y); + max_x = qMax(max_x, x+1); + max_y = qMax(max_y, y+1); + } + buffer->updateBoundingRect(QRectF(min_x, min_y, max_x - min_x, max_y - min_y)); + } +} + +void QPaintBufferEngine::drawPolygon(const QPoint *pts, int count, PolygonDrawMode mode) +{ +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << "QPaintBufferEngine: drawPolygonI: size:" << count << ", mode:" << mode; +#endif + if (mode == QPaintEngine::OddEvenMode || mode == QPaintEngine::WindingMode) { + QPaintBufferCommand *cmd = buffer->addCommand(QPaintBufferPrivate::Cmd_DrawPolygonI, + (int *) pts, 2 * count, count); + cmd->extra = mode; + } else if (mode == QPaintEngine::PolylineMode) { + buffer->addCommand(QPaintBufferPrivate::Cmd_DrawPolylineI, (int *) pts, 2 * count, count); + } else { + buffer->addCommand(QPaintBufferPrivate::Cmd_DrawConvexPolygonI, (int *) pts, 2 * count, count); + } + + if (buffer->calculateBoundingRect) { + int min_x = pts[0].x(); + int min_y = pts[0].y(); + int max_x = pts[0].x(); + int max_y = pts[0].y(); + for (int i=1; i<count; ++i) { + int x = pts[i].x(); + int y = pts[i].y(); + min_x = qMin(min_x, x); + min_y = qMin(min_y, y); + max_x = qMax(max_x, x); + max_y = qMax(max_y, y); + } + buffer->updateBoundingRect(QRectF(min_x, min_y, max_x - min_x, max_y - min_y)); + } +} + +void QPaintBufferEngine::drawPolygon(const QPointF *pts, int count, PolygonDrawMode mode) +{ +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << "QPaintBufferEngine: drawPolygonF: size:" << count << ", mode:" << mode; +#endif + if (mode == QPaintEngine::OddEvenMode || mode == QPaintEngine::WindingMode) { + QPaintBufferCommand *cmd = buffer->addCommand(QPaintBufferPrivate::Cmd_DrawPolygonF, + (qreal *) pts, 2 * count, count); + cmd->extra = mode; + } else if (mode == QPaintEngine::PolylineMode) { + buffer->addCommand(QPaintBufferPrivate::Cmd_DrawPolylineF, (qreal *) pts, 2 * count, count); + } else { + buffer->addCommand(QPaintBufferPrivate::Cmd_DrawConvexPolygonF, (qreal *) pts, 2 * count, count); + } + + if (buffer->calculateBoundingRect) { + qreal min_x = pts[0].x(); + qreal min_y = pts[0].y(); + qreal max_x = pts[0].x(); + qreal max_y = pts[0].y(); + for (int i=1; i<count; ++i) { + qreal x = pts[i].x(); + qreal y = pts[i].y(); + min_x = qMin(min_x, x); + min_y = qMin(min_y, y); + max_x = qMax(max_x, x); + max_y = qMax(max_y, y); + } + buffer->updateBoundingRect(QRectF(min_x, min_y, max_x - min_x, max_y - min_y)); + } +} + +void QPaintBufferEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) +{ +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << "QPaintBufferEngine: drawPixmap: src/dest rects " << r << sr; +#endif + QPaintBufferCommand *cmd = + buffer->addCommand(QPaintBufferPrivate::Cmd_DrawPixmapRect, QVariant(pm)); + cmd->extra = buffer->addData((qreal *) &r, 4); + buffer->addData((qreal *) &sr, 4); + if (buffer->calculateBoundingRect) + buffer->updateBoundingRect(r); +} + +void QPaintBufferEngine::drawPixmap(const QPointF &pos, const QPixmap &pm) +{ +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << "QPaintBufferEngine: drawPixmap: pos:" << pos; +#endif + QPaintBufferCommand *cmd = + buffer->addCommand(QPaintBufferPrivate::Cmd_DrawPixmapPos, QVariant(pm)); + cmd->extra = buffer->addData((qreal *) &pos, 2); + if (buffer->calculateBoundingRect) + buffer->updateBoundingRect(QRectF(pos, pm.size())); +} + +void QPaintBufferEngine::drawImage(const QRectF &r, const QImage &image, const QRectF &sr, + Qt::ImageConversionFlags /*flags */) +{ +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << "QPaintBufferEngine: drawImage: src/dest rects " << r << sr; +#endif + QPaintBufferCommand *cmd = + buffer->addCommand(QPaintBufferPrivate::Cmd_DrawPixmapRect, QVariant(image)); + cmd->extra = buffer->addData((qreal *) &r, 4); + buffer->addData((qreal *) &sr, 4); + // ### flags... + if (buffer->calculateBoundingRect) + buffer->updateBoundingRect(r); +} + +void QPaintBufferEngine::drawImage(const QPointF &pos, const QImage &image) +{ +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << "QPaintBufferEngine: drawImage: pos:" << pos; +#endif + QPaintBufferCommand *cmd = + buffer->addCommand(QPaintBufferPrivate::Cmd_DrawImagePos, QVariant(image)); + cmd->extra = buffer->addData((qreal *) &pos, 2); + if (buffer->calculateBoundingRect) + buffer->updateBoundingRect(QRectF(pos, image.size())); +} + +void QPaintBufferEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pm, const QPointF &s) +{ +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << "QPaintBufferEngine: drawTiledPixmap: src rect/offset:" << r << s; +#endif + QPaintBufferCommand *cmd = + buffer->addCommand(QPaintBufferPrivate::Cmd_DrawTiledPixmap, QVariant(pm)); + cmd->extra = buffer->addData((qreal *) &r, 4); + buffer->addData((qreal *) &s, 2); + if (buffer->calculateBoundingRect) + buffer->updateBoundingRect(r); +} + +void QPaintBufferEngine::drawTextItem(const QPointF &pos, const QTextItem &ti) +{ +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << "QPaintBufferEngine: drawTextItem: pos:" << pos << ti.text(); +#endif + if (m_stream_raw_text_items) { + QPaintBufferCommand *cmd = buffer->addCommand(QPaintBufferPrivate::Cmd_DrawTextItem, qVariantFromValue<void *>(new QTextItemIntCopy(ti))); + + QFont font(ti.font()); + font.setUnderline(false); + font.setStrikeOut(false); + font.setOverline(false); + + const QTextItemInt &si = static_cast<const QTextItemInt &>(ti); + qreal justificationWidth = 0; + if (si.justified) + justificationWidth = si.width.toReal(); + int renderFlags = ti.renderFlags(); + qreal scaleFactor = font.d->dpi/qreal(qt_defaultDpiY()); + + buffer->addData(QVariant(font)); + cmd->extra = buffer->addData((qreal *) &pos, 2); + buffer->addData((qreal *) &justificationWidth, 1); + buffer->addData((qreal *) &scaleFactor, 1); + cmd->offset2 = buffer->addData((int *) &renderFlags, 1); + } else { + QList<QVariant> variants; + variants << QVariant(ti.font()) << QVariant(ti.text()); + QPaintBufferCommand *cmd = buffer->addCommand(QPaintBufferPrivate::Cmd_DrawText, QVariant(variants)); + cmd->extra = buffer->addData((qreal *) &pos, 2); + } + + if (buffer->calculateBoundingRect) + buffer->updateBoundingRect(QRectF(pos, QSize(ti.width(), ti.ascent() + ti.descent() + 1))); +} + + +void QPaintBufferEngine::setState(QPainterState *s) +{ + if (m_begin_detected) { +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << "QPaintBufferEngine: setState: begin, ignoring."; +#endif + m_begin_detected = false; + } else if (m_save_detected) { +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << "QPaintBufferEngine: setState: save."; +#endif + m_save_detected = false; + buffer->addCommand(QPaintBufferPrivate::Cmd_Save); + } else { +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << "QPaintBufferEngine: setState: restore."; +#endif + buffer->addCommand(QPaintBufferPrivate::Cmd_Restore); + } + + QPaintEngineEx::setState(s); +} + + +/*********************************************************************** + * + * class QPaintBufferPlayback_Painter + * + */ + +// QFakeDevice is used to create fonts with a custom DPI +// +class QFakeDevice : public QPaintDevice +{ +public: + QFakeDevice() { dpi_x = qt_defaultDpiX(); dpi_y = qt_defaultDpiY(); } + void setDpiX(int dpi) { dpi_x = dpi; } + void setDpiY(int dpi) { dpi_y = dpi; } + QPaintEngine *paintEngine() const { return 0; } + int metric(PaintDeviceMetric m) const + { + switch(m) { + case PdmPhysicalDpiX: + case PdmDpiX: + return dpi_x; + case PdmPhysicalDpiY: + case PdmDpiY: + return dpi_y; + default: + return QPaintDevice::metric(m); + } + } + +private: + int dpi_x; + int dpi_y; +}; + + +void QPainterReplayer::setupTransform(QPainter *_painter) +{ + painter = _painter; + m_world_matrix = painter->transform(); + m_world_matrix.scale(qreal(painter->device()->logicalDpiX()) / qreal(qt_defaultDpiX()), + qreal(painter->device()->logicalDpiY()) / qreal(qt_defaultDpiY())); + painter->setTransform(m_world_matrix); +} + +void QPainterReplayer::draw(const QPaintBuffer &buffer, QPainter *_painter) +{ + d = buffer.d_ptr; + setupTransform(_painter); + + for (int cmdIndex=0; cmdIndex<d->commands.size(); ++cmdIndex) { + const QPaintBufferCommand &cmd = d->commands.at(cmdIndex); + process(cmd); + } +} + +void QPainterReplayer::process(const QPaintBufferCommand &cmd) +{ + switch (cmd.id) { + case QPaintBufferPrivate::Cmd_Save: { +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> Cmd_Save"; +#endif + painter->save(); + break; } + + case QPaintBufferPrivate::Cmd_Restore: { +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> Cmd_Restore"; +#endif + painter->restore(); + break; } + + case QPaintBufferPrivate::Cmd_SetPen: { + QPen pen = qVariantValue<QPen>(d->variants.at(cmd.offset)); +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> Cmd_SetPen: " << pen; +#endif + painter->setPen(pen); + break; } + + case QPaintBufferPrivate::Cmd_SetBrush: { + QBrush brush = qVariantValue<QBrush>(d->variants.at(cmd.offset)); +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> Cmd_SetBrush: " << brush; +#endif + painter->setBrush(brush); + break; } + + case QPaintBufferPrivate::Cmd_SetBrushOrigin: { +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> Cmd_SetBrushOrigin: " << d->variants.at(cmd.offset).toPointF(); +#endif + painter->setBrushOrigin(d->variants.at(cmd.offset).toPointF()); + break; } + + case QPaintBufferPrivate::Cmd_SetTransform: { + QTransform xform = qVariantValue<QTransform>(d->variants.at(cmd.offset)); +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> Cmd_SetTransform, offset: " << cmd.offset << xform; +#endif + painter->setTransform(xform * m_world_matrix); + break; } + + case QPaintBufferPrivate::Cmd_SetCompositionMode: { + QPainter::CompositionMode mode = (QPainter::CompositionMode) cmd.extra; +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> Cmd_SetCompositionMode, mode: " << mode; +#endif + painter->setCompositionMode(mode); + break; } + + case QPaintBufferPrivate::Cmd_SetRenderHints: { +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> Cmd_SetRenderHints, hints: " << cmd.extra; +#endif + QPainter::RenderHints ph = painter->renderHints(); + QPainter::RenderHints nh = (QPainter::RenderHints) cmd.extra; + QPainter::RenderHints xored = ph ^ nh; + if (xored & QPainter::Antialiasing) + painter->setRenderHint(QPainter::Antialiasing, nh & QPainter::Antialiasing); + if (xored & QPainter::HighQualityAntialiasing) + painter->setRenderHint(QPainter::HighQualityAntialiasing, nh & QPainter::HighQualityAntialiasing); + if (xored & QPainter::TextAntialiasing) + painter->setRenderHint(QPainter::TextAntialiasing, nh & QPainter::TextAntialiasing); + if (xored & QPainter::SmoothPixmapTransform) + painter->setRenderHint(QPainter::SmoothPixmapTransform, nh & QPainter::SmoothPixmapTransform); + if (xored & QPainter::NonCosmeticDefaultPen) + painter->setRenderHint(QPainter::NonCosmeticDefaultPen, nh & QPainter::NonCosmeticDefaultPen); + break; } + + case QPaintBufferPrivate::Cmd_SetOpacity: { +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> Cmd_SetOpacity: " << d->variants.at(cmd.offset).toDouble(); +#endif + painter->setOpacity(d->variants.at(cmd.offset).toDouble()); + break; } + + case QPaintBufferPrivate::Cmd_SetBackgroundMode: { +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> Cmd_SetBackgroundMode: " << cmd.extra; +#endif + painter->setBackgroundMode((Qt::BGMode)cmd.extra); + break; } + + case QPaintBufferPrivate::Cmd_DrawVectorPath: { +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> Cmd_DrawVectorPath: size: " << cmd.size +// << ", hints:" << d->ints[cmd.offset2+cmd.size] + << "pts/elms:" << cmd.offset << cmd.offset2; +#endif + QVectorPathCmd path(d, cmd); + painter->drawPath(path().convertToPainterPath()); + break; } + + case QPaintBufferPrivate::Cmd_StrokeVectorPath: { + QPen pen = qVariantValue<QPen>(d->variants.at(cmd.extra)); +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> Cmd_StrokeVectorPath: size: " << cmd.size +// << ", hints:" << d->ints[cmd.offset2+cmd.size] + << "pts/elms:" << cmd.offset << cmd.offset2; +#endif + QVectorPathCmd path(d, cmd); + painter->strokePath(path().convertToPainterPath(), pen); + break; } + + case QPaintBufferPrivate::Cmd_FillVectorPath: { + QBrush brush = qVariantValue<QBrush>(d->variants.at(cmd.extra)); +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> Cmd_FillVectorPath: size: " << cmd.size +// << ", hints:" << d->ints[cmd.offset2+cmd.size] + << "pts/elms:" << cmd.offset << cmd.offset2 << brush; +#endif + QVectorPathCmd path(d, cmd); + painter->fillPath(path().convertToPainterPath(), brush); + break; } + + case QPaintBufferPrivate::Cmd_DrawPolygonF: { +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> Cmd_DrawPolygonF, offset: " << cmd.offset << " size: " << cmd.size + << " mode: " << cmd.extra + << d->floats.at(cmd.offset) + << d->floats.at(cmd.offset+1); +#endif + Qt::FillRule fill = (QPaintEngine::PolygonDrawMode) cmd.extra == QPaintEngine::OddEvenMode + ? Qt::OddEvenFill : Qt::WindingFill; + painter->drawPolygon((QPointF *) (d->floats.constData() + cmd.offset), cmd.size, fill); + break; } + + case QPaintBufferPrivate::Cmd_DrawPolygonI: { +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> Cmd_DrawPolygonI, offset: " << cmd.offset << " size: " << cmd.size + << " mode: " << cmd.extra + << d->ints.at(cmd.offset) + << d->ints.at(cmd.offset+1); +#endif + Qt::FillRule fill = (QPaintEngine::PolygonDrawMode) cmd.extra == QPaintEngine::OddEvenMode + ? Qt::OddEvenFill : Qt::WindingFill; + painter->drawPolygon((QPoint *) (d->ints.constData() + cmd.offset), cmd.size, fill); + break; } + + case QPaintBufferPrivate::Cmd_DrawPolylineF: { +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> Cmd_DrawPolylineF, offset: " << cmd.offset << " size: " << cmd.size; +#endif + painter->drawPolyline((QPointF *) (d->floats.constData() + cmd.offset), cmd.size); + break; } + + case QPaintBufferPrivate::Cmd_DrawPolylineI: { +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> Cmd_DrawPolylineI, offset: " << cmd.offset << " size: " << cmd.size; +#endif + painter->drawPolyline((QPoint *) (d->ints.constData() + cmd.offset), cmd.size); + break; } + + case QPaintBufferPrivate::Cmd_DrawConvexPolygonF: { +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> Cmd_DrawConvexPolygonF, offset: " << cmd.offset << " size: " << cmd.size; +#endif + painter->drawConvexPolygon((QPointF *) (d->floats.constData() + cmd.offset), cmd.size); + break; } + + case QPaintBufferPrivate::Cmd_DrawConvexPolygonI: { +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> Cmd_DrawConvexPolygonI, offset: " << cmd.offset << " size: " << cmd.size; +#endif + painter->drawConvexPolygon((QPoint *) (d->ints.constData() + cmd.offset), cmd.size); + break; } + + case QPaintBufferPrivate::Cmd_DrawEllipseF: { +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> Cmd_DrawEllipseF, offset: " << cmd.offset; +#endif + painter->drawEllipse(*(QRectF *)(d->floats.constData() + cmd.offset)); + break; } + + case QPaintBufferPrivate::Cmd_DrawEllipseI: { +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> Cmd_DrawEllipseI, offset: " << cmd.offset; +#endif + painter->drawEllipse(*(QRect *)(d->ints.constData() + cmd.offset)); + break; } + + case QPaintBufferPrivate::Cmd_DrawLineF: { +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> Cmd_DrawLineF, offset: " << cmd.offset << " size: " << cmd.size; +#endif + painter->drawLines((QLineF *)(d->floats.constData() + cmd.offset), cmd.size); + break; } + + case QPaintBufferPrivate::Cmd_DrawLineI: { +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> Cmd_DrawLineI, offset: " << cmd.offset << " size: " << cmd.size; +#endif + painter->drawLines((QLine *)(d->ints.constData() + cmd.offset), cmd.size); + break; } + + case QPaintBufferPrivate::Cmd_DrawPointsF: { +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> Cmd_DrawPointsF, offset: " << cmd.offset << " size: " << cmd.size; +#endif + painter->drawPoints((QPointF *)(d->floats.constData() + cmd.offset), cmd.size); + break; } + + case QPaintBufferPrivate::Cmd_DrawPointsI: { +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> Cmd_DrawPointsI, offset: " << cmd.offset << " size: " << cmd.size; +#endif + painter->drawPoints((QPoint *)(d->ints.constData() + cmd.offset), cmd.size); + break; } + + case QPaintBufferPrivate::Cmd_DrawPixmapRect: { + QPixmap pm(d->variants.at(cmd.offset).value<QPixmap>()); + QRectF r(d->floats.at(cmd.extra), d->floats.at(cmd.extra+1), + d->floats.at(cmd.extra+2), d->floats.at(cmd.extra+3)); + + QRectF sr(d->floats.at(cmd.extra+4), d->floats.at(cmd.extra+5), + d->floats.at(cmd.extra+6), d->floats.at(cmd.extra+7)); +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> Cmd_DrawPixmapRect:" << r << sr; +#endif + painter->drawPixmap(r, pm, sr); + break; } + + case QPaintBufferPrivate::Cmd_DrawPixmapPos: { + QPixmap pm(d->variants.at(cmd.offset).value<QPixmap>()); + QPointF pos(d->floats.at(cmd.extra), d->floats.at(cmd.extra+1)); +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> Cmd_DrawPixmapPos:" << pos; +#endif + painter->drawPixmap(pos, pm); + break; } + + case QPaintBufferPrivate::Cmd_DrawTiledPixmap: { + QPixmap pm(d->variants.at(cmd.offset).value<QPixmap>()); + QRectF r(d->floats.at(cmd.extra), d->floats.at(cmd.extra+1), + d->floats.at(cmd.extra+2), d->floats.at(cmd.extra+3)); + + QPointF offset(d->floats.at(cmd.extra+4), d->floats.at(cmd.extra+5)); +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> Cmd_DrawTiledPixmap:" << r << offset; +#endif + painter->drawTiledPixmap(r, pm, offset); + break; } + + case QPaintBufferPrivate::Cmd_DrawImageRect: { + QImage image(d->variants.at(cmd.offset).value<QImage>()); + QRectF r(d->floats.at(cmd.extra), d->floats.at(cmd.extra+1), + d->floats.at(cmd.extra+2), d->floats.at(cmd.extra+3)); + QRectF sr(d->floats.at(cmd.extra+4), d->floats.at(cmd.extra+5), + d->floats.at(cmd.extra+6), d->floats.at(cmd.extra+7)); +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> Cmd_DrawImageRect:" << r << sr; +#endif + painter->drawImage(r, image, sr); + break; } + + case QPaintBufferPrivate::Cmd_DrawImagePos: { + QImage image(d->variants.at(cmd.offset).value<QImage>()); + QPointF pos(d->floats.at(cmd.extra), d->floats.at(cmd.extra+1)); +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> Cmd_DrawImagePos:" << pos; +#endif + painter->drawImage(pos, image); + break; } + + case QPaintBufferPrivate::Cmd_DrawRectF: { +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> Cmd_DrawRectF, offset: " << cmd.offset; +#endif + painter->drawRects((QRectF *)(d->floats.constData() + cmd.offset), cmd.size); + break; } + + case QPaintBufferPrivate::Cmd_DrawRectI: { +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> Cmd_DrawRectI, offset: " << cmd.offset; +#endif + painter->drawRects((QRect *)(d->ints.constData() + cmd.offset), cmd.size); + break; } + + case QPaintBufferPrivate::Cmd_FillRectBrush: { + QBrush brush = qVariantValue<QBrush>(d->variants.at(cmd.extra)); + QRectF *rect = (QRectF *)(d->floats.constData() + cmd.offset); +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> Cmd_FillRectBrush, offset: " << cmd.offset << " rect: " << *rect << " brush: " << brush; +#endif + painter->fillRect(*rect, brush); + break; } + + case QPaintBufferPrivate::Cmd_FillRectColor: { + QColor color = qVariantValue<QColor>(d->variants.at(cmd.extra)); + QRectF *rect = (QRectF *)(d->floats.constData() + cmd.offset); +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> Cmd_FillRectBrush, offset: " << cmd.offset << " rect: " << *rect << " color: " << color; +#endif + painter->fillRect(*rect, color); + break; } + + case QPaintBufferPrivate::Cmd_SetClipEnabled: { + bool clipEnabled = d->variants.at(cmd.offset).toBool(); +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> Cmd_SetClipEnabled:" << clipEnabled; +#endif + painter->setClipping(clipEnabled); + break; } + + case QPaintBufferPrivate::Cmd_ClipVectorPath: { + QVectorPathCmd path(d, cmd); +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> Cmd_ClipVectorPath:" << path().elementCount(); +#endif + painter->setClipPath(path().convertToPainterPath(), Qt::ClipOperation(cmd.extra)); + break; } + + + case QPaintBufferPrivate::Cmd_ClipRect: { + QRect rect(QPoint(d->ints.at(cmd.offset), d->ints.at(cmd.offset + 1)), + QPoint(d->ints.at(cmd.offset + 2), d->ints.at(cmd.offset + 3))); +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> Cmd_ClipRect:" << rect << cmd.extra; +#endif + painter->setClipRect(rect, Qt::ClipOperation(cmd.extra)); + break; } + + case QPaintBufferPrivate::Cmd_ClipRegion: { + QRegion region(d->variants.at(cmd.offset).value<QRegion>()); +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> Cmd_ClipRegion:" << region.boundingRect() << cmd.extra; +#endif + painter->setClipRegion(region, Qt::ClipOperation(cmd.extra)); + break; } + + case QPaintBufferPrivate::Cmd_DrawText: { + QPointF pos(d->floats.at(cmd.extra), d->floats.at(cmd.extra+1)); + QList<QVariant> variants(d->variants.at(cmd.offset).value<QList<QVariant> >()); + + QFont font(variants.at(0).value<QFont>()); + QString text(variants.at(1).value<QString>()); + + painter->setFont(font); + painter->drawText(pos, text); + break; } + + case QPaintBufferPrivate::Cmd_DrawTextItem: { + QPointF pos(d->floats.at(cmd.extra), d->floats.at(cmd.extra+1)); + QTextItemIntCopy *tiCopy = reinterpret_cast<QTextItemIntCopy *>(qVariantValue<void *>(d->variants.at(cmd.offset))); + QTextItemInt &ti = (*tiCopy)(); + QString text(ti.text()); + +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> Cmd_DrawTextItem:" << pos << " " << text << " " << scaleFactor; +#endif + + QFont font(ti.font()); + font.setUnderline(false); + font.setStrikeOut(false); + font.setOverline(false); + + const QTextItemInt &si = static_cast<const QTextItemInt &>(ti); + qreal justificationWidth = 0; + if (si.justified) + justificationWidth = si.width.toReal(); + qreal scaleFactor = font.d->dpi/qreal(qt_defaultDpiY()); + + if (scaleFactor != 1.0) { + QFont fnt(font); + QFakeDevice fake; + fake.setDpiX(qRound(scaleFactor*qt_defaultDpiX())); + fake.setDpiY(qRound(scaleFactor*qt_defaultDpiY())); + font = QFont(fnt, &fake); + } + + int flags = Qt::TextSingleLine | Qt::TextDontClip | Qt::TextForceLeftToRight; + QSizeF size(1, 1); + if (justificationWidth > 0) { + size.setWidth(justificationWidth); + flags |= Qt::TextJustificationForced; + flags |= Qt::AlignJustify; + } + + QFontMetrics fm(font); + QPointF pt(pos.x(), pos.y() - fm.ascent()); + qt_format_text(font, QRectF(pt, size), flags, /*opt*/0, + text, /*brect=*/0, /*tabstops=*/0, /*...*/0, /*tabarraylen=*/0, painter); + break; } + case QPaintBufferPrivate::Cmd_SystemStateChanged: { + QRegion systemClip(d->variants.at(cmd.offset).value<QRegion>()); + +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> Cmd_SystemStateChanged:" << systemClip; +#endif + + painter->paintEngine()->setSystemClip(systemClip); + painter->paintEngine()->d_ptr->systemStateChanged(); + break; } + } +} + +void QPaintEngineExReplayer::process(const QPaintBufferCommand &cmd) +{ + Q_ASSERT(painter->paintEngine()->isExtended()); + QPaintEngineEx *xengine = static_cast<QPaintEngineEx *>(painter->paintEngine()); + + switch (cmd.id) { + case QPaintBufferPrivate::Cmd_SetBrushOrigin: { +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> ExCmd_SetBrushOrigin: " << d->variants.at(cmd.offset).toPointF(); +#endif + xengine->state()->brushOrigin = d->variants.at(cmd.offset).toPointF(); + xengine->brushOriginChanged(); + break; } + + case QPaintBufferPrivate::Cmd_SetCompositionMode: { + QPainter::CompositionMode mode = (QPainter::CompositionMode) cmd.extra; +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> ExCmd_SetCompositionMode, mode: " << mode; +#endif + xengine->state()->composition_mode = mode; + xengine->compositionModeChanged(); + break; } + + case QPaintBufferPrivate::Cmd_SetOpacity: { +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> ExCmd_SetOpacity: " << d->variants.at(cmd.offset).toDouble(); +#endif + xengine->state()->opacity = d->variants.at(cmd.offset).toDouble(); + xengine->opacityChanged(); + break; } + + case QPaintBufferPrivate::Cmd_DrawVectorPath: { +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> ExCmd_DrawVectorPath: size: " << cmd.size +// << ", hints:" << d->ints[cmd.offset2+cmd.size] + << "pts/elms:" << cmd.offset << cmd.offset2; +#endif + QVectorPathCmd path(d, cmd); + xengine->draw(path()); + break; } + + case QPaintBufferPrivate::Cmd_StrokeVectorPath: { + QPen pen = qVariantValue<QPen>(d->variants.at(cmd.extra)); +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> ExCmd_StrokeVectorPath: size: " << cmd.size +// << ", hints:" << d->ints[cmd.offset2+cmd.size] + << "pts/elms:" << cmd.offset << cmd.offset2; +#endif + QVectorPathCmd path(d, cmd); + xengine->stroke(path(), pen); + break; } + + case QPaintBufferPrivate::Cmd_FillVectorPath: { + QBrush brush = qVariantValue<QBrush>(d->variants.at(cmd.extra)); +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> ExCmd_FillVectorPath: size: " << cmd.size +// << ", hints:" << d->ints[cmd.offset2+cmd.size] + << "pts/elms:" << cmd.offset << cmd.offset2 << brush; +#endif + QVectorPathCmd path(d, cmd); + xengine->fill(path(), brush); + break; } + + case QPaintBufferPrivate::Cmd_FillRectBrush: { + QBrush brush = qVariantValue<QBrush>(d->variants.at(cmd.extra)); + QRectF *rect = (QRectF *)(d->floats.constData() + cmd.offset); +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> ExCmd_FillRectBrush, offset: " << cmd.offset << " rect: " << *rect << " brush: " << brush; +#endif + xengine->fillRect(*rect, brush); + break; } + + case QPaintBufferPrivate::Cmd_FillRectColor: { + QColor color = qVariantValue<QColor>(d->variants.at(cmd.extra)); + QRectF *rect = (QRectF *)(d->floats.constData() + cmd.offset); +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> ExCmd_FillRectBrush, offset: " << cmd.offset << " rect: " << *rect << " color: " << color; +#endif + xengine->fillRect(*rect, color); + break; } + + case QPaintBufferPrivate::Cmd_DrawPolygonF: { +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> ExCmd_DrawPolygonF, offset: " << cmd.offset << " size: " << cmd.size + << " mode: " << cmd.extra + << d->floats.at(cmd.offset) + << d->floats.at(cmd.offset+1); +#endif + xengine->drawPolygon((QPointF *) (d->floats.constData() + cmd.offset), cmd.size, + (QPaintEngine::PolygonDrawMode) cmd.extra); + break; } + + case QPaintBufferPrivate::Cmd_DrawPolygonI: { +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> ExCmd_DrawPolygonI, offset: " << cmd.offset << " size: " << cmd.size + << " mode: " << cmd.extra + << d->ints.at(cmd.offset) + << d->ints.at(cmd.offset+1); +#endif + xengine->drawPolygon((QPoint *) (d->ints.constData() + cmd.offset), cmd.size, + (QPaintEngine::PolygonDrawMode) cmd.extra); + break; } + + case QPaintBufferPrivate::Cmd_DrawEllipseF: { +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> ExCmd_DrawEllipseF, offset: " << cmd.offset; +#endif + xengine->drawEllipse(*(QRectF *)(d->floats.constData() + cmd.offset)); + break; } + + case QPaintBufferPrivate::Cmd_DrawEllipseI: { +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> ExCmd_DrawEllipseI, offset: " << cmd.offset; +#endif + xengine->drawEllipse(*(QRect *)(d->ints.constData() + cmd.offset)); + break; } + + case QPaintBufferPrivate::Cmd_DrawLineF: { +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> ExCmd_DrawLineF, offset: " << cmd.offset << " size: " << cmd.size; +#endif + xengine->drawLines((QLineF *)(d->floats.constData() + cmd.offset), cmd.size); + break; } + + case QPaintBufferPrivate::Cmd_DrawLineI: { +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> ExCmd_DrawLineI, offset: " << cmd.offset << " size: " << cmd.size; +#endif + xengine->drawLines((QLine *)(d->ints.constData() + cmd.offset), cmd.size); + break; } + + case QPaintBufferPrivate::Cmd_DrawPointsF: { +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> ExCmd_DrawPointsF, offset: " << cmd.offset << " size: " << cmd.size; +#endif + xengine->drawPoints((QPointF *)(d->floats.constData() + cmd.offset), cmd.size); + break; } + + case QPaintBufferPrivate::Cmd_DrawPointsI: { +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> ExCmd_DrawPointsI, offset: " << cmd.offset << " size: " << cmd.size; +#endif + xengine->drawPoints((QPoint *)(d->ints.constData() + cmd.offset), cmd.size); + break; } + + case QPaintBufferPrivate::Cmd_DrawPolylineF: { +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> ExCmd_DrawPolylineF, offset: " << cmd.offset << " size: " << cmd.size; +#endif + xengine->drawPolygon((QPointF *) (d->floats.constData() + cmd.offset), cmd.size, QPaintEngine::PolylineMode); + break; } + + case QPaintBufferPrivate::Cmd_DrawPolylineI: { +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> ExCmd_DrawPolylineI, offset: " << cmd.offset << " size: " << cmd.size; +#endif + xengine->drawPolygon((QPoint *) (d->ints.constData() + cmd.offset), cmd.size, QPaintEngine::PolylineMode); + break; } + + case QPaintBufferPrivate::Cmd_DrawRectF: { +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> ExCmd_DrawRectF, offset: " << cmd.offset << " size: " << cmd.size; +#endif + xengine->drawRects((QRectF *) (d->floats.constData() + cmd.offset), cmd.size); + break; } + + case QPaintBufferPrivate::Cmd_DrawRectI: { +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> ExCmd_DrawRectI, offset: " << cmd.offset << " size: " << cmd.size; +#endif + xengine->drawRects((QRect *) (d->ints.constData() + cmd.offset), cmd.size); + break; } + + case QPaintBufferPrivate::Cmd_SetClipEnabled: { + bool clipEnabled = d->variants.at(cmd.offset).toBool(); +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> ExCmd_SetClipEnabled:" << clipEnabled; +#endif + xengine->state()->clipEnabled = clipEnabled; + xengine->clipEnabledChanged(); + break; } + + case QPaintBufferPrivate::Cmd_ClipVectorPath: { + QVectorPathCmd path(d, cmd); +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> ExCmd_ClipVectorPath:" << path().elementCount(); +#endif + xengine->clip(path(), Qt::ClipOperation(cmd.extra)); + break; } + + + case QPaintBufferPrivate::Cmd_ClipRect: { + QRect rect(QPoint(d->ints.at(cmd.offset), d->ints.at(cmd.offset + 1)), + QPoint(d->ints.at(cmd.offset + 2), d->ints.at(cmd.offset + 3))); +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> ExCmd_ClipRect:" << rect << cmd.extra; +#endif + xengine->clip(rect, Qt::ClipOperation(cmd.extra)); + break; } + + case QPaintBufferPrivate::Cmd_ClipRegion: { + QRegion region(d->variants.at(cmd.offset).value<QRegion>()); +#ifdef QPAINTBUFFER_DEBUG_DRAW + qDebug() << " -> ExCmd_ClipRegion:" << region.boundingRect() << cmd.extra; +#endif + xengine->clip(region, Qt::ClipOperation(cmd.extra)); + break; } + + default: + QPainterReplayer::process(cmd); + break; + } +} + +QPaintBufferResource::QPaintBufferResource(FreeFunc f, QObject *parent) : QObject(parent), free(f) +{ + connect(QPaintBufferSignalProxy::instance(), SIGNAL(aboutToDestroy(const QPaintBufferPrivate *)), this, SLOT(remove(const QPaintBufferPrivate *))); +} + +QPaintBufferResource::~QPaintBufferResource() +{ + for (Cache::iterator it = m_cache.begin(); it != m_cache.end(); ++it) + free(it.value()); +} + +void QPaintBufferResource::insert(const QPaintBufferPrivate *key, void *value) +{ + Cache::iterator it = m_cache.find(key); + if (it != m_cache.end()) { + free(it.value()); + it.value() = value; + } else { + m_cache.insert(key, value); + } +} + +void *QPaintBufferResource::value(const QPaintBufferPrivate *key) +{ + Cache::iterator it = m_cache.find(key); + if (it != m_cache.end()) + return it.value(); + return 0; +} + +void QPaintBufferResource::remove(const QPaintBufferPrivate *key) +{ + Cache::iterator it = m_cache.find(key); + if (it != m_cache.end()) { + free(it.value()); + m_cache.erase(it); + } +} + +QDataStream &operator<<(QDataStream &stream, const QPaintBufferCommand &command) +{ + quint32 id = command.id; + quint32 size = command.size; + stream << id << size; + stream << command.offset << command.offset2 << command.extra; + return stream; +} + +QDataStream &operator>>(QDataStream &stream, QPaintBufferCommand &command) +{ + quint32 id; + quint32 size; + stream >> id >> size; + stream >> command.offset >> command.offset2 >> command.extra; + command.id = id; + command.size = size; + return stream; +} + +QDataStream &operator<<(QDataStream &stream, const QPaintBuffer &buffer) +{ + stream << buffer.d_ptr->ints; + stream << buffer.d_ptr->floats; + stream << buffer.d_ptr->variants; + stream << buffer.d_ptr->commands; + stream << buffer.d_ptr->boundingRect; + + return stream; +} + +QDataStream &operator>>(QDataStream &stream, QPaintBuffer &buffer) +{ + stream >> buffer.d_ptr->ints; + stream >> buffer.d_ptr->floats; + stream >> buffer.d_ptr->variants; + stream >> buffer.d_ptr->commands; + stream >> buffer.d_ptr->boundingRect; + + return stream; +} + diff --git a/src/gui/painting/qpaintbuffer_p.h b/src/gui/painting/qpaintbuffer_p.h new file mode 100644 index 0000000..b360279 --- /dev/null +++ b/src/gui/painting/qpaintbuffer_p.h @@ -0,0 +1,439 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.1, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPAINTBUFFER_P_H +#define QPAINTBUFFER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <qpaintdevice.h> + +#include <private/qpaintengineex_p.h> +#include <private/qtextengine_p.h> +#include <QDebug> + +class QPaintBufferPrivate; +class QPaintBufferPlayback; + +class Q_GUI_EXPORT QPaintBuffer : public QPaintDevice +{ +public: + QPaintBuffer(); + QPaintBuffer(const QPaintBuffer &other); + ~QPaintBuffer(); + + bool isEmpty() const; + + void draw(QPainter *painter) const; + void setBoundingRect(const QRectF &rect); + QRectF boundingRect() const; + + virtual QPaintEngine *paintEngine() const; + virtual int metric(PaintDeviceMetric m) const; + virtual int devType() const; + + QPaintBuffer &operator=(const QPaintBuffer &other); + +private: + friend class QPainterReplayer; + friend class QOpenGLReplayer; + + friend Q_GUI_EXPORT QDataStream &operator<<(QDataStream &stream, const QPaintBuffer &buffer); + friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &stream, QPaintBuffer &buffer); + + QPaintBufferPrivate *d_ptr; +}; + +Q_GUI_EXPORT QDataStream &operator<<(QDataStream &stream, const QPaintBuffer &buffer); +Q_GUI_EXPORT QDataStream &operator>>(QDataStream &stream, QPaintBuffer &buffer); + +class QPaintBufferEngine; + +class QTextItemIntCopy +{ +public: + QTextItemIntCopy(const QTextItem &item); + ~QTextItemIntCopy(); + QTextItemInt &operator () () {return m_item;} +private: + QTextItemInt m_item; + QFont m_font; +}; + +struct QPaintBufferCommand +{ + uint id : 8; + uint size : 24; + + int offset; + int offset2; + int extra; +}; + +QDataStream &operator<<(QDataStream &stream, const QPaintBufferCommand &command); +QDataStream &operator>>(QDataStream &stream, QPaintBufferCommand &command); + +Q_DECLARE_TYPEINFO(QPaintBufferCommand, Q_MOVABLE_TYPE); + +class QPaintBufferPrivate +{ +public: + enum Command { + Cmd_Save, + Cmd_Restore, + + Cmd_SetBrush, + Cmd_SetBrushOrigin, + Cmd_SetClipEnabled, + Cmd_SetCompositionMode, + Cmd_SetOpacity, + Cmd_SetPen, + Cmd_SetRenderHints, + Cmd_SetTransform, + Cmd_SetBackgroundMode, + + Cmd_ClipPath, + Cmd_ClipRect, + Cmd_ClipRegion, + Cmd_ClipVectorPath, + + Cmd_DrawVectorPath, + Cmd_FillVectorPath, + Cmd_StrokeVectorPath, + + Cmd_DrawConvexPolygonF, + Cmd_DrawConvexPolygonI, + Cmd_DrawEllipseF, + Cmd_DrawEllipseI, + Cmd_DrawLineF, + Cmd_DrawLineI, + Cmd_DrawPath, + Cmd_DrawPointsF, + Cmd_DrawPointsI, + Cmd_DrawPolygonF, + Cmd_DrawPolygonI, + Cmd_DrawPolylineF, + Cmd_DrawPolylineI, + Cmd_DrawRectF, + Cmd_DrawRectI, + + Cmd_FillRectBrush, + Cmd_FillRectColor, + + Cmd_DrawText, + Cmd_DrawTextItem, + + Cmd_DrawImagePos, + Cmd_DrawImageRect, + Cmd_DrawPixmapPos, + Cmd_DrawPixmapRect, + Cmd_DrawTiledPixmap, + + Cmd_SystemStateChanged, + + Cmd_LastCommand + }; + + QPaintBufferPrivate(); + ~QPaintBufferPrivate(); + + int addData(const int *data, int count) { + if (count <= 0) + return 0; + int pos = ints.size(); + ints.resize(pos + count); + memcpy(ints.data() + pos, data, count * sizeof(int)); + return pos; + } + + int addData(const qreal *data, int count) { + if (count <= 0) + return 0; + int pos = floats.size(); + floats.resize(pos + count); + memcpy(floats.data() + pos, data, count * sizeof(qreal)); + return pos; + } + + int addData(const QVariant &var) { + variants << var; + return variants.size() - 1; + } + + QPaintBufferCommand *addCommand(Command command) { + QPaintBufferCommand cmd; + cmd.id = command; + cmd.size = cmd.offset = cmd.offset2 = cmd.extra = 0; + commands << cmd; + return &commands.last(); + } + + QPaintBufferCommand *addCommand(Command command, const QVariant &var) { + QPaintBufferCommand cmd; + cmd.id = command; + cmd.offset = addData(var); + cmd.size = cmd.offset2 = cmd.extra = 0; + commands << cmd; + return &commands.last(); + } + + QPaintBufferCommand *addCommand(Command command, const QVectorPath &path) { + QPaintBufferCommand cmd; + cmd.id = command; + cmd.offset = addData(path.points(), path.elementCount() * 2); + cmd.offset2 = ints.size(); + ints << path.hints(); + // The absence of path elements is indicated by setting the highest bit in 'cmd.offset2'. + if (path.elements()) + addData((const int *) path.elements(), path.elementCount()); + else + cmd.offset2 |= 0x80000000; + cmd.size = path.elementCount(); + cmd.extra = 0; + commands << cmd; + return &commands.last(); + } + + QPaintBufferCommand *addCommand(Command command , const qreal *pts, int arrayLength, int elementCount) { + QPaintBufferCommand cmd; + cmd.id = command; + cmd.offset = addData(pts, arrayLength); + cmd.size = elementCount; + cmd.offset2 = cmd.extra = 0; + commands << cmd; + return &commands.last(); + } + + QPaintBufferCommand *addCommand(Command command , const int *pts, int arrayLength, int elementCount) { + QPaintBufferCommand cmd; + cmd.id = command; + cmd.offset = addData(pts, arrayLength); + cmd.size = elementCount; + cmd.offset2 = cmd.extra = 0; + commands << cmd; + return &commands.last(); + } + + inline void updateBoundingRect(const QRectF &rect); + + QAtomicInt ref; + + QVector<int> ints; + QVector<qreal> floats; + QVector<QVariant> variants; + + QVector<QPaintBufferCommand> commands; + + QPaintBufferEngine *engine; + QRectF boundingRect; + qreal penWidthAdjustment; + uint calculateBoundingRect : 1; + + void *cache; +}; + + +struct QVectorPathCmd +{ + // The absence of path elements is indicated by setting the highest bit in 'cmd.offset2'. + QVectorPathCmd(QPaintBufferPrivate *d, const QPaintBufferCommand &cmd) + : vectorPath(d->floats.constData() + cmd.offset, + cmd.size, + cmd.offset2 & 0x80000000 + ? 0 + : (const QPainterPath::ElementType *) (d->ints.constData() + cmd.offset2 + 1), + *(d->ints.constData() + (cmd.offset2 & 0x7fffffff))) {} + + inline const QVectorPath &operator()() const { return vectorPath; } + + QVectorPath vectorPath; +}; + + +class Q_GUI_EXPORT QPainterReplayer +{ +public: + QPainterReplayer() { } + + virtual ~QPainterReplayer() { } + + void setupTransform(QPainter *painter); + void process(const QPaintBufferCommand &cmd); + void draw(const QPaintBuffer &buffer, QPainter *painter); + +protected: + QPaintBufferPrivate *d; + QTransform m_world_matrix; + + QPainter *painter; +}; + +class Q_GUI_EXPORT QPaintEngineExReplayer : public QPainterReplayer +{ +public: + QPaintEngineExReplayer() { } + + void process(const QPaintBufferCommand &cmd); +}; + +class QPaintBufferEnginePrivate; + +class QPaintBufferEngine : public QPaintEngineEx +{ + Q_DECLARE_PRIVATE(QPaintBufferEngine) +public: + QPaintBufferEngine(QPaintBufferPrivate *buffer); + + virtual bool begin(QPaintDevice *device); + virtual bool end(); + + virtual Type type() const { return QPaintEngine::PaintBuffer; } + + virtual QPainterState *createState(QPainterState *orig) const; + + virtual void draw(const QVectorPath &path); + virtual void fill(const QVectorPath &path, const QBrush &brush); + virtual void stroke(const QVectorPath &path, const QPen &pen); + + virtual void clip(const QVectorPath &path, Qt::ClipOperation op); + virtual void clip(const QRect &rect, Qt::ClipOperation op); + virtual void clip(const QRegion ®ion, Qt::ClipOperation op); + virtual void clip(const QPainterPath &path, Qt::ClipOperation op); + + virtual void clipEnabledChanged(); + virtual void penChanged(); + virtual void brushChanged(); + virtual void brushOriginChanged(); + virtual void opacityChanged(); + virtual void compositionModeChanged(); + virtual void renderHintsChanged(); + virtual void transformChanged(); + virtual void backgroundModeChanged(); + + virtual void fillRect(const QRectF &rect, const QBrush &brush); + virtual void fillRect(const QRectF &rect, const QColor &color); + + virtual void drawRects(const QRect *rects, int rectCount); + virtual void drawRects(const QRectF *rects, int rectCount); + + virtual void drawLines(const QLine *lines, int lineCount); + virtual void drawLines(const QLineF *lines, int lineCount); + + virtual void drawEllipse(const QRectF &r); + virtual void drawEllipse(const QRect &r); + + virtual void drawPath(const QPainterPath &path); + + virtual void drawPoints(const QPointF *points, int pointCount); + virtual void drawPoints(const QPoint *points, int pointCount); + + virtual void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode); + virtual void drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode); + + virtual void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr); + virtual void drawPixmap(const QPointF &pos, const QPixmap &pm); + + virtual void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, + Qt::ImageConversionFlags flags = Qt::AutoColor); + virtual void drawImage(const QPointF &pos, const QImage &image); + + virtual void drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s); + + virtual void drawTextItem(const QPointF &pos, const QTextItem &ti); + + virtual void setState(QPainterState *s); + virtual uint flags() const {return QPaintEngineEx::DoNotEmulate;} + + QPaintBufferPrivate *buffer; + + mutable int m_begin_detected : 1; + mutable int m_save_detected : 1; + mutable int m_stream_raw_text_items : 1; + mutable int m_unused : 29; + + mutable QPainterState *m_created_state; +}; + +class Q_GUI_EXPORT QPaintBufferSignalProxy : public QObject +{ + Q_OBJECT +public: + QPaintBufferSignalProxy() : QObject() {} + void emitAboutToDestroy(const QPaintBufferPrivate *buffer) { + emit aboutToDestroy(buffer); + } + static QPaintBufferSignalProxy *instance(); +Q_SIGNALS: + void aboutToDestroy(const QPaintBufferPrivate *buffer); +}; + +// One resource per paint buffer and vice versa. +class Q_GUI_EXPORT QPaintBufferResource : public QObject +{ + Q_OBJECT +public: + typedef void (*FreeFunc)(void *); + + QPaintBufferResource(FreeFunc f, QObject *parent = 0); + ~QPaintBufferResource(); + // Set resource 'value' for 'key'. + void insert(const QPaintBufferPrivate *key, void *value); + // Return resource for 'key'. + void *value(const QPaintBufferPrivate *key); +public slots: + // Remove entry 'key' from cache and delete resource. + void remove(const QPaintBufferPrivate *key); +private: + typedef QHash<const QPaintBufferPrivate *, void *> Cache; + Cache m_cache; + FreeFunc free; +}; + +#endif // QPAINTBUFFER_P_H diff --git a/src/gui/painting/qpaintengine.h b/src/gui/painting/qpaintengine.h index 5cd17fe..921db4f 100644 --- a/src/gui/painting/qpaintengine.h +++ b/src/gui/painting/qpaintengine.h @@ -212,6 +212,7 @@ public: Pdf, OpenVG, OpenGL2, + PaintBuffer, User = 50, // first user type id MaxUser = 100 // last user type id @@ -248,6 +249,7 @@ private: bool autoDestruct() const { return selfDestruct; } Q_DISABLE_COPY(QPaintEngine) + friend class QPainterReplayer; friend class QFontEngineBox; friend class QFontEngineMac; friend class QFontEngineWin; diff --git a/src/gui/painting/qpaintengineex.cpp b/src/gui/painting/qpaintengineex.cpp index c2703bc..4b0f8ae 100644 --- a/src/gui/painting/qpaintengineex.cpp +++ b/src/gui/painting/qpaintengineex.cpp @@ -92,40 +92,6 @@ QRectF QVectorPath::controlPointRect() const return QRectF(QPointF(m_cp_rect.x1, m_cp_rect.y1), QPointF(m_cp_rect.x2, m_cp_rect.y2)); } -QPainterPath QVectorPath::convertToPainterPath() const -{ - QPainterPath path; - - if (m_count == 0) - return path; - - const QPointF *points = (const QPointF *) m_points; - - if (m_elements) { - for (int i=0; i<m_count; ++i) { - switch (m_elements[i]) { - case QPainterPath::MoveToElement: - path.moveTo(points[i]); - break; - case QPainterPath::LineToElement: - path.lineTo(points[i]); - break; - case QPainterPath::CurveToElement: - path.cubicTo(points[i], points[i+1], points[i+2]); - break; - default: - break; - } - } - } else { - path.moveTo(points[0]); - for (int i=1; i<m_count; ++i) - path.lineTo(points[i]); - } - - return path; -} - const QVectorPath &qtVectorPathForPath(const QPainterPath &path) { Q_ASSERT(path.d_func()); diff --git a/src/gui/painting/qpaintengineex_p.h b/src/gui/painting/qpaintengineex_p.h index a252b5e..1fc1bd1 100644 --- a/src/gui/painting/qpaintengineex_p.h +++ b/src/gui/painting/qpaintengineex_p.h @@ -209,6 +209,14 @@ public: virtual QPixmapFilter *createPixmapFilter(int /*type*/) const { return 0; } + // These flags are needed in the implementation of paint buffers. + enum Flags + { + DoNotEmulate = 0x01, // If set, QPainter will not wrap this engine in an emulation engine. + IsEmulationEngine = 0x02 // If set, this object is a QEmulationEngine. + }; + virtual uint flags() const {return 0;} + protected: QPaintEngineEx(QPaintEngineExPrivate &data); }; diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp index ed400df..a6bea76 100644 --- a/src/gui/painting/qpainter.cpp +++ b/src/gui/painting/qpainter.cpp @@ -162,6 +162,9 @@ static bool qt_painter_thread_test(int devType, const char *what, bool extraCond void QPainterPrivate::checkEmulation() { Q_ASSERT(extended); + if (extended->flags() & QPaintEngineEx::DoNotEmulate) + return; + bool doEmulation = false; if (state->bgMode == Qt::OpaqueMode) doEmulation = true; @@ -1835,11 +1838,6 @@ bool QPainter::end() return true; } - if (d->states.size() > 1) { - qWarning("QPainter::end: Painter ended with %d saved states", - d->states.size()); - } - bool ended = true; if (d->engine->isActive()) { @@ -1853,6 +1851,11 @@ bool QPainter::end() } } + if (d->states.size() > 1) { + qWarning("QPainter::end: Painter ended with %d saved states", + d->states.size()); + } + if (d->engine->autoDestruct()) { delete d->engine; } diff --git a/src/gui/painting/qpainterpath.h b/src/gui/painting/qpainterpath.h index 9d472e8..a0ee749 100644 --- a/src/gui/painting/qpainterpath.h +++ b/src/gui/painting/qpainterpath.h @@ -220,6 +220,7 @@ private: friend class QPainterPathStrokerPrivate; friend class QMatrix; friend class QTransform; + friend class QVectorPath; friend Q_GUI_EXPORT const QVectorPath &qtVectorPathForPath(const QPainterPath &); #ifndef QT_NO_DATASTREAM @@ -237,6 +238,7 @@ public: friend class QPainterPathStrokerPrivate; friend class QMatrix; friend class QTransform; + friend class QVectorPath; friend struct QPainterPathPrivateDeleter; #ifndef QT_NO_DATASTREAM friend Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QPainterPath &); diff --git a/src/gui/painting/qpainterpath_p.h b/src/gui/painting/qpainterpath_p.h index bb455cf..94be339 100644 --- a/src/gui/painting/qpainterpath_p.h +++ b/src/gui/painting/qpainterpath_p.h @@ -176,6 +176,42 @@ public: }; +inline const QPainterPath QVectorPath::convertToPainterPath() const +{ + QPainterPath path; + path.ensureData(); + QPainterPathData *data = path.d_func(); + data->elements.reserve(m_count); + int index = 0; + data->elements[0].x = m_points[index++]; + data->elements[0].y = m_points[index++]; + + if (m_elements) { + data->elements[0].type = m_elements[0]; + for (int i=1; i<m_count; ++i) { + QPainterPath::Element element; + element.x = m_points[index++]; + element.y = m_points[index++]; + element.type = m_elements[i]; + data->elements << element; + } + } else { + data->elements[0].type = QPainterPath::MoveToElement; + for (int i=1; i<m_count; ++i) { + QPainterPath::Element element; + element.x = m_points[index++]; + element.y = m_points[index++]; + element.type = QPainterPath::LineToElement; + data->elements << element; + } + } + + if (m_hints & OddEvenFill) + data->fillRule = Qt::OddEvenFill; + else + data->fillRule = Qt::WindingFill; + return path; +} void Q_GUI_EXPORT qt_find_ellipse_coords(const QRectF &r, qreal angle, qreal length, QPointF* startPoint, QPointF *endPoint); diff --git a/src/gui/painting/qvectorpath_p.h b/src/gui/painting/qvectorpath_p.h index 1e909d4..199d0d6 100644 --- a/src/gui/painting/qvectorpath_p.h +++ b/src/gui/painting/qvectorpath_p.h @@ -127,11 +127,10 @@ public: inline bool isEmpty() const { return m_points == 0; } inline int elementCount() const { return m_count; } + inline const QPainterPath convertToPainterPath() const; static inline uint polygonFlags(QPaintEngine::PolygonDrawMode mode); - QPainterPath convertToPainterPath() const; - private: Q_DISABLE_COPY(QVectorPath) |