Unverified Commit aab47f07 authored by Todd Volkert's avatar Todd Volkert Committed by GitHub

Augment `flutter screenshot` with all supported screenshot types (#17478)

Previously, the only hook into the rasterizer schreenshot code path
was via FlutterDriver.screenshot.  This adds the ability for the
Flutter tool to hook into all three types of screenshots:

1. device (e.g. `adb shell screencap`)
2. Skia (capture spk)
3. Rasterizer (capture PNG via the engine)
parent d54858a0
......@@ -14,22 +14,41 @@ import '../runner/flutter_command.dart';
import '../vmservice.dart';
const String _kOut = 'out';
const String _kSkia = 'skia';
const String _kType = 'type';
const String _kObservatoryPort = 'observatory-port';
const String _kDeviceType = 'device';
const String _kSkiaType = 'skia';
const String _kRasterizerType = 'rasterizer';
class ScreenshotCommand extends FlutterCommand {
ScreenshotCommand() {
argParser.addOption(
_kOut,
abbr: 'o',
valueHelp: 'path/to/file',
help: 'Location to write the screenshot.',
);
argParser.addOption(
_kSkia,
_kObservatoryPort,
valueHelp: 'port',
help: 'Retrieve the last frame rendered by a Flutter app as a Skia picture\n'
'using the specified observatory port.\n'
'To find the observatory port number, use "flutter run --verbose"\n'
'and look for "Forwarded host port ... for Observatory" in the output.'
help: 'The observatory port to connect to.\n'
'This is required when --$_kType is "$_kSkiaType" or "$_kRasterizerType".\n'
'To find the observatory port number, use "flutter run --verbose"\n'
'and look for "Forwarded host port ... for Observatory" in the output.',
);
argParser.addOption(
_kType,
valueHelp: 'type',
help: 'The type of screenshot to retrieve.',
allowed: const <String>[_kDeviceType, _kSkiaType, _kRasterizerType],
allowedHelp: const <String, String>{
_kDeviceType: 'Delegate to the device\'s native screenshot capabilities. This\n'
'screenshots the entire screen currently being displayed (including content\n'
'not rendered by Flutter, like the device status bar).',
_kSkiaType: 'Render the Flutter app as a Skia picture. Requires --$_kObservatoryPort',
_kRasterizerType: 'Render the Flutter app using the rasterizer. Requires --$_kObservatoryPort',
},
defaultsTo: _kDeviceType,
);
}
......@@ -49,8 +68,10 @@ class ScreenshotCommand extends FlutterCommand {
device = await findTargetDevice();
if (device == null)
throwToolExit('Must have a connected device');
if (!device.supportsScreenshot && argResults[_kSkia] == null)
if (argResults[_kType] == _kDeviceType && !device.supportsScreenshot)
throwToolExit('Screenshot not supported for ${device.name}.');
if (argResults[_kType] != _kDeviceType && argResults[_kObservatoryPort] == null)
throwToolExit('Observatory port must be specified for screenshot type ${argResults[_kType]}');
return super.verifyThenRunCommand();
}
......@@ -60,10 +81,13 @@ class ScreenshotCommand extends FlutterCommand {
if (argResults.wasParsed(_kOut))
outputFile = fs.file(argResults[_kOut]);
if (argResults[_kSkia] != null) {
return runSkia(outputFile);
} else {
return runScreenshot(outputFile);
switch (argResults[_kType]) {
case _kDeviceType:
return runScreenshot(outputFile);
case _kSkiaType:
return runSkia(outputFile);
case _kRasterizerType:
return runRasterizer(outputFile);
}
}
......@@ -78,16 +102,33 @@ class ScreenshotCommand extends FlutterCommand {
}
Future<Null> runSkia(File outputFile) async {
final Uri observatoryUri = new Uri(scheme: 'http', host: '127.0.0.1',
port: int.parse(argResults[_kSkia]));
final VMService vmService = await VMService.connect(observatoryUri);
final Map<String, dynamic> skp = await vmService.vm.invokeRpcRaw('_flutter.screenshotSkp');
final Map<String, dynamic> skp = await _invokeVmServiceRpc('_flutter.screenshotSkp');
outputFile ??= getUniqueFile(fs.currentDirectory, 'flutter', 'skp');
final IOSink sink = outputFile.openWrite();
sink.add(base64.decode(skp['skp']));
await sink.close();
await showOutputFileInfo(outputFile);
await _ensureOutputIsNotJsonRpcError(outputFile);
}
Future<void> runRasterizer(File outputFile) async {
final Map<String, dynamic> response = await _invokeVmServiceRpc('_flutter.screenshot');
outputFile ??= getUniqueFile(fs.currentDirectory, 'flutter', 'png');
final IOSink sink = outputFile.openWrite();
sink.add(base64.decode(response['screenshot']));
await sink.close();
await showOutputFileInfo(outputFile);
await _ensureOutputIsNotJsonRpcError(outputFile);
}
Future<Map<String, dynamic>> _invokeVmServiceRpc(String method) async {
final Uri observatoryUri = new Uri(scheme: 'http', host: '127.0.0.1',
port: int.parse(argResults[_kObservatoryPort]));
final VMService vmService = await VMService.connect(observatoryUri);
return await vmService.vm.invokeRpcRaw(method);
}
Future<void> _ensureOutputIsNotJsonRpcError(File outputFile) async {
if (await outputFile.length() < 1000) {
final String content = await outputFile.readAsString(
encoding: const AsciiCodec(allowInvalid: true),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment