diff options
Diffstat (limited to 'src/gui/painting/qpaintengine_blitter.cpp')
-rw-r--r-- | src/gui/painting/qpaintengine_blitter.cpp | 622 |
1 files changed, 622 insertions, 0 deletions
diff --git a/src/gui/painting/qpaintengine_blitter.cpp b/src/gui/painting/qpaintengine_blitter.cpp new file mode 100644 index 0000000..f2e6cdd --- /dev/null +++ b/src/gui/painting/qpaintengine_blitter.cpp @@ -0,0 +1,622 @@ +#include "private/qpaintengine_blitter_p.h" + +#include "private/qblittable_p.h" +#include "private/qpaintengine_raster_p.h" +#include "private/qpainter_p.h" +#include "private/qapplication_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 + + +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)); +} + +class CapabilitiesToStateMask +{ +public: + CapabilitiesToStateMask(QBlittable::Capabilities capabilities) + : m_capabilities(capabilities), + fillRectMask(0), + drawRectMask(0), + drawPixmapMask(0), + capabillitiesState(0) + { + if (capabilities & QBlittable::SolidRectCapability) { + setFillRectMask(); + } + if (capabilities & QBlittable::SourcePixmapCapability) { + setSourcePixmapMask(); + } + if (capabilities & QBlittable::SourceOverPixmapCapability) { + setSourceOverPixmapMask(); + } + if (capabilities & QBlittable::SourceOverScaledPixmapCapability) { + setSourceOverScaledPixmapMask(); + } + } + + inline bool canBlitterFillRect() const + { + return checkStateAgainstMask(capabillitiesState,fillRectMask); + } + + inline bool canBlitterDrawRectMask() const + { + return checkStateAgainstMask(capabillitiesState,drawRectMask); + } + + bool canBlitterDrawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) const + { + if (pm.pixmapData()->classId() != QPixmapData::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; + } + + inline void updateState(uint mask, bool on) { + updateStateBits(&capabillitiesState,mask,on); + } + +public: + + 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 setSourcePixmapMask() { + updateStateBits(&drawPixmapMask, STATE_XFORM_SCALE, true); + 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(&drawRectMask, STATE_XFORM_SCALE, true); + } + + QBlittable::Capabilities m_capabilities; + uint fillRectMask; + uint drawRectMask; + uint drawPixmapMask; + uint capabillitiesState; +}; + +class QBlitterPaintEnginePrivate : public QPaintEngineExPrivate +{ + Q_DECLARE_PUBLIC(QBlitterPaintEngine); +public: + QBlitterPaintEnginePrivate(QBlittablePixmapData *p) + : QPaintEngineExPrivate(), + pmData(p), + isBlitterLocked(false), + hasXForm(false) + + { + raster = new QRasterPaintEngine(p->buffer()); + capabillities = new CapabilitiesToStateMask(pmData->blittable()->capabilities()); + } + + inline void lock() { + if (!isBlitterLocked) { + raster->d_func()->rasterBuffer->prepare(pmData->blittable()->lock()); + isBlitterLocked = true; + } + } + + inline void unlock() { + if (isBlitterLocked) { + pmData->blittable()->unlock(); + isBlitterLocked = false; + } + } + + void fillRect(const QRectF &rect, const QColor &color) { + Q_Q(QBlitterPaintEngine); + pmData->unmarkRasterOverlay(rect); + QRectF targetRect = rect; + if (hasXForm) { + targetRect = q->state()->matrix.mapRect(rect); + } + const QClipData *clipData = q->clip(); + if (clipData) { + if (clipData->hasRectClip) { + unlock(); + pmData->blittable()->fillRect(targetRect & clipData->clipRect, color); + } else if (clipData->hasRegionClip) { + QVector<QRect> rects = clipData->clipRegion.rects(); + for ( int i = 0; i < rects.size(); i++ ) { + QRect intersectRect = rects.at(i).intersected(targetRect.toRect()); + if (!intersectRect.isEmpty()) { + unlock(); + pmData->blittable()->fillRect(intersectRect,color); + } + } + } + } else { + if (targetRect.x() >= 0 && targetRect.y() >= 0 + && targetRect.width() <= raster->paintDevice()->width() + && targetRect.height() <= raster->paintDevice()->height()) { + unlock(); + pmData->blittable()->fillRect(targetRect,color); + } else { + QRectF deviceRect(0,0,raster->paintDevice()->width(), raster->paintDevice()->height()); + unlock(); + pmData->blittable()->fillRect(deviceRect&targetRect,color); + } + } + } + + void clipAndDrawPixmap(const QRectF &clip, const QRectF &target, const QPixmap &pm, const QRectF &sr) { + QRectF intersectedRect = clip.intersected(target); + if (intersectedRect.isEmpty()) + return; + QRectF source = sr; + if(intersectedRect.size() != target.size()) { + 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); + } + pmData->unmarkRasterOverlay(intersectedRect); + pmData->blittable()->drawPixmap(intersectedRect, pm, source); + } + + void updateClip() { + Q_Q(QBlitterPaintEngine); + const QClipData *clip = q->clip(); + bool complex = clip && !(clip->hasRectClip || clip->hasRegionClip); + capabillities->updateState(STATE_CLIP_COMPLEX, complex); + } + + void systemStateChanged() { + raster->d_func()->systemStateChanged(); + } + + QRasterPaintEngine *raster; + + QBlittablePixmapData *pmData; + bool isBlitterLocked; + + CapabilitiesToStateMask *capabillities; + + uint hasXForm; +}; + +QBlitterPaintEngine::QBlitterPaintEngine(QBlittablePixmapData *p) + : QPaintEngineEx(*(new QBlitterPaintEnginePrivate(p))) +{ +} + +QBlitterPaintEngine::~QBlitterPaintEngine() +{ +} + +QPainterState *QBlitterPaintEngine::createState(QPainterState *orig) const +{ + Q_D(const QBlitterPaintEngine); + return d->raster->createState(orig); +} + +bool QBlitterPaintEngine::begin(QPaintDevice *pdev) +{ + Q_D(QBlitterPaintEngine); + + setActive(true); + bool ok = d->raster->begin(pdev); +#ifdef QT_BLITTER_RASTEROVERLAY + d->pmData->unmergeOverlay(); +#endif + return ok; +} + + +bool QBlitterPaintEngine::end() +{ + Q_D(QBlitterPaintEngine); + + setActive(false); +#ifdef QT_BLITTER_RASTEROVERLAY + d->pmData->mergeOverlay(); +#endif + return d->raster->end(); +} + + +void QBlitterPaintEngine::fill(const QVectorPath &path, const QBrush &brush) +{ + Q_D(QBlitterPaintEngine); + if (path.shape() == QVectorPath::RectangleHint) { + QRectF rect(((QPointF *) path.points())[0], ((QPointF *) path.points())[2]); + fillRect(rect, brush); + } else { + d->lock(); + d->pmData->markRasterOverlay(path); + d->raster->fill(path, brush); + } +} + +void QBlitterPaintEngine::fillRect(const QRectF &rect, const QColor &color) +{ + Q_D(QBlitterPaintEngine); + if (d->capabillities->canBlitterFillRect() && color.alpha() == 0xff) { + d->fillRect(rect, color); + } else { + d->lock(); + d->pmData->markRasterOverlay(rect); + d->raster->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 + && qbrush_color(brush).alpha() == 0xff + && d->capabillities->canBlitterFillRect()) + { + d->fillRect(rect, qbrush_color(brush)); + }else if (brush.style() == Qt::TexturePattern + && d->capabillities->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()) % pm.width(); + if (srcX < 0) + srcX = pm.width() + srcX; + const int startX = srcX; + int srcY = int(rect.y() - state()->brushOrigin.y()) % 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 = 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) { + QVector<QRect> clipRects = clipData->clipRegion.rects(); + QRect unclippedTargetRect(x,y,blitWidth,blitHeight); + QRegion intersectedRects = clipData->clipRegion.intersected(unclippedTargetRect); + + for ( int i = 0; i < intersectedRects.rects().size(); i++ ) { + QRect targetRect = intersectedRects.rects().at(i); + 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 (x>=transformedRect.right()) { + x = transformedRect.x(); + srcX = startX; + srcY = 0; + y+=blitHeight; + if (y>=transformedRect.bottom()) + rectIsFilled = true; + } else + srcX = 0; + } + } else { + d->lock(); + d->pmData->markRasterOverlay(rect); + d->raster->fillRect(rect, brush); + } + +} + +void QBlitterPaintEngine::stroke(const QVectorPath &path, const QPen &pen) +{ + Q_D(QBlitterPaintEngine); + d->lock(); + d->pmData->markRasterOverlay(path); + d->raster->stroke(path, pen); +} + +void QBlitterPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op) +{ + Q_D(QBlitterPaintEngine); + d->lock(); + d->raster->clip(path, op); + d->updateClip(); +} +void QBlitterPaintEngine::clip(const QRect &rect, Qt::ClipOperation op){ + Q_D(QBlitterPaintEngine); + d->lock(); + d->raster->clip(rect,op); + d->updateClip(); +} +void QBlitterPaintEngine::clip(const QRegion ®ion, Qt::ClipOperation op) +{ + Q_D(QBlitterPaintEngine); + d->lock(); + d->raster->clip(region,op); + d->updateClip(); +} + +void QBlitterPaintEngine::clipEnabledChanged() +{ + Q_D(QBlitterPaintEngine); + d->lock(); + d->raster->clipEnabledChanged(); +} + +void QBlitterPaintEngine::penChanged() +{ + Q_D(QBlitterPaintEngine); + d->lock(); + d->raster->penChanged(); + d->capabillities->updateState(STATE_PEN_ENABLED,qpen_style(state()->pen) != Qt::NoPen); +} + +void QBlitterPaintEngine::brushChanged() +{ + Q_D(QBlitterPaintEngine); + d->raster->brushChanged(); + + bool solid = qbrush_style(state()->brush) == Qt::SolidPattern; + + d->capabillities->updateState(STATE_BRUSH_PATTERN, !solid); + d->capabillities->updateState(STATE_BRUSH_ALPHA, + qbrush_color(state()->brush).alpha() < 255); +} + +void QBlitterPaintEngine::brushOriginChanged() +{ + Q_D(QBlitterPaintEngine); + d->raster->brushOriginChanged(); +} + +void QBlitterPaintEngine::opacityChanged() +{ + Q_D(QBlitterPaintEngine); + d->raster->opacityChanged(); + + bool translucent = state()->opacity < 1; + d->capabillities->updateState(STATE_ALPHA,translucent); +} + +void QBlitterPaintEngine::compositionModeChanged() +{ + Q_D(QBlitterPaintEngine); + d->raster->compositionModeChanged(); + + bool nonTrivial = state()->composition_mode != QPainter::CompositionMode_SourceOver + && state()->composition_mode != QPainter::CompositionMode_Source; + + d->capabillities->updateState(STATE_BLENDING_COMPLEX,nonTrivial); +} + +void QBlitterPaintEngine::renderHintsChanged() +{ + Q_D(QBlitterPaintEngine); + d->raster->renderHintsChanged(); + + bool aa = state()->renderHints & QPainter::Antialiasing; + d->capabillities->updateState(STATE_ANTIALIASING, aa); + +} + +void QBlitterPaintEngine::transformChanged() +{ + Q_D(QBlitterPaintEngine); + d->raster->transformChanged(); + + QTransform::TransformationType type = state()->matrix.type(); + + d->capabillities->updateState(STATE_XFORM_COMPLEX, type > QTransform::TxScale); + d->capabillities->updateState(STATE_XFORM_SCALE, type > QTransform::TxTranslate); + + d->hasXForm = type >= QTransform::TxTranslate; + +} + +void QBlitterPaintEngine::drawRects(const QRect *rects, int rectCount) +{ + Q_D(QBlitterPaintEngine); + if (d->capabillities->canBlitterDrawRectMask()) { + for (int i=0; i<rectCount; ++i) { + d->fillRect(rects[i], qbrush_color(state()->brush)); + } + } else { + d->pmData->markRasterOverlay(rects,rectCount); + QPaintEngineEx::drawRects(rects, rectCount); + } +} + +void QBlitterPaintEngine::drawRects(const QRectF *rects, int rectCount) +{ + Q_D(QBlitterPaintEngine); + if (d->capabillities->canBlitterDrawRectMask()) { + for (int i=0; i<rectCount; ++i) { + d->fillRect(rects[i], qbrush_color(state()->brush)); + } + } else { + d->pmData->markRasterOverlay(rects,rectCount); + QPaintEngineEx::drawRects(rects, rectCount); + } +} + +void QBlitterPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) +{ + Q_D(QBlitterPaintEngine); + if (d->capabillities->canBlitterDrawPixmap(r,pm,sr)) { + + d->unlock(); + QRectF targetRect = r; + if (d->hasXForm) { + targetRect = state()->matrix.mapRect(r); + } + const QClipData *clipData = clip(); + if (clipData) { + if (clipData->hasRectClip) { + d->clipAndDrawPixmap(clipData->clipRect,targetRect,pm,sr); + }else if (clipData->hasRegionClip) { + QVector<QRect>rects = clipData->clipRegion.rects(); + for (int i = 0; i<rects.size(); i++) { + d->clipAndDrawPixmap(rects.at(i),targetRect,pm,sr); + } + } + } else { + QRectF deviceRect(0,0,d->raster->paintDevice()->width(), d->raster->paintDevice()->height()); + d->clipAndDrawPixmap(deviceRect,targetRect,pm,sr); + } + }else { + d->lock(); + d->pmData->markRasterOverlay(r); + d->raster->drawPixmap(r, pm, sr); + } +} + +void QBlitterPaintEngine::drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, + Qt::ImageConversionFlags flags) +{ + Q_D(QBlitterPaintEngine); + d->lock(); + d->pmData->markRasterOverlay(r); + d->raster->drawImage(r, pm, sr, flags); +} + + +void QBlitterPaintEngine::drawTextItem(const QPointF &pos, const QTextItem &ti) +{ + Q_D(QBlitterPaintEngine); + d->lock(); + d->raster->drawTextItem(pos, ti); + d->pmData->markRasterOverlay(pos,ti); +} + +void QBlitterPaintEngine::drawStaticTextItem(QStaticTextItem *sti) +{ + Q_D(QBlitterPaintEngine); + d->lock(); + d->raster->drawStaticTextItem(sti); + +//#### d->pmData->markRasterOverlay(sti); + qWarning("not implemented: markRasterOverlay for QStaticTextItem"); + +} + + +void QBlitterPaintEngine::drawEllipse(const QRectF &r) +{ + Q_D(QBlitterPaintEngine); + d->lock(); + d->pmData->markRasterOverlay(r); + d->raster->drawEllipse(r); +} + +void QBlitterPaintEngine::setState(QPainterState *s) +{ + Q_D(QBlitterPaintEngine); + d->lock(); + QPaintEngineEx::setState(s); + d->raster->setState(s); + + clipEnabledChanged(); + penChanged(); + brushChanged(); + brushOriginChanged(); + opacityChanged(); + compositionModeChanged(); + renderHintsChanged(); + transformChanged(); + + d->updateClip(); +} + +inline QRasterPaintEngine *QBlitterPaintEngine::raster() const +{ + Q_D(const QBlitterPaintEngine); + return d->raster; +} + +QT_END_NAMESPACE +#endif //QT_NO_BLITTABLE + |