Ignore:
Timestamp:
Aug 14, 2009, 11:18:30 PM (16 years ago)
Author:
Dmitry A. Kuminov
Message:

gui: Implemented the OS/2 bits of QRegion (#37).

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/gui/painting/qregion_pm.cpp

    r100 r113  
    4848#include "qregion.h"
    4949
     50
     51
    5052QT_BEGIN_NAMESPACE
    5153
    52 QRegion::QRegionData QRegion::shared_empty = { Q_BASIC_ATOMIC_INITIALIZER(1) }; // @todo, 0 };
     54//  To compensate the difference between Qt (where y axis goes downwards) and
     55//  GPI (where y axis goes upwards) coordinate spaces when dealing with regions
     56//  we use the following technique: when a GPI resource is allocated for a Qt
     57//  region, we simply change the sign of all y coordinates to quickly flip it
     58//  top to bottom in a manner that doesn't depend on the target device height.
     59//  All we have to do to apply the created GPI region to a particular GPI device
     60//  is to align its y axis to the top of the device (i.e. offset the region
     61//  up by the height of the device), and unalign it afterwards to bring it back
     62//  to the coordinate space of other device-independent (unaligned) regions.
     63//  To optimize this, we remember (in data->hgt) the last height value used to
     64//  align the region, and align it again only if the target device height
     65//  changes. Zero height indicates a device-independent target (such as other
     66//  unaligned Qt region).
     67//
     68//  The handle() function, used for external access to the region, takes an
     69//  argument that must be always set to the height of the target device to
     70//  guarantee the correct coordinate space alignment.
     71
     72#if defined(__GNUC__) && defined(__INNOTEK_LIBC__)
     73
     74// Innotek GCC lacks some API functions in its version of OS/2 Toolkit headers
     75
     76extern "C" HRGN APIENTRY GpiCreateEllipticRegion(HPS hps,
     77                                                 PRECTL prclRect);
     78
     79extern "C" HRGN APIENTRY GpiCreatePolygonRegion(HPS hps,
     80                                                ULONG ulCount,
     81                                                PPOLYGON paplgn,
     82                                                ULONG flOptions);
     83#endif
     84
     85QRegion::QRegionData QRegion::shared_empty = { Q_BASIC_ATOMIC_INITIALIZER(1),
     86                                               NULLHANDLE, 0 };
    5387
    5488QRegion::QRegion()
     
    6094QRegion::QRegion(const QRect &r, RegionType t)
    6195{
    62     d = new QRegionData;
    63     d->ref = 1;
    64     // @todo implement
     96    if (r.isEmpty()) {
     97        d = &shared_empty;
     98        d->ref.ref();
     99    } else {
     100        d = new QRegionData;
     101        d->ref = 1;
     102        d->height = 0;
     103        HPS hps = qt_display_ps();
     104        if (t == Rectangle) {
     105            RECTL rcl = { r.left(), -(r.bottom()+1), r.right()+1, -r.top() };
     106            d->rgn = GpiCreateRegion(hps, 1, &rcl);
     107        } else if (t == Ellipse) {
     108            // if the width or height of the ellipse is odd, GPI always
     109            // converts it to a nearest even value, which is obviously stupid
     110            // So, we don't use GpiCreateEllipticRegion(), but create an array
     111            // of points to call GpiCreatePolygonRegion() instead.
     112            QPainterPath p(QPointF(r.x(), r.y()));
     113            p.arcTo(r.x(), r.y(), r.width(), r.height(), 0, 360);
     114            QPolygon a = p.toFillPolygon().toPolygon();
     115            for (int i = 0; i < a.size(); ++ i)
     116                a[i].ry() = -(a[i].y() + 1);
     117            // GpiCreatePolygonRegion() is bogus and always starts a poligon from
     118            // the current position. Make the last point the current one and reduce
     119            // the number of points by one.
     120            GpiMove(hps, reinterpret_cast<PPOINTL>(&a[a.size() - 1]));
     121            POLYGON poly = { a.size() - 1, reinterpret_cast<PPOINTL>(a.data()) };
     122            d->rgn = GpiCreatePolygonRegion(hps, 1, &poly, POLYGON_ALTERNATE);
     123        }
     124    }
    65125}
    66126
    67127QRegion::QRegion(const QPolygon &a, Qt::FillRule fillRule)
    68128{
    69     d = new QRegionData;
    70     d->ref = 1;
    71     // @todo implement
     129    if (a.size() < 3) {
     130        d = &shared_empty;
     131        d->ref.ref();
     132    } else {
     133        d = new QRegionData;
     134        d->ref = 1;
     135        d->height = 0;
     136        HPS hps = qt_display_ps();
     137        POINTL *pts = new POINTL[a.size()];
     138        for (int i = 0; i < a.size(); ++ i) {
     139            pts[i].x = a[i].x();
     140            pts[i].y = - (a[i].y() + 1);
     141        }
     142        // GpiCreatePolygonRegion() is bogus and always starts a poligon from
     143        // the current position. Make the last point the current one and reduce
     144        // the number of points by one.
     145        GpiMove(hps, &pts[a.size() - 1]);
     146        POLYGON poly = { a.size() - 1, pts };
     147        ULONG opts = Qt::OddEvenFill ? POLYGON_ALTERNATE : POLYGON_WINDING;
     148        d->rgn = GpiCreatePolygonRegion(hps, 1, &poly, opts);
     149        delete[] pts;
     150    }
    72151}
    73152
     
    78157}
    79158
     159
     160
     161
     162
     163
     164
     165
     166
     167
     168
     169
     170
     171
     172
     173
     174
     175
     176
     177
     178
     179
     180
     181
     182
     183
     184
     185
     186
     187
     188
     189
     190
     191
     192
     193
     194
     195
     196
     197
     198
     199
     200
     201
     202
     203
     204
     205
     206
     207
     208
     209
     210
     211
     212
     213
     214
     215
     216
     217
     218
     219
     220
     221
     222
     223
     224
     225
     226
     227
     228
     229
     230
     231
     232
     233
     234
     235
     236
    80237QRegion::QRegion(const QBitmap &bm)
    81238{
    82     d = new QRegionData;
    83     d->ref = 1;
    84     // @todo implement
     239    if (bm.isNull()) {
     240        d = &shared_empty;
     241        d->ref.ref();
     242    } else {
     243        d = new QRegionData;
     244        d->ref = 1;
     245        d->height = 0;
     246        d->rgn = bitmapToRegion(bm);
     247    }
    85248}
    86249
    87250void QRegion::cleanUp(QRegion::QRegionData *x)
    88251{
     252
     253
    89254    delete x;
    90     // @todo implement
    91255}
    92256
     
    109273QRegion QRegion::copy() const
    110274{
    111     // @todo implement
    112     return QRegion();
     275    QRegion r;
     276    QRegionData *x = new QRegionData;
     277    x->ref = 1;
     278    if (d->rgn != NULLHANDLE) {
     279        x->height = d->height;
     280        HPS hps = qt_display_ps();
     281        x->rgn = GpiCreateRegion(hps, 0, NULL);
     282        GpiCombineRegion(hps, x->rgn, d->rgn, NULL, CRGN_COPY);
     283    } else {
     284        x->height = 0;
     285        x->rgn = NULLHANDLE;
     286    }
     287    if (!r.d->ref.deref())
     288        cleanUp(r.d);
     289    r.d = x;
     290    return r;
    113291}
    114292
     
    118296}
    119297
    120 
    121298bool QRegion::contains(const QPoint &p) const
    122299{
    123     // @todo implement
    124     return false;
     300    LONG rc = PRGN_OUTSIDE;
     301    if (d->rgn != NULLHANDLE) {
     302        POINTL ptl = { p.x(), d->height - (p.y() + 1) };
     303        rc = GpiPtInRegion(qt_display_ps(), d->rgn, &ptl);
     304    }
     305    return rc == PRGN_INSIDE;
    125306}
    126307
    127308bool QRegion::contains(const QRect &r) const
    128309{
    129     // @todo implement
    130     return false;
    131 }
    132 
     310    LONG rc = PRGN_OUTSIDE;
     311    if (d->rgn != NULLHANDLE) {
     312        RECTL rcl = { r.left(), d->height - (r.bottom() + 1),
     313                      r.right() + 1, d->height - r.top() };
     314        rc = GpiRectInRegion(qt_display_ps(), d->rgn, &rcl);
     315    }
     316    return rc == RRGN_INSIDE || rc == RRGN_PARTIAL;
     317}
    133318
    134319void QRegion::translate(int dx, int dy)
    135320{
    136     // @todo implement
     321    if (d->rgn == NULLHANDLE || (dx == 0 && dy == 0))
     322        return;
     323    detach();
     324    POINTL ptl = { dx, -dy };
     325    GpiOffsetRegion(qt_display_ps(), d->rgn, &ptl);
     326}
     327
     328#define CRGN_NOP -1
     329
     330// Duplicates of those in qregion.cpp
     331#define QRGN_OR               6
     332#define QRGN_AND              7
     333#define QRGN_SUB              8
     334#define QRGN_XOR              9
     335
     336/*
     337  Performs the actual OR, AND, SUB and XOR operation between regions.
     338  Sets the resulting region handle to 0 to indicate an empty region.
     339*/
     340
     341QRegion QRegion::pmCombine(const QRegion &r, int op) const
     342{
     343    LONG both = CRGN_NOP, left = CRGN_NOP, right = CRGN_NOP;
     344    switch (op) {
     345    case QRGN_OR:
     346        both = CRGN_OR;
     347        left = right = CRGN_COPY;
     348        break;
     349    case QRGN_AND:
     350        both = CRGN_AND;
     351        break;
     352    case QRGN_SUB:
     353        both = CRGN_DIFF;
     354        left = CRGN_COPY;
     355        break;
     356    case QRGN_XOR:
     357        both = CRGN_XOR;
     358        left = right = CRGN_COPY;
     359        break;
     360    default:
     361        qWarning( "QRegion: Internal error in pmCombine" );
     362    }
     363
     364    QRegion result;
     365    if (d->rgn == NULLHANDLE && r.d->rgn == NULLHANDLE)
     366        return result;
     367    HPS hps = qt_display_ps();
     368    result.detach();
     369    result.d->rgn = GpiCreateRegion(hps, 0, NULL);
     370    LONG rc = RGN_NULL;
     371    if (d->rgn != NULLHANDLE && r.d->rgn != NULLHANDLE) {
     372        updateHandle(r.d->height); // bring to the same coordinate space
     373        rc = GpiCombineRegion(hps, result.d->rgn, d->rgn, r.d->rgn, both);
     374        result.d->height = r.d->height;
     375    } else if (d->rgn && left != CRGN_NOP) {
     376        rc = GpiCombineRegion(hps, result.d->rgn, d->rgn, 0, left);
     377        result.d->height = d->height;
     378    } else if (r.d->rgn != NULLHANDLE && right != CRGN_NOP) {
     379        rc = GpiCombineRegion(hps, result.d->rgn, r.d->rgn, 0, right);
     380        result.d->height = r.d->height;
     381    }
     382    if (rc == RGN_NULL || rc == RGN_ERROR) {
     383        result = QRegion(); // shared_empty
     384    }
     385    return result;
    137386}
    138387
    139388QRegion QRegion::unite(const QRegion &r) const
    140389{
    141     // @todo implement
    142     return QRegion();
     390    if (d->rgn == NULLHANDLE)
     391        return r;
     392    if (r.d->rgn == NULLHANDLE)
     393        return *this;
     394    return pmCombine(r, QRGN_OR);
    143395}
    144396
     
    150402QRegion QRegion::intersect(const QRegion &r) const
    151403{
    152     // @todo implement
    153     return QRegion();
     404    if (r.d->rgn == NULLHANDLE || d->rgn == NULLHANDLE)
     405        return QRegion();
     406     return pmCombine(r, QRGN_AND);
    154407}
    155408
    156409QRegion QRegion::subtract(const QRegion &r) const
    157410{
    158     // @todo implement
    159     return QRegion();
     411    if (r.d->rgn == NULLHANDLE || d->rgn == NULLHANDLE)
     412        return *this;
     413    return pmCombine(r, QRGN_SUB);
    160414}
    161415
    162416QRegion QRegion::eor(const QRegion &r) const
    163417{
    164     // @todo implement
    165     return QRegion();
    166 }
    167 
     418    if (d->rgn == NULLHANDLE)
     419        return r;
     420    if (r.d->rgn == NULLHANDLE)
     421        return *this;
     422    return pmCombine(r, QRGN_XOR);
     423}
    168424
    169425QRect QRegion::boundingRect() const
    170426{
    171     // @todo implement
    172     return QRect();
     427    if (!d->rgn)
     428        return QRect();
     429
     430    RECTL rcl;
     431    LONG rc = RGN_NULL;
     432    if (d->rgn != NULLHANDLE)
     433        rc = GpiQueryRegionBox(qt_display_ps(), d->rgn, &rcl);
     434    if (rc == RGN_NULL || rc == RGN_ERROR)
     435        return QRect();
     436    else
     437        return QRect(rcl.xLeft, d->height - rcl.yTop,
     438                     rcl.xRight - rcl.xLeft, rcl.yTop - rcl.yBottom);
    173439}
    174440
    175441QVector<QRect> QRegion::rects() const
    176442{
    177     // @todo implement
    178     return QVector<QRect>();
    179 }
     443    QVector<QRect> a;
     444
     445    if (d->rgn == NULLHANDLE)
     446        return a;
     447
     448    HPS hps = qt_display_ps();
     449    RGNRECT ctl = {1, 0, 0, RECTDIR_LFRT_TOPBOT};
     450    if (!GpiQueryRegionRects(hps, d->rgn, NULL, &ctl, NULL))
     451        return a;
     452
     453    ctl.crc = ctl.crcReturned;
     454    PRECTL rcls = new RECTL[ctl.crcReturned];
     455    if (rcls == 0)
     456        return a;
     457    if (!GpiQueryRegionRects(hps, d->rgn, NULL, &ctl, rcls)) {
     458        delete [] rcls;
     459        return a;
     460    }
     461
     462    a = QVector<QRect>(ctl.crcReturned);
     463    PRECTL r = rcls;
     464    for (int i = 0; i < a.size(); ++i) {
     465        a[i].setRect(r->xLeft, d->height - r->yTop,
     466                     r->xRight - r->xLeft, r->yTop - r->yBottom);
     467        ++r;
     468    }
     469
     470    delete [] rcls;
     471    return a;
     472 }
    180473
    181474void QRegion::setRects(const QRect *rects, int num)
     
    190483int QRegion::numRects() const
    191484{
    192     // @todo implement
    193     return 0;
     485    if (d->rgn == NULLHANDLE)
     486        return 0;
     487
     488    RGNRECT ctl = {1, 0, 0, RECTDIR_LFRT_TOPBOT};
     489    if (!GpiQueryRegionRects(qt_display_ps(), d->rgn, NULL, &ctl, NULL))
     490        return 0;
     491
     492    return ctl.crcReturned;
    194493}
    195494
    196495bool QRegion::operator==(const QRegion &r) const
    197496{
    198     // @todo implement
    199     return false;
     497    if (d == r.d)
     498        return true;
     499    if ((d->rgn == NULLHANDLE) ^ (r.d->rgn == NULLHANDLE)) // one is empty, not both
     500        return false;
     501    if (d->rgn == NULLHANDLE) // both empty
     502        return true;
     503    updateHandle(r.d->height); // bring to the same coordinate space
     504    return // both not empty
     505        GpiEqualRegion(qt_display_ps(), d->rgn, r.d->rgn) == EQRGN_EQUAL;
    200506}
    201507
    202508QRegion& QRegion::operator+=(const QRegion &r)
    203509{
    204     // @todo implement
     510    if (r.d->rgn == NULLHANDLE)
     511        return *this;
     512
     513    if (d->rgn == NULLHANDLE) {
     514        *this = r;
     515        return *this;
     516    }
     517
     518    *this = unite(r);
    205519    return *this;
    206520}
     
    208522QRegion& QRegion::operator-=(const QRegion &r)
    209523{
    210     // @todo implement
     524    if (r.d->rgn == NULLHANDLE || d->rgn == NULLHANDLE)
     525        return *this;
     526
     527    *this = subtract(r);
    211528    return *this;
    212529}
     
    214531QRegion& QRegion::operator&=(const QRegion &r)
    215532{
    216     // @todo implement
     533    if (d->rgn == NULLHANDLE)
     534        return *this;
     535
     536    if (r.d->rgn == NULLHANDLE) {
     537        *this = QRegion();
     538        return *this;
     539    }
     540
     541    *this = intersect(r);
    217542    return *this;
    218543}
     
    225550}
    226551
     552
     553
     554
     555
     556
     557
     558
     559
     560
     561
     562
     563
     564
     565
     566
     567
     568
     569
     570
     571
     572
     573
     574
    227575QT_END_NAMESPACE
Note: See TracChangeset for help on using the changeset viewer.