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 // support invalid rects 565 BPoint a(min_c(r.left, r.right), min_c(r.top, r.bottom)); 566 BPoint b(max_c(r.left, r.right), max_c(r.top, r.bottom)); 567 _Transform(&a, false); 568 _Transform(&b, false); 569 570 // first, try an optimized version 571 if (fPenSize == 1.0 && 572 (fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER)) { 573 pattern p = *fPatternHandler.GetR5Pattern(); 574 if (p == B_SOLID_HIGH) { 575 BRect rect(a, b); 576 StrokeRect(rect, 577 fPatternHandler.HighColor()); 578 return _Clipped(rect); 579 } else if (p == B_SOLID_LOW) { 580 BRect rect(a, b); 581 StrokeRect(rect, 582 fPatternHandler.LowColor()); 583 return _Clipped(rect); 584 } 585 } 586 587 if (fmodf(fPenSize, 2.0) != 0.0) { 588 // shift coords to center of pixels 589 a.x += 0.5; 590 a.y += 0.5; 591 b.x += 0.5; 592 b.y += 0.5; 593 } 594 595 fPath.remove_all(); 596 fPath.move_to(a.x, a.y); 597 if (a.x == b.x || a.y == b.y) { 598 // special case rects with one pixel height or width 599 fPath.line_to(b.x, b.y); 600 } else { 601 fPath.line_to(b.x, a.y); 602 fPath.line_to(b.x, b.y); 603 fPath.line_to(a.x, b.y); 604 } 605 fPath.close_polygon(); 606 607 return _StrokePath(fPath); 608 } 609 610 // StrokeRect 611 void 612 Painter::StrokeRect(const BRect& r, const rgb_color& c) const 613 { 614 StraightLine(BPoint(r.left, r.top), 615 BPoint(r.right - 1, r.top), c); 616 StraightLine(BPoint(r.right, r.top), 617 BPoint(r.right, r.bottom - 1), c); 618 StraightLine(BPoint(r.right, r.bottom), 619 BPoint(r.left + 1, r.bottom), c); 620 StraightLine(BPoint(r.left, r.bottom), 621 BPoint(r.left, r.top + 1), c); 622 } 623 624 // FillRect 625 BRect 626 Painter::FillRect(const BRect& r) const 627 { 628 CHECK_CLIPPING 629 630 // support invalid rects 631 BPoint a(min_c(r.left, r.right), min_c(r.top, r.bottom)); 632 BPoint b(max_c(r.left, r.right), max_c(r.top, r.bottom)); 633 _Transform(&a, false); 634 _Transform(&b, false); 635 636 // first, try an optimized version 637 if (fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER) { 638 pattern p = *fPatternHandler.GetR5Pattern(); 639 if (p == B_SOLID_HIGH) { 640 BRect rect(a, b); 641 FillRect(rect, fPatternHandler.HighColor()); 642 return _Clipped(rect); 643 } else if (p == B_SOLID_LOW) { 644 BRect rect(a, b); 645 FillRect(rect, fPatternHandler.LowColor()); 646 return _Clipped(rect); 647 } 648 } 649 if (fDrawingMode == B_OP_ALPHA && fAlphaFncMode == B_ALPHA_OVERLAY) { 650 pattern p = *fPatternHandler.GetR5Pattern(); 651 if (p == B_SOLID_HIGH) { 652 BRect rect(a, b); 653 _BlendRect32(rect, fPatternHandler.HighColor()); 654 return _Clipped(rect); 655 } else if (p == B_SOLID_LOW) { 656 rgb_color c = fPatternHandler.LowColor(); 657 if (fAlphaSrcMode == B_CONSTANT_ALPHA) 658 c.alpha = fPatternHandler.HighColor().alpha; 659 BRect rect(a, b); 660 _BlendRect32(rect, c); 661 return _Clipped(rect); 662 } 663 } 664 665 666 // account for stricter interpretation of coordinates in AGG 667 // the rectangle ranges from the top-left (.0, .0) 668 // to the bottom-right (.9999, .9999) corner of pixels 669 b.x += 1.0; 670 b.y += 1.0; 671 672 fPath.remove_all(); 673 fPath.move_to(a.x, a.y); 674 fPath.line_to(b.x, a.y); 675 fPath.line_to(b.x, b.y); 676 fPath.line_to(a.x, b.y); 677 fPath.close_polygon(); 678 679 return _FillPath(fPath); 680 } 681 682 // FillRect 683 void 684 Painter::FillRect(const BRect& r, const rgb_color& c) const 685 { 686 if (!fValidClipping) 687 return; 688 689 uint8* dst = fBuffer.row_ptr(0); 690 uint32 bpr = fBuffer.stride(); 691 int32 left = (int32)r.left; 692 int32 top = (int32)r.top; 693 int32 right = (int32)r.right; 694 int32 bottom = (int32)r.bottom; 695 // get a 32 bit pixel ready with the color 696 pixel32 color; 697 color.data8[0] = c.blue; 698 color.data8[1] = c.green; 699 color.data8[2] = c.red; 700 color.data8[3] = c.alpha; 701 // fill rects, iterate over clipping boxes 702 fBaseRenderer.first_clip_box(); 703 do { 704 int32 x1 = max_c(fBaseRenderer.xmin(), left); 705 int32 x2 = min_c(fBaseRenderer.xmax(), right); 706 if (x1 <= x2) { 707 int32 y1 = max_c(fBaseRenderer.ymin(), top); 708 int32 y2 = min_c(fBaseRenderer.ymax(), bottom); 709 uint8* offset = dst + x1 * 4; 710 for (; y1 <= y2; y1++) { 711 // uint32* handle = (uint32*)(offset + y1 * bpr); 712 // for (int32 x = x1; x <= x2; x++) { 713 // *handle++ = color.data32; 714 // } 715 gfxset32(offset + y1 * bpr, color.data32, (x2 - x1 + 1) * 4); 716 } 717 } 718 } while (fBaseRenderer.next_clip_box()); 719 } 720 721 // FillRectNoClipping 722 void 723 Painter::FillRectNoClipping(const BRect& r, const rgb_color& c) const 724 { 725 int32 left = (int32)r.left; 726 int32 y = (int32)r.top; 727 int32 right = (int32)r.right; 728 int32 bottom = (int32)r.bottom; 729 730 uint8* dst = fBuffer.row_ptr(y); 731 uint32 bpr = fBuffer.stride(); 732 733 // get a 32 bit pixel ready with the color 734 pixel32 color; 735 color.data8[0] = c.blue; 736 color.data8[1] = c.green; 737 color.data8[2] = c.red; 738 color.data8[3] = c.alpha; 739 740 dst += left * 4; 741 742 for (; y <= bottom; y++) { 743 // uint32* handle = (uint32*)dst; 744 // for (int32 x = left; x <= right; x++) { 745 // *handle++ = color.data32; 746 // } 747 gfxset32(dst, color.data32, (right - left + 1) * 4); 748 dst += bpr; 749 } 750 } 751 752 // StrokeRoundRect 753 BRect 754 Painter::StrokeRoundRect(const BRect& r, float xRadius, float yRadius) const 755 { 756 CHECK_CLIPPING 757 758 BPoint lt(r.left, r.top); 759 BPoint rb(r.right, r.bottom); 760 bool centerOffset = fPenSize == 1.0; 761 // TODO: use this when using _StrokePath() 762 // bool centerOffset = fmodf(fPenSize, 2.0) != 0.0; 763 _Transform(<, centerOffset); 764 _Transform(&rb, centerOffset); 765 766 if (fPenSize == 1.0) { 767 agg::rounded_rect rect; 768 rect.rect(lt.x, lt.y, rb.x, rb.y); 769 rect.radius(xRadius, yRadius); 770 771 return _StrokePath(rect); 772 } else { 773 // NOTE: This implementation might seem a little strange, but it makes 774 // stroked round rects look like on R5. A more correct way would be to use 775 // _StrokePath() as above (independent from fPenSize). 776 // The fact that the bounding box of the round rect is not enlarged 777 // by fPenSize/2 is actually on purpose, though one could argue it is unexpected. 778 779 // enclose the right and bottom edge 780 rb.x++; 781 rb.y++; 782 783 agg::rounded_rect outer; 784 outer.rect(lt.x, lt.y, rb.x, rb.y); 785 outer.radius(xRadius, yRadius); 786 787 fRasterizer.reset(); 788 fRasterizer.add_path(outer); 789 790 // don't add an inner hole if the "size is negative", this avoids some 791 // defects that can be observed on R5 and could be regarded as a bug. 792 if (2 * fPenSize < rb.x - lt.x && 2 * fPenSize < rb.y - lt.y) { 793 agg::rounded_rect inner; 794 inner.rect(lt.x + fPenSize, lt.y + fPenSize, rb.x - fPenSize, rb.y - fPenSize); 795 inner.radius(max_c(0.0, xRadius - fPenSize), max_c(0.0, yRadius - fPenSize)); 796 797 fRasterizer.add_path(inner); 798 } 799 800 // make the inner rect work as a hole 801 fRasterizer.filling_rule(agg::fill_even_odd); 802 803 if (fPenSize > 2) 804 agg::render_scanlines(fRasterizer, fPackedScanline, fRenderer); 805 else 806 agg::render_scanlines(fRasterizer, fUnpackedScanline, fRenderer); 807 808 // reset to default 809 fRasterizer.filling_rule(agg::fill_non_zero); 810 811 return _Clipped(_BoundingBox(outer)); 812 } 813 } 814 815 // FillRoundRect 816 BRect 817 Painter::FillRoundRect(const BRect& r, float xRadius, float yRadius) const 818 { 819 CHECK_CLIPPING 820 821 BPoint lt(r.left, r.top); 822 BPoint rb(r.right, r.bottom); 823 _Transform(<, false); 824 _Transform(&rb, false); 825 826 // account for stricter interpretation of coordinates in AGG 827 // the rectangle ranges from the top-left (.0, .0) 828 // to the bottom-right (.9999, .9999) corner of pixels 829 rb.x += 1.0; 830 rb.y += 1.0; 831 832 agg::rounded_rect rect; 833 rect.rect(lt.x, lt.y, rb.x, rb.y); 834 rect.radius(xRadius, yRadius); 835 836 return _FillPath(rect); 837 } 838 839 // AlignEllipseRect 840 void 841 Painter::AlignEllipseRect(BRect* rect, bool filled) const 842 { 843 if (!fSubpixelPrecise) { 844 // align rect to pixels 845 align_rect_to_pixels(rect); 846 // account for "pixel index" versus "pixel area" 847 rect->right++; 848 rect->bottom++; 849 if (!filled && fmodf(fPenSize, 2.0) != 0.0) { 850 // align the stroke 851 rect->InsetBy(0.5, 0.5); 852 } 853 } 854 } 855 856 // DrawEllipse 857 BRect 858 Painter::DrawEllipse(BRect r, bool fill) const 859 { 860 CHECK_CLIPPING 861 862 AlignEllipseRect(&r, fill); 863 864 float xRadius = r.Width() / 2.0; 865 float yRadius = r.Height() / 2.0; 866 BPoint center(r.left + xRadius, r.top + yRadius); 867 868 int32 divisions = (int32)((xRadius + yRadius + 2 * fPenSize) * PI / 2); 869 if (divisions < 12) 870 divisions = 12; 871 if (divisions > 4096) 872 divisions = 4096; 873 874 if (fill) { 875 agg::ellipse path(center.x, center.y, xRadius, yRadius, divisions); 876 877 return _FillPath(path); 878 } else { 879 // NOTE: This implementation might seem a little strange, but it makes 880 // stroked ellipses look like on R5. A more correct way would be to use 881 // _StrokePath(), but it currently has its own set of problems with narrow 882 // ellipses (for small xRadii or yRadii). 883 float inset = fPenSize / 2.0; 884 agg::ellipse inner(center.x, center.y, 885 max_c(0.0, xRadius - inset), 886 max_c(0.0, yRadius - inset), 887 divisions); 888 agg::ellipse outer(center.x, center.y, 889 xRadius + inset, 890 yRadius + inset, 891 divisions); 892 893 fRasterizer.reset(); 894 fRasterizer.add_path(outer); 895 fRasterizer.add_path(inner); 896 897 // make the inner ellipse work as a hole 898 fRasterizer.filling_rule(agg::fill_even_odd); 899 900 if (fPenSize > 4) 901 agg::render_scanlines(fRasterizer, fPackedScanline, fRenderer); 902 else 903 agg::render_scanlines(fRasterizer, fUnpackedScanline, fRenderer); 904 905 // reset to default 906 fRasterizer.filling_rule(agg::fill_non_zero); 907 908 return _Clipped(_BoundingBox(outer)); 909 } 910 } 911 912 // StrokeArc 913 BRect 914 Painter::StrokeArc(BPoint center, float xRadius, float yRadius, 915 float angle, float span) const 916 { 917 CHECK_CLIPPING 918 919 _Transform(¢er); 920 921 double angleRad = (angle * PI) / 180.0; 922 double spanRad = (span * PI) / 180.0; 923 agg::bezier_arc arc(center.x, center.y, xRadius, yRadius, 924 -angleRad, -spanRad); 925 926 agg::conv_curve<agg::bezier_arc> path(arc); 927 path.approximation_scale(2.0); 928 929 return _StrokePath(path); 930 } 931 932 // FillArc 933 BRect 934 Painter::FillArc(BPoint center, float xRadius, float yRadius, 935 float angle, float span) const 936 { 937 CHECK_CLIPPING 938 939 _Transform(¢er); 940 941 double angleRad = (angle * PI) / 180.0; 942 double spanRad = (span * PI) / 180.0; 943 agg::bezier_arc arc(center.x, center.y, xRadius, yRadius, 944 -angleRad, -spanRad); 945 946 agg::conv_curve<agg::bezier_arc> segmentedArc(arc); 947 948 fPath.remove_all(); 949 950 // build a new path by starting at the center point, 951 // then traversing the arc, then going back to the center 952 fPath.move_to(center.x, center.y); 953 954 segmentedArc.rewind(0); 955 double x; 956 double y; 957 unsigned cmd = segmentedArc.vertex(&x, &y); 958 while (!agg::is_stop(cmd)) { 959 fPath.line_to(x, y); 960 cmd = segmentedArc.vertex(&x, &y); 961 } 962 963 fPath.close_polygon(); 964 965 return _FillPath(fPath); 966 } 967 968 // #pragma mark - 969 970 // DrawString 971 BRect 972 Painter::DrawString(const char* utf8String, uint32 length, 973 BPoint baseLine, const escapement_delta* delta, 974 FontCacheReference* cacheReference) 975 { 976 CHECK_CLIPPING 977 978 if (!fSubpixelPrecise) { 979 baseLine.x = roundf(baseLine.x); 980 baseLine.y = roundf(baseLine.y); 981 } 982 983 BRect bounds(0.0, 0.0, -1.0, -1.0); 984 985 // text is not rendered with patterns, but we need to 986 // make sure that the previous pattern is restored 987 pattern oldPattern = *fPatternHandler.GetR5Pattern(); 988 SetPattern(B_SOLID_HIGH, true); 989 990 bounds = fTextRenderer.RenderString(utf8String, length, 991 baseLine, fClippingRegion->Frame(), false, NULL, delta, 992 cacheReference); 993 994 SetPattern(oldPattern); 995 996 return _Clipped(bounds); 997 } 998 999 // BoundingBox 1000 BRect 1001 Painter::BoundingBox(const char* utf8String, uint32 length, 1002 BPoint baseLine, BPoint* penLocation, 1003 const escapement_delta* delta, 1004 FontCacheReference* cacheReference) const 1005 { 1006 if (!fSubpixelPrecise) { 1007 baseLine.x = roundf(baseLine.x); 1008 baseLine.y = roundf(baseLine.y); 1009 } 1010 1011 static BRect dummy; 1012 return fTextRenderer.RenderString(utf8String, length, 1013 baseLine, dummy, true, penLocation, delta, cacheReference); 1014 } 1015 1016 // StringWidth 1017 float 1018 Painter::StringWidth(const char* utf8String, uint32 length, 1019 const escapement_delta* delta) 1020 { 1021 return Font().StringWidth(utf8String, length, delta); 1022 } 1023 1024 // #pragma mark - 1025 1026 // DrawBitmap 1027 BRect 1028 Painter::DrawBitmap(const ServerBitmap* bitmap, 1029 BRect bitmapRect, BRect viewRect) const 1030 { 1031 CHECK_CLIPPING 1032 1033 BRect touched = _Clipped(viewRect); 1034 1035 if (bitmap && bitmap->IsValid() && touched.IsValid()) { 1036 // the native bitmap coordinate system 1037 BRect actualBitmapRect(bitmap->Bounds()); 1038 1039 agg::rendering_buffer srcBuffer; 1040 srcBuffer.attach(bitmap->Bits(), 1041 bitmap->Width(), 1042 bitmap->Height(), 1043 bitmap->BytesPerRow()); 1044 1045 _DrawBitmap(srcBuffer, bitmap->ColorSpace(), actualBitmapRect, 1046 bitmapRect, viewRect); 1047 } 1048 return touched; 1049 } 1050 1051 // #pragma mark - 1052 1053 // FillRegion 1054 BRect 1055 Painter::FillRegion(const BRegion* region) const 1056 { 1057 CHECK_CLIPPING 1058 1059 BRegion copy(*region); 1060 int32 count = copy.CountRects(); 1061 BRect touched = FillRect(copy.RectAt(0)); 1062 for (int32 i = 1; i < count; i++) { 1063 touched = touched | FillRect(copy.RectAt(i)); 1064 } 1065 return touched; 1066 } 1067 1068 // InvertRect 1069 BRect 1070 Painter::InvertRect(const BRect& r) const 1071 { 1072 CHECK_CLIPPING 1073 1074 BRegion region(r); 1075 if (fClippingRegion) { 1076 region.IntersectWith(fClippingRegion); 1077 } 1078 // implementation only for B_RGB32 at the moment 1079 int32 count = region.CountRects(); 1080 for (int32 i = 0; i < count; i++) { 1081 _InvertRect32(region.RectAt(i)); 1082 } 1083 return _Clipped(r); 1084 } 1085 1086 // #pragma mark - private 1087 1088 // _Transform 1089 inline void 1090 Painter::_Transform(BPoint* point, bool centerOffset) const 1091 { 1092 // rounding 1093 if (!fSubpixelPrecise) { 1094 // TODO: validate usage of floor() for values < 0 1095 point->x = (int32)point->x; 1096 point->y = (int32)point->y; 1097 } 1098 // this code is supposed to move coordinates to the center of pixels, 1099 // as AGG considers (0,0) to be the "upper left corner" of a pixel, 1100 // but BViews are less strict on those details 1101 if (centerOffset) { 1102 point->x += 0.5; 1103 point->y += 0.5; 1104 } 1105 } 1106 1107 // _Transform 1108 inline BPoint 1109 Painter::_Transform(const BPoint& point, bool centerOffset) const 1110 { 1111 BPoint ret = point; 1112 _Transform(&ret, centerOffset); 1113 return ret; 1114 } 1115 1116 // _Clipped 1117 BRect 1118 Painter::_Clipped(const BRect& rect) const 1119 { 1120 if (rect.IsValid()) { 1121 return BRect(rect & fClippingRegion->Frame()); 1122 } 1123 return BRect(rect); 1124 } 1125 1126 // _UpdateDrawingMode 1127 void 1128 Painter::_UpdateDrawingMode(bool drawingText) 1129 { 1130 // The AGG renderers have their own color setting, however 1131 // almost all drawing mode classes ignore the color given 1132 // by the AGG renderer and use the colors from the PatternHandler 1133 // instead. If we have a B_SOLID_* pattern, we can actually use 1134 // the color in the renderer and special versions of drawing modes 1135 // that don't use PatternHandler and are more efficient. This 1136 // has been implemented for B_OP_COPY and a couple others (the 1137 // DrawingMode*Solid ones) as of now. The PixelFormat knows the 1138 // PatternHandler and makes its decision based on the pattern. 1139 // The last parameter to SetDrawingMode() is a flag if a special 1140 // for when Painter is used to draw text. In this case, another 1141 // special version of B_OP_COPY is used that acts like R5 in that 1142 // anti-aliased pixel are not rendered against the actual background 1143 // but the current low color instead. This way, the frame buffer 1144 // doesn't need to be read. 1145 // When a solid pattern is used, _SetRendererColor() 1146 // has to be called so that all internal colors in the renderes 1147 // are up to date for use by the solid drawing mode version. 1148 fPixelFormat.SetDrawingMode(fDrawingMode, fAlphaSrcMode, 1149 fAlphaFncMode, drawingText); 1150 if (drawingText) 1151 fPatternHandler.MakeOpCopyColorCache(); 1152 } 1153 1154 // _SetRendererColor 1155 void 1156 Painter::_SetRendererColor(const rgb_color& color) const 1157 { 1158 fRenderer.color(agg::rgba(color.red / 255.0, 1159 color.green / 255.0, 1160 color.blue / 255.0, 1161 color.alpha / 255.0)); 1162 // TODO: bitmap fonts not yet correctly setup in AGGTextRenderer 1163 // fRendererBin.color(agg::rgba(color.red / 255.0, 1164 // color.green / 255.0, 1165 // color.blue / 255.0, 1166 // color.alpha / 255.0)); 1167 } 1168 1169 // #pragma mark - 1170 1171 // _DrawTriangle 1172 inline BRect 1173 Painter::_DrawTriangle(BPoint pt1, BPoint pt2, BPoint pt3, bool fill) const 1174 { 1175 CHECK_CLIPPING 1176 1177 _Transform(&pt1); 1178 _Transform(&pt2); 1179 _Transform(&pt3); 1180 1181 fPath.remove_all(); 1182 1183 fPath.move_to(pt1.x, pt1.y); 1184 fPath.line_to(pt2.x, pt2.y); 1185 fPath.line_to(pt3.x, pt3.y); 1186 1187 fPath.close_polygon(); 1188 1189 if (fill) 1190 return _FillPath(fPath); 1191 else 1192 return _StrokePath(fPath); 1193 } 1194 1195 // copy_bitmap_row_cmap8_copy 1196 static inline void 1197 copy_bitmap_row_cmap8_copy(uint8* dst, const uint8* src, int32 numPixels, 1198 const rgb_color* colorMap) 1199 { 1200 uint32* d = (uint32*)dst; 1201 const uint8* s = src; 1202 while (numPixels--) { 1203 const rgb_color c = colorMap[*s++]; 1204 *d++ = (c.alpha << 24) | (c.red << 16) | (c.green << 8) | (c.blue); 1205 } 1206 } 1207 1208 // copy_bitmap_row_cmap8_over 1209 static inline void 1210 copy_bitmap_row_cmap8_over(uint8* dst, const uint8* src, int32 numPixels, 1211 const rgb_color* colorMap) 1212 { 1213 uint32* d = (uint32*)dst; 1214 const uint8* s = src; 1215 while (numPixels--) { 1216 const rgb_color c = colorMap[*s++]; 1217 if (c.alpha) 1218 *d = (c.alpha << 24) | (c.red << 16) | (c.green << 8) | (c.blue); 1219 d++; 1220 } 1221 } 1222 1223 // copy_bitmap_row_bgr32_copy 1224 static inline void 1225 copy_bitmap_row_bgr32_copy(uint8* dst, const uint8* src, int32 numPixels, 1226 const rgb_color* colorMap) 1227 { 1228 memcpy(dst, src, numPixels * 4); 1229 } 1230 1231 // copy_bitmap_row_bgr32_alpha 1232 static inline void 1233 copy_bitmap_row_bgr32_alpha(uint8* dst, const uint8* src, int32 numPixels, 1234 const rgb_color* colorMap) 1235 { 1236 uint32* d = (uint32*)dst; 1237 int32 bytes = numPixels * 4; 1238 uint8 buffer[bytes]; 1239 uint8* b = buffer; 1240 while (numPixels--) { 1241 if (src[3] == 255) { 1242 *(uint32*)b = *(uint32*)src; 1243 } else { 1244 *(uint32*)b = *d; 1245 b[0] = ((src[0] - b[0]) * src[3] + (b[0] << 8)) >> 8; 1246 b[1] = ((src[1] - b[1]) * src[3] + (b[1] << 8)) >> 8; 1247 b[2] = ((src[2] - b[2]) * src[3] + (b[2] << 8)) >> 8; 1248 } 1249 d ++; 1250 b += 4; 1251 src += 4; 1252 } 1253 memcpy(dst, buffer, bytes); 1254 } 1255 1256 // _DrawBitmap 1257 void 1258 Painter::_DrawBitmap(agg::rendering_buffer& srcBuffer, color_space format, 1259 BRect actualBitmapRect, BRect bitmapRect, 1260 BRect viewRect) const 1261 { 1262 if (!fValidClipping 1263 || !bitmapRect.IsValid() || !bitmapRect.Intersects(actualBitmapRect) 1264 || !viewRect.IsValid()) { 1265 return; 1266 } 1267 1268 if (!fSubpixelPrecise) { 1269 align_rect_to_pixels(&viewRect); 1270 align_rect_to_pixels(&bitmapRect); 1271 } 1272 1273 double xScale = (viewRect.Width() + 1) / (bitmapRect.Width() + 1); 1274 double yScale = (viewRect.Height() + 1) / (bitmapRect.Height() + 1); 1275 1276 if (xScale == 0.0 || yScale == 0.0) 1277 return; 1278 1279 // compensate for the lefttop offset the actualBitmapRect might have 1280 // actualBitmapRect has the right size, but put it at B_ORIGIN 1281 // bitmapRect is already in good coordinates 1282 actualBitmapRect.OffsetBy(-actualBitmapRect.left, -actualBitmapRect.top); 1283 1284 // constrain rect to passed bitmap bounds 1285 // and transfer the changes to the viewRect with the right scale 1286 if (bitmapRect.left < actualBitmapRect.left) { 1287 float diff = actualBitmapRect.left - bitmapRect.left; 1288 viewRect.left += diff * xScale; 1289 bitmapRect.left = actualBitmapRect.left; 1290 } 1291 if (bitmapRect.top < actualBitmapRect.top) { 1292 float diff = actualBitmapRect.top - bitmapRect.top; 1293 viewRect.top += diff * yScale; 1294 bitmapRect.top = actualBitmapRect.top; 1295 } 1296 if (bitmapRect.right > actualBitmapRect.right) { 1297 float diff = bitmapRect.right - actualBitmapRect.right; 1298 viewRect.right -= diff * xScale; 1299 bitmapRect.right = actualBitmapRect.right; 1300 } 1301 if (bitmapRect.bottom > actualBitmapRect.bottom) { 1302 float diff = bitmapRect.bottom - actualBitmapRect.bottom; 1303 viewRect.bottom -= diff * yScale; 1304 bitmapRect.bottom = actualBitmapRect.bottom; 1305 } 1306 1307 double xOffset = viewRect.left - bitmapRect.left; 1308 double yOffset = viewRect.top - bitmapRect.top; 1309 1310 switch (format) { 1311 case B_RGB32: 1312 case B_RGBA32: { 1313 // maybe we can use an optimized version 1314 if (xScale == 1.0 && yScale == 1.0) { 1315 if (fDrawingMode == B_OP_COPY) { 1316 _DrawBitmapNoScale32(copy_bitmap_row_bgr32_copy, 4, 1317 srcBuffer, xOffset, yOffset, viewRect); 1318 return; 1319 } else if (fDrawingMode == B_OP_ALPHA 1320 && fAlphaSrcMode == B_PIXEL_ALPHA 1321 && fAlphaFncMode == B_ALPHA_OVERLAY) { 1322 _DrawBitmapNoScale32(copy_bitmap_row_bgr32_alpha, 4, 1323 srcBuffer, xOffset, yOffset, viewRect); 1324 return; 1325 } 1326 } 1327 1328 _DrawBitmapGeneric32(srcBuffer, xOffset, yOffset, 1329 xScale, yScale, viewRect); 1330 break; 1331 } 1332 default: { 1333 if (format == B_CMAP8 && xScale == 1.0 && yScale == 1.0) { 1334 if (fDrawingMode == B_OP_COPY) { 1335 _DrawBitmapNoScale32(copy_bitmap_row_cmap8_copy, 1, 1336 srcBuffer, xOffset, yOffset, viewRect); 1337 return; 1338 } else if (fDrawingMode == B_OP_OVER) { 1339 _DrawBitmapNoScale32(copy_bitmap_row_cmap8_over, 1, 1340 srcBuffer, xOffset, yOffset, viewRect); 1341 return; 1342 } 1343 } 1344 1345 // TODO: this is only a temporary implementation, 1346 // to really handle other colorspaces, one would 1347 // rather do the conversion with much less overhead, 1348 // for example in the nn filter (hm), or in the 1349 // scanline generator (better) 1350 // maybe we can use an optimized version 1351 BBitmap temp(actualBitmapRect, B_BITMAP_NO_SERVER_LINK, B_RGBA32); 1352 status_t err = temp.ImportBits(srcBuffer.buf(), 1353 srcBuffer.height() * srcBuffer.stride(), 1354 srcBuffer.stride(), 1355 0, format); 1356 if (err >= B_OK) { 1357 agg::rendering_buffer convertedBuffer; 1358 convertedBuffer.attach((uint8*)temp.Bits(), 1359 (uint32)actualBitmapRect.IntegerWidth() + 1, 1360 (uint32)actualBitmapRect.IntegerHeight() + 1, 1361 temp.BytesPerRow()); 1362 _DrawBitmapGeneric32(convertedBuffer, xOffset, yOffset, 1363 xScale, yScale, viewRect); 1364 } else { 1365 fprintf(stderr, "Painter::_DrawBitmap() - " 1366 "colorspace conversion failed: %s\n", strerror(err)); 1367 } 1368 break; 1369 } 1370 } 1371 } 1372 1373 #define DEBUG_DRAW_BITMAP 0 1374 1375 // _DrawBitmapNoScale32 1376 template <class F> 1377 void 1378 Painter::_DrawBitmapNoScale32(F copyRowFunction, uint32 bytesPerSourcePixel, 1379 agg::rendering_buffer& srcBuffer, 1380 int32 xOffset, int32 yOffset, 1381 BRect viewRect) const 1382 { 1383 // NOTE: this would crash if viewRect was large enough to read outside the 1384 // bitmap, so make sure this is not the case before calling this function! 1385 uint8* dst = fBuffer.row_ptr(0); 1386 uint32 dstBPR = fBuffer.stride(); 1387 1388 const uint8* src = srcBuffer.row_ptr(0); 1389 uint32 srcBPR = srcBuffer.stride(); 1390 1391 int32 left = (int32)viewRect.left; 1392 int32 top = (int32)viewRect.top; 1393 int32 right = (int32)viewRect.right; 1394 int32 bottom = (int32)viewRect.bottom; 1395 1396 #if DEBUG_DRAW_BITMAP 1397 if (left - xOffset < 0 || left - xOffset >= (int32)srcBuffer.width() || 1398 right - xOffset >= (int32)srcBuffer.width() || 1399 top - yOffset < 0 || top - yOffset >= (int32)srcBuffer.height() || 1400 bottom - yOffset >= (int32)srcBuffer.height()) { 1401 1402 char message[256]; 1403 sprintf(message, "reading outside of bitmap (%ld, %ld, %ld, %ld) " 1404 "(%d, %d) (%ld, %ld)", 1405 left - xOffset, top - yOffset, right - xOffset, bottom - yOffset, 1406 srcBuffer.width(), srcBuffer.height(), xOffset, yOffset); 1407 debugger(message); 1408 } 1409 #endif 1410 1411 const rgb_color* colorMap = SystemPalette(); 1412 1413 // copy rects, iterate over clipping boxes 1414 fBaseRenderer.first_clip_box(); 1415 do { 1416 int32 x1 = max_c(fBaseRenderer.xmin(), left); 1417 int32 x2 = min_c(fBaseRenderer.xmax(), right); 1418 if (x1 <= x2) { 1419 int32 y1 = max_c(fBaseRenderer.ymin(), top); 1420 int32 y2 = min_c(fBaseRenderer.ymax(), bottom); 1421 if (y1 <= y2) { 1422 uint8* dstHandle = dst + y1 * dstBPR + x1 * 4; 1423 const uint8* srcHandle = src + (y1 - yOffset) * srcBPR 1424 + (x1 - xOffset) * bytesPerSourcePixel; 1425 1426 for (; y1 <= y2; y1++) { 1427 copyRowFunction(dstHandle, srcHandle, x2 - x1 + 1, 1428 colorMap); 1429 1430 dstHandle += dstBPR; 1431 srcHandle += srcBPR; 1432 } 1433 } 1434 } 1435 } while (fBaseRenderer.next_clip_box()); 1436 } 1437 1438 // _DrawBitmapGeneric32 1439 void 1440 Painter::_DrawBitmapGeneric32(agg::rendering_buffer& srcBuffer, 1441 double xOffset, double yOffset, 1442 double xScale, double yScale, 1443 BRect viewRect) const 1444 { 1445 // AGG pipeline 1446 1447 // pixel format attached to bitmap 1448 typedef agg::pixfmt_bgra32 pixfmt_image; 1449 pixfmt_image pixf_img(srcBuffer); 1450 1451 agg::trans_affine srcMatrix; 1452 // NOTE: R5 seems to ignore this offset when drawing bitmaps 1453 // srcMatrix *= agg::trans_affine_translation(-actualBitmapRect.left, 1454 // -actualBitmapRect.top); 1455 1456 agg::trans_affine imgMatrix; 1457 imgMatrix *= agg::trans_affine_scaling(xScale, yScale); 1458 imgMatrix *= agg::trans_affine_translation(xOffset, yOffset); 1459 imgMatrix.invert(); 1460 1461 // image interpolator 1462 typedef agg::span_interpolator_linear<> interpolator_type; 1463 interpolator_type interpolator(imgMatrix); 1464 1465 // scanline allocator 1466 agg::span_allocator<pixfmt_image::color_type> spanAllocator; 1467 1468 // image accessor attached to pixel format of bitmap 1469 typedef agg::image_accessor_clip<pixfmt_image> source_type; 1470 source_type source(pixf_img, agg::rgba8(0, 0, 0, 0)); 1471 1472 // image filter (nearest neighbor) 1473 typedef agg::span_image_filter_rgba_nn<source_type, 1474 interpolator_type> span_gen_type; 1475 span_gen_type spanGenerator(source, interpolator); 1476 1477 1478 // clip to the current clipping region's frame 1479 viewRect = viewRect & fClippingRegion->Frame(); 1480 // convert to pixel coords (versus pixel indices) 1481 viewRect.right++; 1482 viewRect.bottom++; 1483 1484 // path enclosing the bitmap 1485 fPath.remove_all(); 1486 fPath.move_to(viewRect.left, viewRect.top); 1487 fPath.line_to(viewRect.right, viewRect.top); 1488 fPath.line_to(viewRect.right, viewRect.bottom); 1489 fPath.line_to(viewRect.left, viewRect.bottom); 1490 fPath.close_polygon(); 1491 1492 agg::conv_transform<agg::path_storage> transformedPath(fPath, srcMatrix); 1493 fRasterizer.reset(); 1494 fRasterizer.add_path(transformedPath); 1495 1496 // render the path with the bitmap as scanline fill 1497 agg::render_scanlines_aa(fRasterizer, 1498 fUnpackedScanline, 1499 fBaseRenderer, 1500 spanAllocator, 1501 spanGenerator); 1502 } 1503 1504 // _InvertRect32 1505 void 1506 Painter::_InvertRect32(BRect r) const 1507 { 1508 int32 width = r.IntegerWidth() + 1; 1509 for (int32 y = (int32)r.top; y <= (int32)r.bottom; y++) { 1510 uint8* dst = fBuffer.row_ptr(y); 1511 dst += (int32)r.left * 4; 1512 for (int32 i = 0; i < width; i++) { 1513 dst[0] = 255 - dst[0]; 1514 dst[1] = 255 - dst[1]; 1515 dst[2] = 255 - dst[2]; 1516 dst += 4; 1517 } 1518 } 1519 } 1520 1521 // _BlendRect32 1522 void 1523 Painter::_BlendRect32(const BRect& r, const rgb_color& c) const 1524 { 1525 if (!fValidClipping) 1526 return; 1527 1528 uint8* dst = fBuffer.row_ptr(0); 1529 uint32 bpr = fBuffer.stride(); 1530 1531 int32 left = (int32)r.left; 1532 int32 top = (int32)r.top; 1533 int32 right = (int32)r.right; 1534 int32 bottom = (int32)r.bottom; 1535 1536 // fill rects, iterate over clipping boxes 1537 fBaseRenderer.first_clip_box(); 1538 do { 1539 int32 x1 = max_c(fBaseRenderer.xmin(), left); 1540 int32 x2 = min_c(fBaseRenderer.xmax(), right); 1541 if (x1 <= x2) { 1542 int32 y1 = max_c(fBaseRenderer.ymin(), top); 1543 int32 y2 = min_c(fBaseRenderer.ymax(), bottom); 1544 1545 uint8* offset = dst + x1 * 4 + y1 * bpr; 1546 for (; y1 <= y2; y1++) { 1547 blend_line32(offset, x2 - x1 + 1, c.red, c.green, c.blue, c.alpha); 1548 offset += bpr; 1549 } 1550 } 1551 } while (fBaseRenderer.next_clip_box()); 1552 } 1553 1554 // #pragma mark - 1555 1556 template<class VertexSource> 1557 BRect 1558 Painter::_BoundingBox(VertexSource& path) const 1559 { 1560 double left = 0.0; 1561 double top = 0.0; 1562 double right = -1.0; 1563 double bottom = -1.0; 1564 uint32 pathID[1]; 1565 pathID[0] = 0; 1566 agg::bounding_rect(path, pathID, 0, 1, &left, &top, &right, &bottom); 1567 return BRect(left, top, right, bottom); 1568 } 1569 1570 // agg_line_cap_mode_for 1571 inline agg::line_cap_e 1572 agg_line_cap_mode_for(cap_mode mode) 1573 { 1574 switch (mode) { 1575 case B_BUTT_CAP: 1576 return agg::butt_cap; 1577 case B_SQUARE_CAP: 1578 return agg::square_cap; 1579 case B_ROUND_CAP: 1580 return agg::round_cap; 1581 } 1582 return agg::butt_cap; 1583 } 1584 1585 // agg_line_join_mode_for 1586 inline agg::line_join_e 1587 agg_line_join_mode_for(join_mode mode) 1588 { 1589 switch (mode) { 1590 case B_MITER_JOIN: 1591 return agg::miter_join; 1592 case B_ROUND_JOIN: 1593 return agg::round_join; 1594 case B_BEVEL_JOIN: 1595 case B_BUTT_JOIN: // ?? 1596 case B_SQUARE_JOIN: // ?? 1597 return agg::bevel_join; 1598 } 1599 return agg::miter_join; 1600 } 1601 1602 // _StrokePath 1603 template<class VertexSource> 1604 BRect 1605 Painter::_StrokePath(VertexSource& path) const 1606 { 1607 agg::conv_stroke<VertexSource> stroke(path); 1608 stroke.width(fPenSize); 1609 1610 stroke.line_cap(agg_line_cap_mode_for(fLineCapMode)); 1611 stroke.line_join(agg_line_join_mode_for(fLineJoinMode)); 1612 stroke.miter_limit(fMiterLimit); 1613 1614 fRasterizer.reset(); 1615 fRasterizer.add_path(stroke); 1616 1617 agg::render_scanlines(fRasterizer, fPackedScanline, fRenderer); 1618 1619 BRect touched = _BoundingBox(path); 1620 float penSize = ceilf(fPenSize / 2.0); 1621 touched.InsetBy(-penSize, -penSize); 1622 1623 return _Clipped(touched); 1624 } 1625 1626 // _FillPath 1627 template<class VertexSource> 1628 BRect 1629 Painter::_FillPath(VertexSource& path) const 1630 { 1631 fRasterizer.reset(); 1632 fRasterizer.add_path(path); 1633 agg::render_scanlines(fRasterizer, fPackedScanline, fRenderer); 1634 1635 return _Clipped(_BoundingBox(path)); 1636 } 1637 1638