Changeset 113 for trunk/src


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).

Location:
trunk/src/gui
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/gui/kernel/qwindowdefs_pm.h

    r108 r113  
    6060typedef LHANDLE HPS;
    6161typedef LHANDLE HPOINTER;
     62typedef LHANDLE HRGN;
    6263typedef struct _QMSG QMSG;
    6364typedef void *MRESULT;
    6465
    6566typedef HWND WId;
     67
     68#define NULLHANDLE ((LHANDLE)0)
    6669
    6770Q_GUI_EXPORT HPS qt_display_ps();
  • trunk/src/gui/painting/qregion.h

    r2 r113  
    144144#if defined(Q_WS_WIN)
    145145    inline HRGN    handle() const { ensureHandle(); return d->rgn; }
     146#elif defined(Q_WS_PM)
     147    inline HRGN handle(int targetHeight = 0) const {
     148        if (d->rgn == NULLHANDLE || d->height != targetHeight)
     149            updateHandle(targetHeight);
     150        return d->rgn;
     151    }
    146152#elif defined(Q_WS_X11)
    147153    inline Region handle() const { if(!d->rgn) updateX11Region(); return d->rgn; }
     
    175181    void ensureHandle() const;
    176182    QRegion winCombine(const QRegion &r, int num) const;
     183#elif defined(Q_WS_PM)
     184    QRegion pmCombine(const QRegion &r, int op) const;
     185    void updateHandle(int targetHeight) const;
    177186#elif defined(Q_WS_X11)
    178187    void updateX11Region() const;
     
    194203#if defined(Q_WS_WIN)
    195204        HRGN   rgn;
     205#elif defined(Q_WS_PM)
     206        HRGN rgn;
     207        int height;
    196208#elif defined(Q_WS_X11)
    197209        Region rgn;
  • trunk/src/gui/painting/qregion_pm.cpp

    r100 r113  
    4848#include "qregion.h"
    4949
     50#include "qt_os2.h"
     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
     159static HRGN bitmapToRegion(const QBitmap& bitmap)
     160{
     161    HRGN region = 0;
     162    QImage image = bitmap.convertToImage();
     163    const int maxrect = 256;
     164    RECTL rects[maxrect];
     165    HPS hps = qt_display_ps();
     166
     167#define FlushSpans \
     168    { \
     169        HRGN r = GpiCreateRegion(hps, n, rects); \
     170            if (region) { \
     171                GpiCombineRegion(hps, region, region, r, CRGN_OR); \
     172                GpiDestroyRegion(hps, r); \
     173        } else { \
     174            region = r; \
     175        } \
     176    }
     177
     178#define AddSpan \
     179    { \
     180        rects[n].xLeft = prev1; \
     181        rects[n].yBottom = -(y+1); \
     182        rects[n].xRight = x-1+1; \
     183        rects[n].yTop = -y; \
     184        n++; \
     185        if (n == maxrect) { \
     186            FlushSpans \
     187            n = 0; \
     188        } \
     189    }
     190
     191    int n = 0;
     192    int zero = 0x00;
     193
     194    int x, y;
     195    for (y = 0; y < image.height(); y++) {
     196        uchar *line = image.scanLine(y);
     197        int w = image.width();
     198        uchar all = zero;
     199        int prev1 = -1;
     200        for (x = 0; x < w;) {
     201            uchar byte = line[x/8];
     202            if (x > w-8 || byte != all) {
     203                for (int b = 8; b > 0 && x < w; b--) {
     204                    if (!(byte & 0x80) == !all) {
     205                    // More of the same
     206                    } else {
     207                                // A change.
     208                                if (all != zero) {
     209                                    AddSpan;
     210                                    all = zero;
     211                                } else {
     212                                    prev1 = x;
     213                                    all = ~zero;
     214                                }
     215                    }
     216                    byte <<= 1;
     217                    x++;
     218                }
     219            } else {
     220                x += 8;
     221            }
     222        }
     223        if (all != zero) {
     224            AddSpan;
     225        }
     226    }
     227    if (n) {
     228        FlushSpans;
     229    }
     230
     231    if (!region)
     232        region = GpiCreateRegion(hps, 0, NULL);
     233
     234    return region;
     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    if (x->rgn != NULLHANDLE)
     253        GpiDestroyRegion(qt_display_ps(), x->rgn);
    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    \internal
     554
     555    Updates the region handle so that it is suitable for selection to
     556    a device with the given \a height.
     557 */
     558void QRegion::updateHandle(int targetHeight) const
     559{
     560    QRegion *that = const_cast<QRegion*>(this); // we're const here
     561    if (d->rgn == NULLHANDLE) {
     562        // a handle of a null region is requested, allocate an empty region
     563        that->detach();
     564        that->d->rgn = GpiCreateRegion(qt_display_ps(), 0, NULL);
     565        that->d->height = targetHeight;
     566    } else if (d->height != targetHeight) {
     567        // align region y axis to the top of the device
     568        that->detach();
     569        POINTL ptl = { 0, targetHeight - d->height };
     570        GpiOffsetRegion(qt_display_ps(), d->rgn, &ptl);
     571        that->d->height = targetHeight;
     572    }
     573}
     574
    227575QT_END_NAMESPACE
Note: See TracChangeset for help on using the changeset viewer.