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