Changeset 73


Ignore:
Timestamp:
Mar 24, 2006, 12:46:08 AM (19 years ago)
Author:
dmik
Message:

Fixed all the drawing artefacts and distortions seen when applying non-identity transformations to canvas views.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/canvas/qcanvas.cpp

    r71 r73  
    4949#include <stdlib.h>
    5050
     51//#define QT_NO_CANVAS_ROUNDING_FIX
     52
     53#if !defined(QT_NO_TRANSFORMATIONS) && !defined(QT_NO_CANVAS_ROUNDING_FIX)
     54#include <math.h>
     55#endif
     56
    5157class QCanvasData {
    5258public:
     
    108114    }
    109115}
     116
     117#if !defined(QT_NO_TRANSFORMATIONS) && !defined(QT_NO_CANVAS_ROUNDING_FIX)
     118
     119/*!
     120 * \internal
     121 *
     122 * Takes inclusive top left and exclusive bottom right poitns of the given
     123 * rectangle, translates it using the given matrix, calculates the bounding
     124 * rectangle and converts resulting doubles to integers so that points of the
     125 * target raster partially covered by resulting double points are included
     126 * to the resulting rectangle.
     127 *
     128 * Using this function instead of QWMatrix::map(const QRect&) eliminates many
     129 * artefacts and distortions seen in canvas views that use world transformation.
     130 * When scaling up, this function works merely as QWMatrix::map() with
     131 * transformation mode set to Areas, but it also produces correct rectangles
     132 * (as necessary to determine affected chunks) in all other cases.
     133 */
     134static
     135QRect mapRectMax( const QWMatrix &m, const QRect &r )
     136{
     137    if ( m.isIdentity() ) {
     138        return r;
     139    }
     140   
     141    QRect rect = r;
     142
     143    // make bottom right exclusive
     144    ++ rect.rBottom();
     145    ++ rect.rRight();
     146   
     147    double x1, y1, x2, y2;
     148   
     149    if ( m.m12() == 0.0 && m.m21() == 0.0 ) {
     150        // no shear or rotation, map two coords
     151        m.map( rect.left(), rect.top(), &x1, &y1 );
     152        m.map( rect.right(), rect.bottom(), &x2, &y2 );
     153    } else {
     154        double xl = rect.left(), yt = rect.top();
     155        double xr = rect.right(), yb = rect.bottom();
     156        double x, y;
     157        // map four coords and find min/max
     158        m.map( xl, yt, &x, &y );
     159        x1 = x;
     160        y1 = y;
     161        x2 = x;
     162        y2 = y;
     163        m.map( xr, yt, &x, &y );
     164        x1 = QMIN( x1, x );
     165        y1 = QMIN( y1, y );
     166        x2 = QMAX( x2, x );
     167        y2 = QMAX( y2, y );
     168        m.map( xl, yb, &x, &y );
     169        x1 = QMIN( x1, x );
     170        y1 = QMIN( y1, y );
     171        x2 = QMAX( x2, x );
     172        y2 = QMAX( y2, y );
     173        m.map( xr, yb, &x, &y );
     174        x1 = QMIN( x1, x );
     175        y1 = QMIN( y1, y );
     176        x2 = QMAX( x2, x );
     177        y2 = QMAX( y2, y );
     178    }
     179   
     180    // get integers (partially covered points are included)
     181    x1 = floor( x1 );
     182    y1 = floor( y1 );
     183    x2 = ceil( x2 );
     184    y2 = ceil( y2 );
     185   
     186    // compose the result
     187    rect.setCoords( int( x1 ), int( y1 ), int( x2 ), int( y2 ) );
     188    // make bottom right inclusive again
     189    -- rect.rBottom();
     190    -- rect.rRight();
     191   
     192    return rect;
     193}
     194
     195/*!
     196 * \internal
     197 *
     198 * Similar to mapRectMax, but with the opposite meaning. Tries to return
     199 * a region that is completely inside the given rectangle after transformation.
     200 * \a br is the target bounding rectangle (to minimize calculations).
     201 *
     202 * Using this function to clip out areas of the canvas view not covered by the
     203 * canvas eliminates garbage at canvas edges that can be well seen when the
     204 * canvas is rotated in the view.
     205 */
     206static
     207QRegion mapRectMin( const QWMatrix &m, const QRect &r, const QRect &br )
     208{
     209    QRegion rgn;
     210   
     211    if ( m.isIdentity() ) {
     212        rgn = QRegion( r );
     213        return rgn;
     214    }
     215
     216    if ( m.m12() == 0.0 && m.m21() == 0.0 ) {
     217        QRect rect = r;   
     218        // make bottom right exclusive
     219        ++ rect.rBottom();
     220        ++ rect.rRight();
     221        double x1, y1, x2, y2;
     222        // no shear or rotation, map two coords
     223        m.map( rect.left(), rect.top(), &x1, &y1 );
     224        m.map( rect.right(), rect.bottom(), &x2, &y2 );
     225        // get integers (partially covered points are excluded)
     226        x1 = ceil( x1 );
     227        y1 = ceil( y1 );
     228        x2 = floor( x2 );
     229        y2 = floor( y2 );
     230        // compose the result
     231        rect.setCoords( int( x1 ), int( y1 ), int( x2 ), int( y2 ) );
     232        // make bottom right inclusive again
     233        -- rect.rBottom();
     234        -- rect.rRight();
     235        rgn = QRegion( rect );
     236    } else {
     237        // make the rectangle smaller to compensate for differences in
     238        // line drawing routines of the underlying OS (when defining regions
     239        // from polygons) and by Qt tansformations (when transforming images
     240        // or when determining invalid chunks)
     241        QPointArray pa( r );
     242        pa = m.map( pa );
     243        rgn = QRegion( pa );
     244        // limit to the bounding rect (expanded to compensate shifts)
     245        QRect rect = br;
     246        rect.addCoords( -1, 0, 1, 0 );
     247        rgn &= QRegion( rect );
     248        // create a copy of the region and shift each towards other by 1 pixel
     249        // to compensate for (clip out) differences in line drawing routines of
     250        // the underlying OS (when defining regions from polygons) and in Qt
     251        // tansformation routines (when transforming images or when determining
     252        // invalid chunks)
     253        QRegion rgn2 = rgn;
     254        rgn.translate( 1, 0 );
     255        rgn2.translate( -1, 0 );
     256        // take an intersection
     257        rgn &= rgn2;
     258    }
     259   
     260    return rgn;
     261}
     262
     263#endif
    110264
    111265/*
     
    10691223    QWMatrix iwm = wm.invert();
    10701224    // ivr = covers all chunks in vr
     1225#if !defined(QT_NO_CANVAS_ROUNDING_FIX)
     1226    QRect ivr = vr;
     1227    if ( wm.m12() != 0.0 || wm.m21() != 0.0 ) {
     1228        // compensate for rounding errors happening on the path
     1229        // update() -> drawViewArea() after many transformations (can be seen
     1230        // when moving items in a canvas view that makes the canvas smaller
     1231        // and rotates it to ~270 degress clockwise)
     1232        ivr.addCoords( -1, -1, 1, 1 );
     1233    }
     1234    ivr = mapRectMax( iwm, ivr );
     1235#else
    10711236    QRect ivr = iwm.map(vr);
     1237#endif   
    10721238    QWMatrix twm;
    10731239    twm.translate(tl.x(),tl.y());
     
    10821248
    10831249#ifndef QT_NO_TRANSFORMATIONS
     1250#if !defined(QT_NO_CANVAS_ROUNDING_FIX)
     1251        QRect cvr = vr; cvr.moveBy(tl.x(),tl.y());
     1252        QRegion ra = mapRectMin( wm * twm, all, cvr );
     1253#else       
    10841254#if !defined(Q_WS_PM)
    10851255        // For translation-only transformation, it is safe to include the right
     
    10921262            a = QPointArray( all );
    10931263#else
    1094         // Polygons on OS/2 already include the right and bottom edges
     1264        // Polygons in OS/2 already include the right and bottom edges
    10951265        // (why shouldn't they do that?) Indeed, this important moment is not
    10961266        // defined in Qt at all.
    10971267        QPointArray a( all );
    10981268#endif
    1099 
    11001269        a = (wm*twm).map(a);
     1270#endif       
    11011271#else
    11021272#if !defined(Q_WS_PM)
     
    11071277#endif
    11081278        if ( view->viewport()->backgroundMode() == NoBackground ) {
     1279#if !defined(QT_NO_CANVAS_ROUNDING_FIX)
     1280            p->setClipRegion( QRegion( cvr ) - ra );
     1281#else
    11091282            QRect cvr = vr; cvr.moveBy(tl.x(),tl.y());
    11101283            p->setClipRegion(QRegion(cvr)-QRegion(a));
     1284#endif
    11111285            p->fillRect(vr,view->viewport()->palette()
    11121286                .brush(QPalette::Active,QColorGroup::Background));
    11131287        }
     1288#if !defined(QT_NO_CANVAS_ROUNDING_FIX)
     1289        p->setClipRegion( ra );
     1290#else
    11141291        p->setClipRegion(a);
     1292#endif           
    11151293    }
    11161294
     
    11741352            if ( !wm.isIdentity() ) {
    11751353                // r = Visible area of the canvas where there are changes
     1354#if !defined(QT_NO_CANVAS_ROUNDING_FIX)
     1355                QRect r = changeBounds( mapRectMax( view->inverseWorldMatrix(),
     1356                                        area ) );
     1357#else
    11761358                QRect r = changeBounds(view->inverseWorldMatrix().map(area));
     1359#endif               
    11771360                if ( !r.isEmpty() ) {
    11781361                    QPainter p(view->viewport());
     
    11801363                    QPoint tl = view->contentsToViewport(QPoint(0,0));
    11811364                    p.translate(tl.x(),tl.y());
     1365#if !defined(QT_NO_CANVAS_ROUNDING_FIX)
     1366                    drawViewArea( view, &p, mapRectMax( wm, r ), dblbuf );
     1367#else
    11821368                    drawViewArea( view, &p, wm.map(r), dblbuf );
     1369#endif                   
    11831370                    doneareas.append(new QRect(r));
    11841371                }
Note: See TracChangeset for help on using the changeset viewer.