Home | History | Annotate | Download | only in context
      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