Skip to content

Commit

Permalink
Add support for circle icons with drop shadow as often used on Android (
Browse files Browse the repository at this point in the history
#233)

This change should be backward compatible.

The new circle icon style can be enabled with following config:

  icons: {
    android: {
      background: true,
      mask: true,
      overlayShadow: true
    }
  }

This change changes some code parts where config is applied in order to
allow more config combinations. All options can now be applied on all
platforms. This is also the reason why background test snapshots have
been updated (background config option was ignored on windows in any case).
  • Loading branch information
uwolfer authored and brunocodutra committed Aug 26, 2018
1 parent ecae2c1 commit 290f081
Show file tree
Hide file tree
Showing 14 changed files with 3,389 additions and 869 deletions.
2 changes: 1 addition & 1 deletion gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ gulp.task("default", () =>
.pipe(babel())
.pipe(gulp.dest("dist/")),

gulp.src(["src/mask.png", "src/overlay.png"]).pipe(gulp.dest("dist/")),
gulp.src(["src/mask.png", "src/overlay-*.png"]).pipe(gulp.dest("dist/")),

gulp.src(["src/config/*"]).pipe(gulp.dest("dist/config/"))
])
Expand Down
19 changes: 11 additions & 8 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,18 @@ var favicons = require('favicons'),
// * false - use default
// * true - force use default, e.g. set background for Android icons
// * color - set background for the specified icons
// * mask - apply mask in order to create circle icon (applied by default for firefox). `boolean`
// * overlayGlow - apply glow effect after mask has been applied (applied by default for firefox). `boolean`
// * overlayShadow - apply drop shadow after mask has been applied .`boolean`
//
android: true, // Create Android homescreen icon. `boolean` or `{ offset, background }`
appleIcon: true, // Create Apple touch icons. `boolean` or `{ offset, background }`
appleStartup: true, // Create Apple startup images. `boolean` or `{ offset, background }`
coast: true, // Create Opera Coast icon. `boolean` or `{ offset, background }`
favicons: true, // Create regular favicons. `boolean`
firefox: true, // Create Firefox OS icons. `boolean` or `{ offset, background }`
windows: true, // Create Windows 8 tile icons. `boolean` or `{ background }`
yandex: true // Create Yandex browser icon. `boolean` or `{ background }`
android: true, // Create Android homescreen icon. `boolean` or `{ offset, background, mask, overlayGlow, overlayShadow }`
appleIcon: true, // Create Apple touch icons. `boolean` or `{ offset, background, mask, overlayGlow, overlayShadow }`
appleStartup: true, // Create Apple startup images. `boolean` or `{ offset, background, mask, overlayGlow, overlayShadow }`
coast: true, // Create Opera Coast icon. `boolean` or `{ offset, background, mask, overlayGlow, overlayShadow }`
favicons: true, // Create regular favicons. `boolean` or `{ offset, background, mask, overlayGlow, overlayShadow }`
firefox: true, // Create Firefox OS icons. `boolean` or `{ offset, background, mask, overlayGlow, overlayShadow }`
windows: true, // Create Windows 8 tile icons. `boolean` or `{ offset, background, mask, overlayGlow, overlayShadow }`
yandex: true // Create Yandex browser icon. `boolean` or `{ offset, background, mask, overlayGlow, overlayShadow }`
}
},
callback = function (error, response) {
Expand Down
9 changes: 6 additions & 3 deletions src/config/icons.json
Original file line number Diff line number Diff line change
Expand Up @@ -274,21 +274,24 @@
"height": 60,
"transparent": false,
"rotate": false,
"mask": true
"mask": true,
"overlayGlow": true
},
"firefox_app_128x128.png": {
"width": 128,
"height": 128,
"transparent": false,
"rotate": false,
"mask": true
"mask": true,
"overlayGlow": true
},
"firefox_app_512x512.png": {
"width": 512,
"height": 512,
"transparent": false,
"rotate": false,
"mask": true
"mask": true,
"overlayGlow": true
}
},
"windows": {
Expand Down
22 changes: 20 additions & 2 deletions src/config/platform-options.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,28 @@
{
"offset": {
"platforms": ["android", "appleIcon", "appleStartup", "firefox", "coast"],
"platforms": ["android", "appleIcon", "appleStartup", "firefox", "coast", "windows", "yandex", "favicons"],
"defaultTo": 0
},
"background": {
"platforms": ["android", "appleIcon", "appleStartup", "firefox", "coast", "windows", "yandex"],
"platforms": ["android", "appleIcon", "appleStartup", "firefox", "coast", "windows", "yandex", "favicons"],
"appleIcon": true,
"appleStartup": true,
"firefox": true,
"coast": true,
"defaultTo": false
},
"mask": {
"platforms": ["android", "appleIcon", "appleStartup", "firefox", "coast", "windows", "yandex", "favicons"],
"firefox": true,
"defaultTo": false
},
"overlayGlow": {
"platforms": ["android", "appleIcon", "appleStartup", "firefox", "coast", "windows", "yandex", "favicons"],
"firefox": true,
"defaultTo": false
},
"overlayShadow": {
"platforms": ["android", "appleIcon", "appleStartup", "firefox", "coast", "windows", "yandex", "favicons"],
"defaultTo": false
}
}
55 changes: 29 additions & 26 deletions src/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,23 +80,17 @@ module.exports = function(options) {
}

for (const key of Object.keys(PLATFORM_OPTIONS)) {
const { platforms, defaultTo } = PLATFORM_OPTIONS[key];
const platformOption = PLATFORM_OPTIONS[key];
const { platforms, defaultTo } = platformOption;

if (!(key in parameters) && platforms.includes(platform)) {
parameters[key] = defaultTo;
parameters[key] =
platform in platformOption ? platformOption[platform] : defaultTo;
}
}

if (typeof parameters.background === "boolean") {
if (platform === "android" && !parameters.background) {
parameters.background = "transparent";
} else {
parameters.background = options.background;
}
}

if (platform === "android" && parameters.background !== "transparent") {
parameters.disableTransparency = true;
if (parameters.background === true) {
parameters.background = options.background;
}

return parameters;
Expand Down Expand Up @@ -198,21 +192,21 @@ module.exports = function(options) {
},

Images: {
create(properties, background) {
create(properties) {
return new Promise((resolve, reject) => {
log(
"Image:create",
`Creating empty ${properties.width}x${
properties.height
} canvas with ${
properties.transparent ? "transparent" : background
properties.transparent ? "transparent" : properties.background
} background`
);

this.jimp = new Jimp(
properties.width,
properties.height,
properties.transparent ? 0 : parseColor(background),
properties.transparent ? 0 : parseColor(properties.background),
(error, canvas) => (error ? reject(error) : resolve(canvas))
);
});
Expand Down Expand Up @@ -280,22 +274,31 @@ module.exports = function(options) {
},

mask: Jimp.read(path.join(__dirname, "mask.png")),
overlay: Jimp.read(path.join(__dirname, "overlay.png")),
overlayGlow: Jimp.read(path.join(__dirname, "overlay-glow.png")),
// Gimp drop shadow filter: input: mask.png, config: X: 2, Y: 5, Offset: 5, Color: black, Opacity: 20
overlayShadow: Jimp.read(path.join(__dirname, "overlay-shadow.png")),

composite(canvas, image, properties, offset, max) {
if (properties.mask) {
log("Images:composite", "Masking composite image on circle");
return Promise.all([this.mask, this.overlay]).then(
([mask, overlay]) => {
canvas.mask(mask.clone().resize(max, Jimp.AUTO), 0, 0);
canvas.composite(overlay.clone().resize(max, Jimp.AUTO), 0, 0);
properties = Object.assign({}, properties, {
mask: false
});

return this.composite(canvas, image, properties, offset, max);
return Promise.all([
this.mask,
this.overlayGlow,
this.overlayShadow
]).then(([mask, glow, shadow]) => {
canvas.mask(mask.clone().resize(max, Jimp.AUTO), 0, 0);
if (properties.overlayGlow) {
canvas.composite(glow.clone().resize(max, Jimp.AUTO), 0, 0);
}
);
if (properties.overlayShadow) {
canvas.composite(shadow.clone().resize(max, Jimp.AUTO), 0, 0);
}
properties = Object.assign({}, properties, {
mask: false
});

return this.composite(canvas, image, properties, offset, max);
});
}

log(
Expand Down
14 changes: 8 additions & 6 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,18 @@ function favicons(source, options = {}, next) {
const maximum = Math.max(properties.width, properties.height);
const offset = Math.round(maximum / 100 * platformOptions.offset) || 0;

if (platformOptions.disableTransparency) {
properties.transparent = false;
}
const mergedProperties = Object.assign({}, properties, platformOptions);

mergedProperties.transparent =
!mergedProperties.background ||
mergedProperties.background === "transparent";

return Promise.all([
µ.Images.create(properties, platformOptions.background),
µ.Images.render(sourceset, properties, offset)
µ.Images.create(mergedProperties),
µ.Images.render(sourceset, mergedProperties, offset)
])
.then(([canvas, buffer]) =>
µ.Images.composite(canvas, buffer, properties, offset, maximum)
µ.Images.composite(canvas, buffer, mergedProperties, offset, maximum)
)
.then(contents => ({ name, contents }));
}
Expand Down
File renamed without changes
Binary file added src/overlay-shadow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 3 additions & 13 deletions test/failure.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,25 +64,15 @@ test("should fail gracefully if path to source image is invalid", async t => {
});

test("should fail gracefully if option is not supported on platform", async t => {
t.plan(2);

try {
await favicons(logo_png, {
icons: {
favicons: { offset: 10 }
}
});
} catch (err) {
t.is(err.message, "Unsupported option 'offset' on platform 'favicons'");
}
t.plan(1);

try {
await favicons(logo_png, {
icons: {
favicons: { background: true }
favicons: { foo: 10 }
}
});
} catch (err) {
t.is(err.message, "Unsupported option 'background' on platform 'favicons'");
t.is(err.message, "Unsupported option 'foo' on platform 'favicons'");
}
});
27 changes: 27 additions & 0 deletions test/overlayshadow.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
const favicons = require("../src");
const test = require("ava");

const { logo_png, normalize } = require("./util");

test("should allow configuring 'overlayShadow'", async t => {
t.plan(1);

const result = await favicons(logo_png, {
icons: {
android: {
background: true,
mask: true,
overlayShadow: true
},
appleIcon: false,
appleStartup: false,
coast: false,
firefox: false,
windows: false,
favicons: false,
yandex: false
}
});

t.snapshot(normalize(result));
});
Loading

0 comments on commit 290f081

Please sign in to comment.