xref: /haiku/src/servers/app/drawing/Painter/Painter.cpp (revision b6b0567fbd186f8ce8a0c90bdc7a7b5b4c649678)
1 /*
2  * Copyright 2009, Christian Packmann.
3  * Copyright 2008, Andrej Spielmann <andrej.spielmann@seh.ox.ac.uk>.
4  * Copyright 2005-2009, Stephan Aßmus <superstippi@gmx.de>.
5  * All rights reserved. Distributed under the terms of the MIT License.
6  */
7 
8 /*!	API to the Anti-Grain Geometry based "Painter" drawing backend. Manages
9 	rendering pipe-lines for stroke, fills, bitmap and text rendering.
10 */
11 
12 #include "Painter.h"
13 
14 #include <new>
15 
16 #include <stdio.h>
17 #include <string.h>
18 
19 #include <Bitmap.h>
20 #include <GraphicsDefs.h>
21 #include <Region.h>
22 #include <String.h>
23 #include <GradientLinear.h>
24 #include <GradientRadial.h>
25 #include <GradientRadialFocus.h>
26 #include <GradientDiamond.h>
27 #include <GradientConic.h>
28 
29 #include <ShapePrivate.h>
30 
31 #include <agg_bezier_arc.h>
32 #include <agg_bounding_rect.h>
33 #include <agg_conv_clip_polygon.h>
34 #include <agg_conv_curve.h>
35 #include <agg_conv_stroke.h>
36 #include <agg_ellipse.h>
37 #include <agg_image_accessors.h>
38 #include <agg_path_storage.h>
39 #include <agg_pixfmt_rgba.h>
40 #include <agg_rounded_rect.h>
41 #include <agg_span_allocator.h>
42 #include <agg_span_image_filter_rgba.h>
43 #include <agg_span_interpolator_linear.h>
44 
45 #include "drawing_support.h"
46 
47 #include "DrawState.h"
48 
49 #include <AutoDeleter.h>
50 #include <View.h>
51 
52 #include "DrawingMode.h"
53 #include "GlobalSubpixelSettings.h"
54 #include "PatternHandler.h"
55 #include "RenderingBuffer.h"
56 #include "ServerBitmap.h"
57 #include "ServerFont.h"
58 #include "SystemPalette.h"
59 
60 #include "AppServer.h"
61 
62 using std::nothrow;
63 
64 #undef TRACE
65 // #define TRACE_PAINTER
66 #ifdef TRACE_PAINTER
67 #	define TRACE(x...)		printf(x)
68 #else
69 #	define TRACE(x...)
70 #endif
71 
72 //#define TRACE_GRADIENTS
73 #ifdef TRACE_GRADIENTS
74 #	include <OS.h>
75 #	define GTRACE(x...)		debug_printf(x)
76 #else
77 #	define GTRACE(x...)
78 #endif
79 
80 
81 #define CHECK_CLIPPING	if (!fValidClipping) return BRect(0, 0, -1, -1);
82 #define CHECK_CLIPPING_NO_RETURN	if (!fValidClipping) return;
83 
84 
85 // constructor
86 Painter::Painter()
87 	:
88 	fBuffer(),
89 	fPixelFormat(fBuffer, &fPatternHandler),
90 	fBaseRenderer(fPixelFormat),
91 	fUnpackedScanline(),
92 	fPackedScanline(),
93 	fSubpixPackedScanline(),
94 	fSubpixUnpackedScanline(),
95 	fSubpixRasterizer(),
96 	fRasterizer(),
97 	fSubpixRenderer(fBaseRenderer),
98 	fRenderer(fBaseRenderer),
99 	fRendererBin(fBaseRenderer),
100 
101 	fPath(),
102 	fCurve(fPath),
103 
104 	fSubpixelPrecise(false),
105 	fValidClipping(false),
106 	fDrawingText(false),
107 	fAttached(false),
108 
109 	fPenSize(1.0),
110 	fClippingRegion(NULL),
111 	fDrawingMode(B_OP_COPY),
112 	fAlphaSrcMode(B_PIXEL_ALPHA),
113 	fAlphaFncMode(B_ALPHA_OVERLAY),
114 	fLineCapMode(B_BUTT_CAP),
115 	fLineJoinMode(B_MITER_JOIN),
116 	fMiterLimit(B_DEFAULT_MITER_LIMIT),
117 
118 	fPatternHandler(),
119 	fTextRenderer(fSubpixRenderer, fRenderer, fRendererBin, fUnpackedScanline,
120 		fSubpixUnpackedScanline, fSubpixRasterizer)
121 {
122 	fPixelFormat.SetDrawingMode(fDrawingMode, fAlphaSrcMode, fAlphaFncMode,
123 		false);
124 
125 #if ALIASED_DRAWING
126 	fRasterizer.gamma(agg::gamma_threshold(0.5));
127 	fSubpixRasterizer.gamma(agg:gamma_threshold(0.5));
128 #endif
129 }
130 
131 
132 // destructor
133 Painter::~Painter()
134 {
135 }
136 
137 
138 // #pragma mark -
139 
140 
141 // AttachToBuffer
142 void
143 Painter::AttachToBuffer(RenderingBuffer* buffer)
144 {
145 	if (buffer && buffer->InitCheck() >= B_OK
146 		&& (buffer->ColorSpace() == B_RGBA32
147 			|| buffer->ColorSpace() == B_RGB32)) {
148 		// TODO: implement drawing on B_RGB24, B_RGB15, B_RGB16,
149 		// B_CMAP8 and B_GRAY8 :-[
150 		// (if ever we want to support some devices where this gives
151 		// a great speed up, right now it seems fine, even in emulation)
152 
153 		fBuffer.attach((uint8*)buffer->Bits(),
154 			buffer->Width(), buffer->Height(), buffer->BytesPerRow());
155 
156 		fAttached = true;
157 		fValidClipping = fClippingRegion
158 			&& fClippingRegion->Frame().IsValid();
159 
160 		// These are the AGG renderes and rasterizes which
161 		// will be used for stroking paths
162 
163 		_SetRendererColor(fPatternHandler.HighColor());
164 	}
165 }
166 
167 
168 // DetachFromBuffer
169 void
170 Painter::DetachFromBuffer()
171 {
172 	fBuffer.attach(NULL, 0, 0, 0);
173 	fAttached = false;
174 	fValidClipping = false;
175 }
176 
177 
178 // Bounds
179 BRect
180 Painter::Bounds() const
181 {
182 	return BRect(0, 0, fBuffer.width() - 1, fBuffer.height() - 1);
183 }
184 
185 
186 // #pragma mark -
187 
188 
189 // SetDrawState
190 void
191 Painter::SetDrawState(const DrawState* data, int32 xOffset, int32 yOffset)
192 {
193 	// NOTE: The custom clipping in "data" is ignored, because it has already
194 	// been taken into account elsewhere
195 
196 	// NOTE: Usually this function is only used when the "current view"
197 	// is switched in the ServerWindow and after the decorator has drawn
198 	// and messed up the state. For other graphics state changes, the
199 	// Painter methods are used directly, so this function is much less
200 	// speed critical than it used to be.
201 
202 	SetPenSize(data->PenSize());
203 
204 	SetFont(data);
205 
206 	fSubpixelPrecise = data->SubPixelPrecise();
207 
208 	// any of these conditions means we need to use a different drawing
209 	// mode instance
210 	bool updateDrawingMode
211 		= !(data->GetPattern() == fPatternHandler.GetPattern())
212 			|| data->GetDrawingMode() != fDrawingMode
213 			|| (data->GetDrawingMode() == B_OP_ALPHA
214 				&& (data->AlphaSrcMode() != fAlphaSrcMode
215 					|| data->AlphaFncMode() != fAlphaFncMode));
216 
217 	fDrawingMode = data->GetDrawingMode();
218 	fAlphaSrcMode = data->AlphaSrcMode();
219 	fAlphaFncMode = data->AlphaFncMode();
220 	fPatternHandler.SetPattern(data->GetPattern());
221 	fPatternHandler.SetOffsets(xOffset, yOffset);
222 	fLineCapMode = data->LineCapMode();
223 	fLineJoinMode = data->LineJoinMode();
224 	fMiterLimit = data->MiterLimit();
225 
226 	// adopt the color *after* the pattern is set
227 	// to set the renderers to the correct color
228 	SetHighColor(data->HighColor());
229 	SetLowColor(data->LowColor());
230 
231 	if (updateDrawingMode || fPixelFormat.UsesOpCopyForText())
232 		_UpdateDrawingMode();
233 }
234 
235 
236 // #pragma mark - state
237 
238 
239 // ConstrainClipping
240 void
241 Painter::ConstrainClipping(const BRegion* region)
242 {
243 	fClippingRegion = region;
244 	fBaseRenderer.set_clipping_region(const_cast<BRegion*>(region));
245 	fValidClipping = region->Frame().IsValid() && fAttached;
246 
247 	if (fValidClipping) {
248 		clipping_rect cb = fClippingRegion->FrameInt();
249 		fRasterizer.clip_box(cb.left, cb.top, cb.right + 1, cb.bottom + 1);
250 		fSubpixRasterizer.clip_box(cb.left, cb.top, cb.right + 1, cb.bottom + 1);
251 	}
252 }
253 
254 
255 // SetHighColor
256 void
257 Painter::SetHighColor(const rgb_color& color)
258 {
259 	if (fPatternHandler.HighColor() == color)
260 		return;
261 	fPatternHandler.SetHighColor(color);
262 	if (*(fPatternHandler.GetR5Pattern()) == B_SOLID_HIGH)
263 		_SetRendererColor(color);
264 }
265 
266 
267 // SetLowColor
268 void
269 Painter::SetLowColor(const rgb_color& color)
270 {
271 	fPatternHandler.SetLowColor(color);
272 	if (*(fPatternHandler.GetR5Pattern()) == B_SOLID_LOW)
273 		_SetRendererColor(color);
274 }
275 
276 
277 // SetDrawingMode
278 void
279 Painter::SetDrawingMode(drawing_mode mode)
280 {
281 	if (fDrawingMode != mode) {
282 		fDrawingMode = mode;
283 		_UpdateDrawingMode();
284 	}
285 }
286 
287 
288 // SetBlendingMode
289 void
290 Painter::SetBlendingMode(source_alpha srcAlpha, alpha_function alphaFunc)
291 {
292 	if (fAlphaSrcMode != srcAlpha || fAlphaFncMode != alphaFunc) {
293 		fAlphaSrcMode = srcAlpha;
294 		fAlphaFncMode = alphaFunc;
295 		if (fDrawingMode == B_OP_ALPHA)
296 			_UpdateDrawingMode();
297 	}
298 }
299 
300 
301 // SetPenSize
302 void
303 Painter::SetPenSize(float size)
304 {
305 	fPenSize = size;
306 }
307 
308 
309 // SetStrokeMode
310 void
311 Painter::SetStrokeMode(cap_mode lineCap, join_mode joinMode, float miterLimit)
312 {
313 	fLineCapMode = lineCap;
314 	fLineJoinMode = joinMode;
315 	fMiterLimit = miterLimit;
316 }
317 
318 
319 // SetPattern
320 void
321 Painter::SetPattern(const pattern& p, bool drawingText)
322 {
323 	if (!(p == *fPatternHandler.GetR5Pattern()) || drawingText != fDrawingText) {
324 		fPatternHandler.SetPattern(p);
325 		fDrawingText = drawingText;
326 		_UpdateDrawingMode(fDrawingText);
327 
328 		// update renderer color if necessary
329 		if (fPatternHandler.IsSolidHigh()) {
330 			// pattern was not solid high before
331 			_SetRendererColor(fPatternHandler.HighColor());
332 		} else if (fPatternHandler.IsSolidLow()) {
333 			// pattern was not solid low before
334 			_SetRendererColor(fPatternHandler.LowColor());
335 		}
336 	}
337 }
338 
339 
340 // SetFont
341 void
342 Painter::SetFont(const ServerFont& font)
343 {
344 	fTextRenderer.SetFont(font);
345 	fTextRenderer.SetAntialiasing(!(font.Flags() & B_DISABLE_ANTIALIASING));
346 }
347 
348 
349 // SetFont
350 void
351 Painter::SetFont(const DrawState* state)
352 {
353 	fTextRenderer.SetFont(state->Font());
354 	fTextRenderer.SetAntialiasing(!state->ForceFontAliasing()
355 		&& (state->Font().Flags() & B_DISABLE_ANTIALIASING) == 0);
356 }
357 
358 
359 // #pragma mark - drawing
360 
361 
362 // StrokeLine
363 void
364 Painter::StrokeLine(BPoint a, BPoint b)
365 {
366 	CHECK_CLIPPING_NO_RETURN
367 
368 	// "false" means not to do the pixel center offset,
369 	// because it would mess up our optimized versions
370 	_Transform(&a, false);
371 	_Transform(&b, false);
372 
373 	// first, try an optimized version
374 	if (fPenSize == 1.0
375 		&& (fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER)) {
376 		pattern pat = *fPatternHandler.GetR5Pattern();
377 		if (pat == B_SOLID_HIGH
378 			&& StraightLine(a, b, fPatternHandler.HighColor())) {
379 			return;
380 		} else if (pat == B_SOLID_LOW
381 			&& StraightLine(a, b, fPatternHandler.LowColor())) {
382 			return;
383 		}
384 	}
385 
386 	fPath.remove_all();
387 
388 	if (a == b) {
389 		// special case dots
390 		if (fPenSize == 1.0 && !fSubpixelPrecise) {
391 			if (fClippingRegion->Contains(a)) {
392 				agg::rgba8 dummyColor;
393 				fPixelFormat.blend_pixel((int)a.x, (int)a.y, dummyColor, 255);
394 			}
395 		} else {
396 			fPath.move_to(a.x, a.y);
397 			fPath.line_to(a.x + 1, a.y);
398 			fPath.line_to(a.x + 1, a.y + 1);
399 			fPath.line_to(a.x, a.y + 1);
400 
401 			_FillPath(fPath);
402 		}
403 	} else {
404 		// do the pixel center offset here
405 		// tweak ends to "include" the pixel at the index,
406 		// we need to do this in order to produce results like R5,
407 		// where coordinates were inclusive
408 		if (!fSubpixelPrecise) {
409 			bool centerOnLine = fmodf(fPenSize, 2.0) != 0.0;
410 			if (a.x == b.x) {
411 				// shift to pixel center vertically
412 				if (centerOnLine) {
413 					a.x += 0.5;
414 					b.x += 0.5;
415 				}
416 				// extend on bottom end
417 				if (a.y < b.y)
418 					b.y++;
419 				else
420 					a.y++;
421 			} else if (a.y == b.y) {
422 				if (centerOnLine) {
423 					// shift to pixel center horizontally
424 					a.y += 0.5;
425 					b.y += 0.5;
426 				}
427 				// extend on right end
428 				if (a.x < b.x)
429 					b.x++;
430 				else
431 					a.x++;
432 			} else {
433 				// do this regardless of pensize
434 				if (a.x < b.x)
435 					b.x++;
436 				else
437 					a.x++;
438 				if (a.y < b.y)
439 					b.y++;
440 				else
441 					a.y++;
442 			}
443 		}
444 
445 		fPath.move_to(a.x, a.y);
446 		fPath.line_to(b.x, b.y);
447 
448 		_StrokePath(fPath);
449 	}
450 }
451 
452 
453 // StraightLine
454 bool
455 Painter::StraightLine(BPoint a, BPoint b, const rgb_color& c) const
456 {
457 	if (!fValidClipping)
458 		return false;
459 
460 	if (a.x == b.x) {
461 		// vertical
462 		uint8* dst = fBuffer.row_ptr(0);
463 		uint32 bpr = fBuffer.stride();
464 		int32 x = (int32)a.x;
465 		dst += x * 4;
466 		int32 y1 = (int32)min_c(a.y, b.y);
467 		int32 y2 = (int32)max_c(a.y, b.y);
468 		pixel32 color;
469 		color.data8[0] = c.blue;
470 		color.data8[1] = c.green;
471 		color.data8[2] = c.red;
472 		color.data8[3] = 255;
473 		// draw a line, iterate over clipping boxes
474 		fBaseRenderer.first_clip_box();
475 		do {
476 			if (fBaseRenderer.xmin() <= x &&
477 				fBaseRenderer.xmax() >= x) {
478 				int32 i = max_c(fBaseRenderer.ymin(), y1);
479 				int32 end = min_c(fBaseRenderer.ymax(), y2);
480 				uint8* handle = dst + i * bpr;
481 				for (; i <= end; i++) {
482 					*(uint32*)handle = color.data32;
483 					handle += bpr;
484 				}
485 			}
486 		} while (fBaseRenderer.next_clip_box());
487 
488 		return true;
489 	}
490 
491 	if (a.y == b.y) {
492 		// horizontal
493 		int32 y = (int32)a.y;
494 		if (y < 0 || y >= (int32)fBuffer.height())
495 			return true;
496 
497 		uint8* dst = fBuffer.row_ptr(y);
498 		int32 x1 = (int32)min_c(a.x, b.x);
499 		int32 x2 = (int32)max_c(a.x, b.x);
500 		pixel32 color;
501 		color.data8[0] = c.blue;
502 		color.data8[1] = c.green;
503 		color.data8[2] = c.red;
504 		color.data8[3] = 255;
505 		// draw a line, iterate over clipping boxes
506 		fBaseRenderer.first_clip_box();
507 		do {
508 			if (fBaseRenderer.ymin() <= y &&
509 				fBaseRenderer.ymax() >= y) {
510 				int32 i = max_c(fBaseRenderer.xmin(), x1);
511 				int32 end = min_c(fBaseRenderer.xmax(), x2);
512 				uint32* handle = (uint32*)(dst + i * 4);
513 				for (; i <= end; i++) {
514 					*handle++ = color.data32;
515 				}
516 			}
517 		} while (fBaseRenderer.next_clip_box());
518 
519 		return true;
520 	}
521 	return false;
522 }
523 
524 
525 // #pragma mark -
526 
527 
528 // StrokeTriangle
529 BRect
530 Painter::StrokeTriangle(BPoint pt1, BPoint pt2, BPoint pt3) const
531 {
532 	return _DrawTriangle(pt1, pt2, pt3, false);
533 }
534 
535 
536 // FillTriangle
537 BRect
538 Painter::FillTriangle(BPoint pt1, BPoint pt2, BPoint pt3) const
539 {
540 	return _DrawTriangle(pt1, pt2, pt3, true);
541 }
542 
543 
544 // FillTriangle
545 BRect
546 Painter::FillTriangle(BPoint pt1, BPoint pt2, BPoint pt3,
547 	const BGradient& gradient) const
548 {
549 	CHECK_CLIPPING
550 
551 	_Transform(&pt1);
552 	_Transform(&pt2);
553 	_Transform(&pt3);
554 
555 	fPath.remove_all();
556 
557 	fPath.move_to(pt1.x, pt1.y);
558 	fPath.line_to(pt2.x, pt2.y);
559 	fPath.line_to(pt3.x, pt3.y);
560 
561 	fPath.close_polygon();
562 
563 	return _FillPath(fPath, gradient);
564 }
565 
566 
567 // DrawPolygon
568 BRect
569 Painter::DrawPolygon(BPoint* p, int32 numPts, bool filled, bool closed) const
570 {
571 	CHECK_CLIPPING
572 
573 	if (numPts > 0) {
574 		fPath.remove_all();
575 
576 		_Transform(p);
577 		fPath.move_to(p->x, p->y);
578 
579 		for (int32 i = 1; i < numPts; i++) {
580 			p++;
581 			_Transform(p);
582 			fPath.line_to(p->x, p->y);
583 		}
584 
585 		if (closed)
586 			fPath.close_polygon();
587 
588 		if (filled)
589 			return _FillPath(fPath);
590 
591 		return _StrokePath(fPath);
592 	}
593 	return BRect(0.0, 0.0, -1.0, -1.0);
594 }
595 
596 
597 // FillPolygon
598 BRect
599 Painter::FillPolygon(BPoint* p, int32 numPts, const BGradient& gradient,
600 	bool closed) const
601 {
602 	CHECK_CLIPPING
603 
604 	if (numPts > 0) {
605 		fPath.remove_all();
606 
607 		_Transform(p);
608 		fPath.move_to(p->x, p->y);
609 
610 		for (int32 i = 1; i < numPts; i++) {
611 			p++;
612 			_Transform(p);
613 			fPath.line_to(p->x, p->y);
614 		}
615 
616 		if (closed)
617 			fPath.close_polygon();
618 
619 		return _FillPath(fPath, gradient);
620 	}
621 	return BRect(0.0, 0.0, -1.0, -1.0);
622 }
623 
624 
625 // DrawBezier
626 BRect
627 Painter::DrawBezier(BPoint* p, bool filled) const
628 {
629 	CHECK_CLIPPING
630 
631 	fPath.remove_all();
632 
633 	_Transform(&(p[0]));
634 	_Transform(&(p[1]));
635 	_Transform(&(p[2]));
636 	_Transform(&(p[3]));
637 
638 	fPath.move_to(p[0].x, p[0].y);
639 	fPath.curve4(p[1].x, p[1].y, p[2].x, p[2].y, p[3].x, p[3].y);
640 
641 	if (filled) {
642 		fPath.close_polygon();
643 		return _FillPath(fCurve);
644 	}
645 
646 	return _StrokePath(fCurve);
647 }
648 
649 
650 // FillBezier
651 BRect
652 Painter::FillBezier(BPoint* p, const BGradient& gradient) const
653 {
654 	CHECK_CLIPPING
655 
656 	fPath.remove_all();
657 
658 	_Transform(&(p[0]));
659 	_Transform(&(p[1]));
660 	_Transform(&(p[2]));
661 	_Transform(&(p[3]));
662 
663 	fPath.move_to(p[0].x, p[0].y);
664 	fPath.curve4(p[1].x, p[1].y, p[2].x, p[2].y, p[3].x, p[3].y);
665 
666 	fPath.close_polygon();
667 	return _FillPath(fCurve, gradient);
668 }
669 
670 
671 // DrawShape
672 BRect
673 Painter::DrawShape(const int32& opCount, const uint32* opList,
674 	const int32& ptCount, const BPoint* points, bool filled) const
675 {
676 	CHECK_CLIPPING
677 
678 	// TODO: if shapes are ever used more heavily in Haiku,
679 	// it would be nice to use BShape data directly (write
680 	// an AGG "VertexSource" adaptor)
681 	fPath.remove_all();
682 	for (int32 i = 0; i < opCount; i++) {
683 		uint32 op = opList[i] & 0xFF000000;
684 		if (op & OP_MOVETO) {
685 			fPath.move_to(points->x, points->y);
686 			points++;
687 		}
688 
689 		if (op & OP_LINETO) {
690 			int32 count = opList[i] & 0x00FFFFFF;
691 			while (count--) {
692 				fPath.line_to(points->x, points->y);
693 				points++;
694 			}
695 		}
696 
697 		if (op & OP_BEZIERTO) {
698 			int32 count = opList[i] & 0x00FFFFFF;
699 			while (count) {
700 				fPath.curve4(points[0].x, points[0].y, points[1].x, points[1].y,
701 					points[2].x, points[2].y);
702 				points += 3;
703 				count -= 3;
704 			}
705 		}
706 
707 		if (op & OP_CLOSE)
708 			fPath.close_polygon();
709 	}
710 
711 	if (filled)
712 		return _FillPath(fCurve);
713 
714 	return _StrokePath(fCurve);
715 }
716 
717 
718 // FillShape
719 BRect
720 Painter::FillShape(const int32& opCount, const uint32* opList,
721 	const int32& ptCount, const BPoint* points, const BGradient& gradient) const
722 {
723 	CHECK_CLIPPING
724 
725 	// TODO: if shapes are ever used more heavily in Haiku,
726 	// it would be nice to use BShape data directly (write
727 	// an AGG "VertexSource" adaptor)
728 	fPath.remove_all();
729 	for (int32 i = 0; i < opCount; i++) {
730 		uint32 op = opList[i] & 0xFF000000;
731 		if (op & OP_MOVETO) {
732 			fPath.move_to(points->x, points->y);
733 			points++;
734 		}
735 
736 		if (op & OP_LINETO) {
737 			int32 count = opList[i] & 0x00FFFFFF;
738 			while (count--) {
739 				fPath.line_to(points->x, points->y);
740 				points++;
741 			}
742 		}
743 
744 		if (op & OP_BEZIERTO) {
745 			int32 count = opList[i] & 0x00FFFFFF;
746 			while (count) {
747 				fPath.curve4(points[0].x, points[0].y, points[1].x, points[1].y,
748 					points[2].x, points[2].y);
749 				points += 3;
750 				count -= 3;
751 			}
752 		}
753 
754 		if (op & OP_CLOSE)
755 			fPath.close_polygon();
756 	}
757 
758 	return _FillPath(fCurve, gradient);
759 }
760 
761 
762 // StrokeRect
763 BRect
764 Painter::StrokeRect(const BRect& r) const
765 {
766 	CHECK_CLIPPING
767 
768 	BPoint a(r.left, r.top);
769 	BPoint b(r.right, r.bottom);
770 	_Transform(&a, false);
771 	_Transform(&b, false);
772 
773 	// first, try an optimized version
774 	if (fPenSize == 1.0 &&
775 		(fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER)) {
776 		pattern p = *fPatternHandler.GetR5Pattern();
777 		if (p == B_SOLID_HIGH) {
778 			BRect rect(a, b);
779 			StrokeRect(rect, fPatternHandler.HighColor());
780 			return _Clipped(rect);
781 		} else if (p == B_SOLID_LOW) {
782 			BRect rect(a, b);
783 			StrokeRect(rect, fPatternHandler.LowColor());
784 			return _Clipped(rect);
785 		}
786 	}
787 
788 	if (fmodf(fPenSize, 2.0) != 0.0) {
789 		// shift coords to center of pixels
790 		a.x += 0.5;
791 		a.y += 0.5;
792 		b.x += 0.5;
793 		b.y += 0.5;
794 	}
795 
796 	fPath.remove_all();
797 	fPath.move_to(a.x, a.y);
798 	if (a.x == b.x || a.y == b.y) {
799 		// special case rects with one pixel height or width
800 		fPath.line_to(b.x, b.y);
801 	} else {
802 		fPath.line_to(b.x, a.y);
803 		fPath.line_to(b.x, b.y);
804 		fPath.line_to(a.x, b.y);
805 	}
806 	fPath.close_polygon();
807 
808 	return _StrokePath(fPath);
809 }
810 
811 
812 // StrokeRect
813 void
814 Painter::StrokeRect(const BRect& r, const rgb_color& c) const
815 {
816 	StraightLine(BPoint(r.left, r.top), BPoint(r.right - 1, r.top), c);
817 	StraightLine(BPoint(r.right, r.top), BPoint(r.right, r.bottom - 1), c);
818 	StraightLine(BPoint(r.right, r.bottom), BPoint(r.left + 1, r.bottom), c);
819 	StraightLine(BPoint(r.left, r.bottom), BPoint(r.left, r.top + 1), c);
820 }
821 
822 
823 // FillRect
824 BRect
825 Painter::FillRect(const BRect& r) const
826 {
827 	CHECK_CLIPPING
828 
829 	// support invalid rects
830 	BPoint a(min_c(r.left, r.right), min_c(r.top, r.bottom));
831 	BPoint b(max_c(r.left, r.right), max_c(r.top, r.bottom));
832 	_Transform(&a, false);
833 	_Transform(&b, false);
834 
835 	// first, try an optimized version
836 	if (fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER) {
837 		pattern p = *fPatternHandler.GetR5Pattern();
838 		if (p == B_SOLID_HIGH) {
839 			BRect rect(a, b);
840 			FillRect(rect, fPatternHandler.HighColor());
841 			return _Clipped(rect);
842 		} else if (p == B_SOLID_LOW) {
843 			BRect rect(a, b);
844 			FillRect(rect, fPatternHandler.LowColor());
845 			return _Clipped(rect);
846 		}
847 	}
848 	if (fDrawingMode == B_OP_ALPHA && fAlphaFncMode == B_ALPHA_OVERLAY) {
849 		pattern p = *fPatternHandler.GetR5Pattern();
850 		if (p == B_SOLID_HIGH) {
851 			BRect rect(a, b);
852 			_BlendRect32(rect, fPatternHandler.HighColor());
853 			return _Clipped(rect);
854 		} else if (p == B_SOLID_LOW) {
855 			rgb_color c = fPatternHandler.LowColor();
856 			if (fAlphaSrcMode == B_CONSTANT_ALPHA)
857 				c.alpha = fPatternHandler.HighColor().alpha;
858 			BRect rect(a, b);
859 			_BlendRect32(rect, c);
860 			return _Clipped(rect);
861 		}
862 	}
863 
864 	// account for stricter interpretation of coordinates in AGG
865 	// the rectangle ranges from the top-left (.0, .0)
866 	// to the bottom-right (.9999, .9999) corner of pixels
867 	b.x += 1.0;
868 	b.y += 1.0;
869 
870 	fPath.remove_all();
871 	fPath.move_to(a.x, a.y);
872 	fPath.line_to(b.x, a.y);
873 	fPath.line_to(b.x, b.y);
874 	fPath.line_to(a.x, b.y);
875 	fPath.close_polygon();
876 
877 	return _FillPath(fPath);
878 }
879 
880 
881 // FillRect
882 BRect
883 Painter::FillRect(const BRect& r, const BGradient& gradient) const
884 {
885 	CHECK_CLIPPING
886 
887 	// support invalid rects
888 	BPoint a(min_c(r.left, r.right), min_c(r.top, r.bottom));
889 	BPoint b(max_c(r.left, r.right), max_c(r.top, r.bottom));
890 	_Transform(&a, false);
891 	_Transform(&b, false);
892 
893 	// first, try an optimized version
894 	if (gradient.GetType() == BGradient::TYPE_LINEAR
895 		&& (fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER)) {
896 		const BGradientLinear* linearGradient
897 			= dynamic_cast<const BGradientLinear*>(&gradient);
898 		if (linearGradient->Start().x == linearGradient->End().x
899 			// TODO: Remove this second check once the optimized method
900 			// handled "upside down" gradients as well...
901 			&& linearGradient->Start().y <= linearGradient->End().y) {
902 			// a vertical gradient
903 			BRect rect(a, b);
904 			FillRectVerticalGradient(rect, *linearGradient);
905 			return _Clipped(rect);
906 		}
907 	}
908 
909 	// account for stricter interpretation of coordinates in AGG
910 	// the rectangle ranges from the top-left (.0, .0)
911 	// to the bottom-right (.9999, .9999) corner of pixels
912 	b.x += 1.0;
913 	b.y += 1.0;
914 
915 	fPath.remove_all();
916 	fPath.move_to(a.x, a.y);
917 	fPath.line_to(b.x, a.y);
918 	fPath.line_to(b.x, b.y);
919 	fPath.line_to(a.x, b.y);
920 	fPath.close_polygon();
921 
922 	return _FillPath(fPath, gradient);
923 }
924 
925 
926 // FillRect
927 void
928 Painter::FillRect(const BRect& r, const rgb_color& c) const
929 {
930 	if (!fValidClipping)
931 		return;
932 
933 	uint8* dst = fBuffer.row_ptr(0);
934 	uint32 bpr = fBuffer.stride();
935 	int32 left = (int32)r.left;
936 	int32 top = (int32)r.top;
937 	int32 right = (int32)r.right;
938 	int32 bottom = (int32)r.bottom;
939 	// get a 32 bit pixel ready with the color
940 	pixel32 color;
941 	color.data8[0] = c.blue;
942 	color.data8[1] = c.green;
943 	color.data8[2] = c.red;
944 	color.data8[3] = c.alpha;
945 	// fill rects, iterate over clipping boxes
946 	fBaseRenderer.first_clip_box();
947 	do {
948 		int32 x1 = max_c(fBaseRenderer.xmin(), left);
949 		int32 x2 = min_c(fBaseRenderer.xmax(), right);
950 		if (x1 <= x2) {
951 			int32 y1 = max_c(fBaseRenderer.ymin(), top);
952 			int32 y2 = min_c(fBaseRenderer.ymax(), bottom);
953 			uint8* offset = dst + x1 * 4;
954 			for (; y1 <= y2; y1++) {
955 //					uint32* handle = (uint32*)(offset + y1 * bpr);
956 //					for (int32 x = x1; x <= x2; x++) {
957 //						*handle++ = color.data32;
958 //					}
959 				gfxset32(offset + y1 * bpr, color.data32, (x2 - x1 + 1) * 4);
960 			}
961 		}
962 	} while (fBaseRenderer.next_clip_box());
963 }
964 
965 
966 // FillRectVerticalGradient
967 void
968 Painter::FillRectVerticalGradient(BRect r, const BGradientLinear& gradient) const
969 {
970 	if (!fValidClipping)
971 		return;
972 
973 	// Make sure the color array is no larger than the screen height.
974 	r = r & fClippingRegion->Frame();
975 
976 	int32 gradientArraySize = r.IntegerHeight() + 1;
977 	uint32 gradientArray[gradientArraySize];
978 	int32 gradientTop = (int32)gradient.Start().y;
979 	int32 gradientBottom = (int32)gradient.End().y;
980 	int32 colorCount = gradientBottom - gradientTop + 1;
981 	if (colorCount < 0) {
982 		// Gradient is upside down. That's currently not supported by this
983 		// method.
984 		return;
985 	}
986 
987 	_MakeGradient(gradient, colorCount, gradientArray,
988 		gradientTop - (int32)r.top, gradientArraySize);
989 
990 	uint8* dst = fBuffer.row_ptr(0);
991 	uint32 bpr = fBuffer.stride();
992 	int32 left = (int32)r.left;
993 	int32 top = (int32)r.top;
994 	int32 right = (int32)r.right;
995 	int32 bottom = (int32)r.bottom;
996 	// fill rects, iterate over clipping boxes
997 	fBaseRenderer.first_clip_box();
998 	do {
999 		int32 x1 = max_c(fBaseRenderer.xmin(), left);
1000 		int32 x2 = min_c(fBaseRenderer.xmax(), right);
1001 		if (x1 <= x2) {
1002 			int32 y1 = max_c(fBaseRenderer.ymin(), top);
1003 			int32 y2 = min_c(fBaseRenderer.ymax(), bottom);
1004 			uint8* offset = dst + x1 * 4;
1005 			for (; y1 <= y2; y1++) {
1006 //					uint32* handle = (uint32*)(offset + y1 * bpr);
1007 //					for (int32 x = x1; x <= x2; x++) {
1008 //						*handle++ = gradientArray[y1 - top];
1009 //					}
1010 				gfxset32(offset + y1 * bpr, gradientArray[y1 - top],
1011 					(x2 - x1 + 1) * 4);
1012 			}
1013 		}
1014 	} while (fBaseRenderer.next_clip_box());
1015 }
1016 
1017 
1018 // FillRectNoClipping
1019 void
1020 Painter::FillRectNoClipping(const clipping_rect& r, const rgb_color& c) const
1021 {
1022 	int32 y = (int32)r.top;
1023 
1024 	uint8* dst = fBuffer.row_ptr(y) + r.left * 4;
1025 	uint32 bpr = fBuffer.stride();
1026 	int32 bytes = (r.right - r.left + 1) * 4;
1027 
1028 	// get a 32 bit pixel ready with the color
1029 	pixel32 color;
1030 	color.data8[0] = c.blue;
1031 	color.data8[1] = c.green;
1032 	color.data8[2] = c.red;
1033 	color.data8[3] = c.alpha;
1034 
1035 	for (; y <= r.bottom; y++) {
1036 //			uint32* handle = (uint32*)dst;
1037 //			for (int32 x = left; x <= right; x++) {
1038 //				*handle++ = color.data32;
1039 //			}
1040 		gfxset32(dst, color.data32, bytes);
1041 		dst += bpr;
1042 	}
1043 }
1044 
1045 
1046 // StrokeRoundRect
1047 BRect
1048 Painter::StrokeRoundRect(const BRect& r, float xRadius, float yRadius) const
1049 {
1050 	CHECK_CLIPPING
1051 
1052 	BPoint lt(r.left, r.top);
1053 	BPoint rb(r.right, r.bottom);
1054 	bool centerOffset = fPenSize == 1.0;
1055 	// TODO: use this when using _StrokePath()
1056 	// bool centerOffset = fmodf(fPenSize, 2.0) != 0.0;
1057 	_Transform(&lt, centerOffset);
1058 	_Transform(&rb, centerOffset);
1059 
1060 	if (fPenSize == 1.0) {
1061 		agg::rounded_rect rect;
1062 		rect.rect(lt.x, lt.y, rb.x, rb.y);
1063 		rect.radius(xRadius, yRadius);
1064 
1065 		return _StrokePath(rect);
1066 	}
1067 
1068 	// NOTE: This implementation might seem a little strange, but it makes
1069 	// stroked round rects look like on R5. A more correct way would be to
1070 	// use _StrokePath() as above (independent from fPenSize).
1071 	// The fact that the bounding box of the round rect is not enlarged
1072 	// by fPenSize/2 is actually on purpose, though one could argue it is
1073 	// unexpected.
1074 
1075 	// enclose the right and bottom edge
1076 	rb.x++;
1077 	rb.y++;
1078 
1079 	agg::rounded_rect outer;
1080 	outer.rect(lt.x, lt.y, rb.x, rb.y);
1081 	outer.radius(xRadius, yRadius);
1082 
1083 	if (gSubpixelAntialiasing) {
1084 		fSubpixRasterizer.reset();
1085 		fSubpixRasterizer.add_path(outer);
1086 
1087 		// don't add an inner hole if the "size is negative", this avoids
1088 		// some defects that can be observed on R5 and could be regarded
1089 		// as a bug.
1090 		if (2 * fPenSize < rb.x - lt.x && 2 * fPenSize < rb.y - lt.y) {
1091 			agg::rounded_rect inner;
1092 			inner.rect(lt.x + fPenSize, lt.y + fPenSize, rb.x - fPenSize,
1093 				rb.y - fPenSize);
1094 			inner.radius(max_c(0.0, xRadius - fPenSize),
1095 				max_c(0.0, yRadius - fPenSize));
1096 
1097 			fSubpixRasterizer.add_path(inner);
1098 		}
1099 
1100 		// make the inner rect work as a hole
1101 		fSubpixRasterizer.filling_rule(agg::fill_even_odd);
1102 
1103 		if (fPenSize > 2) {
1104 			agg::render_scanlines(fSubpixRasterizer, fSubpixPackedScanline,
1105 				fSubpixRenderer);
1106 		} else {
1107 			agg::render_scanlines(fSubpixRasterizer, fSubpixUnpackedScanline,
1108 				fSubpixRenderer);
1109 		}
1110 
1111 		fSubpixRasterizer.filling_rule(agg::fill_non_zero);
1112 	} else {
1113 		fRasterizer.reset();
1114 		fRasterizer.add_path(outer);
1115 
1116 		// don't add an inner hole if the "size is negative", this avoids
1117 		// some defects that can be observed on R5 and could be regarded as
1118 		// a bug.
1119 		if (2 * fPenSize < rb.x - lt.x && 2 * fPenSize < rb.y - lt.y) {
1120 			agg::rounded_rect inner;
1121 			inner.rect(lt.x + fPenSize, lt.y + fPenSize, rb.x - fPenSize,
1122 				rb.y - fPenSize);
1123 			inner.radius(max_c(0.0, xRadius - fPenSize),
1124 				max_c(0.0, yRadius - fPenSize));
1125 
1126 			fRasterizer.add_path(inner);
1127 		}
1128 
1129 		// make the inner rect work as a hole
1130 		fRasterizer.filling_rule(agg::fill_even_odd);
1131 
1132 		if (fPenSize > 2)
1133 			agg::render_scanlines(fRasterizer, fPackedScanline, fRenderer);
1134 		else
1135 			agg::render_scanlines(fRasterizer, fUnpackedScanline, fRenderer);
1136 
1137 		// reset to default
1138 		fRasterizer.filling_rule(agg::fill_non_zero);
1139 	}
1140 
1141 	return _Clipped(_BoundingBox(outer));
1142 }
1143 
1144 
1145 // FillRoundRect
1146 BRect
1147 Painter::FillRoundRect(const BRect& r, float xRadius, float yRadius) const
1148 {
1149 	CHECK_CLIPPING
1150 
1151 	BPoint lt(r.left, r.top);
1152 	BPoint rb(r.right, r.bottom);
1153 	_Transform(&lt, false);
1154 	_Transform(&rb, false);
1155 
1156 	// account for stricter interpretation of coordinates in AGG
1157 	// the rectangle ranges from the top-left (.0, .0)
1158 	// to the bottom-right (.9999, .9999) corner of pixels
1159 	rb.x += 1.0;
1160 	rb.y += 1.0;
1161 
1162 	agg::rounded_rect rect;
1163 	rect.rect(lt.x, lt.y, rb.x, rb.y);
1164 	rect.radius(xRadius, yRadius);
1165 
1166 	return _FillPath(rect);
1167 }
1168 
1169 
1170 // FillRoundRect
1171 BRect
1172 Painter::FillRoundRect(const BRect& r, float xRadius, float yRadius,
1173 	const BGradient& gradient) const
1174 {
1175 	CHECK_CLIPPING
1176 
1177 	BPoint lt(r.left, r.top);
1178 	BPoint rb(r.right, r.bottom);
1179 	_Transform(&lt, false);
1180 	_Transform(&rb, false);
1181 
1182 	// account for stricter interpretation of coordinates in AGG
1183 	// the rectangle ranges from the top-left (.0, .0)
1184 	// to the bottom-right (.9999, .9999) corner of pixels
1185 	rb.x += 1.0;
1186 	rb.y += 1.0;
1187 
1188 	agg::rounded_rect rect;
1189 	rect.rect(lt.x, lt.y, rb.x, rb.y);
1190 	rect.radius(xRadius, yRadius);
1191 
1192 	return _FillPath(rect, gradient);
1193 }
1194 
1195 
1196 // AlignEllipseRect
1197 void
1198 Painter::AlignEllipseRect(BRect* rect, bool filled) const
1199 {
1200 	if (!fSubpixelPrecise) {
1201 		// align rect to pixels
1202 		align_rect_to_pixels(rect);
1203 		// account for "pixel index" versus "pixel area"
1204 		rect->right++;
1205 		rect->bottom++;
1206 		if (!filled && fmodf(fPenSize, 2.0) != 0.0) {
1207 			// align the stroke
1208 			rect->InsetBy(0.5, 0.5);
1209 		}
1210 	}
1211 }
1212 
1213 
1214 // DrawEllipse
1215 BRect
1216 Painter::DrawEllipse(BRect r, bool fill) const
1217 {
1218 	CHECK_CLIPPING
1219 
1220 	AlignEllipseRect(&r, fill);
1221 
1222 	float xRadius = r.Width() / 2.0;
1223 	float yRadius = r.Height() / 2.0;
1224 	BPoint center(r.left + xRadius, r.top + yRadius);
1225 
1226 	int32 divisions = (int32)((xRadius + yRadius + 2 * fPenSize) * M_PI / 2);
1227 	if (divisions < 12)
1228 		divisions = 12;
1229 	if (divisions > 4096)
1230 		divisions = 4096;
1231 
1232 	if (fill) {
1233 		agg::ellipse path(center.x, center.y, xRadius, yRadius, divisions);
1234 
1235 		return _FillPath(path);
1236 	}
1237 
1238 	// NOTE: This implementation might seem a little strange, but it makes
1239 	// stroked ellipses look like on R5. A more correct way would be to use
1240 	// _StrokePath(), but it currently has its own set of problems with
1241 	// narrow ellipses (for small xRadii or yRadii).
1242 	float inset = fPenSize / 2.0;
1243 	agg::ellipse inner(center.x, center.y, max_c(0.0, xRadius - inset),
1244 		max_c(0.0, yRadius - inset), divisions);
1245 	agg::ellipse outer(center.x, center.y, xRadius + inset, yRadius + inset,
1246 		divisions);
1247 
1248 	if (gSubpixelAntialiasing) {
1249 		fSubpixRasterizer.reset();
1250 		fSubpixRasterizer.add_path(outer);
1251 		fSubpixRasterizer.add_path(inner);
1252 
1253 		// make the inner ellipse work as a hole
1254 		fSubpixRasterizer.filling_rule(agg::fill_even_odd);
1255 
1256 		if (fPenSize > 4) {
1257 			agg::render_scanlines(fSubpixRasterizer, fSubpixPackedScanline,
1258 				fSubpixRenderer);
1259 		} else {
1260 			agg::render_scanlines(fSubpixRasterizer, fSubpixUnpackedScanline,
1261 				fSubpixRenderer);
1262 		}
1263 
1264 		// reset to default
1265 		fSubpixRasterizer.filling_rule(agg::fill_non_zero);
1266 	} else {
1267 		fRasterizer.reset();
1268 		fRasterizer.add_path(outer);
1269 		fRasterizer.add_path(inner);
1270 
1271 		// make the inner ellipse work as a hole
1272 		fRasterizer.filling_rule(agg::fill_even_odd);
1273 
1274 		if (fPenSize > 4)
1275 			agg::render_scanlines(fRasterizer, fPackedScanline, fRenderer);
1276 		else
1277 			agg::render_scanlines(fRasterizer, fUnpackedScanline, fRenderer);
1278 
1279 		// reset to default
1280 		fRasterizer.filling_rule(agg::fill_non_zero);
1281 	}
1282 
1283 	return _Clipped(_BoundingBox(outer));
1284 }
1285 
1286 
1287 // FillEllipse
1288 BRect
1289 Painter::FillEllipse(BRect r, const BGradient& gradient) const
1290 {
1291 	CHECK_CLIPPING
1292 
1293 	AlignEllipseRect(&r, true);
1294 
1295 	float xRadius = r.Width() / 2.0;
1296 	float yRadius = r.Height() / 2.0;
1297 	BPoint center(r.left + xRadius, r.top + yRadius);
1298 
1299 	int32 divisions = (int32)((xRadius + yRadius + 2 * fPenSize) * M_PI / 2);
1300 	if (divisions < 12)
1301 		divisions = 12;
1302 	if (divisions > 4096)
1303 		divisions = 4096;
1304 
1305 	agg::ellipse path(center.x, center.y, xRadius, yRadius, divisions);
1306 
1307 	return _FillPath(path, gradient);
1308 }
1309 
1310 
1311 // StrokeArc
1312 BRect
1313 Painter::StrokeArc(BPoint center, float xRadius, float yRadius, float angle,
1314 	float span) const
1315 {
1316 	CHECK_CLIPPING
1317 
1318 	_Transform(&center);
1319 
1320 	double angleRad = (angle * M_PI) / 180.0;
1321 	double spanRad = (span * M_PI) / 180.0;
1322 	agg::bezier_arc arc(center.x, center.y, xRadius, yRadius, -angleRad,
1323 		-spanRad);
1324 
1325 	agg::conv_curve<agg::bezier_arc> path(arc);
1326 	path.approximation_scale(2.0);
1327 
1328 	return _StrokePath(path);
1329 }
1330 
1331 
1332 // FillArc
1333 BRect
1334 Painter::FillArc(BPoint center, float xRadius, float yRadius, float angle,
1335 	float span) const
1336 {
1337 	CHECK_CLIPPING
1338 
1339 	_Transform(&center);
1340 
1341 	double angleRad = (angle * M_PI) / 180.0;
1342 	double spanRad = (span * M_PI) / 180.0;
1343 	agg::bezier_arc arc(center.x, center.y, xRadius, yRadius, -angleRad,
1344 		-spanRad);
1345 
1346 	agg::conv_curve<agg::bezier_arc> segmentedArc(arc);
1347 
1348 	fPath.remove_all();
1349 
1350 	// build a new path by starting at the center point,
1351 	// then traversing the arc, then going back to the center
1352 	fPath.move_to(center.x, center.y);
1353 
1354 	segmentedArc.rewind(0);
1355 	double x;
1356 	double y;
1357 	unsigned cmd = segmentedArc.vertex(&x, &y);
1358 	while (!agg::is_stop(cmd)) {
1359 		fPath.line_to(x, y);
1360 		cmd = segmentedArc.vertex(&x, &y);
1361 	}
1362 
1363 	fPath.close_polygon();
1364 
1365 	return _FillPath(fPath);
1366 }
1367 
1368 
1369 // FillArc
1370 BRect
1371 Painter::FillArc(BPoint center, float xRadius, float yRadius, float angle,
1372 	float span, const BGradient& gradient) const
1373 {
1374 	CHECK_CLIPPING
1375 
1376 	_Transform(&center);
1377 
1378 	double angleRad = (angle * M_PI) / 180.0;
1379 	double spanRad = (span * M_PI) / 180.0;
1380 	agg::bezier_arc arc(center.x, center.y, xRadius, yRadius, -angleRad,
1381 		-spanRad);
1382 
1383 	agg::conv_curve<agg::bezier_arc> segmentedArc(arc);
1384 
1385 	fPath.remove_all();
1386 
1387 	// build a new path by starting at the center point,
1388 	// then traversing the arc, then going back to the center
1389 	fPath.move_to(center.x, center.y);
1390 
1391 	segmentedArc.rewind(0);
1392 	double x;
1393 	double y;
1394 	unsigned cmd = segmentedArc.vertex(&x, &y);
1395 	while (!agg::is_stop(cmd)) {
1396 		fPath.line_to(x, y);
1397 		cmd = segmentedArc.vertex(&x, &y);
1398 	}
1399 
1400 	fPath.close_polygon();
1401 
1402 	return _FillPath(fPath, gradient);
1403 }
1404 
1405 
1406 // #pragma mark -
1407 
1408 
1409 // DrawString
1410 BRect
1411 Painter::DrawString(const char* utf8String, uint32 length, BPoint baseLine,
1412 	const escapement_delta* delta, FontCacheReference* cacheReference)
1413 {
1414 	CHECK_CLIPPING
1415 
1416 	if (!fSubpixelPrecise) {
1417 		baseLine.x = roundf(baseLine.x);
1418 		baseLine.y = roundf(baseLine.y);
1419 	}
1420 
1421 	BRect bounds(0.0, 0.0, -1.0, -1.0);
1422 
1423 	// text is not rendered with patterns, but we need to
1424 	// make sure that the previous pattern is restored
1425 	pattern oldPattern = *fPatternHandler.GetR5Pattern();
1426 	SetPattern(B_SOLID_HIGH, true);
1427 
1428 	bounds = fTextRenderer.RenderString(utf8String, length,
1429 		baseLine, fClippingRegion->Frame(), false, NULL, delta,
1430 		cacheReference);
1431 
1432 	SetPattern(oldPattern);
1433 
1434 	return _Clipped(bounds);
1435 }
1436 
1437 
1438 // BoundingBox
1439 BRect
1440 Painter::BoundingBox(const char* utf8String, uint32 length, BPoint baseLine,
1441 	BPoint* penLocation, const escapement_delta* delta,
1442 	FontCacheReference* cacheReference) const
1443 {
1444 	if (!fSubpixelPrecise) {
1445 		baseLine.x = roundf(baseLine.x);
1446 		baseLine.y = roundf(baseLine.y);
1447 	}
1448 
1449 	static BRect dummy;
1450 	return fTextRenderer.RenderString(utf8String, length,
1451 		baseLine, dummy, true, penLocation, delta, cacheReference);
1452 }
1453 
1454 
1455 // StringWidth
1456 float
1457 Painter::StringWidth(const char* utf8String, uint32 length,
1458 	const escapement_delta* delta)
1459 {
1460 	return Font().StringWidth(utf8String, length, delta);
1461 }
1462 
1463 
1464 // #pragma mark -
1465 
1466 
1467 // DrawBitmap
1468 BRect
1469 Painter::DrawBitmap(const ServerBitmap* bitmap, BRect bitmapRect,
1470 	BRect viewRect, uint32 options) const
1471 {
1472 	CHECK_CLIPPING
1473 
1474 	BRect touched = _Clipped(viewRect);
1475 
1476 	if (bitmap && bitmap->IsValid() && touched.IsValid()) {
1477 		// the native bitmap coordinate system
1478 		BRect actualBitmapRect(bitmap->Bounds());
1479 
1480 		TRACE("Painter::DrawBitmap()\n");
1481 		TRACE("   actualBitmapRect = (%.1f, %.1f) - (%.1f, %.1f)\n",
1482 			actualBitmapRect.left, actualBitmapRect.top,
1483 			actualBitmapRect.right, actualBitmapRect.bottom);
1484 		TRACE("   bitmapRect = (%.1f, %.1f) - (%.1f, %.1f)\n",
1485 			bitmapRect.left, bitmapRect.top, bitmapRect.right,
1486 			bitmapRect.bottom);
1487 		TRACE("   viewRect = (%.1f, %.1f) - (%.1f, %.1f)\n",
1488 			viewRect.left, viewRect.top, viewRect.right, viewRect.bottom);
1489 
1490 		agg::rendering_buffer srcBuffer;
1491 		srcBuffer.attach(bitmap->Bits(), bitmap->Width(), bitmap->Height(),
1492 			bitmap->BytesPerRow());
1493 
1494 		_DrawBitmap(srcBuffer, bitmap->ColorSpace(), actualBitmapRect,
1495 			bitmapRect, viewRect, options);
1496 	}
1497 	return touched;
1498 }
1499 
1500 
1501 // #pragma mark -
1502 
1503 
1504 // FillRegion
1505 BRect
1506 Painter::FillRegion(const BRegion* region) const
1507 {
1508 	CHECK_CLIPPING
1509 
1510 	BRegion copy(*region);
1511 	int32 count = copy.CountRects();
1512 	BRect touched = FillRect(copy.RectAt(0));
1513 	for (int32 i = 1; i < count; i++) {
1514 		touched = touched | FillRect(copy.RectAt(i));
1515 	}
1516 	return touched;
1517 }
1518 
1519 
1520 // FillRegion
1521 BRect
1522 Painter::FillRegion(const BRegion* region, const BGradient& gradient) const
1523 {
1524 	CHECK_CLIPPING
1525 
1526 	BRegion copy(*region);
1527 	int32 count = copy.CountRects();
1528 	BRect touched = FillRect(copy.RectAt(0), gradient);
1529 	for (int32 i = 1; i < count; i++) {
1530 		touched = touched | FillRect(copy.RectAt(i), gradient);
1531 	}
1532 	return touched;
1533 }
1534 
1535 
1536 // InvertRect
1537 BRect
1538 Painter::InvertRect(const BRect& r) const
1539 {
1540 	CHECK_CLIPPING
1541 
1542 	BRegion region(r);
1543 	if (fClippingRegion)
1544 		region.IntersectWith(fClippingRegion);
1545 
1546 	// implementation only for B_RGB32 at the moment
1547 	int32 count = region.CountRects();
1548 	for (int32 i = 0; i < count; i++) {
1549 		_InvertRect32(region.RectAt(i));
1550 	}
1551 	return _Clipped(r);
1552 }
1553 
1554 
1555 // #pragma mark - private
1556 
1557 
1558 // _Transform
1559 inline void
1560 Painter::_Transform(BPoint* point, bool centerOffset) const
1561 {
1562 	// rounding
1563 	if (!fSubpixelPrecise) {
1564 		// TODO: validate usage of floor() for values < 0
1565 		point->x = (int32)point->x;
1566 		point->y = (int32)point->y;
1567 	}
1568 	// this code is supposed to move coordinates to the center of pixels,
1569 	// as AGG considers (0,0) to be the "upper left corner" of a pixel,
1570 	// but BViews are less strict on those details
1571 	if (centerOffset) {
1572 		point->x += 0.5;
1573 		point->y += 0.5;
1574 	}
1575 }
1576 
1577 
1578 // _Transform
1579 inline BPoint
1580 Painter::_Transform(const BPoint& point, bool centerOffset) const
1581 {
1582 	BPoint ret = point;
1583 	_Transform(&ret, centerOffset);
1584 	return ret;
1585 }
1586 
1587 
1588 // _Clipped
1589 BRect
1590 Painter::_Clipped(const BRect& rect) const
1591 {
1592 	if (rect.IsValid())
1593 		return BRect(rect & fClippingRegion->Frame());
1594 
1595 	return BRect(rect);
1596 }
1597 
1598 
1599 // _UpdateDrawingMode
1600 void
1601 Painter::_UpdateDrawingMode(bool drawingText)
1602 {
1603 	// The AGG renderers have their own color setting, however
1604 	// almost all drawing mode classes ignore the color given
1605 	// by the AGG renderer and use the colors from the PatternHandler
1606 	// instead. If we have a B_SOLID_* pattern, we can actually use
1607 	// the color in the renderer and special versions of drawing modes
1608 	// that don't use PatternHandler and are more efficient. This
1609 	// has been implemented for B_OP_COPY and a couple others (the
1610 	// DrawingMode*Solid ones) as of now. The PixelFormat knows the
1611 	// PatternHandler and makes its decision based on the pattern.
1612 	// The last parameter to SetDrawingMode() is a special flag
1613 	// for when Painter is used to draw text. In this case, another
1614 	// special version of B_OP_COPY is used that acts like R5 in that
1615 	// anti-aliased pixel are not rendered against the actual background
1616 	// but the current low color instead. This way, the frame buffer
1617 	// doesn't need to be read.
1618 	// When a solid pattern is used, _SetRendererColor()
1619 	// has to be called so that all internal colors in the renderes
1620 	// are up to date for use by the solid drawing mode version.
1621 	fPixelFormat.SetDrawingMode(fDrawingMode, fAlphaSrcMode, fAlphaFncMode,
1622 		drawingText);
1623 	if (drawingText)
1624 		fPatternHandler.MakeOpCopyColorCache();
1625 }
1626 
1627 
1628 // _SetRendererColor
1629 void
1630 Painter::_SetRendererColor(const rgb_color& color) const
1631 {
1632 	fRenderer.color(agg::rgba(color.red / 255.0, color.green / 255.0,
1633 		color.blue / 255.0, color.alpha / 255.0));
1634 	fSubpixRenderer.color(agg::rgba(color.red / 255.0, color.green / 255.0,
1635 		color.blue / 255.0, color.alpha / 255.0));
1636 // TODO: bitmap fonts not yet correctly setup in AGGTextRenderer
1637 //	fRendererBin.color(agg::rgba(color.red / 255.0, color.green / 255.0,
1638 //		color.blue / 255.0, color.alpha / 255.0));
1639 }
1640 
1641 
1642 // #pragma mark -
1643 
1644 
1645 // _DrawTriangle
1646 inline BRect
1647 Painter::_DrawTriangle(BPoint pt1, BPoint pt2, BPoint pt3, bool fill) const
1648 {
1649 	CHECK_CLIPPING
1650 
1651 	_Transform(&pt1);
1652 	_Transform(&pt2);
1653 	_Transform(&pt3);
1654 
1655 	fPath.remove_all();
1656 
1657 	fPath.move_to(pt1.x, pt1.y);
1658 	fPath.line_to(pt2.x, pt2.y);
1659 	fPath.line_to(pt3.x, pt3.y);
1660 
1661 	fPath.close_polygon();
1662 
1663 	if (fill)
1664 		return _FillPath(fPath);
1665 
1666 	return _StrokePath(fPath);
1667 }
1668 
1669 
1670 // copy_bitmap_row_cmap8_copy
1671 static inline void
1672 copy_bitmap_row_cmap8_copy(uint8* dst, const uint8* src, int32 numPixels,
1673 	const rgb_color* colorMap)
1674 {
1675 	uint32* d = (uint32*)dst;
1676 	const uint8* s = src;
1677 	while (numPixels--) {
1678 		const rgb_color c = colorMap[*s++];
1679 		*d++ = (c.alpha << 24) | (c.red << 16) | (c.green << 8) | (c.blue);
1680 	}
1681 }
1682 
1683 
1684 // copy_bitmap_row_cmap8_over
1685 static inline void
1686 copy_bitmap_row_cmap8_over(uint8* dst, const uint8* src, int32 numPixels,
1687 	const rgb_color* colorMap)
1688 {
1689 	uint32* d = (uint32*)dst;
1690 	const uint8* s = src;
1691 	while (numPixels--) {
1692 		const rgb_color c = colorMap[*s++];
1693 		if (c.alpha)
1694 			*d = (c.alpha << 24) | (c.red << 16) | (c.green << 8) | (c.blue);
1695 		d++;
1696 	}
1697 }
1698 
1699 
1700 // copy_bitmap_row_bgr32_copy
1701 static inline void
1702 copy_bitmap_row_bgr32_copy(uint8* dst, const uint8* src, int32 numPixels,
1703 	const rgb_color* colorMap)
1704 {
1705 	memcpy(dst, src, numPixels * 4);
1706 }
1707 
1708 
1709 // copy_bitmap_row_bgr32_over
1710 static inline void
1711 copy_bitmap_row_bgr32_over(uint8* dst, const uint8* src, int32 numPixels,
1712 	const rgb_color* colorMap)
1713 {
1714 	uint32* d = (uint32*)dst;
1715 	uint32* s = (uint32*)src;
1716 	while (numPixels--) {
1717 		if (*s != B_TRANSPARENT_MAGIC_RGBA32)
1718 			*(uint32*)d = *(uint32*)s;
1719 		d++;
1720 		s++;
1721 	}
1722 }
1723 
1724 
1725 // copy_bitmap_row_bgr32_alpha
1726 static inline void
1727 copy_bitmap_row_bgr32_alpha(uint8* dst, const uint8* src, int32 numPixels,
1728 	const rgb_color* colorMap)
1729 {
1730 	uint32* d = (uint32*)dst;
1731 	int32 bytes = numPixels * 4;
1732 	uint8 buffer[bytes];
1733 	uint8* b = buffer;
1734 	while (numPixels--) {
1735 		if (src[3] == 255) {
1736 			*(uint32*)b = *(uint32*)src;
1737 		} else {
1738 			*(uint32*)b = *d;
1739 			b[0] = ((src[0] - b[0]) * src[3] + (b[0] << 8)) >> 8;
1740 			b[1] = ((src[1] - b[1]) * src[3] + (b[1] << 8)) >> 8;
1741 			b[2] = ((src[2] - b[2]) * src[3] + (b[2] << 8)) >> 8;
1742 		}
1743 		d++;
1744 		b += 4;
1745 		src += 4;
1746 	}
1747 	memcpy(dst, buffer, bytes);
1748 }
1749 
1750 
1751 // _TransparentMagicToAlpha
1752 template<typename sourcePixel>
1753 void
1754 Painter::_TransparentMagicToAlpha(sourcePixel* buffer, uint32 width,
1755 	uint32 height, uint32 sourceBytesPerRow, sourcePixel transparentMagic,
1756 	BBitmap* output) const
1757 {
1758 	uint8* sourceRow = (uint8*)buffer;
1759 	uint8* destRow = (uint8*)output->Bits();
1760 	uint32 destBytesPerRow = output->BytesPerRow();
1761 
1762 	for (uint32 y = 0; y < height; y++) {
1763 		sourcePixel* pixel = (sourcePixel*)sourceRow;
1764 		uint32* destPixel = (uint32*)destRow;
1765 		for (uint32 x = 0; x < width; x++, pixel++, destPixel++) {
1766 			if (*pixel == transparentMagic)
1767 				*destPixel &= 0x00ffffff;
1768 		}
1769 
1770 		sourceRow += sourceBytesPerRow;
1771 		destRow += destBytesPerRow;
1772 	}
1773 }
1774 
1775 
1776 // _DrawBitmap
1777 void
1778 Painter::_DrawBitmap(agg::rendering_buffer& srcBuffer, color_space format,
1779 	BRect actualBitmapRect, BRect bitmapRect, BRect viewRect,
1780 	uint32 options) const
1781 {
1782 	if (!fValidClipping
1783 		|| !bitmapRect.IsValid() || !bitmapRect.Intersects(actualBitmapRect)
1784 		|| !viewRect.IsValid()) {
1785 		return;
1786 	}
1787 
1788 	if (!fSubpixelPrecise) {
1789 		align_rect_to_pixels(&bitmapRect);
1790 		align_rect_to_pixels(&viewRect);
1791 	}
1792 
1793 	TRACE("Painter::_DrawBitmap()\n");
1794 	TRACE("   bitmapRect = (%.1f, %.1f) - (%.1f, %.1f)\n",
1795 		bitmapRect.left, bitmapRect.top, bitmapRect.right, bitmapRect.bottom);
1796 	TRACE("   viewRect = (%.1f, %.1f) - (%.1f, %.1f)\n",
1797 		viewRect.left, viewRect.top, viewRect.right, viewRect.bottom);
1798 
1799 	double xScale = (viewRect.Width() + 1) / (bitmapRect.Width() + 1);
1800 	double yScale = (viewRect.Height() + 1) / (bitmapRect.Height() + 1);
1801 
1802 	if (xScale == 0.0 || yScale == 0.0)
1803 		return;
1804 
1805 	// compensate for the lefttop offset the actualBitmapRect might have
1806 	// actualBitmapRect has the right size, but put it at B_ORIGIN
1807 	// bitmapRect is already in good coordinates
1808 	actualBitmapRect.OffsetBy(-actualBitmapRect.left, -actualBitmapRect.top);
1809 
1810 	// constrain rect to passed bitmap bounds
1811 	// and transfer the changes to the viewRect with the right scale
1812 	if (bitmapRect.left < actualBitmapRect.left) {
1813 		float diff = actualBitmapRect.left - bitmapRect.left;
1814 		viewRect.left += diff * xScale;
1815 		bitmapRect.left = actualBitmapRect.left;
1816 	}
1817 	if (bitmapRect.top < actualBitmapRect.top) {
1818 		float diff = actualBitmapRect.top - bitmapRect.top;
1819 		viewRect.top += diff * yScale;
1820 		bitmapRect.top = actualBitmapRect.top;
1821 	}
1822 	if (bitmapRect.right > actualBitmapRect.right) {
1823 		float diff = bitmapRect.right - actualBitmapRect.right;
1824 		viewRect.right -= diff * xScale;
1825 		bitmapRect.right = actualBitmapRect.right;
1826 	}
1827 	if (bitmapRect.bottom > actualBitmapRect.bottom) {
1828 		float diff = bitmapRect.bottom - actualBitmapRect.bottom;
1829 		viewRect.bottom -= diff * yScale;
1830 		bitmapRect.bottom = actualBitmapRect.bottom;
1831 	}
1832 
1833 	double xOffset = viewRect.left - bitmapRect.left;
1834 	double yOffset = viewRect.top - bitmapRect.top;
1835 
1836 	// optimized code path for B_CMAP8 and no scale
1837 	if (xScale == 1.0 && yScale == 1.0) {
1838 		if (format == B_CMAP8) {
1839 			if (fDrawingMode == B_OP_COPY) {
1840 				_DrawBitmapNoScale32(copy_bitmap_row_cmap8_copy, 1,
1841 					srcBuffer, (int32)xOffset, (int32)yOffset, viewRect);
1842 				return;
1843 			}
1844 			if (fDrawingMode == B_OP_OVER) {
1845 				_DrawBitmapNoScale32(copy_bitmap_row_cmap8_over, 1,
1846 					srcBuffer, (int32)xOffset, (int32)yOffset, viewRect);
1847 				return;
1848 			}
1849 		} else if (format == B_RGB32) {
1850 			if (fDrawingMode == B_OP_OVER) {
1851 				_DrawBitmapNoScale32(copy_bitmap_row_bgr32_over, 4,
1852 					srcBuffer, (int32)xOffset, (int32)yOffset, viewRect);
1853 				return;
1854 			}
1855 		}
1856 	}
1857 
1858 	BBitmap* temp = NULL;
1859 	ObjectDeleter<BBitmap> tempDeleter;
1860 
1861 	if ((format != B_RGBA32 && format != B_RGB32)
1862 		|| (format == B_RGB32 && fDrawingMode != B_OP_COPY
1863 #if 1
1864 // Enabling this would make the behavior compatible to BeOS, which
1865 // treats B_RGB32 bitmaps as B_RGB*A*32 bitmaps in B_OP_ALPHA - unlike in
1866 // all other drawing modes, where B_TRANSPARENT_MAGIC_RGBA32 is handled.
1867 // B_RGB32 bitmaps therefore don't draw correctly on BeOS if they actually
1868 // use this color, unless the alpha channel contains 255 for all other
1869 // pixels, which is inconsistent.
1870 			&& fDrawingMode != B_OP_ALPHA
1871 #endif
1872 		)) {
1873 		temp = new (nothrow) BBitmap(actualBitmapRect, B_BITMAP_NO_SERVER_LINK,
1874 			B_RGBA32);
1875 		if (temp == NULL) {
1876 			fprintf(stderr, "Painter::_DrawBitmap() - "
1877 				"out of memory for creating temporary conversion bitmap\n");
1878 			return;
1879 		}
1880 
1881 		tempDeleter.SetTo(temp);
1882 
1883 		status_t err = temp->ImportBits(srcBuffer.buf(),
1884 			srcBuffer.height() * srcBuffer.stride(),
1885 			srcBuffer.stride(), 0, format);
1886 		if (err < B_OK) {
1887 			fprintf(stderr, "Painter::_DrawBitmap() - "
1888 				"colorspace conversion failed: %s\n", strerror(err));
1889 			return;
1890 		}
1891 
1892 		// the original bitmap might have had some of the
1893 		// transaparent magic colors set that we now need to
1894 		// make transparent in our RGBA32 bitmap again.
1895 		switch (format) {
1896 			case B_RGB32:
1897 				_TransparentMagicToAlpha((uint32 *)srcBuffer.buf(),
1898 					srcBuffer.width(), srcBuffer.height(),
1899 					srcBuffer.stride(), B_TRANSPARENT_MAGIC_RGBA32,
1900 					temp);
1901 				break;
1902 
1903 			// TODO: not sure if this applies to B_RGBA15 too. It
1904 			// should not because B_RGBA15 actually has an alpha
1905 			// channel itself and it should have been preserved
1906 			// when importing the bitmap. Maybe it applies to
1907 			// B_RGB16 though?
1908 			case B_RGB15:
1909 				_TransparentMagicToAlpha((uint16 *)srcBuffer.buf(),
1910 					srcBuffer.width(), srcBuffer.height(),
1911 					srcBuffer.stride(), B_TRANSPARENT_MAGIC_RGBA15,
1912 					temp);
1913 				break;
1914 
1915 			default:
1916 				break;
1917 		}
1918 
1919 		srcBuffer.attach((uint8*)temp->Bits(),
1920 			(uint32)actualBitmapRect.IntegerWidth() + 1,
1921 			(uint32)actualBitmapRect.IntegerHeight() + 1,
1922 			temp->BytesPerRow());
1923 	}
1924 
1925 	// maybe we can use an optimized version if there is no scale
1926 	if (xScale == 1.0 && yScale == 1.0) {
1927 		if (fDrawingMode == B_OP_COPY) {
1928 			_DrawBitmapNoScale32(copy_bitmap_row_bgr32_copy, 4, srcBuffer,
1929 				(int32)xOffset, (int32)yOffset, viewRect);
1930 			return;
1931 		}
1932 		if (fDrawingMode == B_OP_OVER || (fDrawingMode == B_OP_ALPHA
1933 				 && fAlphaSrcMode == B_PIXEL_ALPHA
1934 				 && fAlphaFncMode == B_ALPHA_OVERLAY)) {
1935 			_DrawBitmapNoScale32(copy_bitmap_row_bgr32_alpha, 4, srcBuffer,
1936 				(int32)xOffset, (int32)yOffset, viewRect);
1937 			return;
1938 		}
1939 	}
1940 
1941 	if (fDrawingMode == B_OP_COPY) {
1942 		if ((options & B_FILTER_BITMAP_BILINEAR) != 0) {
1943 			_DrawBitmapBilinearCopy32(srcBuffer, xOffset, yOffset, xScale,
1944 				yScale, viewRect);
1945 		} else {
1946 			_DrawBitmapNearestNeighborCopy32(srcBuffer, xOffset, yOffset,
1947 				xScale, yScale, viewRect);
1948 		}
1949 		return;
1950 	}
1951 
1952 	// for all other cases (non-optimized drawing mode or scaled drawing)
1953 	_DrawBitmapGeneric32(srcBuffer, xOffset, yOffset, xScale, yScale, viewRect,
1954 		options);
1955 }
1956 
1957 
1958 #define DEBUG_DRAW_BITMAP 0
1959 
1960 
1961 // _DrawBitmapNoScale32
1962 template <class F>
1963 void
1964 Painter::_DrawBitmapNoScale32(F copyRowFunction, uint32 bytesPerSourcePixel,
1965 	agg::rendering_buffer& srcBuffer, int32 xOffset, int32 yOffset,
1966 	BRect viewRect) const
1967 {
1968 	// NOTE: this would crash if viewRect was large enough to read outside the
1969 	// bitmap, so make sure this is not the case before calling this function!
1970 	uint8* dst = fBuffer.row_ptr(0);
1971 	uint32 dstBPR = fBuffer.stride();
1972 
1973 	const uint8* src = srcBuffer.row_ptr(0);
1974 	uint32 srcBPR = srcBuffer.stride();
1975 
1976 	int32 left = (int32)viewRect.left;
1977 	int32 top = (int32)viewRect.top;
1978 	int32 right = (int32)viewRect.right;
1979 	int32 bottom = (int32)viewRect.bottom;
1980 
1981 #if DEBUG_DRAW_BITMAP
1982 if (left - xOffset < 0 || left - xOffset >= (int32)srcBuffer.width() ||
1983 	right - xOffset >= (int32)srcBuffer.width() ||
1984 	top - yOffset < 0 || top - yOffset >= (int32)srcBuffer.height() ||
1985 	bottom - yOffset >= (int32)srcBuffer.height()) {
1986 
1987 	char message[256];
1988 	sprintf(message, "reading outside of bitmap (%ld, %ld, %ld, %ld) "
1989 			"(%d, %d) (%ld, %ld)",
1990 		left - xOffset, top - yOffset, right - xOffset, bottom - yOffset,
1991 		srcBuffer.width(), srcBuffer.height(), xOffset, yOffset);
1992 	debugger(message);
1993 }
1994 #endif
1995 
1996 	const rgb_color* colorMap = SystemPalette();
1997 
1998 	// copy rects, iterate over clipping boxes
1999 	fBaseRenderer.first_clip_box();
2000 	do {
2001 		int32 x1 = max_c(fBaseRenderer.xmin(), left);
2002 		int32 x2 = min_c(fBaseRenderer.xmax(), right);
2003 		if (x1 <= x2) {
2004 			int32 y1 = max_c(fBaseRenderer.ymin(), top);
2005 			int32 y2 = min_c(fBaseRenderer.ymax(), bottom);
2006 			if (y1 <= y2) {
2007 				uint8* dstHandle = dst + y1 * dstBPR + x1 * 4;
2008 				const uint8* srcHandle = src + (y1 - yOffset) * srcBPR
2009 					+ (x1 - xOffset) * bytesPerSourcePixel;
2010 
2011 				for (; y1 <= y2; y1++) {
2012 					copyRowFunction(dstHandle, srcHandle, x2 - x1 + 1, colorMap);
2013 
2014 					dstHandle += dstBPR;
2015 					srcHandle += srcBPR;
2016 				}
2017 			}
2018 		}
2019 	} while (fBaseRenderer.next_clip_box());
2020 }
2021 
2022 
2023 // _DrawBitmapNearestNeighborCopy32
2024 void
2025 Painter::_DrawBitmapNearestNeighborCopy32(agg::rendering_buffer& srcBuffer,
2026 	double xOffset, double yOffset, double xScale, double yScale,
2027 	BRect viewRect) const
2028 {
2029 	//bigtime_t now = system_time();
2030 	uint32 dstWidth = viewRect.IntegerWidth() + 1;
2031 	uint32 dstHeight = viewRect.IntegerHeight() + 1;
2032 	uint32 srcWidth = srcBuffer.width();
2033 	uint32 srcHeight = srcBuffer.height();
2034 
2035 	// Do not calculate more filter weights than necessary and also
2036 	// keep the stack based allocations reasonably sized
2037 	if (fClippingRegion->Frame().IntegerWidth() + 1 < (int32)dstWidth)
2038 		dstWidth = fClippingRegion->Frame().IntegerWidth() + 1;
2039 	if (fClippingRegion->Frame().IntegerHeight() + 1 < (int32)dstHeight)
2040 		dstHeight = fClippingRegion->Frame().IntegerHeight() + 1;
2041 
2042 	// When calculating less filter weights than specified by viewRect,
2043 	// we need to compensate the offset.
2044 	uint32 filterWeightXIndexOffset = 0;
2045 	uint32 filterWeightYIndexOffset = 0;
2046 	if (fClippingRegion->Frame().left > viewRect.left) {
2047 		filterWeightXIndexOffset = (int32)(fClippingRegion->Frame().left
2048 			- viewRect.left);
2049 	}
2050 	if (fClippingRegion->Frame().top > viewRect.top) {
2051 		filterWeightYIndexOffset = (int32)(fClippingRegion->Frame().top
2052 			- viewRect.top);
2053 	}
2054 
2055 	// should not pose a problem with stack overflows
2056 	// (needs around 6Kb for 1920x1200)
2057 	uint16 xIndices[dstWidth];
2058 	uint16 yIndices[dstHeight];
2059 
2060 	// Extract the cropping information for the source bitmap,
2061 	// If only a part of the source bitmap is to be drawn with scale,
2062 	// the offset will be different from the viewRect left top corner.
2063 	int32 xBitmapShift = (int32)(viewRect.left - xOffset);
2064 	int32 yBitmapShift = (int32)(viewRect.top - yOffset);
2065 
2066 	for (uint32 i = 0; i < dstWidth; i++) {
2067 		// index into source
2068 		uint16 index = (uint16)((i + filterWeightXIndexOffset) * srcWidth
2069 			/ (srcWidth * xScale));
2070 		// round down to get the left pixel
2071 		xIndices[i] = index;
2072 		// handle cropped source bitmap
2073 		xIndices[i] += xBitmapShift;
2074 		// precompute index for 32 bit pixels
2075 		xIndices[i] *= 4;
2076 	}
2077 
2078 	for (uint32 i = 0; i < dstHeight; i++) {
2079 		// index into source
2080 		uint16 index = (uint16)((i + filterWeightYIndexOffset) * srcHeight
2081 			/ (srcHeight * yScale));
2082 		// round down to get the top pixel
2083 		yIndices[i] = index;
2084 		// handle cropped source bitmap
2085 		yIndices[i] += yBitmapShift;
2086 	}
2087 //printf("X: %d ... %d, %d (%ld or %f)\n",
2088 //	xIndices[0], xIndices[dstWidth - 2], xIndices[dstWidth - 1], dstWidth,
2089 //	srcWidth * xScale);
2090 //printf("Y: %d ... %d, %d (%ld or %f)\n",
2091 //	yIndices[0], yIndices[dstHeight - 2], yIndices[dstHeight - 1], dstHeight,
2092 //	srcHeight * yScale);
2093 
2094 	const int32 left = (int32)viewRect.left;
2095 	const int32 top = (int32)viewRect.top;
2096 	const int32 right = (int32)viewRect.right;
2097 	const int32 bottom = (int32)viewRect.bottom;
2098 
2099 	const uint32 dstBPR = fBuffer.stride();
2100 
2101 	// iterate over clipping boxes
2102 	fBaseRenderer.first_clip_box();
2103 	do {
2104 		const int32 x1 = max_c(fBaseRenderer.xmin(), left);
2105 		const int32 x2 = min_c(fBaseRenderer.xmax(), right);
2106 		if (x1 > x2)
2107 			continue;
2108 
2109 		int32 y1 = max_c(fBaseRenderer.ymin(), top);
2110 		int32 y2 = min_c(fBaseRenderer.ymax(), bottom);
2111 		if (y1 > y2)
2112 			continue;
2113 
2114 		// buffer offset into destination
2115 		uint8* dst = fBuffer.row_ptr(y1) + x1 * 4;
2116 
2117 		// x and y are needed as indeces into the wheight arrays, so the
2118 		// offset into the target buffer needs to be compensated
2119 		const int32 xIndexL = x1 - left - filterWeightXIndexOffset;
2120 		const int32 xIndexR = x2 - left - filterWeightXIndexOffset;
2121 		y1 -= top + filterWeightYIndexOffset;
2122 		y2 -= top + filterWeightYIndexOffset;
2123 
2124 //printf("x: %ld - %ld\n", xIndexL, xIndexR);
2125 //printf("y: %ld - %ld\n", y1, y2);
2126 
2127 		for (; y1 <= y2; y1++) {
2128 			// buffer offset into source (top row)
2129 			register const uint8* src = srcBuffer.row_ptr(yIndices[y1]);
2130 			// buffer handle for destination to be incremented per pixel
2131 			register uint32* d = (uint32*)dst;
2132 
2133 			for (int32 x = xIndexL; x <= xIndexR; x++) {
2134 				*d = *(uint32*)(src + xIndices[x]);
2135 				d++;
2136 			}
2137 			dst += dstBPR;
2138 		}
2139 	} while (fBaseRenderer.next_clip_box());
2140 
2141 //printf("draw bitmap %.5fx%.5f: %lld\n", xScale, yScale, system_time() - now);
2142 }
2143 
2144 
2145 // _DrawBitmapBilinearCopy32
2146 void
2147 Painter::_DrawBitmapBilinearCopy32(agg::rendering_buffer& srcBuffer,
2148 	double xOffset, double yOffset, double xScale, double yScale,
2149 	BRect viewRect) const
2150 {
2151 	//bigtime_t now = system_time();
2152 	uint32 dstWidth = viewRect.IntegerWidth() + 1;
2153 	uint32 dstHeight = viewRect.IntegerHeight() + 1;
2154 	uint32 srcWidth = srcBuffer.width();
2155 	uint32 srcHeight = srcBuffer.height();
2156 
2157 	// Do not calculate more filter weights than necessary and also
2158 	// keep the stack based allocations reasonably sized
2159 	if (fClippingRegion->Frame().IntegerWidth() + 1 < (int32)dstWidth)
2160 		dstWidth = fClippingRegion->Frame().IntegerWidth() + 1;
2161 	if (fClippingRegion->Frame().IntegerHeight() + 1 < (int32)dstHeight)
2162 		dstHeight = fClippingRegion->Frame().IntegerHeight() + 1;
2163 
2164 	// When calculating less filter weights than specified by viewRect,
2165 	// we need to compensate the offset.
2166 	uint32 filterWeightXIndexOffset = 0;
2167 	uint32 filterWeightYIndexOffset = 0;
2168 	if (fClippingRegion->Frame().left > viewRect.left) {
2169 		filterWeightXIndexOffset = (int32)(fClippingRegion->Frame().left
2170 			- viewRect.left);
2171 	}
2172 	if (fClippingRegion->Frame().top > viewRect.top) {
2173 		filterWeightYIndexOffset = (int32)(fClippingRegion->Frame().top
2174 			- viewRect.top);
2175 	}
2176 
2177 	struct FilterInfo {
2178 		uint16 index;	// index into source bitmap row/column
2179 		uint16 weight;	// weight of the pixel at index [0..255]
2180 	};
2181 
2182 //#define FILTER_INFOS_ON_HEAP
2183 #ifdef FILTER_INFOS_ON_HEAP
2184 	FilterInfo* xWeights = new (nothrow) FilterInfo[dstWidth];
2185 	FilterInfo* yWeights = new (nothrow) FilterInfo[dstHeight];
2186 	if (xWeights == NULL || yWeights == NULL) {
2187 		delete[] xWeights;
2188 		delete[] yWeights;
2189 		return;
2190 	}
2191 #else
2192 	// stack based saves about 200µs on 1.85 GHz Core 2 Duo
2193 	// should not pose a problem with stack overflows
2194 	// (needs around 12Kb for 1920x1200)
2195 	FilterInfo xWeights[dstWidth];
2196 	FilterInfo yWeights[dstHeight];
2197 #endif
2198 
2199 	// Extract the cropping information for the source bitmap,
2200 	// If only a part of the source bitmap is to be drawn with scale,
2201 	// the offset will be different from the viewRect left top corner.
2202 	int32 xBitmapShift = (int32)(viewRect.left - xOffset);
2203 	int32 yBitmapShift = (int32)(viewRect.top - yOffset);
2204 
2205 	for (uint32 i = 0; i < dstWidth; i++) {
2206 		// fractional index into source
2207 		// NOTE: It is very important to calculate the fractional index
2208 		// into the source pixel grid like this to prevent out of bounds
2209 		// access! It will result in the rightmost pixel of the destination
2210 		// to access the rightmost pixel of the source with a weighting
2211 		// of 255. This in turn will trigger an optimization in the loop
2212 		// that also prevents out of bounds access.
2213 		float index = (i + filterWeightXIndexOffset) * (srcWidth - 1)
2214 			/ (srcWidth * xScale - 1);
2215 		// round down to get the left pixel
2216 		xWeights[i].index = (uint16)index;
2217 		xWeights[i].weight = 255 - (uint16)((index - xWeights[i].index) * 255);
2218 		// handle cropped source bitmap
2219 		xWeights[i].index += xBitmapShift;
2220 		// precompute index for 32 bit pixels
2221 		xWeights[i].index *= 4;
2222 	}
2223 
2224 	for (uint32 i = 0; i < dstHeight; i++) {
2225 		// fractional index into source
2226 		// NOTE: It is very important to calculate the fractional index
2227 		// into the source pixel grid like this to prevent out of bounds
2228 		// access! It will result in the bottommost pixel of the destination
2229 		// to access the bottommost pixel of the source with a weighting
2230 		// of 255. This in turn will trigger an optimization in the loop
2231 		// that also prevents out of bounds access.
2232 		float index = (i + filterWeightYIndexOffset) * (srcHeight - 1)
2233 			/ (srcHeight * yScale - 1);
2234 		// round down to get the top pixel
2235 		yWeights[i].index = (uint16)index;
2236 		yWeights[i].weight = 255 - (uint16)((index - yWeights[i].index) * 255);
2237 		// handle cropped source bitmap
2238 		yWeights[i].index += yBitmapShift;
2239 	}
2240 //printf("X: %d/%d ... %d/%d, %d/%d (%ld)\n",
2241 //	xWeights[0].index, xWeights[0].weight,
2242 //	xWeights[dstWidth - 2].index, xWeights[dstWidth - 2].weight,
2243 //	xWeights[dstWidth - 1].index, xWeights[dstWidth - 1].weight,
2244 //	dstWidth);
2245 //printf("Y: %d/%d ... %d/%d, %d/%d (%ld)\n",
2246 //	yWeights[0].index, yWeights[0].weight,
2247 //	yWeights[dstHeight - 2].index, yWeights[dstHeight - 2].weight,
2248 //	yWeights[dstHeight - 1].index, yWeights[dstHeight - 1].weight,
2249 //	dstHeight);
2250 
2251 	const int32 left = (int32)viewRect.left;
2252 	const int32 top = (int32)viewRect.top;
2253 	const int32 right = (int32)viewRect.right;
2254 	const int32 bottom = (int32)viewRect.bottom;
2255 
2256 	const uint32 dstBPR = fBuffer.stride();
2257 	const uint32 srcBPR = srcBuffer.stride();
2258 
2259 	// Figure out which version of the code we want to use...
2260 	enum {
2261 		kOptimizeForLowFilterRatio = 0,
2262 		kUseDefaultVersion,
2263 		kUseSIMDVersion
2264 	};
2265 
2266 	int codeSelect = kUseDefaultVersion;
2267 
2268 	uint32 neededSIMDFlags = APPSERVER_SIMD_MMX | APPSERVER_SIMD_SSE;
2269 	if ((gAppServerSIMDFlags & neededSIMDFlags) == neededSIMDFlags)
2270 		codeSelect = kUseSIMDVersion;
2271 	else {
2272 		if (xScale == yScale && (xScale == 1.5 || xScale == 2.0
2273 			|| xScale == 2.5 || xScale == 3.0)) {
2274 			codeSelect = kOptimizeForLowFilterRatio;
2275 		}
2276 	}
2277 
2278 	// iterate over clipping boxes
2279 	fBaseRenderer.first_clip_box();
2280 	do {
2281 		const int32 x1 = max_c(fBaseRenderer.xmin(), left);
2282 		const int32 x2 = min_c(fBaseRenderer.xmax(), right);
2283 		if (x1 > x2)
2284 			continue;
2285 
2286 		int32 y1 = max_c(fBaseRenderer.ymin(), top);
2287 		int32 y2 = min_c(fBaseRenderer.ymax(), bottom);
2288 		if (y1 > y2)
2289 			continue;
2290 
2291 		// buffer offset into destination
2292 		uint8* dst = fBuffer.row_ptr(y1) + x1 * 4;
2293 
2294 		// x and y are needed as indeces into the wheight arrays, so the
2295 		// offset into the target buffer needs to be compensated
2296 		const int32 xIndexL = x1 - left - filterWeightXIndexOffset;
2297 		const int32 xIndexR = x2 - left - filterWeightXIndexOffset;
2298 		y1 -= top + filterWeightYIndexOffset;
2299 		y2 -= top + filterWeightYIndexOffset;
2300 
2301 //printf("x: %ld - %ld\n", xIndexL, xIndexR);
2302 //printf("y: %ld - %ld\n", y1, y2);
2303 
2304 		switch (codeSelect) {
2305 			case kOptimizeForLowFilterRatio:
2306 			{
2307 				// In this mode, we anticipate to hit many destination pixels
2308 				// that map directly to a source pixel, we have more branches
2309 				// in the inner loop but save time because of the special
2310 				// cases. If there are too few direct hit pixels, the branches
2311 				// only waste time.
2312 				for (; y1 <= y2; y1++) {
2313 					// cache the weight of the top and bottom row
2314 					const uint16 wTop = yWeights[y1].weight;
2315 					const uint16 wBottom = 255 - yWeights[y1].weight;
2316 
2317 					// buffer offset into source (top row)
2318 					register const uint8* src
2319 						= srcBuffer.row_ptr(yWeights[y1].index);
2320 					// buffer handle for destination to be incremented per
2321 					// pixel
2322 					register uint8* d = dst;
2323 
2324 					if (wTop == 255) {
2325 						for (int32 x = xIndexL; x <= xIndexR; x++) {
2326 							const uint8* s = src + xWeights[x].index;
2327 							// This case is important to prevent out
2328 							// of bounds access at bottom edge of the source
2329 							// bitmap. If the scale is low and integer, it will
2330 							// also help the speed.
2331 							if (xWeights[x].weight == 255) {
2332 								// As above, but to prevent out of bounds
2333 								// on the right edge.
2334 								*(uint32*)d = *(uint32*)s;
2335 							} else {
2336 								// Only the left and right pixels are
2337 								// interpolated, since the top row has 100%
2338 								// weight.
2339 								const uint16 wLeft = xWeights[x].weight;
2340 								const uint16 wRight = 255 - wLeft;
2341 								d[0] = (s[0] * wLeft + s[4] * wRight) >> 8;
2342 								d[1] = (s[1] * wLeft + s[5] * wRight) >> 8;
2343 								d[2] = (s[2] * wLeft + s[6] * wRight) >> 8;
2344 							}
2345 							d += 4;
2346 						}
2347 					} else {
2348 						for (int32 x = xIndexL; x <= xIndexR; x++) {
2349 							const uint8* s = src + xWeights[x].index;
2350 							if (xWeights[x].weight == 255) {
2351 								// Prevent out of bounds access on the right
2352 								// edge or simply speed up.
2353 								const uint8* sBottom = s + srcBPR;
2354 								d[0] = (s[0] * wTop + sBottom[0] * wBottom)
2355 									>> 8;
2356 								d[1] = (s[1] * wTop + sBottom[1] * wBottom)
2357 									>> 8;
2358 								d[2] = (s[2] * wTop + sBottom[2] * wBottom)
2359 									>> 8;
2360 							} else {
2361 								// calculate the weighted sum of all four
2362 								// interpolated pixels
2363 								const uint16 wLeft = xWeights[x].weight;
2364 								const uint16 wRight = 255 - wLeft;
2365 								// left and right of top row
2366 								uint32 t0 = (s[0] * wLeft + s[4] * wRight)
2367 									* wTop;
2368 								uint32 t1 = (s[1] * wLeft + s[5] * wRight)
2369 									* wTop;
2370 								uint32 t2 = (s[2] * wLeft + s[6] * wRight)
2371 									* wTop;
2372 
2373 								// left and right of bottom row
2374 								s += srcBPR;
2375 								t0 += (s[0] * wLeft + s[4] * wRight) * wBottom;
2376 								t1 += (s[1] * wLeft + s[5] * wRight) * wBottom;
2377 								t2 += (s[2] * wLeft + s[6] * wRight) * wBottom;
2378 
2379 								d[0] = t0 >> 16;
2380 								d[1] = t1 >> 16;
2381 								d[2] = t2 >> 16;
2382 							}
2383 							d += 4;
2384 						}
2385 					}
2386 					dst += dstBPR;
2387 				}
2388 				break;
2389 			}
2390 
2391 			case kUseDefaultVersion:
2392 			{
2393 				// In this mode we anticipate many pixels wich need filtering,
2394 				// there are no special cases for direct hit pixels except for
2395 				// the last column/row and the right/bottom corner pixel.
2396 
2397 				// The last column/row handling does not need to be performed
2398 				// for all clipping rects!
2399 				int32 yMax = y2;
2400 				if (yWeights[yMax].weight == 255)
2401 					yMax--;
2402 				int32 xIndexMax = xIndexR;
2403 				if (xWeights[xIndexMax].weight == 255)
2404 					xIndexMax--;
2405 
2406 				for (; y1 <= yMax; y1++) {
2407 					// cache the weight of the top and bottom row
2408 					const uint16 wTop = yWeights[y1].weight;
2409 					const uint16 wBottom = 255 - yWeights[y1].weight;
2410 
2411 					// buffer offset into source (top row)
2412 					register const uint8* src
2413 						= srcBuffer.row_ptr(yWeights[y1].index);
2414 					// buffer handle for destination to be incremented per
2415 					// pixel
2416 					register uint8* d = dst;
2417 
2418 					for (int32 x = xIndexL; x <= xIndexMax; x++) {
2419 						const uint8* s = src + xWeights[x].index;
2420 						// calculate the weighted sum of all four
2421 						// interpolated pixels
2422 						const uint16 wLeft = xWeights[x].weight;
2423 						const uint16 wRight = 255 - wLeft;
2424 						// left and right of top row
2425 						uint32 t0 = (s[0] * wLeft + s[4] * wRight) * wTop;
2426 						uint32 t1 = (s[1] * wLeft + s[5] * wRight) * wTop;
2427 						uint32 t2 = (s[2] * wLeft + s[6] * wRight) * wTop;
2428 
2429 						// left and right of bottom row
2430 						s += srcBPR;
2431 						t0 += (s[0] * wLeft + s[4] * wRight) * wBottom;
2432 						t1 += (s[1] * wLeft + s[5] * wRight) * wBottom;
2433 						t2 += (s[2] * wLeft + s[6] * wRight) * wBottom;
2434 						d[0] = t0 >> 16;
2435 						d[1] = t1 >> 16;
2436 						d[2] = t2 >> 16;
2437 						d += 4;
2438 					}
2439 					// last column of pixels if necessary
2440 					if (xIndexMax < xIndexR) {
2441 						const uint8* s = src + xWeights[xIndexR].index;
2442 						const uint8* sBottom = s + srcBPR;
2443 						d[0] = (s[0] * wTop + sBottom[0] * wBottom) >> 8;
2444 						d[1] = (s[1] * wTop + sBottom[1] * wBottom) >> 8;
2445 						d[2] = (s[2] * wTop + sBottom[2] * wBottom) >> 8;
2446 					}
2447 
2448 					dst += dstBPR;
2449 				}
2450 
2451 				// last row of pixels if necessary
2452 				// buffer offset into source (bottom row)
2453 				register const uint8* src
2454 					= srcBuffer.row_ptr(yWeights[y2].index);
2455 				// buffer handle for destination to be incremented per pixel
2456 				register uint8* d = dst;
2457 
2458 				if (yMax < y2) {
2459 					for (int32 x = xIndexL; x <= xIndexMax; x++) {
2460 						const uint8* s = src + xWeights[x].index;
2461 						const uint16 wLeft = xWeights[x].weight;
2462 						const uint16 wRight = 255 - wLeft;
2463 						d[0] = (s[0] * wLeft + s[4] * wRight) >> 8;
2464 						d[1] = (s[1] * wLeft + s[5] * wRight) >> 8;
2465 						d[2] = (s[2] * wLeft + s[6] * wRight) >> 8;
2466 						d += 4;
2467 					}
2468 				}
2469 
2470 				// pixel in bottom right corner if necessary
2471 				if (yMax < y2 && xIndexMax < xIndexR) {
2472 					const uint8* s = src + xWeights[xIndexR].index;
2473 					*(uint32*)d = *(uint32*)s;
2474 				}
2475 				break;
2476 			}
2477 
2478 #ifdef __INTEL__
2479 			case kUseSIMDVersion:
2480 			{
2481 				// Basically the same as the "standard" mode, but we use SIMD
2482 				// routines for the processing of the single display lines.
2483 
2484 				// The last column/row handling does not need to be performed
2485 				// for all clipping rects!
2486 				int32 yMax = y2;
2487 				if (yWeights[yMax].weight == 255)
2488 					yMax--;
2489 				int32 xIndexMax = xIndexR;
2490 				if (xWeights[xIndexMax].weight == 255)
2491 					xIndexMax--;
2492 
2493 				for (; y1 <= yMax; y1++) {
2494 					// cache the weight of the top and bottom row
2495 					const uint16 wTop = yWeights[y1].weight;
2496 					const uint16 wBottom = 255 - yWeights[y1].weight;
2497 
2498 					// buffer offset into source (top row)
2499 					const uint8* src = srcBuffer.row_ptr(yWeights[y1].index);
2500 					// buffer handle for destination to be incremented per
2501 					// pixel
2502 					uint8* d = dst;
2503 					bilinear_scale_xloop_mmxsse(src, dst, xWeights,	xIndexL,
2504 						xIndexMax, wTop, srcBPR);
2505 					// increase pointer by processed pixels
2506 					d += (xIndexMax - xIndexL + 1) * 4;
2507 
2508 					// last column of pixels if necessary
2509 					if (xIndexMax < xIndexR) {
2510 						const uint8* s = src + xWeights[xIndexR].index;
2511 						const uint8* sBottom = s + srcBPR;
2512 						d[0] = (s[0] * wTop + sBottom[0] * wBottom) >> 8;
2513 						d[1] = (s[1] * wTop + sBottom[1] * wBottom) >> 8;
2514 						d[2] = (s[2] * wTop + sBottom[2] * wBottom) >> 8;
2515 					}
2516 
2517 					dst += dstBPR;
2518 				}
2519 
2520 				// last row of pixels if necessary
2521 				// buffer offset into source (bottom row)
2522 				register const uint8* src
2523 					= srcBuffer.row_ptr(yWeights[y2].index);
2524 				// buffer handle for destination to be incremented per pixel
2525 				register uint8* d = dst;
2526 
2527 				if (yMax < y2) {
2528 					for (int32 x = xIndexL; x <= xIndexMax; x++) {
2529 						const uint8* s = src + xWeights[x].index;
2530 						const uint16 wLeft = xWeights[x].weight;
2531 						const uint16 wRight = 255 - wLeft;
2532 						d[0] = (s[0] * wLeft + s[4] * wRight) >> 8;
2533 						d[1] = (s[1] * wLeft + s[5] * wRight) >> 8;
2534 						d[2] = (s[2] * wLeft + s[6] * wRight) >> 8;
2535 						d += 4;
2536 					}
2537 				}
2538 
2539 				// pixel in bottom right corner if necessary
2540 				if (yMax < y2 && xIndexMax < xIndexR) {
2541 					const uint8* s = src + xWeights[xIndexR].index;
2542 					*(uint32*)d = *(uint32*)s;
2543 				}
2544 				break;
2545 			}
2546 #endif	// __INTEL__
2547 		}
2548 	} while (fBaseRenderer.next_clip_box());
2549 
2550 #ifdef FILTER_INFOS_ON_HEAP
2551 	delete[] xWeights;
2552 	delete[] yWeights;
2553 #endif
2554 //printf("draw bitmap %.5fx%.5f: %lld\n", xScale, yScale, system_time() - now);
2555 }
2556 
2557 
2558 // _DrawBitmapGeneric32
2559 void
2560 Painter::_DrawBitmapGeneric32(agg::rendering_buffer& srcBuffer,
2561 	double xOffset, double yOffset, double xScale, double yScale,
2562 	BRect viewRect, uint32 options) const
2563 {
2564 	TRACE("Painter::_DrawBitmapGeneric32()\n");
2565 	TRACE("   offset: %.1f, %.1f\n", xOffset, yOffset);
2566 	TRACE("   scale: %.3f, %.3f\n", xScale, yScale);
2567 	TRACE("   viewRect: (%.1f, %.1f) - (%.1f, %.1f)\n",
2568 		viewRect.left, viewRect.top, viewRect.right, viewRect.bottom);
2569 	// AGG pipeline
2570 
2571 	// pixel format attached to bitmap
2572 	typedef agg::pixfmt_bgra32 pixfmt_image;
2573 	pixfmt_image pixf_img(srcBuffer);
2574 
2575 	agg::trans_affine srcMatrix;
2576 	// NOTE: R5 seems to ignore this offset when drawing bitmaps
2577 	//	srcMatrix *= agg::trans_affine_translation(-actualBitmapRect.left,
2578 	//		-actualBitmapRect.top);
2579 
2580 	agg::trans_affine imgMatrix;
2581 	imgMatrix *= agg::trans_affine_translation(xOffset - viewRect.left,
2582 		yOffset - viewRect.top);
2583 	imgMatrix *= agg::trans_affine_scaling(xScale, yScale);
2584 	imgMatrix *= agg::trans_affine_translation(viewRect.left, viewRect.top);
2585 	imgMatrix.invert();
2586 
2587 	// image interpolator
2588 	typedef agg::span_interpolator_linear<> interpolator_type;
2589 	interpolator_type interpolator(imgMatrix);
2590 
2591 	// scanline allocator
2592 	agg::span_allocator<pixfmt_image::color_type> spanAllocator;
2593 
2594 	// image accessor attached to pixel format of bitmap
2595 	typedef agg::image_accessor_clip<pixfmt_image> source_type;
2596 	source_type source(pixf_img, agg::rgba8(0, 0, 0, 0));
2597 
2598 	// clip to the current clipping region's frame
2599 	viewRect = viewRect & fClippingRegion->Frame();
2600 	// convert to pixel coords (versus pixel indices)
2601 	viewRect.right++;
2602 	viewRect.bottom++;
2603 
2604 	// path enclosing the bitmap
2605 	fPath.remove_all();
2606 	fPath.move_to(viewRect.left, viewRect.top);
2607 	fPath.line_to(viewRect.right, viewRect.top);
2608 	fPath.line_to(viewRect.right, viewRect.bottom);
2609 	fPath.line_to(viewRect.left, viewRect.bottom);
2610 	fPath.close_polygon();
2611 
2612 	agg::conv_transform<agg::path_storage> transformedPath(fPath, srcMatrix);
2613 	fRasterizer.reset();
2614 	fRasterizer.add_path(transformedPath);
2615 
2616 	if ((options & B_FILTER_BITMAP_BILINEAR) != 0) {
2617 		// image filter (bilinear)
2618 		typedef agg::span_image_filter_rgba_bilinear<
2619 			source_type, interpolator_type> span_gen_type;
2620 		span_gen_type spanGenerator(source, interpolator);
2621 
2622 		// render the path with the bitmap as scanline fill
2623 		agg::render_scanlines_aa(fRasterizer, fUnpackedScanline, fBaseRenderer,
2624 			spanAllocator, spanGenerator);
2625 	} else {
2626 		// image filter (nearest neighbor)
2627 		typedef agg::span_image_filter_rgba_nn<
2628 			source_type, interpolator_type> span_gen_type;
2629 		span_gen_type spanGenerator(source, interpolator);
2630 
2631 		// render the path with the bitmap as scanline fill
2632 		agg::render_scanlines_aa(fRasterizer, fUnpackedScanline, fBaseRenderer,
2633 			spanAllocator, spanGenerator);
2634 	}
2635 }
2636 
2637 
2638 // _InvertRect32
2639 void
2640 Painter::_InvertRect32(BRect r) const
2641 {
2642 	int32 width = r.IntegerWidth() + 1;
2643 	for (int32 y = (int32)r.top; y <= (int32)r.bottom; y++) {
2644 		uint8* dst = fBuffer.row_ptr(y);
2645 		dst += (int32)r.left * 4;
2646 		for (int32 i = 0; i < width; i++) {
2647 			dst[0] = 255 - dst[0];
2648 			dst[1] = 255 - dst[1];
2649 			dst[2] = 255 - dst[2];
2650 			dst += 4;
2651 		}
2652 	}
2653 }
2654 
2655 
2656 // _BlendRect32
2657 void
2658 Painter::_BlendRect32(const BRect& r, const rgb_color& c) const
2659 {
2660 	if (!fValidClipping)
2661 		return;
2662 
2663 	uint8* dst = fBuffer.row_ptr(0);
2664 	uint32 bpr = fBuffer.stride();
2665 
2666 	int32 left = (int32)r.left;
2667 	int32 top = (int32)r.top;
2668 	int32 right = (int32)r.right;
2669 	int32 bottom = (int32)r.bottom;
2670 
2671 	// fill rects, iterate over clipping boxes
2672 	fBaseRenderer.first_clip_box();
2673 	do {
2674 		int32 x1 = max_c(fBaseRenderer.xmin(), left);
2675 		int32 x2 = min_c(fBaseRenderer.xmax(), right);
2676 		if (x1 <= x2) {
2677 			int32 y1 = max_c(fBaseRenderer.ymin(), top);
2678 			int32 y2 = min_c(fBaseRenderer.ymax(), bottom);
2679 
2680 			uint8* offset = dst + x1 * 4 + y1 * bpr;
2681 			for (; y1 <= y2; y1++) {
2682 				blend_line32(offset, x2 - x1 + 1, c.red, c.green, c.blue,
2683 					c.alpha);
2684 				offset += bpr;
2685 			}
2686 		}
2687 	} while (fBaseRenderer.next_clip_box());
2688 }
2689 
2690 
2691 // #pragma mark -
2692 
2693 
2694 template<class VertexSource>
2695 BRect
2696 Painter::_BoundingBox(VertexSource& path) const
2697 {
2698 	double left = 0.0;
2699 	double top = 0.0;
2700 	double right = -1.0;
2701 	double bottom = -1.0;
2702 	uint32 pathID[1];
2703 	pathID[0] = 0;
2704 	agg::bounding_rect(path, pathID, 0, 1, &left, &top, &right, &bottom);
2705 	return BRect(left, top, right, bottom);
2706 }
2707 
2708 
2709 // agg_line_cap_mode_for
2710 inline agg::line_cap_e
2711 agg_line_cap_mode_for(cap_mode mode)
2712 {
2713 	switch (mode) {
2714 		case B_BUTT_CAP:
2715 			return agg::butt_cap;
2716 		case B_SQUARE_CAP:
2717 			return agg::square_cap;
2718 		case B_ROUND_CAP:
2719 			return agg::round_cap;
2720 	}
2721 	return agg::butt_cap;
2722 }
2723 
2724 
2725 // agg_line_join_mode_for
2726 inline agg::line_join_e
2727 agg_line_join_mode_for(join_mode mode)
2728 {
2729 	switch (mode) {
2730 		case B_MITER_JOIN:
2731 			return agg::miter_join;
2732 		case B_ROUND_JOIN:
2733 			return agg::round_join;
2734 		case B_BEVEL_JOIN:
2735 		case B_BUTT_JOIN: // ??
2736 		case B_SQUARE_JOIN: // ??
2737 			return agg::bevel_join;
2738 	}
2739 	return agg::miter_join;
2740 }
2741 
2742 // _StrokePath
2743 template<class VertexSource>
2744 BRect
2745 Painter::_StrokePath(VertexSource& path) const
2746 {
2747 	agg::conv_stroke<VertexSource> stroke(path);
2748 	stroke.width(fPenSize);
2749 
2750 	stroke.line_cap(agg_line_cap_mode_for(fLineCapMode));
2751 	stroke.line_join(agg_line_join_mode_for(fLineJoinMode));
2752 	stroke.miter_limit(fMiterLimit);
2753 
2754 	if (gSubpixelAntialiasing) {
2755 		fSubpixRasterizer.reset();
2756 		fSubpixRasterizer.add_path(stroke);
2757 
2758 		agg::render_scanlines(fSubpixRasterizer,
2759 			fSubpixPackedScanline, fSubpixRenderer);
2760 	} else {
2761 		fRasterizer.reset();
2762 		fRasterizer.add_path(stroke);
2763 
2764 		agg::render_scanlines(fRasterizer, fPackedScanline, fRenderer);
2765 	}
2766 
2767 	BRect touched = _BoundingBox(path);
2768 	float penSize = ceilf(fPenSize / 2.0);
2769 	touched.InsetBy(-penSize, -penSize);
2770 
2771 	return _Clipped(touched);
2772 }
2773 
2774 
2775 // _FillPath
2776 template<class VertexSource>
2777 BRect
2778 Painter::_FillPath(VertexSource& path) const
2779 {
2780 	if (gSubpixelAntialiasing) {
2781 		fSubpixRasterizer.reset();
2782 		fSubpixRasterizer.add_path(path);
2783 		agg::render_scanlines(fSubpixRasterizer,
2784 			fSubpixPackedScanline, fSubpixRenderer);
2785 	} else {
2786 		fRasterizer.reset();
2787 		fRasterizer.add_path(path);
2788 		agg::render_scanlines(fRasterizer, fPackedScanline, fRenderer);
2789 	}
2790 
2791 	return _Clipped(_BoundingBox(path));
2792 }
2793 
2794 
2795 // _FillPath
2796 template<class VertexSource>
2797 BRect
2798 Painter::_FillPath(VertexSource& path, const BGradient& gradient) const
2799 {
2800 	GTRACE("Painter::_FillPath\n");
2801 
2802 	switch(gradient.GetType()) {
2803 		case BGradient::TYPE_LINEAR: {
2804 			GTRACE(("Painter::_FillPath> type == TYPE_LINEAR\n"));
2805 			_FillPathGradientLinear(path, *((const BGradientLinear*) &gradient));
2806 			break;
2807 		}
2808 		case BGradient::TYPE_RADIAL: {
2809 			GTRACE(("Painter::_FillPathGradient> type == TYPE_RADIAL\n"));
2810 			_FillPathGradientRadial(path,
2811 				*((const BGradientRadial*) &gradient));
2812 			break;
2813 		}
2814 		case BGradient::TYPE_RADIAL_FOCUS: {
2815 			GTRACE(("Painter::_FillPathGradient> type == TYPE_RADIAL_FOCUS\n"));
2816 			_FillPathGradientRadialFocus(path,
2817 				*((const BGradientRadialFocus*) &gradient));
2818 			break;
2819 		}
2820 		case BGradient::TYPE_DIAMOND: {
2821 			GTRACE(("Painter::_FillPathGradient> type == TYPE_DIAMOND\n"));
2822 			_FillPathGradientDiamond(path,
2823 				*((const BGradientDiamond*) &gradient));
2824 			break;
2825 		}
2826 		case BGradient::TYPE_CONIC: {
2827 			GTRACE(("Painter::_FillPathGradient> type == TYPE_CONIC\n"));
2828 			_FillPathGradientConic(path,
2829 				*((const BGradientConic*) &gradient));
2830 			break;
2831 		}
2832 		case BGradient::TYPE_NONE: {
2833 			GTRACE(("Painter::_FillPathGradient> type == TYPE_NONE\n"));
2834 			break;
2835 		}
2836 	}
2837 
2838 	return _Clipped(_BoundingBox(path));
2839 }
2840 
2841 
2842 // _MakeGradient
2843 void
2844 Painter::_MakeGradient(const BGradient& gradient, int32 colorCount,
2845 	uint32* colors, int32 arrayOffset, int32 arraySize) const
2846 {
2847 	BGradient::ColorStop* from = gradient.ColorStopAt(0);
2848 
2849 	if (!from)
2850 		return;
2851 
2852 	// current index into "colors" array
2853 //	int32 index = (int32)floorf(colorCount * from->offset + 0.5)
2854 //		+ arrayOffset;
2855 	int32 index = (int32)floorf(colorCount * from->offset / 255 + 0.5)
2856 		+ arrayOffset;
2857 	if (index > arraySize)
2858 		index = arraySize;
2859 	// Make sure we fill the entire array in case the gradient is outside.
2860 	if (index > 0) {
2861 		uint8* c = (uint8*)&colors[0];
2862 		for (int32 i = 0; i < index; i++) {
2863 			c[0] = from->color.blue;
2864 			c[1] = from->color.green;
2865 			c[2] = from->color.red;
2866 			c[3] = from->color.alpha;
2867 			c += 4;
2868 		}
2869 	}
2870 
2871 	// interpolate "from" to "to"
2872 	int32 stopCount = gradient.CountColorStops();
2873 	for (int32 i = 1; i < stopCount; i++) {
2874 		// find the step with the next offset
2875 		BGradient::ColorStop* to = gradient.ColorStopAtFast(i);
2876 
2877 		// interpolate
2878 //		int32 offset = (int32)floorf((colorCount - 1) * to->offset + 0.5);
2879 		int32 offset = (int32)floorf((colorCount - 1)
2880 			* to->offset / 255 + 0.5);
2881 		if (offset > colorCount - 1)
2882 			offset = colorCount - 1;
2883 		offset += arrayOffset;
2884 		int32 dist = offset - index;
2885 		if (dist >= 0) {
2886 			int32 startIndex = max_c(index, 0);
2887 			int32 stopIndex = min_c(offset, arraySize - 1);
2888 			uint8* c = (uint8*)&colors[startIndex];
2889 			for (int32 i = startIndex; i <= stopIndex; i++) {
2890 				float f = (float)(offset - i) / (float)(dist + 1);
2891 				float t = 1.0 - f;
2892 				c[0] = (uint8)floorf(from->color.blue * f
2893 					+ to->color.blue * t + 0.5);
2894 				c[1] = (uint8)floorf(from->color.green * f
2895 					+ to->color.green * t + 0.5);
2896 				c[2] = (uint8)floorf(from->color.red * f
2897 					+ to->color.red * t + 0.5);
2898 				c[3] = (uint8)floorf(from->color.alpha * f
2899 					+ to->color.alpha * t + 0.5);
2900 				c += 4;
2901 			}
2902 		}
2903 		index = offset + 1;
2904 		// the current "to" will be the "from" in the next interpolation
2905 		from = to;
2906 	}
2907 	//  make sure we fill the entire array
2908 	if (index < arraySize) {
2909 		int32 startIndex = max_c(index, 0);
2910 		uint8* c = (uint8*)&colors[startIndex];
2911 		for (int32 i = startIndex; i < arraySize; i++) {
2912 			c[0] = from->color.blue;
2913 			c[1] = from->color.green;
2914 			c[2] = from->color.red;
2915 			c[3] = from->color.alpha;
2916 			c += 4;
2917 		}
2918 	}
2919 }
2920 
2921 
2922 // _MakeGradient
2923 template<class Array>
2924 void
2925 Painter::_MakeGradient(Array& array, const BGradient& gradient) const
2926 {
2927 	for (int i = 0; i < gradient.CountColorStops() - 1; i++) {
2928 		BGradient::ColorStop* from = gradient.ColorStopAtFast(i);
2929 		BGradient::ColorStop* to = gradient.ColorStopAtFast(i + 1);
2930 		agg::rgba8 fromColor(from->color.red, from->color.green,
2931 							 from->color.blue, from->color.alpha);
2932 		agg::rgba8 toColor(to->color.red, to->color.green,
2933 						   to->color.blue, to->color.alpha);
2934 		GTRACE("Painter::_MakeGradient> fromColor(%d, %d, %d) offset = %f\n",
2935 			   fromColor.r, fromColor.g, fromColor.b, from->offset);
2936 		GTRACE("Painter::_MakeGradient> toColor(%d, %d, %d) offset = %f\n",
2937 			   toColor.r, toColor.g, toColor.b, to->offset);
2938 		float dist = to->offset - from->offset;
2939 		GTRACE("Painter::_MakeGradient> dist = %f\n", dist);
2940 		// TODO: Review this... offset should better be on [0..1]
2941 		if (dist > 0) {
2942 			for (int j = (int)from->offset; j <= (int)to->offset; j++) {
2943 				float f = (float)(to->offset - j) / (float)(dist + 1);
2944 				array[j] = toColor.gradient(fromColor, f);
2945 				GTRACE("Painter::_MakeGradient> array[%d](%d, %d, %d)\n",
2946 					   array[j].r, array[j].g, array[j].b);
2947 			}
2948 		}
2949 	}
2950 }
2951 
2952 
2953 // _CalcLinearGradientTransform
2954 void Painter::_CalcLinearGradientTransform(BPoint startPoint, BPoint endPoint,
2955 	agg::trans_affine& matrix, float gradient_d2) const
2956 {
2957 	float dx = endPoint.x - startPoint.x;
2958 	float dy = endPoint.y - startPoint.y;
2959 
2960 	matrix.reset();
2961 	matrix *= agg::trans_affine_scaling(sqrt(dx * dx + dy * dy) / gradient_d2);
2962 	matrix *= agg::trans_affine_rotation(atan2(dy, dx));
2963 	matrix *= agg::trans_affine_translation(startPoint.x, startPoint.y);
2964 	matrix.invert();
2965 }
2966 
2967 
2968 // _FillPathGradientLinear
2969 template<class VertexSource>
2970 void
2971 Painter::_FillPathGradientLinear(VertexSource& path,
2972 	const BGradientLinear& linear) const
2973 {
2974 	GTRACE("Painter::_FillPathGradientLinear\n");
2975 
2976 	BPoint start = linear.Start();
2977 	BPoint end = linear.End();
2978 
2979 	typedef agg::span_interpolator_linear<> interpolator_type;
2980 	typedef agg::pod_auto_array<agg::rgba8, 256> color_array_type;
2981 	typedef agg::span_allocator<agg::rgba8> span_allocator_type;
2982 	typedef agg::gradient_x	gradient_func_type;
2983 	typedef agg::span_gradient<agg::rgba8, interpolator_type,
2984 				gradient_func_type, color_array_type> span_gradient_type;
2985 	typedef agg::renderer_scanline_aa<renderer_base, span_allocator_type,
2986 				span_gradient_type> renderer_gradient_type;
2987 
2988 	gradient_func_type gradientFunc;
2989 	agg::trans_affine gradientMatrix;
2990 	interpolator_type spanInterpolator(gradientMatrix);
2991 	span_allocator_type spanAllocator;
2992 	color_array_type colorArray;
2993 
2994 	_MakeGradient(colorArray, linear);
2995 
2996 	span_gradient_type spanGradient(spanInterpolator, gradientFunc, colorArray,
2997 		0, 100);
2998 
2999 	renderer_gradient_type gradientRenderer(fBaseRenderer, spanAllocator,
3000 		spanGradient);
3001 
3002 	_CalcLinearGradientTransform(start, end, gradientMatrix);
3003 
3004 	fRasterizer.reset();
3005 	fRasterizer.add_path(path);
3006 	agg::render_scanlines(fRasterizer, fPackedScanline, gradientRenderer);
3007 }
3008 
3009 
3010 // _FillPathGradientRadial
3011 template<class VertexSource>
3012 void
3013 Painter::_FillPathGradientRadial(VertexSource& path,
3014 	const BGradientRadial& radial) const
3015 {
3016 	GTRACE("Painter::_FillPathGradientRadial\n");
3017 
3018 	BPoint center = radial.Center();
3019 // TODO: finish this
3020 //	float radius = radial.Radius();
3021 
3022 	typedef agg::span_interpolator_linear<> interpolator_type;
3023 	typedef agg::pod_auto_array<agg::rgba8, 256> color_array_type;
3024 	typedef agg::span_allocator<agg::rgba8> span_allocator_type;
3025 	typedef agg::gradient_radial gradient_func_type;
3026 	typedef agg::span_gradient<agg::rgba8, interpolator_type,
3027 	gradient_func_type, color_array_type> span_gradient_type;
3028 	typedef agg::renderer_scanline_aa<renderer_base, span_allocator_type,
3029 	span_gradient_type> renderer_gradient_type;
3030 
3031 	gradient_func_type gradientFunc;
3032 	agg::trans_affine gradientMatrix;
3033 	interpolator_type spanInterpolator(gradientMatrix);
3034 	span_allocator_type spanAllocator;
3035 	color_array_type colorArray;
3036 
3037 	_MakeGradient(colorArray, radial);
3038 
3039 	span_gradient_type spanGradient(spanInterpolator, gradientFunc, colorArray,
3040 		0, 100);
3041 
3042 	renderer_gradient_type gradientRenderer(fBaseRenderer, spanAllocator,
3043 		spanGradient);
3044 
3045 	gradientMatrix.reset();
3046 	gradientMatrix *= agg::trans_affine_translation(center.x, center.y);
3047 	gradientMatrix.invert();
3048 
3049 //	_CalcLinearGradientTransform(start, end, gradientMtx);
3050 
3051 	fRasterizer.reset();
3052 	fRasterizer.add_path(path);
3053 	agg::render_scanlines(fRasterizer, fPackedScanline, gradientRenderer);
3054 }
3055 
3056 
3057 // _FillPathGradientRadialFocus
3058 template<class VertexSource>
3059 void
3060 Painter::_FillPathGradientRadialFocus(VertexSource& path,
3061 	const BGradientRadialFocus& focus) const
3062 {
3063 	GTRACE("Painter::_FillPathGradientRadialFocus\n");
3064 
3065 	BPoint center = focus.Center();
3066 // TODO: finish this.
3067 //	BPoint focal = focus.Focal();
3068 //	float radius = focus.Radius();
3069 
3070 	typedef agg::span_interpolator_linear<> interpolator_type;
3071 	typedef agg::pod_auto_array<agg::rgba8, 256> color_array_type;
3072 	typedef agg::span_allocator<agg::rgba8> span_allocator_type;
3073 	typedef agg::gradient_radial_focus gradient_func_type;
3074 	typedef agg::span_gradient<agg::rgba8, interpolator_type,
3075 	gradient_func_type, color_array_type> span_gradient_type;
3076 	typedef agg::renderer_scanline_aa<renderer_base, span_allocator_type,
3077 	span_gradient_type> renderer_gradient_type;
3078 
3079 	gradient_func_type gradientFunc;
3080 	agg::trans_affine gradientMatrix;
3081 	interpolator_type spanInterpolator(gradientMatrix);
3082 	span_allocator_type spanAllocator;
3083 	color_array_type colorArray;
3084 
3085 	_MakeGradient(colorArray, focus);
3086 
3087 	span_gradient_type spanGradient(spanInterpolator, gradientFunc, colorArray,
3088 		0, 100);
3089 
3090 	renderer_gradient_type gradientRenderer(fBaseRenderer, spanAllocator,
3091 		spanGradient);
3092 
3093 	gradientMatrix.reset();
3094 	gradientMatrix *= agg::trans_affine_translation(center.x, center.y);
3095 	gradientMatrix.invert();
3096 
3097 	//	_CalcLinearGradientTransform(start, end, gradientMatrix);
3098 
3099 	fRasterizer.reset();
3100 	fRasterizer.add_path(path);
3101 	agg::render_scanlines(fRasterizer, fPackedScanline, gradientRenderer);
3102 }
3103 
3104 
3105 // _FillPathGradientDiamond
3106 template<class VertexSource>
3107 void
3108 Painter::_FillPathGradientDiamond(VertexSource& path,
3109 	const BGradientDiamond& diamond) const
3110 {
3111 	GTRACE("Painter::_FillPathGradientDiamond\n");
3112 
3113 	BPoint center = diamond.Center();
3114 //	float radius = diamond.Radius();
3115 
3116 	typedef agg::span_interpolator_linear<> interpolator_type;
3117 	typedef agg::pod_auto_array<agg::rgba8, 256> color_array_type;
3118 	typedef agg::span_allocator<agg::rgba8> span_allocator_type;
3119 	typedef agg::gradient_diamond gradient_func_type;
3120 	typedef agg::span_gradient<agg::rgba8, interpolator_type,
3121 	gradient_func_type, color_array_type> span_gradient_type;
3122 	typedef agg::renderer_scanline_aa<renderer_base, span_allocator_type,
3123 	span_gradient_type> renderer_gradient_type;
3124 
3125 	gradient_func_type gradientFunc;
3126 	agg::trans_affine gradientMatrix;
3127 	interpolator_type spanInterpolator(gradientMatrix);
3128 	span_allocator_type spanAllocator;
3129 	color_array_type colorArray;
3130 
3131 	_MakeGradient(colorArray, diamond);
3132 
3133 	span_gradient_type spanGradient(spanInterpolator, gradientFunc, colorArray,
3134 		0, 100);
3135 
3136 	renderer_gradient_type gradientRenderer(fBaseRenderer, spanAllocator,
3137 		spanGradient);
3138 
3139 	gradientMatrix.reset();
3140 	gradientMatrix *= agg::trans_affine_translation(center.x, center.y);
3141 	gradientMatrix.invert();
3142 
3143 	//	_CalcLinearGradientTransform(start, end, gradientMatrix);
3144 
3145 	fRasterizer.reset();
3146 	fRasterizer.add_path(path);
3147 	agg::render_scanlines(fRasterizer, fPackedScanline, gradientRenderer);
3148 }
3149 
3150 
3151 // _FillPathGradientConic
3152 template<class VertexSource>
3153 void
3154 Painter::_FillPathGradientConic(VertexSource& path,
3155 	const BGradientConic& conic) const
3156 {
3157 	GTRACE("Painter::_FillPathGradientConic\n");
3158 
3159 	BPoint center = conic.Center();
3160 //	float radius = conic.Radius();
3161 
3162 	typedef agg::span_interpolator_linear<> interpolator_type;
3163 	typedef agg::pod_auto_array<agg::rgba8, 256> color_array_type;
3164 	typedef agg::span_allocator<agg::rgba8> span_allocator_type;
3165 	typedef agg::gradient_conic gradient_func_type;
3166 	typedef agg::span_gradient<agg::rgba8, interpolator_type,
3167 	gradient_func_type, color_array_type> span_gradient_type;
3168 	typedef agg::renderer_scanline_aa<renderer_base, span_allocator_type,
3169 	span_gradient_type> renderer_gradient_type;
3170 
3171 	gradient_func_type gradientFunc;
3172 	agg::trans_affine gradientMatrix;
3173 	interpolator_type spanInterpolator(gradientMatrix);
3174 	span_allocator_type spanAllocator;
3175 	color_array_type colorArray;
3176 
3177 	_MakeGradient(colorArray, conic);
3178 
3179 	span_gradient_type spanGradient(spanInterpolator, gradientFunc, colorArray,
3180 		0, 100);
3181 
3182 	renderer_gradient_type gradientRenderer(fBaseRenderer, spanAllocator,
3183 		spanGradient);
3184 
3185 	gradientMatrix.reset();
3186 	gradientMatrix *= agg::trans_affine_translation(center.x, center.y);
3187 	gradientMatrix.invert();
3188 
3189 	//	_CalcLinearGradientTransform(start, end, gradientMatrix);
3190 
3191 	fRasterizer.reset();
3192 	fRasterizer.add_path(path);
3193 	agg::render_scanlines(fRasterizer, fPackedScanline, gradientRenderer);
3194 }
3195