1 /* 2 * Copyright 2005-2007, Stephan Aßmus <superstippi@gmx.de>. 3 * Copyright 2008, Andrej Spielmann <andrej.spielmann@seh.ox.ac.uk>. 4 * All rights reserved. Distributed under the terms of the MIT License. 5 * 6 * API to the Anti-Grain Geometry based "Painter" drawing backend. Manages 7 * rendering pipe-lines for stroke, fills, bitmap and text rendering. 8 */ 9 10 #include <new> 11 #include <stdio.h> 12 #include <string.h> 13 14 #include <Bitmap.h> 15 #include <GraphicsDefs.h> 16 #include <Region.h> 17 #include <String.h> 18 19 #include <ShapePrivate.h> 20 21 #include <agg_bezier_arc.h> 22 #include <agg_bounding_rect.h> 23 #include <agg_conv_clip_polygon.h> 24 #include <agg_conv_curve.h> 25 #include <agg_conv_stroke.h> 26 #include <agg_ellipse.h> 27 #include <agg_image_accessors.h> 28 #include <agg_path_storage.h> 29 #include <agg_pixfmt_rgba.h> 30 #include <agg_rounded_rect.h> 31 #include <agg_span_allocator.h> 32 #include <agg_span_image_filter_rgba.h> 33 #include <agg_span_interpolator_linear.h> 34 35 #include "drawing_support.h" 36 37 #include "DrawState.h" 38 39 #include <AutoDeleter.h> 40 #include <View.h> 41 42 #include "DrawingMode.h" 43 #include "GlobalSubpixelSettings.h" 44 #include "PatternHandler.h" 45 #include "RenderingBuffer.h" 46 #include "ServerBitmap.h" 47 #include "ServerFont.h" 48 #include "SystemPalette.h" 49 50 #include "Painter.h" 51 52 using std::nothrow; 53 54 #undef TRACE 55 //#define TRACE_PAINTER 56 #ifdef TRACE_PAINTER 57 # define TRACE(x...) printf(x) 58 #else 59 # define TRACE(x...) 60 #endif 61 62 #define CHECK_CLIPPING if (!fValidClipping) return BRect(0, 0, -1, -1); 63 #define CHECK_CLIPPING_NO_RETURN if (!fValidClipping) return; 64 65 // constructor 66 Painter::Painter() 67 : fBuffer(), 68 fPixelFormat(fBuffer, &fPatternHandler), 69 fBaseRenderer(fPixelFormat), 70 fUnpackedScanline(), 71 fPackedScanline(), 72 fSubpixPackedScanline(), 73 fSubpixUnpackedScanline(), 74 fSubpixRasterizer(), 75 fRasterizer(), 76 fSubpixRenderer(fBaseRenderer), 77 fRenderer(fBaseRenderer), 78 fRendererBin(fBaseRenderer), 79 80 fPath(), 81 fCurve(fPath), 82 83 fSubpixelPrecise(false), 84 fValidClipping(false), 85 fDrawingText(false), 86 fAttached(false), 87 88 fPenSize(1.0), 89 fClippingRegion(NULL), 90 fDrawingMode(B_OP_COPY), 91 fAlphaSrcMode(B_PIXEL_ALPHA), 92 fAlphaFncMode(B_ALPHA_OVERLAY), 93 fLineCapMode(B_BUTT_CAP), 94 fLineJoinMode(B_MITER_JOIN), 95 fMiterLimit(B_DEFAULT_MITER_LIMIT), 96 97 fPatternHandler(), 98 fTextRenderer(fSubpixRenderer, fRenderer, fRendererBin, fUnpackedScanline, 99 fSubpixUnpackedScanline, fSubpixRasterizer) 100 { 101 fPixelFormat.SetDrawingMode(fDrawingMode, fAlphaSrcMode, fAlphaFncMode, false); 102 103 #if ALIASED_DRAWING 104 fRasterizer.gamma(agg::gamma_threshold(0.5)); 105 fSubpixRasterizer.gamma(agg:gamma_threshold(0.5)); 106 #endif 107 } 108 109 // destructor 110 Painter::~Painter() 111 { 112 } 113 114 // #pragma mark - 115 116 // AttachToBuffer 117 void 118 Painter::AttachToBuffer(RenderingBuffer* buffer) 119 { 120 if (buffer && buffer->InitCheck() >= B_OK && 121 (buffer->ColorSpace() == B_RGBA32 || buffer->ColorSpace() == B_RGB32)) { 122 // TODO: implement drawing on B_RGB24, B_RGB15, B_RGB16, 123 // B_CMAP8 and B_GRAY8 :-[ 124 // (if ever we want to support some devices where this gives 125 // a great speed up, right now it seems fine, even in emulation) 126 127 fBuffer.attach((uint8*)buffer->Bits(), 128 buffer->Width(), buffer->Height(), buffer->BytesPerRow()); 129 130 fAttached = true; 131 fValidClipping = fClippingRegion 132 && fClippingRegion->Frame().IsValid(); 133 134 // These are the AGG renderes and rasterizes which 135 // will be used for stroking paths 136 137 _SetRendererColor(fPatternHandler.HighColor()); 138 } 139 } 140 141 // DetachFromBuffer 142 void 143 Painter::DetachFromBuffer() 144 { 145 fBuffer.attach(NULL, 0, 0, 0); 146 fAttached = false; 147 fValidClipping = false; 148 } 149 150 // Bounds 151 BRect 152 Painter::Bounds() const 153 { 154 return BRect(0, 0, fBuffer.width() - 1, fBuffer.height() - 1); 155 } 156 157 // #pragma mark - 158 159 // SetDrawState 160 void 161 Painter::SetDrawState(const DrawState* data, int32 xOffset, int32 yOffset) 162 { 163 // NOTE: The custom clipping in "data" is ignored, because it has already 164 // been taken into account elsewhere 165 166 // NOTE: Usually this function is only used when the "current view" 167 // is switched in the ServerWindow and after the decorator has drawn 168 // and messed up the state. For other graphics state changes, the 169 // Painter methods are used directly, so this function is much less 170 // speed critical than it used to be. 171 172 SetPenSize(data->PenSize()); 173 174 SetFont(data); 175 176 fSubpixelPrecise = data->SubPixelPrecise(); 177 178 // any of these conditions means we need to use a different drawing 179 // mode instance 180 bool updateDrawingMode 181 = !(data->GetPattern() == fPatternHandler.GetPattern()) 182 || data->GetDrawingMode() != fDrawingMode 183 || (data->GetDrawingMode() == B_OP_ALPHA 184 && (data->AlphaSrcMode() != fAlphaSrcMode 185 || data->AlphaFncMode() != fAlphaFncMode)); 186 187 fDrawingMode = data->GetDrawingMode(); 188 fAlphaSrcMode = data->AlphaSrcMode(); 189 fAlphaFncMode = data->AlphaFncMode(); 190 fPatternHandler.SetPattern(data->GetPattern()); 191 fPatternHandler.SetOffsets(xOffset, yOffset); 192 fLineCapMode = data->LineCapMode(); 193 fLineJoinMode = data->LineJoinMode(); 194 fMiterLimit = data->MiterLimit(); 195 196 // adopt the color *after* the pattern is set 197 // to set the renderers to the correct color 198 SetHighColor(data->HighColor()); 199 SetLowColor(data->LowColor()); 200 201 if (updateDrawingMode || fPixelFormat.UsesOpCopyForText()) 202 _UpdateDrawingMode(); 203 } 204 205 // #pragma mark - state 206 207 // ConstrainClipping 208 void 209 Painter::ConstrainClipping(const BRegion* region) 210 { 211 fClippingRegion = region; 212 fBaseRenderer.set_clipping_region(const_cast<BRegion*>(region)); 213 fValidClipping = region->Frame().IsValid() && fAttached; 214 215 if (fValidClipping) { 216 clipping_rect cb = fClippingRegion->FrameInt(); 217 fRasterizer.clip_box(cb.left, cb.top, cb.right + 1, cb.bottom + 1); 218 fSubpixRasterizer.clip_box(cb.left, cb.top, cb.right + 1, cb.bottom + 1); 219 } 220 } 221 222 // SetHighColor 223 void 224 Painter::SetHighColor(const rgb_color& color) 225 { 226 if (fPatternHandler.HighColor() == color) 227 return; 228 fPatternHandler.SetHighColor(color); 229 if (*(fPatternHandler.GetR5Pattern()) == B_SOLID_HIGH) 230 _SetRendererColor(color); 231 } 232 233 // SetLowColor 234 void 235 Painter::SetLowColor(const rgb_color& color) 236 { 237 fPatternHandler.SetLowColor(color); 238 if (*(fPatternHandler.GetR5Pattern()) == B_SOLID_LOW) 239 _SetRendererColor(color); 240 } 241 242 // SetDrawingMode 243 void 244 Painter::SetDrawingMode(drawing_mode mode) 245 { 246 if (fDrawingMode != mode) { 247 fDrawingMode = mode; 248 _UpdateDrawingMode(); 249 } 250 } 251 252 253 // SetBlendingMode 254 void 255 Painter::SetBlendingMode(source_alpha srcAlpha, alpha_function alphaFunc) 256 { 257 if (fAlphaSrcMode != srcAlpha || fAlphaFncMode != alphaFunc) { 258 fAlphaSrcMode = srcAlpha; 259 fAlphaFncMode = alphaFunc; 260 if (fDrawingMode == B_OP_ALPHA) 261 _UpdateDrawingMode(); 262 } 263 } 264 265 // SetPenSize 266 void 267 Painter::SetPenSize(float size) 268 { 269 fPenSize = size; 270 } 271 272 // SetStrokeMode 273 void 274 Painter::SetStrokeMode(cap_mode lineCap, join_mode joinMode, float miterLimit) 275 { 276 fLineCapMode = lineCap; 277 fLineJoinMode = joinMode; 278 fMiterLimit = miterLimit; 279 } 280 281 // SetPattern 282 void 283 Painter::SetPattern(const pattern& p, bool drawingText) 284 { 285 if (!(p == *fPatternHandler.GetR5Pattern()) || drawingText != fDrawingText) { 286 fPatternHandler.SetPattern(p); 287 fDrawingText = drawingText; 288 _UpdateDrawingMode(fDrawingText); 289 290 // update renderer color if necessary 291 if (fPatternHandler.IsSolidHigh()) { 292 // pattern was not solid high before 293 _SetRendererColor(fPatternHandler.HighColor()); 294 } else if (fPatternHandler.IsSolidLow()) { 295 // pattern was not solid low before 296 _SetRendererColor(fPatternHandler.LowColor()); 297 } 298 } 299 } 300 301 // SetFont 302 void 303 Painter::SetFont(const ServerFont& font) 304 { 305 fTextRenderer.SetFont(font); 306 fTextRenderer.SetAntialiasing( 307 !(font.Flags() & B_DISABLE_ANTIALIASING)); 308 } 309 310 // SetFont 311 void 312 Painter::SetFont(const DrawState* state) 313 { 314 fTextRenderer.SetFont(state->Font()); 315 fTextRenderer.SetAntialiasing(!(state->ForceFontAliasing() 316 || state->Font().Flags() & B_DISABLE_ANTIALIASING)); 317 } 318 319 // #pragma mark - drawing 320 321 // StrokeLine 322 void 323 Painter::StrokeLine(BPoint a, BPoint b) 324 { 325 CHECK_CLIPPING_NO_RETURN 326 327 // "false" means not to do the pixel center offset, 328 // because it would mess up our optimized versions 329 _Transform(&a, false); 330 _Transform(&b, false); 331 332 // first, try an optimized version 333 if (fPenSize == 1.0 334 && (fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER)) { 335 336 pattern pat = *fPatternHandler.GetR5Pattern(); 337 if (pat == B_SOLID_HIGH 338 && StraightLine(a, b, fPatternHandler.HighColor())) { 339 return; 340 } else if (pat == B_SOLID_LOW 341 && StraightLine(a, b, fPatternHandler.LowColor())) { 342 return; 343 } 344 } 345 346 fPath.remove_all(); 347 348 if (a == b) { 349 // special case dots 350 if (fPenSize == 1.0 && !fSubpixelPrecise) { 351 if (fClippingRegion->Contains(a)) { 352 agg::rgba8 dummyColor; 353 fPixelFormat.blend_pixel(a.x, a.y, dummyColor, 255); 354 } 355 } else { 356 fPath.move_to(a.x, a.y); 357 fPath.line_to(a.x + 1, a.y); 358 fPath.line_to(a.x + 1, a.y + 1); 359 fPath.line_to(a.x, a.y + 1); 360 361 _FillPath(fPath); 362 } 363 } else { 364 // do the pixel center offset here 365 // tweak ends to "include" the pixel at the index, 366 // we need to do this in order to produce results like R5, 367 // where coordinates were inclusive 368 if (!fSubpixelPrecise) { 369 bool centerOnLine = fmodf(fPenSize, 2.0) != 0.0; 370 if (a.x == b.x) { 371 // shift to pixel center vertically 372 if (centerOnLine) { 373 a.x += 0.5; 374 b.x += 0.5; 375 } 376 // extend on bottom end 377 if (a.y < b.y) 378 b.y++; 379 else 380 a.y++; 381 } else if (a.y == b.y) { 382 if (centerOnLine) { 383 // shift to pixel center horizontally 384 a.y += 0.5; 385 b.y += 0.5; 386 } 387 // extend on right end 388 if (a.x < b.x) 389 b.x++; 390 else 391 a.x++; 392 } else { 393 // do this regardless of pensize 394 if (a.x < b.x) 395 b.x++; 396 else 397 a.x++; 398 if (a.y < b.y) 399 b.y++; 400 else 401 a.y++; 402 } 403 } 404 405 fPath.move_to(a.x, a.y); 406 fPath.line_to(b.x, b.y); 407 408 _StrokePath(fPath); 409 } 410 } 411 412 // StraightLine 413 bool 414 Painter::StraightLine(BPoint a, BPoint b, const rgb_color& c) const 415 { 416 if (!fValidClipping) 417 return false; 418 419 if (a.x == b.x) { 420 // vertical 421 uint8* dst = fBuffer.row_ptr(0); 422 uint32 bpr = fBuffer.stride(); 423 int32 x = (int32)a.x; 424 dst += x * 4; 425 int32 y1 = (int32)min_c(a.y, b.y); 426 int32 y2 = (int32)max_c(a.y, b.y); 427 pixel32 color; 428 color.data8[0] = c.blue; 429 color.data8[1] = c.green; 430 color.data8[2] = c.red; 431 color.data8[3] = 255; 432 // draw a line, iterate over clipping boxes 433 fBaseRenderer.first_clip_box(); 434 do { 435 if (fBaseRenderer.xmin() <= x && 436 fBaseRenderer.xmax() >= x) { 437 int32 i = max_c(fBaseRenderer.ymin(), y1); 438 int32 end = min_c(fBaseRenderer.ymax(), y2); 439 uint8* handle = dst + i * bpr; 440 for (; i <= end; i++) { 441 *(uint32*)handle = color.data32; 442 handle += bpr; 443 } 444 } 445 } while (fBaseRenderer.next_clip_box()); 446 447 return true; 448 449 } else if (a.y == b.y) { 450 // horizontal 451 int32 y = (int32)a.y; 452 uint8* dst = fBuffer.row_ptr(y); 453 int32 x1 = (int32)min_c(a.x, b.x); 454 int32 x2 = (int32)max_c(a.x, b.x); 455 pixel32 color; 456 color.data8[0] = c.blue; 457 color.data8[1] = c.green; 458 color.data8[2] = c.red; 459 color.data8[3] = 255; 460 // draw a line, iterate over clipping boxes 461 fBaseRenderer.first_clip_box(); 462 do { 463 if (fBaseRenderer.ymin() <= y && 464 fBaseRenderer.ymax() >= y) { 465 int32 i = max_c(fBaseRenderer.xmin(), x1); 466 int32 end = min_c(fBaseRenderer.xmax(), x2); 467 uint32* handle = (uint32*)(dst + i * 4); 468 for (; i <= end; i++) { 469 *handle++ = color.data32; 470 } 471 } 472 } while (fBaseRenderer.next_clip_box()); 473 474 return true; 475 } 476 return false; 477 } 478 479 // #pragma mark - 480 481 // StrokeTriangle 482 BRect 483 Painter::StrokeTriangle(BPoint pt1, BPoint pt2, BPoint pt3) const 484 { 485 return _DrawTriangle(pt1, pt2, pt3, false); 486 } 487 488 // FillTriangle 489 BRect 490 Painter::FillTriangle(BPoint pt1, BPoint pt2, BPoint pt3) const 491 { 492 return _DrawTriangle(pt1, pt2, pt3, true); 493 } 494 495 // DrawPolygon 496 BRect 497 Painter::DrawPolygon(BPoint* p, int32 numPts, 498 bool filled, bool closed) const 499 { 500 CHECK_CLIPPING 501 502 if (numPts > 0) { 503 504 fPath.remove_all(); 505 506 _Transform(p); 507 fPath.move_to(p->x, p->y); 508 509 for (int32 i = 1; i < numPts; i++) { 510 p++; 511 _Transform(p); 512 fPath.line_to(p->x, p->y); 513 } 514 515 if (closed) 516 fPath.close_polygon(); 517 518 if (filled) 519 return _FillPath(fPath); 520 else 521 return _StrokePath(fPath); 522 } 523 return BRect(0.0, 0.0, -1.0, -1.0); 524 } 525 526 // DrawBezier 527 BRect 528 Painter::DrawBezier(BPoint* p, bool filled) const 529 { 530 CHECK_CLIPPING 531 532 fPath.remove_all(); 533 534 _Transform(&(p[0])); 535 _Transform(&(p[1])); 536 _Transform(&(p[2])); 537 _Transform(&(p[3])); 538 539 fPath.move_to(p[0].x, p[0].y); 540 fPath.curve4(p[1].x, p[1].y, 541 p[2].x, p[2].y, 542 p[3].x, p[3].y); 543 544 545 if (filled) { 546 fPath.close_polygon(); 547 return _FillPath(fCurve); 548 } else { 549 return _StrokePath(fCurve); 550 } 551 } 552 553 554 555 // DrawShape 556 BRect 557 Painter::DrawShape(const int32& opCount, const uint32* opList, 558 const int32& ptCount, const BPoint* points, 559 bool filled) const 560 { 561 CHECK_CLIPPING 562 563 // TODO: if shapes are ever used more heavily in Haiku, 564 // it would be nice to use BShape data directly (write 565 // an AGG "VertexSource" adaptor) 566 fPath.remove_all(); 567 for (int32 i = 0; i < opCount; i++) { 568 uint32 op = opList[i] & 0xFF000000; 569 if (op & OP_MOVETO) { 570 fPath.move_to(points->x, points->y); 571 points++; 572 } 573 574 if (op & OP_LINETO) { 575 int32 count = opList[i] & 0x00FFFFFF; 576 while (count--) { 577 fPath.line_to(points->x, points->y); 578 points++; 579 } 580 } 581 582 if (op & OP_BEZIERTO) { 583 int32 count = opList[i] & 0x00FFFFFF; 584 while (count) { 585 fPath.curve4(points[0].x, points[0].y, 586 points[1].x, points[1].y, 587 points[2].x, points[2].y); 588 points += 3; 589 count -= 3; 590 } 591 } 592 593 if (op & OP_CLOSE) 594 fPath.close_polygon(); 595 } 596 597 if (filled) 598 return _FillPath(fCurve); 599 else 600 return _StrokePath(fCurve); 601 } 602 603 // StrokeRect 604 BRect 605 Painter::StrokeRect(const BRect& r) const 606 { 607 CHECK_CLIPPING 608 609 BPoint a(r.left, r.top); 610 BPoint b(r.right, r.bottom); 611 _Transform(&a, false); 612 _Transform(&b, false); 613 614 // first, try an optimized version 615 if (fPenSize == 1.0 && 616 (fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER)) { 617 pattern p = *fPatternHandler.GetR5Pattern(); 618 if (p == B_SOLID_HIGH) { 619 BRect rect(a, b); 620 StrokeRect(rect, 621 fPatternHandler.HighColor()); 622 return _Clipped(rect); 623 } else if (p == B_SOLID_LOW) { 624 BRect rect(a, b); 625 StrokeRect(rect, 626 fPatternHandler.LowColor()); 627 return _Clipped(rect); 628 } 629 } 630 631 if (fmodf(fPenSize, 2.0) != 0.0) { 632 // shift coords to center of pixels 633 a.x += 0.5; 634 a.y += 0.5; 635 b.x += 0.5; 636 b.y += 0.5; 637 } 638 639 fPath.remove_all(); 640 fPath.move_to(a.x, a.y); 641 if (a.x == b.x || a.y == b.y) { 642 // special case rects with one pixel height or width 643 fPath.line_to(b.x, b.y); 644 } else { 645 fPath.line_to(b.x, a.y); 646 fPath.line_to(b.x, b.y); 647 fPath.line_to(a.x, b.y); 648 } 649 fPath.close_polygon(); 650 651 return _StrokePath(fPath); 652 } 653 654 // StrokeRect 655 void 656 Painter::StrokeRect(const BRect& r, const rgb_color& c) const 657 { 658 StraightLine(BPoint(r.left, r.top), 659 BPoint(r.right - 1, r.top), c); 660 StraightLine(BPoint(r.right, r.top), 661 BPoint(r.right, r.bottom - 1), c); 662 StraightLine(BPoint(r.right, r.bottom), 663 BPoint(r.left + 1, r.bottom), c); 664 StraightLine(BPoint(r.left, r.bottom), 665 BPoint(r.left, r.top + 1), c); 666 } 667 668 // FillRect 669 BRect 670 Painter::FillRect(const BRect& r) const 671 { 672 CHECK_CLIPPING 673 674 // support invalid rects 675 BPoint a(min_c(r.left, r.right), min_c(r.top, r.bottom)); 676 BPoint b(max_c(r.left, r.right), max_c(r.top, r.bottom)); 677 _Transform(&a, false); 678 _Transform(&b, false); 679 680 // first, try an optimized version 681 if (fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER) { 682 pattern p = *fPatternHandler.GetR5Pattern(); 683 if (p == B_SOLID_HIGH) { 684 BRect rect(a, b); 685 FillRect(rect, fPatternHandler.HighColor()); 686 return _Clipped(rect); 687 } else if (p == B_SOLID_LOW) { 688 BRect rect(a, b); 689 FillRect(rect, fPatternHandler.LowColor()); 690 return _Clipped(rect); 691 } 692 } 693 if (fDrawingMode == B_OP_ALPHA && fAlphaFncMode == B_ALPHA_OVERLAY) { 694 pattern p = *fPatternHandler.GetR5Pattern(); 695 if (p == B_SOLID_HIGH) { 696 BRect rect(a, b); 697 _BlendRect32(rect, fPatternHandler.HighColor()); 698 return _Clipped(rect); 699 } else if (p == B_SOLID_LOW) { 700 rgb_color c = fPatternHandler.LowColor(); 701 if (fAlphaSrcMode == B_CONSTANT_ALPHA) 702 c.alpha = fPatternHandler.HighColor().alpha; 703 BRect rect(a, b); 704 _BlendRect32(rect, c); 705 return _Clipped(rect); 706 } 707 } 708 709 710 // account for stricter interpretation of coordinates in AGG 711 // the rectangle ranges from the top-left (.0, .0) 712 // to the bottom-right (.9999, .9999) corner of pixels 713 b.x += 1.0; 714 b.y += 1.0; 715 716 fPath.remove_all(); 717 fPath.move_to(a.x, a.y); 718 fPath.line_to(b.x, a.y); 719 fPath.line_to(b.x, b.y); 720 fPath.line_to(a.x, b.y); 721 fPath.close_polygon(); 722 723 return _FillPath(fPath); 724 } 725 726 // FillRect 727 void 728 Painter::FillRect(const BRect& r, const rgb_color& c) const 729 { 730 if (!fValidClipping) 731 return; 732 733 uint8* dst = fBuffer.row_ptr(0); 734 uint32 bpr = fBuffer.stride(); 735 int32 left = (int32)r.left; 736 int32 top = (int32)r.top; 737 int32 right = (int32)r.right; 738 int32 bottom = (int32)r.bottom; 739 // get a 32 bit pixel ready with the color 740 pixel32 color; 741 color.data8[0] = c.blue; 742 color.data8[1] = c.green; 743 color.data8[2] = c.red; 744 color.data8[3] = c.alpha; 745 // fill rects, iterate over clipping boxes 746 fBaseRenderer.first_clip_box(); 747 do { 748 int32 x1 = max_c(fBaseRenderer.xmin(), left); 749 int32 x2 = min_c(fBaseRenderer.xmax(), right); 750 if (x1 <= x2) { 751 int32 y1 = max_c(fBaseRenderer.ymin(), top); 752 int32 y2 = min_c(fBaseRenderer.ymax(), bottom); 753 uint8* offset = dst + x1 * 4; 754 for (; y1 <= y2; y1++) { 755 // uint32* handle = (uint32*)(offset + y1 * bpr); 756 // for (int32 x = x1; x <= x2; x++) { 757 // *handle++ = color.data32; 758 // } 759 gfxset32(offset + y1 * bpr, color.data32, (x2 - x1 + 1) * 4); 760 } 761 } 762 } while (fBaseRenderer.next_clip_box()); 763 } 764 765 // FillRectNoClipping 766 void 767 Painter::FillRectNoClipping(const clipping_rect& r, const rgb_color& c) const 768 { 769 int32 y = (int32)r.top; 770 771 uint8* dst = fBuffer.row_ptr(y) + r.left * 4; 772 uint32 bpr = fBuffer.stride(); 773 int32 bytes = (r.right - r.left + 1) * 4; 774 775 // get a 32 bit pixel ready with the color 776 pixel32 color; 777 color.data8[0] = c.blue; 778 color.data8[1] = c.green; 779 color.data8[2] = c.red; 780 color.data8[3] = c.alpha; 781 782 for (; y <= r.bottom; y++) { 783 // uint32* handle = (uint32*)dst; 784 // for (int32 x = left; x <= right; x++) { 785 // *handle++ = color.data32; 786 // } 787 gfxset32(dst, color.data32, bytes); 788 dst += bpr; 789 } 790 } 791 792 // StrokeRoundRect 793 BRect 794 Painter::StrokeRoundRect(const BRect& r, float xRadius, float yRadius) const 795 { 796 CHECK_CLIPPING 797 798 BPoint lt(r.left, r.top); 799 BPoint rb(r.right, r.bottom); 800 bool centerOffset = fPenSize == 1.0; 801 // TODO: use this when using _StrokePath() 802 // bool centerOffset = fmodf(fPenSize, 2.0) != 0.0; 803 _Transform(<, centerOffset); 804 _Transform(&rb, centerOffset); 805 806 if (fPenSize == 1.0) { 807 agg::rounded_rect rect; 808 rect.rect(lt.x, lt.y, rb.x, rb.y); 809 rect.radius(xRadius, yRadius); 810 811 return _StrokePath(rect); 812 } else { 813 // NOTE: This implementation might seem a little strange, but it makes 814 // stroked round rects look like on R5. A more correct way would be to use 815 // _StrokePath() as above (independent from fPenSize). 816 // The fact that the bounding box of the round rect is not enlarged 817 // by fPenSize/2 is actually on purpose, though one could argue it is unexpected. 818 819 // enclose the right and bottom edge 820 rb.x++; 821 rb.y++; 822 823 agg::rounded_rect outer; 824 outer.rect(lt.x, lt.y, rb.x, rb.y); 825 outer.radius(xRadius, yRadius); 826 827 if (gSubpixelAntialiasing) { 828 fSubpixRasterizer.reset(); 829 fSubpixRasterizer.add_path(outer); 830 831 // don't add an inner hole if the "size is negative", this avoids some 832 // defects that can be observed on R5 and could be regarded as a bug. 833 if (2 * fPenSize < rb.x - lt.x && 2 * fPenSize < rb.y - lt.y) { 834 agg::rounded_rect inner; 835 inner.rect(lt.x + fPenSize, lt.y + fPenSize, rb.x - fPenSize, 836 rb.y - fPenSize); 837 inner.radius(max_c(0.0, xRadius - fPenSize), 838 max_c(0.0, yRadius - fPenSize)); 839 840 fSubpixRasterizer.add_path(inner); 841 } 842 843 // make the inner rect work as a hole 844 fSubpixRasterizer.filling_rule(agg::fill_even_odd); 845 846 if (fPenSize > 2) 847 agg::render_scanlines(fSubpixRasterizer, fSubpixPackedScanline, 848 fSubpixRenderer); 849 else 850 agg::render_scanlines(fSubpixRasterizer, fSubpixUnpackedScanline, 851 fSubpixRenderer); 852 853 fSubpixRasterizer.filling_rule(agg::fill_non_zero); 854 } else { 855 fRasterizer.reset(); 856 fRasterizer.add_path(outer); 857 858 // don't add an inner hole if the "size is negative", this avoids some 859 // defects that can be observed on R5 and could be regarded as a bug. 860 if (2 * fPenSize < rb.x - lt.x && 2 * fPenSize < rb.y - lt.y) { 861 agg::rounded_rect inner; 862 inner.rect(lt.x + fPenSize, lt.y + fPenSize, rb.x - fPenSize, 863 rb.y - fPenSize); 864 inner.radius(max_c(0.0, xRadius - fPenSize), 865 max_c(0.0, yRadius - fPenSize)); 866 867 fRasterizer.add_path(inner); 868 } 869 870 // make the inner rect work as a hole 871 fRasterizer.filling_rule(agg::fill_even_odd); 872 873 if (fPenSize > 2) 874 agg::render_scanlines(fRasterizer, fPackedScanline, fRenderer); 875 else 876 agg::render_scanlines(fRasterizer, fUnpackedScanline, fRenderer); 877 878 // reset to default 879 fRasterizer.filling_rule(agg::fill_non_zero); 880 } 881 882 return _Clipped(_BoundingBox(outer)); 883 } 884 } 885 886 // FillRoundRect 887 BRect 888 Painter::FillRoundRect(const BRect& r, float xRadius, float yRadius) const 889 { 890 CHECK_CLIPPING 891 892 BPoint lt(r.left, r.top); 893 BPoint rb(r.right, r.bottom); 894 _Transform(<, false); 895 _Transform(&rb, false); 896 897 // account for stricter interpretation of coordinates in AGG 898 // the rectangle ranges from the top-left (.0, .0) 899 // to the bottom-right (.9999, .9999) corner of pixels 900 rb.x += 1.0; 901 rb.y += 1.0; 902 903 agg::rounded_rect rect; 904 rect.rect(lt.x, lt.y, rb.x, rb.y); 905 rect.radius(xRadius, yRadius); 906 907 return _FillPath(rect); 908 } 909 910 // AlignEllipseRect 911 void 912 Painter::AlignEllipseRect(BRect* rect, bool filled) const 913 { 914 if (!fSubpixelPrecise) { 915 // align rect to pixels 916 align_rect_to_pixels(rect); 917 // account for "pixel index" versus "pixel area" 918 rect->right++; 919 rect->bottom++; 920 if (!filled && fmodf(fPenSize, 2.0) != 0.0) { 921 // align the stroke 922 rect->InsetBy(0.5, 0.5); 923 } 924 } 925 } 926 927 // DrawEllipse 928 BRect 929 Painter::DrawEllipse(BRect r, bool fill) const 930 { 931 CHECK_CLIPPING 932 933 AlignEllipseRect(&r, fill); 934 935 float xRadius = r.Width() / 2.0; 936 float yRadius = r.Height() / 2.0; 937 BPoint center(r.left + xRadius, r.top + yRadius); 938 939 int32 divisions = (int32)((xRadius + yRadius + 2 * fPenSize) * PI / 2); 940 if (divisions < 12) 941 divisions = 12; 942 if (divisions > 4096) 943 divisions = 4096; 944 945 if (fill) { 946 agg::ellipse path(center.x, center.y, xRadius, yRadius, divisions); 947 948 return _FillPath(path); 949 } else { 950 // NOTE: This implementation might seem a little strange, but it makes 951 // stroked ellipses look like on R5. A more correct way would be to use 952 // _StrokePath(), but it currently has its own set of problems with narrow 953 // ellipses (for small xRadii or yRadii). 954 float inset = fPenSize / 2.0; 955 agg::ellipse inner(center.x, center.y, 956 max_c(0.0, xRadius - inset), 957 max_c(0.0, yRadius - inset), 958 divisions); 959 agg::ellipse outer(center.x, center.y, 960 xRadius + inset, 961 yRadius + inset, 962 divisions); 963 964 if (gSubpixelAntialiasing) { 965 fSubpixRasterizer.reset(); 966 fSubpixRasterizer.add_path(outer); 967 fSubpixRasterizer.add_path(inner); 968 969 // make the inner ellipse work as a hole 970 fSubpixRasterizer.filling_rule(agg::fill_even_odd); 971 972 if (fPenSize > 4) 973 agg::render_scanlines(fSubpixRasterizer, fSubpixPackedScanline, 974 fSubpixRenderer); 975 else 976 agg::render_scanlines(fSubpixRasterizer, fSubpixUnpackedScanline, 977 fSubpixRenderer); 978 979 // reset to default 980 fSubpixRasterizer.filling_rule(agg::fill_non_zero); 981 } else { 982 fRasterizer.reset(); 983 fRasterizer.add_path(outer); 984 fRasterizer.add_path(inner); 985 986 // make the inner ellipse work as a hole 987 fRasterizer.filling_rule(agg::fill_even_odd); 988 989 if (fPenSize > 4) 990 agg::render_scanlines(fRasterizer, fPackedScanline, fRenderer); 991 else 992 agg::render_scanlines(fRasterizer, fUnpackedScanline, fRenderer); 993 994 // reset to default 995 fRasterizer.filling_rule(agg::fill_non_zero); 996 } 997 998 return _Clipped(_BoundingBox(outer)); 999 } 1000 } 1001 1002 // StrokeArc 1003 BRect 1004 Painter::StrokeArc(BPoint center, float xRadius, float yRadius, float angle, 1005 float span) const 1006 { 1007 CHECK_CLIPPING 1008 1009 _Transform(¢er); 1010 1011 double angleRad = (angle * PI) / 180.0; 1012 double spanRad = (span * PI) / 180.0; 1013 agg::bezier_arc arc(center.x, center.y, xRadius, yRadius, 1014 -angleRad, -spanRad); 1015 1016 agg::conv_curve<agg::bezier_arc> path(arc); 1017 path.approximation_scale(2.0); 1018 1019 return _StrokePath(path); 1020 } 1021 1022 // FillArc 1023 BRect 1024 Painter::FillArc(BPoint center, float xRadius, float yRadius, float angle, 1025 float span) const 1026 { 1027 CHECK_CLIPPING 1028 1029 _Transform(¢er); 1030 1031 double angleRad = (angle * PI) / 180.0; 1032 double spanRad = (span * PI) / 180.0; 1033 agg::bezier_arc arc(center.x, center.y, xRadius, yRadius, 1034 -angleRad, -spanRad); 1035 1036 agg::conv_curve<agg::bezier_arc> segmentedArc(arc); 1037 1038 fPath.remove_all(); 1039 1040 // build a new path by starting at the center point, 1041 // then traversing the arc, then going back to the center 1042 fPath.move_to(center.x, center.y); 1043 1044 segmentedArc.rewind(0); 1045 double x; 1046 double y; 1047 unsigned cmd = segmentedArc.vertex(&x, &y); 1048 while (!agg::is_stop(cmd)) { 1049 fPath.line_to(x, y); 1050 cmd = segmentedArc.vertex(&x, &y); 1051 } 1052 1053 fPath.close_polygon(); 1054 1055 return _FillPath(fPath); 1056 } 1057 1058 // #pragma mark - 1059 1060 // DrawString 1061 BRect 1062 Painter::DrawString(const char* utf8String, uint32 length, BPoint baseLine, 1063 const escapement_delta* delta, FontCacheReference* cacheReference) 1064 { 1065 CHECK_CLIPPING 1066 1067 if (!fSubpixelPrecise) { 1068 baseLine.x = roundf(baseLine.x); 1069 baseLine.y = roundf(baseLine.y); 1070 } 1071 1072 BRect bounds(0.0, 0.0, -1.0, -1.0); 1073 1074 // text is not rendered with patterns, but we need to 1075 // make sure that the previous pattern is restored 1076 pattern oldPattern = *fPatternHandler.GetR5Pattern(); 1077 SetPattern(B_SOLID_HIGH, true); 1078 1079 bounds = fTextRenderer.RenderString(utf8String, length, 1080 baseLine, fClippingRegion->Frame(), false, NULL, delta, 1081 cacheReference); 1082 1083 SetPattern(oldPattern); 1084 1085 return _Clipped(bounds); 1086 } 1087 1088 // BoundingBox 1089 BRect 1090 Painter::BoundingBox(const char* utf8String, uint32 length, BPoint baseLine, 1091 BPoint* penLocation, const escapement_delta* delta, 1092 FontCacheReference* cacheReference) const 1093 { 1094 if (!fSubpixelPrecise) { 1095 baseLine.x = roundf(baseLine.x); 1096 baseLine.y = roundf(baseLine.y); 1097 } 1098 1099 static BRect dummy; 1100 return fTextRenderer.RenderString(utf8String, length, 1101 baseLine, dummy, true, penLocation, delta, cacheReference); 1102 } 1103 1104 // StringWidth 1105 float 1106 Painter::StringWidth(const char* utf8String, uint32 length, 1107 const escapement_delta* delta) 1108 { 1109 return Font().StringWidth(utf8String, length, delta); 1110 } 1111 1112 // #pragma mark - 1113 1114 // DrawBitmap 1115 BRect 1116 Painter::DrawBitmap(const ServerBitmap* bitmap, BRect bitmapRect, 1117 BRect viewRect, uint32 options) const 1118 { 1119 CHECK_CLIPPING 1120 1121 BRect touched = _Clipped(viewRect); 1122 1123 if (bitmap && bitmap->IsValid() && touched.IsValid()) { 1124 // the native bitmap coordinate system 1125 BRect actualBitmapRect(bitmap->Bounds()); 1126 1127 TRACE("Painter::DrawBitmap()\n"); 1128 TRACE(" actualBitmapRect = (%.1f, %.1f) - (%.1f, %.1f)\n", 1129 actualBitmapRect.left, actualBitmapRect.top, 1130 actualBitmapRect.right, actualBitmapRect.bottom); 1131 TRACE(" bitmapRect = (%.1f, %.1f) - (%.1f, %.1f)\n", 1132 bitmapRect.left, bitmapRect.top, bitmapRect.right, 1133 bitmapRect.bottom); 1134 TRACE(" viewRect = (%.1f, %.1f) - (%.1f, %.1f)\n", 1135 viewRect.left, viewRect.top, viewRect.right, viewRect.bottom); 1136 1137 agg::rendering_buffer srcBuffer; 1138 srcBuffer.attach(bitmap->Bits(), bitmap->Width(), bitmap->Height(), 1139 bitmap->BytesPerRow()); 1140 1141 _DrawBitmap(srcBuffer, bitmap->ColorSpace(), actualBitmapRect, 1142 bitmapRect, viewRect, options); 1143 } 1144 return touched; 1145 } 1146 1147 // #pragma mark - 1148 1149 // FillRegion 1150 BRect 1151 Painter::FillRegion(const BRegion* region) const 1152 { 1153 CHECK_CLIPPING 1154 1155 BRegion copy(*region); 1156 int32 count = copy.CountRects(); 1157 BRect touched = FillRect(copy.RectAt(0)); 1158 for (int32 i = 1; i < count; i++) { 1159 touched = touched | FillRect(copy.RectAt(i)); 1160 } 1161 return touched; 1162 } 1163 1164 // InvertRect 1165 BRect 1166 Painter::InvertRect(const BRect& r) const 1167 { 1168 CHECK_CLIPPING 1169 1170 BRegion region(r); 1171 if (fClippingRegion) { 1172 region.IntersectWith(fClippingRegion); 1173 } 1174 // implementation only for B_RGB32 at the moment 1175 int32 count = region.CountRects(); 1176 for (int32 i = 0; i < count; i++) { 1177 _InvertRect32(region.RectAt(i)); 1178 } 1179 return _Clipped(r); 1180 } 1181 1182 1183 // #pragma mark - private 1184 1185 // _Transform 1186 inline void 1187 Painter::_Transform(BPoint* point, bool centerOffset) const 1188 { 1189 // rounding 1190 if (!fSubpixelPrecise) { 1191 // TODO: validate usage of floor() for values < 0 1192 point->x = (int32)point->x; 1193 point->y = (int32)point->y; 1194 } 1195 // this code is supposed to move coordinates to the center of pixels, 1196 // as AGG considers (0,0) to be the "upper left corner" of a pixel, 1197 // but BViews are less strict on those details 1198 if (centerOffset) { 1199 point->x += 0.5; 1200 point->y += 0.5; 1201 } 1202 } 1203 1204 // _Transform 1205 inline BPoint 1206 Painter::_Transform(const BPoint& point, bool centerOffset) const 1207 { 1208 BPoint ret = point; 1209 _Transform(&ret, centerOffset); 1210 return ret; 1211 } 1212 1213 // _Clipped 1214 BRect 1215 Painter::_Clipped(const BRect& rect) const 1216 { 1217 if (rect.IsValid()) { 1218 return BRect(rect & fClippingRegion->Frame()); 1219 } 1220 return BRect(rect); 1221 } 1222 1223 // _UpdateDrawingMode 1224 void 1225 Painter::_UpdateDrawingMode(bool drawingText) 1226 { 1227 // The AGG renderers have their own color setting, however 1228 // almost all drawing mode classes ignore the color given 1229 // by the AGG renderer and use the colors from the PatternHandler 1230 // instead. If we have a B_SOLID_* pattern, we can actually use 1231 // the color in the renderer and special versions of drawing modes 1232 // that don't use PatternHandler and are more efficient. This 1233 // has been implemented for B_OP_COPY and a couple others (the 1234 // DrawingMode*Solid ones) as of now. The PixelFormat knows the 1235 // PatternHandler and makes its decision based on the pattern. 1236 // The last parameter to SetDrawingMode() is a special flag 1237 // for when Painter is used to draw text. In this case, another 1238 // special version of B_OP_COPY is used that acts like R5 in that 1239 // anti-aliased pixel are not rendered against the actual background 1240 // but the current low color instead. This way, the frame buffer 1241 // doesn't need to be read. 1242 // When a solid pattern is used, _SetRendererColor() 1243 // has to be called so that all internal colors in the renderes 1244 // are up to date for use by the solid drawing mode version. 1245 fPixelFormat.SetDrawingMode(fDrawingMode, fAlphaSrcMode, 1246 fAlphaFncMode, drawingText); 1247 if (drawingText) 1248 fPatternHandler.MakeOpCopyColorCache(); 1249 } 1250 1251 // _SetRendererColor 1252 void 1253 Painter::_SetRendererColor(const rgb_color& color) const 1254 { 1255 fRenderer.color(agg::rgba(color.red / 255.0, 1256 color.green / 255.0, 1257 color.blue / 255.0, 1258 color.alpha / 255.0)); 1259 fSubpixRenderer.color(agg::rgba(color.red / 255.0, 1260 color.green / 255.0, 1261 color.blue / 255.0, 1262 color.alpha / 255.0)); 1263 // TODO: bitmap fonts not yet correctly setup in AGGTextRenderer 1264 // fRendererBin.color(agg::rgba(color.red / 255.0, 1265 // color.green / 255.0, 1266 // color.blue / 255.0, 1267 // color.alpha / 255.0)); 1268 } 1269 1270 // #pragma mark - 1271 1272 // _DrawTriangle 1273 inline BRect 1274 Painter::_DrawTriangle(BPoint pt1, BPoint pt2, BPoint pt3, bool fill) const 1275 { 1276 CHECK_CLIPPING 1277 1278 _Transform(&pt1); 1279 _Transform(&pt2); 1280 _Transform(&pt3); 1281 1282 fPath.remove_all(); 1283 1284 fPath.move_to(pt1.x, pt1.y); 1285 fPath.line_to(pt2.x, pt2.y); 1286 fPath.line_to(pt3.x, pt3.y); 1287 1288 fPath.close_polygon(); 1289 1290 if (fill) 1291 return _FillPath(fPath); 1292 else 1293 return _StrokePath(fPath); 1294 } 1295 1296 // copy_bitmap_row_cmap8_copy 1297 static inline void 1298 copy_bitmap_row_cmap8_copy(uint8* dst, const uint8* src, int32 numPixels, 1299 const rgb_color* colorMap) 1300 { 1301 uint32* d = (uint32*)dst; 1302 const uint8* s = src; 1303 while (numPixels--) { 1304 const rgb_color c = colorMap[*s++]; 1305 *d++ = (c.alpha << 24) | (c.red << 16) | (c.green << 8) | (c.blue); 1306 } 1307 } 1308 1309 // copy_bitmap_row_cmap8_over 1310 static inline void 1311 copy_bitmap_row_cmap8_over(uint8* dst, const uint8* src, int32 numPixels, 1312 const rgb_color* colorMap) 1313 { 1314 uint32* d = (uint32*)dst; 1315 const uint8* s = src; 1316 while (numPixels--) { 1317 const rgb_color c = colorMap[*s++]; 1318 if (c.alpha) 1319 *d = (c.alpha << 24) | (c.red << 16) | (c.green << 8) | (c.blue); 1320 d++; 1321 } 1322 } 1323 1324 // copy_bitmap_row_bgr32_copy 1325 static inline void 1326 copy_bitmap_row_bgr32_copy(uint8* dst, const uint8* src, int32 numPixels, 1327 const rgb_color* colorMap) 1328 { 1329 memcpy(dst, src, numPixels * 4); 1330 } 1331 1332 // copy_bitmap_row_bgr32_over 1333 static inline void 1334 copy_bitmap_row_bgr32_over(uint8* dst, const uint8* src, int32 numPixels, 1335 const rgb_color* colorMap) 1336 { 1337 uint32* d = (uint32*)dst; 1338 uint32* s = (uint32*)src; 1339 while (numPixels--) { 1340 if (*s != B_TRANSPARENT_MAGIC_RGBA32) 1341 *(uint32*)d = *(uint32*)s; 1342 d++; 1343 s++; 1344 } 1345 } 1346 1347 // copy_bitmap_row_bgr32_alpha 1348 static inline void 1349 copy_bitmap_row_bgr32_alpha(uint8* dst, const uint8* src, int32 numPixels, 1350 const rgb_color* colorMap) 1351 { 1352 uint32* d = (uint32*)dst; 1353 int32 bytes = numPixels * 4; 1354 uint8 buffer[bytes]; 1355 uint8* b = buffer; 1356 while (numPixels--) { 1357 if (src[3] == 255) { 1358 *(uint32*)b = *(uint32*)src; 1359 } else { 1360 *(uint32*)b = *d; 1361 b[0] = ((src[0] - b[0]) * src[3] + (b[0] << 8)) >> 8; 1362 b[1] = ((src[1] - b[1]) * src[3] + (b[1] << 8)) >> 8; 1363 b[2] = ((src[2] - b[2]) * src[3] + (b[2] << 8)) >> 8; 1364 } 1365 d++; 1366 b += 4; 1367 src += 4; 1368 } 1369 memcpy(dst, buffer, bytes); 1370 } 1371 1372 1373 template<typename sourcePixel> 1374 void 1375 Painter::_TransparentMagicToAlpha(sourcePixel* buffer, uint32 width, 1376 uint32 height, uint32 sourceBytesPerRow, sourcePixel transparentMagic, 1377 BBitmap* output) const 1378 { 1379 uint8* sourceRow = (uint8*)buffer; 1380 uint8* destRow = (uint8*)output->Bits(); 1381 uint32 destBytesPerRow = output->BytesPerRow(); 1382 1383 for (uint32 y = 0; y < height; y++) { 1384 sourcePixel* pixel = (sourcePixel*)sourceRow; 1385 uint32* destPixel = (uint32*)destRow; 1386 for (uint32 x = 0; x < width; x++, pixel++, destPixel++) { 1387 if (*pixel == transparentMagic) 1388 *destPixel &= 0x00ffffff; 1389 } 1390 1391 sourceRow += sourceBytesPerRow; 1392 destRow += destBytesPerRow; 1393 } 1394 } 1395 1396 1397 // _DrawBitmap 1398 void 1399 Painter::_DrawBitmap(agg::rendering_buffer& srcBuffer, color_space format, 1400 BRect actualBitmapRect, BRect bitmapRect, BRect viewRect, 1401 uint32 options) const 1402 { 1403 if (!fValidClipping 1404 || !bitmapRect.IsValid() || !bitmapRect.Intersects(actualBitmapRect) 1405 || !viewRect.IsValid()) { 1406 return; 1407 } 1408 1409 if (!fSubpixelPrecise) { 1410 align_rect_to_pixels(&bitmapRect); 1411 align_rect_to_pixels(&viewRect); 1412 } 1413 1414 TRACE("Painter::_DrawBitmap()\n"); 1415 TRACE(" bitmapRect = (%.1f, %.1f) - (%.1f, %.1f)\n", 1416 bitmapRect.left, bitmapRect.top, bitmapRect.right, bitmapRect.bottom); 1417 TRACE(" viewRect = (%.1f, %.1f) - (%.1f, %.1f)\n", 1418 viewRect.left, viewRect.top, viewRect.right, viewRect.bottom); 1419 1420 double xScale = (viewRect.Width() + 1) / (bitmapRect.Width() + 1); 1421 double yScale = (viewRect.Height() + 1) / (bitmapRect.Height() + 1); 1422 1423 if (xScale == 0.0 || yScale == 0.0) 1424 return; 1425 1426 // compensate for the lefttop offset the actualBitmapRect might have 1427 // actualBitmapRect has the right size, but put it at B_ORIGIN 1428 // bitmapRect is already in good coordinates 1429 actualBitmapRect.OffsetBy(-actualBitmapRect.left, -actualBitmapRect.top); 1430 1431 // constrain rect to passed bitmap bounds 1432 // and transfer the changes to the viewRect with the right scale 1433 if (bitmapRect.left < actualBitmapRect.left) { 1434 float diff = actualBitmapRect.left - bitmapRect.left; 1435 viewRect.left += diff * xScale; 1436 bitmapRect.left = actualBitmapRect.left; 1437 } 1438 if (bitmapRect.top < actualBitmapRect.top) { 1439 float diff = actualBitmapRect.top - bitmapRect.top; 1440 viewRect.top += diff * yScale; 1441 bitmapRect.top = actualBitmapRect.top; 1442 } 1443 if (bitmapRect.right > actualBitmapRect.right) { 1444 float diff = bitmapRect.right - actualBitmapRect.right; 1445 viewRect.right -= diff * xScale; 1446 bitmapRect.right = actualBitmapRect.right; 1447 } 1448 if (bitmapRect.bottom > actualBitmapRect.bottom) { 1449 float diff = bitmapRect.bottom - actualBitmapRect.bottom; 1450 viewRect.bottom -= diff * yScale; 1451 bitmapRect.bottom = actualBitmapRect.bottom; 1452 } 1453 1454 double xOffset = viewRect.left - bitmapRect.left; 1455 double yOffset = viewRect.top - bitmapRect.top; 1456 1457 // optimized code path for B_CMAP8 and no scale 1458 if (xScale == 1.0 && yScale == 1.0) { 1459 if (format == B_CMAP8) { 1460 if (fDrawingMode == B_OP_COPY) { 1461 _DrawBitmapNoScale32(copy_bitmap_row_cmap8_copy, 1, 1462 srcBuffer, xOffset, yOffset, viewRect); 1463 return; 1464 } else if (fDrawingMode == B_OP_OVER) { 1465 _DrawBitmapNoScale32(copy_bitmap_row_cmap8_over, 1, 1466 srcBuffer, xOffset, yOffset, viewRect); 1467 return; 1468 } 1469 } else if (format == B_RGB32) { 1470 if (fDrawingMode == B_OP_OVER) { 1471 _DrawBitmapNoScale32(copy_bitmap_row_bgr32_over, 4, 1472 srcBuffer, xOffset, yOffset, viewRect); 1473 return; 1474 } 1475 } 1476 } 1477 1478 BBitmap* temp = NULL; 1479 ObjectDeleter<BBitmap> tempDeleter; 1480 1481 1482 1483 if ((format != B_RGBA32 && format != B_RGB32) 1484 || (format == B_RGB32 && fDrawingMode != B_OP_COPY 1485 #if 1 1486 // Enabling this would make the behavior compatible to BeOS, which 1487 // treats B_RGB32 bitmaps as B_RGB*A*32 bitmaps in B_OP_ALPHA - unlike in 1488 // all other drawing modes, where B_TRANSPARENT_MAGIC_RGBA32 is handled. 1489 // B_RGB32 bitmaps therefore don't draw correctly on BeOS if they actually 1490 // use this color, unless the alpha channel contains 255 for all other 1491 // pixels, which is inconsistent. 1492 && fDrawingMode != B_OP_ALPHA 1493 #endif 1494 )) { 1495 temp = new (nothrow) BBitmap(actualBitmapRect, B_BITMAP_NO_SERVER_LINK, 1496 B_RGBA32); 1497 if (temp == NULL) { 1498 fprintf(stderr, "Painter::_DrawBitmap() - " 1499 "out of memory for creating temporary conversion bitmap\n"); 1500 return; 1501 } 1502 1503 tempDeleter.SetTo(temp); 1504 1505 status_t err = temp->ImportBits(srcBuffer.buf(), 1506 srcBuffer.height() * srcBuffer.stride(), 1507 srcBuffer.stride(), 0, format); 1508 if (err < B_OK) { 1509 fprintf(stderr, "Painter::_DrawBitmap() - " 1510 "colorspace conversion failed: %s\n", strerror(err)); 1511 return; 1512 } 1513 1514 // the original bitmap might have had some of the 1515 // transaparent magic colors set that we now need to 1516 // make transparent in our RGBA32 bitmap again. 1517 switch (format) { 1518 case B_RGB32: 1519 _TransparentMagicToAlpha((uint32 *)srcBuffer.buf(), 1520 srcBuffer.width(), srcBuffer.height(), 1521 srcBuffer.stride(), B_TRANSPARENT_MAGIC_RGBA32, 1522 temp); 1523 break; 1524 1525 // TODO: not sure if this applies to B_RGBA15 too. It 1526 // should not because B_RGBA15 actually has an alpha 1527 // channel itself and it should have been preserved 1528 // when importing the bitmap. Maybe it applies to 1529 // B_RGB16 though? 1530 case B_RGB15: 1531 _TransparentMagicToAlpha((uint16 *)srcBuffer.buf(), 1532 srcBuffer.width(), srcBuffer.height(), 1533 srcBuffer.stride(), B_TRANSPARENT_MAGIC_RGBA15, 1534 temp); 1535 break; 1536 1537 default: 1538 break; 1539 } 1540 1541 srcBuffer.attach((uint8*)temp->Bits(), 1542 (uint32)actualBitmapRect.IntegerWidth() + 1, 1543 (uint32)actualBitmapRect.IntegerHeight() + 1, 1544 temp->BytesPerRow()); 1545 } 1546 1547 // maybe we can use an optimized version if there is no scale 1548 if (xScale == 1.0 && yScale == 1.0) { 1549 if (fDrawingMode == B_OP_COPY) { 1550 _DrawBitmapNoScale32(copy_bitmap_row_bgr32_copy, 4, srcBuffer, 1551 xOffset, yOffset, viewRect); 1552 return; 1553 } else if (fDrawingMode == B_OP_OVER 1554 || (fDrawingMode == B_OP_ALPHA 1555 && fAlphaSrcMode == B_PIXEL_ALPHA 1556 && fAlphaFncMode == B_ALPHA_OVERLAY)) { 1557 _DrawBitmapNoScale32(copy_bitmap_row_bgr32_alpha, 4, srcBuffer, 1558 xOffset, yOffset, viewRect); 1559 return; 1560 } 1561 } 1562 1563 if (fDrawingMode == B_OP_COPY && (options & B_FILTER_BITMAP_BILINEAR)) { 1564 _DrawBitmapBilinearCopy32(srcBuffer, xOffset, yOffset, xScale, yScale, 1565 viewRect); 1566 return; 1567 } 1568 1569 // for all other cases (non-optimized drawing mode or scaled drawing) 1570 _DrawBitmapGeneric32(srcBuffer, xOffset, yOffset, xScale, yScale, viewRect, 1571 options); 1572 } 1573 1574 #define DEBUG_DRAW_BITMAP 0 1575 1576 // _DrawBitmapNoScale32 1577 template <class F> 1578 void 1579 Painter::_DrawBitmapNoScale32(F copyRowFunction, uint32 bytesPerSourcePixel, 1580 agg::rendering_buffer& srcBuffer, int32 xOffset, int32 yOffset, 1581 BRect viewRect) const 1582 { 1583 // NOTE: this would crash if viewRect was large enough to read outside the 1584 // bitmap, so make sure this is not the case before calling this function! 1585 uint8* dst = fBuffer.row_ptr(0); 1586 uint32 dstBPR = fBuffer.stride(); 1587 1588 const uint8* src = srcBuffer.row_ptr(0); 1589 uint32 srcBPR = srcBuffer.stride(); 1590 1591 int32 left = (int32)viewRect.left; 1592 int32 top = (int32)viewRect.top; 1593 int32 right = (int32)viewRect.right; 1594 int32 bottom = (int32)viewRect.bottom; 1595 1596 #if DEBUG_DRAW_BITMAP 1597 if (left - xOffset < 0 || left - xOffset >= (int32)srcBuffer.width() || 1598 right - xOffset >= (int32)srcBuffer.width() || 1599 top - yOffset < 0 || top - yOffset >= (int32)srcBuffer.height() || 1600 bottom - yOffset >= (int32)srcBuffer.height()) { 1601 1602 char message[256]; 1603 sprintf(message, "reading outside of bitmap (%ld, %ld, %ld, %ld) " 1604 "(%d, %d) (%ld, %ld)", 1605 left - xOffset, top - yOffset, right - xOffset, bottom - yOffset, 1606 srcBuffer.width(), srcBuffer.height(), xOffset, yOffset); 1607 debugger(message); 1608 } 1609 #endif 1610 1611 const rgb_color* colorMap = SystemPalette(); 1612 1613 // copy rects, iterate over clipping boxes 1614 fBaseRenderer.first_clip_box(); 1615 do { 1616 int32 x1 = max_c(fBaseRenderer.xmin(), left); 1617 int32 x2 = min_c(fBaseRenderer.xmax(), right); 1618 if (x1 <= x2) { 1619 int32 y1 = max_c(fBaseRenderer.ymin(), top); 1620 int32 y2 = min_c(fBaseRenderer.ymax(), bottom); 1621 if (y1 <= y2) { 1622 uint8* dstHandle = dst + y1 * dstBPR + x1 * 4; 1623 const uint8* srcHandle = src + (y1 - yOffset) * srcBPR 1624 + (x1 - xOffset) * bytesPerSourcePixel; 1625 1626 for (; y1 <= y2; y1++) { 1627 copyRowFunction(dstHandle, srcHandle, x2 - x1 + 1, 1628 colorMap); 1629 1630 dstHandle += dstBPR; 1631 srcHandle += srcBPR; 1632 } 1633 } 1634 } 1635 } while (fBaseRenderer.next_clip_box()); 1636 } 1637 1638 // _DrawBitmapBilinearCopy32 1639 void 1640 Painter::_DrawBitmapBilinearCopy32(agg::rendering_buffer& srcBuffer, 1641 double xOffset, double yOffset, double xScale, double yScale, 1642 BRect viewRect) const 1643 { 1644 //bigtime_t now = system_time(); 1645 uint32 dstWidth = viewRect.IntegerWidth() + 1; 1646 uint32 dstHeight = viewRect.IntegerHeight() + 1; 1647 uint32 srcWidth = srcBuffer.width(); 1648 uint32 srcHeight = srcBuffer.height(); 1649 1650 struct FilterInfo { 1651 uint16 index; // index into source bitmap row/column 1652 uint16 weight; // weight of the pixel at index [0..255] 1653 }; 1654 1655 //#define FILTER_INFOS_ON_HEAP 1656 #ifdef FILTER_INFOS_ON_HEAP 1657 FilterInfo* xWeights = new (nothrow) FilterInfo[dstWidth]; 1658 FilterInfo* yWeights = new (nothrow) FilterInfo[dstHeight]; 1659 if (xWeights == NULL || yWeights == NULL) { 1660 delete[] xWeights; 1661 delete[] yWeights; 1662 return; 1663 } 1664 #else 1665 // stack based saves about 200µs on 1.85 GHz Core 2 Duo 1666 // don't know if it could be a problem though with stack overflow 1667 FilterInfo xWeights[dstWidth]; 1668 FilterInfo yWeights[dstHeight]; 1669 #endif 1670 1671 // Extract the cropping information for the source bitmap, 1672 // If only a part of the source bitmap is to be drawn with scale, 1673 // the offset will be different from the viewRect left top corner. 1674 int32 xBitmapShift = (int32)(xOffset - viewRect.left); 1675 int32 yBitmapShift = (int32)(yOffset - viewRect.top); 1676 1677 for (uint32 i = 0; i < dstWidth; i++) { 1678 // fractional index into source 1679 // NOTE: It is very important to calculate the fractional index 1680 // into the source pixel grid like this to prevent out of bounds 1681 // access! It will result in the rightmost pixel of the destination 1682 // to access the rightmost pixel of the source with a weighting 1683 // of 255. This in turn will trigger an optimization in the loop 1684 // that also prevents out of bounds access. 1685 float index = i * (srcWidth - 1) / ((srcWidth * xScale) - 1); 1686 // round down to get the left pixel 1687 xWeights[i].index = (uint16)index; 1688 xWeights[i].weight = 255 - (uint16)((index - xWeights[i].index) * 255); 1689 // handle cropped source bitmap 1690 xWeights[i].index += xBitmapShift; 1691 // precompute index for 32 bit pixels 1692 xWeights[i].index *= 4; 1693 } 1694 1695 for (uint32 i = 0; i < dstHeight; i++) { 1696 // fractional index into source 1697 // NOTE: It is very important to calculate the fractional index 1698 // into the source pixel grid like this to prevent out of bounds 1699 // access! It will result in the bottommost pixel of the destination 1700 // to access the bottommost pixel of the source with a weighting 1701 // of 255. This in turn will trigger an optimization in the loop 1702 // that also prevents out of bounds access. 1703 float index = i * (srcHeight - 1) / ((srcHeight * yScale) - 1); 1704 // round down to get the top pixel 1705 yWeights[i].index = (uint16)index; 1706 yWeights[i].weight = 255 - (uint16)((index - yWeights[i].index) * 255); 1707 // handle cropped source bitmap 1708 yWeights[i].index += yBitmapShift; 1709 } 1710 //printf("X: %d/%d ... %d/%d, %d/%d (%ld)\n", 1711 // xWeights[0].index, xWeights[0].weight, 1712 // xWeights[dstWidth - 2].index, xWeights[dstWidth - 2].weight, 1713 // xWeights[dstWidth - 1].index, xWeights[dstWidth - 1].weight, 1714 // dstWidth); 1715 //printf("Y: %d/%d ... %d/%d, %d/%d (%ld)\n", 1716 // yWeights[0].index, yWeights[0].weight, 1717 // yWeights[dstHeight - 2].index, yWeights[dstHeight - 2].weight, 1718 // yWeights[dstHeight - 1].index, yWeights[dstHeight - 1].weight, 1719 // dstHeight); 1720 1721 const int32 left = (int32)viewRect.left; 1722 const int32 top = (int32)viewRect.top; 1723 const int32 right = (int32)viewRect.right; 1724 const int32 bottom = (int32)viewRect.bottom; 1725 1726 const uint32 dstBPR = fBuffer.stride(); 1727 const uint32 srcBPR = srcBuffer.stride(); 1728 1729 // iterate over clipping boxes 1730 fBaseRenderer.first_clip_box(); 1731 do { 1732 const int32 x1 = max_c(fBaseRenderer.xmin(), left); 1733 const int32 x2 = min_c(fBaseRenderer.xmax(), right); 1734 if (x1 > x2) 1735 continue; 1736 1737 int32 y1 = max_c(fBaseRenderer.ymin(), top); 1738 int32 y2 = min_c(fBaseRenderer.ymax(), bottom); 1739 if (y1 > y2) 1740 continue; 1741 1742 // buffer offset into destination 1743 uint8* dst = fBuffer.row_ptr(y1) + x1 * 4; 1744 1745 // x and y are needed as indeces into the wheight arrays, so the 1746 // offset into the target buffer needs to be compensated 1747 const int32 xIndexL = x1 - (int32)xOffset; 1748 const int32 xIndexR = x2 - (int32)xOffset; 1749 y1 -= (int32)yOffset; 1750 y2 -= (int32)yOffset; 1751 1752 //printf("x: %ld - %ld\n", xIndexL, xIndexR); 1753 //printf("y: %ld - %ld\n", y1, y2); 1754 1755 for (; y1 <= y2; y1++) { 1756 // cache the weight of the top and bottom row 1757 const uint16 wTop = yWeights[y1].weight; 1758 const uint16 wBottom = 255 - yWeights[y1].weight; 1759 1760 // buffer offset into source (top row) 1761 register const uint8* src = srcBuffer.row_ptr(yWeights[y1].index); 1762 // buffer handle for destination to be incremented per pixel 1763 register uint8* d = dst; 1764 1765 if (wTop == 255) { 1766 for (int32 x = xIndexL; x <= xIndexR; x++) { 1767 const uint8* s = src + xWeights[x].index; 1768 // This case is important to prevent out 1769 // of bounds access at bottom edge of the source 1770 // bitmap. If the scale is low and integer, it will 1771 // also help the speed. 1772 if (xWeights[x].weight == 255) { 1773 // As above, but to prevent out of bounds 1774 // on the right edge. 1775 *(uint32*)d = *(uint32*)s; 1776 } else { 1777 // Only the left and right pixels are interpolated, 1778 // since the top row has 100% weight. 1779 const uint16 wLeft = xWeights[x].weight; 1780 const uint16 wRight = 255 - wLeft; 1781 d[0] = (s[0] * wLeft + s[4] * wRight) >> 8; 1782 d[1] = (s[1] * wLeft + s[5] * wRight) >> 8; 1783 d[2] = (s[2] * wLeft + s[6] * wRight) >> 8; 1784 } 1785 d += 4; 1786 } 1787 } else { 1788 for (int32 x = xIndexL; x <= xIndexR; x++) { 1789 const uint8* s = src + xWeights[x].index; 1790 if (xWeights[x].weight == 255) { 1791 // Prevent out of bounds access on the right edge 1792 // or simply speed up. 1793 const uint8* sBottom = s + srcBPR; 1794 d[0] = (s[0] * wTop + sBottom[0] * wBottom) >> 8; 1795 d[1] = (s[1] * wTop + sBottom[1] * wBottom) >> 8; 1796 d[2] = (s[2] * wTop + sBottom[2] * wBottom) >> 8; 1797 } else { 1798 // calculate the weighted sum of all four interpolated 1799 // pixels 1800 const uint16 wLeft = xWeights[x].weight; 1801 const uint16 wRight = 255 - wLeft; 1802 // left and right of top row 1803 uint32 t0 = (s[0] * wLeft + s[4] * wRight) * wTop; 1804 uint32 t1 = (s[1] * wLeft + s[5] * wRight) * wTop; 1805 uint32 t2 = (s[2] * wLeft + s[6] * wRight) * wTop; 1806 1807 // left and right of bottom row 1808 s += srcBPR; 1809 t0 += (s[0] * wLeft + s[4] * wRight) * wBottom; 1810 t1 += (s[1] * wLeft + s[5] * wRight) * wBottom; 1811 t2 += (s[2] * wLeft + s[6] * wRight) * wBottom; 1812 1813 d[0] = t0 >> 16; 1814 d[1] = t1 >> 16; 1815 d[2] = t2 >> 16; 1816 } 1817 d += 4; 1818 } 1819 } 1820 dst += dstBPR; 1821 } 1822 } while (fBaseRenderer.next_clip_box()); 1823 1824 #ifdef FILTER_INFOS_ON_HEAP 1825 delete[] xWeights; 1826 delete[] yWeights; 1827 #endif 1828 //printf("draw bitmap %.5fx%.5f: %lld\n", xScale, yScale, system_time() - now); 1829 } 1830 1831 // _DrawBitmapGeneric32 1832 void 1833 Painter::_DrawBitmapGeneric32(agg::rendering_buffer& srcBuffer, 1834 double xOffset, double yOffset, double xScale, double yScale, 1835 BRect viewRect, uint32 options) const 1836 { 1837 TRACE("Painter::_DrawBitmapGeneric32()\n"); 1838 TRACE(" offset: %.1f, %.1f\n", xOffset, yOffset); 1839 TRACE(" scale: %.3f, %.3f\n", xScale, yScale); 1840 TRACE(" viewRect: (%.1f, %.1f) - (%.1f, %.1f)\n", 1841 viewRect.left, viewRect.top, viewRect.right, viewRect.bottom); 1842 // AGG pipeline 1843 1844 // pixel format attached to bitmap 1845 typedef agg::pixfmt_bgra32 pixfmt_image; 1846 pixfmt_image pixf_img(srcBuffer); 1847 1848 agg::trans_affine srcMatrix; 1849 // NOTE: R5 seems to ignore this offset when drawing bitmaps 1850 // srcMatrix *= agg::trans_affine_translation(-actualBitmapRect.left, 1851 // -actualBitmapRect.top); 1852 1853 agg::trans_affine imgMatrix; 1854 imgMatrix *= agg::trans_affine_translation(xOffset - viewRect.left, 1855 yOffset - viewRect.top); 1856 imgMatrix *= agg::trans_affine_scaling(xScale, yScale); 1857 imgMatrix *= agg::trans_affine_translation(viewRect.left, viewRect.top); 1858 imgMatrix.invert(); 1859 1860 // image interpolator 1861 typedef agg::span_interpolator_linear<> interpolator_type; 1862 interpolator_type interpolator(imgMatrix); 1863 1864 // scanline allocator 1865 agg::span_allocator<pixfmt_image::color_type> spanAllocator; 1866 1867 // image accessor attached to pixel format of bitmap 1868 typedef agg::image_accessor_clip<pixfmt_image> source_type; 1869 source_type source(pixf_img, agg::rgba8(0, 0, 0, 0)); 1870 1871 // clip to the current clipping region's frame 1872 viewRect = viewRect & fClippingRegion->Frame(); 1873 // convert to pixel coords (versus pixel indices) 1874 viewRect.right++; 1875 viewRect.bottom++; 1876 1877 // path enclosing the bitmap 1878 fPath.remove_all(); 1879 fPath.move_to(viewRect.left, viewRect.top); 1880 fPath.line_to(viewRect.right, viewRect.top); 1881 fPath.line_to(viewRect.right, viewRect.bottom); 1882 fPath.line_to(viewRect.left, viewRect.bottom); 1883 fPath.close_polygon(); 1884 1885 agg::conv_transform<agg::path_storage> transformedPath(fPath, srcMatrix); 1886 fRasterizer.reset(); 1887 fRasterizer.add_path(transformedPath); 1888 1889 if ((options & B_FILTER_BITMAP_BILINEAR) != 0) { 1890 // image filter (bilinear) 1891 typedef agg::span_image_filter_rgba_bilinear< 1892 source_type, interpolator_type> span_gen_type; 1893 span_gen_type spanGenerator(source, interpolator); 1894 1895 // render the path with the bitmap as scanline fill 1896 agg::render_scanlines_aa(fRasterizer, fUnpackedScanline, fBaseRenderer, 1897 spanAllocator, spanGenerator); 1898 } else { 1899 // image filter (nearest neighbor) 1900 typedef agg::span_image_filter_rgba_nn< 1901 source_type, interpolator_type> span_gen_type; 1902 span_gen_type spanGenerator(source, interpolator); 1903 1904 // render the path with the bitmap as scanline fill 1905 agg::render_scanlines_aa(fRasterizer, fUnpackedScanline, fBaseRenderer, 1906 spanAllocator, spanGenerator); 1907 } 1908 } 1909 1910 // _InvertRect32 1911 void 1912 Painter::_InvertRect32(BRect r) const 1913 { 1914 int32 width = r.IntegerWidth() + 1; 1915 for (int32 y = (int32)r.top; y <= (int32)r.bottom; y++) { 1916 uint8* dst = fBuffer.row_ptr(y); 1917 dst += (int32)r.left * 4; 1918 for (int32 i = 0; i < width; i++) { 1919 dst[0] = 255 - dst[0]; 1920 dst[1] = 255 - dst[1]; 1921 dst[2] = 255 - dst[2]; 1922 dst += 4; 1923 } 1924 } 1925 } 1926 1927 // _BlendRect32 1928 void 1929 Painter::_BlendRect32(const BRect& r, const rgb_color& c) const 1930 { 1931 if (!fValidClipping) 1932 return; 1933 1934 uint8* dst = fBuffer.row_ptr(0); 1935 uint32 bpr = fBuffer.stride(); 1936 1937 int32 left = (int32)r.left; 1938 int32 top = (int32)r.top; 1939 int32 right = (int32)r.right; 1940 int32 bottom = (int32)r.bottom; 1941 1942 // fill rects, iterate over clipping boxes 1943 fBaseRenderer.first_clip_box(); 1944 do { 1945 int32 x1 = max_c(fBaseRenderer.xmin(), left); 1946 int32 x2 = min_c(fBaseRenderer.xmax(), right); 1947 if (x1 <= x2) { 1948 int32 y1 = max_c(fBaseRenderer.ymin(), top); 1949 int32 y2 = min_c(fBaseRenderer.ymax(), bottom); 1950 1951 uint8* offset = dst + x1 * 4 + y1 * bpr; 1952 for (; y1 <= y2; y1++) { 1953 blend_line32(offset, x2 - x1 + 1, c.red, c.green, c.blue, c.alpha); 1954 offset += bpr; 1955 } 1956 } 1957 } while (fBaseRenderer.next_clip_box()); 1958 } 1959 1960 // #pragma mark - 1961 1962 template<class VertexSource> 1963 BRect 1964 Painter::_BoundingBox(VertexSource& path) const 1965 { 1966 double left = 0.0; 1967 double top = 0.0; 1968 double right = -1.0; 1969 double bottom = -1.0; 1970 uint32 pathID[1]; 1971 pathID[0] = 0; 1972 agg::bounding_rect(path, pathID, 0, 1, &left, &top, &right, &bottom); 1973 return BRect(left, top, right, bottom); 1974 } 1975 1976 // agg_line_cap_mode_for 1977 inline agg::line_cap_e 1978 agg_line_cap_mode_for(cap_mode mode) 1979 { 1980 switch (mode) { 1981 case B_BUTT_CAP: 1982 return agg::butt_cap; 1983 case B_SQUARE_CAP: 1984 return agg::square_cap; 1985 case B_ROUND_CAP: 1986 return agg::round_cap; 1987 } 1988 return agg::butt_cap; 1989 } 1990 1991 // agg_line_join_mode_for 1992 inline agg::line_join_e 1993 agg_line_join_mode_for(join_mode mode) 1994 { 1995 switch (mode) { 1996 case B_MITER_JOIN: 1997 return agg::miter_join; 1998 case B_ROUND_JOIN: 1999 return agg::round_join; 2000 case B_BEVEL_JOIN: 2001 case B_BUTT_JOIN: // ?? 2002 case B_SQUARE_JOIN: // ?? 2003 return agg::bevel_join; 2004 } 2005 return agg::miter_join; 2006 } 2007 2008 // _StrokePath 2009 template<class VertexSource> 2010 BRect 2011 Painter::_StrokePath(VertexSource& path) const 2012 { 2013 agg::conv_stroke<VertexSource> stroke(path); 2014 stroke.width(fPenSize); 2015 2016 stroke.line_cap(agg_line_cap_mode_for(fLineCapMode)); 2017 stroke.line_join(agg_line_join_mode_for(fLineJoinMode)); 2018 stroke.miter_limit(fMiterLimit); 2019 2020 if (gSubpixelAntialiasing) { 2021 2022 fSubpixRasterizer.reset(); 2023 fSubpixRasterizer.add_path(stroke); 2024 2025 agg::render_scanlines(fSubpixRasterizer, 2026 fSubpixPackedScanline, fSubpixRenderer); 2027 } else { 2028 2029 fRasterizer.reset(); 2030 fRasterizer.add_path(stroke); 2031 2032 agg::render_scanlines(fRasterizer, fPackedScanline, fRenderer); 2033 } 2034 2035 BRect touched = _BoundingBox(path); 2036 float penSize = ceilf(fPenSize / 2.0); 2037 touched.InsetBy(-penSize, -penSize); 2038 2039 return _Clipped(touched); 2040 } 2041 2042 // _FillPath 2043 template<class VertexSource> 2044 BRect 2045 Painter::_FillPath(VertexSource& path) const 2046 { 2047 if (gSubpixelAntialiasing) { 2048 2049 fSubpixRasterizer.reset(); 2050 fSubpixRasterizer.add_path(path); 2051 agg::render_scanlines(fSubpixRasterizer, 2052 fSubpixPackedScanline, fSubpixRenderer); 2053 } else { 2054 fRasterizer.reset(); 2055 fRasterizer.add_path(path); 2056 agg::render_scanlines(fRasterizer, fPackedScanline, fRenderer); 2057 } 2058 2059 return _Clipped(_BoundingBox(path)); 2060 } 2061 2062