| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the QtGui 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 "private/qpaintengine_blitter_p.h" |
| |
| #include "private/qblittable_p.h" |
| #include "private/qpaintengine_raster_p.h" |
| #include "private/qpainter_p.h" |
| #include "private/qpixmap_blitter_p.h" |
| |
| #ifndef QT_NO_BLITTABLE |
| QT_BEGIN_NAMESPACE |
| |
| #define STATE_XFORM_SCALE 0x00000001 |
| #define STATE_XFORM_COMPLEX 0x00000002 |
| |
| #define STATE_BRUSH_PATTERN 0x00000010 |
| #define STATE_BRUSH_ALPHA 0x00000020 |
| |
| #define STATE_PEN_ENABLED 0x00000100 |
| |
| #define STATE_ANTIALIASING 0x00001000 |
| #define STATE_ALPHA 0x00002000 |
| #define STATE_BLENDING_COMPLEX 0x00004000 |
| |
| #define STATE_CLIPSYS_COMPLEX 0x00010000 |
| #define STATE_CLIP_COMPLEX 0x00020000 |
| |
| |
| class CapabilitiesToStateMask |
| { |
| public: |
| CapabilitiesToStateMask(QBlittable::Capabilities capabilities) |
| : m_capabilities(capabilities) |
| , fillRectMask(0) |
| , drawRectMask(0) |
| , drawPixmapMask(0) |
| , alphaFillRectMask(0) |
| , opacityPixmapMask(0) |
| , capabillitiesState(0) |
| { |
| if (capabilities & QBlittable::SolidRectCapability) |
| setFillRectMask(); |
| if (capabilities & QBlittable::SourcePixmapCapability) |
| setSourcePixmapMask(); |
| if (capabilities & QBlittable::SourceOverPixmapCapability) |
| setSourceOverPixmapMask(); |
| if (capabilities & QBlittable::SourceOverScaledPixmapCapability) |
| setSourceOverScaledPixmapMask(); |
| if (capabilities & QBlittable::AlphaFillRectCapability) |
| setAlphaFillRectMask(); |
| if (capabilities & QBlittable::OpacityPixmapCapability) |
| setOpacityPixmapMask(); |
| } |
| |
| inline bool canBlitterFillRect() const |
| { |
| return checkStateAgainstMask(capabillitiesState, fillRectMask); |
| } |
| |
| inline bool canBlitterAlphaFillRect() const |
| { |
| return checkStateAgainstMask(capabillitiesState, alphaFillRectMask); |
| } |
| |
| inline bool canBlitterDrawRectMask() const |
| { |
| return checkStateAgainstMask(capabillitiesState, drawRectMask); |
| } |
| |
| bool canBlitterDrawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) const |
| { |
| if (pm.handle()->classId() != QPlatformPixmap::BlitterClass) |
| return false; |
| if (checkStateAgainstMask(capabillitiesState, drawPixmapMask)) { |
| if (m_capabilities & (QBlittable::SourceOverPixmapCapability |
| | QBlittable::SourceOverScaledPixmapCapability)) { |
| if (r.size() != sr.size()) |
| return m_capabilities & QBlittable::SourceOverScaledPixmapCapability; |
| else |
| return m_capabilities & QBlittable::SourceOverPixmapCapability; |
| } |
| if ((m_capabilities & QBlittable::SourcePixmapCapability) && r.size() == sr.size() && !pm.hasAlphaChannel()) |
| return m_capabilities & QBlittable::SourcePixmapCapability; |
| } |
| return false; |
| } |
| |
| bool canBlitterDrawPixmapOpacity(const QPixmap &pm) const |
| { |
| if (pm.handle()->classId() != QPlatformPixmap::BlitterClass) |
| return false; |
| |
| return checkStateAgainstMask(capabillitiesState, opacityPixmapMask); |
| } |
| |
| bool canBlitterDrawCachedGlyphs(const QTransform &transform, QFontEngine::GlyphFormat requestedGlyphFormat, bool complexClip) const |
| { |
| if (transform.type() > QTransform::TxScale) |
| return false; |
| if (!(m_capabilities & QBlittable::DrawScaledCachedGlyphsCapability)) |
| return false; |
| if (requestedGlyphFormat == QFontEngine::Format_ARGB && !(m_capabilities & QBlittable::SubPixelGlyphsCapability)) |
| return false; |
| if (complexClip && !(m_capabilities & QBlittable::ComplexClipCapability)) |
| return false; |
| return true; |
| } |
| |
| inline void updateState(uint mask, bool on) { |
| updateStateBits(&capabillitiesState, mask, on); |
| } |
| |
| private: |
| |
| static inline void updateStateBits(uint *state, uint mask, bool on) |
| { |
| *state = on ? (*state | mask) : (*state & ~mask); |
| } |
| |
| static inline bool checkStateAgainstMask(uint state, uint mask) |
| { |
| return !state || (state & mask && !(state & ~mask)); |
| } |
| |
| void setFillRectMask() { |
| updateStateBits(&fillRectMask, STATE_XFORM_SCALE, false); |
| updateStateBits(&fillRectMask, STATE_XFORM_COMPLEX, false); |
| |
| updateStateBits(&fillRectMask, STATE_BRUSH_PATTERN, false); |
| updateStateBits(&fillRectMask, STATE_BRUSH_ALPHA, false); |
| |
| updateStateBits(&fillRectMask, STATE_PEN_ENABLED, true); |
| |
| //Sub-pixel aliasing should not be sent to the blitter |
| updateStateBits(&fillRectMask, STATE_ANTIALIASING, true); |
| updateStateBits(&fillRectMask, STATE_ALPHA, false); |
| updateStateBits(&fillRectMask, STATE_BLENDING_COMPLEX, false); |
| |
| updateStateBits(&fillRectMask, STATE_CLIPSYS_COMPLEX, false); |
| updateStateBits(&fillRectMask, STATE_CLIP_COMPLEX, false); |
| } |
| |
| void setAlphaFillRectMask() { |
| updateStateBits(&alphaFillRectMask, STATE_XFORM_SCALE, false); |
| updateStateBits(&alphaFillRectMask, STATE_XFORM_COMPLEX, false); |
| |
| updateStateBits(&alphaFillRectMask, STATE_BRUSH_PATTERN, false); |
| updateStateBits(&alphaFillRectMask, STATE_BRUSH_ALPHA, true); |
| |
| updateStateBits(&alphaFillRectMask, STATE_PEN_ENABLED, true); |
| |
| //Sub-pixel aliasing should not be sent to the blitter |
| updateStateBits(&alphaFillRectMask, STATE_ANTIALIASING, true); |
| updateStateBits(&alphaFillRectMask, STATE_ALPHA, false); |
| updateStateBits(&alphaFillRectMask, STATE_BLENDING_COMPLEX, false); |
| |
| updateStateBits(&alphaFillRectMask, STATE_CLIPSYS_COMPLEX, false); |
| updateStateBits(&alphaFillRectMask, STATE_CLIP_COMPLEX, false); |
| } |
| |
| void setSourcePixmapMask() { |
| updateStateBits(&drawPixmapMask, STATE_XFORM_SCALE, false); |
| updateStateBits(&drawPixmapMask, STATE_XFORM_COMPLEX, false); |
| |
| updateStateBits(&drawPixmapMask, STATE_BRUSH_PATTERN, true); |
| updateStateBits(&drawPixmapMask, STATE_BRUSH_ALPHA, false); |
| |
| updateStateBits(&drawPixmapMask, STATE_PEN_ENABLED, true); |
| |
| updateStateBits(&drawPixmapMask, STATE_ANTIALIASING, true); |
| updateStateBits(&drawPixmapMask, STATE_ALPHA, false); |
| updateStateBits(&drawPixmapMask, STATE_BLENDING_COMPLEX, false); |
| |
| updateStateBits(&drawPixmapMask, STATE_CLIPSYS_COMPLEX, false); |
| updateStateBits(&drawPixmapMask, STATE_CLIP_COMPLEX, false); |
| } |
| |
| void setSourceOverPixmapMask() { |
| setSourcePixmapMask(); |
| } |
| |
| void setSourceOverScaledPixmapMask() { |
| setSourceOverPixmapMask(); |
| updateStateBits(&drawPixmapMask, STATE_XFORM_SCALE, true); |
| } |
| |
| void setOpacityPixmapMask() { |
| updateStateBits(&opacityPixmapMask, STATE_XFORM_SCALE, true); |
| updateStateBits(&opacityPixmapMask, STATE_XFORM_COMPLEX, false); |
| |
| updateStateBits(&opacityPixmapMask, STATE_BRUSH_PATTERN, true); |
| updateStateBits(&opacityPixmapMask, STATE_BRUSH_ALPHA, true); |
| |
| updateStateBits(&opacityPixmapMask, STATE_PEN_ENABLED, true); |
| |
| updateStateBits(&opacityPixmapMask, STATE_ANTIALIASING, true); |
| updateStateBits(&opacityPixmapMask, STATE_ALPHA, true); |
| updateStateBits(&opacityPixmapMask, STATE_BLENDING_COMPLEX, false); |
| |
| updateStateBits(&opacityPixmapMask, STATE_CLIPSYS_COMPLEX, false); |
| updateStateBits(&opacityPixmapMask, STATE_CLIP_COMPLEX, false); |
| } |
| |
| QBlittable::Capabilities m_capabilities; |
| uint fillRectMask; |
| uint drawRectMask; |
| uint drawPixmapMask; |
| uint alphaFillRectMask; |
| uint opacityPixmapMask; |
| uint capabillitiesState; |
| }; |
| |
| class QBlitterPaintEnginePrivate : public QRasterPaintEnginePrivate |
| { |
| Q_DECLARE_PUBLIC(QBlitterPaintEngine) |
| public: |
| QBlitterPaintEnginePrivate(QBlittablePlatformPixmap *p) |
| : QRasterPaintEnginePrivate() |
| , pmData(p) |
| , caps(pmData->blittable()->capabilities()) |
| , hasXForm(false) |
| |
| {} |
| |
| void lock(); |
| void unlock(); |
| void fillRect(const QRectF &rect, const QColor &color, bool alpha); |
| void clipAndDrawPixmap(const QRectF &clip, const QRectF &target, const QPixmap &pm, const QRectF &sr, bool opacity); |
| |
| |
| void updateCompleteState(QPainterState *s); |
| void updatePenState(QPainterState *s); |
| void updateBrushState(QPainterState *s); |
| void updateOpacityState(QPainterState *s); |
| void updateCompositionModeState(QPainterState *s); |
| void updateRenderHintsState(QPainterState *s); |
| void updateTransformState(QPainterState *s); |
| void updateClipState(QPainterState *s); |
| |
| QBlittablePlatformPixmap *pmData; |
| CapabilitiesToStateMask caps; |
| uint hasXForm; |
| }; |
| |
| |
| inline void QBlitterPaintEnginePrivate::lock() |
| { |
| if (!pmData->blittable()->isLocked()) |
| rasterBuffer->prepare(pmData->buffer()); |
| } |
| |
| inline void QBlitterPaintEnginePrivate::unlock() |
| { |
| pmData->blittable()->unlock(); |
| } |
| |
| // State tracking to make decisions |
| void QBlitterPaintEnginePrivate::updateCompleteState(QPainterState *s) |
| { |
| updatePenState(s); |
| updateBrushState(s); |
| updateOpacityState(s); |
| updateCompositionModeState(s); |
| updateRenderHintsState(s); |
| updateTransformState(s); |
| updateClipState(s); |
| } |
| |
| void QBlitterPaintEnginePrivate::updatePenState(QPainterState *s) |
| { |
| caps.updateState(STATE_PEN_ENABLED, qpen_style(s->pen) != Qt::NoPen); |
| } |
| |
| void QBlitterPaintEnginePrivate::updateBrushState(QPainterState *s) |
| { |
| Qt::BrushStyle style = qbrush_style(s->brush); |
| |
| caps.updateState(STATE_BRUSH_PATTERN, style != Qt::SolidPattern); |
| caps.updateState(STATE_BRUSH_ALPHA, |
| qbrush_color(s->brush).alpha() < 255); |
| } |
| |
| void QBlitterPaintEnginePrivate::updateOpacityState(QPainterState *s) |
| { |
| bool translucent = s->opacity < 1; |
| caps.updateState(STATE_ALPHA, translucent); |
| } |
| |
| void QBlitterPaintEnginePrivate::updateCompositionModeState(QPainterState *s) |
| { |
| bool nonTrivial = s->composition_mode != QPainter::CompositionMode_SourceOver |
| && s->composition_mode != QPainter::CompositionMode_Source; |
| |
| caps.updateState(STATE_BLENDING_COMPLEX, nonTrivial); |
| } |
| |
| void QBlitterPaintEnginePrivate::updateRenderHintsState(QPainterState *s) |
| { |
| bool aa = s->renderHints & QPainter::Antialiasing; |
| caps.updateState(STATE_ANTIALIASING, aa); |
| } |
| |
| void QBlitterPaintEnginePrivate::updateTransformState(QPainterState *s) |
| { |
| QTransform::TransformationType type = s->matrix.type(); |
| |
| // consider scaling operations with a negative factor as "complex" for now. |
| // as some blitters could handle axisymmetrical operations, we should improve blitter |
| // paint engine to handle them as a capability |
| caps.updateState(STATE_XFORM_COMPLEX, (type > QTransform::TxScale) || |
| ((type == QTransform::TxScale) && ((s->matrix.m11() < 0.0) || (s->matrix.m22() < 0.0)))); |
| caps.updateState(STATE_XFORM_SCALE, type > QTransform::TxTranslate); |
| |
| hasXForm = type >= QTransform::TxTranslate; |
| } |
| |
| void QBlitterPaintEnginePrivate::updateClipState(QPainterState *) |
| { |
| const QClipData *clipData = clip(); |
| bool complexClip = clipData && !(clipData->hasRectClip || clipData->hasRegionClip); |
| caps.updateState(STATE_CLIP_COMPLEX, complexClip); |
| } |
| |
| void QBlitterPaintEnginePrivate::fillRect(const QRectF &rect, const QColor &color, bool alpha) |
| { |
| Q_Q(QBlitterPaintEngine); |
| pmData->unmarkRasterOverlay(rect); |
| QRectF targetRect = rect; |
| if (hasXForm) |
| targetRect = q->state()->matrix.mapRect(rect); |
| const QClipData *clipData = clip(); |
| if (clipData) { |
| if (clipData->hasRectClip) { |
| unlock(); |
| if (alpha) |
| pmData->blittable()->alphaFillRect(targetRect & clipData->clipRect, color, q->state()->compositionMode()); |
| else |
| pmData->blittable()->fillRect(targetRect & clipData->clipRect, color); |
| } else if (clipData->hasRegionClip) { |
| for (const QRect &rect : clipData->clipRegion) { |
| QRect intersectRect = rect.intersected(targetRect.toRect()); |
| if (!intersectRect.isEmpty()) { |
| unlock(); |
| if (alpha) |
| pmData->blittable()->alphaFillRect(intersectRect, color, q->state()->compositionMode()); |
| else |
| pmData->blittable()->fillRect(intersectRect, color); |
| } |
| } |
| } |
| } else { |
| if (targetRect.x() >= 0 && targetRect.y() >= 0 |
| && targetRect.width() <= q->paintDevice()->width() |
| && targetRect.height() <= q->paintDevice()->height()) { |
| unlock(); |
| if (alpha) |
| pmData->blittable()->alphaFillRect(targetRect, color, q->state()->compositionMode()); |
| else |
| pmData->blittable()->fillRect(targetRect, color); |
| } else { |
| QRectF deviceRect(0, 0, q->paintDevice()->width(), q->paintDevice()->height()); |
| unlock(); |
| if (alpha) |
| pmData->blittable()->alphaFillRect(deviceRect & targetRect, color, q->state()->compositionMode()); |
| else |
| pmData->blittable()->fillRect(deviceRect & targetRect, color); |
| } |
| } |
| } |
| |
| void QBlitterPaintEnginePrivate::clipAndDrawPixmap(const QRectF &clip, |
| const QRectF &target, |
| const QPixmap &pm, |
| const QRectF &sr, |
| bool opacity) |
| { |
| Q_Q(QBlitterPaintEngine); |
| QRectF intersectedRect = clip.intersected(target); |
| if (intersectedRect.isEmpty()) |
| return; |
| QRectF source = sr; |
| if (intersectedRect.size() != target.size()) { |
| if (sr.size() == target.size()) { |
| // no resize |
| qreal deltaTop = target.top() - intersectedRect.top(); |
| qreal deltaLeft = target.left() - intersectedRect.left(); |
| qreal deltaBottom = target.bottom() - intersectedRect.bottom(); |
| qreal deltaRight = target.right() - intersectedRect.right(); |
| source.adjust(-deltaLeft, -deltaTop, -deltaRight, -deltaBottom); |
| } else { |
| // resize case |
| qreal hFactor = sr.size().width() / target.size().width(); |
| qreal vFactor = sr.size().height() / target.size().height(); |
| qreal deltaTop = (target.top() - intersectedRect.top()) * vFactor; |
| qreal deltaLeft = (target.left() - intersectedRect.left()) * hFactor; |
| qreal deltaBottom = (target.bottom() - intersectedRect.bottom()) * vFactor; |
| qreal deltaRight = (target.right() - intersectedRect.right()) * hFactor; |
| source.adjust(-deltaLeft, -deltaTop, -deltaRight, -deltaBottom); |
| } |
| } |
| pmData->unmarkRasterOverlay(intersectedRect); |
| if (opacity) |
| pmData->blittable()->drawPixmapOpacity(intersectedRect, pm, source, q->state()->compositionMode(), q->state()->opacity); |
| else |
| pmData->blittable()->drawPixmap(intersectedRect, pm, source); |
| } |
| |
| QBlitterPaintEngine::QBlitterPaintEngine(QBlittablePlatformPixmap *p) |
| : QRasterPaintEngine(*(new QBlitterPaintEnginePrivate(p)), p->buffer()) |
| {} |
| |
| // State tracking |
| void QBlitterPaintEngine::penChanged() |
| { |
| Q_D(QBlitterPaintEngine); |
| |
| QRasterPaintEngine::penChanged(); |
| d->updatePenState(state()); |
| } |
| |
| void QBlitterPaintEngine::brushChanged() |
| { |
| Q_D(QBlitterPaintEngine); |
| |
| QRasterPaintEngine::brushChanged(); |
| d->updateBrushState(state()); |
| } |
| |
| void QBlitterPaintEngine::opacityChanged() |
| { |
| Q_D(QBlitterPaintEngine); |
| |
| QRasterPaintEngine::opacityChanged(); |
| d->updateOpacityState(state()); |
| } |
| |
| void QBlitterPaintEngine::compositionModeChanged() |
| { |
| Q_D(QBlitterPaintEngine); |
| |
| QRasterPaintEngine::compositionModeChanged(); |
| d->updateCompositionModeState(state()); |
| } |
| |
| void QBlitterPaintEngine::renderHintsChanged() |
| { |
| Q_D(QBlitterPaintEngine); |
| |
| QRasterPaintEngine::renderHintsChanged(); |
| d->updateRenderHintsState(state()); |
| } |
| |
| void QBlitterPaintEngine::transformChanged() |
| { |
| Q_D(QBlitterPaintEngine); |
| |
| QRasterPaintEngine::transformChanged(); |
| d->updateTransformState(state()); |
| } |
| |
| void QBlitterPaintEngine::clipEnabledChanged() |
| { |
| Q_D(QBlitterPaintEngine); |
| QRasterPaintEngine::clipEnabledChanged(); |
| d->updateClipState(state()); |
| } |
| |
| bool QBlitterPaintEngine::begin(QPaintDevice *pdev) |
| { |
| Q_D(QBlitterPaintEngine); |
| bool ok = QRasterPaintEngine::begin(pdev); |
| #ifdef QT_BLITTER_RASTEROVERLAY |
| d->pmData->unmergeOverlay(); |
| #endif |
| d->pdev = pdev; |
| return ok; |
| } |
| |
| bool QBlitterPaintEngine::end() |
| { |
| #ifdef QT_BLITTER_RASTEROVERLAY |
| Q_D(QBlitterPaintEngine); |
| d->pmData->mergeOverlay(); |
| #endif |
| |
| return QRasterPaintEngine::end(); |
| } |
| |
| void QBlitterPaintEngine::setState(QPainterState *s) |
| { |
| Q_D(QBlitterPaintEngine); |
| |
| QRasterPaintEngine::setState(s); |
| d->updateCompleteState(s); |
| } |
| |
| // Accelerated paths |
| void QBlitterPaintEngine::fill(const QVectorPath &path, const QBrush &brush) |
| { |
| Q_D(QBlitterPaintEngine); |
| if (path.shape() == QVectorPath::RectangleHint) { |
| QRectF rect(((const QPointF *) path.points())[0], ((const QPointF *) path.points())[2]); |
| fillRect(rect, brush); |
| } else { |
| d->lock(); |
| d->pmData->markRasterOverlay(path); |
| QRasterPaintEngine::fill(path, brush); |
| } |
| } |
| |
| void QBlitterPaintEngine::fillRect(const QRectF &rect, const QColor &color) |
| { |
| Q_D(QBlitterPaintEngine); |
| if (d->caps.canBlitterAlphaFillRect()) { |
| d->fillRect(rect, color, true); |
| } else if (d->caps.canBlitterFillRect() && color.alpha() == 0xff) { |
| d->fillRect(rect, color, false); |
| } else { |
| d->lock(); |
| d->pmData->markRasterOverlay(rect); |
| QRasterPaintEngine::fillRect(rect, color); |
| } |
| } |
| |
| void QBlitterPaintEngine::fillRect(const QRectF &rect, const QBrush &brush) |
| { |
| if (rect.size().isEmpty()) |
| return; |
| |
| Q_D(QBlitterPaintEngine); |
| |
| if (qbrush_style(brush) == Qt::SolidPattern |
| && d->caps.canBlitterAlphaFillRect()) { |
| d->fillRect(rect, qbrush_color(brush), true); |
| } else if (qbrush_style(brush) == Qt::SolidPattern |
| && qbrush_color(brush).alpha() == 0xff |
| && d->caps.canBlitterFillRect()) { |
| d->fillRect(rect, qbrush_color(brush), false); |
| } else if ((brush.style() == Qt::TexturePattern) && |
| (brush.transform().type() <= QTransform::TxTranslate) && |
| ((d->caps.canBlitterDrawPixmapOpacity(brush.texture())) || |
| (d->caps.canBlitterDrawPixmap(rect, brush.texture(), rect)))) { |
| bool rectIsFilled = false; |
| QRectF transformedRect = state()->matrix.mapRect(rect); |
| qreal x = transformedRect.x(); |
| qreal y = transformedRect.y(); |
| QPixmap pm = brush.texture(); |
| d->unlock(); |
| int srcX = int(rect.x() - state()->brushOrigin.x() - brush.transform().dx()) % pm.width(); |
| if (srcX < 0) |
| srcX = pm.width() + srcX; |
| const int startX = srcX; |
| int srcY = int(rect.y() - state()->brushOrigin.y() - brush.transform().dy()) % pm.height(); |
| if (srcY < 0) |
| srcY = pm.height() + srcY; |
| while (!rectIsFilled) { |
| qreal blitWidth = (pm.width() ) - srcX; |
| qreal blitHeight = (pm.height() ) - srcY; |
| if (x + blitWidth > transformedRect.right()) |
| blitWidth = transformedRect.right() -x; |
| if (y + blitHeight > transformedRect.bottom()) |
| blitHeight = transformedRect.bottom() - y; |
| const QClipData *clipData = d->clip(); |
| if (clipData->hasRectClip) { |
| QRect targetRect = QRect(x, y, blitWidth, blitHeight).intersected(clipData->clipRect); |
| if (targetRect.isValid()) { |
| int tmpSrcX = srcX + (targetRect.x() - x); |
| int tmpSrcY = srcY + (targetRect.y() - y); |
| QRect srcRect(tmpSrcX, tmpSrcY, targetRect.width(), targetRect.height()); |
| d->pmData->blittable()->drawPixmap(targetRect, pm, srcRect); |
| } |
| } else if (clipData->hasRegionClip) { |
| QRect unclippedTargetRect(x, y, blitWidth, blitHeight); |
| const QRegion targetRegion = clipData->clipRegion.intersected(unclippedTargetRect); |
| for (const QRect &targetRect : targetRegion) { |
| if (!targetRect.isValid() || targetRect.isEmpty()) |
| continue; |
| int tmpSrcX = srcX + (targetRect.x() - x); |
| int tmpSrcY = srcY + (targetRect.y() - y); |
| QRect srcRect(tmpSrcX, tmpSrcY, targetRect.width(), targetRect.height()); |
| d->pmData->blittable()->drawPixmap(targetRect, pm, srcRect); |
| } |
| } |
| x+=blitWidth; |
| if (qFuzzyCompare(x, transformedRect.right())) { |
| x = transformedRect.x(); |
| srcX = startX; |
| srcY = 0; |
| y += blitHeight; |
| if (qFuzzyCompare(y, transformedRect.bottom())) |
| rectIsFilled = true; |
| } else |
| srcX = 0; |
| } |
| } else { |
| d->lock(); |
| d->pmData->markRasterOverlay(rect); |
| QRasterPaintEngine::fillRect(rect, brush); |
| } |
| |
| } |
| |
| void QBlitterPaintEngine::drawRects(const QRect *rects, int rectCount) |
| { |
| Q_D(QBlitterPaintEngine); |
| if (d->caps.canBlitterDrawRectMask()) { |
| for (int i=0; i<rectCount; ++i) |
| d->fillRect(rects[i], qbrush_color(state()->brush), false); |
| } else { |
| d->pmData->markRasterOverlay(rects, rectCount); |
| QRasterPaintEngine::drawRects(rects, rectCount); |
| } |
| } |
| |
| void QBlitterPaintEngine::drawRects(const QRectF *rects, int rectCount) |
| { |
| Q_D(QBlitterPaintEngine); |
| if (d->caps.canBlitterDrawRectMask()) { |
| for (int i = 0; i < rectCount; ++i) |
| d->fillRect(rects[i], qbrush_color(state()->brush), false); |
| } else { |
| d->pmData->markRasterOverlay(rects, rectCount); |
| QRasterPaintEngine::drawRects(rects, rectCount); |
| } |
| } |
| |
| void QBlitterPaintEngine::drawPixmap(const QPointF &pos, const QPixmap &pm) |
| { |
| drawPixmap(QRectF(pos, pm.size()), pm, pm.rect()); |
| } |
| |
| void QBlitterPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) |
| { |
| Q_D(QBlitterPaintEngine); |
| bool canDrawOpacity; |
| |
| canDrawOpacity = d->caps.canBlitterDrawPixmapOpacity(pm); |
| if (canDrawOpacity || (d->caps.canBlitterDrawPixmap(r, pm, sr))) { |
| |
| d->unlock(); |
| QRectF targetRect = r; |
| if (d->hasXForm) |
| targetRect = state()->matrix.mapRect(r); |
| const QClipData *clipData = d->clip(); |
| if (clipData) { |
| if (clipData->hasRectClip) { |
| d->clipAndDrawPixmap(clipData->clipRect, targetRect, pm, sr, canDrawOpacity); |
| } else if (clipData->hasRegionClip) { |
| for (const QRect &rect : clipData->clipRegion) |
| d->clipAndDrawPixmap(rect, targetRect, pm, sr, canDrawOpacity); |
| } |
| } else { |
| QRectF deviceRect(0, 0, paintDevice()->width(), paintDevice()->height()); |
| d->clipAndDrawPixmap(deviceRect, targetRect, pm, sr, canDrawOpacity); |
| } |
| }else { |
| d->lock(); |
| d->pmData->markRasterOverlay(r); |
| QRasterPaintEngine::drawPixmap(r, pm, sr); |
| } |
| } |
| |
| // Overridden methods to lock the graphics memory |
| void QBlitterPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode) |
| { |
| Q_D(QBlitterPaintEngine); |
| d->lock(); |
| d->pmData->markRasterOverlay(points, pointCount); |
| QRasterPaintEngine::drawPolygon(points, pointCount, mode); |
| } |
| |
| void QBlitterPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode) |
| { |
| Q_D(QBlitterPaintEngine); |
| d->lock(); |
| d->pmData->markRasterOverlay(points, pointCount); |
| QRasterPaintEngine::drawPolygon(points, pointCount, mode); |
| } |
| |
| void QBlitterPaintEngine::fillPath(const QPainterPath &path, QSpanData *fillData) |
| { |
| Q_D(QBlitterPaintEngine); |
| d->lock(); |
| d->pmData->markRasterOverlay(path); |
| QRasterPaintEngine::fillPath(path, fillData); |
| } |
| |
| void QBlitterPaintEngine::fillPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode) |
| { |
| Q_D(QBlitterPaintEngine); |
| d->lock(); |
| d->pmData->markRasterOverlay(points, pointCount); |
| QRasterPaintEngine::fillPolygon(points, pointCount, mode); |
| } |
| |
| void QBlitterPaintEngine::drawEllipse(const QRectF &r) |
| { |
| Q_D(QBlitterPaintEngine); |
| d->lock(); |
| d->pmData->markRasterOverlay(r); |
| QRasterPaintEngine::drawEllipse(r); |
| } |
| |
| void QBlitterPaintEngine::drawImage(const QPointF &pos, const QImage &image) |
| { |
| drawImage(QRectF(pos, image.size()), image, image.rect()); |
| } |
| |
| void QBlitterPaintEngine::drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, |
| Qt::ImageConversionFlags flags) |
| { |
| Q_D(QBlitterPaintEngine); |
| d->lock(); |
| d->pmData->markRasterOverlay(r); |
| QRasterPaintEngine::drawImage(r, pm, sr, flags); |
| } |
| |
| void QBlitterPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pm, const QPointF &sr) |
| { |
| Q_D(QBlitterPaintEngine); |
| d->lock(); |
| d->pmData->markRasterOverlay(r); |
| QRasterPaintEngine::drawTiledPixmap(r, pm, sr); |
| } |
| |
| void QBlitterPaintEngine::drawTextItem(const QPointF &pos, const QTextItem &ti) |
| { |
| Q_D(QBlitterPaintEngine); |
| d->lock(); |
| d->pmData->markRasterOverlay(pos, ti); |
| QRasterPaintEngine::drawTextItem(pos, ti); |
| } |
| |
| void QBlitterPaintEngine::drawPoints(const QPointF *points, int pointCount) |
| { |
| Q_D(QBlitterPaintEngine); |
| d->lock(); |
| d->pmData->markRasterOverlay(points, pointCount); |
| QRasterPaintEngine::drawPoints(points, pointCount); |
| } |
| |
| void QBlitterPaintEngine::drawPoints(const QPoint *points, int pointCount) |
| { |
| Q_D(QBlitterPaintEngine); |
| d->lock(); |
| d->pmData->markRasterOverlay(points, pointCount); |
| QRasterPaintEngine::drawPoints(points, pointCount); |
| } |
| |
| void QBlitterPaintEngine::stroke(const QVectorPath &path, const QPen &pen) |
| { |
| Q_D(QBlitterPaintEngine); |
| d->lock(); |
| d->pmData->markRasterOverlay(path); |
| QRasterPaintEngine::stroke(path, pen); |
| } |
| |
| void QBlitterPaintEngine::drawStaticTextItem(QStaticTextItem *sti) |
| { |
| Q_D(QBlitterPaintEngine); |
| d->lock(); |
| QRasterPaintEngine::drawStaticTextItem(sti); |
| |
| #ifdef QT_BLITTER_RASTEROVERLAY |
| //#### d->pmData->markRasterOverlay(sti); |
| qWarning("not implemented: markRasterOverlay for QStaticTextItem"); |
| #endif |
| } |
| |
| bool QBlitterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs, const QFixedPoint *positions, QFontEngine *fontEngine) |
| { |
| Q_D(QBlitterPaintEngine); |
| QFontEngine::GlyphFormat glyphFormat = d->glyphCacheFormat; |
| if (fontEngine->glyphFormat != QFontEngine::Format_None) |
| glyphFormat = fontEngine->glyphFormat; |
| |
| const QClipData *clipData = d->clip(); |
| const bool complexClip = clipData && !clipData->hasRectClip; |
| |
| const QPainterState *s = state(); |
| if (d->caps.canBlitterDrawCachedGlyphs(s->transform(), glyphFormat, complexClip)) { |
| d->unlock(); |
| const bool result = d->pmData->blittable()->drawCachedGlyphs(s, glyphFormat, numGlyphs, glyphs, positions, fontEngine); |
| // Lock again as the raster paint engine might draw decorations now. |
| d->lock(); |
| return result; |
| } else { |
| return QRasterPaintEngine::drawCachedGlyphs(numGlyphs, glyphs, positions, fontEngine); |
| } |
| } |
| |
| QT_END_NAMESPACE |
| #endif //QT_NO_BLITTABLE |
| |