diff --git a/screenshots/convert_to_store_screenshot.dart b/screenshots/convert_to_store_screenshot.dart new file mode 100644 index 0000000..5ed7570 --- /dev/null +++ b/screenshots/convert_to_store_screenshot.dart @@ -0,0 +1,135 @@ +import 'dart:io'; + +import 'package:image/image.dart'; + +import 'models/screenshot_args.dart'; +import 'models/screenshot_device.dart'; +import 'models/screenshot_layout.dart'; +import 'utils/parse_configs.dart'; + +final _configs = parseScreenshotConfigs(); + +extension ScreenshotImage on Image { + Image convertToStoreScreenshot({ + required ScreenshotArgs args, + required ScreenshotLayout layout, + }) { + if (_configs[args.name] == null) { + return this; + } + print(args.deviceName); + return _addSystemOverlay( + screenshotDevices[args.deviceName]!, + isDark: args.isDark, + ) + ._addDeviceFrame( + screenshotDevices[args.deviceName]!, + ColorRgba8( + args.backgroundColor.r, + args.backgroundColor.g, + args.backgroundColor.b, + args.backgroundColor.a, + ), + ) + ._applyLayout(layout) + ._addText( + layout, + _configs[args.name]!.title, + _configs[args.name]!.subtitle, + ); + } + + Image _addSystemOverlay(ScreenshotDevice device, {required bool isDark}) { + final path = isDark ? device.systemOverlayPathDark : device.systemOverlayPathLight; + final statusBar = copyResize( + decodePng(File(path).readAsBytesSync())!, + width: width, + ); + return compositeImage(this, statusBar); + } + + Image _addDeviceFrame(ScreenshotDevice device, Color backgroundColor) { + final screenshotRounded = copyCrop( + this, + x: 0, + y: 0, + width: width, + height: height, + ); + + final frame = decodePng(File(device.deviceFramePath).readAsBytesSync())!; + final expandedScreenshot = copyExpandCanvas( + copyExpandCanvas( + screenshotRounded, + newWidth: screenshotRounded.width + device.screenshotFrameOffset.dx, + newHeight: screenshotRounded.height + device.screenshotFrameOffset.dy, + position: ExpandCanvasPosition.bottomRight, + backgroundColor: backgroundColor, + ), + newWidth: frame.width, + newHeight: frame.height, + position: ExpandCanvasPosition.topLeft, + backgroundColor: backgroundColor, + ); + + return compositeImage(expandedScreenshot, frame); + } + + Image _applyLayout(ScreenshotLayout layout) { + final scaledScreenshot = copyResize( + this, + width: layout.size.width - (layout.contentPadding.left + layout.contentPadding.right), + ); + + return copyExpandCanvas( + copyExpandCanvas( + scaledScreenshot, + newWidth: scaledScreenshot.width + layout.contentPadding.right, + newHeight: scaledScreenshot.height + layout.contentPadding.bottom, + position: ExpandCanvasPosition.topLeft, + backgroundColor: getPixel(0, 0), + ), + newWidth: layout.size.width, + newHeight: layout.size.height, + position: ExpandCanvasPosition.bottomRight, + backgroundColor: getPixel(0, 0), + ); + } + + Image _addText(ScreenshotLayout layout, String title, String subtitle) { + final titleFont = BitmapFont.fromZip(File(layout.titleFontPath).readAsBytesSync()); + final subtitleFont = BitmapFont.fromZip(File(layout.subtitleFontPath).readAsBytesSync()); + final textImage = fill( + Image( + height: titleFont.lineHeight + 36 + subtitleFont.lineHeight * 2, + width: layout.size.width - (layout.contentPadding.left + layout.contentPadding.right), + ), + color: getPixel(0, 0), + ); + + drawString( + textImage, + title, + font: titleFont, + y: 0, + ); + + int subtitleDy = titleFont.lineHeight + 36; + subtitle.split('\n').forEach((line) { + drawString( + textImage, + line, + font: subtitleFont, + y: subtitleDy, + ); + subtitleDy += subtitleFont.lineHeight; + }); + + return compositeImage( + this, + textImage, + dstX: layout.contentPadding.left, + dstY: layout.contentPadding.top, + ); + } +} diff --git a/screenshots/models/screenshot_device.dart b/screenshots/models/screenshot_device.dart index f1805e2..b952592 100644 --- a/screenshots/models/screenshot_device.dart +++ b/screenshots/models/screenshot_device.dart @@ -1,17 +1,5 @@ enum ScreenshotDevicePlatform { android, ios } -final screenshotDevicesIos = [ - ScreenshotDevice.fromDisplayName( - displayName: 'iPhone 8 Plus', - platform: ScreenshotDevicePlatform.ios, - ), - ScreenshotDevice.fromDisplayName( - displayName: 'iPhone 13 Pro', - platform: ScreenshotDevicePlatform.ios, - screenshotFrameOffset: (dx: 72, dy: 60), - ), -]; - class ScreenshotDevice { final String name; final ScreenshotDevicePlatform platform; @@ -35,3 +23,16 @@ class ScreenshotDevice { 'screenshots/assets/system_overlays/${platform.name}/${name}_system_overlay_dark.png'; String get deviceFramePath => 'screenshots/assets/frames/${platform.name}/${name}_frame.png'; } + +final screenshotDevices = {for (final d in _screenshotDevicesIos) d.name: d}; +final List _screenshotDevicesIos = [ + ScreenshotDevice.fromDisplayName( + displayName: 'iPhone 8 Plus', + platform: ScreenshotDevicePlatform.ios, + ), + ScreenshotDevice.fromDisplayName( + displayName: 'iPhone 13 Pro', + platform: ScreenshotDevicePlatform.ios, + screenshotFrameOffset: (dx: 72, dy: 60), + ), +]; diff --git a/screenshots/utils/parse_configs.dart b/screenshots/utils/parse_configs.dart new file mode 100644 index 0000000..d082b77 --- /dev/null +++ b/screenshots/utils/parse_configs.dart @@ -0,0 +1,14 @@ +import 'dart:convert'; +import 'dart:io'; + +import '../models/screenshot_config.dart'; + +Map parseScreenshotConfigs([String locale = 'en']) { + final configPath = 'screenshots/assets/content/screenshot_titles_$locale.json'; + final data = jsonDecode(File(configPath).readAsStringSync()) as Map; + final entries = (data['screenshots'] as List).map((value) { + final config = ScreenshotConfig.fromJson(value as Map); + return MapEntry(config.screenshotName, config); + }); + return Map.fromEntries(entries); +}