/****************************************************************************
**
** Implementation of TQt3CairoPaintDevice class
**
** Copyright (C) 2012 Timothy Pearson.  All rights reserved.
**
** This file is part of the TDE GTK3 style interface
**
** This file may be used under the terms of the GNU General
** Public License versions 2.0 or 3.0 as published by the Free
** Software Foundation and appearing in the files LICENSE.GPL2
** and LICENSE.GPL3 included in the packaging of this file.
**
** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
** herein.
**
**********************************************************************/

// If USE_TDE_SPECIFIC_LIBRARIES is set, the painter will use certain TDE libraries
// to increase painting performance.  If this dependency is not desired and you can
// live with the corresponding performance loss, comment out the following line:
#define USE_TDE_SPECIFIC_LIBRARIES 1

#include "tqtcairopainter.h"

#include "tqpainter.h"
#include "tqpixmap.h"
#include "tqbitmap.h"
#include "tqimage.h"
#include "tqfile.h"
#include "tqpaintdevicemetrics.h"

#ifdef USE_TDE_SPECIFIC_LIBRARIES
#include <kpixmapio.h>
#endif // USE_TDE_SPECIFIC_LIBRARIES

#define CAIRO_PEN_PIXEL_OFFSET (0.5)
#define CAIRO_BRUSH_PIXEL_OFFSET (0.0)
#define CAIRO_FONT_SIZE_FUDGE_FACTOR (1.4)

#define SET_BIT(x, y) (x |= 1 << y)
#define TEST_BIT(x, y) ((x & (1 << y)) >> y)

// SLOW BUT VERIFIED CORRECT
//#define FASTEST_AVAILABLE_PAINTER (m_painter)
// FAST BUT EXPERIMENTAL AND UNVERIFIED
#define FASTEST_AVAILABLE_PAINTER (intermediateSurfaceInUse()?m_painter:m_devicePainter)

// Little endian
#define ARGB_A_BYTE_NUMBER 3
#define ARGB_R_BYTE_NUMBER 2
#define ARGB_G_BYTE_NUMBER 1
#define ARGB_B_BYTE_NUMBER 0

inline int cairoPainterRectSubtraction(TQPen* pen) {
	return ((pen->style() != TQt::NoPen) && ((pen->width() == 0) || (pen->width() == 1))) ? 1 : 0;
}

inline void standardAlphaToPremultipliedAlpha(unsigned char *a, unsigned char *r, unsigned char *g, unsigned char *b) {
	double alpha_adjust;

	alpha_adjust = (*a / 255.0);
	*r = char( *r * alpha_adjust );
	*g = char( *g * alpha_adjust );
	*b = char( *b * alpha_adjust );
	*a = char( *a * 1.0 );
}

inline void premultipliedAlphaToStandardAlpha(unsigned char *a, unsigned char *r, unsigned char *g, unsigned char *b) {
	double alpha_adjust;

	alpha_adjust = (*a / 255.0);
	*r = char( *r / alpha_adjust );
	*g = char( *g / alpha_adjust );
	*b = char( *b / alpha_adjust );
	*a = char( *a / 1.0 );
}

cairo_surface_t* TQImageToCairoSurface(TQImage origimg) {
	cairo_surface_t* ret;

	TQImage img;
	if (origimg.depth() < 24) {
		img = origimg.convertDepth(32);
	}
	else {
		img = origimg;
	}

	int depth = img.depth();
	if (depth == 32) {
		// Convert ARGB to premultiplied ARGB
		// SLOW
		int x;
		int y;
		for (x=0; x<img.width(); x++) {
			for (y=0; y<img.height(); y++) {
				unsigned int pixel = img.pixel(x, y);
				unsigned char a = (pixel & 0xff000000) >> 24;
				unsigned char r = (pixel & 0x00ff0000) >> 16;
				unsigned char g = (pixel & 0x0000ff00) >> 8;
				unsigned char b = (pixel & 0x000000ff) >> 0;
				standardAlphaToPremultipliedAlpha(&a, &r, &g, &b);
				pixel = (a << 24) | (r << 16) | (g << 8) | (b << 0);
				img.setPixel(x, y, pixel);
			}
		}
	}

	cairo_format_t cairo_format;
	if (depth == 32) {
		cairo_format = CAIRO_FORMAT_ARGB32;
	}
	else if (depth == 24) {
		cairo_format = CAIRO_FORMAT_RGB24;
	}
	else {
		cairo_format = CAIRO_FORMAT_RGB24;
	}

	int stride = cairo_format_stride_for_width(cairo_format, img.width());
	ret = cairo_image_surface_create_for_data(img.bits(), cairo_format, img.width(), img.height(), stride);

	return ret;
}

TQImage CairoSurfaceToTQImage(cairo_surface_t* surface) {
	cairo_surface_flush(surface);

	cairo_format_t cairo_format = cairo_image_surface_get_format(surface);
	int height = cairo_image_surface_get_height(surface);
	int width = cairo_image_surface_get_width(surface);
	int depth;
	if (cairo_format == CAIRO_FORMAT_ARGB32) {
		depth = 32;
	}
	else if (cairo_format == CAIRO_FORMAT_RGB24) {
		depth = 24;
	}
	else {
		// FIXME
		// Force Cairo to convert the surface to a format that TQImage can read!
		printf("[WARNING] Tried to convert a Cairo surface of format %d to a TQImage (NULL image returned!)\n", cairo_format); fflush(stdout);
		return TQImage();
	}

	return TQImage(cairo_image_surface_get_data(surface), width, height, depth, (TQRgb*)NULL, 0, TQImage::BigEndian);
}

void TQt3CairoPaintDevice::resetIntermediateSurface() {
	if (m_intermediateSurface) {
		cairo_surface_destroy(m_intermediateSurface);
	}

	updateSurfaceDimensions();
	int height = m_height;
	int width = m_width;
	m_intermediateSurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
}

// FIXME
// transferIntermediateSurface should only be used when clipping is active or ROP != CopyROP
// Cairo however does not allow the target surface of a context to be switched, effectively preventing
// direct drawing with dynamic switching to and from the intermediate surface on clip or ROP set/unset
// See upstream Cairo bug report https://bugs.freedesktop.org/show_bug.cgi?id=57289
// NOTE
// This has been worked around by applying the same transformations, saves, and restores to both the device and intermediate surface contexts
// If there is not much performance penalty from doing so, this method should probably be left alone
void TQt3CairoPaintDevice::transferIntermediateSurface() {
	bool overlayMerge = true;
	cairo_surface_flush(m_intermediateSurface);

	if (m_rop != TQPainter::CopyROP) {
		overlayMerge = false;
		cairo_surface_flush(m_surface);
		cairo_surface_flush(m_intermediateSurface);
		updateSurfaceDimensions();
		int height = m_height;
		int width = m_width;
		int stride = cairo_format_stride_for_width(cairo_image_surface_get_format(m_surface), width);
		cairo_surface_t *usableDeviceSurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
		cairo_t *copyPainter = cairo_create(usableDeviceSurface);
		cairo_set_source_surface(copyPainter, m_surface, 0, 0);
		cairo_set_operator(copyPainter, CAIRO_OPERATOR_SOURCE);
		cairo_paint(copyPainter);
		cairo_surface_flush(usableDeviceSurface);
		cairo_destroy(copyPainter);
		unsigned char* device_surface_data = cairo_image_surface_get_data(usableDeviceSurface);
		unsigned char* intermediate_surface_data = cairo_image_surface_get_data(m_intermediateSurface);
		int x;
		int y;
		long long offset;
		unsigned char devicePixel_a;
		unsigned char devicePixel_r;
		unsigned char devicePixel_g;
		unsigned char devicePixel_b;
		unsigned char intermediatePixel_a;
		unsigned char intermediatePixel_r;
		unsigned char intermediatePixel_g;
		unsigned char intermediatePixel_b;
		unsigned char combinedPixel_a;
		unsigned char combinedPixel_r;
		unsigned char combinedPixel_g;
		unsigned char combinedPixel_b;
		// Execute the desired raster operation
		// WARNING
		// This is VERY SLOW
		for (y=0; y<height; y++) {
			for (x=0; x<stride; x=x+4) {
				offset = (y*stride)+x;

				// Convert from premultiplied ARGB
				premultipliedAlphaToStandardAlpha(intermediate_surface_data+offset+ARGB_A_BYTE_NUMBER, intermediate_surface_data+offset+ARGB_R_BYTE_NUMBER, intermediate_surface_data+offset+ARGB_G_BYTE_NUMBER, intermediate_surface_data+offset+ARGB_B_BYTE_NUMBER);
				intermediatePixel_a = (*(intermediate_surface_data+offset+ARGB_A_BYTE_NUMBER));
				intermediatePixel_r = (*(intermediate_surface_data+offset+ARGB_R_BYTE_NUMBER));
				intermediatePixel_g = (*(intermediate_surface_data+offset+ARGB_G_BYTE_NUMBER));
				intermediatePixel_b = (*(intermediate_surface_data+offset+ARGB_B_BYTE_NUMBER));
				if (intermediatePixel_a != 0) {
					// Alpha channel of intermediate pixel was set; gather data for bitwise operation
					premultipliedAlphaToStandardAlpha(device_surface_data+offset+ARGB_A_BYTE_NUMBER, device_surface_data+offset+ARGB_R_BYTE_NUMBER, device_surface_data+offset+ARGB_G_BYTE_NUMBER, device_surface_data+offset+ARGB_B_BYTE_NUMBER);
					devicePixel_a = (*(device_surface_data+offset+ARGB_A_BYTE_NUMBER));
					devicePixel_r = (*(device_surface_data+offset+ARGB_R_BYTE_NUMBER));
					devicePixel_g = (*(device_surface_data+offset+ARGB_G_BYTE_NUMBER));
					devicePixel_b = (*(device_surface_data+offset+ARGB_B_BYTE_NUMBER));

					// Perform requested bitwise operation
					if (m_rop == TQPainter::OrROP) {
						combinedPixel_a = ((devicePixel_a != 0) || (intermediatePixel_a != 0))?0xff:0x00;
						combinedPixel_r = devicePixel_r | intermediatePixel_r;
						combinedPixel_g = devicePixel_g | intermediatePixel_g;
						combinedPixel_b = devicePixel_b | intermediatePixel_b;
					}
					else if (m_rop == TQPainter::XorROP) {
						combinedPixel_a = ((devicePixel_a != 0) || (intermediatePixel_a != 0))?0xff:0x00;
						combinedPixel_r = devicePixel_r ^ intermediatePixel_r;
						combinedPixel_g = devicePixel_g ^ intermediatePixel_g;
						combinedPixel_b = devicePixel_b ^ intermediatePixel_b;
					}
					else if (m_rop == TQPainter::NotAndROP) {
						combinedPixel_a = ((devicePixel_a != 0) || (intermediatePixel_a != 0))?0xff:0x00;
						combinedPixel_r = devicePixel_r & (~intermediatePixel_r);
						combinedPixel_g = devicePixel_g & (~intermediatePixel_g);
						combinedPixel_b = devicePixel_b & (~intermediatePixel_b);
					}
					else if (m_rop == TQPainter::NotCopyROP) {
						combinedPixel_a = intermediatePixel_a;
						combinedPixel_r = ~intermediatePixel_r;
						combinedPixel_g = ~intermediatePixel_g;
						combinedPixel_b = ~intermediatePixel_b;
					}
					else if (m_rop == TQPainter::NotOrROP) {
						combinedPixel_a = ((devicePixel_a != 0) || (intermediatePixel_a != 0))?0xff:0x00;
						combinedPixel_r = devicePixel_r | (~intermediatePixel_r);
						combinedPixel_g = devicePixel_g | (~intermediatePixel_g);
						combinedPixel_b = devicePixel_b | (~intermediatePixel_b);
					}
					else if (m_rop == TQPainter::NotXorROP) {
						combinedPixel_a = ((devicePixel_a != 0) || (intermediatePixel_a != 0))?0xff:0x00;
						combinedPixel_r = devicePixel_r ^ (~intermediatePixel_r);
						combinedPixel_g = devicePixel_g ^ (~intermediatePixel_g);
						combinedPixel_b = devicePixel_b ^ (~intermediatePixel_b);
					}
					else if (m_rop == TQPainter::AndROP) {
						combinedPixel_a = ((devicePixel_a != 0) || (intermediatePixel_a != 0))?0xff:0x00;
						combinedPixel_r = devicePixel_r & intermediatePixel_r;
						combinedPixel_g = devicePixel_g & intermediatePixel_g;
						combinedPixel_b = devicePixel_b & intermediatePixel_b;
					}
					else if (m_rop == TQPainter::NotROP) {
						combinedPixel_a = ((devicePixel_a != 0) || (intermediatePixel_a != 0))?0xff:0x00;
						combinedPixel_r = ~devicePixel_r;
						combinedPixel_g = ~devicePixel_g;
						combinedPixel_b = ~devicePixel_b;
					}
					else if (m_rop == TQPainter::ClearROP) {
						combinedPixel_a = 0x0;
						combinedPixel_r = 0x0;
						combinedPixel_g = 0x0;
						combinedPixel_b = 0x0;
					}
					else if (m_rop == TQPainter::SetROP) {
						combinedPixel_a = 0xff;
						combinedPixel_r = 0xff;
						combinedPixel_g = 0xff;
						combinedPixel_b = 0xff;
					}
					else if (m_rop == TQPainter::NopROP) {
						combinedPixel_a = ((devicePixel_a != 0) || (intermediatePixel_a != 0))?0xff:0x00;
						combinedPixel_r = devicePixel_r;
						combinedPixel_g = devicePixel_g;
						combinedPixel_b = devicePixel_b;
					}
					else if (m_rop == TQPainter::AndNotROP) {
						combinedPixel_a = ((devicePixel_a != 0) || (intermediatePixel_a != 0))?0xff:0x00;
						combinedPixel_r = (~devicePixel_r) & intermediatePixel_r;
						combinedPixel_g = (~devicePixel_g) & intermediatePixel_g;
						combinedPixel_b = (~devicePixel_b) & intermediatePixel_b;
					}
					else if (m_rop == TQPainter::OrNotROP) {
						combinedPixel_a = ((devicePixel_a != 0) || (intermediatePixel_a != 0))?0xff:0x00;
						combinedPixel_r = (~devicePixel_r) | intermediatePixel_r;
						combinedPixel_g = (~devicePixel_g) | intermediatePixel_g;
						combinedPixel_b = (~devicePixel_b) | intermediatePixel_b;
					}
					else if (m_rop == TQPainter::NandROP) {
						combinedPixel_a = ((devicePixel_a != 0) || (intermediatePixel_a != 0))?0xff:0x00;
						combinedPixel_r = ~(devicePixel_r & intermediatePixel_r);
						combinedPixel_g = ~(devicePixel_g & intermediatePixel_g);
						combinedPixel_b = ~(devicePixel_b & intermediatePixel_b);
					}
					else if (m_rop == TQPainter::NorROP) {
						combinedPixel_a = ((devicePixel_a != 0) || (intermediatePixel_a != 0))?0xff:0x00;
						combinedPixel_r = ~(devicePixel_r | intermediatePixel_r);
						combinedPixel_g = ~(devicePixel_g | intermediatePixel_g);
						combinedPixel_b = ~(devicePixel_b | intermediatePixel_b);
					}
					else {
						tqWarning("TQt3CairoPaintDevice::cmd: Unhandled raster operation [Was attempting to use raster operation %d\n", m_rop);
						combinedPixel_a = devicePixel_a;
						combinedPixel_r = devicePixel_r;
						combinedPixel_g = devicePixel_g;
						combinedPixel_b = devicePixel_b;
					}

					// Convert to premultiplied ARGB
					(*(intermediate_surface_data+offset+ARGB_A_BYTE_NUMBER)) = combinedPixel_a;
					(*(intermediate_surface_data+offset+ARGB_R_BYTE_NUMBER)) = combinedPixel_r;
					(*(intermediate_surface_data+offset+ARGB_G_BYTE_NUMBER)) = combinedPixel_g;
					(*(intermediate_surface_data+offset+ARGB_B_BYTE_NUMBER)) = combinedPixel_b;
					standardAlphaToPremultipliedAlpha(intermediate_surface_data+offset+ARGB_A_BYTE_NUMBER, intermediate_surface_data+offset+ARGB_R_BYTE_NUMBER, intermediate_surface_data+offset+ARGB_G_BYTE_NUMBER, intermediate_surface_data+offset+ARGB_B_BYTE_NUMBER);
				}
				else {
					// Lossless copy of premultiplied ARGB pixel values
					(*(intermediate_surface_data+offset+ARGB_A_BYTE_NUMBER)) = (*(device_surface_data+offset+ARGB_A_BYTE_NUMBER));
					(*(intermediate_surface_data+offset+ARGB_R_BYTE_NUMBER)) = (*(device_surface_data+offset+ARGB_R_BYTE_NUMBER));
					(*(intermediate_surface_data+offset+ARGB_G_BYTE_NUMBER)) = (*(device_surface_data+offset+ARGB_G_BYTE_NUMBER));
					(*(intermediate_surface_data+offset+ARGB_B_BYTE_NUMBER)) = (*(device_surface_data+offset+ARGB_B_BYTE_NUMBER));
				}
			}
		}
		cairo_surface_mark_dirty(m_intermediateSurface);
		cairo_surface_destroy(usableDeviceSurface);
	}

	if (!m_clipRegionEnabled) {
		// Clipping disabled
		cairo_save(m_devicePainter);
		cairo_reset_clip(m_devicePainter);
		cairo_set_matrix(m_devicePainter, &m_deviceMatrix);

		cairo_set_source_surface(m_devicePainter, m_intermediateSurface, m_offsetX, m_offsetY);
		cairo_set_operator(m_devicePainter, overlayMerge?CAIRO_OPERATOR_OVER:CAIRO_OPERATOR_SOURCE);
		cairo_paint(m_devicePainter);

		cairo_restore(m_devicePainter);
	}
	else {
		// Clipping enabled
		cairo_save(m_devicePainter);
		cairo_reset_clip(m_devicePainter);
		cairo_set_matrix(m_devicePainter, &m_deviceMatrix);

		cairo_surface_t* maskSurface = TQImageToCairoSurface(m_clipRegion);
		cairo_set_source_surface(m_devicePainter, m_intermediateSurface, m_offsetX, m_offsetY);
		cairo_set_operator(m_devicePainter, overlayMerge?CAIRO_OPERATOR_OVER:CAIRO_OPERATOR_SOURCE);

		// Clipping enabled
		if (m_worldMatrixEnabled || m_viewportMatrixEnabled) {
			// The mask needs to be transformed before application
			cairo_surface_t* maskSurface = TQImageToCairoSurface(m_clipRegion);
			cairo_surface_t *transformedMaskSurface;
			cairo_t *cr2;
			int maxSize;
			int w = cairo_image_surface_get_width(maskSurface);
			int h = cairo_image_surface_get_height(maskSurface);
			if (w>h) {
				maxSize = w*2;
			}
			else {
				maxSize = h*2;
			}
   			transformedMaskSurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, maxSize, maxSize);
   			cr2 = cairo_create(transformedMaskSurface);
			setCairoTransformations(cr2);
			cairo_set_source_surface (cr2, maskSurface, 0, 0);
			cairo_set_operator(cr2, CAIRO_OPERATOR_SOURCE);
			cairo_paint(cr2);

			// Paint intermediate surface to final surface through mask
			cairo_set_source_surface(m_devicePainter, m_intermediateSurface, m_offsetX, m_offsetY);
			cairo_set_operator(m_devicePainter, overlayMerge?CAIRO_OPERATOR_OVER:CAIRO_OPERATOR_SOURCE);
			cairo_mask_surface(m_devicePainter, transformedMaskSurface, m_offsetX, m_offsetY);
			cairo_surface_destroy(transformedMaskSurface);
		}
		else {
			// Paint intermediate surface to final surface through mask
			cairo_surface_t* maskSurface = TQImageToCairoSurface(m_clipRegion);
			cairo_set_source_surface(m_devicePainter, m_intermediateSurface, m_offsetX, m_offsetY);
			cairo_set_operator(m_devicePainter, overlayMerge?CAIRO_OPERATOR_OVER:CAIRO_OPERATOR_SOURCE);
			cairo_mask_surface(m_devicePainter, maskSurface, m_offsetX, m_offsetY);
		}
		cairo_surface_destroy(maskSurface);

		cairo_restore(m_devicePainter);
	}

	// Clear intermediate surface
	cairo_t *clearPainter = cairo_create(m_intermediateSurface);
	cairo_set_operator(clearPainter, CAIRO_OPERATOR_SOURCE);
	cairo_set_source_rgba(clearPainter, 0.0, 0.0, 0.0, 0.0);
	cairo_paint(clearPainter);
	cairo_surface_flush(m_intermediateSurface);
	cairo_destroy(clearPainter);

	m_transferNeeded = false;
}

bool TQt3CairoPaintDevice::intermediateSurfaceInUse() const {
	return (m_clipRegionEnabled || (m_rop != TQPainter::CopyROP));
}

void TQt3CairoPaintDevice::dualStrokePen() {
	if (m_bgColorMode == TQt::OpaqueMode) {
		// Draw background
		cairo_save(FASTEST_AVAILABLE_PAINTER);
		updatePen(true);
		cairo_stroke(FASTEST_AVAILABLE_PAINTER);
		cairo_restore(FASTEST_AVAILABLE_PAINTER);
	}
	// Draw foreground
	updatePen(false);
	cairo_stroke(FASTEST_AVAILABLE_PAINTER);
	m_transferNeeded = true;
}

void TQt3CairoPaintDevice::dualStrokeBrush(cairo_fill_rule_t fillMethod) {
	if (m_bgColorMode == TQt::OpaqueMode) {
		// Draw background
		cairo_save(FASTEST_AVAILABLE_PAINTER);
		updateBrush(true, fillMethod);
		cairo_fill(FASTEST_AVAILABLE_PAINTER);
		cairo_restore(FASTEST_AVAILABLE_PAINTER);
	}
	// Draw foreground
	updateBrush(false, fillMethod);
	cairo_fill(FASTEST_AVAILABLE_PAINTER);
	m_transferNeeded = true;
}

void TQt3CairoPaintDevice::updatePen(bool backgroundStroke) {
	if (!m_painter) {
		return;
	}

	int ps = m_pen.style();

	double dashes[10];				// custom pen dashes
	int dash_len = 0;				// length of dash list
	int dash_offset = 0;
	cairo_line_cap_t cp = CAIRO_LINE_CAP_BUTT;
	cairo_line_join_t jn = CAIRO_LINE_JOIN_MITER;

	/*
	We are emulating Windows here.  Windows treats m_pen.width() == 1
	(or 0) as a very special case.  The fudge variable unifies this
	case with the general case.
	*/
	int dot = m_pen.width();				// width of a dot
	int fudge = 1;
	//bool allow_zero_lw = true;
	bool allow_zero_lw = false;
	if ( dot <= 1 ) {
		dot = 3;
		fudge = 2;
	}

	switch( ps ) {
		case TQPainter::NoPen:
		case TQPainter::SolidLine:
			break;
		case TQPainter::DashLine:
			dashes[0] = fudge * 3 * dot;
			dashes[1] = fudge * dot;
			dash_len = 2;
			allow_zero_lw = false;
			break;
		case TQPainter::DotLine:
			dashes[0] = dot;
			dashes[1] = dot;
			dash_len = 2;
			allow_zero_lw = false;
			break;
		case TQPainter::DashDotLine:
			dashes[0] = 3 * dot;
			dashes[1] = fudge * dot;
			dashes[2] = dot;
			dashes[3] = fudge * dot;
			dash_len = 4;
			allow_zero_lw = false;
			break;
		case TQPainter::DashDotDotLine:
			dashes[0] = 3 * dot;
			dashes[1] = dot;
			dashes[2] = dot;
			dashes[3] = dot;
			dashes[4] = dot;
			dashes[5] = dot;
			dash_len = 6;
			allow_zero_lw = false;
			break;
		case TQPainter::FineDotLine:
			dot = 1;
			dashes[0] = dot;
			dashes[1] = dot;
			dash_len = 2;
			allow_zero_lw = false;
	}
	Q_ASSERT( dash_len <= (int) sizeof(dashes) );

	switch ( m_pen.capStyle() ) {
		case TQPainter::SquareCap:
			cp = CAIRO_LINE_CAP_SQUARE;
			break;
		case TQPainter::RoundCap:
			cp = CAIRO_LINE_CAP_ROUND;
			break;
		case TQPainter::FlatCap:
		default:
			cp = CAIRO_LINE_CAP_BUTT;
			break;
	}

	switch ( m_pen.joinStyle() ) {
		case TQPainter::BevelJoin:
			jn = CAIRO_LINE_JOIN_BEVEL;
			break;
		case TQPainter::RoundJoin:
			jn = CAIRO_LINE_JOIN_ROUND;
			break;
		case TQPainter::MiterJoin:
		default:
			jn = CAIRO_LINE_JOIN_MITER;
			break;
	}

	if (backgroundStroke) {
		dash_len = 0;
		
	}

	TQRgb color = (backgroundStroke)?m_bgColor.rgb():m_pen.color().rgb();

	cairo_set_dash(m_painter, dashes, dash_len, dash_offset);
	cairo_set_line_cap(m_painter, cp);
	cairo_set_line_join(m_painter, jn);
	cairo_set_line_width(m_painter, ((!allow_zero_lw) && (m_pen.width() == 0)) ? 1 : m_pen.width());
	cairo_set_source_rgba(m_painter, tqRed(color)/255.0, tqGreen(color)/255.0, tqBlue(color)/255.0, tqAlpha(color)/255.0);

	// BEGIN DUPLICATE
	cairo_set_dash(m_devicePainter, dashes, dash_len, dash_offset);
	cairo_set_line_cap(m_devicePainter, cp);
	cairo_set_line_join(m_devicePainter, jn);
	cairo_set_line_width(m_devicePainter, ((!allow_zero_lw) && (m_pen.width() == 0)) ? 1 : m_pen.width());
	cairo_set_source_rgba(m_devicePainter, tqRed(color)/255.0, tqGreen(color)/255.0, tqBlue(color)/255.0, tqAlpha(color)/255.0);
	// END DUPLICATE
}

void TQt3CairoPaintDevice::updateBrush(bool backgroundStroke, cairo_fill_rule_t fillMethod) {
	if (!m_painter) {
		return;
	}

	if (backgroundStroke) {
		TQRgb color = m_bgColor.rgb();
		cairo_pattern_t* pattern = cairo_pattern_create_rgba(tqRed(color)/255.0, tqGreen(color)/255.0, tqBlue(color)/255.0, tqAlpha(color)/255.0);
		cairo_set_source(m_painter, pattern);
		cairo_set_source(m_devicePainter, pattern);
		cairo_pattern_set_extend(cairo_get_source(m_painter), CAIRO_EXTEND_REPEAT);
		cairo_pattern_set_extend(cairo_get_source(m_devicePainter), CAIRO_EXTEND_REPEAT);
		cairo_pattern_destroy(pattern);
	}
	else {
		static const uchar dense1_pat[] = { 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff };
		static const uchar dense2_pat[] = { 0x77, 0xff, 0xdd, 0xff, 0x77, 0xff, 0xdd, 0xff };
		static const uchar dense3_pat[] = { 0x55, 0xbb, 0x55, 0xee, 0x55, 0xbb, 0x55, 0xee };
		static const uchar dense4_pat[] = { 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa };
		static const uchar dense5_pat[] = { 0xaa, 0x44, 0xaa, 0x11, 0xaa, 0x44, 0xaa, 0x11 };
		static const uchar dense6_pat[] = { 0x88, 0x00, 0x22, 0x00, 0x88, 0x00, 0x22, 0x00 };
		static const uchar dense7_pat[] = { 0x00, 0x44, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00 };
		static const uchar hor_pat[] = {                      // horizontal pattern
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
		static const uchar ver_pat[] = {                      // vertical pattern
			0x08, 0x82, 0x20, 0x08, 0x82, 0x20, 0x08, 0x82, 0x20, 0x08, 0x82, 0x20,
			0x08, 0x82, 0x20, 0x08, 0x82, 0x20, 0x08, 0x82, 0x20, 0x08, 0x82, 0x20,
			0x08, 0x82, 0x20, 0x08, 0x82, 0x20, 0x08, 0x82, 0x20, 0x08, 0x82, 0x20,
			0x08, 0x82, 0x20, 0x08, 0x82, 0x20, 0x08, 0x82, 0x20, 0x08, 0x82, 0x20,
			0x08, 0x82, 0x20, 0x08, 0x82, 0x20, 0x08, 0x82, 0x20, 0x08, 0x82, 0x20,
			0x08, 0x82, 0x20, 0x08, 0x82, 0x20, 0x08, 0x82, 0x20, 0x08, 0x82, 0x20 };
		static const uchar cross_pat[] = {                    // cross pattern
			0x08, 0x82, 0x20, 0x08, 0x82, 0x20, 0x08, 0x82, 0x20, 0xff, 0xff, 0xff,
			0x08, 0x82, 0x20, 0x08, 0x82, 0x20, 0x08, 0x82, 0x20, 0x08, 0x82, 0x20,
			0x08, 0x82, 0x20, 0xff, 0xff, 0xff, 0x08, 0x82, 0x20, 0x08, 0x82, 0x20,
			0x08, 0x82, 0x20, 0x08, 0x82, 0x20, 0x08, 0x82, 0x20, 0xff, 0xff, 0xff,
			0x08, 0x82, 0x20, 0x08, 0x82, 0x20, 0x08, 0x82, 0x20, 0x08, 0x82, 0x20,
			0x08, 0x82, 0x20, 0xff, 0xff, 0xff, 0x08, 0x82, 0x20, 0x08, 0x82, 0x20 };
		static const uchar bdiag_pat[] = {                    // backward diagonal pattern
			0x20, 0x20, 0x10, 0x10, 0x08, 0x08, 0x04, 0x04, 0x02, 0x02, 0x01, 0x01,
			0x80, 0x80, 0x40, 0x40, 0x20, 0x20, 0x10, 0x10, 0x08, 0x08, 0x04, 0x04,
			0x02, 0x02, 0x01, 0x01, 0x80, 0x80, 0x40, 0x40 };
		static const uchar fdiag_pat[] = {                    // forward diagonal pattern
			0x02, 0x02, 0x04, 0x04, 0x08, 0x08, 0x10, 0x10, 0x20, 0x20, 0x40, 0x40,
			0x80, 0x80, 0x01, 0x01, 0x02, 0x02, 0x04, 0x04, 0x08, 0x08, 0x10, 0x10,
			0x20, 0x20, 0x40, 0x40, 0x80, 0x80, 0x01, 0x01 };
		static const uchar dcross_pat[] = {                   // diagonal cross pattern
			0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x14, 0x14, 0x22, 0x22, 0x41, 0x41,
			0x80, 0x80, 0x41, 0x41, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x14, 0x14,
			0x22, 0x22, 0x41, 0x41, 0x80, 0x80, 0x41, 0x41 };
		static const uchar * const pat_tbl[] = {
			dense1_pat, dense2_pat, dense3_pat, dense4_pat, dense5_pat,
			dense6_pat, dense7_pat,
			hor_pat, ver_pat, cross_pat, bdiag_pat, fdiag_pat, dcross_pat };
	
		int  bs = m_brush.style();
	
		const uchar *pat = 0;		// pattern
		int d = 0;			// defalt pattern size: d*d
		if ( bs >= TQBrush::Dense1Pattern && bs <= TQBrush::DiagCrossPattern ) {
			pat = pat_tbl[ bs-TQBrush::Dense1Pattern ];
			if ( bs <= TQBrush::Dense7Pattern ) {
				d = 8;
			}
			else if ( bs <= TQBrush::CrossPattern ) {
				d = 24;
			}
			else {
				d = 16;
			}
		}

		if ( (bs == TQBrush::CustomPattern) || pat ) {
			TQImage brushImage;
			if ( pat ) {
				TQRgb color = m_brush.color().rgb();
				brushImage = TQImage(d, d, 32);
				int x;
				int y;
				int byte = 0;
				int bit = 7;
				for (x=0; x<d; x++) {
					for (y=0; y<d; y++) {
						brushImage.setPixel(y, x, (TEST_BIT(pat[byte], bit))?color:0x00000000);
						bit--;
						if (bit < 0) {
							bit = 7;
							byte++;
						}
					}
				}
			}
			else {
				TQPixmap *pm;
				pm = m_brush.pixmap();
#ifdef USE_TDE_SPECIFIC_LIBRARIES
				brushImage = tdePixmapIO->convertToImage(*pm);
#else // USE_TDE_SPECIFIC_LIBRARIES
				brushImage = pm->convertToImage();
#endif
			}
	
			cairo_surface_t* brushSurface = TQImageToCairoSurface(brushImage);
			cairo_pattern_t* pattern = cairo_pattern_create_for_surface(brushSurface);
			cairo_matrix_t brush_translation_matrix;
			cairo_matrix_init_translate(&brush_translation_matrix, m_brushOrigin.x()+1, m_brushOrigin.y());
			cairo_pattern_set_matrix(pattern, &brush_translation_matrix);
			cairo_set_source(m_painter, pattern);
			cairo_set_source(m_devicePainter, pattern);
			cairo_pattern_set_extend(cairo_get_source(m_painter), CAIRO_EXTEND_REPEAT);
			cairo_pattern_set_extend(cairo_get_source(m_devicePainter), CAIRO_EXTEND_REPEAT);
			cairo_pattern_destroy(pattern);
			cairo_surface_destroy(brushSurface);
		}
		else {
			TQRgb color = m_brush.color().rgb();
			cairo_pattern_t* pattern = cairo_pattern_create_rgba(tqRed(color)/255.0, tqGreen(color)/255.0, tqBlue(color)/255.0, tqAlpha(color)/255.0);
			cairo_set_source(m_painter, pattern);
			cairo_set_source(m_devicePainter, pattern);
			cairo_pattern_set_extend(cairo_get_source(m_painter), CAIRO_EXTEND_REPEAT);
			cairo_pattern_set_extend(cairo_get_source(m_devicePainter), CAIRO_EXTEND_REPEAT);
			cairo_pattern_destroy(pattern);
		}
	}
	cairo_set_fill_rule(m_painter, fillMethod);
	cairo_set_fill_rule(m_devicePainter, fillMethod);
}

static inline void fix_neg_rect( int *x, int *y, int *w, int *h ) {
	if ( *w < 0 ) {
		*w = -*w + 2;
		*x -= *w - 1;
	}
	if ( *h < 0 ) {
		*h = -*h + 2;
		*y -= *h - 1;
	}
}

void TQt3CairoPaintDevice::drawPolygon(const TQPointArray* pointarray, bool winding, bool fill, bool close) {
	int i;

	if (FASTEST_AVAILABLE_PAINTER) {
		cairo_save(FASTEST_AVAILABLE_PAINTER);
		if (pointarray) {
			int x;
			int y;
			bool first;
			if ((m_brush.style() != TQBrush::NoBrush) && fill) {
				first = true;
				for (i=0;i<pointarray->count();i++) {
					pointarray->point(i, &x, &y);
					if (first) {
						cairo_move_to(FASTEST_AVAILABLE_PAINTER, x+CAIRO_PEN_PIXEL_OFFSET, y+CAIRO_PEN_PIXEL_OFFSET);
						first = false;
					}
					else {
						cairo_line_to(FASTEST_AVAILABLE_PAINTER, x+CAIRO_PEN_PIXEL_OFFSET, y+CAIRO_PEN_PIXEL_OFFSET);
					}
				}
				if (close) {
					cairo_close_path(FASTEST_AVAILABLE_PAINTER);
				}
				dualStrokeBrush((winding)?CAIRO_FILL_RULE_EVEN_ODD:CAIRO_FILL_RULE_WINDING);
			}
			if (m_pen.style() != TQPen::NoPen) {
				first = true;
				for (i=0;i<pointarray->count();i++) {
					pointarray->point(i, &x, &y);
					if (first) {
						cairo_move_to(FASTEST_AVAILABLE_PAINTER, x+CAIRO_PEN_PIXEL_OFFSET, y+CAIRO_PEN_PIXEL_OFFSET);
						first = false;
					}
					else {
						cairo_line_to(FASTEST_AVAILABLE_PAINTER, x+CAIRO_PEN_PIXEL_OFFSET, y+CAIRO_PEN_PIXEL_OFFSET);
					}
				}
				if (close) {
					cairo_close_path(FASTEST_AVAILABLE_PAINTER);
				}
				dualStrokePen();

				// WARNING
				// The Cairo anti-aliasing code fights back--HARD--when drawing 0px lines!
				// See http://projecthamster.wordpress.com/2009/12/22/getting-sharp-pixels-and-actually-something-drawn-using-cairo/ for more details.
				// This code works around the problem by overstriking the line end points with a single pixel, thereby ensuring they are properly drawn.
				if (m_pen.width() < 1) {
					cairo_save(FASTEST_AVAILABLE_PAINTER);
					cairo_set_line_cap(FASTEST_AVAILABLE_PAINTER, CAIRO_LINE_CAP_ROUND);
					cairo_set_line_join(FASTEST_AVAILABLE_PAINTER, CAIRO_LINE_JOIN_ROUND);
					cairo_set_line_width(FASTEST_AVAILABLE_PAINTER, 1);
					for (i=0;i<pointarray->count();i++) {
						pointarray->point(i, &x, &y);
						cairo_move_to(FASTEST_AVAILABLE_PAINTER, x+CAIRO_PEN_PIXEL_OFFSET, y+CAIRO_PEN_PIXEL_OFFSET);
						cairo_line_to(FASTEST_AVAILABLE_PAINTER, x+CAIRO_PEN_PIXEL_OFFSET, y+CAIRO_PEN_PIXEL_OFFSET);
					}
					cairo_stroke(FASTEST_AVAILABLE_PAINTER);
					cairo_restore(FASTEST_AVAILABLE_PAINTER);
				}
			}
		}
		cairo_restore(FASTEST_AVAILABLE_PAINTER);
	}
}

void TQt3CairoPaintDevice::drawRoundRect(int x, int y, int w, int h, int xRnd, int yRnd) {
	if (!FASTEST_AVAILABLE_PAINTER) {
		return;
	}

	w=w+2;
	h=h+2;

	if ( xRnd <= 0 || yRnd <= 0 ) {
		// Draw normal rectangle
		TQPDevCmdParam param[2];
		int command = PdcDrawRect;
		TQRect rectangle(x, y, w, h);
		param[0].rect = &rectangle;
		cmd(command, NULL, param);
		return;
	}

	if ( xRnd >= 100 ) {                          // fix ranges
		xRnd = 99;
	}
	if ( yRnd >= 100 ) {
		yRnd = 99;
	}

	if ( w <= 0 || h <= 0 ) {
		fix_neg_rect( &x, &y, &w, &h );
	}
	w--;
	h--;
	int rxx = w*xRnd/200;
	int ryy = h*yRnd/200;
	// were there overflows?
	if ( rxx < 0 ) {
		rxx = w/200*xRnd;
	}
	if ( ryy < 0 ) {
		ryy = h/200*yRnd;
	}
	int rxx2 = 2*rxx;
	int ryy2 = 2*ryy;
	TQPointArray a[4];
	a[0].makeArc( x, y, rxx2, ryy2, 1*16*90, 16*90 );
	a[1].makeArc( x, y+h-ryy2, rxx2, ryy2, 2*16*90, 16*90 );
	a[2].makeArc( x+w-rxx2, y+h-ryy2, rxx2, ryy2, 3*16*90, 16*90 );
	a[3].makeArc( x+w-rxx2, y, rxx2, ryy2, 0*16*90, 16*90 );
	// ### is there a better way to join TQPointArrays?
	TQPointArray aa;
	aa.resize( a[0].size() + a[1].size() + a[2].size() + a[3].size() );
	uint j = 0;
	for ( int k=0; k<4; k++ ) {
		for ( uint i=0; i<a[k].size(); i++ ) {
			aa.setPoint( j, a[k].point(i) );
			j++;
		}
	}

	// Draw polygon
	drawPolygon(&aa, false, true, true);

	return;
}

void TQt3CairoPaintDevice::drawEllipse(int x, int y, int w, int h) {
	if (!FASTEST_AVAILABLE_PAINTER) {
		return;
	}

	TQPointArray a;
	a.makeArc(x, y, w+1, h+1, 0, 360*16);

	// Draw polygon
	drawPolygon(&a, false, true, true);

	return;
}

void TQt3CairoPaintDevice::drawArc(int x, int y, int w, int h, int a, int alen) {
	if (!FASTEST_AVAILABLE_PAINTER) {
		return;
	}

	TQPointArray pa;
	pa.makeArc(x, y, w, h, a, alen); // arc polyline

	// Draw polygon
	drawPolygon(&pa, false, false, false);

	return;
}

void TQt3CairoPaintDevice::drawPie(int x, int y, int w, int h, int a, int alen) {
	if (!FASTEST_AVAILABLE_PAINTER) {
		return;
	}

	// Make sure "a" is 0..360*16, as otherwise a*4 may overflow 16 bits.
	if ( a > (360*16) ) {
		a = a % (360*16);
	}
	else if ( a < 0 ) {
		a = a % (360*16);
		if ( a < 0 ) {
			a += (360*16);
		}
	}

	TQPointArray pa;
	pa.makeArc(x, y, w, h, a, alen); // arc polyline
	int n = pa.size();
	int cx, cy;
	cx = (x+w/2)+1;
	cy = (y+h/2)+1;
	pa.resize(n+2);
	pa.setPoint(n, cx, cy);   // add legs
	pa.setPoint(n+1, pa.at(0));

	// Draw polygon
	drawPolygon(&pa, false, true, true);

	return;
}

void TQt3CairoPaintDevice::drawChord(int x, int y, int w, int h, int a, int alen) {
	if (!FASTEST_AVAILABLE_PAINTER) {
		return;
	}

	TQPointArray pa;
	pa.makeArc(x, y, w, h, a, alen); // arc polygon
	int n = pa.size();
	pa.resize(n+1);
	pa.setPoint(n, pa.at(0));         // connect endpoints

	// Draw polygon
	drawPolygon(&pa, false, true, true);

	return;
}

void TQt3CairoPaintDevice::pangoSetupTextPath(PangoLayout *layout, const char* text) {
	PangoFontDescription *desc;

	pango_layout_set_text(layout, text, -1);

	desc = pango_font_description_new();

	// FIXME
	// overline and a handful of other flags are not supported by Pango!
	// https://bugzilla.gnome.org/show_bug.cgi?id=577190
	TQString family = m_font.family();
// 	bool bold = m_font.bold();
	bool italic = m_font.italic();
	bool underline = m_font.underline();
// 	bool overline = m_font.overline();
	bool strikeout = m_font.strikeOut();
// 	bool fixedPitch = m_font.fixedPitch();
	int stretch = m_font.stretch();
	int weight = m_font.weight();

	int pixelSize = m_font.pixelSize();
	bool usePixelSize = (pixelSize>=0);
	float pointSizeFloat = m_font.pointSizeFloat();
	bool usePointSize = (pointSizeFloat>=0);

	TQFont::StyleStrategy qt3fontstrategy = m_font.styleStrategy();

	PangoWeight pangoWeight;
	switch (weight) {
		case TQFont::Light:
			pangoWeight = PANGO_WEIGHT_LIGHT;
			break;
		case TQFont::Normal:
			pangoWeight = PANGO_WEIGHT_NORMAL;
			break;
		case TQFont::DemiBold:
			pangoWeight = PANGO_WEIGHT_SEMIBOLD;
			break;
		case TQFont::Bold:
			pangoWeight = PANGO_WEIGHT_BOLD;
			break;
		case TQFont::Black:
			pangoWeight = PANGO_WEIGHT_HEAVY;
			break;
	}

	PangoStretch pangoStretch;
	switch (stretch) {
		case TQFont::UltraCondensed:
			pangoStretch = PANGO_STRETCH_ULTRA_CONDENSED;
			break;
		case TQFont::ExtraCondensed:
			pangoStretch = PANGO_STRETCH_EXTRA_CONDENSED;
			break;
		case TQFont::Condensed:
			pangoStretch = PANGO_STRETCH_CONDENSED;
			break;
		case TQFont::SemiCondensed:
			pangoStretch = PANGO_STRETCH_SEMI_CONDENSED;
			break;
		case TQFont::Unstretched:
			pangoStretch = PANGO_STRETCH_NORMAL;
			break;
		case TQFont::SemiExpanded:
			pangoStretch = PANGO_STRETCH_SEMI_EXPANDED;
			break;
		case TQFont::Expanded:
			pangoStretch = PANGO_STRETCH_EXPANDED;
			break;
		case TQFont::ExtraExpanded:
			pangoStretch = PANGO_STRETCH_EXTRA_EXPANDED;
			break;
		case TQFont::UltraExpanded:
			pangoStretch = PANGO_STRETCH_ULTRA_EXPANDED;
			break;
	}

	pango_font_description_set_family(desc, family.ascii());
	if (usePixelSize) {
		pango_font_description_set_absolute_size(desc, pixelSize*PANGO_SCALE);
	}
	if (usePointSize) {
		pango_font_description_set_absolute_size(desc, pointSizeFloat*PANGO_SCALE*CAIRO_FONT_SIZE_FUDGE_FACTOR);
	}
	pango_font_description_set_style(desc, (italic)?PANGO_STYLE_ITALIC:PANGO_STYLE_NORMAL);
	pango_font_description_set_weight(desc, pangoWeight);
	pango_font_description_set_stretch(desc, pangoStretch);

#if 0
	if (qt3fontstrategy & TQFont::PreferDefault)    // FIXME Set Cairo/Pango to follow this hint
	if (qt3fontstrategy & TQFont::PreferBitmap)     // FIXME Set Cairo/Pango to follow this hint
	if (qt3fontstrategy & TQFont::PreferDevice)     // FIXME Set Cairo/Pango to follow this hint
	if (qt3fontstrategy & TQFont::PreferMatch)      // FIXME Set Cairo/Pango to follow this hint
	if (qt3fontstrategy & TQFont::PreferQuality)    // FIXME Set Cairo/Pango to follow this hint
	if (qt3fontstrategy & TQFont::PreferAntialias)  // FIXME Set Cairo/Pango to follow this hint
	if (qt3fontstrategy & TQFont::NoAntialias)      // FIXME Set Cairo/Pango to follow this hint
	if (qt3fontstrategy & TQFont::OpenGLCompatible) // FIXME Set Cairo/Pango to follow this hint
#endif

	pango_layout_set_font_description(layout, desc);
	pango_font_description_free(desc);

	PangoAttrList* attr_list = pango_attr_list_new();
	pango_attr_list_insert(attr_list, pango_attr_underline_new((underline)?PANGO_UNDERLINE_SINGLE:PANGO_UNDERLINE_NONE));
	pango_attr_list_insert(attr_list, pango_attr_strikethrough_new(strikeout));
	pango_layout_set_attributes(layout, attr_list);
	pango_attr_list_unref(attr_list);

	if (m_tabStopArrayValid) {
		pango_layout_set_tabs(layout, m_tabStopArray);
	}
	else if (m_tabStopsValid) {
		pango_layout_set_tabs(layout, m_tabStops);
	}
	else {
		pango_layout_set_tabs(layout, NULL);
	}
}

void TQt3CairoPaintDevice::drawText(TQPainter *p, int x, int y, const TQString &str) {
	if ((!FASTEST_AVAILABLE_PAINTER) || (!p)) {
		return;
	}

	PangoLayout *layout;
	layout = pango_cairo_create_layout(FASTEST_AVAILABLE_PAINTER);

	TQFont::StyleStrategy qt3fontstrategy = m_font.styleStrategy();
	pangoSetupTextPath(layout, str.utf8());

	int baseline_y = pango_layout_get_baseline(layout)/PANGO_SCALE;
	cairo_new_path(FASTEST_AVAILABLE_PAINTER);
	cairo_move_to(FASTEST_AVAILABLE_PAINTER, x, y-baseline_y);
	updatePen(false);

	pango_cairo_update_layout(FASTEST_AVAILABLE_PAINTER, layout);
	pango_cairo_layout_path(FASTEST_AVAILABLE_PAINTER, layout);

	if ((qt3fontstrategy & TQFont::PreferOutline) || (qt3fontstrategy & TQFont::ForceOutline)) {
		cairo_stroke_preserve(FASTEST_AVAILABLE_PAINTER);
	}
	else {
		cairo_fill(FASTEST_AVAILABLE_PAINTER);
	}

	g_object_unref(layout);

	m_transferNeeded = true;
}

void TQt3CairoPaintDevice::drawTextInRect(TQPainter *p, TQRect rect, int textFlags, const TQString &str) {
	if ((!FASTEST_AVAILABLE_PAINTER) || (!p)) {
		return;
	}

	PangoLayout *layout;
	layout = pango_cairo_create_layout(FASTEST_AVAILABLE_PAINTER);

	TQFont::StyleStrategy qt3fontstrategy = m_font.styleStrategy();
	pangoSetupTextPath(layout, str.utf8());

	pango_layout_set_width(layout, rect.width()*PANGO_SCALE);

	int fudgedOffsetY = 0;

	// Layout flags
	if (textFlags & TQt::SingleLine) {
		// Pango special case to force rendering of only one line of text
		pango_layout_set_height(layout, 0);
	}
	if (!(textFlags & TQt::DontClip)) {
		cairo_rectangle(FASTEST_AVAILABLE_PAINTER, rect.x()+CAIRO_PEN_PIXEL_OFFSET, rect.y()+CAIRO_PEN_PIXEL_OFFSET, rect.width(), rect.height());
		cairo_clip(FASTEST_AVAILABLE_PAINTER);
	}
	if (textFlags & TQt::ExpandTabs) {
		// FIXME
	}
	if (textFlags & TQt::ShowPrefix) {
		// FIXME
	}
	if (textFlags & TQt::WordBreak) {
		pango_layout_set_wrap(layout, PANGO_WRAP_WORD);
	}
	if (textFlags & TQt::BreakAnywhere) {
		pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);
	}
	if (textFlags & TQt::NoAccel) {
		// FIXME
	}

	PangoRectangle inkRect;
	PangoRectangle logicalRect;
	pango_layout_get_pixel_extents(layout, &inkRect, &logicalRect);

	int stockWidth = logicalRect.x + logicalRect.width;
	int stockHeight = logicalRect.y + logicalRect.height;

	pango_layout_set_height(layout, rect.height()*PANGO_SCALE);

	// Position flags
	if (textFlags & TQt::AlignAuto) {
		// FIXME
	}
	if (textFlags & TQt::AlignLeft) {
		pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT);
	}
	if (textFlags & TQt::AlignRight) {
		pango_layout_set_alignment(layout, PANGO_ALIGN_RIGHT);
	}
	if (textFlags & TQt::AlignHCenter) {
		pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
	}
	if (textFlags & TQt::AlignJustify) {
		// FIXME
	}
	if (textFlags & TQt::AlignTop) {
		fudgedOffsetY = 0;
	}
	if (textFlags & TQt::AlignBottom) {
		fudgedOffsetY = (rect.height()-stockHeight);
	}
	if (textFlags & TQt::AlignVCenter) {
		fudgedOffsetY = ((rect.height()-stockHeight)/2);
	}

	cairo_new_path(FASTEST_AVAILABLE_PAINTER);
	cairo_move_to(FASTEST_AVAILABLE_PAINTER, rect.x(), rect.y() + fudgedOffsetY);
	updatePen(false);

	pango_cairo_update_layout(FASTEST_AVAILABLE_PAINTER, layout);
	pango_cairo_layout_path(FASTEST_AVAILABLE_PAINTER, layout);

	if ((qt3fontstrategy & TQFont::PreferOutline) || (qt3fontstrategy & TQFont::ForceOutline)) {
		cairo_stroke_preserve(FASTEST_AVAILABLE_PAINTER);
	}
	else {
		cairo_fill(FASTEST_AVAILABLE_PAINTER);
	}
	cairo_reset_clip(FASTEST_AVAILABLE_PAINTER);

	g_object_unref(layout);

	m_transferNeeded = true;
}

void TQt3CairoPaintDevice::setCairoTransformations(cairo_t* cr, bool forceDisable, bool applyBaseDeviceTransform) {
	cairo_matrix_t combinedMatrix;
	cairo_matrix_t tempMatrix;

	cairo_matrix_init_identity(&combinedMatrix);
	if (!forceDisable) {
		if (applyBaseDeviceTransform) {
			// Handle specified offsets
			cairo_matrix_t offsetMatrix;
			cairo_matrix_init_identity(&offsetMatrix);
			cairo_matrix_init_translate(&offsetMatrix, m_offsetX, m_offsetY);

			cairo_matrix_multiply(&tempMatrix, &combinedMatrix, &m_deviceMatrix);
			combinedMatrix = tempMatrix;

			cairo_matrix_multiply(&tempMatrix, &combinedMatrix, &offsetMatrix);
			combinedMatrix = tempMatrix;
		}
		if (m_worldMatrixEnabled) {
			cairo_matrix_multiply(&tempMatrix, &combinedMatrix, &m_worldMatrix);
			combinedMatrix = tempMatrix;
		}
		if (m_viewportMatrixEnabled) {
			cairo_matrix_multiply(&tempMatrix, &combinedMatrix, &m_viewportMatrix);
			combinedMatrix = tempMatrix;
		}
	}

	cairo_set_matrix(cr, &combinedMatrix);
}

/*!
    \class TQt3CairoPaintDevice tdeqt4painter.h
    \brief The TQt3CairoPaintDevice class is a paint device that translates
    TQt paint events to a TQt painter.

    \ingroup graphics
    \ingroup shared

*/

/*!
    Constructs TQt3CairoPaintDevice on an existing QPainter
*/

TQt3CairoPaintDevice::TQt3CairoPaintDevice( cairo_surface_t *cairosurface, int x, int y, int width, int height, cairo_t *overridepainter )
    : TQPaintDevice( TQInternal::Picture | TQInternal::ExternalDevice )
{
	init();

	m_offsetX = x;
	m_offsetY = y;
	if (width >= 0) {
		m_width = width;
	}
	if (height >= 0) {
		m_height = height;
	}
	if (overridepainter) {
		m_overridePainter = overridepainter;
		m_surface = cairo_get_group_target(overridepainter);
	}
	else {
		m_surface = cairosurface;
	}
	m_worldMatrixStack.setAutoDelete(true);

	m_tabStops = pango_tab_array_new(0, false);
	m_tabStopArray = pango_tab_array_new(0, false);
}

/*!
    Destroys the TQt3CairoPaintDevice.
*/
TQt3CairoPaintDevice::~TQt3CairoPaintDevice()
{
	if (m_tabStops) {
		pango_tab_array_free(m_tabStops);
	}
	if (m_tabStopArray) {
		pango_tab_array_free(m_tabStopArray);
	}

	if (m_painter) {
		cairo_destroy(m_painter);
		m_painter = NULL;
	}
	if (m_devicePainter) {
		cairo_destroy(m_devicePainter);
		m_devicePainter = NULL;
	}
	if (m_intermediateSurface) {
		cairo_surface_destroy(m_intermediateSurface);
	}
#ifdef USE_TDE_SPECIFIC_LIBRARIES
	if (tdePixmapIO) {
		delete tdePixmapIO;
	}
#endif // USE_TDE_SPECIFIC_LIBRARIES
}

/*!
    Initializes all data structures
*/
void TQt3CairoPaintDevice::init() {
	m_width = -1;
	m_height = -1;
	m_offsetX = 0;
	m_offsetY = 0;

	m_intermediateSurface = NULL;
	m_painter = NULL;
	m_devicePainter = NULL;
	m_overridePainter = NULL;
	m_tabStops = NULL;
	m_tabStopArray = NULL;

	m_transferNeeded = false;

#ifdef USE_TDE_SPECIFIC_LIBRARIES
	tdePixmapIO = new KPixmapIO();
#else // USE_TDE_SPECIFIC_LIBRARIES
	tdePixmapIO = NULL;
#endif // USE_TDE_SPECIFIC_LIBRARIES
}

void TQt3CairoPaintDevice::updateSurfaceDimensions() const {
	if ((m_width < 0) || (m_height < 0)) {
		m_width = cairo_image_surface_get_width(m_surface);
		m_height = cairo_image_surface_get_height(m_surface);
	}
	if ((m_width < 1) || (m_height < 1)) {
		printf("[WARNING] Cairo surface height or width less than 0; drawing will not be possible!\n"); fflush(stdout);
	}
}

/*!
  \internal
  Implementation of the function forwarded above to the internal data struct.
*/

bool TQt3CairoPaintDevice::cmd( int c, TQPainter *pt, TQPDevCmdParam *p )
{
	unsigned int i;

	double x;
	double y;
	double x2;
	double y2;
	double width;
	double height;

	int index;
	int count;
	int lineCount;

	// Convert data types
	if (p) {
		if ((c == PdcDrawPoint) || (c == PdcMoveTo) || (c == PdcLineTo) || (c == PdcSetBrushOrigin)) {
			x = p[0].point->x();
			y = p[0].point->y();
		}
		if (c == PdcDrawLine) {
			x = p[0].point->x();
			y = p[0].point->y();
			x2 = p[1].point->x();
			y2 = p[1].point->y();
		}
		if ((c == PdcDrawRect) || (c == PdcDrawRoundRect) || (c == PdcDrawEllipse) || (c == PdcDrawArc) || (c == PdcDrawPie) || (c == PdcDrawChord)) {
			x = p[0].rect->x();
			y = p[0].rect->y();
			width = p[0].rect->width();
			height = p[0].rect->height();
		}
	}

	// Perform drawing operation
	switch ( c ) {				// exec cmd
	    case PdcNOP:
		break;
	    case PdcDrawPoint:
		if (FASTEST_AVAILABLE_PAINTER) {
			cairo_save(FASTEST_AVAILABLE_PAINTER);
			if (m_pen.style() != TQPen::NoPen) {
				cairo_move_to(FASTEST_AVAILABLE_PAINTER, x+CAIRO_PEN_PIXEL_OFFSET, y+CAIRO_PEN_PIXEL_OFFSET);
				cairo_line_to(FASTEST_AVAILABLE_PAINTER, x+CAIRO_PEN_PIXEL_OFFSET, y+CAIRO_PEN_PIXEL_OFFSET);
				updatePen(false);
				cairo_set_line_cap(FASTEST_AVAILABLE_PAINTER, CAIRO_LINE_CAP_ROUND);
				cairo_stroke(FASTEST_AVAILABLE_PAINTER);
			}
			cairo_restore(FASTEST_AVAILABLE_PAINTER);
			m_transferNeeded = true;
		}
		break;
	    case PdcMoveTo:
		if (FASTEST_AVAILABLE_PAINTER) {
			cairo_save(FASTEST_AVAILABLE_PAINTER);
			if (m_pen.style() != TQPen::NoPen) {
				cairo_move_to(FASTEST_AVAILABLE_PAINTER, x+CAIRO_PEN_PIXEL_OFFSET, y+CAIRO_PEN_PIXEL_OFFSET);
			}
			cairo_restore(FASTEST_AVAILABLE_PAINTER);
		}
		break;
	    case PdcLineTo:
		if (FASTEST_AVAILABLE_PAINTER) {
			cairo_save(FASTEST_AVAILABLE_PAINTER);
			if (m_pen.style() != TQPen::NoPen) {
				bool has_current_point = cairo_has_current_point(FASTEST_AVAILABLE_PAINTER);
				double cr_x2;
				double cr_y2;
				cairo_get_current_point(FASTEST_AVAILABLE_PAINTER, &cr_x2, &cr_y2);
				int x2 = cr_x2-CAIRO_PEN_PIXEL_OFFSET;
				int y2 = cr_y2-CAIRO_PEN_PIXEL_OFFSET;

				cairo_line_to(FASTEST_AVAILABLE_PAINTER, x+CAIRO_PEN_PIXEL_OFFSET, y+CAIRO_PEN_PIXEL_OFFSET);
				dualStrokePen();

				// WARNING
				// The Cairo anti-aliasing code fights back--HARD--when drawing 0px lines!
				// See http://projecthamster.wordpress.com/2009/12/22/getting-sharp-pixels-and-actually-something-drawn-using-cairo/ for more details.
				// This code works around the problem by overstriking the line end points with a single pixel, thereby ensuring they are properly drawn.
				if (m_pen.width() < 1) {
					cairo_save(FASTEST_AVAILABLE_PAINTER);
					cairo_set_line_cap(FASTEST_AVAILABLE_PAINTER, CAIRO_LINE_CAP_ROUND);
					cairo_set_line_join(FASTEST_AVAILABLE_PAINTER, CAIRO_LINE_JOIN_ROUND);
					cairo_set_line_width(FASTEST_AVAILABLE_PAINTER, 1);
					cairo_move_to(FASTEST_AVAILABLE_PAINTER, x+CAIRO_PEN_PIXEL_OFFSET, y+CAIRO_PEN_PIXEL_OFFSET);
					cairo_line_to(FASTEST_AVAILABLE_PAINTER, x+CAIRO_PEN_PIXEL_OFFSET, y+CAIRO_PEN_PIXEL_OFFSET);
					cairo_move_to(FASTEST_AVAILABLE_PAINTER, x2+CAIRO_PEN_PIXEL_OFFSET, y2+CAIRO_PEN_PIXEL_OFFSET);
					cairo_line_to(FASTEST_AVAILABLE_PAINTER, x2+CAIRO_PEN_PIXEL_OFFSET, y2+CAIRO_PEN_PIXEL_OFFSET);
					cairo_stroke(FASTEST_AVAILABLE_PAINTER);
					cairo_restore(FASTEST_AVAILABLE_PAINTER);
				}
			}
			cairo_restore(FASTEST_AVAILABLE_PAINTER);
		}
		break;
	    case PdcDrawLine:
		if (FASTEST_AVAILABLE_PAINTER) {
			cairo_save(FASTEST_AVAILABLE_PAINTER);
			if (m_pen.style() != TQPen::NoPen) {
				cairo_move_to(FASTEST_AVAILABLE_PAINTER, x+CAIRO_PEN_PIXEL_OFFSET, y+CAIRO_PEN_PIXEL_OFFSET);
				cairo_line_to(FASTEST_AVAILABLE_PAINTER, x2+CAIRO_PEN_PIXEL_OFFSET, y2+CAIRO_PEN_PIXEL_OFFSET);
				dualStrokePen();

				// WARNING
				// The Cairo anti-aliasing code fights back--HARD--when drawing 0px lines!
				// See http://projecthamster.wordpress.com/2009/12/22/getting-sharp-pixels-and-actually-something-drawn-using-cairo/ for more details.
				// This code works around the problem by overstriking the line end points with a single pixel, thereby ensuring they are properly drawn.
				if (m_pen.width() < 1) {
					cairo_save(FASTEST_AVAILABLE_PAINTER);
					cairo_set_line_cap(FASTEST_AVAILABLE_PAINTER, CAIRO_LINE_CAP_ROUND);
					cairo_set_line_join(FASTEST_AVAILABLE_PAINTER, CAIRO_LINE_JOIN_ROUND);
					cairo_set_line_width(FASTEST_AVAILABLE_PAINTER, 1);
					cairo_move_to(FASTEST_AVAILABLE_PAINTER, x+CAIRO_PEN_PIXEL_OFFSET, y+CAIRO_PEN_PIXEL_OFFSET);
					cairo_line_to(FASTEST_AVAILABLE_PAINTER, x+CAIRO_PEN_PIXEL_OFFSET, y+CAIRO_PEN_PIXEL_OFFSET);
					cairo_move_to(FASTEST_AVAILABLE_PAINTER, x2+CAIRO_PEN_PIXEL_OFFSET, y2+CAIRO_PEN_PIXEL_OFFSET);
					cairo_line_to(FASTEST_AVAILABLE_PAINTER, x2+CAIRO_PEN_PIXEL_OFFSET, y2+CAIRO_PEN_PIXEL_OFFSET);
					cairo_stroke(FASTEST_AVAILABLE_PAINTER);
					cairo_restore(FASTEST_AVAILABLE_PAINTER);
				}
			}
			cairo_restore(FASTEST_AVAILABLE_PAINTER);
		}
		break;
	    case PdcDrawRect:
		if (FASTEST_AVAILABLE_PAINTER) {
			cairo_save(FASTEST_AVAILABLE_PAINTER);
			int adjustment = cairoPainterRectSubtraction(&m_pen);
			if (m_brush.style() != TQBrush::NoBrush) {
				int line_width = m_pen.width();
				cairo_rectangle(FASTEST_AVAILABLE_PAINTER, x+line_width+CAIRO_BRUSH_PIXEL_OFFSET, y+line_width+CAIRO_BRUSH_PIXEL_OFFSET, width-(line_width*2)-adjustment, height-(line_width*2)-adjustment);
				dualStrokeBrush(CAIRO_FILL_RULE_EVEN_ODD);
			}
			if (m_pen.style() != TQPen::NoPen) {
				cairo_rectangle(FASTEST_AVAILABLE_PAINTER, x+CAIRO_PEN_PIXEL_OFFSET, y+CAIRO_PEN_PIXEL_OFFSET, width-adjustment, height-adjustment);
				dualStrokePen();
			}
			cairo_restore(FASTEST_AVAILABLE_PAINTER);
		}
		else {
#if defined(QT_CHECK_RANGE)
			tqWarning( "TQt3CairoPaintDevice::cmd: TQPainter::begin must be called before PdcDrawRect" );
#endif
		}
		break;
	    case PdcDrawRoundRect:
		if (FASTEST_AVAILABLE_PAINTER) {
			cairo_save(FASTEST_AVAILABLE_PAINTER);
			int adjustment = cairoPainterRectSubtraction(&m_pen);
			if (p) {
				drawRoundRect(x, y, width-adjustment, height-adjustment, p[1].ival, p[2].ival);
			}
			cairo_restore(FASTEST_AVAILABLE_PAINTER);
		}
		break;
	    case PdcDrawEllipse:
		if (FASTEST_AVAILABLE_PAINTER) {
			cairo_save(FASTEST_AVAILABLE_PAINTER);
			int adjustment = cairoPainterRectSubtraction(&m_pen);
			if (p) {
				drawEllipse(x, y, width-adjustment, height-adjustment);
			}
			cairo_restore(FASTEST_AVAILABLE_PAINTER);
		}
		break;
	    case PdcDrawArc:
		if (FASTEST_AVAILABLE_PAINTER) {
			cairo_save(FASTEST_AVAILABLE_PAINTER);
			int adjustment = cairoPainterRectSubtraction(&m_pen);
			if (p) {
				drawArc(x, y, width-adjustment, height-adjustment, p[1].ival, p[2].ival);
			}
			cairo_restore(FASTEST_AVAILABLE_PAINTER);
		}
		break;
	    case PdcDrawPie:
		if (FASTEST_AVAILABLE_PAINTER) {
			cairo_save(FASTEST_AVAILABLE_PAINTER);
			int adjustment = cairoPainterRectSubtraction(&m_pen);
			if (p) {
				drawPie(x, y, width-adjustment, height-adjustment, p[1].ival, p[2].ival);
			}
			cairo_restore(FASTEST_AVAILABLE_PAINTER);
		}
		break;
	    case PdcDrawChord:
		if (FASTEST_AVAILABLE_PAINTER) {
			cairo_save(FASTEST_AVAILABLE_PAINTER);
			int adjustment = cairoPainterRectSubtraction(&m_pen);
			if (p) {
				drawChord(x, y, width-adjustment, height-adjustment, p[1].ival, p[2].ival);
			}
			cairo_restore(FASTEST_AVAILABLE_PAINTER);
		}
		break;
	    case PdcDrawLineSegments:
		if (FASTEST_AVAILABLE_PAINTER) {
			cairo_save(FASTEST_AVAILABLE_PAINTER);
			if (p) {
				int x;
				int y;
				int x2;
				int y2;
				const TQPointArray* pointarray = p[0].ptarr;
				if (pointarray) {
					if (m_pen.style() != TQPen::NoPen) {
						for (i=0;i<pointarray->count();i=i+2) {
							pointarray->point(i+0, &x, &y);
							pointarray->point(i+1, &x2, &y2);
							cairo_move_to(FASTEST_AVAILABLE_PAINTER, x+CAIRO_PEN_PIXEL_OFFSET, y+CAIRO_PEN_PIXEL_OFFSET);
							cairo_line_to(FASTEST_AVAILABLE_PAINTER, x2+CAIRO_PEN_PIXEL_OFFSET, y2+CAIRO_PEN_PIXEL_OFFSET);
							dualStrokePen();

							// WARNING
							// The Cairo anti-aliasing code fights back--HARD--when drawing 0px lines!
							// See http://projecthamster.wordpress.com/2009/12/22/getting-sharp-pixels-and-actually-something-drawn-using-cairo/ for more details.
							// This code works around the problem by overstriking the line end points with a single pixel, thereby ensuring they are properly drawn.
							if (m_pen.width() < 1) {
								cairo_save(FASTEST_AVAILABLE_PAINTER);
								cairo_set_line_cap(FASTEST_AVAILABLE_PAINTER, CAIRO_LINE_CAP_ROUND);
								cairo_set_line_join(FASTEST_AVAILABLE_PAINTER, CAIRO_LINE_JOIN_ROUND);
								cairo_set_line_width(FASTEST_AVAILABLE_PAINTER, 1);
								cairo_move_to(FASTEST_AVAILABLE_PAINTER, x+CAIRO_PEN_PIXEL_OFFSET, y+CAIRO_PEN_PIXEL_OFFSET);
								cairo_line_to(FASTEST_AVAILABLE_PAINTER, x+CAIRO_PEN_PIXEL_OFFSET, y+CAIRO_PEN_PIXEL_OFFSET);
								cairo_move_to(FASTEST_AVAILABLE_PAINTER, x2+CAIRO_PEN_PIXEL_OFFSET, y2+CAIRO_PEN_PIXEL_OFFSET);
								cairo_line_to(FASTEST_AVAILABLE_PAINTER, x2+CAIRO_PEN_PIXEL_OFFSET, y2+CAIRO_PEN_PIXEL_OFFSET);
								cairo_stroke(FASTEST_AVAILABLE_PAINTER);
								cairo_restore(FASTEST_AVAILABLE_PAINTER);
							}
						}
					}
				}
			}
			cairo_restore(FASTEST_AVAILABLE_PAINTER);
		}
		break;
	    case PdcDrawPolyline:
		if (p) {
			drawPolygon(p[0].ptarr, false, false, false);
		}
		break;
	    case PdcDrawPolygon:
		if (p) {
			drawPolygon(p[0].ptarr, p[1].ival, true, true);
		}
		break;
	    case PdcDrawCubicBezier:
		if (FASTEST_AVAILABLE_PAINTER) {
			cairo_save(FASTEST_AVAILABLE_PAINTER);
			if (p) {
				int x;
				int y;
				int x2;
				int y2;
				int x3;
				int y3;
				int x4;
				int y4;
				const TQPointArray* pointarray = p[0].ptarr;
				if (pointarray) {
					if (m_pen.style() != TQPen::NoPen) {
						for (i=0;i<pointarray->count();i=i+4) {
							pointarray->point(i+0, &x, &y);
							pointarray->point(i+1, &x2, &y2);
							pointarray->point(i+2, &x3, &y3);
							pointarray->point(i+3, &x4, &y4);
							cairo_move_to(FASTEST_AVAILABLE_PAINTER, x+CAIRO_PEN_PIXEL_OFFSET, y+CAIRO_PEN_PIXEL_OFFSET);
							cairo_curve_to(FASTEST_AVAILABLE_PAINTER, x2+CAIRO_PEN_PIXEL_OFFSET, y2+CAIRO_PEN_PIXEL_OFFSET, x3+CAIRO_PEN_PIXEL_OFFSET, y3+CAIRO_PEN_PIXEL_OFFSET, x4+CAIRO_PEN_PIXEL_OFFSET, y4+CAIRO_PEN_PIXEL_OFFSET);
							dualStrokePen();
						}
					}
				}
			}
			cairo_restore(FASTEST_AVAILABLE_PAINTER);
		}
		break;
	    case PdcDrawText:
		if (FASTEST_AVAILABLE_PAINTER) {
			cairo_save(FASTEST_AVAILABLE_PAINTER);
			if (p) {
				TQString string = *p[1].str;
				drawText(pt, p[0].rect->x()+CAIRO_PEN_PIXEL_OFFSET, p[0].rect->y()+CAIRO_PEN_PIXEL_OFFSET, string);
			}
			cairo_restore(FASTEST_AVAILABLE_PAINTER);
		}
		break;
	    case PdcDrawTextFormatted:
		if (FASTEST_AVAILABLE_PAINTER) {
			cairo_save(FASTEST_AVAILABLE_PAINTER);
			if (p) {
				TQRect rect = *p[0].rect;
				TQString string = *p[2].str;
				drawTextInRect(pt, rect, p[1].ival, string);
			}
			cairo_restore(FASTEST_AVAILABLE_PAINTER);
		}
		break;
	    case PdcDrawText2:
		if (FASTEST_AVAILABLE_PAINTER) {
			cairo_save(FASTEST_AVAILABLE_PAINTER);
			if (p) {
				TQString string = *p[1].str;
				drawText(pt, p[0].rect->x()+CAIRO_PEN_PIXEL_OFFSET, p[0].rect->y()+CAIRO_PEN_PIXEL_OFFSET, string);
			}
			cairo_restore(FASTEST_AVAILABLE_PAINTER);
		}
		break;
	    case PdcDrawText2Formatted:
		if (FASTEST_AVAILABLE_PAINTER) {
			cairo_save(FASTEST_AVAILABLE_PAINTER);
			if (p) {
				TQRect rect = *p[0].rect;
				TQString string = *p[2].str;
				drawTextInRect(pt, rect, p[1].ival, string);
			}
			cairo_restore(FASTEST_AVAILABLE_PAINTER);
		}
		break;
	    case PdcDrawPixmap:
		if (FASTEST_AVAILABLE_PAINTER) {
			cairo_save(FASTEST_AVAILABLE_PAINTER);
			if (p) {
				TQImage sourceImage;
				const TQBitmap* bitmap = dynamic_cast<const TQBitmap*>(p[1].pixmap);
				if (bitmap) {
					TQPixmap mergedPixmap = TQPixmap(bitmap->width(), bitmap->height());
					mergedPixmap.fill(m_pen.color());
					mergedPixmap.setMask(*bitmap);
					
#ifdef USE_TDE_SPECIFIC_LIBRARIES
					sourceImage = tdePixmapIO->convertToImage(mergedPixmap);
#else // USE_TDE_SPECIFIC_LIBRARIES
					sourceImage = mergedPixmap.convertToImage();
#endif
				}
				else {
#ifdef USE_TDE_SPECIFIC_LIBRARIES
					sourceImage = tdePixmapIO->convertToImage(*p[1].pixmap);
#else // USE_TDE_SPECIFIC_LIBRARIES
					sourceImage = p[1].pixmap->convertToImage();
#endif
				}
				cairo_surface_t* sourceSurface = TQImageToCairoSurface(sourceImage);
				cairo_rectangle(FASTEST_AVAILABLE_PAINTER, p[0].rect->x(), p[0].rect->y(), p[0].rect->width(), p[0].rect->height());
				cairo_set_source_surface(FASTEST_AVAILABLE_PAINTER, sourceSurface, p[0].rect->x(), p[0].rect->y());
				cairo_fill(FASTEST_AVAILABLE_PAINTER);
				cairo_surface_destroy(sourceSurface);
			}
			cairo_restore(FASTEST_AVAILABLE_PAINTER);
			m_transferNeeded = true;
		}
		break;
	    case PdcDrawImage:
		if (FASTEST_AVAILABLE_PAINTER) {
			cairo_save(FASTEST_AVAILABLE_PAINTER);
			if (p) {
				TQRect rect = *p[0].rect;
				TQImage image = *p[1].image;
				cairo_surface_t* sourceSurface = TQImageToCairoSurface(image);
				cairo_rectangle(FASTEST_AVAILABLE_PAINTER, rect.x(), rect.y(), rect.width(), rect.height());
				cairo_set_source_surface(FASTEST_AVAILABLE_PAINTER, sourceSurface, rect.x(), rect.y());
				cairo_fill(FASTEST_AVAILABLE_PAINTER);
				cairo_surface_destroy(sourceSurface);
			}
			cairo_restore(FASTEST_AVAILABLE_PAINTER);
			m_transferNeeded = true;
		}
		break;
	    case PdcBegin:
		if (!m_painter) {
			m_transferNeeded = false;
			m_bgColor = TQColor(0,0,0);
			m_bgColorMode = TQt::TransparentMode;
			resetIntermediateSurface();
			m_painter = cairo_create(m_intermediateSurface);
			cairo_set_operator(m_painter, CAIRO_OPERATOR_OVER);
			if (m_overridePainter) {
				m_devicePainter = m_overridePainter;
			}
			else {
				m_devicePainter = cairo_create(m_surface);
			}
			cairo_get_matrix(m_devicePainter, &m_deviceMatrix);
			cairo_set_antialias(m_devicePainter, CAIRO_ANTIALIAS_NONE);
			m_pen = TQPen();
			m_brush = TQBrush();
			m_brushOrigin = TQPoint(0,0);
			m_worldMatrixEnabled = false;
			m_viewportMatrixEnabled = false;
			cairo_matrix_init_identity(&m_worldMatrix);
			cairo_matrix_init_identity(&m_viewportMatrix);
			setCairoTransformations(m_painter);
			setCairoTransformations(m_devicePainter, false, true);
			m_rop = TQPainter::CopyROP;
			m_clipRegion = TQImage();
			m_clipRegionEnabled = false;
			m_worldMatrixStack.clear();
			m_tabStopsValid = false;
			m_tabStopArrayValid = false;
		}
		break;
	    case PdcEnd:
		if (m_painter) {
			if (m_transferNeeded) {
				transferIntermediateSurface();
			}
			cairo_destroy(m_painter);
			m_painter = NULL;
		}
		if (m_devicePainter) {
			if (!m_overridePainter) {
				cairo_destroy(m_devicePainter);
			}
			else {
				cairo_set_matrix(m_devicePainter, &m_deviceMatrix);
			}
			m_devicePainter = NULL;
		}
		break;
	    case PdcSave:
		cairo_save(m_painter);
		cairo_save(m_devicePainter);
		break;
	    case PdcRestore:
		cairo_restore(m_painter);
		cairo_restore(m_devicePainter);
		break;
	    case PdcSetBkColor:
		if (p) {
			const TQColor* color = p[0].color;
			if (color) {
				m_bgColor = *color;
			}
		}
		break;
	    case PdcSetBkMode:
		if (p) {
			m_bgColorMode = (TQt::BGMode)p[0].ival;
		}
		break;
	    case PdcSetROP:
		if ((p) && (m_painter)) {
			if (m_transferNeeded) {
				transferIntermediateSurface();
			}

			TQt::RasterOp rop = (TQt::RasterOp)p[0].ival;
			m_rop = rop;
		}
		break;
	    case PdcSetBrushOrigin:
		if (p) {
			const TQPoint* point = p[0].point;
			if (point) {
				m_brushOrigin = *point;
			}
		}
		break;
	    case PdcSetFont:
		if (p) {
			const TQFont* font = p[0].font;
			if (font) {
				m_font = *font;
			}
		}
		break;
	    case PdcSetPen:
		if (p) {
			const TQPen* pen = p[0].pen;
			if (pen) {
				m_pen = *pen;
			}
		}
		break;
	    case PdcSetBrush:
		if (p) {
			const TQBrush* brush = p[0].brush;
			if (brush) {
				m_brush = *brush;
			}
		}
		break;
	    case PdcSetTabStops:
		if (p) {
			int tabspacing = p[0].ival;
			if (tabspacing > 0) {
				// Set up a repeating tab stop pattern
				pango_tab_array_resize(m_tabStops, 2);
				pango_tab_array_set_tab(m_tabStops, 0, PANGO_TAB_LEFT, (tabspacing*1)*PANGO_SCALE);
				pango_tab_array_set_tab(m_tabStops, 1, PANGO_TAB_LEFT, (tabspacing*2)*PANGO_SCALE);
				m_tabStopsValid = true;
			}
			else {
				pango_tab_array_resize(m_tabStops, 0);
				m_tabStopsValid = false;
			}
		}
		break;
	    case PdcSetTabArray:
		if (p) {
			int tabcount = p[0].ival;
			if (tabcount > 0) {
				int* tabarray = p[1].ivec;
				pango_tab_array_resize(m_tabStopArray, tabcount);
				int i;
				for (i=0;i<tabcount;i++) {
					pango_tab_array_set_tab(m_tabStopArray, i, PANGO_TAB_LEFT, tabarray[i]*PANGO_SCALE);
				}
				m_tabStopArrayValid = true;
			}
			else {
				pango_tab_array_resize(m_tabStopArray, 0);
				m_tabStopArrayValid = false;
			}
		}
		break;
	    case PdcSetVXform:
		if ((p) && (m_painter)) {
			m_viewportMatrixEnabled = p[0].ival;
			setCairoTransformations(m_painter);
			setCairoTransformations(m_devicePainter, false, true);
		}
		break;
	    case PdcSetWindow:
		if ((p) && (m_painter) && (pt)) {
			TQRect viewportRect = pt->viewport();
			int wx = p[0].rect->x();
			int wy = p[0].rect->y();
			int ww = p[0].rect->width();
			int wh = p[0].rect->height();
			int vx = viewportRect.x();
			int vy = viewportRect.y();
			int vw = viewportRect.width();
			int vh = viewportRect.height();
			double scaleW = (double)vw/(double)ww;
			double scaleH = (double)vh/(double)wh;
			cairo_matrix_init(&m_viewportMatrix, scaleW, 0, 0, scaleH, vx - wx*scaleW, vy - wy*scaleH);
			setCairoTransformations(m_painter);
			setCairoTransformations(m_devicePainter, false, true);
		}
		break;
	    case PdcSetViewport:
		if ((p) && (m_painter) && (pt)) {
			TQRect windowRect = pt->window();
			int wx = windowRect.x();
			int wy = windowRect.y();
			int ww = windowRect.width();
			int wh = windowRect.height();
			int vx = p[0].rect->x();
			int vy = p[0].rect->y();
			int vw = p[0].rect->width();
			int vh = p[0].rect->height();
			double scaleW = (double)vw/(double)ww;
			double scaleH = (double)vh/(double)wh;
			cairo_matrix_init(&m_viewportMatrix, scaleW, 0, 0, scaleH, vx - wx*scaleW, vy - wy*scaleH);
			setCairoTransformations(m_painter);
			setCairoTransformations(m_devicePainter, false, true);
		}
		break;
	    case PdcSetWXform:
		if ((p) && (m_painter)) {
			m_worldMatrixEnabled = p[0].ival;
			setCairoTransformations(m_painter);
			setCairoTransformations(m_devicePainter, false, true);
		}
		break;
	    case PdcSetWMatrix:
		if ((p) && (m_painter)) {
			const TQWMatrix* tqt3matrix = p[0].matrix;
			if (tqt3matrix) {
				if (p[1].ival) {
					// Combine
					cairo_matrix_t new_matrix;
					cairo_matrix_t original_matrix = m_worldMatrix;
					cairo_matrix_init(&new_matrix, tqt3matrix->m11(), tqt3matrix->m12(), tqt3matrix->m21(), tqt3matrix->m22(), tqt3matrix->dx(), tqt3matrix->dy());
					cairo_matrix_multiply(&m_worldMatrix, &original_matrix, &new_matrix);
				}
				else {
					// Replace
					cairo_matrix_init(&m_worldMatrix, tqt3matrix->m11(), tqt3matrix->m12(), tqt3matrix->m21(), tqt3matrix->m22(), tqt3matrix->dx(), tqt3matrix->dy());
				}
				setCairoTransformations(m_painter);
				setCairoTransformations(m_devicePainter, false, true);
			}
		}
		break;
	    case PdcSaveWMatrix:
		if (p) {
			const TQWMatrix* tqt3matrix = p[0].matrix;
			m_worldMatrixStack.push(new TQWMatrix(*tqt3matrix));
		}
		break;
	    case PdcRestoreWMatrix:
		if (p) {
			if (!m_worldMatrixStack.isEmpty()) {
				TQWMatrix* matrix = m_worldMatrixStack.pop();

				// Set world matrix
				TQPDevCmdParam param[2];
				int command = PdcSetWMatrix;
				param[0].matrix = matrix;
				param[1].ival = 0;
				cmd(command, pt, param);

				delete matrix;
			}
		}
		break;
	    case PdcSetClip:
		if ((p) && (m_painter)) {
			if (m_transferNeeded) {
				transferIntermediateSurface();
			}
			m_clipRegionEnabled = p[0].ival;
		}
		break;
	    case PdcSetClipRegion:
		if ((p) && (m_painter)) {
			if (m_transferNeeded) {
				transferIntermediateSurface();
			}
			// SLOW
			TQRect tqt3br = p[0].rgn->boundingRect();
			if (!tqt3br.isNull()) {
				m_clipRegion = TQImage(tqt3br.x()+tqt3br.width(), tqt3br.y()+tqt3br.height(), 32);
				int x;
				int y;
				for (x=0; x<m_clipRegion.width(); x++) {
					for (y=0; y<m_clipRegion.height(); y++) {
						TQPoint point(x,y);
						m_clipRegion.setPixel(x, y, (p[0].rgn->contains(point))?0xffffffff:0x00000000);
					}
				}
				m_clipRegionEnabled = true;
			}
			else {
				m_clipRegion = TQImage();
				m_clipRegionEnabled = false;
			}
		}
		break;
	    case PdcFlush:
	    case PdcFlushRegion:
		if (m_transferNeeded) {
			transferIntermediateSurface();
		}
	    default:
#if defined(QT_CHECK_RANGE)
		tqWarning( "TQt3CairoPaintDevice::cmd: Invalid command %d", c );
#endif
	}

	return true;
}


/*!
    Internal implementation of the virtual TQPaintDevice::metric()
    function.

    Use the TQPaintDeviceMetrics class instead.

    A picture has the following hard-coded values: dpi=72,
    numcolors=16777216 and depth=24.

    \a m is the metric to get.
*/

int TQt3CairoPaintDevice::metric( int m ) const
{
	int val;

	if (m_surface) {
		double x_pixels_per_inch;
		double y_pixels_per_inch;
		cairo_format_t format;
		switch ( m ) {
			// ### hard coded dpi and color depth values !
			case TQPaintDeviceMetrics::PdmWidth:
				updateSurfaceDimensions();
				val = m_width;
				break;
			case TQPaintDeviceMetrics::PdmHeight:
				updateSurfaceDimensions();
				val = m_height;
				break;
			case TQPaintDeviceMetrics::PdmWidthMM:
				updateSurfaceDimensions();
				cairo_surface_get_fallback_resolution(m_surface, &x_pixels_per_inch, &y_pixels_per_inch);
				val = ((m_width/x_pixels_per_inch)*25.4);
				break;
			case TQPaintDeviceMetrics::PdmHeightMM:
				updateSurfaceDimensions();
				cairo_surface_get_fallback_resolution(m_surface, &x_pixels_per_inch, &y_pixels_per_inch);
				val = ((m_height/y_pixels_per_inch)*25.4);
				break;
			case TQPaintDeviceMetrics::PdmDpiX:
				cairo_surface_get_fallback_resolution(m_surface, &x_pixels_per_inch, &y_pixels_per_inch);
				val = x_pixels_per_inch;
				break;
			case TQPaintDeviceMetrics::PdmPhysicalDpiX:
				cairo_surface_get_fallback_resolution(m_surface, &x_pixels_per_inch, &y_pixels_per_inch);
				val = x_pixels_per_inch;
				break;
			case TQPaintDeviceMetrics::PdmDpiY:
				cairo_surface_get_fallback_resolution(m_surface, &x_pixels_per_inch, &y_pixels_per_inch);
				val = y_pixels_per_inch;
				break;
			case TQPaintDeviceMetrics::PdmPhysicalDpiY:
				cairo_surface_get_fallback_resolution(m_surface, &x_pixels_per_inch, &y_pixels_per_inch);
				val = y_pixels_per_inch;
				break;
			case TQPaintDeviceMetrics::PdmNumColors:
				format = cairo_image_surface_get_format(m_surface);
				if (format == CAIRO_FORMAT_ARGB32) {
					val = INT_MAX;
				}
				else if (format == CAIRO_FORMAT_RGB24) {
					val = 16777216;
				}
				else if (format == CAIRO_FORMAT_RGB16_565) {
					val = 65536;
				}
				else {
					val = 65536;
				}
				break;
			case TQPaintDeviceMetrics::PdmDepth:
				format = cairo_image_surface_get_format(m_surface);
				if (format == CAIRO_FORMAT_ARGB32) {
					val = 32;
				}
				else if (format == CAIRO_FORMAT_RGB24) {
					val = 24;
				}
				else if (format == CAIRO_FORMAT_RGB16_565) {
					val = 16;
				}
				else {
					val = 16;
				}
				break;
			default:
				val = 0;
#if defined(QT_CHECK_RANGE)
				tqWarning( "TQt3CairoPaintDevice::metric: Invalid metric command" );
#endif
		}
	}
	else {
		val = 0;
#if defined(QT_CHECK_RANGE)
		tqWarning( "TQt3CairoPaintDevice::metric: No Cairo surface available" );
#endif
	}
	return val;
}

