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