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