mapbox / spritezero

small opinionated sprites
Other
173 stars 62 forks source link

Bad space utilization when using lots of icons #63

Open JRWilding opened 5 years ago

JRWilding commented 5 years ago

Would it be possible to call ShelfPack's shrink when generating the image file, as currently we're seeing a large unused area of the sprite: image

Whilst the png will compress the area, it is fully loaded as ARGB_8888 on Android wasting a lot of memory (especially on bigger @2x)

mourner commented 4 years ago

shrink won't help in this case due to the way ShelfPack algorithm works, however we can fix the issue by switching from ShelfPack to Potpack: https://github.com/mapbox/potpack — it's designed with static use case in mind (all sizes are known beforehand), and tries to generate a near-square layout with maximum space utilization.

jseppi commented 4 years ago

@JRWilding Could you provide fixtures and code that result in a sprite like the one shown in your original post?

JRWilding commented 4 years ago

Hi @jseppi, I don't have access to the source svg files, but there are 1301 icons in a variety of sizes Heres the code:

'use strict';
​
const spritezero = require('@mapbox/spritezero');
const fs = require('fs');
const glob = require('glob');
const path = require('path');
​
if (!fs.existsSync(path.join(__dirname, 'output'))) {
    fs.mkdirSync(path.join(__dirname, 'output'));
}
if (!fs.existsSync(path.join(__dirname, 'output/glyphs'))) {
    fs.mkdirSync(path.join(__dirname, 'output/glyphs'));
}
​
[1, 1.5, 2, 3, 4].forEach(function (pxRatio) {
    const svgs = glob.sync(path.resolve(path.join(__dirname, 'glyphs/*.svg')))
        .map(function (f) {
            return {
                svg: fs.readFileSync(f),
                id: path.basename(f).replace('.svg', '')
            };
        });
​
    let pngPath = '';
    let jsonPath = '';
    if (pxRatio === 1) {
        pngPath = path.resolve(path.join(__dirname, 'output/glyphs/sprite.png'));
        jsonPath = path.resolve(path.join(__dirname, 'output/glyphs/sprite.json'));
    } else {
        pngPath = path.resolve(path.join(__dirname, 'output/glyphs/sprite@' + pxRatio + 'x.png'));
        jsonPath = path.resolve(path.join(__dirname, 'output/glyphs/sprite@' + pxRatio + 'x.json'));
    }
​
​
    // Pass `true` in the layout parameter to generate a data layout
    // suitable for exporting to a JSON sprite manifest file.
    spritezero.generateLayoutUnique({imgs: svgs, pixelRatio: pxRatio, format: true, removeOversizedIcons: true}, function (err, dataLayout) {
        if (err) return;
        fs.writeFileSync(jsonPath, JSON.stringify(dataLayout));
    });
​
    // Pass `false` in the layout parameter to generate an image layout
    // suitable for exporting to a PNG sprite image file.
    spritezero.generateLayoutUnique({imgs: svgs, pixelRatio: pxRatio, format: false, removeOversizedIcons: true}, function (err, imageLayout) {
        spritezero.generateImage(imageLayout, function (err, image) {
            if (err) return;
            fs.writeFileSync(pngPath, image);
        });
    });
});