xref: /haiku/src/servers/app/drawing/Painter/Painter.cpp (revision 079c69cbfd7cd3c97baae91332251c8388a8bb02)
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 	// support invalid rects
565 	BPoint a(min_c(r.left, r.right), min_c(r.top, r.bottom));
566 	BPoint b(max_c(r.left, r.right), max_c(r.top, r.bottom));
567 	_Transform(&a, false);
568 	_Transform(&b, false);
569 
570 	// first, try an optimized version
571 	if (fPenSize == 1.0 &&
572 		(fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER)) {
573 		pattern p = *fPatternHandler.GetR5Pattern();
574 		if (p == B_SOLID_HIGH) {
575 			BRect rect(a, b);
576 			StrokeRect(rect,
577 					   fPatternHandler.HighColor());
578 			return _Clipped(rect);
579 		} else if (p == B_SOLID_LOW) {
580 			BRect rect(a, b);
581 			StrokeRect(rect,
582 					   fPatternHandler.LowColor());
583 			return _Clipped(rect);
584 		}
585 	}
586 
587 	if (fmodf(fPenSize, 2.0) != 0.0) {
588 		// shift coords to center of pixels
589 		a.x += 0.5;
590 		a.y += 0.5;
591 		b.x += 0.5;
592 		b.y += 0.5;
593 	}
594 
595 	fPath.remove_all();
596 	fPath.move_to(a.x, a.y);
597 	if (a.x == b.x || a.y == b.y) {
598 		// special case rects with one pixel height or width
599 		fPath.line_to(b.x, b.y);
600 	} else {
601 		fPath.line_to(b.x, a.y);
602 		fPath.line_to(b.x, b.y);
603 		fPath.line_to(a.x, b.y);
604 	}
605 	fPath.close_polygon();
606 
607 	return _StrokePath(fPath);
608 }
609 
610 // StrokeRect
611 void
612 Painter::StrokeRect(const BRect& r, const rgb_color& c) const
613 {
614 	StraightLine(BPoint(r.left, r.top),
615 				 BPoint(r.right - 1, r.top), c);
616 	StraightLine(BPoint(r.right, r.top),
617 				 BPoint(r.right, r.bottom - 1), c);
618 	StraightLine(BPoint(r.right, r.bottom),
619 				 BPoint(r.left + 1, r.bottom), c);
620 	StraightLine(BPoint(r.left, r.bottom),
621 				 BPoint(r.left, r.top + 1), c);
622 }
623 
624 // FillRect
625 BRect
626 Painter::FillRect(const BRect& r) const
627 {
628 	CHECK_CLIPPING
629 
630 	// support invalid rects
631 	BPoint a(min_c(r.left, r.right), min_c(r.top, r.bottom));
632 	BPoint b(max_c(r.left, r.right), max_c(r.top, r.bottom));
633 	_Transform(&a, false);
634 	_Transform(&b, false);
635 
636 	// first, try an optimized version
637 	if (fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER) {
638 		pattern p = *fPatternHandler.GetR5Pattern();
639 		if (p == B_SOLID_HIGH) {
640 			BRect rect(a, b);
641 			FillRect(rect, fPatternHandler.HighColor());
642 			return _Clipped(rect);
643 		} else if (p == B_SOLID_LOW) {
644 			BRect rect(a, b);
645 			FillRect(rect, fPatternHandler.LowColor());
646 			return _Clipped(rect);
647 		}
648 	}
649 	if (fDrawingMode == B_OP_ALPHA && fAlphaFncMode == B_ALPHA_OVERLAY) {
650 		pattern p = *fPatternHandler.GetR5Pattern();
651 		if (p == B_SOLID_HIGH) {
652 			BRect rect(a, b);
653 			_BlendRect32(rect, fPatternHandler.HighColor());
654 			return _Clipped(rect);
655 		} else if (p == B_SOLID_LOW) {
656 			rgb_color c = fPatternHandler.LowColor();
657 			if (fAlphaSrcMode == B_CONSTANT_ALPHA)
658 				c.alpha = fPatternHandler.HighColor().alpha;
659 			BRect rect(a, b);
660 			_BlendRect32(rect, c);
661 			return _Clipped(rect);
662 		}
663 	}
664 
665 
666 	// account for stricter interpretation of coordinates in AGG
667 	// the rectangle ranges from the top-left (.0, .0)
668 	// to the bottom-right (.9999, .9999) corner of pixels
669 	b.x += 1.0;
670 	b.y += 1.0;
671 
672 	fPath.remove_all();
673 	fPath.move_to(a.x, a.y);
674 	fPath.line_to(b.x, a.y);
675 	fPath.line_to(b.x, b.y);
676 	fPath.line_to(a.x, b.y);
677 	fPath.close_polygon();
678 
679 	return _FillPath(fPath);
680 }
681 
682 // FillRect
683 void
684 Painter::FillRect(const BRect& r, const rgb_color& c) const
685 {
686 	if (!fValidClipping)
687 		return;
688 
689 	uint8* dst = fBuffer.row_ptr(0);
690 	uint32 bpr = fBuffer.stride();
691 	int32 left = (int32)r.left;
692 	int32 top = (int32)r.top;
693 	int32 right = (int32)r.right;
694 	int32 bottom = (int32)r.bottom;
695 	// get a 32 bit pixel ready with the color
696 	pixel32 color;
697 	color.data8[0] = c.blue;
698 	color.data8[1] = c.green;
699 	color.data8[2] = c.red;
700 	color.data8[3] = c.alpha;
701 	// fill rects, iterate over clipping boxes
702 	fBaseRenderer.first_clip_box();
703 	do {
704 		int32 x1 = max_c(fBaseRenderer.xmin(), left);
705 		int32 x2 = min_c(fBaseRenderer.xmax(), right);
706 		if (x1 <= x2) {
707 			int32 y1 = max_c(fBaseRenderer.ymin(), top);
708 			int32 y2 = min_c(fBaseRenderer.ymax(), bottom);
709 			uint8* offset = dst + x1 * 4;
710 			for (; y1 <= y2; y1++) {
711 //					uint32* handle = (uint32*)(offset + y1 * bpr);
712 //					for (int32 x = x1; x <= x2; x++) {
713 //						*handle++ = color.data32;
714 //					}
715 gfxset32(offset + y1 * bpr, color.data32, (x2 - x1 + 1) * 4);
716 			}
717 		}
718 	} while (fBaseRenderer.next_clip_box());
719 }
720 
721 // FillRectNoClipping
722 void
723 Painter::FillRectNoClipping(const BRect& r, const rgb_color& c) const
724 {
725 	int32 left = (int32)r.left;
726 	int32 y = (int32)r.top;
727 	int32 right = (int32)r.right;
728 	int32 bottom = (int32)r.bottom;
729 
730 	uint8* dst = fBuffer.row_ptr(y);
731 	uint32 bpr = fBuffer.stride();
732 
733 	// get a 32 bit pixel ready with the color
734 	pixel32 color;
735 	color.data8[0] = c.blue;
736 	color.data8[1] = c.green;
737 	color.data8[2] = c.red;
738 	color.data8[3] = c.alpha;
739 
740 	dst += left * 4;
741 
742 	for (; y <= bottom; y++) {
743 //			uint32* handle = (uint32*)dst;
744 //			for (int32 x = left; x <= right; x++) {
745 //				*handle++ = color.data32;
746 //			}
747 gfxset32(dst, color.data32, (right - left + 1) * 4);
748 		dst += bpr;
749 	}
750 }
751 
752 // StrokeRoundRect
753 BRect
754 Painter::StrokeRoundRect(const BRect& r, float xRadius, float yRadius) const
755 {
756 	CHECK_CLIPPING
757 
758 	BPoint lt(r.left, r.top);
759 	BPoint rb(r.right, r.bottom);
760 	bool centerOffset = fPenSize == 1.0;
761 	// TODO: use this when using _StrokePath()
762 	// bool centerOffset = fmodf(fPenSize, 2.0) != 0.0;
763 	_Transform(&lt, centerOffset);
764 	_Transform(&rb, centerOffset);
765 
766 	if (fPenSize == 1.0) {
767 		agg::rounded_rect rect;
768 		rect.rect(lt.x, lt.y, rb.x, rb.y);
769 		rect.radius(xRadius, yRadius);
770 
771 		return _StrokePath(rect);
772 	} else {
773 		// NOTE: This implementation might seem a little strange, but it makes
774 		// stroked round rects look like on R5. A more correct way would be to use
775 		// _StrokePath() as above (independent from fPenSize).
776 		// The fact that the bounding box of the round rect is not enlarged
777 		// by fPenSize/2 is actually on purpose, though one could argue it is unexpected.
778 
779 		// enclose the right and bottom edge
780 		rb.x++;
781 		rb.y++;
782 
783 		agg::rounded_rect outer;
784 		outer.rect(lt.x, lt.y, rb.x, rb.y);
785 		outer.radius(xRadius, yRadius);
786 
787 		fRasterizer.reset();
788 		fRasterizer.add_path(outer);
789 
790 		// don't add an inner hole if the "size is negative", this avoids some
791 		// defects that can be observed on R5 and could be regarded as a bug.
792 		if (2 * fPenSize < rb.x - lt.x && 2 * fPenSize < rb.y - lt.y) {
793 			agg::rounded_rect inner;
794 			inner.rect(lt.x + fPenSize, lt.y + fPenSize, rb.x - fPenSize, rb.y - fPenSize);
795 			inner.radius(max_c(0.0, xRadius - fPenSize), max_c(0.0, yRadius - fPenSize));
796 
797 			fRasterizer.add_path(inner);
798 		}
799 
800 		// make the inner rect work as a hole
801 		fRasterizer.filling_rule(agg::fill_even_odd);
802 
803 		if (fPenSize > 2)
804 			agg::render_scanlines(fRasterizer, fPackedScanline, fRenderer);
805 		else
806 			agg::render_scanlines(fRasterizer, fUnpackedScanline, fRenderer);
807 
808 		// reset to default
809 		fRasterizer.filling_rule(agg::fill_non_zero);
810 
811 		return _Clipped(_BoundingBox(outer));
812 	}
813 }
814 
815 // FillRoundRect
816 BRect
817 Painter::FillRoundRect(const BRect& r, float xRadius, float yRadius) const
818 {
819 	CHECK_CLIPPING
820 
821 	BPoint lt(r.left, r.top);
822 	BPoint rb(r.right, r.bottom);
823 	_Transform(&lt, false);
824 	_Transform(&rb, false);
825 
826 	// account for stricter interpretation of coordinates in AGG
827 	// the rectangle ranges from the top-left (.0, .0)
828 	// to the bottom-right (.9999, .9999) corner of pixels
829 	rb.x += 1.0;
830 	rb.y += 1.0;
831 
832 	agg::rounded_rect rect;
833 	rect.rect(lt.x, lt.y, rb.x, rb.y);
834 	rect.radius(xRadius, yRadius);
835 
836 	return _FillPath(rect);
837 }
838 
839 // AlignEllipseRect
840 void
841 Painter::AlignEllipseRect(BRect* rect, bool filled) const
842 {
843 	if (!fSubpixelPrecise) {
844 		// align rect to pixels
845 		align_rect_to_pixels(rect);
846 		// account for "pixel index" versus "pixel area"
847 		rect->right++;
848 		rect->bottom++;
849 		if (!filled && fmodf(fPenSize, 2.0) != 0.0) {
850 			// align the stroke
851 			rect->InsetBy(0.5, 0.5);
852 		}
853 	}
854 }
855 
856 // DrawEllipse
857 BRect
858 Painter::DrawEllipse(BRect r, bool fill) const
859 {
860 	CHECK_CLIPPING
861 
862 	AlignEllipseRect(&r, fill);
863 
864 	float xRadius = r.Width() / 2.0;
865 	float yRadius = r.Height() / 2.0;
866 	BPoint center(r.left + xRadius, r.top + yRadius);
867 
868 	int32 divisions = (int32)((xRadius + yRadius + 2 * fPenSize) * PI / 2);
869 	if (divisions < 12)
870 		divisions = 12;
871 	if (divisions > 4096)
872 		divisions = 4096;
873 
874 	if (fill) {
875 		agg::ellipse path(center.x, center.y, xRadius, yRadius, divisions);
876 
877 		return _FillPath(path);
878 	} else {
879 		// NOTE: This implementation might seem a little strange, but it makes
880 		// stroked ellipses look like on R5. A more correct way would be to use
881 		// _StrokePath(), but it currently has its own set of problems with narrow
882 		// ellipses (for small xRadii or yRadii).
883 		float inset = fPenSize / 2.0;
884 		agg::ellipse inner(center.x, center.y,
885 						   max_c(0.0, xRadius - inset),
886 						   max_c(0.0, yRadius - inset),
887 						   divisions);
888 		agg::ellipse outer(center.x, center.y,
889 						   xRadius + inset,
890 						   yRadius + inset,
891 						   divisions);
892 
893 		fRasterizer.reset();
894 		fRasterizer.add_path(outer);
895 		fRasterizer.add_path(inner);
896 
897 		// make the inner ellipse work as a hole
898 		fRasterizer.filling_rule(agg::fill_even_odd);
899 
900 		if (fPenSize > 4)
901 			agg::render_scanlines(fRasterizer, fPackedScanline, fRenderer);
902 		else
903 			agg::render_scanlines(fRasterizer, fUnpackedScanline, fRenderer);
904 
905 		// reset to default
906 		fRasterizer.filling_rule(agg::fill_non_zero);
907 
908 		return _Clipped(_BoundingBox(outer));
909 	}
910 }
911 
912 // StrokeArc
913 BRect
914 Painter::StrokeArc(BPoint center, float xRadius, float yRadius,
915 				   float angle, float span) const
916 {
917 	CHECK_CLIPPING
918 
919 	_Transform(&center);
920 
921 	double angleRad = (angle * PI) / 180.0;
922 	double spanRad = (span * PI) / 180.0;
923 	agg::bezier_arc arc(center.x, center.y, xRadius, yRadius,
924 						-angleRad, -spanRad);
925 
926 	agg::conv_curve<agg::bezier_arc> path(arc);
927 	path.approximation_scale(2.0);
928 
929 	return _StrokePath(path);
930 }
931 
932 // FillArc
933 BRect
934 Painter::FillArc(BPoint center, float xRadius, float yRadius,
935 				 float angle, float span) const
936 {
937 	CHECK_CLIPPING
938 
939 	_Transform(&center);
940 
941 	double angleRad = (angle * PI) / 180.0;
942 	double spanRad = (span * PI) / 180.0;
943 	agg::bezier_arc arc(center.x, center.y, xRadius, yRadius,
944 						-angleRad, -spanRad);
945 
946 	agg::conv_curve<agg::bezier_arc> segmentedArc(arc);
947 
948 	fPath.remove_all();
949 
950 	// build a new path by starting at the center point,
951 	// then traversing the arc, then going back to the center
952 	fPath.move_to(center.x, center.y);
953 
954 	segmentedArc.rewind(0);
955 	double x;
956 	double y;
957 	unsigned cmd = segmentedArc.vertex(&x, &y);
958 	while (!agg::is_stop(cmd)) {
959 		fPath.line_to(x, y);
960 		cmd = segmentedArc.vertex(&x, &y);
961 	}
962 
963 	fPath.close_polygon();
964 
965 	return _FillPath(fPath);
966 }
967 
968 // #pragma mark -
969 
970 // DrawString
971 BRect
972 Painter::DrawString(const char* utf8String, uint32 length,
973 					BPoint baseLine, const escapement_delta* delta,
974 					FontCacheReference* cacheReference)
975 {
976 	CHECK_CLIPPING
977 
978 	if (!fSubpixelPrecise) {
979 		baseLine.x = roundf(baseLine.x);
980 		baseLine.y = roundf(baseLine.y);
981 	}
982 
983 	BRect bounds(0.0, 0.0, -1.0, -1.0);
984 
985 	// text is not rendered with patterns, but we need to
986 	// make sure that the previous pattern is restored
987 	pattern oldPattern = *fPatternHandler.GetR5Pattern();
988 	SetPattern(B_SOLID_HIGH, true);
989 
990 	bounds = fTextRenderer.RenderString(utf8String, length,
991 		baseLine, fClippingRegion->Frame(), false, NULL, delta,
992 		cacheReference);
993 
994 	SetPattern(oldPattern);
995 
996 	return _Clipped(bounds);
997 }
998 
999 // BoundingBox
1000 BRect
1001 Painter::BoundingBox(const char* utf8String, uint32 length,
1002 					 BPoint baseLine, BPoint* penLocation,
1003 					 const escapement_delta* delta,
1004 					 FontCacheReference* cacheReference) const
1005 {
1006 	if (!fSubpixelPrecise) {
1007 		baseLine.x = roundf(baseLine.x);
1008 		baseLine.y = roundf(baseLine.y);
1009 	}
1010 
1011 	static BRect dummy;
1012 	return fTextRenderer.RenderString(utf8String, length,
1013 		baseLine, dummy, true, penLocation, delta, cacheReference);
1014 }
1015 
1016 // StringWidth
1017 float
1018 Painter::StringWidth(const char* utf8String, uint32 length,
1019 	const escapement_delta* delta)
1020 {
1021 	return Font().StringWidth(utf8String, length, delta);
1022 }
1023 
1024 // #pragma mark -
1025 
1026 // DrawBitmap
1027 BRect
1028 Painter::DrawBitmap(const ServerBitmap* bitmap,
1029 					BRect bitmapRect, BRect viewRect) const
1030 {
1031 	CHECK_CLIPPING
1032 
1033 	BRect touched = _Clipped(viewRect);
1034 
1035 	if (bitmap && bitmap->IsValid() && touched.IsValid()) {
1036 		// the native bitmap coordinate system
1037 		BRect actualBitmapRect(bitmap->Bounds());
1038 
1039 		agg::rendering_buffer srcBuffer;
1040 		srcBuffer.attach(bitmap->Bits(),
1041 						 bitmap->Width(),
1042 						 bitmap->Height(),
1043 						 bitmap->BytesPerRow());
1044 
1045 		_DrawBitmap(srcBuffer, bitmap->ColorSpace(), actualBitmapRect,
1046 			bitmapRect, viewRect);
1047 	}
1048 	return touched;
1049 }
1050 
1051 // #pragma mark -
1052 
1053 // FillRegion
1054 BRect
1055 Painter::FillRegion(const BRegion* region) const
1056 {
1057 	CHECK_CLIPPING
1058 
1059 	BRegion copy(*region);
1060 	int32 count = copy.CountRects();
1061 	BRect touched = FillRect(copy.RectAt(0));
1062 	for (int32 i = 1; i < count; i++) {
1063 		touched = touched | FillRect(copy.RectAt(i));
1064 	}
1065 	return touched;
1066 }
1067 
1068 // InvertRect
1069 BRect
1070 Painter::InvertRect(const BRect& r) const
1071 {
1072 	CHECK_CLIPPING
1073 
1074 	BRegion region(r);
1075 	if (fClippingRegion) {
1076 		region.IntersectWith(fClippingRegion);
1077 	}
1078 	// implementation only for B_RGB32 at the moment
1079 	int32 count = region.CountRects();
1080 	for (int32 i = 0; i < count; i++) {
1081 		_InvertRect32(region.RectAt(i));
1082 	}
1083 	return _Clipped(r);
1084 }
1085 
1086 // #pragma mark - private
1087 
1088 // _Transform
1089 inline void
1090 Painter::_Transform(BPoint* point, bool centerOffset) const
1091 {
1092 	// rounding
1093 	if (!fSubpixelPrecise) {
1094 		// TODO: validate usage of floor() for values < 0
1095 		point->x = (int32)point->x;
1096 		point->y = (int32)point->y;
1097 	}
1098 	// this code is supposed to move coordinates to the center of pixels,
1099 	// as AGG considers (0,0) to be the "upper left corner" of a pixel,
1100 	// but BViews are less strict on those details
1101 	if (centerOffset) {
1102 		point->x += 0.5;
1103 		point->y += 0.5;
1104 	}
1105 }
1106 
1107 // _Transform
1108 inline BPoint
1109 Painter::_Transform(const BPoint& point, bool centerOffset) const
1110 {
1111 	BPoint ret = point;
1112 	_Transform(&ret, centerOffset);
1113 	return ret;
1114 }
1115 
1116 // _Clipped
1117 BRect
1118 Painter::_Clipped(const BRect& rect) const
1119 {
1120 	if (rect.IsValid()) {
1121 		return BRect(rect & fClippingRegion->Frame());
1122 	}
1123 	return BRect(rect);
1124 }
1125 
1126 // _UpdateDrawingMode
1127 void
1128 Painter::_UpdateDrawingMode(bool drawingText)
1129 {
1130 	// The AGG renderers have their own color setting, however
1131 	// almost all drawing mode classes ignore the color given
1132 	// by the AGG renderer and use the colors from the PatternHandler
1133 	// instead. If we have a B_SOLID_* pattern, we can actually use
1134 	// the color in the renderer and special versions of drawing modes
1135 	// that don't use PatternHandler and are more efficient. This
1136 	// has been implemented for B_OP_COPY and a couple others (the
1137 	// DrawingMode*Solid ones) as of now. The PixelFormat knows the
1138 	// PatternHandler and makes its decision based on the pattern.
1139 	// The last parameter to SetDrawingMode() is a flag if a special
1140 	// for when Painter is used to draw text. In this case, another
1141 	// special version of B_OP_COPY is used that acts like R5 in that
1142 	// anti-aliased pixel are not rendered against the actual background
1143 	// but the current low color instead. This way, the frame buffer
1144 	// doesn't need to be read.
1145 	// When a solid pattern is used, _SetRendererColor()
1146 	// has to be called so that all internal colors in the renderes
1147 	// are up to date for use by the solid drawing mode version.
1148 	fPixelFormat.SetDrawingMode(fDrawingMode, fAlphaSrcMode,
1149 								fAlphaFncMode, drawingText);
1150 	if (drawingText)
1151 		fPatternHandler.MakeOpCopyColorCache();
1152 }
1153 
1154 // _SetRendererColor
1155 void
1156 Painter::_SetRendererColor(const rgb_color& color) const
1157 {
1158 	fRenderer.color(agg::rgba(color.red / 255.0,
1159 							  color.green / 255.0,
1160 							  color.blue / 255.0,
1161 							  color.alpha / 255.0));
1162 // TODO: bitmap fonts not yet correctly setup in AGGTextRenderer
1163 //	fRendererBin.color(agg::rgba(color.red / 255.0,
1164 //								 color.green / 255.0,
1165 //								 color.blue / 255.0,
1166 //								 color.alpha / 255.0));
1167 }
1168 
1169 // #pragma mark -
1170 
1171 // _DrawTriangle
1172 inline BRect
1173 Painter::_DrawTriangle(BPoint pt1, BPoint pt2, BPoint pt3, bool fill) const
1174 {
1175 	CHECK_CLIPPING
1176 
1177 	_Transform(&pt1);
1178 	_Transform(&pt2);
1179 	_Transform(&pt3);
1180 
1181 	fPath.remove_all();
1182 
1183 	fPath.move_to(pt1.x, pt1.y);
1184 	fPath.line_to(pt2.x, pt2.y);
1185 	fPath.line_to(pt3.x, pt3.y);
1186 
1187 	fPath.close_polygon();
1188 
1189 	if (fill)
1190 		return _FillPath(fPath);
1191 	else
1192 		return _StrokePath(fPath);
1193 }
1194 
1195 // copy_bitmap_row_cmap8_copy
1196 static inline void
1197 copy_bitmap_row_cmap8_copy(uint8* dst, const uint8* src, int32 numPixels,
1198 						   const rgb_color* colorMap)
1199 {
1200 	uint32* d = (uint32*)dst;
1201 	const uint8* s = src;
1202 	while (numPixels--) {
1203 		const rgb_color c = colorMap[*s++];
1204 		*d++ = (c.alpha << 24) | (c.red << 16) | (c.green << 8) | (c.blue);
1205 	}
1206 }
1207 
1208 // copy_bitmap_row_cmap8_over
1209 static inline void
1210 copy_bitmap_row_cmap8_over(uint8* dst, const uint8* src, int32 numPixels,
1211 						   const rgb_color* colorMap)
1212 {
1213 	uint32* d = (uint32*)dst;
1214 	const uint8* s = src;
1215 	while (numPixels--) {
1216 		const rgb_color c = colorMap[*s++];
1217 		if (c.alpha)
1218 			*d = (c.alpha << 24) | (c.red << 16) | (c.green << 8) | (c.blue);
1219 		d++;
1220 	}
1221 }
1222 
1223 // copy_bitmap_row_bgr32_copy
1224 static inline void
1225 copy_bitmap_row_bgr32_copy(uint8* dst, const uint8* src, int32 numPixels,
1226 						   const rgb_color* colorMap)
1227 {
1228 	memcpy(dst, src, numPixels * 4);
1229 }
1230 
1231 // copy_bitmap_row_bgr32_alpha
1232 static inline void
1233 copy_bitmap_row_bgr32_alpha(uint8* dst, const uint8* src, int32 numPixels,
1234 							const rgb_color* colorMap)
1235 {
1236 	uint32* d = (uint32*)dst;
1237 	int32 bytes = numPixels * 4;
1238 	uint8 buffer[bytes];
1239 	uint8* b = buffer;
1240 	while (numPixels--) {
1241 		if (src[3] == 255) {
1242 			*(uint32*)b = *(uint32*)src;
1243 		} else {
1244 			*(uint32*)b = *d;
1245 			b[0] = ((src[0] - b[0]) * src[3] + (b[0] << 8)) >> 8;
1246 			b[1] = ((src[1] - b[1]) * src[3] + (b[1] << 8)) >> 8;
1247 			b[2] = ((src[2] - b[2]) * src[3] + (b[2] << 8)) >> 8;
1248 		}
1249 		d ++;
1250 		b += 4;
1251 		src += 4;
1252 	}
1253 	memcpy(dst, buffer, bytes);
1254 }
1255 
1256 // _DrawBitmap
1257 void
1258 Painter::_DrawBitmap(agg::rendering_buffer& srcBuffer, color_space format,
1259 					 BRect actualBitmapRect, BRect bitmapRect,
1260 					 BRect viewRect) const
1261 {
1262 	if (!fValidClipping
1263 		|| !bitmapRect.IsValid() || !bitmapRect.Intersects(actualBitmapRect)
1264 		|| !viewRect.IsValid()) {
1265 		return;
1266 	}
1267 
1268 	if (!fSubpixelPrecise) {
1269 		align_rect_to_pixels(&viewRect);
1270 		align_rect_to_pixels(&bitmapRect);
1271 	}
1272 
1273 	double xScale = (viewRect.Width() + 1) / (bitmapRect.Width() + 1);
1274 	double yScale = (viewRect.Height() + 1) / (bitmapRect.Height() + 1);
1275 
1276 	if (xScale == 0.0 || yScale == 0.0)
1277 		return;
1278 
1279 	// compensate for the lefttop offset the actualBitmapRect might have
1280 	// actualBitmapRect has the right size, but put it at B_ORIGIN
1281 	// bitmapRect is already in good coordinates
1282 	actualBitmapRect.OffsetBy(-actualBitmapRect.left, -actualBitmapRect.top);
1283 
1284 	// constrain rect to passed bitmap bounds
1285 	// and transfer the changes to the viewRect with the right scale
1286 	if (bitmapRect.left < actualBitmapRect.left) {
1287 		float diff = actualBitmapRect.left - bitmapRect.left;
1288 		viewRect.left += diff * xScale;
1289 		bitmapRect.left = actualBitmapRect.left;
1290 	}
1291 	if (bitmapRect.top < actualBitmapRect.top) {
1292 		float diff = actualBitmapRect.top - bitmapRect.top;
1293 		viewRect.top += diff * yScale;
1294 		bitmapRect.top = actualBitmapRect.top;
1295 	}
1296 	if (bitmapRect.right > actualBitmapRect.right) {
1297 		float diff = bitmapRect.right - actualBitmapRect.right;
1298 		viewRect.right -= diff * xScale;
1299 		bitmapRect.right = actualBitmapRect.right;
1300 	}
1301 	if (bitmapRect.bottom > actualBitmapRect.bottom) {
1302 		float diff = bitmapRect.bottom - actualBitmapRect.bottom;
1303 		viewRect.bottom -= diff * yScale;
1304 		bitmapRect.bottom = actualBitmapRect.bottom;
1305 	}
1306 
1307 	double xOffset = viewRect.left - bitmapRect.left;
1308 	double yOffset = viewRect.top - bitmapRect.top;
1309 
1310 	switch (format) {
1311 		case B_RGB32:
1312 		case B_RGBA32: {
1313 			// maybe we can use an optimized version
1314 			if (xScale == 1.0 && yScale == 1.0) {
1315 				if (fDrawingMode == B_OP_COPY) {
1316 					_DrawBitmapNoScale32(copy_bitmap_row_bgr32_copy, 4,
1317 										 srcBuffer, xOffset, yOffset, viewRect);
1318 					return;
1319 				} else if (fDrawingMode == B_OP_ALPHA
1320 						 && fAlphaSrcMode == B_PIXEL_ALPHA
1321 						 && fAlphaFncMode == B_ALPHA_OVERLAY) {
1322 					_DrawBitmapNoScale32(copy_bitmap_row_bgr32_alpha, 4,
1323 										 srcBuffer, xOffset, yOffset, viewRect);
1324 					return;
1325 				}
1326 			}
1327 
1328 			_DrawBitmapGeneric32(srcBuffer, xOffset, yOffset,
1329 								 xScale, yScale, viewRect);
1330 			break;
1331 		}
1332 		default: {
1333 			if (format == B_CMAP8 && xScale == 1.0 && yScale == 1.0) {
1334 				if (fDrawingMode == B_OP_COPY) {
1335 					_DrawBitmapNoScale32(copy_bitmap_row_cmap8_copy, 1,
1336 										 srcBuffer, xOffset, yOffset, viewRect);
1337 					return;
1338 				} else if (fDrawingMode == B_OP_OVER) {
1339 					_DrawBitmapNoScale32(copy_bitmap_row_cmap8_over, 1,
1340 										 srcBuffer, xOffset, yOffset, viewRect);
1341 					return;
1342 				}
1343 			}
1344 
1345 			// TODO: this is only a temporary implementation,
1346 			// to really handle other colorspaces, one would
1347 			// rather do the conversion with much less overhead,
1348 			// for example in the nn filter (hm), or in the
1349 			// scanline generator (better)
1350 			// maybe we can use an optimized version
1351 			BBitmap temp(actualBitmapRect, B_BITMAP_NO_SERVER_LINK, B_RGBA32);
1352 			status_t err = temp.ImportBits(srcBuffer.buf(),
1353 										   srcBuffer.height() * srcBuffer.stride(),
1354 										   srcBuffer.stride(),
1355 										   0, format);
1356 			if (err >= B_OK) {
1357 				agg::rendering_buffer convertedBuffer;
1358 				convertedBuffer.attach((uint8*)temp.Bits(),
1359 									   (uint32)actualBitmapRect.IntegerWidth() + 1,
1360 									   (uint32)actualBitmapRect.IntegerHeight() + 1,
1361 									   temp.BytesPerRow());
1362 				_DrawBitmapGeneric32(convertedBuffer, xOffset, yOffset,
1363 									 xScale, yScale, viewRect);
1364 			} else {
1365 				fprintf(stderr, "Painter::_DrawBitmap() - "
1366 						"colorspace conversion failed: %s\n", strerror(err));
1367 			}
1368 			break;
1369 		}
1370 	}
1371 }
1372 
1373 #define DEBUG_DRAW_BITMAP 0
1374 
1375 // _DrawBitmapNoScale32
1376 template <class F>
1377 void
1378 Painter::_DrawBitmapNoScale32(F copyRowFunction, uint32 bytesPerSourcePixel,
1379 							  agg::rendering_buffer& srcBuffer,
1380 							  int32 xOffset, int32 yOffset,
1381 							  BRect viewRect) const
1382 {
1383 	// NOTE: this would crash if viewRect was large enough to read outside the
1384 	// bitmap, so make sure this is not the case before calling this function!
1385 	uint8* dst = fBuffer.row_ptr(0);
1386 	uint32 dstBPR = fBuffer.stride();
1387 
1388 	const uint8* src = srcBuffer.row_ptr(0);
1389 	uint32 srcBPR = srcBuffer.stride();
1390 
1391 	int32 left = (int32)viewRect.left;
1392 	int32 top = (int32)viewRect.top;
1393 	int32 right = (int32)viewRect.right;
1394 	int32 bottom = (int32)viewRect.bottom;
1395 
1396 #if DEBUG_DRAW_BITMAP
1397 if (left - xOffset < 0 || left - xOffset >= (int32)srcBuffer.width() ||
1398 	right - xOffset >= (int32)srcBuffer.width() ||
1399 	top - yOffset < 0 || top - yOffset >= (int32)srcBuffer.height() ||
1400 	bottom - yOffset >= (int32)srcBuffer.height()) {
1401 
1402 	char message[256];
1403 	sprintf(message, "reading outside of bitmap (%ld, %ld, %ld, %ld) "
1404 			"(%d, %d) (%ld, %ld)",
1405 		left - xOffset, top - yOffset, right - xOffset, bottom - yOffset,
1406 		srcBuffer.width(), srcBuffer.height(), xOffset, yOffset);
1407 	debugger(message);
1408 }
1409 #endif
1410 
1411 	const rgb_color* colorMap = SystemPalette();
1412 
1413 	// copy rects, iterate over clipping boxes
1414 	fBaseRenderer.first_clip_box();
1415 	do {
1416 		int32 x1 = max_c(fBaseRenderer.xmin(), left);
1417 		int32 x2 = min_c(fBaseRenderer.xmax(), right);
1418 		if (x1 <= x2) {
1419 			int32 y1 = max_c(fBaseRenderer.ymin(), top);
1420 			int32 y2 = min_c(fBaseRenderer.ymax(), bottom);
1421 			if (y1 <= y2) {
1422 				uint8* dstHandle = dst + y1 * dstBPR + x1 * 4;
1423 				const uint8* srcHandle = src + (y1 - yOffset) * srcBPR
1424 					+ (x1 - xOffset) * bytesPerSourcePixel;
1425 
1426 				for (; y1 <= y2; y1++) {
1427 					copyRowFunction(dstHandle, srcHandle, x2 - x1 + 1,
1428 									colorMap);
1429 
1430 					dstHandle += dstBPR;
1431 					srcHandle += srcBPR;
1432 				}
1433 			}
1434 		}
1435 	} while (fBaseRenderer.next_clip_box());
1436 }
1437 
1438 // _DrawBitmapGeneric32
1439 void
1440 Painter::_DrawBitmapGeneric32(agg::rendering_buffer& srcBuffer,
1441 							  double xOffset, double yOffset,
1442 							  double xScale, double yScale,
1443 							  BRect viewRect) const
1444 {
1445 	// AGG pipeline
1446 
1447 	// pixel format attached to bitmap
1448 	typedef agg::pixfmt_bgra32 pixfmt_image;
1449 	pixfmt_image pixf_img(srcBuffer);
1450 
1451 	agg::trans_affine srcMatrix;
1452 // NOTE: R5 seems to ignore this offset when drawing bitmaps
1453 //	srcMatrix *= agg::trans_affine_translation(-actualBitmapRect.left,
1454 //											   -actualBitmapRect.top);
1455 
1456 	agg::trans_affine imgMatrix;
1457 	imgMatrix *= agg::trans_affine_scaling(xScale, yScale);
1458 	imgMatrix *= agg::trans_affine_translation(xOffset, yOffset);
1459 	imgMatrix.invert();
1460 
1461 	// image interpolator
1462 	typedef agg::span_interpolator_linear<> interpolator_type;
1463 	interpolator_type interpolator(imgMatrix);
1464 
1465 	// scanline allocator
1466 	agg::span_allocator<pixfmt_image::color_type> spanAllocator;
1467 
1468 	// image accessor attached to pixel format of bitmap
1469 	typedef agg::image_accessor_clip<pixfmt_image> source_type;
1470 	source_type source(pixf_img, agg::rgba8(0, 0, 0, 0));
1471 
1472 	// image filter (nearest neighbor)
1473 	typedef agg::span_image_filter_rgba_nn<source_type,
1474 										   interpolator_type> span_gen_type;
1475 	span_gen_type spanGenerator(source, interpolator);
1476 
1477 
1478 	// clip to the current clipping region's frame
1479 	viewRect = viewRect & fClippingRegion->Frame();
1480 	// convert to pixel coords (versus pixel indices)
1481 	viewRect.right++;
1482 	viewRect.bottom++;
1483 
1484 	// path enclosing the bitmap
1485 	fPath.remove_all();
1486 	fPath.move_to(viewRect.left, viewRect.top);
1487 	fPath.line_to(viewRect.right, viewRect.top);
1488 	fPath.line_to(viewRect.right, viewRect.bottom);
1489 	fPath.line_to(viewRect.left, viewRect.bottom);
1490 	fPath.close_polygon();
1491 
1492 	agg::conv_transform<agg::path_storage> transformedPath(fPath, srcMatrix);
1493 	fRasterizer.reset();
1494 	fRasterizer.add_path(transformedPath);
1495 
1496 	// render the path with the bitmap as scanline fill
1497 	agg::render_scanlines_aa(fRasterizer,
1498 							 fUnpackedScanline,
1499 							 fBaseRenderer,
1500 							 spanAllocator,
1501 							 spanGenerator);
1502 }
1503 
1504 // _InvertRect32
1505 void
1506 Painter::_InvertRect32(BRect r) const
1507 {
1508 	int32 width = r.IntegerWidth() + 1;
1509 	for (int32 y = (int32)r.top; y <= (int32)r.bottom; y++) {
1510 		uint8* dst = fBuffer.row_ptr(y);
1511 		dst += (int32)r.left * 4;
1512 		for (int32 i = 0; i < width; i++) {
1513 			dst[0] = 255 - dst[0];
1514 			dst[1] = 255 - dst[1];
1515 			dst[2] = 255 - dst[2];
1516 			dst += 4;
1517 		}
1518 	}
1519 }
1520 
1521 // _BlendRect32
1522 void
1523 Painter::_BlendRect32(const BRect& r, const rgb_color& c) const
1524 {
1525 	if (!fValidClipping)
1526 		return;
1527 
1528 	uint8* dst = fBuffer.row_ptr(0);
1529 	uint32 bpr = fBuffer.stride();
1530 
1531 	int32 left = (int32)r.left;
1532 	int32 top = (int32)r.top;
1533 	int32 right = (int32)r.right;
1534 	int32 bottom = (int32)r.bottom;
1535 
1536 	// fill rects, iterate over clipping boxes
1537 	fBaseRenderer.first_clip_box();
1538 	do {
1539 		int32 x1 = max_c(fBaseRenderer.xmin(), left);
1540 		int32 x2 = min_c(fBaseRenderer.xmax(), right);
1541 		if (x1 <= x2) {
1542 			int32 y1 = max_c(fBaseRenderer.ymin(), top);
1543 			int32 y2 = min_c(fBaseRenderer.ymax(), bottom);
1544 
1545 			uint8* offset = dst + x1 * 4 + y1 * bpr;
1546 			for (; y1 <= y2; y1++) {
1547 				blend_line32(offset, x2 - x1 + 1, c.red, c.green, c.blue, c.alpha);
1548 				offset += bpr;
1549 			}
1550 		}
1551 	} while (fBaseRenderer.next_clip_box());
1552 }
1553 
1554 // #pragma mark -
1555 
1556 template<class VertexSource>
1557 BRect
1558 Painter::_BoundingBox(VertexSource& path) const
1559 {
1560 	double left = 0.0;
1561 	double top = 0.0;
1562 	double right = -1.0;
1563 	double bottom = -1.0;
1564 	uint32 pathID[1];
1565 	pathID[0] = 0;
1566 	agg::bounding_rect(path, pathID, 0, 1, &left, &top, &right, &bottom);
1567 	return BRect(left, top, right, bottom);
1568 }
1569 
1570 // agg_line_cap_mode_for
1571 inline agg::line_cap_e
1572 agg_line_cap_mode_for(cap_mode mode)
1573 {
1574 	switch (mode) {
1575 		case B_BUTT_CAP:
1576 			return agg::butt_cap;
1577 		case B_SQUARE_CAP:
1578 			return agg::square_cap;
1579 		case B_ROUND_CAP:
1580 			return agg::round_cap;
1581 	}
1582 	return agg::butt_cap;
1583 }
1584 
1585 // agg_line_join_mode_for
1586 inline agg::line_join_e
1587 agg_line_join_mode_for(join_mode mode)
1588 {
1589 	switch (mode) {
1590 		case B_MITER_JOIN:
1591 			return agg::miter_join;
1592 		case B_ROUND_JOIN:
1593 			return agg::round_join;
1594 		case B_BEVEL_JOIN:
1595 		case B_BUTT_JOIN: // ??
1596 		case B_SQUARE_JOIN: // ??
1597 			return agg::bevel_join;
1598 	}
1599 	return agg::miter_join;
1600 }
1601 
1602 // _StrokePath
1603 template<class VertexSource>
1604 BRect
1605 Painter::_StrokePath(VertexSource& path) const
1606 {
1607 	agg::conv_stroke<VertexSource> stroke(path);
1608 	stroke.width(fPenSize);
1609 
1610 	stroke.line_cap(agg_line_cap_mode_for(fLineCapMode));
1611 	stroke.line_join(agg_line_join_mode_for(fLineJoinMode));
1612 	stroke.miter_limit(fMiterLimit);
1613 
1614 	fRasterizer.reset();
1615 	fRasterizer.add_path(stroke);
1616 
1617 	agg::render_scanlines(fRasterizer, fPackedScanline, fRenderer);
1618 
1619 	BRect touched = _BoundingBox(path);
1620 	float penSize = ceilf(fPenSize / 2.0);
1621 	touched.InsetBy(-penSize, -penSize);
1622 
1623 	return _Clipped(touched);
1624 }
1625 
1626 // _FillPath
1627 template<class VertexSource>
1628 BRect
1629 Painter::_FillPath(VertexSource& path) const
1630 {
1631 	fRasterizer.reset();
1632 	fRasterizer.add_path(path);
1633 	agg::render_scanlines(fRasterizer, fPackedScanline, fRenderer);
1634 
1635 	return _Clipped(_BoundingBox(path));
1636 }
1637 
1638