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