1 /* 2 * Copyright 2001-2008, Haiku. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * DarkWyrm <bpmagic@columbus.rr.com> 7 * Adi Oanca <adioanca@mymail.ro> 8 * Stephan Aßmus <superstippi@gmx.de> 9 * Axel Dörfler, axeld@pinc-software.de 10 * Michael Pfeiffer <laplace@users.sourceforge.net> 11 */ 12 13 //! Data classes for working with BView states and draw parameters 14 15 #include "DrawState.h" 16 17 #include <new> 18 #include <stdio.h> 19 20 #include <Region.h> 21 22 #include "AlphaMask.h" 23 #include "LinkReceiver.h" 24 #include "LinkSender.h" 25 #include "ServerProtocolStructs.h" 26 27 28 using std::nothrow; 29 30 31 DrawState::DrawState() 32 : 33 fOrigin(0.0f, 0.0f), 34 fCombinedOrigin(0.0f, 0.0f), 35 fScale(1.0f), 36 fCombinedScale(1.0f), 37 fTransform(), 38 fCombinedTransform(), 39 fClippingRegion(NULL), 40 fAlphaMask(NULL), 41 42 fHighColor((rgb_color){ 0, 0, 0, 255 }), 43 fLowColor((rgb_color){ 255, 255, 255, 255 }), 44 fPattern(kSolidHigh), 45 46 fDrawingMode(B_OP_COPY), 47 fAlphaSrcMode(B_PIXEL_ALPHA), 48 fAlphaFncMode(B_ALPHA_OVERLAY), 49 50 fPenLocation(0.0f, 0.0f), 51 fPenSize(1.0f), 52 53 fFontAliasing(false), 54 fSubPixelPrecise(false), 55 fLineCapMode(B_BUTT_CAP), 56 fLineJoinMode(B_MITER_JOIN), 57 fMiterLimit(B_DEFAULT_MITER_LIMIT), 58 fFillRule(B_NONZERO), 59 fPreviousState(NULL) 60 { 61 fUnscaledFontSize = fFont.Size(); 62 } 63 64 65 DrawState::DrawState(const DrawState& other) 66 : 67 fOrigin(other.fOrigin), 68 fCombinedOrigin(other.fCombinedOrigin), 69 fScale(other.fScale), 70 fCombinedScale(other.fCombinedScale), 71 fTransform(other.fTransform), 72 fCombinedTransform(other.fCombinedTransform), 73 fClippingRegion(NULL), 74 fAlphaMask(NULL), 75 76 fHighColor(other.fHighColor), 77 fLowColor(other.fLowColor), 78 fPattern(other.fPattern), 79 80 fDrawingMode(other.fDrawingMode), 81 fAlphaSrcMode(other.fAlphaSrcMode), 82 fAlphaFncMode(other.fAlphaFncMode), 83 84 fPenLocation(other.fPenLocation), 85 fPenSize(other.fPenSize), 86 87 fFont(other.fFont), 88 fFontAliasing(other.fFontAliasing), 89 90 fSubPixelPrecise(other.fSubPixelPrecise), 91 92 fLineCapMode(other.fLineCapMode), 93 fLineJoinMode(other.fLineJoinMode), 94 fMiterLimit(other.fMiterLimit), 95 fFillRule(other.fFillRule), 96 97 // Since fScale is reset to 1.0, the unscaled 98 // font size is the current size of the font 99 // (which is from->fUnscaledFontSize * from->fCombinedScale) 100 fUnscaledFontSize(other.fUnscaledFontSize), 101 fPreviousState(NULL) 102 { 103 } 104 105 106 DrawState::~DrawState() 107 { 108 delete fClippingRegion; 109 delete fPreviousState; 110 if (fAlphaMask != NULL) 111 fAlphaMask->ReleaseReference(); 112 } 113 114 115 DrawState* 116 DrawState::PushState() 117 { 118 DrawState* next = new (nothrow) DrawState(*this); 119 120 if (next != NULL) { 121 // Prepare state as derived from this state 122 next->fOrigin = BPoint(0.0, 0.0); 123 next->fScale = 1.0; 124 next->fTransform.Reset(); 125 next->fPreviousState = this; 126 next->SetAlphaMask(fAlphaMask); 127 } 128 129 return next; 130 } 131 132 133 DrawState* 134 DrawState::PopState() 135 { 136 DrawState* previous = PreviousState(); 137 138 fPreviousState = NULL; 139 delete this; 140 141 return previous; 142 } 143 144 145 void 146 DrawState::ReadFontFromLink(BPrivate::LinkReceiver& link) 147 { 148 uint16 mask; 149 link.Read<uint16>(&mask); 150 151 if ((mask & B_FONT_FAMILY_AND_STYLE) != 0) { 152 uint32 fontID; 153 link.Read<uint32>(&fontID); 154 fFont.SetFamilyAndStyle(fontID); 155 } 156 157 if ((mask & B_FONT_SIZE) != 0) { 158 float size; 159 link.Read<float>(&size); 160 fUnscaledFontSize = size; 161 fFont.SetSize(fUnscaledFontSize * fCombinedScale); 162 } 163 164 if ((mask & B_FONT_SHEAR) != 0) { 165 float shear; 166 link.Read<float>(&shear); 167 fFont.SetShear(shear); 168 } 169 170 if ((mask & B_FONT_ROTATION) != 0) { 171 float rotation; 172 link.Read<float>(&rotation); 173 fFont.SetRotation(rotation); 174 } 175 176 if ((mask & B_FONT_FALSE_BOLD_WIDTH) != 0) { 177 float falseBoldWidth; 178 link.Read<float>(&falseBoldWidth); 179 fFont.SetFalseBoldWidth(falseBoldWidth); 180 } 181 182 if ((mask & B_FONT_SPACING) != 0) { 183 uint8 spacing; 184 link.Read<uint8>(&spacing); 185 fFont.SetSpacing(spacing); 186 } 187 188 if ((mask & B_FONT_ENCODING) != 0) { 189 uint8 encoding; 190 link.Read<uint8>(&encoding); 191 fFont.SetEncoding(encoding); 192 } 193 194 if ((mask & B_FONT_FACE) != 0) { 195 uint16 face; 196 link.Read<uint16>(&face); 197 fFont.SetFace(face); 198 } 199 200 if ((mask & B_FONT_FLAGS) != 0) { 201 uint32 flags; 202 link.Read<uint32>(&flags); 203 fFont.SetFlags(flags); 204 } 205 } 206 207 208 void 209 DrawState::ReadFromLink(BPrivate::LinkReceiver& link) 210 { 211 ViewSetStateInfo info; 212 213 link.Read<ViewSetStateInfo>(&info); 214 215 fPenLocation = info.penLocation; 216 fPenSize = info.penSize; 217 fHighColor = info.highColor; 218 fLowColor = info.lowColor; 219 fPattern = info.pattern; 220 fDrawingMode = info.drawingMode; 221 fOrigin = info.origin; 222 fScale = info.scale; 223 fTransform = info.transform; 224 fLineJoinMode = info.lineJoin; 225 fLineCapMode = info.lineCap; 226 fMiterLimit = info.miterLimit; 227 fFillRule = info.fillRule; 228 fAlphaSrcMode = info.alphaSourceMode; 229 fAlphaFncMode = info.alphaFunctionMode; 230 fFontAliasing = info.fontAntialiasing; 231 232 if (fPreviousState != NULL) { 233 fCombinedOrigin = fPreviousState->fCombinedOrigin + fOrigin; 234 fCombinedScale = fPreviousState->fCombinedScale * fScale; 235 fCombinedTransform = fPreviousState->fCombinedTransform * fTransform; 236 } else { 237 fCombinedOrigin = fOrigin; 238 fCombinedScale = fScale; 239 fCombinedTransform = fTransform; 240 } 241 242 243 // read clipping 244 // TODO: This could be optimized, but the user clipping regions are rarely 245 // used, so it's low priority... 246 int32 clipRectCount; 247 link.Read<int32>(&clipRectCount); 248 249 if (clipRectCount >= 0) { 250 BRegion region; 251 BRect rect; 252 for (int32 i = 0; i < clipRectCount; i++) { 253 link.Read<BRect>(&rect); 254 region.Include(rect); 255 } 256 SetClippingRegion(®ion); 257 } else { 258 // No user clipping used 259 SetClippingRegion(NULL); 260 } 261 } 262 263 264 void 265 DrawState::WriteToLink(BPrivate::LinkSender& link) const 266 { 267 // Attach font state 268 ViewGetStateInfo info; 269 info.fontID = fFont.GetFamilyAndStyle(); 270 info.fontSize = fFont.Size(); 271 info.fontShear = fFont.Shear(); 272 info.fontRotation = fFont.Rotation(); 273 info.fontFalseBoldWidth = fFont.FalseBoldWidth(); 274 info.fontSpacing = fFont.Spacing(); 275 info.fontEncoding = fFont.Encoding(); 276 info.fontFace = fFont.Face(); 277 info.fontFlags = fFont.Flags(); 278 279 // Attach view state 280 info.viewStateInfo.penLocation = fPenLocation; 281 info.viewStateInfo.penSize = fPenSize; 282 info.viewStateInfo.highColor = fHighColor; 283 info.viewStateInfo.lowColor = fLowColor; 284 info.viewStateInfo.pattern = (::pattern)fPattern.GetPattern(); 285 info.viewStateInfo.drawingMode = fDrawingMode; 286 info.viewStateInfo.origin = fOrigin; 287 info.viewStateInfo.scale = fScale; 288 info.viewStateInfo.transform = fTransform; 289 info.viewStateInfo.lineJoin = fLineJoinMode; 290 info.viewStateInfo.lineCap = fLineCapMode; 291 info.viewStateInfo.miterLimit = fMiterLimit; 292 info.viewStateInfo.fillRule = fFillRule; 293 info.viewStateInfo.alphaSourceMode = fAlphaSrcMode; 294 info.viewStateInfo.alphaFunctionMode = fAlphaFncMode; 295 info.viewStateInfo.fontAntialiasing = fFontAliasing; 296 297 298 link.Attach<ViewGetStateInfo>(info); 299 300 301 // TODO: Could be optimized, but is low prio, since most views do not 302 // use a custom clipping region... 303 if (fClippingRegion != NULL) { 304 int32 clippingRectCount = fClippingRegion->CountRects(); 305 link.Attach<int32>(clippingRectCount); 306 for (int i = 0; i < clippingRectCount; i++) 307 link.Attach<BRect>(fClippingRegion->RectAt(i)); 308 } else { 309 // no client clipping 310 link.Attach<int32>(-1); 311 } 312 } 313 314 315 void 316 DrawState::SetOrigin(BPoint origin) 317 { 318 fOrigin = origin; 319 320 // NOTE: the origins of earlier states are never expected to 321 // change, only the topmost state ever changes 322 if (fPreviousState != NULL) { 323 fCombinedOrigin.x = fPreviousState->fCombinedOrigin.x 324 + fOrigin.x * fPreviousState->fCombinedScale; 325 fCombinedOrigin.y = fPreviousState->fCombinedOrigin.y 326 + fOrigin.y * fPreviousState->fCombinedScale; 327 } else { 328 fCombinedOrigin = fOrigin; 329 } 330 } 331 332 333 void 334 DrawState::SetScale(float scale) 335 { 336 if (fScale == scale) 337 return; 338 339 fScale = scale; 340 341 // NOTE: the scales of earlier states are never expected to 342 // change, only the topmost state ever changes 343 if (fPreviousState != NULL) 344 fCombinedScale = fPreviousState->fCombinedScale * fScale; 345 else 346 fCombinedScale = fScale; 347 348 // update font size 349 // NOTE: This is what makes the call potentially expensive, 350 // hence the introductory check 351 fFont.SetSize(fUnscaledFontSize * fCombinedScale); 352 } 353 354 355 void 356 DrawState::SetTransform(BAffineTransform transform) 357 { 358 if (fTransform == transform) 359 return; 360 361 fTransform = transform; 362 363 // NOTE: the transforms of earlier states are never expected to 364 // change, only the topmost state ever changes 365 if (fPreviousState != NULL) 366 fCombinedTransform = fPreviousState->fCombinedTransform * fTransform; 367 else 368 fCombinedTransform = fTransform; 369 } 370 371 372 void 373 DrawState::SetClippingRegion(const BRegion* region) 374 { 375 if (region) { 376 if (fClippingRegion != NULL) 377 *fClippingRegion = *region; 378 else 379 fClippingRegion = new(nothrow) BRegion(*region); 380 } else { 381 delete fClippingRegion; 382 fClippingRegion = NULL; 383 } 384 } 385 386 387 bool 388 DrawState::HasClipping() const 389 { 390 if (fClippingRegion != NULL) 391 return true; 392 if (fPreviousState != NULL) 393 return fPreviousState->HasClipping(); 394 return false; 395 } 396 397 398 bool 399 DrawState::HasAdditionalClipping() const 400 { 401 return fClippingRegion != NULL; 402 } 403 404 405 bool 406 DrawState::GetCombinedClippingRegion(BRegion* region) const 407 { 408 if (fClippingRegion != NULL) { 409 BRegion localTransformedClipping(*fClippingRegion); 410 Transform(&localTransformedClipping); 411 412 if (fPreviousState != NULL 413 && fPreviousState->GetCombinedClippingRegion(region)) { 414 localTransformedClipping.IntersectWith(region); 415 } 416 *region = localTransformedClipping; 417 return true; 418 } else { 419 if (fPreviousState != NULL) 420 return fPreviousState->GetCombinedClippingRegion(region); 421 } 422 return false; 423 } 424 425 426 void 427 DrawState::SetAlphaMask(AlphaMask* mask) 428 { 429 // NOTE: In BeOS, it wasn't possible to clip to a BPicture and keep 430 // regular custom clipping to a BRegion at the same time. 431 if (fAlphaMask == mask) 432 return; 433 434 if (mask != NULL) 435 mask->AcquireReference(); 436 if (fAlphaMask != NULL) 437 fAlphaMask->ReleaseReference(); 438 fAlphaMask = mask; 439 if (fAlphaMask != NULL && fPreviousState != NULL) 440 fAlphaMask->SetPrevious(fPreviousState->fAlphaMask); 441 442 } 443 444 445 AlphaMask* 446 DrawState::GetAlphaMask() const 447 { 448 return fAlphaMask; 449 } 450 451 452 // #pragma mark - 453 454 455 void 456 DrawState::Transform(float* x, float* y) const 457 { 458 // scale relative to origin, therefore 459 // scale first then translate to 460 // origin 461 *x *= fCombinedScale; 462 *y *= fCombinedScale; 463 *x += fCombinedOrigin.x; 464 *y += fCombinedOrigin.y; 465 } 466 467 468 void 469 DrawState::InverseTransform(float* x, float* y) const 470 { 471 *x -= fCombinedOrigin.x; 472 *y -= fCombinedOrigin.y; 473 if (fCombinedScale != 0.0) { 474 *x /= fCombinedScale; 475 *y /= fCombinedScale; 476 } 477 } 478 479 480 void 481 DrawState::Transform(BPoint* point) const 482 { 483 Transform(&(point->x), &(point->y)); 484 } 485 486 487 void 488 DrawState::Transform(BRect* rect) const 489 { 490 Transform(&(rect->left), &(rect->top)); 491 Transform(&(rect->right), &(rect->bottom)); 492 } 493 494 495 void 496 DrawState::Transform(BRegion* region) const 497 { 498 if (fCombinedScale == 1.0) { 499 region->OffsetBy(fCombinedOrigin.x, fCombinedOrigin.y); 500 } else { 501 // TODO: optimize some more 502 BRegion converted; 503 int32 count = region->CountRects(); 504 for (int32 i = 0; i < count; i++) { 505 BRect r = region->RectAt(i); 506 BPoint lt(r.LeftTop()); 507 BPoint rb(r.RightBottom()); 508 // offset to bottom right corner of pixel before transformation 509 rb.x++; 510 rb.y++; 511 // apply transformation 512 Transform(<.x, <.y); 513 Transform(&rb.x, &rb.y); 514 // reset bottom right to pixel "index" 515 rb.x--; 516 rb.y--; 517 // add rect to converted region 518 // NOTE/TODO: the rect would not have to go 519 // through the whole intersection test process, 520 // it is guaranteed not to overlap with any rect 521 // already contained in the region 522 converted.Include(BRect(lt, rb)); 523 } 524 *region = converted; 525 } 526 } 527 528 529 void 530 DrawState::InverseTransform(BPoint* point) const 531 { 532 InverseTransform(&(point->x), &(point->y)); 533 } 534 535 536 // #pragma mark - 537 538 539 void 540 DrawState::SetHighColor(rgb_color color) 541 { 542 fHighColor = color; 543 } 544 545 546 void 547 DrawState::SetLowColor(rgb_color color) 548 { 549 fLowColor = color; 550 } 551 552 553 void 554 DrawState::SetPattern(const Pattern& pattern) 555 { 556 fPattern = pattern; 557 } 558 559 560 void 561 DrawState::SetDrawingMode(drawing_mode mode) 562 { 563 fDrawingMode = mode; 564 } 565 566 567 void 568 DrawState::SetBlendingMode(source_alpha srcMode, alpha_function fncMode) 569 { 570 fAlphaSrcMode = srcMode; 571 fAlphaFncMode = fncMode; 572 } 573 574 575 void 576 DrawState::SetPenLocation(BPoint location) 577 { 578 fPenLocation = location; 579 } 580 581 582 BPoint 583 DrawState::PenLocation() const 584 { 585 return fPenLocation; 586 } 587 588 589 void 590 DrawState::SetPenSize(float size) 591 { 592 fPenSize = size; 593 } 594 595 596 //! returns the scaled pen size 597 float 598 DrawState::PenSize() const 599 { 600 float penSize = fPenSize * fCombinedScale; 601 // NOTE: As documented in the BeBook, 602 // pen size is never smaller than 1.0. 603 // This is supposed to be the smallest 604 // possible device size. 605 if (penSize < 1.0) 606 penSize = 1.0; 607 return penSize; 608 } 609 610 611 //! returns the unscaled pen size 612 float 613 DrawState::UnscaledPenSize() const 614 { 615 // NOTE: As documented in the BeBook, 616 // pen size is never smaller than 1.0. 617 // This is supposed to be the smallest 618 // possible device size. 619 return max_c(fPenSize, 1.0); 620 } 621 622 623 //! sets the font to be already scaled by fScale 624 void 625 DrawState::SetFont(const ServerFont& font, uint32 flags) 626 { 627 if (flags == B_FONT_ALL) { 628 fFont = font; 629 fUnscaledFontSize = font.Size(); 630 fFont.SetSize(fUnscaledFontSize * fCombinedScale); 631 } else { 632 // family & style 633 if ((flags & B_FONT_FAMILY_AND_STYLE) != 0) 634 fFont.SetFamilyAndStyle(font.GetFamilyAndStyle()); 635 // size 636 if ((flags & B_FONT_SIZE) != 0) { 637 fUnscaledFontSize = font.Size(); 638 fFont.SetSize(fUnscaledFontSize * fCombinedScale); 639 } 640 // shear 641 if ((flags & B_FONT_SHEAR) != 0) 642 fFont.SetShear(font.Shear()); 643 // rotation 644 if ((flags & B_FONT_ROTATION) != 0) 645 fFont.SetRotation(font.Rotation()); 646 // spacing 647 if ((flags & B_FONT_SPACING) != 0) 648 fFont.SetSpacing(font.Spacing()); 649 // encoding 650 if ((flags & B_FONT_ENCODING) != 0) 651 fFont.SetEncoding(font.Encoding()); 652 // face 653 if ((flags & B_FONT_FACE) != 0) 654 fFont.SetFace(font.Face()); 655 // flags 656 if ((flags & B_FONT_FLAGS) != 0) 657 fFont.SetFlags(font.Flags()); 658 } 659 } 660 661 662 void 663 DrawState::SetForceFontAliasing(bool aliasing) 664 { 665 fFontAliasing = aliasing; 666 } 667 668 669 void 670 DrawState::SetSubPixelPrecise(bool precise) 671 { 672 fSubPixelPrecise = precise; 673 } 674 675 676 void 677 DrawState::SetLineCapMode(cap_mode mode) 678 { 679 fLineCapMode = mode; 680 } 681 682 683 void 684 DrawState::SetLineJoinMode(join_mode mode) 685 { 686 fLineJoinMode = mode; 687 } 688 689 690 void 691 DrawState::SetMiterLimit(float limit) 692 { 693 fMiterLimit = limit; 694 } 695 696 697 void 698 DrawState::SetFillRule(int32 fillRule) 699 { 700 fFillRule = fillRule; 701 } 702 703 704 void 705 DrawState::PrintToStream() const 706 { 707 printf("\t Origin: (%.1f, %.1f)\n", fOrigin.x, fOrigin.y); 708 printf("\t Scale: %.2f\n", fScale); 709 printf("\t Transform: %.2f, %.2f, %.2f, %.2f, %.2f, %.2f\n", 710 fTransform.sx, fTransform.shy, fTransform.shx, 711 fTransform.sy, fTransform.tx, fTransform.ty); 712 713 printf("\t Pen Location and Size: (%.1f, %.1f) - %.2f (%.2f)\n", 714 fPenLocation.x, fPenLocation.y, PenSize(), fPenSize); 715 716 printf("\t HighColor: r=%d g=%d b=%d a=%d\n", 717 fHighColor.red, fHighColor.green, fHighColor.blue, fHighColor.alpha); 718 printf("\t LowColor: r=%d g=%d b=%d a=%d\n", 719 fLowColor.red, fLowColor.green, fLowColor.blue, fLowColor.alpha); 720 printf("\t Pattern: %" B_PRIu64 "\n", fPattern.GetInt64()); 721 722 printf("\t DrawMode: %" B_PRIu32 "\n", (uint32)fDrawingMode); 723 printf("\t AlphaSrcMode: %" B_PRId32 "\t AlphaFncMode: %" B_PRId32 "\n", 724 (int32)fAlphaSrcMode, (int32)fAlphaFncMode); 725 726 printf("\t LineCap: %d\t LineJoin: %d\t MiterLimit: %.2f\n", 727 (int16)fLineCapMode, (int16)fLineJoinMode, fMiterLimit); 728 729 if (fClippingRegion != NULL) 730 fClippingRegion->PrintToStream(); 731 732 printf("\t ===== Font Data =====\n"); 733 printf("\t Style: CURRENTLY NOT SET\n"); // ??? 734 printf("\t Size: %.1f (%.1f)\n", fFont.Size(), fUnscaledFontSize); 735 printf("\t Shear: %.2f\n", fFont.Shear()); 736 printf("\t Rotation: %.2f\n", fFont.Rotation()); 737 printf("\t Spacing: %" B_PRId32 "\n", fFont.Spacing()); 738 printf("\t Encoding: %" B_PRId32 "\n", fFont.Encoding()); 739 printf("\t Face: %d\n", fFont.Face()); 740 printf("\t Flags: %" B_PRIu32 "\n", fFont.Flags()); 741 } 742 743