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