| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the QtQuick module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qquickmaskextruder_p.h" |
| #include <QtQml/qqml.h> |
| #include <QtQml/qqmlinfo.h> |
| #include <QImage> |
| #include <QDebug> |
| #include <QRandomGenerator> |
| QT_BEGIN_NAMESPACE |
| /*! |
| \qmltype MaskShape |
| \instantiates QQuickMaskExtruder |
| \inqmlmodule QtQuick.Particles |
| \inherits Shape |
| \brief For representing an image as a shape to affectors and emitters. |
| \ingroup qtquick-particles |
| |
| */ |
| /*! |
| \qmlproperty url QtQuick.Particles::MaskShape::source |
| |
| The image to use as the mask. Areas with non-zero opacity |
| will be considered inside the shape. |
| */ |
| |
| |
| QQuickMaskExtruder::QQuickMaskExtruder(QObject *parent) : |
| QQuickParticleExtruder(parent) |
| , m_lastWidth(-1) |
| , m_lastHeight(-1) |
| { |
| } |
| |
| void QQuickMaskExtruder::setSource(const QUrl &arg) |
| { |
| if (m_source != arg) { |
| m_source = arg; |
| |
| m_lastHeight = -1;//Trigger reset |
| m_lastWidth = -1; |
| emit sourceChanged(m_source); |
| startMaskLoading(); |
| } |
| } |
| |
| void QQuickMaskExtruder::startMaskLoading() |
| { |
| m_pix.clear(this); |
| if (m_source.isEmpty()) |
| return; |
| m_pix.load(qmlEngine(this), m_source); |
| if (m_pix.isLoading()) |
| m_pix.connectFinished(this, SLOT(finishMaskLoading())); |
| else |
| finishMaskLoading(); |
| } |
| |
| void QQuickMaskExtruder::finishMaskLoading() |
| { |
| if (m_pix.isError()) |
| qmlWarning(this) << m_pix.error(); |
| } |
| |
| QPointF QQuickMaskExtruder::extrude(const QRectF &r) |
| { |
| ensureInitialized(r); |
| if (!m_mask.count() || m_img.isNull()) |
| return r.topLeft(); |
| const QPointF p = m_mask[QRandomGenerator::global()->bounded(m_mask.count())]; |
| //### Should random sub-pixel positioning be added? |
| return p + r.topLeft(); |
| } |
| |
| bool QQuickMaskExtruder::contains(const QRectF &bounds, const QPointF &point) |
| { |
| ensureInitialized(bounds);//###Current usage patterns WILL lead to different bounds/r calls. Separate list? |
| if (m_img.isNull()) |
| return false; |
| |
| QPointF pt = point - bounds.topLeft(); |
| QPoint p(pt.x() * m_img.width() / bounds.width(), |
| pt.y() * m_img.height() / bounds.height()); |
| return m_img.rect().contains(p) && (m_img.pixel(p) & 0xff000000); |
| } |
| |
| void QQuickMaskExtruder::ensureInitialized(const QRectF &rf) |
| { |
| // Convert to integer coords to avoid comparing floats and ints which would |
| // often result in rounding errors. |
| QRect r = rf.toRect(); |
| if (m_lastWidth == r.width() && m_lastHeight == r.height()) |
| return;//Same as before |
| if (!m_pix.isReady()) |
| return; |
| m_lastWidth = r.width(); |
| m_lastHeight = r.height(); |
| |
| m_mask.clear(); |
| |
| m_img = m_pix.image(); |
| // Image will in all likelyhood be in this format already, so |
| // no extra memory or conversion takes place |
| if (m_img.format() != QImage::Format_ARGB32 && m_img.format() != QImage::Format_ARGB32_Premultiplied) |
| m_img = m_img.convertToFormat(QImage::Format_ARGB32_Premultiplied); |
| |
| // resample on the fly using 16-bit |
| int sx = (m_img.width() << 16) / r.width(); |
| int sy = (m_img.height() << 16) / r.height(); |
| int w = r.width(); |
| int h = r.height(); |
| for (int y=0; y<h; ++y) { |
| const uint *sl = (const uint *) m_img.constScanLine((y * sy) >> 16); |
| for (int x=0; x<w; ++x) { |
| if (sl[(x * sx) >> 16] & 0xff000000) |
| m_mask << QPointF(x, y); |
| } |
| } |
| } |
| QT_END_NAMESPACE |
| |
| #include "moc_qquickmaskextruder_p.cpp" |