/****************************************************************************
** $Id: qpixmap_pm.cpp 8 2005-11-16 19:36:46Z dmik $
**
** Implementation of QPixmap class for OS/2
**
** Copyright (C) 1992-2002 Trolltech AS.  All rights reserved.
** Copyright (C) 2004 Norman ASA.  Initial OS/2 Port.
** Copyright (C) 2005 netlabs.org.  Further OS/2 Development.
**
** This file is part of the kernel module of the Qt GUI Toolkit.
**
** This file may be distributed under the terms of the Q Public License
** as defined by Trolltech AS of Norway and appearing in the file
** LICENSE.QPL included in the packaging of this file.
**
** This file may be distributed and/or modified under the terms of the
** GNU General Public License version 2 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.
**
** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
** licenses may use this file in accordance with the Qt Commercial License
** Agreement provided with the Software.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
**   information about Qt Commercial License Agreements.
** See http://www.trolltech.com/qpl/ for QPL licensing information.
** See http://www.trolltech.com/gpl/ for GPL licensing information.
**
** Contact info@trolltech.com if any conditions of this licensing are
** not clear to you.
**
**********************************************************************/

#include "qbitmap.h"
#include "qimage.h"
#include "qpaintdevicemetrics.h"
#include "qwmatrix.h"
#include "qapplication.h"
#include "qapplication_p.h"
#include "qt_os2.h"

extern const uchar *qt_get_bitflip_array();		// defined in qimage.cpp

static inline HPS alloc_mem_ps( int w, int h, HPS compat = 0 )
{
    HDC hdcCompat = 0;
    if ( compat )
        hdcCompat = GpiQueryDevice( compat );

    static const PSZ hdcData[4] = { "Display", NULL, NULL, NULL };
    HDC hdc = DevOpenDC( 0, OD_MEMORY, "*", 4, (PDEVOPENDATA) hdcData, hdcCompat );
    if ( !hdc ) {
#if defined(QT_CHECK_NULL)
	qSystemWarning( "alloc_mem_dc: DevOpenDC failed!" );
#endif
	return 0;
    }
    SIZEL size = { w, h };
    HPS hps = GpiCreatePS( 0, hdc, &size, PU_PELS | GPIA_ASSOC | GPIT_MICRO );
    if ( !hps ) {
#if defined(QT_CHECK_NULL)
	qSystemWarning( "alloc_mem_dc: GpiCreatePS failed!" );
#endif
	return 0;
    }
    if ( QColor::hPal() ) {
	GpiSelectPalette( hps, QColor::hPal() );
    } else {
        // direct RGB mode
        GpiCreateLogColorTable( hps, 0, LCOLF_RGB, 0, 0, NULL );
    }
    return hps;
}

static inline void free_mem_ps( HPS hps )
{
    HDC hdc = GpiQueryDevice( hps );
    GpiAssociate( hps, 0 );
    GpiDestroyPS( hps );
    DevCloseDC( hdc );
}

extern HPS qt_alloc_mem_ps( int w, int h, HPS compat = 0 )
{
    return alloc_mem_ps( w, h, compat );
}

extern void qt_free_mem_ps( HPS hps )
{
    free_mem_ps( hps );
}

//@@TODO (dmik): later
//void QPixmap::initAlphaPixmap( uchar *bytes, int length, BITMAPINFO *bmi )
//{
//    if ( data->mcp )
//	freeCell( TRUE );
//    if ( !hdc )
//	hdc = alloc_mem_dc( 0, &data->old_hbm );
//
//    HBITMAP hBitmap = CreateDIBSection( hdc, bmi, DIB_RGB_COLORS, (void**)&data->realAlphaBits, NULL, 0 );
//    if ( bytes )
//	memcpy( data->realAlphaBits, bytes, length );
//
//    DeleteObject( SelectObject( hdc, data->old_hbm ) );
//    data->old_hbm = (HBITMAP)SelectObject( hdc, hBitmap );
//    DATA_HBM = hBitmap;
//}


void QPixmap::init( int w, int h, int d, bool bitmap, Optimization optim )
{
#if defined(QT_CHECK_STATE)
    if ( qApp->type() == QApplication::Tty ) {
	qWarning( "QPixmap: Cannot create a QPixmap when no GUI "
		  "is being used" );
    }
#endif

    static int serial = 0;
    int dd = defaultDepth();

    if ( optim == DefaultOptim )		// use default optimization
	optim = defOptim;

    data = new QPixmapData;
    Q_CHECK_PTR( data );

    memset( data, 0, sizeof(QPixmapData) );
    data->count  = 1;
    data->uninit = TRUE;
    data->bitmap = bitmap;
    data->ser_no = ++serial;
    data->optim	 = optim;

    bool make_null = w == 0 || h == 0;		// create null pixmap
    if ( d == 1 )				// monocrome pixmap
	data->d = 1;
    else if ( d < 0 || d == dd )		// compatible pixmap
	data->d = dd;
    if ( make_null || w < 0 || h < 0 || data->d == 0 ) {
	hps = 0;
	data->hbm = 0;
#if defined(QT_CHECK_RANGE)
	if ( !make_null )			// invalid parameters
	    qWarning( "QPixmap: Invalid pixmap parameters" );
#endif
	return;
    }
    data->w = w;
    data->h = h;

    hps = alloc_mem_ps( w, h );
    BITMAPINFOHEADER2 bmh;
    memset( &bmh, 0, sizeof(BITMAPINFOHEADER2) );
    bmh.cbFix = sizeof(BITMAPINFOHEADER2);
    bmh.cx = w;
    bmh.cy = h;
    bmh.cPlanes = 1;
    if ( data->d == dd )			// compatible bitmap
        bmh.cBitCount = dd;
    else					// monocrome bitmap
        bmh.cBitCount = 1;
    data->hbm = GpiCreateBitmap( hps, &bmh, 0, NULL, NULL );

    if ( !data->hbm ) {
	data->w = 0;
	data->h = 0;
        free_mem_ps( hps );
	hps = 0;
#if defined(QT_CHECK_NULL)
	qSystemWarning( "QPixmap: Pixmap allocation failed" );
#endif
	return;
    }

    GpiSetBitmap( hps, data->hbm );
}


void QPixmap::deref()
{
    if ( data && data->deref() ) {		// last reference lost
	if ( data->mask ) {
	    delete data->mask;
            if ( data->maskedHbm ) {
                GpiDeleteBitmap( data->maskedHbm );
                data->maskedHbm = 0;
            }
        }
	if ( data->hbm ) {
            if ( hps )
                GpiSetBitmap( hps, 0 );
            GpiDeleteBitmap( data->hbm );
            data->hbm = 0;
	}
	if ( hps ) {
	    free_mem_ps( hps );
	    hps = 0;
	}
	delete data;
        data = 0;
    }
}


QPixmap::QPixmap( int w, int h, const uchar *bits, bool isXbitmap )
    : QPaintDevice( QInternal::Pixmap )
{						// for bitmaps only
    init( 0, 0, 0, FALSE, NormalOptim );
    if ( w <= 0 || h <= 0 )			// create null pixmap
	return;

    data->uninit = FALSE;
    data->w = w;
    data->h = h;
    data->d = 1;

    int bitsbpl = (w+7)/8;			// original # bytes per line

    int bpl = ((w + 31) / 32) * 4;              // bytes per scanline,
                                                // doubleword aligned

    uchar *newbits = new uchar[bpl*h];
    uchar *p	= newbits + bpl*(h - 1);        // last scanline
    int x, y, pad;
    pad = bpl - bitsbpl;
    const uchar *f = isXbitmap ? qt_get_bitflip_array() : 0;

    // flip bit order if Xbitmap and flip bitmap top to bottom
    for ( y=0; y<h; y++ ) {
        if ( f ) {
            for ( x=0; x<bitsbpl; x++ )
                *p++ = f[*bits++];
            for ( x=0; x<pad; x++ )
                *p++ = 0;
            p -= bpl;
        } else {
            memcpy( p, bits, bitsbpl );
            memset( p + bitsbpl, 0, pad );
            bits += bitsbpl;
        }
        p -= bpl;
    }

    hps = alloc_mem_ps( w, h );
    // allocate header + 2 palette entries
    char *bmi_data = new char[ sizeof(BITMAPINFOHEADER2) + 4 * 2 ];
    memset( bmi_data, 0, sizeof(BITMAPINFOHEADER2) );
    BITMAPINFOHEADER2 &bmh = *(PBITMAPINFOHEADER2) bmi_data;
    PULONG pal = (PULONG) (bmi_data + sizeof(BITMAPINFOHEADER2));
    bmh.cbFix = sizeof(BITMAPINFOHEADER2);
    bmh.cx = w;
    bmh.cy = h;
    bmh.cPlanes = 1;
    bmh.cBitCount = 1;
    bmh.cclrUsed = 2;
    pal[0] = 0;
    pal[1] = 0x00FFFFFF;
    data->hbm = GpiCreateBitmap(
        hps, &bmh, CBM_INIT, (PBYTE) newbits, (PBITMAPINFO2) bmi_data
    );

    GpiSetBitmap( hps, data->hbm );

    delete [] bmi_data;
    delete [] newbits;
    if ( defOptim != NormalOptim )
	setOptimization( defOptim );
}


void QPixmap::detach()
{
    if ( data->uninit || data->count == 1 )
	data->uninit = FALSE;
    else
	*this = copy();
}


int QPixmap::defaultDepth()
{
    static int dd = 0;
    if ( dd == 0 ) {
        LONG formats [2];
        GpiQueryDeviceBitmapFormats( qt_display_ps(), 2, formats );
        dd = formats [0] * formats [1];
    }
    return dd;
}


void QPixmap::setOptimization( Optimization optimization )
{
    if ( optimization == data->optim )
	return;
    detach();
    data->optim = optimization == DefaultOptim ?
	    defOptim : optimization;
}


void QPixmap::fill( const QColor &fillColor )
{
    if ( isNull() )
	return;
    detach();					// detach other references

    const ULONG AreaAttrs = ABB_COLOR | ABB_MIX_MODE | ABB_SET | ABB_SYMBOL;
    AREABUNDLE oldAb;
    GpiQueryAttrs( hps, PRIM_AREA, AreaAttrs, (PBUNDLE) &oldAb );
    AREABUNDLE newAb;
    newAb.lColor = fillColor.pixel();
    newAb.usMixMode = FM_OVERPAINT;
    newAb.usSet = LCID_DEFAULT;
    newAb.usSymbol = PATSYM_SOLID;
    GpiSetAttrs( hps, PRIM_AREA, AreaAttrs, 0, (PBUNDLE) &newAb );
    POINTL ptl = { 0, 0 };
    GpiMove( hps, &ptl );
    ptl.x = data->w - 1;
    ptl.y = data->h - 1;
    GpiBox( hps, DRO_FILL, &ptl, 0, 0 );
    GpiSetAttrs( hps, PRIM_AREA, AreaAttrs, 0, (PBUNDLE) &newAb );
}


int QPixmap::metric( int m ) const
{
    if ( m == QPaintDeviceMetrics::PdmWidth ) {
	return width();
    } else if ( m == QPaintDeviceMetrics::PdmHeight ) {
	return height();
    } else {
        LONG val;
        HDC hdc = GpiQueryDevice( hps );
	switch ( m ) {
	    case QPaintDeviceMetrics::PdmDpiX:
                DevQueryCaps( hdc, CAPS_HORIZONTAL_FONT_RES, 1, &val );
		break;
	    case QPaintDeviceMetrics::PdmDpiY:
                DevQueryCaps( hdc, CAPS_VERTICAL_FONT_RES, 1, &val );
		break;
	    case QPaintDeviceMetrics::PdmWidthMM:
                DevQueryCaps( hdc, CAPS_HORIZONTAL_RESOLUTION, 1, &val );
                val = width() * 1000 / val;
		break;
	    case QPaintDeviceMetrics::PdmHeightMM:
                DevQueryCaps( hdc, CAPS_VERTICAL_RESOLUTION, 1, &val );
                val = width() * 1000 / val;
		break;
	    case QPaintDeviceMetrics::PdmNumColors:
                if (depth() == 1) {
                    // it's a monochrome bitmap
                    val = 1;
                } else {
                    DevQueryCaps( hdc, CAPS_COLORS, 1, &val );
                }
		break;
	    case QPaintDeviceMetrics::PdmDepth:
                val = depth();
		break;
	    default:
		val = 0;
#if defined(QT_CHECK_RANGE)
		qWarning( "QPixmap::metric: Invalid metric command" );
#endif
	}
	return val;
    }
}

QImage QPixmap::convertToImage() const
{
    if ( isNull() )
	return QImage(); // null image

    int	w = width();
    int	h = height();
    const QBitmap *m = data->realAlphaBits ? 0 : mask();
    int	d = depth();
    int	ncols = 2;

    if ( d > 1 && d <= 8 ) {	// set to nearest valid depth
	d = 8;					//   2..7 ==> 8
	ncols = 256;
    } else if ( d > 8 ) {
	d = 32;					//   > 8  ==> 32
	ncols = 0;
    }

    QImage image( w, h, d, ncols, QImage::BigEndian );
    if ( data->realAlphaBits ) {
        qWarning( "QPixmap::convertToImage() for pixmaps with alpha is not yet implemented on OS/2" );
//@@TODO (dmik): later
//#ifndef Q_OS_TEMP
//	GdiFlush();
//#endif
//	memcpy( image.bits(), data->realAlphaBits, image.numBytes() );
//	image.setAlphaBuffer( TRUE );
//
//	// Windows has premultiplied alpha, so revert it
//	uchar *p = image.bits();
//	uchar *end = p + image.numBytes();
//	uchar alphaByte;
//	while ( p < end ) {
//	    alphaByte = *(p+3);
//	    if ( alphaByte == 0 ) {
//		*p = 255;
//		++p;
//		*p = 255;
//		++p;
//		*p = 255;
//		++p;
//		++p;
//	    } else if ( alphaByte == 255 ) {
//		p += 4;
//	    } else {
//		uchar alphaByte2 = alphaByte / 2;
//		*p = ( (int)(*p) * 255 + alphaByte2 ) / alphaByte;
//		++p;
//		*p = ( (int)(*p) * 255 + alphaByte2 ) / alphaByte;
//		++p;
//		*p = ( (int)(*p) * 255 + alphaByte2 ) / alphaByte;
//		++p;
//		++p;
//	    }
//	}
//	return image;
    }

    // allocate header + ncols palette entries
    char *bmi_data = new char[ sizeof(BITMAPINFOHEADER2) + 4 * ncols ];
    memset( bmi_data, 0, sizeof(BITMAPINFOHEADER2) );
    BITMAPINFOHEADER2 &bmh = *(PBITMAPINFOHEADER2) bmi_data;
    PULONG pal = (PULONG) (bmi_data + sizeof(BITMAPINFOHEADER2));
    bmh.cbFix = sizeof(BITMAPINFOHEADER2);
    bmh.cx = w;
    bmh.cy = h;
    bmh.cPlanes = 1;
    bmh.cBitCount = d;
    bmh.cclrUsed = ncols;
    GpiQueryBitmapBits( hps, 0, h, (PBYTE) image.bits(), (PBITMAPINFO2) bmi_data );

    // flip the image top to bottom
    {
        int bpl = image.bytesPerLine();
        uchar *line = new uchar[bpl];
        uchar *top = image.scanLine( 0 );
        uchar *bottom = image.scanLine( h - 1 );
        while( top < bottom ) {
            memcpy( line, top, bpl );
            memcpy( top, bottom, bpl );
            memcpy( bottom, line, bpl );
            top += bpl;
            bottom -= bpl;
        }
        delete [] line;
    }

    if ( d == 1 ) {
        // use a white/black palette for monochrome pixmaps (istead of
        // black/white supplied by GPI) to make the result of possible
        // converson to a higher depth compatible with other platforms.
        if ( m ) {
            image.setAlphaBuffer( TRUE );
            image.setColor( 0, qRgba( 255, 255, 255, 0 ) );
            image.setColor( 1, qRgba( 0, 0, 0, 255 ) );
        } else {
            image.setColor( 0, qRgb( 255, 255, 255 ) );
            image.setColor( 1, qRgb( 0, 0, 0 ) );
        }
    } else {
        for ( int i=0; i<ncols; i++ ) {		// copy color table
            PRGB2 r = (PRGB2) &pal[i];
            if ( m )
                image.setColor( i, qRgba(r->bRed,
                                   r->bGreen,
                                   r->bBlue,255) );
            else
                image.setColor( i, qRgb(r->bRed,
                                   r->bGreen,
                                   r->bBlue) );
        }
    }

    delete [] bmi_data;

    if ( d != 1 && m ) {
	image.setAlphaBuffer(TRUE);
	QImage msk = m->convertToImage();

	switch ( d ) {
	  case 8: {
	    int used[256];
	    memset( used, 0, sizeof(int)*256 );
	    uchar* p = image.bits();
	    int l = image.numBytes();
	    while (l--) {
		used[*p++]++;
	    }
	    int trans=0;
	    int bestn=INT_MAX;
	    for ( int i=0; i<256; i++ ) {
		if ( used[i] < bestn ) {
		    bestn = used[i];
		    trans = i;
		    if ( !bestn )
			break;
		}
	    }
	    image.setColor( trans, image.color(trans)&0x00ffffff );
	    for ( int y=0; y<image.height(); y++ ) {
		uchar* mb = msk.scanLine(y);
		uchar* ib = image.scanLine(y);
		uchar bit = 0x80;
		int i=image.width();
		while (i--) {
		    if ( !(*mb & bit) )
			*ib = trans;
		    bit /= 2; if ( !bit ) mb++,bit = 0x80; // ROL
		    ib++;
		}
	    }
	  } break;
	  case 32: {
	    for ( int y=0; y<image.height(); y++ ) {
		uchar* mb = msk.scanLine(y);
		QRgb* ib = (QRgb*)image.scanLine(y);
		uchar bit = 0x80;
		int i=image.width();
		while (i--) {
		    if ( *mb & bit )
			*ib |= 0xff000000;
		    else
			*ib &= 0x00ffffff;
		    bit /= 2; if ( !bit ) mb++,bit = 0x80; // ROL
		    ib++;
		}
	    }
	  } break;
	}
    }

    return image;
}


bool QPixmap::convertFromImage( const QImage &img, int conversion_flags )
{
    if ( img.isNull() ) {
#if defined(QT_CHECK_NULL)
	qWarning( "QPixmap::convertFromImage: Cannot convert a null image" );
#endif
	return FALSE;
    }

    QImage image = img;
    int	   d     = image.depth();
    int    dd    = defaultDepth();
    bool force_mono = (dd == 1 || isQBitmap() ||
		       (conversion_flags & ColorMode_Mask)==MonoOnly );

    if ( force_mono ) {				// must be monochrome
	if ( d != 1 ) {				// dither
	    image = image.convertDepth( 1, conversion_flags );
	    d = 1;
	}
    } else {					// can be both
	bool conv8 = FALSE;
	if ( d > 8 && dd <= 8 ) {		// convert to 8 bit
	    if ( (conversion_flags & DitherMode_Mask) == AutoDither )
		conversion_flags = (conversion_flags & ~DitherMode_Mask)
					| PreferDither;
	    conv8 = TRUE;
	} else if ( (conversion_flags & ColorMode_Mask) == ColorOnly ) {
	    conv8 = d == 1;			// native depth wanted
	} else if ( d == 1 ) {
	    if ( image.numColors() == 2 ) {
//@@TODO (dmik): the code below is necessary to prevent converting
//  1-bpp images with alpha channel to 8-bpp images. it is currently
//  commented out only for compatibility with Qt/Win32; Qt/OS2 is ready
//  to handle such images properly.
//		QRgb c0 = image.color(0) | ~RGB_MASK;	// Auto: convert to best
//		QRgb c1 = image.color(1) | ~RGB_MASK;
		QRgb c0 = image.color(0);	// Auto: convert to best
		QRgb c1 = image.color(1);
		conv8 = QMIN(c0,c1) != qRgb(0,0,0) || QMAX(c0,c1) != qRgb(255,255,255);
	    } else {
		// eg. 1-color monochrome images (they do exist).
		conv8 = TRUE;
	    }
	}
	if ( conv8 ) {
	    image = image.convertDepth( 8, conversion_flags );
	    d = 8;
	}
    }

    if ( d == 1 ) {				// 1 bit pixmap (bitmap)
	image = image.convertBitOrder( QImage::BigEndian );
    }

    bool hasRealAlpha = FALSE;
    if ( img.hasAlphaBuffer()
//@@TODO (dmik): remove later.
//        &&
//	    ( QApplication::winVersion() != Qt::WV_95 &&
//	      QApplication::winVersion() != Qt::WV_NT )
    ) {
        if (image.depth() == 8) {
	    const QRgb * const rgb = img.colorTable();
	    for (int i = 0, count = img.numColors(); i < count; ++i) {
		const int alpha = qAlpha(rgb[i]);
		if (alpha != 0 && alpha != 0xff) {
		    hasRealAlpha = true;
		    break;
		}
	    }
	    if (hasRealAlpha) {
//@@TODO (dmik): also need to convert? guess yes.
//		image = image.convertDepth(32, conversion_flags);
//		d = image.depth();
	    }
	} else if (image.depth() == 32) {
	    int i = 0;
	    while ( i<image.height() && !hasRealAlpha ) {
		uchar *p = image.scanLine(i);
		uchar *end = p + image.bytesPerLine();
		p += 3;
		while ( p < end ) {
		    if ( *p!=0 && *p!=0xff ) {
			hasRealAlpha = TRUE;
			break;
		    }
		    p += 4;
		}
		++i;
	    }
	}
    }

//@@TODO (dmik): temporary code. will be removed when alpha support is done.
    if ( hasRealAlpha ) {
        qWarning( "QPixmap::convertFromImage() for images with alpha is not yet implemented on OS/2" );
        hasRealAlpha = FALSE;
    }

    int w = image.width();
    int h = image.height();

    if ( width() == w && height() == h && ( (d == 1 && depth() == 1) ||
					    (d != 1 && depth() != 1) ) ) {
	if ( data->realAlphaBits && !hasRealAlpha ) {
//@@TODO (dmik): later
//	    // pixmap uses a DIB section, but image has no alpha channel, so we
//	    // can't reuse the old pixmap
//	    QPixmap pm(w, h, d == 1 ? 1 : -1, data->bitmap, data->optim);
//	    *this = pm;
	} else {
	    // same size etc., use the existing pixmap
	    detach();
	    if ( data->mask ) {			// get rid of the mask
		delete data->mask;
		data->mask = 0;
                if ( data->maskedHbm ) {
                    GpiDeleteBitmap( data->maskedHbm );
                    data->maskedHbm = 0;
                }
	    }
	}
    } else {
	// different size or depth, make a new pixmap
	QPixmap pm(w, h, d == 1 ? 1 : -1, data->bitmap, data->optim);
	*this = pm;
    }

    int	  ncols	   = image.numColors();

    // allocate header + ncols palette entries
    char *bmi_data = new char[ sizeof(BITMAPINFOHEADER2) + 4 * ncols ];
    memset( bmi_data, 0, sizeof(BITMAPINFOHEADER2) );
    BITMAPINFOHEADER2 &bmh = *(PBITMAPINFOHEADER2) bmi_data;
    PULONG pal = (PULONG) (bmi_data + sizeof(BITMAPINFOHEADER2));
    bmh.cbFix = sizeof(BITMAPINFOHEADER2);
    bmh.cx = w;
    bmh.cy = h;
    bmh.cPlanes = 1;
    bmh.cBitCount = d;
    bmh.cclrUsed = ncols;
    bool doAlloc = ( QApplication::colorSpec() == QApplication::CustomColor
		     && QColor::hPal() );

    if ( d == 1 ) {
        // always use the black/white palette for monochrome pixmaps;
        // otherwise GPI can swap 0s and 1s when creating a bitmap.
        pal [0] = 0;
        pal [1] = 0x00FFFFFF;
    } else {
        for ( int i=0; i<ncols; i++ ) {		// copy color table
            PRGB2 r = (PRGB2) &pal[i];
            QRgb c = image.color(i);
            r->bBlue = qBlue( c );
            r->bGreen = qGreen( c );
            r->bRed = qRed( c);
            r->fcOptions = 0;
            if ( doAlloc ) {
                QColor cl( c );
                cl.alloc();
            }
        }
    }

//@@TODO (dmik): later
//#ifndef Q_OS_TEMP
//    if ( hasRealAlpha ) {
//	initAlphaPixmap( image.bits(), image.numBytes(), bmi );
//
//	// Windows expects premultiplied alpha
//	uchar *p = image.bits();
//	uchar *b = data->realAlphaBits;
//	uchar *end = p + image.numBytes();
//	uchar alphaByte;
//	while ( p < end ) {
//	    alphaByte = *(p+3);
//	    if ( alphaByte == 0 ) {
//		*(b++) = 0;
//		*(b++) = 0;
//		*(b++) = 0;
//		b++;
//		p += 4;
//	    } else if ( alphaByte == 255 ) {
//		b += 4;
//		p += 4;
//	    } else {
//		*(b++) = ( (*(p++)) * (int)alphaByte + 127 ) / 255;
//		*(b++) = ( (*(p++)) * (int)alphaByte + 127 ) / 255;
//		*(b++) = ( (*(p++)) * (int)alphaByte + 127 ) / 255;
//		b++;
//		p++;
//	    }
//	}
//    }
//#else
    data->realAlphaBits = 0;
//#endif

    if ( data->realAlphaBits == 0 ) {
        // flip the image top to bottom
        image.detach();
        int bpl = image.bytesPerLine();
        uchar *line = new uchar[bpl];
        uchar *top = image.scanLine( 0 );
        uchar *bottom = image.scanLine( h - 1 );
        while( top < bottom ) {
            memcpy( line, top, bpl );
            memcpy( top, bottom, bpl );
            memcpy( bottom, line, bpl );
            top += bpl;
            bottom -= bpl;
        }
        delete [] line;
        GpiSetBitmapBits( hps, 0, h, (PBYTE) image.bits(), (PBITMAPINFO2) bmi_data );
    }

    if ( img.hasAlphaBuffer() ) {
	QBitmap m;
	m = img.createAlphaMask( conversion_flags );
	setMask( m );
    }

    delete [] bmi_data;
    data->uninit = FALSE;

    return TRUE;
}


QPixmap QPixmap::grabWindow( WId window, int x, int y, int w, int h )
{
    RECTL rcl;
    WinQueryWindowRect( window, &rcl );
    if ( w <= 0 || h <= 0 ) {
	if ( w == 0 || h == 0 ) {
	    QPixmap nullPixmap;
	    return nullPixmap;
	}
	if ( w < 0 )
	    w = rcl.xRight;
	if ( h < 0 )
	    h = rcl.yTop;
    }
    // flip y coordinate
    y = rcl.yTop - (y + h);
    QPixmap pm( w, h );
    HPS hps = WinGetPS( window );
    POINTL pnts[] = { {0, 0}, {w, h}, {x, y} };
    GpiBitBlt( pm.hps, hps, 3, pnts, ROP_SRCCOPY, BBO_IGNORE );
    WinReleasePS( hps );
    return pm;
}

#ifndef QT_NO_PIXMAP_TRANSFORMATION
QPixmap QPixmap::xForm( const QWMatrix &matrix ) const
{
    int	   w, h;				// size of target pixmap
    int	   ws, hs;				// size of source pixmap
    uchar *dptr;				// data in target pixmap
    int	   dbpl, dbytes;			// bytes per line/bytes total
    uchar *sptr;				// data in original pixmap
    int	   sbpl;				// bytes per line in original
    int	   bpp;					// bits per pixel
    bool   depth1 = depth() == 1;

    if ( isNull() )				// this is a null pixmap
	return copy();

    ws = width();
    hs = height();

    QWMatrix mat( matrix.m11(), matrix.m12(), matrix.m21(), matrix.m22(), 0., 0. );

    if ( matrix.m12() == 0.0F  && matrix.m21() == 0.0F &&
	 matrix.m11() >= 0.0F  && matrix.m22() >= 0.0F ) {
	if ( mat.m11() == 1.0F && mat.m22() == 1.0F )
	    return *this;			// identity matrix

	h = qRound( mat.m22()*hs );
	w = qRound( mat.m11()*ws );
	h = QABS( h );
	w = QABS( w );
	if ( data->realAlphaBits == 0 ) {
	    QPixmap pm( w, h, depth(), optimization() );
            POINTL ptls[] = {
                { 0, 0 }, { w, h },
                { 0, 0 }, { ws, hs }
            };
            GpiBitBlt( pm.hps, hps, 4, ptls, ROP_SRCCOPY, BBO_IGNORE );
	    if ( data->mask ) {
		QBitmap bm =
		    data->selfmask ? *((QBitmap*)(&pm)) :
		    data->mask->xForm( matrix );
		pm.setMask( bm );
	    }
	    return pm;
	}
    } else {
	// rotation or shearing
	QPointArray a( QRect(0,0,ws+1,hs+1) );
	a = mat.map( a );
	QRect r = a.boundingRect().normalize();
	w = r.width()-1;
	h = r.height()-1;
    }

    mat = trueMatrix( mat, ws, hs ); // true matrix

    bool invertible;
    mat = mat.invert( &invertible );		// invert matrix

    if ( h == 0 || w == 0 || !invertible ) {	// error, return null pixmap
	QPixmap pm;
	pm.data->bitmap = data->bitmap;
	return pm;
    }

    if ( data->realAlphaBits ) {
	bpp = 32;
    } else {
	bpp  = depth();
	if ( bpp > 1 && bpp < 8 )
	    bpp = 8;
    }

    sbpl = ((ws*bpp+31)/32)*4;
    sptr = new uchar[hs*sbpl];
    int ncols;
    if ( bpp <= 8 ) {
	ncols = 1 << bpp;
    } else {
	ncols = 0;
    }

    // allocate header + ncols palette entries
    int bmi_data_len = sizeof(BITMAPINFOHEADER2) + 4 * ncols;
    char *bmi_data = new char[ bmi_data_len ];
    memset( bmi_data, 0, sizeof(BITMAPINFOHEADER2) );
    BITMAPINFOHEADER2 &bmh = *(PBITMAPINFOHEADER2) bmi_data;
    bmh.cbFix = sizeof(BITMAPINFOHEADER2);
    bmh.cx = ws;
    bmh.cy = hs;
    bmh.cPlanes = 1;
    bmh.cBitCount = bpp;
    bmh.cclrUsed = ncols;
    if ( depth1 ) {
        // always use the black/white palette for monochrome pixmaps;
        // otherwise GPI can swap 0s and 1s when creating a bitmap.
        PULONG pal = (PULONG) (bmi_data + sizeof(BITMAPINFOHEADER2));
        pal[0] = 0;
        pal[1] = 0x00FFFFFF;
    }

    int result;
    if ( data->realAlphaBits ) {
	memcpy( sptr, data->realAlphaBits, sbpl*hs );
	result = 1;
    } else {
        result = GpiQueryBitmapBits( hps, 0, hs, (PBYTE) sptr, (PBITMAPINFO2) bmi_data );
    }

    if ( !result ) {				// error, return null pixmap
	delete [] sptr;
        delete [] bmi_data;
	return QPixmap( 0, 0, 0, data->bitmap, NormalOptim );
    }

    dbpl   = ((w*bpp+31)/32)*4;
    dbytes = dbpl*h;

    dptr = new uchar[ dbytes ];			// create buffer for bits
    Q_CHECK_PTR( dptr );
    if ( depth1 )
	memset( dptr, 0x00, dbytes );
    else if ( bpp == 8 )
	memset( dptr, white.pixel(), dbytes );
    else if ( data->realAlphaBits )
	memset( dptr, 0x00, dbytes );
    else
	memset( dptr, 0xff, dbytes );

    int	  xbpl, p_inc;
    if ( depth1 ) {
	xbpl  = (w+7)/8;
	p_inc = dbpl - xbpl;
    } else {
	xbpl  = (w*bpp)/8;
	p_inc = dbpl - xbpl;
    }

    // OS/2 stores first image row at the bottom of the pixmap data,
    // so do xForm starting from the bottom scanline to the top one
    if ( !qt_xForm_helper( mat, 0, QT_XFORM_TYPE_MSBFIRST, bpp,
                           dptr + dbytes - dbpl, xbpl, p_inc - dbpl * 2, h,
                           sptr + (hs*sbpl) - sbpl, -sbpl, ws, hs ) ) {
#if defined(QT_CHECK_RANGE)
	qWarning( "QPixmap::xForm: display not supported (bpp=%d)",bpp);
#endif
	QPixmap pm;
	delete [] sptr;
	delete [] bmi_data;
	delete [] dptr;
	return pm;
    }

    delete [] sptr;

    QPixmap pm( w, h, depth(), data->bitmap, optimization() );
    pm.data->uninit = FALSE;
    bmh.cx = w;
    bmh.cy = h;
    if ( data->realAlphaBits ) {
        qWarning( "QPixmap::xForm() for pixmaps with alpha is not yet implemented on OS/2" );
/// @todo (dmik) later
//	pm.initAlphaPixmap( dptr, dbytes, bmi );
    } else {
        GpiSetBitmapBits( pm.hps, 0, h, (PBYTE) dptr, (PBITMAPINFO2) bmi_data );
    }
    delete [] bmi_data;
    delete [] dptr;
    if ( data->mask ) {
	QBitmap bm = data->selfmask ? *((QBitmap*)(&pm)) :
				     data->mask->xForm(matrix);
	pm.setMask( bm );
    }
    return pm;
}
#endif // QT_NO_PIXMAP_TRANSFORMATION

/*!
  \fn HBITMAP QPixmap::hbm() const
  \internal
*/

bool QPixmap::hasAlpha() const
{
    return data->realAlphaBits || data->mask;
}

bool QPixmap::hasAlphaChannel() const
{
    return data->realAlphaBits != 0;
}

//@@TODO (dmik): later
//void QPixmap::convertToAlphaPixmap( bool initAlpha )
//{
//    char bmi_data[sizeof(BITMAPINFO)];
//    memset( bmi_data, 0, sizeof(BITMAPINFO) );
//    BITMAPINFO	     *bmi = (BITMAPINFO*)bmi_data;
//    BITMAPINFOHEADER *bmh = (BITMAPINFOHEADER*)bmi;
//    bmh->biSize		  = sizeof(BITMAPINFOHEADER);
//    bmh->biWidth	  = width();
//    bmh->biHeight	  = -height();			// top-down bitmap
//    bmh->biPlanes	  = 1;
//    bmh->biBitCount	  = 32;
//    bmh->biCompression	  = BI_RGB;
//    bmh->biSizeImage	  = width() * height() * 4;
//    bmh->biClrUsed	  = 0;
//    bmh->biClrImportant	  = 0;
//
//    QPixmap pm( width(), height(), -1 );
//    pm.initAlphaPixmap( 0, 0, bmi );
//
//#ifndef Q_OS_TEMP
//    GetDIBits( qt_display_dc(), DATA_HBM, 0, height(), pm.data->realAlphaBits, bmi, DIB_RGB_COLORS );
//    if ( initAlpha ) {
//	// In bitBlt(), if the destination has an alpha channel and the source
//	// doesn't have one, we bitBlt() the source with the destination's
//	// alpha channel. In that case, there is no need to initialize the
//	// alpha values.
//	uchar *p = pm.data->realAlphaBits;
//	uchar *pe = p + bmh->biSizeImage;
//	if ( mask() ) {
//	    QImage msk = mask()->convertToImage();
//	    int i = 0;
//	    int w = width();
//	    int backgroundIndex = msk.color(0) == Qt::color0.rgb() ? 0 : 1;
//	    while ( p < pe ) {
//		if ( msk.pixelIndex( i%w, i/w ) == backgroundIndex ) {
//		    *(p++) = 0x00;
//		    *(p++) = 0x00;
//		    *(p++) = 0x00;
//		    *(p++) = 0x00;
//		} else {
//		    p += 3;
//		    *(p++) = 0xff;
//		}
//		++i;
//	    }
//	} else {
//	    p += 3;
//	    while ( p < pe ) {
//		*p = 0xff;
//		p += 4;
//	    }
//	}
//    }
//#else
//    memcpy( pm.data->ppvBits, data->ppvBits, bmh->biSizeImage );
//#endif
//    if ( mask() )
//	pm.setMask( *mask() );
//
//    *this = pm;
//}
//
//void QPixmap::bitBltAlphaPixmap( QPixmap *dst, int dx, int dy,
//			         const QPixmap *src, int sx, int sy,
//			         int sw, int sh, bool useDstAlpha )
//{
//    if ( sw < 0 )
//	sw = src->width() - sx;
//    else
//	sw = QMIN( src->width()-sx, sw );
//    sw = QMIN( dst->width()-dx, sw );
//
//    if ( sh < 0 )
//	sh = src->height() - sy ;
//    else
//	sh = QMIN( src->height()-sy, sh );
//    sh = QMIN( dst->height()-dy, sh );
//
//    if ( sw <= 0 || sh <= 0 )
//	return;
//
//#ifndef Q_OS_TEMP
//    GdiFlush();
//#endif
//    uchar *sBits = src->data->realAlphaBits + ( sy * src->width() + sx ) * 4;
//    uchar *dBits = dst->data->realAlphaBits + ( dy * dst->width() + dx ) * 4;
//    int sw4 = sw * 4;
//    int src4 = src->width() * 4;
//    int dst4 = dst->width() * 4;
//    if ( useDstAlpha ) {
//	// Copy the source pixels premultiplied with the destination's alpha
//	// channel. The alpha channel remains the destination's alpha channel.
//	uchar *sCur;
//	uchar *dCur;
//	uchar alphaByte;
//	for ( int i=0; i<sh; i++ ) {
//	    sCur = sBits;
//	    dCur = dBits;
//	    for ( int j=0; j<sw; j++ ) {
//		alphaByte = *(dCur+3);
//		if ( alphaByte == 0 || (*(sCur+3)) == 0 ) {
//		    dCur += 4;
//		    sCur += 4;
//		} else if ( alphaByte == 255 ) {
//		    *(dCur++) = *(sCur++);
//		    *(dCur++) = *(sCur++);
//		    *(dCur++) = *(sCur++);
//		    dCur++;
//		    sCur++;
//		} else {
//		    *(dCur++) = ( (*(sCur++)) * (int)alphaByte + 127 ) / 255;
//		    *(dCur++) = ( (*(sCur++)) * (int)alphaByte + 127 ) / 255;
//		    *(dCur++) = ( (*(sCur++)) * (int)alphaByte + 127 ) / 255;
//		    dCur++;
//		    sCur++;
//		}
//	    }
//	    sBits += src4;
//	    dBits += dst4;
//	}
//    } else {
//	// Copy the source into the destination. Use the source's alpha
//	// channel.
//	for ( int i=0; i<sh; i++ ) {
//	    memcpy( dBits, sBits, sw4 );
//	    sBits += src4;
//	    dBits += dst4;
//	}
//    }
//}

// Prepares for painting the masked pixmap: precomposes and selects the
// masked pixmap into this pixmap's hps. If prepare = FALSE, it does the
// cleanup. Currently used from ::bitBlt().
void QPixmap::prepareForMasking( bool prepare )
{
    if ( !data->mask || data->selfmask )
        return;
    if ( prepare ) {
        if ( !data->maskedHbm ) {
            GpiSetBitmap( hps, 0 );
            BITMAPINFOHEADER2 bmh;
            memset( &bmh, 0, sizeof(BITMAPINFOHEADER2) );
            bmh.cbFix = sizeof(BITMAPINFOHEADER2);
            bmh.cx = data->w;
            bmh.cy = data->h;
            bmh.cPlanes = 1;
            bmh.cBitCount = data->d;
            data->maskedHbm = GpiCreateBitmap( hps, &bmh, 0, NULL, NULL );
            GpiSetBitmap( hps, data->maskedHbm );
            POINTL ptls[] = {
                // dst is inclusive
                { 0, 0 }, { data->w - 1, data->h - 1 },
                { 0, 0 }, { data->w, data->h },
            };
            GpiWCBitBlt( hps, data->hbm, 4, ptls, ROP_SRCCOPY, BBO_IGNORE );
            // dst is exclusive
            ptls[1].x++;
            ptls[1].y++;
            // make transparent pixels black
            const ULONG AllImageAttrs =
                IBB_COLOR | IBB_BACK_COLOR |
                IBB_MIX_MODE | IBB_BACK_MIX_MODE;
            IMAGEBUNDLE oldIb;
            GpiQueryAttrs( hps, PRIM_IMAGE, AllImageAttrs, (PBUNDLE) &oldIb );
            IMAGEBUNDLE newIb = {
                CLR_TRUE, CLR_FALSE, FM_OVERPAINT, BM_OVERPAINT
            };
            GpiSetAttrs( hps, PRIM_IMAGE, AllImageAttrs, 0, (PBUNDLE) &newIb );
            GpiBitBlt( hps, data->mask->hps, 3, ptls, ROP_SRCAND, BBO_IGNORE );
            GpiSetAttrs( hps, PRIM_IMAGE, AllImageAttrs, 0, (PBUNDLE) &oldIb );
        } else {
            GpiSetBitmap( hps, data->maskedHbm );
        }
    } else {
        GpiSetBitmap( hps, data->hbm );
        // delete the precomposed masked pixmap when memory optimization
        // is in force or when no precomosing was done
        if (
            data->maskedHbm &&
            (data->optim != NormalOptim && data->optim != BestOptim)
        ) {
            GpiDeleteBitmap( data->maskedHbm );
            data->maskedHbm = 0;
        }
    }
}

void QPixmap::attachHandle( HBITMAP hbm )
{
    if ( paintingActive() )
	return;

    BITMAPINFOHEADER bmh;
    if (!GpiQueryBitmapParameters( hbm, &bmh ))
        return;

    if ( !data->uninit && !isNull() ) { // has existing pixmap
        deref();
        init( 0, 0, 0, FALSE, defOptim );
    }

    data->uninit = FALSE;
    data->w = bmh.cx;
    data->h = bmh.cy;
    data->d = bmh.cPlanes * bmh.cBitCount;

    hps = alloc_mem_ps( bmh.cx, bmh.cy );
    data->hbm = hbm;

    GpiSetBitmap( hps, data->hbm );
}

HBITMAP QPixmap::detachHandle()
{
    if ( paintingActive() )
	return 0;

    HBITMAP hbm = data->hbm;

    // do what deref() will not do after data->hbm is set to 0
    if ( hps )
        GpiSetBitmap( hps, 0 );

    data->hbm = 0;
    deref();

    return hbm;
}

Q_EXPORT void copyBlt( QPixmap *dst, int dx, int dy,
		       const QPixmap *src, int sx, int sy, int sw, int sh )
{
    if ( ! dst || ! src || sw == 0 || sh == 0 || dst->depth() != src->depth() ) {
#ifdef QT_CHECK_NULL
	Q_ASSERT( dst != 0 );
	Q_ASSERT( src != 0 );
#endif
	return;
    }

    // copy mask data
    if ( src->data->mask ) {
	if ( ! dst->data->mask ) {
	    dst->data->mask = new QBitmap( dst->width(), dst->height() );

	    // new masks are fully opaque by default
	    dst->data->mask->fill( Qt::color1 );
	} else if ( dst->data->maskedHbm ) {
            // reset the precomposed masked pixmap
            GpiDeleteBitmap( dst->data->maskedHbm );
            dst->data->maskedHbm = 0;
        }

	bitBlt( dst->data->mask, dx, dy,
		src->data->mask, sx, sy, sw, sh, Qt::CopyROP, TRUE );
    }

    if ( src->data->realAlphaBits ) {
        qWarning( "::copyBlt() for pixmaps with alpha is not yet implemented on OS/2" );
//@@TODO (dmik): later
//	if ( !dst->data->realAlphaBits )
//	    dst->convertToAlphaPixmap();
//	QPixmap::bitBltAlphaPixmap( dst, dx, dy, src, sx, sy, sw, sh, FALSE );
    } else {
	// copy pixel data
	bitBlt( dst, dx, dy, src, sx, sy, sw, sh, Qt::CopyROP, TRUE );
    }
}
