⛏️ index : haiku.git

// Painter.cpp

#include <stdio.h>
#include <string.h>

#include <Bitmap.h>
#include <GraphicsDefs.h>
#include <Region.h>

#include <agg_bezier_arc.h>
#include <agg_bounding_rect.h>
#include <agg_conv_curve.h>
#include <agg_conv_stroke.h>
#include <agg_ellipse.h>
#include <agg_path_storage.h>
#include <agg_rounded_rect.h>
#include <agg_span_image_filter_rgba32.h>
#include <agg_span_interpolator_linear.h>

#include "LayerData.h"

#include "AGGTextRenderer.h"
#include "DrawingMode.h"
#include "DrawingModeFactory.h"
#include "FontManager.h"
#include "PatternHandler.h"
#include "RenderingBuffer.h"
#include "ShapeConverter.h"
#include "ServerBitmap.h"
#include "ServerFont.h"

#include "Painter.h"

int
roundf(float v)
{
	if (v >= 0.0)
		return (int)floorf(v + 0.5);
	else
		return (int)floorf(v - 0.5);
}

// constructor
Painter::Painter()
	: fBuffer(NULL),
	  fPixelFormat(NULL),
	  fBaseRenderer(NULL),
	  fOutlineRenderer(NULL),
	  fOutlineRasterizer(NULL),
	  fScanline(NULL),
	  fRasterizer(NULL),
	  fRenderer(NULL),
	  fFontRendererSolid(NULL),
	  fFontRendererBin(NULL),
	  fLineProfile(),
	  fSubpixelPrecise(false),
	  fScale(1.0),
	  fPenSize(1.0),
	  fOrigin(0.0, 0.0),
	  fClippingRegion(NULL),
	  fDrawingMode(B_OP_COPY),
	  fAlphaSrcMode(B_PIXEL_ALPHA),
//	  fAlphaSrcMode(B_CONSTANT_ALPHA),
	  fAlphaFncMode(B_ALPHA_OVERLAY),
	  fPenLocation(0.0, 0.0),
	  fPatternHandler(new PatternHandler()),
	  fTextRenderer(new AGGTextRenderer()),
	  fLastFamilyAndStyle(0)
{
	if (fontserver)
		fFont = *fontserver->GetSystemPlain();
	
	_UpdateFont();
	_UpdateLineWidth();
}

// destructor
Painter::~Painter()
{
	_MakeEmpty();

	delete fClippingRegion;
	delete fPatternHandler;
	delete fTextRenderer;
}

// #pragma mark -

// AttachToBuffer
void
Painter::AttachToBuffer(RenderingBuffer* buffer)
{
	if (buffer && buffer->InitCheck() >= B_OK) {
		// clean up previous stuff
		_MakeEmpty();

		fBuffer = new agg::rendering_buffer();
		fBuffer->attach((uint8*)buffer->Bits(),
						buffer->Width(),
						buffer->Height(),
						buffer->BytesPerRow());

		fPixelFormat = new pixfmt(*fBuffer, fPatternHandler);
		fPixelFormat->set_drawing_mode(DrawingModeFactory::DrawingModeFor(fDrawingMode,
																		  fAlphaSrcMode,
																		  fAlphaFncMode,
																		  false));

		fBaseRenderer = new renderer_base(*fPixelFormat);

		// These are the AGG renderes and rasterizes which
		// will be used for stroking paths
rgb_color color = fPatternHandler->HighColor().GetColor32();
#if ALIASED_DRAWING
		fOutlineRenderer = new outline_renderer_type(*fBaseRenderer);
		fOutlineRasterizer = new outline_rasterizer_type(*fOutlineRenderer);
#else
		fOutlineRenderer = new outline_renderer_type(*fBaseRenderer, fLineProfile);
		fOutlineRasterizer = new outline_rasterizer_type(*fOutlineRenderer);

		// attach our line profile to the renderer, it keeps a pointer
		fOutlineRenderer->profile(fLineProfile);
#endif
		// the renderer used for filling paths
		fRenderer = new renderer_type(*fBaseRenderer);
		fRasterizer = new rasterizer_type();
		fScanline = new scanline_type();

#if ALIASED_DRAWING
		fRasterizer->gamma(agg::gamma_threshold(0.5));
#endif

		// These are renderers needed for drawing text
		fFontRendererSolid = new font_renderer_solid_type(*fBaseRenderer);
		fFontRendererBin = new font_renderer_bin_type(*fBaseRenderer);

		_SetRendererColor(fPatternHandler->HighColor().GetColor32());
		_RebuildClipping();
	}
}

// DetachFromBuffer
void
Painter::DetachFromBuffer()
{
	_MakeEmpty();
}

// SetDrawData
void
Painter::SetDrawData(const DrawData* data)
{
	// for now...
	SetHighColor(data->highcolor.GetColor32());
	SetLowColor(data->lowcolor.GetColor32());
	SetScale(data->scale);
	SetPenSize(data->pensize);
//	fOrigin = data->coordOrigin;
	SetDrawingMode(data->draw_mode);
	SetBlendingMode(data->alphaSrcMode, data->alphaFncMode);
	SetPenLocation(data->penlocation);
	SetFont(data->font);
//	if (data->clipReg) {
//		ConstrainClipping(*data->clipReg);
//	}
	fPatternHandler->SetPattern(data->patt);
}

// #pragma mark -

// ConstrainClipping
void
Painter::ConstrainClipping(const BRegion& region)
{
	// The idea is that if the clipping region was
	// never constrained, there is *no* clipping.
	// This is of course different from having
	// an *empty* clipping region.
	if (!fClippingRegion)
		fClippingRegion = new BRegion(region);
	else
		*fClippingRegion = region;
	_RebuildClipping();
}

// SetHighColor
void
Painter::SetHighColor(const rgb_color& color)
{
	fPatternHandler->SetHighColor(color);
}

// SetLowColor
void
Painter::SetLowColor(const rgb_color& color)
{
	fPatternHandler->SetLowColor(color);;
}

// SetScale
void
Painter::SetScale(float scale)
{
	if (fScale != scale) {
		fScale = scale;
		_RebuildClipping();
		_UpdateLineWidth();
	}
}

// SetPenSize
void
Painter::SetPenSize(float size)
{
	if (fPenSize != size) {
		fPenSize = size;
		_UpdateLineWidth();
	}
}

// SetOrigin
void
Painter::SetOrigin(const BPoint& origin)
{
	// NOTE: The BeBook says that the coordinate system
	// of a view cannot be changed during an update, because
	// it would mess up the clipping, and this is indeed
	// what would happen in this implementation as well.
	// I don't know yet what actually happens if you still
	// try to call SetOrigin() from within BView::Draw()
	fOrigin = origin;
	_RebuildClipping();
}

// SetDrawingMode
void
Painter::SetDrawingMode(drawing_mode mode)
{
	if (fDrawingMode != mode) {
		fDrawingMode = mode;
		if (fPixelFormat) {
			fPixelFormat->set_drawing_mode(DrawingModeFactory::DrawingModeFor(fDrawingMode,
																			  fAlphaSrcMode,
																			  fAlphaFncMode));
		}
	}
}

// SetBlendingMode
void
Painter::SetBlendingMode(source_alpha alphaSrcMode, alpha_function alphaFncMode)
{
	if (fAlphaSrcMode != alphaSrcMode || fAlphaFncMode != alphaFncMode) {
		fAlphaSrcMode = alphaSrcMode;
		fAlphaFncMode = alphaFncMode;
		if (fDrawingMode == B_OP_ALPHA && fPixelFormat) {
			fPixelFormat->set_drawing_mode(DrawingModeFactory::DrawingModeFor(fDrawingMode,
																			  fAlphaSrcMode,
																			  fAlphaFncMode));
		}
	}
}

// SetPenLocation
void
Painter::SetPenLocation(const BPoint& location)
{
	fPenLocation = location;
}

// SetFont
void
Painter::SetFont(const BFont& font)
{
	//fFont.SetFamilyAndStyle(font.GetFamily(), font.GetStyle());
	fFont.SetSpacing(font.Spacing());
	fFont.SetShear(font.Shear());
	fFont.SetRotation(font.Rotation());
	fFont.SetSize(font.Size());
	
	_UpdateFont();
}

// SetFont
void
Painter::SetFont(const ServerFont& font)
{
	fFont = font;
	_UpdateFont();
}

// #pragma mark -

// StrokeLine
BRect
Painter::StrokeLine(BPoint a, BPoint b, const pattern& p)
{
	_Transform(&a);
	_Transform(&b);

	BRect touched(a, b);

	// first, try an optimized version
	float penSize = _Transform(fPenSize);
	if (penSize == 1.0 &&
		(fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER)) {
		pattern pat = *fPatternHandler->GetR5Pattern();
		if (pat == B_SOLID_HIGH &&
			StraightLine(a, b, fPatternHandler->HighColor().GetColor32())) {
			SetPenLocation(b);
			return _Clipped(touched);
		} else if (pat == B_SOLID_LOW &&
			StraightLine(a, b, fPatternHandler->LowColor().GetColor32())) {
			SetPenLocation(b);
			return _Clipped(touched);
		}
	}

	agg::path_storage path;
	path.move_to(a.x, a.y);
	path.line_to(b.x, b.y);

	touched = _StrokePath(path, p);

	SetPenLocation(b);

	return _Clipped(touched);
}

// StrokeLine
BRect
Painter::StrokeLine(BPoint b, const pattern& p)
{
	// TODO: move this function elsewhere
	return StrokeLine(fPenLocation, b);
}

// StraightLine
bool
Painter::StraightLine(BPoint a, BPoint b, const rgb_color& c) const
{
	if (fBuffer) {
		if (a.x == b.x) {
			// vertical
			uint8* dst = fBuffer->row(0);
			uint32 bpr = fBuffer->stride();
			int32 x = (int32)a.x;
			dst += x * 4;
			int32 y1 = (int32)min_c(a.y, b.y);
			int32 y2 = (int32)max_c(a.y, b.y);
			// draw a line, iterate over clipping boxes
			fBaseRenderer->first_clip_box();
			do {
				if (fBaseRenderer->xmin() <= x &&
					fBaseRenderer->xmax() >= x) {
					int32 i = max_c(fBaseRenderer->ymin(), y1);
					int32 end = min_c(fBaseRenderer->ymax(), y2);
					uint8* handle = dst + i * bpr;
					for (; i <= end; i++) {
						handle[0] = c.blue;
						handle[1] = c.green;
						handle[2] = c.red;
						handle += bpr;
					}
				}
			} while (fBaseRenderer->next_clip_box());
	
			return true;
	
		} else if (a.y == b.y) {
			// horizontal
			uint8* dst = fBuffer->row(0);
			uint32 bpr = fBuffer->stride();
			int32 y = (int32)a.y;
			dst += y * bpr;
			int32 x1 = (int32)min_c(a.x, b.x);
			int32 x2 = (int32)max_c(a.x, b.x);
			// draw a line, iterate over clipping boxes
			fBaseRenderer->first_clip_box();
			do {
				if (fBaseRenderer->ymin() <= y &&
					fBaseRenderer->ymax() >= y) {
					int32 i = max_c(fBaseRenderer->xmin(), x1);
					int32 end = min_c(fBaseRenderer->xmax(), x2);
					uint8* handle = dst + i * 4;
					for (; i <= end; i++) {
						handle[0] = c.blue;
						handle[1] = c.green;
						handle[2] = c.red;
						handle += 4;
					}
				}
			} while (fBaseRenderer->next_clip_box());
	
			return true;
		}
	}
	return false;
}

// #pragma mark -

// StrokeTriangle
void
Painter::StrokeTriangle(BPoint pt1, BPoint pt2, BPoint pt3, const pattern& p) const
{
	_DrawTriangle(pt1, pt2, pt3, p, false);
}

// FillTriangle
void
Painter::FillTriangle(BPoint pt1, BPoint pt2, BPoint pt3, const pattern& p) const
{
	_DrawTriangle(pt1, pt2, pt3, p, true);
}

// StrokePolygon
void
Painter::StrokePolygon(const BPoint* ptArray, int32 numPts,
					   bool closed, const pattern& p) const
{
	_DrawPolygon(ptArray, numPts, closed, p, false);
}

// FillPolygon
void
Painter::FillPolygon(const BPoint* ptArray, int32 numPts,
					   bool closed, const pattern& p) const
{
	_DrawPolygon(ptArray, numPts, closed, p, true);
}

// StrokeBezier
void
Painter::StrokeBezier(const BPoint* controlPoints, const pattern& p) const
{
	agg::path_storage curve;

	BPoint p1(controlPoints[0]);
	BPoint p2(controlPoints[1]);
	BPoint p3(controlPoints[2]);
	BPoint p4(controlPoints[3]);
	_Transform(&p1);
	_Transform(&p2);
	_Transform(&p3);
	_Transform(&p4);

	curve.move_to(p1.x, p1.y);
	curve.curve4(p1.x, p1.y,
				 p2.x, p2.y,
				 p3.x, p3.y);


	agg::conv_curve<agg::path_storage> path(curve);

	_StrokePath(path, p);
}

// FillBezier
void
Painter::FillBezier(const BPoint* controlPoints, const pattern& p) const
{
	agg::path_storage curve;

	BPoint p1(controlPoints[0]);
	BPoint p2(controlPoints[1]);
	BPoint p3(controlPoints[2]);
	BPoint p4(controlPoints[3]);
	_Transform(&p1);
	_Transform(&p2);
	_Transform(&p3);
	_Transform(&p4);

	curve.move_to(p1.x, p1.y);
	curve.curve4(p1.x, p1.y,
				 p2.x, p2.y,
				 p3.x, p3.y);
	curve.close_polygon();

	agg::conv_curve<agg::path_storage> path(curve);

	_FillPath(path, p);
}

// StrokeShape
void
Painter::StrokeShape(/*const */BShape* shape, const pattern& p) const
{
	_DrawShape(shape, p, false);
}

// FillShape
void
Painter::FillShape(/*const */BShape* shape, const pattern& p) const
{
	_DrawShape(shape, p, true);
}

// StrokeRect
BRect
Painter::StrokeRect(const BRect& r, const pattern& p) const
{
	BPoint a(r.left, r.top);
	BPoint b(r.right, r.bottom);
	_Transform(&a);
	_Transform(&b);

	// first, try an optimized version
	float penSize = _Transform(fPenSize);
	if (penSize == 1.0 &&
		(fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER)) {
// TODO: fix me
//		pattern p = *fPatternHandler->GetR5Pattern();
		if (p == B_SOLID_HIGH) {
			BRect rect(a, b);
			StrokeRect(rect,
					   fPatternHandler->HighColor().GetColor32());
			return _Clipped(rect);
		} else if (p == B_SOLID_LOW) {
			BRect rect(a, b);
			StrokeRect(rect,
					   fPatternHandler->LowColor().GetColor32());
			return _Clipped(rect);
		}
	}

	agg::path_storage path;
	path.move_to(a.x, a.y);
	path.line_to(b.x, a.y);
	path.line_to(b.x, b.y);
	path.line_to(a.x, b.y);
	path.close_polygon();

	return _StrokePath(path, p);
}

// StrokeRect
void
Painter::StrokeRect(const BRect& r, const rgb_color& c) const
{
	StraightLine(BPoint(r.left, r.top),
				 BPoint(r.right - 1, r.top), c);
	StraightLine(BPoint(r.right, r.top),
				 BPoint(r.right, r.bottom - 1), c);
	StraightLine(BPoint(r.right, r.bottom),
				 BPoint(r.left + 1, r.bottom), c);
	StraightLine(BPoint(r.left, r.bottom),
				 BPoint(r.left, r.top + 1), c);
}

// FillRect
BRect
Painter::FillRect(const BRect& r, const pattern& p) const
{
	BPoint a(r.left, r.top);
	BPoint b(r.right, r.bottom);
	_Transform(&a, false);
	_Transform(&b, false);

	// first, try an optimized version
	if (fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER) {
		pattern pat = *fPatternHandler->GetR5Pattern();
		if (pat == B_SOLID_HIGH) {
			BRect rect(a, b);
			FillRect(rect, fPatternHandler->HighColor().GetColor32());
			return _Clipped(rect);
		} else if (pat == B_SOLID_LOW) {
			BRect rect(a, b);
			FillRect(rect, fPatternHandler->LowColor().GetColor32());
			return _Clipped(rect);
		}
	}

	// account for stricter interpretation of coordinates in AGG
	// the rectangle ranges from the top-left (.0, .0)
	// to the bottom-right (.9999, .9999) corner of pixels
	b.x += 1.0;
	b.y += 1.0;

	agg::path_storage path;
	path.move_to(a.x, a.y);
	path.line_to(b.x, a.y);
	path.line_to(b.x, b.y);
	path.line_to(a.x, b.y);
	path.close_polygon();

	return _FillPath(path, p);
}

// FillRect
void
Painter::FillRect(const BRect& r, const rgb_color& c) const
{
	if (fBuffer) {
		uint8* dst = fBuffer->row(0);
		uint32 bpr = fBuffer->stride();
		int32 left = (int32)r.left;
		int32 top = (int32)r.top;
		int32 right = (int32)r.right;
		int32 bottom = (int32)r.bottom;
		// fill rects, iterate over clipping boxes
		fBaseRenderer->first_clip_box();
		do {
			int32 x1 = max_c(fBaseRenderer->xmin(), left);
			int32 x2 = min_c(fBaseRenderer->xmax(), right);
			if (x1 <= x2) {
				int32 y1 = max_c(fBaseRenderer->ymin(), top);
				int32 y2 = min_c(fBaseRenderer->ymax(), bottom);
				uint8* offset = dst + x1 * 4;
				for (; y1 <= y2; y1++) {
					uint8* handle = offset + y1 * bpr;
					for (int32 x = x1; x <= x2; x++) {
						handle[0] = c.blue;
						handle[1] = c.green;
						handle[2] = c.red;
						handle += 4;
					}
				}
			}
		} while (fBaseRenderer->next_clip_box());
	}
}

// StrokeRoundRect
void
Painter::StrokeRoundRect(const BRect& r, float xRadius, float yRadius,
						 const pattern& p) const
{
	BPoint lt(r.left, r.top);
	BPoint rb(r.right, r.bottom);
	_Transform(&lt);
	_Transform(&rb);

	_Transform(&xRadius);
	_Transform(&yRadius);

	agg::rounded_rect rect;
	rect.rect(lt.x, lt.y, rb.x, rb.y);
	rect.radius(xRadius, yRadius);

	_StrokePath(rect, p);
}

// FillRoundRect
void
Painter::FillRoundRect(const BRect& r, float xRadius, float yRadius,
					   const pattern& p) const
{
	BPoint lt(r.left, r.top);
	BPoint rb(r.right, r.bottom);
	_Transform(&lt, false);
	_Transform(&rb, false);

	// account for stricter interpretation of coordinates in AGG
	// the rectangle ranges from the top-left (.0, .0)
	// to the bottom-right (.9999, .9999) corner of pixels
	rb.x += 1.0;
	rb.y += 1.0;

	_Transform(&xRadius);
	_Transform(&yRadius);

	agg::rounded_rect rect;
	rect.rect(lt.x, lt.y, rb.x, rb.y);
	rect.radius(xRadius, yRadius);

	_FillPath(rect, p);
}
									
// StrokeEllipse
void
Painter::StrokeEllipse(BPoint center, float xRadius, float yRadius,
					   const pattern& p) const
{
	_DrawEllipse(center, xRadius, yRadius, p, false);
}

// FillEllipse
void
Painter::FillEllipse(BPoint center, float xRadius, float yRadius,
					 const pattern& p) const
{
	_DrawEllipse(center, xRadius, yRadius, p, true);
}

// StrokeArc
void
Painter::StrokeArc(BPoint center, float xRadius, float yRadius,
				   float angle, float span, const pattern& p) const
{
	_Transform(&center);
	_Transform(&xRadius);
	_Transform(&yRadius);

	double angleRad = (angle * PI) / 180.0;
	double spanRad = (span * PI) / 180.0;
	agg::bezier_arc arc(center.x, center.y, xRadius, yRadius,
						-angleRad, -spanRad);

	agg::conv_curve<agg::bezier_arc> path(arc);

	_StrokePath(path, p);
}

// FillArc
void
Painter::FillArc(BPoint center, float xRadius, float yRadius,
				 float angle, float span, const pattern& p) const
{
	_Transform(&center);
	_Transform(&xRadius);
	_Transform(&yRadius);

	double angleRad = (angle * PI) / 180.0;
	double spanRad = (span * PI) / 180.0;
	agg::bezier_arc arc(center.x, center.y, xRadius, yRadius,
						-angleRad, -spanRad);

	agg::conv_curve<agg::bezier_arc> segmentedArc(arc);

	agg::path_storage path;

	// build a new path by starting at the center point,
	// then traversing the arc, then going back to the center
	path.move_to(center.x, center.y);

	segmentedArc.rewind(0);
	double x;
	double y;
	unsigned cmd = segmentedArc.vertex(&x, &y);
	while (!agg::is_stop(cmd)) {
		path.line_to(x, y);
		cmd = segmentedArc.vertex(&x, &y);
	}

	path.close_polygon();

	_FillPath(path, p);
}

// #pragma mark -

// DrawChar
BRect
Painter::DrawChar(char aChar)
{
	// TODO: to be moved elsewhere
	return DrawChar(aChar, fPenLocation);
}

// DrawChar
BRect
Painter::DrawChar(char aChar, BPoint baseLine)
{
	// TODO: to be moved elsewhere
	char wrapper[2];
	wrapper[0] = aChar;
	wrapper[1] = 0;
	return DrawString(wrapper, 1, baseLine);
}

// DrawString
BRect
Painter::DrawString(const char* utf8String, uint32 length,
					const escapement_delta* delta)
{
	// TODO: to be moved elsewhere
	return DrawString(utf8String, length, fPenLocation, delta);
}

// DrawString
BRect
Painter::DrawString(const char* utf8String, uint32 length,
					BPoint baseLine, const escapement_delta* delta)
{
	BRect bounds(0.0, 0.0, -1.0, -1.0);
	fPatternHandler->SetPattern(B_SOLID_HIGH);

	if (fBuffer) {

		Transformable transform;
		transform.ShearBy(B_ORIGIN, (90.0 - fFont.Shear()) * PI / 180.0, 0.0);
		transform.RotateBy(B_ORIGIN, -fFont.Rotation() * PI / 180.0);
		transform.TranslateBy(baseLine);
		transform.ScaleBy(B_ORIGIN, fScale, fScale);
		transform.TranslateBy(fOrigin);

		BRect clippingFrame;
		if (fClippingRegion)
			clippingFrame = _Transform(fClippingRegion->Frame());

		bounds = fTextRenderer->RenderString(utf8String,
											 length,
											 fFontRendererSolid,
											 fFontRendererBin,
											 transform,
											 clippingFrame,
											 false,
											 &fPenLocation);
		// pen location is not transformed in quite the same way,
		// or transformations would add up
		transform.Reset();
		transform.RotateBy(B_ORIGIN, -fFont.Rotation());
		transform.TranslateBy(baseLine);
		transform.Transform(&fPenLocation);
	}
	return _Clipped(bounds);
}

// DrawString
BRect
Painter::DrawString(const char* utf8String, const escapement_delta* delta)
{
	// TODO: to be moved elsewhere
	return DrawString(utf8String, strlen(utf8String), fPenLocation, delta);
}

// DrawString
BRect
Painter::DrawString(const char* utf8String, BPoint baseLine,
					const escapement_delta* delta)
{
	// TODO: to be moved elsewhere
	return DrawString(utf8String, strlen(utf8String), baseLine, delta);
}

// #pragma mark -

// DrawBitmap
void
Painter::DrawBitmap(const BBitmap* bitmap,
					BRect bitmapRect, BRect viewRect) const
{
	if (bitmap && bitmap->IsValid()) {
		// the native bitmap coordinate system
		// (can have left top corner offset)
		BRect actualBitmapRect(bitmap->Bounds());

		agg::rendering_buffer srcBuffer;
		srcBuffer.attach((uint8*)bitmap->Bits(),
						 (uint32)actualBitmapRect.IntegerWidth() + 1,
						 (uint32)actualBitmapRect.IntegerHeight() + 1,
						 bitmap->BytesPerRow());

		_DrawBitmap(srcBuffer, bitmap->ColorSpace(), actualBitmapRect, bitmapRect, viewRect);
	}
}

// DrawBitmap
void
Painter::DrawBitmap(const ServerBitmap* bitmap,
					BRect bitmapRect, BRect viewRect) const
{
	if (bitmap && bitmap->InitCheck()) {
		// the native bitmap coordinate system
		BRect actualBitmapRect(bitmap->Bounds());

		agg::rendering_buffer srcBuffer;
		srcBuffer.attach(bitmap->Bits(),
						 bitmap->Width(),
						 bitmap->Height(),
						 bitmap->BytesPerRow());

		_DrawBitmap(srcBuffer, bitmap->ColorSpace(), actualBitmapRect, bitmapRect, viewRect);
	}
}

// #pragma mark -

// FillRegion
void
Painter::FillRegion(const BRegion* region, const pattern& p = B_SOLID_HIGH) const
{
	BRegion copy(*region);
	int32 count = copy.CountRects();
	for (int32 i = 0; i < count; i++) {
		FillRect(copy.RectAt(i), p);
	}
}

// InvertRect
void
Painter::InvertRect(const BRect& r) const
{
	BRegion region(r);
	if (fClippingRegion) {
		region.IntersectWith(fClippingRegion);
	}
	// implementation only for B_RGB32 at the moment
	int32 count = region.CountRects();
	for (int32 i = 0; i < count; i++) {
		BRect r = region.RectAt(i);
		_Transform(&r);
		_InvertRect32(r);
	}
}

// BoundingBox
BRect
Painter::BoundingBox(const char* utf8String, uint32 length,
					 const BPoint& baseLine) const
{
	Transformable transform;
	transform.TranslateBy(baseLine);

	BRect dummy;
	return fTextRenderer->RenderString(utf8String,
									length,
									fFontRendererSolid,
									fFontRendererBin,
									transform, dummy, true);
}

// #pragma mark -

// _MakeEmpty
void
Painter::_MakeEmpty()
{
	delete fBuffer;
	fBuffer = NULL;

	delete fPixelFormat;
	fPixelFormat = NULL;

	delete fBaseRenderer;
	fBaseRenderer = NULL;

	delete fOutlineRenderer;
	fOutlineRenderer = NULL;

	delete fOutlineRasterizer;
	fOutlineRasterizer = NULL;

	delete fScanline;
	fScanline = NULL;

	delete fRasterizer;
	fRasterizer = NULL;

	delete fRenderer;
	fRenderer = NULL;

	delete fFontRendererSolid;
	fFontRendererSolid = NULL;

	delete fFontRendererBin;
	fFontRendererBin = NULL;
}

// _Transform
void
Painter::_Transform(BPoint* point, bool centerOffset) const
{
	*point += fOrigin;
	// rounding
	if (!fSubpixelPrecise) {
		// TODO: validate usage of floor() for values < 0
		point->x = floorf(point->x);
		point->y = floorf(point->y);
	}
	// apply the scale
	point->x *= fScale;
	point->y *= fScale;
	// this code is supposed to move coordinates to the center of pixels,
	// as AGG considers (0,0) to be the "upper left corner" of a pixel,
	// but BViews are less strict on those details
	if (centerOffset) {
		point->x += 0.5;
		point->y += 0.5;
	}
}

// _Transform
BPoint
Painter::_Transform(const BPoint& point, bool centerOffset) const
{
	BPoint ret = point;
	_Transform(&ret, centerOffset);
	return ret;
}

// _Transform
void
Painter::_Transform(float* width) const
{
	*width *= fScale;
	if (*width < 1)
		*width = 1;
}

// _Transform
float
Painter::_Transform(const float& width) const
{
	float w = width * fScale;
	if (w < 1)
		w = 1;
	return w;
}

// _Transform
void
Painter::_Transform(BRect* rect) const
{
	// TODO integrate this function more
	rect->right++;
	rect->bottom++;
	rect->left += fOrigin.x;
	rect->top += fOrigin.y;
	rect->right += fOrigin.x;
	rect->bottom += fOrigin.y;
	rect->left *= fScale;
	rect->top *= fScale;
	rect->right *= fScale;
	rect->bottom *= fScale;
	rect->right--;
	rect->bottom--;
}

// _Transform
BRect
Painter::_Transform(const BRect& rect) const
{
	BRect ret = rect;
	_Transform(&ret);
	return ret;
}

// _Clipped
BRect
Painter::_Clipped(const BRect& rect) const
{
	if (rect.IsValid() && fClippingRegion)
		return rect & _Transform(fClippingRegion->Frame());
	return rect;
}

// #pragma mark -

// _RebuildClipping
void
Painter::_RebuildClipping()
{
	if (fBaseRenderer) {
		fBaseRenderer->reset_clipping(!fClippingRegion);
		if (fClippingRegion) {
			int32 count = fClippingRegion->CountRects();
			for (int32 i = 0; i < count; i++) {
				BRect r = fClippingRegion->RectAt(i);
				// NOTE: The rounding here appears to give somewhat
				// different results compared to Be's implementation,
				// though I was unable to figure out the difference
				BPoint lt(r.LeftTop());
				BPoint rb(r.RightBottom());
				// offset to bottom right corner of pixel before transformation
				rb += BPoint(1.0, 1.0);
				// apply transformation
				lt += fOrigin;
				lt.x *= fScale;
				lt.y *= fScale;
				rb += fOrigin;
				rb.x *= fScale;
				rb.y *= fScale;
				// undo offset to bottom right corner after transformation
				rb -= BPoint(1.0, 1.0);
//				fBaseRenderer->add_clip_box(floorf(lt.x),
//											floorf(lt.y),
//											ceilf(rb.x),
//											ceilf(rb.y));
				fBaseRenderer->add_clip_box(roundf(lt.x),
											roundf(lt.y),
											roundf(rb.x),
											roundf(rb.y));
			}
		}
	}
}

// _UpdateFont
void
Painter::_UpdateFont()
{
	if (fLastFamilyAndStyle != fFont.GetFamilyAndStyle()) {
		fLastFamilyAndStyle = fFont.GetFamilyAndStyle();
		
		bool success = false;
		success = fTextRenderer->SetFont(fFont);
		if (!success)
			fprintf(stderr, "unable to set font\n");
	}
	
	fTextRenderer->SetPointSize(fFont.Size());
}

// _UpdateLineWidth
void
Painter::_UpdateLineWidth()
{
	float width = fPenSize;
	_Transform(&width);

	fLineProfile.width(width);	
}

// #pragma mark -

// _DrawTriangle
inline void
Painter::_DrawTriangle(BPoint pt1, BPoint pt2, BPoint pt3,
					   const pattern& p, bool fill) const
{
	_Transform(&pt1);
	_Transform(&pt2);
	_Transform(&pt3);

	agg::path_storage path;

	path.move_to(pt1.x, pt1.y);
	path.line_to(pt2.x, pt2.y);
	path.line_to(pt3.x, pt3.y);

	path.close_polygon();

	if (fill)
		_FillPath(path, p);
	else
		_StrokePath(path, p);
}

// _DrawEllipse
inline void
Painter::_DrawEllipse(BPoint center, float xRadius, float yRadius,
					  const pattern& p, bool fill) const
{
	// TODO: I think the conversion and the offset of
	// pixel centers might not be correct here, and it
	// might even be necessary to treat Fill and Stroke
	// differently, as with Fill-/StrokeRect().
	_Transform(&center);
	_Transform(&xRadius);
	_Transform(&yRadius);

	float width = fPenSize;
	_Transform(&width);

	int32 divisions = (int32)max_c(12, ((xRadius + yRadius) * PI) / 2 * (int32)width);

	agg::ellipse path(center.x, center.y, xRadius, yRadius, divisions);

	if (fill)
		_FillPath(path, p);
	else
		_StrokePath(path, p);
}

// _DrawShape
inline void
Painter::_DrawShape(/*const */BShape* shape, const pattern& p, bool fill) const
{
	// TODO: untested
	agg::path_storage path;
	ShapeConverter converter(&path);

	// account for our view coordinate system
	converter.ScaleBy(B_ORIGIN, fScale, fScale);
	converter.TranslateBy(fOrigin);
	// offset locations to center of pixels
	converter.TranslateBy(BPoint(0.5, 0.5));

	converter.Iterate(shape);

	if (fill)
		_FillPath(path, p);
	else
		_StrokePath(path, p);
}

// _DrawPolygon
inline void
Painter::_DrawPolygon(const BPoint* ptArray, int32 numPts,
					  bool closed, const pattern& p, bool fill) const
{
	if (numPts > 0) {

		agg::path_storage path;
		BPoint point = _Transform(*ptArray);
		path.move_to(point.x, point.y);

		for (int32 i = 1; i < numPts; i++) {
			ptArray++;
			point = _Transform(*ptArray);
			path.line_to(point.x, point.y);
		}

		if (closed)
			path.close_polygon();

		if (fill)
			_FillPath(path, p);
		else
			_StrokePath(path, p);
	}
}

// _DrawBitmap
void
Painter::_DrawBitmap(const agg::rendering_buffer& srcBuffer, color_space format,
					 BRect actualBitmapRect, BRect bitmapRect, BRect viewRect) const
{
	switch (format) {
		case B_RGB32:
		case B_RGBA32:
			_DrawBitmap32(srcBuffer, actualBitmapRect, bitmapRect, viewRect);
			break;
		default:
fprintf(stderr, "Painter::_DrawBitmap() - non-native colorspace: %d\n", format);
#ifdef __HAIKU__
			// TODO: this is only a temporary implementation,
			// to really handle other colorspaces, one would
			// rather do the conversion with much less overhead,
			// for example in the nn filter (hm), or in the
			// scanline generator
			BBitmap temp(actualBitmapRect, 0, B_RGB32);
			status_t err = temp.ImportBits(srcBuffer.buf(),
										   srcBuffer.height() * srcBuffer.stride(),
										   srcBuffer.stride(),
										   0, format);
			if (err >= B_OK) {
				agg::rendering_buffer convertedBuffer;
				convertedBuffer.attach((uint8*)temp.Bits(),
									   (uint32)actualBitmapRect.IntegerWidth() + 1,
									   (uint32)actualBitmapRect.IntegerHeight() + 1,
									   temp.BytesPerRow());
				_DrawBitmap32(convertedBuffer, actualBitmapRect, bitmapRect, viewRect);
			} else {
fprintf(stderr, "Painter::_DrawBitmap() - colorspace conversion failed: %s\n", strerror(err));
			}
#endif // __HAIKU__
			break;
	}
}

// _DrawBitmap32
void
Painter::_DrawBitmap32(const agg::rendering_buffer& srcBuffer,
					   BRect actualBitmapRect, BRect bitmapRect, BRect viewRect) const
{
typedef agg::span_allocator<agg::rgba8> span_alloc_type;
typedef agg::span_interpolator_linear<> interpolator_type;
typedef agg::span_image_filter_rgba32_nn<agg::order_bgra32,
										 interpolator_type> span_gen_type;
typedef agg::renderer_scanline_aa<renderer_base, span_gen_type> image_renderer_type;

	if (bitmapRect.IsValid() && bitmapRect.Intersects(actualBitmapRect)
		&& viewRect.IsValid()) {

		// compensate for the lefttop offset the actualBitmapRect might have
// NOTE: I have no clue why enabling the next call gives a wrong result!
// According to the BeBook, bitmapRect is supposed to be in native
// bitmap space!
//		bitmapRect.OffsetBy(-actualBitmapRect.left, -actualBitmapRect.top);
		actualBitmapRect.OffsetBy(-actualBitmapRect.left, -actualBitmapRect.top);

		// calculate the scaling
		double xScale = (viewRect.Width() + 1) / (bitmapRect.Width() + 1);
		double yScale = (viewRect.Height() + 1) / (bitmapRect.Height() + 1);

		// constrain rect to passed bitmap bounds
		// and transfer the changes to the viewRect
		if (bitmapRect.left < actualBitmapRect.left) {
			float diff = actualBitmapRect.left - bitmapRect.left;
			viewRect.left += diff * xScale;
			bitmapRect.left = actualBitmapRect.left;
		}
		if (bitmapRect.top < actualBitmapRect.top) {
			float diff = actualBitmapRect.top - bitmapRect.top;
			viewRect.top += diff;
			bitmapRect.top = actualBitmapRect.top;
		}
		if (bitmapRect.right > actualBitmapRect.right) {
			float diff = bitmapRect.right - actualBitmapRect.right;
			viewRect.right -= diff;
			bitmapRect.right = actualBitmapRect.right;
		}
		if (bitmapRect.bottom > actualBitmapRect.bottom) {
			float diff = bitmapRect.right - actualBitmapRect.bottom;
			viewRect.bottom -= diff;
			bitmapRect.bottom = actualBitmapRect.bottom;
		}

		float xOffset = viewRect.left - (bitmapRect.left * xScale);
		float yOffset = viewRect.top - (bitmapRect.top * yScale);

		agg::trans_affine srcMatrix;
//		srcMatrix *= agg::trans_affine_translation(-actualBitmapRect.left, -actualBitmapRect.top);
		srcMatrix *= agg::trans_affine_scaling(fScale, fScale);
		srcMatrix *= agg::trans_affine_translation(fOrigin.x, fOrigin.y);

		agg::trans_affine imgMatrix;
		imgMatrix *= agg::trans_affine_scaling(xScale, yScale);
		imgMatrix *= agg::trans_affine_translation(xOffset, yOffset);
		imgMatrix *= agg::trans_affine_scaling(fScale, fScale);
		imgMatrix *= agg::trans_affine_translation(fOrigin.x, fOrigin.y);
		imgMatrix.invert();
		
		span_alloc_type sa;
		interpolator_type interpolator(imgMatrix);

		span_gen_type sg(sa, srcBuffer, agg::rgba(0, 0, 0, 0), interpolator);

		image_renderer_type ri(*fBaseRenderer, sg);

		agg::rasterizer_scanline_aa<> pf;
		agg::scanline_u8 sl;

		// path encloses image
		agg::path_storage path;
		path.move_to(viewRect.left, viewRect.top);
		path.line_to(viewRect.right + 1, viewRect.top);
		path.line_to(viewRect.right + 1, viewRect.bottom + 1);
		path.line_to(viewRect.left, viewRect.bottom + 1);
		path.close_polygon();

		agg::conv_transform<agg::path_storage> tr(path, srcMatrix);

		pf.add_path(tr);
		agg::render_scanlines(pf, sl, ri);
	}
}

// _InvertRect32
void
Painter::_InvertRect32(BRect r) const
{
	if (fBuffer) {
		int32 width = r.IntegerWidth() + 1;
		for (int32 y = (int32)r.top; y <= (int32)r.bottom; y++) {
			uint8* dst = fBuffer->row(y);
			dst += (int32)r.left * 4;
			for (int32 i = 0; i < width; i++) {
				dst[0] = 255 - dst[0];
				dst[1] = 255 - dst[1];
				dst[2] = 255 - dst[2];
				dst += 4;
			}
		}
	}
}

// #pragma mark -

template<class VertexSource>
BRect
Painter::_BoundingBox(VertexSource& path) const
{
	double left = 0.0;
	double top = 0.0;
	double right = -1.0;
	double bottom = -1.0;
	uint32 pathID[1];
	pathID[0] = 0;
	agg::bounding_rect(path, pathID, 0, 1, &left, &top, &right, &bottom);
	return BRect(left, top, right, bottom);
}


// _StrokePath
template<class VertexSource>
BRect
Painter::_StrokePath(VertexSource& path, const pattern& p) const
{
// We're now used by app_server and SetDrawData() was called prior to
// this and it means the pattern is already set
//	fPatternHandler->SetPattern(p);
//	_SetPattern(p);

#if ALIASED_DRAWING
	float width = fPenSize;
	_Transform(&width);
	if (width > 1.0) {
		agg::conv_stroke<VertexSource> stroke(path);
		stroke.width(width);

		fRasterizer->add_path(stroke);
		agg::render_scanlines(*fRasterizer, *fScanline, *fRenderer);
	} else {
		fOutlineRasterizer->add_path(path);
	}
#else
	fOutlineRasterizer->add_path(path);
#endif

	return _Clipped(_BoundingBox(path));
}

// _FillPath
template<class VertexSource>
BRect
Painter::_FillPath(VertexSource& path, const pattern& p) const
{
// We're now used by app_server and SetDrawData() was called prior to
// this and it means the pattern is already set
//	fPatternHandler->SetPattern(p);
//	_SetPattern(p);

	fRasterizer->add_path(path);
	agg::render_scanlines(*fRasterizer, *fScanline, *fRenderer);

	return _Clipped(_BoundingBox(path));
}

// _SetPattern
void
Painter::_SetPattern(const pattern& p) const
{
	if (!(p == *fPatternHandler->GetR5Pattern())) {
printf("Painter::_SetPattern()\n");
		fPatternHandler->SetPattern(p);
		DrawingMode* mode = NULL;
		if (p == B_SOLID_HIGH) {
			_SetRendererColor(fPatternHandler->HighColor().GetColor32());
			mode = DrawingModeFactory::DrawingModeFor(fDrawingMode,
													  fAlphaSrcMode,
													  fAlphaFncMode,
													  true);
		} else if (p == B_SOLID_LOW) {
			_SetRendererColor(fPatternHandler->LowColor().GetColor32());
			mode = DrawingModeFactory::DrawingModeFor(fDrawingMode,
													  fAlphaSrcMode,
													  fAlphaFncMode,
													  true);
		} else {
			mode = DrawingModeFactory::DrawingModeFor(fDrawingMode,
													  fAlphaSrcMode,
													  fAlphaFncMode,
													  false);
		}
		fPixelFormat->set_drawing_mode(mode);
	}
}

// _SetRendererColor
void
Painter::_SetRendererColor(const rgb_color& color) const
{

	if (fOutlineRenderer)
#if ALIASED_DRAWING
		fOutlineRenderer->line_color(agg::rgba(color.red / 255.0,
											   color.green / 255.0,
											   color.blue / 255.0));
#else
		fOutlineRenderer->color(agg::rgba(color.red / 255.0,
										  color.green / 255.0,
										  color.blue / 255.0));
#endif
	if (fRenderer)
		fRenderer->color(agg::rgba(color.red / 255.0,
								   color.green / 255.0,
								   color.blue / 255.0));
	if (fFontRendererSolid)
		fFontRendererSolid->color(agg::rgba(color.red / 255.0,
											color.green / 255.0,
											color.blue / 255.0));
	if (fFontRendererBin)
		fFontRendererBin->color(agg::rgba(color.red / 255.0,
										  color.green / 255.0,
										  color.blue / 255.0));

}