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