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