1 /* 2 * Copyright 2009, Christian Packmann. 3 * Copyright 2008, Andrej Spielmann <andrej.spielmann@seh.ox.ac.uk>. 4 * Copyright 2005-2009, Stephan Aßmus <superstippi@gmx.de>. 5 * All rights reserved. Distributed under the terms of the MIT License. 6 */ 7 8 9 /*! API to the Anti-Grain Geometry based "Painter" drawing backend. Manages 10 rendering pipe-lines for stroke, fills, bitmap and text rendering. 11 */ 12 13 14 #include "Painter.h" 15 16 #include <new> 17 18 #include <stdio.h> 19 #include <string.h> 20 21 #include <Bitmap.h> 22 #include <GraphicsDefs.h> 23 #include <Region.h> 24 #include <String.h> 25 #include <GradientLinear.h> 26 #include <GradientRadial.h> 27 #include <GradientRadialFocus.h> 28 #include <GradientDiamond.h> 29 #include <GradientConic.h> 30 31 #include <ShapePrivate.h> 32 33 #include <agg_bezier_arc.h> 34 #include <agg_bounding_rect.h> 35 #include <agg_conv_clip_polygon.h> 36 #include <agg_conv_curve.h> 37 #include <agg_conv_stroke.h> 38 #include <agg_ellipse.h> 39 #include <agg_image_accessors.h> 40 #include <agg_path_storage.h> 41 #include <agg_pixfmt_rgba.h> 42 #include <agg_rounded_rect.h> 43 #include <agg_span_allocator.h> 44 #include <agg_span_image_filter_rgba.h> 45 #include <agg_span_interpolator_linear.h> 46 47 #include "drawing_support.h" 48 49 #include "DrawState.h" 50 51 #include <AutoDeleter.h> 52 #include <View.h> 53 54 #include "DrawingMode.h" 55 #include "GlobalSubpixelSettings.h" 56 #include "PatternHandler.h" 57 #include "RenderingBuffer.h" 58 #include "ServerBitmap.h" 59 #include "ServerFont.h" 60 #include "SystemPalette.h" 61 62 #include "AppServer.h" 63 64 using std::nothrow; 65 66 #undef TRACE 67 // #define TRACE_PAINTER 68 #ifdef TRACE_PAINTER 69 # define TRACE(x...) printf(x) 70 #else 71 # define TRACE(x...) 72 #endif 73 74 //#define TRACE_GRADIENTS 75 #ifdef TRACE_GRADIENTS 76 # include <OS.h> 77 # define GTRACE(x...) debug_printf(x) 78 #else 79 # define GTRACE(x...) 80 #endif 81 82 83 #define CHECK_CLIPPING if (!fValidClipping) return BRect(0, 0, -1, -1); 84 #define CHECK_CLIPPING_NO_RETURN if (!fValidClipping) return; 85 86 // Defines for SIMD support. 87 #define APPSERVER_SIMD_MMX (1 << 0) 88 #define APPSERVER_SIMD_SSE (1 << 1) 89 90 // Prototypes for assembler routines 91 extern "C" { 92 void bilinear_scale_xloop_mmxsse(const uint8* src, void* dst, 93 void* xWeights, uint32 xmin, uint32 xmax, uint32 wTop, uint32 srcBPR); 94 } 95 96 static uint32 detect_simd(); 97 98 static uint32 sSIMDFlags = detect_simd(); 99 100 101 /*! Detect SIMD flags for use in AppServer. Checks all CPUs in the system 102 and chooses the minimum supported set of instructions. 103 */ 104 static uint32 105 detect_simd() 106 { 107 #if __INTEL__ 108 // Only scan CPUs for which we are certain the SIMD flags are properly 109 // defined. 110 const char* vendorNames[] = { 111 "GenuineIntel", 112 "AuthenticAMD", 113 "CentaurHauls", // Via CPUs, MMX and SSE support 114 "RiseRiseRise", // should be MMX-only 115 "CyrixInstead", // MMX-only, but custom MMX extensions 116 "GenuineTMx86", // MMX and SSE 117 0 118 }; 119 120 system_info systemInfo; 121 if (get_system_info(&systemInfo) != B_OK) 122 return 0; 123 124 // We start out with all flags set and end up with only those flags 125 // supported across all CPUs found. 126 uint32 systemSIMD = 0xffffffff; 127 128 for (int32 cpu = 0; cpu < systemInfo.cpu_count; cpu++) { 129 cpuid_info cpuInfo; 130 get_cpuid(&cpuInfo, 0, cpu); 131 132 // Get the vendor string and terminate it manually 133 char vendor[13]; 134 memcpy(vendor, cpuInfo.eax_0.vendor_id, 12); 135 vendor[12] = 0; 136 137 bool vendorFound = false; 138 for (uint32 i = 0; vendorNames[i] != 0; i++) { 139 if (strcmp(vendor, vendorNames[i]) == 0) 140 vendorFound = true; 141 } 142 143 uint32 cpuSIMD = 0; 144 uint32 maxStdFunc = cpuInfo.regs.eax; 145 if (vendorFound && maxStdFunc >= 1) { 146 get_cpuid(&cpuInfo, 1, 0); 147 uint32 edx = cpuInfo.regs.edx; 148 if (edx & (1 << 23)) 149 cpuSIMD |= APPSERVER_SIMD_MMX; 150 if (edx & (1 << 25)) 151 cpuSIMD |= APPSERVER_SIMD_SSE; 152 } else { 153 // no flags can be identified 154 cpuSIMD = 0; 155 } 156 systemSIMD &= cpuSIMD; 157 } 158 return systemSIMD; 159 #else // !__INTEL__ 160 return 0; 161 #endif 162 } 163 164 165 // #pragma mark - 166 167 168 Painter::Painter() 169 : 170 fBuffer(), 171 fPixelFormat(fBuffer, &fPatternHandler), 172 fBaseRenderer(fPixelFormat), 173 fUnpackedScanline(), 174 fPackedScanline(), 175 fSubpixPackedScanline(), 176 fSubpixUnpackedScanline(), 177 fSubpixRasterizer(), 178 fRasterizer(), 179 fSubpixRenderer(fBaseRenderer), 180 fRenderer(fBaseRenderer), 181 fRendererBin(fBaseRenderer), 182 183 fPath(), 184 fCurve(fPath), 185 186 fSubpixelPrecise(false), 187 fValidClipping(false), 188 fDrawingText(false), 189 fAttached(false), 190 191 fPenSize(1.0), 192 fClippingRegion(NULL), 193 fDrawingMode(B_OP_COPY), 194 fAlphaSrcMode(B_PIXEL_ALPHA), 195 fAlphaFncMode(B_ALPHA_OVERLAY), 196 fLineCapMode(B_BUTT_CAP), 197 fLineJoinMode(B_MITER_JOIN), 198 fMiterLimit(B_DEFAULT_MITER_LIMIT), 199 200 fPatternHandler(), 201 fTextRenderer(fSubpixRenderer, fRenderer, fRendererBin, fUnpackedScanline, 202 fSubpixUnpackedScanline, fSubpixRasterizer) 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* data, int32 xOffset, int32 yOffset) 274 { 275 // NOTE: The custom clipping in "data" 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 SetPenSize(data->PenSize()); 285 286 SetFont(data); 287 288 fSubpixelPrecise = data->SubPixelPrecise(); 289 290 // any of these conditions means we need to use a different drawing 291 // mode instance 292 bool updateDrawingMode 293 = !(data->GetPattern() == fPatternHandler.GetPattern()) 294 || data->GetDrawingMode() != fDrawingMode 295 || (data->GetDrawingMode() == B_OP_ALPHA 296 && (data->AlphaSrcMode() != fAlphaSrcMode 297 || data->AlphaFncMode() != fAlphaFncMode)); 298 299 fDrawingMode = data->GetDrawingMode(); 300 fAlphaSrcMode = data->AlphaSrcMode(); 301 fAlphaFncMode = data->AlphaFncMode(); 302 fPatternHandler.SetPattern(data->GetPattern()); 303 fPatternHandler.SetOffsets(xOffset, yOffset); 304 fLineCapMode = data->LineCapMode(); 305 fLineJoinMode = data->LineJoinMode(); 306 fMiterLimit = data->MiterLimit(); 307 308 // adopt the color *after* the pattern is set 309 // to set the renderers to the correct color 310 SetHighColor(data->HighColor()); 311 SetLowColor(data->LowColor()); 312 313 if (updateDrawingMode || fPixelFormat.UsesOpCopyForText()) 314 _UpdateDrawingMode(); 315 } 316 317 318 // #pragma mark - state 319 320 321 // ConstrainClipping 322 void 323 Painter::ConstrainClipping(const BRegion* region) 324 { 325 fClippingRegion = region; 326 fBaseRenderer.set_clipping_region(const_cast<BRegion*>(region)); 327 fValidClipping = region->Frame().IsValid() && fAttached; 328 329 if (fValidClipping) { 330 clipping_rect cb = fClippingRegion->FrameInt(); 331 fRasterizer.clip_box(cb.left, cb.top, cb.right + 1, cb.bottom + 1); 332 fSubpixRasterizer.clip_box(cb.left, cb.top, cb.right + 1, cb.bottom + 1); 333 } 334 } 335 336 337 // SetHighColor 338 void 339 Painter::SetHighColor(const rgb_color& color) 340 { 341 if (fPatternHandler.HighColor() == color) 342 return; 343 fPatternHandler.SetHighColor(color); 344 if (*(fPatternHandler.GetR5Pattern()) == B_SOLID_HIGH) 345 _SetRendererColor(color); 346 } 347 348 349 // SetLowColor 350 void 351 Painter::SetLowColor(const rgb_color& color) 352 { 353 fPatternHandler.SetLowColor(color); 354 if (*(fPatternHandler.GetR5Pattern()) == B_SOLID_LOW) 355 _SetRendererColor(color); 356 } 357 358 359 // SetDrawingMode 360 void 361 Painter::SetDrawingMode(drawing_mode mode) 362 { 363 if (fDrawingMode != mode) { 364 fDrawingMode = mode; 365 _UpdateDrawingMode(); 366 } 367 } 368 369 370 // SetBlendingMode 371 void 372 Painter::SetBlendingMode(source_alpha srcAlpha, alpha_function alphaFunc) 373 { 374 if (fAlphaSrcMode != srcAlpha || fAlphaFncMode != alphaFunc) { 375 fAlphaSrcMode = srcAlpha; 376 fAlphaFncMode = alphaFunc; 377 if (fDrawingMode == B_OP_ALPHA) 378 _UpdateDrawingMode(); 379 } 380 } 381 382 383 // SetPenSize 384 void 385 Painter::SetPenSize(float size) 386 { 387 fPenSize = size; 388 } 389 390 391 // SetStrokeMode 392 void 393 Painter::SetStrokeMode(cap_mode lineCap, join_mode joinMode, float miterLimit) 394 { 395 fLineCapMode = lineCap; 396 fLineJoinMode = joinMode; 397 fMiterLimit = miterLimit; 398 } 399 400 401 // SetPattern 402 void 403 Painter::SetPattern(const pattern& p, bool drawingText) 404 { 405 if (!(p == *fPatternHandler.GetR5Pattern()) || drawingText != fDrawingText) { 406 fPatternHandler.SetPattern(p); 407 fDrawingText = drawingText; 408 _UpdateDrawingMode(fDrawingText); 409 410 // update renderer color if necessary 411 if (fPatternHandler.IsSolidHigh()) { 412 // pattern was not solid high before 413 _SetRendererColor(fPatternHandler.HighColor()); 414 } else if (fPatternHandler.IsSolidLow()) { 415 // pattern was not solid low before 416 _SetRendererColor(fPatternHandler.LowColor()); 417 } 418 } 419 } 420 421 422 // SetFont 423 void 424 Painter::SetFont(const ServerFont& font) 425 { 426 fTextRenderer.SetFont(font); 427 fTextRenderer.SetAntialiasing(!(font.Flags() & B_DISABLE_ANTIALIASING)); 428 } 429 430 431 // SetFont 432 void 433 Painter::SetFont(const DrawState* state) 434 { 435 fTextRenderer.SetFont(state->Font()); 436 fTextRenderer.SetAntialiasing(!state->ForceFontAliasing() 437 && (state->Font().Flags() & B_DISABLE_ANTIALIASING) == 0); 438 } 439 440 441 // #pragma mark - drawing 442 443 444 // StrokeLine 445 void 446 Painter::StrokeLine(BPoint a, BPoint b) 447 { 448 CHECK_CLIPPING_NO_RETURN 449 450 // "false" means not to do the pixel center offset, 451 // because it would mess up our optimized versions 452 _Transform(&a, false); 453 _Transform(&b, false); 454 455 // first, try an optimized version 456 if (fPenSize == 1.0 457 && (fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER)) { 458 pattern pat = *fPatternHandler.GetR5Pattern(); 459 if (pat == B_SOLID_HIGH 460 && StraightLine(a, b, fPatternHandler.HighColor())) { 461 return; 462 } else if (pat == B_SOLID_LOW 463 && StraightLine(a, b, fPatternHandler.LowColor())) { 464 return; 465 } 466 } 467 468 fPath.remove_all(); 469 470 if (a == b) { 471 // special case dots 472 if (fPenSize == 1.0 && !fSubpixelPrecise) { 473 if (fClippingRegion->Contains(a)) { 474 fPixelFormat.blend_pixel((int)a.x, (int)a.y, fRenderer.color(), 475 255); 476 } 477 } else { 478 fPath.move_to(a.x, a.y); 479 fPath.line_to(a.x + 1, a.y); 480 fPath.line_to(a.x + 1, a.y + 1); 481 fPath.line_to(a.x, a.y + 1); 482 483 _FillPath(fPath); 484 } 485 } else { 486 // do the pixel center offset here 487 // tweak ends to "include" the pixel at the index, 488 // we need to do this in order to produce results like R5, 489 // where coordinates were inclusive 490 if (!fSubpixelPrecise) { 491 bool centerOnLine = fmodf(fPenSize, 2.0) != 0.0; 492 if (a.x == b.x) { 493 // shift to pixel center vertically 494 if (centerOnLine) { 495 a.x += 0.5; 496 b.x += 0.5; 497 } 498 // extend on bottom end 499 if (a.y < b.y) 500 b.y++; 501 else 502 a.y++; 503 } else if (a.y == b.y) { 504 if (centerOnLine) { 505 // shift to pixel center horizontally 506 a.y += 0.5; 507 b.y += 0.5; 508 } 509 // extend on right end 510 if (a.x < b.x) 511 b.x++; 512 else 513 a.x++; 514 } else { 515 // do this regardless of pensize 516 if (a.x < b.x) 517 b.x++; 518 else 519 a.x++; 520 if (a.y < b.y) 521 b.y++; 522 else 523 a.y++; 524 } 525 } 526 527 fPath.move_to(a.x, a.y); 528 fPath.line_to(b.x, b.y); 529 530 _StrokePath(fPath); 531 } 532 } 533 534 535 // StraightLine 536 bool 537 Painter::StraightLine(BPoint a, BPoint b, const rgb_color& c) const 538 { 539 if (!fValidClipping) 540 return false; 541 542 if (a.x == b.x) { 543 // vertical 544 uint8* dst = fBuffer.row_ptr(0); 545 uint32 bpr = fBuffer.stride(); 546 int32 x = (int32)a.x; 547 dst += x * 4; 548 int32 y1 = (int32)min_c(a.y, b.y); 549 int32 y2 = (int32)max_c(a.y, b.y); 550 pixel32 color; 551 color.data8[0] = c.blue; 552 color.data8[1] = c.green; 553 color.data8[2] = c.red; 554 color.data8[3] = 255; 555 // draw a line, iterate over clipping boxes 556 fBaseRenderer.first_clip_box(); 557 do { 558 if (fBaseRenderer.xmin() <= x && 559 fBaseRenderer.xmax() >= x) { 560 int32 i = max_c(fBaseRenderer.ymin(), y1); 561 int32 end = min_c(fBaseRenderer.ymax(), y2); 562 uint8* handle = dst + i * bpr; 563 for (; i <= end; i++) { 564 *(uint32*)handle = color.data32; 565 handle += bpr; 566 } 567 } 568 } while (fBaseRenderer.next_clip_box()); 569 570 return true; 571 } 572 573 if (a.y == b.y) { 574 // horizontal 575 int32 y = (int32)a.y; 576 if (y < 0 || y >= (int32)fBuffer.height()) 577 return true; 578 579 uint8* dst = fBuffer.row_ptr(y); 580 int32 x1 = (int32)min_c(a.x, b.x); 581 int32 x2 = (int32)max_c(a.x, b.x); 582 pixel32 color; 583 color.data8[0] = c.blue; 584 color.data8[1] = c.green; 585 color.data8[2] = c.red; 586 color.data8[3] = 255; 587 // draw a line, iterate over clipping boxes 588 fBaseRenderer.first_clip_box(); 589 do { 590 if (fBaseRenderer.ymin() <= y && 591 fBaseRenderer.ymax() >= y) { 592 int32 i = max_c(fBaseRenderer.xmin(), x1); 593 int32 end = min_c(fBaseRenderer.xmax(), x2); 594 uint32* handle = (uint32*)(dst + i * 4); 595 for (; i <= end; i++) { 596 *handle++ = color.data32; 597 } 598 } 599 } while (fBaseRenderer.next_clip_box()); 600 601 return true; 602 } 603 return false; 604 } 605 606 607 // #pragma mark - 608 609 610 // StrokeTriangle 611 BRect 612 Painter::StrokeTriangle(BPoint pt1, BPoint pt2, BPoint pt3) const 613 { 614 return _DrawTriangle(pt1, pt2, pt3, false); 615 } 616 617 618 // FillTriangle 619 BRect 620 Painter::FillTriangle(BPoint pt1, BPoint pt2, BPoint pt3) const 621 { 622 return _DrawTriangle(pt1, pt2, pt3, true); 623 } 624 625 626 // FillTriangle 627 BRect 628 Painter::FillTriangle(BPoint pt1, BPoint pt2, BPoint pt3, 629 const BGradient& gradient) const 630 { 631 CHECK_CLIPPING 632 633 _Transform(&pt1); 634 _Transform(&pt2); 635 _Transform(&pt3); 636 637 fPath.remove_all(); 638 639 fPath.move_to(pt1.x, pt1.y); 640 fPath.line_to(pt2.x, pt2.y); 641 fPath.line_to(pt3.x, pt3.y); 642 643 fPath.close_polygon(); 644 645 return _FillPath(fPath, gradient); 646 } 647 648 649 // DrawPolygon 650 BRect 651 Painter::DrawPolygon(BPoint* p, int32 numPts, bool filled, bool closed) const 652 { 653 CHECK_CLIPPING 654 655 if (numPts > 0) { 656 fPath.remove_all(); 657 658 _Transform(p); 659 fPath.move_to(p->x, p->y); 660 661 for (int32 i = 1; i < numPts; i++) { 662 p++; 663 _Transform(p); 664 fPath.line_to(p->x, p->y); 665 } 666 667 if (closed) 668 fPath.close_polygon(); 669 670 if (filled) 671 return _FillPath(fPath); 672 673 return _StrokePath(fPath); 674 } 675 return BRect(0.0, 0.0, -1.0, -1.0); 676 } 677 678 679 // FillPolygon 680 BRect 681 Painter::FillPolygon(BPoint* p, int32 numPts, const BGradient& gradient, 682 bool closed) const 683 { 684 CHECK_CLIPPING 685 686 if (numPts > 0) { 687 fPath.remove_all(); 688 689 _Transform(p); 690 fPath.move_to(p->x, p->y); 691 692 for (int32 i = 1; i < numPts; i++) { 693 p++; 694 _Transform(p); 695 fPath.line_to(p->x, p->y); 696 } 697 698 if (closed) 699 fPath.close_polygon(); 700 701 return _FillPath(fPath, gradient); 702 } 703 return BRect(0.0, 0.0, -1.0, -1.0); 704 } 705 706 707 // DrawBezier 708 BRect 709 Painter::DrawBezier(BPoint* p, bool filled) const 710 { 711 CHECK_CLIPPING 712 713 fPath.remove_all(); 714 715 _Transform(&(p[0])); 716 _Transform(&(p[1])); 717 _Transform(&(p[2])); 718 _Transform(&(p[3])); 719 720 fPath.move_to(p[0].x, p[0].y); 721 fPath.curve4(p[1].x, p[1].y, p[2].x, p[2].y, p[3].x, p[3].y); 722 723 if (filled) { 724 fPath.close_polygon(); 725 return _FillPath(fCurve); 726 } 727 728 return _StrokePath(fCurve); 729 } 730 731 732 // FillBezier 733 BRect 734 Painter::FillBezier(BPoint* p, const BGradient& gradient) const 735 { 736 CHECK_CLIPPING 737 738 fPath.remove_all(); 739 740 _Transform(&(p[0])); 741 _Transform(&(p[1])); 742 _Transform(&(p[2])); 743 _Transform(&(p[3])); 744 745 fPath.move_to(p[0].x, p[0].y); 746 fPath.curve4(p[1].x, p[1].y, p[2].x, p[2].y, p[3].x, p[3].y); 747 748 fPath.close_polygon(); 749 return _FillPath(fCurve, gradient); 750 } 751 752 753 static void 754 iterate_shape_data(agg::path_storage& path, 755 const int32& opCount, const uint32* opList, 756 const int32& ptCount, const BPoint* points, 757 const BPoint& viewToScreenOffset, float viewScale) 758 { 759 // TODO: if shapes are ever used more heavily in Haiku, 760 // it would be nice to use BShape data directly (write 761 // an AGG "VertexSource" adaptor) 762 path.remove_all(); 763 for (int32 i = 0; i < opCount; i++) { 764 uint32 op = opList[i] & 0xFF000000; 765 if (op & OP_MOVETO) { 766 path.move_to( 767 points->x * viewScale + viewToScreenOffset.x, 768 points->y * viewScale + viewToScreenOffset.y); 769 points++; 770 } 771 772 if (op & OP_LINETO) { 773 int32 count = opList[i] & 0x00FFFFFF; 774 while (count--) { 775 path.line_to( 776 points->x * viewScale + viewToScreenOffset.x, 777 points->y * viewScale + viewToScreenOffset.y); 778 points++; 779 } 780 } 781 782 if (op & OP_BEZIERTO) { 783 int32 count = opList[i] & 0x00FFFFFF; 784 while (count) { 785 path.curve4( 786 points[0].x * viewScale + viewToScreenOffset.x, 787 points[0].y * viewScale + viewToScreenOffset.y, 788 points[1].x * viewScale + viewToScreenOffset.x, 789 points[1].y * viewScale + viewToScreenOffset.y, 790 points[2].x * viewScale + viewToScreenOffset.x, 791 points[2].y * viewScale + viewToScreenOffset.y); 792 points += 3; 793 count -= 3; 794 } 795 } 796 797 if ((op & OP_LARGE_ARC_TO_CW) || (op & OP_LARGE_ARC_TO_CCW) 798 || (op & OP_SMALL_ARC_TO_CW) || (op & OP_SMALL_ARC_TO_CCW)) { 799 int32 count = opList[i] & 0x00FFFFFF; 800 while (count) { 801 path.arc_to( 802 points[0].x * viewScale, 803 points[0].y * viewScale, 804 points[1].x, 805 op & (OP_LARGE_ARC_TO_CW | OP_LARGE_ARC_TO_CCW), 806 op & (OP_SMALL_ARC_TO_CW | OP_LARGE_ARC_TO_CW), 807 points[2].x * viewScale + viewToScreenOffset.x, 808 points[2].y * viewScale + viewToScreenOffset.y); 809 points += 3; 810 count -= 3; 811 } 812 } 813 814 if (op & OP_CLOSE) 815 path.close_polygon(); 816 } 817 } 818 819 820 // DrawShape 821 BRect 822 Painter::DrawShape(const int32& opCount, const uint32* opList, 823 const int32& ptCount, const BPoint* points, bool filled, 824 const BPoint& viewToScreenOffset, float viewScale) const 825 { 826 CHECK_CLIPPING 827 828 iterate_shape_data(fPath, opCount, opList, ptCount, points, 829 viewToScreenOffset, viewScale); 830 831 if (filled) 832 return _FillPath(fCurve); 833 834 return _StrokePath(fCurve); 835 } 836 837 838 // FillShape 839 BRect 840 Painter::FillShape(const int32& opCount, const uint32* opList, 841 const int32& ptCount, const BPoint* points, const BGradient& gradient, 842 const BPoint& viewToScreenOffset, float viewScale) const 843 { 844 CHECK_CLIPPING 845 846 iterate_shape_data(fPath, opCount, opList, ptCount, points, 847 viewToScreenOffset, viewScale); 848 849 return _FillPath(fCurve, gradient); 850 } 851 852 853 // StrokeRect 854 BRect 855 Painter::StrokeRect(const BRect& r) const 856 { 857 CHECK_CLIPPING 858 859 BPoint a(r.left, r.top); 860 BPoint b(r.right, r.bottom); 861 _Transform(&a, false); 862 _Transform(&b, false); 863 864 // first, try an optimized version 865 if (fPenSize == 1.0 && 866 (fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER)) { 867 pattern p = *fPatternHandler.GetR5Pattern(); 868 if (p == B_SOLID_HIGH) { 869 BRect rect(a, b); 870 StrokeRect(rect, fPatternHandler.HighColor()); 871 return _Clipped(rect); 872 } else if (p == B_SOLID_LOW) { 873 BRect rect(a, b); 874 StrokeRect(rect, fPatternHandler.LowColor()); 875 return _Clipped(rect); 876 } 877 } 878 879 if (fmodf(fPenSize, 2.0) != 0.0) { 880 // shift coords to center of pixels 881 a.x += 0.5; 882 a.y += 0.5; 883 b.x += 0.5; 884 b.y += 0.5; 885 } 886 887 fPath.remove_all(); 888 fPath.move_to(a.x, a.y); 889 if (a.x == b.x || a.y == b.y) { 890 // special case rects with one pixel height or width 891 fPath.line_to(b.x, b.y); 892 } else { 893 fPath.line_to(b.x, a.y); 894 fPath.line_to(b.x, b.y); 895 fPath.line_to(a.x, b.y); 896 } 897 fPath.close_polygon(); 898 899 return _StrokePath(fPath); 900 } 901 902 903 // StrokeRect 904 void 905 Painter::StrokeRect(const BRect& r, const rgb_color& c) const 906 { 907 StraightLine(BPoint(r.left, r.top), BPoint(r.right - 1, r.top), c); 908 StraightLine(BPoint(r.right, r.top), BPoint(r.right, r.bottom - 1), c); 909 StraightLine(BPoint(r.right, r.bottom), BPoint(r.left + 1, r.bottom), c); 910 StraightLine(BPoint(r.left, r.bottom), BPoint(r.left, r.top + 1), c); 911 } 912 913 914 // FillRect 915 BRect 916 Painter::FillRect(const BRect& r) const 917 { 918 CHECK_CLIPPING 919 920 // support invalid rects 921 BPoint a(min_c(r.left, r.right), min_c(r.top, r.bottom)); 922 BPoint b(max_c(r.left, r.right), max_c(r.top, r.bottom)); 923 _Transform(&a, false); 924 _Transform(&b, false); 925 926 // first, try an optimized version 927 if (fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER) { 928 pattern p = *fPatternHandler.GetR5Pattern(); 929 if (p == B_SOLID_HIGH) { 930 BRect rect(a, b); 931 FillRect(rect, fPatternHandler.HighColor()); 932 return _Clipped(rect); 933 } else if (p == B_SOLID_LOW) { 934 BRect rect(a, b); 935 FillRect(rect, fPatternHandler.LowColor()); 936 return _Clipped(rect); 937 } 938 } 939 if (fDrawingMode == B_OP_ALPHA && fAlphaFncMode == B_ALPHA_OVERLAY) { 940 pattern p = *fPatternHandler.GetR5Pattern(); 941 if (p == B_SOLID_HIGH) { 942 BRect rect(a, b); 943 _BlendRect32(rect, fPatternHandler.HighColor()); 944 return _Clipped(rect); 945 } else if (p == B_SOLID_LOW) { 946 rgb_color c = fPatternHandler.LowColor(); 947 if (fAlphaSrcMode == B_CONSTANT_ALPHA) 948 c.alpha = fPatternHandler.HighColor().alpha; 949 BRect rect(a, b); 950 _BlendRect32(rect, c); 951 return _Clipped(rect); 952 } 953 } 954 955 // account for stricter interpretation of coordinates in AGG 956 // the rectangle ranges from the top-left (.0, .0) 957 // to the bottom-right (.9999, .9999) corner of pixels 958 b.x += 1.0; 959 b.y += 1.0; 960 961 fPath.remove_all(); 962 fPath.move_to(a.x, a.y); 963 fPath.line_to(b.x, a.y); 964 fPath.line_to(b.x, b.y); 965 fPath.line_to(a.x, b.y); 966 fPath.close_polygon(); 967 968 return _FillPath(fPath); 969 } 970 971 972 // FillRect 973 BRect 974 Painter::FillRect(const BRect& r, const BGradient& gradient) const 975 { 976 CHECK_CLIPPING 977 978 // support invalid rects 979 BPoint a(min_c(r.left, r.right), min_c(r.top, r.bottom)); 980 BPoint b(max_c(r.left, r.right), max_c(r.top, r.bottom)); 981 _Transform(&a, false); 982 _Transform(&b, false); 983 984 // first, try an optimized version 985 if (gradient.GetType() == BGradient::TYPE_LINEAR 986 && (fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER)) { 987 const BGradientLinear* linearGradient 988 = dynamic_cast<const BGradientLinear*>(&gradient); 989 if (linearGradient->Start().x == linearGradient->End().x 990 // TODO: Remove this second check once the optimized method 991 // handled "upside down" gradients as well... 992 && linearGradient->Start().y <= linearGradient->End().y) { 993 // a vertical gradient 994 BRect rect(a, b); 995 FillRectVerticalGradient(rect, *linearGradient); 996 return _Clipped(rect); 997 } 998 } 999 1000 // account for stricter interpretation of coordinates in AGG 1001 // the rectangle ranges from the top-left (.0, .0) 1002 // to the bottom-right (.9999, .9999) corner of pixels 1003 b.x += 1.0; 1004 b.y += 1.0; 1005 1006 fPath.remove_all(); 1007 fPath.move_to(a.x, a.y); 1008 fPath.line_to(b.x, a.y); 1009 fPath.line_to(b.x, b.y); 1010 fPath.line_to(a.x, b.y); 1011 fPath.close_polygon(); 1012 1013 return _FillPath(fPath, gradient); 1014 } 1015 1016 1017 // FillRect 1018 void 1019 Painter::FillRect(const BRect& r, const rgb_color& c) const 1020 { 1021 if (!fValidClipping) 1022 return; 1023 1024 uint8* dst = fBuffer.row_ptr(0); 1025 uint32 bpr = fBuffer.stride(); 1026 int32 left = (int32)r.left; 1027 int32 top = (int32)r.top; 1028 int32 right = (int32)r.right; 1029 int32 bottom = (int32)r.bottom; 1030 // get a 32 bit pixel ready with the color 1031 pixel32 color; 1032 color.data8[0] = c.blue; 1033 color.data8[1] = c.green; 1034 color.data8[2] = c.red; 1035 color.data8[3] = c.alpha; 1036 // fill rects, iterate over clipping boxes 1037 fBaseRenderer.first_clip_box(); 1038 do { 1039 int32 x1 = max_c(fBaseRenderer.xmin(), left); 1040 int32 x2 = min_c(fBaseRenderer.xmax(), right); 1041 if (x1 <= x2) { 1042 int32 y1 = max_c(fBaseRenderer.ymin(), top); 1043 int32 y2 = min_c(fBaseRenderer.ymax(), bottom); 1044 uint8* offset = dst + x1 * 4; 1045 for (; y1 <= y2; y1++) { 1046 // uint32* handle = (uint32*)(offset + y1 * bpr); 1047 // for (int32 x = x1; x <= x2; x++) { 1048 // *handle++ = color.data32; 1049 // } 1050 gfxset32(offset + y1 * bpr, color.data32, (x2 - x1 + 1) * 4); 1051 } 1052 } 1053 } while (fBaseRenderer.next_clip_box()); 1054 } 1055 1056 1057 // FillRectVerticalGradient 1058 void 1059 Painter::FillRectVerticalGradient(BRect r, const BGradientLinear& gradient) const 1060 { 1061 if (!fValidClipping) 1062 return; 1063 1064 // Make sure the color array is no larger than the screen height. 1065 r = r & fClippingRegion->Frame(); 1066 1067 int32 gradientArraySize = r.IntegerHeight() + 1; 1068 uint32 gradientArray[gradientArraySize]; 1069 int32 gradientTop = (int32)gradient.Start().y; 1070 int32 gradientBottom = (int32)gradient.End().y; 1071 int32 colorCount = gradientBottom - gradientTop + 1; 1072 if (colorCount < 0) { 1073 // Gradient is upside down. That's currently not supported by this 1074 // method. 1075 return; 1076 } 1077 1078 _MakeGradient(gradient, colorCount, gradientArray, 1079 gradientTop - (int32)r.top, gradientArraySize); 1080 1081 uint8* dst = fBuffer.row_ptr(0); 1082 uint32 bpr = fBuffer.stride(); 1083 int32 left = (int32)r.left; 1084 int32 top = (int32)r.top; 1085 int32 right = (int32)r.right; 1086 int32 bottom = (int32)r.bottom; 1087 // fill rects, iterate over clipping boxes 1088 fBaseRenderer.first_clip_box(); 1089 do { 1090 int32 x1 = max_c(fBaseRenderer.xmin(), left); 1091 int32 x2 = min_c(fBaseRenderer.xmax(), right); 1092 if (x1 <= x2) { 1093 int32 y1 = max_c(fBaseRenderer.ymin(), top); 1094 int32 y2 = min_c(fBaseRenderer.ymax(), bottom); 1095 uint8* offset = dst + x1 * 4; 1096 for (; y1 <= y2; y1++) { 1097 // uint32* handle = (uint32*)(offset + y1 * bpr); 1098 // for (int32 x = x1; x <= x2; x++) { 1099 // *handle++ = gradientArray[y1 - top]; 1100 // } 1101 gfxset32(offset + y1 * bpr, gradientArray[y1 - top], 1102 (x2 - x1 + 1) * 4); 1103 } 1104 } 1105 } while (fBaseRenderer.next_clip_box()); 1106 } 1107 1108 1109 // FillRectNoClipping 1110 void 1111 Painter::FillRectNoClipping(const clipping_rect& r, const rgb_color& c) const 1112 { 1113 int32 y = (int32)r.top; 1114 1115 uint8* dst = fBuffer.row_ptr(y) + r.left * 4; 1116 uint32 bpr = fBuffer.stride(); 1117 int32 bytes = (r.right - r.left + 1) * 4; 1118 1119 // get a 32 bit pixel ready with the color 1120 pixel32 color; 1121 color.data8[0] = c.blue; 1122 color.data8[1] = c.green; 1123 color.data8[2] = c.red; 1124 color.data8[3] = c.alpha; 1125 1126 for (; y <= r.bottom; y++) { 1127 // uint32* handle = (uint32*)dst; 1128 // for (int32 x = left; x <= right; x++) { 1129 // *handle++ = color.data32; 1130 // } 1131 gfxset32(dst, color.data32, bytes); 1132 dst += bpr; 1133 } 1134 } 1135 1136 1137 // StrokeRoundRect 1138 BRect 1139 Painter::StrokeRoundRect(const BRect& r, float xRadius, float yRadius) const 1140 { 1141 CHECK_CLIPPING 1142 1143 BPoint lt(r.left, r.top); 1144 BPoint rb(r.right, r.bottom); 1145 bool centerOffset = fPenSize == 1.0; 1146 // TODO: use this when using _StrokePath() 1147 // bool centerOffset = fmodf(fPenSize, 2.0) != 0.0; 1148 _Transform(<, centerOffset); 1149 _Transform(&rb, centerOffset); 1150 1151 if (fPenSize == 1.0) { 1152 agg::rounded_rect rect; 1153 rect.rect(lt.x, lt.y, rb.x, rb.y); 1154 rect.radius(xRadius, yRadius); 1155 1156 return _StrokePath(rect); 1157 } 1158 1159 // NOTE: This implementation might seem a little strange, but it makes 1160 // stroked round rects look like on R5. A more correct way would be to 1161 // use _StrokePath() as above (independent from fPenSize). 1162 // The fact that the bounding box of the round rect is not enlarged 1163 // by fPenSize/2 is actually on purpose, though one could argue it is 1164 // unexpected. 1165 1166 // enclose the right and bottom edge 1167 rb.x++; 1168 rb.y++; 1169 1170 agg::rounded_rect outer; 1171 outer.rect(lt.x, lt.y, rb.x, rb.y); 1172 outer.radius(xRadius, yRadius); 1173 1174 if (gSubpixelAntialiasing) { 1175 fSubpixRasterizer.reset(); 1176 fSubpixRasterizer.add_path(outer); 1177 1178 // don't add an inner hole if the "size is negative", this avoids 1179 // some defects that can be observed on R5 and could be regarded 1180 // as a bug. 1181 if (2 * fPenSize < rb.x - lt.x && 2 * fPenSize < rb.y - lt.y) { 1182 agg::rounded_rect inner; 1183 inner.rect(lt.x + fPenSize, lt.y + fPenSize, rb.x - fPenSize, 1184 rb.y - fPenSize); 1185 inner.radius(max_c(0.0, xRadius - fPenSize), 1186 max_c(0.0, yRadius - fPenSize)); 1187 1188 fSubpixRasterizer.add_path(inner); 1189 } 1190 1191 // make the inner rect work as a hole 1192 fSubpixRasterizer.filling_rule(agg::fill_even_odd); 1193 1194 if (fPenSize > 2) { 1195 agg::render_scanlines(fSubpixRasterizer, fSubpixPackedScanline, 1196 fSubpixRenderer); 1197 } else { 1198 agg::render_scanlines(fSubpixRasterizer, fSubpixUnpackedScanline, 1199 fSubpixRenderer); 1200 } 1201 1202 fSubpixRasterizer.filling_rule(agg::fill_non_zero); 1203 } else { 1204 fRasterizer.reset(); 1205 fRasterizer.add_path(outer); 1206 1207 // don't add an inner hole if the "size is negative", this avoids 1208 // some defects that can be observed on R5 and could be regarded as 1209 // a bug. 1210 if (2 * fPenSize < rb.x - lt.x && 2 * fPenSize < rb.y - lt.y) { 1211 agg::rounded_rect inner; 1212 inner.rect(lt.x + fPenSize, lt.y + fPenSize, rb.x - fPenSize, 1213 rb.y - fPenSize); 1214 inner.radius(max_c(0.0, xRadius - fPenSize), 1215 max_c(0.0, yRadius - fPenSize)); 1216 1217 fRasterizer.add_path(inner); 1218 } 1219 1220 // make the inner rect work as a hole 1221 fRasterizer.filling_rule(agg::fill_even_odd); 1222 1223 if (fPenSize > 2) 1224 agg::render_scanlines(fRasterizer, fPackedScanline, fRenderer); 1225 else 1226 agg::render_scanlines(fRasterizer, fUnpackedScanline, fRenderer); 1227 1228 // reset to default 1229 fRasterizer.filling_rule(agg::fill_non_zero); 1230 } 1231 1232 return _Clipped(_BoundingBox(outer)); 1233 } 1234 1235 1236 // FillRoundRect 1237 BRect 1238 Painter::FillRoundRect(const BRect& r, float xRadius, float yRadius) const 1239 { 1240 CHECK_CLIPPING 1241 1242 BPoint lt(r.left, r.top); 1243 BPoint rb(r.right, r.bottom); 1244 _Transform(<, false); 1245 _Transform(&rb, false); 1246 1247 // account for stricter interpretation of coordinates in AGG 1248 // the rectangle ranges from the top-left (.0, .0) 1249 // to the bottom-right (.9999, .9999) corner of pixels 1250 rb.x += 1.0; 1251 rb.y += 1.0; 1252 1253 agg::rounded_rect rect; 1254 rect.rect(lt.x, lt.y, rb.x, rb.y); 1255 rect.radius(xRadius, yRadius); 1256 1257 return _FillPath(rect); 1258 } 1259 1260 1261 // FillRoundRect 1262 BRect 1263 Painter::FillRoundRect(const BRect& r, float xRadius, float yRadius, 1264 const BGradient& gradient) const 1265 { 1266 CHECK_CLIPPING 1267 1268 BPoint lt(r.left, r.top); 1269 BPoint rb(r.right, r.bottom); 1270 _Transform(<, false); 1271 _Transform(&rb, false); 1272 1273 // account for stricter interpretation of coordinates in AGG 1274 // the rectangle ranges from the top-left (.0, .0) 1275 // to the bottom-right (.9999, .9999) corner of pixels 1276 rb.x += 1.0; 1277 rb.y += 1.0; 1278 1279 agg::rounded_rect rect; 1280 rect.rect(lt.x, lt.y, rb.x, rb.y); 1281 rect.radius(xRadius, yRadius); 1282 1283 return _FillPath(rect, gradient); 1284 } 1285 1286 1287 // AlignEllipseRect 1288 void 1289 Painter::AlignEllipseRect(BRect* rect, bool filled) const 1290 { 1291 if (!fSubpixelPrecise) { 1292 // align rect to pixels 1293 align_rect_to_pixels(rect); 1294 // account for "pixel index" versus "pixel area" 1295 rect->right++; 1296 rect->bottom++; 1297 if (!filled && fmodf(fPenSize, 2.0) != 0.0) { 1298 // align the stroke 1299 rect->InsetBy(0.5, 0.5); 1300 } 1301 } 1302 } 1303 1304 1305 // DrawEllipse 1306 BRect 1307 Painter::DrawEllipse(BRect r, bool fill) const 1308 { 1309 CHECK_CLIPPING 1310 1311 AlignEllipseRect(&r, fill); 1312 1313 float xRadius = r.Width() / 2.0; 1314 float yRadius = r.Height() / 2.0; 1315 BPoint center(r.left + xRadius, r.top + yRadius); 1316 1317 int32 divisions = (int32)((xRadius + yRadius + 2 * fPenSize) * M_PI / 2); 1318 if (divisions < 12) 1319 divisions = 12; 1320 if (divisions > 4096) 1321 divisions = 4096; 1322 1323 if (fill) { 1324 agg::ellipse path(center.x, center.y, xRadius, yRadius, divisions); 1325 1326 return _FillPath(path); 1327 } 1328 1329 // NOTE: This implementation might seem a little strange, but it makes 1330 // stroked ellipses look like on R5. A more correct way would be to use 1331 // _StrokePath(), but it currently has its own set of problems with 1332 // narrow ellipses (for small xRadii or yRadii). 1333 float inset = fPenSize / 2.0; 1334 agg::ellipse inner(center.x, center.y, max_c(0.0, xRadius - inset), 1335 max_c(0.0, yRadius - inset), divisions); 1336 agg::ellipse outer(center.x, center.y, xRadius + inset, yRadius + inset, 1337 divisions); 1338 1339 if (gSubpixelAntialiasing) { 1340 fSubpixRasterizer.reset(); 1341 fSubpixRasterizer.add_path(outer); 1342 fSubpixRasterizer.add_path(inner); 1343 1344 // make the inner ellipse work as a hole 1345 fSubpixRasterizer.filling_rule(agg::fill_even_odd); 1346 1347 if (fPenSize > 4) { 1348 agg::render_scanlines(fSubpixRasterizer, fSubpixPackedScanline, 1349 fSubpixRenderer); 1350 } else { 1351 agg::render_scanlines(fSubpixRasterizer, fSubpixUnpackedScanline, 1352 fSubpixRenderer); 1353 } 1354 1355 // reset to default 1356 fSubpixRasterizer.filling_rule(agg::fill_non_zero); 1357 } else { 1358 fRasterizer.reset(); 1359 fRasterizer.add_path(outer); 1360 fRasterizer.add_path(inner); 1361 1362 // make the inner ellipse work as a hole 1363 fRasterizer.filling_rule(agg::fill_even_odd); 1364 1365 if (fPenSize > 4) 1366 agg::render_scanlines(fRasterizer, fPackedScanline, fRenderer); 1367 else 1368 agg::render_scanlines(fRasterizer, fUnpackedScanline, fRenderer); 1369 1370 // reset to default 1371 fRasterizer.filling_rule(agg::fill_non_zero); 1372 } 1373 1374 return _Clipped(_BoundingBox(outer)); 1375 } 1376 1377 1378 // FillEllipse 1379 BRect 1380 Painter::FillEllipse(BRect r, const BGradient& gradient) const 1381 { 1382 CHECK_CLIPPING 1383 1384 AlignEllipseRect(&r, true); 1385 1386 float xRadius = r.Width() / 2.0; 1387 float yRadius = r.Height() / 2.0; 1388 BPoint center(r.left + xRadius, r.top + yRadius); 1389 1390 int32 divisions = (int32)((xRadius + yRadius + 2 * fPenSize) * M_PI / 2); 1391 if (divisions < 12) 1392 divisions = 12; 1393 if (divisions > 4096) 1394 divisions = 4096; 1395 1396 agg::ellipse path(center.x, center.y, xRadius, yRadius, divisions); 1397 1398 return _FillPath(path, gradient); 1399 } 1400 1401 1402 // StrokeArc 1403 BRect 1404 Painter::StrokeArc(BPoint center, float xRadius, float yRadius, float angle, 1405 float span) const 1406 { 1407 CHECK_CLIPPING 1408 1409 _Transform(¢er); 1410 1411 double angleRad = (angle * M_PI) / 180.0; 1412 double spanRad = (span * M_PI) / 180.0; 1413 agg::bezier_arc arc(center.x, center.y, xRadius, yRadius, -angleRad, 1414 -spanRad); 1415 1416 agg::conv_curve<agg::bezier_arc> path(arc); 1417 path.approximation_scale(2.0); 1418 1419 return _StrokePath(path); 1420 } 1421 1422 1423 // FillArc 1424 BRect 1425 Painter::FillArc(BPoint center, float xRadius, float yRadius, float angle, 1426 float span) const 1427 { 1428 CHECK_CLIPPING 1429 1430 _Transform(¢er); 1431 1432 double angleRad = (angle * M_PI) / 180.0; 1433 double spanRad = (span * M_PI) / 180.0; 1434 agg::bezier_arc arc(center.x, center.y, xRadius, yRadius, -angleRad, 1435 -spanRad); 1436 1437 agg::conv_curve<agg::bezier_arc> segmentedArc(arc); 1438 1439 fPath.remove_all(); 1440 1441 // build a new path by starting at the center point, 1442 // then traversing the arc, then going back to the center 1443 fPath.move_to(center.x, center.y); 1444 1445 segmentedArc.rewind(0); 1446 double x; 1447 double y; 1448 unsigned cmd = segmentedArc.vertex(&x, &y); 1449 while (!agg::is_stop(cmd)) { 1450 fPath.line_to(x, y); 1451 cmd = segmentedArc.vertex(&x, &y); 1452 } 1453 1454 fPath.close_polygon(); 1455 1456 return _FillPath(fPath); 1457 } 1458 1459 1460 // FillArc 1461 BRect 1462 Painter::FillArc(BPoint center, float xRadius, float yRadius, float angle, 1463 float span, const BGradient& gradient) const 1464 { 1465 CHECK_CLIPPING 1466 1467 _Transform(¢er); 1468 1469 double angleRad = (angle * M_PI) / 180.0; 1470 double spanRad = (span * M_PI) / 180.0; 1471 agg::bezier_arc arc(center.x, center.y, xRadius, yRadius, -angleRad, 1472 -spanRad); 1473 1474 agg::conv_curve<agg::bezier_arc> segmentedArc(arc); 1475 1476 fPath.remove_all(); 1477 1478 // build a new path by starting at the center point, 1479 // then traversing the arc, then going back to the center 1480 fPath.move_to(center.x, center.y); 1481 1482 segmentedArc.rewind(0); 1483 double x; 1484 double y; 1485 unsigned cmd = segmentedArc.vertex(&x, &y); 1486 while (!agg::is_stop(cmd)) { 1487 fPath.line_to(x, y); 1488 cmd = segmentedArc.vertex(&x, &y); 1489 } 1490 1491 fPath.close_polygon(); 1492 1493 return _FillPath(fPath, gradient); 1494 } 1495 1496 1497 // #pragma mark - 1498 1499 1500 // DrawString 1501 BRect 1502 Painter::DrawString(const char* utf8String, uint32 length, BPoint baseLine, 1503 const escapement_delta* delta, FontCacheReference* cacheReference) 1504 { 1505 CHECK_CLIPPING 1506 1507 if (!fSubpixelPrecise) { 1508 baseLine.x = roundf(baseLine.x); 1509 baseLine.y = roundf(baseLine.y); 1510 } 1511 1512 BRect bounds; 1513 1514 // text is not rendered with patterns, but we need to 1515 // make sure that the previous pattern is restored 1516 pattern oldPattern = *fPatternHandler.GetR5Pattern(); 1517 SetPattern(B_SOLID_HIGH, true); 1518 1519 bounds = fTextRenderer.RenderString(utf8String, length, 1520 baseLine, fClippingRegion->Frame(), false, NULL, delta, 1521 cacheReference); 1522 1523 SetPattern(oldPattern); 1524 1525 return _Clipped(bounds); 1526 } 1527 1528 1529 // DrawString 1530 BRect 1531 Painter::DrawString(const char* utf8String, uint32 length, 1532 const BPoint* offsets, FontCacheReference* cacheReference) 1533 { 1534 CHECK_CLIPPING 1535 1536 // TODO: Round offsets to device pixel grid if !fSubpixelPrecise? 1537 1538 BRect bounds; 1539 1540 // text is not rendered with patterns, but we need to 1541 // make sure that the previous pattern is restored 1542 pattern oldPattern = *fPatternHandler.GetR5Pattern(); 1543 SetPattern(B_SOLID_HIGH, true); 1544 1545 bounds = fTextRenderer.RenderString(utf8String, length, 1546 offsets, fClippingRegion->Frame(), false, NULL, 1547 cacheReference); 1548 1549 SetPattern(oldPattern); 1550 1551 return _Clipped(bounds); 1552 } 1553 1554 1555 // BoundingBox 1556 BRect 1557 Painter::BoundingBox(const char* utf8String, uint32 length, BPoint baseLine, 1558 BPoint* penLocation, const escapement_delta* delta, 1559 FontCacheReference* cacheReference) const 1560 { 1561 if (!fSubpixelPrecise) { 1562 baseLine.x = roundf(baseLine.x); 1563 baseLine.y = roundf(baseLine.y); 1564 } 1565 1566 static BRect dummy; 1567 return fTextRenderer.RenderString(utf8String, length, 1568 baseLine, dummy, true, penLocation, delta, cacheReference); 1569 } 1570 1571 1572 // BoundingBox 1573 BRect 1574 Painter::BoundingBox(const char* utf8String, uint32 length, 1575 const BPoint* offsets, BPoint* penLocation, 1576 FontCacheReference* cacheReference) const 1577 { 1578 // TODO: Round offsets to device pixel grid if !fSubpixelPrecise? 1579 1580 static BRect dummy; 1581 return fTextRenderer.RenderString(utf8String, length, 1582 offsets, dummy, true, penLocation, cacheReference); 1583 } 1584 1585 1586 // StringWidth 1587 float 1588 Painter::StringWidth(const char* utf8String, uint32 length, 1589 const escapement_delta* delta) 1590 { 1591 return Font().StringWidth(utf8String, length, delta); 1592 } 1593 1594 1595 // #pragma mark - 1596 1597 1598 // DrawBitmap 1599 BRect 1600 Painter::DrawBitmap(const ServerBitmap* bitmap, BRect bitmapRect, 1601 BRect viewRect, uint32 options) const 1602 { 1603 CHECK_CLIPPING 1604 1605 BRect touched = _Clipped(viewRect); 1606 1607 if (bitmap && bitmap->IsValid() && touched.IsValid()) { 1608 // the native bitmap coordinate system 1609 BRect actualBitmapRect(bitmap->Bounds()); 1610 1611 TRACE("Painter::DrawBitmap()\n"); 1612 TRACE(" actualBitmapRect = (%.1f, %.1f) - (%.1f, %.1f)\n", 1613 actualBitmapRect.left, actualBitmapRect.top, 1614 actualBitmapRect.right, actualBitmapRect.bottom); 1615 TRACE(" bitmapRect = (%.1f, %.1f) - (%.1f, %.1f)\n", 1616 bitmapRect.left, bitmapRect.top, bitmapRect.right, 1617 bitmapRect.bottom); 1618 TRACE(" viewRect = (%.1f, %.1f) - (%.1f, %.1f)\n", 1619 viewRect.left, viewRect.top, viewRect.right, viewRect.bottom); 1620 1621 agg::rendering_buffer srcBuffer; 1622 srcBuffer.attach(bitmap->Bits(), bitmap->Width(), bitmap->Height(), 1623 bitmap->BytesPerRow()); 1624 1625 _DrawBitmap(srcBuffer, bitmap->ColorSpace(), actualBitmapRect, 1626 bitmapRect, viewRect, options); 1627 } 1628 return touched; 1629 } 1630 1631 1632 // #pragma mark - 1633 1634 1635 // FillRegion 1636 BRect 1637 Painter::FillRegion(const BRegion* region) const 1638 { 1639 CHECK_CLIPPING 1640 1641 BRegion copy(*region); 1642 int32 count = copy.CountRects(); 1643 BRect touched = FillRect(copy.RectAt(0)); 1644 for (int32 i = 1; i < count; i++) { 1645 touched = touched | FillRect(copy.RectAt(i)); 1646 } 1647 return touched; 1648 } 1649 1650 1651 // FillRegion 1652 BRect 1653 Painter::FillRegion(const BRegion* region, const BGradient& gradient) const 1654 { 1655 CHECK_CLIPPING 1656 1657 BRegion copy(*region); 1658 int32 count = copy.CountRects(); 1659 BRect touched = FillRect(copy.RectAt(0), gradient); 1660 for (int32 i = 1; i < count; i++) { 1661 touched = touched | FillRect(copy.RectAt(i), gradient); 1662 } 1663 return touched; 1664 } 1665 1666 1667 // InvertRect 1668 BRect 1669 Painter::InvertRect(const BRect& r) const 1670 { 1671 CHECK_CLIPPING 1672 1673 BRegion region(r); 1674 region.IntersectWith(fClippingRegion); 1675 1676 // implementation only for B_RGB32 at the moment 1677 int32 count = region.CountRects(); 1678 for (int32 i = 0; i < count; i++) 1679 _InvertRect32(region.RectAt(i)); 1680 1681 return _Clipped(r); 1682 } 1683 1684 1685 // #pragma mark - private 1686 1687 1688 // _Transform 1689 inline void 1690 Painter::_Transform(BPoint* point, bool centerOffset) const 1691 { 1692 // rounding 1693 if (!fSubpixelPrecise) { 1694 // TODO: validate usage of floor() for values < 0 1695 point->x = (int32)point->x; 1696 point->y = (int32)point->y; 1697 } 1698 // this code is supposed to move coordinates to the center of pixels, 1699 // as AGG considers (0,0) to be the "upper left corner" of a pixel, 1700 // but BViews are less strict on those details 1701 if (centerOffset) { 1702 point->x += 0.5; 1703 point->y += 0.5; 1704 } 1705 } 1706 1707 1708 // _Transform 1709 inline BPoint 1710 Painter::_Transform(const BPoint& point, bool centerOffset) const 1711 { 1712 BPoint ret = point; 1713 _Transform(&ret, centerOffset); 1714 return ret; 1715 } 1716 1717 1718 // _Clipped 1719 BRect 1720 Painter::_Clipped(const BRect& rect) const 1721 { 1722 if (rect.IsValid()) 1723 return BRect(rect & fClippingRegion->Frame()); 1724 1725 return BRect(rect); 1726 } 1727 1728 1729 // _UpdateDrawingMode 1730 void 1731 Painter::_UpdateDrawingMode(bool drawingText) 1732 { 1733 // The AGG renderers have their own color setting, however 1734 // almost all drawing mode classes ignore the color given 1735 // by the AGG renderer and use the colors from the PatternHandler 1736 // instead. If we have a B_SOLID_* pattern, we can actually use 1737 // the color in the renderer and special versions of drawing modes 1738 // that don't use PatternHandler and are more efficient. This 1739 // has been implemented for B_OP_COPY and a couple others (the 1740 // DrawingMode*Solid ones) as of now. The PixelFormat knows the 1741 // PatternHandler and makes its decision based on the pattern. 1742 // The last parameter to SetDrawingMode() is a special flag 1743 // for when Painter is used to draw text. In this case, another 1744 // special version of B_OP_COPY is used that acts like R5 in that 1745 // anti-aliased pixel are not rendered against the actual background 1746 // but the current low color instead. This way, the frame buffer 1747 // doesn't need to be read. 1748 // When a solid pattern is used, _SetRendererColor() 1749 // has to be called so that all internal colors in the renderes 1750 // are up to date for use by the solid drawing mode version. 1751 fPixelFormat.SetDrawingMode(fDrawingMode, fAlphaSrcMode, fAlphaFncMode, 1752 drawingText); 1753 if (drawingText) 1754 fPatternHandler.MakeOpCopyColorCache(); 1755 } 1756 1757 1758 // _SetRendererColor 1759 void 1760 Painter::_SetRendererColor(const rgb_color& color) const 1761 { 1762 fRenderer.color(agg::rgba(color.red / 255.0, color.green / 255.0, 1763 color.blue / 255.0, color.alpha / 255.0)); 1764 fSubpixRenderer.color(agg::rgba(color.red / 255.0, color.green / 255.0, 1765 color.blue / 255.0, color.alpha / 255.0)); 1766 // TODO: bitmap fonts not yet correctly setup in AGGTextRenderer 1767 // fRendererBin.color(agg::rgba(color.red / 255.0, color.green / 255.0, 1768 // color.blue / 255.0, color.alpha / 255.0)); 1769 } 1770 1771 1772 // #pragma mark - 1773 1774 1775 // _DrawTriangle 1776 inline BRect 1777 Painter::_DrawTriangle(BPoint pt1, BPoint pt2, BPoint pt3, bool fill) const 1778 { 1779 CHECK_CLIPPING 1780 1781 _Transform(&pt1); 1782 _Transform(&pt2); 1783 _Transform(&pt3); 1784 1785 fPath.remove_all(); 1786 1787 fPath.move_to(pt1.x, pt1.y); 1788 fPath.line_to(pt2.x, pt2.y); 1789 fPath.line_to(pt3.x, pt3.y); 1790 1791 fPath.close_polygon(); 1792 1793 if (fill) 1794 return _FillPath(fPath); 1795 1796 return _StrokePath(fPath); 1797 } 1798 1799 1800 // copy_bitmap_row_cmap8_copy 1801 static inline void 1802 copy_bitmap_row_cmap8_copy(uint8* dst, const uint8* src, int32 numPixels, 1803 const rgb_color* colorMap) 1804 { 1805 uint32* d = (uint32*)dst; 1806 const uint8* s = src; 1807 while (numPixels--) { 1808 const rgb_color c = colorMap[*s++]; 1809 *d++ = (c.alpha << 24) | (c.red << 16) | (c.green << 8) | (c.blue); 1810 } 1811 } 1812 1813 1814 // copy_bitmap_row_cmap8_over 1815 static inline void 1816 copy_bitmap_row_cmap8_over(uint8* dst, const uint8* src, int32 numPixels, 1817 const rgb_color* colorMap) 1818 { 1819 uint32* d = (uint32*)dst; 1820 const uint8* s = src; 1821 while (numPixels--) { 1822 const rgb_color c = colorMap[*s++]; 1823 if (c.alpha) 1824 *d = (c.alpha << 24) | (c.red << 16) | (c.green << 8) | (c.blue); 1825 d++; 1826 } 1827 } 1828 1829 1830 // copy_bitmap_row_bgr32_copy 1831 static inline void 1832 copy_bitmap_row_bgr32_copy(uint8* dst, const uint8* src, int32 numPixels, 1833 const rgb_color* colorMap) 1834 { 1835 memcpy(dst, src, numPixels * 4); 1836 } 1837 1838 1839 // copy_bitmap_row_bgr32_over 1840 static inline void 1841 copy_bitmap_row_bgr32_over(uint8* dst, const uint8* src, int32 numPixels, 1842 const rgb_color* colorMap) 1843 { 1844 uint32* d = (uint32*)dst; 1845 uint32* s = (uint32*)src; 1846 while (numPixels--) { 1847 if (*s != B_TRANSPARENT_MAGIC_RGBA32) 1848 *(uint32*)d = *(uint32*)s; 1849 d++; 1850 s++; 1851 } 1852 } 1853 1854 1855 // copy_bitmap_row_bgr32_alpha 1856 static inline void 1857 copy_bitmap_row_bgr32_alpha(uint8* dst, const uint8* src, int32 numPixels, 1858 const rgb_color* colorMap) 1859 { 1860 uint32* d = (uint32*)dst; 1861 int32 bytes = numPixels * 4; 1862 uint8 buffer[bytes]; 1863 uint8* b = buffer; 1864 while (numPixels--) { 1865 if (src[3] == 255) { 1866 *(uint32*)b = *(uint32*)src; 1867 } else { 1868 *(uint32*)b = *d; 1869 b[0] = ((src[0] - b[0]) * src[3] + (b[0] << 8)) >> 8; 1870 b[1] = ((src[1] - b[1]) * src[3] + (b[1] << 8)) >> 8; 1871 b[2] = ((src[2] - b[2]) * src[3] + (b[2] << 8)) >> 8; 1872 } 1873 d++; 1874 b += 4; 1875 src += 4; 1876 } 1877 memcpy(dst, buffer, bytes); 1878 } 1879 1880 1881 // _TransparentMagicToAlpha 1882 template<typename sourcePixel> 1883 void 1884 Painter::_TransparentMagicToAlpha(sourcePixel* buffer, uint32 width, 1885 uint32 height, uint32 sourceBytesPerRow, sourcePixel transparentMagic, 1886 BBitmap* output) const 1887 { 1888 uint8* sourceRow = (uint8*)buffer; 1889 uint8* destRow = (uint8*)output->Bits(); 1890 uint32 destBytesPerRow = output->BytesPerRow(); 1891 1892 for (uint32 y = 0; y < height; y++) { 1893 sourcePixel* pixel = (sourcePixel*)sourceRow; 1894 uint32* destPixel = (uint32*)destRow; 1895 for (uint32 x = 0; x < width; x++, pixel++, destPixel++) { 1896 if (*pixel == transparentMagic) 1897 *destPixel &= 0x00ffffff; 1898 } 1899 1900 sourceRow += sourceBytesPerRow; 1901 destRow += destBytesPerRow; 1902 } 1903 } 1904 1905 1906 // _DrawBitmap 1907 void 1908 Painter::_DrawBitmap(agg::rendering_buffer& srcBuffer, color_space format, 1909 BRect actualBitmapRect, BRect bitmapRect, BRect viewRect, 1910 uint32 options) const 1911 { 1912 if (!fValidClipping 1913 || !bitmapRect.IsValid() || !bitmapRect.Intersects(actualBitmapRect) 1914 || !viewRect.IsValid()) { 1915 return; 1916 } 1917 1918 if (!fSubpixelPrecise) { 1919 align_rect_to_pixels(&bitmapRect); 1920 align_rect_to_pixels(&viewRect); 1921 } 1922 1923 TRACE("Painter::_DrawBitmap()\n"); 1924 TRACE(" bitmapRect = (%.1f, %.1f) - (%.1f, %.1f)\n", 1925 bitmapRect.left, bitmapRect.top, bitmapRect.right, bitmapRect.bottom); 1926 TRACE(" viewRect = (%.1f, %.1f) - (%.1f, %.1f)\n", 1927 viewRect.left, viewRect.top, viewRect.right, viewRect.bottom); 1928 1929 double xScale = (viewRect.Width() + 1) / (bitmapRect.Width() + 1); 1930 double yScale = (viewRect.Height() + 1) / (bitmapRect.Height() + 1); 1931 1932 if (xScale == 0.0 || yScale == 0.0) 1933 return; 1934 1935 // compensate for the lefttop offset the actualBitmapRect might have 1936 // actualBitmapRect has the right size, but put it at B_ORIGIN 1937 // bitmapRect is already in good coordinates 1938 actualBitmapRect.OffsetBy(-actualBitmapRect.left, -actualBitmapRect.top); 1939 1940 // constrain rect to passed bitmap bounds 1941 // and transfer the changes to the viewRect with the right scale 1942 if (bitmapRect.left < actualBitmapRect.left) { 1943 float diff = actualBitmapRect.left - bitmapRect.left; 1944 viewRect.left += diff * xScale; 1945 bitmapRect.left = actualBitmapRect.left; 1946 } 1947 if (bitmapRect.top < actualBitmapRect.top) { 1948 float diff = actualBitmapRect.top - bitmapRect.top; 1949 viewRect.top += diff * yScale; 1950 bitmapRect.top = actualBitmapRect.top; 1951 } 1952 if (bitmapRect.right > actualBitmapRect.right) { 1953 float diff = bitmapRect.right - actualBitmapRect.right; 1954 viewRect.right -= diff * xScale; 1955 bitmapRect.right = actualBitmapRect.right; 1956 } 1957 if (bitmapRect.bottom > actualBitmapRect.bottom) { 1958 float diff = bitmapRect.bottom - actualBitmapRect.bottom; 1959 viewRect.bottom -= diff * yScale; 1960 bitmapRect.bottom = actualBitmapRect.bottom; 1961 } 1962 1963 double xOffset = viewRect.left - bitmapRect.left; 1964 double yOffset = viewRect.top - bitmapRect.top; 1965 1966 // optimized code path for B_CMAP8 and no scale 1967 if (xScale == 1.0 && yScale == 1.0) { 1968 if (format == B_CMAP8) { 1969 if (fDrawingMode == B_OP_COPY) { 1970 _DrawBitmapNoScale32(copy_bitmap_row_cmap8_copy, 1, 1971 srcBuffer, (int32)xOffset, (int32)yOffset, viewRect); 1972 return; 1973 } 1974 if (fDrawingMode == B_OP_OVER) { 1975 _DrawBitmapNoScale32(copy_bitmap_row_cmap8_over, 1, 1976 srcBuffer, (int32)xOffset, (int32)yOffset, viewRect); 1977 return; 1978 } 1979 } else if (format == B_RGB32) { 1980 if (fDrawingMode == B_OP_OVER) { 1981 _DrawBitmapNoScale32(copy_bitmap_row_bgr32_over, 4, 1982 srcBuffer, (int32)xOffset, (int32)yOffset, viewRect); 1983 return; 1984 } 1985 } 1986 } 1987 1988 BBitmap* temp = NULL; 1989 ObjectDeleter<BBitmap> tempDeleter; 1990 1991 if ((format != B_RGBA32 && format != B_RGB32) 1992 || (format == B_RGB32 && fDrawingMode != B_OP_COPY 1993 #if 1 1994 // Enabling this would make the behavior compatible to BeOS, which 1995 // treats B_RGB32 bitmaps as B_RGB*A*32 bitmaps in B_OP_ALPHA - unlike in 1996 // all other drawing modes, where B_TRANSPARENT_MAGIC_RGBA32 is handled. 1997 // B_RGB32 bitmaps therefore don't draw correctly on BeOS if they actually 1998 // use this color, unless the alpha channel contains 255 for all other 1999 // pixels, which is inconsistent. 2000 && fDrawingMode != B_OP_ALPHA 2001 #endif 2002 )) { 2003 temp = new (nothrow) BBitmap(actualBitmapRect, B_BITMAP_NO_SERVER_LINK, 2004 B_RGBA32); 2005 if (temp == NULL) { 2006 fprintf(stderr, "Painter::_DrawBitmap() - " 2007 "out of memory for creating temporary conversion bitmap\n"); 2008 return; 2009 } 2010 2011 tempDeleter.SetTo(temp); 2012 2013 status_t err = temp->ImportBits(srcBuffer.buf(), 2014 srcBuffer.height() * srcBuffer.stride(), 2015 srcBuffer.stride(), 0, format); 2016 if (err < B_OK) { 2017 fprintf(stderr, "Painter::_DrawBitmap() - " 2018 "colorspace conversion failed: %s\n", strerror(err)); 2019 return; 2020 } 2021 2022 // the original bitmap might have had some of the 2023 // transaparent magic colors set that we now need to 2024 // make transparent in our RGBA32 bitmap again. 2025 switch (format) { 2026 case B_RGB32: 2027 _TransparentMagicToAlpha((uint32 *)srcBuffer.buf(), 2028 srcBuffer.width(), srcBuffer.height(), 2029 srcBuffer.stride(), B_TRANSPARENT_MAGIC_RGBA32, 2030 temp); 2031 break; 2032 2033 // TODO: not sure if this applies to B_RGBA15 too. It 2034 // should not because B_RGBA15 actually has an alpha 2035 // channel itself and it should have been preserved 2036 // when importing the bitmap. Maybe it applies to 2037 // B_RGB16 though? 2038 case B_RGB15: 2039 _TransparentMagicToAlpha((uint16 *)srcBuffer.buf(), 2040 srcBuffer.width(), srcBuffer.height(), 2041 srcBuffer.stride(), B_TRANSPARENT_MAGIC_RGBA15, 2042 temp); 2043 break; 2044 2045 default: 2046 break; 2047 } 2048 2049 srcBuffer.attach((uint8*)temp->Bits(), 2050 (uint32)actualBitmapRect.IntegerWidth() + 1, 2051 (uint32)actualBitmapRect.IntegerHeight() + 1, 2052 temp->BytesPerRow()); 2053 } 2054 2055 // maybe we can use an optimized version if there is no scale 2056 if (xScale == 1.0 && yScale == 1.0) { 2057 if (fDrawingMode == B_OP_COPY) { 2058 _DrawBitmapNoScale32(copy_bitmap_row_bgr32_copy, 4, srcBuffer, 2059 (int32)xOffset, (int32)yOffset, viewRect); 2060 return; 2061 } 2062 if (fDrawingMode == B_OP_OVER || (fDrawingMode == B_OP_ALPHA 2063 && fAlphaSrcMode == B_PIXEL_ALPHA 2064 && fAlphaFncMode == B_ALPHA_OVERLAY)) { 2065 _DrawBitmapNoScale32(copy_bitmap_row_bgr32_alpha, 4, srcBuffer, 2066 (int32)xOffset, (int32)yOffset, viewRect); 2067 return; 2068 } 2069 } 2070 2071 if (fDrawingMode == B_OP_COPY) { 2072 if ((options & B_FILTER_BITMAP_BILINEAR) != 0) { 2073 _DrawBitmapBilinearCopy32(srcBuffer, xOffset, yOffset, xScale, 2074 yScale, viewRect); 2075 } else { 2076 _DrawBitmapNearestNeighborCopy32(srcBuffer, xOffset, yOffset, 2077 xScale, yScale, viewRect); 2078 } 2079 return; 2080 } 2081 2082 // for all other cases (non-optimized drawing mode or scaled drawing) 2083 _DrawBitmapGeneric32(srcBuffer, xOffset, yOffset, xScale, yScale, viewRect, 2084 options); 2085 } 2086 2087 2088 #define DEBUG_DRAW_BITMAP 0 2089 2090 2091 // _DrawBitmapNoScale32 2092 template <class F> 2093 void 2094 Painter::_DrawBitmapNoScale32(F copyRowFunction, uint32 bytesPerSourcePixel, 2095 agg::rendering_buffer& srcBuffer, int32 xOffset, int32 yOffset, 2096 BRect viewRect) const 2097 { 2098 // NOTE: this would crash if viewRect was large enough to read outside the 2099 // bitmap, so make sure this is not the case before calling this function! 2100 uint8* dst = fBuffer.row_ptr(0); 2101 uint32 dstBPR = fBuffer.stride(); 2102 2103 const uint8* src = srcBuffer.row_ptr(0); 2104 uint32 srcBPR = srcBuffer.stride(); 2105 2106 int32 left = (int32)viewRect.left; 2107 int32 top = (int32)viewRect.top; 2108 int32 right = (int32)viewRect.right; 2109 int32 bottom = (int32)viewRect.bottom; 2110 2111 #if DEBUG_DRAW_BITMAP 2112 if (left - xOffset < 0 || left - xOffset >= (int32)srcBuffer.width() || 2113 right - xOffset >= (int32)srcBuffer.width() || 2114 top - yOffset < 0 || top - yOffset >= (int32)srcBuffer.height() || 2115 bottom - yOffset >= (int32)srcBuffer.height()) { 2116 2117 char message[256]; 2118 sprintf(message, "reading outside of bitmap (%ld, %ld, %ld, %ld) " 2119 "(%d, %d) (%ld, %ld)", 2120 left - xOffset, top - yOffset, right - xOffset, bottom - yOffset, 2121 srcBuffer.width(), srcBuffer.height(), xOffset, yOffset); 2122 debugger(message); 2123 } 2124 #endif 2125 2126 const rgb_color* colorMap = SystemPalette(); 2127 2128 // copy rects, iterate over clipping boxes 2129 fBaseRenderer.first_clip_box(); 2130 do { 2131 int32 x1 = max_c(fBaseRenderer.xmin(), left); 2132 int32 x2 = min_c(fBaseRenderer.xmax(), right); 2133 if (x1 <= x2) { 2134 int32 y1 = max_c(fBaseRenderer.ymin(), top); 2135 int32 y2 = min_c(fBaseRenderer.ymax(), bottom); 2136 if (y1 <= y2) { 2137 uint8* dstHandle = dst + y1 * dstBPR + x1 * 4; 2138 const uint8* srcHandle = src + (y1 - yOffset) * srcBPR 2139 + (x1 - xOffset) * bytesPerSourcePixel; 2140 2141 for (; y1 <= y2; y1++) { 2142 copyRowFunction(dstHandle, srcHandle, x2 - x1 + 1, colorMap); 2143 2144 dstHandle += dstBPR; 2145 srcHandle += srcBPR; 2146 } 2147 } 2148 } 2149 } while (fBaseRenderer.next_clip_box()); 2150 } 2151 2152 2153 // _DrawBitmapNearestNeighborCopy32 2154 void 2155 Painter::_DrawBitmapNearestNeighborCopy32(agg::rendering_buffer& srcBuffer, 2156 double xOffset, double yOffset, double xScale, double yScale, 2157 BRect viewRect) const 2158 { 2159 //bigtime_t now = system_time(); 2160 uint32 dstWidth = viewRect.IntegerWidth() + 1; 2161 uint32 dstHeight = viewRect.IntegerHeight() + 1; 2162 uint32 srcWidth = srcBuffer.width(); 2163 uint32 srcHeight = srcBuffer.height(); 2164 2165 // Do not calculate more filter weights than necessary and also 2166 // keep the stack based allocations reasonably sized 2167 if (fClippingRegion->Frame().IntegerWidth() + 1 < (int32)dstWidth) 2168 dstWidth = fClippingRegion->Frame().IntegerWidth() + 1; 2169 if (fClippingRegion->Frame().IntegerHeight() + 1 < (int32)dstHeight) 2170 dstHeight = fClippingRegion->Frame().IntegerHeight() + 1; 2171 2172 // When calculating less filter weights than specified by viewRect, 2173 // we need to compensate the offset. 2174 uint32 filterWeightXIndexOffset = 0; 2175 uint32 filterWeightYIndexOffset = 0; 2176 if (fClippingRegion->Frame().left > viewRect.left) { 2177 filterWeightXIndexOffset = (int32)(fClippingRegion->Frame().left 2178 - viewRect.left); 2179 } 2180 if (fClippingRegion->Frame().top > viewRect.top) { 2181 filterWeightYIndexOffset = (int32)(fClippingRegion->Frame().top 2182 - viewRect.top); 2183 } 2184 2185 // should not pose a problem with stack overflows 2186 // (needs around 6Kb for 1920x1200) 2187 uint16 xIndices[dstWidth]; 2188 uint16 yIndices[dstHeight]; 2189 2190 // Extract the cropping information for the source bitmap, 2191 // If only a part of the source bitmap is to be drawn with scale, 2192 // the offset will be different from the viewRect left top corner. 2193 int32 xBitmapShift = (int32)(viewRect.left - xOffset); 2194 int32 yBitmapShift = (int32)(viewRect.top - yOffset); 2195 2196 for (uint32 i = 0; i < dstWidth; i++) { 2197 // index into source 2198 uint16 index = (uint16)((i + filterWeightXIndexOffset) * srcWidth 2199 / (srcWidth * xScale)); 2200 // round down to get the left pixel 2201 xIndices[i] = index; 2202 // handle cropped source bitmap 2203 xIndices[i] += xBitmapShift; 2204 // precompute index for 32 bit pixels 2205 xIndices[i] *= 4; 2206 } 2207 2208 for (uint32 i = 0; i < dstHeight; i++) { 2209 // index into source 2210 uint16 index = (uint16)((i + filterWeightYIndexOffset) * srcHeight 2211 / (srcHeight * yScale)); 2212 // round down to get the top pixel 2213 yIndices[i] = index; 2214 // handle cropped source bitmap 2215 yIndices[i] += yBitmapShift; 2216 } 2217 //printf("X: %d ... %d, %d (%ld or %f)\n", 2218 // xIndices[0], xIndices[dstWidth - 2], xIndices[dstWidth - 1], dstWidth, 2219 // srcWidth * xScale); 2220 //printf("Y: %d ... %d, %d (%ld or %f)\n", 2221 // yIndices[0], yIndices[dstHeight - 2], yIndices[dstHeight - 1], dstHeight, 2222 // srcHeight * yScale); 2223 2224 const int32 left = (int32)viewRect.left; 2225 const int32 top = (int32)viewRect.top; 2226 const int32 right = (int32)viewRect.right; 2227 const int32 bottom = (int32)viewRect.bottom; 2228 2229 const uint32 dstBPR = fBuffer.stride(); 2230 2231 // iterate over clipping boxes 2232 fBaseRenderer.first_clip_box(); 2233 do { 2234 const int32 x1 = max_c(fBaseRenderer.xmin(), left); 2235 const int32 x2 = min_c(fBaseRenderer.xmax(), right); 2236 if (x1 > x2) 2237 continue; 2238 2239 int32 y1 = max_c(fBaseRenderer.ymin(), top); 2240 int32 y2 = min_c(fBaseRenderer.ymax(), bottom); 2241 if (y1 > y2) 2242 continue; 2243 2244 // buffer offset into destination 2245 uint8* dst = fBuffer.row_ptr(y1) + x1 * 4; 2246 2247 // x and y are needed as indeces into the wheight arrays, so the 2248 // offset into the target buffer needs to be compensated 2249 const int32 xIndexL = x1 - left - filterWeightXIndexOffset; 2250 const int32 xIndexR = x2 - left - filterWeightXIndexOffset; 2251 y1 -= top + filterWeightYIndexOffset; 2252 y2 -= top + filterWeightYIndexOffset; 2253 2254 //printf("x: %ld - %ld\n", xIndexL, xIndexR); 2255 //printf("y: %ld - %ld\n", y1, y2); 2256 2257 for (; y1 <= y2; y1++) { 2258 // buffer offset into source (top row) 2259 register const uint8* src = srcBuffer.row_ptr(yIndices[y1]); 2260 // buffer handle for destination to be incremented per pixel 2261 register uint32* d = (uint32*)dst; 2262 2263 for (int32 x = xIndexL; x <= xIndexR; x++) { 2264 *d = *(uint32*)(src + xIndices[x]); 2265 d++; 2266 } 2267 dst += dstBPR; 2268 } 2269 } while (fBaseRenderer.next_clip_box()); 2270 2271 //printf("draw bitmap %.5fx%.5f: %lld\n", xScale, yScale, system_time() - now); 2272 } 2273 2274 2275 // _DrawBitmapBilinearCopy32 2276 void 2277 Painter::_DrawBitmapBilinearCopy32(agg::rendering_buffer& srcBuffer, 2278 double xOffset, double yOffset, double xScale, double yScale, 2279 BRect viewRect) const 2280 { 2281 //bigtime_t now = system_time(); 2282 uint32 dstWidth = viewRect.IntegerWidth() + 1; 2283 uint32 dstHeight = viewRect.IntegerHeight() + 1; 2284 uint32 srcWidth = srcBuffer.width(); 2285 uint32 srcHeight = srcBuffer.height(); 2286 2287 // Do not calculate more filter weights than necessary and also 2288 // keep the stack based allocations reasonably sized 2289 if (fClippingRegion->Frame().IntegerWidth() + 1 < (int32)dstWidth) 2290 dstWidth = fClippingRegion->Frame().IntegerWidth() + 1; 2291 if (fClippingRegion->Frame().IntegerHeight() + 1 < (int32)dstHeight) 2292 dstHeight = fClippingRegion->Frame().IntegerHeight() + 1; 2293 2294 // When calculating less filter weights than specified by viewRect, 2295 // we need to compensate the offset. 2296 uint32 filterWeightXIndexOffset = 0; 2297 uint32 filterWeightYIndexOffset = 0; 2298 if (fClippingRegion->Frame().left > viewRect.left) { 2299 filterWeightXIndexOffset = (int32)(fClippingRegion->Frame().left 2300 - viewRect.left); 2301 } 2302 if (fClippingRegion->Frame().top > viewRect.top) { 2303 filterWeightYIndexOffset = (int32)(fClippingRegion->Frame().top 2304 - viewRect.top); 2305 } 2306 2307 struct FilterInfo { 2308 uint16 index; // index into source bitmap row/column 2309 uint16 weight; // weight of the pixel at index [0..255] 2310 }; 2311 2312 //#define FILTER_INFOS_ON_HEAP 2313 #ifdef FILTER_INFOS_ON_HEAP 2314 FilterInfo* xWeights = new (nothrow) FilterInfo[dstWidth]; 2315 FilterInfo* yWeights = new (nothrow) FilterInfo[dstHeight]; 2316 if (xWeights == NULL || yWeights == NULL) { 2317 delete[] xWeights; 2318 delete[] yWeights; 2319 return; 2320 } 2321 #else 2322 // stack based saves about 200µs on 1.85 GHz Core 2 Duo 2323 // should not pose a problem with stack overflows 2324 // (needs around 12Kb for 1920x1200) 2325 FilterInfo xWeights[dstWidth]; 2326 FilterInfo yWeights[dstHeight]; 2327 #endif 2328 2329 // Extract the cropping information for the source bitmap, 2330 // If only a part of the source bitmap is to be drawn with scale, 2331 // the offset will be different from the viewRect left top corner. 2332 int32 xBitmapShift = (int32)(viewRect.left - xOffset); 2333 int32 yBitmapShift = (int32)(viewRect.top - yOffset); 2334 2335 for (uint32 i = 0; i < dstWidth; i++) { 2336 // fractional index into source 2337 // NOTE: It is very important to calculate the fractional index 2338 // into the source pixel grid like this to prevent out of bounds 2339 // access! It will result in the rightmost pixel of the destination 2340 // to access the rightmost pixel of the source with a weighting 2341 // of 255. This in turn will trigger an optimization in the loop 2342 // that also prevents out of bounds access. 2343 float index = (i + filterWeightXIndexOffset) * (srcWidth - 1) 2344 / (srcWidth * xScale - 1); 2345 // round down to get the left pixel 2346 xWeights[i].index = (uint16)index; 2347 xWeights[i].weight = 255 - (uint16)((index - xWeights[i].index) * 255); 2348 // handle cropped source bitmap 2349 xWeights[i].index += xBitmapShift; 2350 // precompute index for 32 bit pixels 2351 xWeights[i].index *= 4; 2352 } 2353 2354 for (uint32 i = 0; i < dstHeight; i++) { 2355 // fractional index into source 2356 // NOTE: It is very important to calculate the fractional index 2357 // into the source pixel grid like this to prevent out of bounds 2358 // access! It will result in the bottommost pixel of the destination 2359 // to access the bottommost pixel of the source with a weighting 2360 // of 255. This in turn will trigger an optimization in the loop 2361 // that also prevents out of bounds access. 2362 float index = (i + filterWeightYIndexOffset) * (srcHeight - 1) 2363 / (srcHeight * yScale - 1); 2364 // round down to get the top pixel 2365 yWeights[i].index = (uint16)index; 2366 yWeights[i].weight = 255 - (uint16)((index - yWeights[i].index) * 255); 2367 // handle cropped source bitmap 2368 yWeights[i].index += yBitmapShift; 2369 } 2370 //printf("X: %d/%d ... %d/%d, %d/%d (%ld)\n", 2371 // xWeights[0].index, xWeights[0].weight, 2372 // xWeights[dstWidth - 2].index, xWeights[dstWidth - 2].weight, 2373 // xWeights[dstWidth - 1].index, xWeights[dstWidth - 1].weight, 2374 // dstWidth); 2375 //printf("Y: %d/%d ... %d/%d, %d/%d (%ld)\n", 2376 // yWeights[0].index, yWeights[0].weight, 2377 // yWeights[dstHeight - 2].index, yWeights[dstHeight - 2].weight, 2378 // yWeights[dstHeight - 1].index, yWeights[dstHeight - 1].weight, 2379 // dstHeight); 2380 2381 const int32 left = (int32)viewRect.left; 2382 const int32 top = (int32)viewRect.top; 2383 const int32 right = (int32)viewRect.right; 2384 const int32 bottom = (int32)viewRect.bottom; 2385 2386 const uint32 dstBPR = fBuffer.stride(); 2387 const uint32 srcBPR = srcBuffer.stride(); 2388 2389 // Figure out which version of the code we want to use... 2390 enum { 2391 kOptimizeForLowFilterRatio = 0, 2392 kUseDefaultVersion, 2393 kUseSIMDVersion 2394 }; 2395 2396 int codeSelect = kUseDefaultVersion; 2397 2398 uint32 neededSIMDFlags = APPSERVER_SIMD_MMX | APPSERVER_SIMD_SSE; 2399 if ((sSIMDFlags & neededSIMDFlags) == neededSIMDFlags) 2400 codeSelect = kUseSIMDVersion; 2401 else { 2402 if (xScale == yScale && (xScale == 1.5 || xScale == 2.0 2403 || xScale == 2.5 || xScale == 3.0)) { 2404 codeSelect = kOptimizeForLowFilterRatio; 2405 } 2406 } 2407 2408 // iterate over clipping boxes 2409 fBaseRenderer.first_clip_box(); 2410 do { 2411 const int32 x1 = max_c(fBaseRenderer.xmin(), left); 2412 const int32 x2 = min_c(fBaseRenderer.xmax(), right); 2413 if (x1 > x2) 2414 continue; 2415 2416 int32 y1 = max_c(fBaseRenderer.ymin(), top); 2417 int32 y2 = min_c(fBaseRenderer.ymax(), bottom); 2418 if (y1 > y2) 2419 continue; 2420 2421 // buffer offset into destination 2422 uint8* dst = fBuffer.row_ptr(y1) + x1 * 4; 2423 2424 // x and y are needed as indeces into the wheight arrays, so the 2425 // offset into the target buffer needs to be compensated 2426 const int32 xIndexL = x1 - left - filterWeightXIndexOffset; 2427 const int32 xIndexR = x2 - left - filterWeightXIndexOffset; 2428 y1 -= top + filterWeightYIndexOffset; 2429 y2 -= top + filterWeightYIndexOffset; 2430 2431 //printf("x: %ld - %ld\n", xIndexL, xIndexR); 2432 //printf("y: %ld - %ld\n", y1, y2); 2433 2434 switch (codeSelect) { 2435 case kOptimizeForLowFilterRatio: 2436 { 2437 // In this mode, we anticipate to hit many destination pixels 2438 // that map directly to a source pixel, we have more branches 2439 // in the inner loop but save time because of the special 2440 // cases. If there are too few direct hit pixels, the branches 2441 // only waste time. 2442 for (; y1 <= y2; y1++) { 2443 // cache the weight of the top and bottom row 2444 const uint16 wTop = yWeights[y1].weight; 2445 const uint16 wBottom = 255 - yWeights[y1].weight; 2446 2447 // buffer offset into source (top row) 2448 register const uint8* src 2449 = srcBuffer.row_ptr(yWeights[y1].index); 2450 // buffer handle for destination to be incremented per 2451 // pixel 2452 register uint8* d = dst; 2453 2454 if (wTop == 255) { 2455 for (int32 x = xIndexL; x <= xIndexR; x++) { 2456 const uint8* s = src + xWeights[x].index; 2457 // This case is important to prevent out 2458 // of bounds access at bottom edge of the source 2459 // bitmap. If the scale is low and integer, it will 2460 // also help the speed. 2461 if (xWeights[x].weight == 255) { 2462 // As above, but to prevent out of bounds 2463 // on the right edge. 2464 *(uint32*)d = *(uint32*)s; 2465 } else { 2466 // Only the left and right pixels are 2467 // interpolated, since the top row has 100% 2468 // weight. 2469 const uint16 wLeft = xWeights[x].weight; 2470 const uint16 wRight = 255 - wLeft; 2471 d[0] = (s[0] * wLeft + s[4] * wRight) >> 8; 2472 d[1] = (s[1] * wLeft + s[5] * wRight) >> 8; 2473 d[2] = (s[2] * wLeft + s[6] * wRight) >> 8; 2474 } 2475 d += 4; 2476 } 2477 } else { 2478 for (int32 x = xIndexL; x <= xIndexR; x++) { 2479 const uint8* s = src + xWeights[x].index; 2480 if (xWeights[x].weight == 255) { 2481 // Prevent out of bounds access on the right 2482 // edge or simply speed up. 2483 const uint8* sBottom = s + srcBPR; 2484 d[0] = (s[0] * wTop + sBottom[0] * wBottom) 2485 >> 8; 2486 d[1] = (s[1] * wTop + sBottom[1] * wBottom) 2487 >> 8; 2488 d[2] = (s[2] * wTop + sBottom[2] * wBottom) 2489 >> 8; 2490 } else { 2491 // calculate the weighted sum of all four 2492 // interpolated pixels 2493 const uint16 wLeft = xWeights[x].weight; 2494 const uint16 wRight = 255 - wLeft; 2495 // left and right of top row 2496 uint32 t0 = (s[0] * wLeft + s[4] * wRight) 2497 * wTop; 2498 uint32 t1 = (s[1] * wLeft + s[5] * wRight) 2499 * wTop; 2500 uint32 t2 = (s[2] * wLeft + s[6] * wRight) 2501 * wTop; 2502 2503 // left and right of bottom row 2504 s += srcBPR; 2505 t0 += (s[0] * wLeft + s[4] * wRight) * wBottom; 2506 t1 += (s[1] * wLeft + s[5] * wRight) * wBottom; 2507 t2 += (s[2] * wLeft + s[6] * wRight) * wBottom; 2508 2509 d[0] = t0 >> 16; 2510 d[1] = t1 >> 16; 2511 d[2] = t2 >> 16; 2512 } 2513 d += 4; 2514 } 2515 } 2516 dst += dstBPR; 2517 } 2518 break; 2519 } 2520 2521 case kUseDefaultVersion: 2522 { 2523 // In this mode we anticipate many pixels wich need filtering, 2524 // there are no special cases for direct hit pixels except for 2525 // the last column/row and the right/bottom corner pixel. 2526 2527 // The last column/row handling does not need to be performed 2528 // for all clipping rects! 2529 int32 yMax = y2; 2530 if (yWeights[yMax].weight == 255) 2531 yMax--; 2532 int32 xIndexMax = xIndexR; 2533 if (xWeights[xIndexMax].weight == 255) 2534 xIndexMax--; 2535 2536 for (; y1 <= yMax; y1++) { 2537 // cache the weight of the top and bottom row 2538 const uint16 wTop = yWeights[y1].weight; 2539 const uint16 wBottom = 255 - yWeights[y1].weight; 2540 2541 // buffer offset into source (top row) 2542 register const uint8* src 2543 = srcBuffer.row_ptr(yWeights[y1].index); 2544 // buffer handle for destination to be incremented per 2545 // pixel 2546 register uint8* d = dst; 2547 2548 for (int32 x = xIndexL; x <= xIndexMax; x++) { 2549 const uint8* s = src + xWeights[x].index; 2550 // calculate the weighted sum of all four 2551 // interpolated pixels 2552 const uint16 wLeft = xWeights[x].weight; 2553 const uint16 wRight = 255 - wLeft; 2554 // left and right of top row 2555 uint32 t0 = (s[0] * wLeft + s[4] * wRight) * wTop; 2556 uint32 t1 = (s[1] * wLeft + s[5] * wRight) * wTop; 2557 uint32 t2 = (s[2] * wLeft + s[6] * wRight) * wTop; 2558 2559 // left and right of bottom row 2560 s += srcBPR; 2561 t0 += (s[0] * wLeft + s[4] * wRight) * wBottom; 2562 t1 += (s[1] * wLeft + s[5] * wRight) * wBottom; 2563 t2 += (s[2] * wLeft + s[6] * wRight) * wBottom; 2564 d[0] = t0 >> 16; 2565 d[1] = t1 >> 16; 2566 d[2] = t2 >> 16; 2567 d += 4; 2568 } 2569 // last column of pixels if necessary 2570 if (xIndexMax < xIndexR) { 2571 const uint8* s = src + xWeights[xIndexR].index; 2572 const uint8* sBottom = s + srcBPR; 2573 d[0] = (s[0] * wTop + sBottom[0] * wBottom) >> 8; 2574 d[1] = (s[1] * wTop + sBottom[1] * wBottom) >> 8; 2575 d[2] = (s[2] * wTop + sBottom[2] * wBottom) >> 8; 2576 } 2577 2578 dst += dstBPR; 2579 } 2580 2581 // last row of pixels if necessary 2582 // buffer offset into source (bottom row) 2583 register const uint8* src 2584 = srcBuffer.row_ptr(yWeights[y2].index); 2585 // buffer handle for destination to be incremented per pixel 2586 register uint8* d = dst; 2587 2588 if (yMax < y2) { 2589 for (int32 x = xIndexL; x <= xIndexMax; x++) { 2590 const uint8* s = src + xWeights[x].index; 2591 const uint16 wLeft = xWeights[x].weight; 2592 const uint16 wRight = 255 - wLeft; 2593 d[0] = (s[0] * wLeft + s[4] * wRight) >> 8; 2594 d[1] = (s[1] * wLeft + s[5] * wRight) >> 8; 2595 d[2] = (s[2] * wLeft + s[6] * wRight) >> 8; 2596 d += 4; 2597 } 2598 } 2599 2600 // pixel in bottom right corner if necessary 2601 if (yMax < y2 && xIndexMax < xIndexR) { 2602 const uint8* s = src + xWeights[xIndexR].index; 2603 *(uint32*)d = *(uint32*)s; 2604 } 2605 break; 2606 } 2607 2608 #ifdef __INTEL__ 2609 case kUseSIMDVersion: 2610 { 2611 // Basically the same as the "standard" mode, but we use SIMD 2612 // routines for the processing of the single display lines. 2613 2614 // The last column/row handling does not need to be performed 2615 // for all clipping rects! 2616 int32 yMax = y2; 2617 if (yWeights[yMax].weight == 255) 2618 yMax--; 2619 int32 xIndexMax = xIndexR; 2620 if (xWeights[xIndexMax].weight == 255) 2621 xIndexMax--; 2622 2623 for (; y1 <= yMax; y1++) { 2624 // cache the weight of the top and bottom row 2625 const uint16 wTop = yWeights[y1].weight; 2626 const uint16 wBottom = 255 - yWeights[y1].weight; 2627 2628 // buffer offset into source (top row) 2629 const uint8* src = srcBuffer.row_ptr(yWeights[y1].index); 2630 // buffer handle for destination to be incremented per 2631 // pixel 2632 uint8* d = dst; 2633 bilinear_scale_xloop_mmxsse(src, dst, xWeights, xIndexL, 2634 xIndexMax, wTop, srcBPR); 2635 // increase pointer by processed pixels 2636 d += (xIndexMax - xIndexL + 1) * 4; 2637 2638 // last column of pixels if necessary 2639 if (xIndexMax < xIndexR) { 2640 const uint8* s = src + xWeights[xIndexR].index; 2641 const uint8* sBottom = s + srcBPR; 2642 d[0] = (s[0] * wTop + sBottom[0] * wBottom) >> 8; 2643 d[1] = (s[1] * wTop + sBottom[1] * wBottom) >> 8; 2644 d[2] = (s[2] * wTop + sBottom[2] * wBottom) >> 8; 2645 } 2646 2647 dst += dstBPR; 2648 } 2649 2650 // last row of pixels if necessary 2651 // buffer offset into source (bottom row) 2652 register const uint8* src 2653 = srcBuffer.row_ptr(yWeights[y2].index); 2654 // buffer handle for destination to be incremented per pixel 2655 register uint8* d = dst; 2656 2657 if (yMax < y2) { 2658 for (int32 x = xIndexL; x <= xIndexMax; x++) { 2659 const uint8* s = src + xWeights[x].index; 2660 const uint16 wLeft = xWeights[x].weight; 2661 const uint16 wRight = 255 - wLeft; 2662 d[0] = (s[0] * wLeft + s[4] * wRight) >> 8; 2663 d[1] = (s[1] * wLeft + s[5] * wRight) >> 8; 2664 d[2] = (s[2] * wLeft + s[6] * wRight) >> 8; 2665 d += 4; 2666 } 2667 } 2668 2669 // pixel in bottom right corner if necessary 2670 if (yMax < y2 && xIndexMax < xIndexR) { 2671 const uint8* s = src + xWeights[xIndexR].index; 2672 *(uint32*)d = *(uint32*)s; 2673 } 2674 break; 2675 } 2676 #endif // __INTEL__ 2677 } 2678 } while (fBaseRenderer.next_clip_box()); 2679 2680 #ifdef FILTER_INFOS_ON_HEAP 2681 delete[] xWeights; 2682 delete[] yWeights; 2683 #endif 2684 //printf("draw bitmap %.5fx%.5f: %lld\n", xScale, yScale, system_time() - now); 2685 } 2686 2687 2688 // _DrawBitmapGeneric32 2689 void 2690 Painter::_DrawBitmapGeneric32(agg::rendering_buffer& srcBuffer, 2691 double xOffset, double yOffset, double xScale, double yScale, 2692 BRect viewRect, uint32 options) const 2693 { 2694 TRACE("Painter::_DrawBitmapGeneric32()\n"); 2695 TRACE(" offset: %.1f, %.1f\n", xOffset, yOffset); 2696 TRACE(" scale: %.3f, %.3f\n", xScale, yScale); 2697 TRACE(" viewRect: (%.1f, %.1f) - (%.1f, %.1f)\n", 2698 viewRect.left, viewRect.top, viewRect.right, viewRect.bottom); 2699 // AGG pipeline 2700 2701 // pixel format attached to bitmap 2702 typedef agg::pixfmt_bgra32 pixfmt_image; 2703 pixfmt_image pixf_img(srcBuffer); 2704 2705 agg::trans_affine srcMatrix; 2706 // NOTE: R5 seems to ignore this offset when drawing bitmaps 2707 // srcMatrix *= agg::trans_affine_translation(-actualBitmapRect.left, 2708 // -actualBitmapRect.top); 2709 2710 agg::trans_affine imgMatrix; 2711 imgMatrix *= agg::trans_affine_translation(xOffset - viewRect.left, 2712 yOffset - viewRect.top); 2713 imgMatrix *= agg::trans_affine_scaling(xScale, yScale); 2714 imgMatrix *= agg::trans_affine_translation(viewRect.left, viewRect.top); 2715 imgMatrix.invert(); 2716 2717 // image interpolator 2718 typedef agg::span_interpolator_linear<> interpolator_type; 2719 interpolator_type interpolator(imgMatrix); 2720 2721 // scanline allocator 2722 agg::span_allocator<pixfmt_image::color_type> spanAllocator; 2723 2724 // image accessor attached to pixel format of bitmap 2725 typedef agg::image_accessor_clone<pixfmt_image> source_type; 2726 source_type source(pixf_img); 2727 2728 // clip to the current clipping region's frame 2729 viewRect = viewRect & fClippingRegion->Frame(); 2730 // convert to pixel coords (versus pixel indices) 2731 viewRect.right++; 2732 viewRect.bottom++; 2733 2734 // path enclosing the bitmap 2735 fPath.remove_all(); 2736 fPath.move_to(viewRect.left, viewRect.top); 2737 fPath.line_to(viewRect.right, viewRect.top); 2738 fPath.line_to(viewRect.right, viewRect.bottom); 2739 fPath.line_to(viewRect.left, viewRect.bottom); 2740 fPath.close_polygon(); 2741 2742 agg::conv_transform<agg::path_storage> transformedPath(fPath, srcMatrix); 2743 fRasterizer.reset(); 2744 fRasterizer.add_path(transformedPath); 2745 2746 if ((options & B_FILTER_BITMAP_BILINEAR) != 0) { 2747 // image filter (bilinear) 2748 typedef agg::span_image_filter_rgba_bilinear< 2749 source_type, interpolator_type> span_gen_type; 2750 span_gen_type spanGenerator(source, interpolator); 2751 2752 // render the path with the bitmap as scanline fill 2753 agg::render_scanlines_aa(fRasterizer, fUnpackedScanline, fBaseRenderer, 2754 spanAllocator, spanGenerator); 2755 } else { 2756 // image filter (nearest neighbor) 2757 typedef agg::span_image_filter_rgba_nn< 2758 source_type, interpolator_type> span_gen_type; 2759 span_gen_type spanGenerator(source, interpolator); 2760 2761 // render the path with the bitmap as scanline fill 2762 agg::render_scanlines_aa(fRasterizer, fUnpackedScanline, fBaseRenderer, 2763 spanAllocator, spanGenerator); 2764 } 2765 } 2766 2767 2768 // _InvertRect32 2769 void 2770 Painter::_InvertRect32(BRect r) const 2771 { 2772 int32 width = r.IntegerWidth() + 1; 2773 for (int32 y = (int32)r.top; y <= (int32)r.bottom; y++) { 2774 uint8* dst = fBuffer.row_ptr(y); 2775 dst += (int32)r.left * 4; 2776 for (int32 i = 0; i < width; i++) { 2777 dst[0] = 255 - dst[0]; 2778 dst[1] = 255 - dst[1]; 2779 dst[2] = 255 - dst[2]; 2780 dst += 4; 2781 } 2782 } 2783 } 2784 2785 2786 // _BlendRect32 2787 void 2788 Painter::_BlendRect32(const BRect& r, const rgb_color& c) const 2789 { 2790 if (!fValidClipping) 2791 return; 2792 2793 uint8* dst = fBuffer.row_ptr(0); 2794 uint32 bpr = fBuffer.stride(); 2795 2796 int32 left = (int32)r.left; 2797 int32 top = (int32)r.top; 2798 int32 right = (int32)r.right; 2799 int32 bottom = (int32)r.bottom; 2800 2801 // fill rects, iterate over clipping boxes 2802 fBaseRenderer.first_clip_box(); 2803 do { 2804 int32 x1 = max_c(fBaseRenderer.xmin(), left); 2805 int32 x2 = min_c(fBaseRenderer.xmax(), right); 2806 if (x1 <= x2) { 2807 int32 y1 = max_c(fBaseRenderer.ymin(), top); 2808 int32 y2 = min_c(fBaseRenderer.ymax(), bottom); 2809 2810 uint8* offset = dst + x1 * 4 + y1 * bpr; 2811 for (; y1 <= y2; y1++) { 2812 blend_line32(offset, x2 - x1 + 1, c.red, c.green, c.blue, 2813 c.alpha); 2814 offset += bpr; 2815 } 2816 } 2817 } while (fBaseRenderer.next_clip_box()); 2818 } 2819 2820 2821 // #pragma mark - 2822 2823 2824 template<class VertexSource> 2825 BRect 2826 Painter::_BoundingBox(VertexSource& path) const 2827 { 2828 double left = 0.0; 2829 double top = 0.0; 2830 double right = -1.0; 2831 double bottom = -1.0; 2832 uint32 pathID[1]; 2833 pathID[0] = 0; 2834 agg::bounding_rect(path, pathID, 0, 1, &left, &top, &right, &bottom); 2835 return BRect(left, top, right, bottom); 2836 } 2837 2838 2839 // agg_line_cap_mode_for 2840 inline agg::line_cap_e 2841 agg_line_cap_mode_for(cap_mode mode) 2842 { 2843 switch (mode) { 2844 case B_BUTT_CAP: 2845 return agg::butt_cap; 2846 case B_SQUARE_CAP: 2847 return agg::square_cap; 2848 case B_ROUND_CAP: 2849 return agg::round_cap; 2850 } 2851 return agg::butt_cap; 2852 } 2853 2854 2855 // agg_line_join_mode_for 2856 inline agg::line_join_e 2857 agg_line_join_mode_for(join_mode mode) 2858 { 2859 switch (mode) { 2860 case B_MITER_JOIN: 2861 return agg::miter_join; 2862 case B_ROUND_JOIN: 2863 return agg::round_join; 2864 case B_BEVEL_JOIN: 2865 case B_BUTT_JOIN: // ?? 2866 case B_SQUARE_JOIN: // ?? 2867 return agg::bevel_join; 2868 } 2869 return agg::miter_join; 2870 } 2871 2872 // _StrokePath 2873 template<class VertexSource> 2874 BRect 2875 Painter::_StrokePath(VertexSource& path) const 2876 { 2877 agg::conv_stroke<VertexSource> stroke(path); 2878 stroke.width(fPenSize); 2879 2880 stroke.line_cap(agg_line_cap_mode_for(fLineCapMode)); 2881 stroke.line_join(agg_line_join_mode_for(fLineJoinMode)); 2882 stroke.miter_limit(fMiterLimit); 2883 2884 if (gSubpixelAntialiasing) { 2885 fSubpixRasterizer.reset(); 2886 fSubpixRasterizer.add_path(stroke); 2887 2888 agg::render_scanlines(fSubpixRasterizer, 2889 fSubpixPackedScanline, fSubpixRenderer); 2890 } else { 2891 fRasterizer.reset(); 2892 fRasterizer.add_path(stroke); 2893 2894 agg::render_scanlines(fRasterizer, fPackedScanline, fRenderer); 2895 } 2896 2897 BRect touched = _BoundingBox(path); 2898 float penSize = ceilf(fPenSize / 2.0); 2899 touched.InsetBy(-penSize, -penSize); 2900 2901 return _Clipped(touched); 2902 } 2903 2904 2905 // _FillPath 2906 template<class VertexSource> 2907 BRect 2908 Painter::_FillPath(VertexSource& path) const 2909 { 2910 if (gSubpixelAntialiasing) { 2911 fSubpixRasterizer.reset(); 2912 fSubpixRasterizer.add_path(path); 2913 agg::render_scanlines(fSubpixRasterizer, 2914 fSubpixPackedScanline, fSubpixRenderer); 2915 } else { 2916 fRasterizer.reset(); 2917 fRasterizer.add_path(path); 2918 agg::render_scanlines(fRasterizer, fPackedScanline, fRenderer); 2919 } 2920 2921 return _Clipped(_BoundingBox(path)); 2922 } 2923 2924 2925 // _FillPath 2926 template<class VertexSource> 2927 BRect 2928 Painter::_FillPath(VertexSource& path, const BGradient& gradient) const 2929 { 2930 GTRACE("Painter::_FillPath\n"); 2931 2932 switch(gradient.GetType()) { 2933 case BGradient::TYPE_LINEAR: { 2934 GTRACE(("Painter::_FillPath> type == TYPE_LINEAR\n")); 2935 _FillPathGradientLinear(path, *((const BGradientLinear*) &gradient)); 2936 break; 2937 } 2938 case BGradient::TYPE_RADIAL: { 2939 GTRACE(("Painter::_FillPathGradient> type == TYPE_RADIAL\n")); 2940 _FillPathGradientRadial(path, 2941 *((const BGradientRadial*) &gradient)); 2942 break; 2943 } 2944 case BGradient::TYPE_RADIAL_FOCUS: { 2945 GTRACE(("Painter::_FillPathGradient> type == TYPE_RADIAL_FOCUS\n")); 2946 _FillPathGradientRadialFocus(path, 2947 *((const BGradientRadialFocus*) &gradient)); 2948 break; 2949 } 2950 case BGradient::TYPE_DIAMOND: { 2951 GTRACE(("Painter::_FillPathGradient> type == TYPE_DIAMOND\n")); 2952 _FillPathGradientDiamond(path, 2953 *((const BGradientDiamond*) &gradient)); 2954 break; 2955 } 2956 case BGradient::TYPE_CONIC: { 2957 GTRACE(("Painter::_FillPathGradient> type == TYPE_CONIC\n")); 2958 _FillPathGradientConic(path, 2959 *((const BGradientConic*) &gradient)); 2960 break; 2961 } 2962 case BGradient::TYPE_NONE: { 2963 GTRACE(("Painter::_FillPathGradient> type == TYPE_NONE\n")); 2964 break; 2965 } 2966 } 2967 2968 return _Clipped(_BoundingBox(path)); 2969 } 2970 2971 2972 // _MakeGradient 2973 void 2974 Painter::_MakeGradient(const BGradient& gradient, int32 colorCount, 2975 uint32* colors, int32 arrayOffset, int32 arraySize) const 2976 { 2977 BGradient::ColorStop* from = gradient.ColorStopAt(0); 2978 2979 if (!from) 2980 return; 2981 2982 // current index into "colors" array 2983 // int32 index = (int32)floorf(colorCount * from->offset + 0.5) 2984 // + arrayOffset; 2985 int32 index = (int32)floorf(colorCount * from->offset / 255 + 0.5) 2986 + arrayOffset; 2987 if (index > arraySize) 2988 index = arraySize; 2989 // Make sure we fill the entire array in case the gradient is outside. 2990 if (index > 0) { 2991 uint8* c = (uint8*)&colors[0]; 2992 for (int32 i = 0; i < index; i++) { 2993 c[0] = from->color.blue; 2994 c[1] = from->color.green; 2995 c[2] = from->color.red; 2996 c[3] = from->color.alpha; 2997 c += 4; 2998 } 2999 } 3000 3001 // interpolate "from" to "to" 3002 int32 stopCount = gradient.CountColorStops(); 3003 for (int32 i = 1; i < stopCount; i++) { 3004 // find the step with the next offset 3005 BGradient::ColorStop* to = gradient.ColorStopAtFast(i); 3006 3007 // interpolate 3008 // int32 offset = (int32)floorf((colorCount - 1) * to->offset + 0.5); 3009 int32 offset = (int32)floorf((colorCount - 1) 3010 * to->offset / 255 + 0.5); 3011 if (offset > colorCount - 1) 3012 offset = colorCount - 1; 3013 offset += arrayOffset; 3014 int32 dist = offset - index; 3015 if (dist >= 0) { 3016 int32 startIndex = max_c(index, 0); 3017 int32 stopIndex = min_c(offset, arraySize - 1); 3018 uint8* c = (uint8*)&colors[startIndex]; 3019 for (int32 i = startIndex; i <= stopIndex; i++) { 3020 float f = (float)(offset - i) / (float)(dist + 1); 3021 float t = 1.0 - f; 3022 c[0] = (uint8)floorf(from->color.blue * f 3023 + to->color.blue * t + 0.5); 3024 c[1] = (uint8)floorf(from->color.green * f 3025 + to->color.green * t + 0.5); 3026 c[2] = (uint8)floorf(from->color.red * f 3027 + to->color.red * t + 0.5); 3028 c[3] = (uint8)floorf(from->color.alpha * f 3029 + to->color.alpha * t + 0.5); 3030 c += 4; 3031 } 3032 } 3033 index = offset + 1; 3034 // the current "to" will be the "from" in the next interpolation 3035 from = to; 3036 } 3037 // make sure we fill the entire array 3038 if (index < arraySize) { 3039 int32 startIndex = max_c(index, 0); 3040 uint8* c = (uint8*)&colors[startIndex]; 3041 for (int32 i = startIndex; i < arraySize; i++) { 3042 c[0] = from->color.blue; 3043 c[1] = from->color.green; 3044 c[2] = from->color.red; 3045 c[3] = from->color.alpha; 3046 c += 4; 3047 } 3048 } 3049 } 3050 3051 3052 // _MakeGradient 3053 template<class Array> 3054 void 3055 Painter::_MakeGradient(Array& array, const BGradient& gradient) const 3056 { 3057 for (int i = 0; i < gradient.CountColorStops() - 1; i++) { 3058 BGradient::ColorStop* from = gradient.ColorStopAtFast(i); 3059 BGradient::ColorStop* to = gradient.ColorStopAtFast(i + 1); 3060 agg::rgba8 fromColor(from->color.red, from->color.green, 3061 from->color.blue, from->color.alpha); 3062 agg::rgba8 toColor(to->color.red, to->color.green, 3063 to->color.blue, to->color.alpha); 3064 GTRACE("Painter::_MakeGradient> fromColor(%d, %d, %d) offset = %f\n", 3065 fromColor.r, fromColor.g, fromColor.b, from->offset); 3066 GTRACE("Painter::_MakeGradient> toColor(%d, %d, %d) offset = %f\n", 3067 toColor.r, toColor.g, toColor.b, to->offset); 3068 float dist = to->offset - from->offset; 3069 GTRACE("Painter::_MakeGradient> dist = %f\n", dist); 3070 // TODO: Review this... offset should better be on [0..1] 3071 if (dist > 0) { 3072 for (int j = (int)from->offset; j <= (int)to->offset; j++) { 3073 float f = (float)(to->offset - j) / (float)(dist + 1); 3074 array[j] = toColor.gradient(fromColor, f); 3075 GTRACE("Painter::_MakeGradient> array[%d](%d, %d, %d)\n", 3076 array[j].r, array[j].g, array[j].b); 3077 } 3078 } 3079 } 3080 } 3081 3082 3083 // _CalcLinearGradientTransform 3084 void Painter::_CalcLinearGradientTransform(BPoint startPoint, BPoint endPoint, 3085 agg::trans_affine& matrix, float gradient_d2) const 3086 { 3087 float dx = endPoint.x - startPoint.x; 3088 float dy = endPoint.y - startPoint.y; 3089 3090 matrix.reset(); 3091 matrix *= agg::trans_affine_scaling(sqrt(dx * dx + dy * dy) / gradient_d2); 3092 matrix *= agg::trans_affine_rotation(atan2(dy, dx)); 3093 matrix *= agg::trans_affine_translation(startPoint.x, startPoint.y); 3094 matrix.invert(); 3095 } 3096 3097 3098 // _FillPathGradientLinear 3099 template<class VertexSource> 3100 void 3101 Painter::_FillPathGradientLinear(VertexSource& path, 3102 const BGradientLinear& linear) const 3103 { 3104 GTRACE("Painter::_FillPathGradientLinear\n"); 3105 3106 BPoint start = linear.Start(); 3107 BPoint end = linear.End(); 3108 3109 typedef agg::span_interpolator_linear<> interpolator_type; 3110 typedef agg::pod_auto_array<agg::rgba8, 256> color_array_type; 3111 typedef agg::span_allocator<agg::rgba8> span_allocator_type; 3112 typedef agg::gradient_x gradient_func_type; 3113 typedef agg::span_gradient<agg::rgba8, interpolator_type, 3114 gradient_func_type, color_array_type> span_gradient_type; 3115 typedef agg::renderer_scanline_aa<renderer_base, span_allocator_type, 3116 span_gradient_type> renderer_gradient_type; 3117 3118 gradient_func_type gradientFunc; 3119 agg::trans_affine gradientMatrix; 3120 interpolator_type spanInterpolator(gradientMatrix); 3121 span_allocator_type spanAllocator; 3122 color_array_type colorArray; 3123 3124 _MakeGradient(colorArray, linear); 3125 3126 span_gradient_type spanGradient(spanInterpolator, gradientFunc, colorArray, 3127 0, 100); 3128 3129 renderer_gradient_type gradientRenderer(fBaseRenderer, spanAllocator, 3130 spanGradient); 3131 3132 _CalcLinearGradientTransform(start, end, gradientMatrix); 3133 3134 fRasterizer.reset(); 3135 fRasterizer.add_path(path); 3136 agg::render_scanlines(fRasterizer, fPackedScanline, gradientRenderer); 3137 } 3138 3139 3140 // _FillPathGradientRadial 3141 template<class VertexSource> 3142 void 3143 Painter::_FillPathGradientRadial(VertexSource& path, 3144 const BGradientRadial& radial) const 3145 { 3146 GTRACE("Painter::_FillPathGradientRadial\n"); 3147 3148 BPoint center = radial.Center(); 3149 // TODO: finish this 3150 // float radius = radial.Radius(); 3151 3152 typedef agg::span_interpolator_linear<> interpolator_type; 3153 typedef agg::pod_auto_array<agg::rgba8, 256> color_array_type; 3154 typedef agg::span_allocator<agg::rgba8> span_allocator_type; 3155 typedef agg::gradient_radial gradient_func_type; 3156 typedef agg::span_gradient<agg::rgba8, interpolator_type, 3157 gradient_func_type, color_array_type> span_gradient_type; 3158 typedef agg::renderer_scanline_aa<renderer_base, span_allocator_type, 3159 span_gradient_type> renderer_gradient_type; 3160 3161 gradient_func_type gradientFunc; 3162 agg::trans_affine gradientMatrix; 3163 interpolator_type spanInterpolator(gradientMatrix); 3164 span_allocator_type spanAllocator; 3165 color_array_type colorArray; 3166 3167 _MakeGradient(colorArray, radial); 3168 3169 span_gradient_type spanGradient(spanInterpolator, gradientFunc, colorArray, 3170 0, 100); 3171 3172 renderer_gradient_type gradientRenderer(fBaseRenderer, spanAllocator, 3173 spanGradient); 3174 3175 gradientMatrix.reset(); 3176 gradientMatrix *= agg::trans_affine_translation(center.x, center.y); 3177 gradientMatrix.invert(); 3178 3179 // _CalcLinearGradientTransform(start, end, gradientMtx); 3180 3181 fRasterizer.reset(); 3182 fRasterizer.add_path(path); 3183 agg::render_scanlines(fRasterizer, fPackedScanline, gradientRenderer); 3184 } 3185 3186 3187 // _FillPathGradientRadialFocus 3188 template<class VertexSource> 3189 void 3190 Painter::_FillPathGradientRadialFocus(VertexSource& path, 3191 const BGradientRadialFocus& focus) const 3192 { 3193 GTRACE("Painter::_FillPathGradientRadialFocus\n"); 3194 3195 BPoint center = focus.Center(); 3196 // TODO: finish this. 3197 // BPoint focal = focus.Focal(); 3198 // float radius = focus.Radius(); 3199 3200 typedef agg::span_interpolator_linear<> interpolator_type; 3201 typedef agg::pod_auto_array<agg::rgba8, 256> color_array_type; 3202 typedef agg::span_allocator<agg::rgba8> span_allocator_type; 3203 typedef agg::gradient_radial_focus gradient_func_type; 3204 typedef agg::span_gradient<agg::rgba8, interpolator_type, 3205 gradient_func_type, color_array_type> span_gradient_type; 3206 typedef agg::renderer_scanline_aa<renderer_base, span_allocator_type, 3207 span_gradient_type> renderer_gradient_type; 3208 3209 gradient_func_type gradientFunc; 3210 agg::trans_affine gradientMatrix; 3211 interpolator_type spanInterpolator(gradientMatrix); 3212 span_allocator_type spanAllocator; 3213 color_array_type colorArray; 3214 3215 _MakeGradient(colorArray, focus); 3216 3217 span_gradient_type spanGradient(spanInterpolator, gradientFunc, colorArray, 3218 0, 100); 3219 3220 renderer_gradient_type gradientRenderer(fBaseRenderer, spanAllocator, 3221 spanGradient); 3222 3223 gradientMatrix.reset(); 3224 gradientMatrix *= agg::trans_affine_translation(center.x, center.y); 3225 gradientMatrix.invert(); 3226 3227 // _CalcLinearGradientTransform(start, end, gradientMatrix); 3228 3229 fRasterizer.reset(); 3230 fRasterizer.add_path(path); 3231 agg::render_scanlines(fRasterizer, fPackedScanline, gradientRenderer); 3232 } 3233 3234 3235 // _FillPathGradientDiamond 3236 template<class VertexSource> 3237 void 3238 Painter::_FillPathGradientDiamond(VertexSource& path, 3239 const BGradientDiamond& diamond) const 3240 { 3241 GTRACE("Painter::_FillPathGradientDiamond\n"); 3242 3243 BPoint center = diamond.Center(); 3244 // float radius = diamond.Radius(); 3245 3246 typedef agg::span_interpolator_linear<> interpolator_type; 3247 typedef agg::pod_auto_array<agg::rgba8, 256> color_array_type; 3248 typedef agg::span_allocator<agg::rgba8> span_allocator_type; 3249 typedef agg::gradient_diamond gradient_func_type; 3250 typedef agg::span_gradient<agg::rgba8, interpolator_type, 3251 gradient_func_type, color_array_type> span_gradient_type; 3252 typedef agg::renderer_scanline_aa<renderer_base, span_allocator_type, 3253 span_gradient_type> renderer_gradient_type; 3254 3255 gradient_func_type gradientFunc; 3256 agg::trans_affine gradientMatrix; 3257 interpolator_type spanInterpolator(gradientMatrix); 3258 span_allocator_type spanAllocator; 3259 color_array_type colorArray; 3260 3261 _MakeGradient(colorArray, diamond); 3262 3263 span_gradient_type spanGradient(spanInterpolator, gradientFunc, colorArray, 3264 0, 100); 3265 3266 renderer_gradient_type gradientRenderer(fBaseRenderer, spanAllocator, 3267 spanGradient); 3268 3269 gradientMatrix.reset(); 3270 gradientMatrix *= agg::trans_affine_translation(center.x, center.y); 3271 gradientMatrix.invert(); 3272 3273 // _CalcLinearGradientTransform(start, end, gradientMatrix); 3274 3275 fRasterizer.reset(); 3276 fRasterizer.add_path(path); 3277 agg::render_scanlines(fRasterizer, fPackedScanline, gradientRenderer); 3278 } 3279 3280 3281 // _FillPathGradientConic 3282 template<class VertexSource> 3283 void 3284 Painter::_FillPathGradientConic(VertexSource& path, 3285 const BGradientConic& conic) const 3286 { 3287 GTRACE("Painter::_FillPathGradientConic\n"); 3288 3289 BPoint center = conic.Center(); 3290 // float radius = conic.Radius(); 3291 3292 typedef agg::span_interpolator_linear<> interpolator_type; 3293 typedef agg::pod_auto_array<agg::rgba8, 256> color_array_type; 3294 typedef agg::span_allocator<agg::rgba8> span_allocator_type; 3295 typedef agg::gradient_conic gradient_func_type; 3296 typedef agg::span_gradient<agg::rgba8, interpolator_type, 3297 gradient_func_type, color_array_type> span_gradient_type; 3298 typedef agg::renderer_scanline_aa<renderer_base, span_allocator_type, 3299 span_gradient_type> renderer_gradient_type; 3300 3301 gradient_func_type gradientFunc; 3302 agg::trans_affine gradientMatrix; 3303 interpolator_type spanInterpolator(gradientMatrix); 3304 span_allocator_type spanAllocator; 3305 color_array_type colorArray; 3306 3307 _MakeGradient(colorArray, conic); 3308 3309 span_gradient_type spanGradient(spanInterpolator, gradientFunc, colorArray, 3310 0, 100); 3311 3312 renderer_gradient_type gradientRenderer(fBaseRenderer, spanAllocator, 3313 spanGradient); 3314 3315 gradientMatrix.reset(); 3316 gradientMatrix *= agg::trans_affine_translation(center.x, center.y); 3317 gradientMatrix.invert(); 3318 3319 // _CalcLinearGradientTransform(start, end, gradientMatrix); 3320 3321 fRasterizer.reset(); 3322 fRasterizer.add_path(path); 3323 agg::render_scanlines(fRasterizer, fPackedScanline, gradientRenderer); 3324 } 3325