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