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