diff --git a/src/plugins/analyzerbase/analyzermanager.cpp b/src/plugins/analyzerbase/analyzermanager.cpp
index ee872b9950d..ab4eb527f1d 100644
--- a/src/plugins/analyzerbase/analyzermanager.cpp
+++ b/src/plugins/analyzerbase/analyzermanager.cpp
@@ -101,7 +101,7 @@ using namespace Analyzer::Internal;
// A separate 'Analzye' mode is not used in Qt Creator 2.2.
// Consider re-introducing it if a real use case for a separate main window with docks
// appears.
-enum { useAnalyzeMode = 0 };
+enum { useAnalyzeMode = 1 };
namespace Analyzer {
namespace Internal {
diff --git a/src/plugins/analyzerbase/analyzerruncontrol.cpp b/src/plugins/analyzerbase/analyzerruncontrol.cpp
index e853b67570c..e46aac0b617 100644
--- a/src/plugins/analyzerbase/analyzerruncontrol.cpp
+++ b/src/plugins/analyzerbase/analyzerruncontrol.cpp
@@ -131,6 +131,7 @@ AnalyzerRunControl::~AnalyzerRunControl()
{
if (m_isRunning)
stop();
+ delete m_engine;
}
void AnalyzerRunControl::start()
diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro
index 209562857c0..62495c55b23 100644
--- a/src/plugins/plugins.pro
+++ b/src/plugins/plugins.pro
@@ -38,6 +38,7 @@ SUBDIRS = plugin_coreplugin \
plugin_classview \
plugin_tasklist \
plugin_analyzerbase \
+ plugin_qmlprofiler \
plugin_qmljstools \
plugin_macros \
debugger/dumper.pro
@@ -263,6 +264,10 @@ plugin_analyzerbase.depends += plugin_projectexplorer
plugin_memcheck.depends += plugin_valgrindtoolbase
}
+plugin_qmlprofiler.subdir = qmlprofiler
+plugin_qmlprofiler.depends = plugin_coreplugin
+plugin_qmlprofiler.depends = plugin_analyzerbase
+
plugin_qmljstools.subdir = qmljstools
plugin_qmljstools.depends = plugin_projectexplorer
plugin_qmljstools.depends += plugin_coreplugin
diff --git a/src/plugins/qmlprofiler/Detail.qml b/src/plugins/qmlprofiler/Detail.qml
new file mode 100644
index 00000000000..cc7e852ade1
--- /dev/null
+++ b/src/plugins/qmlprofiler/Detail.qml
@@ -0,0 +1,34 @@
+import QtQuick 1.1
+import Monitor 1.0
+import "MainView.js" as Plotter
+
+Item {
+ id: detail
+ property string label
+ property string content
+ property int maxLines: 4
+ signal linkActivated(string url)
+
+ height: childrenRect.height
+ width: childrenRect.width
+ Item {
+ id: guideline
+ x: 70
+ width: 5
+ }
+ Text {
+ id: lbl
+ text: label + ":"
+ font.pixelSize: 12
+ font.bold: true
+ anchors.right: guideline.left
+ }
+ Text {
+ text: content
+ font.pixelSize: 12
+ anchors.baseline: lbl.baseline
+ anchors.left: guideline.right
+ maximumLineCount: maxLines
+ onLinkActivated: detail.linkActivated(link)
+ }
+}
diff --git a/src/plugins/qmlprofiler/Elapsed.qml b/src/plugins/qmlprofiler/Elapsed.qml
new file mode 100644
index 00000000000..298782e120b
--- /dev/null
+++ b/src/plugins/qmlprofiler/Elapsed.qml
@@ -0,0 +1,26 @@
+import QtQuick 1.1
+import Monitor 1.0
+import "MainView.js" as Plotter
+
+Text {
+ id: elapsed
+ color: "white"
+
+ Timer {
+ property date startDate
+ property bool reset: true
+ running: connection.recording
+ repeat: true
+ onRunningChanged: if (running) reset = true
+ interval: 100
+ triggeredOnStart: true
+ onTriggered: {
+ if (reset) {
+ startDate = new Date()
+ reset = false
+ }
+ var time = (new Date() - startDate)/1000
+ elapsed.text = time.toFixed(1) + "s"
+ }
+ }
+}
diff --git a/src/plugins/qmlprofiler/Label.qml b/src/plugins/qmlprofiler/Label.qml
new file mode 100644
index 00000000000..0363fc2bf88
--- /dev/null
+++ b/src/plugins/qmlprofiler/Label.qml
@@ -0,0 +1,23 @@
+import QtQuick 1.1
+
+Item {
+ property alias text: txt.text
+
+ height: 50
+ width: 150 //### required, or ignored by positioner
+
+ Text {
+ id: txt;
+ x: 5
+ font.pixelSize: 12
+ color: "#232323"
+ anchors.verticalCenter: parent.verticalCenter
+ }
+
+ Rectangle {
+ height: 1
+ width: parent.width
+ color: "#cccccc"
+ anchors.bottom: parent.bottom
+ }
+}
diff --git a/src/plugins/qmlprofiler/MainView.js b/src/plugins/qmlprofiler/MainView.js
new file mode 100644
index 00000000000..fe5df33009e
--- /dev/null
+++ b/src/plugins/qmlprofiler/MainView.js
@@ -0,0 +1,110 @@
+.pragma library
+
+var values = [ ]; //events
+var ranges = [ ];
+var frameFps = [ ];
+var valuesdone = false;
+var xmargin = 0;
+var ymargin = 0;
+
+var names = [ "Painting", "Compiling", "Creating", "Binding", "Handling Signal"]
+//### need better way to manipulate color from QML. In the meantime, these need to be kept in sync.
+var colors = [ "#99CCB3", "#99CCCC", "#99B3CC", "#9999CC", "#CC99B3", "#CC99CC", "#CCCC99", "#CCB399" ];
+var origColors = [ "#99CCB3", "#99CCCC", "#99B3CC", "#9999CC", "#CC99B3", "#CC99CC", "#CCCC99", "#CCB399" ];
+var xRayColors = [ "#6699CCB3", "#6699CCCC", "#6699B3CC", "#669999CC", "#66CC99B3", "#66CC99CC", "#66CCCC99", "#66CCB399" ];
+
+function reset()
+{
+ values = [];
+ ranges = [];
+ frameFps = [];
+ xmargin = 0;
+ ymargin = 0;
+ valuesdone = false;
+}
+
+function calcFps()
+{
+ if (values.length)
+ frameFps = new Array(values.length - 1);
+ for (var i = 0; i < values.length - 1; ++i) {
+ var frameTime = (values[i + 1] - values[i]) / 1000000;
+ frameFps[i] = 1000 / frameTime;
+ }
+}
+
+//draw background of the graph
+function drawGraph(canvas, ctxt, region)
+{
+ var grad = ctxt.createLinearGradient(0, 0, 0, canvas.canvasSize.height);
+ grad.addColorStop(0, '#fff');
+ grad.addColorStop(1, '#ccc');
+ ctxt.fillStyle = grad;
+
+ ctxt.fillRect(0, 0, canvas.canvasSize.width + xmargin, canvas.canvasSize.height - ymargin);
+}
+
+//draw the actual data to be graphed
+function drawData(canvas, ctxt, region)
+{
+ if (values.length == 0 && ranges.length == 0)
+ return;
+
+ var width = canvas.canvasSize.width - xmargin;
+ var height = canvas.height - ymargin;
+
+ var sumValue = ranges[ranges.length - 1].start + ranges[ranges.length - 1].duration - ranges[0].start;
+ var spacing = width / sumValue;
+
+ //### only draw those in range
+ for (var ii = 0; ii < ranges.length; ++ii) {
+
+ var xx = (ranges[ii].start - ranges[0].start) * spacing + xmargin;
+ if (xx > region.x + region.width)
+ continue;
+
+ var size = ranges[ii].duration * spacing;
+ if (xx + size < region.x)
+ continue;
+
+ if (size < 0.5)
+ size = 0.5;
+
+ ctxt.fillStyle = "rgba(0,0,0,1)" //colors[ranges[ii].type];
+ ctxt.fillRect(xx, ranges[ii].type*10, size, 10);
+ }
+
+ //draw fps overlay
+ var heightScale = height / 60;
+ ctxt.beginPath();
+ ctxt.moveTo(0,0);
+ for (var i = 1; i < values.length; ++i) {
+ var xx = (values[i] - ranges[0].start) * spacing + xmargin;
+ ctxt.lineTo(xx, height - frameFps[i-1]*heightScale)
+ }
+ ctxt.lineTo(width, 0);
+ ctxt.closePath();
+ var grad = ctxt.createLinearGradient(0, 0, 0, canvas.canvasSize.height);
+ grad.addColorStop(0, "rgba(255,128,128,.5)");
+ grad.addColorStop(1, "rgba(255,0,0,.5)");
+ ctxt.fillStyle = grad;
+ ctxt.fill();
+}
+
+function plot(canvas, ctxt, region)
+{
+ drawGraph(canvas, ctxt, region);
+ drawData(canvas, ctxt, region);
+}
+
+function xScale(canvas)
+{
+ if (values.length === 0 && ranges.length === 0)
+ return;
+
+ var width = canvas.canvasSize.width - xmargin;
+
+ var sumValue = ranges[ranges.length - 1].start + ranges[ranges.length - 1].duration - ranges[0].start;
+ var spacing = sumValue / width;
+ return spacing;
+}
diff --git a/src/plugins/qmlprofiler/MainView.qml b/src/plugins/qmlprofiler/MainView.qml
new file mode 100644
index 00000000000..c8fb2620550
--- /dev/null
+++ b/src/plugins/qmlprofiler/MainView.qml
@@ -0,0 +1,262 @@
+import QtQuick 1.1
+import Monitor 1.0
+import "MainView.js" as Plotter
+
+Rectangle {
+ id: root
+
+ property variant colors: Plotter.colors //the colors used for the timeline data
+ property bool xRay: false //useful for seeing "nested" ranges (but redraw is buggy -- QGV problem?)
+ property Item currentItem //currently selected item in the view
+
+ property bool zooming:false
+
+ // move the cursor in the editor
+ signal updateCursorPosition
+ property string fileName: ""
+ property int lineNumber: -1
+ function gotoSourceLocation(file,line) {
+ root.fileName = file;
+ root.lineNumber = line;
+ root.updateCursorPosition();
+ }
+
+ //handle debug data coming from C++
+ Connections {
+ target: connection
+ onEvent: {
+ if (Plotter.valuesdone) {
+ Plotter.reset();
+ view.clearData();
+ rangeMover.x = 2
+ rangeMover.opacity = 0
+ }
+
+ if (!Plotter.valuesdone && event === 0) //### only handle paint event
+ Plotter.values.push(time);
+ }
+
+ onRange: {
+ if (Plotter.valuesdone) {
+ Plotter.reset();
+ view.clearData();
+ rangeMover.x = 2
+ rangeMover.opacity = 0
+ }
+
+ if (!Plotter.valuesdone)
+ Plotter.ranges.push( { type: type, start: startTime, duration: length, label: data, fileName: fileName, line: line } );
+ }
+
+ onComplete: {
+ Plotter.valuesdone = true;
+ Plotter.calcFps();
+ view.setRanges(Plotter.ranges);
+ view.updateTimeline();
+ canvas.requestPaint();
+ rangeMover.x = 1 //### hack to get view to display things immediately
+ rangeMover.opacity = 1
+ }
+
+ }
+
+ //timeline background
+ Item {
+ anchors.fill: flick
+ Column {
+ anchors.fill: parent
+
+ Repeater {
+ model: 5 //### values.length?
+ delegate: Rectangle {
+ width: parent.width
+ height: 50 //###
+ color: index % 2 ? "#fafafa" : "white"
+ }
+ }
+ }
+ }
+
+ //our main interaction view
+ Flickable {
+ id: flick
+ anchors.top: parent.top
+ anchors.right: parent.right
+ anchors.left: labels.right
+ anchors.bottom: canvas.top
+ contentWidth: view.totalWidth
+ contentHeight: view.height
+
+ TimelineView {
+ id: view
+
+ width: flick.width; height: flick.height
+
+ startX: flick.contentX
+ onStartXChanged: {
+ var newX = startTime / Plotter.xScale(canvas) - canvas.canvasWindow.x;
+ if (Math.abs(rangeMover.x - newX) > .01)
+ rangeMover.x = newX
+ if (Math.abs(startX - flick.contentX) > .01)
+ flick.contentX = startX
+ }
+ startTime: rangeMover.value
+
+ property real prevXStep: -1
+ property real possibleEndTime: startTime + (rangeMover.width*Plotter.xScale(canvas))
+ onPossibleEndTimeChanged: {
+ var set = ((zooming && prevXStep != canvas.canvasWindow.x) || !zooming);
+ prevXStep = canvas.canvasWindow.x;
+ if (set)
+ endTime = possibleEndTime
+ }
+ onEndTimeChanged: updateTimeline()
+
+ delegate: Rectangle {
+ id: obj
+
+ property color baseColor: colors[type]
+ property color myColor: baseColor
+
+ function conditionalHide() {
+ if (!mouseArea.containsMouse)
+ mouseArea.exited()
+ }
+
+ height: 50
+ gradient: Gradient {
+ GradientStop { position: 0.0; color: myColor }
+ GradientStop { position: 0.5; color: Qt.darker(myColor, 1.1) }
+ GradientStop { position: 1.0; color: myColor }
+ }
+ smooth: true
+ MouseArea {
+ id: mouseArea
+ anchors.fill: parent
+ hoverEnabled: true
+ onEntered: {
+ currentItem = obj
+ myColor = Qt.darker(baseColor, 1.2)
+ rangeDetails.duration = duration
+ rangeDetails.label = label
+ rangeDetails.file = fileName
+ rangeDetails.line = line
+ rangeDetails.type = Plotter.names[type]
+
+ var pos = mapToItem(rangeDetails.parent, mouseX, y+height)
+ var preferredX = Math.max(10, pos.x - rangeDetails.width/2)
+ if (preferredX + rangeDetails.width > rangeDetails.parent.width)
+ preferredX = rangeDetails.parent.width - rangeDetails.width
+ rangeDetails.x = preferredX
+
+ rangeDetails.y = pos.y + 10
+ rangeDetails.visible = true
+ }
+ onExited: {
+ myColor = baseColor
+ rangeDetails.visible = false
+ rangeDetails.duration = ""
+ rangeDetails.label = ""
+ rangeDetails.type = ""
+ rangeDetails.file = ""
+ rangeDetails.line = -1
+ }
+ onClicked: root.gotoSourceLocation(rangeDetails.file, rangeDetails.line);
+ }
+ }
+ }
+ }
+
+ //popup showing the details for the hovered range
+ RangeDetails {
+ id: rangeDetails
+ }
+
+ Rectangle {
+ id: labels
+ width: 150
+ color: "#dcdcdc"
+ anchors.top: root.top
+ anchors.bottom: canvas.top
+
+ Column {
+ //### change to use Repeater + Plotter.names?
+ Label { text: "Painting" }
+ Label { text: "Compiling" }
+ Label { text: "Creating" }
+ Label { text: "Binding" }
+ Label { text: "Signal Handler" }
+ }
+
+ //right border divider
+ Rectangle {
+ width: 1
+ height: parent.height
+ anchors.right: parent.right
+ color: "#cccccc"
+ }
+ }
+
+ //bottom border divider
+ Rectangle {
+ height: 1
+ width: parent.width
+ anchors.bottom: canvas.top
+ color: "#cccccc"
+ }
+
+ //"overview" graph at the bottom
+ TiledCanvas {
+ id: canvas
+
+ anchors.bottom: parent.bottom
+ width: parent.width; height: 50
+
+ property int canvasWidth: width
+
+ canvasSize {
+ width: canvasWidth
+ height: canvas.height
+ }
+
+ tileSize.width: width
+ tileSize.height: height
+
+ canvasWindow.width: width
+ canvasWindow.height: 50
+
+ onDrawRegion: {
+ if (Plotter.valuesdone)
+ Plotter.plot(canvas, ctxt, region);
+ else
+ Plotter.drawGraph(canvas, ctxt, region) //just draw the background
+ }
+ }
+
+ //moves the range mover to the position of a click
+ MouseArea {
+ anchors.fill: canvas
+ //### ideally we could press to position then immediately drag
+ onPressed: rangeMover.x = mouse.x - rangeMover.width/2
+ }
+
+ RangeMover {
+ id: rangeMover
+ opacity: 0
+ anchors.top: canvas.top
+ }
+
+ Rectangle {
+ width: 50
+ height: 30
+ anchors.right: root.right
+ anchors.top: root.top
+ radius: 4
+ color: "#606085"
+ Elapsed {
+ anchors.centerIn: parent
+ horizontalAlignment: Text.AlignHCenter
+ }
+ }
+
+}
diff --git a/src/plugins/qmlprofiler/QmlProfiler.pluginspec.in b/src/plugins/qmlprofiler/QmlProfiler.pluginspec.in
new file mode 100644
index 00000000000..6bdcd694e72
--- /dev/null
+++ b/src/plugins/qmlprofiler/QmlProfiler.pluginspec.in
@@ -0,0 +1,20 @@
+
+ Nokia Corporation
+ (C) 2011 Nokia Corporation
+
+ Commercial Usage
+
+ Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt Commercial License Agreement provided with the Software or, alternatively, in accordance with the terms contained in a written agreement between you and Nokia.
+
+ GNU Lesser General Public License Usage
+
+ Alternatively, this plugin may be used under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation. 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.
+
+ Qml Profiler Plugin
+ http://qt.nokia.com
+
+
+
+
+
+
diff --git a/src/plugins/qmlprofiler/RangeDetails.qml b/src/plugins/qmlprofiler/RangeDetails.qml
new file mode 100644
index 00000000000..c5d5fd0ec35
--- /dev/null
+++ b/src/plugins/qmlprofiler/RangeDetails.qml
@@ -0,0 +1,61 @@
+import QtQuick 1.1
+import Monitor 1.0
+import "MainView.js" as Plotter
+
+BorderImage {
+ id: rangeDetails
+
+ property string duration //###int?
+ property string label
+ property string type
+ property string file
+ property int line
+
+ source: "popup.png"
+ border {
+ left: 10; top: 10
+ right: 20; bottom: 20
+ }
+
+ width: col.width + 45
+ height: childrenRect.height + 30
+ z: 1
+ visible: false
+
+ //title
+ Text {
+ id: typeTitle
+ text: rangeDetails.type
+ font.bold: true
+ y: 10
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.horizontalCenterOffset: -5
+ }
+
+ //details
+ Column {
+ id: col
+ anchors.top: typeTitle.bottom
+ Detail {
+ label: "Duration"
+ content: rangeDetails.duration + "μs"
+ }
+ Detail {
+ opacity: content.length !== 0 ? 1 : 0
+ label: "Details"
+ content: rangeDetails.label
+ }
+ Detail {
+ opacity: content.length !== 0 ? 1 : 0
+ label: "Location"
+ content: {
+ var file = rangeDetails.file
+ var pos = file.lastIndexOf("/")
+ if (pos != -1)
+ file = file.substr(pos+1)
+ return (file.length !== 0) ? (file + ":" + rangeDetails.line) : "";
+ }
+ onLinkActivated: Qt.openUrlExternally(url)
+ }
+ }
+}
diff --git a/src/plugins/qmlprofiler/RangeMover.qml b/src/plugins/qmlprofiler/RangeMover.qml
new file mode 100644
index 00000000000..1247072bb55
--- /dev/null
+++ b/src/plugins/qmlprofiler/RangeMover.qml
@@ -0,0 +1,62 @@
+import "MainView.js" as Plotter
+import QtQuick 1.1
+import Monitor 1.0
+
+Item {
+ id: rangeMover
+ width: rect.width; height: 50
+
+ property real prevXStep: -1
+ property real possibleValue: (canvas.canvasWindow.x + x) * Plotter.xScale(canvas)
+ onPossibleValueChanged: {
+ var set = (!zooming || (zooming && prevXStep != canvas.canvasWindow.x))
+ prevXStep = canvas.canvasWindow.x;
+ if (set)
+ value = possibleValue
+ }
+
+ property real value
+ //onValueChanged: console.log("*************** " + value)
+
+ /*Image {
+ id: leftRange
+ source: "range.png"
+ anchors.horizontalCenter: parent.left
+ anchors.bottom: parent.bottom
+ }*/
+
+ Rectangle {
+ id: rect
+
+ color: "#cc80b2f6"
+ width: 20
+ //anchors.left: parent.left
+ //anchors.right: rightRange.horizontalCenter
+
+ height: parent.height
+ }
+
+ /*Image {
+ id: rightRange
+ source: "range.png"
+ x: 13
+ anchors.bottom: parent.bottom
+
+ MouseArea {
+ width: parent.width
+ height: 15
+ drag.target: rightRange
+ drag.axis: "XAxis"
+ drag.minimumX: -7 //###
+ drag.maximumX: canvas.width - rangeMover.width //###
+ }
+ }*/
+
+ MouseArea {
+ anchors.fill: parent
+ drag.target: rangeMover
+ drag.axis: "XAxis"
+ drag.minimumX: 0
+ drag.maximumX: canvas.width - rangeMover.width //###
+ }
+}
diff --git a/src/plugins/qmlprofiler/RecordButton.qml b/src/plugins/qmlprofiler/RecordButton.qml
new file mode 100644
index 00000000000..29b764a22be
--- /dev/null
+++ b/src/plugins/qmlprofiler/RecordButton.qml
@@ -0,0 +1,53 @@
+import QtQuick 1.1
+
+Rectangle {
+ id: button
+
+ property alias text: textItem.text
+ property bool recording: false
+
+ signal clicked
+
+ width: 80; height: textItem.height + 8
+ border.width: 1
+ border.color: Qt.darker(button.color, 1.4)
+ radius: height/2
+ smooth: true
+
+ color: "#049e0e"
+
+ Text {
+ id: textItem
+ anchors.centerIn: parent
+ font.pixelSize: 14
+ color: "white"
+ text: "Recording"
+ }
+
+ MouseArea {
+ id: mouseArea
+ anchors.fill: parent
+ onClicked: {
+ recording = !recording;
+ button.clicked()
+ }
+ }
+
+ StateGroup {
+ id: recordState
+ states: State {
+ name: "recording"
+ when: recording
+ PropertyChanges { target: button; color: "#ff120c"; }
+ PropertyChanges { target: textItem; text: "Stop" }
+ }
+ }
+
+ StateGroup {
+ states: State {
+ name: "pressed"; when: mouseArea.pressed && mouseArea.containsMouse
+ PropertyChanges { target: button; color: Qt.darker(color, 1.1); explicit: true }
+ PropertyChanges { target: textItem; x: textItem.x + 1; y: textItem.y + 1; explicit: true }
+ }
+ }
+}
diff --git a/src/plugins/qmlprofiler/ToolButton.qml b/src/plugins/qmlprofiler/ToolButton.qml
new file mode 100644
index 00000000000..b2461684d8f
--- /dev/null
+++ b/src/plugins/qmlprofiler/ToolButton.qml
@@ -0,0 +1,27 @@
+import QtQuick 1.1
+import Monitor 1.0
+import "MainView.js" as Plotter
+
+Rectangle {
+ property string label
+ signal clicked
+
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.verticalCenterOffset: -1
+ width: 30; height: 22
+
+ border.color: "#cc80b2f6"
+ color: "transparent"
+
+ Text {
+ anchors.centerIn: parent
+ text: label
+ color: "white"
+ font.pixelSize: 14
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: parent.clicked()
+ }
+}
diff --git a/src/plugins/qmlprofiler/canvas/canvas.pri b/src/plugins/qmlprofiler/canvas/canvas.pri
new file mode 100644
index 00000000000..2b37e8ee5d8
--- /dev/null
+++ b/src/plugins/qmlprofiler/canvas/canvas.pri
@@ -0,0 +1,11 @@
+INCLUDEPATH += $$PWD
+
+HEADERS += $$PWD/qdeclarativecontext2d_p.h \
+ $$PWD/qdeclarativecanvas_p.h \
+ $$PWD/qdeclarativetiledcanvas_p.h \
+ $$PWD/qdeclarativecanvastimer_p.h
+
+SOURCES += $$PWD/qdeclarativecontext2d.cpp \
+ $$PWD/qdeclarativecanvas.cpp \
+ $$PWD/qdeclarativetiledcanvas.cpp \
+ $$PWD/qdeclarativecanvastimer.cpp
diff --git a/src/plugins/qmlprofiler/canvas/qdeclarativecanvas.cpp b/src/plugins/qmlprofiler/canvas/qdeclarativecanvas.cpp
new file mode 100644
index 00000000000..0f7c7ae501a
--- /dev/null
+++ b/src/plugins/qmlprofiler/canvas/qdeclarativecanvas.cpp
@@ -0,0 +1,257 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative 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 "qdeclarativecanvas_p.h"
+#include "qdeclarativecanvastimer_p.h"
+#include "qdeclarativecontext2d_p.h"
+
+#include
+
+QT_BEGIN_NAMESPACE
+
+Canvas::Canvas(QDeclarativeItem *parent)
+ : QDeclarativeItem(parent),
+ m_context(new Context2D(this)),
+ m_canvasWidth(0),
+ m_canvasHeight(0),
+ m_fillMode(Canvas::Stretch),
+ m_color(Qt::white)
+{
+ setFlag(QGraphicsItem::ItemHasNoContents, false);
+ setCacheMode(QGraphicsItem::DeviceCoordinateCache);
+}
+
+
+void Canvas::componentComplete()
+{
+ if (m_canvasWidth == 0 && m_canvasHeight == 0)
+ m_context->setSize(width(), height());
+ else
+ m_context->setSize(m_canvasWidth, m_canvasHeight);
+
+ connect(m_context, SIGNAL(changed()), this, SLOT(requestPaint()));
+ emit init();
+ QDeclarativeItem::componentComplete();
+}
+
+void Canvas::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
+{
+ m_context->setInPaint(true);
+ emit paint();
+
+ bool oldAA = painter->testRenderHint(QPainter::Antialiasing);
+ bool oldSmooth = painter->testRenderHint(QPainter::SmoothPixmapTransform);
+ if (smooth())
+ painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform, smooth());
+
+ if (m_context->pixmap().isNull()) {
+ painter->fillRect(0, 0, width(), height(), m_color);
+ } else if (width() != m_context->pixmap().width() || height() != m_context->pixmap().height()) {
+ if (m_fillMode>= Tile) {
+ if (m_fillMode== Tile) {
+ painter->drawTiledPixmap(QRectF(0,0,width(),height()), m_context->pixmap());
+ } else {
+ qreal widthScale = width() / qreal(m_context->pixmap().width());
+ qreal heightScale = height() / qreal(m_context->pixmap().height());
+
+ QTransform scale;
+ if (m_fillMode== TileVertically) {
+ scale.scale(widthScale, 1.0);
+ QTransform old = painter->transform();
+ painter->setWorldTransform(scale * old);
+ painter->drawTiledPixmap(QRectF(0,0,m_context->pixmap().width(),height()), m_context->pixmap());
+ painter->setWorldTransform(old);
+ } else {
+ scale.scale(1.0, heightScale);
+ QTransform old = painter->transform();
+ painter->setWorldTransform(scale * old);
+ painter->drawTiledPixmap(QRectF(0,0,width(),m_context->pixmap().height()), m_context->pixmap());
+ painter->setWorldTransform(old);
+ }
+ }
+ } else {
+ qreal widthScale = width() / qreal(m_context->pixmap().width());
+ qreal heightScale = height() / qreal(m_context->pixmap().height());
+
+ QTransform scale;
+
+ if (m_fillMode== PreserveAspectFit) {
+ if (widthScale <= heightScale) {
+ heightScale = widthScale;
+ scale.translate(0, (height() - heightScale * m_context->pixmap().height()) / 2);
+ } else if (heightScale < widthScale) {
+ widthScale = heightScale;
+ scale.translate((width() - widthScale * m_context->pixmap().width()) / 2, 0);
+ }
+ } else if (m_fillMode== PreserveAspectCrop) {
+ if (widthScale < heightScale) {
+ widthScale = heightScale;
+ scale.translate((width() - widthScale * m_context->pixmap().width()) / 2, 0);
+ } else if (heightScale < widthScale) {
+ heightScale = widthScale;
+ scale.translate(0, (height() - heightScale * m_context->pixmap().height()) / 2);
+ }
+ }
+ if (clip()) {
+ painter->save();
+ painter->setClipRect(boundingRect(), Qt::IntersectClip);
+ }
+ scale.scale(widthScale, heightScale);
+ QTransform old = painter->transform();
+ painter->setWorldTransform(scale * old);
+ painter->drawPixmap(0, 0, m_context->pixmap());
+ painter->setWorldTransform(old);
+ if (clip()) {
+ painter->restore();
+ }
+ }
+ } else {
+ painter->drawPixmap(0, 0, m_context->pixmap());
+ }
+
+ if (smooth()) {
+ painter->setRenderHint(QPainter::Antialiasing, oldAA);
+ painter->setRenderHint(QPainter::SmoothPixmapTransform, oldSmooth);
+ }
+ m_context->setInPaint(false);
+}
+
+Context2D *Canvas::getContext(const QString &contextId)
+{
+ if (contextId == QLatin1String("2d"))
+ return m_context;
+ qDebug("Canvas:requesting unsupported context");
+ return 0;
+}
+
+void Canvas::requestPaint()
+{
+ update();
+}
+
+void Canvas::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
+{
+ if (m_canvasWidth == 0 && m_canvasHeight == 0
+ && newGeometry.width() > 0 && newGeometry.height() > 0) {
+ m_context->setSize(width(), height());
+ }
+ QDeclarativeItem::geometryChanged(newGeometry, oldGeometry);
+}
+
+void Canvas::setCanvasWidth(int newWidth)
+{
+ if (m_canvasWidth != newWidth) {
+ m_canvasWidth = newWidth;
+ m_context->setSize(m_canvasWidth, m_canvasHeight);
+ emit canvasWidthChanged();
+ }
+}
+
+void Canvas::setCanvasHeight(int newHeight)
+{
+ if (m_canvasHeight != newHeight) {
+ m_canvasHeight = newHeight;
+ m_context->setSize(m_canvasWidth, m_canvasHeight);
+ emit canvasHeightChanged();
+ }
+}
+
+void Canvas::setFillMode(FillMode mode)
+{
+ if (m_fillMode == mode)
+ return;
+
+ m_fillMode = mode;
+ update();
+ emit fillModeChanged();
+}
+
+QColor Canvas::color()
+{
+ return m_color;
+}
+
+void Canvas::setColor(const QColor &color)
+{
+ if (m_color !=color) {
+ m_color = color;
+ colorChanged();
+ }
+}
+
+Canvas::FillMode Canvas::fillMode() const
+{
+ return m_fillMode;
+}
+
+bool Canvas::save(const QString &filename) const
+{
+ return m_context->pixmap().save(filename);
+}
+
+CanvasImage *Canvas::toImage() const
+{
+ return new CanvasImage(m_context->pixmap());
+}
+
+void Canvas::setTimeout(const QScriptValue &handler, long timeout)
+{
+ if (handler.isFunction())
+ CanvasTimer::createTimer(this, handler, timeout, true);
+}
+
+void Canvas::setInterval(const QScriptValue &handler, long interval)
+{
+ if (handler.isFunction())
+ CanvasTimer::createTimer(this, handler, interval, false);
+}
+
+void Canvas::clearTimeout(const QScriptValue &handler)
+{
+ CanvasTimer::removeTimer(handler);
+}
+
+void Canvas::clearInterval(const QScriptValue &handler)
+{
+ CanvasTimer::removeTimer(handler);
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/qmlprofiler/canvas/qdeclarativecanvas_p.h b/src/plugins/qmlprofiler/canvas/qdeclarativecanvas_p.h
new file mode 100644
index 00000000000..72512258e54
--- /dev/null
+++ b/src/plugins/qmlprofiler/canvas/qdeclarativecanvas_p.h
@@ -0,0 +1,124 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative 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 QDECLARATIVECANVAS_P_H
+#define QDECLARATIVECANVAS_P_H
+
+#include
+
+#include "qdeclarativecontext2d_p.h"
+#include "qdeclarativecanvastimer_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+class Canvas : public QDeclarativeItem
+{
+ Q_OBJECT
+
+ Q_ENUMS(FillMode)
+ Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged);
+ Q_PROPERTY(int canvasWidth READ canvasWidth WRITE setCanvasWidth NOTIFY canvasWidthChanged);
+ Q_PROPERTY(int canvasHeight READ canvasHeight WRITE setCanvasHeight NOTIFY canvasHeightChanged);
+ Q_PROPERTY(FillMode fillMode READ fillMode WRITE setFillMode NOTIFY fillModeChanged)
+
+public:
+ Canvas(QDeclarativeItem *parent = 0);
+ enum FillMode { Stretch, PreserveAspectFit, PreserveAspectCrop, Tile, TileVertically, TileHorizontally };
+
+
+ void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *);
+ void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry);
+ void setCanvasWidth(int newWidth);
+ int canvasWidth() {return m_canvasWidth;}
+
+ void setCanvasHeight(int canvasHeight);
+ int canvasHeight() {return m_canvasHeight;}
+
+ void componentComplete();
+
+
+public Q_SLOTS:
+ Context2D *getContext(const QString & = QString("2d"));
+ void requestPaint();
+
+ FillMode fillMode() const;
+ void setFillMode(FillMode);
+
+ QColor color();
+ void setColor(const QColor &);
+
+ // Save current canvas to disk
+ bool save(const QString& filename) const;
+
+ // Timers
+ void setInterval(const QScriptValue &handler, long timeout);
+ void setTimeout(const QScriptValue &handler, long timeout);
+ void clearInterval(const QScriptValue &handler);
+ void clearTimeout(const QScriptValue &handler);
+
+Q_SIGNALS:
+ void fillModeChanged();
+ void canvasWidthChanged();
+ void canvasHeightChanged();
+ void colorChanged();
+ void init();
+ void paint();
+
+private:
+ // Return canvas contents as a drawable image
+ CanvasImage *toImage() const;
+ Context2D *m_context;
+ int m_canvasWidth;
+ int m_canvasHeight;
+ FillMode m_fillMode;
+ QColor m_color;
+
+ friend class Context2D;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif //QDECLARATIVECANVAS_P_H
diff --git a/src/plugins/qmlprofiler/canvas/qdeclarativecanvastimer.cpp b/src/plugins/qmlprofiler/canvas/qdeclarativecanvastimer.cpp
new file mode 100644
index 00000000000..64e6be08d58
--- /dev/null
+++ b/src/plugins/qmlprofiler/canvas/qdeclarativecanvastimer.cpp
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative 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
+#include
+#include
+
+#include "qdeclarativecanvastimer_p.h"
+
+QT_BEGIN_NAMESPACE
+
+Q_GLOBAL_STATIC(QList , activeTimers);
+
+CanvasTimer::CanvasTimer(QObject *parent, const QScriptValue &data)
+ : QTimer(parent), m_value(data)
+{
+}
+
+void CanvasTimer::handleTimeout()
+{
+ Q_ASSERT(m_value.isFunction());
+ m_value.call();
+ if (isSingleShot()) {
+ removeTimer(this);
+ }
+}
+
+void CanvasTimer::createTimer(QObject *parent, const QScriptValue &val, long timeout, bool singleshot)
+{
+
+ CanvasTimer *timer = new CanvasTimer(parent, val);
+ timer->setInterval(timeout);
+ timer->setSingleShot(singleshot);
+ connect(timer, SIGNAL(timeout()), timer, SLOT(handleTimeout()));
+ activeTimers()->append(timer);
+ timer->start();
+}
+
+void CanvasTimer::removeTimer(CanvasTimer *timer)
+{
+ activeTimers()->removeAll(timer);
+ timer->deleteLater();
+}
+
+void CanvasTimer::removeTimer(const QScriptValue &val)
+{
+ if (!val.isFunction())
+ return;
+
+ for (int i = 0 ; i < activeTimers()->count() ; ++i) {
+ CanvasTimer *timer = activeTimers()->at(i);
+ if (timer->equals(val)) {
+ removeTimer(timer);
+ return;
+ }
+ }
+}
+
+QT_END_NAMESPACE
+
diff --git a/src/plugins/qmlprofiler/canvas/qdeclarativecanvastimer_p.h b/src/plugins/qmlprofiler/canvas/qdeclarativecanvastimer_p.h
new file mode 100644
index 00000000000..3e55d0260ac
--- /dev/null
+++ b/src/plugins/qmlprofiler/canvas/qdeclarativecanvastimer_p.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative 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 QDECLARATIVECANVASTIMER_P_H
+#define QDECLARATIVECANVASTIMER_P_H
+
+#include
+#include
+#include
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class CanvasTimer : public QTimer
+{
+ Q_OBJECT
+
+public:
+ CanvasTimer(QObject *parent, const QScriptValue &data);
+
+public Q_SLOTS:
+ void handleTimeout();
+ bool equals(const QScriptValue &value){return m_value.equals(value);}
+
+public:
+ static void createTimer(QObject *parent, const QScriptValue &val, long timeout, bool singleshot);
+ static void removeTimer(CanvasTimer *timer);
+ static void removeTimer(const QScriptValue &);
+
+private:
+ QScriptValue m_value;
+
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QDECLARATIVECANVASTIMER_P_H
diff --git a/src/plugins/qmlprofiler/canvas/qdeclarativecontext2d.cpp b/src/plugins/qmlprofiler/canvas/qdeclarativecontext2d.cpp
new file mode 100644
index 00000000000..a98b707dbe8
--- /dev/null
+++ b/src/plugins/qmlprofiler/canvas/qdeclarativecontext2d.cpp
@@ -0,0 +1,1139 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative 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 "qdeclarativecontext2d_p.h"
+
+#include "qdeclarativecanvas_p.h"
+
+#include
+#include
+
+#include
+#include
+#include
+
+#include
+
+QT_BEGIN_NAMESPACE
+
+static const double Q_PI = 3.14159265358979323846; // pi
+
+class CustomDropShadowEffect : public QGraphicsDropShadowEffect
+{
+public:
+ void draw(QPainter *painter) { QGraphicsDropShadowEffect::draw(painter);}
+ void drawSource(QPainter *painter) { QGraphicsDropShadowEffect::drawSource(painter);}
+};
+
+// Note, this is exported but in a private header as qtopengl depends on it.
+// But it really should be considered private API
+void qt_blurImage(QPainter *p, QImage &blurImage, qreal radius, bool quality, bool alphaOnly, int transposed = 0);
+void qt_blurImage(QImage &blurImage, qreal radius, bool quality, int transposed = 0);
+
+#define DEGREES(t) ((t) * 180.0 / Q_PI)
+
+#define qClamp(val, min, max) qMin(qMax(val, min), max)
+static QList parseNumbersList(QString::const_iterator &itr)
+{
+ QList points;
+ QString temp;
+ while ((*itr).isSpace())
+ ++itr;
+ while ((*itr).isNumber() ||
+ (*itr) == '-' || (*itr) == '+' || (*itr) == '.') {
+ temp = QString();
+
+ if ((*itr) == '-')
+ temp += *itr++;
+ else if ((*itr) == '+')
+ temp += *itr++;
+ while ((*itr).isDigit())
+ temp += *itr++;
+ if ((*itr) == '.')
+ temp += *itr++;
+ while ((*itr).isDigit())
+ temp += *itr++;
+ while ((*itr).isSpace())
+ ++itr;
+ if ((*itr) == ',')
+ ++itr;
+ points.append(temp.toDouble());
+ //eat spaces
+ while ((*itr).isSpace())
+ ++itr;
+ }
+
+ return points;
+}
+
+QColor colorFromString(const QString &name)
+{
+ QString::const_iterator itr = name.constBegin();
+ QList compo;
+ if (name.startsWith("rgba(")) {
+ ++itr; ++itr; ++itr; ++itr; ++itr;
+ compo = parseNumbersList(itr);
+ if (compo.size() != 4) {
+ return QColor();
+ }
+ //alpha seems to be always between 0-1
+ compo[3] *= 255;
+ return QColor((int)compo[0], (int)compo[1],
+ (int)compo[2], (int)compo[3]);
+ } else if (name.startsWith("rgb(")) {
+ ++itr; ++itr; ++itr; ++itr;
+ compo = parseNumbersList(itr);
+ if (compo.size() != 3) {
+ return QColor();
+ }
+ return QColor((int)qClamp(compo[0], qreal(0), qreal(255)),
+ (int)qClamp(compo[1], qreal(0), qreal(255)),
+ (int)qClamp(compo[2], qreal(0), qreal(255)));
+ } else if (name.startsWith("hsla(")){
+ ++itr; ++itr; ++itr; ++itr; ++itr;
+ compo = parseNumbersList(itr);
+ if (compo.size() != 4) {
+ return QColor();
+ }
+ return QColor::fromHslF(compo[0], compo[1],
+ compo[2], compo[3]);
+ } else if (name.startsWith("hsl(")){
+ ++itr; ++itr; ++itr; ++itr; ++itr;
+ compo = parseNumbersList(itr);
+ if (compo.size() != 3) {
+ return QColor();
+ }
+ return QColor::fromHslF(compo[0], compo[1],
+ compo[2]);
+ } else {
+ //QRgb color;
+ //CSSParser::parseColor(name, color);
+ return QColor(name);
+ }
+}
+
+
+static QPainter::CompositionMode compositeOperatorFromString(const QString &compositeOperator)
+{
+ if (compositeOperator == QLatin1String("source-over")) {
+ return QPainter::CompositionMode_SourceOver;
+ } else if (compositeOperator == QLatin1String("source-out")) {
+ return QPainter::CompositionMode_SourceOut;
+ } else if (compositeOperator == QLatin1String("source-in")) {
+ return QPainter::CompositionMode_SourceIn;
+ } else if (compositeOperator == QLatin1String("source-atop")) {
+ return QPainter::CompositionMode_SourceAtop;
+ } else if (compositeOperator == QLatin1String("destination-atop")) {
+ return QPainter::CompositionMode_DestinationAtop;
+ } else if (compositeOperator == QLatin1String("destination-in")) {
+ return QPainter::CompositionMode_DestinationIn;
+ } else if (compositeOperator == QLatin1String("destination-out")) {
+ return QPainter::CompositionMode_DestinationOut;
+ } else if (compositeOperator == QLatin1String("destination-over")) {
+ return QPainter::CompositionMode_DestinationOver;
+ } else if (compositeOperator == QLatin1String("darker")) {
+ return QPainter::CompositionMode_SourceOver;
+ } else if (compositeOperator == QLatin1String("lighter")) {
+ return QPainter::CompositionMode_SourceOver;
+ } else if (compositeOperator == QLatin1String("copy")) {
+ return QPainter::CompositionMode_Source;
+ } else if (compositeOperator == QLatin1String("xor")) {
+ return QPainter::CompositionMode_Xor;
+ }
+
+ return QPainter::CompositionMode_SourceOver;
+}
+
+static QString compositeOperatorToString(QPainter::CompositionMode op)
+{
+ switch (op) {
+ case QPainter::CompositionMode_SourceOver:
+ return "source-over";
+ case QPainter::CompositionMode_DestinationOver:
+ return "destination-over";
+ case QPainter::CompositionMode_Clear:
+ return "clear";
+ case QPainter::CompositionMode_Source:
+ return "source";
+ case QPainter::CompositionMode_Destination:
+ return "destination";
+ case QPainter::CompositionMode_SourceIn:
+ return "source-in";
+ case QPainter::CompositionMode_DestinationIn:
+ return "destination-in";
+ case QPainter::CompositionMode_SourceOut:
+ return "source-out";
+ case QPainter::CompositionMode_DestinationOut:
+ return "destination-out";
+ case QPainter::CompositionMode_SourceAtop:
+ return "source-atop";
+ case QPainter::CompositionMode_DestinationAtop:
+ return "destination-atop";
+ case QPainter::CompositionMode_Xor:
+ return "xor";
+ case QPainter::CompositionMode_Plus:
+ return "plus";
+ case QPainter::CompositionMode_Multiply:
+ return "multiply";
+ case QPainter::CompositionMode_Screen:
+ return "screen";
+ case QPainter::CompositionMode_Overlay:
+ return "overlay";
+ case QPainter::CompositionMode_Darken:
+ return "darken";
+ case QPainter::CompositionMode_Lighten:
+ return "lighten";
+ case QPainter::CompositionMode_ColorDodge:
+ return "color-dodge";
+ case QPainter::CompositionMode_ColorBurn:
+ return "color-burn";
+ case QPainter::CompositionMode_HardLight:
+ return "hard-light";
+ case QPainter::CompositionMode_SoftLight:
+ return "soft-light";
+ case QPainter::CompositionMode_Difference:
+ return "difference";
+ case QPainter::CompositionMode_Exclusion:
+ return "exclusion";
+ default:
+ break;
+ }
+ return QString();
+}
+
+void Context2D::save()
+{
+ m_stateStack.push(m_state);
+}
+
+
+void Context2D::restore()
+{
+ if (!m_stateStack.isEmpty()) {
+ m_state = m_stateStack.pop();
+ m_state.flags = AllIsFullOfDirt;
+ }
+}
+
+
+void Context2D::scale(qreal x, qreal y)
+{
+ m_state.matrix.scale(x, y);
+ m_state.flags |= DirtyTransformationMatrix;
+}
+
+
+void Context2D::rotate(qreal angle)
+{
+ m_state.matrix.rotate(DEGREES(angle));
+ m_state.flags |= DirtyTransformationMatrix;
+}
+
+
+void Context2D::translate(qreal x, qreal y)
+{
+ m_state.matrix.translate(x, y);
+ m_state.flags |= DirtyTransformationMatrix;
+}
+
+
+void Context2D::transform(qreal m11, qreal m12, qreal m21, qreal m22,
+ qreal dx, qreal dy)
+{
+ QMatrix mat(m11, m12,
+ m21, m22,
+ dx, dy);
+ m_state.matrix *= mat;
+ m_state.flags |= DirtyTransformationMatrix;
+}
+
+
+void Context2D::setTransform(qreal m11, qreal m12, qreal m21, qreal m22,
+ qreal dx, qreal dy)
+{
+ QMatrix mat(m11, m12,
+ m21, m22,
+ dx, dy);
+ m_state.matrix = mat;
+ m_state.flags |= DirtyTransformationMatrix;
+}
+
+
+QString Context2D::globalCompositeOperation() const
+{
+ return compositeOperatorToString(m_state.globalCompositeOperation);
+}
+
+void Context2D::setGlobalCompositeOperation(const QString &op)
+{
+ QPainter::CompositionMode mode =
+ compositeOperatorFromString(op);
+ m_state.globalCompositeOperation = mode;
+ m_state.flags |= DirtyGlobalCompositeOperation;
+}
+
+QVariant Context2D::strokeStyle() const
+{
+ return m_state.strokeStyle;
+}
+
+void Context2D::setStrokeStyle(const QVariant &style)
+{
+ CanvasGradient * gradient= qobject_cast(style.value());
+ if (gradient) {
+ m_state.strokeStyle = gradient->value();
+ } else {
+ QColor color = colorFromString(style.toString());
+ m_state.strokeStyle = color;
+ }
+ m_state.flags |= DirtyStrokeStyle;
+}
+
+QVariant Context2D::fillStyle() const
+{
+ return m_state.fillStyle;
+}
+
+void Context2D::setFillStyle(const QVariant &style)
+{
+ CanvasGradient * gradient= qobject_cast(style.value());
+ if (gradient) {
+ m_state.fillStyle = gradient->value();
+ } else {
+ QColor color = colorFromString(style.toString());
+ m_state.fillStyle = color;
+ }
+ m_state.flags |= DirtyFillStyle;
+}
+
+qreal Context2D::globalAlpha() const
+{
+ return m_state.globalAlpha;
+}
+
+void Context2D::setGlobalAlpha(qreal alpha)
+{
+ m_state.globalAlpha = alpha;
+ m_state.flags |= DirtyGlobalAlpha;
+}
+
+CanvasImage *Context2D::createImage(const QString &url)
+{
+ return new CanvasImage(url);
+}
+
+CanvasGradient *Context2D::createLinearGradient(qreal x0, qreal y0,
+ qreal x1, qreal y1)
+{
+ QLinearGradient g(x0, y0, x1, y1);
+ return new CanvasGradient(g);
+}
+
+
+CanvasGradient *Context2D::createRadialGradient(qreal x0, qreal y0,
+ qreal r0, qreal x1,
+ qreal y1, qreal r1)
+{
+ QRadialGradient g(QPointF(x1, y1), r0+r1, QPointF(x0, y0));
+ return new CanvasGradient(g);
+}
+
+qreal Context2D::lineWidth() const
+{
+ return m_state.lineWidth;
+}
+
+void Context2D::setLineWidth(qreal w)
+{
+ m_state.lineWidth = w;
+ m_state.flags |= DirtyLineWidth;
+}
+
+QString Context2D::lineCap() const
+{
+ switch (m_state.lineCap) {
+ case Qt::FlatCap:
+ return "butt";
+ case Qt::SquareCap:
+ return "square";
+ case Qt::RoundCap:
+ return "round";
+ default: ;
+ }
+ return QString();
+}
+
+void Context2D::setLineCap(const QString &capString)
+{
+ Qt::PenCapStyle style;
+ if (capString == QLatin1String("round"))
+ style = Qt::RoundCap;
+ else if (capString == QLatin1String("square"))
+ style = Qt::SquareCap;
+ else //if (capString == "butt")
+ style = Qt::FlatCap;
+ m_state.lineCap = style;
+ m_state.flags |= DirtyLineCap;
+}
+
+QString Context2D::lineJoin() const
+{
+ switch (m_state.lineJoin) {
+ case Qt::RoundJoin:
+ return QLatin1String("round");
+ case Qt::BevelJoin:
+ return QLatin1String("bevel");
+ case Qt::MiterJoin:
+ return QLatin1String("miter");
+ default: ;
+ }
+ return QString();
+}
+
+void Context2D::setLineJoin(const QString &joinString)
+{
+ Qt::PenJoinStyle style;
+ if (joinString == QLatin1String("round"))
+ style = Qt::RoundJoin;
+ else if (joinString == QLatin1String("bevel"))
+ style = Qt::BevelJoin;
+ else //if (joinString == "miter")
+ style = Qt::MiterJoin;
+ m_state.lineJoin = style;
+ m_state.flags |= DirtyLineJoin;
+}
+
+qreal Context2D::miterLimit() const
+{
+ return m_state.miterLimit;
+}
+
+void Context2D::setMiterLimit(qreal m)
+{
+ m_state.miterLimit = m;
+ m_state.flags |= DirtyMiterLimit;
+}
+
+void Context2D::setShadowOffsetX(qreal x)
+{
+ if (m_state.shadowOffsetX == x)
+ return;
+ m_state.shadowOffsetX = x;
+ updateShadowBuffer();
+ if (m_painter.device() == &m_shadowbuffer && m_state.shadowBlur>0)
+ endPainting();
+ m_state.flags |= DirtyShadowOffsetX;
+}
+
+const QList &Context2D::mouseAreas() const
+{
+ return m_mouseAreas;
+}
+
+void Context2D::updateShadowBuffer() {
+ if (m_shadowbuffer.isNull() || m_shadowbuffer.width() != m_width+m_state.shadowOffsetX ||
+ m_shadowbuffer.height() != m_height+m_state.shadowOffsetY) {
+ m_shadowbuffer = QImage(m_width+m_state.shadowOffsetX, m_height+m_state.shadowOffsetY, QImage::Format_ARGB32);
+ m_shadowbuffer.fill(Qt::transparent);
+ }
+}
+
+void Context2D::setShadowOffsetY(qreal y)
+{
+ if (m_state.shadowOffsetY == y)
+ return;
+ m_state.shadowOffsetY = y;
+ updateShadowBuffer();
+ if (m_painter.device() == &m_shadowbuffer && m_state.shadowBlur>0)
+ endPainting();
+
+ m_state.flags |= DirtyShadowOffsetY;
+}
+
+void Context2D::setShadowBlur(qreal b)
+{
+ if (m_state.shadowBlur == b)
+ return;
+ m_state.shadowBlur = b;
+ updateShadowBuffer();
+ if (m_painter.device() == &m_shadowbuffer && m_state.shadowBlur>0)
+ endPainting();
+ m_state.flags |= DirtyShadowBlur;
+}
+
+void Context2D::setShadowColor(const QString &str)
+{
+ m_state.shadowColor = colorFromString(str);
+ if (m_painter.device() == &m_shadowbuffer && m_state.shadowBlur>0)
+ endPainting();
+ m_state.flags |= DirtyShadowColor;
+}
+
+QString Context2D::textBaseline()
+{
+ switch (m_state.textBaseline) {
+ case Context2D::Alphabetic:
+ return QLatin1String("alphabetic");
+ case Context2D::Hanging:
+ return QLatin1String("hanging");
+ case Context2D::Bottom:
+ return QLatin1String("bottom");
+ case Context2D::Top:
+ return QLatin1String("top");
+ case Context2D::Middle:
+ return QLatin1String("middle");
+ default:
+ Q_ASSERT("invalid value");
+ return QLatin1String("start");
+ }
+}
+
+void Context2D::setTextBaseline(const QString &baseline)
+{
+ if (baseline==QLatin1String("alphabetic"))
+ m_state.textBaseline = Context2D::Alphabetic;
+ else if (baseline == QLatin1String("hanging"))
+ m_state.textBaseline = Context2D::Hanging;
+ else if (baseline == QLatin1String("top"))
+ m_state.textBaseline = Context2D::Top;
+ else if (baseline == QLatin1String("bottom"))
+ m_state.textBaseline = Context2D::Bottom;
+ else if (baseline == QLatin1String("middle"))
+ m_state.textBaseline = Context2D::Middle;
+ else {
+ m_state.textBaseline = Context2D::Alphabetic;
+ qWarning() << ("Context2D: invalid baseline:" + baseline);
+ }
+ m_state.flags |= DirtyTextBaseline;
+}
+
+QString Context2D::textAlign()
+{
+ switch (m_state.textAlign) {
+ case Context2D::Left:
+ return QLatin1String("left");
+ case Context2D::Right:
+ return QLatin1String("right");
+ case Context2D::Center:
+ return QLatin1String("center");
+ case Context2D::Start:
+ return QLatin1String("start");
+ case Context2D::End:
+ return QLatin1String("end");
+ default:
+ Q_ASSERT("invalid value");
+ qWarning() << ("Context2D::invalid textAlign");
+ return QLatin1String("start");
+ }
+}
+
+void Context2D::setTextAlign(const QString &baseline)
+{
+ if (baseline==QLatin1String("start"))
+ m_state.textAlign = Context2D::Start;
+ else if (baseline == QLatin1String("end"))
+ m_state.textAlign = Context2D::End;
+ else if (baseline == QLatin1String("left"))
+ m_state.textAlign = Context2D::Left;
+ else if (baseline == QLatin1String("right"))
+ m_state.textAlign = Context2D::Right;
+ else if (baseline == QLatin1String("center"))
+ m_state.textAlign = Context2D::Center;
+ else {
+ m_state.textAlign= Context2D::Start;
+ qWarning("Context2D: invalid text align");
+ }
+ // ### alphabetic, ideographic, hanging
+ m_state.flags |= DirtyTextBaseline;
+}
+
+void Context2D::setFont(const QString &fontString)
+{
+ QFont font;
+ // ### this is simplified and incomplete
+ QStringList tokens = fontString.split(" ");
+ foreach (const QString &token, tokens) {
+ if (token == QLatin1String("italic"))
+ font.setItalic(true);
+ else if (token == QLatin1String("bold"))
+ font.setBold(true);
+ else if (token.endsWith(QLatin1String("px"))) {
+ QString number = token;
+ number.remove("px");
+ font.setPointSizeF(number.trimmed().toFloat());
+ } else
+ font.setFamily(token);
+ }
+ m_state.font = font;
+ m_state.flags |= DirtyFont;
+}
+
+QString Context2D::font()
+{
+ return m_state.font.toString();
+}
+
+qreal Context2D::shadowOffsetX() const
+{
+ return m_state.shadowOffsetX;
+}
+
+qreal Context2D::shadowOffsetY() const
+{
+ return m_state.shadowOffsetY;
+}
+
+
+qreal Context2D::shadowBlur() const
+{
+ return m_state.shadowBlur;
+}
+
+
+QString Context2D::shadowColor() const
+{
+ return m_state.shadowColor.name();
+}
+
+
+void Context2D::clearRect(qreal x, qreal y, qreal w, qreal h)
+{
+ beginPainting();
+ m_painter.save();
+ m_painter.setMatrix(worldMatrix(), false);
+ m_painter.setCompositionMode(QPainter::CompositionMode_Source);
+ QColor fillColor = parent()->property("color").value();
+
+ m_painter.fillRect(QRectF(x, y, w, h), fillColor);
+ m_painter.restore();
+ scheduleChange();
+}
+
+void Context2D::fillRect(qreal x, qreal y, qreal w, qreal h)
+{
+ beginPainting();
+ m_painter.save();
+ m_painter.setMatrix(worldMatrix(), false);
+ m_painter.fillRect(QRectF(x, y, w, h), m_painter.brush());
+ m_painter.restore();
+ scheduleChange();
+}
+
+int Context2D::baseLineOffset(Context2D::TextBaseLine value, const QFontMetrics &metrics)
+{
+ int offset = 0;
+ switch (value) {
+ case Context2D::Top:
+ break;
+ case Context2D::Alphabetic:
+ case Context2D::Middle:
+ case Context2D::Hanging:
+ offset = metrics.ascent();
+ break;
+ case Context2D::Bottom:
+ offset = metrics.height();
+ break;
+ }
+ return offset;
+}
+
+int Context2D::textAlignOffset(Context2D::TextAlign value, const QFontMetrics &metrics, const QString &text)
+{
+ int offset = 0;
+ if (value == Context2D::Start)
+ value = qApp->layoutDirection() == Qt::LeftToRight ? Context2D::Left : Context2D::Right;
+ else if (value == Context2D::End)
+ value = qApp->layoutDirection() == Qt::LeftToRight ? Context2D::Right: Context2D::Left;
+ switch (value) {
+ case Context2D::Center:
+ offset = metrics.width(text)/2;
+ break;
+ case Context2D::Right:
+ offset = metrics.width(text);
+ case Context2D::Left:
+ default:
+ break;
+ }
+ return offset;
+}
+
+void Context2D::fillText(const QString &text, qreal x, qreal y)
+{
+ beginPainting();
+ m_painter.save();
+ m_painter.setPen(QPen(m_state.fillStyle, m_state.lineWidth));
+ m_painter.setMatrix(worldMatrix(), false);
+ QFont font;
+ font.setBold(true);
+ m_painter.setFont(m_state.font);
+ int yoffset = baseLineOffset(m_state.textBaseline, m_painter.fontMetrics());
+ int xoffset = textAlignOffset(m_state.textAlign, m_painter.fontMetrics(), text);
+ QTextOption opt; // Adjust baseLine etc
+ m_painter.drawText(QRectF(x-xoffset, y-yoffset, QWIDGETSIZE_MAX, m_painter.fontMetrics().height()), text, opt);
+ m_painter.restore();
+ endPainting();
+ scheduleChange();
+}
+
+void Context2D::strokeText(const QString &text, qreal x, qreal y)
+{
+ beginPainting();
+ m_painter.save();
+ m_painter.setPen(QPen(m_state.fillStyle,0));
+ m_painter.setMatrix(worldMatrix(), false);
+
+ QPainterPath textPath;
+ QFont font = m_state.font;
+ font.setStyleStrategy(QFont::ForceOutline);
+ m_painter.setFont(font);
+ const QFontMetrics &metrics = m_painter.fontMetrics();
+ int yoffset = baseLineOffset(m_state.textBaseline, metrics);
+ int xoffset = textAlignOffset(m_state.textAlign, metrics, text);
+ textPath.addText(x-xoffset, y-yoffset+metrics.ascent(), font, text);
+ m_painter.strokePath(textPath, QPen(m_state.fillStyle, m_state.lineWidth));
+ m_painter.restore();
+ endPainting();
+ scheduleChange();
+}
+
+void Context2D::strokeRect(qreal x, qreal y, qreal w, qreal h)
+{
+ QPainterPath path;
+ path.addRect(x, y, w, h);
+ beginPainting();
+ m_painter.save();
+ m_painter.setMatrix(worldMatrix(), false);
+ m_painter.strokePath(path, m_painter.pen());
+ m_painter.restore();
+ scheduleChange();
+}
+
+void Context2D::mouseArea(qreal x, qreal y, qreal w, qreal h, const QScriptValue &callback,
+ const QScriptValue &data)
+{
+ MouseArea a = { callback, data, QRectF(x, y, w, h), m_state.matrix };
+ m_mouseAreas << a;
+}
+
+void Context2D::beginPath()
+{
+ m_path = QPainterPath();
+}
+
+
+void Context2D::closePath()
+{
+ m_path.closeSubpath();
+}
+
+
+void Context2D::moveTo(qreal x, qreal y)
+{
+ QPointF pt = worldMatrix().map(QPointF(x, y));
+ m_path.moveTo(pt);
+}
+
+
+void Context2D::lineTo(qreal x, qreal y)
+{
+ QPointF pt = worldMatrix().map(QPointF(x, y));
+ m_path.lineTo(pt);
+}
+
+
+void Context2D::quadraticCurveTo(qreal cpx, qreal cpy, qreal x, qreal y)
+{
+ QPointF cp = worldMatrix().map(QPointF(cpx, cpy));
+ QPointF xy = worldMatrix().map(QPointF(x, y));
+ m_path.quadTo(cp, xy);
+}
+
+
+void Context2D::bezierCurveTo(qreal cp1x, qreal cp1y,
+ qreal cp2x, qreal cp2y, qreal x, qreal y)
+{
+ QPointF cp1 = worldMatrix().map(QPointF(cp1x, cp1y));
+ QPointF cp2 = worldMatrix().map(QPointF(cp2x, cp2y));
+ QPointF end = worldMatrix().map(QPointF(x, y));
+ m_path.cubicTo(cp1, cp2, end);
+}
+
+
+void Context2D::arcTo(qreal x1, qreal y1, qreal x2, qreal y2, qreal radius)
+{
+ //FIXME: this is surely busted
+ QPointF st = worldMatrix().map(QPointF(x1, y1));
+ QPointF end = worldMatrix().map(QPointF(x2, y2));
+ m_path.arcTo(st.x(), st.y(),
+ end.x()-st.x(), end.y()-st.y(),
+ radius, 90);
+}
+
+
+void Context2D::rect(qreal x, qreal y, qreal w, qreal h)
+{
+ QPainterPath path; path.addRect(x, y, w, h);
+ path = worldMatrix().map(path);
+ m_path.addPath(path);
+}
+
+void Context2D::arc(qreal xc, qreal yc, qreal radius,
+ qreal sar, qreal ear,
+ bool anticlockwise)
+{
+ //### HACK
+ // In Qt we don't switch the coordinate system for degrees
+ // and still use the 0,0 as bottom left for degrees so we need
+ // to switch
+ sar = -sar;
+ ear = -ear;
+ anticlockwise = !anticlockwise;
+ //end hack
+
+ float sa = DEGREES(sar);
+ float ea = DEGREES(ear);
+
+ double span = 0;
+
+ double xs = xc - radius;
+ double ys = yc - radius;
+ double width = radius*2;
+ double height = radius*2;
+
+ if (!anticlockwise && (ea < sa)) {
+ span += 360;
+ } else if (anticlockwise && (sa < ea)) {
+ span -= 360;
+ }
+
+ //### this is also due to switched coordinate system
+ // we would end up with a 0 span instead of 360
+ if (!(qFuzzyCompare(span + (ea - sa) + 1, 1) &&
+ qFuzzyCompare(qAbs(span), 360))) {
+ span += ea - sa;
+ }
+
+ QPainterPath path;
+ path.moveTo(QPointF(xc + radius * cos(sar),
+ yc - radius * sin(sar)));
+
+ path.arcTo(xs, ys, width, height, sa, span);
+ path = worldMatrix().map(path);
+ m_path.addPath(path);
+}
+
+
+void Context2D::fill()
+{
+ beginPainting();
+ m_painter.fillPath(m_path, m_painter.brush());
+ scheduleChange();
+}
+
+
+void Context2D::stroke()
+{
+ beginPainting();
+ m_painter.save();
+ m_painter.setMatrix(worldMatrix(), false);
+ QPainterPath tmp = worldMatrix().inverted().map(m_path);
+ m_painter.strokePath(tmp, m_painter.pen());
+ m_painter.restore();
+ scheduleChange();
+}
+
+
+void Context2D::clip()
+{
+ m_state.clipPath = m_path;
+ m_state.flags |= DirtyClippingRegion;
+}
+
+
+bool Context2D::isPointInPath(qreal x, qreal y) const
+{
+ return m_path.contains(QPointF(x, y));
+}
+
+
+ImageData Context2D::getImageData(qreal sx, qreal sy, qreal sw, qreal sh)
+{
+ Q_UNUSED(sx);
+ Q_UNUSED(sy);
+ Q_UNUSED(sw);
+ Q_UNUSED(sh);
+ return ImageData();
+}
+
+
+void Context2D::putImageData(ImageData image, qreal dx, qreal dy)
+{
+ Q_UNUSED(image);
+ Q_UNUSED(dx);
+ Q_UNUSED(dy);
+}
+
+Context2D::Context2D(QObject *parent)
+ : QObject(parent), m_changeTimerId(-1), m_width(0), m_height(0), m_inPaint(false)
+{
+ reset();
+}
+
+void Context2D::setupPainter()
+{
+ m_painter.setRenderHint(QPainter::Antialiasing, true);
+ if ((m_state.flags & DirtyClippingRegion) && !m_state.clipPath.isEmpty())
+ m_painter.setClipPath(m_state.clipPath);
+ if (m_state.flags & DirtyFillStyle)
+ m_painter.setBrush(m_state.fillStyle);
+ if (m_state.flags & DirtyGlobalAlpha)
+ m_painter.setOpacity(m_state.globalAlpha);
+ if (m_state.flags & DirtyGlobalCompositeOperation)
+ m_painter.setCompositionMode(m_state.globalCompositeOperation);
+ if (m_state.flags & MDirtyPen) {
+ QPen pen = m_painter.pen();
+ if (m_state.flags & DirtyStrokeStyle)
+ pen.setBrush(m_state.strokeStyle);
+ if (m_state.flags & DirtyLineWidth)
+ pen.setWidthF(m_state.lineWidth);
+ if (m_state.flags & DirtyLineCap)
+ pen.setCapStyle(m_state.lineCap);
+ if (m_state.flags & DirtyLineJoin)
+ pen.setJoinStyle(m_state.lineJoin);
+ if (m_state.flags & DirtyMiterLimit)
+ pen.setMiterLimit(m_state.miterLimit);
+ m_painter.setPen(pen);
+ }
+}
+
+void Context2D::beginPainting()
+{
+ if (m_width <= 0 || m_height <=0)
+ return;
+
+ if (m_pixmap.width() != m_width || m_pixmap.height() != m_height) {
+ m_pixmap = QPixmap(m_width, m_height);
+ m_pixmap.fill(parent()->property("color").value());
+ }
+
+ if (m_state.shadowBlur > 0 && m_painter.device() != &m_shadowbuffer) {
+ if (m_painter.isActive())
+ m_painter.end();
+ updateShadowBuffer();
+ m_painter.begin(&m_shadowbuffer);
+ m_painter.setViewport(m_state.shadowOffsetX,
+ m_state.shadowOffsetY,
+ m_shadowbuffer.width(),
+ m_shadowbuffer.height());
+ m_shadowbuffer.fill(Qt::transparent);
+ }
+
+ if (!m_painter.isActive()) {
+ m_painter.begin(&m_pixmap);
+ m_painter.setRenderHint(QPainter::Antialiasing);
+ if (!m_state.clipPath.isEmpty())
+ m_painter.setClipPath(m_state.clipPath);
+ m_painter.setBrush(m_state.fillStyle);
+ m_painter.setOpacity(m_state.globalAlpha);
+ QPen pen;
+ pen.setBrush(m_state.strokeStyle);
+ if (pen.style() == Qt::NoPen)
+ pen.setStyle(Qt::SolidLine);
+ pen.setCapStyle(m_state.lineCap);
+ pen.setJoinStyle(m_state.lineJoin);
+ pen.setWidthF(m_state.lineWidth);
+ pen.setMiterLimit(m_state.miterLimit);
+ m_painter.setPen(pen);
+ } else {
+ setupPainter();
+ m_state.flags = 0;
+ }
+}
+
+void Context2D::endPainting()
+{
+ if (m_state.shadowBlur > 0) {
+ QImage alphaChannel = m_shadowbuffer.alphaChannel();
+
+ qt_blurImage(alphaChannel, m_state.shadowBlur, false, 1);
+
+ QRect imageRect = m_shadowbuffer.rect();
+
+ if (m_shadowColorIndexBuffer.isEmpty() || m_shadowColorBuffer != m_state.shadowColor) {
+ m_shadowColorIndexBuffer.clear();
+ m_shadowColorBuffer = m_state.shadowColor;
+
+ for (int i = 0; i < 256; ++i) {
+ m_shadowColorIndexBuffer << qRgba(qRound(255 * m_state.shadowColor.redF()),
+ qRound(255 * m_state.shadowColor.greenF()),
+ qRound(255 * m_state.shadowColor.blueF()),
+ i);
+ }
+ }
+ alphaChannel.setColorTable(m_shadowColorIndexBuffer);
+
+ if (m_painter.isActive())
+ m_painter.end();
+
+ m_painter.begin(&m_pixmap);
+
+ // draw the blurred drop shadow...
+ m_painter.save();
+ QTransform tf = m_painter.transform();
+ m_painter.translate(0, imageRect.height());
+ m_painter.rotate(-90);
+ m_painter.drawImage(0, 0, alphaChannel);
+ m_painter.setTransform(tf);
+ m_painter.restore();
+
+ // draw source
+ m_painter.drawImage(-m_state.shadowOffsetX, -m_state.shadowOffsetY, m_shadowbuffer.copy());
+ m_painter.end();
+ }
+}
+
+void Context2D::clear()
+{
+ m_painter.fillRect(QRect(QPoint(0,0), size()), Qt::white);
+}
+
+void Context2D::reset()
+{
+ m_stateStack.clear();
+ m_state.matrix = QMatrix();
+ m_state.clipPath = QPainterPath();
+ m_state.strokeStyle = Qt::black;
+ m_state.fillStyle = Qt::black;
+ m_state.globalAlpha = 1.0;
+ m_state.lineWidth = 1;
+ m_state.lineCap = Qt::FlatCap;
+ m_state.lineJoin = Qt::MiterJoin;
+ m_state.miterLimit = 10;
+ m_state.shadowOffsetX = 0;
+ m_state.shadowOffsetY = 0;
+ m_state.shadowBlur = 0;
+ m_state.shadowColor = qRgba(0, 0, 0, 0);
+ m_state.globalCompositeOperation = QPainter::CompositionMode_SourceOver;
+ m_state.font = QFont();
+ m_state.textAlign = Start;
+ m_state.textBaseline = Alphabetic;
+ m_state.flags = AllIsFullOfDirt;
+ m_mouseAreas.clear();
+ clear();
+}
+
+void Context2D::drawImage(const QVariant &var, qreal sx, qreal sy,
+ qreal sw = 0, qreal sh = 0)
+{
+ CanvasImage *image = qobject_cast(var.value());
+ if (!image) {
+ Canvas *canvas = qobject_cast