Skip to content

Commit bd1098d

Browse files
ivanovitrosen-vladimirov
authored andcommitted
feat: introduce assets generation
Introduce ``generate-icons`` and ``generate-splashscreens`` commands to generate assets needed for iOS and Android. The operations are as follow: - Icons: - For all platforms we just resize the provided image. - Splash screens: - For Android we generate 2 png for each dpi: blank with background(background.png) and resized image based on provided image(logo.png). During the app start the logo overlays the background. - For iOS we generate 1 png for each dpi which has resized and centered image on the background. All resources with corresponding sizes and operations are described in image-definitions.ts. Added image-generation-test.png in order to provide easy way to test this functionality. For image resizing we use https://github.com/oliver-moran/jimp.
1 parent ce71e70 commit bd1098d

File tree

9 files changed

+476
-2
lines changed

9 files changed

+476
-2
lines changed

.vscode/launch.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@
2222
// "args": [ "run", "android", "--path", "cliapp"]
2323
// "args": [ "debug", "android", "--path", "cliapp"]
2424
// "args": [ "livesync", "android", "--path", "cliapp"]
25-
// "args": [ "livesync", "android", "--watch", "--path", "cliapp"]
25+
// "args": [ "livesync", "android", "--watch", "--path", "cliapp"],
26+
// "args": [ "generate-icons", "./test/image-generation-test.png", "--path", "cliapp" ],
27+
// "args": [ "generate-splashscreens", "./test/image-generation-test.png", "--path", "cliapp", "--background", "black" ],
2628
},
2729
{
2830
// in case you want to debug a single test, modify it's code to be `it.only(...` instead of `it(...`

lib/bootstrap.ts

+4
Original file line numberDiff line numberDiff line change
@@ -161,3 +161,7 @@ $injector.require("terminalSpinnerService", "./services/terminal-spinner-service
161161
$injector.require('playgroundService', './services/playground-service');
162162
$injector.require("platformEnvironmentRequirements", "./services/platform-environment-requirements");
163163
$injector.require("nativescriptCloudExtensionService", "./services/nativescript-cloud-extension-service");
164+
165+
$injector.requireCommand("generate-icons", "./commands/generate-assets")
166+
$injector.requireCommand("generate-splashscreens", "./commands/generate-assets")
167+
$injector.requirePublic("assetsGenerationService", "./services/assets-generation/assets-generation-service");

lib/commands/generate-assets.ts

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { StringCommandParameter } from "../common/command-params";
2+
3+
export abstract class GenerateCommandBase implements ICommand {
4+
public allowedParameters: ICommandParameter[] = [new StringCommandParameter(this.$injector)];
5+
6+
constructor(protected $options: IOptions,
7+
protected $injector: IInjector,
8+
protected $projectData: IProjectData,
9+
protected $assetsGenerationService: IAssetsGenerationService) {
10+
this.$projectData.initializeProjectData();
11+
}
12+
13+
public async execute(args: string[]): Promise<void> {
14+
const [ imagePath ] = args;
15+
const resourcesPath = this.$projectData.getAppResourcesDirectoryPath();
16+
await this.generate(imagePath, resourcesPath, this.$options.background)
17+
}
18+
19+
protected abstract generate(imagePath: string, resourcesPath: string, background?: string): Promise<void>
20+
}
21+
22+
export class GenerateIconsCommand extends GenerateCommandBase implements ICommand {
23+
constructor(protected $options: IOptions,
24+
$injector: IInjector,
25+
$projectData: IProjectData,
26+
$assetsGenerationService: IAssetsGenerationService) {
27+
super($options, $injector, $projectData, $assetsGenerationService);
28+
}
29+
30+
protected async generate(imagePath: string, resourcesPath: string, background?: string): Promise<void> {
31+
await this.$assetsGenerationService.generateIcons(imagePath, resourcesPath);
32+
}
33+
}
34+
35+
$injector.registerCommand("generate-icons", GenerateIconsCommand);
36+
37+
export class GenerateSplashScreensCommand extends GenerateCommandBase implements ICommand {
38+
constructor(protected $options: IOptions,
39+
$injector: IInjector,
40+
$projectData: IProjectData,
41+
$assetsGenerationService: IAssetsGenerationService) {
42+
super($options, $injector, $projectData, $assetsGenerationService);
43+
}
44+
45+
protected async generate(imagePath: string, resourcesPath: string, background?: string): Promise<void> {
46+
await this.$assetsGenerationService.generateSplashScreens(imagePath, resourcesPath, background);
47+
}
48+
}
49+
50+
$injector.registerCommand("generate-splashscreens", GenerateSplashScreensCommand);

lib/declarations.d.ts

+24
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,7 @@ interface IOptions extends ICommonOptions, IBundleString, IPlatformTemplate, IEm
463463
liveEdit: boolean;
464464
chrome: boolean;
465465
inspector: boolean; // the counterpart to --chrome
466+
background: string
466467
}
467468

468469
interface IEnvOptions {
@@ -794,3 +795,26 @@ interface INativescriptCloudExtensionService {
794795
*/
795796
install(): Promise<IExtensionData>;
796797
}
798+
799+
800+
/**
801+
* Describes service used for assets generation
802+
*/
803+
interface IAssetsGenerationService {
804+
/**
805+
* Generate icons for iOS and Android
806+
* @param {string} imagePath Path to the image that will be used for generation
807+
* @param {string} resourcesPath Path to the app resources
808+
* @returns {Promise<void>}
809+
*/
810+
generateIcons(imagePath: string, resourcesPath: string): Promise<void>;
811+
812+
/**
813+
* Generate splash screens for iOS and Android
814+
* @param {string} imagePath Path to the image that will be used for generation
815+
* @param {string} resourcesPath Path to the app resources
816+
* @param {string} background Background color that will be used for background. Defaults to #FFFFFF
817+
* @returns {Promise<void>}
818+
*/
819+
generateSplashScreens(imagePath: string, resourcesPath: string, background?: string): Promise<void>;
820+
}

lib/options.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ export class Options extends commonOptionsLibPath.OptionsBase {
3838
chrome: { type: OptionType.Boolean },
3939
inspector: { type: OptionType.Boolean },
4040
clean: { type: OptionType.Boolean },
41-
watch: { type: OptionType.Boolean, default: true }
41+
watch: { type: OptionType.Boolean, default: true },
42+
background: { type: OptionType.String }
4243
},
4344
$errors, $staticConfig, $settingsService);
4445

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import * as path from "path";
2+
import * as Jimp from "jimp";
3+
import * as Color from "color";
4+
import { Icons, SplashScreens, Operations } from "./image-definitions"
5+
6+
export class AssetsGenerationService implements IAssetsGenerationService {
7+
constructor(private $logger: ILogger) {
8+
}
9+
10+
public async generateIcons(imagePath: string, resourcesPath: string): Promise<void> {
11+
this.$logger.info("Generating icons ...");
12+
await this.generateImagesForDefinitions(imagePath, resourcesPath, Icons)
13+
this.$logger.info("Icons generation completed.");
14+
}
15+
16+
public async generateSplashScreens(imagePath: string, resourcesPath: string, background?: string): Promise<void> {
17+
this.$logger.info("Generating splash screens ...");
18+
await this.generateImagesForDefinitions(imagePath, resourcesPath, SplashScreens, background)
19+
this.$logger.info("Splash screens generation completed.");
20+
}
21+
22+
private async generateImagesForDefinitions(imagePath: string, resourcesPath: string, definitions: any[], background: string = "white") : Promise<void> {
23+
for (let definition of definitions) {
24+
const operation = definition.operation || Operations.Resize;
25+
const scale = definition.scale || 0.8;
26+
const outputPath = this.convertToAbsolutePath(resourcesPath, definition.path);
27+
28+
switch (operation) {
29+
case Operations.OverlayWith:
30+
const imageResize = Math.round(Math.min(definition.width, definition.height) * scale);
31+
const image = await this.resize(imagePath, imageResize, imageResize);
32+
await this.generateImage(background, definition.width, definition.height, outputPath, image);
33+
break;
34+
case Operations.Blank:
35+
await this.generateImage(background, definition.width, definition.height, outputPath);
36+
break;
37+
case Operations.Resize:
38+
const resizedImage = await this.resize(imagePath, definition.width, definition.height);
39+
resizedImage.write(outputPath);
40+
break;
41+
default:
42+
throw new Error(`Invalid image generation operation: ${operation}`);
43+
}
44+
}
45+
}
46+
47+
private async resize(imagePath: string, width: number, height: number) {
48+
const image = await Jimp.read(imagePath);
49+
return image.scaleToFit(width, height);
50+
}
51+
52+
private generateImage(background: string, width: number, height: number, outputPath: string, overlayImage?: Jimp) {
53+
//Typescript declarations for Jimp are not updated to define the constructor with backgroundColor so we workaround it by casting it to <any> for this case only.
54+
let J = <any>Jimp;
55+
const backgroundColor = this.getRgbaNumber(background);
56+
let image = new J(width, height, backgroundColor);
57+
58+
if (overlayImage) {
59+
const centeredWidth = (width - overlayImage.bitmap.width) / 2;
60+
const centeredHeight = (height - overlayImage.bitmap.height) / 2;
61+
image = image.composite(overlayImage, centeredWidth, centeredHeight);
62+
}
63+
64+
image.write(outputPath)
65+
}
66+
67+
private convertToAbsolutePath(resourcesPath: string, resourcePath: string) {
68+
return path.isAbsolute(resourcePath) ? resourcePath : path.join(resourcesPath, resourcePath);
69+
}
70+
71+
private getRgbaNumber(colorString: string) : number {
72+
const color = new Color(colorString);
73+
const colorRgb = color.rgb();
74+
const alpha = Math.round(colorRgb.alpha() * 255);
75+
76+
return Jimp.rgbaToInt(colorRgb.red(), colorRgb.green(), colorRgb.blue(), alpha);
77+
}
78+
}
79+
80+
$injector.register("assetsGenerationService", AssetsGenerationService);

0 commit comments

Comments
 (0)