From c557c0e24cee63047dfba1f2ea6e58bde4512c2e Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Mon, 6 Jun 2022 17:21:49 -0400 Subject: [PATCH] Fix icon generation Signed-off-by: Sebastian Malton --- Makefile | 1 + build/generate-tray-icons.ts | 148 ++++++------------ build/tray/trayIconTemplate@3x.png | Bin 0 -> 1031 bytes build/tray/trayIconTemplate@4x.png | Bin 0 -> 1371 bytes .../tray/trayIconUpdateAvailableTemplate.png | Bin 5557 -> 466 bytes .../trayIconUpdateAvailableTemplate@2x.png | Bin 6049 -> 1048 bytes .../trayIconUpdateAvailableTemplate@3x.png | Bin 0 -> 1658 bytes .../trayIconUpdateAvailableTemplate@4x.png | Bin 0 -> 2439 bytes 8 files changed, 50 insertions(+), 99 deletions(-) create mode 100644 build/tray/trayIconTemplate@3x.png create mode 100644 build/tray/trayIconTemplate@4x.png create mode 100644 build/tray/trayIconUpdateAvailableTemplate@3x.png create mode 100644 build/tray/trayIconUpdateAvailableTemplate@4x.png diff --git a/Makefile b/Makefile index 48ce768766..9dfb2cf512 100644 --- a/Makefile +++ b/Makefile @@ -56,6 +56,7 @@ integration: build build: node_modules binaries/client yarn run npm:fix-build-version $(MAKE) build-extensions -B + yarn run build:tray-icons yarn run compile ifeq "$(DETECTED_OS)" "Windows" # https://github.com/ukoloff/win-ca#clear-pem-folder-on-publish diff --git a/build/generate-tray-icons.ts b/build/generate-tray-icons.ts index 4a1952b63d..179e82a816 100644 --- a/build/generate-tray-icons.ts +++ b/build/generate-tray-icons.ts @@ -31,113 +31,70 @@ function getSvgStyling(colouring: "dark" | "light"): string { `; } -async function getBaseIconTemplates() { +async function getBaseIconImage() { const svgData = await readFile(inputFile, { encoding: "utf-8" }); const dom = new JSDOM(`${svgData}`); const root = dom.window.document.body.getElementsByTagName("svg")[0]; root.innerHTML += getSvgStyling("light"); - return root.outerHTML; + return Buffer.from(root.outerHTML); } -async function generateNormalImages(template: string, size: number, name: string) { +async function generateImage(image: Buffer, size: number, namePrefix: string) { + sharp(image) + .resize({ width: size, height: size }) + .toFile(path.join(outputFolder, `${namePrefix}.png`)); +} + +async function generateImages(image: Buffer, size: number, name: string) { await Promise.all([ - sharp(Buffer.from(template)) - .resize({ width: size, height: size }) - .png() - .toFile(path.join(outputFolder, `${name}.png`)), - sharp(Buffer.from(template)) - .resize({ width: size*2, height: size*2 }) - .png() - .toFile(path.join(outputFolder, `${name}@2x.png`)), + generateImage(image, size, name), + generateImage(image, size*2, `${name}@2x`), + generateImage(image, size*3, `${name}@3x`), + generateImage(image, size*4, `${name}@4x`), ]); } -async function generateUpdateAvailableImages(template: string, size: number, name: string, noticeSvg: string) { - const circleSvg = new JSDOM(` - - - - - - - `).window.document.getElementsByTagName("svg")[0]; - - circleSvg.innerHTML += getSvgStyling("dark"); - - const circleBuffer = await sharp(Buffer.from(circleSvg.outerHTML)) - .resize({ - width: Math.floor(size/1.5), - height: Math.floor(size/1.5), - }) +async function generateUpdateAvailableImages(baseImage: Buffer) { + const noticeIconImage = await getNoticeIconImage(); + const circleBuffer = await sharp(Buffer.from(` + + + + `)) .toBuffer(); - await sharp(circleBuffer) - .toFile(path.join(outputFolder, "circle.png")); - - await Promise.all([ - sharp(Buffer.from(template)) - .resize({ width: size, height: size }) - .composite([ - { - input: circleBuffer, - gravity: "southeast", - /** - * The `clear` blend rule is buggy and currently doesn't work - * - * https://github.com/lovell/sharp/issues/3247 - */ - blend: "clear", - }, - { - input: ( - await sharp(Buffer.from(noticeSvg)) - .resize({ - width: Math.floor(size/1.5), - height: Math.floor(size/1.5), - }) - .toBuffer() - ), - gravity: "southeast", - }, - ]) - .png() - .toFile(path.join(outputFolder, `${name}.png`)), - sharp(Buffer.from(template)) - .composite([ - { - input: circleBuffer, - gravity: "southeast", - blend: "clear", - }, - { - input: ( - await sharp(Buffer.from(noticeSvg)) - .resize({ - width: Math.floor((size * 2)/1.5), - height: Math.floor((size * 2)/1.5), - }) - .toBuffer() - ), - gravity: "southeast", - }, - ]) - .resize({ width: size*2, height: size*2 }) - .png() - .toFile(path.join(outputFolder, `${name}@2x.png`)), - ]); + return sharp(baseImage) + .resize({ width: 128, height: 128 }) + .composite([ + { + input: circleBuffer, + top: 64, + left: 64, + blend: "dest-out", + }, + { + input: ( + await sharp(noticeIconImage) + .resize({ + width: 60, + height: 60, + }) + .toBuffer() + ), + top: 66, + left: 66, + }, + ]) + .toBuffer(); } -async function getNoticeSvg(): Promise { +async function getNoticeIconImage() { const svgData = await readFile(noticeFile, { encoding: "utf-8" }); const noticeSvgRoot = new JSDOM(svgData).window.document.getElementsByTagName("svg")[0]; - return noticeSvgRoot.outerHTML; + return Buffer.from(noticeSvgRoot.outerHTML); } async function generateTrayIcons() { @@ -145,21 +102,14 @@ async function generateTrayIcons() { console.log("Generating tray icon pngs"); await ensureOutputFoler(); - const baseTemplate = await getBaseIconTemplates(); - const noticeTemplate = await getNoticeSvg(); - - void noticeTemplate; - void generateUpdateAvailableImages; + const baseIconImage = await getBaseIconImage(); + const updateAvailableImage = await generateUpdateAvailableImages(baseIconImage); await Promise.all([ - generateNormalImages(baseTemplate, size, "trayIconTemplate"), - // generateUpdateAvailableImages(baseTemplate, size, "trayIconDarkUpdateAvailableTemplate", noticeTemplate), + generateImages(baseIconImage, size, "trayIconTemplate"), + generateImages(updateAvailableImage, size, "trayIconUpdateAvailableTemplate"), ]); - console.warn("Did not update:", [ - "trayIconUpdateAvailableTemplate.png", - "trayIconUpdateAvailableTemplate@2x.png", - ]); console.log("Generated all images"); } catch (error) { console.error(error); diff --git a/build/tray/trayIconTemplate@3x.png b/build/tray/trayIconTemplate@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..2e06ee1a7d06e3568bdb457180fe95aedd41b830 GIT binary patch literal 1031 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 literal 6049 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 EHwvH@k^lez diff --git a/build/tray/trayIconUpdateAvailableTemplate@3x.png b/build/tray/trayIconUpdateAvailableTemplate@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..4fed4a6d0956409e72d40ba8a795e65029078e60 GIT binary patch literal 1658 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 FV1mw_YMKB5 literal 0 HcmV?d00001