xref: /haiku/src/servers/app/drawing/Painter/Painter.cpp (revision b06a48ab8f30b45916a9c157b992827779182163)
1 /*
2  * Copyright 2005-2007, Stephan Aßmus <superstippi@gmx.de>.
3  * Copyright 2008, Andrej Spielmann <andrej.spielmann@seh.ox.ac.uk>.
4  * All rights reserved. Distributed under the terms of the MIT License.
5  *
6  * API to the Anti-Grain Geometry based "Painter" drawing backend. Manages
7  * rendering pipe-lines for stroke, fills, bitmap and text rendering.
8  */
9 
10 #include <new>
11 #include <stdio.h>
12 #include <string.h>
13 
14 #include <Bitmap.h>
15 #include <GraphicsDefs.h>
16 #include <Region.h>
17 #include <String.h>
18 
19 #include <ShapePrivate.h>
20 
21 #include <agg_bezier_arc.h>
22 #include <agg_bounding_rect.h>
23 #include <agg_conv_clip_polygon.h>
24 #include <agg_conv_curve.h>
25 #include <agg_conv_stroke.h>
26 #include <agg_ellipse.h>
27 #include <agg_image_accessors.h>
28 #include <agg_path_storage.h>
29 #include <agg_pixfmt_rgba.h>
30 #include <agg_rounded_rect.h>
31 #include <agg_span_allocator.h>
32 #include <agg_span_image_filter_rgba.h>
33 #include <agg_span_interpolator_linear.h>
34 
35 #include "drawing_support.h"
36 
37 #include "DrawState.h"
38 
39 #include <AutoDeleter.h>
40 #include <View.h>
41 
42 #include "DrawingMode.h"
43 #include "GlobalSubpixelSettings.h"
44 #include "PatternHandler.h"
45 #include "RenderingBuffer.h"
46 #include "ServerBitmap.h"
47 #include "ServerFont.h"
48 #include "SystemPalette.h"
49 
50 #include "Painter.h"
51 
52 using std::nothrow;
53 
54 #undef TRACE
55 //#define TRACE_PAINTER
56 #ifdef TRACE_PAINTER
57 #	define TRACE(x...)		printf(x)
58 #else
59 #	define TRACE(x...)
60 #endif
61 
62 #define CHECK_CLIPPING	if (!fValidClipping) return BRect(0, 0, -1, -1);
63 #define CHECK_CLIPPING_NO_RETURN	if (!fValidClipping) return;
64 
65 // constructor
66 Painter::Painter()
67 	: fBuffer(),
68 	  fPixelFormat(fBuffer, &fPatternHandler),
69 	  fBaseRenderer(fPixelFormat),
70 	  fUnpackedScanline(),
71 	  fPackedScanline(),
72 	  fSubpixPackedScanline(),
73 	  fSubpixUnpackedScanline(),
74 	  fSubpixRasterizer(),
75 	  fRasterizer(),
76 	  fSubpixRenderer(fBaseRenderer),
77 	  fRenderer(fBaseRenderer),
78 	  fRendererBin(fBaseRenderer),
79 
80 	  fPath(),
81 	  fCurve(fPath),
82 
83 	  fSubpixelPrecise(false),
84 	  fValidClipping(false),
85 	  fDrawingText(false),
86 	  fAttached(false),
87 
88 	  fPenSize(1.0),
89 	  fClippingRegion(NULL),
90 	  fDrawingMode(B_OP_COPY),
91 	  fAlphaSrcMode(B_PIXEL_ALPHA),
92 	  fAlphaFncMode(B_ALPHA_OVERLAY),
93 	  fLineCapMode(B_BUTT_CAP),
94 	  fLineJoinMode(B_MITER_JOIN),
95 	  fMiterLimit(B_DEFAULT_MITER_LIMIT),
96 
97 	  fPatternHandler(),
98 	  fTextRenderer(fSubpixRenderer, fRenderer, fRendererBin, fUnpackedScanline,
99 		fSubpixUnpackedScanline, fSubpixRasterizer)
100 {
101 	fPixelFormat.SetDrawingMode(fDrawingMode, fAlphaSrcMode, fAlphaFncMode, false);
102 
103 #if ALIASED_DRAWING
104 	fRasterizer.gamma(agg::gamma_threshold(0.5));
105 	fSubpixRasterizer.gamma(agg:gamma_threshold(0.5));
106 #endif
107 }
108 
109 // destructor
110 Painter::~Painter()
111 {
112 }
113 
114 // #pragma mark -
115 
116 // AttachToBuffer
117 void
118 Painter::AttachToBuffer(RenderingBuffer* buffer)
119 {
120 	if (buffer && buffer->InitCheck() >= B_OK &&
121 		(buffer->ColorSpace() == B_RGBA32 || buffer->ColorSpace() == B_RGB32)) {
122 		// TODO: implement drawing on B_RGB24, B_RGB15, B_RGB16,
123 		// B_CMAP8 and B_GRAY8 :-[
124 		// (if ever we want to support some devices where this gives
125 		// a great speed up, right now it seems fine, even in emulation)
126 
127 		fBuffer.attach((uint8*)buffer->Bits(),
128 			buffer->Width(), buffer->Height(), buffer->BytesPerRow());
129 
130 		fAttached = true;
131 		fValidClipping = fClippingRegion
132 			&& fClippingRegion->Frame().IsValid();
133 
134 		// These are the AGG renderes and rasterizes which
135 		// will be used for stroking paths
136 
137 		_SetRendererColor(fPatternHandler.HighColor());
138 	}
139 }
140 
141 // DetachFromBuffer
142 void
143 Painter::DetachFromBuffer()
144 {
145 	fBuffer.attach(NULL, 0, 0, 0);
146 	fAttached = false;
147 	fValidClipping = false;
148 }
149 
150 // Bounds
151 BRect
152 Painter::Bounds() const
153 {
154 	return BRect(0, 0, fBuffer.width() - 1, fBuffer.height() - 1);
155 }
156 
157 // #pragma mark -
158 
159 // SetDrawState
160 void
161 Painter::SetDrawState(const DrawState* data, int32 xOffset, int32 yOffset)
162 {
163 	// NOTE: The custom clipping in "data" is ignored, because it has already
164 	// been taken into account elsewhere
165 
166 	// NOTE: Usually this function is only used when the "current view"
167 	// is switched in the ServerWindow and after the decorator has drawn
168 	// and messed up the state. For other graphics state changes, the
169 	// Painter methods are used directly, so this function is much less
170 	// speed critical than it used to be.
171 
172 	SetPenSize(data->PenSize());
173 
174 	SetFont(data);
175 
176 	fSubpixelPrecise = data->SubPixelPrecise();
177 
178 	// any of these conditions means we need to use a different drawing
179 	// mode instance
180 	bool updateDrawingMode
181 		= !(data->GetPattern() == fPatternHandler.GetPattern())
182 			|| data->GetDrawingMode() != fDrawingMode
183 			|| (data->GetDrawingMode() == B_OP_ALPHA
184 				&& (data->AlphaSrcMode() != fAlphaSrcMode
185 					|| data->AlphaFncMode() != fAlphaFncMode));
186 
187 	fDrawingMode = data->GetDrawingMode();
188 	fAlphaSrcMode = data->AlphaSrcMode();
189 	fAlphaFncMode = data->AlphaFncMode();
190 	fPatternHandler.SetPattern(data->GetPattern());
191 	fPatternHandler.SetOffsets(xOffset, yOffset);
192 	fLineCapMode = data->LineCapMode();
193 	fLineJoinMode = data->LineJoinMode();
194 	fMiterLimit = data->MiterLimit();
195 
196 	// adopt the color *after* the pattern is set
197 	// to set the renderers to the correct color
198 	SetHighColor(data->HighColor());
199 	SetLowColor(data->LowColor());
200 
201 	if (updateDrawingMode || fPixelFormat.UsesOpCopyForText())
202 		_UpdateDrawingMode();
203 }
204 
205 // #pragma mark - state
206 
207 // ConstrainClipping
208 void
209 Painter::ConstrainClipping(const BRegion* region)
210 {
211 	fClippingRegion = region;
212 	fBaseRenderer.set_clipping_region(const_cast<BRegion*>(region));
213 	fValidClipping = region->Frame().IsValid() && fAttached;
214 
215 	if (fValidClipping) {
216 		clipping_rect cb = fClippingRegion->FrameInt();
217 		fRasterizer.clip_box(cb.left, cb.top, cb.right + 1, cb.bottom + 1);
218 		fSubpixRasterizer.clip_box(cb.left, cb.top, cb.right + 1, cb.bottom + 1);
219 	}
220 }
221 
222 // SetHighColor
223 void
224 Painter::SetHighColor(const rgb_color& color)
225 {
226 	if (fPatternHandler.HighColor() == color)
227 		return;
228 	fPatternHandler.SetHighColor(color);
229 	if (*(fPatternHandler.GetR5Pattern()) == B_SOLID_HIGH)
230 		_SetRendererColor(color);
231 }
232 
233 // SetLowColor
234 void
235 Painter::SetLowColor(const rgb_color& color)
236 {
237 	fPatternHandler.SetLowColor(color);
238 	if (*(fPatternHandler.GetR5Pattern()) == B_SOLID_LOW)
239 		_SetRendererColor(color);
240 }
241 
242 // SetDrawingMode
243 void
244 Painter::SetDrawingMode(drawing_mode mode)
245 {
246 	if (fDrawingMode != mode) {
247 		fDrawingMode = mode;
248 		_UpdateDrawingMode();
249 	}
250 }
251 
252 
253 // SetBlendingMode
254 void
255 Painter::SetBlendingMode(source_alpha srcAlpha, alpha_function alphaFunc)
256 {
257 	if (fAlphaSrcMode != srcAlpha || fAlphaFncMode != alphaFunc) {
258 		fAlphaSrcMode = srcAlpha;
259 		fAlphaFncMode = alphaFunc;
260 		if (fDrawingMode == B_OP_ALPHA)
261 			_UpdateDrawingMode();
262 	}
263 }
264 
265 // SetPenSize
266 void
267 Painter::SetPenSize(float size)
268 {
269 	fPenSize = size;
270 }
271 
272 // SetStrokeMode
273 void
274 Painter::SetStrokeMode(cap_mode lineCap, join_mode joinMode, float miterLimit)
275 {
276 	fLineCapMode = lineCap;
277 	fLineJoinMode = joinMode;
278 	fMiterLimit = miterLimit;
279 }
280 
281 // SetPattern
282 void
283 Painter::SetPattern(const pattern& p, bool drawingText)
284 {
285 	if (!(p == *fPatternHandler.GetR5Pattern()) || drawingText != fDrawingText) {
286 		fPatternHandler.SetPattern(p);
287 		fDrawingText = drawingText;
288 		_UpdateDrawingMode(fDrawingText);
289 
290 		// update renderer color if necessary
291 		if (fPatternHandler.IsSolidHigh()) {
292 			// pattern was not solid high before
293 			_SetRendererColor(fPatternHandler.HighColor());
294 		} else if (fPatternHandler.IsSolidLow()) {
295 			// pattern was not solid low before
296 			_SetRendererColor(fPatternHandler.LowColor());
297 		}
298 	}
299 }
300 
301 // SetFont
302 void
303 Painter::SetFont(const ServerFont& font)
304 {
305 	fTextRenderer.SetFont(font);
306 	fTextRenderer.SetAntialiasing(
307 		!(font.Flags() & B_DISABLE_ANTIALIASING));
308 }
309 
310 // SetFont
311 void
312 Painter::SetFont(const DrawState* state)
313 {
314 	fTextRenderer.SetFont(state->Font());
315 	fTextRenderer.SetAntialiasing(!(state->ForceFontAliasing()
316 		|| state->Font().Flags() & B_DISABLE_ANTIALIASING));
317 }
318 
319 // #pragma mark - drawing
320 
321 // StrokeLine
322 void
323 Painter::StrokeLine(BPoint a, BPoint b)
324 {
325 	CHECK_CLIPPING_NO_RETURN
326 
327 	// "false" means not to do the pixel center offset,
328 	// because it would mess up our optimized versions
329 	_Transform(&a, false);
330 	_Transform(&b, false);
331 
332 	// first, try an optimized version
333 	if (fPenSize == 1.0
334 		&& (fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER)) {
335 
336 		pattern pat = *fPatternHandler.GetR5Pattern();
337 		if (pat == B_SOLID_HIGH
338 			&& StraightLine(a, b, fPatternHandler.HighColor())) {
339 			return;
340 		} else if (pat == B_SOLID_LOW
341 			&& StraightLine(a, b, fPatternHandler.LowColor())) {
342 			return;
343 		}
344 	}
345 
346 	fPath.remove_all();
347 
348 	if (a == b) {
349 		// special case dots
350 		if (fPenSize == 1.0 && !fSubpixelPrecise) {
351 			if (fClippingRegion->Contains(a)) {
352 				agg::rgba8 dummyColor;
353 				fPixelFormat.blend_pixel(a.x, a.y, dummyColor, 255);
354 			}
355 		} else {
356 			fPath.move_to(a.x, a.y);
357 			fPath.line_to(a.x + 1, a.y);
358 			fPath.line_to(a.x + 1, a.y + 1);
359 			fPath.line_to(a.x, a.y + 1);
360 
361 			_FillPath(fPath);
362 		}
363 	} else {
364 		// do the pixel center offset here
365 		// tweak ends to "include" the pixel at the index,
366 		// we need to do this in order to produce results like R5,
367 		// where coordinates were inclusive
368 		if (!fSubpixelPrecise) {
369 			bool centerOnLine = fmodf(fPenSize, 2.0) != 0.0;
370 			if (a.x == b.x) {
371 				// shift to pixel center vertically
372 				if (centerOnLine) {
373 					a.x += 0.5;
374 					b.x += 0.5;
375 				}
376 				// extend on bottom end
377 				if (a.y < b.y)
378 					b.y++;
379 				else
380 					a.y++;
381 			} else if (a.y == b.y) {
382 				if (centerOnLine) {
383 					// shift to pixel center horizontally
384 					a.y += 0.5;
385 					b.y += 0.5;
386 				}
387 				// extend on right end
388 				if (a.x < b.x)
389 					b.x++;
390 				else
391 					a.x++;
392 			} else {
393 				// do this regardless of pensize
394 				if (a.x < b.x)
395 					b.x++;
396 				else
397 					a.x++;
398 				if (a.y < b.y)
399 					b.y++;
400 				else
401 					a.y++;
402 			}
403 		}
404 
405 		fPath.move_to(a.x, a.y);
406 		fPath.line_to(b.x, b.y);
407 
408 		_StrokePath(fPath);
409 	}
410 }
411 
412 // StraightLine
413 bool
414 Painter::StraightLine(BPoint a, BPoint b, const rgb_color& c) const
415 {
416 	if (!fValidClipping)
417 		return false;
418 
419 	if (a.x == b.x) {
420 		// vertical
421 		uint8* dst = fBuffer.row_ptr(0);
422 		uint32 bpr = fBuffer.stride();
423 		int32 x = (int32)a.x;
424 		dst += x * 4;
425 		int32 y1 = (int32)min_c(a.y, b.y);
426 		int32 y2 = (int32)max_c(a.y, b.y);
427 		pixel32 color;
428 		color.data8[0] = c.blue;
429 		color.data8[1] = c.green;
430 		color.data8[2] = c.red;
431 		color.data8[3] = 255;
432 		// draw a line, iterate over clipping boxes
433 		fBaseRenderer.first_clip_box();
434 		do {
435 			if (fBaseRenderer.xmin() <= x &&
436 				fBaseRenderer.xmax() >= x) {
437 				int32 i = max_c(fBaseRenderer.ymin(), y1);
438 				int32 end = min_c(fBaseRenderer.ymax(), y2);
439 				uint8* handle = dst + i * bpr;
440 				for (; i <= end; i++) {
441 					*(uint32*)handle = color.data32;
442 					handle += bpr;
443 				}
444 			}
445 		} while (fBaseRenderer.next_clip_box());
446 
447 		return true;
448 
449 	} else if (a.y == b.y) {
450 		// horizontal
451 		int32 y = (int32)a.y;
452 		uint8* dst = fBuffer.row_ptr(y);
453 		int32 x1 = (int32)min_c(a.x, b.x);
454 		int32 x2 = (int32)max_c(a.x, b.x);
455 		pixel32 color;
456 		color.data8[0] = c.blue;
457 		color.data8[1] = c.green;
458 		color.data8[2] = c.red;
459 		color.data8[3] = 255;
460 		// draw a line, iterate over clipping boxes
461 		fBaseRenderer.first_clip_box();
462 		do {
463 			if (fBaseRenderer.ymin() <= y &&
464 				fBaseRenderer.ymax() >= y) {
465 				int32 i = max_c(fBaseRenderer.xmin(), x1);
466 				int32 end = min_c(fBaseRenderer.xmax(), x2);
467 				uint32* handle = (uint32*)(dst + i * 4);
468 				for (; i <= end; i++) {
469 					*handle++ = color.data32;
470 				}
471 			}
472 		} while (fBaseRenderer.next_clip_box());
473 
474 		return true;
475 	}
476 	return false;
477 }
478 
479 // #pragma mark -
480 
481 // StrokeTriangle
482 BRect
483 Painter::StrokeTriangle(BPoint pt1, BPoint pt2, BPoint pt3) const
484 {
485 	return _DrawTriangle(pt1, pt2, pt3, false);
486 }
487 
488 // FillTriangle
489 BRect
490 Painter::FillTriangle(BPoint pt1, BPoint pt2, BPoint pt3) const
491 {
492 	return _DrawTriangle(pt1, pt2, pt3, true);
493 }
494 
495 // DrawPolygon
496 BRect
497 Painter::DrawPolygon(BPoint* p, int32 numPts,
498 					 bool filled, bool closed) const
499 {
500 	CHECK_CLIPPING
501 
502 	if (numPts > 0) {
503 
504 		fPath.remove_all();
505 
506 		_Transform(p);
507 		fPath.move_to(p->x, p->y);
508 
509 		for (int32 i = 1; i < numPts; i++) {
510 			p++;
511 			_Transform(p);
512 			fPath.line_to(p->x, p->y);
513 		}
514 
515 		if (closed)
516 			fPath.close_polygon();
517 
518 		if (filled)
519 			return _FillPath(fPath);
520 		else
521 			return _StrokePath(fPath);
522 	}
523 	return BRect(0.0, 0.0, -1.0, -1.0);
524 }
525 
526 // DrawBezier
527 BRect
528 Painter::DrawBezier(BPoint* p, bool filled) const
529 {
530 	CHECK_CLIPPING
531 
532 	fPath.remove_all();
533 
534 	_Transform(&(p[0]));
535 	_Transform(&(p[1]));
536 	_Transform(&(p[2]));
537 	_Transform(&(p[3]));
538 
539 	fPath.move_to(p[0].x, p[0].y);
540 	fPath.curve4(p[1].x, p[1].y,
541 				 p[2].x, p[2].y,
542 				 p[3].x, p[3].y);
543 
544 
545 	if (filled) {
546 		fPath.close_polygon();
547 		return _FillPath(fCurve);
548 	} else {
549 		return _StrokePath(fCurve);
550 	}
551 }
552 
553 
554 
555 // DrawShape
556 BRect
557 Painter::DrawShape(const int32& opCount, const uint32* opList,
558 				   const int32& ptCount, const BPoint* points,
559 				   bool filled) const
560 {
561 	CHECK_CLIPPING
562 
563 	// TODO: if shapes are ever used more heavily in Haiku,
564 	// it would be nice to use BShape data directly (write
565 	// an AGG "VertexSource" adaptor)
566 	fPath.remove_all();
567 	for (int32 i = 0; i < opCount; i++) {
568 		uint32 op = opList[i] & 0xFF000000;
569 		if (op & OP_MOVETO) {
570 			fPath.move_to(points->x, points->y);
571 			points++;
572 		}
573 
574 		if (op & OP_LINETO) {
575 			int32 count = opList[i] & 0x00FFFFFF;
576 			while (count--) {
577 				fPath.line_to(points->x, points->y);
578 				points++;
579 			}
580 		}
581 
582 		if (op & OP_BEZIERTO) {
583 			int32 count = opList[i] & 0x00FFFFFF;
584 			while (count) {
585 				fPath.curve4(points[0].x, points[0].y,
586 							 points[1].x, points[1].y,
587 							 points[2].x, points[2].y);
588 				points += 3;
589 				count -= 3;
590 			}
591 		}
592 
593 		if (op & OP_CLOSE)
594 			fPath.close_polygon();
595 	}
596 
597 	if (filled)
598 		return _FillPath(fCurve);
599 	else
600 		return _StrokePath(fCurve);
601 }
602 
603 // StrokeRect
604 BRect
605 Painter::StrokeRect(const BRect& r) const
606 {
607 	CHECK_CLIPPING
608 
609 	BPoint a(r.left, r.top);
610 	BPoint b(r.right, r.bottom);
611 	_Transform(&a, false);
612 	_Transform(&b, false);
613 
614 	// first, try an optimized version
615 	if (fPenSize == 1.0 &&
616 		(fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER)) {
617 		pattern p = *fPatternHandler.GetR5Pattern();
618 		if (p == B_SOLID_HIGH) {
619 			BRect rect(a, b);
620 			StrokeRect(rect,
621 					   fPatternHandler.HighColor());
622 			return _Clipped(rect);
623 		} else if (p == B_SOLID_LOW) {
624 			BRect rect(a, b);
625 			StrokeRect(rect,
626 					   fPatternHandler.LowColor());
627 			return _Clipped(rect);
628 		}
629 	}
630 
631 	if (fmodf(fPenSize, 2.0) != 0.0) {
632 		// shift coords to center of pixels
633 		a.x += 0.5;
634 		a.y += 0.5;
635 		b.x += 0.5;
636 		b.y += 0.5;
637 	}
638 
639 	fPath.remove_all();
640 	fPath.move_to(a.x, a.y);
641 	if (a.x == b.x || a.y == b.y) {
642 		// special case rects with one pixel height or width
643 		fPath.line_to(b.x, b.y);
644 	} else {
645 		fPath.line_to(b.x, a.y);
646 		fPath.line_to(b.x, b.y);
647 		fPath.line_to(a.x, b.y);
648 	}
649 	fPath.close_polygon();
650 
651 	return _StrokePath(fPath);
652 }
653 
654 // StrokeRect
655 void
656 Painter::StrokeRect(const BRect& r, const rgb_color& c) const
657 {
658 	StraightLine(BPoint(r.left, r.top),
659 				 BPoint(r.right - 1, r.top), c);
660 	StraightLine(BPoint(r.right, r.top),
661 				 BPoint(r.right, r.bottom - 1), c);
662 	StraightLine(BPoint(r.right, r.bottom),
663 				 BPoint(r.left + 1, r.bottom), c);
664 	StraightLine(BPoint(r.left, r.bottom),
665 				 BPoint(r.left, r.top + 1), c);
666 }
667 
668 // FillRect
669 BRect
670 Painter::FillRect(const BRect& r) const
671 {
672 	CHECK_CLIPPING
673 
674 	// support invalid rects
675 	BPoint a(min_c(r.left, r.right), min_c(r.top, r.bottom));
676 	BPoint b(max_c(r.left, r.right), max_c(r.top, r.bottom));
677 	_Transform(&a, false);
678 	_Transform(&b, false);
679 
680 	// first, try an optimized version
681 	if (fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER) {
682 		pattern p = *fPatternHandler.GetR5Pattern();
683 		if (p == B_SOLID_HIGH) {
684 			BRect rect(a, b);
685 			FillRect(rect, fPatternHandler.HighColor());
686 			return _Clipped(rect);
687 		} else if (p == B_SOLID_LOW) {
688 			BRect rect(a, b);
689 			FillRect(rect, fPatternHandler.LowColor());
690 			return _Clipped(rect);
691 		}
692 	}
693 	if (fDrawingMode == B_OP_ALPHA && fAlphaFncMode == B_ALPHA_OVERLAY) {
694 		pattern p = *fPatternHandler.GetR5Pattern();
695 		if (p == B_SOLID_HIGH) {
696 			BRect rect(a, b);
697 			_BlendRect32(rect, fPatternHandler.HighColor());
698 			return _Clipped(rect);
699 		} else if (p == B_SOLID_LOW) {
700 			rgb_color c = fPatternHandler.LowColor();
701 			if (fAlphaSrcMode == B_CONSTANT_ALPHA)
702 				c.alpha = fPatternHandler.HighColor().alpha;
703 			BRect rect(a, b);
704 			_BlendRect32(rect, c);
705 			return _Clipped(rect);
706 		}
707 	}
708 
709 
710 	// account for stricter interpretation of coordinates in AGG
711 	// the rectangle ranges from the top-left (.0, .0)
712 	// to the bottom-right (.9999, .9999) corner of pixels
713 	b.x += 1.0;
714 	b.y += 1.0;
715 
716 	fPath.remove_all();
717 	fPath.move_to(a.x, a.y);
718 	fPath.line_to(b.x, a.y);
719 	fPath.line_to(b.x, b.y);
720 	fPath.line_to(a.x, b.y);
721 	fPath.close_polygon();
722 
723 	return _FillPath(fPath);
724 }
725 
726 // FillRect
727 void
728 Painter::FillRect(const BRect& r, const rgb_color& c) const
729 {
730 	if (!fValidClipping)
731 		return;
732 
733 	uint8* dst = fBuffer.row_ptr(0);
734 	uint32 bpr = fBuffer.stride();
735 	int32 left = (int32)r.left;
736 	int32 top = (int32)r.top;
737 	int32 right = (int32)r.right;
738 	int32 bottom = (int32)r.bottom;
739 	// get a 32 bit pixel ready with the color
740 	pixel32 color;
741 	color.data8[0] = c.blue;
742 	color.data8[1] = c.green;
743 	color.data8[2] = c.red;
744 	color.data8[3] = c.alpha;
745 	// fill rects, iterate over clipping boxes
746 	fBaseRenderer.first_clip_box();
747 	do {
748 		int32 x1 = max_c(fBaseRenderer.xmin(), left);
749 		int32 x2 = min_c(fBaseRenderer.xmax(), right);
750 		if (x1 <= x2) {
751 			int32 y1 = max_c(fBaseRenderer.ymin(), top);
752 			int32 y2 = min_c(fBaseRenderer.ymax(), bottom);
753 			uint8* offset = dst + x1 * 4;
754 			for (; y1 <= y2; y1++) {
755 //					uint32* handle = (uint32*)(offset + y1 * bpr);
756 //					for (int32 x = x1; x <= x2; x++) {
757 //						*handle++ = color.data32;
758 //					}
759 gfxset32(offset + y1 * bpr, color.data32, (x2 - x1 + 1) * 4);
760 			}
761 		}
762 	} while (fBaseRenderer.next_clip_box());
763 }
764 
765 // FillRectNoClipping
766 void
767 Painter::FillRectNoClipping(const clipping_rect& r, const rgb_color& c) const
768 {
769 	int32 y = (int32)r.top;
770 
771 	uint8* dst = fBuffer.row_ptr(y) + r.left * 4;
772 	uint32 bpr = fBuffer.stride();
773 	int32 bytes = (r.right - r.left + 1) * 4;
774 
775 	// get a 32 bit pixel ready with the color
776 	pixel32 color;
777 	color.data8[0] = c.blue;
778 	color.data8[1] = c.green;
779 	color.data8[2] = c.red;
780 	color.data8[3] = c.alpha;
781 
782 	for (; y <= r.bottom; y++) {
783 //			uint32* handle = (uint32*)dst;
784 //			for (int32 x = left; x <= right; x++) {
785 //				*handle++ = color.data32;
786 //			}
787 gfxset32(dst, color.data32, bytes);
788 		dst += bpr;
789 	}
790 }
791 
792 // StrokeRoundRect
793 BRect
794 Painter::StrokeRoundRect(const BRect& r, float xRadius, float yRadius) const
795 {
796 	CHECK_CLIPPING
797 
798 	BPoint lt(r.left, r.top);
799 	BPoint rb(r.right, r.bottom);
800 	bool centerOffset = fPenSize == 1.0;
801 	// TODO: use this when using _StrokePath()
802 	// bool centerOffset = fmodf(fPenSize, 2.0) != 0.0;
803 	_Transform(&lt, centerOffset);
804 	_Transform(&rb, centerOffset);
805 
806 	if (fPenSize == 1.0) {
807 		agg::rounded_rect rect;
808 		rect.rect(lt.x, lt.y, rb.x, rb.y);
809 		rect.radius(xRadius, yRadius);
810 
811 		return _StrokePath(rect);
812 	} else {
813 		// NOTE: This implementation might seem a little strange, but it makes
814 		// stroked round rects look like on R5. A more correct way would be to use
815 		// _StrokePath() as above (independent from fPenSize).
816 		// The fact that the bounding box of the round rect is not enlarged
817 		// by fPenSize/2 is actually on purpose, though one could argue it is unexpected.
818 
819 		// enclose the right and bottom edge
820 		rb.x++;
821 		rb.y++;
822 
823 		agg::rounded_rect outer;
824 		outer.rect(lt.x, lt.y, rb.x, rb.y);
825 		outer.radius(xRadius, yRadius);
826 
827 		if (gSubpixelAntialiasing) {
828 			fSubpixRasterizer.reset();
829 			fSubpixRasterizer.add_path(outer);
830 
831 			// don't add an inner hole if the "size is negative", this avoids some
832 			// defects that can be observed on R5 and could be regarded as a bug.
833 			if (2 * fPenSize < rb.x - lt.x && 2 * fPenSize < rb.y - lt.y) {
834 				agg::rounded_rect inner;
835 				inner.rect(lt.x + fPenSize, lt.y + fPenSize, rb.x - fPenSize,
836 					rb.y - fPenSize);
837 				inner.radius(max_c(0.0, xRadius - fPenSize),
838 					max_c(0.0, yRadius - fPenSize));
839 
840 				fSubpixRasterizer.add_path(inner);
841 			}
842 
843 			// make the inner rect work as a hole
844 			fSubpixRasterizer.filling_rule(agg::fill_even_odd);
845 
846 			if (fPenSize > 2)
847 				agg::render_scanlines(fSubpixRasterizer, fSubpixPackedScanline,
848 					fSubpixRenderer);
849 			else
850 				agg::render_scanlines(fSubpixRasterizer, fSubpixUnpackedScanline,
851 					fSubpixRenderer);
852 
853 			fSubpixRasterizer.filling_rule(agg::fill_non_zero);
854 		} else {
855 			fRasterizer.reset();
856 			fRasterizer.add_path(outer);
857 
858 			// don't add an inner hole if the "size is negative", this avoids some
859 			// defects that can be observed on R5 and could be regarded as a bug.
860 			if (2 * fPenSize < rb.x - lt.x && 2 * fPenSize < rb.y - lt.y) {
861 				agg::rounded_rect inner;
862 				inner.rect(lt.x + fPenSize, lt.y + fPenSize, rb.x - fPenSize,
863 					rb.y - fPenSize);
864 				inner.radius(max_c(0.0, xRadius - fPenSize),
865 					max_c(0.0, yRadius - fPenSize));
866 
867 				fRasterizer.add_path(inner);
868 			}
869 
870 			// make the inner rect work as a hole
871 			fRasterizer.filling_rule(agg::fill_even_odd);
872 
873 			if (fPenSize > 2)
874 				agg::render_scanlines(fRasterizer, fPackedScanline, fRenderer);
875 			else
876 				agg::render_scanlines(fRasterizer, fUnpackedScanline, fRenderer);
877 
878 			// reset to default
879 			fRasterizer.filling_rule(agg::fill_non_zero);
880 		}
881 
882 		return _Clipped(_BoundingBox(outer));
883 	}
884 }
885 
886 // FillRoundRect
887 BRect
888 Painter::FillRoundRect(const BRect& r, float xRadius, float yRadius) const
889 {
890 	CHECK_CLIPPING
891 
892 	BPoint lt(r.left, r.top);
893 	BPoint rb(r.right, r.bottom);
894 	_Transform(&lt, false);
895 	_Transform(&rb, false);
896 
897 	// account for stricter interpretation of coordinates in AGG
898 	// the rectangle ranges from the top-left (.0, .0)
899 	// to the bottom-right (.9999, .9999) corner of pixels
900 	rb.x += 1.0;
901 	rb.y += 1.0;
902 
903 	agg::rounded_rect rect;
904 	rect.rect(lt.x, lt.y, rb.x, rb.y);
905 	rect.radius(xRadius, yRadius);
906 
907 	return _FillPath(rect);
908 }
909 
910 // AlignEllipseRect
911 void
912 Painter::AlignEllipseRect(BRect* rect, bool filled) const
913 {
914 	if (!fSubpixelPrecise) {
915 		// align rect to pixels
916 		align_rect_to_pixels(rect);
917 		// account for "pixel index" versus "pixel area"
918 		rect->right++;
919 		rect->bottom++;
920 		if (!filled && fmodf(fPenSize, 2.0) != 0.0) {
921 			// align the stroke
922 			rect->InsetBy(0.5, 0.5);
923 		}
924 	}
925 }
926 
927 // DrawEllipse
928 BRect
929 Painter::DrawEllipse(BRect r, bool fill) const
930 {
931 	CHECK_CLIPPING
932 
933 	AlignEllipseRect(&r, fill);
934 
935 	float xRadius = r.Width() / 2.0;
936 	float yRadius = r.Height() / 2.0;
937 	BPoint center(r.left + xRadius, r.top + yRadius);
938 
939 	int32 divisions = (int32)((xRadius + yRadius + 2 * fPenSize) * PI / 2);
940 	if (divisions < 12)
941 		divisions = 12;
942 	if (divisions > 4096)
943 		divisions = 4096;
944 
945 	if (fill) {
946 		agg::ellipse path(center.x, center.y, xRadius, yRadius, divisions);
947 
948 		return _FillPath(path);
949 	} else {
950 		// NOTE: This implementation might seem a little strange, but it makes
951 		// stroked ellipses look like on R5. A more correct way would be to use
952 		// _StrokePath(), but it currently has its own set of problems with narrow
953 		// ellipses (for small xRadii or yRadii).
954 		float inset = fPenSize / 2.0;
955 		agg::ellipse inner(center.x, center.y,
956 						   max_c(0.0, xRadius - inset),
957 						   max_c(0.0, yRadius - inset),
958 						   divisions);
959 		agg::ellipse outer(center.x, center.y,
960 						   xRadius + inset,
961 						   yRadius + inset,
962 						   divisions);
963 
964 		if (gSubpixelAntialiasing) {
965 			fSubpixRasterizer.reset();
966 			fSubpixRasterizer.add_path(outer);
967 			fSubpixRasterizer.add_path(inner);
968 
969 			// make the inner ellipse work as a hole
970 			fSubpixRasterizer.filling_rule(agg::fill_even_odd);
971 
972 			if (fPenSize > 4)
973 				agg::render_scanlines(fSubpixRasterizer, fSubpixPackedScanline,
974 					fSubpixRenderer);
975 			else
976 				agg::render_scanlines(fSubpixRasterizer, fSubpixUnpackedScanline,
977 					fSubpixRenderer);
978 
979 			// reset to default
980 			fSubpixRasterizer.filling_rule(agg::fill_non_zero);
981 		} else {
982 			fRasterizer.reset();
983 			fRasterizer.add_path(outer);
984 			fRasterizer.add_path(inner);
985 
986 			// make the inner ellipse work as a hole
987 			fRasterizer.filling_rule(agg::fill_even_odd);
988 
989 			if (fPenSize > 4)
990 				agg::render_scanlines(fRasterizer, fPackedScanline, fRenderer);
991 			else
992 				agg::render_scanlines(fRasterizer, fUnpackedScanline, fRenderer);
993 
994 			// reset to default
995 			fRasterizer.filling_rule(agg::fill_non_zero);
996 		}
997 
998 		return _Clipped(_BoundingBox(outer));
999 	}
1000 }
1001 
1002 // StrokeArc
1003 BRect
1004 Painter::StrokeArc(BPoint center, float xRadius, float yRadius, float angle,
1005 	float span) const
1006 {
1007 	CHECK_CLIPPING
1008 
1009 	_Transform(&center);
1010 
1011 	double angleRad = (angle * PI) / 180.0;
1012 	double spanRad = (span * PI) / 180.0;
1013 	agg::bezier_arc arc(center.x, center.y, xRadius, yRadius,
1014 						-angleRad, -spanRad);
1015 
1016 	agg::conv_curve<agg::bezier_arc> path(arc);
1017 	path.approximation_scale(2.0);
1018 
1019 	return _StrokePath(path);
1020 }
1021 
1022 // FillArc
1023 BRect
1024 Painter::FillArc(BPoint center, float xRadius, float yRadius, float angle,
1025 	float span) const
1026 {
1027 	CHECK_CLIPPING
1028 
1029 	_Transform(&center);
1030 
1031 	double angleRad = (angle * PI) / 180.0;
1032 	double spanRad = (span * PI) / 180.0;
1033 	agg::bezier_arc arc(center.x, center.y, xRadius, yRadius,
1034 						-angleRad, -spanRad);
1035 
1036 	agg::conv_curve<agg::bezier_arc> segmentedArc(arc);
1037 
1038 	fPath.remove_all();
1039 
1040 	// build a new path by starting at the center point,
1041 	// then traversing the arc, then going back to the center
1042 	fPath.move_to(center.x, center.y);
1043 
1044 	segmentedArc.rewind(0);
1045 	double x;
1046 	double y;
1047 	unsigned cmd = segmentedArc.vertex(&x, &y);
1048 	while (!agg::is_stop(cmd)) {
1049 		fPath.line_to(x, y);
1050 		cmd = segmentedArc.vertex(&x, &y);
1051 	}
1052 
1053 	fPath.close_polygon();
1054 
1055 	return _FillPath(fPath);
1056 }
1057 
1058 // #pragma mark -
1059 
1060 // DrawString
1061 BRect
1062 Painter::DrawString(const char* utf8String, uint32 length, BPoint baseLine,
1063 	const escapement_delta* delta, FontCacheReference* cacheReference)
1064 {
1065 	CHECK_CLIPPING
1066 
1067 	if (!fSubpixelPrecise) {
1068 		baseLine.x = roundf(baseLine.x);
1069 		baseLine.y = roundf(baseLine.y);
1070 	}
1071 
1072 	BRect bounds(0.0, 0.0, -1.0, -1.0);
1073 
1074 	// text is not rendered with patterns, but we need to
1075 	// make sure that the previous pattern is restored
1076 	pattern oldPattern = *fPatternHandler.GetR5Pattern();
1077 	SetPattern(B_SOLID_HIGH, true);
1078 
1079 	bounds = fTextRenderer.RenderString(utf8String, length,
1080 		baseLine, fClippingRegion->Frame(), false, NULL, delta,
1081 		cacheReference);
1082 
1083 	SetPattern(oldPattern);
1084 
1085 	return _Clipped(bounds);
1086 }
1087 
1088 // BoundingBox
1089 BRect
1090 Painter::BoundingBox(const char* utf8String, uint32 length, BPoint baseLine,
1091 	BPoint* penLocation, const escapement_delta* delta,
1092 	FontCacheReference* cacheReference) const
1093 {
1094 	if (!fSubpixelPrecise) {
1095 		baseLine.x = roundf(baseLine.x);
1096 		baseLine.y = roundf(baseLine.y);
1097 	}
1098 
1099 	static BRect dummy;
1100 	return fTextRenderer.RenderString(utf8String, length,
1101 		baseLine, dummy, true, penLocation, delta, cacheReference);
1102 }
1103 
1104 // StringWidth
1105 float
1106 Painter::StringWidth(const char* utf8String, uint32 length,
1107 	const escapement_delta* delta)
1108 {
1109 	return Font().StringWidth(utf8String, length, delta);
1110 }
1111 
1112 // #pragma mark -
1113 
1114 // DrawBitmap
1115 BRect
1116 Painter::DrawBitmap(const ServerBitmap* bitmap, BRect bitmapRect,
1117 	BRect viewRect, uint32 options) const
1118 {
1119 	CHECK_CLIPPING
1120 
1121 	BRect touched = _Clipped(viewRect);
1122 
1123 	if (bitmap && bitmap->IsValid() && touched.IsValid()) {
1124 		// the native bitmap coordinate system
1125 		BRect actualBitmapRect(bitmap->Bounds());
1126 
1127 		TRACE("Painter::DrawBitmap()\n");
1128 		TRACE("   actualBitmapRect = (%.1f, %.1f) - (%.1f, %.1f)\n",
1129 			actualBitmapRect.left, actualBitmapRect.top,
1130 			actualBitmapRect.right, actualBitmapRect.bottom);
1131 		TRACE("   bitmapRect = (%.1f, %.1f) - (%.1f, %.1f)\n",
1132 			bitmapRect.left, bitmapRect.top, bitmapRect.right,
1133 			bitmapRect.bottom);
1134 		TRACE("   viewRect = (%.1f, %.1f) - (%.1f, %.1f)\n",
1135 			viewRect.left, viewRect.top, viewRect.right, viewRect.bottom);
1136 
1137 		agg::rendering_buffer srcBuffer;
1138 		srcBuffer.attach(bitmap->Bits(), bitmap->Width(), bitmap->Height(),
1139 			bitmap->BytesPerRow());
1140 
1141 		_DrawBitmap(srcBuffer, bitmap->ColorSpace(), actualBitmapRect,
1142 			bitmapRect, viewRect, options);
1143 	}
1144 	return touched;
1145 }
1146 
1147 // #pragma mark -
1148 
1149 // FillRegion
1150 BRect
1151 Painter::FillRegion(const BRegion* region) const
1152 {
1153 	CHECK_CLIPPING
1154 
1155 	BRegion copy(*region);
1156 	int32 count = copy.CountRects();
1157 	BRect touched = FillRect(copy.RectAt(0));
1158 	for (int32 i = 1; i < count; i++) {
1159 		touched = touched | FillRect(copy.RectAt(i));
1160 	}
1161 	return touched;
1162 }
1163 
1164 // InvertRect
1165 BRect
1166 Painter::InvertRect(const BRect& r) const
1167 {
1168 	CHECK_CLIPPING
1169 
1170 	BRegion region(r);
1171 	if (fClippingRegion) {
1172 		region.IntersectWith(fClippingRegion);
1173 	}
1174 	// implementation only for B_RGB32 at the moment
1175 	int32 count = region.CountRects();
1176 	for (int32 i = 0; i < count; i++) {
1177 		_InvertRect32(region.RectAt(i));
1178 	}
1179 	return _Clipped(r);
1180 }
1181 
1182 
1183 // #pragma mark - private
1184 
1185 // _Transform
1186 inline void
1187 Painter::_Transform(BPoint* point, bool centerOffset) const
1188 {
1189 	// rounding
1190 	if (!fSubpixelPrecise) {
1191 		// TODO: validate usage of floor() for values < 0
1192 		point->x = (int32)point->x;
1193 		point->y = (int32)point->y;
1194 	}
1195 	// this code is supposed to move coordinates to the center of pixels,
1196 	// as AGG considers (0,0) to be the "upper left corner" of a pixel,
1197 	// but BViews are less strict on those details
1198 	if (centerOffset) {
1199 		point->x += 0.5;
1200 		point->y += 0.5;
1201 	}
1202 }
1203 
1204 // _Transform
1205 inline BPoint
1206 Painter::_Transform(const BPoint& point, bool centerOffset) const
1207 {
1208 	BPoint ret = point;
1209 	_Transform(&ret, centerOffset);
1210 	return ret;
1211 }
1212 
1213 // _Clipped
1214 BRect
1215 Painter::_Clipped(const BRect& rect) const
1216 {
1217 	if (rect.IsValid()) {
1218 		return BRect(rect & fClippingRegion->Frame());
1219 	}
1220 	return BRect(rect);
1221 }
1222 
1223 // _UpdateDrawingMode
1224 void
1225 Painter::_UpdateDrawingMode(bool drawingText)
1226 {
1227 	// The AGG renderers have their own color setting, however
1228 	// almost all drawing mode classes ignore the color given
1229 	// by the AGG renderer and use the colors from the PatternHandler
1230 	// instead. If we have a B_SOLID_* pattern, we can actually use
1231 	// the color in the renderer and special versions of drawing modes
1232 	// that don't use PatternHandler and are more efficient. This
1233 	// has been implemented for B_OP_COPY and a couple others (the
1234 	// DrawingMode*Solid ones) as of now. The PixelFormat knows the
1235 	// PatternHandler and makes its decision based on the pattern.
1236 	// The last parameter to SetDrawingMode() is a special flag
1237 	// for when Painter is used to draw text. In this case, another
1238 	// special version of B_OP_COPY is used that acts like R5 in that
1239 	// anti-aliased pixel are not rendered against the actual background
1240 	// but the current low color instead. This way, the frame buffer
1241 	// doesn't need to be read.
1242 	// When a solid pattern is used, _SetRendererColor()
1243 	// has to be called so that all internal colors in the renderes
1244 	// are up to date for use by the solid drawing mode version.
1245 	fPixelFormat.SetDrawingMode(fDrawingMode, fAlphaSrcMode,
1246 								fAlphaFncMode, drawingText);
1247 	if (drawingText)
1248 		fPatternHandler.MakeOpCopyColorCache();
1249 }
1250 
1251 // _SetRendererColor
1252 void
1253 Painter::_SetRendererColor(const rgb_color& color) const
1254 {
1255 	fRenderer.color(agg::rgba(color.red / 255.0,
1256 							  color.green / 255.0,
1257 							  color.blue / 255.0,
1258 							  color.alpha / 255.0));
1259 	fSubpixRenderer.color(agg::rgba(color.red / 255.0,
1260 							  color.green / 255.0,
1261 							  color.blue / 255.0,
1262 							  color.alpha / 255.0));
1263 // TODO: bitmap fonts not yet correctly setup in AGGTextRenderer
1264 //	fRendererBin.color(agg::rgba(color.red / 255.0,
1265 //								 color.green / 255.0,
1266 //								 color.blue / 255.0,
1267 //								 color.alpha / 255.0));
1268 }
1269 
1270 // #pragma mark -
1271 
1272 // _DrawTriangle
1273 inline BRect
1274 Painter::_DrawTriangle(BPoint pt1, BPoint pt2, BPoint pt3, bool fill) const
1275 {
1276 	CHECK_CLIPPING
1277 
1278 	_Transform(&pt1);
1279 	_Transform(&pt2);
1280 	_Transform(&pt3);
1281 
1282 	fPath.remove_all();
1283 
1284 	fPath.move_to(pt1.x, pt1.y);
1285 	fPath.line_to(pt2.x, pt2.y);
1286 	fPath.line_to(pt3.x, pt3.y);
1287 
1288 	fPath.close_polygon();
1289 
1290 	if (fill)
1291 		return _FillPath(fPath);
1292 	else
1293 		return _StrokePath(fPath);
1294 }
1295 
1296 // copy_bitmap_row_cmap8_copy
1297 static inline void
1298 copy_bitmap_row_cmap8_copy(uint8* dst, const uint8* src, int32 numPixels,
1299 						   const rgb_color* colorMap)
1300 {
1301 	uint32* d = (uint32*)dst;
1302 	const uint8* s = src;
1303 	while (numPixels--) {
1304 		const rgb_color c = colorMap[*s++];
1305 		*d++ = (c.alpha << 24) | (c.red << 16) | (c.green << 8) | (c.blue);
1306 	}
1307 }
1308 
1309 // copy_bitmap_row_cmap8_over
1310 static inline void
1311 copy_bitmap_row_cmap8_over(uint8* dst, const uint8* src, int32 numPixels,
1312 	const rgb_color* colorMap)
1313 {
1314 	uint32* d = (uint32*)dst;
1315 	const uint8* s = src;
1316 	while (numPixels--) {
1317 		const rgb_color c = colorMap[*s++];
1318 		if (c.alpha)
1319 			*d = (c.alpha << 24) | (c.red << 16) | (c.green << 8) | (c.blue);
1320 		d++;
1321 	}
1322 }
1323 
1324 // copy_bitmap_row_bgr32_copy
1325 static inline void
1326 copy_bitmap_row_bgr32_copy(uint8* dst, const uint8* src, int32 numPixels,
1327 	const rgb_color* colorMap)
1328 {
1329 	memcpy(dst, src, numPixels * 4);
1330 }
1331 
1332 // copy_bitmap_row_bgr32_over
1333 static inline void
1334 copy_bitmap_row_bgr32_over(uint8* dst, const uint8* src, int32 numPixels,
1335 	const rgb_color* colorMap)
1336 {
1337 	uint32* d = (uint32*)dst;
1338 	uint32* s = (uint32*)src;
1339 	while (numPixels--) {
1340 		if (*s != B_TRANSPARENT_MAGIC_RGBA32)
1341 			*(uint32*)d = *(uint32*)s;
1342 		d++;
1343 		s++;
1344 	}
1345 }
1346 
1347 // copy_bitmap_row_bgr32_alpha
1348 static inline void
1349 copy_bitmap_row_bgr32_alpha(uint8* dst, const uint8* src, int32 numPixels,
1350 	const rgb_color* colorMap)
1351 {
1352 	uint32* d = (uint32*)dst;
1353 	int32 bytes = numPixels * 4;
1354 	uint8 buffer[bytes];
1355 	uint8* b = buffer;
1356 	while (numPixels--) {
1357 		if (src[3] == 255) {
1358 			*(uint32*)b = *(uint32*)src;
1359 		} else {
1360 			*(uint32*)b = *d;
1361 			b[0] = ((src[0] - b[0]) * src[3] + (b[0] << 8)) >> 8;
1362 			b[1] = ((src[1] - b[1]) * src[3] + (b[1] << 8)) >> 8;
1363 			b[2] = ((src[2] - b[2]) * src[3] + (b[2] << 8)) >> 8;
1364 		}
1365 		d++;
1366 		b += 4;
1367 		src += 4;
1368 	}
1369 	memcpy(dst, buffer, bytes);
1370 }
1371 
1372 
1373 template<typename sourcePixel>
1374 void
1375 Painter::_TransparentMagicToAlpha(sourcePixel* buffer, uint32 width,
1376 	uint32 height, uint32 sourceBytesPerRow, sourcePixel transparentMagic,
1377 	BBitmap* output) const
1378 {
1379 	uint8* sourceRow = (uint8*)buffer;
1380 	uint8* destRow = (uint8*)output->Bits();
1381 	uint32 destBytesPerRow = output->BytesPerRow();
1382 
1383 	for (uint32 y = 0; y < height; y++) {
1384 		sourcePixel* pixel = (sourcePixel*)sourceRow;
1385 		uint32* destPixel = (uint32*)destRow;
1386 		for (uint32 x = 0; x < width; x++, pixel++, destPixel++) {
1387 			if (*pixel == transparentMagic)
1388 				*destPixel &= 0x00ffffff;
1389 		}
1390 
1391 		sourceRow += sourceBytesPerRow;
1392 		destRow += destBytesPerRow;
1393 	}
1394 }
1395 
1396 
1397 // _DrawBitmap
1398 void
1399 Painter::_DrawBitmap(agg::rendering_buffer& srcBuffer, color_space format,
1400 	BRect actualBitmapRect, BRect bitmapRect, BRect viewRect,
1401 	uint32 options) const
1402 {
1403 	if (!fValidClipping
1404 		|| !bitmapRect.IsValid() || !bitmapRect.Intersects(actualBitmapRect)
1405 		|| !viewRect.IsValid()) {
1406 		return;
1407 	}
1408 
1409 	if (!fSubpixelPrecise) {
1410 		align_rect_to_pixels(&bitmapRect);
1411 		align_rect_to_pixels(&viewRect);
1412 	}
1413 
1414 	TRACE("Painter::_DrawBitmap()\n");
1415 	TRACE("   bitmapRect = (%.1f, %.1f) - (%.1f, %.1f)\n",
1416 		bitmapRect.left, bitmapRect.top, bitmapRect.right, bitmapRect.bottom);
1417 	TRACE("   viewRect = (%.1f, %.1f) - (%.1f, %.1f)\n",
1418 		viewRect.left, viewRect.top, viewRect.right, viewRect.bottom);
1419 
1420 	double xScale = (viewRect.Width() + 1) / (bitmapRect.Width() + 1);
1421 	double yScale = (viewRect.Height() + 1) / (bitmapRect.Height() + 1);
1422 
1423 	if (xScale == 0.0 || yScale == 0.0)
1424 		return;
1425 
1426 	// compensate for the lefttop offset the actualBitmapRect might have
1427 	// actualBitmapRect has the right size, but put it at B_ORIGIN
1428 	// bitmapRect is already in good coordinates
1429 	actualBitmapRect.OffsetBy(-actualBitmapRect.left, -actualBitmapRect.top);
1430 
1431 	// constrain rect to passed bitmap bounds
1432 	// and transfer the changes to the viewRect with the right scale
1433 	if (bitmapRect.left < actualBitmapRect.left) {
1434 		float diff = actualBitmapRect.left - bitmapRect.left;
1435 		viewRect.left += diff * xScale;
1436 		bitmapRect.left = actualBitmapRect.left;
1437 	}
1438 	if (bitmapRect.top < actualBitmapRect.top) {
1439 		float diff = actualBitmapRect.top - bitmapRect.top;
1440 		viewRect.top += diff * yScale;
1441 		bitmapRect.top = actualBitmapRect.top;
1442 	}
1443 	if (bitmapRect.right > actualBitmapRect.right) {
1444 		float diff = bitmapRect.right - actualBitmapRect.right;
1445 		viewRect.right -= diff * xScale;
1446 		bitmapRect.right = actualBitmapRect.right;
1447 	}
1448 	if (bitmapRect.bottom > actualBitmapRect.bottom) {
1449 		float diff = bitmapRect.bottom - actualBitmapRect.bottom;
1450 		viewRect.bottom -= diff * yScale;
1451 		bitmapRect.bottom = actualBitmapRect.bottom;
1452 	}
1453 
1454 	double xOffset = viewRect.left - bitmapRect.left;
1455 	double yOffset = viewRect.top - bitmapRect.top;
1456 
1457 	// optimized code path for B_CMAP8 and no scale
1458 	if (xScale == 1.0 && yScale == 1.0) {
1459 		if (format == B_CMAP8) {
1460 			if (fDrawingMode == B_OP_COPY) {
1461 				_DrawBitmapNoScale32(copy_bitmap_row_cmap8_copy, 1,
1462 					srcBuffer, xOffset, yOffset, viewRect);
1463 				return;
1464 			} else if (fDrawingMode == B_OP_OVER) {
1465 				_DrawBitmapNoScale32(copy_bitmap_row_cmap8_over, 1,
1466 					srcBuffer, xOffset, yOffset, viewRect);
1467 				return;
1468 			}
1469 		} else if (format == B_RGB32) {
1470 			if (fDrawingMode == B_OP_OVER) {
1471 				_DrawBitmapNoScale32(copy_bitmap_row_bgr32_over, 4,
1472 					srcBuffer, xOffset, yOffset, viewRect);
1473 				return;
1474 			}
1475 		}
1476 	}
1477 
1478 	BBitmap* temp = NULL;
1479 	ObjectDeleter<BBitmap> tempDeleter;
1480 
1481 
1482 
1483 	if ((format != B_RGBA32 && format != B_RGB32)
1484 		|| (format == B_RGB32 && fDrawingMode != B_OP_COPY
1485 #if 1
1486 // Enabling this would make the behavior compatible to BeOS, which
1487 // treats B_RGB32 bitmaps as B_RGB*A*32 bitmaps in B_OP_ALPHA - unlike in
1488 // all other drawing modes, where B_TRANSPARENT_MAGIC_RGBA32 is handled.
1489 // B_RGB32 bitmaps therefore don't draw correctly on BeOS if they actually
1490 // use this color, unless the alpha channel contains 255 for all other
1491 // pixels, which is inconsistent.
1492 			&& fDrawingMode != B_OP_ALPHA
1493 #endif
1494 		)) {
1495 		temp = new (nothrow) BBitmap(actualBitmapRect, B_BITMAP_NO_SERVER_LINK,
1496 			B_RGBA32);
1497 		if (temp == NULL) {
1498 			fprintf(stderr, "Painter::_DrawBitmap() - "
1499 				"out of memory for creating temporary conversion bitmap\n");
1500 			return;
1501 		}
1502 
1503 		tempDeleter.SetTo(temp);
1504 
1505 		status_t err = temp->ImportBits(srcBuffer.buf(),
1506 			srcBuffer.height() * srcBuffer.stride(),
1507 			srcBuffer.stride(), 0, format);
1508 		if (err < B_OK) {
1509 			fprintf(stderr, "Painter::_DrawBitmap() - "
1510 				"colorspace conversion failed: %s\n", strerror(err));
1511 			return;
1512 		}
1513 
1514 		// the original bitmap might have had some of the
1515 		// transaparent magic colors set that we now need to
1516 		// make transparent in our RGBA32 bitmap again.
1517 		switch (format) {
1518 			case B_RGB32:
1519 				_TransparentMagicToAlpha((uint32 *)srcBuffer.buf(),
1520 					srcBuffer.width(), srcBuffer.height(),
1521 					srcBuffer.stride(), B_TRANSPARENT_MAGIC_RGBA32,
1522 					temp);
1523 				break;
1524 
1525 			// TODO: not sure if this applies to B_RGBA15 too. It
1526 			// should not because B_RGBA15 actually has an alpha
1527 			// channel itself and it should have been preserved
1528 			// when importing the bitmap. Maybe it applies to
1529 			// B_RGB16 though?
1530 			case B_RGB15:
1531 				_TransparentMagicToAlpha((uint16 *)srcBuffer.buf(),
1532 					srcBuffer.width(), srcBuffer.height(),
1533 					srcBuffer.stride(), B_TRANSPARENT_MAGIC_RGBA15,
1534 					temp);
1535 				break;
1536 
1537 			default:
1538 				break;
1539 		}
1540 
1541 		srcBuffer.attach((uint8*)temp->Bits(),
1542 			(uint32)actualBitmapRect.IntegerWidth() + 1,
1543 			(uint32)actualBitmapRect.IntegerHeight() + 1,
1544 			temp->BytesPerRow());
1545 	}
1546 
1547 	// maybe we can use an optimized version if there is no scale
1548 	if (xScale == 1.0 && yScale == 1.0) {
1549 		if (fDrawingMode == B_OP_COPY) {
1550 			_DrawBitmapNoScale32(copy_bitmap_row_bgr32_copy, 4, srcBuffer,
1551 				xOffset, yOffset, viewRect);
1552 			return;
1553 		} else if (fDrawingMode == B_OP_OVER
1554 			|| (fDrawingMode == B_OP_ALPHA
1555 				 && fAlphaSrcMode == B_PIXEL_ALPHA
1556 				 && fAlphaFncMode == B_ALPHA_OVERLAY)) {
1557 			_DrawBitmapNoScale32(copy_bitmap_row_bgr32_alpha, 4, srcBuffer,
1558 				xOffset, yOffset, viewRect);
1559 			return;
1560 		}
1561 	}
1562 
1563 	if (fDrawingMode == B_OP_COPY && (options & B_FILTER_BITMAP_BILINEAR)) {
1564 		_DrawBitmapBilinearCopy32(srcBuffer, xOffset, yOffset, xScale, yScale,
1565 			viewRect);
1566 		return;
1567 	}
1568 
1569 	// for all other cases (non-optimized drawing mode or scaled drawing)
1570 	_DrawBitmapGeneric32(srcBuffer, xOffset, yOffset, xScale, yScale, viewRect,
1571 		options);
1572 }
1573 
1574 #define DEBUG_DRAW_BITMAP 0
1575 
1576 // _DrawBitmapNoScale32
1577 template <class F>
1578 void
1579 Painter::_DrawBitmapNoScale32(F copyRowFunction, uint32 bytesPerSourcePixel,
1580 	agg::rendering_buffer& srcBuffer, int32 xOffset, int32 yOffset,
1581 	BRect viewRect) const
1582 {
1583 	// NOTE: this would crash if viewRect was large enough to read outside the
1584 	// bitmap, so make sure this is not the case before calling this function!
1585 	uint8* dst = fBuffer.row_ptr(0);
1586 	uint32 dstBPR = fBuffer.stride();
1587 
1588 	const uint8* src = srcBuffer.row_ptr(0);
1589 	uint32 srcBPR = srcBuffer.stride();
1590 
1591 	int32 left = (int32)viewRect.left;
1592 	int32 top = (int32)viewRect.top;
1593 	int32 right = (int32)viewRect.right;
1594 	int32 bottom = (int32)viewRect.bottom;
1595 
1596 #if DEBUG_DRAW_BITMAP
1597 if (left - xOffset < 0 || left - xOffset >= (int32)srcBuffer.width() ||
1598 	right - xOffset >= (int32)srcBuffer.width() ||
1599 	top - yOffset < 0 || top - yOffset >= (int32)srcBuffer.height() ||
1600 	bottom - yOffset >= (int32)srcBuffer.height()) {
1601 
1602 	char message[256];
1603 	sprintf(message, "reading outside of bitmap (%ld, %ld, %ld, %ld) "
1604 			"(%d, %d) (%ld, %ld)",
1605 		left - xOffset, top - yOffset, right - xOffset, bottom - yOffset,
1606 		srcBuffer.width(), srcBuffer.height(), xOffset, yOffset);
1607 	debugger(message);
1608 }
1609 #endif
1610 
1611 	const rgb_color* colorMap = SystemPalette();
1612 
1613 	// copy rects, iterate over clipping boxes
1614 	fBaseRenderer.first_clip_box();
1615 	do {
1616 		int32 x1 = max_c(fBaseRenderer.xmin(), left);
1617 		int32 x2 = min_c(fBaseRenderer.xmax(), right);
1618 		if (x1 <= x2) {
1619 			int32 y1 = max_c(fBaseRenderer.ymin(), top);
1620 			int32 y2 = min_c(fBaseRenderer.ymax(), bottom);
1621 			if (y1 <= y2) {
1622 				uint8* dstHandle = dst + y1 * dstBPR + x1 * 4;
1623 				const uint8* srcHandle = src + (y1 - yOffset) * srcBPR
1624 					+ (x1 - xOffset) * bytesPerSourcePixel;
1625 
1626 				for (; y1 <= y2; y1++) {
1627 					copyRowFunction(dstHandle, srcHandle, x2 - x1 + 1,
1628 									colorMap);
1629 
1630 					dstHandle += dstBPR;
1631 					srcHandle += srcBPR;
1632 				}
1633 			}
1634 		}
1635 	} while (fBaseRenderer.next_clip_box());
1636 }
1637 
1638 // _DrawBitmapBilinearCopy32
1639 void
1640 Painter::_DrawBitmapBilinearCopy32(agg::rendering_buffer& srcBuffer,
1641 	double xOffset, double yOffset, double xScale, double yScale,
1642 	BRect viewRect) const
1643 {
1644 //bigtime_t now = system_time();
1645 	uint32 dstWidth = viewRect.IntegerWidth() + 1;
1646 	uint32 dstHeight = viewRect.IntegerHeight() + 1;
1647 	uint32 srcWidth = srcBuffer.width();
1648 	uint32 srcHeight = srcBuffer.height();
1649 
1650 	struct FilterInfo {
1651 		uint16 index;	// index into source bitmap row/column
1652 		uint16 weight;	// weight of the pixel at index [0..255]
1653 	};
1654 
1655 //#define FILTER_INFOS_ON_HEAP
1656 #ifdef FILTER_INFOS_ON_HEAP
1657 	FilterInfo* xWeights = new (nothrow) FilterInfo[dstWidth];
1658 	FilterInfo* yWeights = new (nothrow) FilterInfo[dstHeight];
1659 	if (xWeights == NULL || yWeights == NULL) {
1660 		delete[] xWeights;
1661 		delete[] yWeights;
1662 		return;
1663 	}
1664 #else
1665 	// stack based saves about 200µs on 1.85 GHz Core 2 Duo
1666 	// don't know if it could be a problem though with stack overflow
1667 	FilterInfo xWeights[dstWidth];
1668 	FilterInfo yWeights[dstHeight];
1669 #endif
1670 
1671 	// Extract the cropping information for the source bitmap,
1672 	// If only a part of the source bitmap is to be drawn with scale,
1673 	// the offset will be different from the viewRect left top corner.
1674 	int32 xBitmapShift = (int32)(xOffset - viewRect.left);
1675 	int32 yBitmapShift = (int32)(yOffset - viewRect.top);
1676 
1677 	for (uint32 i = 0; i < dstWidth; i++) {
1678 		// fractional index into source
1679 		// NOTE: It is very important to calculate the fractional index
1680 		// into the source pixel grid like this to prevent out of bounds
1681 		// access! It will result in the rightmost pixel of the destination
1682 		// to access the rightmost pixel of the source with a weighting
1683 		// of 255. This in turn will trigger an optimization in the loop
1684 		// that also prevents out of bounds access.
1685 		float index = i * (srcWidth - 1) / ((srcWidth * xScale) - 1);
1686 		// round down to get the left pixel
1687 		xWeights[i].index = (uint16)index;
1688 		xWeights[i].weight = 255 - (uint16)((index - xWeights[i].index) * 255);
1689 		// handle cropped source bitmap
1690 		xWeights[i].index += xBitmapShift;
1691 		// precompute index for 32 bit pixels
1692 		xWeights[i].index *= 4;
1693 	}
1694 
1695 	for (uint32 i = 0; i < dstHeight; i++) {
1696 		// fractional index into source
1697 		// NOTE: It is very important to calculate the fractional index
1698 		// into the source pixel grid like this to prevent out of bounds
1699 		// access! It will result in the bottommost pixel of the destination
1700 		// to access the bottommost pixel of the source with a weighting
1701 		// of 255. This in turn will trigger an optimization in the loop
1702 		// that also prevents out of bounds access.
1703 		float index = i * (srcHeight - 1) / ((srcHeight * yScale) - 1);
1704 		// round down to get the top pixel
1705 		yWeights[i].index = (uint16)index;
1706 		yWeights[i].weight = 255 - (uint16)((index - yWeights[i].index) * 255);
1707 		// handle cropped source bitmap
1708 		yWeights[i].index += yBitmapShift;
1709 	}
1710 //printf("X: %d/%d ... %d/%d, %d/%d (%ld)\n",
1711 //	xWeights[0].index, xWeights[0].weight,
1712 //	xWeights[dstWidth - 2].index, xWeights[dstWidth - 2].weight,
1713 //	xWeights[dstWidth - 1].index, xWeights[dstWidth - 1].weight,
1714 //	dstWidth);
1715 //printf("Y: %d/%d ... %d/%d, %d/%d (%ld)\n",
1716 //	yWeights[0].index, yWeights[0].weight,
1717 //	yWeights[dstHeight - 2].index, yWeights[dstHeight - 2].weight,
1718 //	yWeights[dstHeight - 1].index, yWeights[dstHeight - 1].weight,
1719 //	dstHeight);
1720 
1721 	const int32 left = (int32)viewRect.left;
1722 	const int32 top = (int32)viewRect.top;
1723 	const int32 right = (int32)viewRect.right;
1724 	const int32 bottom = (int32)viewRect.bottom;
1725 
1726 	const uint32 dstBPR = fBuffer.stride();
1727 	const uint32 srcBPR = srcBuffer.stride();
1728 
1729 	// iterate over clipping boxes
1730 	fBaseRenderer.first_clip_box();
1731 	do {
1732 		const int32 x1 = max_c(fBaseRenderer.xmin(), left);
1733 		const int32 x2 = min_c(fBaseRenderer.xmax(), right);
1734 		if (x1 > x2)
1735 			continue;
1736 
1737 		int32 y1 = max_c(fBaseRenderer.ymin(), top);
1738 		int32 y2 = min_c(fBaseRenderer.ymax(), bottom);
1739 		if (y1 > y2)
1740 			continue;
1741 
1742 		// buffer offset into destination
1743 		uint8* dst = fBuffer.row_ptr(y1) + x1 * 4;
1744 
1745 		// x and y are needed as indeces into the wheight arrays, so the
1746 		// offset into the target buffer needs to be compensated
1747 		const int32 xIndexL = x1 - (int32)xOffset;
1748 		const int32 xIndexR = x2 - (int32)xOffset;
1749 		y1 -= (int32)yOffset;
1750 		y2 -= (int32)yOffset;
1751 
1752 //printf("x: %ld - %ld\n", xIndexL, xIndexR);
1753 //printf("y: %ld - %ld\n", y1, y2);
1754 
1755 		for (; y1 <= y2; y1++) {
1756 			// cache the weight of the top and bottom row
1757 			const uint16 wTop = yWeights[y1].weight;
1758 			const uint16 wBottom = 255 - yWeights[y1].weight;
1759 
1760 			// buffer offset into source (top row)
1761 			register const uint8* src = srcBuffer.row_ptr(yWeights[y1].index);
1762 			// buffer handle for destination to be incremented per pixel
1763 			register uint8* d = dst;
1764 
1765 			if (wTop == 255) {
1766 				for (int32 x = xIndexL; x <= xIndexR; x++) {
1767 					const uint8* s = src + xWeights[x].index;
1768 					// This case is important to prevent out
1769 					// of bounds access at bottom edge of the source
1770 					// bitmap. If the scale is low and integer, it will
1771 					// also help the speed.
1772 					if (xWeights[x].weight == 255) {
1773 						// As above, but to prevent out of bounds
1774 						// on the right edge.
1775 						*(uint32*)d = *(uint32*)s;
1776 					} else {
1777 						// Only the left and right pixels are interpolated,
1778 						// since the top row has 100% weight.
1779 						const uint16 wLeft = xWeights[x].weight;
1780 						const uint16 wRight = 255 - wLeft;
1781 						d[0] = (s[0] * wLeft + s[4] * wRight) >> 8;
1782 						d[1] = (s[1] * wLeft + s[5] * wRight) >> 8;
1783 						d[2] = (s[2] * wLeft + s[6] * wRight) >> 8;
1784 					}
1785 					d += 4;
1786 				}
1787 			} else {
1788 				for (int32 x = xIndexL; x <= xIndexR; x++) {
1789 					const uint8* s = src + xWeights[x].index;
1790 					if (xWeights[x].weight == 255) {
1791 						// Prevent out of bounds access on the right edge
1792 						// or simply speed up.
1793 						const uint8* sBottom = s + srcBPR;
1794 						d[0] = (s[0] * wTop + sBottom[0] * wBottom) >> 8;
1795 						d[1] = (s[1] * wTop + sBottom[1] * wBottom) >> 8;
1796 						d[2] = (s[2] * wTop + sBottom[2] * wBottom) >> 8;
1797 					} else {
1798 						// calculate the weighted sum of all four interpolated
1799 						// pixels
1800 						const uint16 wLeft = xWeights[x].weight;
1801 						const uint16 wRight = 255 - wLeft;
1802 						// left and right of top row
1803 						uint32 t0 = (s[0] * wLeft + s[4] * wRight) * wTop;
1804 						uint32 t1 = (s[1] * wLeft + s[5] * wRight) * wTop;
1805 						uint32 t2 = (s[2] * wLeft + s[6] * wRight) * wTop;
1806 
1807 						// left and right of bottom row
1808 						s += srcBPR;
1809 						t0 += (s[0] * wLeft + s[4] * wRight) * wBottom;
1810 						t1 += (s[1] * wLeft + s[5] * wRight) * wBottom;
1811 						t2 += (s[2] * wLeft + s[6] * wRight) * wBottom;
1812 
1813 						d[0] = t0 >> 16;
1814 						d[1] = t1 >> 16;
1815 						d[2] = t2 >> 16;
1816 					}
1817 					d += 4;
1818 				}
1819 			}
1820 			dst += dstBPR;
1821 		}
1822 	} while (fBaseRenderer.next_clip_box());
1823 
1824 #ifdef FILTER_INFOS_ON_HEAP
1825 	delete[] xWeights;
1826 	delete[] yWeights;
1827 #endif
1828 //printf("draw bitmap %.5fx%.5f: %lld\n", xScale, yScale, system_time() - now);
1829 }
1830 
1831 // _DrawBitmapGeneric32
1832 void
1833 Painter::_DrawBitmapGeneric32(agg::rendering_buffer& srcBuffer,
1834 	double xOffset, double yOffset, double xScale, double yScale,
1835 	BRect viewRect, uint32 options) const
1836 {
1837 	TRACE("Painter::_DrawBitmapGeneric32()\n");
1838 	TRACE("   offset: %.1f, %.1f\n", xOffset, yOffset);
1839 	TRACE("   scale: %.3f, %.3f\n", xScale, yScale);
1840 	TRACE("   viewRect: (%.1f, %.1f) - (%.1f, %.1f)\n",
1841 		viewRect.left, viewRect.top, viewRect.right, viewRect.bottom);
1842 	// AGG pipeline
1843 
1844 	// pixel format attached to bitmap
1845 	typedef agg::pixfmt_bgra32 pixfmt_image;
1846 	pixfmt_image pixf_img(srcBuffer);
1847 
1848 	agg::trans_affine srcMatrix;
1849 // NOTE: R5 seems to ignore this offset when drawing bitmaps
1850 //	srcMatrix *= agg::trans_affine_translation(-actualBitmapRect.left,
1851 //											   -actualBitmapRect.top);
1852 
1853 	agg::trans_affine imgMatrix;
1854 	imgMatrix *= agg::trans_affine_translation(xOffset - viewRect.left,
1855 		yOffset - viewRect.top);
1856 	imgMatrix *= agg::trans_affine_scaling(xScale, yScale);
1857 	imgMatrix *= agg::trans_affine_translation(viewRect.left, viewRect.top);
1858 	imgMatrix.invert();
1859 
1860 	// image interpolator
1861 	typedef agg::span_interpolator_linear<> interpolator_type;
1862 	interpolator_type interpolator(imgMatrix);
1863 
1864 	// scanline allocator
1865 	agg::span_allocator<pixfmt_image::color_type> spanAllocator;
1866 
1867 	// image accessor attached to pixel format of bitmap
1868 	typedef agg::image_accessor_clip<pixfmt_image> source_type;
1869 	source_type source(pixf_img, agg::rgba8(0, 0, 0, 0));
1870 
1871 	// clip to the current clipping region's frame
1872 	viewRect = viewRect & fClippingRegion->Frame();
1873 	// convert to pixel coords (versus pixel indices)
1874 	viewRect.right++;
1875 	viewRect.bottom++;
1876 
1877 	// path enclosing the bitmap
1878 	fPath.remove_all();
1879 	fPath.move_to(viewRect.left, viewRect.top);
1880 	fPath.line_to(viewRect.right, viewRect.top);
1881 	fPath.line_to(viewRect.right, viewRect.bottom);
1882 	fPath.line_to(viewRect.left, viewRect.bottom);
1883 	fPath.close_polygon();
1884 
1885 	agg::conv_transform<agg::path_storage> transformedPath(fPath, srcMatrix);
1886 	fRasterizer.reset();
1887 	fRasterizer.add_path(transformedPath);
1888 
1889 	if ((options & B_FILTER_BITMAP_BILINEAR) != 0) {
1890 		// image filter (bilinear)
1891 		typedef agg::span_image_filter_rgba_bilinear<
1892 			source_type, interpolator_type> span_gen_type;
1893 		span_gen_type spanGenerator(source, interpolator);
1894 
1895 		// render the path with the bitmap as scanline fill
1896 		agg::render_scanlines_aa(fRasterizer, fUnpackedScanline, fBaseRenderer,
1897 			spanAllocator, spanGenerator);
1898 	} else {
1899 		// image filter (nearest neighbor)
1900 		typedef agg::span_image_filter_rgba_nn<
1901 			source_type, interpolator_type> span_gen_type;
1902 		span_gen_type spanGenerator(source, interpolator);
1903 
1904 		// render the path with the bitmap as scanline fill
1905 		agg::render_scanlines_aa(fRasterizer, fUnpackedScanline, fBaseRenderer,
1906 			spanAllocator, spanGenerator);
1907 	}
1908 }
1909 
1910 // _InvertRect32
1911 void
1912 Painter::_InvertRect32(BRect r) const
1913 {
1914 	int32 width = r.IntegerWidth() + 1;
1915 	for (int32 y = (int32)r.top; y <= (int32)r.bottom; y++) {
1916 		uint8* dst = fBuffer.row_ptr(y);
1917 		dst += (int32)r.left * 4;
1918 		for (int32 i = 0; i < width; i++) {
1919 			dst[0] = 255 - dst[0];
1920 			dst[1] = 255 - dst[1];
1921 			dst[2] = 255 - dst[2];
1922 			dst += 4;
1923 		}
1924 	}
1925 }
1926 
1927 // _BlendRect32
1928 void
1929 Painter::_BlendRect32(const BRect& r, const rgb_color& c) const
1930 {
1931 	if (!fValidClipping)
1932 		return;
1933 
1934 	uint8* dst = fBuffer.row_ptr(0);
1935 	uint32 bpr = fBuffer.stride();
1936 
1937 	int32 left = (int32)r.left;
1938 	int32 top = (int32)r.top;
1939 	int32 right = (int32)r.right;
1940 	int32 bottom = (int32)r.bottom;
1941 
1942 	// fill rects, iterate over clipping boxes
1943 	fBaseRenderer.first_clip_box();
1944 	do {
1945 		int32 x1 = max_c(fBaseRenderer.xmin(), left);
1946 		int32 x2 = min_c(fBaseRenderer.xmax(), right);
1947 		if (x1 <= x2) {
1948 			int32 y1 = max_c(fBaseRenderer.ymin(), top);
1949 			int32 y2 = min_c(fBaseRenderer.ymax(), bottom);
1950 
1951 			uint8* offset = dst + x1 * 4 + y1 * bpr;
1952 			for (; y1 <= y2; y1++) {
1953 				blend_line32(offset, x2 - x1 + 1, c.red, c.green, c.blue, c.alpha);
1954 				offset += bpr;
1955 			}
1956 		}
1957 	} while (fBaseRenderer.next_clip_box());
1958 }
1959 
1960 // #pragma mark -
1961 
1962 template<class VertexSource>
1963 BRect
1964 Painter::_BoundingBox(VertexSource& path) const
1965 {
1966 	double left = 0.0;
1967 	double top = 0.0;
1968 	double right = -1.0;
1969 	double bottom = -1.0;
1970 	uint32 pathID[1];
1971 	pathID[0] = 0;
1972 	agg::bounding_rect(path, pathID, 0, 1, &left, &top, &right, &bottom);
1973 	return BRect(left, top, right, bottom);
1974 }
1975 
1976 // agg_line_cap_mode_for
1977 inline agg::line_cap_e
1978 agg_line_cap_mode_for(cap_mode mode)
1979 {
1980 	switch (mode) {
1981 		case B_BUTT_CAP:
1982 			return agg::butt_cap;
1983 		case B_SQUARE_CAP:
1984 			return agg::square_cap;
1985 		case B_ROUND_CAP:
1986 			return agg::round_cap;
1987 	}
1988 	return agg::butt_cap;
1989 }
1990 
1991 // agg_line_join_mode_for
1992 inline agg::line_join_e
1993 agg_line_join_mode_for(join_mode mode)
1994 {
1995 	switch (mode) {
1996 		case B_MITER_JOIN:
1997 			return agg::miter_join;
1998 		case B_ROUND_JOIN:
1999 			return agg::round_join;
2000 		case B_BEVEL_JOIN:
2001 		case B_BUTT_JOIN: // ??
2002 		case B_SQUARE_JOIN: // ??
2003 			return agg::bevel_join;
2004 	}
2005 	return agg::miter_join;
2006 }
2007 
2008 // _StrokePath
2009 template<class VertexSource>
2010 BRect
2011 Painter::_StrokePath(VertexSource& path) const
2012 {
2013 	agg::conv_stroke<VertexSource> stroke(path);
2014 	stroke.width(fPenSize);
2015 
2016 	stroke.line_cap(agg_line_cap_mode_for(fLineCapMode));
2017 	stroke.line_join(agg_line_join_mode_for(fLineJoinMode));
2018 	stroke.miter_limit(fMiterLimit);
2019 
2020 	if (gSubpixelAntialiasing) {
2021 
2022 		fSubpixRasterizer.reset();
2023 		fSubpixRasterizer.add_path(stroke);
2024 
2025 		agg::render_scanlines(fSubpixRasterizer,
2026 			fSubpixPackedScanline, fSubpixRenderer);
2027 	} else {
2028 
2029 		fRasterizer.reset();
2030 		fRasterizer.add_path(stroke);
2031 
2032 		agg::render_scanlines(fRasterizer, fPackedScanline, fRenderer);
2033 	}
2034 
2035 	BRect touched = _BoundingBox(path);
2036 	float penSize = ceilf(fPenSize / 2.0);
2037 	touched.InsetBy(-penSize, -penSize);
2038 
2039 	return _Clipped(touched);
2040 }
2041 
2042 // _FillPath
2043 template<class VertexSource>
2044 BRect
2045 Painter::_FillPath(VertexSource& path) const
2046 {
2047 	if (gSubpixelAntialiasing) {
2048 
2049 		fSubpixRasterizer.reset();
2050 		fSubpixRasterizer.add_path(path);
2051 		agg::render_scanlines(fSubpixRasterizer,
2052 			fSubpixPackedScanline, fSubpixRenderer);
2053 	} else {
2054 		fRasterizer.reset();
2055 		fRasterizer.add_path(path);
2056 		agg::render_scanlines(fRasterizer, fPackedScanline, fRenderer);
2057 	}
2058 
2059 	return _Clipped(_BoundingBox(path));
2060 }
2061 
2062