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