It is currently September 29th, 2024, 1:21 pm

Filling a font

General topics related to Rainmeter.
User avatar
jsmorley
Developer
Posts: 22790
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Filling a font

Post by jsmorley »

I wonder if there isn't a cleaner way to come at this...

Code: Select all

[Rainmeter]
Update=1000
DynamicWindowSize=1
BackgroundMode=2
SolidColor=0,0,0,255
AccurateText=1

[Variables]
TopFontPadding=34
BottomFontPadding=21

; Use this for testing...
; [MeasureCPU]
; Measure=Calc
; Formula=(MeasureCPU % 100) + 1
; MinValue=0
; MaxValue=100

[MeasureCPU]
Measure=CPU

[MeterCPUBack]
Meter=String
FontFace=Segoe UI
FontSize=70
FontColor=0,255,0,255
StringEffect=Border
FontEffectColor=47,47,47,255
Padding=0,-#TopFontPadding#,0,-#BottomFontPadding#
AntiAlias=1
Text=CPU

[MeterCPUFill]
Meter=String
W=([MeterCPUBack:W] + 1)
H=([MeterCPUBack:H] - ([MeasureCPU:%] / 100) * [MeterCPUBack:H] + #TopFontPadding#)
FontFace=Segoe UI
FontSize=70
FontColor=150,150,150,255
StringEffect=Border
FontEffectColor=47,47,47,255
Padding=0,-#TopFontPadding#,0,-#BottomFontPadding#
AntiAlias=1
ClipString=1
Text=CPU
DynamicVariables=1
2014-06-27_232246.jpg
The rub with this is that the idea, to use the "height" on a second string meter to truncate the text and so give a "fill" effect is simple in principle, (although you have to do some lying due to the fact that fonts are drawn from the top down, not the bottom up) but gets complicated in execution.

The issue is that all fonts have some built-in "padding" around the actual font glyphs, (probably to account for lower case letters like "y" or "g" that may go below the "baseline" and some symbols that may go above the "topline") and that amount of padding varies from font to font. You have to deal with this padding, either by sort of stripping it off, like I do with the negative Padding= options on the string meters, or by factoring it into the calculations for setting the height of the string.

What makes this complicated is that the padding around the characters in the font are not something you can really "know" or "calculate" based on FontSize, and you more or less have to get it with some trial and error. That makes it hard indeed to make this something that could easily be modified by a user for a different font or a different font size.

Not sure if I am missing something here, or if there is another way to come at this that can simplify it some.
You do not have the required permissions to view the files attached to this post.
User avatar
moshi
Posts: 1740
Joined: November 13th, 2012, 9:53 pm

Re: Filling a font

Post by moshi »

what you are looking for is called "internal leading"
http://clanlib.org/img/FontMetrics.png

it should be possible to figure out this value from an AFM file with webparser and calc measures.

in *NIX operating systems there are command line tools like ttf2afm or ttf2pt1 that generate AFM files from TTF files. Windows ports probably exist.

not sure if it's worth the needed research and effort though.



a nicer way would be a Trim=1 option for meters that strips all the fully transparent parts of a meter (similar to Photoshop function in the Image menu).
even better: Trim=1,1,0,1 to define the edges.
http://cbtcafe.com/photoshop/trim/index.html
User avatar
jsmorley
Developer
Posts: 22790
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Re: Filling a font

Post by jsmorley »

Yeah, figuring out the internal padding is probably not the best long-term solution. In any case, that might not even be the answer really. For that example I posted, you would want something different if the text was "cpu", which has a character that goes below the baseline, rather than "CPU", where it doesn't. Something like the trim command in Photoshop, that would trim the meter down to the the smallest possible rectangle that encloses all non-transparent pixels, after the text is drawn, would probably be the most practical.
User avatar
moshi
Posts: 1740
Joined: November 13th, 2012, 9:53 pm

Re: Filling a font

Post by moshi »

lowercase letters wouldn't be a good idea here though. it would be too hard to make sense of what's diplayed on the screen.

but for future Rainmeter versions:
- vertical bar meter with dynamic height and width
- trimmed text meter used as mask with "invisible" FontColor
User avatar
moshi
Posts: 1740
Joined: November 13th, 2012, 9:53 pm

Re: Filling a font

Post by moshi »

or, as it's just brainstorming anyways:
advanced dynamic gradient features instead of FontColor
User avatar
moshi
Posts: 1740
Joined: November 13th, 2012, 9:53 pm

Re: Filling a font

Post by moshi »

i like my dynamic gradients idea. so here are some examples:

there would be FontColor2, FontColor3, etc. these are obviously colors.
also there would be FontColor2Position, etc. these would be values from 0 to 1. 1 being the default value.

a gradient from red to green:

Code: Select all

FontColor=ff0000
FontColor2=00ff00
a gradient from red to green to blue:

Code: Select all

FontColor=ff0000
FontColor2=00ff00
FontColor2Position=0.5
FontColor3=0000ff
there could also be two (or more) full colors instead of gradients:

Code: Select all

FontColor=ff0000
FontColor2=ff0000
FontColor2Position=0.5
FontColor3=00ff00
FontColor3Position=0.5
and to get something like in the first post:

Code: Select all

FontColor=ff0000
FontColor2=ff0000
FontColor2Position=[Measure:%]
FontColor3=00ff00
FontColor3Position=[Measure:%]
DynamicVariables=1
User avatar
iNjUST
Posts: 117
Joined: June 20th, 2012, 12:44 am

Re: Filling a font

Post by iNjUST »

I achieved a similar effect with a suite of skins a while ago: LIM!T by injust29

This was prior to D2D rendering and Padding, and actually the skin is slightly broken/inaccurate now since those updates--I need to go back and patch this in my skin at some point. Your method is nicer in regards to trimming it to use the string effect; I really like that.

Key code from skin:

Code: Select all

[Variables]
; font name
fname=Hasteristico
; font-specific parameter
lineheight=1.04
; font-specific offset value
offset=0.53
; main string size
size=150

;---Styles---

[sText]
FontFace=#fname#
FontSize=#size#
StringEffect=SHADOW
FontEffectColor=#shadow#
StringCase=UPPER
StringAlign=LeftBottom
AntiAlias=1

[sBG]
FontColor=#bgcolor#

[sFill]
X=0r
Y=0r
StringEffect=NONE
ClipString=1
DynamicVariables=1

;---Measures---

[mTime]
Measure=Time
[mHour]
Measure=Time
Format=%I

[cHourH]
; Calculate the height of the Hour string
Measure=Calc
Formula=(FRAC(mTime/3600)+#offset#)*#lineheight#*#size#

;---Meters---

[HourBG]
MeasureName=mHour
Meter=String
MeterStyle=sText | sBG
X=(2*#size#)
Y=(1.6*#size#)
StringAlign=RightBottom
[HourFill]
MeasureName=mHour
Meter=String
MeterStyle=sText | sFill
W=(2*#size#)
H=[cHourH:]
StringAlign=RightBottom
FontColor=#color1#
I used Bottom-aligned text and then clipped the text from the top, so that all positions and sizes are measured from the artificial baseline of the text instead of the top. I used a calculation that in addition to the measure itself (e.g. Time) requires 3 variables for a given font face: Size, LineHeight, and Offset. Size is just the font size in pixels, then LineHeight and Offset are trial-and-error tweaks for approximately how tall a capital letter is relative to the font size, and how much vertical offset (or padding) there is between the base of the glyph field and the actual baseline of the text.

You pointed out the obvious nuisance of tweaking for this padding and sizing, but because font sizing is linear, for any given font face (other than artsy or scripty fonts), the skin/meters scale just fine, as demonstrated in my suite. I think that restricting a technique like this to capital letters or other full-height, above-baseline characters is the only reasonable approach. If it were easier to grab information about where exactly the baseline is located for any given fontface, that would be ideal, but I don't know if that's at all possible/worthwhile.