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