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
64using 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
91extern "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
96static uint32 detect_simd();
97
98static 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*/
104static uint32
105detect_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
168Painter::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
215Painter::~Painter()
216{
217}
218
219
220// #pragma mark -
221
222
223// AttachToBuffer
224void
225Painter::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
251void
252Painter::DetachFromBuffer()
253{
254	fBuffer.attach(NULL, 0, 0, 0);
255	fAttached = false;
256	fValidClipping = false;
257}
258
259
260// Bounds
261BRect
262Painter::Bounds() const
263{
264	return BRect(0, 0, fBuffer.width() - 1, fBuffer.height() - 1);
265}
266
267
268// #pragma mark -
269
270
271// SetDrawState
272void
273Painter::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
322void
323Painter::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
338void
339Painter::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
350void
351Painter::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
360void
361Painter::SetDrawingMode(drawing_mode mode)
362{
363	if (fDrawingMode != mode) {
364		fDrawingMode = mode;
365		_UpdateDrawingMode();
366	}
367}
368
369
370// SetBlendingMode
371void
372Painter::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
384void
385Painter::SetPenSize(float size)
386{
387	fPenSize = size;
388}
389
390
391// SetStrokeMode
392void
393Painter::SetStrokeMode(cap_mode lineCap, join_mode joinMode, float miterLimit)
394{
395	fLineCapMode = lineCap;
396	fLineJoinMode = joinMode;
397	fMiterLimit = miterLimit;
398}
399
400
401// SetPattern
402void
403Painter::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
423void
424Painter::SetFont(const ServerFont& font)
425{
426	fTextRenderer.SetFont(font);
427	fTextRenderer.SetAntialiasing(!(font.Flags() & B_DISABLE_ANTIALIASING));
428}
429
430
431// SetFont
432void
433Painter::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
445void
446Painter::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
536bool
537Painter::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
611BRect
612Painter::StrokeTriangle(BPoint pt1, BPoint pt2, BPoint pt3) const
613{
614	return _DrawTriangle(pt1, pt2, pt3, false);
615}
616
617
618// FillTriangle
619BRect
620Painter::FillTriangle(BPoint pt1, BPoint pt2, BPoint pt3) const
621{
622	return _DrawTriangle(pt1, pt2, pt3, true);
623}
624
625
626// FillTriangle
627BRect
628Painter::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
650BRect
651Painter::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
680BRect
681Painter::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
708BRect
709Painter::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
733BRect
734Painter::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
753static void
754iterate_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
821BRect
822Painter::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
839BRect
840Painter::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
854BRect
855Painter::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
904void
905Painter::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
915BRect
916Painter::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
973BRect
974Painter::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
1018void
1019Painter::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
1058void
1059Painter::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
1110void
1111Painter::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
1138BRect
1139Painter::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
1237BRect
1238Painter::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
1262BRect
1263Painter::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
1288void
1289Painter::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
1306BRect
1307Painter::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
1379BRect
1380Painter::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
1403BRect
1404Painter::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
1424BRect
1425Painter::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
1461BRect
1462Painter::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
1501BRect
1502Painter::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
1530BRect
1531Painter::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
1556BRect
1557Painter::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
1573BRect
1574Painter::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
1587float
1588Painter::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
1599BRect
1600Painter::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
1636BRect
1637Painter::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
1652BRect
1653Painter::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
1668BRect
1669Painter::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
1689inline void
1690Painter::_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
1709inline BPoint
1710Painter::_Transform(const BPoint& point, bool centerOffset) const
1711{
1712	BPoint ret = point;
1713	_Transform(&ret, centerOffset);
1714	return ret;
1715}
1716
1717
1718// _Clipped
1719BRect
1720Painter::_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
1730void
1731Painter::_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
1759void
1760Painter::_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
1776inline BRect
1777Painter::_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
1801static inline void
1802copy_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
1815static inline void
1816copy_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
1831static inline void
1832copy_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
1840static inline void
1841copy_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
1856static inline void
1857copy_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
1882template<typename sourcePixel>
1883void
1884Painter::_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
1907void
1908Painter::_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
2092template <class F>
2093void
2094Painter::_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
2112if (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
2154void
2155Painter::_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
2276void
2277Painter::_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
2689void
2690Painter::_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_clip<pixfmt_image> source_type;
2726	source_type source(pixf_img, agg::rgba8(0, 0, 0, 0));
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
2769void
2770Painter::_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
2787void
2788Painter::_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
2824template<class VertexSource>
2825BRect
2826Painter::_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
2840inline agg::line_cap_e
2841agg_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
2856inline agg::line_join_e
2857agg_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
2873template<class VertexSource>
2874BRect
2875Painter::_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
2906template<class VertexSource>
2907BRect
2908Painter::_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
2926template<class VertexSource>
2927BRect
2928Painter::_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
2973void
2974Painter::_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
3053template<class Array>
3054void
3055Painter::_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
3084void 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
3099template<class VertexSource>
3100void
3101Painter::_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
3141template<class VertexSource>
3142void
3143Painter::_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
3188template<class VertexSource>
3189void
3190Painter::_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
3236template<class VertexSource>
3237void
3238Painter::_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
3282template<class VertexSource>
3283void
3284Painter::_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