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