themix-project/oomox-gtk-theme

Produce Theme Specific GTK3 and Metacity Thumbnails

actionless opened this issue · 24 comments

From @smurphos on August 26, 2017 9:7

The thumbnails currently produced on exporting a theme are the generic Numix thumbnails - it would be good if they reflected the theme characteristics being exported.

Copied from original issue: themix-project/themix-gui#97

Hi,
I tried creating a SVG version of gtk-3.0/thumbnail.png:
(zip is here)

<svg xmlns="http://www.w3.org/2000/svg" width="120" height="35" viewBox="0 0 120 35">
  <defs>
    <linearGradient id="bg"><stop stop-color="#DEDEDE"/></linearGradient>
    <linearGradient id="button-bg" x2="0" y2="1">
      <stop stop-color="#F0F0F0"/>
      <stop stop-color="#F0F0F0" offset="1"/>
    </linearGradient>
    <linearGradient id="button-fg"><stop stop-color="#555555"/></linearGradient>
    <linearGradient id="button-border"><stop stop-color="#9C9C9C"/></linearGradient>
    <linearGradient id="checkradio-bg"><stop stop-color="#F9F9F9"/></linearGradient>
    <linearGradient id="checkradio-fg"><stop stop-color="#D64937"/></linearGradient>
    <linearGradient id="checkradio-border"><stop stop-color="#A8A8A8"/></linearGradient>
    <filter id="drop-shadow" color-interpolation-filters="sRGB" x="-0.5" y="-0.5" width="2" height="2">
      <feGaussianBlur in="SourceAlpha" stdDeviation="1"/>
      <feOffset dx="0" dy="0.5" result="offsetblur"/>
      <feFlood flood-color="#000000" flood-opacity="0.12"/>
      <feComposite in2="offsetblur" operator="in"/>
      <feMerge>
        <feMergeNode/>
        <feMergeNode in="SourceGraphic"/>
      </feMerge>
    </filter>
  </defs>
  <rect width="120" height="35" fill="url(#bg)"/>
  <g transform="translate(2, 2)" id="button">
    <rect x="0.5" y="0.5" width="50" height="24" ry="1.5" fill="url(#button-bg)" stroke="url(#button-border)" stroke-width="1" filter="url(#drop-shadow)"/>
    <text x="25" y="17" text-anchor="middle" font-family="sans-serif" font-size="9.75pt" fill="url(#button-fg)">Button</text>
  </g>
  <g transform="translate(60, 6)" id="check">
    <rect x="0.5" y="0.5" width="15" height="15" ry="1.5" fill="url(#checkradio-bg)" stroke="url(#checkradio-border)"/>
    <path d="M11.681 3.928a1.467 1.467 0 0 0-.987.427L6.653 8.2 5.27 6.65c-.494-.659-1.574-.729-2.146-.142-.572.588-.494 1.692.156 2.186l2.39 2.569c.94.94 1.362.814 2.31-.138 0 0 3.258-3.92 5.169-5.243.423-.4.599-.513.376-1.054-.22-.544-1.27-.92-1.845-.9z" fill="url(#checkradio-fg)"/>
  </g>
  <g transform="translate(86, 6)" id="radio">
    <circle cx="8" cy="8" r="7.5" fill="url(#checkradio-bg)" stroke="url(#checkradio-border)"/>
    <circle cx="8" cy="8" r="3" fill="url(#checkradio-fg)"/>
  </g>
</svg>

Hopefully, users can create a specific PNG with Inkscape (e.g. inkscape -z -e thumbnail.png thumbnail.svg) after editing the following values with script:

  • stop-color of id="bg" for BG
  • stop-color of id="button-bg" for BTN_BG (and hopefully GRADIENT)
  • stop-color of id="button-fg" for BTN_FG
  • stop-color of id="button-border" for darker BTN_BG (?)
  • stop-color of id="checkradio-bg" for TXT_BG
  • stop-color of id="checkradio-fg" for SEL_BG
  • stop-color of id="checkradio-border" for darker TXT_BG (?)
  • ry (value = border-radius - border-width / 2) for ROUNDNESS
  • stroke-width for when oomox makes border-width configurable (?)

Possibly I need to revise this SVG for the border colors. (It will be a bit complicated to handle ry then.)
Or can we handle the colors without revision? 🤔 What do you think?

that's really nice, thanks a lot!
tonight i'll integrate that to change_color script

using semi-transparent FG for checkbox will be accurate: https://github.com/actionless/oomox-gtk-theme/blob/master/src/assets/checkbox-checked.svg?short_path=64ec759#L6

also i think border color is not that much important in such preview as for me so for buttons a similar trick can be used (but with BTN_FG instead of FG)

So I've updated the SVG for border colors. Simply making stroke semi-transparent doesn't look good, so I separated fill and stroke.

<svg xmlns="http://www.w3.org/2000/svg" width="120" height="35" viewBox="0 0 120 35">
  <defs>
    <linearGradient id="bg"><stop stop-color="#DEDEDE"/></linearGradient>
    <linearGradient id="button-bg" x2="0" y2="1">
      <stop stop-color="#F0F0F0"/>
      <stop stop-color="#F0F0F0" offset="1"/>
    </linearGradient>
    <linearGradient id="button-fg"><stop stop-color="#555555"/></linearGradient>
    <linearGradient id="button-border"><stop stop-color="#555555" stop-opacity="0.3"/></linearGradient>
    <linearGradient id="checkradio-bg"><stop stop-color="#F9F9F9"/></linearGradient>
    <linearGradient id="checkradio-fg"><stop stop-color="#D64937"/></linearGradient>
    <linearGradient id="checkradio-border"><stop stop-color="#555555" stop-opacity="0.3"/></linearGradient>
    <filter id="drop-shadow" color-interpolation-filters="sRGB" x="-0.5" y="-0.5" width="2" height="2">
      <feGaussianBlur in="SourceAlpha" stdDeviation="1"/>
      <feOffset dx="0" dy="0.5" result="offsetblur"/>
      <feFlood flood-color="#000000" flood-opacity="0.12"/>
      <feComposite in2="offsetblur" operator="in"/>
      <feMerge>
        <feMergeNode/>
        <feMergeNode in="SourceGraphic"/>
      </feMerge>
    </filter>
  </defs>
  <rect width="120" height="35" fill="url(#bg)"/>
  <g transform="translate(2, 2)" id="button">
    <rect x="0" y="0" width="51" height="25" ry="2" fill="url(#button-bg)" filter="url(#drop-shadow)"/>
    <rect x="0.5" y="0.5" width="50" height="24" ry="1.5" fill="none" stroke="url(#button-border)" stroke-width="1"/>
    <text x="25" y="17" text-anchor="middle" font-family="sans-serif" font-size="9.75pt" fill="url(#button-fg)">Button</text>
  </g>
  <g transform="translate(60, 6)" id="check">
    <rect x="0.5" y="0.5" width="15" height="15" ry="1.5" fill="none" stroke="url(#checkradio-border)"/>
    <rect x="1" y="1" width="14" height="14" ry="1" fill="url(#checkradio-bg)"/>
    <path d="M11.681 3.928a1.467 1.467 0 0 0-.987.427L6.653 8.2 5.27 6.65c-.494-.659-1.574-.729-2.146-.142-.572.588-.494 1.692.156 2.186l2.39 2.569c.94.94 1.362.814 2.31-.138 0 0 3.258-3.92 5.169-5.243.423-.4.599-.513.376-1.054-.22-.544-1.27-.92-1.845-.9z" fill="url(#checkradio-fg)"/>
  </g>
  <g transform="translate(86, 6)" id="radio">
    <circle cx="8" cy="8" r="7.5" fill="none" stroke="url(#checkradio-border)"/>
    <circle cx="8" cy="8" r="7" fill="url(#checkradio-bg)"/>
    <circle cx="8" cy="8" r="3" fill="url(#checkradio-fg)"/>
  </g>
</svg>

Further update. Simplified the code based on your reply.
(If you don't need a slight drop shadow for the button, please remove the <filter>...</filter> and filter="url(#drop-shadow)".)

<svg xmlns="http://www.w3.org/2000/svg" width="120" height="35" viewBox="0 0 120 35">
  <defs>
    <linearGradient id="button-bg" x2="0" y2="1">
      <stop stop-color="#%BTN_BG%"/>
      <stop stop-color="#%BTN_BG%" offset="1"/>
    </linearGradient>
    <filter id="drop-shadow" color-interpolation-filters="sRGB" x="-0.5" y="-0.5" width="2" height="2">
      <feGaussianBlur in="SourceAlpha" stdDeviation="1"/>
      <feOffset dx="0" dy="0.5" result="offsetblur"/>
      <feFlood flood-color="#000000" flood-opacity="0.12"/>
      <feComposite in2="offsetblur" operator="in"/>
      <feMerge>
        <feMergeNode/>
        <feMergeNode in="SourceGraphic"/>
      </feMerge>
    </filter>
  </defs>
  <rect width="120" height="35" fill="#%BG%"/>
  <g transform="translate(2, 2)" id="button">
    <rect x="0" y="0" width="51" height="25" ry="2" fill="url(#button-bg)" filter="url(#drop-shadow)"/>
    <rect x="0.5" y="0.5" width="50" height="24" ry="1.5" fill="none" stroke="#%BTN_FG%" stroke-opacity="0.3" stroke-width="1"/>
    <text x="25" y="17" text-anchor="middle" font-family="sans-serif" font-size="9.75pt" fill="#%BTN_FG%">Button</text>
  </g>
  <g transform="translate(60, 6)" id="check">
    <rect x="0.5" y="0.5" width="15" height="15" ry="1.5" fill="none" stroke="#%FG%" stroke-opacity="0.3"/>
    <rect x="1" y="1" width="14" height="14" ry="1" fill="#%TXT_BG%"/>
    <path d="M11.681 3.928a1.467 1.467 0 0 0-.987.427L6.653 8.2 5.27 6.65c-.494-.659-1.574-.729-2.146-.142-.572.588-.494 1.692.156 2.186l2.39 2.569c.94.94 1.362.814 2.31-.138 0 0 3.258-3.92 5.169-5.243.423-.4.599-.513.376-1.054-.22-.544-1.27-.92-1.845-.9z" fill="#%SEL_BG%"/>
  </g>
  <g transform="translate(86, 6)" id="radio">
    <circle cx="8" cy="8" r="7.5" fill="none" stroke="#%FG%" stroke-opacity="0.3"/>
    <circle cx="8" cy="8" r="7" fill="#%TXT_BG%"/>
    <circle cx="8" cy="8" r="3" fill="#%SEL_BG%"/>
  </g>
</svg>

I hope I made no mistakes...

Also I made the SVG version of metacity-1/thumbnail.png.
zip is here.

Feel free to use and edit it. :)

so i've came back home and tried to integrate your assets, looks very nice:

thumbnail

thumbnail

btw funny and totally unrelated thing:

$ ll thumbnail.*
.rw-r--r-- 437 lie 17 Jan 21:43 thumbnail.png
.rw-r--r-- 437 lie 17 Jan 21:43 thumbnail.svg

$ file thumbnail.*
thumbnail.png: PNG image data, 100 x 32, 8-bit/color RGBA, non-interlaced
thumbnail.svg: SVG Scalable Vector Graphics image

@nana-4 i've tried using rsvg-convert instead of inkscape and result seems to be fine

the reason was speed, i was thinking if that's possible mb to replace in materia inkscape to rsvg-convert too? from its --help i see it can extract image by id-s as well

btw do you also want to get screenshot-based tests for materia? it helps to see if some change could break some other unexpected widget:

for example one of the previous test results:
https://travis-ci.org/actionless/oomox-gtk-theme/builds/328109403

in the end of the log you could see:

clearlooks gtk2-awf:
https://transfer.sh/gw8MZ/13-15-08_theme-clearlooks-gtk2-awf.test.png
https://transfer.sh/aFZOB/13-15-08_theme-clearlooks-gtk2-awf.orig.png
https://transfer.sh/dwQVc/13-15-08_theme-clearlooks-gtk2-awf.diff.png

I'm glad that it worked well. 😄

i've tried using rsvg-convert instead of inkscape and result seems to be fine
the reason was speed,

Wow, rsvg-convert has much faster and better font rendering! It looks really nice.
(BTW, not a big matter but it requires additional package for some distros. e.g. librsvg2-tools for Fedora, librsvg2-bin for Ubuntu, etc.)

i was thinking if that's possible mb to replace in materia inkscape to rsvg-convert too? from its --help i see it can extract image by id-s as well

That's very interesting, but it seems rsvg-convert doesn't zoom IDs correctly. :(
This will generate PNG with nothing drawn:

cd materia-theme/src/gtk
rsvg-convert assets.svg -z 2 -i box-shadow -o box-shadow@2.png

I tried other options such as -d, -p, -x, -y, -w and -h, but they also didn't work for IDs.
I guess this is a bug of rsvg-convert and even if I rewrite the svg, I don't think it will work.
Any ideas?

btw do you also want to get screenshot-based tests for materia? it helps to see if some change could break some other unexpected widget:

Probably I think I don't need the test tool. Because I usually check the changes with gtk3-widget-factory etc before per commit. But thanks for your kindness. :)

Okay, I figured out a workaround for zooming IDs.
Instead of using -z option, expand the source SVG itself:

SRC_FILE="assets.svg"
SRC_FILE_2="assets@2.svg"

cp $SRC_FILE $SRC_FILE_2 && \
sed -i -e 's/width="400"/width="800"/1' -e 's/height="400"/height="800"/1' $SRC_FILE_2

rsvg-convert $SRC_FILE_2 -i box-shadow -o box-shadow@2.png

rm $SRC_FILE_2

This will render the zoomed IDs correctly. What do you think?

I usually check the changes with gtk3-widget-factory etc before per commit.

my point was what human could not notice some really small but unwanted change (let's say some single widget lost 1px of its left padding) while using compare tool for making image diffs will highlight all such cases. that's became even more important when testing theme generation with different colors, because some change which looks good in light colors could look bad in dark colorscheme and the opposite

This will render the zoomed IDs correctly. What do you think?

if it's working correctly but faster -- why not to use it? :)

Thanks. So, when I solve other minor bug, I'll use rsvg-convert for Materia. :)
(However, I'm going to remove GNU parallel because rsvg-convert with parallel causes errors irregularly.)

About the tests,

So, how can I get it? If handling is easier I would like to use it. :)

how can i reproduce the issue with parallel?

re: tests:

it's quite simple flow:

  1. first docker builds an image with all dependencies which i need for generating the theme:
    https://github.com/actionless/oomox-gtk-theme/blob/master/.travis.yml#L13
    https://github.com/actionless/oomox-gtk-theme/blob/master/Dockerfile

  2. next i am running my test script inside that docker container:
    https://github.com/actionless/oomox-gtk-theme/blob/master/.travis.yml#L16
    https://github.com/actionless/oomox-gtk-theme/blob/master/test/all_tests.sh

2.1) here i am generating/building the themes:
https://github.com/actionless/oomox-gtk-theme/blob/master/test/all_tests.sh#L59-L73

2.2) next running the tests:
https://github.com/actionless/oomox-gtk-theme/blob/master/test/all_tests.sh#L82-L92
https://github.com/actionless/oomox-gtk-theme/blob/master/test/test.sh

2.2.a) choosing generated GTK theme in settings:
https://github.com/actionless/oomox-gtk-theme/blob/master/test/test.sh#L36-L50

2.2.b) starting Xvfb and inside gtk widget factory, making screenshot, compare output:
https://github.com/actionless/oomox-gtk-theme/blob/master/test/test.sh#L167-L173

MISC:
how to generate reference screenshots:
https://github.com/actionless/oomox-gtk-theme#hacking

also for local development of screenshot-based tests it's usable to have git diff extension like that:
http://www.akikoskinen.info/image-diffs-with-git/
i had some display artifacts with display app ( :-) ) used in the example, so using different image viewer: https://github.com/actionless/dotfiles/blob/master/misc/bin/git-imgdiff

you can reuse most of the testcode, just update the dependencies, theme name in docker paths and test results screenshots

also a good improvement could be to use ubuntu or debian as a base in tests' docker container instead of arch:

because arch is rolling, on almost every freetype (or other font-rendering-related) package update i have to regenerate screenshots because some characters are rendering differently now

how can i reproduce the issue with parallel?

Please copy and paste the following to materia-theme/src/gtk/render-asset.sh

#! /bin/bash
set -ueo pipefail

RSVG_CONVERT="/usr/bin/rsvg-convert"
OPTIPNG="/usr/bin/optipng"

SRC_FILE="assets.svg"
SRC_FILE_2="assets@2.svg"
ASSETS_DIR="assets"

i=${1}

cp $SRC_FILE $SRC_FILE_2 && sed -i -e 's/width="400"/width="800"/1' -e 's/height="400"/height="800"/1' $SRC_FILE_2

if [ -f $ASSETS_DIR/$i.png ]; then
    echo $ASSETS_DIR/$i.png exists.
else
    echo Rendering $ASSETS_DIR/$i.png
    $RSVG_CONVERT --export-id=$i \
                  --output=$ASSETS_DIR/$i.png $SRC_FILE >/dev/null \
    && $OPTIPNG -o7 --quiet $ASSETS_DIR/$i.png
fi
if [ -f $ASSETS_DIR/$i@2.png ]; then
    echo $ASSETS_DIR/$i@2.png exists.
else
    echo Rendering $ASSETS_DIR/$i@2.png
    $RSVG_CONVERT --export-id=$i \
                  --output=$ASSETS_DIR/$i@2.png $SRC_FILE_2 >/dev/null \
    && $OPTIPNG -o7 --quiet $ASSETS_DIR/$i@2.png
fi

exit 0

then run: rm assets/*.png && ./render-assets.sh

At least in my environment (rsvg-convert version 2.40.20), like the following logs are irregularly outputed and 0 byte PNGs are generated. It seems all errors are only in *@2.png.

Rendering assets/radio-checked-dark@2.png
Error reading SVG:Input file is too short
Rendering assets/checkbox-unchecked-hover@2.png
Error reading SVG:Error domain 1 code 77 on line 285 column 7 of data: Premature end of data in tag svg line 1

re:re:tests:

Thanks a lot! I'll check them in detail later. :)

right, because 4 (or whatever is yours nproc) different scripts at the same time trying to create assets@2.svg

to avoid that it could be two possible solutions:

  1. "convert" assets.svg to assets@2.svg only once, before running render-asset.sh (ie in render-assets.sh)
  2. replace SRC_FILE_2="assets@2.svg" to SRC_FILE_2="$(mktemp XXXXXXX.svg)"
    and clean-up that file in the end of render-asset.sh

i prefer the first way but it could make things harder for testing mb

also if you make it to work it will be interesting to measure the time difference

Errors are gone! Thank you for your insight. :)
I will apply the first way.

i've measured average generation time for gnome-colors icons:

inkscape no parallel:
85.93

inkscape nproc (4):
34.48

rsvg no parallel:
17.35

rsvg nproc (4):
8.62

Thanks for this!
However, after testing, it turned out that migration is difficult with Materia due to some rendering bugs. :(
I'm going to open an issue to materia-theme repo and point out the rendering issues later.

btw could you show some rendering errors examples?

also, are they happening for both normal and @2 assets?

@actionless I'm sorry for the late response.

Now I've opened a new issue and shown examples of bugs: nana-4/materia-theme#202

They are happening for both normal and @2 assets, but they are not @2 specific problems.