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