1 /* 2 * Copyright 2005-2007, Stephan Aßmus <superstippi@gmx.de>. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * API to the Anti-Grain Geometry based "Painter" drawing backend. Manages 6 * rendering pipe-lines for stroke, fills, bitmap and text rendering. 7 */ 8 9 10 #include <stdio.h> 11 #include <string.h> 12 13 #include <Bitmap.h> 14 #include <GraphicsDefs.h> 15 #include <Region.h> 16 #include <String.h> 17 18 #include <ShapePrivate.h> 19 20 #include <agg_bezier_arc.h> 21 #include <agg_bounding_rect.h> 22 #include <agg_conv_clip_polygon.h> 23 #include <agg_conv_curve.h> 24 #include <agg_conv_stroke.h> 25 #include <agg_ellipse.h> 26 #include <agg_image_accessors.h> 27 #include <agg_path_storage.h> 28 #include <agg_pixfmt_rgba.h> 29 #include <agg_rounded_rect.h> 30 #include <agg_span_allocator.h> 31 #include <agg_span_image_filter_rgba.h> 32 #include <agg_span_interpolator_linear.h> 33 34 #include "drawing_support.h" 35 36 #include "DrawState.h" 37 38 #include "DrawingMode.h" 39 #include "PatternHandler.h" 40 #include "RenderingBuffer.h" 41 #include "ServerBitmap.h" 42 #include "ServerFont.h" 43 #include "SystemPalette.h" 44 45 #include "Painter.h" 46 47 48 #define CHECK_CLIPPING if (!fValidClipping) return BRect(0, 0, -1, -1); 49 #define CHECK_CLIPPING_NO_RETURN if (!fValidClipping) return; 50 51 52 // constructor 53 Painter::Painter() 54 : fBuffer(), 55 fPixelFormat(fBuffer, &fPatternHandler), 56 fBaseRenderer(fPixelFormat), 57 fUnpackedScanline(), 58 fPackedScanline(), 59 fRasterizer(), 60 fRenderer(fBaseRenderer), 61 fRendererBin(fBaseRenderer), 62 63 fPath(), 64 fCurve(fPath), 65 66 fSubpixelPrecise(false), 67 68 fPenSize(1.0), 69 fClippingRegion(NULL), 70 fValidClipping(false), 71 fDrawingMode(B_OP_COPY), 72 fDrawingText(false), 73 fAlphaSrcMode(B_PIXEL_ALPHA), 74 fAlphaFncMode(B_ALPHA_OVERLAY), 75 fLineCapMode(B_BUTT_CAP), 76 fLineJoinMode(B_MITER_JOIN), 77 fMiterLimit(B_DEFAULT_MITER_LIMIT), 78 79 fPatternHandler(), 80 fTextRenderer(fRenderer, fRendererBin, fUnpackedScanline) 81 { 82 fPixelFormat.SetDrawingMode(fDrawingMode, fAlphaSrcMode, fAlphaFncMode, false); 83 84 #if ALIASED_DRAWING 85 fRasterizer.gamma(agg::gamma_threshold(0.5)); 86 #endif 87 } 88 89 // destructor 90 Painter::~Painter() 91 { 92 } 93 94 // #pragma mark - 95 96 // AttachToBuffer 97 void 98 Painter::AttachToBuffer(RenderingBuffer* buffer) 99 { 100 if (buffer && buffer->InitCheck() >= B_OK && 101 // TODO: implement drawing on B_RGB24, B_RGB15, B_RGB16, B_CMAP8 and B_GRAY8 :-[ 102 (buffer->ColorSpace() == B_RGBA32 || buffer->ColorSpace() == B_RGB32)) { 103 fBuffer.attach((uint8*)buffer->Bits(), 104 buffer->Width(), buffer->Height(), buffer->BytesPerRow()); 105 106 107 // These are the AGG renderes and rasterizes which 108 // will be used for stroking paths 109 110 _SetRendererColor(fPatternHandler.HighColor()); 111 } 112 } 113 114 // DetachFromBuffer 115 void 116 Painter::DetachFromBuffer() 117 { 118 } 119 120 // #pragma mark - 121 122 // SetDrawState 123 void 124 Painter::SetDrawState(const DrawState* data, int32 xOffset, int32 yOffset) 125 { 126 // NOTE: The custom clipping in "data" is ignored, because it has already 127 // been taken into account elsewhere 128 129 // TODO: optimize "context switch" for speed... 130 // but for now... 131 SetPenSize(data->PenSize()); 132 133 SetFont(data); 134 135 fSubpixelPrecise = data->SubPixelPrecise(); 136 137 // any of these conditions means we need to use a different drawing 138 // mode instance 139 bool updateDrawingMode = !(data->GetPattern() == fPatternHandler.GetPattern()) || 140 data->GetDrawingMode() != fDrawingMode || 141 (data->GetDrawingMode() == B_OP_ALPHA && (data->AlphaSrcMode() != fAlphaSrcMode || 142 data->AlphaFncMode() != fAlphaFncMode)); 143 144 fDrawingMode = data->GetDrawingMode(); 145 fAlphaSrcMode = data->AlphaSrcMode(); 146 fAlphaFncMode = data->AlphaFncMode(); 147 fPatternHandler.SetPattern(data->GetPattern()); 148 fPatternHandler.SetOffsets(xOffset, yOffset); 149 fLineCapMode = data->LineCapMode(); 150 fLineJoinMode = data->LineJoinMode(); 151 fMiterLimit = data->MiterLimit(); 152 153 // adopt the color *after* the pattern is set 154 // to set the renderers to the correct color 155 SetHighColor(data->HighColor()); 156 SetLowColor(data->LowColor()); 157 158 if (updateDrawingMode || fPixelFormat.UsesOpCopyForText()) 159 _UpdateDrawingMode(); 160 } 161 162 // #pragma mark - state 163 164 // ConstrainClipping 165 void 166 Painter::ConstrainClipping(const BRegion* region) 167 { 168 fClippingRegion = region; 169 fBaseRenderer.set_clipping_region(const_cast<BRegion*>(region)); 170 fValidClipping = region->Frame().IsValid(); 171 172 if (fValidClipping) { 173 clipping_rect cb = fClippingRegion->FrameInt(); 174 fRasterizer.clip_box(cb.left, cb.top, cb.right + 1, cb.bottom + 1); 175 } 176 } 177 178 // SetHighColor 179 void 180 Painter::SetHighColor(const rgb_color& color) 181 { 182 if (fPatternHandler.HighColor() == color) 183 return; 184 fPatternHandler.SetHighColor(color); 185 if (*(fPatternHandler.GetR5Pattern()) == B_SOLID_HIGH) 186 _SetRendererColor(color); 187 } 188 189 // SetLowColor 190 void 191 Painter::SetLowColor(const rgb_color& color) 192 { 193 fPatternHandler.SetLowColor(color); 194 if (*(fPatternHandler.GetR5Pattern()) == B_SOLID_LOW) 195 _SetRendererColor(color); 196 } 197 198 // SetDrawingMode 199 void 200 Painter::SetDrawingMode(drawing_mode mode) 201 { 202 if (fDrawingMode != mode) { 203 fDrawingMode = mode; 204 _UpdateDrawingMode(); 205 } 206 } 207 208 // SetBlendingMode 209 void 210 Painter::SetBlendingMode(source_alpha srcAlpha, alpha_function alphaFunc) 211 { 212 if (fAlphaSrcMode != srcAlpha || fAlphaFncMode != alphaFunc) { 213 fAlphaSrcMode = srcAlpha; 214 fAlphaFncMode = alphaFunc; 215 if (fDrawingMode == B_OP_ALPHA) 216 _UpdateDrawingMode(); 217 } 218 } 219 220 // SetPenSize 221 void 222 Painter::SetPenSize(float size) 223 { 224 fPenSize = size; 225 } 226 227 // SetStrokeMode 228 void 229 Painter::SetStrokeMode(cap_mode lineCap, join_mode joinMode, float miterLimit) 230 { 231 fLineCapMode = lineCap; 232 fLineJoinMode = joinMode; 233 fMiterLimit = miterLimit; 234 } 235 236 // SetPattern 237 void 238 Painter::SetPattern(const pattern& p, bool drawingText) 239 { 240 if (!(p == *fPatternHandler.GetR5Pattern()) || drawingText != fDrawingText) { 241 fPatternHandler.SetPattern(p); 242 fDrawingText = drawingText; 243 _UpdateDrawingMode(fDrawingText); 244 245 // update renderer color if necessary 246 if (fPatternHandler.IsSolidHigh()) { 247 // pattern was not solid high before 248 _SetRendererColor(fPatternHandler.HighColor()); 249 } else if (fPatternHandler.IsSolidLow()) { 250 // pattern was not solid low before 251 _SetRendererColor(fPatternHandler.LowColor()); 252 } 253 } 254 } 255 256 // SetFont 257 void 258 Painter::SetFont(const ServerFont& font) 259 { 260 fTextRenderer.SetFont(font); 261 fTextRenderer.SetAntialiasing( 262 !(font.Flags() & B_DISABLE_ANTIALIASING)); 263 } 264 265 // SetFont 266 void 267 Painter::SetFont(const DrawState* state) 268 { 269 fTextRenderer.SetFont(state->Font()); 270 fTextRenderer.SetAntialiasing(!(state->ForceFontAliasing() 271 || state->Font().Flags() & B_DISABLE_ANTIALIASING)); 272 } 273 274 // #pragma mark - drawing 275 276 // StrokeLine 277 void 278 Painter::StrokeLine(BPoint a, BPoint b) 279 { 280 CHECK_CLIPPING_NO_RETURN 281 282 // "false" means not to do the pixel center offset, 283 // because it would mess up our optimized versions 284 _Transform(&a, false); 285 _Transform(&b, false); 286 287 // first, try an optimized version 288 if (fPenSize == 1.0 289 && (fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER)) { 290 291 pattern pat = *fPatternHandler.GetR5Pattern(); 292 if (pat == B_SOLID_HIGH 293 && StraightLine(a, b, fPatternHandler.HighColor())) { 294 return; 295 } else if (pat == B_SOLID_LOW 296 && StraightLine(a, b, fPatternHandler.LowColor())) { 297 return; 298 } 299 } 300 301 fPath.remove_all(); 302 303 if (a == b) { 304 // special case dots 305 if (fPenSize == 1.0 && !fSubpixelPrecise) { 306 if (fClippingRegion->Contains(a)) { 307 agg::rgba8 dummyColor; 308 fPixelFormat.blend_pixel(a.x, a.y, dummyColor, 255); 309 } 310 } else { 311 fPath.move_to(a.x, a.y); 312 fPath.line_to(a.x + 1, a.y); 313 fPath.line_to(a.x + 1, a.y + 1); 314 fPath.line_to(a.x, a.y + 1); 315 316 _FillPath(fPath); 317 } 318 } else { 319 // do the pixel center offset here 320 // tweak ends to "include" the pixel at the index, 321 // we need to do this in order to produce results like R5, 322 // where coordinates were inclusive 323 if (!fSubpixelPrecise) { 324 bool centerOnLine = fmodf(fPenSize, 2.0) != 0.0; 325 if (a.x == b.x) { 326 // shift to pixel center vertically 327 if (centerOnLine) { 328 a.x += 0.5; 329 b.x += 0.5; 330 } 331 // extend on bottom end 332 if (a.y < b.y) 333 b.y++; 334 else 335 a.y++; 336 } else if (a.y == b.y) { 337 if (centerOnLine) { 338 // shift to pixel center horizontally 339 a.y += 0.5; 340 b.y += 0.5; 341 } 342 // extend on right end 343 if (a.x < b.x) 344 b.x++; 345 else 346 a.x++; 347 } else { 348 // do this regardless of pensize 349 if (a.x < b.x) 350 b.x++; 351 else 352 a.x++; 353 if (a.y < b.y) 354 b.y++; 355 else 356 a.y++; 357 } 358 } 359 360 fPath.move_to(a.x, a.y); 361 fPath.line_to(b.x, b.y); 362 363 _StrokePath(fPath); 364 } 365 } 366 367 // StraightLine 368 bool 369 Painter::StraightLine(BPoint a, BPoint b, const rgb_color& c) const 370 { 371 if (!fValidClipping) 372 return false; 373 374 if (a.x == b.x) { 375 // vertical 376 uint8* dst = fBuffer.row_ptr(0); 377 uint32 bpr = fBuffer.stride(); 378 int32 x = (int32)a.x; 379 dst += x * 4; 380 int32 y1 = (int32)min_c(a.y, b.y); 381 int32 y2 = (int32)max_c(a.y, b.y); 382 pixel32 color; 383 color.data8[0] = c.blue; 384 color.data8[1] = c.green; 385 color.data8[2] = c.red; 386 color.data8[3] = 255; 387 // draw a line, iterate over clipping boxes 388 fBaseRenderer.first_clip_box(); 389 do { 390 if (fBaseRenderer.xmin() <= x && 391 fBaseRenderer.xmax() >= x) { 392 int32 i = max_c(fBaseRenderer.ymin(), y1); 393 int32 end = min_c(fBaseRenderer.ymax(), y2); 394 uint8* handle = dst + i * bpr; 395 for (; i <= end; i++) { 396 *(uint32*)handle = color.data32; 397 handle += bpr; 398 } 399 } 400 } while (fBaseRenderer.next_clip_box()); 401 402 return true; 403 404 } else if (a.y == b.y) { 405 // horizontal 406 int32 y = (int32)a.y; 407 uint8* dst = fBuffer.row_ptr(y); 408 int32 x1 = (int32)min_c(a.x, b.x); 409 int32 x2 = (int32)max_c(a.x, b.x); 410 pixel32 color; 411 color.data8[0] = c.blue; 412 color.data8[1] = c.green; 413 color.data8[2] = c.red; 414 color.data8[3] = 255; 415 // draw a line, iterate over clipping boxes 416 fBaseRenderer.first_clip_box(); 417 do { 418 if (fBaseRenderer.ymin() <= y && 419 fBaseRenderer.ymax() >= y) { 420 int32 i = max_c(fBaseRenderer.xmin(), x1); 421 int32 end = min_c(fBaseRenderer.xmax(), x2); 422 uint32* handle = (uint32*)(dst + i * 4); 423 for (; i <= end; i++) { 424 *handle++ = color.data32; 425 } 426 } 427 } while (fBaseRenderer.next_clip_box()); 428 429 return true; 430 } 431 return false; 432 } 433 434 // #pragma mark - 435 436 // StrokeTriangle 437 BRect 438 Painter::StrokeTriangle(BPoint pt1, BPoint pt2, BPoint pt3) const 439 { 440 return _DrawTriangle(pt1, pt2, pt3, false); 441 } 442 443 // FillTriangle 444 BRect 445 Painter::FillTriangle(BPoint pt1, BPoint pt2, BPoint pt3) const 446 { 447 return _DrawTriangle(pt1, pt2, pt3, true); 448 } 449 450 // DrawPolygon 451 BRect 452 Painter::DrawPolygon(BPoint* p, int32 numPts, 453 bool filled, bool closed) const 454 { 455 CHECK_CLIPPING 456 457 if (numPts > 0) { 458 459 fPath.remove_all(); 460 461 _Transform(p); 462 fPath.move_to(p->x, p->y); 463 464 for (int32 i = 1; i < numPts; i++) { 465 p++; 466 _Transform(p); 467 fPath.line_to(p->x, p->y); 468 } 469 470 if (closed) 471 fPath.close_polygon(); 472 473 if (filled) 474 return _FillPath(fPath); 475 else 476 return _StrokePath(fPath); 477 } 478 return BRect(0.0, 0.0, -1.0, -1.0); 479 } 480 481 // DrawBezier 482 BRect 483 Painter::DrawBezier(BPoint* p, bool filled) const 484 { 485 CHECK_CLIPPING 486 487 fPath.remove_all(); 488 489 _Transform(&(p[0])); 490 _Transform(&(p[1])); 491 _Transform(&(p[2])); 492 _Transform(&(p[3])); 493 494 fPath.move_to(p[0].x, p[0].y); 495 fPath.curve4(p[1].x, p[1].y, 496 p[2].x, p[2].y, 497 p[3].x, p[3].y); 498 499 500 if (filled) { 501 fPath.close_polygon(); 502 return _FillPath(fCurve); 503 } else { 504 return _StrokePath(fCurve); 505 } 506 } 507 508 509 510 // DrawShape 511 BRect 512 Painter::DrawShape(const int32& opCount, const uint32* opList, 513 const int32& ptCount, const BPoint* points, 514 bool filled) const 515 { 516 CHECK_CLIPPING 517 518 // TODO: if shapes are ever used more heavily in Haiku, 519 // it would be nice to use BShape data directly (write 520 // an AGG "VertexSource" adaptor) 521 fPath.remove_all(); 522 for (int32 i = 0; i < opCount; i++) { 523 uint32 op = opList[i] & 0xFF000000; 524 if (op & OP_MOVETO) { 525 fPath.move_to(points->x, points->y); 526 points++; 527 } 528 529 if (op & OP_LINETO) { 530 int32 count = opList[i] & 0x00FFFFFF; 531 while (count--) { 532 fPath.line_to(points->x, points->y); 533 points++; 534 } 535 } 536 537 if (op & OP_BEZIERTO) { 538 int32 count = opList[i] & 0x00FFFFFF; 539 while (count) { 540 fPath.curve4(points[0].x, points[0].y, 541 points[1].x, points[1].y, 542 points[2].x, points[2].y); 543 points += 3; 544 count -= 3; 545 } 546 } 547 548 if (op & OP_CLOSE) 549 fPath.close_polygon(); 550 } 551 552 if (filled) 553 return _FillPath(fCurve); 554 else 555 return _StrokePath(fCurve); 556 } 557 558 // StrokeRect 559 BRect 560 Painter::StrokeRect(const BRect& r) const 561 { 562 CHECK_CLIPPING 563 564 BPoint a(r.left, r.top); 565 BPoint b(r.right, r.bottom); 566 _Transform(&a, false); 567 _Transform(&b, false); 568 569 // first, try an optimized version 570 if (fPenSize == 1.0 && 571 (fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER)) { 572 pattern p = *fPatternHandler.GetR5Pattern(); 573 if (p == B_SOLID_HIGH) { 574 BRect rect(a, b); 575 StrokeRect(rect, 576 fPatternHandler.HighColor()); 577 return _Clipped(rect); 578 } else if (p == B_SOLID_LOW) { 579 BRect rect(a, b); 580 StrokeRect(rect, 581 fPatternHandler.LowColor()); 582 return _Clipped(rect); 583 } 584 } 585 586 if (fmodf(fPenSize, 2.0) != 0.0) { 587 // shift coords to center of pixels 588 a.x += 0.5; 589 a.y += 0.5; 590 b.x += 0.5; 591 b.y += 0.5; 592 } 593 594 fPath.remove_all(); 595 fPath.move_to(a.x, a.y); 596 if (a.x == b.x || a.y == b.y) { 597 // special case rects with one pixel height or width 598 fPath.line_to(b.x, b.y); 599 } else { 600 fPath.line_to(b.x, a.y); 601 fPath.line_to(b.x, b.y); 602 fPath.line_to(a.x, b.y); 603 } 604 fPath.close_polygon(); 605 606 return _StrokePath(fPath); 607 } 608 609 // StrokeRect 610 void 611 Painter::StrokeRect(const BRect& r, const rgb_color& c) const 612 { 613 StraightLine(BPoint(r.left, r.top), 614 BPoint(r.right - 1, r.top), c); 615 StraightLine(BPoint(r.right, r.top), 616 BPoint(r.right, r.bottom - 1), c); 617 StraightLine(BPoint(r.right, r.bottom), 618 BPoint(r.left + 1, r.bottom), c); 619 StraightLine(BPoint(r.left, r.bottom), 620 BPoint(r.left, r.top + 1), c); 621 } 622 623 // FillRect 624 BRect 625 Painter::FillRect(const BRect& r) const 626 { 627 CHECK_CLIPPING 628 629 // support invalid rects 630 BPoint a(min_c(r.left, r.right), min_c(r.top, r.bottom)); 631 BPoint b(max_c(r.left, r.right), max_c(r.top, r.bottom)); 632 _Transform(&a, false); 633 _Transform(&b, false); 634 635 // first, try an optimized version 636 if (fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER) { 637 pattern p = *fPatternHandler.GetR5Pattern(); 638 if (p == B_SOLID_HIGH) { 639 BRect rect(a, b); 640 FillRect(rect, fPatternHandler.HighColor()); 641 return _Clipped(rect); 642 } else if (p == B_SOLID_LOW) { 643 BRect rect(a, b); 644 FillRect(rect, fPatternHandler.LowColor()); 645 return _Clipped(rect); 646 } 647 } 648 if (fDrawingMode == B_OP_ALPHA && fAlphaFncMode == B_ALPHA_OVERLAY) { 649 pattern p = *fPatternHandler.GetR5Pattern(); 650 if (p == B_SOLID_HIGH) { 651 BRect rect(a, b); 652 _BlendRect32(rect, fPatternHandler.HighColor()); 653 return _Clipped(rect); 654 } else if (p == B_SOLID_LOW) { 655 rgb_color c = fPatternHandler.LowColor(); 656 if (fAlphaSrcMode == B_CONSTANT_ALPHA) 657 c.alpha = fPatternHandler.HighColor().alpha; 658 BRect rect(a, b); 659 _BlendRect32(rect, c); 660 return _Clipped(rect); 661 } 662 } 663 664 665 // account for stricter interpretation of coordinates in AGG 666 // the rectangle ranges from the top-left (.0, .0) 667 // to the bottom-right (.9999, .9999) corner of pixels 668 b.x += 1.0; 669 b.y += 1.0; 670 671 fPath.remove_all(); 672 fPath.move_to(a.x, a.y); 673 fPath.line_to(b.x, a.y); 674 fPath.line_to(b.x, b.y); 675 fPath.line_to(a.x, b.y); 676 fPath.close_polygon(); 677 678 return _FillPath(fPath); 679 } 680 681 // FillRect 682 void 683 Painter::FillRect(const BRect& r, const rgb_color& c) const 684 { 685 if (!fValidClipping) 686 return; 687 688 uint8* dst = fBuffer.row_ptr(0); 689 uint32 bpr = fBuffer.stride(); 690 int32 left = (int32)r.left; 691 int32 top = (int32)r.top; 692 int32 right = (int32)r.right; 693 int32 bottom = (int32)r.bottom; 694 // get a 32 bit pixel ready with the color 695 pixel32 color; 696 color.data8[0] = c.blue; 697 color.data8[1] = c.green; 698 color.data8[2] = c.red; 699 color.data8[3] = c.alpha; 700 // fill rects, iterate over clipping boxes 701 fBaseRenderer.first_clip_box(); 702 do { 703 int32 x1 = max_c(fBaseRenderer.xmin(), left); 704 int32 x2 = min_c(fBaseRenderer.xmax(), right); 705 if (x1 <= x2) { 706 int32 y1 = max_c(fBaseRenderer.ymin(), top); 707 int32 y2 = min_c(fBaseRenderer.ymax(), bottom); 708 uint8* offset = dst + x1 * 4; 709 for (; y1 <= y2; y1++) { 710 // uint32* handle = (uint32*)(offset + y1 * bpr); 711 // for (int32 x = x1; x <= x2; x++) { 712 // *handle++ = color.data32; 713 // } 714 gfxset32(offset + y1 * bpr, color.data32, (x2 - x1 + 1) * 4); 715 } 716 } 717 } while (fBaseRenderer.next_clip_box()); 718 } 719 720 // FillRectNoClipping 721 void 722 Painter::FillRectNoClipping(const BRect& r, const rgb_color& c) const 723 { 724 int32 left = (int32)r.left; 725 int32 y = (int32)r.top; 726 int32 right = (int32)r.right; 727 int32 bottom = (int32)r.bottom; 728 729 uint8* dst = fBuffer.row_ptr(y); 730 uint32 bpr = fBuffer.stride(); 731 732 // get a 32 bit pixel ready with the color 733 pixel32 color; 734 color.data8[0] = c.blue; 735 color.data8[1] = c.green; 736 color.data8[2] = c.red; 737 color.data8[3] = c.alpha; 738 739 dst += left * 4; 740 741 for (; y <= bottom; y++) { 742 // uint32* handle = (uint32*)dst; 743 // for (int32 x = left; x <= right; x++) { 744 // *handle++ = color.data32; 745 // } 746 gfxset32(dst, color.data32, (right - left + 1) * 4); 747 dst += bpr; 748 } 749 } 750 751 // StrokeRoundRect 752 BRect 753 Painter::StrokeRoundRect(const BRect& r, float xRadius, float yRadius) const 754 { 755 CHECK_CLIPPING 756 757 BPoint lt(r.left, r.top); 758 BPoint rb(r.right, r.bottom); 759 bool centerOffset = fPenSize == 1.0; 760 // TODO: use this when using _StrokePath() 761 // bool centerOffset = fmodf(fPenSize, 2.0) != 0.0; 762 _Transform(<, centerOffset); 763 _Transform(&rb, centerOffset); 764 765 if (fPenSize == 1.0) { 766 agg::rounded_rect rect; 767 rect.rect(lt.x, lt.y, rb.x, rb.y); 768 rect.radius(xRadius, yRadius); 769 770 return _StrokePath(rect); 771 } else { 772 // NOTE: This implementation might seem a little strange, but it makes 773 // stroked round rects look like on R5. A more correct way would be to use 774 // _StrokePath() as above (independent from fPenSize). 775 // The fact that the bounding box of the round rect is not enlarged 776 // by fPenSize/2 is actually on purpose, though one could argue it is unexpected. 777 778 // enclose the right and bottom edge 779 rb.x++; 780 rb.y++; 781 782 agg::rounded_rect outer; 783 outer.rect(lt.x, lt.y, rb.x, rb.y); 784 outer.radius(xRadius, yRadius); 785 786 fRasterizer.reset(); 787 fRasterizer.add_path(outer); 788 789 // don't add an inner hole if the "size is negative", this avoids some 790 // defects that can be observed on R5 and could be regarded as a bug. 791 if (2 * fPenSize < rb.x - lt.x && 2 * fPenSize < rb.y - lt.y) { 792 agg::rounded_rect inner; 793 inner.rect(lt.x + fPenSize, lt.y + fPenSize, rb.x - fPenSize, rb.y - fPenSize); 794 inner.radius(max_c(0.0, xRadius - fPenSize), max_c(0.0, yRadius - fPenSize)); 795 796 fRasterizer.add_path(inner); 797 } 798 799 // make the inner rect work as a hole 800 fRasterizer.filling_rule(agg::fill_even_odd); 801 802 if (fPenSize > 2) 803 agg::render_scanlines(fRasterizer, fPackedScanline, fRenderer); 804 else 805 agg::render_scanlines(fRasterizer, fUnpackedScanline, fRenderer); 806 807 // reset to default 808 fRasterizer.filling_rule(agg::fill_non_zero); 809 810 return _Clipped(_BoundingBox(outer)); 811 } 812 } 813 814 // FillRoundRect 815 BRect 816 Painter::FillRoundRect(const BRect& r, float xRadius, float yRadius) const 817 { 818 CHECK_CLIPPING 819 820 BPoint lt(r.left, r.top); 821 BPoint rb(r.right, r.bottom); 822 _Transform(<, false); 823 _Transform(&rb, false); 824 825 // account for stricter interpretation of coordinates in AGG 826 // the rectangle ranges from the top-left (.0, .0) 827 // to the bottom-right (.9999, .9999) corner of pixels 828 rb.x += 1.0; 829 rb.y += 1.0; 830 831 agg::rounded_rect rect; 832 rect.rect(lt.x, lt.y, rb.x, rb.y); 833 rect.radius(xRadius, yRadius); 834 835 return _FillPath(rect); 836 } 837 838 // AlignEllipseRect 839 void 840 Painter::AlignEllipseRect(BRect* rect, bool filled) const 841 { 842 if (!fSubpixelPrecise) { 843 // align rect to pixels 844 align_rect_to_pixels(rect); 845 // account for "pixel index" versus "pixel area" 846 rect->right++; 847 rect->bottom++; 848 if (!filled && fmodf(fPenSize, 2.0) != 0.0) { 849 // align the stroke 850 rect->InsetBy(0.5, 0.5); 851 } 852 } 853 } 854 855 // DrawEllipse 856 BRect 857 Painter::DrawEllipse(BRect r, bool fill) const 858 { 859 CHECK_CLIPPING 860 861 AlignEllipseRect(&r, fill); 862 863 float xRadius = r.Width() / 2.0; 864 float yRadius = r.Height() / 2.0; 865 BPoint center(r.left + xRadius, r.top + yRadius); 866 867 int32 divisions = (int32)((xRadius + yRadius + 2 * fPenSize) * PI / 2); 868 if (divisions < 12) 869 divisions = 12; 870 if (divisions > 4096) 871 divisions = 4096; 872 873 if (fill) { 874 agg::ellipse path(center.x, center.y, xRadius, yRadius, divisions); 875 876 return _FillPath(path); 877 } else { 878 // NOTE: This implementation might seem a little strange, but it makes 879 // stroked ellipses look like on R5. A more correct way would be to use 880 // _StrokePath(), but it currently has its own set of problems with narrow 881 // ellipses (for small xRadii or yRadii). 882 float inset = fPenSize / 2.0; 883 agg::ellipse inner(center.x, center.y, 884 max_c(0.0, xRadius - inset), 885 max_c(0.0, yRadius - inset), 886 divisions); 887 agg::ellipse outer(center.x, center.y, 888 xRadius + inset, 889 yRadius + inset, 890 divisions); 891 892 fRasterizer.reset(); 893 fRasterizer.add_path(outer); 894 fRasterizer.add_path(inner); 895 896 // make the inner ellipse work as a hole 897 fRasterizer.filling_rule(agg::fill_even_odd); 898 899 if (fPenSize > 4) 900 agg::render_scanlines(fRasterizer, fPackedScanline, fRenderer); 901 else 902 agg::render_scanlines(fRasterizer, fUnpackedScanline, fRenderer); 903 904 // reset to default 905 fRasterizer.filling_rule(agg::fill_non_zero); 906 907 return _Clipped(_BoundingBox(outer)); 908 } 909 } 910 911 // StrokeArc 912 BRect 913 Painter::StrokeArc(BPoint center, float xRadius, float yRadius, 914 float angle, float span) const 915 { 916 CHECK_CLIPPING 917 918 _Transform(¢er); 919 920 double angleRad = (angle * PI) / 180.0; 921 double spanRad = (span * PI) / 180.0; 922 agg::bezier_arc arc(center.x, center.y, xRadius, yRadius, 923 -angleRad, -spanRad); 924 925 agg::conv_curve<agg::bezier_arc> path(arc); 926 path.approximation_scale(2.0); 927 928 return _StrokePath(path); 929 } 930 931 // FillArc 932 BRect 933 Painter::FillArc(BPoint center, float xRadius, float yRadius, 934 float angle, float span) const 935 { 936 CHECK_CLIPPING 937 938 _Transform(¢er); 939 940 double angleRad = (angle * PI) / 180.0; 941 double spanRad = (span * PI) / 180.0; 942 agg::bezier_arc arc(center.x, center.y, xRadius, yRadius, 943 -angleRad, -spanRad); 944 945 agg::conv_curve<agg::bezier_arc> segmentedArc(arc); 946 947 fPath.remove_all(); 948 949 // build a new path by starting at the center point, 950 // then traversing the arc, then going back to the center 951 fPath.move_to(center.x, center.y); 952 953 segmentedArc.rewind(0); 954 double x; 955 double y; 956 unsigned cmd = segmentedArc.vertex(&x, &y); 957 while (!agg::is_stop(cmd)) { 958 fPath.line_to(x, y); 959 cmd = segmentedArc.vertex(&x, &y); 960 } 961 962 fPath.close_polygon(); 963 964 return _FillPath(fPath); 965 } 966 967 // #pragma mark - 968 969 // DrawString 970 BRect 971 Painter::DrawString(const char* utf8String, uint32 length, 972 BPoint baseLine, const escapement_delta* delta, 973 FontCacheReference* cacheReference) 974 { 975 CHECK_CLIPPING 976 977 if (!fSubpixelPrecise) { 978 baseLine.x = roundf(baseLine.x); 979 baseLine.y = roundf(baseLine.y); 980 } 981 982 BRect bounds(0.0, 0.0, -1.0, -1.0); 983 984 // text is not rendered with patterns, but we need to 985 // make sure that the previous pattern is restored 986 pattern oldPattern = *fPatternHandler.GetR5Pattern(); 987 SetPattern(B_SOLID_HIGH, true); 988 989 bounds = fTextRenderer.RenderString(utf8String, length, 990 baseLine, fClippingRegion->Frame(), false, NULL, delta, 991 cacheReference); 992 993 SetPattern(oldPattern); 994 995 return _Clipped(bounds); 996 } 997 998 // BoundingBox 999 BRect 1000 Painter::BoundingBox(const char* utf8String, uint32 length, 1001 BPoint baseLine, BPoint* penLocation, 1002 const escapement_delta* delta, 1003 FontCacheReference* cacheReference) const 1004 { 1005 if (!fSubpixelPrecise) { 1006 baseLine.x = roundf(baseLine.x); 1007 baseLine.y = roundf(baseLine.y); 1008 } 1009 1010 static BRect dummy; 1011 return fTextRenderer.RenderString(utf8String, length, 1012 baseLine, dummy, true, penLocation, delta, cacheReference); 1013 } 1014 1015 // StringWidth 1016 float 1017 Painter::StringWidth(const char* utf8String, uint32 length, 1018 const escapement_delta* delta) 1019 { 1020 return Font().StringWidth(utf8String, length, delta); 1021 } 1022 1023 // #pragma mark - 1024 1025 // DrawBitmap 1026 BRect 1027 Painter::DrawBitmap(const ServerBitmap* bitmap, 1028 BRect bitmapRect, BRect viewRect) const 1029 { 1030 CHECK_CLIPPING 1031 1032 BRect touched = _Clipped(viewRect); 1033 1034 if (bitmap && bitmap->IsValid() && touched.IsValid()) { 1035 // the native bitmap coordinate system 1036 BRect actualBitmapRect(bitmap->Bounds()); 1037 1038 agg::rendering_buffer srcBuffer; 1039 srcBuffer.attach(bitmap->Bits(), 1040 bitmap->Width(), 1041 bitmap->Height(), 1042 bitmap->BytesPerRow()); 1043 1044 _DrawBitmap(srcBuffer, bitmap->ColorSpace(), actualBitmapRect, 1045 bitmapRect, viewRect); 1046 } 1047 return touched; 1048 } 1049 1050 // #pragma mark - 1051 1052 // FillRegion 1053 BRect 1054 Painter::FillRegion(const BRegion* region) const 1055 { 1056 CHECK_CLIPPING 1057 1058 BRegion copy(*region); 1059 int32 count = copy.CountRects(); 1060 BRect touched = FillRect(copy.RectAt(0)); 1061 for (int32 i = 1; i < count; i++) { 1062 touched = touched | FillRect(copy.RectAt(i)); 1063 } 1064 return touched; 1065 } 1066 1067 // InvertRect 1068 BRect 1069 Painter::InvertRect(const BRect& r) const 1070 { 1071 CHECK_CLIPPING 1072 1073 BRegion region(r); 1074 if (fClippingRegion) { 1075 region.IntersectWith(fClippingRegion); 1076 } 1077 // implementation only for B_RGB32 at the moment 1078 int32 count = region.CountRects(); 1079 for (int32 i = 0; i < count; i++) { 1080 _InvertRect32(region.RectAt(i)); 1081 } 1082 return _Clipped(r); 1083 } 1084 1085 // #pragma mark - private 1086 1087 // _Transform 1088 inline void 1089 Painter::_Transform(BPoint* point, bool centerOffset) const 1090 { 1091 // rounding 1092 if (!fSubpixelPrecise) { 1093 // TODO: validate usage of floor() for values < 0 1094 point->x = (int32)point->x; 1095 point->y = (int32)point->y; 1096 } 1097 // this code is supposed to move coordinates to the center of pixels, 1098 // as AGG considers (0,0) to be the "upper left corner" of a pixel, 1099 // but BViews are less strict on those details 1100 if (centerOffset) { 1101 point->x += 0.5; 1102 point->y += 0.5; 1103 } 1104 } 1105 1106 // _Transform 1107 inline BPoint 1108 Painter::_Transform(const BPoint& point, bool centerOffset) const 1109 { 1110 BPoint ret = point; 1111 _Transform(&ret, centerOffset); 1112 return ret; 1113 } 1114 1115 // _Clipped 1116 BRect 1117 Painter::_Clipped(const BRect& rect) const 1118 { 1119 if (rect.IsValid()) { 1120 return BRect(rect & fClippingRegion->Frame()); 1121 } 1122 return BRect(rect); 1123 } 1124 1125 // _UpdateDrawingMode 1126 void 1127 Painter::_UpdateDrawingMode(bool drawingText) 1128 { 1129 // The AGG renderers have their own color setting, however 1130 // almost all drawing mode classes ignore the color given 1131 // by the AGG renderer and use the colors from the PatternHandler 1132 // instead. If we have a B_SOLID_* pattern, we can actually use 1133 // the color in the renderer and special versions of drawing modes 1134 // that don't use PatternHandler and are more efficient. This 1135 // has been implemented for B_OP_COPY and a couple others (the 1136 // DrawingMode*Solid ones) as of now. The PixelFormat knows the 1137 // PatternHandler and makes its decision based on the pattern. 1138 // The last parameter to SetDrawingMode() is a flag if a special 1139 // for when Painter is used to draw text. In this case, another 1140 // special version of B_OP_COPY is used that acts like R5 in that 1141 // anti-aliased pixel are not rendered against the actual background 1142 // but the current low color instead. This way, the frame buffer 1143 // doesn't need to be read. 1144 // When a solid pattern is used, _SetRendererColor() 1145 // has to be called so that all internal colors in the renderes 1146 // are up to date for use by the solid drawing mode version. 1147 fPixelFormat.SetDrawingMode(fDrawingMode, fAlphaSrcMode, 1148 fAlphaFncMode, drawingText); 1149 if (drawingText) 1150 fPatternHandler.MakeOpCopyColorCache(); 1151 } 1152 1153 // _SetRendererColor 1154 void 1155 Painter::_SetRendererColor(const rgb_color& color) const 1156 { 1157 fRenderer.color(agg::rgba(color.red / 255.0, 1158 color.green / 255.0, 1159 color.blue / 255.0, 1160 color.alpha / 255.0)); 1161 // TODO: bitmap fonts not yet correctly setup in AGGTextRenderer 1162 // fRendererBin.color(agg::rgba(color.red / 255.0, 1163 // color.green / 255.0, 1164 // color.blue / 255.0, 1165 // color.alpha / 255.0)); 1166 } 1167 1168 // #pragma mark - 1169 1170 // _DrawTriangle 1171 inline BRect 1172 Painter::_DrawTriangle(BPoint pt1, BPoint pt2, BPoint pt3, bool fill) const 1173 { 1174 CHECK_CLIPPING 1175 1176 _Transform(&pt1); 1177 _Transform(&pt2); 1178 _Transform(&pt3); 1179 1180 fPath.remove_all(); 1181 1182 fPath.move_to(pt1.x, pt1.y); 1183 fPath.line_to(pt2.x, pt2.y); 1184 fPath.line_to(pt3.x, pt3.y); 1185 1186 fPath.close_polygon(); 1187 1188 if (fill) 1189 return _FillPath(fPath); 1190 else 1191 return _StrokePath(fPath); 1192 } 1193 1194 // copy_bitmap_row_cmap8_copy 1195 static inline void 1196 copy_bitmap_row_cmap8_copy(uint8* dst, const uint8* src, int32 numPixels, 1197 const rgb_color* colorMap) 1198 { 1199 uint32* d = (uint32*)dst; 1200 const uint8* s = src; 1201 while (numPixels--) { 1202 const rgb_color c = colorMap[*s++]; 1203 *d++ = (c.alpha << 24) | (c.red << 16) | (c.green << 8) | (c.blue); 1204 } 1205 } 1206 1207 // copy_bitmap_row_cmap8_over 1208 static inline void 1209 copy_bitmap_row_cmap8_over(uint8* dst, const uint8* src, int32 numPixels, 1210 const rgb_color* colorMap) 1211 { 1212 uint32* d = (uint32*)dst; 1213 const uint8* s = src; 1214 while (numPixels--) { 1215 const rgb_color c = colorMap[*s++]; 1216 if (c.alpha) 1217 *d = (c.alpha << 24) | (c.red << 16) | (c.green << 8) | (c.blue); 1218 d++; 1219 } 1220 } 1221 1222 // copy_bitmap_row_bgr32_copy 1223 static inline void 1224 copy_bitmap_row_bgr32_copy(uint8* dst, const uint8* src, int32 numPixels, 1225 const rgb_color* colorMap) 1226 { 1227 memcpy(dst, src, numPixels * 4); 1228 } 1229 1230 // copy_bitmap_row_bgr32_alpha 1231 static inline void 1232 copy_bitmap_row_bgr32_alpha(uint8* dst, const uint8* src, int32 numPixels, 1233 const rgb_color* colorMap) 1234 { 1235 uint32* d = (uint32*)dst; 1236 int32 bytes = numPixels * 4; 1237 uint8 buffer[bytes]; 1238 uint8* b = buffer; 1239 while (numPixels--) { 1240 if (src[3] == 255) { 1241 *(uint32*)b = *(uint32*)src; 1242 } else { 1243 *(uint32*)b = *d; 1244 b[0] = ((src[0] - b[0]) * src[3] + (b[0] << 8)) >> 8; 1245 b[1] = ((src[1] - b[1]) * src[3] + (b[1] << 8)) >> 8; 1246 b[2] = ((src[2] - b[2]) * src[3] + (b[2] << 8)) >> 8; 1247 } 1248 d ++; 1249 b += 4; 1250 src += 4; 1251 } 1252 memcpy(dst, buffer, bytes); 1253 } 1254 1255 // _DrawBitmap 1256 void 1257 Painter::_DrawBitmap(agg::rendering_buffer& srcBuffer, color_space format, 1258 BRect actualBitmapRect, BRect bitmapRect, 1259 BRect viewRect) const 1260 { 1261 if (!fValidClipping 1262 || !bitmapRect.IsValid() || !bitmapRect.Intersects(actualBitmapRect) 1263 || !viewRect.IsValid()) { 1264 return; 1265 } 1266 1267 if (!fSubpixelPrecise) { 1268 align_rect_to_pixels(&viewRect); 1269 align_rect_to_pixels(&bitmapRect); 1270 } 1271 1272 double xScale = (viewRect.Width() + 1) / (bitmapRect.Width() + 1); 1273 double yScale = (viewRect.Height() + 1) / (bitmapRect.Height() + 1); 1274 1275 if (xScale == 0.0 || yScale == 0.0) 1276 return; 1277 1278 // compensate for the lefttop offset the actualBitmapRect might have 1279 // actualBitmapRect has the right size, but put it at B_ORIGIN 1280 // bitmapRect is already in good coordinates 1281 actualBitmapRect.OffsetBy(-actualBitmapRect.left, -actualBitmapRect.top); 1282 1283 // constrain rect to passed bitmap bounds 1284 // and transfer the changes to the viewRect with the right scale 1285 if (bitmapRect.left < actualBitmapRect.left) { 1286 float diff = actualBitmapRect.left - bitmapRect.left; 1287 viewRect.left += diff * xScale; 1288 bitmapRect.left = actualBitmapRect.left; 1289 } 1290 if (bitmapRect.top < actualBitmapRect.top) { 1291 float diff = actualBitmapRect.top - bitmapRect.top; 1292 viewRect.top += diff * yScale; 1293 bitmapRect.top = actualBitmapRect.top; 1294 } 1295 if (bitmapRect.right > actualBitmapRect.right) { 1296 float diff = bitmapRect.right - actualBitmapRect.right; 1297 viewRect.right -= diff * xScale; 1298 bitmapRect.right = actualBitmapRect.right; 1299 } 1300 if (bitmapRect.bottom > actualBitmapRect.bottom) { 1301 float diff = bitmapRect.bottom - actualBitmapRect.bottom; 1302 viewRect.bottom -= diff * yScale; 1303 bitmapRect.bottom = actualBitmapRect.bottom; 1304 } 1305 1306 double xOffset = viewRect.left - bitmapRect.left; 1307 double yOffset = viewRect.top - bitmapRect.top; 1308 1309 switch (format) { 1310 case B_RGB32: 1311 case B_RGBA32: { 1312 // maybe we can use an optimized version 1313 if (xScale == 1.0 && yScale == 1.0) { 1314 if (fDrawingMode == B_OP_COPY) { 1315 _DrawBitmapNoScale32(copy_bitmap_row_bgr32_copy, 4, 1316 srcBuffer, xOffset, yOffset, viewRect); 1317 return; 1318 } else if (fDrawingMode == B_OP_ALPHA 1319 && fAlphaSrcMode == B_PIXEL_ALPHA 1320 && fAlphaFncMode == B_ALPHA_OVERLAY) { 1321 _DrawBitmapNoScale32(copy_bitmap_row_bgr32_alpha, 4, 1322 srcBuffer, xOffset, yOffset, viewRect); 1323 return; 1324 } 1325 } 1326 1327 _DrawBitmapGeneric32(srcBuffer, xOffset, yOffset, 1328 xScale, yScale, viewRect); 1329 break; 1330 } 1331 default: { 1332 if (format == B_CMAP8 && xScale == 1.0 && yScale == 1.0) { 1333 if (fDrawingMode == B_OP_COPY) { 1334 _DrawBitmapNoScale32(copy_bitmap_row_cmap8_copy, 1, 1335 srcBuffer, xOffset, yOffset, viewRect); 1336 return; 1337 } else if (fDrawingMode == B_OP_OVER) { 1338 _DrawBitmapNoScale32(copy_bitmap_row_cmap8_over, 1, 1339 srcBuffer, xOffset, yOffset, viewRect); 1340 return; 1341 } 1342 } 1343 1344 // TODO: this is only a temporary implementation, 1345 // to really handle other colorspaces, one would 1346 // rather do the conversion with much less overhead, 1347 // for example in the nn filter (hm), or in the 1348 // scanline generator (better) 1349 // maybe we can use an optimized version 1350 BBitmap temp(actualBitmapRect, B_BITMAP_NO_SERVER_LINK, B_RGBA32); 1351 status_t err = temp.ImportBits(srcBuffer.buf(), 1352 srcBuffer.height() * srcBuffer.stride(), 1353 srcBuffer.stride(), 1354 0, format); 1355 if (err >= B_OK) { 1356 agg::rendering_buffer convertedBuffer; 1357 convertedBuffer.attach((uint8*)temp.Bits(), 1358 (uint32)actualBitmapRect.IntegerWidth() + 1, 1359 (uint32)actualBitmapRect.IntegerHeight() + 1, 1360 temp.BytesPerRow()); 1361 _DrawBitmapGeneric32(convertedBuffer, xOffset, yOffset, 1362 xScale, yScale, viewRect); 1363 } else { 1364 fprintf(stderr, "Painter::_DrawBitmap() - " 1365 "colorspace conversion failed: %s\n", strerror(err)); 1366 } 1367 break; 1368 } 1369 } 1370 } 1371 1372 #define DEBUG_DRAW_BITMAP 0 1373 1374 // _DrawBitmapNoScale32 1375 template <class F> 1376 void 1377 Painter::_DrawBitmapNoScale32(F copyRowFunction, uint32 bytesPerSourcePixel, 1378 agg::rendering_buffer& srcBuffer, 1379 int32 xOffset, int32 yOffset, 1380 BRect viewRect) const 1381 { 1382 // NOTE: this would crash if viewRect was large enough to read outside the 1383 // bitmap, so make sure this is not the case before calling this function! 1384 uint8* dst = fBuffer.row_ptr(0); 1385 uint32 dstBPR = fBuffer.stride(); 1386 1387 const uint8* src = srcBuffer.row_ptr(0); 1388 uint32 srcBPR = srcBuffer.stride(); 1389 1390 int32 left = (int32)viewRect.left; 1391 int32 top = (int32)viewRect.top; 1392 int32 right = (int32)viewRect.right; 1393 int32 bottom = (int32)viewRect.bottom; 1394 1395 #if DEBUG_DRAW_BITMAP 1396 if (left - xOffset < 0 || left - xOffset >= (int32)srcBuffer.width() || 1397 right - xOffset >= (int32)srcBuffer.width() || 1398 top - yOffset < 0 || top - yOffset >= (int32)srcBuffer.height() || 1399 bottom - yOffset >= (int32)srcBuffer.height()) { 1400 1401 char message[256]; 1402 sprintf(message, "reading outside of bitmap (%ld, %ld, %ld, %ld) " 1403 "(%d, %d) (%ld, %ld)", 1404 left - xOffset, top - yOffset, right - xOffset, bottom - yOffset, 1405 srcBuffer.width(), srcBuffer.height(), xOffset, yOffset); 1406 debugger(message); 1407 } 1408 #endif 1409 1410 const rgb_color* colorMap = SystemPalette(); 1411 1412 // copy rects, iterate over clipping boxes 1413 fBaseRenderer.first_clip_box(); 1414 do { 1415 int32 x1 = max_c(fBaseRenderer.xmin(), left); 1416 int32 x2 = min_c(fBaseRenderer.xmax(), right); 1417 if (x1 <= x2) { 1418 int32 y1 = max_c(fBaseRenderer.ymin(), top); 1419 int32 y2 = min_c(fBaseRenderer.ymax(), bottom); 1420 if (y1 <= y2) { 1421 uint8* dstHandle = dst + y1 * dstBPR + x1 * 4; 1422 const uint8* srcHandle = src + (y1 - yOffset) * srcBPR 1423 + (x1 - xOffset) * bytesPerSourcePixel; 1424 1425 for (; y1 <= y2; y1++) { 1426 copyRowFunction(dstHandle, srcHandle, x2 - x1 + 1, 1427 colorMap); 1428 1429 dstHandle += dstBPR; 1430 srcHandle += srcBPR; 1431 } 1432 } 1433 } 1434 } while (fBaseRenderer.next_clip_box()); 1435 } 1436 1437 // _DrawBitmapGeneric32 1438 void 1439 Painter::_DrawBitmapGeneric32(agg::rendering_buffer& srcBuffer, 1440 double xOffset, double yOffset, 1441 double xScale, double yScale, 1442 BRect viewRect) const 1443 { 1444 // AGG pipeline 1445 1446 // pixel format attached to bitmap 1447 typedef agg::pixfmt_bgra32 pixfmt_image; 1448 pixfmt_image pixf_img(srcBuffer); 1449 1450 agg::trans_affine srcMatrix; 1451 // NOTE: R5 seems to ignore this offset when drawing bitmaps 1452 // srcMatrix *= agg::trans_affine_translation(-actualBitmapRect.left, 1453 // -actualBitmapRect.top); 1454 1455 agg::trans_affine imgMatrix; 1456 imgMatrix *= agg::trans_affine_scaling(xScale, yScale); 1457 imgMatrix *= agg::trans_affine_translation(xOffset, yOffset); 1458 imgMatrix.invert(); 1459 1460 // image interpolator 1461 typedef agg::span_interpolator_linear<> interpolator_type; 1462 interpolator_type interpolator(imgMatrix); 1463 1464 // scanline allocator 1465 agg::span_allocator<pixfmt_image::color_type> spanAllocator; 1466 1467 // image accessor attached to pixel format of bitmap 1468 typedef agg::image_accessor_clip<pixfmt_image> source_type; 1469 source_type source(pixf_img, agg::rgba8(0, 0, 0, 0)); 1470 1471 // image filter (nearest neighbor) 1472 typedef agg::span_image_filter_rgba_nn<source_type, 1473 interpolator_type> span_gen_type; 1474 span_gen_type spanGenerator(source, interpolator); 1475 1476 1477 // clip to the current clipping region's frame 1478 viewRect = viewRect & fClippingRegion->Frame(); 1479 // convert to pixel coords (versus pixel indices) 1480 viewRect.right++; 1481 viewRect.bottom++; 1482 1483 // path enclosing the bitmap 1484 fPath.remove_all(); 1485 fPath.move_to(viewRect.left, viewRect.top); 1486 fPath.line_to(viewRect.right, viewRect.top); 1487 fPath.line_to(viewRect.right, viewRect.bottom); 1488 fPath.line_to(viewRect.left, viewRect.bottom); 1489 fPath.close_polygon(); 1490 1491 agg::conv_transform<agg::path_storage> transformedPath(fPath, srcMatrix); 1492 fRasterizer.reset(); 1493 fRasterizer.add_path(transformedPath); 1494 1495 // render the path with the bitmap as scanline fill 1496 agg::render_scanlines_aa(fRasterizer, 1497 fUnpackedScanline, 1498 fBaseRenderer, 1499 spanAllocator, 1500 spanGenerator); 1501 } 1502 1503 // _InvertRect32 1504 void 1505 Painter::_InvertRect32(BRect r) const 1506 { 1507 int32 width = r.IntegerWidth() + 1; 1508 for (int32 y = (int32)r.top; y <= (int32)r.bottom; y++) { 1509 uint8* dst = fBuffer.row_ptr(y); 1510 dst += (int32)r.left * 4; 1511 for (int32 i = 0; i < width; i++) { 1512 dst[0] = 255 - dst[0]; 1513 dst[1] = 255 - dst[1]; 1514 dst[2] = 255 - dst[2]; 1515 dst += 4; 1516 } 1517 } 1518 } 1519 1520 // _BlendRect32 1521 void 1522 Painter::_BlendRect32(const BRect& r, const rgb_color& c) const 1523 { 1524 if (!fValidClipping) 1525 return; 1526 1527 uint8* dst = fBuffer.row_ptr(0); 1528 uint32 bpr = fBuffer.stride(); 1529 1530 int32 left = (int32)r.left; 1531 int32 top = (int32)r.top; 1532 int32 right = (int32)r.right; 1533 int32 bottom = (int32)r.bottom; 1534 1535 // fill rects, iterate over clipping boxes 1536 fBaseRenderer.first_clip_box(); 1537 do { 1538 int32 x1 = max_c(fBaseRenderer.xmin(), left); 1539 int32 x2 = min_c(fBaseRenderer.xmax(), right); 1540 if (x1 <= x2) { 1541 int32 y1 = max_c(fBaseRenderer.ymin(), top); 1542 int32 y2 = min_c(fBaseRenderer.ymax(), bottom); 1543 1544 uint8* offset = dst + x1 * 4 + y1 * bpr; 1545 for (; y1 <= y2; y1++) { 1546 blend_line32(offset, x2 - x1 + 1, c.red, c.green, c.blue, c.alpha); 1547 offset += bpr; 1548 } 1549 } 1550 } while (fBaseRenderer.next_clip_box()); 1551 } 1552 1553 // #pragma mark - 1554 1555 template<class VertexSource> 1556 BRect 1557 Painter::_BoundingBox(VertexSource& path) const 1558 { 1559 double left = 0.0; 1560 double top = 0.0; 1561 double right = -1.0; 1562 double bottom = -1.0; 1563 uint32 pathID[1]; 1564 pathID[0] = 0; 1565 agg::bounding_rect(path, pathID, 0, 1, &left, &top, &right, &bottom); 1566 return BRect(left, top, right, bottom); 1567 } 1568 1569 // agg_line_cap_mode_for 1570 inline agg::line_cap_e 1571 agg_line_cap_mode_for(cap_mode mode) 1572 { 1573 switch (mode) { 1574 case B_BUTT_CAP: 1575 return agg::butt_cap; 1576 case B_SQUARE_CAP: 1577 return agg::square_cap; 1578 case B_ROUND_CAP: 1579 return agg::round_cap; 1580 } 1581 return agg::butt_cap; 1582 } 1583 1584 // agg_line_join_mode_for 1585 inline agg::line_join_e 1586 agg_line_join_mode_for(join_mode mode) 1587 { 1588 switch (mode) { 1589 case B_MITER_JOIN: 1590 return agg::miter_join; 1591 case B_ROUND_JOIN: 1592 return agg::round_join; 1593 case B_BEVEL_JOIN: 1594 case B_BUTT_JOIN: // ?? 1595 case B_SQUARE_JOIN: // ?? 1596 return agg::bevel_join; 1597 } 1598 return agg::miter_join; 1599 } 1600 1601 // _StrokePath 1602 template<class VertexSource> 1603 BRect 1604 Painter::_StrokePath(VertexSource& path) const 1605 { 1606 agg::conv_stroke<VertexSource> stroke(path); 1607 stroke.width(fPenSize); 1608 1609 stroke.line_cap(agg_line_cap_mode_for(fLineCapMode)); 1610 stroke.line_join(agg_line_join_mode_for(fLineJoinMode)); 1611 stroke.miter_limit(fMiterLimit); 1612 1613 fRasterizer.reset(); 1614 fRasterizer.add_path(stroke); 1615 1616 agg::render_scanlines(fRasterizer, fPackedScanline, fRenderer); 1617 1618 BRect touched = _BoundingBox(path); 1619 float penSize = ceilf(fPenSize / 2.0); 1620 touched.InsetBy(-penSize, -penSize); 1621 1622 return _Clipped(touched); 1623 } 1624 1625 // _FillPath 1626 template<class VertexSource> 1627 BRect 1628 Painter::_FillPath(VertexSource& path) const 1629 { 1630 fRasterizer.reset(); 1631 fRasterizer.add_path(path); 1632 agg::render_scanlines(fRasterizer, fPackedScanline, fRenderer); 1633 1634 return _Clipped(_BoundingBox(path)); 1635 } 1636 1637