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