mirror of https://github.com/tauri-apps/tauri
feat(tauricon): fix transparency (#1678)
Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
This commit is contained in:
parent
8941790f98
commit
82a580ec45
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
"cli.js": patch
|
||||
---
|
||||
|
||||
Updates to `tauri icon`
|
||||
- detect if icon is NOT transparent via metadata
|
||||
- detect again on pixel level
|
||||
- remove pngquant as a waste of space
|
||||
- make optipng default
|
||||
- relax optipng settings
|
|
@ -43,7 +43,8 @@ if (argv.help) {
|
|||
--help, -h Displays this message
|
||||
--log, l Logging [boolean]
|
||||
--target, t Target folder (default: 'src-tauri/icons')
|
||||
--compression, c Compression type [pngquant|optipng|zopfli]
|
||||
--compression, c Compression type [optipng|zopfli]
|
||||
--ci Runs the script in CI mode
|
||||
`)
|
||||
process.exit(0)
|
||||
}
|
||||
|
|
|
@ -58,7 +58,6 @@
|
|||
"got": "11.8.2",
|
||||
"imagemin": "8.0.0",
|
||||
"imagemin-optipng": "8.0.0",
|
||||
"imagemin-pngquant": "9.0.2",
|
||||
"imagemin-zopfli": "7.0.0",
|
||||
"inquirer": "8.0.0",
|
||||
"is-png": "3.0.0",
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
import { access, ensureDir, ensureFileSync, writeFileSync } from 'fs-extra'
|
||||
import imagemin, { Plugin } from 'imagemin'
|
||||
import optipng from 'imagemin-optipng'
|
||||
import pngquant, { Options as PngQuantOptions } from 'imagemin-pngquant'
|
||||
import zopfli from 'imagemin-zopfli'
|
||||
import isPng from 'is-png'
|
||||
import path from 'path'
|
||||
|
@ -38,7 +37,7 @@ const log = logger('app:spawn')
|
|||
const warn = logger('app:spawn', chalk.red)
|
||||
|
||||
let image: boolean | sharp.Sharp = false
|
||||
const spinnerInterval = false
|
||||
let spinnerInterval: NodeJS.Timeout | null = null
|
||||
|
||||
const exists = async function (file: string | Buffer): Promise<boolean> {
|
||||
try {
|
||||
|
@ -70,7 +69,26 @@ const checkSrc = async (src: string): Promise<boolean | sharp.Sharp> => {
|
|||
} else {
|
||||
const buffer = await readChunk(src, 0, 8)
|
||||
if (isPng(buffer)) {
|
||||
return (image = sharp(src))
|
||||
image = sharp(src)
|
||||
const meta = await image.metadata()
|
||||
if (!meta.hasAlpha || meta.channels !== 4) {
|
||||
if (spinnerInterval) clearInterval(spinnerInterval)
|
||||
warn('[ERROR] Source png for tauricon is not transparent')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
// just because PNG is sneaky, lets look at the
|
||||
// individual pixels for something weird
|
||||
const stats = await image.stats()
|
||||
if (stats.isOpaque) {
|
||||
if (spinnerInterval) clearInterval(spinnerInterval)
|
||||
warn(
|
||||
'[ERROR] Source png for tauricon could not be detected as transparent'
|
||||
)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
return image
|
||||
} else {
|
||||
image = false
|
||||
if (spinnerInterval) clearInterval(spinnerInterval)
|
||||
|
@ -164,7 +182,10 @@ const progress = (msg: string): void => {
|
|||
* // later
|
||||
* clearInterval(spinnerInterval)
|
||||
*/
|
||||
const spinner = (): NodeJS.Timeout => {
|
||||
const spinner = (): NodeJS.Timeout | null => {
|
||||
if ('CI' in process.env || process.argv.some((arg) => arg === '--ci')) {
|
||||
return null
|
||||
}
|
||||
return setInterval(() => {
|
||||
process.stdout.write('/ \r')
|
||||
setTimeout(() => {
|
||||
|
@ -197,7 +218,7 @@ const tauricon = (exports.tauricon = {
|
|||
if (!src) {
|
||||
src = path.resolve(appDir, 'app-icon.png')
|
||||
}
|
||||
const spinnerInterval = spinner()
|
||||
spinnerInterval = spinner()
|
||||
options = options || settings.options.tauri
|
||||
progress(`Building Tauri icns and ico from "${src}"`)
|
||||
await this.validate(src, target)
|
||||
|
@ -211,7 +232,7 @@ const tauricon = (exports.tauricon = {
|
|||
log('no minify strategy')
|
||||
}
|
||||
progress('Tauricon Finished')
|
||||
clearInterval(spinnerInterval)
|
||||
if (spinnerInterval) clearInterval(spinnerInterval)
|
||||
return true
|
||||
},
|
||||
|
||||
|
@ -403,10 +424,6 @@ const tauricon = (exports.tauricon = {
|
|||
strategy = minify.type
|
||||
}
|
||||
switch (strategy) {
|
||||
case 'pngquant':
|
||||
// TODO: is minify.pngquantOptions the proper format?
|
||||
cmd = pngquant((minify.pngquantOptions as any) as PngQuantOptions)
|
||||
break
|
||||
case 'optipng':
|
||||
cmd = optipng(minify.optipngOptions)
|
||||
break
|
||||
|
|
|
@ -14,20 +14,10 @@ export const options = {
|
|||
minify: {
|
||||
batch: false,
|
||||
overwrite: true,
|
||||
available: ['pngquant', 'optipng', 'zopfli'],
|
||||
type: 'pngquant',
|
||||
pngcrushOptions: {
|
||||
reduce: true
|
||||
},
|
||||
pngquantOptions: {
|
||||
quality: [0.6, 0.8],
|
||||
floyd: 0.1, // 0.1 - 1
|
||||
speed: 10 // 1 - 10
|
||||
},
|
||||
available: ['optipng', 'zopfli'],
|
||||
type: 'optipng',
|
||||
optipngOptions: {
|
||||
optimizationLevel: 4,
|
||||
bitDepthReduction: true,
|
||||
colorTypeReduction: true,
|
||||
paletteReduction: true
|
||||
},
|
||||
zopfliOptions: {
|
||||
|
|
|
@ -27,6 +27,17 @@ describe('[CLI] tauri-icon internals', () => {
|
|||
expect(process.exit.mock.calls[0][0]).toBe(1)
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it('should fail if PNG does not have transparency', async () => {
|
||||
jest.spyOn(process, 'exit').mockImplementation(() => true)
|
||||
await tauricon.validate(
|
||||
'test/jest/fixtures/no-alpha.png',
|
||||
'test/jest/fixtures/'
|
||||
)
|
||||
expect(process.exit.mock.calls[0][0]).toBe(1)
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it('can validate an image as PNG', async () => {
|
||||
const valid = await tauricon.validate(
|
||||
'test/jest/fixtures/tauri-logo.png',
|
||||
|
@ -45,31 +56,18 @@ describe('[CLI] tauri-icon builder', () => {
|
|||
)
|
||||
expect(valid).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('[CLI] tauri-icon builder', () => {
|
||||
it('will not validate a non-file', async () => {
|
||||
try {
|
||||
await tauricon.make(
|
||||
'test/jest/fixtures/tauri-foo-not-found.png',
|
||||
'test/jest/tmp/pngquant',
|
||||
'pngquant'
|
||||
'test/jest/tmp/optipng',
|
||||
'optipng'
|
||||
)
|
||||
} catch (e) {
|
||||
expect(e.message).toBe('Input file is missing')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('[CLI] tauri-icon builder', () => {
|
||||
it('makes a set of icons with pngquant', async () => {
|
||||
const valid = await tauricon.make(
|
||||
'test/jest/fixtures/tauri-logo.png',
|
||||
'test/jest/tmp/pngquant',
|
||||
'pngquant'
|
||||
)
|
||||
expect(valid).toBe(true)
|
||||
})
|
||||
|
||||
it('makes a set of icons with optipng', async () => {
|
||||
const valid = await tauricon.make(
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 863 B |
|
@ -4368,17 +4368,6 @@ imagemin-optipng@8.0.0:
|
|||
is-png "^2.0.0"
|
||||
optipng-bin "^7.0.0"
|
||||
|
||||
imagemin-pngquant@9.0.2:
|
||||
version "9.0.2"
|
||||
resolved "https://registry.yarnpkg.com/imagemin-pngquant/-/imagemin-pngquant-9.0.2.tgz#38155702b0cc4f60f671ba7c2b086ea3805d9567"
|
||||
integrity sha512-cj//bKo8+Frd/DM8l6Pg9pws1pnDUjgb7ae++sUX1kUVdv2nrngPykhiUOgFeE0LGY/LmUbCf4egCHC4YUcZSg==
|
||||
dependencies:
|
||||
execa "^4.0.0"
|
||||
is-png "^2.0.0"
|
||||
is-stream "^2.0.0"
|
||||
ow "^0.17.0"
|
||||
pngquant-bin "^6.0.0"
|
||||
|
||||
imagemin-zopfli@7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/imagemin-zopfli/-/imagemin-zopfli-7.0.0.tgz#a44daa3bb80e2620cd1dc883d823b20b4d3788d6"
|
||||
|
@ -6071,13 +6060,6 @@ os-tmpdir@~1.0.2:
|
|||
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
|
||||
integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=
|
||||
|
||||
ow@^0.17.0:
|
||||
version "0.17.0"
|
||||
resolved "https://registry.yarnpkg.com/ow/-/ow-0.17.0.tgz#4f938999fed6264c9048cd6254356e0f1e7f688c"
|
||||
integrity sha512-i3keDzDQP5lWIe4oODyDFey1qVrq2hXKTuTH2VpqwpYtzPiKZt2ziRI4NBQmgW40AnV5Euz17OyWweCb+bNEQA==
|
||||
dependencies:
|
||||
type-fest "^0.11.0"
|
||||
|
||||
p-cancelable@^0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.3.0.tgz#b9e123800bcebb7ac13a479be195b507b98d30fa"
|
||||
|
@ -6378,16 +6360,6 @@ png2icons@2.0.1:
|
|||
resolved "https://registry.yarnpkg.com/png2icons/-/png2icons-2.0.1.tgz#09d8f10b71302e98ca178d3324bc4deff9b90124"
|
||||
integrity sha512-GDEQJr8OG4e6JMp7mABtXFSEpgJa1CCpbQiAR+EjhkHJHnUL9zPPtbOrjsMD8gUbikgv3j7x404b0YJsV3aVFA==
|
||||
|
||||
pngquant-bin@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/pngquant-bin/-/pngquant-bin-6.0.0.tgz#aff0d7e61095feb96ced379ad8c7294ad3dd1712"
|
||||
integrity sha512-oXWAS9MQ9iiDAJRdAZ9KO1mC5UwhzKkJsmetiu0iqIjJuW7JsuLhmc4JdRm7uJkIWRzIAou/Vq2VcjfJwz30Ow==
|
||||
dependencies:
|
||||
bin-build "^3.0.0"
|
||||
bin-wrapper "^4.0.1"
|
||||
execa "^4.0.0"
|
||||
logalot "^2.0.0"
|
||||
|
||||
posix-character-classes@^0.1.0:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
|
||||
|
@ -7827,11 +7799,6 @@ type-detect@4.0.8:
|
|||
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
|
||||
integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==
|
||||
|
||||
type-fest@^0.11.0:
|
||||
version "0.11.0"
|
||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1"
|
||||
integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==
|
||||
|
||||
type-fest@^0.20.2:
|
||||
version "0.20.2"
|
||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
|
||||
|
|
Loading…
Reference in New Issue