DisplayListOp.h revision 4494599e5a4931426d5649e1d1c4f9db83824ae8
1/*
2 * Copyright (C) 2013 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#ifndef ANDROID_HWUI_DISPLAY_OPERATION_H
18#define ANDROID_HWUI_DISPLAY_OPERATION_H
19
20#ifndef LOG_TAG
21    #define LOG_TAG "OpenGLRenderer"
22#endif
23
24#include <SkXfermode.h>
25
26#include <private/hwui/DrawGlInfo.h>
27
28#include "OpenGLRenderer.h"
29#include "DeferredDisplayList.h"
30#include "DisplayListRenderer.h"
31#include "utils/LinearAllocator.h"
32
33#define CRASH() do { \
34    *(int *)(uintptr_t)0xbbadbeef = 0; \
35    ((void(*)())0)(); /* More reliable, but doesn't say BBADBEEF */ \
36} while(false)
37
38#define MATRIX_STRING "[%.2f %.2f %.2f] [%.2f %.2f %.2f] [%.2f %.2f %.2f]"
39#define MATRIX_ARGS(m) \
40    m->get(0), m->get(1), m->get(2), \
41    m->get(3), m->get(4), m->get(5), \
42    m->get(6), m->get(7), m->get(8)
43#define RECT_STRING "%.2f %.2f %.2f %.2f"
44#define RECT_ARGS(r) \
45    r.left, r.top, r.right, r.bottom
46
47// Use OP_LOG for logging with arglist, OP_LOGS if just printing char*
48#define OP_LOGS(s) OP_LOG("%s", s)
49#define OP_LOG(s, ...) ALOGD( "%*s" s, level * 2, "", __VA_ARGS__ )
50
51namespace android {
52namespace uirenderer {
53
54/**
55 * Structure for storing canvas operations when they are recorded into a DisplayList, so that they
56 * may be replayed to an OpenGLRenderer.
57 *
58 * To avoid individual memory allocations, DisplayListOps may only be allocated into a
59 * LinearAllocator's managed memory buffers.  Each pointer held by a DisplayListOp is either a
60 * pointer into memory also allocated in the LinearAllocator (mostly for text and float buffers) or
61 * references a externally refcounted object (Sk... and Skia... objects). ~DisplayListOp() is
62 * never called as LinearAllocators are simply discarded, so no memory management should be done in
63 * this class.
64 */
65class DisplayListOp {
66public:
67    // These objects should always be allocated with a LinearAllocator, and never destroyed/deleted.
68    // standard new() intentionally not implemented, and delete/deconstructor should never be used.
69    virtual ~DisplayListOp() { CRASH(); }
70    static void operator delete(void* ptr) { CRASH(); }
71    /** static void* operator new(size_t size); PURPOSELY OMITTED **/
72    static void* operator new(size_t size, LinearAllocator& allocator) {
73        return allocator.alloc(size);
74    }
75
76    enum OpLogFlag {
77        kOpLogFlag_Recurse = 0x1,
78        kOpLogFlag_JSON = 0x2 // TODO: add?
79    };
80
81    virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level) = 0;
82
83    virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level) = 0;
84
85    virtual void output(int level, uint32_t logFlags = 0) = 0;
86
87    // NOTE: it would be nice to declare constants and overriding the implementation in each op to
88    // point at the constants, but that seems to require a .cpp file
89    virtual const char* name() = 0;
90
91    /**
92     * Stores the relevant canvas state of the object between deferral and replay (if the canvas
93     * state supports being stored) See OpenGLRenderer::simpleClipAndState()
94     *
95     * TODO: don't reserve space for StateOps that won't be deferred
96     */
97    DeferredDisplayState state;
98
99};
100
101class StateOp : public DisplayListOp {
102public:
103    StateOp() {};
104
105    virtual ~StateOp() {}
106
107    virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level) {
108        // default behavior only affects immediate, deferrable state, issue directly to renderer
109        applyState(deferStruct.mRenderer, saveCount);
110    }
111
112    /**
113     * State operations are applied directly to the renderer, but can cause the deferred drawing op
114     * list to flush
115     */
116    virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level) {
117        applyState(replayStruct.mRenderer, saveCount);
118    }
119
120    virtual void applyState(OpenGLRenderer& renderer, int saveCount) = 0;
121};
122
123class DrawOp : public DisplayListOp {
124public:
125    DrawOp(SkPaint* paint)
126            : mPaint(paint), mQuickRejected(false) {}
127
128    virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level) {
129        if (mQuickRejected &&
130                CC_LIKELY(deferStruct.mReplayFlags & DisplayList::kReplayFlag_ClipChildren)) {
131            return;
132        }
133
134        if (!getLocalBounds(state.mBounds)) {
135            // empty bounds signify bounds can't be calculated
136            state.mBounds.setEmpty();
137        }
138
139        deferStruct.mDeferredList.addDrawOp(deferStruct.mRenderer, this);
140    }
141
142    virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level) {
143        if (mQuickRejected &&
144                CC_LIKELY(replayStruct.mReplayFlags & DisplayList::kReplayFlag_ClipChildren)) {
145            return;
146        }
147
148        replayStruct.mDrawGlStatus |= applyDraw(replayStruct.mRenderer, replayStruct.mDirty, level);
149    }
150
151    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) = 0;
152
153    virtual void onDrawOpDeferred(OpenGLRenderer& renderer) {
154    }
155
156    // returns true if bounds exist
157    virtual bool getLocalBounds(Rect& localBounds) { return false; }
158
159    // TODO: better refine localbounds usage
160    void setQuickRejected(bool quickRejected) { mQuickRejected = quickRejected; }
161    bool getQuickRejected() { return mQuickRejected; }
162
163    /** Batching disabled by default, turned on for individual ops */
164    virtual DeferredDisplayList::OpBatchId getBatchId() {
165        return DeferredDisplayList::kOpBatch_None;
166    }
167
168    float strokeWidthOutset() { return mPaint->getStrokeWidth() * 0.5f; }
169
170protected:
171    SkPaint* getPaint(OpenGLRenderer& renderer) {
172        return renderer.filterPaint(mPaint);
173    }
174
175    SkPaint* mPaint; // should be accessed via getPaint() when applying
176    bool mQuickRejected;
177};
178
179class DrawBoundedOp : public DrawOp {
180public:
181    DrawBoundedOp(float left, float top, float right, float bottom, SkPaint* paint)
182            : DrawOp(paint), mLocalBounds(left, top, right, bottom) {}
183
184    // Calculates bounds as smallest rect encompassing all points
185    // NOTE: requires at least 1 vertex, and doesn't account for stroke size (should be handled in
186    // subclass' constructor)
187    DrawBoundedOp(const float* points, int count, SkPaint* paint)
188            : DrawOp(paint), mLocalBounds(points[0], points[1], points[0], points[1]) {
189        for (int i = 2; i < count; i += 2) {
190            mLocalBounds.left = fminf(mLocalBounds.left, points[i]);
191            mLocalBounds.right = fmaxf(mLocalBounds.right, points[i]);
192            mLocalBounds.top = fminf(mLocalBounds.top, points[i + 1]);
193            mLocalBounds.bottom = fmaxf(mLocalBounds.bottom, points[i + 1]);
194        }
195    }
196
197    // default empty constructor for bounds, to be overridden in child constructor body
198    DrawBoundedOp(SkPaint* paint)
199            : DrawOp(paint) {}
200
201    bool getLocalBounds(Rect& localBounds) {
202        localBounds.set(mLocalBounds);
203        return true;
204    }
205
206protected:
207    Rect mLocalBounds; // displayed area in LOCAL coord. doesn't incorporate stroke, so check paint
208};
209
210///////////////////////////////////////////////////////////////////////////////
211// STATE OPERATIONS - these may affect the state of the canvas/renderer, but do
212//         not directly draw or alter output
213///////////////////////////////////////////////////////////////////////////////
214
215class SaveOp : public StateOp {
216    friend class DisplayList; // give DisplayList private constructor/reinit access
217public:
218    SaveOp(int flags)
219            : mFlags(flags) {}
220
221    virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level) {
222        int newSaveCount = deferStruct.mRenderer.save(mFlags);
223        deferStruct.mDeferredList.addSave(deferStruct.mRenderer, this, newSaveCount);
224    }
225
226    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
227        renderer.save(mFlags);
228    }
229
230    virtual void output(int level, uint32_t logFlags) {
231        OP_LOG("Save flags %x", mFlags);
232    }
233
234    virtual const char* name() { return "Save"; }
235
236    int getFlags() const { return mFlags; }
237private:
238    SaveOp() {}
239    DisplayListOp* reinit(int flags) {
240        mFlags = flags;
241        return this;
242    }
243
244    int mFlags;
245};
246
247class RestoreToCountOp : public StateOp {
248    friend class DisplayList; // give DisplayList private constructor/reinit access
249public:
250    RestoreToCountOp(int count)
251            : mCount(count) {}
252
253    virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level) {
254        deferStruct.mDeferredList.addRestoreToCount(deferStruct.mRenderer, saveCount + mCount);
255        deferStruct.mRenderer.restoreToCount(saveCount + mCount);
256    }
257
258    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
259        renderer.restoreToCount(saveCount + mCount);
260    }
261
262    virtual void output(int level, uint32_t logFlags) {
263        OP_LOG("Restore to count %d", mCount);
264    }
265
266    virtual const char* name() { return "RestoreToCount"; }
267
268private:
269    RestoreToCountOp() {}
270    DisplayListOp* reinit(int count) {
271        mCount = count;
272        return this;
273    }
274
275    int mCount;
276};
277
278class SaveLayerOp : public StateOp {
279    friend class DisplayList; // give DisplayList private constructor/reinit access
280public:
281    SaveLayerOp(float left, float top, float right, float bottom,
282            int alpha, SkXfermode::Mode mode, int flags)
283            : mArea(left, top, right, bottom), mAlpha(alpha), mMode(mode), mFlags(flags) {}
284
285    virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level) {
286        // NOTE: don't bother with actual saveLayer, instead issuing it at flush time
287        int newSaveCount = deferStruct.mRenderer.save(mFlags);
288        deferStruct.mDeferredList.addSaveLayer(deferStruct.mRenderer, this, newSaveCount);
289    }
290
291    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
292        renderer.saveLayer(mArea.left, mArea.top, mArea.right, mArea.bottom, mAlpha, mMode, mFlags);
293    }
294
295    virtual void output(int level, uint32_t logFlags) {
296        OP_LOG("SaveLayer%s of area " RECT_STRING,
297                (isSaveLayerAlpha() ? "Alpha" : ""),RECT_ARGS(mArea));
298    }
299
300    virtual const char* name() { return isSaveLayerAlpha() ? "SaveLayerAlpha" : "SaveLayer"; }
301
302    int getFlags() { return mFlags; }
303
304private:
305    // Special case, reserved for direct DisplayList usage
306    SaveLayerOp() {}
307    DisplayListOp* reinit(float left, float top, float right, float bottom,
308            int alpha, SkXfermode::Mode mode, int flags) {
309        mArea.set(left, top, right, bottom);
310        mAlpha = alpha;
311        mMode = mode;
312        mFlags = flags;
313        return this;
314    }
315
316    bool isSaveLayerAlpha() { return mAlpha < 255 && mMode == SkXfermode::kSrcOver_Mode; }
317    Rect mArea;
318    int mAlpha;
319    SkXfermode::Mode mMode;
320    int mFlags;
321};
322
323class TranslateOp : public StateOp {
324public:
325    TranslateOp(float dx, float dy)
326            : mDx(dx), mDy(dy) {}
327
328    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
329        renderer.translate(mDx, mDy);
330    }
331
332    virtual void output(int level, uint32_t logFlags) {
333        OP_LOG("Translate by %f %f", mDx, mDy);
334    }
335
336    virtual const char* name() { return "Translate"; }
337
338private:
339    float mDx;
340    float mDy;
341};
342
343class RotateOp : public StateOp {
344public:
345    RotateOp(float degrees)
346            : mDegrees(degrees) {}
347
348    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
349        renderer.rotate(mDegrees);
350    }
351
352    virtual void output(int level, uint32_t logFlags) {
353        OP_LOG("Rotate by %f degrees", mDegrees);
354    }
355
356    virtual const char* name() { return "Rotate"; }
357
358private:
359    float mDegrees;
360};
361
362class ScaleOp : public StateOp {
363public:
364    ScaleOp(float sx, float sy)
365            : mSx(sx), mSy(sy) {}
366
367    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
368        renderer.scale(mSx, mSy);
369    }
370
371    virtual void output(int level, uint32_t logFlags) {
372        OP_LOG("Scale by %f %f", mSx, mSy);
373    }
374
375    virtual const char* name() { return "Scale"; }
376
377private:
378    float mSx;
379    float mSy;
380};
381
382class SkewOp : public StateOp {
383public:
384    SkewOp(float sx, float sy)
385            : mSx(sx), mSy(sy) {}
386
387    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
388        renderer.skew(mSx, mSy);
389    }
390
391    virtual void output(int level, uint32_t logFlags) {
392        OP_LOG("Skew by %f %f", mSx, mSy);
393    }
394
395    virtual const char* name() { return "Skew"; }
396
397private:
398    float mSx;
399    float mSy;
400};
401
402class SetMatrixOp : public StateOp {
403public:
404    SetMatrixOp(SkMatrix* matrix)
405            : mMatrix(matrix) {}
406
407    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
408        renderer.setMatrix(mMatrix);
409    }
410
411    virtual void output(int level, uint32_t logFlags) {
412        OP_LOG("SetMatrix " MATRIX_STRING, MATRIX_ARGS(mMatrix));
413    }
414
415    virtual const char* name() { return "SetMatrix"; }
416
417private:
418    SkMatrix* mMatrix;
419};
420
421class ConcatMatrixOp : public StateOp {
422public:
423    ConcatMatrixOp(SkMatrix* matrix)
424            : mMatrix(matrix) {}
425
426    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
427        renderer.concatMatrix(mMatrix);
428    }
429
430    virtual void output(int level, uint32_t logFlags) {
431        OP_LOG("ConcatMatrix " MATRIX_STRING, MATRIX_ARGS(mMatrix));
432    }
433
434    virtual const char* name() { return "ConcatMatrix"; }
435
436private:
437    SkMatrix* mMatrix;
438};
439
440class ClipOp : public StateOp {
441public:
442    ClipOp(SkRegion::Op op) : mOp(op) {}
443
444    virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level) {
445        // NOTE: must defer op BEFORE applying state, since it may read clip
446        deferStruct.mDeferredList.addClip(deferStruct.mRenderer, this);
447
448        // TODO: Can we avoid applying complex clips at defer time?
449        applyState(deferStruct.mRenderer, saveCount);
450    }
451
452    bool canCauseComplexClip() {
453        return ((mOp != SkRegion::kIntersect_Op) && (mOp != SkRegion::kReplace_Op)) || !isRect();
454    }
455
456protected:
457    ClipOp() {}
458    virtual bool isRect() { return false; }
459
460    SkRegion::Op mOp;
461};
462
463class ClipRectOp : public ClipOp {
464    friend class DisplayList; // give DisplayList private constructor/reinit access
465public:
466    ClipRectOp(float left, float top, float right, float bottom, SkRegion::Op op)
467            : ClipOp(op), mArea(left, top, right, bottom) {}
468
469    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
470        renderer.clipRect(mArea.left, mArea.top, mArea.right, mArea.bottom, mOp);
471    }
472
473    virtual void output(int level, uint32_t logFlags) {
474        OP_LOG("ClipRect " RECT_STRING, RECT_ARGS(mArea));
475    }
476
477    virtual const char* name() { return "ClipRect"; }
478
479protected:
480    virtual bool isRect() { return true; }
481
482private:
483    ClipRectOp() {}
484    DisplayListOp* reinit(float left, float top, float right, float bottom, SkRegion::Op op) {
485        mOp = op;
486        mArea.set(left, top, right, bottom);
487        return this;
488    }
489
490    Rect mArea;
491};
492
493class ClipPathOp : public ClipOp {
494public:
495    ClipPathOp(SkPath* path, SkRegion::Op op)
496            : ClipOp(op), mPath(path) {}
497
498    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
499        renderer.clipPath(mPath, mOp);
500    }
501
502    virtual void output(int level, uint32_t logFlags) {
503        SkRect bounds = mPath->getBounds();
504        OP_LOG("ClipPath bounds " RECT_STRING,
505                bounds.left(), bounds.top(), bounds.right(), bounds.bottom());
506    }
507
508    virtual const char* name() { return "ClipPath"; }
509
510private:
511    SkPath* mPath;
512};
513
514class ClipRegionOp : public ClipOp {
515public:
516    ClipRegionOp(SkRegion* region, SkRegion::Op op)
517            : ClipOp(op), mRegion(region) {}
518
519    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
520        renderer.clipRegion(mRegion, mOp);
521    }
522
523    virtual void output(int level, uint32_t logFlags) {
524        SkIRect bounds = mRegion->getBounds();
525        OP_LOG("ClipRegion bounds %d %d %d %d",
526                bounds.left(), bounds.top(), bounds.right(), bounds.bottom());
527    }
528
529    virtual const char* name() { return "ClipRegion"; }
530
531private:
532    SkRegion* mRegion;
533    SkRegion::Op mOp;
534};
535
536class ResetShaderOp : public StateOp {
537public:
538    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
539        renderer.resetShader();
540    }
541
542    virtual void output(int level, uint32_t logFlags) {
543        OP_LOGS("ResetShader");
544    }
545
546    virtual const char* name() { return "ResetShader"; }
547};
548
549class SetupShaderOp : public StateOp {
550public:
551    SetupShaderOp(SkiaShader* shader)
552            : mShader(shader) {}
553    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
554        renderer.setupShader(mShader);
555    }
556
557    virtual void output(int level, uint32_t logFlags) {
558        OP_LOG("SetupShader, shader %p", mShader);
559    }
560
561    virtual const char* name() { return "SetupShader"; }
562
563private:
564    SkiaShader* mShader;
565};
566
567class ResetColorFilterOp : public StateOp {
568public:
569    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
570        renderer.resetColorFilter();
571    }
572
573    virtual void output(int level, uint32_t logFlags) {
574        OP_LOGS("ResetColorFilter");
575    }
576
577    virtual const char* name() { return "ResetColorFilter"; }
578};
579
580class SetupColorFilterOp : public StateOp {
581public:
582    SetupColorFilterOp(SkiaColorFilter* colorFilter)
583            : mColorFilter(colorFilter) {}
584
585    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
586        renderer.setupColorFilter(mColorFilter);
587    }
588
589    virtual void output(int level, uint32_t logFlags) {
590        OP_LOG("SetupColorFilter, filter %p", mColorFilter);
591    }
592
593    virtual const char* name() { return "SetupColorFilter"; }
594
595private:
596    SkiaColorFilter* mColorFilter;
597};
598
599class ResetShadowOp : public StateOp {
600public:
601    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
602        renderer.resetShadow();
603    }
604
605    virtual void output(int level, uint32_t logFlags) {
606        OP_LOGS("ResetShadow");
607    }
608
609    virtual const char* name() { return "ResetShadow"; }
610};
611
612class SetupShadowOp : public StateOp {
613public:
614    SetupShadowOp(float radius, float dx, float dy, int color)
615            : mRadius(radius), mDx(dx), mDy(dy), mColor(color) {}
616
617    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
618        renderer.setupShadow(mRadius, mDx, mDy, mColor);
619    }
620
621    virtual void output(int level, uint32_t logFlags) {
622        OP_LOG("SetupShadow, radius %f, %f, %f, color %#x", mRadius, mDx, mDy, mColor);
623    }
624
625    virtual const char* name() { return "SetupShadow"; }
626
627private:
628    float mRadius;
629    float mDx;
630    float mDy;
631    int mColor;
632};
633
634class ResetPaintFilterOp : public StateOp {
635public:
636    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
637        renderer.resetPaintFilter();
638    }
639
640    virtual void output(int level, uint32_t logFlags) {
641        OP_LOGS("ResetPaintFilter");
642    }
643
644    virtual const char* name() { return "ResetPaintFilter"; }
645};
646
647class SetupPaintFilterOp : public StateOp {
648public:
649    SetupPaintFilterOp(int clearBits, int setBits)
650            : mClearBits(clearBits), mSetBits(setBits) {}
651
652    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
653        renderer.setupPaintFilter(mClearBits, mSetBits);
654    }
655
656    virtual void output(int level, uint32_t logFlags) {
657        OP_LOG("SetupPaintFilter, clear %#x, set %#x", mClearBits, mSetBits);
658    }
659
660    virtual const char* name() { return "SetupPaintFilter"; }
661
662private:
663    int mClearBits;
664    int mSetBits;
665};
666
667
668///////////////////////////////////////////////////////////////////////////////
669// DRAW OPERATIONS - these are operations that can draw to the canvas's device
670///////////////////////////////////////////////////////////////////////////////
671
672class DrawBitmapOp : public DrawBoundedOp {
673public:
674    DrawBitmapOp(SkBitmap* bitmap, float left, float top, SkPaint* paint)
675            : DrawBoundedOp(left, top, left + bitmap->width(), top + bitmap->height(),
676                    paint),
677            mBitmap(bitmap) {}
678
679    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
680        return renderer.drawBitmap(mBitmap, mLocalBounds.left, mLocalBounds.top,
681                getPaint(renderer));
682    }
683
684    virtual void output(int level, uint32_t logFlags) {
685        OP_LOG("Draw bitmap %p at %f %f", mBitmap, mLocalBounds.left, mLocalBounds.top);
686    }
687
688    virtual const char* name() { return "DrawBitmap"; }
689    virtual DeferredDisplayList::OpBatchId getBatchId() {
690        return DeferredDisplayList::kOpBatch_Bitmap;
691    }
692
693protected:
694    SkBitmap* mBitmap;
695};
696
697class DrawBitmapMatrixOp : public DrawBoundedOp {
698public:
699    DrawBitmapMatrixOp(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint)
700            : DrawBoundedOp(paint), mBitmap(bitmap), mMatrix(matrix) {
701        mLocalBounds.set(0, 0, bitmap->width(), bitmap->height());
702        const mat4 transform(*matrix);
703        transform.mapRect(mLocalBounds);
704    }
705
706    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
707        return renderer.drawBitmap(mBitmap, mMatrix, getPaint(renderer));
708    }
709
710    virtual void output(int level, uint32_t logFlags) {
711        OP_LOG("Draw bitmap %p matrix " MATRIX_STRING, mBitmap, MATRIX_ARGS(mMatrix));
712    }
713
714    virtual const char* name() { return "DrawBitmap"; }
715    virtual DeferredDisplayList::OpBatchId getBatchId() {
716        return DeferredDisplayList::kOpBatch_Bitmap;
717    }
718
719private:
720    SkBitmap* mBitmap;
721    SkMatrix* mMatrix;
722};
723
724class DrawBitmapRectOp : public DrawBoundedOp {
725public:
726    DrawBitmapRectOp(SkBitmap* bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom,
727            float dstLeft, float dstTop, float dstRight, float dstBottom, SkPaint* paint)
728            : DrawBoundedOp(dstLeft, dstTop, dstRight, dstBottom, paint),
729            mBitmap(bitmap), mSrc(srcLeft, srcTop, srcRight, srcBottom) {}
730
731    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
732        return renderer.drawBitmap(mBitmap, mSrc.left, mSrc.top, mSrc.right, mSrc.bottom,
733                mLocalBounds.left, mLocalBounds.top, mLocalBounds.right, mLocalBounds.bottom,
734                getPaint(renderer));
735    }
736
737    virtual void output(int level, uint32_t logFlags) {
738        OP_LOG("Draw bitmap %p src="RECT_STRING", dst="RECT_STRING,
739                mBitmap, RECT_ARGS(mSrc), RECT_ARGS(mLocalBounds));
740    }
741
742    virtual const char* name() { return "DrawBitmapRect"; }
743    virtual DeferredDisplayList::OpBatchId getBatchId() {
744        return DeferredDisplayList::kOpBatch_Bitmap;
745    }
746
747private:
748    SkBitmap* mBitmap;
749    Rect mSrc;
750};
751
752class DrawBitmapDataOp : public DrawBitmapOp {
753public:
754    DrawBitmapDataOp(SkBitmap* bitmap, float left, float top, SkPaint* paint)
755            : DrawBitmapOp(bitmap, left, top, paint) {}
756
757    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
758        return renderer.drawBitmapData(mBitmap, mLocalBounds.left,
759                mLocalBounds.top, getPaint(renderer));
760    }
761
762    virtual void output(int level, uint32_t logFlags) {
763        OP_LOG("Draw bitmap %p", mBitmap);
764    }
765
766    virtual const char* name() { return "DrawBitmapData"; }
767    virtual DeferredDisplayList::OpBatchId getBatchId() {
768        return DeferredDisplayList::kOpBatch_Bitmap;
769    }
770};
771
772class DrawBitmapMeshOp : public DrawBoundedOp {
773public:
774    DrawBitmapMeshOp(SkBitmap* bitmap, int meshWidth, int meshHeight,
775            float* vertices, int* colors, SkPaint* paint)
776            : DrawBoundedOp(vertices, 2 * (meshWidth + 1) * (meshHeight + 1), paint),
777            mBitmap(bitmap), mMeshWidth(meshWidth), mMeshHeight(meshHeight),
778            mVertices(vertices), mColors(colors) {}
779
780    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
781        return renderer.drawBitmapMesh(mBitmap, mMeshWidth, mMeshHeight,
782                mVertices, mColors, getPaint(renderer));
783    }
784
785    virtual void output(int level, uint32_t logFlags) {
786        OP_LOG("Draw bitmap %p mesh %d x %d", mBitmap, mMeshWidth, mMeshHeight);
787    }
788
789    virtual const char* name() { return "DrawBitmapMesh"; }
790    virtual DeferredDisplayList::OpBatchId getBatchId() {
791        return DeferredDisplayList::kOpBatch_Bitmap;
792    }
793
794private:
795    SkBitmap* mBitmap;
796    int mMeshWidth;
797    int mMeshHeight;
798    float* mVertices;
799    int* mColors;
800};
801
802class DrawPatchOp : public DrawBoundedOp {
803public:
804    DrawPatchOp(SkBitmap* bitmap, const int32_t* xDivs,
805            const int32_t* yDivs, const uint32_t* colors, uint32_t width, uint32_t height,
806            int8_t numColors, float left, float top, float right, float bottom,
807            int alpha, SkXfermode::Mode mode)
808            : DrawBoundedOp(left, top, right, bottom, 0),
809            mBitmap(bitmap), mxDivs(xDivs), myDivs(yDivs),
810            mColors(colors), mxDivsCount(width), myDivsCount(height),
811            mNumColors(numColors), mAlpha(alpha), mMode(mode) {};
812
813    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
814        // NOTE: not calling the virtual method, which takes a paint
815        return renderer.drawPatch(mBitmap, mxDivs, myDivs, mColors,
816                mxDivsCount, myDivsCount, mNumColors,
817                mLocalBounds.left, mLocalBounds.top,
818                mLocalBounds.right, mLocalBounds.bottom, mAlpha, mMode);
819    }
820
821    virtual void output(int level, uint32_t logFlags) {
822        OP_LOG("Draw patch "RECT_STRING, RECT_ARGS(mLocalBounds));
823    }
824
825    virtual const char* name() { return "DrawPatch"; }
826    virtual DeferredDisplayList::OpBatchId getBatchId() {
827        return DeferredDisplayList::kOpBatch_Patch;
828    }
829
830private:
831    SkBitmap* mBitmap;
832    const int32_t* mxDivs;
833    const int32_t* myDivs;
834    const uint32_t* mColors;
835    uint32_t mxDivsCount;
836    uint32_t myDivsCount;
837    int8_t mNumColors;
838    int mAlpha;
839    SkXfermode::Mode mMode;
840};
841
842class DrawColorOp : public DrawOp {
843public:
844    DrawColorOp(int color, SkXfermode::Mode mode)
845            : DrawOp(0), mColor(color), mMode(mode) {};
846
847    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
848        return renderer.drawColor(mColor, mMode);
849    }
850
851    virtual void output(int level, uint32_t logFlags) {
852        OP_LOG("Draw color %#x, mode %d", mColor, mMode);
853    }
854
855    virtual const char* name() { return "DrawColor"; }
856
857private:
858    int mColor;
859    SkXfermode::Mode mMode;
860};
861
862class DrawStrokableOp : public DrawBoundedOp {
863public:
864    DrawStrokableOp(float left, float top, float right, float bottom, SkPaint* paint)
865            : DrawBoundedOp(left, top, right, bottom, paint) {};
866
867    bool getLocalBounds(Rect& localBounds) {
868        localBounds.set(mLocalBounds);
869        if (mPaint && mPaint->getStyle() != SkPaint::kFill_Style) {
870            localBounds.outset(strokeWidthOutset());
871        }
872        return true;
873    }
874
875    virtual DeferredDisplayList::OpBatchId getBatchId() {
876        if (mPaint->getPathEffect()) {
877            return DeferredDisplayList::kOpBatch_AlphaMaskTexture;
878        }
879        return mPaint->isAntiAlias() ?
880                DeferredDisplayList::kOpBatch_AlphaVertices :
881                DeferredDisplayList::kOpBatch_Vertices;
882    }
883};
884
885class DrawRectOp : public DrawStrokableOp {
886public:
887    DrawRectOp(float left, float top, float right, float bottom, SkPaint* paint)
888            : DrawStrokableOp(left, top, right, bottom, paint) {}
889
890    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
891        return renderer.drawRect(mLocalBounds.left, mLocalBounds.top,
892                mLocalBounds.right, mLocalBounds.bottom, getPaint(renderer));
893    }
894
895    virtual void output(int level, uint32_t logFlags) {
896        OP_LOG("Draw Rect "RECT_STRING, RECT_ARGS(mLocalBounds));
897    }
898
899    virtual const char* name() { return "DrawRect"; }
900};
901
902class DrawRectsOp : public DrawBoundedOp {
903public:
904    DrawRectsOp(const float* rects, int count, SkPaint* paint)
905            : DrawBoundedOp(rects, count, paint),
906            mRects(rects), mCount(count) {}
907
908    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
909        return renderer.drawRects(mRects, mCount, getPaint(renderer));
910    }
911
912    virtual void output(int level, uint32_t logFlags) {
913        OP_LOG("Draw Rects count %d", mCount);
914    }
915
916    virtual const char* name() { return "DrawRects"; }
917
918    virtual DeferredDisplayList::OpBatchId getBatchId() {
919        return DeferredDisplayList::kOpBatch_Vertices;
920    }
921
922private:
923    const float* mRects;
924    int mCount;
925};
926
927class DrawRoundRectOp : public DrawStrokableOp {
928public:
929    DrawRoundRectOp(float left, float top, float right, float bottom,
930            float rx, float ry, SkPaint* paint)
931            : DrawStrokableOp(left, top, right, bottom, paint), mRx(rx), mRy(ry) {}
932
933    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
934        return renderer.drawRoundRect(mLocalBounds.left, mLocalBounds.top,
935                mLocalBounds.right, mLocalBounds.bottom, mRx, mRy, getPaint(renderer));
936    }
937
938    virtual void output(int level, uint32_t logFlags) {
939        OP_LOG("Draw RoundRect "RECT_STRING", rx %f, ry %f", RECT_ARGS(mLocalBounds), mRx, mRy);
940    }
941
942    virtual const char* name() { return "DrawRoundRect"; }
943
944private:
945    float mRx;
946    float mRy;
947};
948
949class DrawCircleOp : public DrawStrokableOp {
950public:
951    DrawCircleOp(float x, float y, float radius, SkPaint* paint)
952            : DrawStrokableOp(x - radius, y - radius, x + radius, y + radius, paint),
953            mX(x), mY(y), mRadius(radius) {}
954
955    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
956        return renderer.drawCircle(mX, mY, mRadius, getPaint(renderer));
957    }
958
959    virtual void output(int level, uint32_t logFlags) {
960        OP_LOG("Draw Circle x %f, y %f, r %f", mX, mY, mRadius);
961    }
962
963    virtual const char* name() { return "DrawCircle"; }
964
965private:
966    float mX;
967    float mY;
968    float mRadius;
969};
970
971class DrawOvalOp : public DrawStrokableOp {
972public:
973    DrawOvalOp(float left, float top, float right, float bottom, SkPaint* paint)
974            : DrawStrokableOp(left, top, right, bottom, paint) {}
975
976    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
977        return renderer.drawOval(mLocalBounds.left, mLocalBounds.top,
978                mLocalBounds.right, mLocalBounds.bottom, getPaint(renderer));
979    }
980
981    virtual void output(int level, uint32_t logFlags) {
982        OP_LOG("Draw Oval "RECT_STRING, RECT_ARGS(mLocalBounds));
983    }
984
985    virtual const char* name() { return "DrawOval"; }
986};
987
988class DrawArcOp : public DrawStrokableOp {
989public:
990    DrawArcOp(float left, float top, float right, float bottom,
991            float startAngle, float sweepAngle, bool useCenter, SkPaint* paint)
992            : DrawStrokableOp(left, top, right, bottom, paint),
993            mStartAngle(startAngle), mSweepAngle(sweepAngle), mUseCenter(useCenter) {}
994
995    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
996        return renderer.drawArc(mLocalBounds.left, mLocalBounds.top,
997                mLocalBounds.right, mLocalBounds.bottom,
998                mStartAngle, mSweepAngle, mUseCenter, getPaint(renderer));
999    }
1000
1001    virtual void output(int level, uint32_t logFlags) {
1002        OP_LOG("Draw Arc "RECT_STRING", start %f, sweep %f, useCenter %d",
1003                RECT_ARGS(mLocalBounds), mStartAngle, mSweepAngle, mUseCenter);
1004    }
1005
1006    virtual const char* name() { return "DrawArc"; }
1007
1008private:
1009    float mStartAngle;
1010    float mSweepAngle;
1011    bool mUseCenter;
1012};
1013
1014class DrawPathOp : public DrawBoundedOp {
1015public:
1016    DrawPathOp(SkPath* path, SkPaint* paint)
1017            : DrawBoundedOp(paint), mPath(path) {
1018        float left, top, offset;
1019        uint32_t width, height;
1020        PathCache::computePathBounds(path, paint, left, top, offset, width, height);
1021        left -= offset;
1022        top -= offset;
1023        mLocalBounds.set(left, top, left + width, top + height);
1024    }
1025
1026    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
1027        return renderer.drawPath(mPath, getPaint(renderer));
1028    }
1029
1030    virtual void onDrawOpDeferred(OpenGLRenderer& renderer) {
1031        SkPaint* paint = getPaint(renderer);
1032        renderer.getCaches().pathCache.precache(mPath, paint);
1033    }
1034
1035    virtual void output(int level, uint32_t logFlags) {
1036        OP_LOG("Draw Path %p in "RECT_STRING, mPath, RECT_ARGS(mLocalBounds));
1037    }
1038
1039    virtual const char* name() { return "DrawPath"; }
1040
1041    virtual DeferredDisplayList::OpBatchId getBatchId() {
1042        return DeferredDisplayList::kOpBatch_AlphaMaskTexture;
1043    }
1044private:
1045    SkPath* mPath;
1046};
1047
1048class DrawLinesOp : public DrawBoundedOp {
1049public:
1050    DrawLinesOp(float* points, int count, SkPaint* paint)
1051            : DrawBoundedOp(points, count, paint),
1052            mPoints(points), mCount(count) {
1053        mLocalBounds.outset(strokeWidthOutset());
1054    }
1055
1056    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
1057        return renderer.drawLines(mPoints, mCount, getPaint(renderer));
1058    }
1059
1060    virtual void output(int level, uint32_t logFlags) {
1061        OP_LOG("Draw Lines count %d", mCount);
1062    }
1063
1064    virtual const char* name() { return "DrawLines"; }
1065
1066    virtual DeferredDisplayList::OpBatchId getBatchId() {
1067        return mPaint->isAntiAlias() ?
1068                DeferredDisplayList::kOpBatch_AlphaVertices :
1069                DeferredDisplayList::kOpBatch_Vertices;
1070    }
1071
1072protected:
1073    float* mPoints;
1074    int mCount;
1075};
1076
1077class DrawPointsOp : public DrawLinesOp {
1078public:
1079    DrawPointsOp(float* points, int count, SkPaint* paint)
1080            : DrawLinesOp(points, count, paint) {}
1081
1082    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
1083        return renderer.drawPoints(mPoints, mCount, getPaint(renderer));
1084    }
1085
1086    virtual void output(int level, uint32_t logFlags) {
1087        OP_LOG("Draw Points count %d", mCount);
1088    }
1089
1090    virtual const char* name() { return "DrawPoints"; }
1091};
1092
1093class DrawSomeTextOp : public DrawOp {
1094public:
1095    DrawSomeTextOp(const char* text, int bytesCount, int count, SkPaint* paint)
1096            : DrawOp(paint), mText(text), mBytesCount(bytesCount), mCount(count) {};
1097
1098    virtual void output(int level, uint32_t logFlags) {
1099        OP_LOG("Draw some text, %d bytes", mBytesCount);
1100    }
1101
1102    virtual void onDrawOpDeferred(OpenGLRenderer& renderer) {
1103        SkPaint* paint = getPaint(renderer);
1104        FontRenderer& fontRenderer = renderer.getCaches().fontRenderer->getFontRenderer(paint);
1105        fontRenderer.precache(paint, mText, mCount, mat4::identity());
1106    }
1107
1108    virtual DeferredDisplayList::OpBatchId getBatchId() {
1109        return mPaint->getColor() == 0xff000000 ?
1110                DeferredDisplayList::kOpBatch_Text :
1111                DeferredDisplayList::kOpBatch_ColorText;
1112    }
1113protected:
1114    const char* mText;
1115    int mBytesCount;
1116    int mCount;
1117};
1118
1119class DrawTextOnPathOp : public DrawSomeTextOp {
1120public:
1121    DrawTextOnPathOp(const char* text, int bytesCount, int count,
1122            SkPath* path, float hOffset, float vOffset, SkPaint* paint)
1123            : DrawSomeTextOp(text, bytesCount, count, paint),
1124            mPath(path), mHOffset(hOffset), mVOffset(vOffset) {
1125        /* TODO: inherit from DrawBounded and init mLocalBounds */
1126    }
1127
1128    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
1129        return renderer.drawTextOnPath(mText, mBytesCount, mCount, mPath,
1130                mHOffset, mVOffset, getPaint(renderer));
1131    }
1132
1133    virtual const char* name() { return "DrawTextOnPath"; }
1134
1135private:
1136    SkPath* mPath;
1137    float mHOffset;
1138    float mVOffset;
1139};
1140
1141class DrawPosTextOp : public DrawSomeTextOp {
1142public:
1143    DrawPosTextOp(const char* text, int bytesCount, int count,
1144            const float* positions, SkPaint* paint)
1145            : DrawSomeTextOp(text, bytesCount, count, paint), mPositions(positions) {
1146        /* TODO: inherit from DrawBounded and init mLocalBounds */
1147    }
1148
1149    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
1150        return renderer.drawPosText(mText, mBytesCount, mCount, mPositions, getPaint(renderer));
1151    }
1152
1153    virtual const char* name() { return "DrawPosText"; }
1154
1155private:
1156    const float* mPositions;
1157};
1158
1159class DrawTextOp : public DrawBoundedOp {
1160public:
1161    DrawTextOp(const char* text, int bytesCount, int count, float x, float y,
1162            const float* positions, SkPaint* paint, float length)
1163            : DrawBoundedOp(paint), mText(text), mBytesCount(bytesCount), mCount(count),
1164            mX(x), mY(y), mPositions(positions), mLength(length) {
1165        // duplicates bounds calculation from OpenGLRenderer::drawText, but doesn't alter mX
1166        SkPaint::FontMetrics metrics;
1167        paint->getFontMetrics(&metrics, 0.0f);
1168        switch (paint->getTextAlign()) {
1169        case SkPaint::kCenter_Align:
1170            x -= length / 2.0f;
1171            break;
1172        case SkPaint::kRight_Align:
1173            x -= length;
1174            break;
1175        default:
1176            break;
1177        }
1178        mLocalBounds.set(x, mY + metrics.fTop, x + length, mY + metrics.fBottom);
1179        memset(&mPrecacheTransform.data[0], 0xff, 16 * sizeof(float));
1180    }
1181
1182    /*
1183     * When this method is invoked the state field  is initialized to have the
1184     * final rendering state. We can thus use it to process data as it will be
1185     * used at draw time.
1186     */
1187    virtual void onDrawOpDeferred(OpenGLRenderer& renderer) {
1188        SkPaint* paint = getPaint(renderer);
1189        FontRenderer& fontRenderer = renderer.getCaches().fontRenderer->getFontRenderer(paint);
1190        const mat4& transform = renderer.findBestFontTransform(state.mMatrix);
1191        if (mPrecacheTransform != transform) {
1192            fontRenderer.precache(paint, mText, mCount, transform);
1193            mPrecacheTransform = transform;
1194        }
1195    }
1196
1197    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
1198        return renderer.drawText(mText, mBytesCount, mCount, mX, mY,
1199                mPositions, getPaint(renderer), mLength);
1200    }
1201
1202    virtual void output(int level, uint32_t logFlags) {
1203        OP_LOG("Draw Text of count %d, bytes %d", mCount, mBytesCount);
1204    }
1205
1206    virtual const char* name() { return "DrawText"; }
1207
1208    virtual DeferredDisplayList::OpBatchId getBatchId() {
1209        return mPaint->getColor() == 0xff000000 ?
1210                DeferredDisplayList::kOpBatch_Text :
1211                DeferredDisplayList::kOpBatch_ColorText;
1212    }
1213
1214private:
1215    const char* mText;
1216    int mBytesCount;
1217    int mCount;
1218    float mX;
1219    float mY;
1220    const float* mPositions;
1221    float mLength;
1222    mat4 mPrecacheTransform;
1223};
1224
1225///////////////////////////////////////////////////////////////////////////////
1226// SPECIAL DRAW OPERATIONS
1227///////////////////////////////////////////////////////////////////////////////
1228
1229class DrawFunctorOp : public DrawOp {
1230public:
1231    DrawFunctorOp(Functor* functor)
1232            : DrawOp(0), mFunctor(functor) {}
1233
1234    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
1235        renderer.startMark("GL functor");
1236        status_t ret = renderer.callDrawGLFunction(mFunctor, dirty);
1237        renderer.endMark();
1238        return ret;
1239    }
1240
1241    virtual void output(int level, uint32_t logFlags) {
1242        OP_LOG("Draw Functor %p", mFunctor);
1243    }
1244
1245    virtual const char* name() { return "DrawFunctor"; }
1246
1247private:
1248    Functor* mFunctor;
1249};
1250
1251class DrawDisplayListOp : public DrawBoundedOp {
1252public:
1253    DrawDisplayListOp(DisplayList* displayList, int flags)
1254            : DrawBoundedOp(0, 0, displayList->getWidth(), displayList->getHeight(), 0),
1255            mDisplayList(displayList), mFlags(flags) {}
1256
1257    virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level) {
1258        if (mDisplayList && mDisplayList->isRenderable()) {
1259            mDisplayList->defer(deferStruct, level + 1);
1260        }
1261    }
1262virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level) {
1263        if (mDisplayList && mDisplayList->isRenderable()) {
1264            mDisplayList->replay(replayStruct, level + 1);
1265        }
1266    }
1267
1268    // NOT USED since replay() is overridden
1269    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
1270        return DrawGlInfo::kStatusDone;
1271    }
1272
1273    virtual void output(int level, uint32_t logFlags) {
1274        OP_LOG("Draw Display List %p, flags %#x", mDisplayList, mFlags);
1275        if (mDisplayList && (logFlags & kOpLogFlag_Recurse)) {
1276            mDisplayList->output(level + 1);
1277        }
1278    }
1279
1280    virtual const char* name() { return "DrawDisplayList"; }
1281
1282private:
1283    DisplayList* mDisplayList;
1284    int mFlags;
1285};
1286
1287class DrawLayerOp : public DrawOp {
1288public:
1289    DrawLayerOp(Layer* layer, float x, float y)
1290            : DrawOp(0), mLayer(layer), mX(x), mY(y) {}
1291
1292    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
1293        return renderer.drawLayer(mLayer, mX, mY);
1294    }
1295
1296    virtual void output(int level, uint32_t logFlags) {
1297        OP_LOG("Draw Layer %p at %f %f", mLayer, mX, mY);
1298    }
1299
1300    virtual const char* name() { return "DrawLayer"; }
1301
1302private:
1303    Layer* mLayer;
1304    float mX;
1305    float mY;
1306};
1307
1308}; // namespace uirenderer
1309}; // namespace android
1310
1311#endif // ANDROID_HWUI_DISPLAY_OPERATION_H
1312