1 /*
2  * Copyright (C) 2019 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 package com.android.systemui.glwallpaper;
18 
19 import static android.opengl.GLES20.GL_COLOR_BUFFER_BIT;
20 import static android.opengl.GLES20.glClear;
21 import static android.opengl.GLES20.glClearColor;
22 import static android.opengl.GLES20.glViewport;
23 
24 import android.app.WallpaperManager;
25 import android.content.Context;
26 import android.graphics.Bitmap;
27 import android.graphics.Rect;
28 import android.util.Log;
29 import android.util.Size;
30 
31 import com.android.systemui.R;
32 
33 import java.io.FileDescriptor;
34 import java.io.PrintWriter;
35 import java.util.concurrent.atomic.AtomicInteger;
36 import java.util.function.Consumer;
37 
38 /**
39  * A GL renderer for image wallpaper.
40  */
41 public class ImageWallpaperRenderer implements GLWallpaperRenderer {
42     private static final String TAG = ImageWallpaperRenderer.class.getSimpleName();
43     private static final boolean DEBUG = false;
44 
45     private final ImageGLProgram mProgram;
46     private final ImageGLWallpaper mWallpaper;
47     private final Rect mSurfaceSize = new Rect();
48     private final WallpaperTexture mTexture;
49     private Consumer<Bitmap> mOnBitmapUpdated;
50 
ImageWallpaperRenderer(Context context)51     public ImageWallpaperRenderer(Context context) {
52         final WallpaperManager wpm = context.getSystemService(WallpaperManager.class);
53         if (wpm == null) {
54             Log.w(TAG, "WallpaperManager not available");
55         }
56 
57         mTexture = new WallpaperTexture(wpm);
58         mProgram = new ImageGLProgram(context);
59         mWallpaper = new ImageGLWallpaper(mProgram);
60     }
61 
62     /**
63      * @hide
64      */
setOnBitmapChanged(Consumer<Bitmap> c)65     public void setOnBitmapChanged(Consumer<Bitmap> c) {
66         mOnBitmapUpdated = c;
67     }
68 
69     /**
70      * @hide
71      */
use(Consumer<Bitmap> c)72     public void use(Consumer<Bitmap> c) {
73         mTexture.use(c);
74     }
75 
76     @Override
isWcgContent()77     public boolean isWcgContent() {
78         return mTexture.isWcgContent();
79     }
80 
81     @Override
onSurfaceCreated()82     public void onSurfaceCreated() {
83         glClearColor(0f, 0f, 0f, 1.0f);
84         mProgram.useGLProgram(
85                 R.raw.image_wallpaper_vertex_shader, R.raw.image_wallpaper_fragment_shader);
86 
87         mTexture.use(bitmap -> {
88             if (bitmap == null) {
89                 Log.w(TAG, "reload texture failed!");
90             } else if (mOnBitmapUpdated != null) {
91                 mOnBitmapUpdated.accept(bitmap);
92             }
93             mWallpaper.setup(bitmap);
94         });
95     }
96 
97     @Override
onSurfaceChanged(int width, int height)98     public void onSurfaceChanged(int width, int height) {
99         glViewport(0, 0, width, height);
100     }
101 
102     @Override
onDrawFrame()103     public void onDrawFrame() {
104         glClear(GL_COLOR_BUFFER_BIT);
105         glViewport(0, 0, mSurfaceSize.width(), mSurfaceSize.height());
106         mWallpaper.useTexture();
107         mWallpaper.draw();
108     }
109 
110     @Override
reportSurfaceSize()111     public Size reportSurfaceSize() {
112         mSurfaceSize.set(mTexture.getTextureDimensions());
113         return new Size(mSurfaceSize.width(), mSurfaceSize.height());
114     }
115 
116     @Override
finish()117     public void finish() {
118     }
119 
120     @Override
dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args)121     public void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) {
122         out.print(prefix); out.print("mSurfaceSize="); out.print(mSurfaceSize);
123         out.print(prefix); out.print("mWcgContent="); out.print(isWcgContent());
124         mWallpaper.dump(prefix, fd, out, args);
125     }
126 
127     static class WallpaperTexture {
128         private final AtomicInteger mRefCount;
129         private final Rect mDimensions;
130         private final WallpaperManager mWallpaperManager;
131         private Bitmap mBitmap;
132         private boolean mWcgContent;
133         private boolean mTextureUsed;
134 
WallpaperTexture(WallpaperManager wallpaperManager)135         private WallpaperTexture(WallpaperManager wallpaperManager) {
136             mWallpaperManager = wallpaperManager;
137             mRefCount = new AtomicInteger();
138             mDimensions = new Rect();
139         }
140 
use(Consumer<Bitmap> consumer)141         public void use(Consumer<Bitmap> consumer) {
142             mRefCount.incrementAndGet();
143             synchronized (mRefCount) {
144                 if (mBitmap == null) {
145                     mBitmap = mWallpaperManager.getBitmap(false /* hardware */);
146                     mWcgContent = mWallpaperManager.wallpaperSupportsWcg(
147                             WallpaperManager.FLAG_SYSTEM);
148                     mWallpaperManager.forgetLoadedWallpaper();
149                     if (mBitmap != null) {
150                         mDimensions.set(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
151                         mTextureUsed = true;
152                     } else {
153                         Log.w(TAG, "Can't get bitmap");
154                     }
155                 }
156             }
157             if (consumer != null) {
158                 consumer.accept(mBitmap);
159             }
160             synchronized (mRefCount) {
161                 final int count = mRefCount.decrementAndGet();
162                 if (count == 0 && mBitmap != null) {
163                     if (DEBUG) {
164                         Log.v(TAG, "WallpaperTexture: release 0x" + getHash()
165                                 + ", refCount=" + count);
166                     }
167                     mBitmap.recycle();
168                     mBitmap = null;
169                 }
170             }
171         }
172 
isWcgContent()173         private boolean isWcgContent() {
174             return mWcgContent;
175         }
176 
getHash()177         private String getHash() {
178             return mBitmap != null ? Integer.toHexString(mBitmap.hashCode()) : "null";
179         }
180 
getTextureDimensions()181         private Rect getTextureDimensions() {
182             if (!mTextureUsed) {
183                 mDimensions.set(mWallpaperManager.peekBitmapDimensions());
184             }
185             return mDimensions;
186         }
187 
188         @Override
toString()189         public String toString() {
190             return "{" + getHash() + ", " + mRefCount.get() + "}";
191         }
192     }
193 }
194