summaryrefslogtreecommitdiffstats
path: root/src/gui/painting/qpaintengine_blitter.cpp
diff options
context:
space:
mode:
authorJørgen Lind <jorgen.lind@nokia.com>2009-11-09 14:07:50 +0100
committerJørgen Lind <jorgen.lind@nokia.com>2009-11-09 14:08:55 +0100
commit49696780e9cf026b2a7063976eb42f823a6792d8 (patch)
tree2c7ef648030b63494372fca92751d7321b1f7002 /src/gui/painting/qpaintengine_blitter.cpp
parenta43f98dea9024f84223ae8c8c59db1b4e5ead588 (diff)
Adding the blitter paint engine
Diffstat (limited to 'src/gui/painting/qpaintengine_blitter.cpp')
-rw-r--r--src/gui/painting/qpaintengine_blitter.cpp638
1 files changed, 638 insertions, 0 deletions
diff --git a/src/gui/painting/qpaintengine_blitter.cpp b/src/gui/painting/qpaintengine_blitter.cpp
new file mode 100644
index 0000000000..367a8b506c
--- /dev/null
+++ b/src/gui/painting/qpaintengine_blitter.cpp
@@ -0,0 +1,638 @@
+#include "private/qpaintengine_blitter_p.h"
+#include "private/qpaintengine_raster_p.h"
+#include "private/qpainter_p.h"
+#include "private/qapplication_p.h"
+
+#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
+
+//#define RASTEROVERLAY
+
+#ifdef RASTEROVERLAY
+QColor rasterColor(255,0,0,100);
+QBrush rasterBrush(rasterColor);
+#endif
+
+static inline void updateStateBits(uint *state, uint mask, bool on)
+{
+ *state = on ? (*state | mask) : (*state & ~mask);
+}
+
+class CapabilitiesToStateMask
+{
+public:
+ CapabilitiesToStateMask(QBlittable::Capabilities capabilities)
+ : m_capabilities(capabilities),
+ fillRectMask(0), fillRectState(0),
+ drawRectMask(0), drawRectState(0),
+ drawPixmapMask(0), drawPixmapState(0)
+ {
+ if (capabilities & QBlittable::SolidRectCapability) {
+ setFillRectMask();
+ }
+ if (capabilities & QBlittable::SourcePixmapCapability) {
+ setSourcePixmapMask();
+ }
+ if (capabilities & QBlittable::SourceOverPixmapCapability) {
+ setSourceOverPixmapMask();
+ }
+ }
+
+ inline bool canBlitterFillRect() const
+ {
+ return !fillRectState || (fillRectState & fillRectMask);
+ }
+
+ inline bool canBlitterDrawRectMask() const
+ {
+ return drawRectState & drawRectMask;
+ }
+
+ bool canBlitterDrawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) const
+ {
+ if ((!drawPixmapState) || drawPixmapState & 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 updateFillRectBits(uint mask, bool on) {
+ updateStateBits(&fillRectState,mask,on);
+ }
+
+ inline void updateDrawRectBits(uint mask, bool on) {
+ updateStateBits(&drawRectState,mask,on);
+ }
+
+ inline void updateDrawPixmapBits(uint mask, bool on) {
+ updateStateBits(&drawPixmapState,mask,on);
+ }
+ inline void updateAllWithBits(uint mask, bool on) {
+ updateStateBits(&fillRectState,mask,on);
+ updateStateBits(&drawRectState,mask,on);
+ updateStateBits(&drawPixmapState,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, false);
+
+ updateStateBits(&fillRectMask, STATE_ANTIALIASING, false);
+ 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, false);
+ updateStateBits(&drawPixmapMask, STATE_BRUSH_ALPHA, false);
+
+ updateStateBits(&drawPixmapMask, STATE_PEN_ENABLED, false);
+
+ updateStateBits(&drawPixmapMask, STATE_ANTIALIASING, false);
+ 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();
+ }
+
+ QBlittable::Capabilities m_capabilities;
+ uint fillRectMask;
+ uint fillRectState;
+ uint drawRectMask;
+ uint drawRectState;
+ uint drawPixmapMask;
+ uint drawPixmapState;
+};
+
+class QBlitterPaintEnginePrivate : public QPaintEngineExPrivate
+{
+public:
+ QBlitterPaintEnginePrivate(QPaintDevice *p)
+ : QPaintEngineExPrivate(),
+ raster(new QRasterPaintEngine(p)), isBlitterLocked(false),
+ capabillities(0), hasXForm(false)
+ {
+ if (QGraphicsSystem *gs = QApplicationPrivate::graphicsSystem()) {
+// QBlittable *b = gs->createBlitter(p);
+// if (b) {
+// blitter=b;
+// capabillities = new CapabilitiesToStateMask(blitter->capabilities());
+// blitter->unlock();
+// } else
+// qWarning("No blitter returned from the graphics system. QBlitterPaintEngine will not work");
+ }
+
+ }
+
+ inline void lock() {
+ if (!isBlitterLocked) {
+ raster->d_func()->rasterBuffer->prepare(blitter->lock());
+ isBlitterLocked = true;
+ }
+ }
+
+ inline void unlock() {
+ if (isBlitterLocked) {
+ blitter->unlock();
+ isBlitterLocked = false;
+ }
+ }
+
+ void fillRect(const QRectF &rect, const QColor &color) {
+ unlock();
+ QRectF targetRect = rect;
+ if (hasXForm) {
+ targetRect = state->matrix.mapRect(rect);
+ }
+ QClipData *clipData = raster->state()->clip;
+ if (clipData) {
+ if (clipData->hasRectClip)
+ blitter->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()) {
+ blitter->fillRect(intersectRect,color);
+ }
+ }
+ }
+ }else {
+ if (targetRect.x() >= 0 && targetRect.y() >= 0
+ && targetRect.width() <= raster->paintDevice()->width()
+ && targetRect.height() <= raster->paintDevice()->height())
+ blitter->fillRect(targetRect,color);
+ else {
+ QRectF deviceRect(0,0,raster->paintDevice()->width(), raster->paintDevice()->height());
+ blitter->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);
+ }
+ blitter->drawPixmap(intersectedRect, pm, source);
+ }
+
+ void updateClip() {
+ const QClipData *clip = raster->d_func()->clip();
+ bool complex = clip != 0 && !(clip->hasRectClip || clip->hasRegionClip);
+ capabillities->updateAllWithBits(STATE_CLIP_COMPLEX, complex);
+ }
+
+ void systemStateChanged() {
+ raster->d_func()->systemStateChanged();
+ }
+
+ QRasterPaintEngine *raster;
+ QRasterPaintEngineState *state;
+
+ QBlittable *blitter;
+ bool isBlitterLocked;
+
+ CapabilitiesToStateMask *capabillities;
+
+ uint hasXForm;
+};
+
+QBlitterPaintEngine::QBlitterPaintEngine(QPaintDevice *p)
+ : QPaintEngineEx(*(new QBlitterPaintEnginePrivate(p)))
+{
+}
+
+QBlitterPaintEngine::~QBlitterPaintEngine()
+{
+ Q_D(QBlitterPaintEngine);
+}
+
+QPainterState *QBlitterPaintEngine::createState(QPainterState *orig) const
+{
+ Q_D(const QBlitterPaintEngine);
+ return d->raster->createState(orig);
+}
+
+bool QBlitterPaintEngine::begin(QPaintDevice *pdev)
+{
+ Q_D(QBlitterPaintEngine);
+
+ d->raster->setSystemRect(systemRect());
+ d->raster->setSystemClip(systemClip());
+
+ bool ok = d->raster->begin(pdev);
+ return ok;
+}
+
+
+bool QBlitterPaintEngine::end()
+{
+ Q_D(QBlitterPaintEngine);
+
+ d->raster->setPaintDevice(0);
+ d->raster->setActive(false);
+ 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->raster->fill(path, brush);
+#ifdef RASTEROVERLAY
+ d->raster->fill(path,rasterBrush);
+#endif
+ }
+}
+
+void QBlitterPaintEngine::fillRect(const QRectF &rect, const QColor &color)
+{
+ Q_D(QBlitterPaintEngine);
+ if (d->capabillities->canBlitterFillRect() && color.alpha() == 255) {
+ d->fillRect(rect, color);
+ } else {
+ d->lock();
+ d->raster->fillRect(rect, color);
+#ifdef RASTEROVERLAY
+ d->raster->fillRect(rect,rasterColor);
+#endif
+ }
+}
+
+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() == 255
+ && 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() - d->raster->state()->brushOrigin.x()) % pm.width();
+ if (srcX < 0)
+ srcX = pm.width() + srcX;
+ const int startX = srcX;
+ int srcY = (int)(rect.y() - d->raster->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 = d->raster->d_func()->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->blitter->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->blitter->drawPixmap(targetRect,pm,srcRect);
+ }
+ } else Q_ASSERT(false);//should never happen
+ 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->raster->fillRect(rect, brush);
+#ifdef RASTEROVERLAY
+ d->raster->fillRect(rect,rasterBrush);
+#endif
+ }
+
+}
+
+void QBlitterPaintEngine::stroke(const QVectorPath &path, const QPen &pen)
+{
+ Q_D(QBlitterPaintEngine);
+ d->lock();
+ d->raster->stroke(path, pen);
+#ifdef RASTEROVERLAY
+ d->raster->stroke(path,QPen(rasterColor));
+#endif
+}
+
+void QBlitterPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op)
+{
+ Q_D(QBlitterPaintEngine);
+ d->raster->clip(path, op);
+ d->updateClip();
+}
+void QBlitterPaintEngine::clip(const QRect &rect, Qt::ClipOperation op){
+ Q_D(QBlitterPaintEngine);
+ d->raster->clip(rect,op);
+ d->updateClip();
+}
+void QBlitterPaintEngine::clip(const QRegion &region, Qt::ClipOperation op)
+{
+ Q_D(QBlitterPaintEngine);
+ d->raster->clip(region,op);
+ d->updateClip();
+}
+
+void QBlitterPaintEngine::clipEnabledChanged()
+{
+ Q_D(QBlitterPaintEngine);
+ d->raster->clipEnabledChanged();
+}
+
+void QBlitterPaintEngine::penChanged()
+{
+ Q_D(QBlitterPaintEngine);
+ d->raster->penChanged();
+ d->capabillities->updateDrawRectBits(STATE_PEN_ENABLED,qpen_style(d->state->pen) != Qt::NoPen);
+}
+
+void QBlitterPaintEngine::brushChanged()
+{
+ Q_D(QBlitterPaintEngine);
+ d->raster->brushChanged();
+
+ bool solid = qbrush_style(d->state->brush) == Qt::SolidPattern;
+
+ d->capabillities->updateDrawRectBits(STATE_BRUSH_PATTERN, !solid);
+ d->capabillities->updateDrawRectBits(STATE_BRUSH_ALPHA,
+ qbrush_color(d->state->brush).alpha() < 255);
+}
+
+void QBlitterPaintEngine::brushOriginChanged()
+{
+ Q_D(QBlitterPaintEngine);
+ d->raster->brushOriginChanged();
+}
+
+void QBlitterPaintEngine::opacityChanged()
+{
+ Q_D(QBlitterPaintEngine);
+ d->raster->opacityChanged();
+
+ bool translucent = d->state->opacity < 1;
+ d->capabillities->updateAllWithBits(STATE_ALPHA,translucent);
+}
+
+void QBlitterPaintEngine::compositionModeChanged()
+{
+ Q_D(QBlitterPaintEngine);
+ d->raster->compositionModeChanged();
+
+ bool nonTrivial = d->state->composition_mode != QPainter::CompositionMode_SourceOver
+ && d->state->composition_mode != QPainter::CompositionMode_Source;
+
+ d->capabillities->updateAllWithBits(STATE_BLENDING_COMPLEX,nonTrivial);
+}
+
+void QBlitterPaintEngine::renderHintsChanged()
+{
+ Q_D(QBlitterPaintEngine);
+ d->raster->renderHintsChanged();
+
+ bool aa = d->state->renderHints & QPainter::Antialiasing;
+ d->capabillities->updateFillRectBits(STATE_ANTIALIASING, aa);
+ d->capabillities->updateDrawRectBits(STATE_ANTIALIASING, aa);
+
+}
+
+void QBlitterPaintEngine::transformChanged()
+{
+ Q_D(QBlitterPaintEngine);
+ d->raster->transformChanged();
+
+ QTransform::TransformationType type = d->state->matrix.type();
+
+ d->capabillities->updateAllWithBits(STATE_XFORM_COMPLEX, type > QTransform::TxScale);
+
+ 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(d->state->brush));
+ }
+ } else {
+ QPaintEngineEx::drawRects(rects, rectCount);
+#ifdef RASTEROVERLAY
+ d->raster->drawRects(rects,rectCount);
+#endif
+ }
+}
+
+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(d->state->brush));
+ }
+ } else {
+ QPaintEngineEx::drawRects(rects, rectCount);
+#ifdef RASTEROVERLAY
+ d->raster->drawRects(rects,rectCount);
+#endif
+ }
+}
+
+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 = d->raster->d_func()->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->raster->drawPixmap(r, pm, sr);
+#ifdef RASTEROVERLAY
+ d->raster->fillRect(r,rasterColor);
+#endif
+ }
+}
+
+void QBlitterPaintEngine::drawImage(const QRectF &r, const QImage &pm, const QRectF &sr,
+ Qt::ImageConversionFlags flags)
+{
+ Q_D(QBlitterPaintEngine);
+ d->lock();
+ d->raster->drawImage(r, pm, sr, flags);
+#ifdef RASTEROVERLAY
+ d->raster->fillRect(r,rasterColor);
+#endif
+}
+
+
+void QBlitterPaintEngine::drawTextItem(const QPointF &pos, const QTextItem &ti)
+{
+ Q_D(QBlitterPaintEngine);
+ d->lock();
+ d->raster->drawTextItem(pos, ti);
+#ifdef RASTEROVERLAY
+ QFontMetricsF fm(ti.font());
+ QRectF rect = fm.tightBoundingRect(ti.text());
+ rect.moveBottomLeft(pos);
+ d->raster->fillRect(rect, rasterColor);
+#endif
+}
+
+
+void QBlitterPaintEngine::setState(QPainterState *s)
+{
+ Q_D(QBlitterPaintEngine);
+ QPaintEngineEx::setState(s);
+ d->raster->setState(s);
+ d->state = (QRasterPaintEngineState *) s;
+
+ clipEnabledChanged();
+ penChanged();
+ brushChanged();
+ brushOriginChanged();
+ opacityChanged();
+ compositionModeChanged();
+ renderHintsChanged();
+ transformChanged();
+
+ d->updateClip();
+}
+
+class QBlittablePrivate
+{
+public:
+ QBlittable::Capabilities caps;
+};
+
+
+QBlittable::QBlittable(Capabilities caps)
+ : d_ptr(new QBlittablePrivate)
+{
+ d_ptr->caps = caps;
+}
+
+QBlittable::QBlittable(QBlittablePrivate &d, Capabilities caps)
+ : d_ptr(&d)
+{
+ d_ptr->caps = caps;
+}
+
+
+QBlittable::~QBlittable()
+{
+ delete d_ptr;
+}
+
+
+QBlittable::Capabilities QBlittable::capabilities() const
+{
+ Q_D(const QBlittable);
+ return d->caps;
+}
+