1// Painter.cpp 2 3#include <stdio.h> 4#include <string.h> 5 6#include <Bitmap.h> 7#include <GraphicsDefs.h> 8#include <Region.h> 9 10#include <agg_bezier_arc.h> 11#include <agg_bounding_rect.h> 12#include <agg_conv_curve.h> 13#include <agg_conv_stroke.h> 14#include <agg_ellipse.h> 15#include <agg_path_storage.h> 16#include <agg_rounded_rect.h> 17#include <agg_span_image_filter_rgba32.h> 18#include <agg_span_interpolator_linear.h> 19 20#include "LayerData.h" 21 22#include "AGGTextRenderer.h" 23#include "DrawingMode.h" 24#include "DrawingModeFactory.h" 25#include "FontManager.h" 26#include "PatternHandler.h" 27#include "RenderingBuffer.h" 28#include "ShapeConverter.h" 29#include "ServerBitmap.h" 30#include "ServerFont.h" 31 32#include "Painter.h" 33 34int 35roundf(float v) 36{ 37 if (v >= 0.0) 38 return (int)floorf(v + 0.5); 39 else 40 return (int)floorf(v - 0.5); 41} 42 43// constructor 44Painter::Painter() 45 : fBuffer(NULL), 46 fPixelFormat(NULL), 47 fBaseRenderer(NULL), 48 fOutlineRenderer(NULL), 49 fOutlineRasterizer(NULL), 50 fScanline(NULL), 51 fRasterizer(NULL), 52 fRenderer(NULL), 53 fFontRendererSolid(NULL), 54 fFontRendererBin(NULL), 55 fLineProfile(), 56 fSubpixelPrecise(false), 57 fScale(1.0), 58 fPenSize(1.0), 59 fOrigin(0.0, 0.0), 60 fClippingRegion(NULL), 61 fDrawingMode(B_OP_COPY), 62 fAlphaSrcMode(B_PIXEL_ALPHA), 63// fAlphaSrcMode(B_CONSTANT_ALPHA), 64 fAlphaFncMode(B_ALPHA_OVERLAY), 65 fPenLocation(0.0, 0.0), 66 fPatternHandler(new PatternHandler()), 67 fTextRenderer(new AGGTextRenderer()), 68 fLastFamilyAndStyle(0) 69{ 70 if (fontserver) 71 fFont = *fontserver->GetSystemPlain(); 72 73 _UpdateFont(); 74 _UpdateLineWidth(); 75} 76 77// destructor 78Painter::~Painter() 79{ 80 _MakeEmpty(); 81 82 delete fClippingRegion; 83 delete fPatternHandler; 84 delete fTextRenderer; 85} 86 87// #pragma mark - 88 89// AttachToBuffer 90void 91Painter::AttachToBuffer(RenderingBuffer* buffer) 92{ 93 if (buffer && buffer->InitCheck() >= B_OK) { 94 // clean up previous stuff 95 _MakeEmpty(); 96 97 fBuffer = new agg::rendering_buffer(); 98 fBuffer->attach((uint8*)buffer->Bits(), 99 buffer->Width(), 100 buffer->Height(), 101 buffer->BytesPerRow()); 102 103 fPixelFormat = new pixfmt(*fBuffer, fPatternHandler); 104 fPixelFormat->set_drawing_mode(DrawingModeFactory::DrawingModeFor(fDrawingMode, 105 fAlphaSrcMode, 106 fAlphaFncMode, 107 false)); 108 109 fBaseRenderer = new renderer_base(*fPixelFormat); 110 111 // These are the AGG renderes and rasterizes which 112 // will be used for stroking paths 113rgb_color color = fPatternHandler->HighColor().GetColor32(); 114#if ALIASED_DRAWING 115 fOutlineRenderer = new outline_renderer_type(*fBaseRenderer); 116 fOutlineRasterizer = new outline_rasterizer_type(*fOutlineRenderer); 117#else 118 fOutlineRenderer = new outline_renderer_type(*fBaseRenderer, fLineProfile); 119 fOutlineRasterizer = new outline_rasterizer_type(*fOutlineRenderer); 120 121 // attach our line profile to the renderer, it keeps a pointer 122 fOutlineRenderer->profile(fLineProfile); 123#endif 124 // the renderer used for filling paths 125 fRenderer = new renderer_type(*fBaseRenderer); 126 fRasterizer = new rasterizer_type(); 127 fScanline = new scanline_type(); 128 129#if ALIASED_DRAWING 130 fRasterizer->gamma(agg::gamma_threshold(0.5)); 131#endif 132 133 // These are renderers needed for drawing text 134 fFontRendererSolid = new font_renderer_solid_type(*fBaseRenderer); 135 fFontRendererBin = new font_renderer_bin_type(*fBaseRenderer); 136 137 _SetRendererColor(fPatternHandler->HighColor().GetColor32()); 138 _RebuildClipping(); 139 } 140} 141 142// DetachFromBuffer 143void 144Painter::DetachFromBuffer() 145{ 146 _MakeEmpty(); 147} 148 149// SetDrawData 150void 151Painter::SetDrawData(const DrawData* data) 152{ 153 // for now... 154 SetHighColor(data->highcolor.GetColor32()); 155 SetLowColor(data->lowcolor.GetColor32()); 156 SetScale(data->scale); 157 SetPenSize(data->pensize); 158// fOrigin = data->coordOrigin; 159 SetDrawingMode(data->draw_mode); 160 SetBlendingMode(data->alphaSrcMode, data->alphaFncMode); 161 SetPenLocation(data->penlocation); 162 SetFont(data->font); 163// if (data->clipReg) { 164// ConstrainClipping(*data->clipReg); 165// } 166 fPatternHandler->SetPattern(data->patt); 167} 168 169// #pragma mark - 170 171// ConstrainClipping 172void 173Painter::ConstrainClipping(const BRegion& region) 174{ 175 // The idea is that if the clipping region was 176 // never constrained, there is *no* clipping. 177 // This is of course different from having 178 // an *empty* clipping region. 179 if (!fClippingRegion) 180 fClippingRegion = new BRegion(region); 181 else 182 *fClippingRegion = region; 183 _RebuildClipping(); 184} 185 186// SetHighColor 187void 188Painter::SetHighColor(const rgb_color& color) 189{ 190 fPatternHandler->SetHighColor(color); 191} 192 193// SetLowColor 194void 195Painter::SetLowColor(const rgb_color& color) 196{ 197 fPatternHandler->SetLowColor(color);; 198} 199 200// SetScale 201void 202Painter::SetScale(float scale) 203{ 204 if (fScale != scale) { 205 fScale = scale; 206 _RebuildClipping(); 207 _UpdateLineWidth(); 208 } 209} 210 211// SetPenSize 212void 213Painter::SetPenSize(float size) 214{ 215 if (fPenSize != size) { 216 fPenSize = size; 217 _UpdateLineWidth(); 218 } 219} 220 221// SetOrigin 222void 223Painter::SetOrigin(const BPoint& origin) 224{ 225 // NOTE: The BeBook says that the coordinate system 226 // of a view cannot be changed during an update, because 227 // it would mess up the clipping, and this is indeed 228 // what would happen in this implementation as well. 229 // I don't know yet what actually happens if you still 230 // try to call SetOrigin() from within BView::Draw() 231 fOrigin = origin; 232 _RebuildClipping(); 233} 234 235// SetDrawingMode 236void 237Painter::SetDrawingMode(drawing_mode mode) 238{ 239 if (fDrawingMode != mode) { 240 fDrawingMode = mode; 241 if (fPixelFormat) { 242 fPixelFormat->set_drawing_mode(DrawingModeFactory::DrawingModeFor(fDrawingMode, 243 fAlphaSrcMode, 244 fAlphaFncMode)); 245 } 246 } 247} 248 249// SetBlendingMode 250void 251Painter::SetBlendingMode(source_alpha alphaSrcMode, alpha_function alphaFncMode) 252{ 253 if (fAlphaSrcMode != alphaSrcMode || fAlphaFncMode != alphaFncMode) { 254 fAlphaSrcMode = alphaSrcMode; 255 fAlphaFncMode = alphaFncMode; 256 if (fDrawingMode == B_OP_ALPHA && fPixelFormat) { 257 fPixelFormat->set_drawing_mode(DrawingModeFactory::DrawingModeFor(fDrawingMode, 258 fAlphaSrcMode, 259 fAlphaFncMode)); 260 } 261 } 262} 263 264// SetPenLocation 265void 266Painter::SetPenLocation(const BPoint& location) 267{ 268 fPenLocation = location; 269} 270 271// SetFont 272void 273Painter::SetFont(const BFont& font) 274{ 275 //fFont.SetFamilyAndStyle(font.GetFamily(), font.GetStyle()); 276 fFont.SetSpacing(font.Spacing()); 277 fFont.SetShear(font.Shear()); 278 fFont.SetRotation(font.Rotation()); 279 fFont.SetSize(font.Size()); 280 281 _UpdateFont(); 282} 283 284// SetFont 285void 286Painter::SetFont(const ServerFont& font) 287{ 288 fFont = font; 289 _UpdateFont(); 290} 291 292// #pragma mark - 293 294// StrokeLine 295BRect 296Painter::StrokeLine(BPoint a, BPoint b, const pattern& p) 297{ 298 _Transform(&a); 299 _Transform(&b); 300 301 BRect touched(a, b); 302 303 // first, try an optimized version 304 float penSize = _Transform(fPenSize); 305 if (penSize == 1.0 && 306 (fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER)) { 307 pattern pat = *fPatternHandler->GetR5Pattern(); 308 if (pat == B_SOLID_HIGH && 309 StraightLine(a, b, fPatternHandler->HighColor().GetColor32())) { 310 SetPenLocation(b); 311 return _Clipped(touched); 312 } else if (pat == B_SOLID_LOW && 313 StraightLine(a, b, fPatternHandler->LowColor().GetColor32())) { 314 SetPenLocation(b); 315 return _Clipped(touched); 316 } 317 } 318 319 agg::path_storage path; 320 path.move_to(a.x, a.y); 321 path.line_to(b.x, b.y); 322 323 touched = _StrokePath(path, p); 324 325 SetPenLocation(b); 326 327 return _Clipped(touched); 328} 329 330// StrokeLine 331BRect 332Painter::StrokeLine(BPoint b, const pattern& p) 333{ 334 // TODO: move this function elsewhere 335 return StrokeLine(fPenLocation, b); 336} 337 338// StraightLine 339bool 340Painter::StraightLine(BPoint a, BPoint b, const rgb_color& c) const 341{ 342 if (fBuffer) { 343 if (a.x == b.x) { 344 // vertical 345 uint8* dst = fBuffer->row(0); 346 uint32 bpr = fBuffer->stride(); 347 int32 x = (int32)a.x; 348 dst += x * 4; 349 int32 y1 = (int32)min_c(a.y, b.y); 350 int32 y2 = (int32)max_c(a.y, b.y); 351 // draw a line, iterate over clipping boxes 352 fBaseRenderer->first_clip_box(); 353 do { 354 if (fBaseRenderer->xmin() <= x && 355 fBaseRenderer->xmax() >= x) { 356 int32 i = max_c(fBaseRenderer->ymin(), y1); 357 int32 end = min_c(fBaseRenderer->ymax(), y2); 358 uint8* handle = dst + i * bpr; 359 for (; i <= end; i++) { 360 handle[0] = c.blue; 361 handle[1] = c.green; 362 handle[2] = c.red; 363 handle += bpr; 364 } 365 } 366 } while (fBaseRenderer->next_clip_box()); 367 368 return true; 369 370 } else if (a.y == b.y) { 371 // horizontal 372 uint8* dst = fBuffer->row(0); 373 uint32 bpr = fBuffer->stride(); 374 int32 y = (int32)a.y; 375 dst += y * bpr; 376 int32 x1 = (int32)min_c(a.x, b.x); 377 int32 x2 = (int32)max_c(a.x, b.x); 378 // draw a line, iterate over clipping boxes 379 fBaseRenderer->first_clip_box(); 380 do { 381 if (fBaseRenderer->ymin() <= y && 382 fBaseRenderer->ymax() >= y) { 383 int32 i = max_c(fBaseRenderer->xmin(), x1); 384 int32 end = min_c(fBaseRenderer->xmax(), x2); 385 uint8* handle = dst + i * 4; 386 for (; i <= end; i++) { 387 handle[0] = c.blue; 388 handle[1] = c.green; 389 handle[2] = c.red; 390 handle += 4; 391 } 392 } 393 } while (fBaseRenderer->next_clip_box()); 394 395 return true; 396 } 397 } 398 return false; 399} 400 401// #pragma mark - 402 403// StrokeTriangle 404void 405Painter::StrokeTriangle(BPoint pt1, BPoint pt2, BPoint pt3, const pattern& p) const 406{ 407 _DrawTriangle(pt1, pt2, pt3, p, false); 408} 409 410// FillTriangle 411void 412Painter::FillTriangle(BPoint pt1, BPoint pt2, BPoint pt3, const pattern& p) const 413{ 414 _DrawTriangle(pt1, pt2, pt3, p, true); 415} 416 417// StrokePolygon 418void 419Painter::StrokePolygon(const BPoint* ptArray, int32 numPts, 420 bool closed, const pattern& p) const 421{ 422 _DrawPolygon(ptArray, numPts, closed, p, false); 423} 424 425// FillPolygon 426void 427Painter::FillPolygon(const BPoint* ptArray, int32 numPts, 428 bool closed, const pattern& p) const 429{ 430 _DrawPolygon(ptArray, numPts, closed, p, true); 431} 432 433// StrokeBezier 434void 435Painter::StrokeBezier(const BPoint* controlPoints, const pattern& p) const 436{ 437 agg::path_storage curve; 438 439 BPoint p1(controlPoints[0]); 440 BPoint p2(controlPoints[1]); 441 BPoint p3(controlPoints[2]); 442 BPoint p4(controlPoints[3]); 443 _Transform(&p1); 444 _Transform(&p2); 445 _Transform(&p3); 446 _Transform(&p4); 447 448 curve.move_to(p1.x, p1.y); 449 curve.curve4(p1.x, p1.y, 450 p2.x, p2.y, 451 p3.x, p3.y); 452 453 454 agg::conv_curve<agg::path_storage> path(curve); 455 456 _StrokePath(path, p); 457} 458 459// FillBezier 460void 461Painter::FillBezier(const BPoint* controlPoints, const pattern& p) const 462{ 463 agg::path_storage curve; 464 465 BPoint p1(controlPoints[0]); 466 BPoint p2(controlPoints[1]); 467 BPoint p3(controlPoints[2]); 468 BPoint p4(controlPoints[3]); 469 _Transform(&p1); 470 _Transform(&p2); 471 _Transform(&p3); 472 _Transform(&p4); 473 474 curve.move_to(p1.x, p1.y); 475 curve.curve4(p1.x, p1.y, 476 p2.x, p2.y, 477 p3.x, p3.y); 478 curve.close_polygon(); 479 480 agg::conv_curve<agg::path_storage> path(curve); 481 482 _FillPath(path, p); 483} 484 485// StrokeShape 486void 487Painter::StrokeShape(/*const */BShape* shape, const pattern& p) const 488{ 489 _DrawShape(shape, p, false); 490} 491 492// FillShape 493void 494Painter::FillShape(/*const */BShape* shape, const pattern& p) const 495{ 496 _DrawShape(shape, p, true); 497} 498 499// StrokeRect 500BRect 501Painter::StrokeRect(const BRect& r, const pattern& p) const 502{ 503 BPoint a(r.left, r.top); 504 BPoint b(r.right, r.bottom); 505 _Transform(&a); 506 _Transform(&b); 507 508 // first, try an optimized version 509 float penSize = _Transform(fPenSize); 510 if (penSize == 1.0 && 511 (fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER)) { 512// TODO: fix me 513// pattern p = *fPatternHandler->GetR5Pattern(); 514 if (p == B_SOLID_HIGH) { 515 BRect rect(a, b); 516 StrokeRect(rect, 517 fPatternHandler->HighColor().GetColor32()); 518 return _Clipped(rect); 519 } else if (p == B_SOLID_LOW) { 520 BRect rect(a, b); 521 StrokeRect(rect, 522 fPatternHandler->LowColor().GetColor32()); 523 return _Clipped(rect); 524 } 525 } 526 527 agg::path_storage path; 528 path.move_to(a.x, a.y); 529 path.line_to(b.x, a.y); 530 path.line_to(b.x, b.y); 531 path.line_to(a.x, b.y); 532 path.close_polygon(); 533 534 return _StrokePath(path, p); 535} 536 537// StrokeRect 538void 539Painter::StrokeRect(const BRect& r, const rgb_color& c) const 540{ 541 StraightLine(BPoint(r.left, r.top), 542 BPoint(r.right - 1, r.top), c); 543 StraightLine(BPoint(r.right, r.top), 544 BPoint(r.right, r.bottom - 1), c); 545 StraightLine(BPoint(r.right, r.bottom), 546 BPoint(r.left + 1, r.bottom), c); 547 StraightLine(BPoint(r.left, r.bottom), 548 BPoint(r.left, r.top + 1), c); 549} 550 551// FillRect 552BRect 553Painter::FillRect(const BRect& r, const pattern& p) const 554{ 555 BPoint a(r.left, r.top); 556 BPoint b(r.right, r.bottom); 557 _Transform(&a, false); 558 _Transform(&b, false); 559 560 // first, try an optimized version 561 if (fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER) { 562 pattern pat = *fPatternHandler->GetR5Pattern(); 563 if (pat == B_SOLID_HIGH) { 564 BRect rect(a, b); 565 FillRect(rect, fPatternHandler->HighColor().GetColor32()); 566 return _Clipped(rect); 567 } else if (pat == B_SOLID_LOW) { 568 BRect rect(a, b); 569 FillRect(rect, fPatternHandler->LowColor().GetColor32()); 570 return _Clipped(rect); 571 } 572 } 573 574 // account for stricter interpretation of coordinates in AGG 575 // the rectangle ranges from the top-left (.0, .0) 576 // to the bottom-right (.9999, .9999) corner of pixels 577 b.x += 1.0; 578 b.y += 1.0; 579 580 agg::path_storage path; 581 path.move_to(a.x, a.y); 582 path.line_to(b.x, a.y); 583 path.line_to(b.x, b.y); 584 path.line_to(a.x, b.y); 585 path.close_polygon(); 586 587 return _FillPath(path, p); 588} 589 590// FillRect 591void 592Painter::FillRect(const BRect& r, const rgb_color& c) const 593{ 594 if (fBuffer) { 595 uint8* dst = fBuffer->row(0); 596 uint32 bpr = fBuffer->stride(); 597 int32 left = (int32)r.left; 598 int32 top = (int32)r.top; 599 int32 right = (int32)r.right; 600 int32 bottom = (int32)r.bottom; 601 // fill rects, iterate over clipping boxes 602 fBaseRenderer->first_clip_box(); 603 do { 604 int32 x1 = max_c(fBaseRenderer->xmin(), left); 605 int32 x2 = min_c(fBaseRenderer->xmax(), right); 606 if (x1 <= x2) { 607 int32 y1 = max_c(fBaseRenderer->ymin(), top); 608 int32 y2 = min_c(fBaseRenderer->ymax(), bottom); 609 uint8* offset = dst + x1 * 4; 610 for (; y1 <= y2; y1++) { 611 uint8* handle = offset + y1 * bpr; 612 for (int32 x = x1; x <= x2; x++) { 613 handle[0] = c.blue; 614 handle[1] = c.green; 615 handle[2] = c.red; 616 handle += 4; 617 } 618 } 619 } 620 } while (fBaseRenderer->next_clip_box()); 621 } 622} 623 624// StrokeRoundRect 625void 626Painter::StrokeRoundRect(const BRect& r, float xRadius, float yRadius, 627 const pattern& p) const 628{ 629 BPoint lt(r.left, r.top); 630 BPoint rb(r.right, r.bottom); 631 _Transform(<); 632 _Transform(&rb); 633 634 _Transform(&xRadius); 635 _Transform(&yRadius); 636 637 agg::rounded_rect rect; 638 rect.rect(lt.x, lt.y, rb.x, rb.y); 639 rect.radius(xRadius, yRadius); 640 641 _StrokePath(rect, p); 642} 643 644// FillRoundRect 645void 646Painter::FillRoundRect(const BRect& r, float xRadius, float yRadius, 647 const pattern& p) const 648{ 649 BPoint lt(r.left, r.top); 650 BPoint rb(r.right, r.bottom); 651 _Transform(<, false); 652 _Transform(&rb, false); 653 654 // account for stricter interpretation of coordinates in AGG 655 // the rectangle ranges from the top-left (.0, .0) 656 // to the bottom-right (.9999, .9999) corner of pixels 657 rb.x += 1.0; 658 rb.y += 1.0; 659 660 _Transform(&xRadius); 661 _Transform(&yRadius); 662 663 agg::rounded_rect rect; 664 rect.rect(lt.x, lt.y, rb.x, rb.y); 665 rect.radius(xRadius, yRadius); 666 667 _FillPath(rect, p); 668} 669 670// StrokeEllipse 671void 672Painter::StrokeEllipse(BPoint center, float xRadius, float yRadius, 673 const pattern& p) const 674{ 675 _DrawEllipse(center, xRadius, yRadius, p, false); 676} 677 678// FillEllipse 679void 680Painter::FillEllipse(BPoint center, float xRadius, float yRadius, 681 const pattern& p) const 682{ 683 _DrawEllipse(center, xRadius, yRadius, p, true); 684} 685 686// StrokeArc 687void 688Painter::StrokeArc(BPoint center, float xRadius, float yRadius, 689 float angle, float span, const pattern& p) const 690{ 691 _Transform(¢er); 692 _Transform(&xRadius); 693 _Transform(&yRadius); 694 695 double angleRad = (angle * PI) / 180.0; 696 double spanRad = (span * PI) / 180.0; 697 agg::bezier_arc arc(center.x, center.y, xRadius, yRadius, 698 -angleRad, -spanRad); 699 700 agg::conv_curve<agg::bezier_arc> path(arc); 701 702 _StrokePath(path, p); 703} 704 705// FillArc 706void 707Painter::FillArc(BPoint center, float xRadius, float yRadius, 708 float angle, float span, const pattern& p) const 709{ 710 _Transform(¢er); 711 _Transform(&xRadius); 712 _Transform(&yRadius); 713 714 double angleRad = (angle * PI) / 180.0; 715 double spanRad = (span * PI) / 180.0; 716 agg::bezier_arc arc(center.x, center.y, xRadius, yRadius, 717 -angleRad, -spanRad); 718 719 agg::conv_curve<agg::bezier_arc> segmentedArc(arc); 720 721 agg::path_storage path; 722 723 // build a new path by starting at the center point, 724 // then traversing the arc, then going back to the center 725 path.move_to(center.x, center.y); 726 727 segmentedArc.rewind(0); 728 double x; 729 double y; 730 unsigned cmd = segmentedArc.vertex(&x, &y); 731 while (!agg::is_stop(cmd)) { 732 path.line_to(x, y); 733 cmd = segmentedArc.vertex(&x, &y); 734 } 735 736 path.close_polygon(); 737 738 _FillPath(path, p); 739} 740 741// #pragma mark - 742 743// DrawChar 744BRect 745Painter::DrawChar(char aChar) 746{ 747 // TODO: to be moved elsewhere 748 return DrawChar(aChar, fPenLocation); 749} 750 751// DrawChar 752BRect 753Painter::DrawChar(char aChar, BPoint baseLine) 754{ 755 // TODO: to be moved elsewhere 756 char wrapper[2]; 757 wrapper[0] = aChar; 758 wrapper[1] = 0; 759 return DrawString(wrapper, 1, baseLine); 760} 761 762// DrawString 763BRect 764Painter::DrawString(const char* utf8String, uint32 length, 765 const escapement_delta* delta) 766{ 767 // TODO: to be moved elsewhere 768 return DrawString(utf8String, length, fPenLocation, delta); 769} 770 771// DrawString 772BRect 773Painter::DrawString(const char* utf8String, uint32 length, 774 BPoint baseLine, const escapement_delta* delta) 775{ 776 BRect bounds(0.0, 0.0, -1.0, -1.0); 777 fPatternHandler->SetPattern(B_SOLID_HIGH); 778 779 if (fBuffer) { 780 781 Transformable transform; 782 transform.ShearBy(B_ORIGIN, (90.0 - fFont.Shear()) * PI / 180.0, 0.0); 783 transform.RotateBy(B_ORIGIN, -fFont.Rotation() * PI / 180.0); 784 transform.TranslateBy(baseLine); 785 transform.ScaleBy(B_ORIGIN, fScale, fScale); 786 transform.TranslateBy(fOrigin); 787 788 BRect clippingFrame; 789 if (fClippingRegion) 790 clippingFrame = _Transform(fClippingRegion->Frame()); 791 792 bounds = fTextRenderer->RenderString(utf8String, 793 length, 794 fFontRendererSolid, 795 fFontRendererBin, 796 transform, 797 clippingFrame, 798 false, 799 &fPenLocation); 800 // pen location is not transformed in quite the same way, 801 // or transformations would add up 802 transform.Reset(); 803 transform.RotateBy(B_ORIGIN, -fFont.Rotation()); 804 transform.TranslateBy(baseLine); 805 transform.Transform(&fPenLocation); 806 } 807 return _Clipped(bounds); 808} 809 810// DrawString 811BRect 812Painter::DrawString(const char* utf8String, const escapement_delta* delta) 813{ 814 // TODO: to be moved elsewhere 815 return DrawString(utf8String, strlen(utf8String), fPenLocation, delta); 816} 817 818// DrawString 819BRect 820Painter::DrawString(const char* utf8String, BPoint baseLine, 821 const escapement_delta* delta) 822{ 823 // TODO: to be moved elsewhere 824 return DrawString(utf8String, strlen(utf8String), baseLine, delta); 825} 826 827// #pragma mark - 828 829// DrawBitmap 830void 831Painter::DrawBitmap(const BBitmap* bitmap, 832 BRect bitmapRect, BRect viewRect) const 833{ 834 if (bitmap && bitmap->IsValid()) { 835 // the native bitmap coordinate system 836 // (can have left top corner offset) 837 BRect actualBitmapRect(bitmap->Bounds()); 838 839 agg::rendering_buffer srcBuffer; 840 srcBuffer.attach((uint8*)bitmap->Bits(), 841 (uint32)actualBitmapRect.IntegerWidth() + 1, 842 (uint32)actualBitmapRect.IntegerHeight() + 1, 843 bitmap->BytesPerRow()); 844 845 _DrawBitmap(srcBuffer, bitmap->ColorSpace(), actualBitmapRect, bitmapRect, viewRect); 846 } 847} 848 849// DrawBitmap 850void 851Painter::DrawBitmap(const ServerBitmap* bitmap, 852 BRect bitmapRect, BRect viewRect) const 853{ 854 if (bitmap && bitmap->InitCheck()) { 855 // the native bitmap coordinate system 856 BRect actualBitmapRect(bitmap->Bounds()); 857 858 agg::rendering_buffer srcBuffer; 859 srcBuffer.attach(bitmap->Bits(), 860 bitmap->Width(), 861 bitmap->Height(), 862 bitmap->BytesPerRow()); 863 864 _DrawBitmap(srcBuffer, bitmap->ColorSpace(), actualBitmapRect, bitmapRect, viewRect); 865 } 866} 867 868// #pragma mark - 869 870// FillRegion 871void 872Painter::FillRegion(const BRegion* region, const pattern& p = B_SOLID_HIGH) const 873{ 874 BRegion copy(*region); 875 int32 count = copy.CountRects(); 876 for (int32 i = 0; i < count; i++) { 877 FillRect(copy.RectAt(i), p); 878 } 879} 880 881// InvertRect 882void 883Painter::InvertRect(const BRect& r) const 884{ 885 BRegion region(r); 886 if (fClippingRegion) { 887 region.IntersectWith(fClippingRegion); 888 } 889 // implementation only for B_RGB32 at the moment 890 int32 count = region.CountRects(); 891 for (int32 i = 0; i < count; i++) { 892 BRect r = region.RectAt(i); 893 _Transform(&r); 894 _InvertRect32(r); 895 } 896} 897 898// BoundingBox 899BRect 900Painter::BoundingBox(const char* utf8String, uint32 length, 901 const BPoint& baseLine) const 902{ 903 Transformable transform; 904 transform.TranslateBy(baseLine); 905 906 BRect dummy; 907 return fTextRenderer->RenderString(utf8String, 908 length, 909 fFontRendererSolid, 910 fFontRendererBin, 911 transform, dummy, true); 912} 913 914// #pragma mark - 915 916// _MakeEmpty 917void 918Painter::_MakeEmpty() 919{ 920 delete fBuffer; 921 fBuffer = NULL; 922 923 delete fPixelFormat; 924 fPixelFormat = NULL; 925 926 delete fBaseRenderer; 927 fBaseRenderer = NULL; 928 929 delete fOutlineRenderer; 930 fOutlineRenderer = NULL; 931 932 delete fOutlineRasterizer; 933 fOutlineRasterizer = NULL; 934 935 delete fScanline; 936 fScanline = NULL; 937 938 delete fRasterizer; 939 fRasterizer = NULL; 940 941 delete fRenderer; 942 fRenderer = NULL; 943 944 delete fFontRendererSolid; 945 fFontRendererSolid = NULL; 946 947 delete fFontRendererBin; 948 fFontRendererBin = NULL; 949} 950 951// _Transform 952void 953Painter::_Transform(BPoint* point, bool centerOffset) const 954{ 955 *point += fOrigin; 956 // rounding 957 if (!fSubpixelPrecise) { 958 // TODO: validate usage of floor() for values < 0 959 point->x = floorf(point->x); 960 point->y = floorf(point->y); 961 } 962 // apply the scale 963 point->x *= fScale; 964 point->y *= fScale; 965 // this code is supposed to move coordinates to the center of pixels, 966 // as AGG considers (0,0) to be the "upper left corner" of a pixel, 967 // but BViews are less strict on those details 968 if (centerOffset) { 969 point->x += 0.5; 970 point->y += 0.5; 971 } 972} 973 974// _Transform 975BPoint 976Painter::_Transform(const BPoint& point, bool centerOffset) const 977{ 978 BPoint ret = point; 979 _Transform(&ret, centerOffset); 980 return ret; 981} 982 983// _Transform 984void 985Painter::_Transform(float* width) const 986{ 987 *width *= fScale; 988 if (*width < 1) 989 *width = 1; 990} 991 992// _Transform 993float 994Painter::_Transform(const float& width) const 995{ 996 float w = width * fScale; 997 if (w < 1) 998 w = 1; 999 return w; 1000} 1001 1002// _Transform 1003void 1004Painter::_Transform(BRect* rect) const 1005{ 1006 // TODO integrate this function more 1007 rect->right++; 1008 rect->bottom++; 1009 rect->left += fOrigin.x; 1010 rect->top += fOrigin.y; 1011 rect->right += fOrigin.x; 1012 rect->bottom += fOrigin.y; 1013 rect->left *= fScale; 1014 rect->top *= fScale; 1015 rect->right *= fScale; 1016 rect->bottom *= fScale; 1017 rect->right--; 1018 rect->bottom--; 1019} 1020 1021// _Transform 1022BRect 1023Painter::_Transform(const BRect& rect) const 1024{ 1025 BRect ret = rect; 1026 _Transform(&ret); 1027 return ret; 1028} 1029 1030// _Clipped 1031BRect 1032Painter::_Clipped(const BRect& rect) const 1033{ 1034 if (rect.IsValid() && fClippingRegion) 1035 return rect & _Transform(fClippingRegion->Frame()); 1036 return rect; 1037} 1038 1039// #pragma mark - 1040 1041// _RebuildClipping 1042void 1043Painter::_RebuildClipping() 1044{ 1045 if (fBaseRenderer) { 1046 fBaseRenderer->reset_clipping(!fClippingRegion); 1047 if (fClippingRegion) { 1048 int32 count = fClippingRegion->CountRects(); 1049 for (int32 i = 0; i < count; i++) { 1050 BRect r = fClippingRegion->RectAt(i); 1051 // NOTE: The rounding here appears to give somewhat 1052 // different results compared to Be's implementation, 1053 // though I was unable to figure out the difference 1054 BPoint lt(r.LeftTop()); 1055 BPoint rb(r.RightBottom()); 1056 // offset to bottom right corner of pixel before transformation 1057 rb += BPoint(1.0, 1.0); 1058 // apply transformation 1059 lt += fOrigin; 1060 lt.x *= fScale; 1061 lt.y *= fScale; 1062 rb += fOrigin; 1063 rb.x *= fScale; 1064 rb.y *= fScale; 1065 // undo offset to bottom right corner after transformation 1066 rb -= BPoint(1.0, 1.0); 1067// fBaseRenderer->add_clip_box(floorf(lt.x), 1068// floorf(lt.y), 1069// ceilf(rb.x), 1070// ceilf(rb.y)); 1071 fBaseRenderer->add_clip_box(roundf(lt.x), 1072 roundf(lt.y), 1073 roundf(rb.x), 1074 roundf(rb.y)); 1075 } 1076 } 1077 } 1078} 1079 1080// _UpdateFont 1081void 1082Painter::_UpdateFont() 1083{ 1084 if (fLastFamilyAndStyle != fFont.GetFamilyAndStyle()) { 1085 fLastFamilyAndStyle = fFont.GetFamilyAndStyle(); 1086 1087 bool success = false; 1088 success = fTextRenderer->SetFont(fFont); 1089 if (!success) 1090 fprintf(stderr, "unable to set font\n"); 1091 } 1092 1093 fTextRenderer->SetPointSize(fFont.Size()); 1094} 1095 1096// _UpdateLineWidth 1097void 1098Painter::_UpdateLineWidth() 1099{ 1100 float width = fPenSize; 1101 _Transform(&width); 1102 1103 fLineProfile.width(width); 1104} 1105 1106// #pragma mark - 1107 1108// _DrawTriangle 1109inline void 1110Painter::_DrawTriangle(BPoint pt1, BPoint pt2, BPoint pt3, 1111 const pattern& p, bool fill) const 1112{ 1113 _Transform(&pt1); 1114 _Transform(&pt2); 1115 _Transform(&pt3); 1116 1117 agg::path_storage path; 1118 1119 path.move_to(pt1.x, pt1.y); 1120 path.line_to(pt2.x, pt2.y); 1121 path.line_to(pt3.x, pt3.y); 1122 1123 path.close_polygon(); 1124 1125 if (fill) 1126 _FillPath(path, p); 1127 else 1128 _StrokePath(path, p); 1129} 1130 1131// _DrawEllipse 1132inline void 1133Painter::_DrawEllipse(BPoint center, float xRadius, float yRadius, 1134 const pattern& p, bool fill) const 1135{ 1136 // TODO: I think the conversion and the offset of 1137 // pixel centers might not be correct here, and it 1138 // might even be necessary to treat Fill and Stroke 1139 // differently, as with Fill-/StrokeRect(). 1140 _Transform(¢er); 1141 _Transform(&xRadius); 1142 _Transform(&yRadius); 1143 1144 float width = fPenSize; 1145 _Transform(&width); 1146 1147 int32 divisions = (int32)max_c(12, ((xRadius + yRadius) * PI) / 2 * (int32)width); 1148 1149 agg::ellipse path(center.x, center.y, xRadius, yRadius, divisions); 1150 1151 if (fill) 1152 _FillPath(path, p); 1153 else 1154 _StrokePath(path, p); 1155} 1156 1157// _DrawShape 1158inline void 1159Painter::_DrawShape(/*const */BShape* shape, const pattern& p, bool fill) const 1160{ 1161 // TODO: untested 1162 agg::path_storage path; 1163 ShapeConverter converter(&path); 1164 1165 // account for our view coordinate system 1166 converter.ScaleBy(B_ORIGIN, fScale, fScale); 1167 converter.TranslateBy(fOrigin); 1168 // offset locations to center of pixels 1169 converter.TranslateBy(BPoint(0.5, 0.5)); 1170 1171 converter.Iterate(shape); 1172 1173 if (fill) 1174 _FillPath(path, p); 1175 else 1176 _StrokePath(path, p); 1177} 1178 1179// _DrawPolygon 1180inline void 1181Painter::_DrawPolygon(const BPoint* ptArray, int32 numPts, 1182 bool closed, const pattern& p, bool fill) const 1183{ 1184 if (numPts > 0) { 1185 1186 agg::path_storage path; 1187 BPoint point = _Transform(*ptArray); 1188 path.move_to(point.x, point.y); 1189 1190 for (int32 i = 1; i < numPts; i++) { 1191 ptArray++; 1192 point = _Transform(*ptArray); 1193 path.line_to(point.x, point.y); 1194 } 1195 1196 if (closed) 1197 path.close_polygon(); 1198 1199 if (fill) 1200 _FillPath(path, p); 1201 else 1202 _StrokePath(path, p); 1203 } 1204} 1205 1206// _DrawBitmap 1207void 1208Painter::_DrawBitmap(const agg::rendering_buffer& srcBuffer, color_space format, 1209 BRect actualBitmapRect, BRect bitmapRect, BRect viewRect) const 1210{ 1211 switch (format) { 1212 case B_RGB32: 1213 case B_RGBA32: 1214 _DrawBitmap32(srcBuffer, actualBitmapRect, bitmapRect, viewRect); 1215 break; 1216 default: 1217fprintf(stderr, "Painter::_DrawBitmap() - non-native colorspace: %d\n", format); 1218#ifdef __HAIKU__ 1219 // TODO: this is only a temporary implementation, 1220 // to really handle other colorspaces, one would 1221 // rather do the conversion with much less overhead, 1222 // for example in the nn filter (hm), or in the 1223 // scanline generator 1224 BBitmap temp(actualBitmapRect, 0, B_RGB32); 1225 status_t err = temp.ImportBits(srcBuffer.buf(), 1226 srcBuffer.height() * srcBuffer.stride(), 1227 srcBuffer.stride(), 1228 0, format); 1229 if (err >= B_OK) { 1230 agg::rendering_buffer convertedBuffer; 1231 convertedBuffer.attach((uint8*)temp.Bits(), 1232 (uint32)actualBitmapRect.IntegerWidth() + 1, 1233 (uint32)actualBitmapRect.IntegerHeight() + 1, 1234 temp.BytesPerRow()); 1235 _DrawBitmap32(convertedBuffer, actualBitmapRect, bitmapRect, viewRect); 1236 } else { 1237fprintf(stderr, "Painter::_DrawBitmap() - colorspace conversion failed: %s\n", strerror(err)); 1238 } 1239#endif // __HAIKU__ 1240 break; 1241 } 1242} 1243 1244// _DrawBitmap32 1245void 1246Painter::_DrawBitmap32(const agg::rendering_buffer& srcBuffer, 1247 BRect actualBitmapRect, BRect bitmapRect, BRect viewRect) const 1248{ 1249typedef agg::span_allocator<agg::rgba8> span_alloc_type; 1250typedef agg::span_interpolator_linear<> interpolator_type; 1251typedef agg::span_image_filter_rgba32_nn<agg::order_bgra32, 1252 interpolator_type> span_gen_type; 1253typedef agg::renderer_scanline_aa<renderer_base, span_gen_type> image_renderer_type; 1254 1255 if (bitmapRect.IsValid() && bitmapRect.Intersects(actualBitmapRect) 1256 && viewRect.IsValid()) { 1257 1258 // compensate for the lefttop offset the actualBitmapRect might have 1259// NOTE: I have no clue why enabling the next call gives a wrong result! 1260// According to the BeBook, bitmapRect is supposed to be in native 1261// bitmap space! 1262// bitmapRect.OffsetBy(-actualBitmapRect.left, -actualBitmapRect.top); 1263 actualBitmapRect.OffsetBy(-actualBitmapRect.left, -actualBitmapRect.top); 1264 1265 // calculate the scaling 1266 double xScale = (viewRect.Width() + 1) / (bitmapRect.Width() + 1); 1267 double yScale = (viewRect.Height() + 1) / (bitmapRect.Height() + 1); 1268 1269 // constrain rect to passed bitmap bounds 1270 // and transfer the changes to the viewRect 1271 if (bitmapRect.left < actualBitmapRect.left) { 1272 float diff = actualBitmapRect.left - bitmapRect.left; 1273 viewRect.left += diff * xScale; 1274 bitmapRect.left = actualBitmapRect.left; 1275 } 1276 if (bitmapRect.top < actualBitmapRect.top) { 1277 float diff = actualBitmapRect.top - bitmapRect.top; 1278 viewRect.top += diff; 1279 bitmapRect.top = actualBitmapRect.top; 1280 } 1281 if (bitmapRect.right > actualBitmapRect.right) { 1282 float diff = bitmapRect.right - actualBitmapRect.right; 1283 viewRect.right -= diff; 1284 bitmapRect.right = actualBitmapRect.right; 1285 } 1286 if (bitmapRect.bottom > actualBitmapRect.bottom) { 1287 float diff = bitmapRect.right - actualBitmapRect.bottom; 1288 viewRect.bottom -= diff; 1289 bitmapRect.bottom = actualBitmapRect.bottom; 1290 } 1291 1292 float xOffset = viewRect.left - (bitmapRect.left * xScale); 1293 float yOffset = viewRect.top - (bitmapRect.top * yScale); 1294 1295 agg::trans_affine srcMatrix; 1296// srcMatrix *= agg::trans_affine_translation(-actualBitmapRect.left, -actualBitmapRect.top); 1297 srcMatrix *= agg::trans_affine_scaling(fScale, fScale); 1298 srcMatrix *= agg::trans_affine_translation(fOrigin.x, fOrigin.y); 1299 1300 agg::trans_affine imgMatrix; 1301 imgMatrix *= agg::trans_affine_scaling(xScale, yScale); 1302 imgMatrix *= agg::trans_affine_translation(xOffset, yOffset); 1303 imgMatrix *= agg::trans_affine_scaling(fScale, fScale); 1304 imgMatrix *= agg::trans_affine_translation(fOrigin.x, fOrigin.y); 1305 imgMatrix.invert(); 1306 1307 span_alloc_type sa; 1308 interpolator_type interpolator(imgMatrix); 1309 1310 span_gen_type sg(sa, srcBuffer, agg::rgba(0, 0, 0, 0), interpolator); 1311 1312 image_renderer_type ri(*fBaseRenderer, sg); 1313 1314 agg::rasterizer_scanline_aa<> pf; 1315 agg::scanline_u8 sl; 1316 1317 // path encloses image 1318 agg::path_storage path; 1319 path.move_to(viewRect.left, viewRect.top); 1320 path.line_to(viewRect.right + 1, viewRect.top); 1321 path.line_to(viewRect.right + 1, viewRect.bottom + 1); 1322 path.line_to(viewRect.left, viewRect.bottom + 1); 1323 path.close_polygon(); 1324 1325 agg::conv_transform<agg::path_storage> tr(path, srcMatrix); 1326 1327 pf.add_path(tr); 1328 agg::render_scanlines(pf, sl, ri); 1329 } 1330} 1331 1332// _InvertRect32 1333void 1334Painter::_InvertRect32(BRect r) const 1335{ 1336 if (fBuffer) { 1337 int32 width = r.IntegerWidth() + 1; 1338 for (int32 y = (int32)r.top; y <= (int32)r.bottom; y++) { 1339 uint8* dst = fBuffer->row(y); 1340 dst += (int32)r.left * 4; 1341 for (int32 i = 0; i < width; i++) { 1342 dst[0] = 255 - dst[0]; 1343 dst[1] = 255 - dst[1]; 1344 dst[2] = 255 - dst[2]; 1345 dst += 4; 1346 } 1347 } 1348 } 1349} 1350 1351// #pragma mark - 1352 1353template<class VertexSource> 1354BRect 1355Painter::_BoundingBox(VertexSource& path) const 1356{ 1357 double left = 0.0; 1358 double top = 0.0; 1359 double right = -1.0; 1360 double bottom = -1.0; 1361 uint32 pathID[1]; 1362 pathID[0] = 0; 1363 agg::bounding_rect(path, pathID, 0, 1, &left, &top, &right, &bottom); 1364 return BRect(left, top, right, bottom); 1365} 1366 1367 1368// _StrokePath 1369template<class VertexSource> 1370BRect 1371Painter::_StrokePath(VertexSource& path, const pattern& p) const 1372{ 1373// We're now used by app_server and SetDrawData() was called prior to 1374// this and it means the pattern is already set 1375// fPatternHandler->SetPattern(p); 1376// _SetPattern(p); 1377 1378#if ALIASED_DRAWING 1379 float width = fPenSize; 1380 _Transform(&width); 1381 if (width > 1.0) { 1382 agg::conv_stroke<VertexSource> stroke(path); 1383 stroke.width(width); 1384 1385 fRasterizer->add_path(stroke); 1386 agg::render_scanlines(*fRasterizer, *fScanline, *fRenderer); 1387 } else { 1388 fOutlineRasterizer->add_path(path); 1389 } 1390#else 1391 fOutlineRasterizer->add_path(path); 1392#endif 1393 1394 return _Clipped(_BoundingBox(path)); 1395} 1396 1397// _FillPath 1398template<class VertexSource> 1399BRect 1400Painter::_FillPath(VertexSource& path, const pattern& p) const 1401{ 1402// We're now used by app_server and SetDrawData() was called prior to 1403// this and it means the pattern is already set 1404// fPatternHandler->SetPattern(p); 1405// _SetPattern(p); 1406 1407 fRasterizer->add_path(path); 1408 agg::render_scanlines(*fRasterizer, *fScanline, *fRenderer); 1409 1410 return _Clipped(_BoundingBox(path)); 1411} 1412 1413// _SetPattern 1414void 1415Painter::_SetPattern(const pattern& p) const 1416{ 1417 if (!(p == *fPatternHandler->GetR5Pattern())) { 1418printf("Painter::_SetPattern()\n"); 1419 fPatternHandler->SetPattern(p); 1420 DrawingMode* mode = NULL; 1421 if (p == B_SOLID_HIGH) { 1422 _SetRendererColor(fPatternHandler->HighColor().GetColor32()); 1423 mode = DrawingModeFactory::DrawingModeFor(fDrawingMode, 1424 fAlphaSrcMode, 1425 fAlphaFncMode, 1426 true); 1427 } else if (p == B_SOLID_LOW) { 1428 _SetRendererColor(fPatternHandler->LowColor().GetColor32()); 1429 mode = DrawingModeFactory::DrawingModeFor(fDrawingMode, 1430 fAlphaSrcMode, 1431 fAlphaFncMode, 1432 true); 1433 } else { 1434 mode = DrawingModeFactory::DrawingModeFor(fDrawingMode, 1435 fAlphaSrcMode, 1436 fAlphaFncMode, 1437 false); 1438 } 1439 fPixelFormat->set_drawing_mode(mode); 1440 } 1441} 1442 1443// _SetRendererColor 1444void 1445Painter::_SetRendererColor(const rgb_color& color) const 1446{ 1447 1448 if (fOutlineRenderer) 1449#if ALIASED_DRAWING 1450 fOutlineRenderer->line_color(agg::rgba(color.red / 255.0, 1451 color.green / 255.0, 1452 color.blue / 255.0)); 1453#else 1454 fOutlineRenderer->color(agg::rgba(color.red / 255.0, 1455 color.green / 255.0, 1456 color.blue / 255.0)); 1457#endif 1458 if (fRenderer) 1459 fRenderer->color(agg::rgba(color.red / 255.0, 1460 color.green / 255.0, 1461 color.blue / 255.0)); 1462 if (fFontRendererSolid) 1463 fFontRendererSolid->color(agg::rgba(color.red / 255.0, 1464 color.green / 255.0, 1465 color.blue / 255.0)); 1466 if (fFontRendererBin) 1467 fFontRendererBin->color(agg::rgba(color.red / 255.0, 1468 color.green / 255.0, 1469 color.blue / 255.0)); 1470 1471} 1472