1
1
import { ensureDir } from '@ionic/utils-fs' ;
2
2
import Debug from 'debug' ;
3
3
import pathlib from 'path' ;
4
+ import { Sharp } from 'sharp' ;
4
5
5
6
import { BadInputError , ResolveSourceImageError } from './error' ;
6
7
import { ImageSchema , debugSourceImage , generateImage , readSourceImage , resolveSourceImage } from './image' ;
@@ -27,13 +28,25 @@ export interface GeneratedResource extends ResourceKeyValues {
27
28
} ;
28
29
}
29
30
30
- export interface SimpleResourceOptions {
31
+ export type TransformFunction = ( image : ImageSchema , pipeline : Sharp ) => Sharp ;
32
+
33
+ export interface ResourceOptions < S > {
34
+ /**
35
+ * Represents the sources to use for this resource.
36
+ *
37
+ * Usually, this is a file path or {@link ImageSource}. In the case of
38
+ * Android Adaptive Icons, this may be a {@link ColorSource}.
39
+ */
40
+ readonly sources : readonly S [ ] ;
41
+
31
42
/**
32
- * Paths to source images to use for this resource .
43
+ * Additional image transformations to apply .
33
44
*/
34
- sources : ( string | ImageSource ) [ ] ;
45
+ readonly transform ?: TransformFunction ;
35
46
}
36
47
48
+ export type SimpleResourceOptions = ResourceOptions < string | ImageSource > ;
49
+
37
50
export interface SimpleResourceResult {
38
51
resources : GeneratedResource [ ] ;
39
52
source : ResolvedSource ;
@@ -56,13 +69,7 @@ export interface AdaptiveIconResourceOptions {
56
69
/**
57
70
* Options for the background portion of adaptive icons.
58
71
*/
59
- background : {
60
-
61
- /**
62
- * Paths to source images or colors to use for this resource.
63
- */
64
- sources : ( string | ImageSource | ColorSource ) [ ] ;
65
- } ;
72
+ background : ResourceOptions < string | ImageSource | ColorSource > ;
66
73
}
67
74
68
75
export interface RunPlatformOptions {
@@ -157,7 +164,7 @@ export async function generateSimpleResources(type: ResourceType.ICON | Resource
157
164
const resources = await Promise . all ( config . resources . map (
158
165
async ( resource ) : Promise < GeneratedResource > => ( {
159
166
...resource ,
160
- ...await generateImageResource ( type , platform , resourcesDirectory , config , source . image , resource , errstream ) ,
167
+ ...await generateImageResource ( type , platform , resourcesDirectory , config , source . image , resource , getResourceTransformFunction ( platform , type , options ) , errstream ) ,
161
168
} )
162
169
) ) ;
163
170
@@ -167,6 +174,25 @@ export async function generateSimpleResources(type: ResourceType.ICON | Resource
167
174
} ;
168
175
}
169
176
177
+ export function getResourceTransformFunction ( platform : Platform , type : ResourceType , { transform = ( image , pipeline ) => pipeline } : Readonly < SimpleResourceOptions > ) : TransformFunction {
178
+ const transforms = [ transform ] ;
179
+
180
+ if ( platform === Platform . IOS && type === ResourceType . ICON ) {
181
+ // Automatically remove the alpha channel for iOS icons. If alpha channels
182
+ // exist in iOS icons when uploaded to the App Store, the app may be
183
+ // rejected referencing ITMS-90717.
184
+ //
185
+ // @see https://github.com/ionic-team/cordova-res/issues/94
186
+ transforms . push ( ( image , pipeline ) => pipeline . flatten ( { background : { r : 255 , g : 255 , b : 255 } } ) ) ;
187
+ }
188
+
189
+ return combineTransformFunctions ( transforms ) ;
190
+ }
191
+
192
+ export function combineTransformFunctions ( transformations : readonly TransformFunction [ ] ) : TransformFunction {
193
+ return transformations . reduce ( ( acc , transformation ) => ( image , pipeline ) => transformation ( image , acc ( image , pipeline ) ) ) ;
194
+ }
195
+
170
196
/**
171
197
* Attempt to generate Adaptive Icons for any platform.
172
198
*
@@ -200,10 +226,10 @@ export async function generateAdaptiveIconResources(resourcesDirectory: string,
200
226
debug ( 'Building %s resources' , ResourceType . ADAPTIVE_ICON ) ;
201
227
202
228
const { resources : iconResources = [ ] , source : iconSource } = ( await safelyGenerateSimpleResources ( ResourceType . ICON , Platform . ANDROID , resourcesDirectory , options . icon , errstream ) ) || { source : undefined } ;
203
- const { resources : foregroundResources , source : foregroundSource } = await generateAdaptiveIconResourcesPortion ( resourcesDirectory , ResourceKey . FOREGROUND , options . foreground . sources , errstream ) ;
229
+ const { resources : foregroundResources , source : foregroundSource } = await generateAdaptiveIconResourcesPortion ( resourcesDirectory , ResourceKey . FOREGROUND , options . foreground . sources , options . foreground . transform , errstream ) ;
204
230
const resolvedBackgroundSource = await resolveSource ( Platform . ANDROID , ResourceType . ADAPTIVE_ICON , ResourceKey . BACKGROUND , options . background . sources , errstream ) ;
205
231
const backgroundResources = resolvedBackgroundSource . type === SourceType . RASTER
206
- ? await generateAdaptiveIconResourcesPortionFromImageSource ( resourcesDirectory , ResourceKey . BACKGROUND , resolvedBackgroundSource , errstream )
232
+ ? await generateAdaptiveIconResourcesPortionFromImageSource ( resourcesDirectory , ResourceKey . BACKGROUND , resolvedBackgroundSource , options . background . transform , errstream )
207
233
: foregroundResources . map ( resource => ( { ...resource , src : '@color/background' } ) ) ;
208
234
209
235
const resources = await consolidateAdaptiveIconResources ( foregroundResources , backgroundResources ) ;
@@ -245,16 +271,16 @@ export async function consolidateAdaptiveIconResources(foregrounds: readonly Gen
245
271
/**
246
272
* Generate the foreground of Adaptive Icons.
247
273
*/
248
- export async function generateAdaptiveIconResourcesPortion ( resourcesDirectory : string , type : ResourceKey . FOREGROUND | ResourceKey . BACKGROUND , sources : ( string | ImageSource ) [ ] , errstream ?: NodeJS . WritableStream ) : Promise < SimpleResourceResult > {
274
+ export async function generateAdaptiveIconResourcesPortion ( resourcesDirectory : string , type : ResourceKey . FOREGROUND | ResourceKey . BACKGROUND , sources : readonly ( string | ImageSource ) [ ] , transform : TransformFunction = ( image , pipeline ) => pipeline , errstream ?: NodeJS . WritableStream ) : Promise < SimpleResourceResult > {
249
275
const source = await resolveSourceImage ( Platform . ANDROID , ResourceType . ADAPTIVE_ICON , sources . map ( s => imageSourceToPath ( s ) ) , errstream ) ;
250
276
251
277
return {
252
- resources : await generateAdaptiveIconResourcesPortionFromImageSource ( resourcesDirectory , type , source , errstream ) ,
278
+ resources : await generateAdaptiveIconResourcesPortionFromImageSource ( resourcesDirectory , type , source , transform , errstream ) ,
253
279
source,
254
280
} ;
255
281
}
256
282
257
- export async function generateAdaptiveIconResourcesPortionFromImageSource ( resourcesDirectory : string , type : ResourceKey . FOREGROUND | ResourceKey . BACKGROUND , source : ResolvedImageSource , errstream ?: NodeJS . WritableStream ) : Promise < GeneratedResource [ ] > {
283
+ export async function generateAdaptiveIconResourcesPortionFromImageSource ( resourcesDirectory : string , type : ResourceKey . FOREGROUND | ResourceKey . BACKGROUND , source : ResolvedImageSource , transform : TransformFunction = ( image , pipeline ) => pipeline , errstream ?: NodeJS . WritableStream ) : Promise < GeneratedResource [ ] > {
258
284
debug ( 'Using %O for %s source image for %s' , source . image . src , ResourceType . ADAPTIVE_ICON , Platform . ANDROID ) ;
259
285
260
286
const config = getResourcesConfig ( Platform . ANDROID , ResourceType . ADAPTIVE_ICON ) ;
@@ -268,14 +294,15 @@ export async function generateAdaptiveIconResourcesPortionFromImageSource(resour
268
294
config ,
269
295
source . image ,
270
296
{ ...resource , src : resource [ type ] } ,
297
+ transform ,
271
298
errstream
272
299
) ,
273
300
} ) ) ) ;
274
301
275
302
return resources ;
276
303
}
277
304
278
- export async function generateImageResource ( type : ResourceType , platform : Platform , resourcesDirectory : string , config : ResourcesTypeConfig < ResourceKeyValues , ResourceKey > , image : ImageSourceData , schema : ResourceKeyValues & ImageSchema , errstream ?: NodeJS . WritableStream ) : Promise < GeneratedResource > {
305
+ export async function generateImageResource ( type : ResourceType , platform : Platform , resourcesDirectory : string , config : ResourcesTypeConfig < ResourceKeyValues , ResourceKey > , image : ImageSourceData , schema : ResourceKeyValues & ImageSchema , transform : TransformFunction = ( image , pipeline ) => pipeline , errstream ?: NodeJS . WritableStream ) : Promise < GeneratedResource > {
279
306
const { pipeline, metadata } = image ;
280
307
const { src, format, width, height } = schema ;
281
308
const { nodeName, nodeAttributes, indexAttribute, includedResources } = config . configXml ;
@@ -285,7 +312,9 @@ export async function generateImageResource(type: ResourceType, platform: Platfo
285
312
const dest = pathlib . join ( resourcesDirectory , src ) ;
286
313
287
314
await ensureDir ( pathlib . dirname ( dest ) ) ;
288
- await generateImage ( { src : dest , format, width, height } , pipeline . clone ( ) , metadata , errstream ) ;
315
+
316
+ const generatedImage : ImageSchema = { src : dest , format, width, height } ;
317
+ await generateImage ( generatedImage , transform ( generatedImage , pipeline . clone ( ) ) , metadata , errstream ) ;
289
318
290
319
return {
291
320
type,
0 commit comments