1 /* 2 * Copyright 2012, The Android Open Source Project 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * * Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * * Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #define LOG_TAG "PlatformGraphicsContextRecording" 27 #define LOG_NDEBUG 1 28 29 #include "config.h" 30 #include "PlatformGraphicsContextRecording.h" 31 32 #include "AndroidLog.h" 33 #include "FloatRect.h" 34 #include "FloatQuad.h" 35 #include "Font.h" 36 #include "GraphicsContext.h" 37 #include "GraphicsOperation.h" 38 #include "PlatformGraphicsContextSkia.h" 39 #include "RTree.h" 40 #include "SkDevice.h" 41 42 #include "wtf/NonCopyingSort.h" 43 #include "wtf/HashSet.h" 44 #include "wtf/StringHasher.h" 45 46 #include <utils/LinearAllocator.h> 47 48 #define NEW_OP(X) new (heap()) GraphicsOperation::X 49 50 #define USE_CLIPPING_PAINTER true 51 52 // Operations smaller than this area aren't considered opaque, and thus don't 53 // clip operations below. Chosen empirically. 54 #define MIN_TRACKED_OPAQUE_AREA 750 55 56 // Cap on ClippingPainter's recursive depth. Chosen empirically. 57 #define MAX_CLIPPING_RECURSION_COUNT 400 58 59 namespace WebCore { 60 61 static FloatRect approximateTextBounds(size_t numGlyphs, 62 const SkPoint pos[], const SkPaint& paint) 63 { 64 if (!numGlyphs || !pos) { 65 return FloatRect(); 66 } 67 68 // get glyph position bounds 69 SkScalar minX = pos[0].x(); 70 SkScalar maxX = minX; 71 SkScalar minY = pos[0].y(); 72 SkScalar maxY = minY; 73 for (size_t i = 1; i < numGlyphs; ++i) { 74 SkScalar x = pos[i].x(); 75 SkScalar y = pos[i].y(); 76 minX = std::min(minX, x); 77 maxX = std::max(maxX, x); 78 minY = std::min(minY, y); 79 maxY = std::max(maxY, y); 80 } 81 82 // build final rect 83 SkPaint::FontMetrics metrics; 84 SkScalar bufY = paint.getFontMetrics(&metrics); 85 SkScalar bufX = bufY * 2; 86 SkScalar adjY = metrics.fAscent / 2; 87 minY += adjY; 88 maxY += adjY; 89 SkRect rect; 90 rect.set(minX - bufX, minY - bufY, maxX + bufX, maxY + bufY); 91 return rect; 92 } 93 94 class StateHash { 95 public: 96 static unsigned hash(PlatformGraphicsContext::State* const& state) 97 { 98 return StringHasher::hashMemory(state, sizeof(PlatformGraphicsContext::State)); 99 } 100 101 static bool equal(PlatformGraphicsContext::State* const& a, 102 PlatformGraphicsContext::State* const& b) 103 { 104 return a && b && !memcmp(a, b, sizeof(PlatformGraphicsContext::State)); 105 } 106 107 static const bool safeToCompareToEmptyOrDeleted = false; 108 }; 109 110 class SkPaintHash { 111 public: 112 static unsigned hash(const SkPaint* const& paint) 113 { 114 return StringHasher::hashMemory(paint, sizeof(SkPaint)); 115 } 116 117 static bool equal(const SkPaint* const& a, 118 const SkPaint* const& b) 119 { 120 return a && b && (*a == *b); 121 } 122 123 static const bool safeToCompareToEmptyOrDeleted = false; 124 }; 125 126 typedef HashSet<PlatformGraphicsContext::State*, StateHash> StateHashSet; 127 typedef HashSet<const SkPaint*, SkPaintHash> SkPaintHashSet; 128 129 class CanvasState { 130 public: 131 CanvasState(CanvasState* parent) 132 : m_parent(parent) 133 , m_isTransparencyLayer(false) 134 {} 135 136 CanvasState(CanvasState* parent, float opacity) 137 : m_parent(parent) 138 , m_isTransparencyLayer(true) 139 , m_opacity(opacity) 140 {} 141 142 ~CanvasState() { 143 ALOGV("Delete %p", this); 144 for (size_t i = 0; i < m_operations.size(); i++) 145 m_operations[i]->~RecordingData(); 146 m_operations.clear(); 147 } 148 149 bool isParentOf(CanvasState* other) { 150 while (other->m_parent) { 151 if (other->m_parent == this) 152 return true; 153 other = other->m_parent; 154 } 155 return false; 156 } 157 158 void playback(PlatformGraphicsContext* context, size_t fromId, size_t toId) const { 159 ALOGV("playback %p from %d->%d", this, fromId, toId); 160 for (size_t i = 0; i < m_operations.size(); i++) { 161 RecordingData *data = m_operations[i]; 162 if (data->m_orderBy < fromId) 163 continue; 164 if (data->m_orderBy > toId) 165 break; 166 ALOGV("Applying operation[%d] %p->%s()", i, data->m_operation, 167 data->m_operation->name()); 168 data->m_operation->apply(context); 169 } 170 } 171 172 CanvasState* parent() { return m_parent; } 173 174 void enterState(PlatformGraphicsContext* context) { 175 ALOGV("enterState %p", this); 176 if (m_isTransparencyLayer) 177 context->beginTransparencyLayer(m_opacity); 178 else 179 context->save(); 180 } 181 182 void exitState(PlatformGraphicsContext* context) { 183 ALOGV("exitState %p", this); 184 if (m_isTransparencyLayer) 185 context->endTransparencyLayer(); 186 else 187 context->restore(); 188 } 189 190 void adoptAndAppend(RecordingData* data) { 191 m_operations.append(data); 192 } 193 194 bool isTransparencyLayer() { 195 return m_isTransparencyLayer; 196 } 197 198 void* operator new(size_t size, android::LinearAllocator* la) { 199 return la->alloc(size); 200 } 201 202 private: 203 CanvasState *m_parent; 204 bool m_isTransparencyLayer; 205 float m_opacity; 206 Vector<RecordingData*> m_operations; 207 }; 208 209 class RecordingImpl { 210 private: 211 // Careful, ordering matters here. Ordering is first constructed == last destroyed, 212 // so we have to make sure our Heap is the first thing listed so that it is 213 // the last thing destroyed. 214 android::LinearAllocator m_heap; 215 public: 216 RecordingImpl() 217 : m_tree(&m_heap) 218 , m_nodeCount(0) 219 { 220 } 221 222 ~RecordingImpl() { 223 clearStates(); 224 clearCanvasStates(); 225 clearSkPaints(); 226 } 227 228 PlatformGraphicsContext::State* getState(PlatformGraphicsContext::State* inState) { 229 StateHashSet::iterator it = m_states.find(inState); 230 if (it != m_states.end()) 231 return (*it); 232 void* buf = heap()->alloc(sizeof(PlatformGraphicsContext::State)); 233 PlatformGraphicsContext::State* state = new (buf) PlatformGraphicsContext::State(*inState); 234 m_states.add(state); 235 return state; 236 } 237 238 const SkPaint* getSkPaint(const SkPaint& inPaint) { 239 SkPaintHashSet::iterator it = m_paints.find(&inPaint); 240 if (it != m_paints.end()) 241 return (*it); 242 void* buf = heap()->alloc(sizeof(SkPaint)); 243 SkPaint* paint = new (buf) SkPaint(inPaint); 244 m_paints.add(paint); 245 return paint; 246 } 247 248 void addCanvasState(CanvasState* state) { 249 m_canvasStates.append(state); 250 } 251 252 void removeCanvasState(const CanvasState* state) { 253 if (m_canvasStates.last() == state) 254 m_canvasStates.removeLast(); 255 else { 256 size_t indx = m_canvasStates.find(state); 257 m_canvasStates.remove(indx); 258 } 259 } 260 261 void applyState(PlatformGraphicsContext* context, 262 CanvasState* fromState, size_t fromId, 263 CanvasState* toState, size_t toId) { 264 ALOGV("applyState(%p->%p, %d-%d)", fromState, toState, fromId, toId); 265 if (fromState != toState && fromState) { 266 if (fromState->isParentOf(toState)) { 267 // Going down the tree, playback any parent operations then save 268 // before playing back our current operations 269 applyState(context, fromState, fromId, toState->parent(), toId); 270 toState->enterState(context); 271 } else if (toState->isParentOf(fromState)) { 272 // Going up the tree, pop some states 273 while (fromState != toState) { 274 fromState->exitState(context); 275 fromState = fromState->parent(); 276 } 277 } else { 278 // Siblings in the tree 279 fromState->exitState(context); 280 applyState(context, fromState->parent(), fromId, toState, toId); 281 return; 282 } 283 } else if (!fromState) { 284 if (toState->parent()) 285 applyState(context, fromState, fromId, toState->parent(), toId); 286 toState->enterState(context); 287 } 288 toState->playback(context, fromId, toId); 289 } 290 291 android::LinearAllocator* heap() { return &m_heap; } 292 293 RTree::RTree m_tree; 294 int m_nodeCount; 295 296 void dumpMemoryStats() { 297 static const char* PREFIX = " "; 298 ALOGD("Heap:"); 299 m_heap.dumpMemoryStats(PREFIX); 300 } 301 302 private: 303 304 void clearStates() { 305 StateHashSet::iterator end = m_states.end(); 306 for (StateHashSet::iterator it = m_states.begin(); it != end; ++it) 307 (*it)->~State(); 308 m_states.clear(); 309 } 310 311 void clearSkPaints() { 312 SkPaintHashSet::iterator end = m_paints.end(); 313 for (SkPaintHashSet::iterator it = m_paints.begin(); it != end; ++it) 314 (*it)->~SkPaint(); 315 m_paints.clear(); 316 } 317 318 void clearCanvasStates() { 319 for (size_t i = 0; i < m_canvasStates.size(); i++) 320 m_canvasStates[i]->~CanvasState(); 321 m_canvasStates.clear(); 322 } 323 324 StateHashSet m_states; 325 SkPaintHashSet m_paints; 326 Vector<CanvasState*> m_canvasStates; 327 }; 328 329 Recording::~Recording() 330 { 331 delete m_recording; 332 } 333 334 static bool CompareRecordingDataOrder(const RecordingData* a, const RecordingData* b) 335 { 336 return a->m_orderBy < b->m_orderBy; 337 } 338 339 static IntRect enclosedIntRect(const FloatRect& rect) 340 { 341 float left = ceilf(rect.x()); 342 float top = ceilf(rect.y()); 343 float width = floorf(rect.maxX()) - left; 344 float height = floorf(rect.maxY()) - top; 345 346 return IntRect(clampToInteger(left), clampToInteger(top), 347 clampToInteger(width), clampToInteger(height)); 348 } 349 350 #if USE_CLIPPING_PAINTER 351 class ClippingPainter { 352 public: 353 ClippingPainter(RecordingImpl* recording, 354 PlatformGraphicsContextSkia& context, 355 const SkMatrix& initialMatrix, 356 Vector<RecordingData*> &nodes) 357 : m_recording(recording) 358 , m_context(context) 359 , m_initialMatrix(initialMatrix) 360 , m_nodes(nodes) 361 , m_lastOperationId(0) 362 , m_currState(0) 363 {} 364 365 void draw(const SkIRect& bounds) { 366 drawWithClipRecursive(static_cast<int>(m_nodes.size()) - 1, bounds, 0); 367 368 while (m_currState) { 369 m_currState->exitState(&m_context); 370 m_currState = m_currState->parent(); 371 } 372 } 373 374 private: 375 void drawOperation(RecordingData* node, const SkRegion* uncovered) 376 { 377 GraphicsOperation::Operation* op = node->m_operation; 378 m_recording->applyState(&m_context, m_currState, 379 m_lastOperationId, op->m_canvasState, node->m_orderBy); 380 m_currState = op->m_canvasState; 381 m_lastOperationId = node->m_orderBy; 382 383 // if other opaque operations will cover the current one, clip that area out 384 // (and restore the clip immediately after drawing) 385 if (uncovered) { 386 m_context.save(); 387 m_context.canvas()->clipRegion(*uncovered, SkRegion::kIntersect_Op); 388 } 389 op->apply(&(m_context)); 390 if (uncovered) 391 m_context.restore(); 392 } 393 394 void drawWithClipRecursive(int index, const SkIRect& bounds, const SkRegion* uncovered) 395 { 396 if (index < 0) 397 return; 398 RecordingData* recordingData = m_nodes[index]; 399 GraphicsOperation::Operation* op = recordingData->m_operation; 400 if (index != 0) { 401 const IntRect* opaqueRect = op->opaqueRect(); 402 if (!opaqueRect || opaqueRect->isEmpty()) { 403 drawWithClipRecursive(index - 1, bounds, uncovered); 404 } else { 405 SkRegion newUncovered; 406 if (uncovered) 407 newUncovered = *uncovered; 408 else 409 newUncovered = SkRegion(bounds); 410 411 SkRect mappedRect = *opaqueRect; 412 m_initialMatrix.mapRect(&mappedRect); 413 newUncovered.op(enclosedIntRect(mappedRect), SkRegion::kDifference_Op); 414 if (!newUncovered.isEmpty()) 415 drawWithClipRecursive(index - 1, bounds, &newUncovered); 416 } 417 } 418 419 if (!uncovered || !uncovered->isEmpty()) 420 drawOperation(recordingData, uncovered); 421 } 422 423 RecordingImpl* m_recording; 424 PlatformGraphicsContextSkia& m_context; 425 const SkMatrix& m_initialMatrix; 426 const Vector<RecordingData*>& m_nodes; 427 size_t m_lastOperationId; 428 CanvasState* m_currState; 429 }; 430 #endif // USE_CLIPPING_PAINTER 431 432 void Recording::draw(SkCanvas* canvas) 433 { 434 if (!m_recording) { 435 ALOGW("No recording!"); 436 return; 437 } 438 SkRect clip; 439 if (!canvas->getClipBounds(&clip)) { 440 ALOGW("Empty clip!"); 441 return; 442 } 443 Vector<RecordingData*> nodes; 444 445 WebCore::IntRect iclip = enclosingIntRect(clip); 446 m_recording->m_tree.search(iclip, nodes); 447 448 size_t count = nodes.size(); 449 ALOGV("Drawing %d nodes out of %d", count, m_recording->m_nodeCount); 450 if (count) { 451 int saveCount = canvas->getSaveCount(); 452 nonCopyingSort(nodes.begin(), nodes.end(), CompareRecordingDataOrder); 453 PlatformGraphicsContextSkia context(canvas); 454 #if USE_CLIPPING_PAINTER 455 if (canvas->getDevice() && canvas->getDevice()->config() != SkBitmap::kNo_Config 456 && count < MAX_CLIPPING_RECURSION_COUNT) { 457 ClippingPainter painter(recording(), context, canvas->getTotalMatrix(), nodes); 458 painter.draw(canvas->getTotalClip().getBounds()); 459 } else 460 #endif 461 { 462 CanvasState* currState = 0; 463 size_t lastOperationId = 0; 464 for (size_t i = 0; i < count; i++) { 465 GraphicsOperation::Operation* op = nodes[i]->m_operation; 466 m_recording->applyState(&context, currState, lastOperationId, 467 op->m_canvasState, nodes[i]->m_orderBy); 468 currState = op->m_canvasState; 469 lastOperationId = nodes[i]->m_orderBy; 470 ALOGV("apply: %p->%s()", op, op->name()); 471 op->apply(&context); 472 } 473 while (currState) { 474 currState->exitState(&context); 475 currState = currState->parent(); 476 } 477 } 478 if (saveCount != canvas->getSaveCount()) { 479 ALOGW("Save/restore mismatch! %d vs. %d", saveCount, canvas->getSaveCount()); 480 } 481 } 482 } 483 484 void Recording::setRecording(RecordingImpl* impl) 485 { 486 if (m_recording == impl) 487 return; 488 if (m_recording) 489 delete m_recording; 490 m_recording = impl; 491 } 492 493 //************************************** 494 // PlatformGraphicsContextRecording 495 //************************************** 496 497 PlatformGraphicsContextRecording::PlatformGraphicsContextRecording(Recording* recording) 498 : PlatformGraphicsContext() 499 , mPicture(0) 500 , mRecording(recording) 501 , mOperationState(0) 502 , m_maxZoomScale(1) 503 , m_isEmpty(true) 504 , m_canvasProxy(this) 505 { 506 ALOGV("RECORDING: begin"); 507 if (mRecording) 508 mRecording->setRecording(new RecordingImpl()); 509 mMatrixStack.append(SkMatrix::I()); 510 mCurrentMatrix = &(mMatrixStack.last()); 511 pushStateOperation(new (heap()) CanvasState(0)); 512 } 513 514 PlatformGraphicsContextRecording::~PlatformGraphicsContextRecording() 515 { 516 ALOGV("RECORDING: end"); 517 IF_ALOGV() 518 mRecording->recording()->dumpMemoryStats(); 519 } 520 521 bool PlatformGraphicsContextRecording::isPaintingDisabled() 522 { 523 return !mRecording; 524 } 525 526 SkCanvas* PlatformGraphicsContextRecording::recordingCanvas() 527 { 528 m_maxZoomScale = 1e6f; 529 return &m_canvasProxy; 530 } 531 532 //************************************** 533 // State management 534 //************************************** 535 536 void PlatformGraphicsContextRecording::beginTransparencyLayer(float opacity) 537 { 538 CanvasState* parent = mRecordingStateStack.last().mCanvasState; 539 pushStateOperation(new (heap()) CanvasState(parent, opacity)); 540 mRecordingStateStack.last().disableOpaqueTracking(); 541 } 542 543 void PlatformGraphicsContextRecording::endTransparencyLayer() 544 { 545 popStateOperation(); 546 } 547 548 void PlatformGraphicsContextRecording::save() 549 { 550 PlatformGraphicsContext::save(); 551 CanvasState* parent = mRecordingStateStack.last().mCanvasState; 552 pushStateOperation(new (heap()) CanvasState(parent)); 553 pushMatrix(); 554 } 555 556 void PlatformGraphicsContextRecording::restore() 557 { 558 PlatformGraphicsContext::restore(); 559 popMatrix(); 560 popStateOperation(); 561 } 562 563 //************************************** 564 // State setters 565 //************************************** 566 567 void PlatformGraphicsContextRecording::setAlpha(float alpha) 568 { 569 PlatformGraphicsContext::setAlpha(alpha); 570 mOperationState = 0; 571 } 572 573 void PlatformGraphicsContextRecording::setCompositeOperation(CompositeOperator op) 574 { 575 PlatformGraphicsContext::setCompositeOperation(op); 576 mOperationState = 0; 577 } 578 579 bool PlatformGraphicsContextRecording::setFillColor(const Color& c) 580 { 581 if (PlatformGraphicsContext::setFillColor(c)) { 582 mOperationState = 0; 583 return true; 584 } 585 return false; 586 } 587 588 bool PlatformGraphicsContextRecording::setFillShader(SkShader* fillShader) 589 { 590 if (PlatformGraphicsContext::setFillShader(fillShader)) { 591 mOperationState = 0; 592 return true; 593 } 594 return false; 595 } 596 597 void PlatformGraphicsContextRecording::setLineCap(LineCap cap) 598 { 599 PlatformGraphicsContext::setLineCap(cap); 600 mOperationState = 0; 601 } 602 603 void PlatformGraphicsContextRecording::setLineDash(const DashArray& dashes, float dashOffset) 604 { 605 PlatformGraphicsContext::setLineDash(dashes, dashOffset); 606 mOperationState = 0; 607 } 608 609 void PlatformGraphicsContextRecording::setLineJoin(LineJoin join) 610 { 611 PlatformGraphicsContext::setLineJoin(join); 612 mOperationState = 0; 613 } 614 615 void PlatformGraphicsContextRecording::setMiterLimit(float limit) 616 { 617 PlatformGraphicsContext::setMiterLimit(limit); 618 mOperationState = 0; 619 } 620 621 void PlatformGraphicsContextRecording::setShadow(int radius, int dx, int dy, SkColor c) 622 { 623 PlatformGraphicsContext::setShadow(radius, dx, dy, c); 624 mOperationState = 0; 625 } 626 627 void PlatformGraphicsContextRecording::setShouldAntialias(bool useAA) 628 { 629 m_state->useAA = useAA; 630 PlatformGraphicsContext::setShouldAntialias(useAA); 631 mOperationState = 0; 632 } 633 634 bool PlatformGraphicsContextRecording::setStrokeColor(const Color& c) 635 { 636 if (PlatformGraphicsContext::setStrokeColor(c)) { 637 mOperationState = 0; 638 return true; 639 } 640 return false; 641 } 642 643 bool PlatformGraphicsContextRecording::setStrokeShader(SkShader* strokeShader) 644 { 645 if (PlatformGraphicsContext::setStrokeShader(strokeShader)) { 646 mOperationState = 0; 647 return true; 648 } 649 return false; 650 } 651 652 void PlatformGraphicsContextRecording::setStrokeStyle(StrokeStyle style) 653 { 654 PlatformGraphicsContext::setStrokeStyle(style); 655 mOperationState = 0; 656 } 657 658 void PlatformGraphicsContextRecording::setStrokeThickness(float f) 659 { 660 PlatformGraphicsContext::setStrokeThickness(f); 661 mOperationState = 0; 662 } 663 664 //************************************** 665 // Matrix operations 666 //************************************** 667 668 void PlatformGraphicsContextRecording::concatCTM(const AffineTransform& affine) 669 { 670 mCurrentMatrix->preConcat(affine); 671 appendStateOperation(NEW_OP(ConcatCTM)(affine)); 672 } 673 674 void PlatformGraphicsContextRecording::rotate(float angleInRadians) 675 { 676 float value = angleInRadians * (180.0f / 3.14159265f); 677 mCurrentMatrix->preRotate(SkFloatToScalar(value)); 678 appendStateOperation(NEW_OP(Rotate)(angleInRadians)); 679 } 680 681 void PlatformGraphicsContextRecording::scale(const FloatSize& size) 682 { 683 mCurrentMatrix->preScale(SkFloatToScalar(size.width()), SkFloatToScalar(size.height())); 684 appendStateOperation(NEW_OP(Scale)(size)); 685 } 686 687 void PlatformGraphicsContextRecording::translate(float x, float y) 688 { 689 mCurrentMatrix->preTranslate(SkFloatToScalar(x), SkFloatToScalar(y)); 690 appendStateOperation(NEW_OP(Translate)(x, y)); 691 } 692 693 const SkMatrix& PlatformGraphicsContextRecording::getTotalMatrix() 694 { 695 return *mCurrentMatrix; 696 } 697 698 //************************************** 699 // Clipping 700 //************************************** 701 702 void PlatformGraphicsContextRecording::addInnerRoundedRectClip(const IntRect& rect, 703 int thickness) 704 { 705 mRecordingStateStack.last().disableOpaqueTracking(); 706 appendStateOperation(NEW_OP(InnerRoundedRectClip)(rect, thickness)); 707 } 708 709 void PlatformGraphicsContextRecording::canvasClip(const Path& path) 710 { 711 mRecordingStateStack.last().disableOpaqueTracking(); 712 clip(path); 713 } 714 715 bool PlatformGraphicsContextRecording::clip(const FloatRect& rect) 716 { 717 clipState(rect); 718 appendStateOperation(NEW_OP(Clip)(rect)); 719 return true; 720 } 721 722 bool PlatformGraphicsContextRecording::clip(const Path& path) 723 { 724 mRecordingStateStack.last().disableOpaqueTracking(); 725 clipState(path.boundingRect()); 726 appendStateOperation(NEW_OP(ClipPath)(path)); 727 return true; 728 } 729 730 bool PlatformGraphicsContextRecording::clipConvexPolygon(size_t numPoints, 731 const FloatPoint*, bool antialias) 732 { 733 // TODO 734 return true; 735 } 736 737 bool PlatformGraphicsContextRecording::clipOut(const IntRect& r) 738 { 739 mRecordingStateStack.last().disableOpaqueTracking(); 740 appendStateOperation(NEW_OP(ClipOut)(r)); 741 return true; 742 } 743 744 bool PlatformGraphicsContextRecording::clipOut(const Path& path) 745 { 746 mRecordingStateStack.last().disableOpaqueTracking(); 747 appendStateOperation(NEW_OP(ClipPath)(path, true)); 748 return true; 749 } 750 751 bool PlatformGraphicsContextRecording::clipPath(const Path& pathToClip, WindRule clipRule) 752 { 753 mRecordingStateStack.last().disableOpaqueTracking(); 754 clipState(pathToClip.boundingRect()); 755 GraphicsOperation::ClipPath* operation = NEW_OP(ClipPath)(pathToClip); 756 operation->setWindRule(clipRule); 757 appendStateOperation(operation); 758 return true; 759 } 760 761 void PlatformGraphicsContextRecording::clearRect(const FloatRect& rect) 762 { 763 appendDrawingOperation(NEW_OP(ClearRect)(rect), rect); 764 } 765 766 //************************************** 767 // Drawing 768 //************************************** 769 770 void PlatformGraphicsContextRecording::drawBitmapPattern( 771 const SkBitmap& bitmap, const SkMatrix& matrix, 772 CompositeOperator compositeOp, const FloatRect& destRect) 773 { 774 appendDrawingOperation( 775 NEW_OP(DrawBitmapPattern)(bitmap, matrix, compositeOp, destRect), 776 destRect); 777 } 778 779 void PlatformGraphicsContextRecording::drawBitmapRect(const SkBitmap& bitmap, 780 const SkIRect* srcPtr, const SkRect& dst, 781 CompositeOperator op) 782 { 783 float widthScale = dst.width() == 0 ? 1 : bitmap.width() / dst.width(); 784 float heightScale = dst.height() == 0 ? 1 : bitmap.height() / dst.height(); 785 m_maxZoomScale = std::max(m_maxZoomScale, std::max(widthScale, heightScale)); 786 // null src implies full bitmap as source rect 787 SkIRect src = srcPtr ? *srcPtr : SkIRect::MakeWH(bitmap.width(), bitmap.height()); 788 appendDrawingOperation(NEW_OP(DrawBitmapRect)(bitmap, src, dst, op), dst); 789 } 790 791 void PlatformGraphicsContextRecording::drawConvexPolygon(size_t numPoints, 792 const FloatPoint* points, 793 bool shouldAntialias) 794 { 795 if (numPoints < 1) return; 796 if (numPoints != 4) { 797 // TODO: Build a path and call draw on that (webkit currently never calls this) 798 ALOGW("drawConvexPolygon with numPoints != 4 is not supported!"); 799 return; 800 } 801 FloatRect bounds; 802 bounds.fitToPoints(points[0], points[1], points[2], points[3]); 803 appendDrawingOperation(NEW_OP(DrawConvexPolygonQuad)(points, shouldAntialias), bounds); 804 } 805 806 void PlatformGraphicsContextRecording::drawEllipse(const IntRect& rect) 807 { 808 appendDrawingOperation(NEW_OP(DrawEllipse)(rect), rect); 809 } 810 811 void PlatformGraphicsContextRecording::drawFocusRing(const Vector<IntRect>& rects, 812 int width, int offset, 813 const Color& color) 814 { 815 if (!rects.size()) 816 return; 817 IntRect bounds = rects[0]; 818 for (size_t i = 1; i < rects.size(); i++) 819 bounds.unite(rects[i]); 820 appendDrawingOperation(NEW_OP(DrawFocusRing)(rects, width, offset, color), bounds); 821 } 822 823 void PlatformGraphicsContextRecording::drawHighlightForText( 824 const Font& font, const TextRun& run, const FloatPoint& point, int h, 825 const Color& backgroundColor, ColorSpace colorSpace, int from, 826 int to, bool isActive) 827 { 828 IntRect rect = (IntRect)font.selectionRectForText(run, point, h, from, to); 829 if (isActive) 830 fillRect(rect, backgroundColor); 831 else { 832 int x = rect.x(), y = rect.y(), w = rect.width(), h = rect.height(); 833 const int t = 3, t2 = t * 2; 834 835 fillRect(IntRect(x, y, w, t), backgroundColor); 836 fillRect(IntRect(x, y+h-t, w, t), backgroundColor); 837 fillRect(IntRect(x, y+t, t, h-t2), backgroundColor); 838 fillRect(IntRect(x+w-t, y+t, t, h-t2), backgroundColor); 839 } 840 } 841 842 void PlatformGraphicsContextRecording::drawLine(const IntPoint& point1, 843 const IntPoint& point2) 844 { 845 FloatRect bounds = FloatQuad(point1, point1, point2, point2).boundingBox(); 846 float width = m_state->strokeThickness; 847 if (!width) width = 1; 848 bounds.inflate(width); 849 appendDrawingOperation(NEW_OP(DrawLine)(point1, point2), bounds); 850 } 851 852 void PlatformGraphicsContextRecording::drawLineForText(const FloatPoint& pt, float width) 853 { 854 FloatRect bounds(pt.x(), pt.y(), width, m_state->strokeThickness); 855 appendDrawingOperation(NEW_OP(DrawLineForText)(pt, width), bounds); 856 } 857 858 void PlatformGraphicsContextRecording::drawLineForTextChecking(const FloatPoint& pt, 859 float width, GraphicsContext::TextCheckingLineStyle lineStyle) 860 { 861 FloatRect bounds(pt.x(), pt.y(), width, m_state->strokeThickness); 862 appendDrawingOperation(NEW_OP(DrawLineForTextChecking)(pt, width, lineStyle), bounds); 863 } 864 865 void PlatformGraphicsContextRecording::drawRect(const IntRect& rect) 866 { 867 appendDrawingOperation(NEW_OP(DrawRect)(rect), rect); 868 } 869 870 void PlatformGraphicsContextRecording::fillPath(const Path& pathToFill, WindRule fillRule) 871 { 872 appendDrawingOperation(NEW_OP(FillPath)(pathToFill, fillRule), pathToFill.boundingRect()); 873 } 874 875 void PlatformGraphicsContextRecording::fillRect(const FloatRect& rect) 876 { 877 appendDrawingOperation(NEW_OP(FillRect)(rect), rect); 878 } 879 880 void PlatformGraphicsContextRecording::fillRect(const FloatRect& rect, 881 const Color& color) 882 { 883 GraphicsOperation::FillRect* operation = NEW_OP(FillRect)(rect); 884 operation->setColor(color); 885 appendDrawingOperation(operation, rect); 886 } 887 888 void PlatformGraphicsContextRecording::fillRoundedRect( 889 const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, 890 const IntSize& bottomLeft, const IntSize& bottomRight, 891 const Color& color) 892 { 893 appendDrawingOperation(NEW_OP(FillRoundedRect)(rect, topLeft, 894 topRight, bottomLeft, bottomRight, color), rect); 895 } 896 897 void PlatformGraphicsContextRecording::strokeArc(const IntRect& r, int startAngle, 898 int angleSpan) 899 { 900 appendDrawingOperation(NEW_OP(StrokeArc)(r, startAngle, angleSpan), r); 901 } 902 903 void PlatformGraphicsContextRecording::strokePath(const Path& pathToStroke) 904 { 905 appendDrawingOperation(NEW_OP(StrokePath)(pathToStroke), pathToStroke.boundingRect()); 906 } 907 908 void PlatformGraphicsContextRecording::strokeRect(const FloatRect& rect, float lineWidth) 909 { 910 FloatRect bounds = rect; 911 bounds.inflate(lineWidth); 912 appendDrawingOperation(NEW_OP(StrokeRect)(rect, lineWidth), bounds); 913 } 914 915 void PlatformGraphicsContextRecording::drawPosText(const void* inText, size_t byteLength, 916 const SkPoint inPos[], const SkPaint& inPaint) 917 { 918 if (inPaint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) { 919 ALOGE("Unsupported text encoding! %d", inPaint.getTextEncoding()); 920 return; 921 } 922 FloatRect bounds = approximateTextBounds(byteLength / sizeof(uint16_t), inPos, inPaint); 923 bounds.move(m_textOffset); // compensate font rendering-side translates 924 925 const SkPaint* paint = mRecording->recording()->getSkPaint(inPaint); 926 size_t posSize = sizeof(SkPoint) * paint->countText(inText, byteLength); 927 void* text = heap()->alloc(byteLength); 928 SkPoint* pos = (SkPoint*) heap()->alloc(posSize); 929 memcpy(text, inText, byteLength); 930 memcpy(pos, inPos, posSize); 931 appendDrawingOperation(NEW_OP(DrawPosText)(text, byteLength, pos, paint), bounds); 932 } 933 934 void PlatformGraphicsContextRecording::drawMediaButton(const IntRect& rect, RenderSkinMediaButton::MediaButton buttonType, 935 bool translucent, bool drawBackground, 936 const IntRect& thumb) 937 { 938 appendDrawingOperation(NEW_OP(DrawMediaButton)(rect, buttonType, 939 translucent, drawBackground, thumb), rect); 940 } 941 942 void PlatformGraphicsContextRecording::clipState(const FloatRect& clip) 943 { 944 if (mRecordingStateStack.size()) { 945 SkRect mapBounds; 946 mCurrentMatrix->mapRect(&mapBounds, clip); 947 mRecordingStateStack.last().clip(mapBounds); 948 } 949 } 950 951 void PlatformGraphicsContextRecording::pushStateOperation(CanvasState* canvasState) 952 { 953 ALOGV("RECORDING: pushStateOperation: %p(isLayer=%d)", canvasState, canvasState->isTransparencyLayer()); 954 955 RecordingState* parent = mRecordingStateStack.isEmpty() ? 0 : &(mRecordingStateStack.last()); 956 mRecordingStateStack.append(RecordingState(canvasState, parent)); 957 mRecording->recording()->addCanvasState(canvasState); 958 } 959 960 void PlatformGraphicsContextRecording::popStateOperation() 961 { 962 RecordingState state = mRecordingStateStack.last(); 963 mRecordingStateStack.removeLast(); 964 mOperationState = 0; 965 if (!state.mHasDrawing) { 966 ALOGV("RECORDING: popStateOperation is deleting %p(isLayer=%d)", 967 state.mCanvasState, state.mCanvasState->isTransparencyLayer()); 968 mRecording->recording()->removeCanvasState(state.mCanvasState); 969 state.mCanvasState->~CanvasState(); 970 heap()->rewindIfLastAlloc(state.mCanvasState, sizeof(CanvasState)); 971 } else { 972 ALOGV("RECORDING: popStateOperation: %p(isLayer=%d)", 973 state.mCanvasState, state.mCanvasState->isTransparencyLayer()); 974 // Make sure we propagate drawing upwards so we don't delete our parent 975 mRecordingStateStack.last().mHasDrawing = true; 976 } 977 } 978 979 void PlatformGraphicsContextRecording::pushMatrix() 980 { 981 mMatrixStack.append(mMatrixStack.last()); 982 mCurrentMatrix = &(mMatrixStack.last()); 983 } 984 985 void PlatformGraphicsContextRecording::popMatrix() 986 { 987 mMatrixStack.removeLast(); 988 mCurrentMatrix = &(mMatrixStack.last()); 989 } 990 991 IntRect PlatformGraphicsContextRecording::calculateFinalBounds(FloatRect bounds) 992 { 993 if (bounds.isEmpty() && mRecordingStateStack.last().mHasClip) { 994 ALOGV("Empty bounds, but has clip so using that"); 995 return enclosingIntRect(mRecordingStateStack.last().mBounds); 996 } 997 if (m_gc->hasShadow()) { 998 const ShadowRec& shadow = m_state->shadow; 999 if (shadow.blur > 0) 1000 bounds.inflate(ceilf(shadow.blur)); 1001 bounds.setWidth(bounds.width() + abs(shadow.dx)); 1002 bounds.setHeight(bounds.height() + abs(shadow.dy)); 1003 if (shadow.dx < 0) 1004 bounds.move(shadow.dx, 0); 1005 if (shadow.dy < 0) 1006 bounds.move(0, shadow.dy); 1007 // Add a bit extra to deal with rounding and blurring 1008 bounds.inflate(4); 1009 } 1010 if (m_state->strokeStyle != NoStroke) 1011 bounds.inflate(std::min(1.0f, m_state->strokeThickness)); 1012 SkRect translated; 1013 mCurrentMatrix->mapRect(&translated, bounds); 1014 FloatRect ftrect = translated; 1015 if (mRecordingStateStack.last().mHasClip 1016 && !translated.intersect(mRecordingStateStack.last().mBounds)) { 1017 ALOGV("Operation bounds=" FLOAT_RECT_FORMAT " clipped out by clip=" FLOAT_RECT_FORMAT, 1018 FLOAT_RECT_ARGS(ftrect), FLOAT_RECT_ARGS(mRecordingStateStack.last().mBounds)); 1019 return IntRect(); 1020 } 1021 return enclosingIntRect(translated); 1022 } 1023 1024 IntRect PlatformGraphicsContextRecording::calculateCoveredBounds(FloatRect bounds) 1025 { 1026 if (mRecordingStateStack.last().mOpaqueTrackingDisabled 1027 || m_state->alpha != 1.0f 1028 || (m_state->fillShader != 0 && !m_state->fillShader->isOpaque()) 1029 || (m_state->mode != SkXfermode::kSrc_Mode && m_state->mode != SkXfermode::kSrcOver_Mode) 1030 || !mCurrentMatrix->rectStaysRect()) { 1031 return IntRect(); 1032 } 1033 1034 SkRect translated; 1035 mCurrentMatrix->mapRect(&translated, bounds); 1036 FloatRect ftrect = translated; 1037 if (mRecordingStateStack.last().mHasClip 1038 && !translated.intersect(mRecordingStateStack.last().mBounds)) { 1039 ALOGV("Operation opaque area=" FLOAT_RECT_FORMAT " clipped out by clip=" FLOAT_RECT_FORMAT, 1040 FLOAT_RECT_ARGS(ftrect), FLOAT_RECT_ARGS(mRecordingStateStack.last().mBounds)); 1041 return IntRect(); 1042 } 1043 return enclosedIntRect(translated); 1044 } 1045 1046 void PlatformGraphicsContextRecording::appendDrawingOperation( 1047 GraphicsOperation::Operation* operation, const FloatRect& untranslatedBounds) 1048 { 1049 m_isEmpty = false; 1050 RecordingState& state = mRecordingStateStack.last(); 1051 state.mHasDrawing = true; 1052 if (!mOperationState) 1053 mOperationState = mRecording->recording()->getState(m_state); 1054 operation->m_state = mOperationState; 1055 operation->m_canvasState = state.mCanvasState; 1056 1057 WebCore::IntRect ibounds = calculateFinalBounds(untranslatedBounds); 1058 if (ibounds.isEmpty()) { 1059 ALOGV("RECORDING: Operation %s() was clipped out", operation->name()); 1060 operation->~Operation(); 1061 return; 1062 } 1063 #if USE_CLIPPING_PAINTER 1064 if (operation->isOpaque() 1065 && !untranslatedBounds.isEmpty() 1066 && (untranslatedBounds.width() * untranslatedBounds.height() > MIN_TRACKED_OPAQUE_AREA)) { 1067 // if the operation maps to an opaque rect, record the area it will cover 1068 operation->setOpaqueRect(calculateCoveredBounds(untranslatedBounds)); 1069 } 1070 #endif 1071 ALOGV("RECORDING: appendOperation %p->%s() bounds " INT_RECT_FORMAT, operation, operation->name(), 1072 INT_RECT_ARGS(ibounds)); 1073 RecordingData* data = new (heap()) RecordingData(operation, mRecording->recording()->m_nodeCount++); 1074 mRecording->recording()->m_tree.insert(ibounds, data); 1075 } 1076 1077 void PlatformGraphicsContextRecording::appendStateOperation(GraphicsOperation::Operation* operation) 1078 { 1079 ALOGV("RECORDING: appendOperation %p->%s()", operation, operation->name()); 1080 RecordingData* data = new (heap()) RecordingData(operation, mRecording->recording()->m_nodeCount++); 1081 mRecordingStateStack.last().mCanvasState->adoptAndAppend(data); 1082 } 1083 1084 android::LinearAllocator* PlatformGraphicsContextRecording::heap() 1085 { 1086 return mRecording->recording()->heap(); 1087 } 1088 1089 } // WebCore 1090