xref: /haiku/src/servers/app/drawing/Painter/Painter.cpp (revision fae7ea18b62865f5e1159e1678c851d3aa1ddce0)
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  * All rights reserved. Distributed under the terms of the MIT License.
6  */
7 
8 
9 /*!	API to the Anti-Grain Geometry based "Painter" drawing backend. Manages
10 	rendering pipe-lines for stroke, fills, bitmap and text rendering.
11 */
12 
13 
14 #include "Painter.h"
15 
16 #include <new>
17 
18 #include <stdio.h>
19 #include <string.h>
20 #include <syslog.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 "DrawingMode.h"
57 #include "GlobalSubpixelSettings.h"
58 #include "PatternHandler.h"
59 #include "RenderingBuffer.h"
60 #include "ServerBitmap.h"
61 #include "ServerFont.h"
62 #include "SystemPalette.h"
63 
64 #include "AppServer.h"
65 
66 using std::nothrow;
67 
68 #undef TRACE
69 // #define TRACE_PAINTER
70 #ifdef TRACE_PAINTER
71 #	define TRACE(x...)		printf(x)
72 #else
73 #	define TRACE(x...)
74 #endif
75 
76 //#define TRACE_GRADIENTS
77 #ifdef TRACE_GRADIENTS
78 #	include <OS.h>
79 #	define GTRACE(x...)		debug_printf(x)
80 #else
81 #	define GTRACE(x...)
82 #endif
83 
84 
85 #define CHECK_CLIPPING	if (!fValidClipping) return BRect(0, 0, -1, -1);
86 #define CHECK_CLIPPING_NO_RETURN	if (!fValidClipping) return;
87 
88 // Defines for SIMD support.
89 #define APPSERVER_SIMD_MMX	(1 << 0)
90 #define APPSERVER_SIMD_SSE	(1 << 1)
91 
92 // Prototypes for assembler routines
93 extern "C" {
94 	void bilinear_scale_xloop_mmxsse(const uint8* src, void* dst,
95 		void* xWeights, uint32 xmin, uint32 xmax, uint32 wTop, uint32 srcBPR);
96 }
97 
98 static uint32 detect_simd();
99 
100 static uint32 sSIMDFlags = detect_simd();
101 
102 
103 /*!	Detect SIMD flags for use in AppServer. Checks all CPUs in the system
104 	and chooses the minimum supported set of instructions.
105 */
106 static uint32
107 detect_simd()
108 {
109 #if __INTEL__
110 	// Only scan CPUs for which we are certain the SIMD flags are properly
111 	// defined.
112 	const char* vendorNames[] = {
113 		"GenuineIntel",
114 		"AuthenticAMD",
115 		"CentaurHauls", // Via CPUs, MMX and SSE support
116 		"RiseRiseRise", // should be MMX-only
117 		"CyrixInstead", // MMX-only, but custom MMX extensions
118 		"GenuineTMx86", // MMX and SSE
119 		0
120 	};
121 
122 	system_info systemInfo;
123 	if (get_system_info(&systemInfo) != B_OK)
124 		return 0;
125 
126 	// We start out with all flags set and end up with only those flags
127 	// supported across all CPUs found.
128 	uint32 systemSIMD = 0xffffffff;
129 
130 	for (uint32 cpu = 0; cpu < systemInfo.cpu_count; cpu++) {
131 		cpuid_info cpuInfo;
132 		get_cpuid(&cpuInfo, 0, cpu);
133 
134 		// Get the vendor string and terminate it manually
135 		char vendor[13];
136 		memcpy(vendor, cpuInfo.eax_0.vendor_id, 12);
137 		vendor[12] = 0;
138 
139 		bool vendorFound = false;
140 		for (uint32 i = 0; vendorNames[i] != 0; i++) {
141 			if (strcmp(vendor, vendorNames[i]) == 0)
142 				vendorFound = true;
143 		}
144 
145 		uint32 cpuSIMD = 0;
146 		uint32 maxStdFunc = cpuInfo.regs.eax;
147 		if (vendorFound && maxStdFunc >= 1) {
148 			get_cpuid(&cpuInfo, 1, 0);
149 			uint32 edx = cpuInfo.regs.edx;
150 			if (edx & (1 << 23))
151 				cpuSIMD |= APPSERVER_SIMD_MMX;
152 			if (edx & (1 << 25))
153 				cpuSIMD |= APPSERVER_SIMD_SSE;
154 		} else {
155 			// no flags can be identified
156 			cpuSIMD = 0;
157 		}
158 		systemSIMD &= cpuSIMD;
159 	}
160 	return systemSIMD;
161 #else	// !__INTEL__
162 	return 0;
163 #endif
164 }
165 
166 
167 // #pragma mark -
168 
169 
170 Painter::Painter()
171 	:
172 	fBuffer(),
173 	fPixelFormat(fBuffer, &fPatternHandler),
174 	fBaseRenderer(fPixelFormat),
175 	fUnpackedScanline(),
176 	fPackedScanline(),
177 	fRasterizer(),
178 	fRenderer(fBaseRenderer),
179 	fRendererBin(fBaseRenderer),
180 	fSubpixPackedScanline(),
181 	fSubpixUnpackedScanline(),
182 	fSubpixRasterizer(),
183 	fSubpixRenderer(fBaseRenderer),
184 	fMaskedUnpackedScanline(NULL),
185 
186 	fPath(),
187 	fCurve(fPath),
188 
189 	fSubpixelPrecise(false),
190 	fValidClipping(false),
191 	fDrawingText(false),
192 	fAttached(false),
193 
194 	fPenSize(1.0),
195 	fClippingRegion(NULL),
196 	fDrawingMode(B_OP_COPY),
197 	fAlphaSrcMode(B_PIXEL_ALPHA),
198 	fAlphaFncMode(B_ALPHA_OVERLAY),
199 	fLineCapMode(B_BUTT_CAP),
200 	fLineJoinMode(B_MITER_JOIN),
201 	fMiterLimit(B_DEFAULT_MITER_LIMIT),
202 
203 	fPatternHandler(),
204 	fTextRenderer(fSubpixRenderer, fRenderer, fRendererBin, fUnpackedScanline,
205 		fSubpixUnpackedScanline, fSubpixRasterizer, fMaskedUnpackedScanline,
206 		fTransform)
207 {
208 	fPixelFormat.SetDrawingMode(fDrawingMode, fAlphaSrcMode, fAlphaFncMode,
209 		false);
210 
211 #if ALIASED_DRAWING
212 	fRasterizer.gamma(agg::gamma_threshold(0.5));
213 	fSubpixRasterizer.gamma(agg:gamma_threshold(0.5));
214 #endif
215 }
216 
217 
218 // destructor
219 Painter::~Painter()
220 {
221 }
222 
223 
224 // #pragma mark -
225 
226 
227 // AttachToBuffer
228 void
229 Painter::AttachToBuffer(RenderingBuffer* buffer)
230 {
231 	if (buffer && buffer->InitCheck() >= B_OK
232 		&& (buffer->ColorSpace() == B_RGBA32
233 			|| buffer->ColorSpace() == B_RGB32)) {
234 		// TODO: implement drawing on B_RGB24, B_RGB15, B_RGB16,
235 		// B_CMAP8 and B_GRAY8 :-[
236 		// (if ever we want to support some devices where this gives
237 		// a great speed up, right now it seems fine, even in emulation)
238 
239 		fBuffer.attach((uint8*)buffer->Bits(),
240 			buffer->Width(), buffer->Height(), buffer->BytesPerRow());
241 
242 		fAttached = true;
243 		fValidClipping = fClippingRegion != NULL
244 			&& fClippingRegion->Frame().IsValid();
245 
246 		// These are the AGG renderes and rasterizes which
247 		// will be used for stroking paths
248 
249 		_SetRendererColor(fPatternHandler.HighColor());
250 	}
251 }
252 
253 
254 // DetachFromBuffer
255 void
256 Painter::DetachFromBuffer()
257 {
258 	fBuffer.attach(NULL, 0, 0, 0);
259 	fAttached = false;
260 	fValidClipping = false;
261 }
262 
263 
264 // Bounds
265 BRect
266 Painter::Bounds() const
267 {
268 	return BRect(0, 0, fBuffer.width() - 1, fBuffer.height() - 1);
269 }
270 
271 
272 // #pragma mark -
273 
274 
275 // SetDrawState
276 void
277 Painter::SetDrawState(const DrawState* state, int32 xOffset, int32 yOffset)
278 {
279 	// NOTE: The custom clipping in "state" is ignored, because it has already
280 	// been taken into account elsewhere
281 
282 	// NOTE: Usually this function is only used when the "current view"
283 	// is switched in the ServerWindow and after the decorator has drawn
284 	// and messed up the state. For other graphics state changes, the
285 	// Painter methods are used directly, so this function is much less
286 	// speed critical than it used to be.
287 	SetTransform(state->CombinedTransform(), xOffset, yOffset);
288 
289 	SetPenSize(state->PenSize());
290 
291 	SetFont(state);
292 
293 	fSubpixelPrecise = state->SubPixelPrecise();
294 
295 	if (state->GetAlphaMask() != NULL)
296 		fMaskedUnpackedScanline = state->GetAlphaMask()->Generate();
297 	else
298 		fMaskedUnpackedScanline = NULL;
299 
300 	// any of these conditions means we need to use a different drawing
301 	// mode instance
302 	bool updateDrawingMode
303 		= !(state->GetPattern() == fPatternHandler.GetPattern())
304 			|| state->GetDrawingMode() != fDrawingMode
305 			|| (state->GetDrawingMode() == B_OP_ALPHA
306 				&& (state->AlphaSrcMode() != fAlphaSrcMode
307 					|| state->AlphaFncMode() != fAlphaFncMode));
308 
309 	fDrawingMode = state->GetDrawingMode();
310 	fAlphaSrcMode = state->AlphaSrcMode();
311 	fAlphaFncMode = state->AlphaFncMode();
312 	fPatternHandler.SetPattern(state->GetPattern());
313 	fPatternHandler.SetOffsets(xOffset, yOffset);
314 	fLineCapMode = state->LineCapMode();
315 	fLineJoinMode = state->LineJoinMode();
316 	fMiterLimit = state->MiterLimit();
317 
318 	SetFillRule(state->FillRule());
319 
320 	// adopt the color *after* the pattern is set
321 	// to set the renderers to the correct color
322 	SetHighColor(state->HighColor());
323 	SetLowColor(state->LowColor());
324 
325 	if (updateDrawingMode || fPixelFormat.UsesOpCopyForText())
326 		_UpdateDrawingMode();
327 }
328 
329 
330 // #pragma mark - state
331 
332 
333 // ConstrainClipping
334 void
335 Painter::ConstrainClipping(const BRegion* region)
336 {
337 	fClippingRegion = region;
338 	fBaseRenderer.set_clipping_region(const_cast<BRegion*>(region));
339 	fValidClipping = region->Frame().IsValid() && fAttached;
340 
341 	if (fValidClipping) {
342 		clipping_rect cb = fClippingRegion->FrameInt();
343 		fRasterizer.clip_box(cb.left, cb.top, cb.right + 1, cb.bottom + 1);
344 		fSubpixRasterizer.clip_box(cb.left, cb.top, cb.right + 1, cb.bottom + 1);
345 	}
346 }
347 
348 
349 void
350 Painter::SetTransform(BAffineTransform transform, int32 xOffset, int32 yOffset)
351 {
352 	fIdentityTransform = transform.IsIdentity();
353 	if (!fIdentityTransform) {
354 		fTransform = agg::trans_affine_translation(-xOffset, -yOffset);
355 		fTransform *= agg::trans_affine(transform.sx, transform.shy,
356 			transform.shx, transform.sy, transform.tx, transform.ty);
357 		fTransform *= agg::trans_affine_translation(xOffset, yOffset);
358 	} else {
359 		fTransform.reset();
360 	}
361 }
362 
363 
364 // SetHighColor
365 void
366 Painter::SetHighColor(const rgb_color& color)
367 {
368 	if (fPatternHandler.HighColor() == color)
369 		return;
370 	fPatternHandler.SetHighColor(color);
371 	if (*(fPatternHandler.GetR5Pattern()) == B_SOLID_HIGH)
372 		_SetRendererColor(color);
373 }
374 
375 
376 // SetLowColor
377 void
378 Painter::SetLowColor(const rgb_color& color)
379 {
380 	fPatternHandler.SetLowColor(color);
381 	if (*(fPatternHandler.GetR5Pattern()) == B_SOLID_LOW)
382 		_SetRendererColor(color);
383 }
384 
385 
386 // SetDrawingMode
387 void
388 Painter::SetDrawingMode(drawing_mode mode)
389 {
390 	if (fDrawingMode != mode) {
391 		fDrawingMode = mode;
392 		_UpdateDrawingMode();
393 	}
394 }
395 
396 
397 // SetBlendingMode
398 void
399 Painter::SetBlendingMode(source_alpha srcAlpha, alpha_function alphaFunc)
400 {
401 	if (fAlphaSrcMode != srcAlpha || fAlphaFncMode != alphaFunc) {
402 		fAlphaSrcMode = srcAlpha;
403 		fAlphaFncMode = alphaFunc;
404 		if (fDrawingMode == B_OP_ALPHA)
405 			_UpdateDrawingMode();
406 	}
407 }
408 
409 
410 // SetPenSize
411 void
412 Painter::SetPenSize(float size)
413 {
414 	fPenSize = size;
415 }
416 
417 
418 // SetStrokeMode
419 void
420 Painter::SetStrokeMode(cap_mode lineCap, join_mode joinMode, float miterLimit)
421 {
422 	fLineCapMode = lineCap;
423 	fLineJoinMode = joinMode;
424 	fMiterLimit = miterLimit;
425 }
426 
427 
428 void
429 Painter::SetFillRule(int32 fillRule)
430 {
431 	agg::filling_rule_e aggFillRule = fillRule == B_EVEN_ODD
432 		? agg::fill_even_odd : agg::fill_non_zero;
433 
434 	fRasterizer.filling_rule(aggFillRule);
435 	fSubpixRasterizer.filling_rule(aggFillRule);
436 }
437 
438 
439 // SetPattern
440 void
441 Painter::SetPattern(const pattern& p, bool drawingText)
442 {
443 	if (!(p == *fPatternHandler.GetR5Pattern()) || drawingText != fDrawingText) {
444 		fPatternHandler.SetPattern(p);
445 		fDrawingText = drawingText;
446 		_UpdateDrawingMode(fDrawingText);
447 
448 		// update renderer color if necessary
449 		if (fPatternHandler.IsSolidHigh()) {
450 			// pattern was not solid high before
451 			_SetRendererColor(fPatternHandler.HighColor());
452 		} else if (fPatternHandler.IsSolidLow()) {
453 			// pattern was not solid low before
454 			_SetRendererColor(fPatternHandler.LowColor());
455 		}
456 	}
457 }
458 
459 
460 // SetFont
461 void
462 Painter::SetFont(const ServerFont& font)
463 {
464 	fTextRenderer.SetFont(font);
465 	fTextRenderer.SetAntialiasing(!(font.Flags() & B_DISABLE_ANTIALIASING));
466 }
467 
468 
469 // SetFont
470 void
471 Painter::SetFont(const DrawState* state)
472 {
473 	fTextRenderer.SetFont(state->Font());
474 	fTextRenderer.SetAntialiasing(!state->ForceFontAliasing()
475 		&& (state->Font().Flags() & B_DISABLE_ANTIALIASING) == 0);
476 }
477 
478 
479 // #pragma mark - drawing
480 
481 
482 // StrokeLine
483 void
484 Painter::StrokeLine(BPoint a, BPoint b)
485 {
486 	CHECK_CLIPPING_NO_RETURN
487 
488 	// "false" means not to do the pixel center offset,
489 	// because it would mess up our optimized versions
490 	_Align(&a, false);
491 	_Align(&b, false);
492 
493 	// first, try an optimized version
494 	if (fPenSize == 1.0 && fIdentityTransform
495 		&& (fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER)
496 		&& fMaskedUnpackedScanline == NULL) {
497 		pattern pat = *fPatternHandler.GetR5Pattern();
498 		if (pat == B_SOLID_HIGH
499 			&& StraightLine(a, b, fPatternHandler.HighColor())) {
500 			return;
501 		} else if (pat == B_SOLID_LOW
502 			&& StraightLine(a, b, fPatternHandler.LowColor())) {
503 			return;
504 		}
505 	}
506 
507 	fPath.remove_all();
508 
509 	if (a == b) {
510 		// special case dots
511 		if (fPenSize == 1.0 && !fSubpixelPrecise && fIdentityTransform) {
512 			if (fClippingRegion->Contains(a)) {
513 				fPixelFormat.blend_pixel((int)a.x, (int)a.y, 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, true);
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, true);
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 (bitmap && bitmap->IsValid() && touched.IsValid()) {
1434 		// the native bitmap coordinate system
1435 		BRect actualBitmapRect(bitmap->Bounds());
1436 
1437 		TRACE("Painter::DrawBitmap()\n");
1438 		TRACE("   actualBitmapRect = (%.1f, %.1f) - (%.1f, %.1f)\n",
1439 			actualBitmapRect.left, actualBitmapRect.top,
1440 			actualBitmapRect.right, actualBitmapRect.bottom);
1441 		TRACE("   bitmapRect = (%.1f, %.1f) - (%.1f, %.1f)\n",
1442 			bitmapRect.left, bitmapRect.top, bitmapRect.right,
1443 			bitmapRect.bottom);
1444 		TRACE("   viewRect = (%.1f, %.1f) - (%.1f, %.1f)\n",
1445 			viewRect.left, viewRect.top, viewRect.right, viewRect.bottom);
1446 
1447 		agg::rendering_buffer srcBuffer;
1448 		srcBuffer.attach(bitmap->Bits(), bitmap->Width(), bitmap->Height(),
1449 			bitmap->BytesPerRow());
1450 
1451 		_DrawBitmap(srcBuffer, bitmap->ColorSpace(), actualBitmapRect,
1452 			bitmapRect, viewRect, options);
1453 	}
1454 	return touched;
1455 }
1456 
1457 
1458 // #pragma mark -
1459 
1460 
1461 // FillRegion
1462 BRect
1463 Painter::FillRegion(const BRegion* region) const
1464 {
1465 	CHECK_CLIPPING
1466 
1467 	BRegion copy(*region);
1468 	int32 count = copy.CountRects();
1469 	BRect touched = FillRect(copy.RectAt(0));
1470 	for (int32 i = 1; i < count; i++) {
1471 		touched = touched | FillRect(copy.RectAt(i));
1472 	}
1473 	return touched;
1474 }
1475 
1476 
1477 // FillRegion
1478 BRect
1479 Painter::FillRegion(const BRegion* region, const BGradient& gradient) const
1480 {
1481 	CHECK_CLIPPING
1482 
1483 	BRegion copy(*region);
1484 	int32 count = copy.CountRects();
1485 	BRect touched = FillRect(copy.RectAt(0), gradient);
1486 	for (int32 i = 1; i < count; i++) {
1487 		touched = touched | FillRect(copy.RectAt(i), gradient);
1488 	}
1489 	return touched;
1490 }
1491 
1492 
1493 // InvertRect
1494 BRect
1495 Painter::InvertRect(const BRect& r) const
1496 {
1497 	CHECK_CLIPPING
1498 
1499 	BRegion region(r);
1500 	region.IntersectWith(fClippingRegion);
1501 
1502 	// implementation only for B_RGB32 at the moment
1503 	int32 count = region.CountRects();
1504 	for (int32 i = 0; i < count; i++)
1505 		_InvertRect32(region.RectAt(i));
1506 
1507 	return _Clipped(r);
1508 }
1509 
1510 
1511 // #pragma mark - private
1512 
1513 
1514 inline float
1515 Painter::_Align(float coord, bool round, bool centerOffset) const
1516 {
1517 	// rounding
1518 	if (round)
1519 		coord = (int32)coord;
1520 
1521 	// This code is supposed to move coordinates to the center of pixels,
1522 	// as AGG considers (0,0) to be the "upper left corner" of a pixel,
1523 	// but BViews are less strict on those details
1524 	if (centerOffset)
1525 		coord += 0.5;
1526 
1527 	return coord;
1528 }
1529 
1530 
1531 inline void
1532 Painter::_Align(BPoint* point, bool centerOffset) const
1533 {
1534 	_Align(point, !fSubpixelPrecise, centerOffset);
1535 }
1536 
1537 
1538 inline void
1539 Painter::_Align(BPoint* point, bool round, bool centerOffset) const
1540 {
1541 	point->x = _Align(point->x, round, centerOffset);
1542 	point->y = _Align(point->y, round, centerOffset);
1543 }
1544 
1545 
1546 inline BPoint
1547 Painter::_Align(const BPoint& point, bool centerOffset) const
1548 {
1549 	BPoint ret(point);
1550 	_Align(&ret, centerOffset);
1551 	return ret;
1552 }
1553 
1554 
1555 // _Clipped
1556 BRect
1557 Painter::_Clipped(const BRect& rect) const
1558 {
1559 	if (rect.IsValid())
1560 		return BRect(rect & fClippingRegion->Frame());
1561 
1562 	return BRect(rect);
1563 }
1564 
1565 
1566 // _UpdateDrawingMode
1567 void
1568 Painter::_UpdateDrawingMode(bool drawingText)
1569 {
1570 	// The AGG renderers have their own color setting, however
1571 	// almost all drawing mode classes ignore the color given
1572 	// by the AGG renderer and use the colors from the PatternHandler
1573 	// instead. If we have a B_SOLID_* pattern, we can actually use
1574 	// the color in the renderer and special versions of drawing modes
1575 	// that don't use PatternHandler and are more efficient. This
1576 	// has been implemented for B_OP_COPY and a couple others (the
1577 	// DrawingMode*Solid ones) as of now. The PixelFormat knows the
1578 	// PatternHandler and makes its decision based on the pattern.
1579 	// The last parameter to SetDrawingMode() is a special flag
1580 	// for when Painter is used to draw text. In this case, another
1581 	// special version of B_OP_COPY is used that acts like R5 in that
1582 	// anti-aliased pixel are not rendered against the actual background
1583 	// but the current low color instead. This way, the frame buffer
1584 	// doesn't need to be read.
1585 	// When a solid pattern is used, _SetRendererColor()
1586 	// has to be called so that all internal colors in the renderes
1587 	// are up to date for use by the solid drawing mode version.
1588 	fPixelFormat.SetDrawingMode(fDrawingMode, fAlphaSrcMode, fAlphaFncMode,
1589 		drawingText);
1590 	if (drawingText)
1591 		fPatternHandler.MakeOpCopyColorCache();
1592 }
1593 
1594 
1595 // _SetRendererColor
1596 void
1597 Painter::_SetRendererColor(const rgb_color& color) const
1598 {
1599 	fRenderer.color(agg::rgba(color.red / 255.0, color.green / 255.0,
1600 		color.blue / 255.0, color.alpha / 255.0));
1601 	fSubpixRenderer.color(agg::rgba(color.red / 255.0, color.green / 255.0,
1602 		color.blue / 255.0, color.alpha / 255.0));
1603 // TODO: bitmap fonts not yet correctly setup in AGGTextRenderer
1604 //	fRendererBin.color(agg::rgba(color.red / 255.0, color.green / 255.0,
1605 //		color.blue / 255.0, color.alpha / 255.0));
1606 }
1607 
1608 
1609 // #pragma mark -
1610 
1611 
1612 // _DrawTriangle
1613 inline BRect
1614 Painter::_DrawTriangle(BPoint pt1, BPoint pt2, BPoint pt3, bool fill) const
1615 {
1616 	CHECK_CLIPPING
1617 
1618 	_Align(&pt1);
1619 	_Align(&pt2);
1620 	_Align(&pt3);
1621 
1622 	fPath.remove_all();
1623 
1624 	fPath.move_to(pt1.x, pt1.y);
1625 	fPath.line_to(pt2.x, pt2.y);
1626 	fPath.line_to(pt3.x, pt3.y);
1627 
1628 	fPath.close_polygon();
1629 
1630 	if (fill)
1631 		return _FillPath(fPath);
1632 
1633 	return _StrokePath(fPath);
1634 }
1635 
1636 
1637 // copy_bitmap_row_cmap8_copy
1638 static inline void
1639 copy_bitmap_row_cmap8_copy(uint8* dst, const uint8* src, int32 numPixels,
1640 	const rgb_color* colorMap)
1641 {
1642 	uint32* d = (uint32*)dst;
1643 	const uint8* s = src;
1644 	while (numPixels--) {
1645 		const rgb_color c = colorMap[*s++];
1646 		*d++ = (c.alpha << 24) | (c.red << 16) | (c.green << 8) | (c.blue);
1647 	}
1648 }
1649 
1650 
1651 // copy_bitmap_row_cmap8_over
1652 static inline void
1653 copy_bitmap_row_cmap8_over(uint8* dst, const uint8* src, int32 numPixels,
1654 	const rgb_color* colorMap)
1655 {
1656 	uint32* d = (uint32*)dst;
1657 	const uint8* s = src;
1658 	while (numPixels--) {
1659 		const rgb_color c = colorMap[*s++];
1660 		if (c.alpha)
1661 			*d = (c.alpha << 24) | (c.red << 16) | (c.green << 8) | (c.blue);
1662 		d++;
1663 	}
1664 }
1665 
1666 
1667 // copy_bitmap_row_bgr32_copy
1668 static inline void
1669 copy_bitmap_row_bgr32_copy(uint8* dst, const uint8* src, int32 numPixels,
1670 	const rgb_color* colorMap)
1671 {
1672 	memcpy(dst, src, numPixels * 4);
1673 }
1674 
1675 
1676 // copy_bitmap_row_bgr32_over
1677 static inline void
1678 copy_bitmap_row_bgr32_over(uint8* dst, const uint8* src, int32 numPixels,
1679 	const rgb_color* colorMap)
1680 {
1681 	uint32* d = (uint32*)dst;
1682 	uint32* s = (uint32*)src;
1683 	while (numPixels--) {
1684 		if (*s != B_TRANSPARENT_MAGIC_RGBA32)
1685 			*(uint32*)d = *(uint32*)s;
1686 		d++;
1687 		s++;
1688 	}
1689 }
1690 
1691 
1692 void
1693 Painter::_IterateShapeData(const int32& opCount, const uint32* opList,
1694 	const int32& ptCount, const BPoint* points,
1695 	const BPoint& viewToScreenOffset, float viewScale) const
1696 {
1697 	// TODO: if shapes are ever used more heavily in Haiku,
1698 	// it would be nice to use BShape data directly (write
1699 	// an AGG "VertexSource" adaptor)
1700 	fPath.remove_all();
1701 	for (int32 i = 0; i < opCount; i++) {
1702 		uint32 op = opList[i] & 0xFF000000;
1703 		if ((op & OP_MOVETO) != 0) {
1704 			fPath.move_to(
1705 				points->x * viewScale + viewToScreenOffset.x,
1706 				points->y * viewScale + viewToScreenOffset.y);
1707 			points++;
1708 		}
1709 
1710 		if ((op & OP_LINETO) != 0) {
1711 			int32 count = opList[i] & 0x00FFFFFF;
1712 			while (count--) {
1713 				fPath.line_to(
1714 					points->x * viewScale + viewToScreenOffset.x,
1715 					points->y * viewScale + viewToScreenOffset.y);
1716 				points++;
1717 			}
1718 		}
1719 
1720 		if ((op & OP_BEZIERTO) != 0) {
1721 			int32 count = opList[i] & 0x00FFFFFF;
1722 			while (count) {
1723 				fPath.curve4(
1724 					points[0].x * viewScale + viewToScreenOffset.x,
1725 					points[0].y * viewScale + viewToScreenOffset.y,
1726 					points[1].x * viewScale + viewToScreenOffset.x,
1727 					points[1].y * viewScale + viewToScreenOffset.y,
1728 					points[2].x * viewScale + viewToScreenOffset.x,
1729 					points[2].y * viewScale + viewToScreenOffset.y);
1730 				points += 3;
1731 				count -= 3;
1732 			}
1733 		}
1734 
1735 		if ((op & OP_LARGE_ARC_TO_CW) != 0 || (op & OP_LARGE_ARC_TO_CCW) != 0
1736 			|| (op & OP_SMALL_ARC_TO_CW) != 0
1737 			|| (op & OP_SMALL_ARC_TO_CCW) != 0) {
1738 			int32 count = opList[i] & 0x00FFFFFF;
1739 			while (count > 0) {
1740 				fPath.arc_to(
1741 					points[0].x * viewScale,
1742 					points[0].y * viewScale,
1743 					points[1].x,
1744 					op & (OP_LARGE_ARC_TO_CW | OP_LARGE_ARC_TO_CCW),
1745 					op & (OP_SMALL_ARC_TO_CW | OP_LARGE_ARC_TO_CW),
1746 					points[2].x * viewScale + viewToScreenOffset.x,
1747 					points[2].y * viewScale + viewToScreenOffset.y);
1748 				points += 3;
1749 				count -= 3;
1750 			}
1751 		}
1752 
1753 		if ((op & OP_CLOSE) != 0)
1754 			fPath.close_polygon();
1755 	}
1756 }
1757 
1758 
1759 // copy_bitmap_row_bgr32_alpha
1760 static inline void
1761 copy_bitmap_row_bgr32_alpha(uint8* dst, const uint8* src, int32 numPixels,
1762 	const rgb_color* colorMap)
1763 {
1764 	uint32* d = (uint32*)dst;
1765 	int32 bytes = numPixels * 4;
1766 	uint8 buffer[bytes];
1767 	uint8* b = buffer;
1768 	while (numPixels--) {
1769 		if (src[3] == 255) {
1770 			*(uint32*)b = *(uint32*)src;
1771 		} else {
1772 			*(uint32*)b = *d;
1773 			b[0] = ((src[0] - b[0]) * src[3] + (b[0] << 8)) >> 8;
1774 			b[1] = ((src[1] - b[1]) * src[3] + (b[1] << 8)) >> 8;
1775 			b[2] = ((src[2] - b[2]) * src[3] + (b[2] << 8)) >> 8;
1776 		}
1777 		d++;
1778 		b += 4;
1779 		src += 4;
1780 	}
1781 	memcpy(dst, buffer, bytes);
1782 }
1783 
1784 
1785 // _TransparentMagicToAlpha
1786 template<typename sourcePixel>
1787 void
1788 Painter::_TransparentMagicToAlpha(sourcePixel* buffer, uint32 width,
1789 	uint32 height, uint32 sourceBytesPerRow, sourcePixel transparentMagic,
1790 	BBitmap* output) const
1791 {
1792 	uint8* sourceRow = (uint8*)buffer;
1793 	uint8* destRow = (uint8*)output->Bits();
1794 	uint32 destBytesPerRow = output->BytesPerRow();
1795 
1796 	for (uint32 y = 0; y < height; y++) {
1797 		sourcePixel* pixel = (sourcePixel*)sourceRow;
1798 		uint32* destPixel = (uint32*)destRow;
1799 		for (uint32 x = 0; x < width; x++, pixel++, destPixel++) {
1800 			if (*pixel == transparentMagic)
1801 				*destPixel &= 0x00ffffff;
1802 		}
1803 
1804 		sourceRow += sourceBytesPerRow;
1805 		destRow += destBytesPerRow;
1806 	}
1807 }
1808 
1809 
1810 // _DrawBitmap
1811 void
1812 Painter::_DrawBitmap(agg::rendering_buffer& srcBuffer, color_space format,
1813 	BRect actualBitmapRect, BRect bitmapRect, BRect viewRect,
1814 	uint32 options) const
1815 {
1816 	if (!fValidClipping
1817 		|| !bitmapRect.IsValid() || !bitmapRect.Intersects(actualBitmapRect)
1818 		|| !viewRect.IsValid()) {
1819 		return;
1820 	}
1821 
1822 	if (!fSubpixelPrecise) {
1823 		align_rect_to_pixels(&bitmapRect);
1824 		align_rect_to_pixels(&viewRect);
1825 	}
1826 
1827 	TRACE("Painter::_DrawBitmap()\n");
1828 	TRACE("   bitmapRect = (%.1f, %.1f) - (%.1f, %.1f)\n",
1829 		bitmapRect.left, bitmapRect.top, bitmapRect.right, bitmapRect.bottom);
1830 	TRACE("   viewRect = (%.1f, %.1f) - (%.1f, %.1f)\n",
1831 		viewRect.left, viewRect.top, viewRect.right, viewRect.bottom);
1832 
1833 	double xScale = (viewRect.Width() + 1) / (bitmapRect.Width() + 1);
1834 	double yScale = (viewRect.Height() + 1) / (bitmapRect.Height() + 1);
1835 
1836 	if (xScale == 0.0 || yScale == 0.0)
1837 		return;
1838 
1839 	// compensate for the lefttop offset the actualBitmapRect might have
1840 	// actualBitmapRect has the right size, but put it at B_ORIGIN
1841 	// bitmapRect is already in good coordinates
1842 	actualBitmapRect.OffsetBy(-actualBitmapRect.left, -actualBitmapRect.top);
1843 
1844 	// constrain rect to passed bitmap bounds
1845 	// and transfer the changes to the viewRect with the right scale
1846 	if (bitmapRect.left < actualBitmapRect.left) {
1847 		float diff = actualBitmapRect.left - bitmapRect.left;
1848 		viewRect.left += diff * xScale;
1849 		bitmapRect.left = actualBitmapRect.left;
1850 	}
1851 	if (bitmapRect.top < actualBitmapRect.top) {
1852 		float diff = actualBitmapRect.top - bitmapRect.top;
1853 		viewRect.top += diff * yScale;
1854 		bitmapRect.top = actualBitmapRect.top;
1855 	}
1856 	if (bitmapRect.right > actualBitmapRect.right) {
1857 		float diff = bitmapRect.right - actualBitmapRect.right;
1858 		viewRect.right -= diff * xScale;
1859 		bitmapRect.right = actualBitmapRect.right;
1860 	}
1861 	if (bitmapRect.bottom > actualBitmapRect.bottom) {
1862 		float diff = bitmapRect.bottom - actualBitmapRect.bottom;
1863 		viewRect.bottom -= diff * yScale;
1864 		bitmapRect.bottom = actualBitmapRect.bottom;
1865 	}
1866 
1867 	double xOffset = viewRect.left - bitmapRect.left;
1868 	double yOffset = viewRect.top - bitmapRect.top;
1869 
1870 	// optimized code path for B_CMAP8 and no scale
1871 	if (xScale == 1.0 && yScale == 1.0 && fIdentityTransform
1872 		&& fMaskedUnpackedScanline == NULL) {
1873 		if (format == B_CMAP8) {
1874 			if (fDrawingMode == B_OP_COPY) {
1875 				_DrawBitmapNoScale32(copy_bitmap_row_cmap8_copy, 1,
1876 					srcBuffer, (int32)xOffset, (int32)yOffset, viewRect);
1877 				return;
1878 			}
1879 			if (fDrawingMode == B_OP_OVER) {
1880 				_DrawBitmapNoScale32(copy_bitmap_row_cmap8_over, 1,
1881 					srcBuffer, (int32)xOffset, (int32)yOffset, viewRect);
1882 				return;
1883 			}
1884 		} else if (format == B_RGB32) {
1885 			if (fDrawingMode == B_OP_OVER) {
1886 				_DrawBitmapNoScale32(copy_bitmap_row_bgr32_over, 4,
1887 					srcBuffer, (int32)xOffset, (int32)yOffset, viewRect);
1888 				return;
1889 			}
1890 		}
1891 	}
1892 
1893 	BBitmap* temp = NULL;
1894 	ObjectDeleter<BBitmap> tempDeleter;
1895 
1896 	if ((format != B_RGBA32 && format != B_RGB32)
1897 		|| (format == B_RGB32 && fDrawingMode != B_OP_COPY
1898 #if 1
1899 // Enabling this would make the behavior compatible to BeOS, which
1900 // treats B_RGB32 bitmaps as B_RGB*A*32 bitmaps in B_OP_ALPHA - unlike in
1901 // all other drawing modes, where B_TRANSPARENT_MAGIC_RGBA32 is handled.
1902 // B_RGB32 bitmaps therefore don't draw correctly on BeOS if they actually
1903 // use this color, unless the alpha channel contains 255 for all other
1904 // pixels, which is inconsistent.
1905 			&& fDrawingMode != B_OP_ALPHA
1906 #endif
1907 		)) {
1908 		temp = new (nothrow) BBitmap(actualBitmapRect, B_BITMAP_NO_SERVER_LINK,
1909 			B_RGBA32);
1910 		if (temp == NULL) {
1911 			fprintf(stderr, "Painter::_DrawBitmap() - "
1912 				"out of memory for creating temporary conversion bitmap\n");
1913 			return;
1914 		}
1915 
1916 		tempDeleter.SetTo(temp);
1917 
1918 		status_t err = temp->ImportBits(srcBuffer.buf(),
1919 			srcBuffer.height() * srcBuffer.stride(),
1920 			srcBuffer.stride(), 0, format);
1921 		if (err < B_OK) {
1922 			fprintf(stderr, "Painter::_DrawBitmap() - "
1923 				"colorspace conversion failed: %s\n", strerror(err));
1924 			return;
1925 		}
1926 
1927 		// the original bitmap might have had some of the
1928 		// transaparent magic colors set that we now need to
1929 		// make transparent in our RGBA32 bitmap again.
1930 		switch (format) {
1931 			case B_RGB32:
1932 				_TransparentMagicToAlpha((uint32 *)srcBuffer.buf(),
1933 					srcBuffer.width(), srcBuffer.height(),
1934 					srcBuffer.stride(), B_TRANSPARENT_MAGIC_RGBA32,
1935 					temp);
1936 				break;
1937 
1938 			// TODO: not sure if this applies to B_RGBA15 too. It
1939 			// should not because B_RGBA15 actually has an alpha
1940 			// channel itself and it should have been preserved
1941 			// when importing the bitmap. Maybe it applies to
1942 			// B_RGB16 though?
1943 			case B_RGB15:
1944 				_TransparentMagicToAlpha((uint16 *)srcBuffer.buf(),
1945 					srcBuffer.width(), srcBuffer.height(),
1946 					srcBuffer.stride(), B_TRANSPARENT_MAGIC_RGBA15,
1947 					temp);
1948 				break;
1949 
1950 			default:
1951 				break;
1952 		}
1953 
1954 		srcBuffer.attach((uint8*)temp->Bits(),
1955 			(uint32)actualBitmapRect.IntegerWidth() + 1,
1956 			(uint32)actualBitmapRect.IntegerHeight() + 1,
1957 			temp->BytesPerRow());
1958 	}
1959 
1960 	// maybe we can use an optimized version if there is no scale
1961 	if (xScale == 1.0 && yScale == 1.0 && fIdentityTransform
1962 		&& fMaskedUnpackedScanline == NULL) {
1963 		if (fDrawingMode == B_OP_COPY) {
1964 			_DrawBitmapNoScale32(copy_bitmap_row_bgr32_copy, 4, srcBuffer,
1965 				(int32)xOffset, (int32)yOffset, viewRect);
1966 			return;
1967 		}
1968 		if (fDrawingMode == B_OP_OVER || (fDrawingMode == B_OP_ALPHA
1969 				 && fAlphaSrcMode == B_PIXEL_ALPHA
1970 				 && fAlphaFncMode == B_ALPHA_OVERLAY)) {
1971 			_DrawBitmapNoScale32(copy_bitmap_row_bgr32_alpha, 4, srcBuffer,
1972 				(int32)xOffset, (int32)yOffset, viewRect);
1973 			return;
1974 		}
1975 	}
1976 
1977 	if (fDrawingMode == B_OP_COPY && fIdentityTransform
1978 		&& fMaskedUnpackedScanline == NULL) {
1979 		if ((options & B_FILTER_BITMAP_BILINEAR) != 0) {
1980 			_DrawBitmapBilinearCopy32(srcBuffer, xOffset, yOffset, xScale,
1981 				yScale, viewRect);
1982 		} else {
1983 			_DrawBitmapNearestNeighborCopy32(srcBuffer, xOffset, yOffset,
1984 				xScale, yScale, viewRect);
1985 		}
1986 		return;
1987 	}
1988 
1989 	// for all other cases (non-optimized drawing mode or scaled drawing)
1990 	_DrawBitmapGeneric32(srcBuffer, xOffset, yOffset, xScale, yScale, viewRect,
1991 		options);
1992 }
1993 
1994 
1995 #define DEBUG_DRAW_BITMAP 0
1996 
1997 
1998 // _DrawBitmapNoScale32
1999 template <class F>
2000 void
2001 Painter::_DrawBitmapNoScale32(F copyRowFunction, uint32 bytesPerSourcePixel,
2002 	agg::rendering_buffer& srcBuffer, int32 xOffset, int32 yOffset,
2003 	BRect viewRect) const
2004 {
2005 	// NOTE: this would crash if viewRect was large enough to read outside the
2006 	// bitmap, so make sure this is not the case before calling this function!
2007 	uint8* dst = fBuffer.row_ptr(0);
2008 	uint32 dstBPR = fBuffer.stride();
2009 
2010 	const uint8* src = srcBuffer.row_ptr(0);
2011 	uint32 srcBPR = srcBuffer.stride();
2012 
2013 	int32 left = (int32)viewRect.left;
2014 	int32 top = (int32)viewRect.top;
2015 	int32 right = (int32)viewRect.right;
2016 	int32 bottom = (int32)viewRect.bottom;
2017 
2018 #if DEBUG_DRAW_BITMAP
2019 if (left - xOffset < 0 || left - xOffset >= (int32)srcBuffer.width() ||
2020 	right - xOffset >= (int32)srcBuffer.width() ||
2021 	top - yOffset < 0 || top - yOffset >= (int32)srcBuffer.height() ||
2022 	bottom - yOffset >= (int32)srcBuffer.height()) {
2023 
2024 	char message[256];
2025 	sprintf(message, "reading outside of bitmap (%ld, %ld, %ld, %ld) "
2026 			"(%d, %d) (%ld, %ld)",
2027 		left - xOffset, top - yOffset, right - xOffset, bottom - yOffset,
2028 		srcBuffer.width(), srcBuffer.height(), xOffset, yOffset);
2029 	debugger(message);
2030 }
2031 #endif
2032 
2033 	const rgb_color* colorMap = SystemPalette();
2034 
2035 	// copy rects, iterate over clipping boxes
2036 	fBaseRenderer.first_clip_box();
2037 	do {
2038 		int32 x1 = max_c(fBaseRenderer.xmin(), left);
2039 		int32 x2 = min_c(fBaseRenderer.xmax(), right);
2040 		if (x1 <= x2) {
2041 			int32 y1 = max_c(fBaseRenderer.ymin(), top);
2042 			int32 y2 = min_c(fBaseRenderer.ymax(), bottom);
2043 			if (y1 <= y2) {
2044 				uint8* dstHandle = dst + y1 * dstBPR + x1 * 4;
2045 				const uint8* srcHandle = src + (y1 - yOffset) * srcBPR
2046 					+ (x1 - xOffset) * bytesPerSourcePixel;
2047 
2048 				for (; y1 <= y2; y1++) {
2049 					copyRowFunction(dstHandle, srcHandle, x2 - x1 + 1, colorMap);
2050 
2051 					dstHandle += dstBPR;
2052 					srcHandle += srcBPR;
2053 				}
2054 			}
2055 		}
2056 	} while (fBaseRenderer.next_clip_box());
2057 }
2058 
2059 
2060 // _DrawBitmapNearestNeighborCopy32
2061 void
2062 Painter::_DrawBitmapNearestNeighborCopy32(agg::rendering_buffer& srcBuffer,
2063 	double xOffset, double yOffset, double xScale, double yScale,
2064 	BRect viewRect) const
2065 {
2066 	//bigtime_t now = system_time();
2067 	uint32 dstWidth = viewRect.IntegerWidth() + 1;
2068 	uint32 dstHeight = viewRect.IntegerHeight() + 1;
2069 	uint32 srcWidth = srcBuffer.width();
2070 	uint32 srcHeight = srcBuffer.height();
2071 
2072 	// Do not calculate more filter weights than necessary and also
2073 	// keep the stack based allocations reasonably sized
2074 	if (fClippingRegion->Frame().IntegerWidth() + 1 < (int32)dstWidth)
2075 		dstWidth = fClippingRegion->Frame().IntegerWidth() + 1;
2076 	if (fClippingRegion->Frame().IntegerHeight() + 1 < (int32)dstHeight)
2077 		dstHeight = fClippingRegion->Frame().IntegerHeight() + 1;
2078 
2079 	// When calculating less filter weights than specified by viewRect,
2080 	// we need to compensate the offset.
2081 	uint32 filterWeightXIndexOffset = 0;
2082 	uint32 filterWeightYIndexOffset = 0;
2083 	if (fClippingRegion->Frame().left > viewRect.left) {
2084 		filterWeightXIndexOffset = (int32)(fClippingRegion->Frame().left
2085 			- viewRect.left);
2086 	}
2087 	if (fClippingRegion->Frame().top > viewRect.top) {
2088 		filterWeightYIndexOffset = (int32)(fClippingRegion->Frame().top
2089 			- viewRect.top);
2090 	}
2091 
2092 	// should not pose a problem with stack overflows
2093 	// (needs around 6Kb for 1920x1200)
2094 	uint16 xIndices[dstWidth];
2095 	uint16 yIndices[dstHeight];
2096 
2097 	// Extract the cropping information for the source bitmap,
2098 	// If only a part of the source bitmap is to be drawn with scale,
2099 	// the offset will be different from the viewRect left top corner.
2100 	int32 xBitmapShift = (int32)(viewRect.left - xOffset);
2101 	int32 yBitmapShift = (int32)(viewRect.top - yOffset);
2102 
2103 	for (uint32 i = 0; i < dstWidth; i++) {
2104 		// index into source
2105 		uint16 index = (uint16)((i + filterWeightXIndexOffset) * srcWidth
2106 			/ (srcWidth * xScale));
2107 		// round down to get the left pixel
2108 		xIndices[i] = index;
2109 		// handle cropped source bitmap
2110 		xIndices[i] += xBitmapShift;
2111 		// precompute index for 32 bit pixels
2112 		xIndices[i] *= 4;
2113 	}
2114 
2115 	for (uint32 i = 0; i < dstHeight; i++) {
2116 		// index into source
2117 		uint16 index = (uint16)((i + filterWeightYIndexOffset) * srcHeight
2118 			/ (srcHeight * yScale));
2119 		// round down to get the top pixel
2120 		yIndices[i] = index;
2121 		// handle cropped source bitmap
2122 		yIndices[i] += yBitmapShift;
2123 	}
2124 //printf("X: %d ... %d, %d (%ld or %f)\n",
2125 //	xIndices[0], xIndices[dstWidth - 2], xIndices[dstWidth - 1], dstWidth,
2126 //	srcWidth * xScale);
2127 //printf("Y: %d ... %d, %d (%ld or %f)\n",
2128 //	yIndices[0], yIndices[dstHeight - 2], yIndices[dstHeight - 1], dstHeight,
2129 //	srcHeight * yScale);
2130 
2131 	const int32 left = (int32)viewRect.left;
2132 	const int32 top = (int32)viewRect.top;
2133 	const int32 right = (int32)viewRect.right;
2134 	const int32 bottom = (int32)viewRect.bottom;
2135 
2136 	const uint32 dstBPR = fBuffer.stride();
2137 
2138 	// iterate over clipping boxes
2139 	fBaseRenderer.first_clip_box();
2140 	do {
2141 		const int32 x1 = max_c(fBaseRenderer.xmin(), left);
2142 		const int32 x2 = min_c(fBaseRenderer.xmax(), right);
2143 		if (x1 > x2)
2144 			continue;
2145 
2146 		int32 y1 = max_c(fBaseRenderer.ymin(), top);
2147 		int32 y2 = min_c(fBaseRenderer.ymax(), bottom);
2148 		if (y1 > y2)
2149 			continue;
2150 
2151 		// buffer offset into destination
2152 		uint8* dst = fBuffer.row_ptr(y1) + x1 * 4;
2153 
2154 		// x and y are needed as indeces into the wheight arrays, so the
2155 		// offset into the target buffer needs to be compensated
2156 		const int32 xIndexL = x1 - left - filterWeightXIndexOffset;
2157 		const int32 xIndexR = x2 - left - filterWeightXIndexOffset;
2158 		y1 -= top + filterWeightYIndexOffset;
2159 		y2 -= top + filterWeightYIndexOffset;
2160 
2161 //printf("x: %ld - %ld\n", xIndexL, xIndexR);
2162 //printf("y: %ld - %ld\n", y1, y2);
2163 
2164 		for (; y1 <= y2; y1++) {
2165 			// buffer offset into source (top row)
2166 			register const uint8* src = srcBuffer.row_ptr(yIndices[y1]);
2167 			// buffer handle for destination to be incremented per pixel
2168 			register uint32* d = (uint32*)dst;
2169 
2170 			for (int32 x = xIndexL; x <= xIndexR; x++) {
2171 				*d = *(uint32*)(src + xIndices[x]);
2172 				d++;
2173 			}
2174 			dst += dstBPR;
2175 		}
2176 	} while (fBaseRenderer.next_clip_box());
2177 
2178 //printf("draw bitmap %.5fx%.5f: %lld\n", xScale, yScale, system_time() - now);
2179 }
2180 
2181 
2182 // _DrawBitmapBilinearCopy32
2183 void
2184 Painter::_DrawBitmapBilinearCopy32(agg::rendering_buffer& srcBuffer,
2185 	double xOffset, double yOffset, double xScale, double yScale,
2186 	BRect viewRect) const
2187 {
2188 	//bigtime_t now = system_time();
2189 	uint32 dstWidth = viewRect.IntegerWidth() + 1;
2190 	uint32 dstHeight = viewRect.IntegerHeight() + 1;
2191 	uint32 srcWidth = srcBuffer.width();
2192 	uint32 srcHeight = srcBuffer.height();
2193 
2194 	// Do not calculate more filter weights than necessary and also
2195 	// keep the stack based allocations reasonably sized
2196 	if (fClippingRegion->Frame().IntegerWidth() + 1 < (int32)dstWidth)
2197 		dstWidth = fClippingRegion->Frame().IntegerWidth() + 1;
2198 	if (fClippingRegion->Frame().IntegerHeight() + 1 < (int32)dstHeight)
2199 		dstHeight = fClippingRegion->Frame().IntegerHeight() + 1;
2200 
2201 	// When calculating less filter weights than specified by viewRect,
2202 	// we need to compensate the offset.
2203 	uint32 filterWeightXIndexOffset = 0;
2204 	uint32 filterWeightYIndexOffset = 0;
2205 	if (fClippingRegion->Frame().left > viewRect.left) {
2206 		filterWeightXIndexOffset = (int32)(fClippingRegion->Frame().left
2207 			- viewRect.left);
2208 	}
2209 	if (fClippingRegion->Frame().top > viewRect.top) {
2210 		filterWeightYIndexOffset = (int32)(fClippingRegion->Frame().top
2211 			- viewRect.top);
2212 	}
2213 
2214 	struct FilterInfo {
2215 		uint16 index;	// index into source bitmap row/column
2216 		uint16 weight;	// weight of the pixel at index [0..255]
2217 	};
2218 
2219 //#define FILTER_INFOS_ON_HEAP
2220 #ifdef FILTER_INFOS_ON_HEAP
2221 	FilterInfo* xWeights = new (nothrow) FilterInfo[dstWidth];
2222 	FilterInfo* yWeights = new (nothrow) FilterInfo[dstHeight];
2223 	if (xWeights == NULL || yWeights == NULL) {
2224 		delete[] xWeights;
2225 		delete[] yWeights;
2226 		return;
2227 	}
2228 #else
2229 	// stack based saves about 200µs on 1.85 GHz Core 2 Duo
2230 	// should not pose a problem with stack overflows
2231 	// (needs around 12Kb for 1920x1200)
2232 	FilterInfo xWeights[dstWidth];
2233 	FilterInfo yWeights[dstHeight];
2234 #endif
2235 
2236 	// Extract the cropping information for the source bitmap,
2237 	// If only a part of the source bitmap is to be drawn with scale,
2238 	// the offset will be different from the viewRect left top corner.
2239 	int32 xBitmapShift = (int32)(viewRect.left - xOffset);
2240 	int32 yBitmapShift = (int32)(viewRect.top - yOffset);
2241 
2242 	for (uint32 i = 0; i < dstWidth; i++) {
2243 		// fractional index into source
2244 		// NOTE: It is very important to calculate the fractional index
2245 		// into the source pixel grid like this to prevent out of bounds
2246 		// access! It will result in the rightmost pixel of the destination
2247 		// to access the rightmost pixel of the source with a weighting
2248 		// of 255. This in turn will trigger an optimization in the loop
2249 		// that also prevents out of bounds access.
2250 		float index = (i + filterWeightXIndexOffset) * (srcWidth - 1)
2251 			/ (srcWidth * xScale - 1);
2252 		// round down to get the left pixel
2253 		xWeights[i].index = (uint16)index;
2254 		xWeights[i].weight = 255 - (uint16)((index - xWeights[i].index) * 255);
2255 		// handle cropped source bitmap
2256 		xWeights[i].index += xBitmapShift;
2257 		// precompute index for 32 bit pixels
2258 		xWeights[i].index *= 4;
2259 	}
2260 
2261 	for (uint32 i = 0; i < dstHeight; i++) {
2262 		// fractional index into source
2263 		// NOTE: It is very important to calculate the fractional index
2264 		// into the source pixel grid like this to prevent out of bounds
2265 		// access! It will result in the bottommost pixel of the destination
2266 		// to access the bottommost pixel of the source with a weighting
2267 		// of 255. This in turn will trigger an optimization in the loop
2268 		// that also prevents out of bounds access.
2269 		float index = (i + filterWeightYIndexOffset) * (srcHeight - 1)
2270 			/ (srcHeight * yScale - 1);
2271 		// round down to get the top pixel
2272 		yWeights[i].index = (uint16)index;
2273 		yWeights[i].weight = 255 - (uint16)((index - yWeights[i].index) * 255);
2274 		// handle cropped source bitmap
2275 		yWeights[i].index += yBitmapShift;
2276 	}
2277 //printf("X: %d/%d ... %d/%d, %d/%d (%ld)\n",
2278 //	xWeights[0].index, xWeights[0].weight,
2279 //	xWeights[dstWidth - 2].index, xWeights[dstWidth - 2].weight,
2280 //	xWeights[dstWidth - 1].index, xWeights[dstWidth - 1].weight,
2281 //	dstWidth);
2282 //printf("Y: %d/%d ... %d/%d, %d/%d (%ld)\n",
2283 //	yWeights[0].index, yWeights[0].weight,
2284 //	yWeights[dstHeight - 2].index, yWeights[dstHeight - 2].weight,
2285 //	yWeights[dstHeight - 1].index, yWeights[dstHeight - 1].weight,
2286 //	dstHeight);
2287 
2288 	const int32 left = (int32)viewRect.left;
2289 	const int32 top = (int32)viewRect.top;
2290 	const int32 right = (int32)viewRect.right;
2291 	const int32 bottom = (int32)viewRect.bottom;
2292 
2293 	const uint32 dstBPR = fBuffer.stride();
2294 	const uint32 srcBPR = srcBuffer.stride();
2295 
2296 	// Figure out which version of the code we want to use...
2297 	enum {
2298 		kOptimizeForLowFilterRatio = 0,
2299 		kUseDefaultVersion,
2300 		kUseSIMDVersion
2301 	};
2302 
2303 	int codeSelect = kUseDefaultVersion;
2304 
2305 	uint32 neededSIMDFlags = APPSERVER_SIMD_MMX | APPSERVER_SIMD_SSE;
2306 	if ((sSIMDFlags & neededSIMDFlags) == neededSIMDFlags)
2307 		codeSelect = kUseSIMDVersion;
2308 	else {
2309 		if (xScale == yScale && (xScale == 1.5 || xScale == 2.0
2310 			|| xScale == 2.5 || xScale == 3.0)) {
2311 			codeSelect = kOptimizeForLowFilterRatio;
2312 		}
2313 	}
2314 
2315 	// iterate over clipping boxes
2316 	fBaseRenderer.first_clip_box();
2317 	do {
2318 		const int32 x1 = max_c(fBaseRenderer.xmin(), left);
2319 		const int32 x2 = min_c(fBaseRenderer.xmax(), right);
2320 		if (x1 > x2)
2321 			continue;
2322 
2323 		int32 y1 = max_c(fBaseRenderer.ymin(), top);
2324 		int32 y2 = min_c(fBaseRenderer.ymax(), bottom);
2325 		if (y1 > y2)
2326 			continue;
2327 
2328 		// buffer offset into destination
2329 		uint8* dst = fBuffer.row_ptr(y1) + x1 * 4;
2330 
2331 		// x and y are needed as indeces into the wheight arrays, so the
2332 		// offset into the target buffer needs to be compensated
2333 		const int32 xIndexL = x1 - left - filterWeightXIndexOffset;
2334 		const int32 xIndexR = x2 - left - filterWeightXIndexOffset;
2335 		y1 -= top + filterWeightYIndexOffset;
2336 		y2 -= top + filterWeightYIndexOffset;
2337 
2338 //printf("x: %ld - %ld\n", xIndexL, xIndexR);
2339 //printf("y: %ld - %ld\n", y1, y2);
2340 
2341 		switch (codeSelect) {
2342 			case kOptimizeForLowFilterRatio:
2343 			{
2344 				// In this mode, we anticipate to hit many destination pixels
2345 				// that map directly to a source pixel, we have more branches
2346 				// in the inner loop but save time because of the special
2347 				// cases. If there are too few direct hit pixels, the branches
2348 				// only waste time.
2349 				for (; y1 <= y2; y1++) {
2350 					// cache the weight of the top and bottom row
2351 					const uint16 wTop = yWeights[y1].weight;
2352 					const uint16 wBottom = 255 - yWeights[y1].weight;
2353 
2354 					// buffer offset into source (top row)
2355 					register const uint8* src
2356 						= srcBuffer.row_ptr(yWeights[y1].index);
2357 					// buffer handle for destination to be incremented per
2358 					// pixel
2359 					register uint8* d = dst;
2360 
2361 					if (wTop == 255) {
2362 						for (int32 x = xIndexL; x <= xIndexR; x++) {
2363 							const uint8* s = src + xWeights[x].index;
2364 							// This case is important to prevent out
2365 							// of bounds access at bottom edge of the source
2366 							// bitmap. If the scale is low and integer, it will
2367 							// also help the speed.
2368 							if (xWeights[x].weight == 255) {
2369 								// As above, but to prevent out of bounds
2370 								// on the right edge.
2371 								*(uint32*)d = *(uint32*)s;
2372 							} else {
2373 								// Only the left and right pixels are
2374 								// interpolated, since the top row has 100%
2375 								// weight.
2376 								const uint16 wLeft = xWeights[x].weight;
2377 								const uint16 wRight = 255 - wLeft;
2378 								d[0] = (s[0] * wLeft + s[4] * wRight) >> 8;
2379 								d[1] = (s[1] * wLeft + s[5] * wRight) >> 8;
2380 								d[2] = (s[2] * wLeft + s[6] * wRight) >> 8;
2381 							}
2382 							d += 4;
2383 						}
2384 					} else {
2385 						for (int32 x = xIndexL; x <= xIndexR; x++) {
2386 							const uint8* s = src + xWeights[x].index;
2387 							if (xWeights[x].weight == 255) {
2388 								// Prevent out of bounds access on the right
2389 								// edge or simply speed up.
2390 								const uint8* sBottom = s + srcBPR;
2391 								d[0] = (s[0] * wTop + sBottom[0] * wBottom)
2392 									>> 8;
2393 								d[1] = (s[1] * wTop + sBottom[1] * wBottom)
2394 									>> 8;
2395 								d[2] = (s[2] * wTop + sBottom[2] * wBottom)
2396 									>> 8;
2397 							} else {
2398 								// calculate the weighted sum of all four
2399 								// interpolated pixels
2400 								const uint16 wLeft = xWeights[x].weight;
2401 								const uint16 wRight = 255 - wLeft;
2402 								// left and right of top row
2403 								uint32 t0 = (s[0] * wLeft + s[4] * wRight)
2404 									* wTop;
2405 								uint32 t1 = (s[1] * wLeft + s[5] * wRight)
2406 									* wTop;
2407 								uint32 t2 = (s[2] * wLeft + s[6] * wRight)
2408 									* wTop;
2409 
2410 								// left and right of bottom row
2411 								s += srcBPR;
2412 								t0 += (s[0] * wLeft + s[4] * wRight) * wBottom;
2413 								t1 += (s[1] * wLeft + s[5] * wRight) * wBottom;
2414 								t2 += (s[2] * wLeft + s[6] * wRight) * wBottom;
2415 
2416 								d[0] = t0 >> 16;
2417 								d[1] = t1 >> 16;
2418 								d[2] = t2 >> 16;
2419 							}
2420 							d += 4;
2421 						}
2422 					}
2423 					dst += dstBPR;
2424 				}
2425 				break;
2426 			}
2427 
2428 			case kUseDefaultVersion:
2429 			{
2430 				// In this mode we anticipate many pixels wich need filtering,
2431 				// there are no special cases for direct hit pixels except for
2432 				// the last column/row and the right/bottom corner pixel.
2433 
2434 				// The last column/row handling does not need to be performed
2435 				// for all clipping rects!
2436 				int32 yMax = y2;
2437 				if (yWeights[yMax].weight == 255)
2438 					yMax--;
2439 				int32 xIndexMax = xIndexR;
2440 				if (xWeights[xIndexMax].weight == 255)
2441 					xIndexMax--;
2442 
2443 				for (; y1 <= yMax; y1++) {
2444 					// cache the weight of the top and bottom row
2445 					const uint16 wTop = yWeights[y1].weight;
2446 					const uint16 wBottom = 255 - yWeights[y1].weight;
2447 
2448 					// buffer offset into source (top row)
2449 					register const uint8* src
2450 						= srcBuffer.row_ptr(yWeights[y1].index);
2451 					// buffer handle for destination to be incremented per
2452 					// pixel
2453 					register uint8* d = dst;
2454 
2455 					for (int32 x = xIndexL; x <= xIndexMax; x++) {
2456 						const uint8* s = src + xWeights[x].index;
2457 						// calculate the weighted sum of all four
2458 						// interpolated pixels
2459 						const uint16 wLeft = xWeights[x].weight;
2460 						const uint16 wRight = 255 - wLeft;
2461 						// left and right of top row
2462 						uint32 t0 = (s[0] * wLeft + s[4] * wRight) * wTop;
2463 						uint32 t1 = (s[1] * wLeft + s[5] * wRight) * wTop;
2464 						uint32 t2 = (s[2] * wLeft + s[6] * wRight) * wTop;
2465 
2466 						// left and right of bottom row
2467 						s += srcBPR;
2468 						t0 += (s[0] * wLeft + s[4] * wRight) * wBottom;
2469 						t1 += (s[1] * wLeft + s[5] * wRight) * wBottom;
2470 						t2 += (s[2] * wLeft + s[6] * wRight) * wBottom;
2471 						d[0] = t0 >> 16;
2472 						d[1] = t1 >> 16;
2473 						d[2] = t2 >> 16;
2474 						d += 4;
2475 					}
2476 					// last column of pixels if necessary
2477 					if (xIndexMax < xIndexR) {
2478 						const uint8* s = src + xWeights[xIndexR].index;
2479 						const uint8* sBottom = s + srcBPR;
2480 						d[0] = (s[0] * wTop + sBottom[0] * wBottom) >> 8;
2481 						d[1] = (s[1] * wTop + sBottom[1] * wBottom) >> 8;
2482 						d[2] = (s[2] * wTop + sBottom[2] * wBottom) >> 8;
2483 					}
2484 
2485 					dst += dstBPR;
2486 				}
2487 
2488 				// last row of pixels if necessary
2489 				// buffer offset into source (bottom row)
2490 				register const uint8* src
2491 					= srcBuffer.row_ptr(yWeights[y2].index);
2492 				// buffer handle for destination to be incremented per pixel
2493 				register uint8* d = dst;
2494 
2495 				if (yMax < y2) {
2496 					for (int32 x = xIndexL; x <= xIndexMax; x++) {
2497 						const uint8* s = src + xWeights[x].index;
2498 						const uint16 wLeft = xWeights[x].weight;
2499 						const uint16 wRight = 255 - wLeft;
2500 						d[0] = (s[0] * wLeft + s[4] * wRight) >> 8;
2501 						d[1] = (s[1] * wLeft + s[5] * wRight) >> 8;
2502 						d[2] = (s[2] * wLeft + s[6] * wRight) >> 8;
2503 						d += 4;
2504 					}
2505 				}
2506 
2507 				// pixel in bottom right corner if necessary
2508 				if (yMax < y2 && xIndexMax < xIndexR) {
2509 					const uint8* s = src + xWeights[xIndexR].index;
2510 					*(uint32*)d = *(uint32*)s;
2511 				}
2512 				break;
2513 			}
2514 
2515 #ifdef __INTEL__
2516 			case kUseSIMDVersion:
2517 			{
2518 				// Basically the same as the "standard" mode, but we use SIMD
2519 				// routines for the processing of the single display lines.
2520 
2521 				// The last column/row handling does not need to be performed
2522 				// for all clipping rects!
2523 				int32 yMax = y2;
2524 				if (yWeights[yMax].weight == 255)
2525 					yMax--;
2526 				int32 xIndexMax = xIndexR;
2527 				if (xWeights[xIndexMax].weight == 255)
2528 					xIndexMax--;
2529 
2530 				for (; y1 <= yMax; y1++) {
2531 					// cache the weight of the top and bottom row
2532 					const uint16 wTop = yWeights[y1].weight;
2533 					const uint16 wBottom = 255 - yWeights[y1].weight;
2534 
2535 					// buffer offset into source (top row)
2536 					const uint8* src = srcBuffer.row_ptr(yWeights[y1].index);
2537 					// buffer handle for destination to be incremented per
2538 					// pixel
2539 					uint8* d = dst;
2540 					bilinear_scale_xloop_mmxsse(src, dst, xWeights,	xIndexL,
2541 						xIndexMax, wTop, srcBPR);
2542 					// increase pointer by processed pixels
2543 					d += (xIndexMax - xIndexL + 1) * 4;
2544 
2545 					// last column of pixels if necessary
2546 					if (xIndexMax < xIndexR) {
2547 						const uint8* s = src + xWeights[xIndexR].index;
2548 						const uint8* sBottom = s + srcBPR;
2549 						d[0] = (s[0] * wTop + sBottom[0] * wBottom) >> 8;
2550 						d[1] = (s[1] * wTop + sBottom[1] * wBottom) >> 8;
2551 						d[2] = (s[2] * wTop + sBottom[2] * wBottom) >> 8;
2552 					}
2553 
2554 					dst += dstBPR;
2555 				}
2556 
2557 				// last row of pixels if necessary
2558 				// buffer offset into source (bottom row)
2559 				register const uint8* src
2560 					= srcBuffer.row_ptr(yWeights[y2].index);
2561 				// buffer handle for destination to be incremented per pixel
2562 				register uint8* d = dst;
2563 
2564 				if (yMax < y2) {
2565 					for (int32 x = xIndexL; x <= xIndexMax; x++) {
2566 						const uint8* s = src + xWeights[x].index;
2567 						const uint16 wLeft = xWeights[x].weight;
2568 						const uint16 wRight = 255 - wLeft;
2569 						d[0] = (s[0] * wLeft + s[4] * wRight) >> 8;
2570 						d[1] = (s[1] * wLeft + s[5] * wRight) >> 8;
2571 						d[2] = (s[2] * wLeft + s[6] * wRight) >> 8;
2572 						d += 4;
2573 					}
2574 				}
2575 
2576 				// pixel in bottom right corner if necessary
2577 				if (yMax < y2 && xIndexMax < xIndexR) {
2578 					const uint8* s = src + xWeights[xIndexR].index;
2579 					*(uint32*)d = *(uint32*)s;
2580 				}
2581 				break;
2582 			}
2583 #endif	// __INTEL__
2584 		}
2585 	} while (fBaseRenderer.next_clip_box());
2586 
2587 #ifdef FILTER_INFOS_ON_HEAP
2588 	delete[] xWeights;
2589 	delete[] yWeights;
2590 #endif
2591 //printf("draw bitmap %.5fx%.5f: %lld\n", xScale, yScale, system_time() - now);
2592 }
2593 
2594 
2595 // _DrawBitmapGeneric32
2596 void
2597 Painter::_DrawBitmapGeneric32(agg::rendering_buffer& srcBuffer,
2598 	double xOffset, double yOffset, double xScale, double yScale,
2599 	BRect viewRect, uint32 options) const
2600 {
2601 	TRACE("Painter::_DrawBitmapGeneric32()\n");
2602 	TRACE("   offset: %.1f, %.1f\n", xOffset, yOffset);
2603 	TRACE("   scale: %.3f, %.3f\n", xScale, yScale);
2604 	TRACE("   viewRect: (%.1f, %.1f) - (%.1f, %.1f)\n",
2605 		viewRect.left, viewRect.top, viewRect.right, viewRect.bottom);
2606 	// AGG pipeline
2607 
2608 	// pixel format attached to bitmap
2609 	typedef agg::pixfmt_bgra32 pixfmt_image;
2610 	pixfmt_image pixf_img(srcBuffer);
2611 
2612 	agg::trans_affine srcMatrix;
2613 	// NOTE: R5 seems to ignore this offset when drawing bitmaps
2614 	//	srcMatrix *= agg::trans_affine_translation(-actualBitmapRect.left,
2615 	//		-actualBitmapRect.top);
2616 	srcMatrix *= fTransform;
2617 
2618 	agg::trans_affine imgMatrix;
2619 	imgMatrix *= agg::trans_affine_translation(xOffset - viewRect.left,
2620 		yOffset - viewRect.top);
2621 	imgMatrix *= agg::trans_affine_scaling(xScale, yScale);
2622 	imgMatrix *= agg::trans_affine_translation(viewRect.left, viewRect.top);
2623 	imgMatrix *= fTransform;
2624 	imgMatrix.invert();
2625 
2626 	// image interpolator
2627 	typedef agg::span_interpolator_linear<> interpolator_type;
2628 	interpolator_type interpolator(imgMatrix);
2629 
2630 	// scanline allocator
2631 	agg::span_allocator<pixfmt_image::color_type> spanAllocator;
2632 
2633 	// image accessor attached to pixel format of bitmap
2634 	typedef agg::image_accessor_clone<pixfmt_image> source_type;
2635 	source_type source(pixf_img);
2636 
2637 	// clip to the current clipping region's frame
2638 	if (fIdentityTransform)
2639 		viewRect = viewRect & fClippingRegion->Frame();
2640 	// convert to pixel coords (versus pixel indices)
2641 	viewRect.right++;
2642 	viewRect.bottom++;
2643 
2644 	// path enclosing the bitmap
2645 	fPath.remove_all();
2646 	fPath.move_to(viewRect.left, viewRect.top);
2647 	fPath.line_to(viewRect.right, viewRect.top);
2648 	fPath.line_to(viewRect.right, viewRect.bottom);
2649 	fPath.line_to(viewRect.left, viewRect.bottom);
2650 	fPath.close_polygon();
2651 
2652 	agg::conv_transform<agg::path_storage> transformedPath(fPath, srcMatrix);
2653 	fRasterizer.reset();
2654 	fRasterizer.add_path(transformedPath);
2655 
2656 	if ((options & B_FILTER_BITMAP_BILINEAR) != 0) {
2657 		// image filter (bilinear)
2658 		typedef agg::span_image_filter_rgba_bilinear<
2659 			source_type, interpolator_type> span_gen_type;
2660 		span_gen_type spanGenerator(source, interpolator);
2661 
2662 		// render the path with the bitmap as scanline fill
2663 		if (fMaskedUnpackedScanline != NULL) {
2664 			agg::render_scanlines_aa(fRasterizer, *fMaskedUnpackedScanline,
2665 				fBaseRenderer, spanAllocator, spanGenerator);
2666 		} else {
2667 			agg::render_scanlines_aa(fRasterizer, fUnpackedScanline,
2668 				fBaseRenderer, spanAllocator, spanGenerator);
2669 		}
2670 	} else {
2671 		// image filter (nearest neighbor)
2672 		typedef agg::span_image_filter_rgba_nn<
2673 			source_type, interpolator_type> span_gen_type;
2674 		span_gen_type spanGenerator(source, interpolator);
2675 
2676 		// render the path with the bitmap as scanline fill
2677 		if (fMaskedUnpackedScanline != NULL) {
2678 			agg::render_scanlines_aa(fRasterizer, *fMaskedUnpackedScanline,
2679 				fBaseRenderer, spanAllocator, spanGenerator);
2680 		} else {
2681 			agg::render_scanlines_aa(fRasterizer, fUnpackedScanline,
2682 				fBaseRenderer, spanAllocator, spanGenerator);
2683 		}
2684 	}
2685 }
2686 
2687 
2688 // _InvertRect32
2689 void
2690 Painter::_InvertRect32(BRect r) const
2691 {
2692 	int32 width = r.IntegerWidth() + 1;
2693 	for (int32 y = (int32)r.top; y <= (int32)r.bottom; y++) {
2694 		uint8* dst = fBuffer.row_ptr(y);
2695 		dst += (int32)r.left * 4;
2696 		for (int32 i = 0; i < width; i++) {
2697 			dst[0] = 255 - dst[0];
2698 			dst[1] = 255 - dst[1];
2699 			dst[2] = 255 - dst[2];
2700 			dst += 4;
2701 		}
2702 	}
2703 }
2704 
2705 
2706 // _BlendRect32
2707 void
2708 Painter::_BlendRect32(const BRect& r, const rgb_color& c) const
2709 {
2710 	if (!fValidClipping)
2711 		return;
2712 
2713 	uint8* dst = fBuffer.row_ptr(0);
2714 	uint32 bpr = fBuffer.stride();
2715 
2716 	int32 left = (int32)r.left;
2717 	int32 top = (int32)r.top;
2718 	int32 right = (int32)r.right;
2719 	int32 bottom = (int32)r.bottom;
2720 
2721 	// fill rects, iterate over clipping boxes
2722 	fBaseRenderer.first_clip_box();
2723 	do {
2724 		int32 x1 = max_c(fBaseRenderer.xmin(), left);
2725 		int32 x2 = min_c(fBaseRenderer.xmax(), right);
2726 		if (x1 <= x2) {
2727 			int32 y1 = max_c(fBaseRenderer.ymin(), top);
2728 			int32 y2 = min_c(fBaseRenderer.ymax(), bottom);
2729 
2730 			uint8* offset = dst + x1 * 4 + y1 * bpr;
2731 			for (; y1 <= y2; y1++) {
2732 				blend_line32(offset, x2 - x1 + 1, c.red, c.green, c.blue,
2733 					c.alpha);
2734 				offset += bpr;
2735 			}
2736 		}
2737 	} while (fBaseRenderer.next_clip_box());
2738 }
2739 
2740 
2741 // #pragma mark -
2742 
2743 
2744 template<class VertexSource>
2745 BRect
2746 Painter::_BoundingBox(VertexSource& path) const
2747 {
2748 	double left = 0.0;
2749 	double top = 0.0;
2750 	double right = -1.0;
2751 	double bottom = -1.0;
2752 	uint32 pathID[1];
2753 	pathID[0] = 0;
2754 	agg::bounding_rect(path, pathID, 0, 1, &left, &top, &right, &bottom);
2755 	return BRect(left, top, right, bottom);
2756 }
2757 
2758 
2759 // agg_line_cap_mode_for
2760 inline agg::line_cap_e
2761 agg_line_cap_mode_for(cap_mode mode)
2762 {
2763 	switch (mode) {
2764 		case B_BUTT_CAP:
2765 			return agg::butt_cap;
2766 		case B_SQUARE_CAP:
2767 			return agg::square_cap;
2768 		case B_ROUND_CAP:
2769 			return agg::round_cap;
2770 	}
2771 	return agg::butt_cap;
2772 }
2773 
2774 
2775 // agg_line_join_mode_for
2776 inline agg::line_join_e
2777 agg_line_join_mode_for(join_mode mode)
2778 {
2779 	switch (mode) {
2780 		case B_MITER_JOIN:
2781 			return agg::miter_join;
2782 		case B_ROUND_JOIN:
2783 			return agg::round_join;
2784 		case B_BEVEL_JOIN:
2785 		case B_BUTT_JOIN: // ??
2786 		case B_SQUARE_JOIN: // ??
2787 			return agg::bevel_join;
2788 	}
2789 	return agg::miter_join;
2790 }
2791 
2792 
2793 template<class VertexSource>
2794 BRect
2795 Painter::_StrokePath(VertexSource& path) const
2796 {
2797 	return _StrokePath(path, fLineCapMode);
2798 }
2799 
2800 
2801 template<class VertexSource>
2802 BRect
2803 Painter::_StrokePath(VertexSource& path, cap_mode capMode) const
2804 {
2805 	agg::conv_stroke<VertexSource> stroke(path);
2806 	stroke.width(fPenSize);
2807 
2808 	stroke.line_cap(agg_line_cap_mode_for(capMode));
2809 	stroke.line_join(agg_line_join_mode_for(fLineJoinMode));
2810 	stroke.miter_limit(fMiterLimit);
2811 
2812 	if (fIdentityTransform)
2813 		return _RasterizePath(stroke);
2814 
2815 	stroke.approximation_scale(fTransform.scale());
2816 
2817 	agg::conv_transform<agg::conv_stroke<VertexSource> > transformedStroke(
2818 		stroke, fTransform);
2819 	return _RasterizePath(transformedStroke);
2820 }
2821 
2822 
2823 // _FillPath
2824 template<class VertexSource>
2825 BRect
2826 Painter::_FillPath(VertexSource& path) const
2827 {
2828 	if (fIdentityTransform)
2829 		return _RasterizePath(path);
2830 
2831 	agg::conv_transform<VertexSource> transformedPath(path, fTransform);
2832 	return _RasterizePath(transformedPath);
2833 }
2834 
2835 
2836 // _RasterizePath
2837 template<class VertexSource>
2838 BRect
2839 Painter::_RasterizePath(VertexSource& path) const
2840 {
2841 	if (fMaskedUnpackedScanline != NULL) {
2842 		// TODO: we can't do both alpha-masking and subpixel AA.
2843 		fRasterizer.reset();
2844 		fRasterizer.add_path(path);
2845 		agg::render_scanlines(fRasterizer, *fMaskedUnpackedScanline,
2846 			fRenderer);
2847 	} else if (gSubpixelAntialiasing) {
2848 		fSubpixRasterizer.reset();
2849 		fSubpixRasterizer.add_path(path);
2850 		agg::render_scanlines(fSubpixRasterizer,
2851 			fSubpixPackedScanline, fSubpixRenderer);
2852 	} else {
2853 		fRasterizer.reset();
2854 		fRasterizer.add_path(path);
2855 		agg::render_scanlines(fRasterizer, fPackedScanline, fRenderer);
2856 	}
2857 
2858 	return _Clipped(_BoundingBox(path));
2859 }
2860 
2861 
2862 // _FillPath
2863 template<class VertexSource>
2864 BRect
2865 Painter::_FillPath(VertexSource& path, const BGradient& gradient) const
2866 {
2867 	if (fIdentityTransform)
2868 		return _RasterizePath(path, gradient);
2869 
2870 	agg::conv_transform<VertexSource> transformedPath(path, fTransform);
2871 	return _RasterizePath(transformedPath, gradient);
2872 }
2873 
2874 
2875 // _FillPath
2876 template<class VertexSource>
2877 BRect
2878 Painter::_RasterizePath(VertexSource& path, const BGradient& gradient) const
2879 {
2880 	GTRACE("Painter::_RasterizePath\n");
2881 
2882 	agg::trans_affine gradientTransform;
2883 
2884 	switch(gradient.GetType()) {
2885 		case BGradient::TYPE_LINEAR:
2886 		{
2887 			GTRACE(("Painter::_FillPath> type == TYPE_LINEAR\n"));
2888 			const BGradientLinear& linearGradient
2889 				= (const BGradientLinear&) gradient;
2890 			agg::gradient_x gradientFunction;
2891 			_CalcLinearGradientTransform(linearGradient.Start(),
2892 				linearGradient.End(), gradientTransform);
2893 			_RasterizePath(path, gradient, gradientFunction, gradientTransform);
2894 			break;
2895 		}
2896 		case BGradient::TYPE_RADIAL:
2897 		{
2898 			GTRACE(("Painter::_FillPathGradient> type == TYPE_RADIAL\n"));
2899 			const BGradientRadial& radialGradient
2900 				= (const BGradientRadial&) gradient;
2901 			agg::gradient_radial gradientFunction;
2902 			_CalcRadialGradientTransform(radialGradient.Center(),
2903 				gradientTransform);
2904 			_RasterizePath(path, gradient, gradientFunction, gradientTransform,
2905 				radialGradient.Radius());
2906 			break;
2907 		}
2908 		case BGradient::TYPE_RADIAL_FOCUS:
2909 		{
2910 			GTRACE(("Painter::_FillPathGradient> type == TYPE_RADIAL_FOCUS\n"));
2911 			const BGradientRadialFocus& radialGradient
2912 				= (const BGradientRadialFocus&) gradient;
2913 			agg::gradient_radial_focus gradientFunction;
2914 			_CalcRadialGradientTransform(radialGradient.Center(),
2915 				gradientTransform);
2916 			_RasterizePath(path, gradient, gradientFunction, gradientTransform,
2917 				radialGradient.Radius());
2918 			break;
2919 		}
2920 		case BGradient::TYPE_DIAMOND:
2921 		{
2922 			GTRACE(("Painter::_FillPathGradient> type == TYPE_DIAMOND\n"));
2923 			const BGradientDiamond& diamontGradient
2924 				= (const BGradientDiamond&) gradient;
2925 			agg::gradient_diamond gradientFunction;
2926 			_CalcRadialGradientTransform(diamontGradient.Center(),
2927 				gradientTransform);
2928 			_RasterizePath(path, gradient, gradientFunction, gradientTransform);
2929 			break;
2930 		}
2931 		case BGradient::TYPE_CONIC:
2932 		{
2933 			GTRACE(("Painter::_FillPathGradient> type == TYPE_CONIC\n"));
2934 			const BGradientConic& conicGradient
2935 				= (const BGradientConic&) gradient;
2936 			agg::gradient_conic gradientFunction;
2937 			_CalcRadialGradientTransform(conicGradient.Center(),
2938 				gradientTransform);
2939 			_RasterizePath(path, gradient, gradientFunction, gradientTransform);
2940 			break;
2941 		}
2942 
2943 		default:
2944 		case BGradient::TYPE_NONE:
2945 			GTRACE(("Painter::_FillPathGradient> type == TYPE_NONE/unkown\n"));
2946 			break;
2947 	}
2948 
2949 	return _Clipped(_BoundingBox(path));
2950 }
2951 
2952 
2953 void
2954 Painter::_CalcLinearGradientTransform(BPoint startPoint, BPoint endPoint,
2955 	agg::trans_affine& matrix, float gradient_d2) const
2956 {
2957 	float dx = endPoint.x - startPoint.x;
2958 	float dy = endPoint.y - startPoint.y;
2959 
2960 	matrix.reset();
2961 	matrix *= agg::trans_affine_scaling(sqrt(dx * dx + dy * dy) / gradient_d2);
2962 	matrix *= agg::trans_affine_rotation(atan2(dy, dx));
2963 	matrix *= agg::trans_affine_translation(startPoint.x, startPoint.y);
2964 	matrix *= fTransform;
2965 	matrix.invert();
2966 }
2967 
2968 
2969 void
2970 Painter::_CalcRadialGradientTransform(BPoint center,
2971 	agg::trans_affine& matrix, float gradient_d2) const
2972 {
2973 	matrix.reset();
2974 	matrix *= agg::trans_affine_translation(center.x, center.y);
2975 	matrix *= fTransform;
2976 	matrix.invert();
2977 }
2978 
2979 
2980 void
2981 Painter::_MakeGradient(const BGradient& gradient, int32 colorCount,
2982 	uint32* colors, int32 arrayOffset, int32 arraySize) const
2983 {
2984 	BGradient::ColorStop* from = gradient.ColorStopAt(0);
2985 
2986 	if (!from)
2987 		return;
2988 
2989 	// current index into "colors" array
2990 //	int32 index = (int32)floorf(colorCount * from->offset + 0.5)
2991 //		+ arrayOffset;
2992 	int32 index = (int32)floorf(colorCount * from->offset / 255 + 0.5)
2993 		+ arrayOffset;
2994 	if (index > arraySize)
2995 		index = arraySize;
2996 	// Make sure we fill the entire array in case the gradient is outside.
2997 	if (index > 0) {
2998 		uint8* c = (uint8*)&colors[0];
2999 		for (int32 i = 0; i < index; i++) {
3000 			c[0] = from->color.blue;
3001 			c[1] = from->color.green;
3002 			c[2] = from->color.red;
3003 			c[3] = from->color.alpha;
3004 			c += 4;
3005 		}
3006 	}
3007 
3008 	// interpolate "from" to "to"
3009 	int32 stopCount = gradient.CountColorStops();
3010 	for (int32 i = 1; i < stopCount; i++) {
3011 		// find the step with the next offset
3012 		BGradient::ColorStop* to = gradient.ColorStopAtFast(i);
3013 
3014 		// interpolate
3015 //		int32 offset = (int32)floorf((colorCount - 1) * to->offset + 0.5);
3016 		int32 offset = (int32)floorf((colorCount - 1)
3017 			* to->offset / 255 + 0.5);
3018 		if (offset > colorCount - 1)
3019 			offset = colorCount - 1;
3020 		offset += arrayOffset;
3021 		int32 dist = offset - index;
3022 		if (dist >= 0) {
3023 			int32 startIndex = max_c(index, 0);
3024 			int32 stopIndex = min_c(offset, arraySize - 1);
3025 			uint8* c = (uint8*)&colors[startIndex];
3026 			for (int32 i = startIndex; i <= stopIndex; i++) {
3027 				float f = (float)(offset - i) / (float)(dist + 1);
3028 				float t = 1.0 - f;
3029 				c[0] = (uint8)floorf(from->color.blue * f
3030 					+ to->color.blue * t + 0.5);
3031 				c[1] = (uint8)floorf(from->color.green * f
3032 					+ to->color.green * t + 0.5);
3033 				c[2] = (uint8)floorf(from->color.red * f
3034 					+ to->color.red * t + 0.5);
3035 				c[3] = (uint8)floorf(from->color.alpha * f
3036 					+ to->color.alpha * t + 0.5);
3037 				c += 4;
3038 			}
3039 		}
3040 		index = offset + 1;
3041 		// the current "to" will be the "from" in the next interpolation
3042 		from = to;
3043 	}
3044 	//  make sure we fill the entire array
3045 	if (index < arraySize) {
3046 		int32 startIndex = max_c(index, 0);
3047 		uint8* c = (uint8*)&colors[startIndex];
3048 		for (int32 i = startIndex; i < arraySize; i++) {
3049 			c[0] = from->color.blue;
3050 			c[1] = from->color.green;
3051 			c[2] = from->color.red;
3052 			c[3] = from->color.alpha;
3053 			c += 4;
3054 		}
3055 	}
3056 }
3057 
3058 
3059 template<class Array>
3060 void
3061 Painter::_MakeGradient(Array& array, const BGradient& gradient) const
3062 {
3063 	for (int i = 0; i < gradient.CountColorStops() - 1; i++) {
3064 		BGradient::ColorStop* from = gradient.ColorStopAtFast(i);
3065 		BGradient::ColorStop* to = gradient.ColorStopAtFast(i + 1);
3066 		agg::rgba8 fromColor(from->color.red, from->color.green,
3067 							 from->color.blue, from->color.alpha);
3068 		agg::rgba8 toColor(to->color.red, to->color.green,
3069 						   to->color.blue, to->color.alpha);
3070 		GTRACE("Painter::_MakeGradient> fromColor(%d, %d, %d, %d) offset = %f\n",
3071 			   fromColor.r, fromColor.g, fromColor.b, fromColor.a,
3072 			   from->offset);
3073 		GTRACE("Painter::_MakeGradient> toColor(%d, %d, %d %d) offset = %f\n",
3074 			   toColor.r, toColor.g, toColor.b, toColor.a, to->offset);
3075 		float dist = to->offset - from->offset;
3076 		GTRACE("Painter::_MakeGradient> dist = %f\n", dist);
3077 		// TODO: Review this... offset should better be on [0..1]
3078 		if (dist > 0) {
3079 			for (int j = (int)from->offset; j <= (int)to->offset; j++) {
3080 				float f = (float)(to->offset - j) / (float)(dist + 1);
3081 				array[j] = toColor.gradient(fromColor, f);
3082 				GTRACE("Painter::_MakeGradient> array[%d](%d, %d, %d, %d)\n",
3083 					   j, array[j].r, array[j].g, array[j].b, array[j].a);
3084 			}
3085 		}
3086 	}
3087 }
3088 
3089 
3090 template<class VertexSource, typename GradientFunction>
3091 void
3092 Painter::_RasterizePath(VertexSource& path, const BGradient& gradient,
3093 	GradientFunction function, agg::trans_affine& gradientTransform,
3094 	int gradientStop) const
3095 {
3096 	GTRACE("Painter::_RasterizePath\n");
3097 
3098 	typedef agg::span_interpolator_linear<> interpolator_type;
3099 	typedef agg::pod_auto_array<agg::rgba8, 256> color_array_type;
3100 	typedef agg::span_allocator<agg::rgba8> span_allocator_type;
3101 	typedef agg::span_gradient<agg::rgba8, interpolator_type,
3102 				GradientFunction, color_array_type> span_gradient_type;
3103 	typedef agg::renderer_scanline_aa<renderer_base, span_allocator_type,
3104 				span_gradient_type> renderer_gradient_type;
3105 
3106 	interpolator_type spanInterpolator(gradientTransform);
3107 	span_allocator_type spanAllocator;
3108 	color_array_type colorArray;
3109 
3110 	_MakeGradient(colorArray, gradient);
3111 
3112 	span_gradient_type spanGradient(spanInterpolator, function, colorArray,
3113 		0, gradientStop);
3114 
3115 	renderer_gradient_type gradientRenderer(fBaseRenderer, spanAllocator,
3116 		spanGradient);
3117 
3118 	fRasterizer.reset();
3119 	fRasterizer.add_path(path);
3120 	if (fMaskedUnpackedScanline == NULL)
3121 		agg::render_scanlines(fRasterizer, fUnpackedScanline, gradientRenderer);
3122 	else {
3123 		agg::render_scanlines(fRasterizer, *fMaskedUnpackedScanline,
3124 			gradientRenderer);
3125 	}
3126 }
3127 
3128