4
4
5
5
package io .flutter .embedding .android ;
6
6
7
- import android .annotation .SuppressLint ;
8
7
import android .annotation .TargetApi ;
9
8
import android .content .Context ;
10
9
import android .graphics .Bitmap ;
15
14
import android .media .Image ;
16
15
import android .media .Image .Plane ;
17
16
import android .media .ImageReader ;
17
+ import android .util .AttributeSet ;
18
18
import android .view .Surface ;
19
19
import android .view .View ;
20
20
import androidx .annotation .NonNull ;
21
21
import androidx .annotation .Nullable ;
22
22
import androidx .annotation .VisibleForTesting ;
23
23
import io .flutter .embedding .engine .renderer .FlutterRenderer ;
24
24
import io .flutter .embedding .engine .renderer .RenderSurface ;
25
+ import java .util .LinkedList ;
26
+ import java .util .Queue ;
25
27
26
28
/**
27
29
* Paints a Flutter UI provided by an {@link android.media.ImageReader} onto a {@link
35
37
* an {@link android.media.Image} and renders it to the {@link android.graphics.Canvas} in {@code
36
38
* onDraw}.
37
39
*/
38
- @ SuppressLint ("ViewConstructor" )
39
40
@ TargetApi (19 )
40
41
public class FlutterImageView extends View implements RenderSurface {
41
42
@ NonNull private ImageReader imageReader ;
42
- @ Nullable private Image nextImage ;
43
+ @ Nullable private Queue < Image > imageQueue ;
43
44
@ Nullable private Image currentImage ;
44
45
@ Nullable private Bitmap currentBitmap ;
45
46
@ Nullable private FlutterRenderer flutterRenderer ;
@@ -70,17 +71,24 @@ public enum SurfaceKind {
70
71
* the Flutter UI.
71
72
*/
72
73
public FlutterImageView (@ NonNull Context context , int width , int height , SurfaceKind kind ) {
73
- super (context , null );
74
- this .imageReader = createImageReader (width , height );
75
- this .kind = kind ;
76
- init ();
74
+ this (context , createImageReader (width , height ), kind );
75
+ }
76
+
77
+ public FlutterImageView (@ NonNull Context context ) {
78
+ this (context , 1 , 1 , SurfaceKind .background );
79
+ }
80
+
81
+ public FlutterImageView (@ NonNull Context context , @ NonNull AttributeSet attrs ) {
82
+ this (context , 1 , 1 , SurfaceKind .background );
77
83
}
78
84
79
85
@ VisibleForTesting
80
- FlutterImageView (@ NonNull Context context , @ NonNull ImageReader imageReader , SurfaceKind kind ) {
86
+ /*package*/ FlutterImageView (
87
+ @ NonNull Context context , @ NonNull ImageReader imageReader , SurfaceKind kind ) {
81
88
super (context , null );
82
89
this .imageReader = imageReader ;
83
90
this .kind = kind ;
91
+ this .imageQueue = new LinkedList <>();
84
92
init ();
85
93
}
86
94
@@ -150,12 +158,14 @@ public void detachFromRenderer() {
150
158
// attached to the renderer again.
151
159
acquireLatestImage ();
152
160
// Clear drawings.
153
- pendingImages = 0 ;
154
161
currentBitmap = null ;
155
- if (nextImage != null ) {
156
- nextImage .close ();
157
- nextImage = null ;
162
+
163
+ // Close the images in the queue and clear the queue.
164
+ for (final Image image : imageQueue ) {
165
+ image .close ();
158
166
}
167
+ imageQueue .clear ();
168
+ // Close and clear the current image if any.
159
169
if (currentImage != null ) {
160
170
currentImage .close ();
161
171
currentImage = null ;
@@ -168,7 +178,10 @@ public void pause() {
168
178
// Not supported.
169
179
}
170
180
171
- /** Acquires the next image to be drawn to the {@link android.graphics.Canvas}. */
181
+ /**
182
+ * Acquires the next image to be drawn to the {@link android.graphics.Canvas}. Returns true if
183
+ * there's an image available in the queue.
184
+ */
172
185
@ TargetApi (19 )
173
186
public boolean acquireLatestImage () {
174
187
if (!isAttachedToFlutterRenderer ) {
@@ -182,14 +195,14 @@ public boolean acquireLatestImage() {
182
195
// While the engine will also stop producing frames, there is a race condition.
183
196
//
184
197
// To avoid exceptions, check if a new image can be acquired.
185
- if (pendingImages < imageReader .getMaxImages ()) {
186
- nextImage = imageReader .acquireLatestImage ();
187
- if (nextImage != null ) {
188
- pendingImages ++ ;
198
+ if (imageQueue . size () < imageReader .getMaxImages ()) {
199
+ final Image image = imageReader .acquireLatestImage ();
200
+ if (image != null ) {
201
+ imageQueue . add ( image ) ;
189
202
}
190
203
}
191
204
invalidate ();
192
- return nextImage != null ;
205
+ return ! imageQueue . isEmpty () ;
193
206
}
194
207
195
208
/** Creates a new image reader with the provided size. */
@@ -200,15 +213,10 @@ public void resizeIfNeeded(int width, int height) {
200
213
if (width == imageReader .getWidth () && height == imageReader .getHeight ()) {
201
214
return ;
202
215
}
203
- // Close resources.
204
- if (nextImage != null ) {
205
- nextImage .close ();
206
- nextImage = null ;
207
- }
208
- if (currentImage != null ) {
209
- currentImage .close ();
210
- currentImage = null ;
211
- }
216
+ imageQueue .clear ();
217
+ currentImage = null ;
218
+ // Close all the resources associated with the image reader,
219
+ // including the images.
212
220
imageReader .close ();
213
221
// Image readers cannot be resized once created.
214
222
imageReader = createImageReader (width , height );
@@ -218,16 +226,14 @@ public void resizeIfNeeded(int width, int height) {
218
226
@ Override
219
227
protected void onDraw (Canvas canvas ) {
220
228
super .onDraw (canvas );
221
- if (nextImage != null ) {
229
+
230
+ if (!imageQueue .isEmpty ()) {
222
231
if (currentImage != null ) {
223
232
currentImage .close ();
224
- pendingImages --;
225
233
}
226
- currentImage = nextImage ;
227
- nextImage = null ;
234
+ currentImage = imageQueue .poll ();
228
235
updateCurrentBitmap ();
229
236
}
230
-
231
237
if (currentBitmap != null ) {
232
238
canvas .drawBitmap (currentBitmap , 0 , 0 , null );
233
239
}
@@ -238,6 +244,7 @@ private void updateCurrentBitmap() {
238
244
if (android .os .Build .VERSION .SDK_INT >= 29 ) {
239
245
final HardwareBuffer buffer = currentImage .getHardwareBuffer ();
240
246
currentBitmap = Bitmap .wrapHardwareBuffer (buffer , ColorSpace .get (ColorSpace .Named .SRGB ));
247
+ buffer .close ();
241
248
} else {
242
249
final Plane [] imagePlanes = currentImage .getPlanes ();
243
250
if (imagePlanes .length != 1 ) {
@@ -255,7 +262,6 @@ private void updateCurrentBitmap() {
255
262
Bitmap .createBitmap (
256
263
desiredWidth , desiredHeight , android .graphics .Bitmap .Config .ARGB_8888 );
257
264
}
258
-
259
265
currentBitmap .copyPixelsFromBuffer (imagePlane .getBuffer ());
260
266
}
261
267
}
0 commit comments