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 agg::rgba8 dummyColor; 393 fPixelFormat.blend_pixel((int)a.x, (int)a.y, dummyColor, 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 // DrawShape 672 BRect 673 Painter::DrawShape(const int32& opCount, const uint32* opList, 674 const int32& ptCount, const BPoint* points, bool filled) const 675 { 676 CHECK_CLIPPING 677 678 // TODO: if shapes are ever used more heavily in Haiku, 679 // it would be nice to use BShape data directly (write 680 // an AGG "VertexSource" adaptor) 681 fPath.remove_all(); 682 for (int32 i = 0; i < opCount; i++) { 683 uint32 op = opList[i] & 0xFF000000; 684 if (op & OP_MOVETO) { 685 fPath.move_to(points->x, points->y); 686 points++; 687 } 688 689 if (op & OP_LINETO) { 690 int32 count = opList[i] & 0x00FFFFFF; 691 while (count--) { 692 fPath.line_to(points->x, points->y); 693 points++; 694 } 695 } 696 697 if (op & OP_BEZIERTO) { 698 int32 count = opList[i] & 0x00FFFFFF; 699 while (count) { 700 fPath.curve4(points[0].x, points[0].y, points[1].x, points[1].y, 701 points[2].x, points[2].y); 702 points += 3; 703 count -= 3; 704 } 705 } 706 707 if (op & OP_CLOSE) 708 fPath.close_polygon(); 709 } 710 711 if (filled) 712 return _FillPath(fCurve); 713 714 return _StrokePath(fCurve); 715 } 716 717 718 // FillShape 719 BRect 720 Painter::FillShape(const int32& opCount, const uint32* opList, 721 const int32& ptCount, const BPoint* points, const BGradient& gradient) const 722 { 723 CHECK_CLIPPING 724 725 // TODO: if shapes are ever used more heavily in Haiku, 726 // it would be nice to use BShape data directly (write 727 // an AGG "VertexSource" adaptor) 728 fPath.remove_all(); 729 for (int32 i = 0; i < opCount; i++) { 730 uint32 op = opList[i] & 0xFF000000; 731 if (op & OP_MOVETO) { 732 fPath.move_to(points->x, points->y); 733 points++; 734 } 735 736 if (op & OP_LINETO) { 737 int32 count = opList[i] & 0x00FFFFFF; 738 while (count--) { 739 fPath.line_to(points->x, points->y); 740 points++; 741 } 742 } 743 744 if (op & OP_BEZIERTO) { 745 int32 count = opList[i] & 0x00FFFFFF; 746 while (count) { 747 fPath.curve4(points[0].x, points[0].y, points[1].x, points[1].y, 748 points[2].x, points[2].y); 749 points += 3; 750 count -= 3; 751 } 752 } 753 754 if (op & OP_CLOSE) 755 fPath.close_polygon(); 756 } 757 758 return _FillPath(fCurve, gradient); 759 } 760 761 762 // StrokeRect 763 BRect 764 Painter::StrokeRect(const BRect& r) const 765 { 766 CHECK_CLIPPING 767 768 BPoint a(r.left, r.top); 769 BPoint b(r.right, r.bottom); 770 _Transform(&a, false); 771 _Transform(&b, false); 772 773 // first, try an optimized version 774 if (fPenSize == 1.0 && 775 (fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER)) { 776 pattern p = *fPatternHandler.GetR5Pattern(); 777 if (p == B_SOLID_HIGH) { 778 BRect rect(a, b); 779 StrokeRect(rect, fPatternHandler.HighColor()); 780 return _Clipped(rect); 781 } else if (p == B_SOLID_LOW) { 782 BRect rect(a, b); 783 StrokeRect(rect, fPatternHandler.LowColor()); 784 return _Clipped(rect); 785 } 786 } 787 788 if (fmodf(fPenSize, 2.0) != 0.0) { 789 // shift coords to center of pixels 790 a.x += 0.5; 791 a.y += 0.5; 792 b.x += 0.5; 793 b.y += 0.5; 794 } 795 796 fPath.remove_all(); 797 fPath.move_to(a.x, a.y); 798 if (a.x == b.x || a.y == b.y) { 799 // special case rects with one pixel height or width 800 fPath.line_to(b.x, b.y); 801 } else { 802 fPath.line_to(b.x, a.y); 803 fPath.line_to(b.x, b.y); 804 fPath.line_to(a.x, b.y); 805 } 806 fPath.close_polygon(); 807 808 return _StrokePath(fPath); 809 } 810 811 812 // StrokeRect 813 void 814 Painter::StrokeRect(const BRect& r, const rgb_color& c) const 815 { 816 StraightLine(BPoint(r.left, r.top), BPoint(r.right - 1, r.top), c); 817 StraightLine(BPoint(r.right, r.top), BPoint(r.right, r.bottom - 1), c); 818 StraightLine(BPoint(r.right, r.bottom), BPoint(r.left + 1, r.bottom), c); 819 StraightLine(BPoint(r.left, r.bottom), BPoint(r.left, r.top + 1), c); 820 } 821 822 823 // FillRect 824 BRect 825 Painter::FillRect(const BRect& r) const 826 { 827 CHECK_CLIPPING 828 829 // support invalid rects 830 BPoint a(min_c(r.left, r.right), min_c(r.top, r.bottom)); 831 BPoint b(max_c(r.left, r.right), max_c(r.top, r.bottom)); 832 _Transform(&a, false); 833 _Transform(&b, false); 834 835 // first, try an optimized version 836 if (fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER) { 837 pattern p = *fPatternHandler.GetR5Pattern(); 838 if (p == B_SOLID_HIGH) { 839 BRect rect(a, b); 840 FillRect(rect, fPatternHandler.HighColor()); 841 return _Clipped(rect); 842 } else if (p == B_SOLID_LOW) { 843 BRect rect(a, b); 844 FillRect(rect, fPatternHandler.LowColor()); 845 return _Clipped(rect); 846 } 847 } 848 if (fDrawingMode == B_OP_ALPHA && fAlphaFncMode == B_ALPHA_OVERLAY) { 849 pattern p = *fPatternHandler.GetR5Pattern(); 850 if (p == B_SOLID_HIGH) { 851 BRect rect(a, b); 852 _BlendRect32(rect, fPatternHandler.HighColor()); 853 return _Clipped(rect); 854 } else if (p == B_SOLID_LOW) { 855 rgb_color c = fPatternHandler.LowColor(); 856 if (fAlphaSrcMode == B_CONSTANT_ALPHA) 857 c.alpha = fPatternHandler.HighColor().alpha; 858 BRect rect(a, b); 859 _BlendRect32(rect, c); 860 return _Clipped(rect); 861 } 862 } 863 864 // account for stricter interpretation of coordinates in AGG 865 // the rectangle ranges from the top-left (.0, .0) 866 // to the bottom-right (.9999, .9999) corner of pixels 867 b.x += 1.0; 868 b.y += 1.0; 869 870 fPath.remove_all(); 871 fPath.move_to(a.x, a.y); 872 fPath.line_to(b.x, a.y); 873 fPath.line_to(b.x, b.y); 874 fPath.line_to(a.x, b.y); 875 fPath.close_polygon(); 876 877 return _FillPath(fPath); 878 } 879 880 881 // FillRect 882 BRect 883 Painter::FillRect(const BRect& r, const BGradient& gradient) const 884 { 885 CHECK_CLIPPING 886 887 // support invalid rects 888 BPoint a(min_c(r.left, r.right), min_c(r.top, r.bottom)); 889 BPoint b(max_c(r.left, r.right), max_c(r.top, r.bottom)); 890 _Transform(&a, false); 891 _Transform(&b, false); 892 893 // first, try an optimized version 894 if (gradient.GetType() == BGradient::TYPE_LINEAR 895 && (fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER)) { 896 const BGradientLinear* linearGradient 897 = dynamic_cast<const BGradientLinear*>(&gradient); 898 if (linearGradient->Start().x == linearGradient->End().x 899 // TODO: Remove this second check once the optimized method 900 // handled "upside down" gradients as well... 901 && linearGradient->Start().y <= linearGradient->End().y) { 902 // a vertical gradient 903 BRect rect(a, b); 904 FillRectVerticalGradient(rect, *linearGradient); 905 return _Clipped(rect); 906 } 907 } 908 909 // account for stricter interpretation of coordinates in AGG 910 // the rectangle ranges from the top-left (.0, .0) 911 // to the bottom-right (.9999, .9999) corner of pixels 912 b.x += 1.0; 913 b.y += 1.0; 914 915 fPath.remove_all(); 916 fPath.move_to(a.x, a.y); 917 fPath.line_to(b.x, a.y); 918 fPath.line_to(b.x, b.y); 919 fPath.line_to(a.x, b.y); 920 fPath.close_polygon(); 921 922 return _FillPath(fPath, gradient); 923 } 924 925 926 // FillRect 927 void 928 Painter::FillRect(const BRect& r, const rgb_color& c) const 929 { 930 if (!fValidClipping) 931 return; 932 933 uint8* dst = fBuffer.row_ptr(0); 934 uint32 bpr = fBuffer.stride(); 935 int32 left = (int32)r.left; 936 int32 top = (int32)r.top; 937 int32 right = (int32)r.right; 938 int32 bottom = (int32)r.bottom; 939 // get a 32 bit pixel ready with the color 940 pixel32 color; 941 color.data8[0] = c.blue; 942 color.data8[1] = c.green; 943 color.data8[2] = c.red; 944 color.data8[3] = c.alpha; 945 // fill rects, iterate over clipping boxes 946 fBaseRenderer.first_clip_box(); 947 do { 948 int32 x1 = max_c(fBaseRenderer.xmin(), left); 949 int32 x2 = min_c(fBaseRenderer.xmax(), right); 950 if (x1 <= x2) { 951 int32 y1 = max_c(fBaseRenderer.ymin(), top); 952 int32 y2 = min_c(fBaseRenderer.ymax(), bottom); 953 uint8* offset = dst + x1 * 4; 954 for (; y1 <= y2; y1++) { 955 // uint32* handle = (uint32*)(offset + y1 * bpr); 956 // for (int32 x = x1; x <= x2; x++) { 957 // *handle++ = color.data32; 958 // } 959 gfxset32(offset + y1 * bpr, color.data32, (x2 - x1 + 1) * 4); 960 } 961 } 962 } while (fBaseRenderer.next_clip_box()); 963 } 964 965 966 // FillRectVerticalGradient 967 void 968 Painter::FillRectVerticalGradient(BRect r, const BGradientLinear& gradient) const 969 { 970 if (!fValidClipping) 971 return; 972 973 // Make sure the color array is no larger than the screen height. 974 r = r & fClippingRegion->Frame(); 975 976 int32 gradientArraySize = r.IntegerHeight() + 1; 977 uint32 gradientArray[gradientArraySize]; 978 int32 gradientTop = (int32)gradient.Start().y; 979 int32 gradientBottom = (int32)gradient.End().y; 980 int32 colorCount = gradientBottom - gradientTop + 1; 981 if (colorCount < 0) { 982 // Gradient is upside down. That's currently not supported by this 983 // method. 984 return; 985 } 986 987 _MakeGradient(gradient, colorCount, gradientArray, 988 gradientTop - (int32)r.top, gradientArraySize); 989 990 uint8* dst = fBuffer.row_ptr(0); 991 uint32 bpr = fBuffer.stride(); 992 int32 left = (int32)r.left; 993 int32 top = (int32)r.top; 994 int32 right = (int32)r.right; 995 int32 bottom = (int32)r.bottom; 996 // fill rects, iterate over clipping boxes 997 fBaseRenderer.first_clip_box(); 998 do { 999 int32 x1 = max_c(fBaseRenderer.xmin(), left); 1000 int32 x2 = min_c(fBaseRenderer.xmax(), right); 1001 if (x1 <= x2) { 1002 int32 y1 = max_c(fBaseRenderer.ymin(), top); 1003 int32 y2 = min_c(fBaseRenderer.ymax(), bottom); 1004 uint8* offset = dst + x1 * 4; 1005 for (; y1 <= y2; y1++) { 1006 // uint32* handle = (uint32*)(offset + y1 * bpr); 1007 // for (int32 x = x1; x <= x2; x++) { 1008 // *handle++ = gradientArray[y1 - top]; 1009 // } 1010 gfxset32(offset + y1 * bpr, gradientArray[y1 - top], 1011 (x2 - x1 + 1) * 4); 1012 } 1013 } 1014 } while (fBaseRenderer.next_clip_box()); 1015 } 1016 1017 1018 // FillRectNoClipping 1019 void 1020 Painter::FillRectNoClipping(const clipping_rect& r, const rgb_color& c) const 1021 { 1022 int32 y = (int32)r.top; 1023 1024 uint8* dst = fBuffer.row_ptr(y) + r.left * 4; 1025 uint32 bpr = fBuffer.stride(); 1026 int32 bytes = (r.right - r.left + 1) * 4; 1027 1028 // get a 32 bit pixel ready with the color 1029 pixel32 color; 1030 color.data8[0] = c.blue; 1031 color.data8[1] = c.green; 1032 color.data8[2] = c.red; 1033 color.data8[3] = c.alpha; 1034 1035 for (; y <= r.bottom; y++) { 1036 // uint32* handle = (uint32*)dst; 1037 // for (int32 x = left; x <= right; x++) { 1038 // *handle++ = color.data32; 1039 // } 1040 gfxset32(dst, color.data32, bytes); 1041 dst += bpr; 1042 } 1043 } 1044 1045 1046 // StrokeRoundRect 1047 BRect 1048 Painter::StrokeRoundRect(const BRect& r, float xRadius, float yRadius) const 1049 { 1050 CHECK_CLIPPING 1051 1052 BPoint lt(r.left, r.top); 1053 BPoint rb(r.right, r.bottom); 1054 bool centerOffset = fPenSize == 1.0; 1055 // TODO: use this when using _StrokePath() 1056 // bool centerOffset = fmodf(fPenSize, 2.0) != 0.0; 1057 _Transform(<, centerOffset); 1058 _Transform(&rb, centerOffset); 1059 1060 if (fPenSize == 1.0) { 1061 agg::rounded_rect rect; 1062 rect.rect(lt.x, lt.y, rb.x, rb.y); 1063 rect.radius(xRadius, yRadius); 1064 1065 return _StrokePath(rect); 1066 } 1067 1068 // NOTE: This implementation might seem a little strange, but it makes 1069 // stroked round rects look like on R5. A more correct way would be to 1070 // use _StrokePath() as above (independent from fPenSize). 1071 // The fact that the bounding box of the round rect is not enlarged 1072 // by fPenSize/2 is actually on purpose, though one could argue it is 1073 // unexpected. 1074 1075 // enclose the right and bottom edge 1076 rb.x++; 1077 rb.y++; 1078 1079 agg::rounded_rect outer; 1080 outer.rect(lt.x, lt.y, rb.x, rb.y); 1081 outer.radius(xRadius, yRadius); 1082 1083 if (gSubpixelAntialiasing) { 1084 fSubpixRasterizer.reset(); 1085 fSubpixRasterizer.add_path(outer); 1086 1087 // don't add an inner hole if the "size is negative", this avoids 1088 // some defects that can be observed on R5 and could be regarded 1089 // as a bug. 1090 if (2 * fPenSize < rb.x - lt.x && 2 * fPenSize < rb.y - lt.y) { 1091 agg::rounded_rect inner; 1092 inner.rect(lt.x + fPenSize, lt.y + fPenSize, rb.x - fPenSize, 1093 rb.y - fPenSize); 1094 inner.radius(max_c(0.0, xRadius - fPenSize), 1095 max_c(0.0, yRadius - fPenSize)); 1096 1097 fSubpixRasterizer.add_path(inner); 1098 } 1099 1100 // make the inner rect work as a hole 1101 fSubpixRasterizer.filling_rule(agg::fill_even_odd); 1102 1103 if (fPenSize > 2) { 1104 agg::render_scanlines(fSubpixRasterizer, fSubpixPackedScanline, 1105 fSubpixRenderer); 1106 } else { 1107 agg::render_scanlines(fSubpixRasterizer, fSubpixUnpackedScanline, 1108 fSubpixRenderer); 1109 } 1110 1111 fSubpixRasterizer.filling_rule(agg::fill_non_zero); 1112 } else { 1113 fRasterizer.reset(); 1114 fRasterizer.add_path(outer); 1115 1116 // don't add an inner hole if the "size is negative", this avoids 1117 // some defects that can be observed on R5 and could be regarded as 1118 // a bug. 1119 if (2 * fPenSize < rb.x - lt.x && 2 * fPenSize < rb.y - lt.y) { 1120 agg::rounded_rect inner; 1121 inner.rect(lt.x + fPenSize, lt.y + fPenSize, rb.x - fPenSize, 1122 rb.y - fPenSize); 1123 inner.radius(max_c(0.0, xRadius - fPenSize), 1124 max_c(0.0, yRadius - fPenSize)); 1125 1126 fRasterizer.add_path(inner); 1127 } 1128 1129 // make the inner rect work as a hole 1130 fRasterizer.filling_rule(agg::fill_even_odd); 1131 1132 if (fPenSize > 2) 1133 agg::render_scanlines(fRasterizer, fPackedScanline, fRenderer); 1134 else 1135 agg::render_scanlines(fRasterizer, fUnpackedScanline, fRenderer); 1136 1137 // reset to default 1138 fRasterizer.filling_rule(agg::fill_non_zero); 1139 } 1140 1141 return _Clipped(_BoundingBox(outer)); 1142 } 1143 1144 1145 // FillRoundRect 1146 BRect 1147 Painter::FillRoundRect(const BRect& r, float xRadius, float yRadius) const 1148 { 1149 CHECK_CLIPPING 1150 1151 BPoint lt(r.left, r.top); 1152 BPoint rb(r.right, r.bottom); 1153 _Transform(<, false); 1154 _Transform(&rb, false); 1155 1156 // account for stricter interpretation of coordinates in AGG 1157 // the rectangle ranges from the top-left (.0, .0) 1158 // to the bottom-right (.9999, .9999) corner of pixels 1159 rb.x += 1.0; 1160 rb.y += 1.0; 1161 1162 agg::rounded_rect rect; 1163 rect.rect(lt.x, lt.y, rb.x, rb.y); 1164 rect.radius(xRadius, yRadius); 1165 1166 return _FillPath(rect); 1167 } 1168 1169 1170 // FillRoundRect 1171 BRect 1172 Painter::FillRoundRect(const BRect& r, float xRadius, float yRadius, 1173 const BGradient& gradient) const 1174 { 1175 CHECK_CLIPPING 1176 1177 BPoint lt(r.left, r.top); 1178 BPoint rb(r.right, r.bottom); 1179 _Transform(<, false); 1180 _Transform(&rb, false); 1181 1182 // account for stricter interpretation of coordinates in AGG 1183 // the rectangle ranges from the top-left (.0, .0) 1184 // to the bottom-right (.9999, .9999) corner of pixels 1185 rb.x += 1.0; 1186 rb.y += 1.0; 1187 1188 agg::rounded_rect rect; 1189 rect.rect(lt.x, lt.y, rb.x, rb.y); 1190 rect.radius(xRadius, yRadius); 1191 1192 return _FillPath(rect, gradient); 1193 } 1194 1195 1196 // AlignEllipseRect 1197 void 1198 Painter::AlignEllipseRect(BRect* rect, bool filled) const 1199 { 1200 if (!fSubpixelPrecise) { 1201 // align rect to pixels 1202 align_rect_to_pixels(rect); 1203 // account for "pixel index" versus "pixel area" 1204 rect->right++; 1205 rect->bottom++; 1206 if (!filled && fmodf(fPenSize, 2.0) != 0.0) { 1207 // align the stroke 1208 rect->InsetBy(0.5, 0.5); 1209 } 1210 } 1211 } 1212 1213 1214 // DrawEllipse 1215 BRect 1216 Painter::DrawEllipse(BRect r, bool fill) const 1217 { 1218 CHECK_CLIPPING 1219 1220 AlignEllipseRect(&r, fill); 1221 1222 float xRadius = r.Width() / 2.0; 1223 float yRadius = r.Height() / 2.0; 1224 BPoint center(r.left + xRadius, r.top + yRadius); 1225 1226 int32 divisions = (int32)((xRadius + yRadius + 2 * fPenSize) * M_PI / 2); 1227 if (divisions < 12) 1228 divisions = 12; 1229 if (divisions > 4096) 1230 divisions = 4096; 1231 1232 if (fill) { 1233 agg::ellipse path(center.x, center.y, xRadius, yRadius, divisions); 1234 1235 return _FillPath(path); 1236 } 1237 1238 // NOTE: This implementation might seem a little strange, but it makes 1239 // stroked ellipses look like on R5. A more correct way would be to use 1240 // _StrokePath(), but it currently has its own set of problems with 1241 // narrow ellipses (for small xRadii or yRadii). 1242 float inset = fPenSize / 2.0; 1243 agg::ellipse inner(center.x, center.y, max_c(0.0, xRadius - inset), 1244 max_c(0.0, yRadius - inset), divisions); 1245 agg::ellipse outer(center.x, center.y, xRadius + inset, yRadius + inset, 1246 divisions); 1247 1248 if (gSubpixelAntialiasing) { 1249 fSubpixRasterizer.reset(); 1250 fSubpixRasterizer.add_path(outer); 1251 fSubpixRasterizer.add_path(inner); 1252 1253 // make the inner ellipse work as a hole 1254 fSubpixRasterizer.filling_rule(agg::fill_even_odd); 1255 1256 if (fPenSize > 4) { 1257 agg::render_scanlines(fSubpixRasterizer, fSubpixPackedScanline, 1258 fSubpixRenderer); 1259 } else { 1260 agg::render_scanlines(fSubpixRasterizer, fSubpixUnpackedScanline, 1261 fSubpixRenderer); 1262 } 1263 1264 // reset to default 1265 fSubpixRasterizer.filling_rule(agg::fill_non_zero); 1266 } else { 1267 fRasterizer.reset(); 1268 fRasterizer.add_path(outer); 1269 fRasterizer.add_path(inner); 1270 1271 // make the inner ellipse work as a hole 1272 fRasterizer.filling_rule(agg::fill_even_odd); 1273 1274 if (fPenSize > 4) 1275 agg::render_scanlines(fRasterizer, fPackedScanline, fRenderer); 1276 else 1277 agg::render_scanlines(fRasterizer, fUnpackedScanline, fRenderer); 1278 1279 // reset to default 1280 fRasterizer.filling_rule(agg::fill_non_zero); 1281 } 1282 1283 return _Clipped(_BoundingBox(outer)); 1284 } 1285 1286 1287 // FillEllipse 1288 BRect 1289 Painter::FillEllipse(BRect r, const BGradient& gradient) const 1290 { 1291 CHECK_CLIPPING 1292 1293 AlignEllipseRect(&r, true); 1294 1295 float xRadius = r.Width() / 2.0; 1296 float yRadius = r.Height() / 2.0; 1297 BPoint center(r.left + xRadius, r.top + yRadius); 1298 1299 int32 divisions = (int32)((xRadius + yRadius + 2 * fPenSize) * M_PI / 2); 1300 if (divisions < 12) 1301 divisions = 12; 1302 if (divisions > 4096) 1303 divisions = 4096; 1304 1305 agg::ellipse path(center.x, center.y, xRadius, yRadius, divisions); 1306 1307 return _FillPath(path, gradient); 1308 } 1309 1310 1311 // StrokeArc 1312 BRect 1313 Painter::StrokeArc(BPoint center, float xRadius, float yRadius, float angle, 1314 float span) const 1315 { 1316 CHECK_CLIPPING 1317 1318 _Transform(¢er); 1319 1320 double angleRad = (angle * M_PI) / 180.0; 1321 double spanRad = (span * M_PI) / 180.0; 1322 agg::bezier_arc arc(center.x, center.y, xRadius, yRadius, -angleRad, 1323 -spanRad); 1324 1325 agg::conv_curve<agg::bezier_arc> path(arc); 1326 path.approximation_scale(2.0); 1327 1328 return _StrokePath(path); 1329 } 1330 1331 1332 // FillArc 1333 BRect 1334 Painter::FillArc(BPoint center, float xRadius, float yRadius, float angle, 1335 float span) const 1336 { 1337 CHECK_CLIPPING 1338 1339 _Transform(¢er); 1340 1341 double angleRad = (angle * M_PI) / 180.0; 1342 double spanRad = (span * M_PI) / 180.0; 1343 agg::bezier_arc arc(center.x, center.y, xRadius, yRadius, -angleRad, 1344 -spanRad); 1345 1346 agg::conv_curve<agg::bezier_arc> segmentedArc(arc); 1347 1348 fPath.remove_all(); 1349 1350 // build a new path by starting at the center point, 1351 // then traversing the arc, then going back to the center 1352 fPath.move_to(center.x, center.y); 1353 1354 segmentedArc.rewind(0); 1355 double x; 1356 double y; 1357 unsigned cmd = segmentedArc.vertex(&x, &y); 1358 while (!agg::is_stop(cmd)) { 1359 fPath.line_to(x, y); 1360 cmd = segmentedArc.vertex(&x, &y); 1361 } 1362 1363 fPath.close_polygon(); 1364 1365 return _FillPath(fPath); 1366 } 1367 1368 1369 // FillArc 1370 BRect 1371 Painter::FillArc(BPoint center, float xRadius, float yRadius, float angle, 1372 float span, const BGradient& gradient) const 1373 { 1374 CHECK_CLIPPING 1375 1376 _Transform(¢er); 1377 1378 double angleRad = (angle * M_PI) / 180.0; 1379 double spanRad = (span * M_PI) / 180.0; 1380 agg::bezier_arc arc(center.x, center.y, xRadius, yRadius, -angleRad, 1381 -spanRad); 1382 1383 agg::conv_curve<agg::bezier_arc> segmentedArc(arc); 1384 1385 fPath.remove_all(); 1386 1387 // build a new path by starting at the center point, 1388 // then traversing the arc, then going back to the center 1389 fPath.move_to(center.x, center.y); 1390 1391 segmentedArc.rewind(0); 1392 double x; 1393 double y; 1394 unsigned cmd = segmentedArc.vertex(&x, &y); 1395 while (!agg::is_stop(cmd)) { 1396 fPath.line_to(x, y); 1397 cmd = segmentedArc.vertex(&x, &y); 1398 } 1399 1400 fPath.close_polygon(); 1401 1402 return _FillPath(fPath, gradient); 1403 } 1404 1405 1406 // #pragma mark - 1407 1408 1409 // DrawString 1410 BRect 1411 Painter::DrawString(const char* utf8String, uint32 length, BPoint baseLine, 1412 const escapement_delta* delta, FontCacheReference* cacheReference) 1413 { 1414 CHECK_CLIPPING 1415 1416 if (!fSubpixelPrecise) { 1417 baseLine.x = roundf(baseLine.x); 1418 baseLine.y = roundf(baseLine.y); 1419 } 1420 1421 BRect bounds(0.0, 0.0, -1.0, -1.0); 1422 1423 // text is not rendered with patterns, but we need to 1424 // make sure that the previous pattern is restored 1425 pattern oldPattern = *fPatternHandler.GetR5Pattern(); 1426 SetPattern(B_SOLID_HIGH, true); 1427 1428 bounds = fTextRenderer.RenderString(utf8String, length, 1429 baseLine, fClippingRegion->Frame(), false, NULL, delta, 1430 cacheReference); 1431 1432 SetPattern(oldPattern); 1433 1434 return _Clipped(bounds); 1435 } 1436 1437 1438 // BoundingBox 1439 BRect 1440 Painter::BoundingBox(const char* utf8String, uint32 length, BPoint baseLine, 1441 BPoint* penLocation, const escapement_delta* delta, 1442 FontCacheReference* cacheReference) const 1443 { 1444 if (!fSubpixelPrecise) { 1445 baseLine.x = roundf(baseLine.x); 1446 baseLine.y = roundf(baseLine.y); 1447 } 1448 1449 static BRect dummy; 1450 return fTextRenderer.RenderString(utf8String, length, 1451 baseLine, dummy, true, penLocation, delta, cacheReference); 1452 } 1453 1454 1455 // StringWidth 1456 float 1457 Painter::StringWidth(const char* utf8String, uint32 length, 1458 const escapement_delta* delta) 1459 { 1460 return Font().StringWidth(utf8String, length, delta); 1461 } 1462 1463 1464 // #pragma mark - 1465 1466 1467 // DrawBitmap 1468 BRect 1469 Painter::DrawBitmap(const ServerBitmap* bitmap, BRect bitmapRect, 1470 BRect viewRect, uint32 options) const 1471 { 1472 CHECK_CLIPPING 1473 1474 BRect touched = _Clipped(viewRect); 1475 1476 if (bitmap && bitmap->IsValid() && touched.IsValid()) { 1477 // the native bitmap coordinate system 1478 BRect actualBitmapRect(bitmap->Bounds()); 1479 1480 TRACE("Painter::DrawBitmap()\n"); 1481 TRACE(" actualBitmapRect = (%.1f, %.1f) - (%.1f, %.1f)\n", 1482 actualBitmapRect.left, actualBitmapRect.top, 1483 actualBitmapRect.right, actualBitmapRect.bottom); 1484 TRACE(" bitmapRect = (%.1f, %.1f) - (%.1f, %.1f)\n", 1485 bitmapRect.left, bitmapRect.top, bitmapRect.right, 1486 bitmapRect.bottom); 1487 TRACE(" viewRect = (%.1f, %.1f) - (%.1f, %.1f)\n", 1488 viewRect.left, viewRect.top, viewRect.right, viewRect.bottom); 1489 1490 agg::rendering_buffer srcBuffer; 1491 srcBuffer.attach(bitmap->Bits(), bitmap->Width(), bitmap->Height(), 1492 bitmap->BytesPerRow()); 1493 1494 _DrawBitmap(srcBuffer, bitmap->ColorSpace(), actualBitmapRect, 1495 bitmapRect, viewRect, options); 1496 } 1497 return touched; 1498 } 1499 1500 1501 // #pragma mark - 1502 1503 1504 // FillRegion 1505 BRect 1506 Painter::FillRegion(const BRegion* region) const 1507 { 1508 CHECK_CLIPPING 1509 1510 BRegion copy(*region); 1511 int32 count = copy.CountRects(); 1512 BRect touched = FillRect(copy.RectAt(0)); 1513 for (int32 i = 1; i < count; i++) { 1514 touched = touched | FillRect(copy.RectAt(i)); 1515 } 1516 return touched; 1517 } 1518 1519 1520 // FillRegion 1521 BRect 1522 Painter::FillRegion(const BRegion* region, const BGradient& gradient) const 1523 { 1524 CHECK_CLIPPING 1525 1526 BRegion copy(*region); 1527 int32 count = copy.CountRects(); 1528 BRect touched = FillRect(copy.RectAt(0), gradient); 1529 for (int32 i = 1; i < count; i++) { 1530 touched = touched | FillRect(copy.RectAt(i), gradient); 1531 } 1532 return touched; 1533 } 1534 1535 1536 // InvertRect 1537 BRect 1538 Painter::InvertRect(const BRect& r) const 1539 { 1540 CHECK_CLIPPING 1541 1542 BRegion region(r); 1543 if (fClippingRegion) 1544 region.IntersectWith(fClippingRegion); 1545 1546 // implementation only for B_RGB32 at the moment 1547 int32 count = region.CountRects(); 1548 for (int32 i = 0; i < count; i++) { 1549 _InvertRect32(region.RectAt(i)); 1550 } 1551 return _Clipped(r); 1552 } 1553 1554 1555 // #pragma mark - private 1556 1557 1558 // _Transform 1559 inline void 1560 Painter::_Transform(BPoint* point, bool centerOffset) const 1561 { 1562 // rounding 1563 if (!fSubpixelPrecise) { 1564 // TODO: validate usage of floor() for values < 0 1565 point->x = (int32)point->x; 1566 point->y = (int32)point->y; 1567 } 1568 // this code is supposed to move coordinates to the center of pixels, 1569 // as AGG considers (0,0) to be the "upper left corner" of a pixel, 1570 // but BViews are less strict on those details 1571 if (centerOffset) { 1572 point->x += 0.5; 1573 point->y += 0.5; 1574 } 1575 } 1576 1577 1578 // _Transform 1579 inline BPoint 1580 Painter::_Transform(const BPoint& point, bool centerOffset) const 1581 { 1582 BPoint ret = point; 1583 _Transform(&ret, centerOffset); 1584 return ret; 1585 } 1586 1587 1588 // _Clipped 1589 BRect 1590 Painter::_Clipped(const BRect& rect) const 1591 { 1592 if (rect.IsValid()) 1593 return BRect(rect & fClippingRegion->Frame()); 1594 1595 return BRect(rect); 1596 } 1597 1598 1599 // _UpdateDrawingMode 1600 void 1601 Painter::_UpdateDrawingMode(bool drawingText) 1602 { 1603 // The AGG renderers have their own color setting, however 1604 // almost all drawing mode classes ignore the color given 1605 // by the AGG renderer and use the colors from the PatternHandler 1606 // instead. If we have a B_SOLID_* pattern, we can actually use 1607 // the color in the renderer and special versions of drawing modes 1608 // that don't use PatternHandler and are more efficient. This 1609 // has been implemented for B_OP_COPY and a couple others (the 1610 // DrawingMode*Solid ones) as of now. The PixelFormat knows the 1611 // PatternHandler and makes its decision based on the pattern. 1612 // The last parameter to SetDrawingMode() is a special flag 1613 // for when Painter is used to draw text. In this case, another 1614 // special version of B_OP_COPY is used that acts like R5 in that 1615 // anti-aliased pixel are not rendered against the actual background 1616 // but the current low color instead. This way, the frame buffer 1617 // doesn't need to be read. 1618 // When a solid pattern is used, _SetRendererColor() 1619 // has to be called so that all internal colors in the renderes 1620 // are up to date for use by the solid drawing mode version. 1621 fPixelFormat.SetDrawingMode(fDrawingMode, fAlphaSrcMode, fAlphaFncMode, 1622 drawingText); 1623 if (drawingText) 1624 fPatternHandler.MakeOpCopyColorCache(); 1625 } 1626 1627 1628 // _SetRendererColor 1629 void 1630 Painter::_SetRendererColor(const rgb_color& color) const 1631 { 1632 fRenderer.color(agg::rgba(color.red / 255.0, color.green / 255.0, 1633 color.blue / 255.0, color.alpha / 255.0)); 1634 fSubpixRenderer.color(agg::rgba(color.red / 255.0, color.green / 255.0, 1635 color.blue / 255.0, color.alpha / 255.0)); 1636 // TODO: bitmap fonts not yet correctly setup in AGGTextRenderer 1637 // fRendererBin.color(agg::rgba(color.red / 255.0, color.green / 255.0, 1638 // color.blue / 255.0, color.alpha / 255.0)); 1639 } 1640 1641 1642 // #pragma mark - 1643 1644 1645 // _DrawTriangle 1646 inline BRect 1647 Painter::_DrawTriangle(BPoint pt1, BPoint pt2, BPoint pt3, bool fill) const 1648 { 1649 CHECK_CLIPPING 1650 1651 _Transform(&pt1); 1652 _Transform(&pt2); 1653 _Transform(&pt3); 1654 1655 fPath.remove_all(); 1656 1657 fPath.move_to(pt1.x, pt1.y); 1658 fPath.line_to(pt2.x, pt2.y); 1659 fPath.line_to(pt3.x, pt3.y); 1660 1661 fPath.close_polygon(); 1662 1663 if (fill) 1664 return _FillPath(fPath); 1665 1666 return _StrokePath(fPath); 1667 } 1668 1669 1670 // copy_bitmap_row_cmap8_copy 1671 static inline void 1672 copy_bitmap_row_cmap8_copy(uint8* dst, const uint8* src, int32 numPixels, 1673 const rgb_color* colorMap) 1674 { 1675 uint32* d = (uint32*)dst; 1676 const uint8* s = src; 1677 while (numPixels--) { 1678 const rgb_color c = colorMap[*s++]; 1679 *d++ = (c.alpha << 24) | (c.red << 16) | (c.green << 8) | (c.blue); 1680 } 1681 } 1682 1683 1684 // copy_bitmap_row_cmap8_over 1685 static inline void 1686 copy_bitmap_row_cmap8_over(uint8* dst, const uint8* src, int32 numPixels, 1687 const rgb_color* colorMap) 1688 { 1689 uint32* d = (uint32*)dst; 1690 const uint8* s = src; 1691 while (numPixels--) { 1692 const rgb_color c = colorMap[*s++]; 1693 if (c.alpha) 1694 *d = (c.alpha << 24) | (c.red << 16) | (c.green << 8) | (c.blue); 1695 d++; 1696 } 1697 } 1698 1699 1700 // copy_bitmap_row_bgr32_copy 1701 static inline void 1702 copy_bitmap_row_bgr32_copy(uint8* dst, const uint8* src, int32 numPixels, 1703 const rgb_color* colorMap) 1704 { 1705 memcpy(dst, src, numPixels * 4); 1706 } 1707 1708 1709 // copy_bitmap_row_bgr32_over 1710 static inline void 1711 copy_bitmap_row_bgr32_over(uint8* dst, const uint8* src, int32 numPixels, 1712 const rgb_color* colorMap) 1713 { 1714 uint32* d = (uint32*)dst; 1715 uint32* s = (uint32*)src; 1716 while (numPixels--) { 1717 if (*s != B_TRANSPARENT_MAGIC_RGBA32) 1718 *(uint32*)d = *(uint32*)s; 1719 d++; 1720 s++; 1721 } 1722 } 1723 1724 1725 // copy_bitmap_row_bgr32_alpha 1726 static inline void 1727 copy_bitmap_row_bgr32_alpha(uint8* dst, const uint8* src, int32 numPixels, 1728 const rgb_color* colorMap) 1729 { 1730 uint32* d = (uint32*)dst; 1731 int32 bytes = numPixels * 4; 1732 uint8 buffer[bytes]; 1733 uint8* b = buffer; 1734 while (numPixels--) { 1735 if (src[3] == 255) { 1736 *(uint32*)b = *(uint32*)src; 1737 } else { 1738 *(uint32*)b = *d; 1739 b[0] = ((src[0] - b[0]) * src[3] + (b[0] << 8)) >> 8; 1740 b[1] = ((src[1] - b[1]) * src[3] + (b[1] << 8)) >> 8; 1741 b[2] = ((src[2] - b[2]) * src[3] + (b[2] << 8)) >> 8; 1742 } 1743 d++; 1744 b += 4; 1745 src += 4; 1746 } 1747 memcpy(dst, buffer, bytes); 1748 } 1749 1750 1751 // _TransparentMagicToAlpha 1752 template<typename sourcePixel> 1753 void 1754 Painter::_TransparentMagicToAlpha(sourcePixel* buffer, uint32 width, 1755 uint32 height, uint32 sourceBytesPerRow, sourcePixel transparentMagic, 1756 BBitmap* output) const 1757 { 1758 uint8* sourceRow = (uint8*)buffer; 1759 uint8* destRow = (uint8*)output->Bits(); 1760 uint32 destBytesPerRow = output->BytesPerRow(); 1761 1762 for (uint32 y = 0; y < height; y++) { 1763 sourcePixel* pixel = (sourcePixel*)sourceRow; 1764 uint32* destPixel = (uint32*)destRow; 1765 for (uint32 x = 0; x < width; x++, pixel++, destPixel++) { 1766 if (*pixel == transparentMagic) 1767 *destPixel &= 0x00ffffff; 1768 } 1769 1770 sourceRow += sourceBytesPerRow; 1771 destRow += destBytesPerRow; 1772 } 1773 } 1774 1775 1776 // _DrawBitmap 1777 void 1778 Painter::_DrawBitmap(agg::rendering_buffer& srcBuffer, color_space format, 1779 BRect actualBitmapRect, BRect bitmapRect, BRect viewRect, 1780 uint32 options) const 1781 { 1782 if (!fValidClipping 1783 || !bitmapRect.IsValid() || !bitmapRect.Intersects(actualBitmapRect) 1784 || !viewRect.IsValid()) { 1785 return; 1786 } 1787 1788 if (!fSubpixelPrecise) { 1789 align_rect_to_pixels(&bitmapRect); 1790 align_rect_to_pixels(&viewRect); 1791 } 1792 1793 TRACE("Painter::_DrawBitmap()\n"); 1794 TRACE(" bitmapRect = (%.1f, %.1f) - (%.1f, %.1f)\n", 1795 bitmapRect.left, bitmapRect.top, bitmapRect.right, bitmapRect.bottom); 1796 TRACE(" viewRect = (%.1f, %.1f) - (%.1f, %.1f)\n", 1797 viewRect.left, viewRect.top, viewRect.right, viewRect.bottom); 1798 1799 double xScale = (viewRect.Width() + 1) / (bitmapRect.Width() + 1); 1800 double yScale = (viewRect.Height() + 1) / (bitmapRect.Height() + 1); 1801 1802 if (xScale == 0.0 || yScale == 0.0) 1803 return; 1804 1805 // compensate for the lefttop offset the actualBitmapRect might have 1806 // actualBitmapRect has the right size, but put it at B_ORIGIN 1807 // bitmapRect is already in good coordinates 1808 actualBitmapRect.OffsetBy(-actualBitmapRect.left, -actualBitmapRect.top); 1809 1810 // constrain rect to passed bitmap bounds 1811 // and transfer the changes to the viewRect with the right scale 1812 if (bitmapRect.left < actualBitmapRect.left) { 1813 float diff = actualBitmapRect.left - bitmapRect.left; 1814 viewRect.left += diff * xScale; 1815 bitmapRect.left = actualBitmapRect.left; 1816 } 1817 if (bitmapRect.top < actualBitmapRect.top) { 1818 float diff = actualBitmapRect.top - bitmapRect.top; 1819 viewRect.top += diff * yScale; 1820 bitmapRect.top = actualBitmapRect.top; 1821 } 1822 if (bitmapRect.right > actualBitmapRect.right) { 1823 float diff = bitmapRect.right - actualBitmapRect.right; 1824 viewRect.right -= diff * xScale; 1825 bitmapRect.right = actualBitmapRect.right; 1826 } 1827 if (bitmapRect.bottom > actualBitmapRect.bottom) { 1828 float diff = bitmapRect.bottom - actualBitmapRect.bottom; 1829 viewRect.bottom -= diff * yScale; 1830 bitmapRect.bottom = actualBitmapRect.bottom; 1831 } 1832 1833 double xOffset = viewRect.left - bitmapRect.left; 1834 double yOffset = viewRect.top - bitmapRect.top; 1835 1836 // optimized code path for B_CMAP8 and no scale 1837 if (xScale == 1.0 && yScale == 1.0) { 1838 if (format == B_CMAP8) { 1839 if (fDrawingMode == B_OP_COPY) { 1840 _DrawBitmapNoScale32(copy_bitmap_row_cmap8_copy, 1, 1841 srcBuffer, (int32)xOffset, (int32)yOffset, viewRect); 1842 return; 1843 } 1844 if (fDrawingMode == B_OP_OVER) { 1845 _DrawBitmapNoScale32(copy_bitmap_row_cmap8_over, 1, 1846 srcBuffer, (int32)xOffset, (int32)yOffset, viewRect); 1847 return; 1848 } 1849 } else if (format == B_RGB32) { 1850 if (fDrawingMode == B_OP_OVER) { 1851 _DrawBitmapNoScale32(copy_bitmap_row_bgr32_over, 4, 1852 srcBuffer, (int32)xOffset, (int32)yOffset, viewRect); 1853 return; 1854 } 1855 } 1856 } 1857 1858 BBitmap* temp = NULL; 1859 ObjectDeleter<BBitmap> tempDeleter; 1860 1861 if ((format != B_RGBA32 && format != B_RGB32) 1862 || (format == B_RGB32 && fDrawingMode != B_OP_COPY 1863 #if 1 1864 // Enabling this would make the behavior compatible to BeOS, which 1865 // treats B_RGB32 bitmaps as B_RGB*A*32 bitmaps in B_OP_ALPHA - unlike in 1866 // all other drawing modes, where B_TRANSPARENT_MAGIC_RGBA32 is handled. 1867 // B_RGB32 bitmaps therefore don't draw correctly on BeOS if they actually 1868 // use this color, unless the alpha channel contains 255 for all other 1869 // pixels, which is inconsistent. 1870 && fDrawingMode != B_OP_ALPHA 1871 #endif 1872 )) { 1873 temp = new (nothrow) BBitmap(actualBitmapRect, B_BITMAP_NO_SERVER_LINK, 1874 B_RGBA32); 1875 if (temp == NULL) { 1876 fprintf(stderr, "Painter::_DrawBitmap() - " 1877 "out of memory for creating temporary conversion bitmap\n"); 1878 return; 1879 } 1880 1881 tempDeleter.SetTo(temp); 1882 1883 status_t err = temp->ImportBits(srcBuffer.buf(), 1884 srcBuffer.height() * srcBuffer.stride(), 1885 srcBuffer.stride(), 0, format); 1886 if (err < B_OK) { 1887 fprintf(stderr, "Painter::_DrawBitmap() - " 1888 "colorspace conversion failed: %s\n", strerror(err)); 1889 return; 1890 } 1891 1892 // the original bitmap might have had some of the 1893 // transaparent magic colors set that we now need to 1894 // make transparent in our RGBA32 bitmap again. 1895 switch (format) { 1896 case B_RGB32: 1897 _TransparentMagicToAlpha((uint32 *)srcBuffer.buf(), 1898 srcBuffer.width(), srcBuffer.height(), 1899 srcBuffer.stride(), B_TRANSPARENT_MAGIC_RGBA32, 1900 temp); 1901 break; 1902 1903 // TODO: not sure if this applies to B_RGBA15 too. It 1904 // should not because B_RGBA15 actually has an alpha 1905 // channel itself and it should have been preserved 1906 // when importing the bitmap. Maybe it applies to 1907 // B_RGB16 though? 1908 case B_RGB15: 1909 _TransparentMagicToAlpha((uint16 *)srcBuffer.buf(), 1910 srcBuffer.width(), srcBuffer.height(), 1911 srcBuffer.stride(), B_TRANSPARENT_MAGIC_RGBA15, 1912 temp); 1913 break; 1914 1915 default: 1916 break; 1917 } 1918 1919 srcBuffer.attach((uint8*)temp->Bits(), 1920 (uint32)actualBitmapRect.IntegerWidth() + 1, 1921 (uint32)actualBitmapRect.IntegerHeight() + 1, 1922 temp->BytesPerRow()); 1923 } 1924 1925 // maybe we can use an optimized version if there is no scale 1926 if (xScale == 1.0 && yScale == 1.0) { 1927 if (fDrawingMode == B_OP_COPY) { 1928 _DrawBitmapNoScale32(copy_bitmap_row_bgr32_copy, 4, srcBuffer, 1929 (int32)xOffset, (int32)yOffset, viewRect); 1930 return; 1931 } 1932 if (fDrawingMode == B_OP_OVER || (fDrawingMode == B_OP_ALPHA 1933 && fAlphaSrcMode == B_PIXEL_ALPHA 1934 && fAlphaFncMode == B_ALPHA_OVERLAY)) { 1935 _DrawBitmapNoScale32(copy_bitmap_row_bgr32_alpha, 4, srcBuffer, 1936 (int32)xOffset, (int32)yOffset, viewRect); 1937 return; 1938 } 1939 } 1940 1941 if (fDrawingMode == B_OP_COPY) { 1942 if ((options & B_FILTER_BITMAP_BILINEAR) != 0) { 1943 _DrawBitmapBilinearCopy32(srcBuffer, xOffset, yOffset, xScale, 1944 yScale, viewRect); 1945 } else { 1946 _DrawBitmapNearestNeighborCopy32(srcBuffer, xOffset, yOffset, 1947 xScale, yScale, viewRect); 1948 } 1949 return; 1950 } 1951 1952 // for all other cases (non-optimized drawing mode or scaled drawing) 1953 _DrawBitmapGeneric32(srcBuffer, xOffset, yOffset, xScale, yScale, viewRect, 1954 options); 1955 } 1956 1957 1958 #define DEBUG_DRAW_BITMAP 0 1959 1960 1961 // _DrawBitmapNoScale32 1962 template <class F> 1963 void 1964 Painter::_DrawBitmapNoScale32(F copyRowFunction, uint32 bytesPerSourcePixel, 1965 agg::rendering_buffer& srcBuffer, int32 xOffset, int32 yOffset, 1966 BRect viewRect) const 1967 { 1968 // NOTE: this would crash if viewRect was large enough to read outside the 1969 // bitmap, so make sure this is not the case before calling this function! 1970 uint8* dst = fBuffer.row_ptr(0); 1971 uint32 dstBPR = fBuffer.stride(); 1972 1973 const uint8* src = srcBuffer.row_ptr(0); 1974 uint32 srcBPR = srcBuffer.stride(); 1975 1976 int32 left = (int32)viewRect.left; 1977 int32 top = (int32)viewRect.top; 1978 int32 right = (int32)viewRect.right; 1979 int32 bottom = (int32)viewRect.bottom; 1980 1981 #if DEBUG_DRAW_BITMAP 1982 if (left - xOffset < 0 || left - xOffset >= (int32)srcBuffer.width() || 1983 right - xOffset >= (int32)srcBuffer.width() || 1984 top - yOffset < 0 || top - yOffset >= (int32)srcBuffer.height() || 1985 bottom - yOffset >= (int32)srcBuffer.height()) { 1986 1987 char message[256]; 1988 sprintf(message, "reading outside of bitmap (%ld, %ld, %ld, %ld) " 1989 "(%d, %d) (%ld, %ld)", 1990 left - xOffset, top - yOffset, right - xOffset, bottom - yOffset, 1991 srcBuffer.width(), srcBuffer.height(), xOffset, yOffset); 1992 debugger(message); 1993 } 1994 #endif 1995 1996 const rgb_color* colorMap = SystemPalette(); 1997 1998 // copy rects, iterate over clipping boxes 1999 fBaseRenderer.first_clip_box(); 2000 do { 2001 int32 x1 = max_c(fBaseRenderer.xmin(), left); 2002 int32 x2 = min_c(fBaseRenderer.xmax(), right); 2003 if (x1 <= x2) { 2004 int32 y1 = max_c(fBaseRenderer.ymin(), top); 2005 int32 y2 = min_c(fBaseRenderer.ymax(), bottom); 2006 if (y1 <= y2) { 2007 uint8* dstHandle = dst + y1 * dstBPR + x1 * 4; 2008 const uint8* srcHandle = src + (y1 - yOffset) * srcBPR 2009 + (x1 - xOffset) * bytesPerSourcePixel; 2010 2011 for (; y1 <= y2; y1++) { 2012 copyRowFunction(dstHandle, srcHandle, x2 - x1 + 1, colorMap); 2013 2014 dstHandle += dstBPR; 2015 srcHandle += srcBPR; 2016 } 2017 } 2018 } 2019 } while (fBaseRenderer.next_clip_box()); 2020 } 2021 2022 2023 // _DrawBitmapNearestNeighborCopy32 2024 void 2025 Painter::_DrawBitmapNearestNeighborCopy32(agg::rendering_buffer& srcBuffer, 2026 double xOffset, double yOffset, double xScale, double yScale, 2027 BRect viewRect) const 2028 { 2029 //bigtime_t now = system_time(); 2030 uint32 dstWidth = viewRect.IntegerWidth() + 1; 2031 uint32 dstHeight = viewRect.IntegerHeight() + 1; 2032 uint32 srcWidth = srcBuffer.width(); 2033 uint32 srcHeight = srcBuffer.height(); 2034 2035 // Do not calculate more filter weights than necessary and also 2036 // keep the stack based allocations reasonably sized 2037 if (fClippingRegion->Frame().IntegerWidth() + 1 < (int32)dstWidth) 2038 dstWidth = fClippingRegion->Frame().IntegerWidth() + 1; 2039 if (fClippingRegion->Frame().IntegerHeight() + 1 < (int32)dstHeight) 2040 dstHeight = fClippingRegion->Frame().IntegerHeight() + 1; 2041 2042 // When calculating less filter weights than specified by viewRect, 2043 // we need to compensate the offset. 2044 uint32 filterWeightXIndexOffset = 0; 2045 uint32 filterWeightYIndexOffset = 0; 2046 if (fClippingRegion->Frame().left > viewRect.left) { 2047 filterWeightXIndexOffset = (int32)(fClippingRegion->Frame().left 2048 - viewRect.left); 2049 } 2050 if (fClippingRegion->Frame().top > viewRect.top) { 2051 filterWeightYIndexOffset = (int32)(fClippingRegion->Frame().top 2052 - viewRect.top); 2053 } 2054 2055 // should not pose a problem with stack overflows 2056 // (needs around 6Kb for 1920x1200) 2057 uint16 xIndices[dstWidth]; 2058 uint16 yIndices[dstHeight]; 2059 2060 // Extract the cropping information for the source bitmap, 2061 // If only a part of the source bitmap is to be drawn with scale, 2062 // the offset will be different from the viewRect left top corner. 2063 int32 xBitmapShift = (int32)(viewRect.left - xOffset); 2064 int32 yBitmapShift = (int32)(viewRect.top - yOffset); 2065 2066 for (uint32 i = 0; i < dstWidth; i++) { 2067 // index into source 2068 uint16 index = (uint16)((i + filterWeightXIndexOffset) * srcWidth 2069 / (srcWidth * xScale)); 2070 // round down to get the left pixel 2071 xIndices[i] = index; 2072 // handle cropped source bitmap 2073 xIndices[i] += xBitmapShift; 2074 // precompute index for 32 bit pixels 2075 xIndices[i] *= 4; 2076 } 2077 2078 for (uint32 i = 0; i < dstHeight; i++) { 2079 // index into source 2080 uint16 index = (uint16)((i + filterWeightYIndexOffset) * srcHeight 2081 / (srcHeight * yScale)); 2082 // round down to get the top pixel 2083 yIndices[i] = index; 2084 // handle cropped source bitmap 2085 yIndices[i] += yBitmapShift; 2086 } 2087 //printf("X: %d ... %d, %d (%ld or %f)\n", 2088 // xIndices[0], xIndices[dstWidth - 2], xIndices[dstWidth - 1], dstWidth, 2089 // srcWidth * xScale); 2090 //printf("Y: %d ... %d, %d (%ld or %f)\n", 2091 // yIndices[0], yIndices[dstHeight - 2], yIndices[dstHeight - 1], dstHeight, 2092 // srcHeight * yScale); 2093 2094 const int32 left = (int32)viewRect.left; 2095 const int32 top = (int32)viewRect.top; 2096 const int32 right = (int32)viewRect.right; 2097 const int32 bottom = (int32)viewRect.bottom; 2098 2099 const uint32 dstBPR = fBuffer.stride(); 2100 2101 // iterate over clipping boxes 2102 fBaseRenderer.first_clip_box(); 2103 do { 2104 const int32 x1 = max_c(fBaseRenderer.xmin(), left); 2105 const int32 x2 = min_c(fBaseRenderer.xmax(), right); 2106 if (x1 > x2) 2107 continue; 2108 2109 int32 y1 = max_c(fBaseRenderer.ymin(), top); 2110 int32 y2 = min_c(fBaseRenderer.ymax(), bottom); 2111 if (y1 > y2) 2112 continue; 2113 2114 // buffer offset into destination 2115 uint8* dst = fBuffer.row_ptr(y1) + x1 * 4; 2116 2117 // x and y are needed as indeces into the wheight arrays, so the 2118 // offset into the target buffer needs to be compensated 2119 const int32 xIndexL = x1 - left - filterWeightXIndexOffset; 2120 const int32 xIndexR = x2 - left - filterWeightXIndexOffset; 2121 y1 -= top + filterWeightYIndexOffset; 2122 y2 -= top + filterWeightYIndexOffset; 2123 2124 //printf("x: %ld - %ld\n", xIndexL, xIndexR); 2125 //printf("y: %ld - %ld\n", y1, y2); 2126 2127 for (; y1 <= y2; y1++) { 2128 // buffer offset into source (top row) 2129 register const uint8* src = srcBuffer.row_ptr(yIndices[y1]); 2130 // buffer handle for destination to be incremented per pixel 2131 register uint32* d = (uint32*)dst; 2132 2133 for (int32 x = xIndexL; x <= xIndexR; x++) { 2134 *d = *(uint32*)(src + xIndices[x]); 2135 d++; 2136 } 2137 dst += dstBPR; 2138 } 2139 } while (fBaseRenderer.next_clip_box()); 2140 2141 //printf("draw bitmap %.5fx%.5f: %lld\n", xScale, yScale, system_time() - now); 2142 } 2143 2144 2145 // _DrawBitmapBilinearCopy32 2146 void 2147 Painter::_DrawBitmapBilinearCopy32(agg::rendering_buffer& srcBuffer, 2148 double xOffset, double yOffset, double xScale, double yScale, 2149 BRect viewRect) const 2150 { 2151 //bigtime_t now = system_time(); 2152 uint32 dstWidth = viewRect.IntegerWidth() + 1; 2153 uint32 dstHeight = viewRect.IntegerHeight() + 1; 2154 uint32 srcWidth = srcBuffer.width(); 2155 uint32 srcHeight = srcBuffer.height(); 2156 2157 // Do not calculate more filter weights than necessary and also 2158 // keep the stack based allocations reasonably sized 2159 if (fClippingRegion->Frame().IntegerWidth() + 1 < (int32)dstWidth) 2160 dstWidth = fClippingRegion->Frame().IntegerWidth() + 1; 2161 if (fClippingRegion->Frame().IntegerHeight() + 1 < (int32)dstHeight) 2162 dstHeight = fClippingRegion->Frame().IntegerHeight() + 1; 2163 2164 // When calculating less filter weights than specified by viewRect, 2165 // we need to compensate the offset. 2166 uint32 filterWeightXIndexOffset = 0; 2167 uint32 filterWeightYIndexOffset = 0; 2168 if (fClippingRegion->Frame().left > viewRect.left) { 2169 filterWeightXIndexOffset = (int32)(fClippingRegion->Frame().left 2170 - viewRect.left); 2171 } 2172 if (fClippingRegion->Frame().top > viewRect.top) { 2173 filterWeightYIndexOffset = (int32)(fClippingRegion->Frame().top 2174 - viewRect.top); 2175 } 2176 2177 struct FilterInfo { 2178 uint16 index; // index into source bitmap row/column 2179 uint16 weight; // weight of the pixel at index [0..255] 2180 }; 2181 2182 //#define FILTER_INFOS_ON_HEAP 2183 #ifdef FILTER_INFOS_ON_HEAP 2184 FilterInfo* xWeights = new (nothrow) FilterInfo[dstWidth]; 2185 FilterInfo* yWeights = new (nothrow) FilterInfo[dstHeight]; 2186 if (xWeights == NULL || yWeights == NULL) { 2187 delete[] xWeights; 2188 delete[] yWeights; 2189 return; 2190 } 2191 #else 2192 // stack based saves about 200µs on 1.85 GHz Core 2 Duo 2193 // should not pose a problem with stack overflows 2194 // (needs around 12Kb for 1920x1200) 2195 FilterInfo xWeights[dstWidth]; 2196 FilterInfo yWeights[dstHeight]; 2197 #endif 2198 2199 // Extract the cropping information for the source bitmap, 2200 // If only a part of the source bitmap is to be drawn with scale, 2201 // the offset will be different from the viewRect left top corner. 2202 int32 xBitmapShift = (int32)(viewRect.left - xOffset); 2203 int32 yBitmapShift = (int32)(viewRect.top - yOffset); 2204 2205 for (uint32 i = 0; i < dstWidth; i++) { 2206 // fractional index into source 2207 // NOTE: It is very important to calculate the fractional index 2208 // into the source pixel grid like this to prevent out of bounds 2209 // access! It will result in the rightmost pixel of the destination 2210 // to access the rightmost pixel of the source with a weighting 2211 // of 255. This in turn will trigger an optimization in the loop 2212 // that also prevents out of bounds access. 2213 float index = (i + filterWeightXIndexOffset) * (srcWidth - 1) 2214 / (srcWidth * xScale - 1); 2215 // round down to get the left pixel 2216 xWeights[i].index = (uint16)index; 2217 xWeights[i].weight = 255 - (uint16)((index - xWeights[i].index) * 255); 2218 // handle cropped source bitmap 2219 xWeights[i].index += xBitmapShift; 2220 // precompute index for 32 bit pixels 2221 xWeights[i].index *= 4; 2222 } 2223 2224 for (uint32 i = 0; i < dstHeight; i++) { 2225 // fractional index into source 2226 // NOTE: It is very important to calculate the fractional index 2227 // into the source pixel grid like this to prevent out of bounds 2228 // access! It will result in the bottommost pixel of the destination 2229 // to access the bottommost pixel of the source with a weighting 2230 // of 255. This in turn will trigger an optimization in the loop 2231 // that also prevents out of bounds access. 2232 float index = (i + filterWeightYIndexOffset) * (srcHeight - 1) 2233 / (srcHeight * yScale - 1); 2234 // round down to get the top pixel 2235 yWeights[i].index = (uint16)index; 2236 yWeights[i].weight = 255 - (uint16)((index - yWeights[i].index) * 255); 2237 // handle cropped source bitmap 2238 yWeights[i].index += yBitmapShift; 2239 } 2240 //printf("X: %d/%d ... %d/%d, %d/%d (%ld)\n", 2241 // xWeights[0].index, xWeights[0].weight, 2242 // xWeights[dstWidth - 2].index, xWeights[dstWidth - 2].weight, 2243 // xWeights[dstWidth - 1].index, xWeights[dstWidth - 1].weight, 2244 // dstWidth); 2245 //printf("Y: %d/%d ... %d/%d, %d/%d (%ld)\n", 2246 // yWeights[0].index, yWeights[0].weight, 2247 // yWeights[dstHeight - 2].index, yWeights[dstHeight - 2].weight, 2248 // yWeights[dstHeight - 1].index, yWeights[dstHeight - 1].weight, 2249 // dstHeight); 2250 2251 const int32 left = (int32)viewRect.left; 2252 const int32 top = (int32)viewRect.top; 2253 const int32 right = (int32)viewRect.right; 2254 const int32 bottom = (int32)viewRect.bottom; 2255 2256 const uint32 dstBPR = fBuffer.stride(); 2257 const uint32 srcBPR = srcBuffer.stride(); 2258 2259 // Figure out which version of the code we want to use... 2260 enum { 2261 kOptimizeForLowFilterRatio = 0, 2262 kUseDefaultVersion, 2263 kUseSIMDVersion 2264 }; 2265 2266 int codeSelect = kUseDefaultVersion; 2267 2268 uint32 neededSIMDFlags = APPSERVER_SIMD_MMX | APPSERVER_SIMD_SSE; 2269 if ((gAppServerSIMDFlags & neededSIMDFlags) == neededSIMDFlags) 2270 codeSelect = kUseSIMDVersion; 2271 else { 2272 if (xScale == yScale && (xScale == 1.5 || xScale == 2.0 2273 || xScale == 2.5 || xScale == 3.0)) { 2274 codeSelect = kOptimizeForLowFilterRatio; 2275 } 2276 } 2277 2278 // iterate over clipping boxes 2279 fBaseRenderer.first_clip_box(); 2280 do { 2281 const int32 x1 = max_c(fBaseRenderer.xmin(), left); 2282 const int32 x2 = min_c(fBaseRenderer.xmax(), right); 2283 if (x1 > x2) 2284 continue; 2285 2286 int32 y1 = max_c(fBaseRenderer.ymin(), top); 2287 int32 y2 = min_c(fBaseRenderer.ymax(), bottom); 2288 if (y1 > y2) 2289 continue; 2290 2291 // buffer offset into destination 2292 uint8* dst = fBuffer.row_ptr(y1) + x1 * 4; 2293 2294 // x and y are needed as indeces into the wheight arrays, so the 2295 // offset into the target buffer needs to be compensated 2296 const int32 xIndexL = x1 - left - filterWeightXIndexOffset; 2297 const int32 xIndexR = x2 - left - filterWeightXIndexOffset; 2298 y1 -= top + filterWeightYIndexOffset; 2299 y2 -= top + filterWeightYIndexOffset; 2300 2301 //printf("x: %ld - %ld\n", xIndexL, xIndexR); 2302 //printf("y: %ld - %ld\n", y1, y2); 2303 2304 switch (codeSelect) { 2305 case kOptimizeForLowFilterRatio: 2306 { 2307 // In this mode, we anticipate to hit many destination pixels 2308 // that map directly to a source pixel, we have more branches 2309 // in the inner loop but save time because of the special 2310 // cases. If there are too few direct hit pixels, the branches 2311 // only waste time. 2312 for (; y1 <= y2; y1++) { 2313 // cache the weight of the top and bottom row 2314 const uint16 wTop = yWeights[y1].weight; 2315 const uint16 wBottom = 255 - yWeights[y1].weight; 2316 2317 // buffer offset into source (top row) 2318 register const uint8* src 2319 = srcBuffer.row_ptr(yWeights[y1].index); 2320 // buffer handle for destination to be incremented per 2321 // pixel 2322 register uint8* d = dst; 2323 2324 if (wTop == 255) { 2325 for (int32 x = xIndexL; x <= xIndexR; x++) { 2326 const uint8* s = src + xWeights[x].index; 2327 // This case is important to prevent out 2328 // of bounds access at bottom edge of the source 2329 // bitmap. If the scale is low and integer, it will 2330 // also help the speed. 2331 if (xWeights[x].weight == 255) { 2332 // As above, but to prevent out of bounds 2333 // on the right edge. 2334 *(uint32*)d = *(uint32*)s; 2335 } else { 2336 // Only the left and right pixels are 2337 // interpolated, since the top row has 100% 2338 // weight. 2339 const uint16 wLeft = xWeights[x].weight; 2340 const uint16 wRight = 255 - wLeft; 2341 d[0] = (s[0] * wLeft + s[4] * wRight) >> 8; 2342 d[1] = (s[1] * wLeft + s[5] * wRight) >> 8; 2343 d[2] = (s[2] * wLeft + s[6] * wRight) >> 8; 2344 } 2345 d += 4; 2346 } 2347 } else { 2348 for (int32 x = xIndexL; x <= xIndexR; x++) { 2349 const uint8* s = src + xWeights[x].index; 2350 if (xWeights[x].weight == 255) { 2351 // Prevent out of bounds access on the right 2352 // edge or simply speed up. 2353 const uint8* sBottom = s + srcBPR; 2354 d[0] = (s[0] * wTop + sBottom[0] * wBottom) 2355 >> 8; 2356 d[1] = (s[1] * wTop + sBottom[1] * wBottom) 2357 >> 8; 2358 d[2] = (s[2] * wTop + sBottom[2] * wBottom) 2359 >> 8; 2360 } else { 2361 // calculate the weighted sum of all four 2362 // interpolated pixels 2363 const uint16 wLeft = xWeights[x].weight; 2364 const uint16 wRight = 255 - wLeft; 2365 // left and right of top row 2366 uint32 t0 = (s[0] * wLeft + s[4] * wRight) 2367 * wTop; 2368 uint32 t1 = (s[1] * wLeft + s[5] * wRight) 2369 * wTop; 2370 uint32 t2 = (s[2] * wLeft + s[6] * wRight) 2371 * wTop; 2372 2373 // left and right of bottom row 2374 s += srcBPR; 2375 t0 += (s[0] * wLeft + s[4] * wRight) * wBottom; 2376 t1 += (s[1] * wLeft + s[5] * wRight) * wBottom; 2377 t2 += (s[2] * wLeft + s[6] * wRight) * wBottom; 2378 2379 d[0] = t0 >> 16; 2380 d[1] = t1 >> 16; 2381 d[2] = t2 >> 16; 2382 } 2383 d += 4; 2384 } 2385 } 2386 dst += dstBPR; 2387 } 2388 break; 2389 } 2390 2391 case kUseDefaultVersion: 2392 { 2393 // In this mode we anticipate many pixels wich need filtering, 2394 // there are no special cases for direct hit pixels except for 2395 // the last column/row and the right/bottom corner pixel. 2396 2397 // The last column/row handling does not need to be performed 2398 // for all clipping rects! 2399 int32 yMax = y2; 2400 if (yWeights[yMax].weight == 255) 2401 yMax--; 2402 int32 xIndexMax = xIndexR; 2403 if (xWeights[xIndexMax].weight == 255) 2404 xIndexMax--; 2405 2406 for (; y1 <= yMax; y1++) { 2407 // cache the weight of the top and bottom row 2408 const uint16 wTop = yWeights[y1].weight; 2409 const uint16 wBottom = 255 - yWeights[y1].weight; 2410 2411 // buffer offset into source (top row) 2412 register const uint8* src 2413 = srcBuffer.row_ptr(yWeights[y1].index); 2414 // buffer handle for destination to be incremented per 2415 // pixel 2416 register uint8* d = dst; 2417 2418 for (int32 x = xIndexL; x <= xIndexMax; x++) { 2419 const uint8* s = src + xWeights[x].index; 2420 // calculate the weighted sum of all four 2421 // interpolated pixels 2422 const uint16 wLeft = xWeights[x].weight; 2423 const uint16 wRight = 255 - wLeft; 2424 // left and right of top row 2425 uint32 t0 = (s[0] * wLeft + s[4] * wRight) * wTop; 2426 uint32 t1 = (s[1] * wLeft + s[5] * wRight) * wTop; 2427 uint32 t2 = (s[2] * wLeft + s[6] * wRight) * wTop; 2428 2429 // left and right of bottom row 2430 s += srcBPR; 2431 t0 += (s[0] * wLeft + s[4] * wRight) * wBottom; 2432 t1 += (s[1] * wLeft + s[5] * wRight) * wBottom; 2433 t2 += (s[2] * wLeft + s[6] * wRight) * wBottom; 2434 d[0] = t0 >> 16; 2435 d[1] = t1 >> 16; 2436 d[2] = t2 >> 16; 2437 d += 4; 2438 } 2439 // last column of pixels if necessary 2440 if (xIndexMax < xIndexR) { 2441 const uint8* s = src + xWeights[xIndexR].index; 2442 const uint8* sBottom = s + srcBPR; 2443 d[0] = (s[0] * wTop + sBottom[0] * wBottom) >> 8; 2444 d[1] = (s[1] * wTop + sBottom[1] * wBottom) >> 8; 2445 d[2] = (s[2] * wTop + sBottom[2] * wBottom) >> 8; 2446 } 2447 2448 dst += dstBPR; 2449 } 2450 2451 // last row of pixels if necessary 2452 // buffer offset into source (bottom row) 2453 register const uint8* src 2454 = srcBuffer.row_ptr(yWeights[y2].index); 2455 // buffer handle for destination to be incremented per pixel 2456 register uint8* d = dst; 2457 2458 if (yMax < y2) { 2459 for (int32 x = xIndexL; x <= xIndexMax; x++) { 2460 const uint8* s = src + xWeights[x].index; 2461 const uint16 wLeft = xWeights[x].weight; 2462 const uint16 wRight = 255 - wLeft; 2463 d[0] = (s[0] * wLeft + s[4] * wRight) >> 8; 2464 d[1] = (s[1] * wLeft + s[5] * wRight) >> 8; 2465 d[2] = (s[2] * wLeft + s[6] * wRight) >> 8; 2466 d += 4; 2467 } 2468 } 2469 2470 // pixel in bottom right corner if necessary 2471 if (yMax < y2 && xIndexMax < xIndexR) { 2472 const uint8* s = src + xWeights[xIndexR].index; 2473 *(uint32*)d = *(uint32*)s; 2474 } 2475 break; 2476 } 2477 2478 #ifdef __INTEL__ 2479 case kUseSIMDVersion: 2480 { 2481 // Basically the same as the "standard" mode, but we use SIMD 2482 // routines for the processing of the single display lines. 2483 2484 // The last column/row handling does not need to be performed 2485 // for all clipping rects! 2486 int32 yMax = y2; 2487 if (yWeights[yMax].weight == 255) 2488 yMax--; 2489 int32 xIndexMax = xIndexR; 2490 if (xWeights[xIndexMax].weight == 255) 2491 xIndexMax--; 2492 2493 for (; y1 <= yMax; y1++) { 2494 // cache the weight of the top and bottom row 2495 const uint16 wTop = yWeights[y1].weight; 2496 const uint16 wBottom = 255 - yWeights[y1].weight; 2497 2498 // buffer offset into source (top row) 2499 const uint8* src = srcBuffer.row_ptr(yWeights[y1].index); 2500 // buffer handle for destination to be incremented per 2501 // pixel 2502 uint8* d = dst; 2503 bilinear_scale_xloop_mmxsse(src, dst, xWeights, xIndexL, 2504 xIndexMax, wTop, srcBPR); 2505 // increase pointer by processed pixels 2506 d += (xIndexMax - xIndexL + 1) * 4; 2507 2508 // last column of pixels if necessary 2509 if (xIndexMax < xIndexR) { 2510 const uint8* s = src + xWeights[xIndexR].index; 2511 const uint8* sBottom = s + srcBPR; 2512 d[0] = (s[0] * wTop + sBottom[0] * wBottom) >> 8; 2513 d[1] = (s[1] * wTop + sBottom[1] * wBottom) >> 8; 2514 d[2] = (s[2] * wTop + sBottom[2] * wBottom) >> 8; 2515 } 2516 2517 dst += dstBPR; 2518 } 2519 2520 // last row of pixels if necessary 2521 // buffer offset into source (bottom row) 2522 register const uint8* src 2523 = srcBuffer.row_ptr(yWeights[y2].index); 2524 // buffer handle for destination to be incremented per pixel 2525 register uint8* d = dst; 2526 2527 if (yMax < y2) { 2528 for (int32 x = xIndexL; x <= xIndexMax; x++) { 2529 const uint8* s = src + xWeights[x].index; 2530 const uint16 wLeft = xWeights[x].weight; 2531 const uint16 wRight = 255 - wLeft; 2532 d[0] = (s[0] * wLeft + s[4] * wRight) >> 8; 2533 d[1] = (s[1] * wLeft + s[5] * wRight) >> 8; 2534 d[2] = (s[2] * wLeft + s[6] * wRight) >> 8; 2535 d += 4; 2536 } 2537 } 2538 2539 // pixel in bottom right corner if necessary 2540 if (yMax < y2 && xIndexMax < xIndexR) { 2541 const uint8* s = src + xWeights[xIndexR].index; 2542 *(uint32*)d = *(uint32*)s; 2543 } 2544 break; 2545 } 2546 #endif // __INTEL__ 2547 } 2548 } while (fBaseRenderer.next_clip_box()); 2549 2550 #ifdef FILTER_INFOS_ON_HEAP 2551 delete[] xWeights; 2552 delete[] yWeights; 2553 #endif 2554 //printf("draw bitmap %.5fx%.5f: %lld\n", xScale, yScale, system_time() - now); 2555 } 2556 2557 2558 // _DrawBitmapGeneric32 2559 void 2560 Painter::_DrawBitmapGeneric32(agg::rendering_buffer& srcBuffer, 2561 double xOffset, double yOffset, double xScale, double yScale, 2562 BRect viewRect, uint32 options) const 2563 { 2564 TRACE("Painter::_DrawBitmapGeneric32()\n"); 2565 TRACE(" offset: %.1f, %.1f\n", xOffset, yOffset); 2566 TRACE(" scale: %.3f, %.3f\n", xScale, yScale); 2567 TRACE(" viewRect: (%.1f, %.1f) - (%.1f, %.1f)\n", 2568 viewRect.left, viewRect.top, viewRect.right, viewRect.bottom); 2569 // AGG pipeline 2570 2571 // pixel format attached to bitmap 2572 typedef agg::pixfmt_bgra32 pixfmt_image; 2573 pixfmt_image pixf_img(srcBuffer); 2574 2575 agg::trans_affine srcMatrix; 2576 // NOTE: R5 seems to ignore this offset when drawing bitmaps 2577 // srcMatrix *= agg::trans_affine_translation(-actualBitmapRect.left, 2578 // -actualBitmapRect.top); 2579 2580 agg::trans_affine imgMatrix; 2581 imgMatrix *= agg::trans_affine_translation(xOffset - viewRect.left, 2582 yOffset - viewRect.top); 2583 imgMatrix *= agg::trans_affine_scaling(xScale, yScale); 2584 imgMatrix *= agg::trans_affine_translation(viewRect.left, viewRect.top); 2585 imgMatrix.invert(); 2586 2587 // image interpolator 2588 typedef agg::span_interpolator_linear<> interpolator_type; 2589 interpolator_type interpolator(imgMatrix); 2590 2591 // scanline allocator 2592 agg::span_allocator<pixfmt_image::color_type> spanAllocator; 2593 2594 // image accessor attached to pixel format of bitmap 2595 typedef agg::image_accessor_clip<pixfmt_image> source_type; 2596 source_type source(pixf_img, agg::rgba8(0, 0, 0, 0)); 2597 2598 // clip to the current clipping region's frame 2599 viewRect = viewRect & fClippingRegion->Frame(); 2600 // convert to pixel coords (versus pixel indices) 2601 viewRect.right++; 2602 viewRect.bottom++; 2603 2604 // path enclosing the bitmap 2605 fPath.remove_all(); 2606 fPath.move_to(viewRect.left, viewRect.top); 2607 fPath.line_to(viewRect.right, viewRect.top); 2608 fPath.line_to(viewRect.right, viewRect.bottom); 2609 fPath.line_to(viewRect.left, viewRect.bottom); 2610 fPath.close_polygon(); 2611 2612 agg::conv_transform<agg::path_storage> transformedPath(fPath, srcMatrix); 2613 fRasterizer.reset(); 2614 fRasterizer.add_path(transformedPath); 2615 2616 if ((options & B_FILTER_BITMAP_BILINEAR) != 0) { 2617 // image filter (bilinear) 2618 typedef agg::span_image_filter_rgba_bilinear< 2619 source_type, interpolator_type> span_gen_type; 2620 span_gen_type spanGenerator(source, interpolator); 2621 2622 // render the path with the bitmap as scanline fill 2623 agg::render_scanlines_aa(fRasterizer, fUnpackedScanline, fBaseRenderer, 2624 spanAllocator, spanGenerator); 2625 } else { 2626 // image filter (nearest neighbor) 2627 typedef agg::span_image_filter_rgba_nn< 2628 source_type, interpolator_type> span_gen_type; 2629 span_gen_type spanGenerator(source, interpolator); 2630 2631 // render the path with the bitmap as scanline fill 2632 agg::render_scanlines_aa(fRasterizer, fUnpackedScanline, fBaseRenderer, 2633 spanAllocator, spanGenerator); 2634 } 2635 } 2636 2637 2638 // _InvertRect32 2639 void 2640 Painter::_InvertRect32(BRect r) const 2641 { 2642 int32 width = r.IntegerWidth() + 1; 2643 for (int32 y = (int32)r.top; y <= (int32)r.bottom; y++) { 2644 uint8* dst = fBuffer.row_ptr(y); 2645 dst += (int32)r.left * 4; 2646 for (int32 i = 0; i < width; i++) { 2647 dst[0] = 255 - dst[0]; 2648 dst[1] = 255 - dst[1]; 2649 dst[2] = 255 - dst[2]; 2650 dst += 4; 2651 } 2652 } 2653 } 2654 2655 2656 // _BlendRect32 2657 void 2658 Painter::_BlendRect32(const BRect& r, const rgb_color& c) const 2659 { 2660 if (!fValidClipping) 2661 return; 2662 2663 uint8* dst = fBuffer.row_ptr(0); 2664 uint32 bpr = fBuffer.stride(); 2665 2666 int32 left = (int32)r.left; 2667 int32 top = (int32)r.top; 2668 int32 right = (int32)r.right; 2669 int32 bottom = (int32)r.bottom; 2670 2671 // fill rects, iterate over clipping boxes 2672 fBaseRenderer.first_clip_box(); 2673 do { 2674 int32 x1 = max_c(fBaseRenderer.xmin(), left); 2675 int32 x2 = min_c(fBaseRenderer.xmax(), right); 2676 if (x1 <= x2) { 2677 int32 y1 = max_c(fBaseRenderer.ymin(), top); 2678 int32 y2 = min_c(fBaseRenderer.ymax(), bottom); 2679 2680 uint8* offset = dst + x1 * 4 + y1 * bpr; 2681 for (; y1 <= y2; y1++) { 2682 blend_line32(offset, x2 - x1 + 1, c.red, c.green, c.blue, 2683 c.alpha); 2684 offset += bpr; 2685 } 2686 } 2687 } while (fBaseRenderer.next_clip_box()); 2688 } 2689 2690 2691 // #pragma mark - 2692 2693 2694 template<class VertexSource> 2695 BRect 2696 Painter::_BoundingBox(VertexSource& path) const 2697 { 2698 double left = 0.0; 2699 double top = 0.0; 2700 double right = -1.0; 2701 double bottom = -1.0; 2702 uint32 pathID[1]; 2703 pathID[0] = 0; 2704 agg::bounding_rect(path, pathID, 0, 1, &left, &top, &right, &bottom); 2705 return BRect(left, top, right, bottom); 2706 } 2707 2708 2709 // agg_line_cap_mode_for 2710 inline agg::line_cap_e 2711 agg_line_cap_mode_for(cap_mode mode) 2712 { 2713 switch (mode) { 2714 case B_BUTT_CAP: 2715 return agg::butt_cap; 2716 case B_SQUARE_CAP: 2717 return agg::square_cap; 2718 case B_ROUND_CAP: 2719 return agg::round_cap; 2720 } 2721 return agg::butt_cap; 2722 } 2723 2724 2725 // agg_line_join_mode_for 2726 inline agg::line_join_e 2727 agg_line_join_mode_for(join_mode mode) 2728 { 2729 switch (mode) { 2730 case B_MITER_JOIN: 2731 return agg::miter_join; 2732 case B_ROUND_JOIN: 2733 return agg::round_join; 2734 case B_BEVEL_JOIN: 2735 case B_BUTT_JOIN: // ?? 2736 case B_SQUARE_JOIN: // ?? 2737 return agg::bevel_join; 2738 } 2739 return agg::miter_join; 2740 } 2741 2742 // _StrokePath 2743 template<class VertexSource> 2744 BRect 2745 Painter::_StrokePath(VertexSource& path) const 2746 { 2747 agg::conv_stroke<VertexSource> stroke(path); 2748 stroke.width(fPenSize); 2749 2750 stroke.line_cap(agg_line_cap_mode_for(fLineCapMode)); 2751 stroke.line_join(agg_line_join_mode_for(fLineJoinMode)); 2752 stroke.miter_limit(fMiterLimit); 2753 2754 if (gSubpixelAntialiasing) { 2755 fSubpixRasterizer.reset(); 2756 fSubpixRasterizer.add_path(stroke); 2757 2758 agg::render_scanlines(fSubpixRasterizer, 2759 fSubpixPackedScanline, fSubpixRenderer); 2760 } else { 2761 fRasterizer.reset(); 2762 fRasterizer.add_path(stroke); 2763 2764 agg::render_scanlines(fRasterizer, fPackedScanline, fRenderer); 2765 } 2766 2767 BRect touched = _BoundingBox(path); 2768 float penSize = ceilf(fPenSize / 2.0); 2769 touched.InsetBy(-penSize, -penSize); 2770 2771 return _Clipped(touched); 2772 } 2773 2774 2775 // _FillPath 2776 template<class VertexSource> 2777 BRect 2778 Painter::_FillPath(VertexSource& path) const 2779 { 2780 if (gSubpixelAntialiasing) { 2781 fSubpixRasterizer.reset(); 2782 fSubpixRasterizer.add_path(path); 2783 agg::render_scanlines(fSubpixRasterizer, 2784 fSubpixPackedScanline, fSubpixRenderer); 2785 } else { 2786 fRasterizer.reset(); 2787 fRasterizer.add_path(path); 2788 agg::render_scanlines(fRasterizer, fPackedScanline, fRenderer); 2789 } 2790 2791 return _Clipped(_BoundingBox(path)); 2792 } 2793 2794 2795 // _FillPath 2796 template<class VertexSource> 2797 BRect 2798 Painter::_FillPath(VertexSource& path, const BGradient& gradient) const 2799 { 2800 GTRACE("Painter::_FillPath\n"); 2801 2802 switch(gradient.GetType()) { 2803 case BGradient::TYPE_LINEAR: { 2804 GTRACE(("Painter::_FillPath> type == TYPE_LINEAR\n")); 2805 _FillPathGradientLinear(path, *((const BGradientLinear*) &gradient)); 2806 break; 2807 } 2808 case BGradient::TYPE_RADIAL: { 2809 GTRACE(("Painter::_FillPathGradient> type == TYPE_RADIAL\n")); 2810 _FillPathGradientRadial(path, 2811 *((const BGradientRadial*) &gradient)); 2812 break; 2813 } 2814 case BGradient::TYPE_RADIAL_FOCUS: { 2815 GTRACE(("Painter::_FillPathGradient> type == TYPE_RADIAL_FOCUS\n")); 2816 _FillPathGradientRadialFocus(path, 2817 *((const BGradientRadialFocus*) &gradient)); 2818 break; 2819 } 2820 case BGradient::TYPE_DIAMOND: { 2821 GTRACE(("Painter::_FillPathGradient> type == TYPE_DIAMOND\n")); 2822 _FillPathGradientDiamond(path, 2823 *((const BGradientDiamond*) &gradient)); 2824 break; 2825 } 2826 case BGradient::TYPE_CONIC: { 2827 GTRACE(("Painter::_FillPathGradient> type == TYPE_CONIC\n")); 2828 _FillPathGradientConic(path, 2829 *((const BGradientConic*) &gradient)); 2830 break; 2831 } 2832 case BGradient::TYPE_NONE: { 2833 GTRACE(("Painter::_FillPathGradient> type == TYPE_NONE\n")); 2834 break; 2835 } 2836 } 2837 2838 return _Clipped(_BoundingBox(path)); 2839 } 2840 2841 2842 // _MakeGradient 2843 void 2844 Painter::_MakeGradient(const BGradient& gradient, int32 colorCount, 2845 uint32* colors, int32 arrayOffset, int32 arraySize) const 2846 { 2847 BGradient::ColorStop* from = gradient.ColorStopAt(0); 2848 2849 if (!from) 2850 return; 2851 2852 // current index into "colors" array 2853 // int32 index = (int32)floorf(colorCount * from->offset + 0.5) 2854 // + arrayOffset; 2855 int32 index = (int32)floorf(colorCount * from->offset / 255 + 0.5) 2856 + arrayOffset; 2857 if (index > arraySize) 2858 index = arraySize; 2859 // Make sure we fill the entire array in case the gradient is outside. 2860 if (index > 0) { 2861 uint8* c = (uint8*)&colors[0]; 2862 for (int32 i = 0; i < index; i++) { 2863 c[0] = from->color.blue; 2864 c[1] = from->color.green; 2865 c[2] = from->color.red; 2866 c[3] = from->color.alpha; 2867 c += 4; 2868 } 2869 } 2870 2871 // interpolate "from" to "to" 2872 int32 stopCount = gradient.CountColorStops(); 2873 for (int32 i = 1; i < stopCount; i++) { 2874 // find the step with the next offset 2875 BGradient::ColorStop* to = gradient.ColorStopAtFast(i); 2876 2877 // interpolate 2878 // int32 offset = (int32)floorf((colorCount - 1) * to->offset + 0.5); 2879 int32 offset = (int32)floorf((colorCount - 1) 2880 * to->offset / 255 + 0.5); 2881 if (offset > colorCount - 1) 2882 offset = colorCount - 1; 2883 offset += arrayOffset; 2884 int32 dist = offset - index; 2885 if (dist >= 0) { 2886 int32 startIndex = max_c(index, 0); 2887 int32 stopIndex = min_c(offset, arraySize - 1); 2888 uint8* c = (uint8*)&colors[startIndex]; 2889 for (int32 i = startIndex; i <= stopIndex; i++) { 2890 float f = (float)(offset - i) / (float)(dist + 1); 2891 float t = 1.0 - f; 2892 c[0] = (uint8)floorf(from->color.blue * f 2893 + to->color.blue * t + 0.5); 2894 c[1] = (uint8)floorf(from->color.green * f 2895 + to->color.green * t + 0.5); 2896 c[2] = (uint8)floorf(from->color.red * f 2897 + to->color.red * t + 0.5); 2898 c[3] = (uint8)floorf(from->color.alpha * f 2899 + to->color.alpha * t + 0.5); 2900 c += 4; 2901 } 2902 } 2903 index = offset + 1; 2904 // the current "to" will be the "from" in the next interpolation 2905 from = to; 2906 } 2907 // make sure we fill the entire array 2908 if (index < arraySize) { 2909 int32 startIndex = max_c(index, 0); 2910 uint8* c = (uint8*)&colors[startIndex]; 2911 for (int32 i = startIndex; i < arraySize; 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 2921 2922 // _MakeGradient 2923 template<class Array> 2924 void 2925 Painter::_MakeGradient(Array& array, const BGradient& gradient) const 2926 { 2927 for (int i = 0; i < gradient.CountColorStops() - 1; i++) { 2928 BGradient::ColorStop* from = gradient.ColorStopAtFast(i); 2929 BGradient::ColorStop* to = gradient.ColorStopAtFast(i + 1); 2930 agg::rgba8 fromColor(from->color.red, from->color.green, 2931 from->color.blue, from->color.alpha); 2932 agg::rgba8 toColor(to->color.red, to->color.green, 2933 to->color.blue, to->color.alpha); 2934 GTRACE("Painter::_MakeGradient> fromColor(%d, %d, %d) offset = %f\n", 2935 fromColor.r, fromColor.g, fromColor.b, from->offset); 2936 GTRACE("Painter::_MakeGradient> toColor(%d, %d, %d) offset = %f\n", 2937 toColor.r, toColor.g, toColor.b, to->offset); 2938 float dist = to->offset - from->offset; 2939 GTRACE("Painter::_MakeGradient> dist = %f\n", dist); 2940 // TODO: Review this... offset should better be on [0..1] 2941 if (dist > 0) { 2942 for (int j = (int)from->offset; j <= (int)to->offset; j++) { 2943 float f = (float)(to->offset - j) / (float)(dist + 1); 2944 array[j] = toColor.gradient(fromColor, f); 2945 GTRACE("Painter::_MakeGradient> array[%d](%d, %d, %d)\n", 2946 array[j].r, array[j].g, array[j].b); 2947 } 2948 } 2949 } 2950 } 2951 2952 2953 // _CalcLinearGradientTransform 2954 void Painter::_CalcLinearGradientTransform(BPoint startPoint, BPoint endPoint, 2955 agg::trans_affine& matrix, float gradient_d2) const 2956 { 2957 float dx = endPoint.x - startPoint.x; 2958 float dy = endPoint.y - startPoint.y; 2959 2960 matrix.reset(); 2961 matrix *= agg::trans_affine_scaling(sqrt(dx * dx + dy * dy) / gradient_d2); 2962 matrix *= agg::trans_affine_rotation(atan2(dy, dx)); 2963 matrix *= agg::trans_affine_translation(startPoint.x, startPoint.y); 2964 matrix.invert(); 2965 } 2966 2967 2968 // _FillPathGradientLinear 2969 template<class VertexSource> 2970 void 2971 Painter::_FillPathGradientLinear(VertexSource& path, 2972 const BGradientLinear& linear) const 2973 { 2974 GTRACE("Painter::_FillPathGradientLinear\n"); 2975 2976 BPoint start = linear.Start(); 2977 BPoint end = linear.End(); 2978 2979 typedef agg::span_interpolator_linear<> interpolator_type; 2980 typedef agg::pod_auto_array<agg::rgba8, 256> color_array_type; 2981 typedef agg::span_allocator<agg::rgba8> span_allocator_type; 2982 typedef agg::gradient_x gradient_func_type; 2983 typedef agg::span_gradient<agg::rgba8, interpolator_type, 2984 gradient_func_type, color_array_type> span_gradient_type; 2985 typedef agg::renderer_scanline_aa<renderer_base, span_allocator_type, 2986 span_gradient_type> renderer_gradient_type; 2987 2988 gradient_func_type gradientFunc; 2989 agg::trans_affine gradientMatrix; 2990 interpolator_type spanInterpolator(gradientMatrix); 2991 span_allocator_type spanAllocator; 2992 color_array_type colorArray; 2993 2994 _MakeGradient(colorArray, linear); 2995 2996 span_gradient_type spanGradient(spanInterpolator, gradientFunc, colorArray, 2997 0, 100); 2998 2999 renderer_gradient_type gradientRenderer(fBaseRenderer, spanAllocator, 3000 spanGradient); 3001 3002 _CalcLinearGradientTransform(start, end, gradientMatrix); 3003 3004 fRasterizer.reset(); 3005 fRasterizer.add_path(path); 3006 agg::render_scanlines(fRasterizer, fPackedScanline, gradientRenderer); 3007 } 3008 3009 3010 // _FillPathGradientRadial 3011 template<class VertexSource> 3012 void 3013 Painter::_FillPathGradientRadial(VertexSource& path, 3014 const BGradientRadial& radial) const 3015 { 3016 GTRACE("Painter::_FillPathGradientRadial\n"); 3017 3018 BPoint center = radial.Center(); 3019 // TODO: finish this 3020 // float radius = radial.Radius(); 3021 3022 typedef agg::span_interpolator_linear<> interpolator_type; 3023 typedef agg::pod_auto_array<agg::rgba8, 256> color_array_type; 3024 typedef agg::span_allocator<agg::rgba8> span_allocator_type; 3025 typedef agg::gradient_radial gradient_func_type; 3026 typedef agg::span_gradient<agg::rgba8, interpolator_type, 3027 gradient_func_type, color_array_type> span_gradient_type; 3028 typedef agg::renderer_scanline_aa<renderer_base, span_allocator_type, 3029 span_gradient_type> renderer_gradient_type; 3030 3031 gradient_func_type gradientFunc; 3032 agg::trans_affine gradientMatrix; 3033 interpolator_type spanInterpolator(gradientMatrix); 3034 span_allocator_type spanAllocator; 3035 color_array_type colorArray; 3036 3037 _MakeGradient(colorArray, radial); 3038 3039 span_gradient_type spanGradient(spanInterpolator, gradientFunc, colorArray, 3040 0, 100); 3041 3042 renderer_gradient_type gradientRenderer(fBaseRenderer, spanAllocator, 3043 spanGradient); 3044 3045 gradientMatrix.reset(); 3046 gradientMatrix *= agg::trans_affine_translation(center.x, center.y); 3047 gradientMatrix.invert(); 3048 3049 // _CalcLinearGradientTransform(start, end, gradientMtx); 3050 3051 fRasterizer.reset(); 3052 fRasterizer.add_path(path); 3053 agg::render_scanlines(fRasterizer, fPackedScanline, gradientRenderer); 3054 } 3055 3056 3057 // _FillPathGradientRadialFocus 3058 template<class VertexSource> 3059 void 3060 Painter::_FillPathGradientRadialFocus(VertexSource& path, 3061 const BGradientRadialFocus& focus) const 3062 { 3063 GTRACE("Painter::_FillPathGradientRadialFocus\n"); 3064 3065 BPoint center = focus.Center(); 3066 // TODO: finish this. 3067 // BPoint focal = focus.Focal(); 3068 // float radius = focus.Radius(); 3069 3070 typedef agg::span_interpolator_linear<> interpolator_type; 3071 typedef agg::pod_auto_array<agg::rgba8, 256> color_array_type; 3072 typedef agg::span_allocator<agg::rgba8> span_allocator_type; 3073 typedef agg::gradient_radial_focus gradient_func_type; 3074 typedef agg::span_gradient<agg::rgba8, interpolator_type, 3075 gradient_func_type, color_array_type> span_gradient_type; 3076 typedef agg::renderer_scanline_aa<renderer_base, span_allocator_type, 3077 span_gradient_type> renderer_gradient_type; 3078 3079 gradient_func_type gradientFunc; 3080 agg::trans_affine gradientMatrix; 3081 interpolator_type spanInterpolator(gradientMatrix); 3082 span_allocator_type spanAllocator; 3083 color_array_type colorArray; 3084 3085 _MakeGradient(colorArray, focus); 3086 3087 span_gradient_type spanGradient(spanInterpolator, gradientFunc, colorArray, 3088 0, 100); 3089 3090 renderer_gradient_type gradientRenderer(fBaseRenderer, spanAllocator, 3091 spanGradient); 3092 3093 gradientMatrix.reset(); 3094 gradientMatrix *= agg::trans_affine_translation(center.x, center.y); 3095 gradientMatrix.invert(); 3096 3097 // _CalcLinearGradientTransform(start, end, gradientMatrix); 3098 3099 fRasterizer.reset(); 3100 fRasterizer.add_path(path); 3101 agg::render_scanlines(fRasterizer, fPackedScanline, gradientRenderer); 3102 } 3103 3104 3105 // _FillPathGradientDiamond 3106 template<class VertexSource> 3107 void 3108 Painter::_FillPathGradientDiamond(VertexSource& path, 3109 const BGradientDiamond& diamond) const 3110 { 3111 GTRACE("Painter::_FillPathGradientDiamond\n"); 3112 3113 BPoint center = diamond.Center(); 3114 // float radius = diamond.Radius(); 3115 3116 typedef agg::span_interpolator_linear<> interpolator_type; 3117 typedef agg::pod_auto_array<agg::rgba8, 256> color_array_type; 3118 typedef agg::span_allocator<agg::rgba8> span_allocator_type; 3119 typedef agg::gradient_diamond gradient_func_type; 3120 typedef agg::span_gradient<agg::rgba8, interpolator_type, 3121 gradient_func_type, color_array_type> span_gradient_type; 3122 typedef agg::renderer_scanline_aa<renderer_base, span_allocator_type, 3123 span_gradient_type> renderer_gradient_type; 3124 3125 gradient_func_type gradientFunc; 3126 agg::trans_affine gradientMatrix; 3127 interpolator_type spanInterpolator(gradientMatrix); 3128 span_allocator_type spanAllocator; 3129 color_array_type colorArray; 3130 3131 _MakeGradient(colorArray, diamond); 3132 3133 span_gradient_type spanGradient(spanInterpolator, gradientFunc, colorArray, 3134 0, 100); 3135 3136 renderer_gradient_type gradientRenderer(fBaseRenderer, spanAllocator, 3137 spanGradient); 3138 3139 gradientMatrix.reset(); 3140 gradientMatrix *= agg::trans_affine_translation(center.x, center.y); 3141 gradientMatrix.invert(); 3142 3143 // _CalcLinearGradientTransform(start, end, gradientMatrix); 3144 3145 fRasterizer.reset(); 3146 fRasterizer.add_path(path); 3147 agg::render_scanlines(fRasterizer, fPackedScanline, gradientRenderer); 3148 } 3149 3150 3151 // _FillPathGradientConic 3152 template<class VertexSource> 3153 void 3154 Painter::_FillPathGradientConic(VertexSource& path, 3155 const BGradientConic& conic) const 3156 { 3157 GTRACE("Painter::_FillPathGradientConic\n"); 3158 3159 BPoint center = conic.Center(); 3160 // float radius = conic.Radius(); 3161 3162 typedef agg::span_interpolator_linear<> interpolator_type; 3163 typedef agg::pod_auto_array<agg::rgba8, 256> color_array_type; 3164 typedef agg::span_allocator<agg::rgba8> span_allocator_type; 3165 typedef agg::gradient_conic gradient_func_type; 3166 typedef agg::span_gradient<agg::rgba8, interpolator_type, 3167 gradient_func_type, color_array_type> span_gradient_type; 3168 typedef agg::renderer_scanline_aa<renderer_base, span_allocator_type, 3169 span_gradient_type> renderer_gradient_type; 3170 3171 gradient_func_type gradientFunc; 3172 agg::trans_affine gradientMatrix; 3173 interpolator_type spanInterpolator(gradientMatrix); 3174 span_allocator_type spanAllocator; 3175 color_array_type colorArray; 3176 3177 _MakeGradient(colorArray, conic); 3178 3179 span_gradient_type spanGradient(spanInterpolator, gradientFunc, colorArray, 3180 0, 100); 3181 3182 renderer_gradient_type gradientRenderer(fBaseRenderer, spanAllocator, 3183 spanGradient); 3184 3185 gradientMatrix.reset(); 3186 gradientMatrix *= agg::trans_affine_translation(center.x, center.y); 3187 gradientMatrix.invert(); 3188 3189 // _CalcLinearGradientTransform(start, end, gradientMatrix); 3190 3191 fRasterizer.reset(); 3192 fRasterizer.add_path(path); 3193 agg::render_scanlines(fRasterizer, fPackedScanline, gradientRenderer); 3194 } 3195