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