1/* 2 * Copyright 2009-2010, Haiku, Inc. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Michael Lotz <mmlr@mlotz.ch> 7 * Fran��ois Revol <revol@free.fr> 8 */ 9 10#include "HTML5DrawingEngine.h" 11#include "CanvasMessage.h" 12 13#include "BitmapDrawingEngine.h" 14#include "DrawState.h" 15 16#include <Bitmap.h> 17#include <utf8_functions.h> 18 19#include <new> 20 21 22HTML5DrawingEngine::HTML5DrawingEngine(HTML5HWInterface* interface) 23 : 24 DrawingEngine(interface), 25 fHWInterface(interface), 26 fToken((uint32)this), // TODO: need to redo that for 64 bit 27 fExtendWidth(0), 28 fCallbackAdded(false), 29 fResultNotify(-1), 30 fStringWidthResult(0.0f), 31 fReadBitmapResult(NULL), 32 fBitmapDrawingEngine(NULL) 33{ 34 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 35 message.Start(RP_CREATE_STATE); 36 message.Add(fToken); 37} 38 39 40HTML5DrawingEngine::~HTML5DrawingEngine() 41{ 42 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 43 message.Start(RP_DELETE_STATE); 44 message.Add(fToken); 45 message.Flush(); 46 47 delete fBitmapDrawingEngine; 48 49 if (fCallbackAdded) 50 fHWInterface->RemoveCallback(fToken); 51 if (fResultNotify >= 0) 52 delete_sem(fResultNotify); 53} 54 55 56// #pragma mark - 57 58 59void 60HTML5DrawingEngine::FrameBufferChanged() 61{ 62 // Not allowed 63} 64 65 66// #pragma mark - 67 68 69void 70HTML5DrawingEngine::SetCopyToFrontEnabled(bool enabled) 71{ 72 DrawingEngine::SetCopyToFrontEnabled(enabled); 73 74 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 75 message.Start(enabled ? RP_ENABLE_SYNC_DRAWING : RP_DISABLE_SYNC_DRAWING); 76 message.Add(fToken); 77} 78 79 80// #pragma mark - 81 82 83//! the HTML5DrawingEngine needs to be locked! 84void 85HTML5DrawingEngine::ConstrainClippingRegion(const BRegion* region) 86{ 87 if (fClippingRegion == *region) 88 return; 89 90 fClippingRegion = *region; 91 92 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 93 message.Start(RP_CONSTRAIN_CLIPPING_REGION); 94 message.Add(fToken); 95 message.AddRegion(*region); 96} 97 98 99void 100HTML5DrawingEngine::SetDrawState(const DrawState* state, int32 xOffset, 101 int32 yOffset) 102{ 103 SetPenSize(state->PenSize()); 104 SetDrawingMode(state->GetDrawingMode()); 105 SetBlendingMode(state->AlphaSrcMode(), state->AlphaFncMode()); 106 SetPattern(state->GetPattern().GetPattern()); 107 SetStrokeMode(state->LineCapMode(), state->LineJoinMode(), 108 state->MiterLimit()); 109 SetHighColor(state->HighColor()); 110 SetLowColor(state->LowColor()); 111 SetFont(state->Font()); 112 113 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 114 message.Start(RP_SET_OFFSETS); 115 message.Add(fToken); 116 message.Add(xOffset); 117 message.Add(yOffset); 118} 119 120 121void 122HTML5DrawingEngine::SetHighColor(const rgb_color& color) 123{ 124 if (fState.HighColor() == color) 125 return; 126 127 fState.SetHighColor(color); 128 129 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 130 message.Start(RP_SET_HIGH_COLOR); 131 message.Add(fToken); 132 message.Add(color); 133} 134 135 136void 137HTML5DrawingEngine::SetLowColor(const rgb_color& color) 138{ 139 if (fState.LowColor() == color) 140 return; 141 142 fState.SetLowColor(color); 143 144 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 145 message.Start(RP_SET_LOW_COLOR); 146 message.Add(fToken); 147 message.Add(color); 148} 149 150 151void 152HTML5DrawingEngine::SetPenSize(float size) 153{ 154 if (fState.PenSize() == size) 155 return; 156 157 fState.SetPenSize(size); 158 fExtendWidth = -(size / 2); 159 160 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 161 message.Start(RP_SET_PEN_SIZE); 162 message.Add(fToken); 163 message.Add(size); 164} 165 166 167void 168HTML5DrawingEngine::SetStrokeMode(cap_mode lineCap, join_mode joinMode, 169 float miterLimit) 170{ 171 if (fState.LineCapMode() == lineCap && fState.LineJoinMode() == joinMode 172 && fState.MiterLimit() == miterLimit) 173 return; 174 175 fState.SetLineCapMode(lineCap); 176 fState.SetLineJoinMode(joinMode); 177 fState.SetMiterLimit(miterLimit); 178 179 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 180 message.Start(RP_SET_STROKE_MODE); 181 message.Add(fToken); 182 message.Add(lineCap); 183 message.Add(joinMode); 184 message.Add(miterLimit); 185} 186 187 188void 189HTML5DrawingEngine::SetBlendingMode(source_alpha sourceAlpha, 190 alpha_function alphaFunc) 191{ 192 if (fState.AlphaSrcMode() == sourceAlpha 193 && fState.AlphaFncMode() == alphaFunc) 194 return; 195 196 fState.SetBlendingMode(sourceAlpha, alphaFunc); 197 198 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 199 message.Start(RP_SET_BLENDING_MODE); 200 message.Add(fToken); 201 message.Add(sourceAlpha); 202 message.Add(alphaFunc); 203} 204 205 206void 207HTML5DrawingEngine::SetPattern(const struct pattern& pattern) 208{ 209 if (fState.GetPattern() == pattern) 210 return; 211 212 fState.SetPattern(pattern); 213 214 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 215 message.Start(RP_SET_PATTERN); 216 message.Add(fToken); 217 message.Add(pattern); 218} 219 220 221void 222HTML5DrawingEngine::SetDrawingMode(drawing_mode mode) 223{ 224 if (fState.GetDrawingMode() == mode) 225 return; 226 227 fState.SetDrawingMode(mode); 228 229 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 230 message.Start(RP_SET_DRAWING_MODE); 231 message.Add(fToken); 232 message.Add(mode); 233} 234 235 236void 237HTML5DrawingEngine::SetDrawingMode(drawing_mode mode, drawing_mode& oldMode) 238{ 239 oldMode = fState.GetDrawingMode(); 240 SetDrawingMode(mode); 241} 242 243 244void 245HTML5DrawingEngine::SetFont(const ServerFont& font) 246{ 247 if (fState.Font() == font) 248 return; 249 250 fState.SetFont(font); 251 252 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 253 message.Start(RP_SET_FONT); 254 message.Add(fToken); 255 message.AddFont(font); 256} 257 258 259void 260HTML5DrawingEngine::SetFont(const DrawState* state) 261{ 262 SetFont(state->Font()); 263} 264 265 266// #pragma mark - 267 268 269BRect 270HTML5DrawingEngine::CopyRect(BRect rect, int32 xOffset, int32 yOffset) const 271{ 272 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 273 message.Start(RP_COPY_RECT_NO_CLIPPING); 274 message.Add(xOffset); 275 message.Add(yOffset); 276 message.Add(rect); 277 return rect.OffsetBySelf(xOffset, yOffset); 278} 279 280 281void 282HTML5DrawingEngine::InvertRect(BRect rect) 283{ 284 if (!fClippingRegion.Intersects(rect)) 285 return; 286 287 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 288 message.Start(RP_INVERT_RECT); 289 message.Add(fToken); 290 message.Add(rect); 291} 292 293 294void 295HTML5DrawingEngine::DrawBitmap(ServerBitmap* bitmap, const BRect& _bitmapRect, 296 const BRect& _viewRect, uint32 options) 297{ 298 BRect bitmapRect = _bitmapRect; 299 BRect viewRect = _viewRect; 300 double xScale = (bitmapRect.Width() + 1) / (viewRect.Width() + 1); 301 double yScale = (bitmapRect.Height() + 1) / (viewRect.Height() + 1); 302 303 // constrain rect to passed bitmap bounds 304 // and transfer the changes to the viewRect with the right scale 305 BRect actualBitmapRect = bitmap->Bounds(); 306 if (bitmapRect.left < actualBitmapRect.left) { 307 float diff = actualBitmapRect.left - bitmapRect.left; 308 viewRect.left += diff / xScale; 309 bitmapRect.left = actualBitmapRect.left; 310 } 311 if (bitmapRect.top < actualBitmapRect.top) { 312 float diff = actualBitmapRect.top - bitmapRect.top; 313 viewRect.top += diff / yScale; 314 bitmapRect.top = actualBitmapRect.top; 315 } 316 if (bitmapRect.right > actualBitmapRect.right) { 317 float diff = bitmapRect.right - actualBitmapRect.right; 318 viewRect.right -= diff / xScale; 319 bitmapRect.right = actualBitmapRect.right; 320 } 321 if (bitmapRect.bottom > actualBitmapRect.bottom) { 322 float diff = bitmapRect.bottom - actualBitmapRect.bottom; 323 viewRect.bottom -= diff / yScale; 324 bitmapRect.bottom = actualBitmapRect.bottom; 325 } 326 327 BRegion clippedRegion(viewRect); 328 clippedRegion.IntersectWith(&fClippingRegion); 329 330 int32 rectCount = clippedRegion.CountRects(); 331 if (rectCount == 0) 332 return; 333 334 if (rectCount > 1 || (rectCount == 1 && clippedRegion.RectAt(0) != viewRect) 335 || viewRect.Width() < bitmapRect.Width() 336 || viewRect.Height() < bitmapRect.Height()) { 337 UtilityBitmap** bitmaps; 338 if (_ExtractBitmapRegions(*bitmap, options, bitmapRect, viewRect, 339 xScale, yScale, clippedRegion, bitmaps) != B_OK) { 340 return; 341 } 342 343 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 344 message.Start(RP_DRAW_BITMAP_RECTS); 345 message.Add(fToken); 346 message.Add(options); 347 message.Add(bitmap->ColorSpace()); 348 message.Add(bitmap->Flags()); 349 message.Add(rectCount); 350 351 for (int32 i = 0; i < rectCount; i++) { 352 message.Add(clippedRegion.RectAt(i)); 353 message.AddBitmap(*bitmaps[i], true); 354 delete bitmaps[i]; 355 } 356 357 free(bitmaps); 358 return; 359 } 360 361 // TODO: we may want to cache/checksum bitmaps 362 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 363 message.Start(RP_DRAW_BITMAP); 364 message.Add(fToken); 365 message.Add(bitmapRect); 366 message.Add(viewRect); 367 message.Add(options); 368 message.AddBitmap(*bitmap); 369} 370 371 372void 373HTML5DrawingEngine::DrawArc(BRect rect, const float& angle, const float& span, 374 bool filled) 375{ 376 BRect bounds = rect; 377 if (!filled) 378 bounds.InsetBy(fExtendWidth, fExtendWidth); 379 380 if (!fClippingRegion.Intersects(bounds)) 381 return; 382 383 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 384 message.Start(filled ? RP_FILL_ARC : RP_STROKE_ARC); 385 message.Add(fToken); 386 message.Add(rect); 387 message.Add(angle); 388 message.Add(span); 389} 390 391void 392HTML5DrawingEngine::FillArc(BRect rect, const float& angle, const float& span, 393 const BGradient& gradient) 394{ 395 if (!fClippingRegion.Intersects(rect)) 396 return; 397 398 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 399 message.Start(RP_FILL_ARC_GRADIENT); 400 message.Add(fToken); 401 message.Add(rect); 402 message.Add(angle); 403 message.Add(span); 404 message.AddGradient(gradient); 405} 406 407 408void 409HTML5DrawingEngine::DrawBezier(BPoint* points, bool filled) 410{ 411 BRect bounds = _BuildBounds(points, 4); 412 if (!filled) 413 bounds.InsetBy(fExtendWidth, fExtendWidth); 414 415 if (!fClippingRegion.Intersects(bounds)) 416 return; 417 418 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 419 message.Start(filled ? RP_FILL_BEZIER : RP_STROKE_BEZIER); 420 message.Add(fToken); 421 message.AddList(points, 4); 422} 423 424 425void 426HTML5DrawingEngine::FillBezier(BPoint* points, const BGradient& gradient) 427{ 428 BRect bounds = _BuildBounds(points, 4); 429 if (!fClippingRegion.Intersects(bounds)) 430 return; 431 432 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 433 message.Start(RP_FILL_BEZIER_GRADIENT); 434 message.Add(fToken); 435 message.AddList(points, 4); 436 message.AddGradient(gradient); 437} 438 439 440void 441HTML5DrawingEngine::DrawEllipse(BRect rect, bool filled) 442{ 443 BRect bounds = rect; 444 if (!filled) 445 bounds.InsetBy(fExtendWidth, fExtendWidth); 446 447 if (!fClippingRegion.Intersects(bounds)) 448 return; 449 450 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 451 message.Start(filled ? RP_FILL_ELLIPSE : RP_STROKE_ELLIPSE); 452 message.Add(fToken); 453 message.Add(rect); 454} 455 456 457void 458HTML5DrawingEngine::FillEllipse(BRect rect, const BGradient& gradient) 459{ 460 if (!fClippingRegion.Intersects(rect)) 461 return; 462 463 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 464 message.Start(RP_FILL_ELLIPSE_GRADIENT); 465 message.Add(fToken); 466 message.Add(rect); 467 message.AddGradient(gradient); 468} 469 470 471void 472HTML5DrawingEngine::DrawPolygon(BPoint* pointList, int32 numPoints, 473 BRect bounds, bool filled, bool closed) 474{ 475 BRect clipBounds = bounds; 476 if (!filled) 477 clipBounds.InsetBy(fExtendWidth, fExtendWidth); 478 479 if (!fClippingRegion.Intersects(clipBounds)) 480 return; 481 482 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 483 message.Start(filled ? RP_FILL_POLYGON : RP_STROKE_POLYGON); 484 message.Add(fToken); 485 message.Add(bounds); 486 message.Add(closed); 487 message.Add(numPoints); 488 for (int32 i = 0; i < numPoints; i++) 489 message.Add(pointList[i]); 490} 491 492 493void 494HTML5DrawingEngine::FillPolygon(BPoint* pointList, int32 numPoints, 495 BRect bounds, const BGradient& gradient, bool closed) 496{ 497 if (!fClippingRegion.Intersects(bounds)) 498 return; 499 500 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 501 message.Start(RP_FILL_POLYGON_GRADIENT); 502 message.Add(fToken); 503 message.Add(bounds); 504 message.Add(closed); 505 message.Add(numPoints); 506 for (int32 i = 0; i < numPoints; i++) 507 message.Add(pointList[i]); 508 message.AddGradient(gradient); 509} 510 511 512// #pragma mark - rgb_color versions 513 514 515void 516HTML5DrawingEngine::StrokePoint(const BPoint& point, const rgb_color& color) 517{ 518 BRect bounds(point, point); 519 bounds.InsetBy(fExtendWidth, fExtendWidth); 520 521 if (!fClippingRegion.Intersects(bounds)) 522 return; 523 524 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 525 message.Start(RP_STROKE_POINT_COLOR); 526 message.Add(fToken); 527 message.Add(point); 528 message.Add(color); 529} 530 531 532void 533HTML5DrawingEngine::StrokeLine(const BPoint& start, const BPoint& end, 534 const rgb_color& color) 535{ 536 BPoint points[2] = { start, end }; 537 BRect bounds = _BuildBounds(points, 2); 538 539 if (!fClippingRegion.Intersects(bounds)) 540 return; 541 542 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 543 message.Start(RP_STROKE_LINE_1PX_COLOR); 544 message.Add(fToken); 545 message.AddList(points, 2); 546 message.Add(color); 547} 548 549 550void 551HTML5DrawingEngine::StrokeRect(BRect rect, const rgb_color &color) 552{ 553 BRect bounds = rect; 554 bounds.InsetBy(fExtendWidth, fExtendWidth); 555 556 if (!fClippingRegion.Intersects(bounds)) 557 return; 558 559 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 560 message.Start(RP_STROKE_RECT_1PX_COLOR); 561 message.Add(fToken); 562 message.Add(rect); 563 message.Add(color); 564} 565 566 567void 568HTML5DrawingEngine::FillRect(BRect rect, const rgb_color& color) 569{ 570 if (!fClippingRegion.Intersects(rect)) 571 return; 572 573 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 574 message.Start(RP_FILL_RECT_COLOR); 575 message.Add(fToken); 576 message.Add(rect); 577 message.Add(color); 578} 579 580 581void 582HTML5DrawingEngine::FillRegion(BRegion& region, const rgb_color& color) 583{ 584 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 585 message.Start(RP_FILL_REGION_COLOR_NO_CLIPPING); 586 message.AddRegion(region); 587 message.Add(color); 588} 589 590 591// #pragma mark - DrawState versions 592 593 594void 595HTML5DrawingEngine::StrokeRect(BRect rect) 596{ 597 BRect bounds = rect; 598 bounds.InsetBy(fExtendWidth, fExtendWidth); 599 600 if (!fClippingRegion.Intersects(bounds)) 601 return; 602 603 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 604 message.Start(RP_STROKE_RECT); 605 message.Add(fToken); 606 message.Add(rect); 607} 608 609 610void 611HTML5DrawingEngine::FillRect(BRect rect) 612{ 613 if (!fClippingRegion.Intersects(rect)) 614 return; 615 616 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 617 message.Start(RP_FILL_RECT); 618 message.Add(fToken); 619 message.Add(rect); 620} 621 622 623void 624HTML5DrawingEngine::FillRect(BRect rect, const BGradient& gradient) 625{ 626 if (!fClippingRegion.Intersects(rect)) 627 return; 628 629 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 630 message.Start(RP_FILL_RECT_GRADIENT); 631 message.Add(fToken); 632 message.Add(rect); 633 message.AddGradient(gradient); 634} 635 636 637void 638HTML5DrawingEngine::FillRegion(BRegion& region) 639{ 640 BRegion clippedRegion = region; 641 clippedRegion.IntersectWith(&fClippingRegion); 642 if (clippedRegion.CountRects() == 0) 643 return; 644 645 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 646 message.Start(RP_FILL_REGION); 647 message.Add(fToken); 648 message.AddRegion(clippedRegion.CountRects() < region.CountRects() 649 ? clippedRegion : region); 650} 651 652 653void 654HTML5DrawingEngine::FillRegion(BRegion& region, const BGradient& gradient) 655{ 656 BRegion clippedRegion = region; 657 clippedRegion.IntersectWith(&fClippingRegion); 658 if (clippedRegion.CountRects() == 0) 659 return; 660 661 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 662 message.Start(RP_FILL_REGION_GRADIENT); 663 message.Add(fToken); 664 message.AddRegion(clippedRegion.CountRects() < region.CountRects() 665 ? clippedRegion : region); 666 message.AddGradient(gradient); 667} 668 669 670void 671HTML5DrawingEngine::DrawRoundRect(BRect rect, float xRadius, float yRadius, 672 bool filled) 673{ 674 BRect bounds = rect; 675 if (!filled) 676 bounds.InsetBy(fExtendWidth, fExtendWidth); 677 678 if (!fClippingRegion.Intersects(bounds)) 679 return; 680 681 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 682 message.Start(filled ? RP_FILL_ROUND_RECT : RP_STROKE_ROUND_RECT); 683 message.Add(fToken); 684 message.Add(rect); 685 message.Add(xRadius); 686 message.Add(yRadius); 687} 688 689 690void 691HTML5DrawingEngine::FillRoundRect(BRect rect, float xRadius, float yRadius, 692 const BGradient& gradient) 693{ 694 if (!fClippingRegion.Intersects(rect)) 695 return; 696 697 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 698 message.Start(RP_FILL_ROUND_RECT_GRADIENT); 699 message.Add(fToken); 700 message.Add(rect); 701 message.Add(xRadius); 702 message.Add(yRadius); 703 message.AddGradient(gradient); 704} 705 706 707void 708HTML5DrawingEngine::DrawShape(const BRect& bounds, int32 opCount, 709 const uint32* opList, int32 pointCount, const BPoint* pointList, 710 bool filled, const BPoint& viewToScreenOffset, float viewScale) 711{ 712 BRect clipBounds = bounds; 713 if (!filled) 714 clipBounds.InsetBy(fExtendWidth, fExtendWidth); 715 716 if (!fClippingRegion.Intersects(clipBounds)) 717 return; 718 719 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 720 message.Start(filled ? RP_FILL_SHAPE : RP_STROKE_SHAPE); 721 message.Add(fToken); 722 message.Add(bounds); 723 message.Add(opCount); 724 message.AddList(opList, opCount); 725 message.Add(pointCount); 726 message.AddList(pointList, pointCount); 727 message.Add(viewToScreenOffset); 728 message.Add(viewScale); 729} 730 731 732void 733HTML5DrawingEngine::FillShape(const BRect& bounds, int32 opCount, 734 const uint32* opList, int32 pointCount, const BPoint* pointList, 735 const BGradient& gradient, const BPoint& viewToScreenOffset, 736 float viewScale) 737{ 738 if (!fClippingRegion.Intersects(bounds)) 739 return; 740 741 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 742 message.Start(RP_FILL_SHAPE_GRADIENT); 743 message.Add(fToken); 744 message.Add(bounds); 745 message.Add(opCount); 746 message.AddList(opList, opCount); 747 message.Add(pointCount); 748 message.AddList(pointList, pointCount); 749 message.AddGradient(gradient); 750 message.Add(viewToScreenOffset); 751 message.Add(viewScale); 752} 753 754 755void 756HTML5DrawingEngine::DrawTriangle(BPoint* points, const BRect& bounds, 757 bool filled) 758{ 759 BRect clipBounds = bounds; 760 if (!filled) 761 clipBounds.InsetBy(fExtendWidth, fExtendWidth); 762 763 if (!fClippingRegion.Intersects(clipBounds)) 764 return; 765 766 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 767 message.Start(filled ? RP_FILL_TRIANGLE : RP_STROKE_TRIANGLE); 768 message.Add(fToken); 769 message.AddList(points, 3); 770 message.Add(bounds); 771} 772 773 774void 775HTML5DrawingEngine::FillTriangle(BPoint* points, const BRect& bounds, 776 const BGradient& gradient) 777{ 778 if (!fClippingRegion.Intersects(bounds)) 779 return; 780 781 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 782 message.Start(RP_FILL_TRIANGLE_GRADIENT); 783 message.Add(fToken); 784 message.Add(points[0]); 785 message.Add(points[1]); 786 message.Add(points[2]); 787 message.Add(bounds); 788 message.AddGradient(gradient); 789} 790 791 792void 793HTML5DrawingEngine::StrokeLine(const BPoint &start, const BPoint &end) 794{ 795 BPoint points[2] = { start, end }; 796 BRect bounds = _BuildBounds(points, 2); 797 798 if (!fClippingRegion.Intersects(bounds)) 799 return; 800 801 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 802 message.Start(RP_STROKE_LINE); 803 message.Add(fToken); 804 message.AddList(points, 2); 805} 806 807 808void 809HTML5DrawingEngine::StrokeLineArray(int32 numLines, 810 const ViewLineArrayInfo *lineData) 811{ 812 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 813 message.Start(RP_STROKE_LINE_ARRAY); 814 message.Add(fToken); 815 message.Add(numLines); 816 for (int32 i = 0; i < numLines; i++) 817 message.AddArrayLine(lineData[i]); 818} 819 820 821// #pragma mark - string functions 822 823 824BPoint 825HTML5DrawingEngine::DrawString(const char* string, int32 length, 826 const BPoint& point, escapement_delta* delta) 827{ 828 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 829 830 message.Start(RP_DRAW_STRING); 831 message.Add(fToken); 832 message.Add(point); 833 message.AddString(string, length); 834 message.Add(delta != NULL); 835 if (delta != NULL) 836 message.AddList(delta, length); 837 838 status_t result = _AddCallback(); 839 if (message.Flush() != B_OK) 840 return point; 841 842 if (result != B_OK) 843 return point; 844 845 do { 846 result = acquire_sem_etc(fResultNotify, 1, B_RELATIVE_TIMEOUT, 847 1 * 1000 * 1000); 848 } while (result == B_INTERRUPTED); 849 850 if (result != B_OK) 851 return point; 852 853 return fDrawStringResult; 854} 855 856 857BPoint 858HTML5DrawingEngine::DrawString(const char* string, int32 length, 859 const BPoint* offsets) 860{ 861 // Guaranteed to have at least one point. 862 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 863 864 message.Start(RP_DRAW_STRING_WITH_OFFSETS); 865 message.Add(fToken); 866 message.AddString(string, length); 867 message.AddList(offsets, UTF8CountChars(string, length)); 868 869 status_t result = _AddCallback(); 870 if (message.Flush() != B_OK) 871 return offsets[0]; 872 873 if (result != B_OK) 874 return offsets[0]; 875 876 do { 877 result = acquire_sem_etc(fResultNotify, 1, B_RELATIVE_TIMEOUT, 878 1 * 1000 * 1000); 879 } while (result == B_INTERRUPTED); 880 881 if (result != B_OK) 882 return offsets[0]; 883 884 return fDrawStringResult; 885} 886 887 888float 889HTML5DrawingEngine::StringWidth(const char* string, int32 length, 890 escapement_delta* delta) 891{ 892 // TODO: decide if really needed and use callback if so 893 return fState.Font().StringWidth(string, length, delta); 894} 895 896 897// #pragma mark - 898 899 900status_t 901HTML5DrawingEngine::ReadBitmap(ServerBitmap* bitmap, bool drawCursor, 902 BRect bounds) 903{ 904 if (_AddCallback() != B_OK) 905 return B_UNSUPPORTED; 906 907 CanvasMessage message(NULL, fHWInterface->SendBuffer()); 908 909 message.Start(RP_READ_BITMAP); 910 message.Add(fToken); 911 message.Add(bounds); 912 message.Add(drawCursor); 913 if (message.Flush() != B_OK) 914 return B_UNSUPPORTED; 915 916 status_t result; 917 do { 918 result = acquire_sem_etc(fResultNotify, 1, B_RELATIVE_TIMEOUT, 919 100 * 1000 * 1000); 920 } while (result == B_INTERRUPTED); 921 922 if (result != B_OK) 923 return result; 924 925 BBitmap* read = fReadBitmapResult; 926 if (read == NULL) 927 return B_UNSUPPORTED; 928 929 result = bitmap->ImportBits(read->Bits(), read->BitsLength(), 930 read->BytesPerRow(), read->ColorSpace()); 931 delete read; 932 return result; 933} 934 935 936// #pragma mark - 937 938 939status_t 940HTML5DrawingEngine::_AddCallback() 941{ 942 if (fCallbackAdded) 943 return B_OK; 944 945 if (fResultNotify < 0) 946 fResultNotify = create_sem(0, "drawing engine result"); 947 if (fResultNotify < 0) 948 return fResultNotify; 949 950 status_t result = fHWInterface->AddCallback(fToken, &_DrawingEngineResult, 951 this); 952 953 fCallbackAdded = result == B_OK; 954 return result; 955} 956 957 958bool 959HTML5DrawingEngine::_DrawingEngineResult(void* cookie, CanvasMessage& message) 960{ 961 HTML5DrawingEngine* engine = (HTML5DrawingEngine*)cookie; 962 963 switch (message.Code()) { 964 case RP_DRAW_STRING_RESULT: 965 if (message.Read(engine->fDrawStringResult) != B_OK) 966 return false; 967 break; 968 969 case RP_STRING_WIDTH_RESULT: 970 if (message.Read(engine->fStringWidthResult) != B_OK) 971 return false; 972 break; 973 974 case RP_READ_BITMAP_RESULT: 975 if (message.ReadBitmap(&engine->fReadBitmapResult) != B_OK) 976 return false; 977 break; 978 979 default: 980 return false; 981 } 982 983 release_sem(engine->fResultNotify); 984 return true; 985} 986 987 988BRect 989HTML5DrawingEngine::_BuildBounds(BPoint* points, int32 pointCount) 990{ 991 BRect bounds(1000000, 1000000, 0, 0); 992 for (int32 i = 0; i < pointCount; i++) { 993 bounds.left = min_c(bounds.left, points[i].x); 994 bounds.top = min_c(bounds.top, points[i].y); 995 bounds.right = max_c(bounds.right, points[i].x); 996 bounds.bottom = max_c(bounds.bottom, points[i].y); 997 } 998 999 return bounds; 1000} 1001 1002 1003status_t 1004HTML5DrawingEngine::_ExtractBitmapRegions(ServerBitmap& bitmap, uint32 options, 1005 const BRect& bitmapRect, const BRect& viewRect, double xScale, 1006 double yScale, BRegion& region, UtilityBitmap**& bitmaps) 1007{ 1008 int32 rectCount = region.CountRects(); 1009 bitmaps = (UtilityBitmap**)malloc(rectCount * sizeof(UtilityBitmap*)); 1010 if (bitmaps == NULL) 1011 return B_NO_MEMORY; 1012 1013 for (int32 i = 0; i < rectCount; i++) { 1014 BRect sourceRect = region.RectAt(i).OffsetByCopy(-viewRect.LeftTop()); 1015 int32 targetWidth = (int32)(sourceRect.Width() + 1.5); 1016 int32 targetHeight = (int32)(sourceRect.Height() + 1.5); 1017 1018 if (xScale != 1.0) { 1019 sourceRect.left = (int32)(sourceRect.left * xScale + 0.5); 1020 sourceRect.right = (int32)(sourceRect.right * xScale + 0.5); 1021 if (xScale < 1.0) 1022 targetWidth = (int32)(sourceRect.Width() + 1.5); 1023 } 1024 1025 if (yScale != 1.0) { 1026 sourceRect.top = (int32)(sourceRect.top * yScale + 0.5); 1027 sourceRect.bottom = (int32)(sourceRect.bottom * yScale + 0.5); 1028 if (yScale < 1.0) 1029 targetHeight = (int32)(sourceRect.Height() + 1.5); 1030 } 1031 1032 sourceRect.OffsetBy(bitmapRect.LeftTop()); 1033 // sourceRect is now the part of the bitmap we want copied 1034 1035 status_t result = B_OK; 1036 if ((xScale > 1.0 || yScale > 1.0) 1037 && (targetWidth * targetHeight < (int32)(sourceRect.Width() + 1.5) 1038 * (int32)(sourceRect.Height() + 1.5))) { 1039 // the target bitmap is smaller than the source, scale it locally 1040 // and send over the smaller version to avoid sending any extra data 1041 if (fBitmapDrawingEngine == NULL) { 1042 fBitmapDrawingEngine 1043 = new(std::nothrow) BitmapDrawingEngine(B_RGBA32); 1044 if (fBitmapDrawingEngine == NULL) 1045 result = B_NO_MEMORY; 1046 } 1047 1048 if (result == B_OK) { 1049 result = fBitmapDrawingEngine->SetSize(targetWidth, 1050 targetHeight); 1051 } 1052 1053 if (result == B_OK) { 1054 fBitmapDrawingEngine->SetDrawingMode(B_OP_COPY); 1055 1056 switch (bitmap.ColorSpace()) { 1057 case B_RGBA32: 1058 case B_RGBA32_BIG: 1059 case B_RGBA15: 1060 case B_RGBA15_BIG: 1061 break; 1062 1063 default: 1064 { 1065 // we need to clear the background if there may be 1066 // transparency through transparent magic (we use 1067 // B_OP_COPY when we draw alpha enabled bitmaps, so we 1068 // don't need to clear there) 1069 // TODO: this is not actually correct, as we're going to 1070 // loose the transparency with the conversion to the 1071 // original non-alpha colorspace happening in 1072 // ExportToBitmap 1073 rgb_color background = { 0, 0, 0, 0 }; 1074 fBitmapDrawingEngine->FillRect( 1075 BRect(0, 0, targetWidth - 1, targetHeight -1), 1076 background); 1077 fBitmapDrawingEngine->SetDrawingMode(B_OP_OVER); 1078 break; 1079 } 1080 } 1081 1082 fBitmapDrawingEngine->DrawBitmap(&bitmap, sourceRect, 1083 BRect(0, 0, targetWidth - 1, targetHeight - 1), options); 1084 bitmaps[i] = fBitmapDrawingEngine->ExportToBitmap(targetWidth, 1085 targetHeight, bitmap.ColorSpace()); 1086 if (bitmaps[i] == NULL) 1087 result = B_NO_MEMORY; 1088 } 1089 } else { 1090 // source is smaller or equal target, extract the relevant rects 1091 // directly without any scaling and conversion 1092 targetWidth = (int32)(sourceRect.Width() + 1.5); 1093 targetHeight = (int32)(sourceRect.Height() + 1.5); 1094 1095 bitmaps[i] = new(std::nothrow) UtilityBitmap( 1096 BRect(0, 0, targetWidth - 1, targetHeight - 1), 1097 bitmap.ColorSpace(), 0); 1098 if (bitmaps[i] == NULL) 1099 result = B_NO_MEMORY; 1100 1101 result = bitmaps[i]->ImportBits(bitmap.Bits(), bitmap.BitsLength(), 1102 bitmap.BytesPerRow(), bitmap.ColorSpace(), sourceRect.LeftTop(), 1103 BPoint(0, 0), targetWidth, targetHeight); 1104 if (result != B_OK) 1105 delete bitmaps[i]; 1106 } 1107 1108 if (result != B_OK) { 1109 for (int32 j = 0; j < i; j++) 1110 delete bitmaps[j]; 1111 free(bitmaps); 1112 return result; 1113 } 1114 } 1115 1116 return B_OK; 1117} 1118