DisplayListRenderer.cpp revision a566b7c3aada08d37cf08096c972e3e641bed773
1/*
2 * Copyright (C) 2010 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#define LOG_TAG "OpenGLRenderer"
18
19#include "DisplayListRenderer.h"
20
21namespace android {
22namespace uirenderer {
23
24///////////////////////////////////////////////////////////////////////////////
25// Defines
26///////////////////////////////////////////////////////////////////////////////
27
28#define PATH_HEAP_SIZE 64
29
30///////////////////////////////////////////////////////////////////////////////
31// Helpers
32///////////////////////////////////////////////////////////////////////////////
33
34PathHeap::PathHeap(): mHeap(PATH_HEAP_SIZE * sizeof(SkPath)) {
35}
36
37PathHeap::PathHeap(SkFlattenableReadBuffer& buffer): mHeap(PATH_HEAP_SIZE * sizeof(SkPath)) {
38    int count = buffer.readS32();
39
40    mPaths.setCount(count);
41    SkPath** ptr = mPaths.begin();
42    SkPath* p = (SkPath*) mHeap.allocThrow(count * sizeof(SkPath));
43
44    for (int i = 0; i < count; i++) {
45        new (p) SkPath;
46        p->unflatten(buffer);
47        *ptr++ = p;
48        p++;
49    }
50}
51
52PathHeap::~PathHeap() {
53    SkPath** iter = mPaths.begin();
54    SkPath** stop = mPaths.end();
55    while (iter < stop) {
56        (*iter)->~SkPath();
57        iter++;
58    }
59}
60
61int PathHeap::append(const SkPath& path) {
62    SkPath* p = (SkPath*) mHeap.allocThrow(sizeof(SkPath));
63    new (p) SkPath(path);
64    *mPaths.append() = p;
65    return mPaths.count();
66}
67
68void PathHeap::flatten(SkFlattenableWriteBuffer& buffer) const {
69    int count = mPaths.count();
70
71    buffer.write32(count);
72    SkPath** iter = mPaths.begin();
73    SkPath** stop = mPaths.end();
74    while (iter < stop) {
75        (*iter)->flatten(buffer);
76        iter++;
77    }
78}
79
80///////////////////////////////////////////////////////////////////////////////
81// Display list
82///////////////////////////////////////////////////////////////////////////////
83
84const char* DisplayList::OP_NAMES[] = {
85    "AcquireContext",
86    "ReleaseContext",
87    "Save",
88    "Restore",
89    "RestoreToCount",
90    "SaveLayer",
91    "SaveLayerAlpha",
92    "Translate",
93    "Rotate",
94    "Scale",
95    "Skew",
96    "SetMatrix",
97    "ConcatMatrix",
98    "ClipRect",
99    "DrawDisplayList",
100    "DrawLayer",
101    "DrawBitmap",
102    "DrawBitmapMatrix",
103    "DrawBitmapRect",
104    "DrawBitmapMesh",
105    "DrawPatch",
106    "DrawColor",
107    "DrawRect",
108    "DrawRoundRect",
109    "DrawCircle",
110    "DrawOval",
111    "DrawArc",
112    "DrawPath",
113    "DrawLines",
114    "DrawText",
115    "ResetShader",
116    "SetupShader",
117    "ResetColorFilter",
118    "SetupColorFilter",
119    "ResetShadow",
120    "SetupShadow"
121};
122
123DisplayList::DisplayList(const DisplayListRenderer& recorder) {
124    initFromDisplayListRenderer(recorder);
125}
126
127DisplayList::~DisplayList() {
128    sk_free((void*) mReader.base());
129
130    Caches& caches = Caches::getInstance();
131
132    for (size_t i = 0; i < mBitmapResources.size(); i++) {
133        caches.resourceCache.decrementRefcount(mBitmapResources.itemAt(i));
134    }
135    mBitmapResources.clear();
136
137    for (size_t i = 0; i < mShaders.size(); i++) {
138        caches.resourceCache.decrementRefcount(mShaders.itemAt(i));
139    }
140    mShaders.clear();
141
142    for (size_t i = 0; i < mPaints.size(); i++) {
143        delete mPaints.itemAt(i);
144    }
145    mPaints.clear();
146
147    for (size_t i = 0; i < mMatrices.size(); i++) {
148        delete mMatrices.itemAt(i);
149    }
150    mMatrices.clear();
151
152    if (mPathHeap) {
153        for (int i = 0; i < mPathHeap->count(); i++) {
154            caches.pathCache.removeDeferred(&(*mPathHeap)[i]);
155        }
156        mPathHeap->safeUnref();
157    }
158}
159
160void DisplayList::initFromDisplayListRenderer(const DisplayListRenderer& recorder) {
161    const SkWriter32& writer = recorder.writeStream();
162    init();
163
164    if (writer.size() == 0) {
165        return;
166    }
167
168    size_t size = writer.size();
169    void* buffer = sk_malloc_throw(size);
170    writer.flatten(buffer);
171    mReader.setMemory(buffer, size);
172
173    mRCPlayback.reset(&recorder.mRCRecorder);
174    mRCPlayback.setupBuffer(mReader);
175
176    mTFPlayback.reset(&recorder.mTFRecorder);
177    mTFPlayback.setupBuffer(mReader);
178
179    Caches& caches = Caches::getInstance();
180
181    const Vector<SkBitmap*> &bitmapResources = recorder.getBitmapResources();
182    for (size_t i = 0; i < bitmapResources.size(); i++) {
183        SkBitmap* resource = bitmapResources.itemAt(i);
184        mBitmapResources.add(resource);
185        caches.resourceCache.incrementRefcount(resource);
186    }
187
188    const Vector<SkiaShader*> &shaders = recorder.getShaders();
189    for (size_t i = 0; i < shaders.size(); i++) {
190        SkiaShader* shader = shaders.itemAt(i);
191        mShaders.add(shader);
192        caches.resourceCache.incrementRefcount(shader);
193    }
194
195    const Vector<SkPaint*> &paints = recorder.getPaints();
196    for (size_t i = 0; i < paints.size(); i++) {
197        mPaints.add(paints.itemAt(i));
198    }
199
200    const Vector<SkMatrix*> &matrices = recorder.getMatrices();
201    for (size_t i = 0; i < matrices.size(); i++) {
202        mMatrices.add(matrices.itemAt(i));
203    }
204
205    mPathHeap = recorder.mPathHeap;
206    if (mPathHeap) {
207        mPathHeap->safeRef();
208    }
209}
210
211void DisplayList::init() {
212    mPathHeap = NULL;
213}
214
215void DisplayList::replay(OpenGLRenderer& renderer, uint32_t level) {
216    TextContainer text;
217    mReader.rewind();
218
219#if DEBUG_DISPLAY_LIST
220    uint32_t count = (level + 1) * 2;
221    char indent[count + 1];
222    for (uint32_t i = 0; i < count; i++) {
223        indent[i] = ' ';
224    }
225    indent[count] = '\0';
226    DISPLAY_LIST_LOGD("%sStart display list (%p)", (char*) indent + 2, this);
227#endif
228
229    int saveCount = renderer.getSaveCount() - 1;
230    while (!mReader.eof()) {
231        int op = mReader.readInt();
232        DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]);
233
234        switch (op) {
235            case AcquireContext: {
236                renderer.acquireContext();
237            }
238            break;
239            case ReleaseContext: {
240                renderer.releaseContext();
241            }
242            break;
243            case Save: {
244                renderer.save(getInt());
245            }
246            break;
247            case Restore: {
248                renderer.restore();
249            }
250            break;
251            case RestoreToCount: {
252                renderer.restoreToCount(saveCount + getInt());
253            }
254            break;
255            case SaveLayer: {
256                renderer.saveLayer(getFloat(), getFloat(), getFloat(), getFloat(),
257                        getPaint(), getInt());
258            }
259            break;
260            case SaveLayerAlpha: {
261                renderer.saveLayerAlpha(getFloat(), getFloat(), getFloat(), getFloat(),
262                        getInt(), getInt());
263            }
264            break;
265            case Translate: {
266                renderer.translate(getFloat(), getFloat());
267            }
268            break;
269            case Rotate: {
270                renderer.rotate(getFloat());
271            }
272            break;
273            case Scale: {
274                renderer.scale(getFloat(), getFloat());
275            }
276            break;
277            case Skew: {
278                renderer.skew(getFloat(), getFloat());
279            }
280            break;
281            case SetMatrix: {
282                renderer.setMatrix(getMatrix());
283            }
284            break;
285            case ConcatMatrix: {
286                renderer.concatMatrix(getMatrix());
287            }
288            break;
289            case ClipRect: {
290                renderer.clipRect(getFloat(), getFloat(), getFloat(), getFloat(),
291                        (SkRegion::Op) getInt());
292            }
293            break;
294            case DrawDisplayList: {
295                renderer.drawDisplayList(getDisplayList(), level + 1);
296            }
297            break;
298            case DrawLayer: {
299                renderer.drawLayer((Layer*) getInt(), getFloat(), getFloat(), getPaint());
300            }
301            break;
302            case DrawBitmap: {
303                renderer.drawBitmap(getBitmap(), getFloat(), getFloat(), getPaint());
304            }
305            break;
306            case DrawBitmapMatrix: {
307                renderer.drawBitmap(getBitmap(), getMatrix(), getPaint());
308            }
309            break;
310            case DrawBitmapRect: {
311                renderer.drawBitmap(getBitmap(), getFloat(), getFloat(), getFloat(), getFloat(),
312                        getFloat(), getFloat(), getFloat(), getFloat(), getPaint());
313            }
314            break;
315            case DrawBitmapMesh: {
316                int verticesCount = 0;
317                uint32_t colorsCount = 0;
318
319                SkBitmap* bitmap = getBitmap();
320                uint32_t meshWidth = getInt();
321                uint32_t meshHeight = getInt();
322                float* vertices = getFloats(verticesCount);
323                bool hasColors = getInt();
324                int* colors = hasColors ? getInts(colorsCount) : NULL;
325
326                renderer.drawBitmapMesh(bitmap, meshWidth, meshHeight, vertices, colors, getPaint());
327            }
328            break;
329            case DrawPatch: {
330                int32_t* xDivs = NULL;
331                int32_t* yDivs = NULL;
332                uint32_t* colors = NULL;
333                uint32_t xDivsCount = 0;
334                uint32_t yDivsCount = 0;
335                int8_t numColors = 0;
336
337                SkBitmap* bitmap = getBitmap();
338
339                xDivs = getInts(xDivsCount);
340                yDivs = getInts(yDivsCount);
341                colors = getUInts(numColors);
342
343                renderer.drawPatch(bitmap, xDivs, yDivs, colors, xDivsCount, yDivsCount,
344                        numColors, getFloat(), getFloat(), getFloat(), getFloat(), getPaint());
345            }
346            break;
347            case DrawColor: {
348                renderer.drawColor(getInt(), (SkXfermode::Mode) getInt());
349            }
350            break;
351            case DrawRect: {
352                renderer.drawRect(getFloat(), getFloat(), getFloat(), getFloat(), getPaint());
353            }
354            break;
355            case DrawRoundRect: {
356                renderer.drawRoundRect(getFloat(), getFloat(), getFloat(), getFloat(),
357                        getFloat(), getFloat(), getPaint());
358            }
359            break;
360            case DrawCircle: {
361                renderer.drawCircle(getFloat(), getFloat(), getFloat(), getPaint());
362            }
363            break;
364            case DrawOval: {
365                renderer.drawOval(getFloat(), getFloat(), getFloat(), getFloat(), getPaint());
366            }
367            break;
368            case DrawArc: {
369                renderer.drawArc(getFloat(), getFloat(), getFloat(), getFloat(),
370                        getFloat(), getFloat(), getInt() == 1, getPaint());
371            }
372            break;
373            case DrawPath: {
374                renderer.drawPath(getPath(), getPaint());
375            }
376            break;
377            case DrawLines: {
378                int count = 0;
379                float* points = getFloats(count);
380                renderer.drawLines(points, count, getPaint());
381            }
382            break;
383            case DrawText: {
384                getText(&text);
385                renderer.drawText(text.text(), text.length(), getInt(),
386                        getFloat(), getFloat(), getPaint());
387            }
388            break;
389            case ResetShader: {
390                renderer.resetShader();
391            }
392            break;
393            case SetupShader: {
394                renderer.setupShader(getShader());
395            }
396            break;
397            case ResetColorFilter: {
398                renderer.resetColorFilter();
399            }
400            break;
401            case SetupColorFilter: {
402                renderer.setupColorFilter(getColorFilter());
403            }
404            break;
405            case ResetShadow: {
406                renderer.resetShadow();
407            }
408            break;
409            case SetupShadow: {
410                renderer.setupShadow(getFloat(), getFloat(), getFloat(), getInt());
411            }
412            break;
413        }
414    }
415
416    DISPLAY_LIST_LOGD("%sDone", (char*) indent + 2);
417}
418
419///////////////////////////////////////////////////////////////////////////////
420// Base structure
421///////////////////////////////////////////////////////////////////////////////
422
423DisplayListRenderer::DisplayListRenderer():
424        mHeap(HEAP_BLOCK_SIZE), mWriter(MIN_WRITER_SIZE) {
425    mPathHeap = NULL;
426    mDisplayList = NULL;
427}
428
429DisplayListRenderer::~DisplayListRenderer() {
430    reset();
431}
432
433void DisplayListRenderer::reset() {
434    if (mPathHeap) {
435        mPathHeap->unref();
436        mPathHeap = NULL;
437    }
438
439    mWriter.reset();
440    mHeap.reset();
441
442    mRCRecorder.reset();
443    mTFRecorder.reset();
444
445    Caches& caches = Caches::getInstance();
446    for (size_t i = 0; i < mBitmapResources.size(); i++) {
447        SkBitmap* resource = mBitmapResources.itemAt(i);
448        caches.resourceCache.decrementRefcount(resource);
449    }
450    mBitmapResources.clear();
451
452    for (size_t i = 0; i < mShaders.size(); i++) {
453       caches.resourceCache.decrementRefcount(mShaders.itemAt(i));
454    }
455    mShaders.clear();
456    mShaderMap.clear();
457
458    mPaints.clear();
459    mPaintMap.clear();
460    mMatrices.clear();
461}
462
463///////////////////////////////////////////////////////////////////////////////
464// Operations
465///////////////////////////////////////////////////////////////////////////////
466
467DisplayList* DisplayListRenderer::getDisplayList() {
468    if (mDisplayList == NULL) {
469        mDisplayList = new DisplayList(*this);
470    } else {
471        mDisplayList->initFromDisplayListRenderer(*this);
472    }
473    return mDisplayList;
474}
475
476void DisplayListRenderer::setViewport(int width, int height) {
477    mOrthoMatrix.loadOrtho(0, width, height, 0, -1, 1);
478
479    mWidth = width;
480    mHeight = height;
481}
482
483void DisplayListRenderer::prepare(bool opaque) {
484    mSnapshot = new Snapshot(mFirstSnapshot,
485            SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
486    mSaveCount = 1;
487    mSnapshot->setClip(0.0f, 0.0f, mWidth, mHeight);
488    mRestoreSaveCount = -1;
489}
490
491void DisplayListRenderer::finish() {
492    insertRestoreToCount();
493    OpenGLRenderer::finish();
494}
495
496void DisplayListRenderer::acquireContext() {
497    addOp(DisplayList::AcquireContext);
498    OpenGLRenderer::acquireContext();
499}
500
501void DisplayListRenderer::releaseContext() {
502    addOp(DisplayList::ReleaseContext);
503    OpenGLRenderer::releaseContext();
504}
505
506int DisplayListRenderer::save(int flags) {
507    addOp(DisplayList::Save);
508    addInt(flags);
509    return OpenGLRenderer::save(flags);
510}
511
512void DisplayListRenderer::restore() {
513    addOp(DisplayList::Restore);
514    OpenGLRenderer::restore();
515}
516
517void DisplayListRenderer::restoreToCount(int saveCount) {
518    mRestoreSaveCount = saveCount;
519    OpenGLRenderer::restoreToCount(saveCount);
520}
521
522int DisplayListRenderer::saveLayer(float left, float top, float right, float bottom,
523        SkPaint* p, int flags) {
524    addOp(DisplayList::SaveLayer);
525    addBounds(left, top, right, bottom);
526    addPaint(p);
527    addInt(flags);
528    return OpenGLRenderer::save(flags);
529}
530
531int DisplayListRenderer::saveLayerAlpha(float left, float top, float right, float bottom,
532        int alpha, int flags) {
533    addOp(DisplayList::SaveLayerAlpha);
534    addBounds(left, top, right, bottom);
535    addInt(alpha);
536    addInt(flags);
537    return OpenGLRenderer::save(flags);
538}
539
540void DisplayListRenderer::translate(float dx, float dy) {
541    addOp(DisplayList::Translate);
542    addPoint(dx, dy);
543    OpenGLRenderer::translate(dx, dy);
544}
545
546void DisplayListRenderer::rotate(float degrees) {
547    addOp(DisplayList::Rotate);
548    addFloat(degrees);
549    OpenGLRenderer::rotate(degrees);
550}
551
552void DisplayListRenderer::scale(float sx, float sy) {
553    addOp(DisplayList::Scale);
554    addPoint(sx, sy);
555    OpenGLRenderer::scale(sx, sy);
556}
557
558void DisplayListRenderer::skew(float sx, float sy) {
559    addOp(DisplayList::Skew);
560    addPoint(sx, sy);
561    OpenGLRenderer::skew(sx, sy);
562}
563
564void DisplayListRenderer::setMatrix(SkMatrix* matrix) {
565    addOp(DisplayList::SetMatrix);
566    addMatrix(matrix);
567    OpenGLRenderer::setMatrix(matrix);
568}
569
570void DisplayListRenderer::concatMatrix(SkMatrix* matrix) {
571    addOp(DisplayList::ConcatMatrix);
572    addMatrix(matrix);
573    OpenGLRenderer::concatMatrix(matrix);
574}
575
576bool DisplayListRenderer::clipRect(float left, float top, float right, float bottom,
577        SkRegion::Op op) {
578    addOp(DisplayList::ClipRect);
579    addBounds(left, top, right, bottom);
580    addInt(op);
581    return OpenGLRenderer::clipRect(left, top, right, bottom, op);
582}
583
584void DisplayListRenderer::drawDisplayList(DisplayList* displayList, uint32_t level) {
585    addOp(DisplayList::DrawDisplayList);
586    addDisplayList(displayList);
587}
588
589void DisplayListRenderer::drawLayer(Layer* layer, float x, float y, SkPaint* paint) {
590    addOp(DisplayList::DrawLayer);
591    addInt((int) layer);
592    addPoint(x, y);
593    addPaint(paint);
594}
595
596void DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float left, float top,
597        SkPaint* paint) {
598    addOp(DisplayList::DrawBitmap);
599    addBitmap(bitmap);
600    addPoint(left, top);
601    addPaint(paint);
602}
603
604void DisplayListRenderer::drawBitmap(SkBitmap* bitmap, SkMatrix* matrix,
605        SkPaint* paint) {
606    addOp(DisplayList::DrawBitmapMatrix);
607    addBitmap(bitmap);
608    addMatrix(matrix);
609    addPaint(paint);
610}
611
612void DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop,
613        float srcRight, float srcBottom, float dstLeft, float dstTop,
614        float dstRight, float dstBottom, SkPaint* paint) {
615    addOp(DisplayList::DrawBitmapRect);
616    addBitmap(bitmap);
617    addBounds(srcLeft, srcTop, srcRight, srcBottom);
618    addBounds(dstLeft, dstTop, dstRight, dstBottom);
619    addPaint(paint);
620}
621
622void DisplayListRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int meshHeight,
623        float* vertices, int* colors, SkPaint* paint) {
624    addOp(DisplayList::DrawBitmapMesh);
625    addBitmap(bitmap);
626    addInt(meshWidth);
627    addInt(meshHeight);
628    addFloats(vertices, (meshWidth + 1) * (meshHeight + 1) * 2);
629    if (colors) {
630        addInt(1);
631        addInts(colors, (meshWidth + 1) * (meshHeight + 1));
632    } else {
633        addInt(0);
634    }
635    addPaint(paint);
636}
637
638void DisplayListRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs,
639        const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors,
640        float left, float top, float right, float bottom, SkPaint* paint) {
641    addOp(DisplayList::DrawPatch);
642    addBitmap(bitmap);
643    addInts(xDivs, width);
644    addInts(yDivs, height);
645    addUInts(colors, numColors);
646    addBounds(left, top, right, bottom);
647    addPaint(paint);
648}
649
650void DisplayListRenderer::drawColor(int color, SkXfermode::Mode mode) {
651    addOp(DisplayList::DrawColor);
652    addInt(color);
653    addInt(mode);
654}
655
656void DisplayListRenderer::drawRect(float left, float top, float right, float bottom,
657        SkPaint* paint) {
658    addOp(DisplayList::DrawRect);
659    addBounds(left, top, right, bottom);
660    addPaint(paint);
661}
662
663void DisplayListRenderer::drawRoundRect(float left, float top, float right, float bottom,
664            float rx, float ry, SkPaint* paint) {
665    addOp(DisplayList::DrawRoundRect);
666    addBounds(left, top, right, bottom);
667    addPoint(rx, ry);
668    addPaint(paint);
669}
670
671void DisplayListRenderer::drawCircle(float x, float y, float radius, SkPaint* paint) {
672    addOp(DisplayList::DrawCircle);
673    addPoint(x, y);
674    addFloat(radius);
675    addPaint(paint);
676}
677
678void DisplayListRenderer::drawOval(float left, float top, float right, float bottom,
679        SkPaint* paint) {
680    addOp(DisplayList::DrawOval);
681    addBounds(left, top, right, bottom);
682    addPaint(paint);
683}
684
685void DisplayListRenderer::drawArc(float left, float top, float right, float bottom,
686        float startAngle, float sweepAngle, bool useCenter, SkPaint* paint) {
687    addOp(DisplayList::DrawOval);
688    addBounds(left, top, right, bottom);
689    addPoint(startAngle, sweepAngle);
690    addInt(useCenter ? 1 : 0);
691    addPaint(paint);
692}
693
694void DisplayListRenderer::drawPath(SkPath* path, SkPaint* paint) {
695    addOp(DisplayList::DrawPath);
696    addPath(path);
697    addPaint(paint);
698}
699
700void DisplayListRenderer::drawLines(float* points, int count, SkPaint* paint) {
701    addOp(DisplayList::DrawLines);
702    addFloats(points, count);
703    addPaint(paint);
704}
705
706void DisplayListRenderer::drawText(const char* text, int bytesCount, int count,
707        float x, float y, SkPaint* paint) {
708    addOp(DisplayList::DrawText);
709    addText(text, bytesCount);
710    addInt(count);
711    addPoint(x, y);
712    addPaint(paint);
713}
714
715void DisplayListRenderer::resetShader() {
716    addOp(DisplayList::ResetShader);
717}
718
719void DisplayListRenderer::setupShader(SkiaShader* shader) {
720    addOp(DisplayList::SetupShader);
721    addShader(shader);
722}
723
724void DisplayListRenderer::resetColorFilter() {
725    addOp(DisplayList::ResetColorFilter);
726}
727
728void DisplayListRenderer::setupColorFilter(SkiaColorFilter* filter) {
729    addOp(DisplayList::SetupColorFilter);
730    addColorFilter(filter);
731}
732
733void DisplayListRenderer::resetShadow() {
734    addOp(DisplayList::ResetShadow);
735}
736
737void DisplayListRenderer::setupShadow(float radius, float dx, float dy, int color) {
738    addOp(DisplayList::SetupShadow);
739    addFloat(radius);
740    addPoint(dx, dy);
741    addInt(color);
742}
743
744}; // namespace uirenderer
745}; // namespace android
746