1 /* 2 * Copyright 2009, Christian Packmann. 3 * Copyright 2008, Andrej Spielmann <andrej.spielmann@seh.ox.ac.uk>. 4 * Copyright 2005-2014, Stephan Aßmus <superstippi@gmx.de>. 5 * All rights reserved. Distributed under the terms of the MIT License. 6 */ 7 8 9 /*! API to the Anti-Grain Geometry based "Painter" drawing backend. Manages 10 rendering pipe-lines for stroke, fills, bitmap and text rendering. 11 */ 12 13 14 #include "Painter.h" 15 16 #include <new> 17 18 #include <stdio.h> 19 #include <string.h> 20 #include <syslog.h> 21 22 #include <Bitmap.h> 23 #include <GraphicsDefs.h> 24 #include <Region.h> 25 #include <String.h> 26 #include <GradientLinear.h> 27 #include <GradientRadial.h> 28 #include <GradientRadialFocus.h> 29 #include <GradientDiamond.h> 30 #include <GradientConic.h> 31 32 #include <ShapePrivate.h> 33 34 #include <agg_bezier_arc.h> 35 #include <agg_bounding_rect.h> 36 #include <agg_conv_clip_polygon.h> 37 #include <agg_conv_curve.h> 38 #include <agg_conv_stroke.h> 39 #include <agg_ellipse.h> 40 #include <agg_image_accessors.h> 41 #include <agg_path_storage.h> 42 #include <agg_pixfmt_rgba.h> 43 #include <agg_rounded_rect.h> 44 #include <agg_span_allocator.h> 45 #include <agg_span_image_filter_rgba.h> 46 #include <agg_span_interpolator_linear.h> 47 48 #include "drawing_support.h" 49 50 #include "DrawState.h" 51 52 #include <AutoDeleter.h> 53 #include <View.h> 54 55 #include "AlphaMask.h" 56 #include "DrawingMode.h" 57 #include "GlobalSubpixelSettings.h" 58 #include "PatternHandler.h" 59 #include "RenderingBuffer.h" 60 #include "ServerBitmap.h" 61 #include "ServerFont.h" 62 #include "SystemPalette.h" 63 64 #include "AppServer.h" 65 66 using std::nothrow; 67 68 #undef TRACE 69 // #define TRACE_PAINTER 70 #ifdef TRACE_PAINTER 71 # define TRACE(x...) printf(x) 72 #else 73 # define TRACE(x...) 74 #endif 75 76 //#define TRACE_GRADIENTS 77 #ifdef TRACE_GRADIENTS 78 # include <OS.h> 79 # define GTRACE(x...) debug_printf(x) 80 #else 81 # define GTRACE(x...) 82 #endif 83 84 85 #define CHECK_CLIPPING if (!fValidClipping) return BRect(0, 0, -1, -1); 86 #define CHECK_CLIPPING_NO_RETURN if (!fValidClipping) return; 87 88 // Defines for SIMD support. 89 #define APPSERVER_SIMD_MMX (1 << 0) 90 #define APPSERVER_SIMD_SSE (1 << 1) 91 92 // Prototypes for assembler routines 93 extern "C" { 94 void bilinear_scale_xloop_mmxsse(const uint8* src, void* dst, 95 void* xWeights, uint32 xmin, uint32 xmax, uint32 wTop, uint32 srcBPR); 96 } 97 98 static uint32 detect_simd(); 99 100 static uint32 sSIMDFlags = detect_simd(); 101 102 103 /*! Detect SIMD flags for use in AppServer. Checks all CPUs in the system 104 and chooses the minimum supported set of instructions. 105 */ 106 static uint32 107 detect_simd() 108 { 109 #if __INTEL__ 110 // Only scan CPUs for which we are certain the SIMD flags are properly 111 // defined. 112 const char* vendorNames[] = { 113 "GenuineIntel", 114 "AuthenticAMD", 115 "CentaurHauls", // Via CPUs, MMX and SSE support 116 "RiseRiseRise", // should be MMX-only 117 "CyrixInstead", // MMX-only, but custom MMX extensions 118 "GenuineTMx86", // MMX and SSE 119 0 120 }; 121 122 system_info systemInfo; 123 if (get_system_info(&systemInfo) != B_OK) 124 return 0; 125 126 // We start out with all flags set and end up with only those flags 127 // supported across all CPUs found. 128 uint32 systemSIMD = 0xffffffff; 129 130 for (uint32 cpu = 0; cpu < systemInfo.cpu_count; cpu++) { 131 cpuid_info cpuInfo; 132 get_cpuid(&cpuInfo, 0, cpu); 133 134 // Get the vendor string and terminate it manually 135 char vendor[13]; 136 memcpy(vendor, cpuInfo.eax_0.vendor_id, 12); 137 vendor[12] = 0; 138 139 bool vendorFound = false; 140 for (uint32 i = 0; vendorNames[i] != 0; i++) { 141 if (strcmp(vendor, vendorNames[i]) == 0) 142 vendorFound = true; 143 } 144 145 uint32 cpuSIMD = 0; 146 uint32 maxStdFunc = cpuInfo.regs.eax; 147 if (vendorFound && maxStdFunc >= 1) { 148 get_cpuid(&cpuInfo, 1, 0); 149 uint32 edx = cpuInfo.regs.edx; 150 if (edx & (1 << 23)) 151 cpuSIMD |= APPSERVER_SIMD_MMX; 152 if (edx & (1 << 25)) 153 cpuSIMD |= APPSERVER_SIMD_SSE; 154 } else { 155 // no flags can be identified 156 cpuSIMD = 0; 157 } 158 systemSIMD &= cpuSIMD; 159 } 160 return systemSIMD; 161 #else // !__INTEL__ 162 return 0; 163 #endif 164 } 165 166 167 // #pragma mark - 168 169 170 Painter::Painter() 171 : 172 fBuffer(), 173 fPixelFormat(fBuffer, &fPatternHandler), 174 fBaseRenderer(fPixelFormat), 175 fUnpackedScanline(), 176 fPackedScanline(), 177 fRasterizer(), 178 fRenderer(fBaseRenderer), 179 fRendererBin(fBaseRenderer), 180 fSubpixPackedScanline(), 181 fSubpixUnpackedScanline(), 182 fSubpixRasterizer(), 183 fSubpixRenderer(fBaseRenderer), 184 fMaskedUnpackedScanline(NULL), 185 186 fPath(), 187 fCurve(fPath), 188 189 fSubpixelPrecise(false), 190 fValidClipping(false), 191 fDrawingText(false), 192 fAttached(false), 193 194 fPenSize(1.0), 195 fClippingRegion(NULL), 196 fDrawingMode(B_OP_COPY), 197 fAlphaSrcMode(B_PIXEL_ALPHA), 198 fAlphaFncMode(B_ALPHA_OVERLAY), 199 fLineCapMode(B_BUTT_CAP), 200 fLineJoinMode(B_MITER_JOIN), 201 fMiterLimit(B_DEFAULT_MITER_LIMIT), 202 203 fPatternHandler(), 204 fTextRenderer(fSubpixRenderer, fRenderer, fRendererBin, fUnpackedScanline, 205 fSubpixUnpackedScanline, fSubpixRasterizer, fMaskedUnpackedScanline, 206 fTransform) 207 { 208 fPixelFormat.SetDrawingMode(fDrawingMode, fAlphaSrcMode, fAlphaFncMode, 209 false); 210 211 #if ALIASED_DRAWING 212 fRasterizer.gamma(agg::gamma_threshold(0.5)); 213 fSubpixRasterizer.gamma(agg:gamma_threshold(0.5)); 214 #endif 215 } 216 217 218 // destructor 219 Painter::~Painter() 220 { 221 } 222 223 224 // #pragma mark - 225 226 227 // AttachToBuffer 228 void 229 Painter::AttachToBuffer(RenderingBuffer* buffer) 230 { 231 if (buffer && buffer->InitCheck() >= B_OK 232 && (buffer->ColorSpace() == B_RGBA32 233 || buffer->ColorSpace() == B_RGB32)) { 234 // TODO: implement drawing on B_RGB24, B_RGB15, B_RGB16, 235 // B_CMAP8 and B_GRAY8 :-[ 236 // (if ever we want to support some devices where this gives 237 // a great speed up, right now it seems fine, even in emulation) 238 239 fBuffer.attach((uint8*)buffer->Bits(), 240 buffer->Width(), buffer->Height(), buffer->BytesPerRow()); 241 242 fAttached = true; 243 fValidClipping = fClippingRegion != NULL 244 && fClippingRegion->Frame().IsValid(); 245 246 // These are the AGG renderes and rasterizes which 247 // will be used for stroking paths 248 249 _SetRendererColor(fPatternHandler.HighColor()); 250 } 251 } 252 253 254 // DetachFromBuffer 255 void 256 Painter::DetachFromBuffer() 257 { 258 fBuffer.attach(NULL, 0, 0, 0); 259 fAttached = false; 260 fValidClipping = false; 261 } 262 263 264 // Bounds 265 BRect 266 Painter::Bounds() const 267 { 268 return BRect(0, 0, fBuffer.width() - 1, fBuffer.height() - 1); 269 } 270 271 272 // #pragma mark - 273 274 275 // SetDrawState 276 void 277 Painter::SetDrawState(const DrawState* state, int32 xOffset, int32 yOffset) 278 { 279 // NOTE: The custom clipping in "state" is ignored, because it has already 280 // been taken into account elsewhere 281 282 // NOTE: Usually this function is only used when the "current view" 283 // is switched in the ServerWindow and after the decorator has drawn 284 // and messed up the state. For other graphics state changes, the 285 // Painter methods are used directly, so this function is much less 286 // speed critical than it used to be. 287 SetTransform(state->CombinedTransform(), xOffset, yOffset); 288 289 SetPenSize(state->PenSize()); 290 291 SetFont(state); 292 293 fSubpixelPrecise = state->SubPixelPrecise(); 294 295 if (state->GetAlphaMask() != NULL) 296 fMaskedUnpackedScanline = state->GetAlphaMask()->Generate(); 297 else 298 fMaskedUnpackedScanline = NULL; 299 300 // any of these conditions means we need to use a different drawing 301 // mode instance 302 bool updateDrawingMode 303 = !(state->GetPattern() == fPatternHandler.GetPattern()) 304 || state->GetDrawingMode() != fDrawingMode 305 || (state->GetDrawingMode() == B_OP_ALPHA 306 && (state->AlphaSrcMode() != fAlphaSrcMode 307 || state->AlphaFncMode() != fAlphaFncMode)); 308 309 fDrawingMode = state->GetDrawingMode(); 310 fAlphaSrcMode = state->AlphaSrcMode(); 311 fAlphaFncMode = state->AlphaFncMode(); 312 fPatternHandler.SetPattern(state->GetPattern()); 313 fPatternHandler.SetOffsets(xOffset, yOffset); 314 fLineCapMode = state->LineCapMode(); 315 fLineJoinMode = state->LineJoinMode(); 316 fMiterLimit = state->MiterLimit(); 317 318 SetFillRule(state->FillRule()); 319 320 // adopt the color *after* the pattern is set 321 // to set the renderers to the correct color 322 SetHighColor(state->HighColor()); 323 SetLowColor(state->LowColor()); 324 325 if (updateDrawingMode || fPixelFormat.UsesOpCopyForText()) 326 _UpdateDrawingMode(); 327 } 328 329 330 // #pragma mark - state 331 332 333 // ConstrainClipping 334 void 335 Painter::ConstrainClipping(const BRegion* region) 336 { 337 fClippingRegion = region; 338 fBaseRenderer.set_clipping_region(const_cast<BRegion*>(region)); 339 fValidClipping = region->Frame().IsValid() && fAttached; 340 341 if (fValidClipping) { 342 clipping_rect cb = fClippingRegion->FrameInt(); 343 fRasterizer.clip_box(cb.left, cb.top, cb.right + 1, cb.bottom + 1); 344 fSubpixRasterizer.clip_box(cb.left, cb.top, cb.right + 1, cb.bottom + 1); 345 } 346 } 347 348 349 void 350 Painter::SetTransform(BAffineTransform transform, int32 xOffset, int32 yOffset) 351 { 352 fIdentityTransform = transform.IsIdentity(); 353 if (!fIdentityTransform) { 354 fTransform = agg::trans_affine_translation(-xOffset, -yOffset); 355 fTransform *= agg::trans_affine(transform.sx, transform.shy, 356 transform.shx, transform.sy, transform.tx, transform.ty); 357 fTransform *= agg::trans_affine_translation(xOffset, yOffset); 358 } else { 359 fTransform.reset(); 360 } 361 } 362 363 364 // SetHighColor 365 void 366 Painter::SetHighColor(const rgb_color& color) 367 { 368 if (fPatternHandler.HighColor() == color) 369 return; 370 fPatternHandler.SetHighColor(color); 371 if (*(fPatternHandler.GetR5Pattern()) == B_SOLID_HIGH) 372 _SetRendererColor(color); 373 } 374 375 376 // SetLowColor 377 void 378 Painter::SetLowColor(const rgb_color& color) 379 { 380 fPatternHandler.SetLowColor(color); 381 if (*(fPatternHandler.GetR5Pattern()) == B_SOLID_LOW) 382 _SetRendererColor(color); 383 } 384 385 386 // SetDrawingMode 387 void 388 Painter::SetDrawingMode(drawing_mode mode) 389 { 390 if (fDrawingMode != mode) { 391 fDrawingMode = mode; 392 _UpdateDrawingMode(); 393 } 394 } 395 396 397 // SetBlendingMode 398 void 399 Painter::SetBlendingMode(source_alpha srcAlpha, alpha_function alphaFunc) 400 { 401 if (fAlphaSrcMode != srcAlpha || fAlphaFncMode != alphaFunc) { 402 fAlphaSrcMode = srcAlpha; 403 fAlphaFncMode = alphaFunc; 404 if (fDrawingMode == B_OP_ALPHA) 405 _UpdateDrawingMode(); 406 } 407 } 408 409 410 // SetPenSize 411 void 412 Painter::SetPenSize(float size) 413 { 414 fPenSize = size; 415 } 416 417 418 // SetStrokeMode 419 void 420 Painter::SetStrokeMode(cap_mode lineCap, join_mode joinMode, float miterLimit) 421 { 422 fLineCapMode = lineCap; 423 fLineJoinMode = joinMode; 424 fMiterLimit = miterLimit; 425 } 426 427 428 void 429 Painter::SetFillRule(int32 fillRule) 430 { 431 agg::filling_rule_e aggFillRule = fillRule == B_EVEN_ODD 432 ? agg::fill_even_odd : agg::fill_non_zero; 433 434 fRasterizer.filling_rule(aggFillRule); 435 fSubpixRasterizer.filling_rule(aggFillRule); 436 } 437 438 439 // SetPattern 440 void 441 Painter::SetPattern(const pattern& p, bool drawingText) 442 { 443 if (!(p == *fPatternHandler.GetR5Pattern()) || drawingText != fDrawingText) { 444 fPatternHandler.SetPattern(p); 445 fDrawingText = drawingText; 446 _UpdateDrawingMode(fDrawingText); 447 448 // update renderer color if necessary 449 if (fPatternHandler.IsSolidHigh()) { 450 // pattern was not solid high before 451 _SetRendererColor(fPatternHandler.HighColor()); 452 } else if (fPatternHandler.IsSolidLow()) { 453 // pattern was not solid low before 454 _SetRendererColor(fPatternHandler.LowColor()); 455 } 456 } 457 } 458 459 460 // SetFont 461 void 462 Painter::SetFont(const ServerFont& font) 463 { 464 fTextRenderer.SetFont(font); 465 fTextRenderer.SetAntialiasing(!(font.Flags() & B_DISABLE_ANTIALIASING)); 466 } 467 468 469 // SetFont 470 void 471 Painter::SetFont(const DrawState* state) 472 { 473 fTextRenderer.SetFont(state->Font()); 474 fTextRenderer.SetAntialiasing(!state->ForceFontAliasing() 475 && (state->Font().Flags() & B_DISABLE_ANTIALIASING) == 0); 476 } 477 478 479 // #pragma mark - drawing 480 481 482 // StrokeLine 483 void 484 Painter::StrokeLine(BPoint a, BPoint b) 485 { 486 CHECK_CLIPPING_NO_RETURN 487 488 // "false" means not to do the pixel center offset, 489 // because it would mess up our optimized versions 490 _Align(&a, false); 491 _Align(&b, false); 492 493 // first, try an optimized version 494 if (fPenSize == 1.0 && fIdentityTransform 495 && (fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER) 496 && fMaskedUnpackedScanline == NULL) { 497 pattern pat = *fPatternHandler.GetR5Pattern(); 498 if (pat == B_SOLID_HIGH 499 && StraightLine(a, b, fPatternHandler.HighColor())) { 500 return; 501 } else if (pat == B_SOLID_LOW 502 && StraightLine(a, b, fPatternHandler.LowColor())) { 503 return; 504 } 505 } 506 507 fPath.remove_all(); 508 509 if (a == b) { 510 // special case dots 511 if (fPenSize == 1.0 && !fSubpixelPrecise && fIdentityTransform) { 512 if (fClippingRegion->Contains(a)) { 513 fPixelFormat.blend_pixel((int)a.x, (int)a.y, fRenderer.color(), 514 255); 515 } 516 } else { 517 fPath.move_to(a.x, a.y); 518 fPath.line_to(a.x + 1, a.y); 519 fPath.line_to(a.x + 1, a.y + 1); 520 fPath.line_to(a.x, a.y + 1); 521 522 _FillPath(fPath); 523 } 524 } else { 525 // Do the pixel center offset here 526 if (!fSubpixelPrecise && fmodf(fPenSize, 2.0) != 0.0) { 527 _Align(&a, true); 528 _Align(&b, true); 529 } 530 531 fPath.move_to(a.x, a.y); 532 fPath.line_to(b.x, b.y); 533 534 if (!fSubpixelPrecise && fPenSize == 1.0f) { 535 // Tweak ends to "include" the pixel at the index, 536 // we need to do this in order to produce results like R5, 537 // where coordinates were inclusive 538 _StrokePath(fPath, B_SQUARE_CAP); 539 } else 540 _StrokePath(fPath); 541 } 542 } 543 544 545 // StraightLine 546 bool 547 Painter::StraightLine(BPoint a, BPoint b, const rgb_color& c) const 548 { 549 if (!fValidClipping) 550 return false; 551 552 if (a.x == b.x) { 553 // vertical 554 uint8* dst = fBuffer.row_ptr(0); 555 uint32 bpr = fBuffer.stride(); 556 int32 x = (int32)a.x; 557 dst += x * 4; 558 int32 y1 = (int32)min_c(a.y, b.y); 559 int32 y2 = (int32)max_c(a.y, b.y); 560 pixel32 color; 561 color.data8[0] = c.blue; 562 color.data8[1] = c.green; 563 color.data8[2] = c.red; 564 color.data8[3] = 255; 565 // draw a line, iterate over clipping boxes 566 fBaseRenderer.first_clip_box(); 567 do { 568 if (fBaseRenderer.xmin() <= x && 569 fBaseRenderer.xmax() >= x) { 570 int32 i = max_c(fBaseRenderer.ymin(), y1); 571 int32 end = min_c(fBaseRenderer.ymax(), y2); 572 uint8* handle = dst + i * bpr; 573 for (; i <= end; i++) { 574 *(uint32*)handle = color.data32; 575 handle += bpr; 576 } 577 } 578 } while (fBaseRenderer.next_clip_box()); 579 580 return true; 581 } 582 583 if (a.y == b.y) { 584 // horizontal 585 int32 y = (int32)a.y; 586 if (y < 0 || y >= (int32)fBuffer.height()) 587 return true; 588 589 uint8* dst = fBuffer.row_ptr(y); 590 int32 x1 = (int32)min_c(a.x, b.x); 591 int32 x2 = (int32)max_c(a.x, b.x); 592 pixel32 color; 593 color.data8[0] = c.blue; 594 color.data8[1] = c.green; 595 color.data8[2] = c.red; 596 color.data8[3] = 255; 597 // draw a line, iterate over clipping boxes 598 fBaseRenderer.first_clip_box(); 599 do { 600 if (fBaseRenderer.ymin() <= y && 601 fBaseRenderer.ymax() >= y) { 602 int32 i = max_c(fBaseRenderer.xmin(), x1); 603 int32 end = min_c(fBaseRenderer.xmax(), x2); 604 uint32* handle = (uint32*)(dst + i * 4); 605 for (; i <= end; i++) { 606 *handle++ = color.data32; 607 } 608 } 609 } while (fBaseRenderer.next_clip_box()); 610 611 return true; 612 } 613 return false; 614 } 615 616 617 // #pragma mark - 618 619 620 // StrokeTriangle 621 BRect 622 Painter::StrokeTriangle(BPoint pt1, BPoint pt2, BPoint pt3) const 623 { 624 return _DrawTriangle(pt1, pt2, pt3, false); 625 } 626 627 628 // FillTriangle 629 BRect 630 Painter::FillTriangle(BPoint pt1, BPoint pt2, BPoint pt3) const 631 { 632 return _DrawTriangle(pt1, pt2, pt3, true); 633 } 634 635 636 // FillTriangle 637 BRect 638 Painter::FillTriangle(BPoint pt1, BPoint pt2, BPoint pt3, 639 const BGradient& gradient) const 640 { 641 CHECK_CLIPPING 642 643 _Align(&pt1); 644 _Align(&pt2); 645 _Align(&pt3); 646 647 fPath.remove_all(); 648 649 fPath.move_to(pt1.x, pt1.y); 650 fPath.line_to(pt2.x, pt2.y); 651 fPath.line_to(pt3.x, pt3.y); 652 653 fPath.close_polygon(); 654 655 return _FillPath(fPath, gradient); 656 } 657 658 659 // DrawPolygon 660 BRect 661 Painter::DrawPolygon(BPoint* p, int32 numPts, bool filled, bool closed) const 662 { 663 CHECK_CLIPPING 664 665 if (numPts == 0) 666 return BRect(0.0, 0.0, -1.0, -1.0); 667 668 bool centerOffset = !filled && fIdentityTransform 669 && fmodf(fPenSize, 2.0) != 0.0; 670 671 fPath.remove_all(); 672 673 _Align(p, centerOffset); 674 fPath.move_to(p->x, p->y); 675 676 for (int32 i = 1; i < numPts; i++) { 677 p++; 678 _Align(p, centerOffset); 679 fPath.line_to(p->x, p->y); 680 } 681 682 if (closed) 683 fPath.close_polygon(); 684 685 if (filled) 686 return _FillPath(fPath); 687 688 return _StrokePath(fPath); 689 } 690 691 692 // FillPolygon 693 BRect 694 Painter::FillPolygon(BPoint* p, int32 numPts, const BGradient& gradient, 695 bool closed) const 696 { 697 CHECK_CLIPPING 698 699 if (numPts > 0) { 700 fPath.remove_all(); 701 702 _Align(p); 703 fPath.move_to(p->x, p->y); 704 705 for (int32 i = 1; i < numPts; i++) { 706 p++; 707 _Align(p); 708 fPath.line_to(p->x, p->y); 709 } 710 711 if (closed) 712 fPath.close_polygon(); 713 714 return _FillPath(fPath, gradient); 715 } 716 return BRect(0.0, 0.0, -1.0, -1.0); 717 } 718 719 720 // DrawBezier 721 BRect 722 Painter::DrawBezier(BPoint* p, bool filled) const 723 { 724 CHECK_CLIPPING 725 726 fPath.remove_all(); 727 728 _Align(&(p[0])); 729 _Align(&(p[1])); 730 _Align(&(p[2])); 731 _Align(&(p[3])); 732 733 fPath.move_to(p[0].x, p[0].y); 734 fPath.curve4(p[1].x, p[1].y, p[2].x, p[2].y, p[3].x, p[3].y); 735 736 if (filled) { 737 fPath.close_polygon(); 738 return _FillPath(fCurve); 739 } 740 741 return _StrokePath(fCurve); 742 } 743 744 745 // FillBezier 746 BRect 747 Painter::FillBezier(BPoint* p, const BGradient& gradient) const 748 { 749 CHECK_CLIPPING 750 751 fPath.remove_all(); 752 753 _Align(&(p[0])); 754 _Align(&(p[1])); 755 _Align(&(p[2])); 756 _Align(&(p[3])); 757 758 fPath.move_to(p[0].x, p[0].y); 759 fPath.curve4(p[1].x, p[1].y, p[2].x, p[2].y, p[3].x, p[3].y); 760 761 fPath.close_polygon(); 762 return _FillPath(fCurve, gradient); 763 } 764 765 766 // DrawShape 767 BRect 768 Painter::DrawShape(const int32& opCount, const uint32* opList, 769 const int32& ptCount, const BPoint* points, bool filled, 770 const BPoint& viewToScreenOffset, float viewScale) const 771 { 772 CHECK_CLIPPING 773 774 _IterateShapeData(opCount, opList, ptCount, points, viewToScreenOffset, 775 viewScale); 776 777 if (filled) 778 return _FillPath(fCurve); 779 780 return _StrokePath(fCurve); 781 } 782 783 784 // FillShape 785 BRect 786 Painter::FillShape(const int32& opCount, const uint32* opList, 787 const int32& ptCount, const BPoint* points, const BGradient& gradient, 788 const BPoint& viewToScreenOffset, float viewScale) const 789 { 790 CHECK_CLIPPING 791 792 _IterateShapeData(opCount, opList, ptCount, points, viewToScreenOffset, 793 viewScale); 794 795 return _FillPath(fCurve, gradient); 796 } 797 798 799 // StrokeRect 800 BRect 801 Painter::StrokeRect(const BRect& r) const 802 { 803 CHECK_CLIPPING 804 805 BPoint a(r.left, r.top); 806 BPoint b(r.right, r.bottom); 807 _Align(&a, false); 808 _Align(&b, false); 809 810 // first, try an optimized version 811 if (fPenSize == 1.0 && fIdentityTransform 812 && (fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER) 813 && fMaskedUnpackedScanline == NULL) { 814 pattern p = *fPatternHandler.GetR5Pattern(); 815 if (p == B_SOLID_HIGH) { 816 BRect rect(a, b); 817 StrokeRect(rect, fPatternHandler.HighColor()); 818 return _Clipped(rect); 819 } else if (p == B_SOLID_LOW) { 820 BRect rect(a, b); 821 StrokeRect(rect, fPatternHandler.LowColor()); 822 return _Clipped(rect); 823 } 824 } 825 826 if (fIdentityTransform && fmodf(fPenSize, 2.0) != 0.0) { 827 // shift coords to center of pixels 828 a.x += 0.5; 829 a.y += 0.5; 830 b.x += 0.5; 831 b.y += 0.5; 832 } 833 834 fPath.remove_all(); 835 fPath.move_to(a.x, a.y); 836 if (a.x == b.x || a.y == b.y) { 837 // special case rects with one pixel height or width 838 fPath.line_to(b.x, b.y); 839 } else { 840 fPath.line_to(b.x, a.y); 841 fPath.line_to(b.x, b.y); 842 fPath.line_to(a.x, b.y); 843 } 844 fPath.close_polygon(); 845 846 return _StrokePath(fPath); 847 } 848 849 850 // StrokeRect 851 void 852 Painter::StrokeRect(const BRect& r, const rgb_color& c) const 853 { 854 StraightLine(BPoint(r.left, r.top), BPoint(r.right - 1, r.top), c); 855 StraightLine(BPoint(r.right, r.top), BPoint(r.right, r.bottom - 1), c); 856 StraightLine(BPoint(r.right, r.bottom), BPoint(r.left + 1, r.bottom), c); 857 StraightLine(BPoint(r.left, r.bottom), BPoint(r.left, r.top + 1), c); 858 } 859 860 861 // FillRect 862 BRect 863 Painter::FillRect(const BRect& r) const 864 { 865 CHECK_CLIPPING 866 867 // support invalid rects 868 BPoint a(min_c(r.left, r.right), min_c(r.top, r.bottom)); 869 BPoint b(max_c(r.left, r.right), max_c(r.top, r.bottom)); 870 _Align(&a, true, false); 871 _Align(&b, true, false); 872 873 // first, try an optimized version 874 if ((fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER) 875 && fMaskedUnpackedScanline == NULL && fIdentityTransform) { 876 pattern p = *fPatternHandler.GetR5Pattern(); 877 if (p == B_SOLID_HIGH) { 878 BRect rect(a, b); 879 FillRect(rect, fPatternHandler.HighColor()); 880 return _Clipped(rect); 881 } else if (p == B_SOLID_LOW) { 882 BRect rect(a, b); 883 FillRect(rect, fPatternHandler.LowColor()); 884 return _Clipped(rect); 885 } 886 } 887 if (fDrawingMode == B_OP_ALPHA && fAlphaFncMode == B_ALPHA_OVERLAY 888 && fMaskedUnpackedScanline == NULL && fIdentityTransform) { 889 pattern p = *fPatternHandler.GetR5Pattern(); 890 if (p == B_SOLID_HIGH) { 891 BRect rect(a, b); 892 _BlendRect32(rect, fPatternHandler.HighColor()); 893 return _Clipped(rect); 894 } else if (p == B_SOLID_LOW) { 895 rgb_color c = fPatternHandler.LowColor(); 896 if (fAlphaSrcMode == B_CONSTANT_ALPHA) 897 c.alpha = fPatternHandler.HighColor().alpha; 898 BRect rect(a, b); 899 _BlendRect32(rect, c); 900 return _Clipped(rect); 901 } 902 } 903 904 // account for stricter interpretation of coordinates in AGG 905 // the rectangle ranges from the top-left (.0, .0) 906 // to the bottom-right (.9999, .9999) corner of pixels 907 b.x += 1.0; 908 b.y += 1.0; 909 910 fPath.remove_all(); 911 fPath.move_to(a.x, a.y); 912 fPath.line_to(b.x, a.y); 913 fPath.line_to(b.x, b.y); 914 fPath.line_to(a.x, b.y); 915 fPath.close_polygon(); 916 917 return _FillPath(fPath); 918 } 919 920 921 // FillRect 922 BRect 923 Painter::FillRect(const BRect& r, const BGradient& gradient) const 924 { 925 CHECK_CLIPPING 926 927 // support invalid rects 928 BPoint a(min_c(r.left, r.right), min_c(r.top, r.bottom)); 929 BPoint b(max_c(r.left, r.right), max_c(r.top, r.bottom)); 930 _Align(&a, true, false); 931 _Align(&b, true, false); 932 933 // first, try an optimized version 934 if (gradient.GetType() == BGradient::TYPE_LINEAR 935 && (fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER) 936 && fMaskedUnpackedScanline == NULL && fIdentityTransform) { 937 const BGradientLinear* linearGradient 938 = dynamic_cast<const BGradientLinear*>(&gradient); 939 if (linearGradient->Start().x == linearGradient->End().x 940 // TODO: Remove this second check once the optimized method 941 // handled "upside down" gradients as well... 942 && linearGradient->Start().y <= linearGradient->End().y) { 943 // a vertical gradient 944 BRect rect(a, b); 945 FillRectVerticalGradient(rect, *linearGradient); 946 return _Clipped(rect); 947 } 948 } 949 950 // account for stricter interpretation of coordinates in AGG 951 // the rectangle ranges from the top-left (.0, .0) 952 // to the bottom-right (.9999, .9999) corner of pixels 953 b.x += 1.0; 954 b.y += 1.0; 955 956 fPath.remove_all(); 957 fPath.move_to(a.x, a.y); 958 fPath.line_to(b.x, a.y); 959 fPath.line_to(b.x, b.y); 960 fPath.line_to(a.x, b.y); 961 fPath.close_polygon(); 962 963 return _FillPath(fPath, gradient); 964 } 965 966 967 // FillRect 968 void 969 Painter::FillRect(const BRect& r, const rgb_color& c) const 970 { 971 if (!fValidClipping) 972 return; 973 974 uint8* dst = fBuffer.row_ptr(0); 975 uint32 bpr = fBuffer.stride(); 976 int32 left = (int32)r.left; 977 int32 top = (int32)r.top; 978 int32 right = (int32)r.right; 979 int32 bottom = (int32)r.bottom; 980 // get a 32 bit pixel ready with the color 981 pixel32 color; 982 color.data8[0] = c.blue; 983 color.data8[1] = c.green; 984 color.data8[2] = c.red; 985 color.data8[3] = c.alpha; 986 // fill rects, iterate over clipping boxes 987 fBaseRenderer.first_clip_box(); 988 do { 989 int32 x1 = max_c(fBaseRenderer.xmin(), left); 990 int32 x2 = min_c(fBaseRenderer.xmax(), right); 991 if (x1 <= x2) { 992 int32 y1 = max_c(fBaseRenderer.ymin(), top); 993 int32 y2 = min_c(fBaseRenderer.ymax(), bottom); 994 uint8* offset = dst + x1 * 4; 995 for (; y1 <= y2; y1++) { 996 // uint32* handle = (uint32*)(offset + y1 * bpr); 997 // for (int32 x = x1; x <= x2; x++) { 998 // *handle++ = color.data32; 999 // } 1000 gfxset32(offset + y1 * bpr, color.data32, (x2 - x1 + 1) * 4); 1001 } 1002 } 1003 } while (fBaseRenderer.next_clip_box()); 1004 } 1005 1006 1007 // FillRectVerticalGradient 1008 void 1009 Painter::FillRectVerticalGradient(BRect r, 1010 const BGradientLinear& gradient) const 1011 { 1012 if (!fValidClipping) 1013 return; 1014 1015 // Make sure the color array is no larger than the screen height. 1016 r = r & fClippingRegion->Frame(); 1017 1018 int32 gradientArraySize = r.IntegerHeight() + 1; 1019 uint32 gradientArray[gradientArraySize]; 1020 int32 gradientTop = (int32)gradient.Start().y; 1021 int32 gradientBottom = (int32)gradient.End().y; 1022 int32 colorCount = gradientBottom - gradientTop + 1; 1023 if (colorCount < 0) { 1024 // Gradient is upside down. That's currently not supported by this 1025 // method. 1026 return; 1027 } 1028 1029 _MakeGradient(gradient, colorCount, gradientArray, 1030 gradientTop - (int32)r.top, gradientArraySize); 1031 1032 uint8* dst = fBuffer.row_ptr(0); 1033 uint32 bpr = fBuffer.stride(); 1034 int32 left = (int32)r.left; 1035 int32 top = (int32)r.top; 1036 int32 right = (int32)r.right; 1037 int32 bottom = (int32)r.bottom; 1038 // fill rects, iterate over clipping boxes 1039 fBaseRenderer.first_clip_box(); 1040 do { 1041 int32 x1 = max_c(fBaseRenderer.xmin(), left); 1042 int32 x2 = min_c(fBaseRenderer.xmax(), right); 1043 if (x1 <= x2) { 1044 int32 y1 = max_c(fBaseRenderer.ymin(), top); 1045 int32 y2 = min_c(fBaseRenderer.ymax(), bottom); 1046 uint8* offset = dst + x1 * 4; 1047 for (; y1 <= y2; y1++) { 1048 // uint32* handle = (uint32*)(offset + y1 * bpr); 1049 // for (int32 x = x1; x <= x2; x++) { 1050 // *handle++ = gradientArray[y1 - top]; 1051 // } 1052 gfxset32(offset + y1 * bpr, gradientArray[y1 - top], 1053 (x2 - x1 + 1) * 4); 1054 } 1055 } 1056 } while (fBaseRenderer.next_clip_box()); 1057 } 1058 1059 1060 // FillRectNoClipping 1061 void 1062 Painter::FillRectNoClipping(const clipping_rect& r, const rgb_color& c) const 1063 { 1064 int32 y = (int32)r.top; 1065 1066 uint8* dst = fBuffer.row_ptr(y) + r.left * 4; 1067 uint32 bpr = fBuffer.stride(); 1068 int32 bytes = (r.right - r.left + 1) * 4; 1069 1070 // get a 32 bit pixel ready with the color 1071 pixel32 color; 1072 color.data8[0] = c.blue; 1073 color.data8[1] = c.green; 1074 color.data8[2] = c.red; 1075 color.data8[3] = c.alpha; 1076 1077 for (; y <= r.bottom; y++) { 1078 // uint32* handle = (uint32*)dst; 1079 // for (int32 x = left; x <= right; x++) { 1080 // *handle++ = color.data32; 1081 // } 1082 gfxset32(dst, color.data32, bytes); 1083 dst += bpr; 1084 } 1085 } 1086 1087 1088 // StrokeRoundRect 1089 BRect 1090 Painter::StrokeRoundRect(const BRect& r, float xRadius, float yRadius) const 1091 { 1092 CHECK_CLIPPING 1093 1094 BPoint lt(r.left, r.top); 1095 BPoint rb(r.right, r.bottom); 1096 bool centerOffset = fmodf(fPenSize, 2.0) != 0.0; 1097 _Align(<, centerOffset); 1098 _Align(&rb, centerOffset); 1099 1100 agg::rounded_rect rect; 1101 rect.rect(lt.x, lt.y, rb.x, rb.y); 1102 rect.radius(xRadius, yRadius); 1103 1104 return _StrokePath(rect); 1105 } 1106 1107 1108 // FillRoundRect 1109 BRect 1110 Painter::FillRoundRect(const BRect& r, float xRadius, float yRadius) const 1111 { 1112 CHECK_CLIPPING 1113 1114 BPoint lt(r.left, r.top); 1115 BPoint rb(r.right, r.bottom); 1116 _Align(<, false); 1117 _Align(&rb, false); 1118 1119 // account for stricter interpretation of coordinates in AGG 1120 // the rectangle ranges from the top-left (.0, .0) 1121 // to the bottom-right (.9999, .9999) corner of pixels 1122 rb.x += 1.0; 1123 rb.y += 1.0; 1124 1125 agg::rounded_rect rect; 1126 rect.rect(lt.x, lt.y, rb.x, rb.y); 1127 rect.radius(xRadius, yRadius); 1128 1129 return _FillPath(rect); 1130 } 1131 1132 1133 // FillRoundRect 1134 BRect 1135 Painter::FillRoundRect(const BRect& r, float xRadius, float yRadius, 1136 const BGradient& gradient) const 1137 { 1138 CHECK_CLIPPING 1139 1140 BPoint lt(r.left, r.top); 1141 BPoint rb(r.right, r.bottom); 1142 _Align(<, false); 1143 _Align(&rb, false); 1144 1145 // account for stricter interpretation of coordinates in AGG 1146 // the rectangle ranges from the top-left (.0, .0) 1147 // to the bottom-right (.9999, .9999) corner of pixels 1148 rb.x += 1.0; 1149 rb.y += 1.0; 1150 1151 agg::rounded_rect rect; 1152 rect.rect(lt.x, lt.y, rb.x, rb.y); 1153 rect.radius(xRadius, yRadius); 1154 1155 return _FillPath(rect, gradient); 1156 } 1157 1158 1159 // AlignEllipseRect 1160 void 1161 Painter::AlignEllipseRect(BRect* rect, bool filled) const 1162 { 1163 if (!fSubpixelPrecise) { 1164 // align rect to pixels 1165 align_rect_to_pixels(rect); 1166 // account for "pixel index" versus "pixel area" 1167 rect->right++; 1168 rect->bottom++; 1169 if (!filled && fmodf(fPenSize, 2.0) != 0.0) { 1170 // align the stroke 1171 rect->InsetBy(0.5, 0.5); 1172 } 1173 } 1174 } 1175 1176 1177 // DrawEllipse 1178 BRect 1179 Painter::DrawEllipse(BRect r, bool fill) const 1180 { 1181 CHECK_CLIPPING 1182 1183 AlignEllipseRect(&r, fill); 1184 1185 float xRadius = r.Width() / 2.0; 1186 float yRadius = r.Height() / 2.0; 1187 BPoint center(r.left + xRadius, r.top + yRadius); 1188 1189 int32 divisions = (int32)((xRadius + yRadius + 2 * fPenSize) * M_PI / 2); 1190 if (divisions < 12) 1191 divisions = 12; 1192 if (divisions > 4096) 1193 divisions = 4096; 1194 1195 agg::ellipse path(center.x, center.y, xRadius, yRadius, divisions); 1196 1197 if (fill) 1198 return _FillPath(path); 1199 else 1200 return _StrokePath(path); 1201 } 1202 1203 1204 // FillEllipse 1205 BRect 1206 Painter::FillEllipse(BRect r, const BGradient& gradient) const 1207 { 1208 CHECK_CLIPPING 1209 1210 AlignEllipseRect(&r, true); 1211 1212 float xRadius = r.Width() / 2.0; 1213 float yRadius = r.Height() / 2.0; 1214 BPoint center(r.left + xRadius, r.top + yRadius); 1215 1216 int32 divisions = (int32)((xRadius + yRadius + 2 * fPenSize) * M_PI / 2); 1217 if (divisions < 12) 1218 divisions = 12; 1219 if (divisions > 4096) 1220 divisions = 4096; 1221 1222 agg::ellipse path(center.x, center.y, xRadius, yRadius, divisions); 1223 1224 return _FillPath(path, gradient); 1225 } 1226 1227 1228 // StrokeArc 1229 BRect 1230 Painter::StrokeArc(BPoint center, float xRadius, float yRadius, float angle, 1231 float span) const 1232 { 1233 CHECK_CLIPPING 1234 1235 _Align(¢er); 1236 1237 double angleRad = (angle * M_PI) / 180.0; 1238 double spanRad = (span * M_PI) / 180.0; 1239 agg::bezier_arc arc(center.x, center.y, xRadius, yRadius, -angleRad, 1240 -spanRad); 1241 1242 agg::conv_curve<agg::bezier_arc> path(arc); 1243 path.approximation_scale(2.0); 1244 1245 return _StrokePath(path); 1246 } 1247 1248 1249 // FillArc 1250 BRect 1251 Painter::FillArc(BPoint center, float xRadius, float yRadius, float angle, 1252 float span) const 1253 { 1254 CHECK_CLIPPING 1255 1256 _Align(¢er); 1257 1258 double angleRad = (angle * M_PI) / 180.0; 1259 double spanRad = (span * M_PI) / 180.0; 1260 agg::bezier_arc arc(center.x, center.y, xRadius, yRadius, -angleRad, 1261 -spanRad); 1262 1263 agg::conv_curve<agg::bezier_arc> segmentedArc(arc); 1264 1265 fPath.remove_all(); 1266 1267 // build a new path by starting at the center point, 1268 // then traversing the arc, then going back to the center 1269 fPath.move_to(center.x, center.y); 1270 1271 segmentedArc.rewind(0); 1272 double x; 1273 double y; 1274 unsigned cmd = segmentedArc.vertex(&x, &y); 1275 while (!agg::is_stop(cmd)) { 1276 fPath.line_to(x, y); 1277 cmd = segmentedArc.vertex(&x, &y); 1278 } 1279 1280 fPath.close_polygon(); 1281 1282 return _FillPath(fPath); 1283 } 1284 1285 1286 // FillArc 1287 BRect 1288 Painter::FillArc(BPoint center, float xRadius, float yRadius, float angle, 1289 float span, const BGradient& gradient) const 1290 { 1291 CHECK_CLIPPING 1292 1293 _Align(¢er); 1294 1295 double angleRad = (angle * M_PI) / 180.0; 1296 double spanRad = (span * M_PI) / 180.0; 1297 agg::bezier_arc arc(center.x, center.y, xRadius, yRadius, -angleRad, 1298 -spanRad); 1299 1300 agg::conv_curve<agg::bezier_arc> segmentedArc(arc); 1301 1302 fPath.remove_all(); 1303 1304 // build a new path by starting at the center point, 1305 // then traversing the arc, then going back to the center 1306 fPath.move_to(center.x, center.y); 1307 1308 segmentedArc.rewind(0); 1309 double x; 1310 double y; 1311 unsigned cmd = segmentedArc.vertex(&x, &y); 1312 while (!agg::is_stop(cmd)) { 1313 fPath.line_to(x, y); 1314 cmd = segmentedArc.vertex(&x, &y); 1315 } 1316 1317 fPath.close_polygon(); 1318 1319 return _FillPath(fPath, gradient); 1320 } 1321 1322 1323 // #pragma mark - 1324 1325 1326 // DrawString 1327 BRect 1328 Painter::DrawString(const char* utf8String, uint32 length, BPoint baseLine, 1329 const escapement_delta* delta, FontCacheReference* cacheReference) 1330 { 1331 CHECK_CLIPPING 1332 1333 if (!fSubpixelPrecise) { 1334 baseLine.x = roundf(baseLine.x); 1335 baseLine.y = roundf(baseLine.y); 1336 } 1337 1338 BRect bounds; 1339 1340 // text is not rendered with patterns, but we need to 1341 // make sure that the previous pattern is restored 1342 pattern oldPattern = *fPatternHandler.GetR5Pattern(); 1343 SetPattern(B_SOLID_HIGH, true); 1344 1345 bounds = fTextRenderer.RenderString(utf8String, length, 1346 baseLine, fClippingRegion->Frame(), false, NULL, delta, 1347 cacheReference); 1348 1349 SetPattern(oldPattern); 1350 1351 return _Clipped(bounds); 1352 } 1353 1354 1355 // DrawString 1356 BRect 1357 Painter::DrawString(const char* utf8String, uint32 length, 1358 const BPoint* offsets, FontCacheReference* cacheReference) 1359 { 1360 CHECK_CLIPPING 1361 1362 // TODO: Round offsets to device pixel grid if !fSubpixelPrecise? 1363 1364 BRect bounds; 1365 1366 // text is not rendered with patterns, but we need to 1367 // make sure that the previous pattern is restored 1368 pattern oldPattern = *fPatternHandler.GetR5Pattern(); 1369 SetPattern(B_SOLID_HIGH, true); 1370 1371 bounds = fTextRenderer.RenderString(utf8String, length, 1372 offsets, fClippingRegion->Frame(), false, NULL, 1373 cacheReference); 1374 1375 SetPattern(oldPattern); 1376 1377 return _Clipped(bounds); 1378 } 1379 1380 1381 // BoundingBox 1382 BRect 1383 Painter::BoundingBox(const char* utf8String, uint32 length, BPoint baseLine, 1384 BPoint* penLocation, const escapement_delta* delta, 1385 FontCacheReference* cacheReference) const 1386 { 1387 if (!fSubpixelPrecise) { 1388 baseLine.x = roundf(baseLine.x); 1389 baseLine.y = roundf(baseLine.y); 1390 } 1391 1392 static BRect dummy; 1393 return fTextRenderer.RenderString(utf8String, length, 1394 baseLine, dummy, true, penLocation, delta, cacheReference); 1395 } 1396 1397 1398 // BoundingBox 1399 BRect 1400 Painter::BoundingBox(const char* utf8String, uint32 length, 1401 const BPoint* offsets, BPoint* penLocation, 1402 FontCacheReference* cacheReference) const 1403 { 1404 // TODO: Round offsets to device pixel grid if !fSubpixelPrecise? 1405 1406 static BRect dummy; 1407 return fTextRenderer.RenderString(utf8String, length, 1408 offsets, dummy, true, penLocation, cacheReference); 1409 } 1410 1411 1412 // StringWidth 1413 float 1414 Painter::StringWidth(const char* utf8String, uint32 length, 1415 const escapement_delta* delta) 1416 { 1417 return Font().StringWidth(utf8String, length, delta); 1418 } 1419 1420 1421 // #pragma mark - 1422 1423 1424 // DrawBitmap 1425 BRect 1426 Painter::DrawBitmap(const ServerBitmap* bitmap, BRect bitmapRect, 1427 BRect viewRect, uint32 options) const 1428 { 1429 CHECK_CLIPPING 1430 1431 BRect touched = TransformAlignAndClipRect(viewRect); 1432 1433 if (bitmap && bitmap->IsValid() && touched.IsValid()) { 1434 // the native bitmap coordinate system 1435 BRect actualBitmapRect(bitmap->Bounds()); 1436 1437 TRACE("Painter::DrawBitmap()\n"); 1438 TRACE(" actualBitmapRect = (%.1f, %.1f) - (%.1f, %.1f)\n", 1439 actualBitmapRect.left, actualBitmapRect.top, 1440 actualBitmapRect.right, actualBitmapRect.bottom); 1441 TRACE(" bitmapRect = (%.1f, %.1f) - (%.1f, %.1f)\n", 1442 bitmapRect.left, bitmapRect.top, bitmapRect.right, 1443 bitmapRect.bottom); 1444 TRACE(" viewRect = (%.1f, %.1f) - (%.1f, %.1f)\n", 1445 viewRect.left, viewRect.top, viewRect.right, viewRect.bottom); 1446 1447 agg::rendering_buffer srcBuffer; 1448 srcBuffer.attach(bitmap->Bits(), bitmap->Width(), bitmap->Height(), 1449 bitmap->BytesPerRow()); 1450 1451 _DrawBitmap(srcBuffer, bitmap->ColorSpace(), actualBitmapRect, 1452 bitmapRect, viewRect, options); 1453 } 1454 return touched; 1455 } 1456 1457 1458 // #pragma mark - 1459 1460 1461 // FillRegion 1462 BRect 1463 Painter::FillRegion(const BRegion* region) const 1464 { 1465 CHECK_CLIPPING 1466 1467 BRegion copy(*region); 1468 int32 count = copy.CountRects(); 1469 BRect touched = FillRect(copy.RectAt(0)); 1470 for (int32 i = 1; i < count; i++) { 1471 touched = touched | FillRect(copy.RectAt(i)); 1472 } 1473 return touched; 1474 } 1475 1476 1477 // FillRegion 1478 BRect 1479 Painter::FillRegion(const BRegion* region, const BGradient& gradient) const 1480 { 1481 CHECK_CLIPPING 1482 1483 BRegion copy(*region); 1484 int32 count = copy.CountRects(); 1485 BRect touched = FillRect(copy.RectAt(0), gradient); 1486 for (int32 i = 1; i < count; i++) { 1487 touched = touched | FillRect(copy.RectAt(i), gradient); 1488 } 1489 return touched; 1490 } 1491 1492 1493 // InvertRect 1494 BRect 1495 Painter::InvertRect(const BRect& r) const 1496 { 1497 CHECK_CLIPPING 1498 1499 BRegion region(r); 1500 region.IntersectWith(fClippingRegion); 1501 1502 // implementation only for B_RGB32 at the moment 1503 int32 count = region.CountRects(); 1504 for (int32 i = 0; i < count; i++) 1505 _InvertRect32(region.RectAt(i)); 1506 1507 return _Clipped(r); 1508 } 1509 1510 1511 // #pragma mark - private 1512 1513 1514 inline float 1515 Painter::_Align(float coord, bool round, bool centerOffset) const 1516 { 1517 // rounding 1518 if (round) 1519 coord = (int32)coord; 1520 1521 // This code is supposed to move coordinates to the center of pixels, 1522 // as AGG considers (0,0) to be the "upper left corner" of a pixel, 1523 // but BViews are less strict on those details 1524 if (centerOffset) 1525 coord += 0.5; 1526 1527 return coord; 1528 } 1529 1530 1531 inline void 1532 Painter::_Align(BPoint* point, bool centerOffset) const 1533 { 1534 _Align(point, !fSubpixelPrecise, centerOffset); 1535 } 1536 1537 1538 inline void 1539 Painter::_Align(BPoint* point, bool round, bool centerOffset) const 1540 { 1541 point->x = _Align(point->x, round, centerOffset); 1542 point->y = _Align(point->y, round, centerOffset); 1543 } 1544 1545 1546 inline BPoint 1547 Painter::_Align(const BPoint& point, bool centerOffset) const 1548 { 1549 BPoint ret(point); 1550 _Align(&ret, centerOffset); 1551 return ret; 1552 } 1553 1554 1555 // _Clipped 1556 BRect 1557 Painter::_Clipped(const BRect& rect) const 1558 { 1559 if (rect.IsValid()) 1560 return BRect(rect & fClippingRegion->Frame()); 1561 1562 return BRect(rect); 1563 } 1564 1565 1566 // _UpdateDrawingMode 1567 void 1568 Painter::_UpdateDrawingMode(bool drawingText) 1569 { 1570 // The AGG renderers have their own color setting, however 1571 // almost all drawing mode classes ignore the color given 1572 // by the AGG renderer and use the colors from the PatternHandler 1573 // instead. If we have a B_SOLID_* pattern, we can actually use 1574 // the color in the renderer and special versions of drawing modes 1575 // that don't use PatternHandler and are more efficient. This 1576 // has been implemented for B_OP_COPY and a couple others (the 1577 // DrawingMode*Solid ones) as of now. The PixelFormat knows the 1578 // PatternHandler and makes its decision based on the pattern. 1579 // The last parameter to SetDrawingMode() is a special flag 1580 // for when Painter is used to draw text. In this case, another 1581 // special version of B_OP_COPY is used that acts like R5 in that 1582 // anti-aliased pixel are not rendered against the actual background 1583 // but the current low color instead. This way, the frame buffer 1584 // doesn't need to be read. 1585 // When a solid pattern is used, _SetRendererColor() 1586 // has to be called so that all internal colors in the renderes 1587 // are up to date for use by the solid drawing mode version. 1588 fPixelFormat.SetDrawingMode(fDrawingMode, fAlphaSrcMode, fAlphaFncMode, 1589 drawingText); 1590 if (drawingText) 1591 fPatternHandler.MakeOpCopyColorCache(); 1592 } 1593 1594 1595 // _SetRendererColor 1596 void 1597 Painter::_SetRendererColor(const rgb_color& color) const 1598 { 1599 fRenderer.color(agg::rgba(color.red / 255.0, color.green / 255.0, 1600 color.blue / 255.0, color.alpha / 255.0)); 1601 fSubpixRenderer.color(agg::rgba(color.red / 255.0, color.green / 255.0, 1602 color.blue / 255.0, color.alpha / 255.0)); 1603 // TODO: bitmap fonts not yet correctly setup in AGGTextRenderer 1604 // fRendererBin.color(agg::rgba(color.red / 255.0, color.green / 255.0, 1605 // color.blue / 255.0, color.alpha / 255.0)); 1606 } 1607 1608 1609 // #pragma mark - 1610 1611 1612 // _DrawTriangle 1613 inline BRect 1614 Painter::_DrawTriangle(BPoint pt1, BPoint pt2, BPoint pt3, bool fill) const 1615 { 1616 CHECK_CLIPPING 1617 1618 _Align(&pt1); 1619 _Align(&pt2); 1620 _Align(&pt3); 1621 1622 fPath.remove_all(); 1623 1624 fPath.move_to(pt1.x, pt1.y); 1625 fPath.line_to(pt2.x, pt2.y); 1626 fPath.line_to(pt3.x, pt3.y); 1627 1628 fPath.close_polygon(); 1629 1630 if (fill) 1631 return _FillPath(fPath); 1632 1633 return _StrokePath(fPath); 1634 } 1635 1636 1637 // copy_bitmap_row_cmap8_copy 1638 static inline void 1639 copy_bitmap_row_cmap8_copy(uint8* dst, const uint8* src, int32 numPixels, 1640 const rgb_color* colorMap) 1641 { 1642 uint32* d = (uint32*)dst; 1643 const uint8* s = src; 1644 while (numPixels--) { 1645 const rgb_color c = colorMap[*s++]; 1646 *d++ = (c.alpha << 24) | (c.red << 16) | (c.green << 8) | (c.blue); 1647 } 1648 } 1649 1650 1651 // copy_bitmap_row_cmap8_over 1652 static inline void 1653 copy_bitmap_row_cmap8_over(uint8* dst, const uint8* src, int32 numPixels, 1654 const rgb_color* colorMap) 1655 { 1656 uint32* d = (uint32*)dst; 1657 const uint8* s = src; 1658 while (numPixels--) { 1659 const rgb_color c = colorMap[*s++]; 1660 if (c.alpha) 1661 *d = (c.alpha << 24) | (c.red << 16) | (c.green << 8) | (c.blue); 1662 d++; 1663 } 1664 } 1665 1666 1667 // copy_bitmap_row_bgr32_copy 1668 static inline void 1669 copy_bitmap_row_bgr32_copy(uint8* dst, const uint8* src, int32 numPixels, 1670 const rgb_color* colorMap) 1671 { 1672 memcpy(dst, src, numPixels * 4); 1673 } 1674 1675 1676 // copy_bitmap_row_bgr32_over 1677 static inline void 1678 copy_bitmap_row_bgr32_over(uint8* dst, const uint8* src, int32 numPixels, 1679 const rgb_color* colorMap) 1680 { 1681 uint32* d = (uint32*)dst; 1682 uint32* s = (uint32*)src; 1683 while (numPixels--) { 1684 if (*s != B_TRANSPARENT_MAGIC_RGBA32) 1685 *(uint32*)d = *(uint32*)s; 1686 d++; 1687 s++; 1688 } 1689 } 1690 1691 1692 void 1693 Painter::_IterateShapeData(const int32& opCount, const uint32* opList, 1694 const int32& ptCount, const BPoint* points, 1695 const BPoint& viewToScreenOffset, float viewScale) const 1696 { 1697 // TODO: if shapes are ever used more heavily in Haiku, 1698 // it would be nice to use BShape data directly (write 1699 // an AGG "VertexSource" adaptor) 1700 fPath.remove_all(); 1701 for (int32 i = 0; i < opCount; i++) { 1702 uint32 op = opList[i] & 0xFF000000; 1703 if ((op & OP_MOVETO) != 0) { 1704 fPath.move_to( 1705 points->x * viewScale + viewToScreenOffset.x, 1706 points->y * viewScale + viewToScreenOffset.y); 1707 points++; 1708 } 1709 1710 if ((op & OP_LINETO) != 0) { 1711 int32 count = opList[i] & 0x00FFFFFF; 1712 while (count--) { 1713 fPath.line_to( 1714 points->x * viewScale + viewToScreenOffset.x, 1715 points->y * viewScale + viewToScreenOffset.y); 1716 points++; 1717 } 1718 } 1719 1720 if ((op & OP_BEZIERTO) != 0) { 1721 int32 count = opList[i] & 0x00FFFFFF; 1722 while (count) { 1723 fPath.curve4( 1724 points[0].x * viewScale + viewToScreenOffset.x, 1725 points[0].y * viewScale + viewToScreenOffset.y, 1726 points[1].x * viewScale + viewToScreenOffset.x, 1727 points[1].y * viewScale + viewToScreenOffset.y, 1728 points[2].x * viewScale + viewToScreenOffset.x, 1729 points[2].y * viewScale + viewToScreenOffset.y); 1730 points += 3; 1731 count -= 3; 1732 } 1733 } 1734 1735 if ((op & OP_LARGE_ARC_TO_CW) != 0 || (op & OP_LARGE_ARC_TO_CCW) != 0 1736 || (op & OP_SMALL_ARC_TO_CW) != 0 1737 || (op & OP_SMALL_ARC_TO_CCW) != 0) { 1738 int32 count = opList[i] & 0x00FFFFFF; 1739 while (count > 0) { 1740 fPath.arc_to( 1741 points[0].x * viewScale, 1742 points[0].y * viewScale, 1743 points[1].x, 1744 op & (OP_LARGE_ARC_TO_CW | OP_LARGE_ARC_TO_CCW), 1745 op & (OP_SMALL_ARC_TO_CW | OP_LARGE_ARC_TO_CW), 1746 points[2].x * viewScale + viewToScreenOffset.x, 1747 points[2].y * viewScale + viewToScreenOffset.y); 1748 points += 3; 1749 count -= 3; 1750 } 1751 } 1752 1753 if ((op & OP_CLOSE) != 0) 1754 fPath.close_polygon(); 1755 } 1756 } 1757 1758 1759 // copy_bitmap_row_bgr32_alpha 1760 static inline void 1761 copy_bitmap_row_bgr32_alpha(uint8* dst, const uint8* src, int32 numPixels, 1762 const rgb_color* colorMap) 1763 { 1764 uint32* d = (uint32*)dst; 1765 int32 bytes = numPixels * 4; 1766 uint8 buffer[bytes]; 1767 uint8* b = buffer; 1768 while (numPixels--) { 1769 if (src[3] == 255) { 1770 *(uint32*)b = *(uint32*)src; 1771 } else { 1772 *(uint32*)b = *d; 1773 b[0] = ((src[0] - b[0]) * src[3] + (b[0] << 8)) >> 8; 1774 b[1] = ((src[1] - b[1]) * src[3] + (b[1] << 8)) >> 8; 1775 b[2] = ((src[2] - b[2]) * src[3] + (b[2] << 8)) >> 8; 1776 } 1777 d++; 1778 b += 4; 1779 src += 4; 1780 } 1781 memcpy(dst, buffer, bytes); 1782 } 1783 1784 1785 // _TransparentMagicToAlpha 1786 template<typename sourcePixel> 1787 void 1788 Painter::_TransparentMagicToAlpha(sourcePixel* buffer, uint32 width, 1789 uint32 height, uint32 sourceBytesPerRow, sourcePixel transparentMagic, 1790 BBitmap* output) const 1791 { 1792 uint8* sourceRow = (uint8*)buffer; 1793 uint8* destRow = (uint8*)output->Bits(); 1794 uint32 destBytesPerRow = output->BytesPerRow(); 1795 1796 for (uint32 y = 0; y < height; y++) { 1797 sourcePixel* pixel = (sourcePixel*)sourceRow; 1798 uint32* destPixel = (uint32*)destRow; 1799 for (uint32 x = 0; x < width; x++, pixel++, destPixel++) { 1800 if (*pixel == transparentMagic) 1801 *destPixel &= 0x00ffffff; 1802 } 1803 1804 sourceRow += sourceBytesPerRow; 1805 destRow += destBytesPerRow; 1806 } 1807 } 1808 1809 1810 // _DrawBitmap 1811 void 1812 Painter::_DrawBitmap(agg::rendering_buffer& srcBuffer, color_space format, 1813 BRect actualBitmapRect, BRect bitmapRect, BRect viewRect, 1814 uint32 options) const 1815 { 1816 if (!fValidClipping 1817 || !bitmapRect.IsValid() || !bitmapRect.Intersects(actualBitmapRect) 1818 || !viewRect.IsValid()) { 1819 return; 1820 } 1821 1822 if (!fSubpixelPrecise) { 1823 align_rect_to_pixels(&bitmapRect); 1824 align_rect_to_pixels(&viewRect); 1825 } 1826 1827 TRACE("Painter::_DrawBitmap()\n"); 1828 TRACE(" bitmapRect = (%.1f, %.1f) - (%.1f, %.1f)\n", 1829 bitmapRect.left, bitmapRect.top, bitmapRect.right, bitmapRect.bottom); 1830 TRACE(" viewRect = (%.1f, %.1f) - (%.1f, %.1f)\n", 1831 viewRect.left, viewRect.top, viewRect.right, viewRect.bottom); 1832 1833 double xScale = (viewRect.Width() + 1) / (bitmapRect.Width() + 1); 1834 double yScale = (viewRect.Height() + 1) / (bitmapRect.Height() + 1); 1835 1836 if (xScale == 0.0 || yScale == 0.0) 1837 return; 1838 1839 // compensate for the lefttop offset the actualBitmapRect might have 1840 // actualBitmapRect has the right size, but put it at B_ORIGIN 1841 // bitmapRect is already in good coordinates 1842 actualBitmapRect.OffsetBy(-actualBitmapRect.left, -actualBitmapRect.top); 1843 1844 // constrain rect to passed bitmap bounds 1845 // and transfer the changes to the viewRect with the right scale 1846 if (bitmapRect.left < actualBitmapRect.left) { 1847 float diff = actualBitmapRect.left - bitmapRect.left; 1848 viewRect.left += diff * xScale; 1849 bitmapRect.left = actualBitmapRect.left; 1850 } 1851 if (bitmapRect.top < actualBitmapRect.top) { 1852 float diff = actualBitmapRect.top - bitmapRect.top; 1853 viewRect.top += diff * yScale; 1854 bitmapRect.top = actualBitmapRect.top; 1855 } 1856 if (bitmapRect.right > actualBitmapRect.right) { 1857 float diff = bitmapRect.right - actualBitmapRect.right; 1858 viewRect.right -= diff * xScale; 1859 bitmapRect.right = actualBitmapRect.right; 1860 } 1861 if (bitmapRect.bottom > actualBitmapRect.bottom) { 1862 float diff = bitmapRect.bottom - actualBitmapRect.bottom; 1863 viewRect.bottom -= diff * yScale; 1864 bitmapRect.bottom = actualBitmapRect.bottom; 1865 } 1866 1867 double xOffset = viewRect.left - bitmapRect.left; 1868 double yOffset = viewRect.top - bitmapRect.top; 1869 1870 // optimized code path for B_CMAP8 and no scale 1871 if (xScale == 1.0 && yScale == 1.0 && fIdentityTransform 1872 && fMaskedUnpackedScanline == NULL) { 1873 if (format == B_CMAP8) { 1874 if (fDrawingMode == B_OP_COPY) { 1875 _DrawBitmapNoScale32(copy_bitmap_row_cmap8_copy, 1, 1876 srcBuffer, (int32)xOffset, (int32)yOffset, viewRect); 1877 return; 1878 } 1879 if (fDrawingMode == B_OP_OVER) { 1880 _DrawBitmapNoScale32(copy_bitmap_row_cmap8_over, 1, 1881 srcBuffer, (int32)xOffset, (int32)yOffset, viewRect); 1882 return; 1883 } 1884 } else if (format == B_RGB32) { 1885 if (fDrawingMode == B_OP_OVER) { 1886 _DrawBitmapNoScale32(copy_bitmap_row_bgr32_over, 4, 1887 srcBuffer, (int32)xOffset, (int32)yOffset, viewRect); 1888 return; 1889 } 1890 } 1891 } 1892 1893 BBitmap* temp = NULL; 1894 ObjectDeleter<BBitmap> tempDeleter; 1895 1896 if ((format != B_RGBA32 && format != B_RGB32) 1897 || (format == B_RGB32 && fDrawingMode != B_OP_COPY 1898 #if 1 1899 // Enabling this would make the behavior compatible to BeOS, which 1900 // treats B_RGB32 bitmaps as B_RGB*A*32 bitmaps in B_OP_ALPHA - unlike in 1901 // all other drawing modes, where B_TRANSPARENT_MAGIC_RGBA32 is handled. 1902 // B_RGB32 bitmaps therefore don't draw correctly on BeOS if they actually 1903 // use this color, unless the alpha channel contains 255 for all other 1904 // pixels, which is inconsistent. 1905 && fDrawingMode != B_OP_ALPHA 1906 #endif 1907 )) { 1908 temp = new (nothrow) BBitmap(actualBitmapRect, B_BITMAP_NO_SERVER_LINK, 1909 B_RGBA32); 1910 if (temp == NULL) { 1911 fprintf(stderr, "Painter::_DrawBitmap() - " 1912 "out of memory for creating temporary conversion bitmap\n"); 1913 return; 1914 } 1915 1916 tempDeleter.SetTo(temp); 1917 1918 status_t err = temp->ImportBits(srcBuffer.buf(), 1919 srcBuffer.height() * srcBuffer.stride(), 1920 srcBuffer.stride(), 0, format); 1921 if (err < B_OK) { 1922 fprintf(stderr, "Painter::_DrawBitmap() - " 1923 "colorspace conversion failed: %s\n", strerror(err)); 1924 return; 1925 } 1926 1927 // the original bitmap might have had some of the 1928 // transaparent magic colors set that we now need to 1929 // make transparent in our RGBA32 bitmap again. 1930 switch (format) { 1931 case B_RGB32: 1932 _TransparentMagicToAlpha((uint32 *)srcBuffer.buf(), 1933 srcBuffer.width(), srcBuffer.height(), 1934 srcBuffer.stride(), B_TRANSPARENT_MAGIC_RGBA32, 1935 temp); 1936 break; 1937 1938 // TODO: not sure if this applies to B_RGBA15 too. It 1939 // should not because B_RGBA15 actually has an alpha 1940 // channel itself and it should have been preserved 1941 // when importing the bitmap. Maybe it applies to 1942 // B_RGB16 though? 1943 case B_RGB15: 1944 _TransparentMagicToAlpha((uint16 *)srcBuffer.buf(), 1945 srcBuffer.width(), srcBuffer.height(), 1946 srcBuffer.stride(), B_TRANSPARENT_MAGIC_RGBA15, 1947 temp); 1948 break; 1949 1950 default: 1951 break; 1952 } 1953 1954 srcBuffer.attach((uint8*)temp->Bits(), 1955 (uint32)actualBitmapRect.IntegerWidth() + 1, 1956 (uint32)actualBitmapRect.IntegerHeight() + 1, 1957 temp->BytesPerRow()); 1958 } 1959 1960 // maybe we can use an optimized version if there is no scale 1961 if (xScale == 1.0 && yScale == 1.0 && fIdentityTransform 1962 && fMaskedUnpackedScanline == NULL) { 1963 if (fDrawingMode == B_OP_COPY) { 1964 _DrawBitmapNoScale32(copy_bitmap_row_bgr32_copy, 4, srcBuffer, 1965 (int32)xOffset, (int32)yOffset, viewRect); 1966 return; 1967 } 1968 if (fDrawingMode == B_OP_OVER || (fDrawingMode == B_OP_ALPHA 1969 && fAlphaSrcMode == B_PIXEL_ALPHA 1970 && fAlphaFncMode == B_ALPHA_OVERLAY)) { 1971 _DrawBitmapNoScale32(copy_bitmap_row_bgr32_alpha, 4, srcBuffer, 1972 (int32)xOffset, (int32)yOffset, viewRect); 1973 return; 1974 } 1975 } 1976 1977 if (fDrawingMode == B_OP_COPY && fIdentityTransform 1978 && fMaskedUnpackedScanline == NULL) { 1979 if ((options & B_FILTER_BITMAP_BILINEAR) != 0) { 1980 _DrawBitmapBilinearCopy32(srcBuffer, xOffset, yOffset, xScale, 1981 yScale, viewRect); 1982 } else { 1983 _DrawBitmapNearestNeighborCopy32(srcBuffer, xOffset, yOffset, 1984 xScale, yScale, viewRect); 1985 } 1986 return; 1987 } 1988 1989 // for all other cases (non-optimized drawing mode or scaled drawing) 1990 _DrawBitmapGeneric32(srcBuffer, xOffset, yOffset, xScale, yScale, viewRect, 1991 options); 1992 } 1993 1994 1995 #define DEBUG_DRAW_BITMAP 0 1996 1997 1998 // _DrawBitmapNoScale32 1999 template <class F> 2000 void 2001 Painter::_DrawBitmapNoScale32(F copyRowFunction, uint32 bytesPerSourcePixel, 2002 agg::rendering_buffer& srcBuffer, int32 xOffset, int32 yOffset, 2003 BRect viewRect) const 2004 { 2005 // NOTE: this would crash if viewRect was large enough to read outside the 2006 // bitmap, so make sure this is not the case before calling this function! 2007 uint8* dst = fBuffer.row_ptr(0); 2008 uint32 dstBPR = fBuffer.stride(); 2009 2010 const uint8* src = srcBuffer.row_ptr(0); 2011 uint32 srcBPR = srcBuffer.stride(); 2012 2013 int32 left = (int32)viewRect.left; 2014 int32 top = (int32)viewRect.top; 2015 int32 right = (int32)viewRect.right; 2016 int32 bottom = (int32)viewRect.bottom; 2017 2018 #if DEBUG_DRAW_BITMAP 2019 if (left - xOffset < 0 || left - xOffset >= (int32)srcBuffer.width() || 2020 right - xOffset >= (int32)srcBuffer.width() || 2021 top - yOffset < 0 || top - yOffset >= (int32)srcBuffer.height() || 2022 bottom - yOffset >= (int32)srcBuffer.height()) { 2023 2024 char message[256]; 2025 sprintf(message, "reading outside of bitmap (%ld, %ld, %ld, %ld) " 2026 "(%d, %d) (%ld, %ld)", 2027 left - xOffset, top - yOffset, right - xOffset, bottom - yOffset, 2028 srcBuffer.width(), srcBuffer.height(), xOffset, yOffset); 2029 debugger(message); 2030 } 2031 #endif 2032 2033 const rgb_color* colorMap = SystemPalette(); 2034 2035 // copy rects, iterate over clipping boxes 2036 fBaseRenderer.first_clip_box(); 2037 do { 2038 int32 x1 = max_c(fBaseRenderer.xmin(), left); 2039 int32 x2 = min_c(fBaseRenderer.xmax(), right); 2040 if (x1 <= x2) { 2041 int32 y1 = max_c(fBaseRenderer.ymin(), top); 2042 int32 y2 = min_c(fBaseRenderer.ymax(), bottom); 2043 if (y1 <= y2) { 2044 uint8* dstHandle = dst + y1 * dstBPR + x1 * 4; 2045 const uint8* srcHandle = src + (y1 - yOffset) * srcBPR 2046 + (x1 - xOffset) * bytesPerSourcePixel; 2047 2048 for (; y1 <= y2; y1++) { 2049 copyRowFunction(dstHandle, srcHandle, x2 - x1 + 1, colorMap); 2050 2051 dstHandle += dstBPR; 2052 srcHandle += srcBPR; 2053 } 2054 } 2055 } 2056 } while (fBaseRenderer.next_clip_box()); 2057 } 2058 2059 2060 // _DrawBitmapNearestNeighborCopy32 2061 void 2062 Painter::_DrawBitmapNearestNeighborCopy32(agg::rendering_buffer& srcBuffer, 2063 double xOffset, double yOffset, double xScale, double yScale, 2064 BRect viewRect) const 2065 { 2066 //bigtime_t now = system_time(); 2067 uint32 dstWidth = viewRect.IntegerWidth() + 1; 2068 uint32 dstHeight = viewRect.IntegerHeight() + 1; 2069 uint32 srcWidth = srcBuffer.width(); 2070 uint32 srcHeight = srcBuffer.height(); 2071 2072 // Do not calculate more filter weights than necessary and also 2073 // keep the stack based allocations reasonably sized 2074 if (fClippingRegion->Frame().IntegerWidth() + 1 < (int32)dstWidth) 2075 dstWidth = fClippingRegion->Frame().IntegerWidth() + 1; 2076 if (fClippingRegion->Frame().IntegerHeight() + 1 < (int32)dstHeight) 2077 dstHeight = fClippingRegion->Frame().IntegerHeight() + 1; 2078 2079 // When calculating less filter weights than specified by viewRect, 2080 // we need to compensate the offset. 2081 uint32 filterWeightXIndexOffset = 0; 2082 uint32 filterWeightYIndexOffset = 0; 2083 if (fClippingRegion->Frame().left > viewRect.left) { 2084 filterWeightXIndexOffset = (int32)(fClippingRegion->Frame().left 2085 - viewRect.left); 2086 } 2087 if (fClippingRegion->Frame().top > viewRect.top) { 2088 filterWeightYIndexOffset = (int32)(fClippingRegion->Frame().top 2089 - viewRect.top); 2090 } 2091 2092 // should not pose a problem with stack overflows 2093 // (needs around 6Kb for 1920x1200) 2094 uint16 xIndices[dstWidth]; 2095 uint16 yIndices[dstHeight]; 2096 2097 // Extract the cropping information for the source bitmap, 2098 // If only a part of the source bitmap is to be drawn with scale, 2099 // the offset will be different from the viewRect left top corner. 2100 int32 xBitmapShift = (int32)(viewRect.left - xOffset); 2101 int32 yBitmapShift = (int32)(viewRect.top - yOffset); 2102 2103 for (uint32 i = 0; i < dstWidth; i++) { 2104 // index into source 2105 uint16 index = (uint16)((i + filterWeightXIndexOffset) * srcWidth 2106 / (srcWidth * xScale)); 2107 // round down to get the left pixel 2108 xIndices[i] = index; 2109 // handle cropped source bitmap 2110 xIndices[i] += xBitmapShift; 2111 // precompute index for 32 bit pixels 2112 xIndices[i] *= 4; 2113 } 2114 2115 for (uint32 i = 0; i < dstHeight; i++) { 2116 // index into source 2117 uint16 index = (uint16)((i + filterWeightYIndexOffset) * srcHeight 2118 / (srcHeight * yScale)); 2119 // round down to get the top pixel 2120 yIndices[i] = index; 2121 // handle cropped source bitmap 2122 yIndices[i] += yBitmapShift; 2123 } 2124 //printf("X: %d ... %d, %d (%ld or %f)\n", 2125 // xIndices[0], xIndices[dstWidth - 2], xIndices[dstWidth - 1], dstWidth, 2126 // srcWidth * xScale); 2127 //printf("Y: %d ... %d, %d (%ld or %f)\n", 2128 // yIndices[0], yIndices[dstHeight - 2], yIndices[dstHeight - 1], dstHeight, 2129 // srcHeight * yScale); 2130 2131 const int32 left = (int32)viewRect.left; 2132 const int32 top = (int32)viewRect.top; 2133 const int32 right = (int32)viewRect.right; 2134 const int32 bottom = (int32)viewRect.bottom; 2135 2136 const uint32 dstBPR = fBuffer.stride(); 2137 2138 // iterate over clipping boxes 2139 fBaseRenderer.first_clip_box(); 2140 do { 2141 const int32 x1 = max_c(fBaseRenderer.xmin(), left); 2142 const int32 x2 = min_c(fBaseRenderer.xmax(), right); 2143 if (x1 > x2) 2144 continue; 2145 2146 int32 y1 = max_c(fBaseRenderer.ymin(), top); 2147 int32 y2 = min_c(fBaseRenderer.ymax(), bottom); 2148 if (y1 > y2) 2149 continue; 2150 2151 // buffer offset into destination 2152 uint8* dst = fBuffer.row_ptr(y1) + x1 * 4; 2153 2154 // x and y are needed as indeces into the wheight arrays, so the 2155 // offset into the target buffer needs to be compensated 2156 const int32 xIndexL = x1 - left - filterWeightXIndexOffset; 2157 const int32 xIndexR = x2 - left - filterWeightXIndexOffset; 2158 y1 -= top + filterWeightYIndexOffset; 2159 y2 -= top + filterWeightYIndexOffset; 2160 2161 //printf("x: %ld - %ld\n", xIndexL, xIndexR); 2162 //printf("y: %ld - %ld\n", y1, y2); 2163 2164 for (; y1 <= y2; y1++) { 2165 // buffer offset into source (top row) 2166 register const uint8* src = srcBuffer.row_ptr(yIndices[y1]); 2167 // buffer handle for destination to be incremented per pixel 2168 register uint32* d = (uint32*)dst; 2169 2170 for (int32 x = xIndexL; x <= xIndexR; x++) { 2171 *d = *(uint32*)(src + xIndices[x]); 2172 d++; 2173 } 2174 dst += dstBPR; 2175 } 2176 } while (fBaseRenderer.next_clip_box()); 2177 2178 //printf("draw bitmap %.5fx%.5f: %lld\n", xScale, yScale, system_time() - now); 2179 } 2180 2181 2182 // _DrawBitmapBilinearCopy32 2183 void 2184 Painter::_DrawBitmapBilinearCopy32(agg::rendering_buffer& srcBuffer, 2185 double xOffset, double yOffset, double xScale, double yScale, 2186 BRect viewRect) const 2187 { 2188 //bigtime_t now = system_time(); 2189 uint32 dstWidth = viewRect.IntegerWidth() + 1; 2190 uint32 dstHeight = viewRect.IntegerHeight() + 1; 2191 uint32 srcWidth = srcBuffer.width(); 2192 uint32 srcHeight = srcBuffer.height(); 2193 2194 // Do not calculate more filter weights than necessary and also 2195 // keep the stack based allocations reasonably sized 2196 if (fClippingRegion->Frame().IntegerWidth() + 1 < (int32)dstWidth) 2197 dstWidth = fClippingRegion->Frame().IntegerWidth() + 1; 2198 if (fClippingRegion->Frame().IntegerHeight() + 1 < (int32)dstHeight) 2199 dstHeight = fClippingRegion->Frame().IntegerHeight() + 1; 2200 2201 // When calculating less filter weights than specified by viewRect, 2202 // we need to compensate the offset. 2203 uint32 filterWeightXIndexOffset = 0; 2204 uint32 filterWeightYIndexOffset = 0; 2205 if (fClippingRegion->Frame().left > viewRect.left) { 2206 filterWeightXIndexOffset = (int32)(fClippingRegion->Frame().left 2207 - viewRect.left); 2208 } 2209 if (fClippingRegion->Frame().top > viewRect.top) { 2210 filterWeightYIndexOffset = (int32)(fClippingRegion->Frame().top 2211 - viewRect.top); 2212 } 2213 2214 struct FilterInfo { 2215 uint16 index; // index into source bitmap row/column 2216 uint16 weight; // weight of the pixel at index [0..255] 2217 }; 2218 2219 //#define FILTER_INFOS_ON_HEAP 2220 #ifdef FILTER_INFOS_ON_HEAP 2221 FilterInfo* xWeights = new (nothrow) FilterInfo[dstWidth]; 2222 FilterInfo* yWeights = new (nothrow) FilterInfo[dstHeight]; 2223 if (xWeights == NULL || yWeights == NULL) { 2224 delete[] xWeights; 2225 delete[] yWeights; 2226 return; 2227 } 2228 #else 2229 // stack based saves about 200µs on 1.85 GHz Core 2 Duo 2230 // should not pose a problem with stack overflows 2231 // (needs around 12Kb for 1920x1200) 2232 FilterInfo xWeights[dstWidth]; 2233 FilterInfo yWeights[dstHeight]; 2234 #endif 2235 2236 // Extract the cropping information for the source bitmap, 2237 // If only a part of the source bitmap is to be drawn with scale, 2238 // the offset will be different from the viewRect left top corner. 2239 int32 xBitmapShift = (int32)(viewRect.left - xOffset); 2240 int32 yBitmapShift = (int32)(viewRect.top - yOffset); 2241 2242 for (uint32 i = 0; i < dstWidth; i++) { 2243 // fractional index into source 2244 // NOTE: It is very important to calculate the fractional index 2245 // into the source pixel grid like this to prevent out of bounds 2246 // access! It will result in the rightmost pixel of the destination 2247 // to access the rightmost pixel of the source with a weighting 2248 // of 255. This in turn will trigger an optimization in the loop 2249 // that also prevents out of bounds access. 2250 float index = (i + filterWeightXIndexOffset) * (srcWidth - 1) 2251 / (srcWidth * xScale - 1); 2252 // round down to get the left pixel 2253 xWeights[i].index = (uint16)index; 2254 xWeights[i].weight = 255 - (uint16)((index - xWeights[i].index) * 255); 2255 // handle cropped source bitmap 2256 xWeights[i].index += xBitmapShift; 2257 // precompute index for 32 bit pixels 2258 xWeights[i].index *= 4; 2259 } 2260 2261 for (uint32 i = 0; i < dstHeight; i++) { 2262 // fractional index into source 2263 // NOTE: It is very important to calculate the fractional index 2264 // into the source pixel grid like this to prevent out of bounds 2265 // access! It will result in the bottommost pixel of the destination 2266 // to access the bottommost pixel of the source with a weighting 2267 // of 255. This in turn will trigger an optimization in the loop 2268 // that also prevents out of bounds access. 2269 float index = (i + filterWeightYIndexOffset) * (srcHeight - 1) 2270 / (srcHeight * yScale - 1); 2271 // round down to get the top pixel 2272 yWeights[i].index = (uint16)index; 2273 yWeights[i].weight = 255 - (uint16)((index - yWeights[i].index) * 255); 2274 // handle cropped source bitmap 2275 yWeights[i].index += yBitmapShift; 2276 } 2277 //printf("X: %d/%d ... %d/%d, %d/%d (%ld)\n", 2278 // xWeights[0].index, xWeights[0].weight, 2279 // xWeights[dstWidth - 2].index, xWeights[dstWidth - 2].weight, 2280 // xWeights[dstWidth - 1].index, xWeights[dstWidth - 1].weight, 2281 // dstWidth); 2282 //printf("Y: %d/%d ... %d/%d, %d/%d (%ld)\n", 2283 // yWeights[0].index, yWeights[0].weight, 2284 // yWeights[dstHeight - 2].index, yWeights[dstHeight - 2].weight, 2285 // yWeights[dstHeight - 1].index, yWeights[dstHeight - 1].weight, 2286 // dstHeight); 2287 2288 const int32 left = (int32)viewRect.left; 2289 const int32 top = (int32)viewRect.top; 2290 const int32 right = (int32)viewRect.right; 2291 const int32 bottom = (int32)viewRect.bottom; 2292 2293 const uint32 dstBPR = fBuffer.stride(); 2294 const uint32 srcBPR = srcBuffer.stride(); 2295 2296 // Figure out which version of the code we want to use... 2297 enum { 2298 kOptimizeForLowFilterRatio = 0, 2299 kUseDefaultVersion, 2300 kUseSIMDVersion 2301 }; 2302 2303 int codeSelect = kUseDefaultVersion; 2304 2305 uint32 neededSIMDFlags = APPSERVER_SIMD_MMX | APPSERVER_SIMD_SSE; 2306 if ((sSIMDFlags & neededSIMDFlags) == neededSIMDFlags) 2307 codeSelect = kUseSIMDVersion; 2308 else { 2309 if (xScale == yScale && (xScale == 1.5 || xScale == 2.0 2310 || xScale == 2.5 || xScale == 3.0)) { 2311 codeSelect = kOptimizeForLowFilterRatio; 2312 } 2313 } 2314 2315 // iterate over clipping boxes 2316 fBaseRenderer.first_clip_box(); 2317 do { 2318 const int32 x1 = max_c(fBaseRenderer.xmin(), left); 2319 const int32 x2 = min_c(fBaseRenderer.xmax(), right); 2320 if (x1 > x2) 2321 continue; 2322 2323 int32 y1 = max_c(fBaseRenderer.ymin(), top); 2324 int32 y2 = min_c(fBaseRenderer.ymax(), bottom); 2325 if (y1 > y2) 2326 continue; 2327 2328 // buffer offset into destination 2329 uint8* dst = fBuffer.row_ptr(y1) + x1 * 4; 2330 2331 // x and y are needed as indeces into the wheight arrays, so the 2332 // offset into the target buffer needs to be compensated 2333 const int32 xIndexL = x1 - left - filterWeightXIndexOffset; 2334 const int32 xIndexR = x2 - left - filterWeightXIndexOffset; 2335 y1 -= top + filterWeightYIndexOffset; 2336 y2 -= top + filterWeightYIndexOffset; 2337 2338 //printf("x: %ld - %ld\n", xIndexL, xIndexR); 2339 //printf("y: %ld - %ld\n", y1, y2); 2340 2341 switch (codeSelect) { 2342 case kOptimizeForLowFilterRatio: 2343 { 2344 // In this mode, we anticipate to hit many destination pixels 2345 // that map directly to a source pixel, we have more branches 2346 // in the inner loop but save time because of the special 2347 // cases. If there are too few direct hit pixels, the branches 2348 // only waste time. 2349 for (; y1 <= y2; y1++) { 2350 // cache the weight of the top and bottom row 2351 const uint16 wTop = yWeights[y1].weight; 2352 const uint16 wBottom = 255 - yWeights[y1].weight; 2353 2354 // buffer offset into source (top row) 2355 register const uint8* src 2356 = srcBuffer.row_ptr(yWeights[y1].index); 2357 // buffer handle for destination to be incremented per 2358 // pixel 2359 register uint8* d = dst; 2360 2361 if (wTop == 255) { 2362 for (int32 x = xIndexL; x <= xIndexR; x++) { 2363 const uint8* s = src + xWeights[x].index; 2364 // This case is important to prevent out 2365 // of bounds access at bottom edge of the source 2366 // bitmap. If the scale is low and integer, it will 2367 // also help the speed. 2368 if (xWeights[x].weight == 255) { 2369 // As above, but to prevent out of bounds 2370 // on the right edge. 2371 *(uint32*)d = *(uint32*)s; 2372 } else { 2373 // Only the left and right pixels are 2374 // interpolated, since the top row has 100% 2375 // weight. 2376 const uint16 wLeft = xWeights[x].weight; 2377 const uint16 wRight = 255 - wLeft; 2378 d[0] = (s[0] * wLeft + s[4] * wRight) >> 8; 2379 d[1] = (s[1] * wLeft + s[5] * wRight) >> 8; 2380 d[2] = (s[2] * wLeft + s[6] * wRight) >> 8; 2381 } 2382 d += 4; 2383 } 2384 } else { 2385 for (int32 x = xIndexL; x <= xIndexR; x++) { 2386 const uint8* s = src + xWeights[x].index; 2387 if (xWeights[x].weight == 255) { 2388 // Prevent out of bounds access on the right 2389 // edge or simply speed up. 2390 const uint8* sBottom = s + srcBPR; 2391 d[0] = (s[0] * wTop + sBottom[0] * wBottom) 2392 >> 8; 2393 d[1] = (s[1] * wTop + sBottom[1] * wBottom) 2394 >> 8; 2395 d[2] = (s[2] * wTop + sBottom[2] * wBottom) 2396 >> 8; 2397 } else { 2398 // calculate the weighted sum of all four 2399 // interpolated pixels 2400 const uint16 wLeft = xWeights[x].weight; 2401 const uint16 wRight = 255 - wLeft; 2402 // left and right of top row 2403 uint32 t0 = (s[0] * wLeft + s[4] * wRight) 2404 * wTop; 2405 uint32 t1 = (s[1] * wLeft + s[5] * wRight) 2406 * wTop; 2407 uint32 t2 = (s[2] * wLeft + s[6] * wRight) 2408 * wTop; 2409 2410 // left and right of bottom row 2411 s += srcBPR; 2412 t0 += (s[0] * wLeft + s[4] * wRight) * wBottom; 2413 t1 += (s[1] * wLeft + s[5] * wRight) * wBottom; 2414 t2 += (s[2] * wLeft + s[6] * wRight) * wBottom; 2415 2416 d[0] = t0 >> 16; 2417 d[1] = t1 >> 16; 2418 d[2] = t2 >> 16; 2419 } 2420 d += 4; 2421 } 2422 } 2423 dst += dstBPR; 2424 } 2425 break; 2426 } 2427 2428 case kUseDefaultVersion: 2429 { 2430 // In this mode we anticipate many pixels wich need filtering, 2431 // there are no special cases for direct hit pixels except for 2432 // the last column/row and the right/bottom corner pixel. 2433 2434 // The last column/row handling does not need to be performed 2435 // for all clipping rects! 2436 int32 yMax = y2; 2437 if (yWeights[yMax].weight == 255) 2438 yMax--; 2439 int32 xIndexMax = xIndexR; 2440 if (xWeights[xIndexMax].weight == 255) 2441 xIndexMax--; 2442 2443 for (; y1 <= yMax; y1++) { 2444 // cache the weight of the top and bottom row 2445 const uint16 wTop = yWeights[y1].weight; 2446 const uint16 wBottom = 255 - yWeights[y1].weight; 2447 2448 // buffer offset into source (top row) 2449 register const uint8* src 2450 = srcBuffer.row_ptr(yWeights[y1].index); 2451 // buffer handle for destination to be incremented per 2452 // pixel 2453 register uint8* d = dst; 2454 2455 for (int32 x = xIndexL; x <= xIndexMax; x++) { 2456 const uint8* s = src + xWeights[x].index; 2457 // calculate the weighted sum of all four 2458 // interpolated pixels 2459 const uint16 wLeft = xWeights[x].weight; 2460 const uint16 wRight = 255 - wLeft; 2461 // left and right of top row 2462 uint32 t0 = (s[0] * wLeft + s[4] * wRight) * wTop; 2463 uint32 t1 = (s[1] * wLeft + s[5] * wRight) * wTop; 2464 uint32 t2 = (s[2] * wLeft + s[6] * wRight) * wTop; 2465 2466 // left and right of bottom row 2467 s += srcBPR; 2468 t0 += (s[0] * wLeft + s[4] * wRight) * wBottom; 2469 t1 += (s[1] * wLeft + s[5] * wRight) * wBottom; 2470 t2 += (s[2] * wLeft + s[6] * wRight) * wBottom; 2471 d[0] = t0 >> 16; 2472 d[1] = t1 >> 16; 2473 d[2] = t2 >> 16; 2474 d += 4; 2475 } 2476 // last column of pixels if necessary 2477 if (xIndexMax < xIndexR) { 2478 const uint8* s = src + xWeights[xIndexR].index; 2479 const uint8* sBottom = s + srcBPR; 2480 d[0] = (s[0] * wTop + sBottom[0] * wBottom) >> 8; 2481 d[1] = (s[1] * wTop + sBottom[1] * wBottom) >> 8; 2482 d[2] = (s[2] * wTop + sBottom[2] * wBottom) >> 8; 2483 } 2484 2485 dst += dstBPR; 2486 } 2487 2488 // last row of pixels if necessary 2489 // buffer offset into source (bottom row) 2490 register const uint8* src 2491 = srcBuffer.row_ptr(yWeights[y2].index); 2492 // buffer handle for destination to be incremented per pixel 2493 register uint8* d = dst; 2494 2495 if (yMax < y2) { 2496 for (int32 x = xIndexL; x <= xIndexMax; x++) { 2497 const uint8* s = src + xWeights[x].index; 2498 const uint16 wLeft = xWeights[x].weight; 2499 const uint16 wRight = 255 - wLeft; 2500 d[0] = (s[0] * wLeft + s[4] * wRight) >> 8; 2501 d[1] = (s[1] * wLeft + s[5] * wRight) >> 8; 2502 d[2] = (s[2] * wLeft + s[6] * wRight) >> 8; 2503 d += 4; 2504 } 2505 } 2506 2507 // pixel in bottom right corner if necessary 2508 if (yMax < y2 && xIndexMax < xIndexR) { 2509 const uint8* s = src + xWeights[xIndexR].index; 2510 *(uint32*)d = *(uint32*)s; 2511 } 2512 break; 2513 } 2514 2515 #ifdef __INTEL__ 2516 case kUseSIMDVersion: 2517 { 2518 // Basically the same as the "standard" mode, but we use SIMD 2519 // routines for the processing of the single display lines. 2520 2521 // The last column/row handling does not need to be performed 2522 // for all clipping rects! 2523 int32 yMax = y2; 2524 if (yWeights[yMax].weight == 255) 2525 yMax--; 2526 int32 xIndexMax = xIndexR; 2527 if (xWeights[xIndexMax].weight == 255) 2528 xIndexMax--; 2529 2530 for (; y1 <= yMax; y1++) { 2531 // cache the weight of the top and bottom row 2532 const uint16 wTop = yWeights[y1].weight; 2533 const uint16 wBottom = 255 - yWeights[y1].weight; 2534 2535 // buffer offset into source (top row) 2536 const uint8* src = srcBuffer.row_ptr(yWeights[y1].index); 2537 // buffer handle for destination to be incremented per 2538 // pixel 2539 uint8* d = dst; 2540 bilinear_scale_xloop_mmxsse(src, dst, xWeights, xIndexL, 2541 xIndexMax, wTop, srcBPR); 2542 // increase pointer by processed pixels 2543 d += (xIndexMax - xIndexL + 1) * 4; 2544 2545 // last column of pixels if necessary 2546 if (xIndexMax < xIndexR) { 2547 const uint8* s = src + xWeights[xIndexR].index; 2548 const uint8* sBottom = s + srcBPR; 2549 d[0] = (s[0] * wTop + sBottom[0] * wBottom) >> 8; 2550 d[1] = (s[1] * wTop + sBottom[1] * wBottom) >> 8; 2551 d[2] = (s[2] * wTop + sBottom[2] * wBottom) >> 8; 2552 } 2553 2554 dst += dstBPR; 2555 } 2556 2557 // last row of pixels if necessary 2558 // buffer offset into source (bottom row) 2559 register const uint8* src 2560 = srcBuffer.row_ptr(yWeights[y2].index); 2561 // buffer handle for destination to be incremented per pixel 2562 register uint8* d = dst; 2563 2564 if (yMax < y2) { 2565 for (int32 x = xIndexL; x <= xIndexMax; x++) { 2566 const uint8* s = src + xWeights[x].index; 2567 const uint16 wLeft = xWeights[x].weight; 2568 const uint16 wRight = 255 - wLeft; 2569 d[0] = (s[0] * wLeft + s[4] * wRight) >> 8; 2570 d[1] = (s[1] * wLeft + s[5] * wRight) >> 8; 2571 d[2] = (s[2] * wLeft + s[6] * wRight) >> 8; 2572 d += 4; 2573 } 2574 } 2575 2576 // pixel in bottom right corner if necessary 2577 if (yMax < y2 && xIndexMax < xIndexR) { 2578 const uint8* s = src + xWeights[xIndexR].index; 2579 *(uint32*)d = *(uint32*)s; 2580 } 2581 break; 2582 } 2583 #endif // __INTEL__ 2584 } 2585 } while (fBaseRenderer.next_clip_box()); 2586 2587 #ifdef FILTER_INFOS_ON_HEAP 2588 delete[] xWeights; 2589 delete[] yWeights; 2590 #endif 2591 //printf("draw bitmap %.5fx%.5f: %lld\n", xScale, yScale, system_time() - now); 2592 } 2593 2594 2595 // _DrawBitmapGeneric32 2596 void 2597 Painter::_DrawBitmapGeneric32(agg::rendering_buffer& srcBuffer, 2598 double xOffset, double yOffset, double xScale, double yScale, 2599 BRect viewRect, uint32 options) const 2600 { 2601 TRACE("Painter::_DrawBitmapGeneric32()\n"); 2602 TRACE(" offset: %.1f, %.1f\n", xOffset, yOffset); 2603 TRACE(" scale: %.3f, %.3f\n", xScale, yScale); 2604 TRACE(" viewRect: (%.1f, %.1f) - (%.1f, %.1f)\n", 2605 viewRect.left, viewRect.top, viewRect.right, viewRect.bottom); 2606 // AGG pipeline 2607 2608 // pixel format attached to bitmap 2609 typedef agg::pixfmt_bgra32 pixfmt_image; 2610 pixfmt_image pixf_img(srcBuffer); 2611 2612 agg::trans_affine srcMatrix; 2613 // NOTE: R5 seems to ignore this offset when drawing bitmaps 2614 // srcMatrix *= agg::trans_affine_translation(-actualBitmapRect.left, 2615 // -actualBitmapRect.top); 2616 srcMatrix *= fTransform; 2617 2618 agg::trans_affine imgMatrix; 2619 imgMatrix *= agg::trans_affine_translation(xOffset - viewRect.left, 2620 yOffset - viewRect.top); 2621 imgMatrix *= agg::trans_affine_scaling(xScale, yScale); 2622 imgMatrix *= agg::trans_affine_translation(viewRect.left, viewRect.top); 2623 imgMatrix *= fTransform; 2624 imgMatrix.invert(); 2625 2626 // image interpolator 2627 typedef agg::span_interpolator_linear<> interpolator_type; 2628 interpolator_type interpolator(imgMatrix); 2629 2630 // scanline allocator 2631 agg::span_allocator<pixfmt_image::color_type> spanAllocator; 2632 2633 // image accessor attached to pixel format of bitmap 2634 typedef agg::image_accessor_clone<pixfmt_image> source_type; 2635 source_type source(pixf_img); 2636 2637 // clip to the current clipping region's frame 2638 if (fIdentityTransform) 2639 viewRect = viewRect & fClippingRegion->Frame(); 2640 // convert to pixel coords (versus pixel indices) 2641 viewRect.right++; 2642 viewRect.bottom++; 2643 2644 // path enclosing the bitmap 2645 fPath.remove_all(); 2646 fPath.move_to(viewRect.left, viewRect.top); 2647 fPath.line_to(viewRect.right, viewRect.top); 2648 fPath.line_to(viewRect.right, viewRect.bottom); 2649 fPath.line_to(viewRect.left, viewRect.bottom); 2650 fPath.close_polygon(); 2651 2652 agg::conv_transform<agg::path_storage> transformedPath(fPath, srcMatrix); 2653 fRasterizer.reset(); 2654 fRasterizer.add_path(transformedPath); 2655 2656 if ((options & B_FILTER_BITMAP_BILINEAR) != 0) { 2657 // image filter (bilinear) 2658 typedef agg::span_image_filter_rgba_bilinear< 2659 source_type, interpolator_type> span_gen_type; 2660 span_gen_type spanGenerator(source, interpolator); 2661 2662 // render the path with the bitmap as scanline fill 2663 if (fMaskedUnpackedScanline != NULL) { 2664 agg::render_scanlines_aa(fRasterizer, *fMaskedUnpackedScanline, 2665 fBaseRenderer, spanAllocator, spanGenerator); 2666 } else { 2667 agg::render_scanlines_aa(fRasterizer, fUnpackedScanline, 2668 fBaseRenderer, spanAllocator, spanGenerator); 2669 } 2670 } else { 2671 // image filter (nearest neighbor) 2672 typedef agg::span_image_filter_rgba_nn< 2673 source_type, interpolator_type> span_gen_type; 2674 span_gen_type spanGenerator(source, interpolator); 2675 2676 // render the path with the bitmap as scanline fill 2677 if (fMaskedUnpackedScanline != NULL) { 2678 agg::render_scanlines_aa(fRasterizer, *fMaskedUnpackedScanline, 2679 fBaseRenderer, spanAllocator, spanGenerator); 2680 } else { 2681 agg::render_scanlines_aa(fRasterizer, fUnpackedScanline, 2682 fBaseRenderer, spanAllocator, spanGenerator); 2683 } 2684 } 2685 } 2686 2687 2688 // _InvertRect32 2689 void 2690 Painter::_InvertRect32(BRect r) const 2691 { 2692 int32 width = r.IntegerWidth() + 1; 2693 for (int32 y = (int32)r.top; y <= (int32)r.bottom; y++) { 2694 uint8* dst = fBuffer.row_ptr(y); 2695 dst += (int32)r.left * 4; 2696 for (int32 i = 0; i < width; i++) { 2697 dst[0] = 255 - dst[0]; 2698 dst[1] = 255 - dst[1]; 2699 dst[2] = 255 - dst[2]; 2700 dst += 4; 2701 } 2702 } 2703 } 2704 2705 2706 // _BlendRect32 2707 void 2708 Painter::_BlendRect32(const BRect& r, const rgb_color& c) const 2709 { 2710 if (!fValidClipping) 2711 return; 2712 2713 uint8* dst = fBuffer.row_ptr(0); 2714 uint32 bpr = fBuffer.stride(); 2715 2716 int32 left = (int32)r.left; 2717 int32 top = (int32)r.top; 2718 int32 right = (int32)r.right; 2719 int32 bottom = (int32)r.bottom; 2720 2721 // fill rects, iterate over clipping boxes 2722 fBaseRenderer.first_clip_box(); 2723 do { 2724 int32 x1 = max_c(fBaseRenderer.xmin(), left); 2725 int32 x2 = min_c(fBaseRenderer.xmax(), right); 2726 if (x1 <= x2) { 2727 int32 y1 = max_c(fBaseRenderer.ymin(), top); 2728 int32 y2 = min_c(fBaseRenderer.ymax(), bottom); 2729 2730 uint8* offset = dst + x1 * 4 + y1 * bpr; 2731 for (; y1 <= y2; y1++) { 2732 blend_line32(offset, x2 - x1 + 1, c.red, c.green, c.blue, 2733 c.alpha); 2734 offset += bpr; 2735 } 2736 } 2737 } while (fBaseRenderer.next_clip_box()); 2738 } 2739 2740 2741 // #pragma mark - 2742 2743 2744 template<class VertexSource> 2745 BRect 2746 Painter::_BoundingBox(VertexSource& path) const 2747 { 2748 double left = 0.0; 2749 double top = 0.0; 2750 double right = -1.0; 2751 double bottom = -1.0; 2752 uint32 pathID[1]; 2753 pathID[0] = 0; 2754 agg::bounding_rect(path, pathID, 0, 1, &left, &top, &right, &bottom); 2755 return BRect(left, top, right, bottom); 2756 } 2757 2758 2759 // agg_line_cap_mode_for 2760 inline agg::line_cap_e 2761 agg_line_cap_mode_for(cap_mode mode) 2762 { 2763 switch (mode) { 2764 case B_BUTT_CAP: 2765 return agg::butt_cap; 2766 case B_SQUARE_CAP: 2767 return agg::square_cap; 2768 case B_ROUND_CAP: 2769 return agg::round_cap; 2770 } 2771 return agg::butt_cap; 2772 } 2773 2774 2775 // agg_line_join_mode_for 2776 inline agg::line_join_e 2777 agg_line_join_mode_for(join_mode mode) 2778 { 2779 switch (mode) { 2780 case B_MITER_JOIN: 2781 return agg::miter_join; 2782 case B_ROUND_JOIN: 2783 return agg::round_join; 2784 case B_BEVEL_JOIN: 2785 case B_BUTT_JOIN: // ?? 2786 case B_SQUARE_JOIN: // ?? 2787 return agg::bevel_join; 2788 } 2789 return agg::miter_join; 2790 } 2791 2792 2793 template<class VertexSource> 2794 BRect 2795 Painter::_StrokePath(VertexSource& path) const 2796 { 2797 return _StrokePath(path, fLineCapMode); 2798 } 2799 2800 2801 template<class VertexSource> 2802 BRect 2803 Painter::_StrokePath(VertexSource& path, cap_mode capMode) const 2804 { 2805 agg::conv_stroke<VertexSource> stroke(path); 2806 stroke.width(fPenSize); 2807 2808 stroke.line_cap(agg_line_cap_mode_for(capMode)); 2809 stroke.line_join(agg_line_join_mode_for(fLineJoinMode)); 2810 stroke.miter_limit(fMiterLimit); 2811 2812 if (fIdentityTransform) 2813 return _RasterizePath(stroke); 2814 2815 stroke.approximation_scale(fTransform.scale()); 2816 2817 agg::conv_transform<agg::conv_stroke<VertexSource> > transformedStroke( 2818 stroke, fTransform); 2819 return _RasterizePath(transformedStroke); 2820 } 2821 2822 2823 // _FillPath 2824 template<class VertexSource> 2825 BRect 2826 Painter::_FillPath(VertexSource& path) const 2827 { 2828 if (fIdentityTransform) 2829 return _RasterizePath(path); 2830 2831 agg::conv_transform<VertexSource> transformedPath(path, fTransform); 2832 return _RasterizePath(transformedPath); 2833 } 2834 2835 2836 // _RasterizePath 2837 template<class VertexSource> 2838 BRect 2839 Painter::_RasterizePath(VertexSource& path) const 2840 { 2841 if (fMaskedUnpackedScanline != NULL) { 2842 // TODO: we can't do both alpha-masking and subpixel AA. 2843 fRasterizer.reset(); 2844 fRasterizer.add_path(path); 2845 agg::render_scanlines(fRasterizer, *fMaskedUnpackedScanline, 2846 fRenderer); 2847 } else if (gSubpixelAntialiasing) { 2848 fSubpixRasterizer.reset(); 2849 fSubpixRasterizer.add_path(path); 2850 agg::render_scanlines(fSubpixRasterizer, 2851 fSubpixPackedScanline, fSubpixRenderer); 2852 } else { 2853 fRasterizer.reset(); 2854 fRasterizer.add_path(path); 2855 agg::render_scanlines(fRasterizer, fPackedScanline, fRenderer); 2856 } 2857 2858 return _Clipped(_BoundingBox(path)); 2859 } 2860 2861 2862 // _FillPath 2863 template<class VertexSource> 2864 BRect 2865 Painter::_FillPath(VertexSource& path, const BGradient& gradient) const 2866 { 2867 if (fIdentityTransform) 2868 return _RasterizePath(path, gradient); 2869 2870 agg::conv_transform<VertexSource> transformedPath(path, fTransform); 2871 return _RasterizePath(transformedPath, gradient); 2872 } 2873 2874 2875 // _FillPath 2876 template<class VertexSource> 2877 BRect 2878 Painter::_RasterizePath(VertexSource& path, const BGradient& gradient) const 2879 { 2880 GTRACE("Painter::_RasterizePath\n"); 2881 2882 agg::trans_affine gradientTransform; 2883 2884 switch(gradient.GetType()) { 2885 case BGradient::TYPE_LINEAR: 2886 { 2887 GTRACE(("Painter::_FillPath> type == TYPE_LINEAR\n")); 2888 const BGradientLinear& linearGradient 2889 = (const BGradientLinear&) gradient; 2890 agg::gradient_x gradientFunction; 2891 _CalcLinearGradientTransform(linearGradient.Start(), 2892 linearGradient.End(), gradientTransform); 2893 _RasterizePath(path, gradient, gradientFunction, gradientTransform); 2894 break; 2895 } 2896 case BGradient::TYPE_RADIAL: 2897 { 2898 GTRACE(("Painter::_FillPathGradient> type == TYPE_RADIAL\n")); 2899 const BGradientRadial& radialGradient 2900 = (const BGradientRadial&) gradient; 2901 agg::gradient_radial gradientFunction; 2902 _CalcRadialGradientTransform(radialGradient.Center(), 2903 gradientTransform); 2904 _RasterizePath(path, gradient, gradientFunction, gradientTransform, 2905 radialGradient.Radius()); 2906 break; 2907 } 2908 case BGradient::TYPE_RADIAL_FOCUS: 2909 { 2910 GTRACE(("Painter::_FillPathGradient> type == TYPE_RADIAL_FOCUS\n")); 2911 const BGradientRadialFocus& radialGradient 2912 = (const BGradientRadialFocus&) gradient; 2913 agg::gradient_radial_focus gradientFunction; 2914 _CalcRadialGradientTransform(radialGradient.Center(), 2915 gradientTransform); 2916 _RasterizePath(path, gradient, gradientFunction, gradientTransform, 2917 radialGradient.Radius()); 2918 break; 2919 } 2920 case BGradient::TYPE_DIAMOND: 2921 { 2922 GTRACE(("Painter::_FillPathGradient> type == TYPE_DIAMOND\n")); 2923 const BGradientDiamond& diamontGradient 2924 = (const BGradientDiamond&) gradient; 2925 agg::gradient_diamond gradientFunction; 2926 _CalcRadialGradientTransform(diamontGradient.Center(), 2927 gradientTransform); 2928 _RasterizePath(path, gradient, gradientFunction, gradientTransform); 2929 break; 2930 } 2931 case BGradient::TYPE_CONIC: 2932 { 2933 GTRACE(("Painter::_FillPathGradient> type == TYPE_CONIC\n")); 2934 const BGradientConic& conicGradient 2935 = (const BGradientConic&) gradient; 2936 agg::gradient_conic gradientFunction; 2937 _CalcRadialGradientTransform(conicGradient.Center(), 2938 gradientTransform); 2939 _RasterizePath(path, gradient, gradientFunction, gradientTransform); 2940 break; 2941 } 2942 2943 default: 2944 case BGradient::TYPE_NONE: 2945 GTRACE(("Painter::_FillPathGradient> type == TYPE_NONE/unkown\n")); 2946 break; 2947 } 2948 2949 return _Clipped(_BoundingBox(path)); 2950 } 2951 2952 2953 void 2954 Painter::_CalcLinearGradientTransform(BPoint startPoint, BPoint endPoint, 2955 agg::trans_affine& matrix, float gradient_d2) const 2956 { 2957 float dx = endPoint.x - startPoint.x; 2958 float dy = endPoint.y - startPoint.y; 2959 2960 matrix.reset(); 2961 matrix *= agg::trans_affine_scaling(sqrt(dx * dx + dy * dy) / gradient_d2); 2962 matrix *= agg::trans_affine_rotation(atan2(dy, dx)); 2963 matrix *= agg::trans_affine_translation(startPoint.x, startPoint.y); 2964 matrix *= fTransform; 2965 matrix.invert(); 2966 } 2967 2968 2969 void 2970 Painter::_CalcRadialGradientTransform(BPoint center, 2971 agg::trans_affine& matrix, float gradient_d2) const 2972 { 2973 matrix.reset(); 2974 matrix *= agg::trans_affine_translation(center.x, center.y); 2975 matrix *= fTransform; 2976 matrix.invert(); 2977 } 2978 2979 2980 void 2981 Painter::_MakeGradient(const BGradient& gradient, int32 colorCount, 2982 uint32* colors, int32 arrayOffset, int32 arraySize) const 2983 { 2984 BGradient::ColorStop* from = gradient.ColorStopAt(0); 2985 2986 if (!from) 2987 return; 2988 2989 // current index into "colors" array 2990 // int32 index = (int32)floorf(colorCount * from->offset + 0.5) 2991 // + arrayOffset; 2992 int32 index = (int32)floorf(colorCount * from->offset / 255 + 0.5) 2993 + arrayOffset; 2994 if (index > arraySize) 2995 index = arraySize; 2996 // Make sure we fill the entire array in case the gradient is outside. 2997 if (index > 0) { 2998 uint8* c = (uint8*)&colors[0]; 2999 for (int32 i = 0; i < index; i++) { 3000 c[0] = from->color.blue; 3001 c[1] = from->color.green; 3002 c[2] = from->color.red; 3003 c[3] = from->color.alpha; 3004 c += 4; 3005 } 3006 } 3007 3008 // interpolate "from" to "to" 3009 int32 stopCount = gradient.CountColorStops(); 3010 for (int32 i = 1; i < stopCount; i++) { 3011 // find the step with the next offset 3012 BGradient::ColorStop* to = gradient.ColorStopAtFast(i); 3013 3014 // interpolate 3015 // int32 offset = (int32)floorf((colorCount - 1) * to->offset + 0.5); 3016 int32 offset = (int32)floorf((colorCount - 1) 3017 * to->offset / 255 + 0.5); 3018 if (offset > colorCount - 1) 3019 offset = colorCount - 1; 3020 offset += arrayOffset; 3021 int32 dist = offset - index; 3022 if (dist >= 0) { 3023 int32 startIndex = max_c(index, 0); 3024 int32 stopIndex = min_c(offset, arraySize - 1); 3025 uint8* c = (uint8*)&colors[startIndex]; 3026 for (int32 i = startIndex; i <= stopIndex; i++) { 3027 float f = (float)(offset - i) / (float)(dist + 1); 3028 float t = 1.0 - f; 3029 c[0] = (uint8)floorf(from->color.blue * f 3030 + to->color.blue * t + 0.5); 3031 c[1] = (uint8)floorf(from->color.green * f 3032 + to->color.green * t + 0.5); 3033 c[2] = (uint8)floorf(from->color.red * f 3034 + to->color.red * t + 0.5); 3035 c[3] = (uint8)floorf(from->color.alpha * f 3036 + to->color.alpha * t + 0.5); 3037 c += 4; 3038 } 3039 } 3040 index = offset + 1; 3041 // the current "to" will be the "from" in the next interpolation 3042 from = to; 3043 } 3044 // make sure we fill the entire array 3045 if (index < arraySize) { 3046 int32 startIndex = max_c(index, 0); 3047 uint8* c = (uint8*)&colors[startIndex]; 3048 for (int32 i = startIndex; i < arraySize; i++) { 3049 c[0] = from->color.blue; 3050 c[1] = from->color.green; 3051 c[2] = from->color.red; 3052 c[3] = from->color.alpha; 3053 c += 4; 3054 } 3055 } 3056 } 3057 3058 3059 template<class Array> 3060 void 3061 Painter::_MakeGradient(Array& array, const BGradient& gradient) const 3062 { 3063 for (int i = 0; i < gradient.CountColorStops() - 1; i++) { 3064 BGradient::ColorStop* from = gradient.ColorStopAtFast(i); 3065 BGradient::ColorStop* to = gradient.ColorStopAtFast(i + 1); 3066 agg::rgba8 fromColor(from->color.red, from->color.green, 3067 from->color.blue, from->color.alpha); 3068 agg::rgba8 toColor(to->color.red, to->color.green, 3069 to->color.blue, to->color.alpha); 3070 GTRACE("Painter::_MakeGradient> fromColor(%d, %d, %d, %d) offset = %f\n", 3071 fromColor.r, fromColor.g, fromColor.b, fromColor.a, 3072 from->offset); 3073 GTRACE("Painter::_MakeGradient> toColor(%d, %d, %d %d) offset = %f\n", 3074 toColor.r, toColor.g, toColor.b, toColor.a, to->offset); 3075 float dist = to->offset - from->offset; 3076 GTRACE("Painter::_MakeGradient> dist = %f\n", dist); 3077 // TODO: Review this... offset should better be on [0..1] 3078 if (dist > 0) { 3079 for (int j = (int)from->offset; j <= (int)to->offset; j++) { 3080 float f = (float)(to->offset - j) / (float)(dist + 1); 3081 array[j] = toColor.gradient(fromColor, f); 3082 GTRACE("Painter::_MakeGradient> array[%d](%d, %d, %d, %d)\n", 3083 j, array[j].r, array[j].g, array[j].b, array[j].a); 3084 } 3085 } 3086 } 3087 } 3088 3089 3090 template<class VertexSource, typename GradientFunction> 3091 void 3092 Painter::_RasterizePath(VertexSource& path, const BGradient& gradient, 3093 GradientFunction function, agg::trans_affine& gradientTransform, 3094 int gradientStop) const 3095 { 3096 GTRACE("Painter::_RasterizePath\n"); 3097 3098 typedef agg::span_interpolator_linear<> interpolator_type; 3099 typedef agg::pod_auto_array<agg::rgba8, 256> color_array_type; 3100 typedef agg::span_allocator<agg::rgba8> span_allocator_type; 3101 typedef agg::span_gradient<agg::rgba8, interpolator_type, 3102 GradientFunction, color_array_type> span_gradient_type; 3103 typedef agg::renderer_scanline_aa<renderer_base, span_allocator_type, 3104 span_gradient_type> renderer_gradient_type; 3105 3106 interpolator_type spanInterpolator(gradientTransform); 3107 span_allocator_type spanAllocator; 3108 color_array_type colorArray; 3109 3110 _MakeGradient(colorArray, gradient); 3111 3112 span_gradient_type spanGradient(spanInterpolator, function, colorArray, 3113 0, gradientStop); 3114 3115 renderer_gradient_type gradientRenderer(fBaseRenderer, spanAllocator, 3116 spanGradient); 3117 3118 fRasterizer.reset(); 3119 fRasterizer.add_path(path); 3120 if (fMaskedUnpackedScanline == NULL) 3121 agg::render_scanlines(fRasterizer, fUnpackedScanline, gradientRenderer); 3122 else { 3123 agg::render_scanlines(fRasterizer, *fMaskedUnpackedScanline, 3124 gradientRenderer); 3125 } 3126 } 3127 3128