1// Painter.cpp
2
3#include <stdio.h>
4#include <string.h>
5
6#include <Bitmap.h>
7#include <GraphicsDefs.h>
8#include <Region.h>
9
10#include <agg_bezier_arc.h>
11#include <agg_bounding_rect.h>
12#include <agg_conv_curve.h>
13#include <agg_conv_stroke.h>
14#include <agg_ellipse.h>
15#include <agg_path_storage.h>
16#include <agg_rounded_rect.h>
17#include <agg_span_image_filter_rgba32.h>
18#include <agg_span_interpolator_linear.h>
19
20#include "LayerData.h"
21
22#include "AGGTextRenderer.h"
23#include "DrawingMode.h"
24#include "DrawingModeFactory.h"
25#include "FontManager.h"
26#include "PatternHandler.h"
27#include "RenderingBuffer.h"
28#include "ShapeConverter.h"
29#include "ServerBitmap.h"
30#include "ServerFont.h"
31
32#include "Painter.h"
33
34int
35roundf(float v)
36{
37	if (v >= 0.0)
38		return (int)floorf(v + 0.5);
39	else
40		return (int)floorf(v - 0.5);
41}
42
43// constructor
44Painter::Painter()
45	: fBuffer(NULL),
46	  fPixelFormat(NULL),
47	  fBaseRenderer(NULL),
48	  fOutlineRenderer(NULL),
49	  fOutlineRasterizer(NULL),
50	  fScanline(NULL),
51	  fRasterizer(NULL),
52	  fRenderer(NULL),
53	  fFontRendererSolid(NULL),
54	  fFontRendererBin(NULL),
55	  fLineProfile(),
56	  fSubpixelPrecise(false),
57	  fScale(1.0),
58	  fPenSize(1.0),
59	  fOrigin(0.0, 0.0),
60	  fClippingRegion(NULL),
61	  fDrawingMode(B_OP_COPY),
62	  fAlphaSrcMode(B_PIXEL_ALPHA),
63//	  fAlphaSrcMode(B_CONSTANT_ALPHA),
64	  fAlphaFncMode(B_ALPHA_OVERLAY),
65	  fPenLocation(0.0, 0.0),
66	  fPatternHandler(new PatternHandler()),
67	  fTextRenderer(new AGGTextRenderer()),
68	  fLastFamilyAndStyle(0)
69{
70	if (fontserver)
71		fFont = *fontserver->GetSystemPlain();
72
73	_UpdateFont();
74	_UpdateLineWidth();
75}
76
77// destructor
78Painter::~Painter()
79{
80	_MakeEmpty();
81
82	delete fClippingRegion;
83	delete fPatternHandler;
84	delete fTextRenderer;
85}
86
87// #pragma mark -
88
89// AttachToBuffer
90void
91Painter::AttachToBuffer(RenderingBuffer* buffer)
92{
93	if (buffer && buffer->InitCheck() >= B_OK) {
94		// clean up previous stuff
95		_MakeEmpty();
96
97		fBuffer = new agg::rendering_buffer();
98		fBuffer->attach((uint8*)buffer->Bits(),
99						buffer->Width(),
100						buffer->Height(),
101						buffer->BytesPerRow());
102
103		fPixelFormat = new pixfmt(*fBuffer, fPatternHandler);
104		fPixelFormat->set_drawing_mode(DrawingModeFactory::DrawingModeFor(fDrawingMode,
105																		  fAlphaSrcMode,
106																		  fAlphaFncMode,
107																		  false));
108
109		fBaseRenderer = new renderer_base(*fPixelFormat);
110
111		// These are the AGG renderes and rasterizes which
112		// will be used for stroking paths
113rgb_color color = fPatternHandler->HighColor().GetColor32();
114#if ALIASED_DRAWING
115		fOutlineRenderer = new outline_renderer_type(*fBaseRenderer);
116		fOutlineRasterizer = new outline_rasterizer_type(*fOutlineRenderer);
117#else
118		fOutlineRenderer = new outline_renderer_type(*fBaseRenderer, fLineProfile);
119		fOutlineRasterizer = new outline_rasterizer_type(*fOutlineRenderer);
120
121		// attach our line profile to the renderer, it keeps a pointer
122		fOutlineRenderer->profile(fLineProfile);
123#endif
124		// the renderer used for filling paths
125		fRenderer = new renderer_type(*fBaseRenderer);
126		fRasterizer = new rasterizer_type();
127		fScanline = new scanline_type();
128
129#if ALIASED_DRAWING
130		fRasterizer->gamma(agg::gamma_threshold(0.5));
131#endif
132
133		// These are renderers needed for drawing text
134		fFontRendererSolid = new font_renderer_solid_type(*fBaseRenderer);
135		fFontRendererBin = new font_renderer_bin_type(*fBaseRenderer);
136
137		_SetRendererColor(fPatternHandler->HighColor().GetColor32());
138		_RebuildClipping();
139	}
140}
141
142// DetachFromBuffer
143void
144Painter::DetachFromBuffer()
145{
146	_MakeEmpty();
147}
148
149// SetDrawData
150void
151Painter::SetDrawData(const DrawData* data)
152{
153	// for now...
154	SetHighColor(data->highcolor.GetColor32());
155	SetLowColor(data->lowcolor.GetColor32());
156	SetScale(data->scale);
157	SetPenSize(data->pensize);
158//	fOrigin = data->coordOrigin;
159	SetDrawingMode(data->draw_mode);
160	SetBlendingMode(data->alphaSrcMode, data->alphaFncMode);
161	SetPenLocation(data->penlocation);
162	SetFont(data->font);
163//	if (data->clipReg) {
164//		ConstrainClipping(*data->clipReg);
165//	}
166	fPatternHandler->SetPattern(data->patt);
167}
168
169// #pragma mark -
170
171// ConstrainClipping
172void
173Painter::ConstrainClipping(const BRegion& region)
174{
175	// The idea is that if the clipping region was
176	// never constrained, there is *no* clipping.
177	// This is of course different from having
178	// an *empty* clipping region.
179	if (!fClippingRegion)
180		fClippingRegion = new BRegion(region);
181	else
182		*fClippingRegion = region;
183	_RebuildClipping();
184}
185
186// SetHighColor
187void
188Painter::SetHighColor(const rgb_color& color)
189{
190	fPatternHandler->SetHighColor(color);
191}
192
193// SetLowColor
194void
195Painter::SetLowColor(const rgb_color& color)
196{
197	fPatternHandler->SetLowColor(color);;
198}
199
200// SetScale
201void
202Painter::SetScale(float scale)
203{
204	if (fScale != scale) {
205		fScale = scale;
206		_RebuildClipping();
207		_UpdateLineWidth();
208	}
209}
210
211// SetPenSize
212void
213Painter::SetPenSize(float size)
214{
215	if (fPenSize != size) {
216		fPenSize = size;
217		_UpdateLineWidth();
218	}
219}
220
221// SetOrigin
222void
223Painter::SetOrigin(const BPoint& origin)
224{
225	// NOTE: The BeBook says that the coordinate system
226	// of a view cannot be changed during an update, because
227	// it would mess up the clipping, and this is indeed
228	// what would happen in this implementation as well.
229	// I don't know yet what actually happens if you still
230	// try to call SetOrigin() from within BView::Draw()
231	fOrigin = origin;
232	_RebuildClipping();
233}
234
235// SetDrawingMode
236void
237Painter::SetDrawingMode(drawing_mode mode)
238{
239	if (fDrawingMode != mode) {
240		fDrawingMode = mode;
241		if (fPixelFormat) {
242			fPixelFormat->set_drawing_mode(DrawingModeFactory::DrawingModeFor(fDrawingMode,
243																			  fAlphaSrcMode,
244																			  fAlphaFncMode));
245		}
246	}
247}
248
249// SetBlendingMode
250void
251Painter::SetBlendingMode(source_alpha alphaSrcMode, alpha_function alphaFncMode)
252{
253	if (fAlphaSrcMode != alphaSrcMode || fAlphaFncMode != alphaFncMode) {
254		fAlphaSrcMode = alphaSrcMode;
255		fAlphaFncMode = alphaFncMode;
256		if (fDrawingMode == B_OP_ALPHA && fPixelFormat) {
257			fPixelFormat->set_drawing_mode(DrawingModeFactory::DrawingModeFor(fDrawingMode,
258																			  fAlphaSrcMode,
259																			  fAlphaFncMode));
260		}
261	}
262}
263
264// SetPenLocation
265void
266Painter::SetPenLocation(const BPoint& location)
267{
268	fPenLocation = location;
269}
270
271// SetFont
272void
273Painter::SetFont(const BFont& font)
274{
275	//fFont.SetFamilyAndStyle(font.GetFamily(), font.GetStyle());
276	fFont.SetSpacing(font.Spacing());
277	fFont.SetShear(font.Shear());
278	fFont.SetRotation(font.Rotation());
279	fFont.SetSize(font.Size());
280
281	_UpdateFont();
282}
283
284// SetFont
285void
286Painter::SetFont(const ServerFont& font)
287{
288	fFont = font;
289	_UpdateFont();
290}
291
292// #pragma mark -
293
294// StrokeLine
295BRect
296Painter::StrokeLine(BPoint a, BPoint b, const pattern& p)
297{
298	_Transform(&a);
299	_Transform(&b);
300
301	BRect touched(a, b);
302
303	// first, try an optimized version
304	float penSize = _Transform(fPenSize);
305	if (penSize == 1.0 &&
306		(fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER)) {
307		pattern pat = *fPatternHandler->GetR5Pattern();
308		if (pat == B_SOLID_HIGH &&
309			StraightLine(a, b, fPatternHandler->HighColor().GetColor32())) {
310			SetPenLocation(b);
311			return _Clipped(touched);
312		} else if (pat == B_SOLID_LOW &&
313			StraightLine(a, b, fPatternHandler->LowColor().GetColor32())) {
314			SetPenLocation(b);
315			return _Clipped(touched);
316		}
317	}
318
319	agg::path_storage path;
320	path.move_to(a.x, a.y);
321	path.line_to(b.x, b.y);
322
323	touched = _StrokePath(path, p);
324
325	SetPenLocation(b);
326
327	return _Clipped(touched);
328}
329
330// StrokeLine
331BRect
332Painter::StrokeLine(BPoint b, const pattern& p)
333{
334	// TODO: move this function elsewhere
335	return StrokeLine(fPenLocation, b);
336}
337
338// StraightLine
339bool
340Painter::StraightLine(BPoint a, BPoint b, const rgb_color& c) const
341{
342	if (fBuffer) {
343		if (a.x == b.x) {
344			// vertical
345			uint8* dst = fBuffer->row(0);
346			uint32 bpr = fBuffer->stride();
347			int32 x = (int32)a.x;
348			dst += x * 4;
349			int32 y1 = (int32)min_c(a.y, b.y);
350			int32 y2 = (int32)max_c(a.y, b.y);
351			// draw a line, iterate over clipping boxes
352			fBaseRenderer->first_clip_box();
353			do {
354				if (fBaseRenderer->xmin() <= x &&
355					fBaseRenderer->xmax() >= x) {
356					int32 i = max_c(fBaseRenderer->ymin(), y1);
357					int32 end = min_c(fBaseRenderer->ymax(), y2);
358					uint8* handle = dst + i * bpr;
359					for (; i <= end; i++) {
360						handle[0] = c.blue;
361						handle[1] = c.green;
362						handle[2] = c.red;
363						handle += bpr;
364					}
365				}
366			} while (fBaseRenderer->next_clip_box());
367
368			return true;
369
370		} else if (a.y == b.y) {
371			// horizontal
372			uint8* dst = fBuffer->row(0);
373			uint32 bpr = fBuffer->stride();
374			int32 y = (int32)a.y;
375			dst += y * bpr;
376			int32 x1 = (int32)min_c(a.x, b.x);
377			int32 x2 = (int32)max_c(a.x, b.x);
378			// draw a line, iterate over clipping boxes
379			fBaseRenderer->first_clip_box();
380			do {
381				if (fBaseRenderer->ymin() <= y &&
382					fBaseRenderer->ymax() >= y) {
383					int32 i = max_c(fBaseRenderer->xmin(), x1);
384					int32 end = min_c(fBaseRenderer->xmax(), x2);
385					uint8* handle = dst + i * 4;
386					for (; i <= end; i++) {
387						handle[0] = c.blue;
388						handle[1] = c.green;
389						handle[2] = c.red;
390						handle += 4;
391					}
392				}
393			} while (fBaseRenderer->next_clip_box());
394
395			return true;
396		}
397	}
398	return false;
399}
400
401// #pragma mark -
402
403// StrokeTriangle
404void
405Painter::StrokeTriangle(BPoint pt1, BPoint pt2, BPoint pt3, const pattern& p) const
406{
407	_DrawTriangle(pt1, pt2, pt3, p, false);
408}
409
410// FillTriangle
411void
412Painter::FillTriangle(BPoint pt1, BPoint pt2, BPoint pt3, const pattern& p) const
413{
414	_DrawTriangle(pt1, pt2, pt3, p, true);
415}
416
417// StrokePolygon
418void
419Painter::StrokePolygon(const BPoint* ptArray, int32 numPts,
420					   bool closed, const pattern& p) const
421{
422	_DrawPolygon(ptArray, numPts, closed, p, false);
423}
424
425// FillPolygon
426void
427Painter::FillPolygon(const BPoint* ptArray, int32 numPts,
428					   bool closed, const pattern& p) const
429{
430	_DrawPolygon(ptArray, numPts, closed, p, true);
431}
432
433// StrokeBezier
434void
435Painter::StrokeBezier(const BPoint* controlPoints, const pattern& p) const
436{
437	agg::path_storage curve;
438
439	BPoint p1(controlPoints[0]);
440	BPoint p2(controlPoints[1]);
441	BPoint p3(controlPoints[2]);
442	BPoint p4(controlPoints[3]);
443	_Transform(&p1);
444	_Transform(&p2);
445	_Transform(&p3);
446	_Transform(&p4);
447
448	curve.move_to(p1.x, p1.y);
449	curve.curve4(p1.x, p1.y,
450				 p2.x, p2.y,
451				 p3.x, p3.y);
452
453
454	agg::conv_curve<agg::path_storage> path(curve);
455
456	_StrokePath(path, p);
457}
458
459// FillBezier
460void
461Painter::FillBezier(const BPoint* controlPoints, const pattern& p) const
462{
463	agg::path_storage curve;
464
465	BPoint p1(controlPoints[0]);
466	BPoint p2(controlPoints[1]);
467	BPoint p3(controlPoints[2]);
468	BPoint p4(controlPoints[3]);
469	_Transform(&p1);
470	_Transform(&p2);
471	_Transform(&p3);
472	_Transform(&p4);
473
474	curve.move_to(p1.x, p1.y);
475	curve.curve4(p1.x, p1.y,
476				 p2.x, p2.y,
477				 p3.x, p3.y);
478	curve.close_polygon();
479
480	agg::conv_curve<agg::path_storage> path(curve);
481
482	_FillPath(path, p);
483}
484
485// StrokeShape
486void
487Painter::StrokeShape(/*const */BShape* shape, const pattern& p) const
488{
489	_DrawShape(shape, p, false);
490}
491
492// FillShape
493void
494Painter::FillShape(/*const */BShape* shape, const pattern& p) const
495{
496	_DrawShape(shape, p, true);
497}
498
499// StrokeRect
500BRect
501Painter::StrokeRect(const BRect& r, const pattern& p) const
502{
503	BPoint a(r.left, r.top);
504	BPoint b(r.right, r.bottom);
505	_Transform(&a);
506	_Transform(&b);
507
508	// first, try an optimized version
509	float penSize = _Transform(fPenSize);
510	if (penSize == 1.0 &&
511		(fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER)) {
512// TODO: fix me
513//		pattern p = *fPatternHandler->GetR5Pattern();
514		if (p == B_SOLID_HIGH) {
515			BRect rect(a, b);
516			StrokeRect(rect,
517					   fPatternHandler->HighColor().GetColor32());
518			return _Clipped(rect);
519		} else if (p == B_SOLID_LOW) {
520			BRect rect(a, b);
521			StrokeRect(rect,
522					   fPatternHandler->LowColor().GetColor32());
523			return _Clipped(rect);
524		}
525	}
526
527	agg::path_storage path;
528	path.move_to(a.x, a.y);
529	path.line_to(b.x, a.y);
530	path.line_to(b.x, b.y);
531	path.line_to(a.x, b.y);
532	path.close_polygon();
533
534	return _StrokePath(path, p);
535}
536
537// StrokeRect
538void
539Painter::StrokeRect(const BRect& r, const rgb_color& c) const
540{
541	StraightLine(BPoint(r.left, r.top),
542				 BPoint(r.right - 1, r.top), c);
543	StraightLine(BPoint(r.right, r.top),
544				 BPoint(r.right, r.bottom - 1), c);
545	StraightLine(BPoint(r.right, r.bottom),
546				 BPoint(r.left + 1, r.bottom), c);
547	StraightLine(BPoint(r.left, r.bottom),
548				 BPoint(r.left, r.top + 1), c);
549}
550
551// FillRect
552BRect
553Painter::FillRect(const BRect& r, const pattern& p) const
554{
555	BPoint a(r.left, r.top);
556	BPoint b(r.right, r.bottom);
557	_Transform(&a, false);
558	_Transform(&b, false);
559
560	// first, try an optimized version
561	if (fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER) {
562		pattern pat = *fPatternHandler->GetR5Pattern();
563		if (pat == B_SOLID_HIGH) {
564			BRect rect(a, b);
565			FillRect(rect, fPatternHandler->HighColor().GetColor32());
566			return _Clipped(rect);
567		} else if (pat == B_SOLID_LOW) {
568			BRect rect(a, b);
569			FillRect(rect, fPatternHandler->LowColor().GetColor32());
570			return _Clipped(rect);
571		}
572	}
573
574	// account for stricter interpretation of coordinates in AGG
575	// the rectangle ranges from the top-left (.0, .0)
576	// to the bottom-right (.9999, .9999) corner of pixels
577	b.x += 1.0;
578	b.y += 1.0;
579
580	agg::path_storage path;
581	path.move_to(a.x, a.y);
582	path.line_to(b.x, a.y);
583	path.line_to(b.x, b.y);
584	path.line_to(a.x, b.y);
585	path.close_polygon();
586
587	return _FillPath(path, p);
588}
589
590// FillRect
591void
592Painter::FillRect(const BRect& r, const rgb_color& c) const
593{
594	if (fBuffer) {
595		uint8* dst = fBuffer->row(0);
596		uint32 bpr = fBuffer->stride();
597		int32 left = (int32)r.left;
598		int32 top = (int32)r.top;
599		int32 right = (int32)r.right;
600		int32 bottom = (int32)r.bottom;
601		// fill rects, iterate over clipping boxes
602		fBaseRenderer->first_clip_box();
603		do {
604			int32 x1 = max_c(fBaseRenderer->xmin(), left);
605			int32 x2 = min_c(fBaseRenderer->xmax(), right);
606			if (x1 <= x2) {
607				int32 y1 = max_c(fBaseRenderer->ymin(), top);
608				int32 y2 = min_c(fBaseRenderer->ymax(), bottom);
609				uint8* offset = dst + x1 * 4;
610				for (; y1 <= y2; y1++) {
611					uint8* handle = offset + y1 * bpr;
612					for (int32 x = x1; x <= x2; x++) {
613						handle[0] = c.blue;
614						handle[1] = c.green;
615						handle[2] = c.red;
616						handle += 4;
617					}
618				}
619			}
620		} while (fBaseRenderer->next_clip_box());
621	}
622}
623
624// StrokeRoundRect
625void
626Painter::StrokeRoundRect(const BRect& r, float xRadius, float yRadius,
627						 const pattern& p) const
628{
629	BPoint lt(r.left, r.top);
630	BPoint rb(r.right, r.bottom);
631	_Transform(&lt);
632	_Transform(&rb);
633
634	_Transform(&xRadius);
635	_Transform(&yRadius);
636
637	agg::rounded_rect rect;
638	rect.rect(lt.x, lt.y, rb.x, rb.y);
639	rect.radius(xRadius, yRadius);
640
641	_StrokePath(rect, p);
642}
643
644// FillRoundRect
645void
646Painter::FillRoundRect(const BRect& r, float xRadius, float yRadius,
647					   const pattern& p) const
648{
649	BPoint lt(r.left, r.top);
650	BPoint rb(r.right, r.bottom);
651	_Transform(&lt, false);
652	_Transform(&rb, false);
653
654	// account for stricter interpretation of coordinates in AGG
655	// the rectangle ranges from the top-left (.0, .0)
656	// to the bottom-right (.9999, .9999) corner of pixels
657	rb.x += 1.0;
658	rb.y += 1.0;
659
660	_Transform(&xRadius);
661	_Transform(&yRadius);
662
663	agg::rounded_rect rect;
664	rect.rect(lt.x, lt.y, rb.x, rb.y);
665	rect.radius(xRadius, yRadius);
666
667	_FillPath(rect, p);
668}
669
670// StrokeEllipse
671void
672Painter::StrokeEllipse(BPoint center, float xRadius, float yRadius,
673					   const pattern& p) const
674{
675	_DrawEllipse(center, xRadius, yRadius, p, false);
676}
677
678// FillEllipse
679void
680Painter::FillEllipse(BPoint center, float xRadius, float yRadius,
681					 const pattern& p) const
682{
683	_DrawEllipse(center, xRadius, yRadius, p, true);
684}
685
686// StrokeArc
687void
688Painter::StrokeArc(BPoint center, float xRadius, float yRadius,
689				   float angle, float span, const pattern& p) const
690{
691	_Transform(&center);
692	_Transform(&xRadius);
693	_Transform(&yRadius);
694
695	double angleRad = (angle * PI) / 180.0;
696	double spanRad = (span * PI) / 180.0;
697	agg::bezier_arc arc(center.x, center.y, xRadius, yRadius,
698						-angleRad, -spanRad);
699
700	agg::conv_curve<agg::bezier_arc> path(arc);
701
702	_StrokePath(path, p);
703}
704
705// FillArc
706void
707Painter::FillArc(BPoint center, float xRadius, float yRadius,
708				 float angle, float span, const pattern& p) const
709{
710	_Transform(&center);
711	_Transform(&xRadius);
712	_Transform(&yRadius);
713
714	double angleRad = (angle * PI) / 180.0;
715	double spanRad = (span * PI) / 180.0;
716	agg::bezier_arc arc(center.x, center.y, xRadius, yRadius,
717						-angleRad, -spanRad);
718
719	agg::conv_curve<agg::bezier_arc> segmentedArc(arc);
720
721	agg::path_storage path;
722
723	// build a new path by starting at the center point,
724	// then traversing the arc, then going back to the center
725	path.move_to(center.x, center.y);
726
727	segmentedArc.rewind(0);
728	double x;
729	double y;
730	unsigned cmd = segmentedArc.vertex(&x, &y);
731	while (!agg::is_stop(cmd)) {
732		path.line_to(x, y);
733		cmd = segmentedArc.vertex(&x, &y);
734	}
735
736	path.close_polygon();
737
738	_FillPath(path, p);
739}
740
741// #pragma mark -
742
743// DrawChar
744BRect
745Painter::DrawChar(char aChar)
746{
747	// TODO: to be moved elsewhere
748	return DrawChar(aChar, fPenLocation);
749}
750
751// DrawChar
752BRect
753Painter::DrawChar(char aChar, BPoint baseLine)
754{
755	// TODO: to be moved elsewhere
756	char wrapper[2];
757	wrapper[0] = aChar;
758	wrapper[1] = 0;
759	return DrawString(wrapper, 1, baseLine);
760}
761
762// DrawString
763BRect
764Painter::DrawString(const char* utf8String, uint32 length,
765					const escapement_delta* delta)
766{
767	// TODO: to be moved elsewhere
768	return DrawString(utf8String, length, fPenLocation, delta);
769}
770
771// DrawString
772BRect
773Painter::DrawString(const char* utf8String, uint32 length,
774					BPoint baseLine, const escapement_delta* delta)
775{
776	BRect bounds(0.0, 0.0, -1.0, -1.0);
777	fPatternHandler->SetPattern(B_SOLID_HIGH);
778
779	if (fBuffer) {
780
781		Transformable transform;
782		transform.ShearBy(B_ORIGIN, (90.0 - fFont.Shear()) * PI / 180.0, 0.0);
783		transform.RotateBy(B_ORIGIN, -fFont.Rotation() * PI / 180.0);
784		transform.TranslateBy(baseLine);
785		transform.ScaleBy(B_ORIGIN, fScale, fScale);
786		transform.TranslateBy(fOrigin);
787
788		BRect clippingFrame;
789		if (fClippingRegion)
790			clippingFrame = _Transform(fClippingRegion->Frame());
791
792		bounds = fTextRenderer->RenderString(utf8String,
793											 length,
794											 fFontRendererSolid,
795											 fFontRendererBin,
796											 transform,
797											 clippingFrame,
798											 false,
799											 &fPenLocation);
800		// pen location is not transformed in quite the same way,
801		// or transformations would add up
802		transform.Reset();
803		transform.RotateBy(B_ORIGIN, -fFont.Rotation());
804		transform.TranslateBy(baseLine);
805		transform.Transform(&fPenLocation);
806	}
807	return _Clipped(bounds);
808}
809
810// DrawString
811BRect
812Painter::DrawString(const char* utf8String, const escapement_delta* delta)
813{
814	// TODO: to be moved elsewhere
815	return DrawString(utf8String, strlen(utf8String), fPenLocation, delta);
816}
817
818// DrawString
819BRect
820Painter::DrawString(const char* utf8String, BPoint baseLine,
821					const escapement_delta* delta)
822{
823	// TODO: to be moved elsewhere
824	return DrawString(utf8String, strlen(utf8String), baseLine, delta);
825}
826
827// #pragma mark -
828
829// DrawBitmap
830void
831Painter::DrawBitmap(const BBitmap* bitmap,
832					BRect bitmapRect, BRect viewRect) const
833{
834	if (bitmap && bitmap->IsValid()) {
835		// the native bitmap coordinate system
836		// (can have left top corner offset)
837		BRect actualBitmapRect(bitmap->Bounds());
838
839		agg::rendering_buffer srcBuffer;
840		srcBuffer.attach((uint8*)bitmap->Bits(),
841						 (uint32)actualBitmapRect.IntegerWidth() + 1,
842						 (uint32)actualBitmapRect.IntegerHeight() + 1,
843						 bitmap->BytesPerRow());
844
845		_DrawBitmap(srcBuffer, bitmap->ColorSpace(), actualBitmapRect, bitmapRect, viewRect);
846	}
847}
848
849// DrawBitmap
850void
851Painter::DrawBitmap(const ServerBitmap* bitmap,
852					BRect bitmapRect, BRect viewRect) const
853{
854	if (bitmap && bitmap->InitCheck()) {
855		// the native bitmap coordinate system
856		BRect actualBitmapRect(bitmap->Bounds());
857
858		agg::rendering_buffer srcBuffer;
859		srcBuffer.attach(bitmap->Bits(),
860						 bitmap->Width(),
861						 bitmap->Height(),
862						 bitmap->BytesPerRow());
863
864		_DrawBitmap(srcBuffer, bitmap->ColorSpace(), actualBitmapRect, bitmapRect, viewRect);
865	}
866}
867
868// #pragma mark -
869
870// FillRegion
871void
872Painter::FillRegion(const BRegion* region, const pattern& p = B_SOLID_HIGH) const
873{
874	BRegion copy(*region);
875	int32 count = copy.CountRects();
876	for (int32 i = 0; i < count; i++) {
877		FillRect(copy.RectAt(i), p);
878	}
879}
880
881// InvertRect
882void
883Painter::InvertRect(const BRect& r) const
884{
885	BRegion region(r);
886	if (fClippingRegion) {
887		region.IntersectWith(fClippingRegion);
888	}
889	// implementation only for B_RGB32 at the moment
890	int32 count = region.CountRects();
891	for (int32 i = 0; i < count; i++) {
892		BRect r = region.RectAt(i);
893		_Transform(&r);
894		_InvertRect32(r);
895	}
896}
897
898// BoundingBox
899BRect
900Painter::BoundingBox(const char* utf8String, uint32 length,
901					 const BPoint& baseLine) const
902{
903	Transformable transform;
904	transform.TranslateBy(baseLine);
905
906	BRect dummy;
907	return fTextRenderer->RenderString(utf8String,
908									length,
909									fFontRendererSolid,
910									fFontRendererBin,
911									transform, dummy, true);
912}
913
914// #pragma mark -
915
916// _MakeEmpty
917void
918Painter::_MakeEmpty()
919{
920	delete fBuffer;
921	fBuffer = NULL;
922
923	delete fPixelFormat;
924	fPixelFormat = NULL;
925
926	delete fBaseRenderer;
927	fBaseRenderer = NULL;
928
929	delete fOutlineRenderer;
930	fOutlineRenderer = NULL;
931
932	delete fOutlineRasterizer;
933	fOutlineRasterizer = NULL;
934
935	delete fScanline;
936	fScanline = NULL;
937
938	delete fRasterizer;
939	fRasterizer = NULL;
940
941	delete fRenderer;
942	fRenderer = NULL;
943
944	delete fFontRendererSolid;
945	fFontRendererSolid = NULL;
946
947	delete fFontRendererBin;
948	fFontRendererBin = NULL;
949}
950
951// _Transform
952void
953Painter::_Transform(BPoint* point, bool centerOffset) const
954{
955	*point += fOrigin;
956	// rounding
957	if (!fSubpixelPrecise) {
958		// TODO: validate usage of floor() for values < 0
959		point->x = floorf(point->x);
960		point->y = floorf(point->y);
961	}
962	// apply the scale
963	point->x *= fScale;
964	point->y *= fScale;
965	// this code is supposed to move coordinates to the center of pixels,
966	// as AGG considers (0,0) to be the "upper left corner" of a pixel,
967	// but BViews are less strict on those details
968	if (centerOffset) {
969		point->x += 0.5;
970		point->y += 0.5;
971	}
972}
973
974// _Transform
975BPoint
976Painter::_Transform(const BPoint& point, bool centerOffset) const
977{
978	BPoint ret = point;
979	_Transform(&ret, centerOffset);
980	return ret;
981}
982
983// _Transform
984void
985Painter::_Transform(float* width) const
986{
987	*width *= fScale;
988	if (*width < 1)
989		*width = 1;
990}
991
992// _Transform
993float
994Painter::_Transform(const float& width) const
995{
996	float w = width * fScale;
997	if (w < 1)
998		w = 1;
999	return w;
1000}
1001
1002// _Transform
1003void
1004Painter::_Transform(BRect* rect) const
1005{
1006	// TODO integrate this function more
1007	rect->right++;
1008	rect->bottom++;
1009	rect->left += fOrigin.x;
1010	rect->top += fOrigin.y;
1011	rect->right += fOrigin.x;
1012	rect->bottom += fOrigin.y;
1013	rect->left *= fScale;
1014	rect->top *= fScale;
1015	rect->right *= fScale;
1016	rect->bottom *= fScale;
1017	rect->right--;
1018	rect->bottom--;
1019}
1020
1021// _Transform
1022BRect
1023Painter::_Transform(const BRect& rect) const
1024{
1025	BRect ret = rect;
1026	_Transform(&ret);
1027	return ret;
1028}
1029
1030// _Clipped
1031BRect
1032Painter::_Clipped(const BRect& rect) const
1033{
1034	if (rect.IsValid() && fClippingRegion)
1035		return rect & _Transform(fClippingRegion->Frame());
1036	return rect;
1037}
1038
1039// #pragma mark -
1040
1041// _RebuildClipping
1042void
1043Painter::_RebuildClipping()
1044{
1045	if (fBaseRenderer) {
1046		fBaseRenderer->reset_clipping(!fClippingRegion);
1047		if (fClippingRegion) {
1048			int32 count = fClippingRegion->CountRects();
1049			for (int32 i = 0; i < count; i++) {
1050				BRect r = fClippingRegion->RectAt(i);
1051				// NOTE: The rounding here appears to give somewhat
1052				// different results compared to Be's implementation,
1053				// though I was unable to figure out the difference
1054				BPoint lt(r.LeftTop());
1055				BPoint rb(r.RightBottom());
1056				// offset to bottom right corner of pixel before transformation
1057				rb += BPoint(1.0, 1.0);
1058				// apply transformation
1059				lt += fOrigin;
1060				lt.x *= fScale;
1061				lt.y *= fScale;
1062				rb += fOrigin;
1063				rb.x *= fScale;
1064				rb.y *= fScale;
1065				// undo offset to bottom right corner after transformation
1066				rb -= BPoint(1.0, 1.0);
1067//				fBaseRenderer->add_clip_box(floorf(lt.x),
1068//											floorf(lt.y),
1069//											ceilf(rb.x),
1070//											ceilf(rb.y));
1071				fBaseRenderer->add_clip_box(roundf(lt.x),
1072											roundf(lt.y),
1073											roundf(rb.x),
1074											roundf(rb.y));
1075			}
1076		}
1077	}
1078}
1079
1080// _UpdateFont
1081void
1082Painter::_UpdateFont()
1083{
1084	if (fLastFamilyAndStyle != fFont.GetFamilyAndStyle()) {
1085		fLastFamilyAndStyle = fFont.GetFamilyAndStyle();
1086
1087		bool success = false;
1088		success = fTextRenderer->SetFont(fFont);
1089		if (!success)
1090			fprintf(stderr, "unable to set font\n");
1091	}
1092
1093	fTextRenderer->SetPointSize(fFont.Size());
1094}
1095
1096// _UpdateLineWidth
1097void
1098Painter::_UpdateLineWidth()
1099{
1100	float width = fPenSize;
1101	_Transform(&width);
1102
1103	fLineProfile.width(width);
1104}
1105
1106// #pragma mark -
1107
1108// _DrawTriangle
1109inline void
1110Painter::_DrawTriangle(BPoint pt1, BPoint pt2, BPoint pt3,
1111					   const pattern& p, bool fill) const
1112{
1113	_Transform(&pt1);
1114	_Transform(&pt2);
1115	_Transform(&pt3);
1116
1117	agg::path_storage path;
1118
1119	path.move_to(pt1.x, pt1.y);
1120	path.line_to(pt2.x, pt2.y);
1121	path.line_to(pt3.x, pt3.y);
1122
1123	path.close_polygon();
1124
1125	if (fill)
1126		_FillPath(path, p);
1127	else
1128		_StrokePath(path, p);
1129}
1130
1131// _DrawEllipse
1132inline void
1133Painter::_DrawEllipse(BPoint center, float xRadius, float yRadius,
1134					  const pattern& p, bool fill) const
1135{
1136	// TODO: I think the conversion and the offset of
1137	// pixel centers might not be correct here, and it
1138	// might even be necessary to treat Fill and Stroke
1139	// differently, as with Fill-/StrokeRect().
1140	_Transform(&center);
1141	_Transform(&xRadius);
1142	_Transform(&yRadius);
1143
1144	float width = fPenSize;
1145	_Transform(&width);
1146
1147	int32 divisions = (int32)max_c(12, ((xRadius + yRadius) * PI) / 2 * (int32)width);
1148
1149	agg::ellipse path(center.x, center.y, xRadius, yRadius, divisions);
1150
1151	if (fill)
1152		_FillPath(path, p);
1153	else
1154		_StrokePath(path, p);
1155}
1156
1157// _DrawShape
1158inline void
1159Painter::_DrawShape(/*const */BShape* shape, const pattern& p, bool fill) const
1160{
1161	// TODO: untested
1162	agg::path_storage path;
1163	ShapeConverter converter(&path);
1164
1165	// account for our view coordinate system
1166	converter.ScaleBy(B_ORIGIN, fScale, fScale);
1167	converter.TranslateBy(fOrigin);
1168	// offset locations to center of pixels
1169	converter.TranslateBy(BPoint(0.5, 0.5));
1170
1171	converter.Iterate(shape);
1172
1173	if (fill)
1174		_FillPath(path, p);
1175	else
1176		_StrokePath(path, p);
1177}
1178
1179// _DrawPolygon
1180inline void
1181Painter::_DrawPolygon(const BPoint* ptArray, int32 numPts,
1182					  bool closed, const pattern& p, bool fill) const
1183{
1184	if (numPts > 0) {
1185
1186		agg::path_storage path;
1187		BPoint point = _Transform(*ptArray);
1188		path.move_to(point.x, point.y);
1189
1190		for (int32 i = 1; i < numPts; i++) {
1191			ptArray++;
1192			point = _Transform(*ptArray);
1193			path.line_to(point.x, point.y);
1194		}
1195
1196		if (closed)
1197			path.close_polygon();
1198
1199		if (fill)
1200			_FillPath(path, p);
1201		else
1202			_StrokePath(path, p);
1203	}
1204}
1205
1206// _DrawBitmap
1207void
1208Painter::_DrawBitmap(const agg::rendering_buffer& srcBuffer, color_space format,
1209					 BRect actualBitmapRect, BRect bitmapRect, BRect viewRect) const
1210{
1211	switch (format) {
1212		case B_RGB32:
1213		case B_RGBA32:
1214			_DrawBitmap32(srcBuffer, actualBitmapRect, bitmapRect, viewRect);
1215			break;
1216		default:
1217fprintf(stderr, "Painter::_DrawBitmap() - non-native colorspace: %d\n", format);
1218#ifdef __HAIKU__
1219			// TODO: this is only a temporary implementation,
1220			// to really handle other colorspaces, one would
1221			// rather do the conversion with much less overhead,
1222			// for example in the nn filter (hm), or in the
1223			// scanline generator
1224			BBitmap temp(actualBitmapRect, 0, B_RGB32);
1225			status_t err = temp.ImportBits(srcBuffer.buf(),
1226										   srcBuffer.height() * srcBuffer.stride(),
1227										   srcBuffer.stride(),
1228										   0, format);
1229			if (err >= B_OK) {
1230				agg::rendering_buffer convertedBuffer;
1231				convertedBuffer.attach((uint8*)temp.Bits(),
1232									   (uint32)actualBitmapRect.IntegerWidth() + 1,
1233									   (uint32)actualBitmapRect.IntegerHeight() + 1,
1234									   temp.BytesPerRow());
1235				_DrawBitmap32(convertedBuffer, actualBitmapRect, bitmapRect, viewRect);
1236			} else {
1237fprintf(stderr, "Painter::_DrawBitmap() - colorspace conversion failed: %s\n", strerror(err));
1238			}
1239#endif // __HAIKU__
1240			break;
1241	}
1242}
1243
1244// _DrawBitmap32
1245void
1246Painter::_DrawBitmap32(const agg::rendering_buffer& srcBuffer,
1247					   BRect actualBitmapRect, BRect bitmapRect, BRect viewRect) const
1248{
1249typedef agg::span_allocator<agg::rgba8> span_alloc_type;
1250typedef agg::span_interpolator_linear<> interpolator_type;
1251typedef agg::span_image_filter_rgba32_nn<agg::order_bgra32,
1252										 interpolator_type> span_gen_type;
1253typedef agg::renderer_scanline_aa<renderer_base, span_gen_type> image_renderer_type;
1254
1255	if (bitmapRect.IsValid() && bitmapRect.Intersects(actualBitmapRect)
1256		&& viewRect.IsValid()) {
1257
1258		// compensate for the lefttop offset the actualBitmapRect might have
1259// NOTE: I have no clue why enabling the next call gives a wrong result!
1260// According to the BeBook, bitmapRect is supposed to be in native
1261// bitmap space!
1262//		bitmapRect.OffsetBy(-actualBitmapRect.left, -actualBitmapRect.top);
1263		actualBitmapRect.OffsetBy(-actualBitmapRect.left, -actualBitmapRect.top);
1264
1265		// calculate the scaling
1266		double xScale = (viewRect.Width() + 1) / (bitmapRect.Width() + 1);
1267		double yScale = (viewRect.Height() + 1) / (bitmapRect.Height() + 1);
1268
1269		// constrain rect to passed bitmap bounds
1270		// and transfer the changes to the viewRect
1271		if (bitmapRect.left < actualBitmapRect.left) {
1272			float diff = actualBitmapRect.left - bitmapRect.left;
1273			viewRect.left += diff * xScale;
1274			bitmapRect.left = actualBitmapRect.left;
1275		}
1276		if (bitmapRect.top < actualBitmapRect.top) {
1277			float diff = actualBitmapRect.top - bitmapRect.top;
1278			viewRect.top += diff;
1279			bitmapRect.top = actualBitmapRect.top;
1280		}
1281		if (bitmapRect.right > actualBitmapRect.right) {
1282			float diff = bitmapRect.right - actualBitmapRect.right;
1283			viewRect.right -= diff;
1284			bitmapRect.right = actualBitmapRect.right;
1285		}
1286		if (bitmapRect.bottom > actualBitmapRect.bottom) {
1287			float diff = bitmapRect.right - actualBitmapRect.bottom;
1288			viewRect.bottom -= diff;
1289			bitmapRect.bottom = actualBitmapRect.bottom;
1290		}
1291
1292		float xOffset = viewRect.left - (bitmapRect.left * xScale);
1293		float yOffset = viewRect.top - (bitmapRect.top * yScale);
1294
1295		agg::trans_affine srcMatrix;
1296//		srcMatrix *= agg::trans_affine_translation(-actualBitmapRect.left, -actualBitmapRect.top);
1297		srcMatrix *= agg::trans_affine_scaling(fScale, fScale);
1298		srcMatrix *= agg::trans_affine_translation(fOrigin.x, fOrigin.y);
1299
1300		agg::trans_affine imgMatrix;
1301		imgMatrix *= agg::trans_affine_scaling(xScale, yScale);
1302		imgMatrix *= agg::trans_affine_translation(xOffset, yOffset);
1303		imgMatrix *= agg::trans_affine_scaling(fScale, fScale);
1304		imgMatrix *= agg::trans_affine_translation(fOrigin.x, fOrigin.y);
1305		imgMatrix.invert();
1306
1307		span_alloc_type sa;
1308		interpolator_type interpolator(imgMatrix);
1309
1310		span_gen_type sg(sa, srcBuffer, agg::rgba(0, 0, 0, 0), interpolator);
1311
1312		image_renderer_type ri(*fBaseRenderer, sg);
1313
1314		agg::rasterizer_scanline_aa<> pf;
1315		agg::scanline_u8 sl;
1316
1317		// path encloses image
1318		agg::path_storage path;
1319		path.move_to(viewRect.left, viewRect.top);
1320		path.line_to(viewRect.right + 1, viewRect.top);
1321		path.line_to(viewRect.right + 1, viewRect.bottom + 1);
1322		path.line_to(viewRect.left, viewRect.bottom + 1);
1323		path.close_polygon();
1324
1325		agg::conv_transform<agg::path_storage> tr(path, srcMatrix);
1326
1327		pf.add_path(tr);
1328		agg::render_scanlines(pf, sl, ri);
1329	}
1330}
1331
1332// _InvertRect32
1333void
1334Painter::_InvertRect32(BRect r) const
1335{
1336	if (fBuffer) {
1337		int32 width = r.IntegerWidth() + 1;
1338		for (int32 y = (int32)r.top; y <= (int32)r.bottom; y++) {
1339			uint8* dst = fBuffer->row(y);
1340			dst += (int32)r.left * 4;
1341			for (int32 i = 0; i < width; i++) {
1342				dst[0] = 255 - dst[0];
1343				dst[1] = 255 - dst[1];
1344				dst[2] = 255 - dst[2];
1345				dst += 4;
1346			}
1347		}
1348	}
1349}
1350
1351// #pragma mark -
1352
1353template<class VertexSource>
1354BRect
1355Painter::_BoundingBox(VertexSource& path) const
1356{
1357	double left = 0.0;
1358	double top = 0.0;
1359	double right = -1.0;
1360	double bottom = -1.0;
1361	uint32 pathID[1];
1362	pathID[0] = 0;
1363	agg::bounding_rect(path, pathID, 0, 1, &left, &top, &right, &bottom);
1364	return BRect(left, top, right, bottom);
1365}
1366
1367
1368// _StrokePath
1369template<class VertexSource>
1370BRect
1371Painter::_StrokePath(VertexSource& path, const pattern& p) const
1372{
1373// We're now used by app_server and SetDrawData() was called prior to
1374// this and it means the pattern is already set
1375//	fPatternHandler->SetPattern(p);
1376//	_SetPattern(p);
1377
1378#if ALIASED_DRAWING
1379	float width = fPenSize;
1380	_Transform(&width);
1381	if (width > 1.0) {
1382		agg::conv_stroke<VertexSource> stroke(path);
1383		stroke.width(width);
1384
1385		fRasterizer->add_path(stroke);
1386		agg::render_scanlines(*fRasterizer, *fScanline, *fRenderer);
1387	} else {
1388		fOutlineRasterizer->add_path(path);
1389	}
1390#else
1391	fOutlineRasterizer->add_path(path);
1392#endif
1393
1394	return _Clipped(_BoundingBox(path));
1395}
1396
1397// _FillPath
1398template<class VertexSource>
1399BRect
1400Painter::_FillPath(VertexSource& path, const pattern& p) const
1401{
1402// We're now used by app_server and SetDrawData() was called prior to
1403// this and it means the pattern is already set
1404//	fPatternHandler->SetPattern(p);
1405//	_SetPattern(p);
1406
1407	fRasterizer->add_path(path);
1408	agg::render_scanlines(*fRasterizer, *fScanline, *fRenderer);
1409
1410	return _Clipped(_BoundingBox(path));
1411}
1412
1413// _SetPattern
1414void
1415Painter::_SetPattern(const pattern& p) const
1416{
1417	if (!(p == *fPatternHandler->GetR5Pattern())) {
1418printf("Painter::_SetPattern()\n");
1419		fPatternHandler->SetPattern(p);
1420		DrawingMode* mode = NULL;
1421		if (p == B_SOLID_HIGH) {
1422			_SetRendererColor(fPatternHandler->HighColor().GetColor32());
1423			mode = DrawingModeFactory::DrawingModeFor(fDrawingMode,
1424													  fAlphaSrcMode,
1425													  fAlphaFncMode,
1426													  true);
1427		} else if (p == B_SOLID_LOW) {
1428			_SetRendererColor(fPatternHandler->LowColor().GetColor32());
1429			mode = DrawingModeFactory::DrawingModeFor(fDrawingMode,
1430													  fAlphaSrcMode,
1431													  fAlphaFncMode,
1432													  true);
1433		} else {
1434			mode = DrawingModeFactory::DrawingModeFor(fDrawingMode,
1435													  fAlphaSrcMode,
1436													  fAlphaFncMode,
1437													  false);
1438		}
1439		fPixelFormat->set_drawing_mode(mode);
1440	}
1441}
1442
1443// _SetRendererColor
1444void
1445Painter::_SetRendererColor(const rgb_color& color) const
1446{
1447
1448	if (fOutlineRenderer)
1449#if ALIASED_DRAWING
1450		fOutlineRenderer->line_color(agg::rgba(color.red / 255.0,
1451											   color.green / 255.0,
1452											   color.blue / 255.0));
1453#else
1454		fOutlineRenderer->color(agg::rgba(color.red / 255.0,
1455										  color.green / 255.0,
1456										  color.blue / 255.0));
1457#endif
1458	if (fRenderer)
1459		fRenderer->color(agg::rgba(color.red / 255.0,
1460								   color.green / 255.0,
1461								   color.blue / 255.0));
1462	if (fFontRendererSolid)
1463		fFontRendererSolid->color(agg::rgba(color.red / 255.0,
1464											color.green / 255.0,
1465											color.blue / 255.0));
1466	if (fFontRendererBin)
1467		fFontRendererBin->color(agg::rgba(color.red / 255.0,
1468										  color.green / 255.0,
1469										  color.blue / 255.0));
1470
1471}
1472