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