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