Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge control images into a single sprite sheet? #30

Closed
TransparentLC opened this issue May 5, 2022 · 11 comments · Fixed by #46
Closed

Merge control images into a single sprite sheet? #30

TransparentLC opened this issue May 5, 2022 · 11 comments · Fixed by #46
Assignees
Labels
enhancement New feature or request WIP Work in progress

Comments

@TransparentLC
Copy link

This theme is really helpful to make a GUI application with modern style. I have already used it in my personal projects!

The theme's controls are made up of hundreds of png images, and I think that lots of tiny files might be a little unfriendly to hard drive and compression. So I tried to merge these images into a single sprite sheet (the following image) and it works great. This is a small enhancement and may be useful for distributing the theme with GUI applications since the users generally don't need to modify these images.

sprites

(I put the light and dark theme together)

merged.zip

The zip archive contains the following files:

  • sprites.png The merged sprite sheet image. It can be created with TexturePacker (non-free) or Free texture packer (free and open-source).
  • sprites.json The sprite sheet image's metadata in "JSON (array)" format.
  • extract.py Reads the metadata and generates tcl code to load separate files from the sprite sheet. The code replaces load_images.
  • sun-valley.tcl Modified tcl code.
@rdbende
Copy link
Owner

rdbende commented May 13, 2022

Looks really cool! thanks!

I'll look more into it, when I have more time.

@rdbende
Copy link
Owner

rdbende commented May 13, 2022

Just tried it out. WOW!

@TransparentLC
Copy link
Author

TransparentLC commented May 14, 2022

Free texture packer uses Max Rects Packer written in JS to get the images' position (x, y, w, h) in packed sprite sheet. So the sprite sheet and tcl code can be generated programmatically.

I suggest using GitHub Actions to generate the sprite sheet and modified tcl code automatically then upload them as artifacts and I'm glad to make a pull request if you need. The problem is how can I replace the load_images in a cleaner way than keyword/regex substitution?

@onyx-and-iris
Copy link

will you be updating the pypi package sv_ttk with this change?

@rdbende
Copy link
Owner

rdbende commented May 27, 2022

I need to investigate this a bit more, but then yes.

@TransparentLC
Copy link
Author

TransparentLC commented May 28, 2022

My Node.js script to generate the sprite sheet (then compress with TinyPNG) and tcl code that replaces load_images:

package.json
{
    "type": "module",
    "dependencies": {
        "maxrects-packer": "^2.7.3",
        "sharp": "^0.30.5"
    }
}
pack.js
import fs from 'fs';
import path from 'path';
import sharp from 'sharp';
import { MaxRectsPacker } from 'maxrects-packer';

for (const theme of ['dark', 'light']) {
    const packer = new MaxRectsPacker(Infinity, Infinity, 0, {
        pot: false,
        square: true,
    });
    packer.addArray(await Promise.all(fs.readdirSync(theme).map(async f => {
        const p = path.join(theme, f);
        const { width, height } = await sharp(p).metadata();
        const imagePath = path.join(theme, f);
        return {
            width,
            height,
            imagePath,
        };
    })));

    const packedBin = packer.bins[0];

    const packedImage = await sharp({
        create: {
            width: packedBin.width,
            height: packedBin.height,
            channels: 4,
            background: 'transparent',
        },
    })
        .composite(packedBin.rects.map(e => ({
            input: e.imagePath,
            left: e.x,
            top: e.y,
        })))
        .png({
            compressionLevel: 9,
        })
        .toBuffer();

    try {
        const shrinked = (await fetch('https://tinypng.com/web/shrink', {
            method: 'post',
            body: packedImage,
            headers: {
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko',
                'X-Forwarded-For': Array(4).fill().map(() => Math.floor(Math.random() * 256)).join('.'),
            },
        }).then(r => r.json())).output.url;
        fs.writeFileSync(`${theme}.png`, Buffer.from((await fetch(shrinked).then(r => r.arrayBuffer()))));
    } catch (err) {
        console.log('Failed to minify with TinyPNG.', err);
        fs.writeFileSync(`${theme}.png`, packedImage);
    }

    fs.writeFileSync(`${theme}.packed.tcl`, [
        'variable images',
        `variable s [image create photo -file [file join [file dirname [info script]] ${theme}.png] -format png]`,
        `foreach {k x y w h} [list ${packedBin.rects.map(e => `${path.parse(e.imagePath).name} ${e.x} ${e.y} ${e.width} ${e.height}`).join(' ')}] {`,
        '    set images($k) [image create photo -width $w -height $h]',
        '    $images($k) copy $sprites -from $x $y [expr {$x+$w}] [expr {$y+$h}]',
        '}',
        'unset s',
    ].join('\n'));
}

Generated tcl code example:

variable images
variable s [image create photo -file [file join [file dirname [info script]] light.png] -format png]
foreach {k x y w h} [list card 0 0 50 50 notebook-border 50 0 40 40 ...] {
    set images($k) [image create photo -width $w -height $h]
    $images($k) copy $sprites -from $x $y [expr {$x+$w}] [expr {$y+$h}]
}
unset s

@rdbende rdbende pinned this issue Jul 11, 2022
@rdbende rdbende added the enhancement New feature or request label Jul 16, 2022
@rdbende rdbende self-assigned this Jul 16, 2022
@rdbende rdbende added the WIP Work in progress label Aug 8, 2022
@rdbende
Copy link
Owner

rdbende commented Sep 4, 2022

@TransparentLC Just wanna let you know that this is almost finished together with some bugfixes, I just need to do some cleanup in the theme files, and then I'll make a new release. Probably I'll use this in my Azure and Forest theme as well.

@onyx-and-iris
Copy link

This will be an awesome update! Thanks both of you.

@rdbende
Copy link
Owner

rdbende commented Sep 10, 2022

@TransparentLC Just wanna let you know that this is almost finished together with some bugfixes, I just need to do some cleanup in the theme files, and then I'll make a new release. Probably I'll use this in my Azure and Forest theme as well.

Shit, I lost all my edits somewhere on my computer. I remember, it was in some totally random folder, and I didn't commit my changes, and now I can't find it. I'm so angry 🤬🤬🤬

@rdbende
Copy link
Owner

rdbende commented Sep 10, 2022

Never mind, after 45 minutes of searching I found them inside the trash, deleted from ~/Themes/ttk_themes/Theme/sv_theme. Sheesh... 😌

@rdbende rdbende mentioned this issue Sep 11, 2022
@rdbende rdbende linked a pull request Sep 11, 2022 that will close this issue
@rdbende
Copy link
Owner

rdbende commented Sep 11, 2022

@TransparentLC Huge thanks for all this! 👏🎉

Version 2 is now out: #47

@rdbende rdbende unpinned this issue Sep 11, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request WIP Work in progress
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants