Skip to content

Commit

Permalink
feat: store screenshot added.
Browse files Browse the repository at this point in the history
  • Loading branch information
mathrunet committed Feb 15, 2023
1 parent 4553801 commit 9d7171a
Show file tree
Hide file tree
Showing 5 changed files with 301 additions and 4 deletions.
2 changes: 2 additions & 0 deletions packages/katana_cli/bin/katana.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ library katana_cli;
import 'dart:io';

// Package imports:
import 'package:katana_cli/command/store/store.dart';
import 'package:yaml/yaml.dart';

// Project imports:
Expand All @@ -19,6 +20,7 @@ const commands = <String, CliCommand>{
"git": GitCliCommand(),
"apply": ApplyCliCommand(),
"create": CreateCliCommand(),
"store": StoreCliCommand(),
};

Future<void> main(List<String> args) async {
Expand Down
253 changes: 253 additions & 0 deletions packages/katana_cli/lib/command/store/screenshot.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
part of katana_cli.store;

const _resolution = <String, Map<String, _Size>>{
"portrait": {
"iphone5.5": _Size(1242, 2208),
"iphone6.5": _Size(1284, 2778),
"ipad": _Size(2048, 2732),
"purchase": _Size(640, 920),
},
"landscape": {
"iphone5.5": _Size(2208, 1242),
"iphone6.5": _Size(2778, 1284),
"ipad": _Size(2732, 2048),
"purchase": _Size(920, 640),
},
};

class _Size {
const _Size(this.width, this.height);

final int width;
final int height;
}

/// Create screenshot images, etc. for the store based on the images in the `source_dir`.
///
/// The images are stored in `export_dir`.
///
/// The `color` specifies the background color.
///
/// The `orientation` specifies the orientation.
///
/// `source_dir`にある画像を元にストア用のスクリーンショット画像等を作成します。
///
/// `export_dir`に画像が格納されます。
///
/// `color`には背景色を指定します。
///
/// `orientation`には向きを指定します。
class StoreScreenshotCliCommand extends CliCommand {
/// Create screenshot images, etc. for the store based on the images in the `source_dir`.
///
/// The images are stored in `export_dir`.
///
/// The `color` specifies the background color.
///
/// The `orientation` specifies the orientation.
///
/// `source_dir`にある画像を元にストア用のスクリーンショット画像等を作成します。
///
/// `export_dir`に画像が格納されます。
///
/// `color`には背景色を指定します。
///
/// `orientation`には向きを指定します。
const StoreScreenshotCliCommand();

@override
String get description =>
"Create screenshot images, etc. for the store. ストア用のスクリーンショット画像等の作成を行います。";

@override
Future<void> exec(ExecContext context) async {
final store = context.yaml.getAsMap("store");
final screenshot = store.getAsMap("screenshot");
final exportDir = screenshot.get("export_dir", "").trimStringRight("/");
final colorCode = screenshot.get("color", "").trimString("#");
final orientation = screenshot.get("orientation", "");
final sourceDir = screenshot.get("source_dir", "");
if (exportDir.isEmpty) {
error(
"[store]->[screenshot]->[export_dir] is not found. Fill in the destination folder here.",
);
return;
}
if (sourceDir.isEmpty) {
error(
"[store]->[screenshot]->[source_dir] is not found. Fill in the source folder here.",
);
return;
}
if (colorCode.isEmpty) {
error(
"[store]->[screenshot]->[color] is not found. Enter the color code for the background color here.",
);
return;
}
label("Retrieve the source.");
// 色の変換
final color = ColorUint8.rgb(
int.parse(colorCode.substring(0, 2), radix: 16),
int.parse(colorCode.substring(2, 4), radix: 16),
int.parse(colorCode.substring(4, 6), radix: 16),
);
final document = Directory(exportDir);
if (!document.existsSync()) {
document.createSync(recursive: true);
}
final source = Directory(sourceDir);
if (!source.existsSync()) {
source.createSync(recursive: true);
}
final root = source.path.replaceAll(r"\", "/");
final sources = source.listSync().where((file) {
final path = file.path.replaceAll(r"\", "/").replaceAll("$root/", "");
if (path.startsWith(".") || path.contains("/.")) {
return false;
}
return true;
}).toList();
label("Create a featured image.");
final featureImage = File("$exportDir/feature_image.png");
if (!featureImage.existsSync()) {
final image = Image(
width: 1024,
height: 500,
)..clear(color);
File("$exportDir/feature_image.png").writeAsBytesSync(encodePng(image));
}
label("Create other screenshots.");
if (sources.isEmpty) {
for (final tmp in _resolution.entries) {
if (tmp.key != orientation) {
continue;
}
for (final val in tmp.value.entries) {
final key = val.key;
final size = val.value;
if (key == "purchase") {
final file = File("$exportDir/${key}_0.png");
if (file.existsSync()) {
continue;
}
final image = Image(width: size.width, height: size.height)
..clear(color);
file.writeAsBytesSync(encodePng(image));
} else {
for (int i = 0; i < 2; i++) {
final file = File("$exportDir/${key}_$i.png");
if (file.existsSync()) {
continue;
}
final image = Image(width: size.width, height: size.height)
..clear(color);
file.writeAsBytesSync(encodePng(image));
}
}
}
}
} else {
for (final tmp in _resolution.entries) {
if (tmp.key != orientation) {
continue;
}
for (final val in tmp.value.entries) {
final key = val.key;
final size = val.value;
for (int i = 0; i < sources.length; i++) {
if (key == "purchase") {
final file = File(sources[i].path);
final image = decodeImage(file.readAsBytesSync())!;
if (orientation == "portrait") {
final resized = copyResize(image, width: size.width);
final cropped = copyCrop(
resized,
x: 0,
y: ((resized.height - size.height) / 2.0).floor(),
width: resized.width,
height: size.height,
);
File("$exportDir/${key}_$i.png")
.writeAsBytesSync(encodePng(cropped));
} else {
final resized = copyResize(image, height: size.height);
final cropped = copyCrop(
resized,
x: ((resized.width - size.width) / 2.0).floor(),
y: 0,
width: size.width,
height: resized.height,
);
File("$exportDir/${key}_$i.png")
.writeAsBytesSync(encodePng(cropped));
}
} else {
final file = File(sources[i].path);
final image = Image(width: size.width, height: size.height)
..clear(color);
final sourceImage = dropShadow(
_resize(
_crop(
decodeImage(file.readAsBytesSync())!,
76,
48,
),
image,
64,
64,
64,
64,
),
16,
16,
16,
);
final merged = compositeImage(
image,
sourceImage,
dstX: ((image.width / 2) - (sourceImage.width / 2)).round(),
dstY: ((image.height / 2) - (sourceImage.height / 2)).round(),
);
File("$exportDir/${key}_$i.png")
.writeAsBytesSync(encodePng(merged));
}
}
}
}
}
}

Image _crop(
Image source,
double top,
double bottom,
) {
return copyCrop(
source,
x: 0,
y: top.round(),
width: source.width,
height: source.height - (top + bottom).round(),
);
}

Image _resize(
Image source,
Image container,
double left,
double top,
double right,
double bottom,
) {
final width = container.width - (left + right);
final height = container.height - (top + bottom);
final wRatio = source.width / width;
final hRatio = source.height / height;
if (wRatio >= hRatio) {
return copyResize(source, width: width.floor());
} else {
return copyResize(source, height: height.floor());
}
}
}
25 changes: 25 additions & 0 deletions packages/katana_cli/lib/command/store/store.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
library katana_cli.store;

// Dart imports:
import 'dart:io';

// Package imports:
import 'package:image/image.dart';

// Project imports:
import 'package:katana_cli/katana_cli.dart';

part 'screenshot.dart';

class StoreCliCommand extends CliCommandGroup {
const StoreCliCommand();

@override
String get groupDescription =>
"Configure settings for the store, including creating screenshots. スクリーンショットの作成など、ストア用の設定を行います。";

@override
Map<String, CliCommand> get commands => const {
"screenshot": StoreScreenshotCliCommand(),
};
}
17 changes: 17 additions & 0 deletions packages/katana_cli/lib/config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -155,5 +155,22 @@ github:
# Please copy and include your team ID from https://developer.apple.com/account.
# https://developer.apple.com/account のチームIDをコピーして記載してください。
team_id:
# Store-related information.
# ストア関連の情報を記載します。
store:
# Create a simple screenshot to be posted in the store. It can be executed with the following command.
# ストアに掲載する簡易的なスクリーンショットの作成を行います。下記コマンドで実行可能です。
# `katana store screenshot`
#
# Generate images for screenshots in [export_dir] based on the list of images in [source_dir].
# You can specify the orientation (`portrait` or `landscape`) with [orientation] and the background color with [color].
# [source_dir]にある画像一覧を元に[export_dir]にスクリーンショット用の画像を生成します。
# [orientation]で(`portrait` or `landscape`)向きを指定でき、[color]を指定すると背景色を指定できます。
screenshot:
source_dir: document/screenshot
export_dir: document/store
orientation: portrait
color: ffffff
""";
}
8 changes: 4 additions & 4 deletions packages/katana_cli/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ packages:
dependency: transitive
description:
name: args
sha256: "139d809800a412ebb26a3892da228b2d0ba36f0ef5d9a82166e5e52ec8d61611"
sha256: "4cab82a83ffef80b262ddedf47a0a8e56ee6fbf7fe21e6e768b02792034dd440"
url: "https://pub.dev"
source: hosted
version: "2.3.2"
version: "2.4.0"
asn1lib:
dependency: transitive
description:
Expand Down Expand Up @@ -109,10 +109,10 @@ packages:
dependency: "direct main"
description:
name: image
sha256: "3686865febd85c57a632d87a0fb1f0a8a9ef602fdb68d909c3e9aa29ec70fd24"
sha256: "483a389d6ccb292b570c31b3a193779b1b0178e7eb571986d9a49904b6861227"
url: "https://pub.dev"
source: hosted
version: "4.0.13"
version: "4.0.15"
import_sorter:
dependency: "direct dev"
description:
Expand Down

0 comments on commit 9d7171a

Please sign in to comment.