mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Initial work toward converting the FlutterView to use a FlutterImageView on demand (flutter/engine#19279)
This commit is contained in:
parent
dea0224390
commit
5b8a1cb82e
@ -17,6 +17,8 @@ import android.media.ImageReader;
|
||||
import android.view.View;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import io.flutter.embedding.engine.renderer.FlutterRenderer;
|
||||
import io.flutter.embedding.engine.renderer.RenderSurface;
|
||||
|
||||
/**
|
||||
* Paints a Flutter UI provided by an {@link android.media.ImageReader} onto a {@link
|
||||
@ -32,10 +34,12 @@ import androidx.annotation.Nullable;
|
||||
*/
|
||||
@SuppressLint("ViewConstructor")
|
||||
@TargetApi(19)
|
||||
public class FlutterImageView extends View {
|
||||
public class FlutterImageView extends View implements RenderSurface {
|
||||
private final ImageReader imageReader;
|
||||
@Nullable private Image nextImage;
|
||||
@Nullable private Image currentImage;
|
||||
@Nullable private Bitmap currentBitmap;
|
||||
@Nullable private FlutterRenderer flutterRenderer;
|
||||
|
||||
/**
|
||||
* Constructs a {@code FlutterImageView} with an {@link android.media.ImageReader} that provides
|
||||
@ -46,6 +50,37 @@ public class FlutterImageView extends View {
|
||||
this.imageReader = imageReader;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public FlutterRenderer getAttachedRenderer() {
|
||||
return flutterRenderer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked by the owner of this {@code FlutterImageView} when it wants to begin rendering a
|
||||
* Flutter UI to this {@code FlutterImageView}.
|
||||
*/
|
||||
@Override
|
||||
public void attachToRenderer(@NonNull FlutterRenderer flutterRenderer) {
|
||||
if (this.flutterRenderer != null) {
|
||||
this.flutterRenderer.stopRenderingToSurface();
|
||||
}
|
||||
|
||||
this.flutterRenderer = flutterRenderer;
|
||||
flutterRenderer.startRenderingToSurface(imageReader.getSurface());
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked by the owner of this {@code FlutterImageView} when it no longer wants to render a
|
||||
* Flutter UI to this {@code FlutterImageView}.
|
||||
*/
|
||||
public void detachFromRenderer() {
|
||||
if (flutterRenderer != null) {
|
||||
flutterRenderer.stopRenderingToSurface();
|
||||
flutterRenderer = null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Acquires the next image to be drawn to the {@link android.graphics.Canvas}. */
|
||||
@TargetApi(19)
|
||||
public void acquireLatestImage() {
|
||||
@ -56,51 +91,44 @@ public class FlutterImageView extends View {
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
if (nextImage == null) {
|
||||
return;
|
||||
if (nextImage != null) {
|
||||
if (currentImage != null) {
|
||||
currentImage.close();
|
||||
}
|
||||
currentImage = nextImage;
|
||||
nextImage = null;
|
||||
updateCurrentBitmap();
|
||||
}
|
||||
|
||||
if (currentImage != null) {
|
||||
currentImage.close();
|
||||
if (currentBitmap != null) {
|
||||
canvas.drawBitmap(currentBitmap, 0, 0, null);
|
||||
}
|
||||
currentImage = nextImage;
|
||||
nextImage = null;
|
||||
|
||||
if (android.os.Build.VERSION.SDK_INT >= 29) {
|
||||
drawImageBuffer(canvas);
|
||||
return;
|
||||
}
|
||||
|
||||
drawImagePlane(canvas);
|
||||
}
|
||||
|
||||
@TargetApi(29)
|
||||
private void drawImageBuffer(@NonNull Canvas canvas) {
|
||||
final HardwareBuffer buffer = currentImage.getHardwareBuffer();
|
||||
private void updateCurrentBitmap() {
|
||||
if (android.os.Build.VERSION.SDK_INT >= 29) {
|
||||
final HardwareBuffer buffer = currentImage.getHardwareBuffer();
|
||||
currentBitmap = Bitmap.wrapHardwareBuffer(buffer, ColorSpace.get(ColorSpace.Named.SRGB));
|
||||
} else {
|
||||
final Plane[] imagePlanes = currentImage.getPlanes();
|
||||
if (imagePlanes.length != 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Bitmap bitmap = Bitmap.wrapHardwareBuffer(buffer, ColorSpace.get(ColorSpace.Named.SRGB));
|
||||
canvas.drawBitmap(bitmap, 0, 0, null);
|
||||
}
|
||||
final Plane imagePlane = imagePlanes[0];
|
||||
final int desiredWidth = imagePlane.getRowStride() / imagePlane.getPixelStride();
|
||||
final int desiredHeight = currentImage.getHeight();
|
||||
|
||||
private void drawImagePlane(@NonNull Canvas canvas) {
|
||||
if (currentImage == null) {
|
||||
return;
|
||||
if (currentBitmap == null
|
||||
|| currentBitmap.getWidth() != desiredWidth
|
||||
|| currentBitmap.getHeight() != desiredHeight) {
|
||||
currentBitmap =
|
||||
Bitmap.createBitmap(
|
||||
desiredWidth, desiredHeight, android.graphics.Bitmap.Config.ARGB_8888);
|
||||
}
|
||||
|
||||
currentBitmap.copyPixelsFromBuffer(imagePlane.getBuffer());
|
||||
}
|
||||
|
||||
final Plane[] imagePlanes = currentImage.getPlanes();
|
||||
if (imagePlanes.length != 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Plane imagePlane = imagePlanes[0];
|
||||
final int desiredWidth = imagePlane.getRowStride() / imagePlane.getPixelStride();
|
||||
final int desiredHeight = currentImage.getHeight();
|
||||
|
||||
final Bitmap bitmap =
|
||||
android.graphics.Bitmap.createBitmap(
|
||||
desiredWidth, desiredHeight, android.graphics.Bitmap.Config.ARGB_8888);
|
||||
|
||||
bitmap.copyPixelsFromBuffer(imagePlane.getBuffer());
|
||||
canvas.drawBitmap(bitmap, 0, 0, null);
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Insets;
|
||||
import android.graphics.Rect;
|
||||
import android.media.ImageReader;
|
||||
import android.os.Build;
|
||||
import android.text.format.DateFormat;
|
||||
import android.util.AttributeSet;
|
||||
@ -303,6 +304,7 @@ public class FlutterView extends FrameLayout implements MouseCursorPlugin.MouseC
|
||||
super(context, attrs);
|
||||
|
||||
this.flutterImageView = flutterImageView;
|
||||
this.renderSurface = flutterImageView;
|
||||
|
||||
init();
|
||||
}
|
||||
@ -940,6 +942,32 @@ public class FlutterView extends FrameLayout implements MouseCursorPlugin.MouseC
|
||||
flutterEngine = null;
|
||||
}
|
||||
|
||||
public void convertToImageView() {
|
||||
renderSurface.detachFromRenderer();
|
||||
|
||||
ImageReader imageReader = PlatformViewsController.createImageReader(getWidth(), getHeight());
|
||||
flutterImageView = new FlutterImageView(getContext(), imageReader);
|
||||
renderSurface = flutterImageView;
|
||||
if (flutterEngine != null) {
|
||||
renderSurface.attachToRenderer(flutterEngine.getRenderer());
|
||||
}
|
||||
|
||||
removeAllViews();
|
||||
addView(flutterImageView);
|
||||
|
||||
// TODO(jsimmons): this is a temporary hack that schedules a redraw of the FlutterImageView
|
||||
// at a time when the engine has presumably posted a frame. Remove this when
|
||||
// PlatformViewsController.onEndFrame callbacks have been implemented.
|
||||
postDelayed(
|
||||
new Runnable() {
|
||||
public void run() {
|
||||
flutterImageView.acquireLatestImage();
|
||||
flutterImageView.invalidate();
|
||||
}
|
||||
},
|
||||
1000);
|
||||
}
|
||||
|
||||
/** Returns true if this {@code FlutterView} is currently attached to a {@link FlutterEngine}. */
|
||||
@VisibleForTesting
|
||||
public boolean isAttachedToFlutterEngine() {
|
||||
|
||||
@ -22,6 +22,7 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.UiThread;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import io.flutter.embedding.android.FlutterImageView;
|
||||
import io.flutter.embedding.android.FlutterView;
|
||||
import io.flutter.embedding.engine.FlutterOverlaySurface;
|
||||
import io.flutter.embedding.engine.dart.DartExecutor;
|
||||
import io.flutter.embedding.engine.systemchannels.PlatformViewsChannel;
|
||||
@ -79,9 +80,12 @@ public class PlatformViewsController implements PlatformViewsAccessibilityDelega
|
||||
// Map of unique IDs to views that render overlay layers.
|
||||
private final LongSparseArray<FlutterImageView> overlayLayerViews;
|
||||
|
||||
// Next available unique ID for use in overlayLayerViews;
|
||||
// Next available unique ID for use in overlayLayerViews.
|
||||
private long nextOverlayLayerId = 0;
|
||||
|
||||
// Tracks whether the flutterView has been converted to use a FlutterImageView.
|
||||
private boolean flutterViewConvertedToImageView = false;
|
||||
|
||||
private final PlatformViewsChannel.PlatformViewsHandler channelHandler =
|
||||
new PlatformViewsChannel.PlatformViewsHandler() {
|
||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
|
||||
@ -552,7 +556,10 @@ public class PlatformViewsController implements PlatformViewsAccessibilityDelega
|
||||
}
|
||||
|
||||
public void onDisplayOverlaySurface(int id, int x, int y, int width, int height) {
|
||||
// TODO: Implement this method. https://github.com/flutter/flutter/issues/58288
|
||||
if (!flutterViewConvertedToImageView) {
|
||||
((FlutterView) flutterView).convertToImageView();
|
||||
flutterViewConvertedToImageView = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void onBeginFrame() {
|
||||
@ -564,22 +571,22 @@ public class PlatformViewsController implements PlatformViewsAccessibilityDelega
|
||||
}
|
||||
|
||||
@TargetApi(19)
|
||||
public FlutterOverlaySurface createOverlaySurface() {
|
||||
ImageReader imageReader;
|
||||
public static ImageReader createImageReader(int width, int height) {
|
||||
if (android.os.Build.VERSION.SDK_INT >= 29) {
|
||||
imageReader =
|
||||
ImageReader.newInstance(
|
||||
flutterView.getWidth(),
|
||||
flutterView.getHeight(),
|
||||
PixelFormat.RGBA_8888,
|
||||
2,
|
||||
HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE | HardwareBuffer.USAGE_GPU_COLOR_OUTPUT);
|
||||
return ImageReader.newInstance(
|
||||
width,
|
||||
height,
|
||||
PixelFormat.RGBA_8888,
|
||||
2,
|
||||
HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE | HardwareBuffer.USAGE_GPU_COLOR_OUTPUT);
|
||||
} else {
|
||||
imageReader =
|
||||
ImageReader.newInstance(
|
||||
flutterView.getWidth(), flutterView.getHeight(), PixelFormat.RGBA_8888, 2);
|
||||
return ImageReader.newInstance(width, height, PixelFormat.RGBA_8888, 2);
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(19)
|
||||
public FlutterOverlaySurface createOverlaySurface() {
|
||||
ImageReader imageReader = createImageReader(flutterView.getWidth(), flutterView.getHeight());
|
||||
FlutterImageView imageView = new FlutterImageView(flutterView.getContext(), imageReader);
|
||||
long id = nextOverlayLayerId++;
|
||||
overlayLayerViews.put(id, imageView);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user