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