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