diff options
18 files changed, 172 insertions, 464 deletions
diff --git a/examples/quick/canvas/bezierCurve/bezierCurve.qml b/examples/quick/canvas/bezierCurve/bezierCurve.qml index f18c4a1608..6337b12eec 100644 --- a/examples/quick/canvas/bezierCurve/bezierCurve.qml +++ b/examples/quick/canvas/bezierCurve/bezierCurve.qml @@ -64,8 +64,6 @@ Item { property real scaleY : scaleYCtrl.value property real rotate : rotateCtrl.value smooth:true - renderTarget:Canvas.Image - renderStrategy: Canvas.Immediate Behavior on scaleX { SpringAnimation { spring: 2; damping: 0.2; loops:Animation.Infinite } } diff --git a/examples/quick/canvas/clip/clip.qml b/examples/quick/canvas/clip/clip.qml index 3fdc3e4906..55c7913476 100644 --- a/examples/quick/canvas/clip/clip.qml +++ b/examples/quick/canvas/clip/clip.qml @@ -65,8 +65,6 @@ Item { property real alpha:alphaCtrl.value property string imagefile:"../contents/qt-logo.png" smooth:true - renderTarget:Canvas.Image - renderStrategy: Canvas.Immediate Component.onCompleted:loadImage(canvas.imagefile); onAlphaChanged:requestPaint(); diff --git a/examples/quick/canvas/contents/Stocks.qml b/examples/quick/canvas/contents/Stocks.qml deleted file mode 100644 index 043bca132e..0000000000 --- a/examples/quick/canvas/contents/Stocks.qml +++ /dev/null @@ -1,147 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/ -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor -** the names of its contributors may be used to endorse or promote -** products derived from this software without specific prior written -** permission. -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -import QtQuick 2.0 - -ListModel { - id:stocks - //Data from : http://en.wikipedia.org/wiki/NASDAQ-100 - - ListElement {name:"Activision Blizzard"; stockId:"ATVI"} - ListElement {name:"Adobe Systems Incorporated"; stockId:"ADBE"} - ListElement {name:"Akamai Technologies, Inc"; stockId:"AKAM"} - ListElement {name:"Alexion Pharmaceuticals"; stockId:"ALXN"} - ListElement {name:"Altera Corporation"; stockId:"ALTR"} - ListElement {name:"Amazon.com, Inc."; stockId:"AMZN"} - ListElement {name:"Amgen Inc."; stockId:"AMGN"} - ListElement {name:"Apollo Group, Inc."; stockId:"APOL"} - ListElement {name:"Apple Inc."; stockId:"AAPL"} - ListElement {name:"Applied Materials, Inc."; stockId:"AMAT"} - ListElement {name:"Autodesk, Inc."; stockId:"ADSK"} - ListElement {name:"Automatic Data Processing, Inc."; stockId:"ADP"} - ListElement {name:"Baidu.com, Inc."; stockId:"BIDU"} - ListElement {name:"Bed Bath & Beyond Inc."; stockId:"BBBY"} - ListElement {name:"Biogen Idec, Inc"; stockId:"BIIB"} - ListElement {name:"BMC Software, Inc."; stockId:"BMC"} - ListElement {name:"Broadcom Corporation"; stockId:"BRCM"} - ListElement {name:"C. H. Robinson Worldwide, Inc."; stockId:"CHRW"} - ListElement {name:"CA, Inc."; stockId:"CA"} - ListElement {name:"Celgene Corporation"; stockId:"CELG"} - ListElement {name:"Cephalon, Inc."; stockId:"CEPH"} - ListElement {name:"Cerner Corporation"; stockId:"CERN"} - ListElement {name:"Check Point Software Technologies Ltd."; stockId:"CHKP"} - ListElement {name:"Cisco Systems, Inc."; stockId:"CSCO"} - ListElement {name:"Citrix Systems, Inc."; stockId:"CTXS"} - ListElement {name:"Cognizant Technology Solutions Corporation"; stockId:"CTSH"} - ListElement {name:"Comcast Corporation"; stockId:"CMCSA"} - ListElement {name:"Costco Wholesale Corporation"; stockId:"COST"} - ListElement {name:"Ctrip.com International, Ltd."; stockId:"CTRP"} - ListElement {name:"Dell Inc."; stockId:"DELL"} - ListElement {name:"DENTSPLY International Inc."; stockId:"XRAY"} - ListElement {name:"DirecTV"; stockId:"DTV"} - ListElement {name:"Dollar Tree, Inc."; stockId:"DLTR"} - ListElement {name:"eBay Inc."; stockId:"EBAY"} - ListElement {name:"Electronic Arts Inc."; stockId:"ERTS"} - ListElement {name:"Expedia, Inc."; stockId:"EXPE"} - ListElement {name:"Expeditors International of Washington, Inc."; stockId:"EXPD"} - ListElement {name:"Express Scripts, Inc."; stockId:"ESRX"} - ListElement {name:"F5 Networks, Inc."; stockId:"FFIV"} - ListElement {name:"Fastenal Company"; stockId:"FAST"} - ListElement {name:"First Solar, Inc."; stockId:"FSLR"} - ListElement {name:"Fiserv, Inc."; stockId:"FISV"} - ListElement {name:"Flextronics International Ltd."; stockId:"FLEX"} - ListElement {name:"FLIR Systems, Inc."; stockId:"FLIR"} - ListElement {name:"Garmin Ltd."; stockId:"GRMN"} - ListElement {name:"Gilead Sciences, Inc."; stockId:"GILD"} - ListElement {name:"Google Inc."; stockId:"GOOG"} - ListElement {name:"Green Mountain Coffee Roasters, Inc."; stockId:"GMCR"} - ListElement {name:"Henry Schein, Inc."; stockId:"HSIC"} - ListElement {name:"Illumina, Inc."; stockId:"ILMN"} - ListElement {name:"Infosys Technologies"; stockId:"INFY"} - ListElement {name:"Intel Corporation"; stockId:"INTC"} - ListElement {name:"Intuit, Inc."; stockId:"INTU"} - ListElement {name:"Intuitive Surgical Inc."; stockId:"ISRG"} - ListElement {name:"Joy Global Inc."; stockId:"JOYG"} - ListElement {name:"KLA Tencor Corporation"; stockId:"KLAC"} - ListElement {name:"Lam Research Corporation"; stockId:"LRCX"} - ListElement {name:"Liberty Media Corporation, Interactive Series A"; stockId:"LINTA"} - ListElement {name:"Life Technologies Corporation"; stockId:"LIFE"} - ListElement {name:"Linear Technology Corporation"; stockId:"LLTC"} - ListElement {name:"Marvell Technology Group, Ltd."; stockId:"MRVL"} - ListElement {name:"Mattel, Inc."; stockId:"MAT"} - ListElement {name:"Maxim Integrated Products"; stockId:"MXIM"} - ListElement {name:"Microchip Technology Incorporated"; stockId:"MCHP"} - ListElement {name:"Micron Technology, Inc."; stockId:"MU"} - ListElement {name:"Microsoft Corporation"; stockId:"MSFT"} - ListElement {name:"Mylan, Inc."; stockId:"MYL"} - ListElement {name:"NetApp, Inc."; stockId:"NTAP"} - ListElement {name:"Netflix, Inc."; stockId:"NFLX"} - ListElement {name:"News Corporation, Ltd."; stockId:"NWSA"} - ListElement {name:"NII Holdings, Inc."; stockId:"NIHD"} - ListElement {name:"NVIDIA Corporation"; stockId:"NVDA"} - ListElement {name:"O'Reilly Automotive, Inc."; stockId:"ORLY"} - ListElement {name:"Oracle Corporation"; stockId:"ORCL"} - ListElement {name:"PACCAR Inc."; stockId:"PCAR"} - ListElement {name:"Paychex, Inc."; stockId:"PAYX"} - ListElement {name:"Priceline.com, Incorporated"; stockId:"PCLN"} - ListElement {name:"Qiagen N.V."; stockId:"QGEN"} - ListElement {name:"QUALCOMM Incorporated"; stockId:"QCOM"} - ListElement {name:"Research in Motion Limited"; stockId:"RIMM"} - ListElement {name:"Ross Stores Inc."; stockId:"ROST"} - ListElement {name:"SanDisk Corporation"; stockId:"SNDK"} - ListElement {name:"Seagate Technology Holdings"; stockId:"STX"} - ListElement {name:"Sears Holdings Corporation"; stockId:"SHLD"} - ListElement {name:"Sigma-Aldrich Corporation"; stockId:"SIAL"} - ListElement {name:"Staples Inc."; stockId:"SPLS"} - ListElement {name:"Starbucks Corporation"; stockId:"SBUX"} - ListElement {name:"Stericycle, Inc"; stockId:"SRCL"} - ListElement {name:"Symantec Corporation"; stockId:"SYMC"} - ListElement {name:"Teva Pharmaceutical Industries Ltd."; stockId:"TEVA"} - ListElement {name:"Urban Outfitters, Inc."; stockId:"URBN"} - ListElement {name:"VeriSign, Inc."; stockId:"VRSN"} - ListElement {name:"Vertex Pharmaceuticals"; stockId:"VRTX"} - ListElement {name:"Virgin Media, Inc."; stockId:"VMED"} - ListElement {name:"Vodafone Group, plc."; stockId:"VOD"} - ListElement {name:"Warner Chilcott, Ltd."; stockId:"WCRX"} - ListElement {name:"Whole Foods Market, Inc."; stockId:"WFM"} - ListElement {name:"Wynn Resorts Ltd."; stockId:"WYNN"} - ListElement {name:"Xilinx, Inc."; stockId:"XLNX"} - ListElement {name:"Yahoo! Inc."; stockId:"YHOO"} -} diff --git a/examples/quick/canvas/quadraticCurveTo/quadraticCurveTo.qml b/examples/quick/canvas/quadraticCurveTo/quadraticCurveTo.qml index f0e2d11747..e7121c1a59 100644 --- a/examples/quick/canvas/quadraticCurveTo/quadraticCurveTo.qml +++ b/examples/quick/canvas/quadraticCurveTo/quadraticCurveTo.qml @@ -64,8 +64,6 @@ Item { property real scaleY : scaleYCtrl.value property real rotate : rotateCtrl.value smooth:true - renderTarget:Canvas.Image - renderStrategy: Canvas.Immediate onLineWidthChanged:requestPaint(); onFillChanged:requestPaint(); diff --git a/examples/quick/canvas/roundedrect/roundedrect.qml b/examples/quick/canvas/roundedrect/roundedrect.qml index a773895563..568b387fd7 100644 --- a/examples/quick/canvas/roundedrect/roundedrect.qml +++ b/examples/quick/canvas/roundedrect/roundedrect.qml @@ -54,8 +54,6 @@ Item { width:320 height:280 smooth:true - renderTarget:Canvas.Image - renderStrategy: Canvas.Immediate property int radius: rCtrl.value property int rectx: rxCtrl.value diff --git a/examples/quick/canvas/smile/smile.qml b/examples/quick/canvas/smile/smile.qml index 4ab040ce94..00ded9303a 100644 --- a/examples/quick/canvas/smile/smile.qml +++ b/examples/quick/canvas/smile/smile.qml @@ -55,8 +55,6 @@ Item { width:320 height:280 smooth:true - renderTarget:Canvas.Image - renderStrategy: Canvas.Immediate property string strokeStyle:"green" property string fillStyle:"yellow" diff --git a/examples/quick/canvas/squircle/squircle.qml b/examples/quick/canvas/squircle/squircle.qml index e3e764058d..a102dc4286 100644 --- a/examples/quick/canvas/squircle/squircle.qml +++ b/examples/quick/canvas/squircle/squircle.qml @@ -60,8 +60,6 @@ Item { width:320 height:250 smooth:true - renderTarget:Canvas.Image - renderStrategy: Canvas.Immediate property string strokeStyle:"blue" property string fillStyle:"steelblue" diff --git a/examples/quick/canvas/tiger/tiger.qml b/examples/quick/canvas/tiger/tiger.qml index 620ba6a715..92fe2fe1a9 100644 --- a/examples/quick/canvas/tiger/tiger.qml +++ b/examples/quick/canvas/tiger/tiger.qml @@ -56,8 +56,6 @@ Item { width:320 height:280 smooth:true - renderTarget:Canvas.FramebufferObject - renderStrategy: Canvas.Cooperative property string strokeStyle:"steelblue" property string fillStyle:"yellow" property bool fill:true diff --git a/src/quick/items/context2d/qquickcanvasitem.cpp b/src/quick/items/context2d/qquickcanvasitem.cpp index 939e7b4fba..16f08d3a3a 100644 --- a/src/quick/items/context2d/qquickcanvasitem.cpp +++ b/src/quick/items/context2d/qquickcanvasitem.cpp @@ -545,7 +545,6 @@ void QQuickCanvasItem::setRenderStrategy(QQuickCanvasItem::RenderStrategy strate qmlInfo(this) << "Canvas:renderStrategy not changeable once context is active."; return; } - d->renderStrategy = strategy; emit renderStrategyChanged(); } @@ -603,6 +602,16 @@ void QQuickCanvasItem::geometryChanged(const QRectF &newGeometry, const QRectF & requestPaint(); } +void QQuickCanvasItem::releaseResources() +{ + Q_D(QQuickCanvasItem); + + if (d->context) { + delete d->context; + d->context = 0; + } +} + void QQuickCanvasItem::componentComplete() { QQuickItem::componentComplete(); @@ -678,17 +687,9 @@ QSGNode *QQuickCanvasItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData if (!d->contextInitialized) return 0; - class CanvasTextureNode : public QSGSimpleTextureNode - { - public: - CanvasTextureNode() : QSGSimpleTextureNode() {} - ~CanvasTextureNode() {delete texture();} - }; - - CanvasTextureNode *node = static_cast<CanvasTextureNode*>(oldNode); - if (!node) { - node = new CanvasTextureNode; - } + QSGSimpleTextureNode *node = static_cast<QSGSimpleTextureNode*>(oldNode); + if (!node) + node = new QSGSimpleTextureNode; if (d->renderStrategy == QQuickCanvasItem::Cooperative) d->context->flush(); diff --git a/src/quick/items/context2d/qquickcanvasitem_p.h b/src/quick/items/context2d/qquickcanvasitem_p.h index 4e592bcba7..be95c4a775 100644 --- a/src/quick/items/context2d/qquickcanvasitem_p.h +++ b/src/quick/items/context2d/qquickcanvasitem_p.h @@ -177,7 +177,7 @@ protected: void updatePolish(); QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); - + void releaseResources(); private: Q_DECLARE_PRIVATE(QQuickCanvasItem) Q_INVOKABLE void delayedCreate(); diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp index f448c3fdfb..40a9b8e34c 100644 --- a/src/quick/items/context2d/qquickcontext2d.cpp +++ b/src/quick/items/context2d/qquickcontext2d.cpp @@ -63,8 +63,6 @@ #include <QtCore/qnumeric.h> #include <private/qquickwindow_p.h> #include <private/qquickwindowmanager_p.h> -#include <QtGui/private/qguiapplication_p.h> -#include <qpa/qplatformintegration.h> #ifdef Q_OS_QNX #include <ctype.h> @@ -3265,6 +3263,11 @@ static int textAlignOffset(QQuickContext2D::TextAlignType value, const QFontMetr return offset; } +void QQuickContext2D::setGrabbedImage(const QImage& grab) +{ + m_grabbedImage = grab; + m_grabbed = true; +} QQmlRefPointer<QQuickCanvasPixmap> QQuickContext2D::createPixmap(const QUrl& url) { @@ -3347,12 +3350,15 @@ QQuickContext2D::QQuickContext2D(QObject *parent) , m_windowManager(0) , m_surface(0) , m_glContext(0) + , m_thread(0) + , m_grabbed(false) { } QQuickContext2D::~QQuickContext2D() { delete m_buffer; + m_texture->deleteLater(); } v8::Handle<v8::Object> QQuickContext2D::v8value() const @@ -3378,15 +3384,10 @@ void QQuickContext2D::init(QQuickCanvasItem *canvasItem, const QVariantMap &args switch (m_renderTarget) { case QQuickCanvasItem::Image: - m_texture = new QQuickContext2DImageTexture(m_renderStrategy == QQuickCanvasItem::Threaded); + m_texture = new QQuickContext2DImageTexture; break; case QQuickCanvasItem::FramebufferObject: - { m_texture = new QQuickContext2DFBOTexture; - // No BufferQueueingOpenGL, falls back to Cooperative mode - if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::BufferQueueingOpenGL)) - m_renderStrategy = QQuickCanvasItem::Cooperative; - } break; } @@ -3396,7 +3397,9 @@ void QQuickContext2D::init(QQuickCanvasItem *canvasItem, const QVariantMap &args m_texture->setCanvasSize(canvasItem->canvasSize().toSize()); m_texture->setSmooth(canvasItem->smooth()); - QThread *renderThread = QThread::currentThread(); + m_thread = QThread::currentThread(); + + QThread *renderThread = m_thread; QThread *sceneGraphThread = window->openglContext() ? window->openglContext()->thread() : 0; if (m_renderStrategy == QQuickCanvasItem::Threaded) @@ -3404,6 +3407,9 @@ void QQuickContext2D::init(QQuickCanvasItem *canvasItem, const QVariantMap &args else if (m_renderStrategy == QQuickCanvasItem::Cooperative) renderThread = sceneGraphThread; + if (renderThread && renderThread != QThread::currentThread()) + m_texture->moveToThread(renderThread); + if (m_renderTarget == QQuickCanvasItem::FramebufferObject && renderThread != sceneGraphThread) { QOpenGLContext *cc = QQuickWindowPrivate::get(window)->context->glContext(); m_surface = window; @@ -3421,32 +3427,24 @@ void QQuickContext2D::init(QQuickCanvasItem *canvasItem, const QVariantMap &args void QQuickContext2D::prepare(const QSize& canvasSize, const QSize& tileSize, const QRect& canvasWindow, const QRect& dirtyRect, bool smooth) { - m_texture->canvasChanged(canvasSize, tileSize, canvasWindow, dirtyRect, smooth); + QMetaObject::invokeMethod(m_texture, + "canvasChanged", + Qt::AutoConnection, + Q_ARG(QSize, canvasSize), + Q_ARG(QSize, tileSize), + Q_ARG(QRect, canvasWindow), + Q_ARG(QRect, dirtyRect), + Q_ARG(bool, smooth)); } void QQuickContext2D::flush() { - if (!m_buffer->isEmpty()) { - QMutexLocker lock(&m_bufferMutex); - m_bufferQueue.enqueue(m_buffer); - m_buffer = new QQuickContext2DCommandBuffer; - } else - return; - - switch (m_renderStrategy) { - case QQuickCanvasItem::Immediate: - // Cause the texture to consume paint commands immediately - m_texture->paint(); - break; - case QQuickCanvasItem::Threaded: - // wake up thread to consume paint commands - m_texture->paint(); - break; - case QQuickCanvasItem::Cooperative: - // NOTE: On SG Thread - m_texture->paint(); - break; - } + if (m_buffer) + QMetaObject::invokeMethod(m_texture, + "paint", + Qt::AutoConnection, + Q_ARG(QQuickContext2DCommandBuffer*, m_buffer)); + m_buffer = new QQuickContext2DCommandBuffer(); } QSGDynamicTexture *QQuickContext2D::texture() const @@ -3456,16 +3454,22 @@ QSGDynamicTexture *QQuickContext2D::texture() const QImage QQuickContext2D::toImage(const QRectF& bounds) { - switch (m_renderStrategy) { - case QQuickCanvasItem::Immediate: - case QQuickCanvasItem::Threaded: - flush(); - break; - case QQuickCanvasItem::Cooperative: - break; + flush(); + if (m_texture->thread() == QThread::currentThread()) + m_texture->grabImage(bounds); + else if (m_renderStrategy == QQuickCanvasItem::Cooperative) { + qWarning() << "Pixel read back is not support in Cooperative mode, please try Theaded or Immediate mode"; + return QImage(); + } else { + QMetaObject::invokeMethod(m_texture, + "grabImage", + Qt::BlockingQueuedConnection, + Q_ARG(QRectF, bounds)); } - - return m_texture->toImage(bounds); + QImage img = m_grabbedImage; + m_grabbedImage = QImage(); + m_grabbed = false; + return img; } @@ -3696,7 +3700,7 @@ void QQuickContext2D::setV8Engine(QV8Engine *engine) QQuickContext2DCommandBuffer* QQuickContext2D::nextBuffer() { - QMutexLocker lock(&m_bufferMutex); + QMutexLocker lock(&m_mutex); return m_bufferQueue.isEmpty() ? 0 : m_bufferQueue.dequeue(); } diff --git a/src/quick/items/context2d/qquickcontext2d_p.h b/src/quick/items/context2d/qquickcontext2d_p.h index e2952ca580..295be051ba 100644 --- a/src/quick/items/context2d/qquickcontext2d_p.h +++ b/src/quick/items/context2d/qquickcontext2d_p.h @@ -53,7 +53,7 @@ #include <QtCore/qstack.h> #include <QtCore/qqueue.h> #include <private/qv8engine_p.h> - +#include <QtCore/QWaitCondition> //#define QQUICKCONTEXT2D_DEBUG //enable this for just DEBUG purpose! @@ -172,6 +172,7 @@ public: void prepare(const QSize& canvasSize, const QSize& tileSize, const QRect& canvasWindow, const QRect& dirtyRect, bool smooth); void flush(); void sync(); + QThread *thread() const {return m_thread;} QSGDynamicTexture *texture() const; QImage toImage(const QRectF& bounds); @@ -228,6 +229,7 @@ public: QOpenGLContext *glContext() { return m_glContext; } QSurface *surface() { return m_surface; } + void setGrabbedImage(const QImage& grab); State state; QStack<QQuickContext2D::State> m_stateStack; @@ -246,7 +248,11 @@ public: QQuickCanvasItem::RenderTarget m_renderTarget; QQuickCanvasItem::RenderStrategy m_renderStrategy; QQueue<QQuickContext2DCommandBuffer*> m_bufferQueue; - QMutex m_bufferMutex; + QThread *m_thread; + QImage m_grabbedImage; + bool m_grabbed:1; + + QMutex m_mutex; }; diff --git a/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp b/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp index 196981d971..47f7e1a90d 100644 --- a/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp +++ b/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp @@ -499,6 +499,11 @@ QQuickContext2DCommandBuffer::QQuickContext2DCommandBuffer() , imageIdx(0) , pixmapIdx(0) { + static bool registered = false; + if (!registered) { + qRegisterMetaType<QQuickContext2DCommandBuffer*>("QQuickContext2DCommandBuffer*"); + registered = true; + } } diff --git a/src/quick/items/context2d/qquickcontext2dtexture.cpp b/src/quick/items/context2d/qquickcontext2dtexture.cpp index 2cb183d715..a315d30f8f 100644 --- a/src/quick/items/context2d/qquickcontext2dtexture.cpp +++ b/src/quick/items/context2d/qquickcontext2dtexture.cpp @@ -50,7 +50,6 @@ #include <QOpenGLFramebufferObject> #include <QOpenGLFramebufferObjectFormat> #include <QtCore/QThread> -#include <QtCore/QCoreApplication> QT_BEGIN_NAMESPACE @@ -88,19 +87,14 @@ struct GLAcquireContext { QOpenGLContext *ctx; }; -Q_GLOBAL_STATIC(QThread, globalCanvasThreadRenderInstance) - - QQuickContext2DTexture::QQuickContext2DTexture() : m_context(0) , m_item(0) , m_dirtyCanvas(false) , m_canvasWindowChanged(false) , m_dirtyTexture(false) - , m_threadRendering(false) , m_smooth(false) , m_tiledCanvas(false) - , m_doGrabImage(false) , m_painting(false) { } @@ -117,12 +111,9 @@ QSize QQuickContext2DTexture::textureSize() const void QQuickContext2DTexture::markDirtyTexture() { - const bool inGrab = m_doGrabImage; - m_dirtyTexture = true; updateTexture(); - if (!inGrab) - emit textureChanged(); + emit textureChanged(); } bool QQuickContext2DTexture::setCanvasSize(const QSize &size) @@ -185,8 +176,6 @@ bool QQuickContext2DTexture::setDirtyRect(const QRect &r) void QQuickContext2DTexture::canvasChanged(const QSize& canvasSize, const QSize& tileSize, const QRect& canvasWindow, const QRect& dirtyRect, bool smooth) { - lock(); - QSize ts = tileSize; if (ts.width() > canvasSize.width()) ts.setWidth(canvasSize.width()); @@ -209,20 +198,15 @@ void QQuickContext2DTexture::canvasChanged(const QSize& canvasSize, const QSize& setDirtyRect(dirtyRect); setSmooth(smooth); - - unlock(); } -void QQuickContext2DTexture::paintWithoutTiles() +void QQuickContext2DTexture::paintWithoutTiles(QQuickContext2DCommandBuffer *ccb) { - QQuickContext2DCommandBuffer* ccb = m_context->nextBuffer(); - if (!ccb || ccb->isEmpty()) return; QPaintDevice* device = beginPainting(); if (!device) { - delete ccb; endPainting(); return; } @@ -238,100 +222,58 @@ void QQuickContext2DTexture::paintWithoutTiles() p.setCompositionMode(QPainter::CompositionMode_SourceOver); ccb->replay(&p, m_state); - ccb->clear(); - delete ccb; - endPainting(); - markDirtyTexture(); } bool QQuickContext2DTexture::canvasDestroyed() { - bool noCanvas = false; - lock(); - noCanvas = m_item == 0; - unlock(); - return noCanvas; + return m_item == 0; } -void QQuickContext2DTexture::paint() +void QQuickContext2DTexture::paint(QQuickContext2DCommandBuffer *ccb) { - if (canvasDestroyed()) + if (canvasDestroyed()) { + delete ccb; return; + } GLAcquireContext currentContext(m_context->glContext(), m_context->surface()); - if (m_threadRendering && QThread::currentThread() != globalCanvasThreadRenderInstance()) { - Q_ASSERT(thread() == globalCanvasThreadRenderInstance()); - QMetaObject::invokeMethod(this, "paint", Qt::QueuedConnection); + if (!m_tiledCanvas) { + paintWithoutTiles(ccb); + delete ccb; return; } - if (!m_tiledCanvas) { - paintWithoutTiles(); - } else { - lock(); - QRect tiledRegion = createTiles(m_canvasWindow.intersected(QRect(QPoint(0, 0), m_canvasSize))); - unlock(); - - if (!tiledRegion.isEmpty()) { - if (m_threadRendering && !m_doGrabImage) { - QRect dirtyRect; - - lock(); - foreach (QQuickContext2DTile* tile, m_tiles) { - if (tile->dirty()) { - if (dirtyRect.isEmpty()) - dirtyRect = tile->rect(); - else - dirtyRect |= tile->rect(); - } - } - unlock(); + QRect tiledRegion = createTiles(m_canvasWindow.intersected(QRect(QPoint(0, 0), m_canvasSize))); + if (!tiledRegion.isEmpty()) { + QRect dirtyRect; + foreach (QQuickContext2DTile* tile, m_tiles) { + if (tile->dirty()) { + if (dirtyRect.isEmpty()) + dirtyRect = tile->rect(); + else + dirtyRect |= tile->rect(); } + } - if (beginPainting()) { - QQuickContext2D::State oldState = m_state; - QQuickContext2DCommandBuffer* ccb = m_context->nextBuffer(); - if (!ccb || ccb->isEmpty()) { - endPainting(); - delete ccb; - return; - } - foreach (QQuickContext2DTile* tile, m_tiles) { - bool dirtyTile = false, dirtyCanvas = false, smooth = false; - - lock(); - dirtyTile = tile->dirty(); - smooth = m_smooth; - dirtyCanvas = m_dirtyCanvas; - unlock(); - - //canvas size or tile size may change during painting tiles - if (dirtyCanvas) { - if (m_threadRendering) - QMetaObject::invokeMethod(this, "paint", Qt::QueuedConnection); - endPainting(); - return; - } else if (dirtyTile) { - ccb->replay(tile->createPainter(smooth), oldState); - tile->drawFinished(); - lock(); - tile->markDirty(false); - unlock(); - } - - compositeTile(tile); + if (beginPainting()) { + QQuickContext2D::State oldState = m_state; + foreach (QQuickContext2DTile* tile, m_tiles) { + if (tile->dirty()) { + ccb->replay(tile->createPainter(m_smooth), oldState); + tile->drawFinished(); + tile->markDirty(false); } - ccb->clear(); - delete ccb; - endPainting(); - m_state = oldState; - markDirtyTexture(); + compositeTile(tile); } + endPainting(); + m_state = oldState; + markDirtyTexture(); } } + delete ccb; } QRect QQuickContext2DTexture::tiledRect(const QRectF& window, const QSize& tileSize) @@ -438,11 +380,15 @@ QQuickContext2DFBOTexture::QQuickContext2DFBOTexture() , m_multisampledFbo(0) , m_paint_device(0) { - m_threadRendering = false; } QQuickContext2DFBOTexture::~QQuickContext2DFBOTexture() { + if (m_multisampledFbo) + m_multisampledFbo->release(); + else if (m_fbo) + m_fbo->release(); + delete m_fbo; delete m_multisampledFbo; delete m_paint_device; @@ -477,14 +423,7 @@ int QQuickContext2DFBOTexture::textureId() const bool QQuickContext2DFBOTexture::updateTexture() { bool textureUpdated = m_dirtyTexture; - m_dirtyTexture = false; - - if (m_doGrabImage) { - grabImage(); - m_condition.wakeOne(); - m_doGrabImage = false; - } return textureUpdated; } @@ -493,13 +432,6 @@ QQuickContext2DTile* QQuickContext2DFBOTexture::createTile() const return new QQuickContext2DFBOTile(); } -void QQuickContext2DFBOTexture::grabImage() -{ - if (m_fbo) { - m_grabedImage = m_fbo->toImage(); - } -} - bool QQuickContext2DFBOTexture::doMultisampling() const { static bool extensionsChecked = false; @@ -515,27 +447,22 @@ bool QQuickContext2DFBOTexture::doMultisampling() const return multisamplingSupported && m_smooth; } -QImage QQuickContext2DFBOTexture::toImage(const QRectF& region) +void QQuickContext2DFBOTexture::grabImage(const QRectF& rf) { - const unsigned long context2d_wait_max = 5000; + Q_ASSERT(rf.isValid()); - m_doGrabImage = true; - if (m_item) // forces a call to updatePaintNode (repaints) - m_item->update(); + if (!m_fbo) { + m_context->setGrabbedImage(QImage()); + return; + } QImage grabbed; - m_mutex.lock(); - bool ok = m_condition.wait(&m_mutex, context2d_wait_max); - - if (!ok) - grabbed = QImage(); + { + GLAcquireContext ctx(m_context->glContext(), m_context->surface()); + grabbed = m_fbo->toImage().mirrored().copy(rf.toRect()); + } - if (region.isValid()) - grabbed = m_grabedImage.copy(region.toRect()); - else - grabbed = m_grabedImage; - m_grabedImage = QImage(); - return grabbed; + m_context->setGrabbedImage(grabbed); } void QQuickContext2DFBOTexture::compositeTile(QQuickContext2DTile* tile) @@ -551,15 +478,17 @@ void QQuickContext2DFBOTexture::compositeTile(QQuickContext2DTile* tile) QOpenGLFramebufferObject::blitFramebuffer(m_fbo, target, t->fbo(), source); } } + QQuickCanvasItem::RenderTarget QQuickContext2DFBOTexture::renderTarget() const { return QQuickCanvasItem::FramebufferObject; } + QPaintDevice* QQuickContext2DFBOTexture::beginPainting() { QQuickContext2DTexture::beginPainting(); - if (m_canvasWindow.size().isEmpty() && !m_threadRendering) { + if (m_canvasWindow.size().isEmpty()) { delete m_fbo; delete m_multisampledFbo; delete m_paint_device; @@ -615,78 +544,27 @@ QPaintDevice* QQuickContext2DFBOTexture::beginPainting() void QQuickContext2DFBOTexture::endPainting() { QQuickContext2DTexture::endPainting(); - if (m_multisampledFbo) { + if (m_multisampledFbo) QOpenGLFramebufferObject::blitFramebuffer(m_fbo, m_multisampledFbo); - m_multisampledFbo->release(); - } else if (m_fbo) - m_fbo->release(); -} -void qt_quit_context2d_render_thread() -{ - QThread* thread = globalCanvasThreadRenderInstance(); - - if (thread->isRunning()) { - thread->exit(0); - thread->wait(1000); - } } -QQuickContext2DImageTexture::QQuickContext2DImageTexture(bool threadRendering) +QQuickContext2DImageTexture::QQuickContext2DImageTexture() : QQuickContext2DTexture() - , m_texture(new QSGPlainTexture()) + , m_texture(0) { - m_texture->setOwnsTexture(true); - m_texture->setHasMipmaps(false); - - m_threadRendering = threadRendering; - - if (m_threadRendering) { - QThread* thread = globalCanvasThreadRenderInstance(); - moveToThread(thread); - - if (!thread->isRunning()) { - qAddPostRoutine(qt_quit_context2d_render_thread); // XXX: change this method - thread->start(); - } - } } QQuickContext2DImageTexture::~QQuickContext2DImageTexture() { - delete m_texture; + if (m_texture && m_texture->thread() != QThread::currentThread()) + m_texture->deleteLater(); + else + delete m_texture; } int QQuickContext2DImageTexture::textureId() const { - return m_texture->textureId(); -} - -void QQuickContext2DImageTexture::lock() -{ - if (m_threadRendering) - m_mutex.lock(); -} -void QQuickContext2DImageTexture::unlock() -{ - if (m_threadRendering) - m_mutex.unlock(); -} - -void QQuickContext2DImageTexture::wait() -{ - if (m_threadRendering) - m_waitCondition.wait(&m_mutex); -} - -void QQuickContext2DImageTexture::wake() -{ - if (m_threadRendering) - m_waitCondition.wakeOne(); -} - -bool QQuickContext2DImageTexture::supportDirectRendering() const -{ - return !m_threadRendering; + return imageTexture()->textureId(); } QQuickCanvasItem::RenderTarget QQuickContext2DImageTexture::renderTarget() const @@ -696,14 +574,14 @@ QQuickCanvasItem::RenderTarget QQuickContext2DImageTexture::renderTarget() const void QQuickContext2DImageTexture::bind() { - m_texture->bind(); + imageTexture()->bind(); } bool QQuickContext2DImageTexture::updateTexture() { bool textureUpdated = m_dirtyTexture; if (m_dirtyTexture) { - m_texture->setImage(m_image); + imageTexture()->setImage(m_image); m_dirtyTexture = false; } return textureUpdated; @@ -714,26 +592,23 @@ QQuickContext2DTile* QQuickContext2DImageTexture::createTile() const return new QQuickContext2DImageTile(); } -void QQuickContext2DImageTexture::grabImage(const QRect& r) +void QQuickContext2DImageTexture::grabImage(const QRectF& rf) { - m_doGrabImage = true; - paint(); - m_doGrabImage = false; - m_grabedImage = m_image.copy(r); + Q_ASSERT(rf.isValid()); + Q_ASSERT(m_context); + QImage grabbed = m_image.copy(rf.toRect()); + m_context->setGrabbedImage(grabbed); } -QImage QQuickContext2DImageTexture::toImage(const QRectF& rect) +QSGPlainTexture *QQuickContext2DImageTexture::imageTexture() const { - QRect r = rect.isValid() ? rect.toRect() : QRect(QPoint(0, 0), m_canvasWindow.size()); - if (threadRendering()) { - wake(); - QMetaObject::invokeMethod(this, "grabImage", Qt::BlockingQueuedConnection, Q_ARG(QRect, r)); - } else { - QMetaObject::invokeMethod(this, "grabImage", Qt::DirectConnection, Q_ARG(QRect, r)); + if (!m_texture) { + QQuickContext2DImageTexture *that = const_cast<QQuickContext2DImageTexture *>(this); + that->m_texture = new QSGPlainTexture; + that->m_texture->setOwnsTexture(true); + that->m_texture->setHasMipmaps(false); } - QImage image = m_grabedImage; - m_grabedImage = QImage(); - return image; + return m_texture; } QPaintDevice* QQuickContext2DImageTexture::beginPainting() @@ -762,14 +637,11 @@ void QQuickContext2DImageTexture::compositeTile(QQuickContext2DTile* tile) source.moveTo(source.topLeft() - t->rect().topLeft()); target.moveTo(target.topLeft() - m_canvasWindow.topLeft()); - lock(); m_painter.begin(&m_image); m_painter.setCompositionMode(QPainter::CompositionMode_Source); m_painter.drawImage(target, t->image(), source); m_painter.end(); - unlock(); } } QT_END_NAMESPACE - diff --git a/src/quick/items/context2d/qquickcontext2dtexture_p.h b/src/quick/items/context2d/qquickcontext2dtexture_p.h index 81b239dc16..684185572c 100644 --- a/src/quick/items/context2d/qquickcontext2dtexture_p.h +++ b/src/quick/items/context2d/qquickcontext2dtexture_p.h @@ -70,15 +70,7 @@ public: virtual bool hasAlphaChannel() const {return true;} virtual bool hasMipmaps() const {return false;} virtual QSize textureSize() const; - virtual void lock() {} - virtual void unlock() {} - virtual void wait() {} - virtual void wake() {} - bool threadRendering() const {return m_threadRendering;} - virtual bool supportThreadRendering() const = 0; - virtual bool supportDirectRendering() const = 0; virtual QQuickCanvasItem::RenderTarget renderTarget() const = 0; - virtual QImage toImage(const QRectF& region = QRectF()) = 0; static QRect tiledRect(const QRectF& window, const QSize& tileSize); bool setCanvasSize(const QSize &size); @@ -86,18 +78,20 @@ public: bool setCanvasWindow(const QRect& canvasWindow); void setSmooth(bool smooth); bool setDirtyRect(const QRect &dirtyRect); - virtual void canvasChanged(const QSize& canvasSize, const QSize& tileSize, const QRect& canvasWindow, const QRect& dirtyRect, bool smooth); bool canvasDestroyed(); + Q_SIGNALS: void textureChanged(); public Q_SLOTS: void markDirtyTexture(); void setItem(QQuickCanvasItem* item); - void paint(); + void canvasChanged(const QSize& canvasSize, const QSize& tileSize, const QRect& canvasWindow, const QRect& dirtyRect, bool smooth); + void paint(QQuickContext2DCommandBuffer *ccb); + virtual void grabImage(const QRectF& region = QRectF()) = 0; protected: - void paintWithoutTiles(); + void paintWithoutTiles(QQuickContext2DCommandBuffer *ccb); virtual QPaintDevice* beginPainting() {m_painting = true; return 0; } virtual void endPainting() {m_painting = false;} virtual QQuickContext2DTile* createTile() const = 0; @@ -120,10 +114,8 @@ protected: uint m_dirtyCanvas : 1; uint m_canvasWindowChanged : 1; uint m_dirtyTexture : 1; - uint m_threadRendering : 1; uint m_smooth : 1; uint m_tiledCanvas : 1; - uint m_doGrabImage : 1; uint m_painting : 1; }; @@ -137,23 +129,19 @@ public: virtual int textureId() const; virtual bool updateTexture(); virtual QQuickContext2DTile* createTile() const; - virtual QImage toImage(const QRectF& region = QRectF()); virtual QPaintDevice* beginPainting(); virtual void endPainting(); QRectF normalizedTextureSubRect() const; - virtual bool supportThreadRendering() const {return false;} - virtual bool supportDirectRendering() const {return false;} virtual QQuickCanvasItem::RenderTarget renderTarget() const; virtual void compositeTile(QQuickContext2DTile* tile); virtual void bind(); QSize adjustedTileSize(const QSize &ts); -private Q_SLOTS: - void grabImage(); +public Q_SLOTS: + virtual void grabImage(const QRectF& region = QRectF()); private: bool doMultisampling() const; - QImage m_grabedImage; QOpenGLFramebufferObject *m_fbo; QOpenGLFramebufferObject *m_multisampledFbo; QMutex m_mutex; @@ -168,31 +156,24 @@ class QQuickContext2DImageTexture : public QQuickContext2DTexture Q_OBJECT public: - QQuickContext2DImageTexture(bool threadRendering = true); + QQuickContext2DImageTexture(); ~QQuickContext2DImageTexture(); virtual int textureId() const; virtual void bind(); - virtual bool supportThreadRendering() const {return true;} - virtual bool supportDirectRendering() const; + virtual QQuickCanvasItem::RenderTarget renderTarget() const; - virtual void lock(); - virtual void unlock(); - virtual void wait(); - virtual void wake(); virtual bool updateTexture(); virtual QQuickContext2DTile* createTile() const; - virtual QImage toImage(const QRectF& region = QRectF()); virtual QPaintDevice* beginPainting(); virtual void compositeTile(QQuickContext2DTile* tile); -private Q_SLOTS: - void grabImage(const QRect& r); +public Q_SLOTS: + virtual void grabImage(const QRectF& region = QRectF()); + private: + QSGPlainTexture *imageTexture() const; QImage m_image; - QImage m_grabedImage; - QMutex m_mutex; - QWaitCondition m_waitCondition; QPainter m_painter; QSGPlainTexture* m_texture; }; diff --git a/tests/auto/quick/qquickcanvasitem/data/CanvasTestCase.qml b/tests/auto/quick/qquickcanvasitem/data/CanvasTestCase.qml index 4aae317a7a..bc11d349fa 100644 --- a/tests/auto/quick/qquickcanvasitem/data/CanvasTestCase.qml +++ b/tests/auto/quick/qquickcanvasitem/data/CanvasTestCase.qml @@ -13,13 +13,12 @@ TestCase { function testData(type) { if (type === "2d") return [ - { tag:"image threaded", properties:{width:100, height:100, renderTarget:Canvas.Image, renderStrategy:Canvas.Threaded}} - //TODO: Enable the followings later - //{ tag:"image cooperative", properties:{width:100, height:100, renderTarget:Canvas.Image, renderStrategy:Canvas.Cooperative}}, - //{ tag:"image immediate", properties:{width:100, height:100, renderTarget:Canvas.Image, renderStrategy:Canvas.Immediate}}, - //{ tag:"fbo cooperative", properties:{width:100, height:100, renderTarget:Canvas.FramebufferObject, renderStrategy:Canvas.Cooperative}}, - //{ tag:"fbo immediate", properties:{width:100, height:100, renderTarget:Canvas.FramebufferObject, renderStrategy:Canvas.Immediate}}, - //{ tag:"fbo threaded", properties:{width:100, height:100, renderTarget:Canvas.FramebufferObject, renderStrategy:Canvas.Threaded}} + { tag:"image threaded", properties:{width:100, height:100, renderTarget:Canvas.Image, renderStrategy:Canvas.Threaded}}, +// { tag:"image cooperative", properties:{width:100, height:100, renderTarget:Canvas.Image, renderStrategy:Canvas.Cooperative}}, + { tag:"image immediate", properties:{width:100, height:100, renderTarget:Canvas.Image, renderStrategy:Canvas.Immediate}}, +// { tag:"fbo cooperative", properties:{width:100, height:100, renderTarget:Canvas.FramebufferObject, renderStrategy:Canvas.Cooperative}}, +// { tag:"fbo immediate", properties:{width:100, height:100, renderTarget:Canvas.FramebufferObject, renderStrategy:Canvas.Immediate}}, +// { tag:"fbo threaded", properties:{width:100, height:100, renderTarget:Canvas.FramebufferObject, renderStrategy:Canvas.Threaded}} ]; return []; } @@ -43,4 +42,4 @@ TestCase { qtest_fail('Pixel compare fail:\nactual :[' + c[0]+','+c[1]+','+c[2]+','+c[3] + ']\nexpected:['+r+','+g+','+b+','+a+'] +/- '+d, 1); } -}
\ No newline at end of file +} diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_image.qml b/tests/auto/quick/qquickcanvasitem/data/tst_image.qml index 71931acae8..72b6dcdb00 100644 --- a/tests/auto/quick/qquickcanvasitem/data/tst_image.qml +++ b/tests/auto/quick/qquickcanvasitem/data/tst_image.qml @@ -653,7 +653,7 @@ CanvasTestCase { var ctx = canvas.getContext('2d'); loadImages(canvas); - var canvas2 = Qt.createQmlObject("import QtQuick 2.0; Canvas{renderTarget:Canvas.Image}", canvas); + var canvas2 = Qt.createQmlObject("import QtQuick 2.0; Canvas{renderTarget:Canvas.Image; renderStrategy:Canvas.Immediate}", canvas); canvas2.width = 100; canvas2.height = 50; var ctx2 = canvas2.getContext('2d'); @@ -704,4 +704,4 @@ CanvasTestCase { comparePixel(ctx, 25,25, 255,0,0,255, 2); comparePixel(ctx, 75,25, 255,0,0,255, 2); } -}
\ No newline at end of file +} diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_svgpath.qml b/tests/auto/quick/qquickcanvasitem/data/tst_svgpath.qml index 2966811021..2b39357bed 100644 --- a/tests/auto/quick/qquickcanvasitem/data/tst_svgpath.qml +++ b/tests/auto/quick/qquickcanvasitem/data/tst_svgpath.qml @@ -23,16 +23,16 @@ CanvasTestCase { ]; var blues = [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, + -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 0, 0, 0, 0, 0, 0,-1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, - 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, + 1, 1, 1, 0,-1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, - 1, 0, 0, 0, 1, 1, 1, 0, 0, 0 + -1, 0, 0, 0, 1, 1, 1, 0, 0, 0 ]; ctx.fillRule = Qt.OddEvenFill; @@ -45,6 +45,7 @@ CanvasTestCase { var x, y; for (x=0; x < 10; x++) { for (y=0; y < 10; y++) { + if (blues[y*10 +x] == -1) continue; //edge point, different render target may have different value if (blues[y * 10 + x]) { comparePixel(ctx, x * 5, y * 5, 0, 0, 255, 255); } else { |