1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "SkiaRecordingCanvas.h"
18
19#include "Layer.h"
20#include "RenderNode.h"
21#include "LayerDrawable.h"
22#include "NinePatchUtils.h"
23#include "pipeline/skia/AnimatedDrawables.h"
24#include <SkImagePriv.h>
25
26namespace android {
27namespace uirenderer {
28namespace skiapipeline {
29
30// ----------------------------------------------------------------------------
31// Recording Canvas Setup
32// ----------------------------------------------------------------------------
33
34void SkiaRecordingCanvas::initDisplayList(uirenderer::RenderNode* renderNode, int width,
35        int height) {
36    mCurrentBarrier = nullptr;
37    SkASSERT(mDisplayList.get() == nullptr);
38
39    if (renderNode) {
40        mDisplayList = renderNode->detachAvailableList();
41    }
42    if (!mDisplayList) {
43        mDisplayList.reset(new SkiaDisplayList());
44    }
45
46    mDisplayList->attachRecorder(&mRecorder, SkIRect::MakeWH(width, height));
47    SkiaCanvas::reset(&mRecorder);
48}
49
50uirenderer::DisplayList* SkiaRecordingCanvas::finishRecording() {
51    // close any existing chunks if necessary
52    insertReorderBarrier(false);
53    mRecorder.restoreToCount(1);
54    return mDisplayList.release();
55}
56
57// ----------------------------------------------------------------------------
58// Recording Canvas draw operations: View System
59// ----------------------------------------------------------------------------
60
61void SkiaRecordingCanvas::drawRoundRect(uirenderer::CanvasPropertyPrimitive* left,
62        uirenderer::CanvasPropertyPrimitive* top, uirenderer::CanvasPropertyPrimitive* right,
63        uirenderer::CanvasPropertyPrimitive* bottom, uirenderer::CanvasPropertyPrimitive* rx,
64        uirenderer::CanvasPropertyPrimitive* ry, uirenderer::CanvasPropertyPaint* paint) {
65    drawDrawable(mDisplayList->allocateDrawable<AnimatedRoundRect>(left, top, right, bottom,
66            rx, ry, paint));
67}
68
69void SkiaRecordingCanvas::drawCircle(uirenderer::CanvasPropertyPrimitive* x,
70        uirenderer::CanvasPropertyPrimitive* y, uirenderer::CanvasPropertyPrimitive* radius,
71        uirenderer::CanvasPropertyPaint* paint) {
72    drawDrawable(mDisplayList->allocateDrawable<AnimatedCircle>(x, y, radius, paint));
73}
74
75void SkiaRecordingCanvas::insertReorderBarrier(bool enableReorder) {
76    if (nullptr != mCurrentBarrier) {
77        // finish off the existing chunk
78        SkDrawable* drawable =
79                mDisplayList->allocateDrawable<EndReorderBarrierDrawable>(
80                mCurrentBarrier);
81        mCurrentBarrier = nullptr;
82        drawDrawable(drawable);
83    }
84    if (enableReorder) {
85        mCurrentBarrier = (StartReorderBarrierDrawable*)
86                mDisplayList->allocateDrawable<StartReorderBarrierDrawable>(
87                mDisplayList.get());
88        drawDrawable(mCurrentBarrier);
89    }
90}
91
92void SkiaRecordingCanvas::drawLayer(uirenderer::DeferredLayerUpdater* layerUpdater) {
93    if (layerUpdater != nullptr && layerUpdater->backingLayer() != nullptr) {
94        uirenderer::Layer* layer = layerUpdater->backingLayer();
95        sk_sp<SkDrawable> drawable(new LayerDrawable(layer));
96        drawDrawable(drawable.get());
97    }
98}
99
100void SkiaRecordingCanvas::drawRenderNode(uirenderer::RenderNode* renderNode) {
101    // record the child node
102    mDisplayList->mChildNodes.emplace_back(renderNode, asSkCanvas(), true, mCurrentBarrier);
103    auto& renderNodeDrawable = mDisplayList->mChildNodes.back();
104    drawDrawable(&renderNodeDrawable);
105
106    // use staging property, since recording on UI thread
107    if (renderNode->stagingProperties().isProjectionReceiver()) {
108        mDisplayList->mProjectionReceiver = &renderNodeDrawable;
109        // set projectionReceiveIndex so that RenderNode.hasProjectionReceiver returns true
110        mDisplayList->projectionReceiveIndex = mDisplayList->mChildNodes.size() - 1;
111    }
112}
113
114void SkiaRecordingCanvas::callDrawGLFunction(Functor* functor,
115        uirenderer::GlFunctorLifecycleListener* listener) {
116    mDisplayList->mChildFunctors.emplace_back(functor, listener, asSkCanvas());
117    drawDrawable(&mDisplayList->mChildFunctors.back());
118}
119
120class VectorDrawable : public SkDrawable {
121 public:
122    VectorDrawable(VectorDrawableRoot* tree) : mRoot(tree) {}
123
124 protected:
125     virtual SkRect onGetBounds() override {
126         return SkRect::MakeLargest();
127     }
128     virtual void onDraw(SkCanvas* canvas) override {
129         Bitmap& hwuiBitmap = mRoot->getBitmapUpdateIfDirty();
130         SkBitmap bitmap;
131         hwuiBitmap.getSkBitmap(&bitmap);
132         SkPaint* paint = mRoot->getPaint();
133         canvas->drawBitmapRect(bitmap, mRoot->mutateProperties()->getBounds(), paint);
134         /*
135          * TODO we can draw this directly but need to address the following...
136          *
137          * 1) Add drawDirect(SkCanvas*) to VectorDrawableRoot
138          * 2) fix VectorDrawable.cpp's Path::draw to not make a temporary path
139          *    so that we don't break caching
140          * 3) figure out how to set path's as volatile during animation
141          * 4) if mRoot->getPaint() != null either promote to layer (during
142          *    animation) or cache in SkSurface (for static content)
143          *
144          */
145     }
146
147 private:
148    sp<VectorDrawableRoot> mRoot;
149};
150
151void SkiaRecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
152    drawDrawable(mDisplayList->allocateDrawable<VectorDrawable>(tree));
153    mDisplayList->mVectorDrawables.push_back(tree);
154}
155
156// ----------------------------------------------------------------------------
157// Recording Canvas draw operations: Bitmaps
158// ----------------------------------------------------------------------------
159
160inline static const SkPaint* nonAAPaint(const SkPaint* origPaint, SkPaint* tmpPaint) {
161    if (origPaint && origPaint->isAntiAlias()) {
162        *tmpPaint = *origPaint;
163        tmpPaint->setAntiAlias(false);
164        return tmpPaint;
165    } else {
166        return origPaint;
167    }
168}
169
170void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) {
171    SkBitmap skBitmap;
172    bitmap.getSkBitmap(&skBitmap);
173
174    sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(skBitmap, kNever_SkCopyPixelsMode);
175    if (!skBitmap.isImmutable()) {
176        mDisplayList->mMutableImages.push_back(image.get());
177    }
178    SkPaint tmpPaint;
179    mRecorder.drawImage(image, left, top, nonAAPaint(paint, &tmpPaint));
180}
181
182void SkiaRecordingCanvas::drawBitmap(Bitmap& hwuiBitmap, const SkMatrix& matrix,
183        const SkPaint* paint) {
184    SkBitmap bitmap;
185    hwuiBitmap.getSkBitmap(&bitmap);
186    SkAutoCanvasRestore acr(&mRecorder, true);
187    concat(matrix);
188    sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode);
189    if (!bitmap.isImmutable()) {
190        mDisplayList->mMutableImages.push_back(image.get());
191    }
192    SkPaint tmpPaint;
193    mRecorder.drawImage(image, 0, 0, nonAAPaint(paint, &tmpPaint));
194}
195
196void SkiaRecordingCanvas::drawBitmap(Bitmap& hwuiBitmap, float srcLeft, float srcTop,
197        float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight,
198        float dstBottom, const SkPaint* paint) {
199    SkBitmap bitmap;
200    hwuiBitmap.getSkBitmap(&bitmap);
201    SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
202    SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
203    sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode);
204    if (!bitmap.isImmutable()) {
205        mDisplayList->mMutableImages.push_back(image.get());
206    }
207    SkPaint tmpPaint;
208    mRecorder.drawImageRect(image, srcRect, dstRect, nonAAPaint(paint, &tmpPaint));
209}
210
211void SkiaRecordingCanvas::drawNinePatch(Bitmap& hwuiBitmap, const Res_png_9patch& chunk,
212        float dstLeft, float dstTop, float dstRight, float dstBottom, const SkPaint* paint) {
213    SkBitmap bitmap;
214    hwuiBitmap.getSkBitmap(&bitmap);
215
216    SkCanvas::Lattice lattice;
217    NinePatchUtils::SetLatticeDivs(&lattice, chunk, bitmap.width(), bitmap.height());
218
219    lattice.fFlags = nullptr;
220    int numFlags = 0;
221    if (chunk.numColors > 0 && chunk.numColors == NinePatchUtils::NumDistinctRects(lattice)) {
222        // We can expect the framework to give us a color for every distinct rect.
223        // Skia requires placeholder flags for degenerate rects.
224        numFlags = (lattice.fXCount + 1) * (lattice.fYCount + 1);
225    }
226
227    SkAutoSTMalloc<25, SkCanvas::Lattice::Flags> flags(numFlags);
228    if (numFlags > 0) {
229        NinePatchUtils::SetLatticeFlags(&lattice, flags.get(), numFlags, chunk);
230    }
231
232    lattice.fBounds = nullptr;
233    SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
234    sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode);
235    if (!bitmap.isImmutable()) {
236        mDisplayList->mMutableImages.push_back(image.get());
237    }
238
239    SkPaint tmpPaint;
240    mRecorder.drawImageLattice(image.get(), lattice, dst, nonAAPaint(paint, &tmpPaint));
241}
242
243}; // namespace skiapipeline
244}; // namespace uirenderer
245}; // namespace android
246