diff options
Diffstat (limited to 'examples/script/context2d')
32 files changed, 3433 insertions, 0 deletions
diff --git a/examples/script/context2d/context2d.cpp b/examples/script/context2d/context2d.cpp new file mode 100644 index 0000000..d49cc3d --- /dev/null +++ b/examples/script/context2d/context2d.cpp @@ -0,0 +1,825 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "context2d.h" + +#include <QVariant> + +#include <math.h> +static const double Q_PI = 3.14159265358979323846; // pi + +#define DEGREES(t) ((t) * 180.0 / Q_PI) + +#define qClamp(val, min, max) qMin(qMax(val, min), max) +static QList<qreal> parseNumbersList(QString::const_iterator &itr) +{ + QList<qreal> 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<qreal> 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 { + //QRgb color; + //CSSParser::parseColor(name, color); + return QColor(name); + } +} + + +static QPainter::CompositionMode compositeOperatorFromString(const QString &compositeOperator) +{ + if ( compositeOperator == "source-over" ) { + return QPainter::CompositionMode_SourceOver; + } else if ( compositeOperator == "source-out" ) { + return QPainter::CompositionMode_SourceOut; + } else if ( compositeOperator == "source-in" ) { + return QPainter::CompositionMode_SourceIn; + } else if ( compositeOperator == "source-atop" ) { + return QPainter::CompositionMode_SourceAtop; + } else if ( compositeOperator == "destination-atop" ) { + return QPainter::CompositionMode_DestinationAtop; + } else if ( compositeOperator == "destination-in" ) { + return QPainter::CompositionMode_DestinationIn; + } else if ( compositeOperator == "destination-out" ) { + return QPainter::CompositionMode_DestinationOut; + } else if ( compositeOperator == "destination-over" ) { + return QPainter::CompositionMode_DestinationOver; + } else if ( compositeOperator == "darker" ) { + return QPainter::CompositionMode_SourceOver; + } else if ( compositeOperator == "lighter" ) { + return QPainter::CompositionMode_SourceOver; + } else if ( compositeOperator == "copy" ) { + return QPainter::CompositionMode_Source; + } else if ( compositeOperator == "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) +{ + if (qVariantCanConvert<CanvasGradient>(style)) { + CanvasGradient cg = qvariant_cast<CanvasGradient>(style); + m_state.strokeStyle = cg.value; + } else { + QColor color = colorFromString(style.toString()); + m_state.strokeStyle = color; + } + m_state.flags |= DirtyStrokeStyle; +} + +QVariant Context2D::fillStyle() const +{ + return m_state.fillStyle; +} + +//! [3] +void Context2D::setFillStyle(const QVariant &style) +{ + if (qVariantCanConvert<CanvasGradient>(style)) { + CanvasGradient cg = qvariant_cast<CanvasGradient>(style); + m_state.fillStyle = cg.value; + } else { + QColor color = colorFromString(style.toString()); + m_state.fillStyle = color; + } + m_state.flags |= DirtyFillStyle; +} +//! [3] + +qreal Context2D::globalAlpha() const +{ + return m_state.globalAlpha; +} + +void Context2D::setGlobalAlpha(qreal alpha) +{ + m_state.globalAlpha = alpha; + m_state.flags |= DirtyGlobalAlpha; +} + + +CanvasGradient Context2D::createLinearGradient(qreal x0, qreal y0, + qreal x1, qreal y1) +{ + QLinearGradient g(x0, y0, x1, y1); + return 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 CanvasGradient(g); +} + +qreal Context2D::lineWidth() const +{ + return m_state.lineWidth; +} + +void Context2D::setLineWidth(qreal w) +{ + m_state.lineWidth = w; + m_state.flags |= DirtyLineWidth; +} + +//! [0] +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 == "round") + style = Qt::RoundCap; + else if (capString == "square") + style = Qt::SquareCap; + else if (capString == "butt") + style = Qt::FlatCap; + m_state.lineCap = style; + m_state.flags |= DirtyLineCap; +} +//! [0] + +QString Context2D::lineJoin() const +{ + switch (m_state.lineJoin) { + case Qt::RoundJoin: + return "round"; + case Qt::BevelJoin: + return "bevel"; + case Qt::MiterJoin: + return "miter"; + default: ; + } + return QString(); +} + +void Context2D::setLineJoin(const QString &joinString) +{ + Qt::PenJoinStyle style; + if (joinString == "round") + style = Qt::RoundJoin; + else if (joinString == "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) +{ + m_state.shadowOffsetX = x; + m_state.flags |= DirtyShadowOffsetX; +} + +void Context2D::setShadowOffsetY(qreal y) +{ + m_state.shadowOffsetY = y; + m_state.flags |= DirtyShadowOffsetY; +} + +void Context2D::setShadowBlur(qreal b) +{ + m_state.shadowBlur = b; + m_state.flags |= DirtyShadowBlur; +} + +void Context2D::setShadowColor(const QString &str) +{ + m_state.shadowColor = colorFromString(str); + m_state.flags |= DirtyShadowColor; +} + +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(m_state.matrix, false); + m_painter.setCompositionMode(QPainter::CompositionMode_Source); + m_painter.fillRect(QRectF(x, y, w, h), QColor(0, 0, 0, 0)); + m_painter.restore(); + scheduleChange(); +} + + +//! [1] +void Context2D::fillRect(qreal x, qreal y, qreal w, qreal h) +{ + beginPainting(); + m_painter.save(); + m_painter.setMatrix(m_state.matrix, false); + m_painter.fillRect(QRectF(x, y, w, h), m_painter.brush()); + m_painter.restore(); + scheduleChange(); +} +//! [1] + + +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(m_state.matrix, false); + m_painter.strokePath(path, m_painter.pen()); + m_painter.restore(); + scheduleChange(); +} + + +void Context2D::beginPath() +{ + m_path = QPainterPath(); +} + + +void Context2D::closePath() +{ + m_path.closeSubpath(); +} + + +void Context2D::moveTo(qreal x, qreal y) +{ + QPointF pt = m_state.matrix.map(QPointF(x, y)); + m_path.moveTo(pt); +} + + +void Context2D::lineTo(qreal x, qreal y) +{ + QPointF pt = m_state.matrix.map(QPointF(x, y)); + m_path.lineTo(pt); +} + + +void Context2D::quadraticCurveTo(qreal cpx, qreal cpy, qreal x, qreal y) +{ + QPointF cp = m_state.matrix.map(QPointF(cpx, cpy)); + QPointF xy = m_state.matrix.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 = m_state.matrix.map(QPointF(cp1x, cp1y)); + QPointF cp2 = m_state.matrix.map(QPointF(cp2x, cp2y)); + QPointF end = m_state.matrix.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 = m_state.matrix.map(QPointF(x1, y1)); + QPointF end = m_state.matrix.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 = m_state.matrix.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 = m_state.matrix.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(m_state.matrix, false); + QPainterPath tmp = m_state.matrix.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) +{ + reset(); +} + +const QImage &Context2D::endPainting() +{ + if (m_painter.isActive()) + m_painter.end(); + return m_image; +} + +void Context2D::beginPainting() +{ + if (!m_painter.isActive()) { + m_painter.begin(&m_image); + 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 { + 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); + } + m_state.flags = 0; + } +} + +void Context2D::clear() +{ + endPainting(); + m_image.fill(qRgba(0,0,0,0)); + scheduleChange(); +} + +void Context2D::reset() +{ + m_stateStack.clear(); + m_state.matrix = QMatrix(); + m_state.clipPath = QPainterPath(); + m_state.globalAlpha = 1.0; + m_state.globalCompositeOperation = QPainter::CompositionMode_SourceOver; + m_state.strokeStyle = Qt::black; + m_state.fillStyle = Qt::black; + 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.flags = AllIsFullOfDirt; + clear(); +} + +void Context2D::setSize(int width, int height) +{ + endPainting(); + QImage newi(width, height, QImage::Format_ARGB32_Premultiplied); + newi.fill(qRgba(0,0,0,0)); + QPainter p(&newi); + p.drawImage(0, 0, m_image); + p.end(); + m_image = newi; + scheduleChange(); +} + +void Context2D::setSize(const QSize &size) +{ + setSize(size.width(), size.height()); +} + +QSize Context2D::size() const +{ + return m_image.size(); +} + +void Context2D::drawImage(DomImage *image, qreal dx, qreal dy) +{ + if (!image) + return; + if (dx < 0) { + qreal sx = qAbs(dx); + qreal sy = qAbs(dy); + qreal sw = image->width() - sx; + qreal sh = image->height() - sy; + + drawImage(image, sx, sy, sw, sh, 0, 0, sw, sh); + } else { + beginPainting(); + m_painter.drawImage(QPointF(dx, dy), image->image()); + scheduleChange(); + } +} + +void Context2D::drawImage(DomImage *image, qreal dx, qreal dy, + qreal dw, qreal dh) +{ + if (!image) + return; + beginPainting(); + m_painter.drawImage(QRectF(dx, dy, dw, dh).toRect(), image->image()); + scheduleChange(); +} + +void Context2D::drawImage(DomImage *image, qreal sx, qreal sy, + qreal sw, qreal sh, qreal dx, qreal dy, + qreal dw, qreal dh) +{ + if (!image) + return; + beginPainting(); + m_painter.drawImage(QRectF(dx, dy, dw, dh), image->image(), + QRectF(sx, sy, sw, sh)); + scheduleChange(); +} + +//! [2] +void Context2D::scheduleChange() +{ + if (m_changeTimerId == -1) + m_changeTimerId = startTimer(0); +} + +void Context2D::timerEvent(QTimerEvent *e) +{ + if (e->timerId() == m_changeTimerId) { + killTimer(m_changeTimerId); + m_changeTimerId = -1; + emit changed(endPainting()); + } else { + QObject::timerEvent(e); + } +} +//! [2] diff --git a/examples/script/context2d/context2d.h b/examples/script/context2d/context2d.h new file mode 100644 index 0000000..38188f7 --- /dev/null +++ b/examples/script/context2d/context2d.h @@ -0,0 +1,261 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CONTEXT2D_H +#define CONTEXT2D_H + +#include "domimage.h" + +#include <QPainter> +#include <QPainterPath> +#include <QString> +#include <QStack> +#include <QMetaType> +#include <QTimerEvent> + +// [3] +class CanvasGradient +{ +public: + CanvasGradient(const QGradient &v) + : value(v) {} + CanvasGradient() {} + + QGradient value; +}; +// [3] + +Q_DECLARE_METATYPE(CanvasGradient) +Q_DECLARE_METATYPE(CanvasGradient*) + +class ImageData { +}; + +class QContext2DCanvas; + +//! [0] +class Context2D : public QObject +{ + Q_OBJECT + // compositing + Q_PROPERTY(qreal globalAlpha READ globalAlpha WRITE setGlobalAlpha) + Q_PROPERTY(QString globalCompositeOperation READ globalCompositeOperation WRITE setGlobalCompositeOperation) + Q_PROPERTY(QVariant strokeStyle READ strokeStyle WRITE setStrokeStyle) + Q_PROPERTY(QVariant fillStyle READ fillStyle WRITE setFillStyle) + // line caps/joins + Q_PROPERTY(qreal lineWidth READ lineWidth WRITE setLineWidth) + Q_PROPERTY(QString lineCap READ lineCap WRITE setLineCap) + Q_PROPERTY(QString lineJoin READ lineJoin WRITE setLineJoin) + Q_PROPERTY(qreal miterLimit READ miterLimit WRITE setMiterLimit) + // shadows + Q_PROPERTY(qreal shadowOffsetX READ shadowOffsetX WRITE setShadowOffsetX) + Q_PROPERTY(qreal shadowOffsetY READ shadowOffsetY WRITE setShadowOffsetY) + Q_PROPERTY(qreal shadowBlur READ shadowBlur WRITE setShadowBlur) + Q_PROPERTY(QString shadowColor READ shadowColor WRITE setShadowColor) +//! [0] + +public: + Context2D(QObject *parent = 0); + void setSize(int width, int height); + void setSize(const QSize &size); + QSize size() const; + + void clear(); + void reset(); + + // compositing + qreal globalAlpha() const; // (default 1.0) + QString globalCompositeOperation() const; // (default over) + QVariant strokeStyle() const; // (default black) + QVariant fillStyle() const; // (default black) + + void setGlobalAlpha(qreal alpha); + void setGlobalCompositeOperation(const QString &op); + void setStrokeStyle(const QVariant &style); + void setFillStyle(const QVariant &style); + + // line caps/joins + qreal lineWidth() const; // (default 1) + QString lineCap() const; // "butt", "round", "square" (default "butt") + QString lineJoin() const; // "round", "bevel", "miter" (default "miter") + qreal miterLimit() const; // (default 10) + + void setLineWidth(qreal w); + void setLineCap(const QString &s); + void setLineJoin(const QString &s); + void setMiterLimit(qreal m); + + // shadows + qreal shadowOffsetX() const; // (default 0) + qreal shadowOffsetY() const; // (default 0) + qreal shadowBlur() const; // (default 0) + QString shadowColor() const; // (default black) + + void setShadowOffsetX(qreal x); + void setShadowOffsetY(qreal y); + void setShadowBlur(qreal b); + void setShadowColor(const QString &str); + +//! [1] +public slots: + void save(); // push state on state stack + void restore(); // pop state stack and restore state + + void scale(qreal x, qreal y); + void rotate(qreal angle); + void translate(qreal x, qreal y); + void transform(qreal m11, qreal m12, qreal m21, qreal m22, + qreal dx, qreal dy); + void setTransform(qreal m11, qreal m12, qreal m21, qreal m22, + qreal dx, qreal dy); + + CanvasGradient createLinearGradient(qreal x0, qreal y0, + qreal x1, qreal y1); + CanvasGradient createRadialGradient(qreal x0, qreal y0, + qreal r0, qreal x1, + qreal y1, qreal r1); + + // rects + void clearRect(qreal x, qreal y, qreal w, qreal h); + void fillRect(qreal x, qreal y, qreal w, qreal h); + void strokeRect(qreal x, qreal y, qreal w, qreal h); + + // path API + void beginPath(); + void closePath(); + void moveTo(qreal x, qreal y); + void lineTo(qreal x, qreal y); + void quadraticCurveTo(qreal cpx, qreal cpy, qreal x, qreal y); + void bezierCurveTo(qreal cp1x, qreal cp1y, + qreal cp2x, qreal cp2y, qreal x, qreal y); + void arcTo(qreal x1, qreal y1, qreal x2, qreal y2, qreal radius); + void rect(qreal x, qreal y, qreal w, qreal h); + void arc(qreal x, qreal y, qreal radius, + qreal startAngle, qreal endAngle, + bool anticlockwise); + void fill(); + void stroke(); + void clip(); + bool isPointInPath(qreal x, qreal y) const; +//! [1] + + // drawing images + void drawImage(DomImage *image, qreal dx, qreal dy); + void drawImage(DomImage *image, qreal dx, qreal dy, + qreal dw, qreal dh); + void drawImage(DomImage *image, qreal sx, qreal sy, + qreal sw, qreal sh, qreal dx, qreal dy, + qreal dw, qreal dh); + + // pixel manipulation + ImageData getImageData(qreal sx, qreal sy, qreal sw, qreal sh); + void putImageData(ImageData image, qreal dx, qreal dy); + +//! [2] +signals: + void changed(const QImage &image); +//! [2] + +protected: + void timerEvent(QTimerEvent *e); + +private: + void beginPainting(); + const QImage &endPainting(); + void scheduleChange(); + + int m_changeTimerId; + QImage m_image; + QPainter m_painter; + QPainterPath m_path; + + enum DirtyFlag { + DirtyTransformationMatrix = 0x00001, + DirtyClippingRegion = 0x00002, + DirtyStrokeStyle = 0x00004, + DirtyFillStyle = 0x00008, + DirtyGlobalAlpha = 0x00010, + DirtyLineWidth = 0x00020, + DirtyLineCap = 0x00040, + DirtyLineJoin = 0x00080, + DirtyMiterLimit = 0x00100, + MDirtyPen = DirtyStrokeStyle + | DirtyLineWidth + | DirtyLineCap + | DirtyLineJoin + | DirtyMiterLimit, + DirtyShadowOffsetX = 0x00200, + DirtyShadowOffsetY = 0x00400, + DirtyShadowBlur = 0x00800, + DirtyShadowColor = 0x01000, + DirtyGlobalCompositeOperation = 0x2000, + DirtyFont = 0x04000, + DirtyTextAlign = 0x08000, + DirtyTextBaseline = 0x10000, + AllIsFullOfDirt = 0xfffff + }; + + struct State { + State() : flags(0) {} + QMatrix matrix; + QPainterPath clipPath; + QBrush strokeStyle; + QBrush fillStyle; + qreal globalAlpha; + qreal lineWidth; + Qt::PenCapStyle lineCap; + Qt::PenJoinStyle lineJoin; + qreal miterLimit; + qreal shadowOffsetX; + qreal shadowOffsetY; + qreal shadowBlur; + QColor shadowColor; + QPainter::CompositionMode globalCompositeOperation; + QFont font; + int textAlign; + int textBaseline; + int flags; + }; + State m_state; + QStack<State> m_stateStack; +}; + +#endif diff --git a/examples/script/context2d/context2d.pro b/examples/script/context2d/context2d.pro new file mode 100644 index 0000000..30ec9a7 --- /dev/null +++ b/examples/script/context2d/context2d.pro @@ -0,0 +1,23 @@ +TEMPLATE = app +QT += script +# Input +HEADERS += qcontext2dcanvas.h \ + context2d.h \ + domimage.h \ + environment.h \ + window.h +SOURCES += qcontext2dcanvas.cpp \ + context2d.cpp \ + domimage.cpp \ + environment.cpp \ + window.cpp \ + main.cpp +RESOURCES += context2d.qrc + +contains(QT_CONFIG, scripttools): QT += scripttools + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/script/context2d +sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS context2d.pro scripts +sources.path = $$[QT_INSTALL_EXAMPLES]/script/context2d +INSTALLS += target sources diff --git a/examples/script/context2d/context2d.qrc b/examples/script/context2d/context2d.qrc new file mode 100644 index 0000000..75e570c --- /dev/null +++ b/examples/script/context2d/context2d.qrc @@ -0,0 +1,5 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource prefix="/"> + <file>scripts</file> +</qresource> +</RCC> diff --git a/examples/script/context2d/domimage.cpp b/examples/script/context2d/domimage.cpp new file mode 100644 index 0000000..66a2c95 --- /dev/null +++ b/examples/script/context2d/domimage.cpp @@ -0,0 +1,157 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "domimage.h" + +#include <QVariant> + +#include <qscriptcontext.h> + +QScriptValue DomImage::s_self; + +DomImage::DomImage() +{ +} + + +int DomImage::width() const +{ + return m_image.width(); +} + + +int DomImage::height() const +{ + return m_image.height(); +} + + +QString DomImage::src() const +{ + return m_src; +} + +void DomImage::setSrc(const QString &src) +{ + m_src = src; + m_image = QImage(m_src); +} + + +QString DomImage::name() const +{ + return m_src; +} + +static QScriptValue Image(QScriptContext *context, QScriptEngine *env) +{ + QScriptValue val = context->thisObject(); + DomImage *image = new DomImage(); + QScriptValue klass = env->newVariant(qVariantFromValue(image)); + klass.setPrototype(DomImage::s_self); + return klass; +} + + +static QScriptValue width(QScriptContext *context, QScriptEngine *) +{ + QScriptValue val = context->thisObject(); + + DomImage *image = qvariant_cast<DomImage*> (val.toVariant()); + if (image) + return image->width(); + + return 0; +} + + +static QScriptValue height(QScriptContext *context, QScriptEngine *) +{ + QScriptValue val = context->thisObject(); + + DomImage *image = qvariant_cast<DomImage*> (val.toVariant()); + if (image) + return image->height(); + + return 0; +} + + +static QScriptValue setSrc(QScriptContext *context, QScriptEngine *env) +{ + QScriptValue val = context->thisObject(); + QString src = context->argument(0).toString(); + + DomImage *image = qvariant_cast<DomImage*> (val.toVariant()); + if (image) + image->setSrc(src); + + return env->undefinedValue(); +} + + +static QScriptValue name(QScriptContext *context, QScriptEngine *) +{ + QScriptValue val = context->thisObject(); + + DomImage *image = qvariant_cast<DomImage*> (val.toVariant()); + if (image) + return image->name(); + + return QString(); +} + + +void DomImage::setup(QScriptEngine *e) +{ + qRegisterMetaType<DomImage>(); + + e->globalObject().setProperty("Image", + e->newFunction(::Image, 0)); + + s_self = e->newObject(); + s_self.setProperty("setSrc", e->newFunction(&::setSrc, 1)); + s_self.setProperty("width", e->newFunction(&::width)); + s_self.setProperty("height", e->newFunction(&::height)); + s_self.setProperty("name", e->newFunction(&::name)); + + e->setDefaultPrototype(qMetaTypeId<DomImage>(), s_self); +} diff --git a/examples/script/context2d/domimage.h b/examples/script/context2d/domimage.h new file mode 100644 index 0000000..0793d1d --- /dev/null +++ b/examples/script/context2d/domimage.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DOMIMAGE_H +#define DOMIMAGE_H + +#include <QPixmap> +#include <QImage> +#include <QMetaType> + +#include <qscriptengine.h> + +class DomImage +{ +public: + DomImage(); + static void setup(QScriptEngine *e); + + int width() const; + int height() const; + + QString src() const; + void setSrc(const QString &src); + + QString name() const; + + static QScriptValue s_self; + + const QImage &image() const + { + return m_image; + } +private: + QImage m_image; + QString m_src; + //attribute boolean isMap; + //attribute DOMString longDesc; + //attribute DOMString useMap; + //attribute DOMString align; + //attribute DOMString alt; + //attribute DOMString border; + //attribute long vspace; + //attribute long hspace; +}; + +Q_DECLARE_METATYPE(DomImage) +Q_DECLARE_METATYPE(DomImage*) + +#endif diff --git a/examples/script/context2d/environment.cpp b/examples/script/context2d/environment.cpp new file mode 100644 index 0000000..1c1a3a6 --- /dev/null +++ b/examples/script/context2d/environment.cpp @@ -0,0 +1,561 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "environment.h" +#include "qcontext2dcanvas.h" +#include "context2d.h" +#include <QScriptValueIterator> +#include <QDateTime> + +struct FakeDomEvent +{ + enum KeyCodes { + DOM_VK_UNDEFINED = 0x0, + DOM_VK_RIGHT_ALT = 0x12, + DOM_VK_LEFT_ALT = 0x12, + DOM_VK_LEFT_CONTROL = 0x11, + DOM_VK_RIGHT_CONTROL = 0x11, + DOM_VK_LEFT_SHIFT = 0x10, + DOM_VK_RIGHT_SHIFT = 0x10, + DOM_VK_META = 0x9D, + DOM_VK_BACK_SPACE = 0x08, + DOM_VK_CAPS_LOCK = 0x14, + DOM_VK_DELETE = 0x7F, + DOM_VK_END = 0x23, + DOM_VK_ENTER = 0x0D, + DOM_VK_ESCAPE = 0x1B, + DOM_VK_HOME = 0x24, + DOM_VK_NUM_LOCK = 0x90, + DOM_VK_PAUSE = 0x13, + DOM_VK_PRINTSCREEN = 0x9A, + DOM_VK_SCROLL_LOCK = 0x91, + DOM_VK_SPACE = 0x20, + DOM_VK_TAB = 0x09, + DOM_VK_LEFT = 0x25, + DOM_VK_RIGHT = 0x27, + DOM_VK_UP = 0x26, + DOM_VK_DOWN = 0x28, + DOM_VK_PAGE_DOWN = 0x22, + DOM_VK_PAGE_UP = 0x21, + DOM_VK_F1 = 0x70, + DOM_VK_F2 = 0x71, + DOM_VK_F3 = 0x72, + DOM_VK_F4 = 0x73, + DOM_VK_F5 = 0x74, + DOM_VK_F6 = 0x75, + DOM_VK_F7 = 0x76, + DOM_VK_F8 = 0x77, + DOM_VK_F9 = 0x78, + DOM_VK_F10 = 0x79, + DOM_VK_F11 = 0x7A, + DOM_VK_F12 = 0x7B, + DOM_VK_F13 = 0xF000, + DOM_VK_F14 = 0xF001, + DOM_VK_F15 = 0xF002, + DOM_VK_F16 = 0xF003, + DOM_VK_F17 = 0xF004, + DOM_VK_F18 = 0xF005, + DOM_VK_F19 = 0xF006, + DOM_VK_F20 = 0xF007, + DOM_VK_F21 = 0xF008, + DOM_VK_F22 = 0xF009, + DOM_VK_F23 = 0xF00A, + DOM_VK_F24 = 0xF00B + }; + + static int qtToDomKey(int keyCode); +}; + +int FakeDomEvent::qtToDomKey(int keyCode) +{ + switch (keyCode) { + case Qt::Key_Backspace: + return DOM_VK_BACK_SPACE; + case Qt::Key_Enter: + return DOM_VK_ENTER; + case Qt::Key_Return: + return DOM_VK_ENTER; + case Qt::Key_NumLock: + return DOM_VK_NUM_LOCK; + case Qt::Key_Alt: + return DOM_VK_RIGHT_ALT; + case Qt::Key_Control: + return DOM_VK_LEFT_CONTROL; + case Qt::Key_Shift: + return DOM_VK_LEFT_SHIFT; + case Qt::Key_Meta: + return DOM_VK_META; + case Qt::Key_CapsLock: + return DOM_VK_CAPS_LOCK; + case Qt::Key_Delete: + return DOM_VK_DELETE; + case Qt::Key_End: + return DOM_VK_END; + case Qt::Key_Escape: + return DOM_VK_ESCAPE; + case Qt::Key_Home: + return DOM_VK_HOME; + case Qt::Key_Pause: + return DOM_VK_PAUSE; + case Qt::Key_Print: + return DOM_VK_PRINTSCREEN; + case Qt::Key_ScrollLock: + return DOM_VK_SCROLL_LOCK; + case Qt::Key_Left: + return DOM_VK_LEFT; + case Qt::Key_Right: + return DOM_VK_RIGHT; + case Qt::Key_Up: + return DOM_VK_UP; + case Qt::Key_Down: + return DOM_VK_DOWN; + case Qt::Key_PageDown: + return DOM_VK_PAGE_DOWN; + case Qt::Key_PageUp: + return DOM_VK_PAGE_UP; + case Qt::Key_F1: + return DOM_VK_F1; + case Qt::Key_F2: + return DOM_VK_F2; + case Qt::Key_F3: + return DOM_VK_F3; + case Qt::Key_F4: + return DOM_VK_F4; + case Qt::Key_F5: + return DOM_VK_F5; + case Qt::Key_F6: + return DOM_VK_F6; + case Qt::Key_F7: + return DOM_VK_F7; + case Qt::Key_F8: + return DOM_VK_F8; + case Qt::Key_F9: + return DOM_VK_F9; + case Qt::Key_F10: + return DOM_VK_F10; + case Qt::Key_F11: + return DOM_VK_F11; + case Qt::Key_F12: + return DOM_VK_F12; + case Qt::Key_F13: + return DOM_VK_F13; + case Qt::Key_F14: + return DOM_VK_F14; + case Qt::Key_F15: + return DOM_VK_F15; + case Qt::Key_F16: + return DOM_VK_F16; + case Qt::Key_F17: + return DOM_VK_F17; + case Qt::Key_F18: + return DOM_VK_F18; + case Qt::Key_F19: + return DOM_VK_F19; + case Qt::Key_F20: + return DOM_VK_F20; + case Qt::Key_F21: + return DOM_VK_F21; + case Qt::Key_F22: + return DOM_VK_F22; + case Qt::Key_F23: + return DOM_VK_F23; + case Qt::Key_F24: + return DOM_VK_F24; + } + return keyCode; +} + +//! [0] +Environment::Environment(QObject *parent) + : QObject(parent) +{ + m_engine = new QScriptEngine(this); + + m_document = m_engine->newQObject( + new Document(this), QScriptEngine::QtOwnership, + QScriptEngine::ExcludeSuperClassContents); + + CanvasGradientPrototype::setup(m_engine); + + m_originalGlobalObject = m_engine->globalObject(); + reset(); +} +//! [0] + +Environment::~Environment() +{ +} + +QScriptEngine *Environment::engine() const +{ + return m_engine; +} + +QScriptValue Environment::document() const +{ + return m_document; +} + +int Environment::setTimeout(const QScriptValue &expression, int delay) +{ + if (expression.isString() || expression.isFunction()) { + int timerId = startTimer(delay); + m_timeoutHash.insert(timerId, expression); + return timerId; + } + return -1; +} + +void Environment::clearTimeout(int timerId) +{ + killTimer(timerId); + m_timeoutHash.remove(timerId); +} + +//! [1] +int Environment::setInterval(const QScriptValue &expression, int delay) +{ + if (expression.isString() || expression.isFunction()) { + int timerId = startTimer(delay); + m_intervalHash.insert(timerId, expression); + return timerId; + } + return -1; +} + +void Environment::clearInterval(int timerId) +{ + killTimer(timerId); + m_intervalHash.remove(timerId); +} + +void Environment::timerEvent(QTimerEvent *event) +{ + int id = event->timerId(); + QScriptValue expression = m_intervalHash.value(id); + if (!expression.isValid()) { + expression = m_timeoutHash.value(id); + if (expression.isValid()) + killTimer(id); + } + if (expression.isString()) { + evaluate(expression.toString()); + } else if (expression.isFunction()) { + expression.call(); + } + maybeEmitScriptError(); +} +//! [1] + +//! [5] +void Environment::addCanvas(QContext2DCanvas *canvas) +{ + m_canvases.append(canvas); +} + +QContext2DCanvas *Environment::canvasByName(const QString &name) const +{ + for (int i = 0; i < m_canvases.size(); ++i) { + QContext2DCanvas *canvas = m_canvases.at(i); + if (canvas->objectName() == name) + return canvas; + } + return 0; +} +//! [5] + +QList<QContext2DCanvas*> Environment::canvases() const +{ + return m_canvases; +} + +void Environment::reset() +{ + if (m_engine->isEvaluating()) + m_engine->abortEvaluation(); + + { + QHash<int, QScriptValue>::const_iterator it; + for (it = m_intervalHash.constBegin(); it != m_intervalHash.constEnd(); ++it) + killTimer(it.key()); + m_intervalHash.clear(); + for (it = m_timeoutHash.constBegin(); it != m_timeoutHash.constEnd(); ++it) + killTimer(it.key()); + m_timeoutHash.clear(); + } + + for (int i = 0; i < m_canvases.size(); ++i) + m_canvases.at(i)->reset(); + + QScriptValue self = m_engine->newQObject( + this, QScriptEngine::QtOwnership, + QScriptEngine::ExcludeSuperClassContents); + + { + QScriptValueIterator it(m_originalGlobalObject); + while (it.hasNext()) { + it.next(); + self.setProperty(it.scriptName(), it.value(), it.flags()); + } + } + + self.setProperty("self", self); + self.setProperty("window", self); + + QScriptValue navigator = m_engine->newObject(); + navigator.setProperty("appCodeName", "context2d"); + navigator.setProperty("appMinorVersion", 1); + navigator.setProperty("appVersion", 1); + navigator.setProperty("browserLanguage", "en_US"); + navigator.setProperty("cookieEnabled", false); + navigator.setProperty("cpuClass", "i686"); + navigator.setProperty("onLine", false); + navigator.setProperty("platform", "bogus OS"); + navigator.setProperty("systemLanguage", "en_US"); + navigator.setProperty("userAgent", "Context2D/1.1"); + navigator.setProperty("userLanguage", "en_US"); + self.setProperty("navigator", navigator); + + m_engine->setGlobalObject(self); + + m_engine->collectGarbage(); +} + +QScriptValue Environment::evaluate(const QString &code, const QString &fileName) +{ + return m_engine->evaluate(code, fileName); +} + +//! [2] +QScriptValue Environment::toWrapper(QObject *object) +{ + return m_engine->newQObject(object, QScriptEngine::QtOwnership, + QScriptEngine::PreferExistingWrapperObject + | QScriptEngine::ExcludeSuperClassContents); +} +//! [2] + +//! [3] +void Environment::handleEvent(QContext2DCanvas *canvas, QMouseEvent *e) +{ + QString type; + switch (e->type()) { + case QEvent::MouseButtonPress: + type = "mousedown"; break; + case QEvent::MouseButtonRelease: + type = "mouseup"; break; + case QEvent::MouseMove: + type = "mousemove"; break; + default: break; + } + if (type.isEmpty()) + return; + + QScriptValue handlerObject; + QScriptValue handler = eventHandler(canvas, type, &handlerObject); + if (!handler.isFunction()) + return; + + QScriptValue scriptEvent = newFakeDomEvent(type, toWrapper(canvas)); + // MouseEvent + scriptEvent.setProperty("screenX", e->globalX(), QScriptValue::ReadOnly); + scriptEvent.setProperty("screenY", e->globalY(), QScriptValue::ReadOnly); + scriptEvent.setProperty("clientX", e->x(), QScriptValue::ReadOnly); + scriptEvent.setProperty("clientY", e->y(), QScriptValue::ReadOnly); + scriptEvent.setProperty("layerX", e->x(), QScriptValue::ReadOnly); + scriptEvent.setProperty("layerY", e->y(), QScriptValue::ReadOnly); + scriptEvent.setProperty("pageX", e->x(), QScriptValue::ReadOnly); + scriptEvent.setProperty("pageY", e->y(), QScriptValue::ReadOnly); + scriptEvent.setProperty("altKey", (e->modifiers() & Qt::AltModifier) != 0, + QScriptValue::ReadOnly); + scriptEvent.setProperty("ctrlKey", (e->modifiers() & Qt::ControlModifier) != 0, + QScriptValue::ReadOnly); + scriptEvent.setProperty("metaKey", (e->modifiers() & Qt::MetaModifier) != 0, + QScriptValue::ReadOnly); + scriptEvent.setProperty("shiftKey", (e->modifiers() & Qt::ShiftModifier) != 0, + QScriptValue::ReadOnly); + int button = 0; + if (e->button() == Qt::RightButton) + button = 2; + else if (e->button() == Qt::MidButton) + button = 1; + scriptEvent.setProperty("button", button); + scriptEvent.setProperty("relatedTarget", m_engine->nullValue(), + QScriptValue::ReadOnly); + handler.call(handlerObject, QScriptValueList() << scriptEvent); + maybeEmitScriptError(); +} +//! [3] + +void Environment::handleEvent(QContext2DCanvas *canvas, QKeyEvent *e) +{ + QString type; + switch (e->type()) { + case QEvent::KeyPress: + type = "keydown"; break; + case QEvent::KeyRelease: + type = "keyup"; break; + default: break; + } + if (type.isEmpty()) + return; + + QScriptValue handlerObject; + QScriptValue handler = eventHandler(canvas, type, &handlerObject); + if (!handler.isFunction()) + return; + + QScriptValue scriptEvent = newFakeDomEvent(type, toWrapper(canvas)); + // KeyEvent + scriptEvent.setProperty("isChar", !e->text().isEmpty()); + scriptEvent.setProperty("charCode", e->text()); + scriptEvent.setProperty("keyCode", FakeDomEvent::qtToDomKey(e->key())); + scriptEvent.setProperty("which", e->key()); + + handler.call(handlerObject, QScriptValueList() << scriptEvent); + maybeEmitScriptError(); +} + +QScriptValue Environment::eventHandler(QContext2DCanvas *canvas, const QString &type, + QScriptValue *who) +{ + QString handlerName = "on" + type; + QScriptValue obj = toWrapper(canvas); + QScriptValue handler = obj.property(handlerName); + if (!handler.isValid()) { + obj = m_document; + handler = obj.property(handlerName); + } + if (who && handler.isFunction()) + *who = obj; + return handler; +} + +//! [4] +QScriptValue Environment::newFakeDomEvent(const QString &type, const QScriptValue &target) +{ + QScriptValue e = m_engine->newObject(); + // Event + e.setProperty("type", type, QScriptValue::ReadOnly); + e.setProperty("bubbles", true, QScriptValue::ReadOnly); + e.setProperty("cancelable", false, QScriptValue::ReadOnly); + e.setProperty("target", target, QScriptValue::ReadOnly); + e.setProperty("currentTarget", target, QScriptValue::ReadOnly); + e.setProperty("eventPhase", 3); // bubbling + e.setProperty("timeStamp", QDateTime::currentDateTime().toTime_t()); + // UIEvent + e.setProperty("detail", 0, QScriptValue::ReadOnly); + e.setProperty("view", m_engine->globalObject(), QScriptValue::ReadOnly); + return e; +} +//! [4] + +void Environment::maybeEmitScriptError() +{ + if (m_engine->hasUncaughtException()) + emit scriptError(m_engine->uncaughtException()); +} + + +Document::Document(Environment *env) + : QObject(env) +{ +} + +Document::~Document() +{ +} + +QScriptValue Document::getElementById(const QString &id) const +{ + Environment *env = qobject_cast<Environment*>(parent()); + QContext2DCanvas *canvas = env->canvasByName(id); + if (!canvas) + return QScriptValue(); + return env->toWrapper(canvas); +} + +QScriptValue Document::getElementsByTagName(const QString &name) const +{ + if (name != "canvas") + return QScriptValue(); + Environment *env = qobject_cast<Environment*>(parent()); + QList<QContext2DCanvas*> list = env->canvases(); + QScriptValue result = env->engine()->newArray(list.size()); + for (int i = 0; i < list.size(); ++i) + result.setProperty(i, env->toWrapper(list.at(i))); + return result; +} + +void Document::addEventListener(const QString &type, const QScriptValue &listener, + bool useCapture) +{ + Q_UNUSED(useCapture); + if (listener.isFunction()) { + Environment *env = qobject_cast<Environment*>(parent()); + QScriptValue self = env->toWrapper(this); + self.setProperty("on" + type, listener); + } +} + + +QColor colorFromString(const QString &name); + +CanvasGradientPrototype::CanvasGradientPrototype(QObject *parent) + : QObject(parent) +{ +} + +void CanvasGradientPrototype::addColorStop(qreal offset, const QString &color) +{ + CanvasGradient *self = qscriptvalue_cast<CanvasGradient*>(thisObject()); + if (!self || (self->value.type() == QGradient::NoGradient)) + return; + self->value.setColorAt(offset, colorFromString(color)); +} + +void CanvasGradientPrototype::setup(QScriptEngine *engine) +{ + CanvasGradientPrototype *proto = new CanvasGradientPrototype(); + engine->setDefaultPrototype(qMetaTypeId<CanvasGradient>(), + engine->newQObject(proto, QScriptEngine::ScriptOwnership, + QScriptEngine::ExcludeSuperClassContents)); +} diff --git a/examples/script/context2d/environment.h b/examples/script/context2d/environment.h new file mode 100644 index 0000000..e47e6cb --- /dev/null +++ b/examples/script/context2d/environment.h @@ -0,0 +1,145 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ENVIRONMENT_H +#define ENVIRONMENT_H + +#include <qobject.h> +#include <qlist.h> +#include <qhash.h> +#include <QTimerEvent> +#include <QMouseEvent> +#include <QKeyEvent> +#include <QScriptEngine> +#include <QScriptable> +class QContext2DCanvas; + +//! [0] +class Environment : public QObject +{ + Q_OBJECT + Q_PROPERTY(QScriptValue document READ document) +public: + Environment(QObject *parent = 0); + ~Environment(); + + QScriptValue document() const; + + void addCanvas(QContext2DCanvas *canvas); + QContext2DCanvas *canvasByName(const QString &name) const; + QList<QContext2DCanvas*> canvases() const; + + QScriptValue evaluate(const QString &code, + const QString &fileName = QString()); + + QScriptValue toWrapper(QObject *object); + + void handleEvent(QContext2DCanvas *canvas, QMouseEvent *e); + void handleEvent(QContext2DCanvas *canvas, QKeyEvent *e); + + void reset(); +//! [0] + + QScriptEngine *engine() const; + +//! [1] +public slots: + int setInterval(const QScriptValue &expression, int delay); + void clearInterval(int timerId); + + int setTimeout(const QScriptValue &expression, int delay); + void clearTimeout(int timerId); +//! [1] + +//! [2] +signals: + void scriptError(const QScriptValue &error); +//! [2] + +protected: + void timerEvent(QTimerEvent *event); + +private: + QScriptValue eventHandler(QContext2DCanvas *canvas, + const QString &type, QScriptValue *who); + QScriptValue newFakeDomEvent(const QString &type, + const QScriptValue &target); + void maybeEmitScriptError(); + + QScriptEngine *m_engine; + QScriptValue m_originalGlobalObject; + QScriptValue m_document; + QList<QContext2DCanvas*> m_canvases; + QHash<int, QScriptValue> m_intervalHash; + QHash<int, QScriptValue> m_timeoutHash; +}; + +//! [3] +class Document : public QObject +{ + Q_OBJECT +public: + Document(Environment *env); + ~Document(); + +public slots: + QScriptValue getElementById(const QString &id) const; + QScriptValue getElementsByTagName(const QString &name) const; + + // EventTarget + void addEventListener(const QString &type, const QScriptValue &listener, + bool useCapture); +}; +//! [3] + +class CanvasGradientPrototype : public QObject, public QScriptable +{ + Q_OBJECT +protected: + CanvasGradientPrototype(QObject *parent = 0); +public: + static void setup(QScriptEngine *engine); + +public slots: + void addColorStop(qreal offset, const QString &color); +}; + +#endif diff --git a/examples/script/context2d/main.cpp b/examples/script/context2d/main.cpp new file mode 100644 index 0000000..78c9df9 --- /dev/null +++ b/examples/script/context2d/main.cpp @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "window.h" +#include <QApplication> + +int main(int argc, char **argv) +{ + Q_INIT_RESOURCE(context2d); + + QApplication app(argc, argv); + Window win; + win.show(); + return app.exec(); +} diff --git a/examples/script/context2d/qcontext2dcanvas.cpp b/examples/script/context2d/qcontext2dcanvas.cpp new file mode 100644 index 0000000..30bb3eb --- /dev/null +++ b/examples/script/context2d/qcontext2dcanvas.cpp @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qcontext2dcanvas.h" + +#include "context2d.h" +#include "environment.h" +#include "domimage.h" + +#include <QPainter> +#include <QPaintEvent> + +//! [3] +QContext2DCanvas::QContext2DCanvas(Context2D *context, Environment *env, QWidget *parent) + : QWidget(parent), m_context(context), m_env(env) +{ + QObject::connect(context, SIGNAL(changed(QImage)), this, SLOT(contentsChanged(QImage))); + setMouseTracking(true); +} +//! [3] + +QContext2DCanvas::~QContext2DCanvas() +{ +} + +Context2D *QContext2DCanvas::context() const +{ + return m_context; +} + +//! [0] +QScriptValue QContext2DCanvas::getContext(const QString &str) +{ + if (str != "2d") + return QScriptValue(); + return m_env->toWrapper(m_context); +} +//! [0] + +//! [1] +void QContext2DCanvas::contentsChanged(const QImage &image) +{ + m_image = image; + update(); +} + +void QContext2DCanvas::paintEvent(QPaintEvent *e) +{ + QPainter p(this); + p.setClipRect(e->rect()); + p.drawImage(0, 0, m_image); +} +//! [1] + +//! [2] +void QContext2DCanvas::mouseMoveEvent(QMouseEvent *e) +{ + m_env->handleEvent(this, e); +} + +void QContext2DCanvas::mousePressEvent(QMouseEvent *e) +{ + m_env->handleEvent(this, e); +} + +void QContext2DCanvas::mouseReleaseEvent(QMouseEvent *e) +{ + m_env->handleEvent(this, e); +} + +void QContext2DCanvas::keyPressEvent(QKeyEvent *e) +{ + m_env->handleEvent(this, e); +} + +void QContext2DCanvas::keyReleaseEvent(QKeyEvent *e) +{ + m_env->handleEvent(this, e); +} +//! [2] + +void QContext2DCanvas::resizeEvent(QResizeEvent *e) +{ + m_context->setSize(e->size().width(), e->size().height()); +} + +void QContext2DCanvas::resize(int width, int height) +{ + QWidget::resize(width, height); +} + +void QContext2DCanvas::reset() +{ + m_context->reset(); +} + +void QContext2DCanvas::addEventListener(const QString &type, const QScriptValue &listener, + bool useCapture) +{ + Q_UNUSED(useCapture); + if (listener.isFunction()) { + QScriptValue self = m_env->toWrapper(this); + self.setProperty("on" + type, listener); + } +} diff --git a/examples/script/context2d/qcontext2dcanvas.h b/examples/script/context2d/qcontext2dcanvas.h new file mode 100644 index 0000000..657ffc3 --- /dev/null +++ b/examples/script/context2d/qcontext2dcanvas.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCONTEXT2DCANVAS_H +#define QCONTEXT2DCANVAS_H + +#include <qscriptengine.h> +#include <qscriptcontext.h> +#include <qscriptvalue.h> + +#include <QWidget> + +QT_BEGIN_NAMESPACE +class QPaintEvent; +class QResizeEvent; +class QMouseEvent; +class QKeyEvent; +QT_END_NAMESPACE +class Environment; +class Context2D; + +//! [0] +class QContext2DCanvas : public QWidget +{ + Q_OBJECT +public: + QContext2DCanvas(Context2D *context, Environment *env, QWidget *parent = 0); + ~QContext2DCanvas(); + + Context2D *context() const; + void reset(); + +public slots: + QScriptValue getContext(const QString &str); + void resize(int width, int height); + + // EventTarget + void addEventListener(const QString &type, const QScriptValue &listener, + bool useCapture); + +protected: + virtual void paintEvent(QPaintEvent *e); + virtual void mouseMoveEvent(QMouseEvent *e); + virtual void mousePressEvent(QMouseEvent *e); + virtual void mouseReleaseEvent(QMouseEvent *e); + virtual void keyPressEvent(QKeyEvent *e); + virtual void keyReleaseEvent(QKeyEvent *e); + virtual void resizeEvent(QResizeEvent *e); + +private slots: + void contentsChanged(const QImage &image); +//! [0] + +private: + Context2D *m_context; + Environment *m_env; + QImage m_image; +}; + +#endif diff --git a/examples/script/context2d/scripts/alpha.js b/examples/script/context2d/scripts/alpha.js new file mode 100644 index 0000000..23fa5d4 --- /dev/null +++ b/examples/script/context2d/scripts/alpha.js @@ -0,0 +1,21 @@ +var ctx = document.getElementById('tutorial').getContext('2d'); + // draw background + ctx.fillStyle = '#FD0'; + ctx.fillRect(0,0,75,75); + ctx.fillStyle = '#6C0'; + ctx.fillRect(75,0,75,75); + ctx.fillStyle = '#09F'; + ctx.fillRect(0,75,75,75); + ctx.fillStyle = '#F30'; + ctx.fillRect(75,75,75,75); + ctx.fillStyle = '#FFF'; + + // set transparency value + ctx.globalAlpha = 0.2; + + // Draw semi transparent circles + for (i=0;i<7;i++){ + ctx.beginPath(); + ctx.arc(75,75,10+10*i,0,Math.PI*2,true); + ctx.fill(); + } diff --git a/examples/script/context2d/scripts/arc.js b/examples/script/context2d/scripts/arc.js new file mode 100644 index 0000000..650bcda --- /dev/null +++ b/examples/script/context2d/scripts/arc.js @@ -0,0 +1,30 @@ +var canvas = document.getElementById('tutorial'); + + // Make sure we don't execute when canvas isn't supported + if (canvas.getContext){ + + // use getContext to use the canvas for drawing + var ctx = canvas.getContext('2d'); + + // Draw shapes + for (i=0;i<4;i++){ + for(j=0;j<3;j++){ + ctx.beginPath(); + var x = 25+j*50; // x coordinate + var y = 25+i*50; // y coordinate + var radius = 20; // Arc radius + var startAngle = 0; // Starting point on circle + var endAngle = Math.PI+(Math.PI*j)/2; // End point on circle + var clockwise = i%2==0 ? false : true; // clockwise or anticlockwise + + ctx.arc(x,y,radius,startAngle,endAngle, clockwise); + + if (i>1){ + ctx.fill(); + } else { + ctx.stroke(); + } + } + } + + } diff --git a/examples/script/context2d/scripts/bezier.js b/examples/script/context2d/scripts/bezier.js new file mode 100644 index 0000000..719800e --- /dev/null +++ b/examples/script/context2d/scripts/bezier.js @@ -0,0 +1,26 @@ +function drawShape() { +// get the canvas element using the DOM +var canvas = document.getElementById('tutorial'); + + // Make sure we don't execute when canvas isn't supported + if (canvas.getContext){ + + // use getContext to use the canvas for drawing + var ctx = canvas.getContext('2d'); + + // Draw shapes + + ctx.beginPath(); + ctx.moveTo(75,40); + ctx.bezierCurveTo(75,37,70,25,50,25); + ctx.bezierCurveTo(20,25,20,62.5,20,62.5); + ctx.bezierCurveTo(20,80,40,102,75,120); + ctx.bezierCurveTo(110,102,130,80,130,62.5); + ctx.bezierCurveTo(130,62.5,130,25,100,25); + ctx.bezierCurveTo(85,25,75,37,75,40); + ctx.fill(); + ctx.globalAlpha = 0.5; + } +} + +drawShape(); diff --git a/examples/script/context2d/scripts/clock.js b/examples/script/context2d/scripts/clock.js new file mode 100644 index 0000000..ad287d8 --- /dev/null +++ b/examples/script/context2d/scripts/clock.js @@ -0,0 +1,99 @@ +function init(){ + clock(); + setInterval('clock()',1000); +} +function clock(){ + var now = new Date(); + var ctx = document.getElementById('tutorial').getContext('2d'); + ctx.save(); + ctx.clearRect(0,0,150,150); + ctx.translate(75,75); + ctx.scale(0.4,0.4); + ctx.rotate(-Math.PI/2); + ctx.strokeStyle = "black"; + ctx.fillStyle = "white"; + ctx.lineWidth = 8; + ctx.lineCap = "round"; + + // Hour marks + ctx.save(); + ctx.beginPath(); + for (i=0;i<12;i++){ + ctx.rotate(Math.PI/6); + ctx.moveTo(100,0); + ctx.lineTo(120,0); + } + ctx.stroke(); + ctx.restore(); + + // Minute marks + ctx.save(); + ctx.lineWidth = 5; + ctx.beginPath(); + for (i=0;i<60;i++){ + if (i%5!=0) { + ctx.moveTo(117,0); + ctx.lineTo(120,0); + } + ctx.rotate(Math.PI/30); + } + ctx.stroke(); + ctx.restore(); + + var sec = now.getSeconds(); + var min = now.getMinutes(); + var hr = now.getHours(); + hr = hr>=12 ? hr-12 : hr; + + ctx.fillStyle = "black"; + + // write Hours + ctx.save(); + ctx.rotate( hr*(Math.PI/6) + (Math.PI/360)*min + (Math.PI/21600)*sec ) + ctx.lineWidth = 14; + ctx.beginPath(); + ctx.moveTo(-20,0); + ctx.lineTo(80,0); + ctx.stroke(); + ctx.restore(); + + // write Minutes + ctx.save(); + ctx.rotate( (Math.PI/30)*min + (Math.PI/1800)*sec ) + ctx.lineWidth = 10; + ctx.beginPath(); + ctx.moveTo(-28,0); + ctx.lineTo(112,0); + ctx.stroke(); + ctx.restore(); + + // Write seconds + ctx.save(); + ctx.rotate(sec * Math.PI/30); + ctx.strokeStyle = "#D40000"; + ctx.fillStyle = "#D40000"; + ctx.lineWidth = 6; + ctx.beginPath(); + ctx.moveTo(-30,0); + ctx.lineTo(83,0); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(0,0,10,0,Math.PI*2,true); + ctx.fill(); + ctx.beginPath(); + ctx.arc(95,0,10,0,Math.PI*2,true); + ctx.stroke(); + ctx.fillStyle = "#555"; + ctx.arc(0,0,3,0,Math.PI*2,true); + ctx.fill(); + ctx.restore(); + + ctx.beginPath(); + ctx.lineWidth = 14; + ctx.strokeStyle = '#325FA2'; + ctx.arc(0,0,142,0,Math.PI*2,true); + ctx.stroke(); + + ctx.restore(); +} +init(); diff --git a/examples/script/context2d/scripts/fill1.js b/examples/script/context2d/scripts/fill1.js new file mode 100644 index 0000000..db5eeb7 --- /dev/null +++ b/examples/script/context2d/scripts/fill1.js @@ -0,0 +1,8 @@ +var ctx = document.getElementById('tutorial').getContext('2d'); + for (i=0;i<6;i++){ + for (j=0;j<6;j++){ + ctx.fillStyle = 'rgb(' + Math.floor(255-42.5*i) + ',' + + Math.floor(255-42.5*j) + ',0)'; + ctx.fillRect(j*25,i*25,25,25); + } + } diff --git a/examples/script/context2d/scripts/grad.js b/examples/script/context2d/scripts/grad.js new file mode 100644 index 0000000..24ccfbb --- /dev/null +++ b/examples/script/context2d/scripts/grad.js @@ -0,0 +1,20 @@ + var ctx = document.getElementById('tutorial').getContext('2d'); + + // Create gradients + var lingrad = ctx.createLinearGradient(0,0,0,150); + lingrad.addColorStop(0, '#00ABEB'); + lingrad.addColorStop(0.5, '#fff'); + lingrad.addColorStop(0.5, '#66CC00'); + lingrad.addColorStop(1, '#fff'); + + var lingrad2 = ctx.createLinearGradient(0,50,0,95); + lingrad2.addColorStop(0.5, '#000'); + lingrad2.addColorStop(1, 'rgba(0,0,0,0)'); + + // assign gradients to fill and stroke styles + ctx.fillStyle = lingrad; + ctx.strokeStyle = lingrad2; + + // draw shapes + ctx.fillRect(10,10,130,130); + ctx.strokeRect(50,50,50,50); diff --git a/examples/script/context2d/scripts/linecap.js b/examples/script/context2d/scripts/linecap.js new file mode 100644 index 0000000..18ceb23 --- /dev/null +++ b/examples/script/context2d/scripts/linecap.js @@ -0,0 +1,24 @@ +var ctx = document.getElementById('tutorial').getContext('2d'); + var lineCap = ['butt','round','square']; + + // Draw guides + ctx.save(); + ctx.strokeStyle = '#09f'; + ctx.beginPath(); + ctx.moveTo(10,10); + ctx.lineTo(140,10); + ctx.moveTo(10,140); + ctx.lineTo(140,140); + ctx.stroke(); + + // Draw lines + ctx.strokeStyle = 'black'; + for (i=0;i<lineCap.length;i++){ + ctx.lineWidth = 15; + ctx.lineCap = lineCap[i]; + ctx.beginPath(); + ctx.moveTo(25+i*50,10); + ctx.lineTo(25+i*50,140); + ctx.stroke(); + } + ctx.restore(); diff --git a/examples/script/context2d/scripts/linestye.js b/examples/script/context2d/scripts/linestye.js new file mode 100644 index 0000000..728e3e6 --- /dev/null +++ b/examples/script/context2d/scripts/linestye.js @@ -0,0 +1,10 @@ +var ctx = document.getElementById('tutorial').getContext('2d'); +ctx.save(); + for (i=0;i<10;i++){ + ctx.lineWidth = 1+i; + ctx.beginPath(); + ctx.moveTo(5+i*14,5); + ctx.lineTo(5+i*14,140); + ctx.stroke(); + } +ctx.restore();
\ No newline at end of file diff --git a/examples/script/context2d/scripts/moveto.js b/examples/script/context2d/scripts/moveto.js new file mode 100644 index 0000000..73c4e8d --- /dev/null +++ b/examples/script/context2d/scripts/moveto.js @@ -0,0 +1,20 @@ +var canvas = document.getElementById('tutorial'); + + // Make sure we don't execute when canvas isn't supported + if (canvas.getContext){ + + // use getContext to use the canvas for drawing + var ctx = canvas.getContext('2d'); + + // Draw shapes + ctx.beginPath(); + ctx.arc(75,75,50,0,Math.PI*2,true); // Outer circle + ctx.moveTo(110,75); + ctx.arc(75,75,35,0,Math.PI,false); // Mouth + ctx.moveTo(65,65); + ctx.arc(60,65,5,0,Math.PI*2,true); // Left eye + ctx.moveTo(95,65); + ctx.arc(90,65,5,0,Math.PI*2,true); // Right eye + ctx.stroke(); + + } diff --git a/examples/script/context2d/scripts/moveto2.js b/examples/script/context2d/scripts/moveto2.js new file mode 100644 index 0000000..021f47b --- /dev/null +++ b/examples/script/context2d/scripts/moveto2.js @@ -0,0 +1,24 @@ +var canvas = document.getElementById('tutorial'); + + // Make sure we don't execute when canvas isn't supported + if (canvas.getContext){ + + // use getContext to use the canvas for drawing + var ctx = canvas.getContext('2d'); + + // Filled triangle + ctx.beginPath(); + ctx.moveTo(25,25); + ctx.lineTo(105,25); + ctx.lineTo(25,105); + ctx.fill(); + + // Stroked triangle + ctx.beginPath(); + ctx.moveTo(125,125); + ctx.lineTo(125,45); + ctx.lineTo(45,125); + ctx.closePath(); + ctx.stroke(); + + } diff --git a/examples/script/context2d/scripts/pacman.js b/examples/script/context2d/scripts/pacman.js new file mode 100644 index 0000000..af3750f --- /dev/null +++ b/examples/script/context2d/scripts/pacman.js @@ -0,0 +1,83 @@ +function roundedRect(ctx,x,y,width,height,radius){ + ctx.beginPath(); + ctx.moveTo(x,y+radius); + ctx.lineTo(x,y+height-radius); + ctx.quadraticCurveTo(x,y+height,x+radius,y+height); + ctx.lineTo(x+width-radius,y+height); + ctx.quadraticCurveTo(x+width,y+height,x+width,y+height-radius); + ctx.lineTo(x+width,y+radius); + ctx.quadraticCurveTo(x+width,y,x+width-radius,y); + ctx.lineTo(x+radius,y); + ctx.quadraticCurveTo(x,y,x,y+radius); + ctx.stroke(); +} + +var canvas = document.getElementById('tutorial'); + + // Make sure we don't execute when canvas isn't supported + if (canvas.getContext){ + + // use getContext to use the canvas for drawing + var ctx = canvas.getContext('2d'); + + // Draw shapes + roundedRect(ctx,12,12,150,150,15); + roundedRect(ctx,19,19,150,150,9); + roundedRect(ctx,53,53,49,33,10); + roundedRect(ctx,53,119,49,16,6); + roundedRect(ctx,135,53,49,33,10); + roundedRect(ctx,135,119,25,49,10); + + // Character 1 + ctx.beginPath(); + ctx.arc(37,37,13,Math.PI/7,-Math.PI/7,false); + ctx.lineTo(34,37); + ctx.fill(); + + // blocks + for(i=0;i<8;i++){ + ctx.fillRect(51+i*16,35,4,4); + } + for(i=0;i<6;i++){ + ctx.fillRect(115,51+i*16,4,4); + } + for(i=0;i<8;i++){ + ctx.fillRect(51+i*16,99,4,4); + } + + // character 2 + ctx.beginPath(); + ctx.moveTo(83,116); + ctx.lineTo(83,102); + + ctx.bezierCurveTo(83,94,89,88,97,88); + ctx.bezierCurveTo(105,88,111,94,111,102); + ctx.lineTo(111,116); + ctx.lineTo(106.333,111.333); + ctx.lineTo(101.666,116); + ctx.lineTo(97,111.333); + ctx.lineTo(92.333,116); + ctx.lineTo(87.666,111.333); + ctx.lineTo(83,116); + ctx.fill(); + ctx.fillStyle = "white"; + ctx.beginPath(); + ctx.moveTo(91,96); + ctx.bezierCurveTo(88,96,87,99,87,101); + ctx.bezierCurveTo(87,103,88,106,91,106); + ctx.bezierCurveTo(94,106,95,103,95,101); + ctx.bezierCurveTo(95,99,94,96,91,96); + ctx.moveTo(103,96); + ctx.bezierCurveTo(100,96,99,99,99,101); + ctx.bezierCurveTo(99,103,100,106,103,106); + ctx.bezierCurveTo(106,106,107,103,107,101); + ctx.bezierCurveTo(107,99,106,96,103,96); + ctx.fill(); + ctx.fillStyle = "black"; + ctx.beginPath(); + ctx.arc(101,102,2,0,Math.PI*2,true); + ctx.fill(); + ctx.beginPath(); + ctx.arc(89,102,2,0,Math.PI*2,true); + ctx.fill(); + } diff --git a/examples/script/context2d/scripts/plasma.js b/examples/script/context2d/scripts/plasma.js new file mode 100644 index 0000000..1aa9294 --- /dev/null +++ b/examples/script/context2d/scripts/plasma.js @@ -0,0 +1,58 @@ +var counter = 0; + +var PIXEL_SIZE = 4; + +var temp_1 = 0; + +function init() +{ + setInterval('render()',50); +} + +function dist(a, b, c, d) +{ + return Math.sqrt((a - c) * (a - c) + (b - d) * (b - d)); +} + + +function render() +{ + var canvas = document.getElementById('tutorial'); + canvas.resize(128, 128); + var ctx = canvas.getContext('2d'); + ctx.save(); + + var time = counter * 5; + + + for( y = 0; y < 128; y+=PIXEL_SIZE) { + for( x = 0 ; x < 128; x+=PIXEL_SIZE) { + + + var temp_val = Math.floor(Math.sin(dist(x + time, y, 128.0, 128.0) / 8.0) + + Math.sin(dist(x, y, 64.0, 64.0) / 8.0) + + Math.sin(dist(x, y + time / 7, 192.0, 64) / 7.0) + + Math.sin(dist(x, y, 192.0, 100.0) / 8.0)); + + + + + var temp_col = Math.floor((2 + temp_val) * 50); + + var rand_red = temp_col * 3; + var rand_green = temp_col ; + var rand_blue = 128 - temp_col; + + ctx.fillStyle = "rgb("+rand_red+","+rand_green+","+rand_blue+")"; + + ctx.fillRect(x,y,PIXEL_SIZE,PIXEL_SIZE); + } + } + + + ctx.restore(); + counter++; + +} + +init(); diff --git a/examples/script/context2d/scripts/pong.js b/examples/script/context2d/scripts/pong.js new file mode 100644 index 0000000..2bff053 --- /dev/null +++ b/examples/script/context2d/scripts/pong.js @@ -0,0 +1,235 @@ +// globals +playarea_canvas = document.getElementById('tutorial'); +playarea_canvas.resize(320,200); +playarea = playarea_canvas.getContext('2d'); +//p1_scr = document.getElementById('p1_scr'); +//p2_scr = document.getElementById('p2_scr'); +//status_msg = document.getElementById('status'); +//debug = document.getElementById('debug'); +ball_direction = 0; +up = -1; +down = 1; + +//key codes +key_up = 38; +key_down = 40; +key_W = 87; +key_S = 83; +key_pause = 32; + +speed = 2; //controls the speed of the ball +paddle_inc = 10; //how many pixels paddle can move in either direction +pause = false; + +player_1 = 0; //player IDs +player_2 = 1; +player_1_scr = 0; //player scores +player_2_scr = 0; +player_1_direction = null; //null = no movement whatsoever +player_2_direction = null; + +pa = new Array(); +divider = new Array(); +paddle_1 = new Array(); +paddle_2 = new Array(); +ball = new Array(); + + +function sleep(numberMillis) +{ + var now = new Date(); + var exitTime = now.getTime() + numberMillis; + while (true) { + now = new Date(); + if (now.getTime() > exitTime) + return; + } +} + +function init() +{ + pa['width'] = 150; + pa['height'] = 140; + pa['player_margin'] = 10; //area behind player paddles + pa['foreground'] = "#FFFFFF"; + pa['background'] = "#000000"; + + divider['pos'] = pa['width']/2; + divider['width'] = 4; + + paddle_1['width'] = 8; + paddle_1['height'] = 64; + paddle_1['x'] = pa['player_margin']; + paddle_1['y'] = (pa['height'] /2 ) - (paddle_1['height'] / 2); + + paddle_2['width'] = 8; + paddle_2['height'] = 64; + paddle_2['x'] = (pa['width'] - pa['player_margin'] - paddle_2['width']); + paddle_2['y'] = (pa['height'] /2 ) - (paddle_2['height'] / 2); + + ball['width'] = 10; + ball['height'] = 10; + ball['x'] = (pa['width']/2) - (ball['width'] / 2); + ball['y'] = (pa['height']/2) - (ball['height'] / 2); + + ball_direction = Math.random() * 360; //initialize ball direction, which is determined by angle, at random + speed = 2; +} + +function renderPlayarea() +{ + playarea.beginPath(); + + playarea.clearRect(0,0,pa['width'],pa['height']); + playarea.fillStyle = pa['background']; + playarea.strokeStyle = pa['foreground']; + playarea.fillRect(0,0, pa['width'], pa['height']); + + //move paddles + if(player_1_direction != null) + { + if(player_1_direction == up) + paddle_1['y'] = paddle_1['y'] - paddle_inc; + else + paddle_1['y'] = paddle_1['y'] + paddle_inc; + } + if(player_2_direction != null) + { + if(player_2_direction == up) + paddle_2['y'] = paddle_2['y'] - paddle_inc; + else + paddle_2['y'] = paddle_2['y'] + paddle_inc; + } + playarea.rect(paddle_1['x'],paddle_1['y'],paddle_1['width'],paddle_1['height']); + playarea.rect(paddle_2['x'],paddle_2['y'],paddle_2['width'],paddle_2['height']); + + //move ball + playarea.rect(ball['x'], ball['y'], ball['width'], ball['height']); + ball['x'] = ball['x'] + Math.cos((ball_direction)*Math.PI/180) * speed; + ball['y'] = ball['y'] + Math.sin((ball_direction)*Math.PI/180) * speed; + + playarea.fillStyle = pa['foreground']; + playarea.fill(); + + playarea.beginPath(); + //redraw divider + playarea.lineWidth = divider['width']; + playarea.lineTo(divider['pos'], 0); + playarea.lineTo(divider['pos'], pa['height'] = 200); + playarea.lineWidth = 1; + + playarea.stroke(); + playarea.closePath(); +} + +function testCollisions() +{ + //make sure paddles don't go beyond play area + if(((paddle_1['y'] <= 0) && (player_1_direction == up)) || ((paddle_1['y'] >= (pa['height'] - paddle_1['height'])) && (player_1_direction == down))) + player_1_direction = null; + if(((paddle_2['y'] <= 0) && (player_2_direction == up)) || ((paddle_2['y'] >= (pa['height'] - paddle_2['height'])) && (player_2_direction == down))) + player_2_direction = null; + + //check to see if ball went beyond paddles, and if so, score accordingly and reset playarea + if(ball['x'] <= 0) + { + setScore(player_2); + init() + sleep(1000); + } + if(ball['x'] >= (pa['width'] - ball['width'])) + { + setScore(player_1); + init(); + sleep(1000); + } + + //check to see if ball hit top or bottom wall. if so, change direction + if((ball['y'] >= (pa['height'] - ball['height'])) || ball['y'] <= 0) + ball_direction = -ball_direction; + + //check to see if the ball hit a paddle, and if so, change ball angle dependant on where it hit the paddle + if((ball['x'] <= (paddle_1['x'] + paddle_1['width'])) && (ball['y'] >= paddle_1['y']) && (ball['y'] <= (paddle_1['y'] + paddle_1['height']))) + { + ball_direction = -ball_direction/2; + speed += .5; + } + if(((ball['x'] + ball['width']) >= paddle_2['x']) && (ball['y'] >= paddle_2['y']) && (ball['y'] <= (paddle_2['y'] + paddle_2['height']))) + { + ball_direction = (180+ball_direction)/2; + speed += .5; + } +} + +function setScore(p) +{ + if(p == player_1) + { + player_1_scr++; + //p1_scr.firstChild.nodeValue = player_1_scr; + } + if(p == player_2) + { + player_2_scr++; + //p2_scr.firstChild.nodeValue = player_2_scr; + } +} + + +//handle input +document.onkeydown = function(ev) +{ + switch(ev.keyCode) + { + case key_W: + player_1_direction = up; + break; + case key_S: + player_1_direction = down; + break; + case key_up: + player_2_direction = up; + break; + case key_down: + player_2_direction = down; + break; + } +} + +document.onkeyup = function(ev) +{ + switch(ev.keyCode) + { + case key_W: + case key_S: + player_1_direction = null; + break; + case key_up: + case key_down: + player_2_direction = null; + break; + case key_pause: + if(pause == false) + { + clearInterval(game); + //status_msg.style.visibility = "visible"; + pause = true; + } + else + { + game = setInterval(main, 25); + //status_msg.style.visibility = "hidden"; + pause = false; + } + break; + } +} + +function main() +{ + testCollisions(); + renderPlayarea(); +} + +init(); +game = setInterval(main, 25); diff --git a/examples/script/context2d/scripts/quad.js b/examples/script/context2d/scripts/quad.js new file mode 100644 index 0000000..ad3a0d5 --- /dev/null +++ b/examples/script/context2d/scripts/quad.js @@ -0,0 +1,21 @@ +var canvas = document.getElementById('tutorial'); + + // Make sure we don't execute when canvas isn't supported + if (canvas.getContext){ + + // use getContext to use the canvas for drawing + var ctx = canvas.getContext('2d'); + + // Draw shapes + + ctx.beginPath(); + ctx.moveTo(75,25); + ctx.quadraticCurveTo(25,25,25,62.5); + ctx.quadraticCurveTo(25,100,50,100); + ctx.quadraticCurveTo(50,120,30,125); + ctx.quadraticCurveTo(60,120,65,100); + ctx.quadraticCurveTo(125,100,125,62.5); + ctx.quadraticCurveTo(125,25,75,25); + ctx.stroke(); + + } diff --git a/examples/script/context2d/scripts/rgba.js b/examples/script/context2d/scripts/rgba.js new file mode 100644 index 0000000..a4e5e9a --- /dev/null +++ b/examples/script/context2d/scripts/rgba.js @@ -0,0 +1,19 @@ +var ctx = document.getElementById('tutorial').getContext('2d'); + + // Draw background + ctx.fillStyle = 'rgb(255,221,0)'; + ctx.fillRect(0,0,150,37.5); + ctx.fillStyle = 'rgb(102,204,0)'; + ctx.fillRect(0,37.5,150,37.5); + ctx.fillStyle = 'rgb(0,153,255)'; + ctx.fillRect(0,75,150,37.5); + ctx.fillStyle = 'rgb(255,51,0)'; + ctx.fillRect(0,112.5,150,37.5); + + // Draw semi transparent rectangles + for (i=0;i<10;i++){ + ctx.fillStyle = 'rgba(255,255,255,'+(i+1)/10+')'; + for (j=0;j<4;j++){ + ctx.fillRect(5+i*14,5+j*37.5,14,27.5) + } + } diff --git a/examples/script/context2d/scripts/rotate.js b/examples/script/context2d/scripts/rotate.js new file mode 100644 index 0000000..c7ef369 --- /dev/null +++ b/examples/script/context2d/scripts/rotate.js @@ -0,0 +1,16 @@ +var ctx = document.getElementById('tutorial').getContext('2d'); + ctx.translate(75,75); + + for (i=1;i<6;i++){ // Loop through rings (from inside to out) + ctx.save(); + ctx.fillStyle = 'rgb('+(51*i)+','+(255-51*i)+',255)'; + + for (j=0;j<i*6;j++){ // draw individual dots + ctx.rotate(Math.PI*2/(i*6)); + ctx.beginPath(); + ctx.arc(0,i*12.5,5,0,Math.PI*2,true); + ctx.fill(); + } + + ctx.restore(); + } diff --git a/examples/script/context2d/scripts/scale.js b/examples/script/context2d/scripts/scale.js new file mode 100644 index 0000000..75ef865 --- /dev/null +++ b/examples/script/context2d/scripts/scale.js @@ -0,0 +1,67 @@ +var ctx = document.getElementById('tutorial').getContext('2d'); + ctx.strokeStyle = "#fc0"; + ctx.lineWidth = 1.5; + ctx.fillRect(0,0,300,300); + + // Uniform scaling + ctx.save() + ctx.translate(50,50); + drawSpirograph(ctx,22,6,5); // no scaling + + ctx.translate(100,0); + ctx.scale(0.75,0.75); + drawSpirograph(ctx,22,6,5); + + ctx.translate(133.333,0); + ctx.scale(0.75,0.75); + drawSpirograph(ctx,22,6,5); + ctx.restore(); + + // Non uniform scaling (y direction) + ctx.strokeStyle = "#0cf"; + ctx.save() + ctx.translate(50,150); + ctx.scale(1,0.75); + drawSpirograph(ctx,22,6,5); + + ctx.translate(100,0); + ctx.scale(1,0.75); + drawSpirograph(ctx,22,6,5); + + ctx.translate(100,0); + ctx.scale(1,0.75); + drawSpirograph(ctx,22,6,5); + ctx.restore(); + + // Non uniform scaling (x direction) + ctx.strokeStyle = "#cf0"; + ctx.save() + ctx.translate(50,250); + ctx.scale(0.75,1); + drawSpirograph(ctx,22,6,5); + + ctx.translate(133.333,0); + ctx.scale(0.75,1); + drawSpirograph(ctx,22,6,5); + + ctx.translate(177.777,0); + ctx.scale(0.75,1); + drawSpirograph(ctx,22,6,5); + ctx.restore(); +function drawSpirograph(ctx,R,r,O){ + var x1 = R-O; + var y1 = 0; + var i = 1; + ctx.beginPath(); + ctx.moveTo(x1,y1); + do { + if (i>20000) break; + var x2 = (R+r)*Math.cos(i*Math.PI/72) - (r+O)*Math.cos(((R+r)/r)*(i*Math.PI/72)) + var y2 = (R+r)*Math.sin(i*Math.PI/72) - (r+O)*Math.sin(((R+r)/r)*(i*Math.PI/72)) + ctx.lineTo(x2,y2); + x1 = x2; + y1 = y2; + i++; + } while (x2 != R-O && y2 != 0 ); + ctx.stroke(); +} diff --git a/examples/script/context2d/scripts/stroke1.js b/examples/script/context2d/scripts/stroke1.js new file mode 100644 index 0000000..0561a52 --- /dev/null +++ b/examples/script/context2d/scripts/stroke1.js @@ -0,0 +1,10 @@ +var ctx = document.getElementById('tutorial').getContext('2d'); + for (i=0;i<6;i++){ + for (j=0;j<6;j++){ + ctx.strokeStyle = 'rgb(0,' + Math.floor(255-42.5*i) + ',' + + Math.floor(255-42.5*j) + ')'; + ctx.beginPath(); + ctx.arc(12.5+j*25,12.5+i*25,10,0,Math.PI*2,true); + ctx.stroke(); + } + } diff --git a/examples/script/context2d/scripts/translate.js b/examples/script/context2d/scripts/translate.js new file mode 100644 index 0000000..7c94433 --- /dev/null +++ b/examples/script/context2d/scripts/translate.js @@ -0,0 +1,29 @@ + var ctx = document.getElementById('tutorial').getContext('2d'); + ctx.fillRect(0,0,300,300); + for (var i=0;i<3;i++) { + for (var j=0;j<3;j++) { + ctx.save(); + ctx.strokeStyle = "#9CFF00"; + ctx.translate(50+j*100,50+i*100); + drawSpirograph(ctx,20*(j+2)/(j+1),-8*(i+3)/(i+1),10); + ctx.restore(); + } + } + +function drawSpirograph(ctx,R,r,O){ + var x1 = R-O; + var y1 = 0; + var i = 1; + ctx.beginPath(); + ctx.moveTo(x1,y1); + do { + if (i>20000) break; + var x2 = (R+r)*Math.cos(i*Math.PI/72) - (r+O)*Math.cos(((R+r)/r)*(i*Math.PI/72)) + var y2 = (R+r)*Math.sin(i*Math.PI/72) - (r+O)*Math.sin(((R+r)/r)*(i*Math.PI/72)) + ctx.lineTo(x2,y2); + x1 = x2; + y1 = y2; + i++; + } while (x2 != R-O && y2 != 0 ); + ctx.stroke(); +} diff --git a/examples/script/context2d/window.cpp b/examples/script/context2d/window.cpp new file mode 100644 index 0000000..a93bf30 --- /dev/null +++ b/examples/script/context2d/window.cpp @@ -0,0 +1,174 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "window.h" +#include "environment.h" +#include "context2d.h" +#include "qcontext2dcanvas.h" +#include <QHBoxLayout> +#include <QListWidget> +#include <QDir> +#include <QMessageBox> + +#ifndef QT_NO_SCRIPTTOOLS +#include <QAction> +#include <QApplication> +#include <QMainWindow> +#include <QPushButton> +#include <QVBoxLayout> +#include <QScriptEngineDebugger> +#endif + +static QString scriptsDir() +{ + if (QFile::exists("./scripts")) + return "./scripts"; + return ":/scripts"; +} + +//! [0] +Window::Window(QWidget *parent) + : QWidget(parent) +{ + m_env = new Environment(this); + QObject::connect(m_env, SIGNAL(scriptError(QScriptValue)), + this, SLOT(reportScriptError(QScriptValue))); + + Context2D *context = new Context2D(this); + context->setSize(150, 150); + m_canvas = new QContext2DCanvas(context, m_env, this); + m_canvas->setFixedSize(context->size()); + m_canvas->setObjectName("tutorial"); + m_env->addCanvas(m_canvas); +//! [0] + +#ifndef QT_NO_SCRIPTTOOLS + QVBoxLayout *vbox = new QVBoxLayout(); + vbox->addWidget(m_canvas); + m_debugButton = new QPushButton(tr("Run in Debugger")); + connect(m_debugButton, SIGNAL(clicked()), this, SLOT(runInDebugger())); + vbox->addWidget(m_debugButton); +#endif + + QHBoxLayout *hbox = new QHBoxLayout(this); + m_view = new QListWidget(this); + m_view->setEditTriggers(QAbstractItemView::NoEditTriggers); + hbox->addWidget(m_view); +#ifndef QT_NO_SCRIPTTOOLS + hbox->addLayout(vbox); +#else + hbox->addWidget(m_canvas); +#endif + +//! [1] + QDir dir(scriptsDir()); + QFileInfoList entries = dir.entryInfoList(QStringList() << "*.js"); + for (int i = 0; i < entries.size(); ++i) + m_view->addItem(entries.at(i).fileName()); + connect(m_view, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)), + this, SLOT(selectScript(QListWidgetItem*))); +//! [1] + +#ifndef QT_NO_SCRIPTTOOLS + m_debugger = new QScriptEngineDebugger(this); + m_debugger->attachTo(m_env->engine()); + m_debugWindow = m_debugger->standardWindow(); + m_debugWindow->setWindowModality(Qt::ApplicationModal); + m_debugWindow->resize(1280, 704); +#endif + + setWindowTitle(tr("Context 2D")); +} + +//! [2] +void Window::selectScript(QListWidgetItem *item) +{ + QString fileName = item->text(); + runScript(fileName, /*debug=*/false); +} +//! [2] + +void Window::reportScriptError(const QScriptValue &error) +{ + QMessageBox::warning(this, tr("Context 2D"), tr("Line %0: %1") + .arg(error.property("lineNumber").toInt32()) + .arg(error.toString())); +} + +#ifndef QT_NO_SCRIPTTOOLS +//! [3] +void Window::runInDebugger() +{ + QListWidgetItem *item = m_view->currentItem(); + if (item) { + QString fileName = item->text(); + runScript(fileName, /*debug=*/true); + } +} +//! [3] +#endif + +//! [4] +void Window::runScript(const QString &fileName, bool debug) +{ + QFile file(scriptsDir() + "/" + fileName); + file.open(QIODevice::ReadOnly); + QString contents = file.readAll(); + file.close(); + m_env->reset(); + +#ifndef QT_NO_SCRIPTTOOLS + if (debug) + m_debugger->action(QScriptEngineDebugger::InterruptAction)->trigger(); +#else + Q_UNUSED(debug); +#endif + + QScriptValue ret = m_env->evaluate(contents, fileName); + +#ifndef QT_NO_SCRIPTTOOLS + m_debugWindow->hide(); +#endif + + if (ret.isError()) + reportScriptError(ret); +} +//! [4] diff --git a/examples/script/context2d/window.h b/examples/script/context2d/window.h new file mode 100644 index 0000000..a0aff8e --- /dev/null +++ b/examples/script/context2d/window.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef WINDOW_H +#define WINDOW_H + +#include <QWidget> +class Environment; +class QContext2DCanvas; +QT_BEGIN_NAMESPACE +class QListWidget; +class QListWidgetItem; +class QMainWindow; +class QPushButton; +class QScriptValue; +class QScriptEngineDebugger; +QT_END_NAMESPACE + +class Window : public QWidget +{ + Q_OBJECT +public: + Window(QWidget *parent = 0); +private slots: + void selectScript(QListWidgetItem *item); + void reportScriptError(const QScriptValue &value); +#ifndef QT_NO_SCRIPTTOOLS + void runInDebugger(); +#endif +private: + void runScript(const QString &fileName, bool debug); + + QListWidget *m_view; + Environment *m_env; + QContext2DCanvas *m_canvas; +#ifndef QT_NO_SCRIPTTOOLS + QScriptEngineDebugger *m_debugger; + QPushButton *m_debugButton; + QMainWindow *m_debugWindow; +#endif +}; + +#endif // WINDOW_H |