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