xref: /haiku/src/servers/app/drawing/Painter/Painter.cpp (revision 302f62604763c95777d6d04cca456e876f471c4f)
1 /*
2  * Copyright 2005-2006, Stephan Aßmus <superstippi@gmx.de>. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * API to the Anti-Grain Geometry based "Painter" drawing backend. Manages
6  * rendering pipe-lines for stroke, fills, bitmap and text rendering.
7  */
8 
9 
10 #include <stdio.h>
11 #include <string.h>
12 
13 #include <Bitmap.h>
14 #include <GraphicsDefs.h>
15 #include <Region.h>
16 #include <String.h>
17 
18 #include <agg_bezier_arc.h>
19 #include <agg_bounding_rect.h>
20 #include <agg_conv_clip_polygon.h>
21 #include <agg_conv_curve.h>
22 #include <agg_conv_stroke.h>
23 #include <agg_ellipse.h>
24 #include <agg_image_accessors.h>
25 #include <agg_path_storage.h>
26 #include <agg_pixfmt_rgba.h>
27 #include <agg_rounded_rect.h>
28 #include <agg_span_allocator.h>
29 #include <agg_span_image_filter_rgba.h>
30 #include <agg_span_interpolator_linear.h>
31 
32 #include "drawing_support.h"
33 
34 #include "DrawState.h"
35 
36 #include "AGGTextRenderer.h"
37 #include "DrawingMode.h"
38 #include "PatternHandler.h"
39 #include "RenderingBuffer.h"
40 #include "ServerBitmap.h"
41 #include "ServerFont.h"
42 #include "SystemPalette.h"
43 
44 #include "Painter.h"
45 
46 
47 #if ALIASED_DRAWING
48 // in this case, we _cannot_ use the outline rasterizer.
49 # define USE_OUTLINE_RASTERIZER 0
50 #else
51 // in this case, we can optionally use the outline rasterizer (faster).
52 // NOTE: The outline rasterizer is different from the "general purpose"
53 // rasterizer and can speed up the stroking of lines. It has some problems
54 // though, for example the butts of the lines are not anti-aliased. So we
55 // use the much more powerfull general purpose rasterizer, and live with the
56 // performance hit for now. See _StrokePath().
57 // NOTE: The outline rasterizer will still be used for lines with 1 pixel width!
58 # define USE_OUTLINE_RASTERIZER 0
59 #endif
60 
61 
62 #define CHECK_CLIPPING	if (!fValidClipping) return BRect(0, 0, -1, -1);
63 
64 
65 // constructor
66 Painter::Painter()
67 	: fBuffer(NULL),
68 	  fPixelFormat(NULL),
69 	  fBaseRenderer(NULL),
70 	  fOutlineRenderer(NULL),
71 	  fOutlineRasterizer(NULL),
72 	  fUnpackedScanline(NULL),
73 	  fPackedScanline(NULL),
74 	  fRasterizer(NULL),
75 	  fRenderer(NULL),
76 	  fRendererBin(NULL),
77 
78 	  fLineProfile(),
79 	  fPath(),
80 	  fCurve(fPath),
81 
82 	  fSubpixelPrecise(false),
83 
84 	  fPenSize(1.0),
85 	  fClippingRegion(new BRegion()),
86 	  fValidClipping(false),
87 	  fDrawingMode(B_OP_COPY),
88 	  fDrawingText(false),
89 	  fAlphaSrcMode(B_PIXEL_ALPHA),
90 	  fAlphaFncMode(B_ALPHA_OVERLAY),
91 	  fPenLocation(0.0, 0.0),
92 	  fLineCapMode(B_BUTT_CAP),
93 	  fLineJoinMode(B_MITER_JOIN),
94 	  fMiterLimit(B_DEFAULT_MITER_LIMIT),
95 
96 	  fPatternHandler(new PatternHandler()),
97 	  fTextRenderer(new AGGTextRenderer())
98 {
99 	// Usually, the drawing engine will lock the font for us when
100 	// needed - unfortunately, it can't know we need it here
101 	fFont.Lock();
102 	_UpdateFont();
103 	fFont.Unlock();
104 
105 #if USE_OUTLINE_RASTERIZER
106 	_UpdateLineWidth();
107 #endif
108 }
109 
110 // destructor
111 Painter::~Painter()
112 {
113 	_MakeEmpty();
114 
115 	delete fClippingRegion;
116 	delete fPatternHandler;
117 	delete fTextRenderer;
118 }
119 
120 // #pragma mark -
121 
122 // AttachToBuffer
123 void
124 Painter::AttachToBuffer(RenderingBuffer* buffer)
125 {
126 	if (buffer && buffer->InitCheck() >= B_OK &&
127 		// TODO: implement drawing on B_RGB24, B_RGB15, B_RGB16, B_CMAP8 and B_GRAY8 :-[
128 		(buffer->ColorSpace() == B_RGBA32 || buffer->ColorSpace() == B_RGB32)) {
129 		// clean up previous stuff
130 		_MakeEmpty();
131 
132 		fBuffer = new agg::rendering_buffer();
133 		fBuffer->attach((uint8*)buffer->Bits(),
134 						buffer->Width(),
135 						buffer->Height(),
136 						buffer->BytesPerRow());
137 
138 		fPixelFormat = new pixfmt(*fBuffer, fPatternHandler);
139 		fPixelFormat->SetDrawingMode(fDrawingMode, fAlphaSrcMode, fAlphaFncMode, false);
140 
141 		fBaseRenderer = new renderer_base(*fPixelFormat);
142 		// attach our clipping region to the renderer, it keeps a pointer
143 		fBaseRenderer->set_clipping_region(fClippingRegion);
144 
145 		// These are the AGG renderes and rasterizes which
146 		// will be used for stroking paths
147 #if USE_OUTLINE_RASTERIZER
148 #if ALIASED_DRAWING
149 		fOutlineRenderer = new outline_renderer_type(*fBaseRenderer);
150 		fOutlineRasterizer = new outline_rasterizer_type(*fOutlineRenderer);
151 #else
152 		fOutlineRenderer = new outline_renderer_type(*fBaseRenderer, fLineProfile);
153 		fOutlineRasterizer = new outline_rasterizer_type(*fOutlineRenderer);
154 
155 		// attach our line profile to the renderer, it keeps a pointer
156 		fOutlineRenderer->profile(fLineProfile);
157 #endif // ALIASED_DRAWING
158 #endif // USE_OUTLINE_RASTERIZER
159 		// the renderer used for filling paths
160 		fRenderer = new renderer_type(*fBaseRenderer);
161 		fRasterizer = new rasterizer_type();
162 		fUnpackedScanline = new scanline_unpacked_type();
163 		fPackedScanline = new scanline_packed_type();
164 
165 #if ALIASED_DRAWING
166 		fRasterizer->gamma(agg::gamma_threshold(0.5));
167 #endif
168 
169 		// possibly needed for drawing text (if the font is
170 		// a one bit bitmap font, which is currently not supported yet)
171 		fRendererBin = new renderer_bin_type(*fBaseRenderer);
172 
173 		_SetRendererColor(fPatternHandler->HighColor().GetColor32());
174 	}
175 }
176 
177 // DetachFromBuffer
178 void
179 Painter::DetachFromBuffer()
180 {
181 	_MakeEmpty();
182 }
183 
184 // #pragma mark -
185 
186 // SetDrawState
187 void
188 Painter::SetDrawState(const DrawState* data, bool updateFont)
189 {
190 	// NOTE: The custom clipping in "data" is ignored, because it has already been
191 	// taken into account elsewhere
192 
193 	// TODO: optimize "context switch" for speed...
194 	// but for now...
195 	SetPenSize(data->PenSize());
196 	SetPenLocation(data->PenLocation());
197 
198 	if (updateFont)
199 		SetFont(data->Font());
200 
201 	fTextRenderer->SetAntialiasing(!(data->ForceFontAliasing() || data->Font().Flags() & B_DISABLE_ANTIALIASING));
202 
203 	fSubpixelPrecise = data->SubPixelPrecise();
204 
205 	// any of these conditions means we need to use a different drawing
206 	// mode instance
207 	bool updateDrawingMode = !(data->GetPattern() == fPatternHandler->GetPattern()) ||
208 		data->GetDrawingMode() != fDrawingMode ||
209 		(data->GetDrawingMode() == B_OP_ALPHA && (data->AlphaSrcMode() != fAlphaSrcMode ||
210 												  data->AlphaFncMode() != fAlphaFncMode));
211 
212 	fDrawingMode	= data->GetDrawingMode();
213 	fAlphaSrcMode	= data->AlphaSrcMode();
214 	fAlphaFncMode	= data->AlphaFncMode();
215 	fPatternHandler->SetPattern(data->GetPattern());
216 	fLineCapMode	= data->LineCapMode();
217 	fLineJoinMode	= data->LineJoinMode();
218 	fMiterLimit		= data->MiterLimit();
219 
220 	// adopt the color *after* the pattern is set
221 	// to set the renderers to the correct color
222 	SetHighColor(data->HighColor().GetColor32());
223 	SetLowColor(data->LowColor().GetColor32());
224 
225 	if (updateDrawingMode || fPixelFormat->UsesOpCopyForText())
226 		_UpdateDrawingMode();
227 }
228 
229 // #pragma mark - state
230 
231 // ConstrainClipping
232 void
233 Painter::ConstrainClipping(const BRegion* region)
234 {
235 	*fClippingRegion = *region;
236 	fValidClipping = fClippingRegion->Frame().IsValid();
237 
238 	if (fValidClipping) {
239 		clipping_rect cb = fClippingRegion->FrameInt();
240 		fRasterizer->clip_box(cb.left, cb.top, cb.right + 1, cb.bottom + 1);
241 	}
242 // TODO: would be nice if we didn't need to copy a region
243 // for *each* drawing command...
244 //fBaseRenderer->set_clipping_region(const_cast<BRegion*>(region));
245 //fValidClipping = region->Frame().IsValid();
246 }
247 
248 // SetHighColor
249 void
250 Painter::SetHighColor(const rgb_color& color)
251 {
252 	fPatternHandler->SetHighColor(color);
253 	if (*(fPatternHandler->GetR5Pattern()) == B_SOLID_HIGH)
254 		_SetRendererColor(color);
255 }
256 
257 // SetLowColor
258 void
259 Painter::SetLowColor(const rgb_color& color)
260 {
261 	fPatternHandler->SetLowColor(color);
262 	if (*(fPatternHandler->GetR5Pattern()) == B_SOLID_LOW)
263 		_SetRendererColor(color);
264 }
265 
266 // SetPenSize
267 void
268 Painter::SetPenSize(float size)
269 {
270 	if (fPenSize != size) {
271 		fPenSize = size;
272 #if USE_OUTLINE_RASTERIZER
273 // NOTE: _UpdateLineWidth() updates the line profile which is quite a heavy resource!
274 // fortunately, we don't need it when using the general purpose rasterizer
275 		_UpdateLineWidth();
276 #endif
277 	}
278 }
279 
280 // SetPattern
281 void
282 Painter::SetPattern(const pattern& p, bool drawingText)
283 {
284 	if (!(p == *fPatternHandler->GetR5Pattern()) || drawingText != fDrawingText) {
285 		fPatternHandler->SetPattern(p);
286 		fDrawingText = drawingText;
287 		_UpdateDrawingMode(fDrawingText);
288 
289 		// update renderer color if necessary
290 		if (fPatternHandler->IsSolidHigh()) {
291 			// pattern was not solid high before
292 			_SetRendererColor(fPatternHandler->HighColor().GetColor32());
293 		} else if (fPatternHandler->IsSolidLow()) {
294 			// pattern was not solid low before
295 			_SetRendererColor(fPatternHandler->LowColor().GetColor32());
296 		}
297 	}
298 }
299 
300 // SetPenLocation
301 void
302 Painter::SetPenLocation(const BPoint& location)
303 {
304 	fPenLocation = location;
305 }
306 
307 // SetFont
308 void
309 Painter::SetFont(const ServerFont& font)
310 {
311 	fFont = font;
312 	_UpdateFont();
313 }
314 
315 // #pragma mark - drawing
316 
317 // StrokeLine
318 BRect
319 Painter::StrokeLine(BPoint a, BPoint b)
320 {
321 	CHECK_CLIPPING
322 
323 	// "false" means not to do the pixel center offset,
324 	// because it would mess up our optimized versions
325 	_Transform(&a, false);
326 	_Transform(&b, false);
327 
328 	BRect touched(min_c(a.x, b.x), min_c(a.y, b.y),
329 				  max_c(a.x, b.x), max_c(a.y, b.y));
330 
331 	// This is supposed to stop right here if we can see
332 	// that we're definitaly outside the clipping reagion.
333 	// Extending by penSize like that is not really correct,
334 	// but fast and only triggers unnecessary calculation
335 	// in a few edge cases
336 	touched.InsetBy(-(fPenSize - 1), -(fPenSize - 1));
337 	if (!touched.Intersects(fClippingRegion->Frame())) {
338 		touched.Set(0.0, 0.0, -1.0, -1.0);
339 		return touched;
340 	}
341 
342 	// first, try an optimized version
343 	if (fPenSize == 1.0 &&
344 		(fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER)) {
345 		pattern pat = *fPatternHandler->GetR5Pattern();
346 		if (pat == B_SOLID_HIGH &&
347 			StraightLine(a, b, fPatternHandler->HighColor().GetColor32())) {
348 			return _Clipped(touched);
349 		} else if (pat == B_SOLID_LOW &&
350 			StraightLine(a, b, fPatternHandler->LowColor().GetColor32())) {
351 			return _Clipped(touched);
352 		}
353 	}
354 
355 	fPath.remove_all();
356 
357 	if (a == b) {
358 		// special case dots
359 		fPath.move_to(a.x, a.y);
360 		fPath.line_to(a.x + 1, a.y);
361 		fPath.line_to(a.x + 1, a.y + 1);
362 		fPath.line_to(a.x, a.y + 1);
363 
364 		touched = _FillPath(fPath);
365 	} else {
366 		// do the pixel center offset here
367 		if (!fSubpixelPrecise && fmodf(fPenSize, 2.0) != 0.0) {
368 			a.x += 0.5;
369 			a.y += 0.5;
370 			b.x += 0.5;
371 			b.y += 0.5;
372 		}
373 
374 		fPath.move_to(a.x, a.y);
375 		fPath.line_to(b.x, b.y);
376 
377 		touched = _StrokePath(fPath);
378 	}
379 
380 	return _Clipped(touched);
381 }
382 
383 // StraightLine
384 bool
385 Painter::StraightLine(BPoint a, BPoint b, const rgb_color& c) const
386 {
387 	if (fBuffer && fValidClipping) {
388 		if (a.x == b.x) {
389 			// vertical
390 			uint8* dst = fBuffer->row_ptr(0);
391 			uint32 bpr = fBuffer->stride();
392 			int32 x = (int32)a.x;
393 			dst += x * 4;
394 			int32 y1 = (int32)min_c(a.y, b.y);
395 			int32 y2 = (int32)max_c(a.y, b.y);
396 			pixel32 color;
397 			color.data8[0] = c.blue;
398 			color.data8[1] = c.green;
399 			color.data8[2] = c.red;
400 			color.data8[3] = 255;
401 			// draw a line, iterate over clipping boxes
402 			fBaseRenderer->first_clip_box();
403 			do {
404 				if (fBaseRenderer->xmin() <= x &&
405 					fBaseRenderer->xmax() >= x) {
406 					int32 i = max_c(fBaseRenderer->ymin(), y1);
407 					int32 end = min_c(fBaseRenderer->ymax(), y2);
408 					uint8* handle = dst + i * bpr;
409 					for (; i <= end; i++) {
410 						*(uint32*)handle = color.data32;
411 						handle += bpr;
412 					}
413 				}
414 			} while (fBaseRenderer->next_clip_box());
415 
416 			return true;
417 
418 		} else if (a.y == b.y) {
419 			// horizontal
420 			int32 y = (int32)a.y;
421 			uint8* dst = fBuffer->row_ptr(y);
422 			int32 x1 = (int32)min_c(a.x, b.x);
423 			int32 x2 = (int32)max_c(a.x, b.x);
424 			pixel32 color;
425 			color.data8[0] = c.blue;
426 			color.data8[1] = c.green;
427 			color.data8[2] = c.red;
428 			color.data8[3] = 255;
429 			// draw a line, iterate over clipping boxes
430 			fBaseRenderer->first_clip_box();
431 			do {
432 				if (fBaseRenderer->ymin() <= y &&
433 					fBaseRenderer->ymax() >= y) {
434 					int32 i = max_c(fBaseRenderer->xmin(), x1);
435 					int32 end = min_c(fBaseRenderer->xmax(), x2);
436 					uint32* handle = (uint32*)(dst + i * 4);
437 					for (; i <= end; i++) {
438 						*handle++ = color.data32;
439 					}
440 				}
441 			} while (fBaseRenderer->next_clip_box());
442 
443 			return true;
444 		}
445 	}
446 	return false;
447 }
448 
449 // #pragma mark -
450 
451 // StrokeTriangle
452 BRect
453 Painter::StrokeTriangle(BPoint pt1, BPoint pt2, BPoint pt3) const
454 {
455 	return _DrawTriangle(pt1, pt2, pt3, false);
456 }
457 
458 // FillTriangle
459 BRect
460 Painter::FillTriangle(BPoint pt1, BPoint pt2, BPoint pt3) const
461 {
462 	return _DrawTriangle(pt1, pt2, pt3, true);
463 }
464 
465 // DrawPolygon
466 BRect
467 Painter::DrawPolygon(BPoint* p, int32 numPts,
468 					 bool filled, bool closed) const
469 {
470 	CHECK_CLIPPING
471 
472 	if (numPts > 0) {
473 
474 		fPath.remove_all();
475 
476 		_Transform(p);
477 		fPath.move_to(p->x, p->y);
478 
479 		for (int32 i = 1; i < numPts; i++) {
480 			p++;
481 			_Transform(p);
482 			fPath.line_to(p->x, p->y);
483 		}
484 
485 		if (closed)
486 			fPath.close_polygon();
487 
488 		if (filled)
489 			return _FillPath(fPath);
490 		else
491 			return _StrokePath(fPath);
492 	}
493 	return BRect(0.0, 0.0, -1.0, -1.0);
494 }
495 
496 // DrawBezier
497 BRect
498 Painter::DrawBezier(BPoint* p, bool filled) const
499 {
500 	CHECK_CLIPPING
501 
502 	fPath.remove_all();
503 
504 	_Transform(&(p[0]));
505 	_Transform(&(p[1]));
506 	_Transform(&(p[2]));
507 	_Transform(&(p[3]));
508 
509 	fPath.move_to(p[0].x, p[0].y);
510 	fPath.curve4(p[1].x, p[1].y,
511 				 p[2].x, p[2].y,
512 				 p[3].x, p[3].y);
513 
514 
515 	if (filled) {
516 		fPath.close_polygon();
517 		return _FillPath(fCurve);
518 	} else {
519 		return _StrokePath(fCurve);
520 	}
521 }
522 
523 // this comes from Shape.cpp
524 // code duplication ahead...
525 #define OP_LINETO		0x10000000
526 #define OP_BEZIERTO		0x20000000
527 #define OP_CLOSE		0x40000000
528 #define OP_MOVETO		0x80000000
529 
530 // DrawShape
531 BRect
532 Painter::DrawShape(const int32& opCount, const uint32* opList,
533 				   const int32& ptCount, const BPoint* points,
534 				   bool filled) const
535 {
536 	CHECK_CLIPPING
537 
538 	// TODO: if shapes are ever used more heavily in Haiku,
539 	// it would be nice to use BShape data directly (write
540 	// an AGG "VertexSource" adaptor)
541 	fPath.remove_all();
542 	for (int32 i = 0; i < opCount; i++) {
543 		uint32 op = opList[i] & 0xFF000000;
544 		if (op & OP_MOVETO) {
545 			fPath.move_to(points->x + fPenLocation.x, points->y + fPenLocation.y);
546 			points++;
547 		}
548 
549 		if (op & OP_LINETO) {
550 			int32 count = opList[i] & 0x00FFFFFF;
551 			while (count--) {
552 				fPath.line_to(points->x + fPenLocation.x, points->y + fPenLocation.y);
553 				points++;
554 			}
555 		}
556 
557 		if (op & OP_BEZIERTO) {
558 			int32 count = opList[i] & 0x00FFFFFF;
559 			while (count) {
560 				fPath.curve4(points[0].x + fPenLocation.x, points[0].y + fPenLocation.y,
561 							 points[1].x + fPenLocation.x, points[1].y + fPenLocation.y,
562 							 points[2].x + fPenLocation.x, points[2].y + fPenLocation.y);
563 				points += 3;
564 				count -= 3;
565 			}
566 		}
567 
568 		if (op & OP_CLOSE)
569 			fPath.close_polygon();
570 	}
571 
572 	if (filled)
573 		return _FillPath(fCurve);
574 	else
575 		return _StrokePath(fCurve);
576 }
577 
578 // StrokeRect
579 BRect
580 Painter::StrokeRect(const BRect& r) const
581 {
582 	CHECK_CLIPPING
583 
584 	// support invalid rects
585 	BPoint a(min_c(r.left, r.right), min_c(r.top, r.bottom));
586 	BPoint b(max_c(r.left, r.right), max_c(r.top, r.bottom));
587 	_Transform(&a, false);
588 	_Transform(&b, false);
589 
590 	// first, try an optimized version
591 	if (fPenSize == 1.0 &&
592 		(fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER)) {
593 		pattern p = *fPatternHandler->GetR5Pattern();
594 		if (p == B_SOLID_HIGH) {
595 			BRect rect(a, b);
596 			StrokeRect(rect,
597 					   fPatternHandler->HighColor().GetColor32());
598 			return _Clipped(rect);
599 		} else if (p == B_SOLID_LOW) {
600 			BRect rect(a, b);
601 			StrokeRect(rect,
602 					   fPatternHandler->LowColor().GetColor32());
603 			return _Clipped(rect);
604 		}
605 	}
606 
607 	if (fmodf(fPenSize, 2.0) != 0.0) {
608 		// shift coords to center of pixels
609 		a.x += 0.5;
610 		a.y += 0.5;
611 		b.x += 0.5;
612 		b.y += 0.5;
613 	}
614 
615 	fPath.remove_all();
616 	fPath.move_to(a.x, a.y);
617 	if (a.x == b.x || a.y == b.y) {
618 		// special case rects with one pixel height or width
619 		fPath.line_to(b.x, b.y);
620 	} else {
621 		fPath.line_to(b.x, a.y);
622 		fPath.line_to(b.x, b.y);
623 		fPath.line_to(a.x, b.y);
624 	}
625 	fPath.close_polygon();
626 
627 	return _StrokePath(fPath);
628 }
629 
630 // StrokeRect
631 void
632 Painter::StrokeRect(const BRect& r, const rgb_color& c) const
633 {
634 	StraightLine(BPoint(r.left, r.top),
635 				 BPoint(r.right - 1, r.top), c);
636 	StraightLine(BPoint(r.right, r.top),
637 				 BPoint(r.right, r.bottom - 1), c);
638 	StraightLine(BPoint(r.right, r.bottom),
639 				 BPoint(r.left + 1, r.bottom), c);
640 	StraightLine(BPoint(r.left, r.bottom),
641 				 BPoint(r.left, r.top + 1), c);
642 }
643 
644 // FillRect
645 BRect
646 Painter::FillRect(const BRect& r) const
647 {
648 	CHECK_CLIPPING
649 
650 	// support invalid rects
651 	BPoint a(min_c(r.left, r.right), min_c(r.top, r.bottom));
652 	BPoint b(max_c(r.left, r.right), max_c(r.top, r.bottom));
653 	_Transform(&a, false);
654 	_Transform(&b, false);
655 
656 	// first, try an optimized version
657 	if (fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER) {
658 		pattern p = *fPatternHandler->GetR5Pattern();
659 		if (p == B_SOLID_HIGH) {
660 			BRect rect(a, b);
661 			FillRect(rect, fPatternHandler->HighColor().GetColor32());
662 			return _Clipped(rect);
663 		} else if (p == B_SOLID_LOW) {
664 			BRect rect(a, b);
665 			FillRect(rect, fPatternHandler->LowColor().GetColor32());
666 			return _Clipped(rect);
667 		}
668 	}
669 	if (fDrawingMode == B_OP_ALPHA && fAlphaFncMode == B_ALPHA_OVERLAY) {
670 		pattern p = *fPatternHandler->GetR5Pattern();
671 		if (p == B_SOLID_HIGH) {
672 			BRect rect(a, b);
673 			_BlendRect32(rect, fPatternHandler->HighColor().GetColor32());
674 			return _Clipped(rect);
675 		} else if (p == B_SOLID_LOW) {
676 			rgb_color c = fPatternHandler->LowColor().GetColor32();
677 			if (fAlphaSrcMode == B_CONSTANT_ALPHA)
678 				c.alpha = fPatternHandler->HighColor().GetColor32().alpha;
679 			BRect rect(a, b);
680 			_BlendRect32(rect, c);
681 			return _Clipped(rect);
682 		}
683 	}
684 
685 
686 	// account for stricter interpretation of coordinates in AGG
687 	// the rectangle ranges from the top-left (.0, .0)
688 	// to the bottom-right (.9999, .9999) corner of pixels
689 	b.x += 1.0;
690 	b.y += 1.0;
691 
692 	fPath.remove_all();
693 	fPath.move_to(a.x, a.y);
694 	fPath.line_to(b.x, a.y);
695 	fPath.line_to(b.x, b.y);
696 	fPath.line_to(a.x, b.y);
697 	fPath.close_polygon();
698 
699 	return _FillPath(fPath);
700 }
701 
702 // FillRect
703 void
704 Painter::FillRect(const BRect& r, const rgb_color& c) const
705 {
706 	if (fBuffer && fValidClipping) {
707 		uint8* dst = fBuffer->row_ptr(0);
708 		uint32 bpr = fBuffer->stride();
709 		int32 left = (int32)r.left;
710 		int32 top = (int32)r.top;
711 		int32 right = (int32)r.right;
712 		int32 bottom = (int32)r.bottom;
713 		// get a 32 bit pixel ready with the color
714 		pixel32 color;
715 		color.data8[0] = c.blue;
716 		color.data8[1] = c.green;
717 		color.data8[2] = c.red;
718 		color.data8[3] = c.alpha;
719 		// fill rects, iterate over clipping boxes
720 		fBaseRenderer->first_clip_box();
721 		do {
722 			int32 x1 = max_c(fBaseRenderer->xmin(), left);
723 			int32 x2 = min_c(fBaseRenderer->xmax(), right);
724 			if (x1 <= x2) {
725 				int32 y1 = max_c(fBaseRenderer->ymin(), top);
726 				int32 y2 = min_c(fBaseRenderer->ymax(), bottom);
727 				uint8* offset = dst + x1 * 4;
728 				for (; y1 <= y2; y1++) {
729 //					uint32* handle = (uint32*)(offset + y1 * bpr);
730 //					for (int32 x = x1; x <= x2; x++) {
731 //						*handle++ = color.data32;
732 //					}
733 gfxset32(offset + y1 * bpr, color.data32, (x2 - x1 + 1) * 4);
734 				}
735 			}
736 		} while (fBaseRenderer->next_clip_box());
737 	}
738 }
739 
740 // FillRectNoClipping
741 void
742 Painter::FillRectNoClipping(const BRect& r, const rgb_color& c) const
743 {
744 	if (fBuffer) {
745 		int32 left = (int32)r.left;
746 		int32 y = (int32)r.top;
747 		int32 right = (int32)r.right;
748 		int32 bottom = (int32)r.bottom;
749 
750 		uint8* dst = fBuffer->row_ptr(y);
751 		uint32 bpr = fBuffer->stride();
752 
753 		// get a 32 bit pixel ready with the color
754 		pixel32 color;
755 		color.data8[0] = c.blue;
756 		color.data8[1] = c.green;
757 		color.data8[2] = c.red;
758 		color.data8[3] = c.alpha;
759 
760 		dst += left * 4;
761 
762 		for (; y <= bottom; y++) {
763 //			uint32* handle = (uint32*)dst;
764 //			for (int32 x = left; x <= right; x++) {
765 //				*handle++ = color.data32;
766 //			}
767 gfxset32(dst, color.data32, (right - left + 1) * 4);
768 			dst += bpr;
769 		}
770 	}
771 }
772 
773 // StrokeRoundRect
774 BRect
775 Painter::StrokeRoundRect(const BRect& r, float xRadius, float yRadius) const
776 {
777 	CHECK_CLIPPING
778 
779 	BPoint lt(r.left, r.top);
780 	BPoint rb(r.right, r.bottom);
781 	bool centerOffset = fPenSize == 1.0;
782 	// TODO: use this when using _StrokePath()
783 	// bool centerOffset = fmodf(fPenSize, 2.0) != 0.0;
784 	_Transform(&lt, centerOffset);
785 	_Transform(&rb, centerOffset);
786 
787 	if (fPenSize == 1.0) {
788 		agg::rounded_rect rect;
789 		rect.rect(lt.x, lt.y, rb.x, rb.y);
790 		rect.radius(xRadius, yRadius);
791 
792 		return _StrokePath(rect);
793 	} else {
794 		// NOTE: This implementation might seem a little strange, but it makes
795 		// stroked round rects look like on R5. A more correct way would be to use
796 		// _StrokePath() as above (independent from fPenSize).
797 		// The fact that the bounding box of the round rect is not enlarged
798 		// by fPenSize/2 is actually on purpose, though one could argue it is unexpected.
799 
800 		// enclose the right and bottom edge
801 		rb.x++;
802 		rb.y++;
803 
804 		agg::rounded_rect outer;
805 		outer.rect(lt.x, lt.y, rb.x, rb.y);
806 		outer.radius(xRadius, yRadius);
807 
808 		fRasterizer->reset();
809 		fRasterizer->add_path(outer);
810 
811 		// don't add an inner hole if the "size is negative", this avoids some
812 		// defects that can be observed on R5 and could be regarded as a bug.
813 		if (2 * fPenSize < rb.x - lt.x && 2 * fPenSize < rb.y - lt.y) {
814 			agg::rounded_rect inner;
815 			inner.rect(lt.x + fPenSize, lt.y + fPenSize, rb.x - fPenSize, rb.y - fPenSize);
816 			inner.radius(max_c(0.0, xRadius - fPenSize), max_c(0.0, yRadius - fPenSize));
817 
818 			fRasterizer->add_path(inner);
819 		}
820 
821 		// make the inner rect work as a hole
822 		fRasterizer->filling_rule(agg::fill_even_odd);
823 
824 		if (fPenSize > 2)
825 			agg::render_scanlines(*fRasterizer, *fPackedScanline, *fRenderer);
826 		else
827 			agg::render_scanlines(*fRasterizer, *fUnpackedScanline, *fRenderer);
828 
829 		// reset to default
830 		fRasterizer->filling_rule(agg::fill_non_zero);
831 
832 		return _Clipped(_BoundingBox(outer));
833 	}
834 }
835 
836 // FillRoundRect
837 BRect
838 Painter::FillRoundRect(const BRect& r, float xRadius, float yRadius) const
839 {
840 	CHECK_CLIPPING
841 
842 	BPoint lt(r.left, r.top);
843 	BPoint rb(r.right, r.bottom);
844 	_Transform(&lt, false);
845 	_Transform(&rb, false);
846 
847 	// account for stricter interpretation of coordinates in AGG
848 	// the rectangle ranges from the top-left (.0, .0)
849 	// to the bottom-right (.9999, .9999) corner of pixels
850 	rb.x += 1.0;
851 	rb.y += 1.0;
852 
853 	agg::rounded_rect rect;
854 	rect.rect(lt.x, lt.y, rb.x, rb.y);
855 	rect.radius(xRadius, yRadius);
856 
857 	return _FillPath(rect);
858 }
859 
860 // AlignEllipseRect
861 void
862 Painter::AlignEllipseRect(BRect* rect, bool filled) const
863 {
864 	if (!fSubpixelPrecise) {
865 		// align rect to pixels
866 		align_rect_to_pixels(rect);
867 		// account for "pixel index" versus "pixel area"
868 		rect->right++;
869 		rect->bottom++;
870 		if (!filled && fmodf(fPenSize, 2.0) != 0.0) {
871 			// align the stroke
872 			rect->InsetBy(0.5, 0.5);
873 		}
874 	}
875 }
876 
877 // DrawEllipse
878 BRect
879 Painter::DrawEllipse(BRect r, bool fill) const
880 {
881 	CHECK_CLIPPING
882 
883 	AlignEllipseRect(&r, fill);
884 
885 	float xRadius = r.Width() / 2.0;
886 	float yRadius = r.Height() / 2.0;
887 	BPoint center(r.left + xRadius, r.top + yRadius);
888 
889 	int32 divisions = (int32)((xRadius + yRadius + 2 * fPenSize) * PI / 2);
890 	if (divisions < 12)
891 		divisions = 12;
892 	if (divisions > 4096)
893 		divisions = 4096;
894 
895 	if (fill) {
896 		agg::ellipse path(center.x, center.y, xRadius, yRadius, divisions);
897 
898 		return _FillPath(path);
899 	} else {
900 		// NOTE: This implementation might seem a little strange, but it makes
901 		// stroked ellipses look like on R5. A more correct way would be to use
902 		// _StrokePath(), but it currently has its own set of problems with narrow
903 		// ellipses (for small xRadii or yRadii).
904 		float inset = fPenSize / 2.0;
905 		agg::ellipse inner(center.x, center.y,
906 						   max_c(0.0, xRadius - inset),
907 						   max_c(0.0, yRadius - inset),
908 						   divisions);
909 		agg::ellipse outer(center.x, center.y,
910 						   xRadius + inset,
911 						   yRadius + inset,
912 						   divisions);
913 
914 		fRasterizer->reset();
915 		fRasterizer->add_path(outer);
916 		fRasterizer->add_path(inner);
917 
918 		// make the inner ellipse work as a hole
919 		fRasterizer->filling_rule(agg::fill_even_odd);
920 
921 		if (fPenSize > 4)
922 			agg::render_scanlines(*fRasterizer, *fPackedScanline, *fRenderer);
923 		else
924 			agg::render_scanlines(*fRasterizer, *fUnpackedScanline, *fRenderer);
925 
926 		// reset to default
927 		fRasterizer->filling_rule(agg::fill_non_zero);
928 
929 		return _Clipped(_BoundingBox(outer));
930 	}
931 }
932 
933 // StrokeArc
934 BRect
935 Painter::StrokeArc(BPoint center, float xRadius, float yRadius,
936 				   float angle, float span) const
937 {
938 	CHECK_CLIPPING
939 
940 	_Transform(&center);
941 
942 	double angleRad = (angle * PI) / 180.0;
943 	double spanRad = (span * PI) / 180.0;
944 	agg::bezier_arc arc(center.x, center.y, xRadius, yRadius,
945 						-angleRad, -spanRad);
946 
947 	agg::conv_curve<agg::bezier_arc> path(arc);
948 	path.approximation_scale(2.0);
949 
950 	return _StrokePath(path);
951 }
952 
953 // FillArc
954 BRect
955 Painter::FillArc(BPoint center, float xRadius, float yRadius,
956 				 float angle, float span) const
957 {
958 	CHECK_CLIPPING
959 
960 	_Transform(&center);
961 
962 	double angleRad = (angle * PI) / 180.0;
963 	double spanRad = (span * PI) / 180.0;
964 	agg::bezier_arc arc(center.x, center.y, xRadius, yRadius,
965 						-angleRad, -spanRad);
966 
967 	agg::conv_curve<agg::bezier_arc> segmentedArc(arc);
968 
969 	fPath.remove_all();
970 
971 	// build a new path by starting at the center point,
972 	// then traversing the arc, then going back to the center
973 	fPath.move_to(center.x, center.y);
974 
975 	segmentedArc.rewind(0);
976 	double x;
977 	double y;
978 	unsigned cmd = segmentedArc.vertex(&x, &y);
979 	while (!agg::is_stop(cmd)) {
980 		fPath.line_to(x, y);
981 		cmd = segmentedArc.vertex(&x, &y);
982 	}
983 
984 	fPath.close_polygon();
985 
986 	return _FillPath(fPath);
987 }
988 
989 // #pragma mark -
990 
991 // DrawString
992 BRect
993 Painter::DrawString(const char* utf8String, uint32 length,
994 					BPoint baseLine, const escapement_delta* delta)
995 {
996 	CHECK_CLIPPING
997 
998 	if (!fSubpixelPrecise) {
999 		baseLine.x = roundf(baseLine.x);
1000 		baseLine.y = roundf(baseLine.y);
1001 	}
1002 
1003 	BRect bounds(0.0, 0.0, -1.0, -1.0);
1004 
1005 	SetPattern(B_SOLID_HIGH, true);
1006 
1007 	if (fBuffer) {
1008 		bounds = fTextRenderer->RenderString(utf8String,
1009 											 length,
1010 											 fRenderer,
1011 											 fRendererBin,
1012 											 baseLine,
1013 											 fClippingRegion->Frame(),
1014 											 false,
1015 											 &fPenLocation,
1016 											 delta);
1017 	}
1018 	return _Clipped(bounds);
1019 }
1020 
1021 // BoundingBox
1022 BRect
1023 Painter::BoundingBox(const char* utf8String, uint32 length,
1024 					 BPoint baseLine, BPoint* penLocation,
1025 					 const escapement_delta* delta) const
1026 {
1027 	if (!fSubpixelPrecise) {
1028 		baseLine.x = roundf(baseLine.x);
1029 		baseLine.y = roundf(baseLine.y);
1030 	}
1031 
1032 	static BRect dummy;
1033 	return fTextRenderer->RenderString(utf8String,
1034 									   length,
1035 									   fRenderer,
1036 									   fRendererBin,
1037 									   baseLine, dummy, true, penLocation,
1038 									   delta);
1039 }
1040 
1041 // StringWidth
1042 float
1043 Painter::StringWidth(const char* utf8String, uint32 length, const DrawState* context)
1044 {
1045 	SetFont(context->Font());
1046 	return fTextRenderer->StringWidth(utf8String, length);
1047 }
1048 
1049 // #pragma mark -
1050 
1051 // DrawBitmap
1052 BRect
1053 Painter::DrawBitmap(const ServerBitmap* bitmap,
1054 					BRect bitmapRect, BRect viewRect) const
1055 {
1056 	CHECK_CLIPPING
1057 
1058 	BRect touched = _Clipped(viewRect);
1059 
1060 	if (bitmap && bitmap->IsValid() && touched.IsValid()) {
1061 		// the native bitmap coordinate system
1062 		BRect actualBitmapRect(bitmap->Bounds());
1063 
1064 		agg::rendering_buffer srcBuffer;
1065 		srcBuffer.attach(bitmap->Bits(),
1066 						 bitmap->Width(),
1067 						 bitmap->Height(),
1068 						 bitmap->BytesPerRow());
1069 
1070 		_DrawBitmap(srcBuffer, bitmap->ColorSpace(), actualBitmapRect, bitmapRect, viewRect);
1071 	}
1072 	return touched;
1073 }
1074 
1075 // #pragma mark -
1076 
1077 // FillRegion
1078 BRect
1079 Painter::FillRegion(const BRegion* region) const
1080 {
1081 	CHECK_CLIPPING
1082 
1083 	BRegion copy(*region);
1084 	int32 count = copy.CountRects();
1085 	BRect touched = FillRect(copy.RectAt(0));
1086 	for (int32 i = 1; i < count; i++) {
1087 		touched = touched | FillRect(copy.RectAt(i));
1088 	}
1089 	return touched;
1090 }
1091 
1092 // InvertRect
1093 BRect
1094 Painter::InvertRect(const BRect& r) const
1095 {
1096 	CHECK_CLIPPING
1097 
1098 	BRegion region(r);
1099 	if (fClippingRegion) {
1100 		region.IntersectWith(fClippingRegion);
1101 	}
1102 	// implementation only for B_RGB32 at the moment
1103 	int32 count = region.CountRects();
1104 	for (int32 i = 0; i < count; i++) {
1105 		_InvertRect32(region.RectAt(i));
1106 	}
1107 	return _Clipped(r);
1108 }
1109 
1110 // #pragma mark - private
1111 
1112 // _MakeEmpty
1113 void
1114 Painter::_MakeEmpty()
1115 {
1116 	delete fBuffer;
1117 	fBuffer = NULL;
1118 
1119 	delete fPixelFormat;
1120 	fPixelFormat = NULL;
1121 
1122 	delete fBaseRenderer;
1123 	fBaseRenderer = NULL;
1124 
1125 #if USE_OUTLINE_RASTERIZER
1126 	delete fOutlineRenderer;
1127 	fOutlineRenderer = NULL;
1128 
1129 	delete fOutlineRasterizer;
1130 	fOutlineRasterizer = NULL;
1131 #endif
1132 
1133 	delete fUnpackedScanline;
1134 	fUnpackedScanline = NULL;
1135 	delete fPackedScanline;
1136 	fPackedScanline = NULL;
1137 
1138 	delete fRasterizer;
1139 	fRasterizer = NULL;
1140 
1141 	delete fRenderer;
1142 	fRenderer = NULL;
1143 
1144 	delete fRendererBin;
1145 	fRendererBin = NULL;
1146 }
1147 
1148 // _Transform
1149 void
1150 Painter::_Transform(BPoint* point, bool centerOffset) const
1151 {
1152 	// rounding
1153 	if (!fSubpixelPrecise) {
1154 		// TODO: validate usage of floor() for values < 0
1155 		point->x = floorf(point->x);
1156 		point->y = floorf(point->y);
1157 	}
1158 	// this code is supposed to move coordinates to the center of pixels,
1159 	// as AGG considers (0,0) to be the "upper left corner" of a pixel,
1160 	// but BViews are less strict on those details
1161 	if (centerOffset) {
1162 		point->x += 0.5;
1163 		point->y += 0.5;
1164 	}
1165 }
1166 
1167 // _Transform
1168 BPoint
1169 Painter::_Transform(const BPoint& point, bool centerOffset) const
1170 {
1171 	BPoint ret = point;
1172 	_Transform(&ret, centerOffset);
1173 	return ret;
1174 }
1175 
1176 // _Clipped
1177 BRect
1178 Painter::_Clipped(const BRect& rect) const
1179 {
1180 	if (rect.IsValid()) {
1181 		return BRect(rect & fClippingRegion->Frame());
1182 	}
1183 	return BRect(rect);
1184 }
1185 
1186 // _UpdateFont
1187 void
1188 Painter::_UpdateFont()
1189 {
1190 	fTextRenderer->SetFont(fFont);
1191 }
1192 
1193 // _UpdateLineWidth
1194 void
1195 Painter::_UpdateLineWidth()
1196 {
1197 	fLineProfile.width(fPenSize);
1198 }
1199 
1200 // _UpdateDrawingMode
1201 void
1202 Painter::_UpdateDrawingMode(bool drawingText)
1203 {
1204 	// The AGG renderers have their own color setting, however
1205 	// almost all drawing mode classes ignore the color given
1206 	// by the AGG renderer and use the colors from the PatternHandler
1207 	// instead. If we have a B_SOLID_* pattern, we can actually use
1208 	// the color in the renderer and special versions of drawing modes
1209 	// that don't use PatternHandler and are more efficient. This
1210 	// has been implemented for B_OP_COPY and a couple others (the
1211 	// DrawingMode*Solid ones) as of now. The PixelFormat knows the
1212 	// PatternHandler and makes its decision based on the pattern.
1213 	// The last parameter to SetDrawingMode() is a flag if a special
1214 	// for when Painter is used to draw text. In this case, another
1215 	// special version of B_OP_COPY is used that acts like R5 in that
1216 	// anti-aliased pixel are not rendered against the actual background
1217 	// but the current low color instead. This way, the frame buffer
1218 	// doesn't need to be read.
1219 	// When a solid pattern is used, _SetRendererColor()
1220 	// has to be called so that all internal colors in the renderes
1221 	// are up to date for use by the solid drawing mode version.
1222 	fPixelFormat->SetDrawingMode(fDrawingMode, fAlphaSrcMode,
1223 								 fAlphaFncMode, drawingText);
1224 }
1225 
1226 // _SetRendererColor
1227 void
1228 Painter::_SetRendererColor(const rgb_color& color) const
1229 {
1230 #if USE_OUTLINE_RASTERIZER
1231 	if (fOutlineRenderer)
1232 #if ALIASED_DRAWING
1233 		fOutlineRenderer->line_color(agg::rgba(color.red / 255.0,
1234 											   color.green / 255.0,
1235 											   color.blue / 255.0,
1236 											   color.alpha / 255.0));
1237 #else
1238 		fOutlineRenderer->color(agg::rgba(color.red / 255.0,
1239 										  color.green / 255.0,
1240 										  color.blue / 255.0,
1241 										  color.alpha / 255.0));
1242 #endif // ALIASED_DRAWING
1243 #endif // USE_OUTLINE_RASTERIZER
1244 	if (fRenderer)
1245 		fRenderer->color(agg::rgba(color.red / 255.0,
1246 								   color.green / 255.0,
1247 								   color.blue / 255.0,
1248 								   color.alpha / 255.0));
1249 // TODO: bitmap fonts not yet correctly setup in AGGTextRenderer
1250 //	if (fRendererBin)
1251 //		fRendererBin->color(agg::rgba(color.red / 255.0,
1252 //									  color.green / 255.0,
1253 //									  color.blue / 255.0,
1254 //									  color.alpha / 255.0));
1255 
1256 }
1257 
1258 // #pragma mark -
1259 
1260 // _DrawTriangle
1261 inline BRect
1262 Painter::_DrawTriangle(BPoint pt1, BPoint pt2, BPoint pt3, bool fill) const
1263 {
1264 	CHECK_CLIPPING
1265 
1266 	_Transform(&pt1);
1267 	_Transform(&pt2);
1268 	_Transform(&pt3);
1269 
1270 	fPath.remove_all();
1271 
1272 	fPath.move_to(pt1.x, pt1.y);
1273 	fPath.line_to(pt2.x, pt2.y);
1274 	fPath.line_to(pt3.x, pt3.y);
1275 
1276 	fPath.close_polygon();
1277 
1278 	if (fill)
1279 		return _FillPath(fPath);
1280 	else
1281 		return _StrokePath(fPath);
1282 }
1283 
1284 // copy_bitmap_row_cmap8_copy
1285 static inline void
1286 copy_bitmap_row_cmap8_copy(uint8* dst, const uint8* src, int32 numPixels,
1287 						   const rgb_color* colorMap)
1288 {
1289 	uint32* d = (uint32*)dst;
1290 	const uint8* s = src;
1291 	while (numPixels--) {
1292 		const rgb_color c = colorMap[*s++];
1293 		*d++ = (c.alpha << 24) | (c.red << 16) | (c.green << 8) | (c.blue);
1294 	}
1295 }
1296 
1297 // copy_bitmap_row_cmap8_over
1298 static inline void
1299 copy_bitmap_row_cmap8_over(uint8* dst, const uint8* src, int32 numPixels,
1300 						   const rgb_color* colorMap)
1301 {
1302 	uint32* d = (uint32*)dst;
1303 	const uint8* s = src;
1304 	while (numPixels--) {
1305 		const rgb_color c = colorMap[*s++];
1306 		if (c.alpha)
1307 			*d = (c.alpha << 24) | (c.red << 16) | (c.green << 8) | (c.blue);
1308 		d++;
1309 	}
1310 }
1311 
1312 // copy_bitmap_row_bgr32_copy
1313 static inline void
1314 copy_bitmap_row_bgr32_copy(uint8* dst, const uint8* src, int32 numPixels,
1315 						   const rgb_color* colorMap)
1316 {
1317 	memcpy(dst, src, numPixels * 4);
1318 }
1319 
1320 // copy_bitmap_row_bgr32_alpha
1321 static inline void
1322 copy_bitmap_row_bgr32_alpha(uint8* dst, const uint8* src, int32 numPixels,
1323 							const rgb_color* colorMap)
1324 {
1325 	uint32* d = (uint32*)dst;
1326 	int32 bytes = numPixels * 4;
1327 	uint8 buffer[bytes];
1328 	uint8* b = buffer;
1329 	while (numPixels--) {
1330 		if (src[3] == 255) {
1331 			*(uint32*)b = *(uint32*)src;
1332 		} else {
1333 			*(uint32*)b = *d;
1334 			b[0] = ((src[0] - b[0]) * src[3] + (b[0] << 8)) >> 8;
1335 			b[1] = ((src[1] - b[1]) * src[3] + (b[1] << 8)) >> 8;
1336 			b[2] = ((src[2] - b[2]) * src[3] + (b[2] << 8)) >> 8;
1337 		}
1338 		d ++;
1339 		b += 4;
1340 		src += 4;
1341 	}
1342 	memcpy(dst, buffer, bytes);
1343 }
1344 
1345 // _DrawBitmap
1346 void
1347 Painter::_DrawBitmap(agg::rendering_buffer& srcBuffer, color_space format,
1348 					 BRect actualBitmapRect, BRect bitmapRect,
1349 					 BRect viewRect) const
1350 {
1351 	if (!fBuffer || !fValidClipping
1352 		|| !bitmapRect.IsValid() || !bitmapRect.Intersects(actualBitmapRect)
1353 		|| !viewRect.IsValid()) {
1354 		return;
1355 	}
1356 
1357 	if (!fSubpixelPrecise) {
1358 		align_rect_to_pixels(&viewRect);
1359 		align_rect_to_pixels(&bitmapRect);
1360 	}
1361 
1362 	double xScale = (viewRect.Width() + 1) / (bitmapRect.Width() + 1);
1363 	double yScale = (viewRect.Height() + 1) / (bitmapRect.Height() + 1);
1364 
1365 	if (xScale == 0.0 || yScale == 0.0)
1366 		return;
1367 
1368 	// compensate for the lefttop offset the actualBitmapRect might have
1369 	// actualBitmapRect has the right size, but put it at B_ORIGIN
1370 	// bitmapRect is already in good coordinates
1371 	actualBitmapRect.OffsetBy(-actualBitmapRect.left, -actualBitmapRect.top);
1372 
1373 	// constrain rect to passed bitmap bounds
1374 	// and transfer the changes to the viewRect with the right scale
1375 	if (bitmapRect.left < actualBitmapRect.left) {
1376 		float diff = actualBitmapRect.left - bitmapRect.left;
1377 		viewRect.left += diff * xScale;
1378 		bitmapRect.left = actualBitmapRect.left;
1379 	}
1380 	if (bitmapRect.top < actualBitmapRect.top) {
1381 		float diff = actualBitmapRect.top - bitmapRect.top;
1382 		viewRect.top += diff * yScale;
1383 		bitmapRect.top = actualBitmapRect.top;
1384 	}
1385 	if (bitmapRect.right > actualBitmapRect.right) {
1386 		float diff = bitmapRect.right - actualBitmapRect.right;
1387 		viewRect.right -= diff * xScale;
1388 		bitmapRect.right = actualBitmapRect.right;
1389 	}
1390 	if (bitmapRect.bottom > actualBitmapRect.bottom) {
1391 		float diff = bitmapRect.bottom - actualBitmapRect.bottom;
1392 		viewRect.bottom -= diff * yScale;
1393 		bitmapRect.bottom = actualBitmapRect.bottom;
1394 	}
1395 
1396 	double xOffset = viewRect.left - bitmapRect.left;
1397 	double yOffset = viewRect.top - bitmapRect.top;
1398 
1399 	switch (format) {
1400 		case B_RGB32:
1401 		case B_RGBA32: {
1402 			// maybe we can use an optimized version
1403 			if (xScale == 1.0 && yScale == 1.0) {
1404 				if (fDrawingMode == B_OP_COPY) {
1405 					_DrawBitmapNoScale32(copy_bitmap_row_bgr32_copy, 4,
1406 										 srcBuffer, xOffset, yOffset, viewRect);
1407 					return;
1408 				} else if (fDrawingMode == B_OP_ALPHA
1409 						 && fAlphaSrcMode == B_PIXEL_ALPHA
1410 						 && fAlphaFncMode == B_ALPHA_OVERLAY) {
1411 					_DrawBitmapNoScale32(copy_bitmap_row_bgr32_alpha, 4,
1412 										 srcBuffer, xOffset, yOffset, viewRect);
1413 					return;
1414 				}
1415 			}
1416 
1417 			_DrawBitmapGeneric32(srcBuffer, xOffset, yOffset,
1418 								 xScale, yScale, viewRect);
1419 			break;
1420 		}
1421 		default: {
1422 			if (format == B_CMAP8 && xScale == 1.0 && yScale == 1.0) {
1423 				if (fDrawingMode == B_OP_COPY) {
1424 					_DrawBitmapNoScale32(copy_bitmap_row_cmap8_copy, 1,
1425 										 srcBuffer, xOffset, yOffset, viewRect);
1426 					return;
1427 				} else if (fDrawingMode == B_OP_OVER) {
1428 					_DrawBitmapNoScale32(copy_bitmap_row_cmap8_over, 1,
1429 										 srcBuffer, xOffset, yOffset, viewRect);
1430 					return;
1431 				}
1432 			}
1433 
1434 			// TODO: this is only a temporary implementation,
1435 			// to really handle other colorspaces, one would
1436 			// rather do the conversion with much less overhead,
1437 			// for example in the nn filter (hm), or in the
1438 			// scanline generator (better)
1439 			// maybe we can use an optimized version
1440 			BBitmap temp(actualBitmapRect, B_BITMAP_NO_SERVER_LINK, B_RGBA32);
1441 			status_t err = temp.ImportBits(srcBuffer.buf(),
1442 										   srcBuffer.height() * srcBuffer.stride(),
1443 										   srcBuffer.stride(),
1444 										   0, format);
1445 			if (err >= B_OK) {
1446 				agg::rendering_buffer convertedBuffer;
1447 				convertedBuffer.attach((uint8*)temp.Bits(),
1448 									   (uint32)actualBitmapRect.IntegerWidth() + 1,
1449 									   (uint32)actualBitmapRect.IntegerHeight() + 1,
1450 									   temp.BytesPerRow());
1451 				_DrawBitmapGeneric32(convertedBuffer, xOffset, yOffset,
1452 									 xScale, yScale, viewRect);
1453 			} else {
1454 				fprintf(stderr, "Painter::_DrawBitmap() - "
1455 						"colorspace conversion failed: %s\n", strerror(err));
1456 			}
1457 			break;
1458 		}
1459 	}
1460 }
1461 
1462 #define DEBUG_DRAW_BITMAP 0
1463 
1464 // _DrawBitmapNoScale32
1465 template <class F>
1466 void
1467 Painter::_DrawBitmapNoScale32(F copyRowFunction, uint32 bytesPerSourcePixel,
1468 							  agg::rendering_buffer& srcBuffer,
1469 							  int32 xOffset, int32 yOffset,
1470 							  BRect viewRect) const
1471 {
1472 	// NOTE: this would crash if viewRect was large enough to read outside the
1473 	// bitmap, so make sure this is not the case before calling this function!
1474 	uint8* dst = fBuffer->row_ptr(0);
1475 	uint32 dstBPR = fBuffer->stride();
1476 
1477 	const uint8* src = srcBuffer.row_ptr(0);
1478 	uint32 srcBPR = srcBuffer.stride();
1479 
1480 	int32 left = (int32)viewRect.left;
1481 	int32 top = (int32)viewRect.top;
1482 	int32 right = (int32)viewRect.right;
1483 	int32 bottom = (int32)viewRect.bottom;
1484 
1485 #if DEBUG_DRAW_BITMAP
1486 if (left - xOffset < 0 || left - xOffset >= (int32)srcBuffer.width() ||
1487 	right - xOffset >= (int32)srcBuffer.width() ||
1488 	top - yOffset < 0 || top - yOffset >= (int32)srcBuffer.height() ||
1489 	bottom - yOffset >= (int32)srcBuffer.height()) {
1490 
1491 	char message[256];
1492 	sprintf(message, "reading outside of bitmap (%ld, %ld, %ld, %ld) "
1493 			"(%d, %d) (%ld, %ld)",
1494 		left - xOffset, top - yOffset, right - xOffset, bottom - yOffset,
1495 		srcBuffer.width(), srcBuffer.height(), xOffset, yOffset);
1496 	debugger(message);
1497 }
1498 #endif
1499 
1500 	const rgb_color* colorMap = SystemPalette();
1501 
1502 	// copy rects, iterate over clipping boxes
1503 	fBaseRenderer->first_clip_box();
1504 	do {
1505 		int32 x1 = max_c(fBaseRenderer->xmin(), left);
1506 		int32 x2 = min_c(fBaseRenderer->xmax(), right);
1507 		if (x1 <= x2) {
1508 			int32 y1 = max_c(fBaseRenderer->ymin(), top);
1509 			int32 y2 = min_c(fBaseRenderer->ymax(), bottom);
1510 			if (y1 <= y2) {
1511 				uint8* dstHandle = dst + y1 * dstBPR + x1 * 4;
1512 				const uint8* srcHandle = src + (y1 - yOffset) * srcBPR
1513 					+ (x1 - xOffset) * bytesPerSourcePixel;
1514 
1515 				for (; y1 <= y2; y1++) {
1516 					copyRowFunction(dstHandle, srcHandle, x2 - x1 + 1,
1517 									colorMap);
1518 
1519 					dstHandle += dstBPR;
1520 					srcHandle += srcBPR;
1521 				}
1522 			}
1523 		}
1524 	} while (fBaseRenderer->next_clip_box());
1525 }
1526 
1527 // _DrawBitmapGeneric32
1528 void
1529 Painter::_DrawBitmapGeneric32(agg::rendering_buffer& srcBuffer,
1530 							  double xOffset, double yOffset,
1531 							  double xScale, double yScale,
1532 							  BRect viewRect) const
1533 {
1534 	// AGG pipeline
1535 
1536 	// pixel format attached to bitmap
1537 	typedef agg::pixfmt_bgra32 pixfmt_image;
1538 	pixfmt_image pixf_img(srcBuffer);
1539 
1540 	agg::trans_affine srcMatrix;
1541 // NOTE: R5 seems to ignore this offset when drawing bitmaps
1542 //	srcMatrix *= agg::trans_affine_translation(-actualBitmapRect.left,
1543 //											   -actualBitmapRect.top);
1544 
1545 	agg::trans_affine imgMatrix;
1546 	imgMatrix *= agg::trans_affine_scaling(xScale, yScale);
1547 	imgMatrix *= agg::trans_affine_translation(xOffset, yOffset);
1548 	imgMatrix.invert();
1549 
1550 	// image interpolator
1551 	typedef agg::span_interpolator_linear<> interpolator_type;
1552 	interpolator_type interpolator(imgMatrix);
1553 
1554 	// scanline allocator
1555 	agg::span_allocator<pixfmt_image::color_type> spanAllocator;
1556 
1557 	// image accessor attached to pixel format of bitmap
1558 	typedef agg::image_accessor_clip<pixfmt_image> source_type;
1559 	source_type source(pixf_img, agg::rgba8(0, 0, 0, 0));
1560 
1561 	// image filter (nearest neighbor)
1562 	typedef agg::span_image_filter_rgba_nn<source_type,
1563 										   interpolator_type> span_gen_type;
1564 	span_gen_type spanGenerator(source, interpolator);
1565 
1566 
1567 	// clip to the current clipping region's frame
1568 	viewRect = viewRect & fClippingRegion->Frame();
1569 	// convert to pixel coords (versus pixel indices)
1570 	viewRect.right++;
1571 	viewRect.bottom++;
1572 
1573 	// path enclosing the bitmap
1574 	fPath.remove_all();
1575 	fPath.move_to(viewRect.left, viewRect.top);
1576 	fPath.line_to(viewRect.right, viewRect.top);
1577 	fPath.line_to(viewRect.right, viewRect.bottom);
1578 	fPath.line_to(viewRect.left, viewRect.bottom);
1579 	fPath.close_polygon();
1580 
1581 	agg::conv_transform<agg::path_storage> transformedPath(fPath, srcMatrix);
1582 	fRasterizer->reset();
1583 	fRasterizer->add_path(transformedPath);
1584 
1585 	// render the path with the bitmap as scanline fill
1586 	agg::render_scanlines_aa(*fRasterizer,
1587 							 *fUnpackedScanline,
1588 							 *fBaseRenderer,
1589 							 spanAllocator,
1590 							 spanGenerator);
1591 }
1592 
1593 // _InvertRect32
1594 void
1595 Painter::_InvertRect32(BRect r) const
1596 {
1597 	if (fBuffer) {
1598 		int32 width = r.IntegerWidth() + 1;
1599 		for (int32 y = (int32)r.top; y <= (int32)r.bottom; y++) {
1600 			uint8* dst = fBuffer->row_ptr(y);
1601 			dst += (int32)r.left * 4;
1602 			for (int32 i = 0; i < width; i++) {
1603 				dst[0] = 255 - dst[0];
1604 				dst[1] = 255 - dst[1];
1605 				dst[2] = 255 - dst[2];
1606 				dst += 4;
1607 			}
1608 		}
1609 	}
1610 }
1611 
1612 // _BlendRect32
1613 void
1614 Painter::_BlendRect32(const BRect& r, const rgb_color& c) const
1615 {
1616 	if (fBuffer && fValidClipping) {
1617 		uint8* dst = fBuffer->row_ptr(0);
1618 		uint32 bpr = fBuffer->stride();
1619 
1620 		int32 left = (int32)r.left;
1621 		int32 top = (int32)r.top;
1622 		int32 right = (int32)r.right;
1623 		int32 bottom = (int32)r.bottom;
1624 
1625 		// fill rects, iterate over clipping boxes
1626 		fBaseRenderer->first_clip_box();
1627 		do {
1628 			int32 x1 = max_c(fBaseRenderer->xmin(), left);
1629 			int32 x2 = min_c(fBaseRenderer->xmax(), right);
1630 			if (x1 <= x2) {
1631 				int32 y1 = max_c(fBaseRenderer->ymin(), top);
1632 				int32 y2 = min_c(fBaseRenderer->ymax(), bottom);
1633 
1634 				uint8* offset = dst + x1 * 4 + y1 * bpr;
1635 				for (; y1 <= y2; y1++) {
1636 					blend_line32(offset, x2 - x1 + 1, c.red, c.green, c.blue, c.alpha);
1637 					offset += bpr;
1638 				}
1639 			}
1640 		} while (fBaseRenderer->next_clip_box());
1641 	}
1642 }
1643 
1644 // #pragma mark -
1645 
1646 template<class VertexSource>
1647 BRect
1648 Painter::_BoundingBox(VertexSource& path) const
1649 {
1650 	double left = 0.0;
1651 	double top = 0.0;
1652 	double right = -1.0;
1653 	double bottom = -1.0;
1654 	uint32 pathID[1];
1655 	pathID[0] = 0;
1656 	agg::bounding_rect(path, pathID, 0, 1, &left, &top, &right, &bottom);
1657 	return BRect(left, top, right, bottom);
1658 }
1659 
1660 // agg_line_cap_mode_for
1661 inline agg::line_cap_e
1662 agg_line_cap_mode_for(cap_mode mode)
1663 {
1664 	switch (mode) {
1665 		case B_BUTT_CAP:
1666 			return agg::butt_cap;
1667 		case B_SQUARE_CAP:
1668 			return agg::square_cap;
1669 		case B_ROUND_CAP:
1670 			return agg::round_cap;
1671 	}
1672 	return agg::butt_cap;
1673 }
1674 
1675 // agg_line_join_mode_for
1676 inline agg::line_join_e
1677 agg_line_join_mode_for(join_mode mode)
1678 {
1679 	switch (mode) {
1680 		case B_MITER_JOIN:
1681 			return agg::miter_join;
1682 		case B_ROUND_JOIN:
1683 			return agg::round_join;
1684 		case B_BEVEL_JOIN:
1685 		case B_BUTT_JOIN: // ??
1686 		case B_SQUARE_JOIN: // ??
1687 			return agg::bevel_join;
1688 	}
1689 	return agg::miter_join;
1690 }
1691 
1692 // _StrokePath
1693 template<class VertexSource>
1694 BRect
1695 Painter::_StrokePath(VertexSource& path) const
1696 {
1697 #if USE_OUTLINE_RASTERIZER
1698 	fOutlineRasterizer->add_path(path);
1699 #else
1700 //	if (fPenSize > 1.0) {
1701 		agg::conv_stroke<VertexSource> stroke(path);
1702 		stroke.width(fPenSize);
1703 
1704 		// special case line width = 1 with square caps
1705 		// this has a couple of advantages and it looks
1706 		// like this is also the R5 behaviour.
1707 		if (fPenSize == 1.0 && fLineCapMode == B_BUTT_CAP) {
1708 			stroke.line_cap(agg::square_cap);
1709 		} else {
1710 			stroke.line_cap(agg_line_cap_mode_for(fLineCapMode));
1711 		}
1712 		stroke.line_join(agg_line_join_mode_for(fLineJoinMode));
1713 		stroke.miter_limit(fMiterLimit);
1714 
1715 		fRasterizer->reset();
1716 		fRasterizer->add_path(stroke);
1717 
1718 		agg::render_scanlines(*fRasterizer, *fPackedScanline, *fRenderer);
1719 //	} else {
1720 	// TODO: update to AGG 2.3 to get rid of the remaining problems:
1721 	// rects which are 2 or 1 pixel high/wide don't render at all.
1722 //		fOutlineRasterizer->add_path(path);
1723 //	}
1724 #endif
1725 
1726 	BRect touched = _BoundingBox(path);
1727 	float penSize = ceilf(fPenSize / 2.0);
1728 	touched.InsetBy(-penSize, -penSize);
1729 
1730 	return _Clipped(touched);
1731 }
1732 
1733 // _FillPath
1734 template<class VertexSource>
1735 BRect
1736 Painter::_FillPath(VertexSource& path) const
1737 {
1738 	fRasterizer->reset();
1739 	fRasterizer->add_path(path);
1740 	agg::render_scanlines(*fRasterizer, *fPackedScanline, *fRenderer);
1741 
1742 	return _Clipped(_BoundingBox(path));
1743 }
1744 
1745