It is currently March 24th, 2019, 10:46 pm

Scaling and placing an image

Help with creating, editing & fixing problems with skins
User avatar
qwerky
Posts: 156
Joined: April 10th, 2014, 12:31 am
Location: Canada

Scaling and placing an image

qwerky » February 17th, 2019, 1:15 am

Given a colored rectangle of, for example, 237x128 pixels (the width will change with relation to the screen size), and given a group of images with various aspect ratios (such as 900x700, 1024x1024, etc.), I wish to place a selected image within the rectangle, at the top right corner of the rectangle (I actually wish to leave a one pixel border around the image), but with a maximum width of 112 pixels. So the maximum size of the image would be 112x126. Like using PreserveAspectRatio=1 on the image meter, but with the resulting image at the top, rather than in the center. But how to do this is eluding me.
You do not have the required permissions to view the files attached to this post.
User avatar
jsmorley
Developer
Posts: 19076
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Re: Scaling and placing an image

jsmorley » February 17th, 2019, 1:43 am

This assumes a couple of things:

1) The image will always be wider than it is tall. (or is square) This is because of how PreserveAspectRatio works, as illustrated later.
2) The image will always be at least 112 pixels wide. There is no such thing as "maximum width". Width is either precisely defined, or it is derived when H and PreserveAspectRatio are used, or it is just the actual size of the image.

if those two assumptions are always going to be true for you, then this will work fine...

Code: Select all

[Rainmeter]
Update=1000
DynamicWindowSize=1
AccurateText=1

[MeterRect]
Meter=Image
W=237
H=128
SolidColor=47,47,47,255

[MeterImage]
Meter=Image
ImagePath=#@#Images\
ImageName=laughing.jpg
X=((237 - 112) -1)
Y=1
W=112
PreserveAspectRatio=1

1.jpg


However....

You really can't have an image that may be either wide or tall, and use PreserveAspectRatio while specifically placing the image. If you specify BOTH W and H on the meter, the PreserveAspectRatio will determine which is greater, width or height, and will scale the image to fit in a meter that is sized to fit those dimensions. That will mean that if the ratio is not exactly what you define, it will be like a "letterbox" movie, in that there will be empty space on either the top and bottom, or the left and right. A "letterbox" is always centered.

Another way of looking at that is that if you specify both W and H, it will strictly obey you. It will create an image meter that exact size. Then it will scale and center the actual image into that space.

Code: Select all

[Rainmeter]
Update=1000
DynamicWindowSize=1
AccurateText=1

[MeterRect]
Meter=Image
W=237
H=128
SolidColor=47,47,47,255

[MeterImage]
Meter=Image
ImagePath=#@#Images\
ImageName=laughing.jpg
X=((237 - 112)-1)
Y=1
W=112
H=126
PreserveAspectRatio=1
SolidColor=255,0,0,90

2.jpg
You do not have the required permissions to view the files attached to this post.
User avatar
jsmorley
Developer
Posts: 19076
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Re: Scaling and placing an image

jsmorley » February 17th, 2019, 2:28 am

The only way to be able to scale and align the image at the top, (or right) without correctly guessing that the image is wider or taller, is to know the size of the actual image up front, and for that you need a plugin.

https://forum.rainmeter.net/viewtopic.php?f=127&t=18822&p=113408#p101884

If, and once, you know the actual size of the image, you can actually address both of the issues I mentioned earlier. Just need an IfCondition, and use Min(), to set the attributes of the Image meter. If the actual image is "wide" then you set the W option on the Image meter, "up to" some maximum value, and let PreserveAspectRatio do its job. If the actual image is "tall" then you set the H option instead. You almost never want to set both W and H when using PreserveAspectRatio.

Like this:

Code: Select all

[Rainmeter]
Update=1000
DynamicWindowSize=1
AccurateText=1

[Variables]
ImageName=#@#Images\lampoon.jpg
MaxWidth=112
MaxHeight=126

[MeasureWidth]
Measure=Plugin
Plugin=ImageSize
ImageName=#ImageName#
Dimension=Width
UpdateDivider=-1

[MeasureHeight]
Measure=Plugin
Plugin=ImageSize
ImageName=#ImageName#
Dimension=Height
UpdateDivider=-1
IfCondition=MeasureWidth >= MeasureHeight
IfTrueAction=[!SetOption MeterImage H ""][!SetOption MeterImage W "(Min([*MeasureWidth*], #MaxWidth#))"][!Update]
IfFalseAction=[!SetOption MeterImage W ""][!SetOption MeterImage H "(Min([*MeasureHeight*],#MaxHeight#))"][!Update]

[MeterRect]
Meter=Image
W=237
H=128
SolidColor=47,47,47,255
UpdateDivider=-1

[MeterImage]
Meter=Image
ImageName=#ImageName#
X=(237 - ([MeterImage:W])-1)
Y=1
PreserveAspectRatio=1
DynamicVariables=1

1.jpg

3.jpg



Note that the [!Update] at the end of the actions is to eliminate a one update lag from when the W or H is set on the Image meter and when the X option uses that to calculate itself.

Note also that I *escaped* the measure values I used to set W or H, so if the IfCondition remains "true" or "false", but the size of the image changes, the current value is still used by the meter, without needing to set a rather expensive IfConditionMode=1 on the measure. Won't matter with the static image in this example, but would if the image is dynamically changing in your skin.
You do not have the required permissions to view the files attached to this post.
User avatar
qwerky
Posts: 156
Joined: April 10th, 2014, 12:31 am
Location: Canada

Re: Scaling and placing an image

qwerky » February 17th, 2019, 7:59 pm

Oh, that is a terrific explanation! :great: Thanks for the excellent code. :thumbup:
jsmorley wrote:
February 17th, 2019, 2:28 am
The only way to be able to scale and align the image at the top, (or right) without correctly guessing that the image is wider or taller, is to know the size of the actual image up front, and for that you need a plugin.

https://forum.rainmeter.net/viewtopic.php?f=127&t=18822&p=113408#p101884
I suspected this to be the case, and had already downloaded the ImageSize plugin. But even believing I would need the plugin, I was still wrestling with how to code it; and then you wrote:
If, and once, you know the actual size of the image, you can actually address both of the issues I mentioned earlier. Just need an IfCondition, and use Min(), to set the attributes of the Image meter. If the actual image is "wide" then you set the W option on the Image meter, "up to" some maximum value, and let PreserveAspectRatio do its job. If the actual image is "tall" then you set the H option instead. You almost never want to set both W and H when using PreserveAspectRatio.
And it was so simply solved! Also, I really like this X=(237 - ([MeterImage:W])-1) to save having to set the X coordinate in each IfCondition action statement: I didn't know that you could reference a meter's options within itself.
Note that the [!Update] at the end of the actions is to eliminate a one update lag from when the W or H is set on the Image meter and when the X option uses that to calculate itself.

Note also that I *escaped* the measure values I used to set W or H, so if the IfCondition remains "true" or "false", but the size of the image changes, the current value is still used by the meter, without needing to set a rather expensive IfConditionMode=1 on the measure. Won't matter with the static image in this example, but would if the image is dynamically changing in your skin.
And this is also good information; so I've learned quite a bit from your post.

But even before that, you wrote:
jsmorley wrote:
February 17th, 2019, 1:43 am
This assumes a couple of things:

1) The image will always be wider than it is tall. (or is square) This is because of how PreserveAspectRatio works, as illustrated later.
2) The image will always be at least 112 pixels wide. There is no such thing as "maximum width". Width is either precisely defined, or it is derived when H and PreserveAspectRatio are used, or it is just the actual size of the image.
And your assumptions were correct for my particular use case here, where I am using satellite images from here, and I believe they are always either square, or wider than high. And yes, I was wrestling with that letter-box effect, and your solution was so easy! :D
if those two assumptions are always going to be true for you, then this will work fine...
So in my case, your simpler code will work well. And, now I also know what's needed in the more complex case, should I need it. Thanks again!
User avatar
qwerky
Posts: 156
Joined: April 10th, 2014, 12:31 am
Location: Canada

Re: Scaling and placing an image

qwerky » February 17th, 2019, 9:53 pm

Thinking further about this, I will need to go with the more complicated method after all, since as I said originally, the rectangle width increases with screen resolution. Those numbers I mentioned, were for a 1680x1050 screen, but when the screen increases to 1920x1080 or larger, then the width of the thumbnail becomes larger than the allotted height, so for a square image, the height exceeds the allotted space, and therefore the need for the alternate method.

So, the ImageSize plugin is installed. But the next problem, is that the satellite images are downloaded:

Code: Select all

[msrSatImage1]
Measure=Plugin
Plugin=WebParser
URL=#SatURL1#
UpdateRate=#SiteUpdateRate#
Download=1
How does one tell ImageSize to use a downloaded image?

Code: Select all

[msrSatImageWidth]
Measure=Plugin
Plugin=ImageSize
ImageName=[msrSatImage1]
Dimension=Width
UpdateDivider=-1
This code produces a log error: "ImageSize.dll: File error for [msrSatImage1]". I tried using an ampersand, tried using asterisks, tried both, tried using MeasureName instead of ImageName, but haven't got it to work.
User avatar
jsmorley
Developer
Posts: 19076
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Re: Scaling and placing an image

jsmorley » February 17th, 2019, 9:56 pm

qwerky wrote:
February 17th, 2019, 9:53 pm
Thinking further about this, I will need to go with the more complicated method after all, since as I said originally, the rectangle width increases with screen resolution. Those numbers I mentioned, were for a 1680x1050 screen, but when the screen increases to 1920x1080 or larger, then the width of the thumbnail becomes larger than the allotted height, so for a square image, the height exceeds the allotted space, and therefore the need for the alternate method.

So, the ImageSize plugin is installed. But the next problem, is that the satellite images are downloaded:

Code: Select all

[msrSatImage1]
Measure=Plugin
Plugin=WebParser
URL=#SatURL1#
UpdateRate=#SiteUpdateRate#
Download=1
How does one tell ImageSize to use a downloaded image?

Code: Select all

[msrSatImageWidth]
Measure=Plugin
Plugin=ImageSize
ImageName=[msrSatImage1]
Dimension=Width
UpdateDivider=-1
This code produces a log error: "ImageSize.dll: File error for [msrSatImage1]". I tried using an ampersand, tried using asterisks, tried both, tried using MeasureName instead of ImageName, but haven't got it to work.
DynamicVariables=1

Be sure to have that ImageSize measure disabled, and only enable it and update it, using FinishAction on the WebParser measure. Otherwise, it will be executed while the WebParser measure is still out on its journey to get the file, and you will initially get the same "file error", as the value of the WebParser measure will be "" an empty string. With UpdateDivider=-1 on there, it will never recover from that.

Mind you, the UpdateDivider=-1 is fine and appropriate, as you don't want it getting the dimensions of the same image over and over again on every skin update, but you then need to "control" when it is updated with the WebParser measure. It should be, and only be, updated the same time the WebParser measure is.
User avatar
jsmorley
Developer
Posts: 19076
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Re: Scaling and placing an image

jsmorley » February 17th, 2019, 10:10 pm

So like this:

Code: Select all

[Rainmeter]
Update=1000
DynamicWindowSize=1
AccurateText=1

[Variables]
MaxWidth=112
MaxHeight=126

[MeasureImage]
Measure=WebParser
URL=https://forum.rainmeter.net/styles/elegance/theme/images/logo.png
Download=1
FinishAction=[!EnableMeasureGroup GetImage][!UpdateMeasureGroup GetImage]

[MeasureWidth]
Measure=Plugin
Plugin=ImageSize
Group=GetImage
ImageName=[MeasureImage]
Dimension=Width
DynamicVariables=1
Disabled=1
UpdateDivider=-1

[MeasureHeight]
Measure=Plugin
Plugin=ImageSize
Group=GetImage
ImageName=[MeasureImage]
Dimension=Height
DynamicVariables=1
Disabled=1
UpdateDivider=-1
IfCondition=MeasureWidth >= MeasureHeight
IfTrueAction=[!SetOption MeterImage H ""][!SetOption MeterImage W "(Min([*MeasureWidth*], #MaxWidth#))"][!Update][!ShowMeterGroup GetImage]
IfFalseAction=[!SetOption MeterImage W ""][!SetOption MeterImage H "(Min([*MeasureHeight*],#MaxHeight#))"][!Update][!ShowMeterGroup GetImage]

[MeterRect]
Meter=Image
W=237
H=128
SolidColor=47,47,47,255
UpdateDivider=-1

[MeterImage]
Meter=Image
Group=GetImage
ImageName=[MeasureImage]
X=(237 - ([MeterImage:W])-1)
Y=1
PreserveAspectRatio=1
Hidden=1
DynamicVariables=1
User avatar
jsmorley
Developer
Posts: 19076
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Re: Scaling and placing an image

jsmorley » February 17th, 2019, 10:16 pm

Hmm... Seems that the ImageSize plugin is not obeying Disabled=1 on the measure, which is a fault of the plugin, and unfortunate. However although that means you will get one initial error in the log, all will be well as soon as the WebParser measure is "finished".

I'll look at changing the plugin. It is getting the size of the image on Reload(), not in Update(), so it won't initially see or obey any disabled state.

But it's a single, non-fatal error in the log when you first load or refresh the skin. It won't happen on an ongoing basis.
User avatar
jsmorley
Developer
Posts: 19076
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Re: Scaling and placing an image

jsmorley » February 17th, 2019, 10:41 pm

In the meantime, the error can be avoided like this:

Code: Select all

[Rainmeter]
Update=1000
DynamicWindowSize=1
AccurateText=1

[Variables]
MaxWidth=112
MaxHeight=126
TempImage=C:\Windows\System32\DefaultAccountTile.png

[MeasureImage]
Measure=WebParser
URL=https://forum.rainmeter.net/styles/elegance/theme/images/logo.png
Download=1
FinishAction=[!SetOptionGroup GetImage ImageName "[*MeasureImage*]"][!UpdateMeasureGroup GetImage]

[MeasureWidth]
Measure=Plugin
Plugin=ImageSize
Group=GetImage
ImageName=#TempImage#
Dimension=Width
DynamicVariables=1
UpdateDivider=-1

[MeasureHeight]
Measure=Plugin
Plugin=ImageSize
Group=GetImage
ImageName=#TempImage#
Dimension=Height
DynamicVariables=1
UpdateDivider=-1
IfCondition=MeasureWidth >= MeasureHeight
IfTrueAction=[!SetOption MeterImage H ""][!SetOption MeterImage W "(Min([*MeasureWidth*], #MaxWidth#))"][!Update][!ShowMeterGroup GetImage]
IfFalseAction=[!SetOption MeterImage W ""][!SetOption MeterImage H "(Min([*MeasureHeight*],#MaxHeight#))"][!Update][!ShowMeterGroup GetImage]

[MeterRect]
Meter=Image
W=237
H=128
SolidColor=47,47,47,255
UpdateDivider=-1

[MeterImage]
Meter=Image
Group=GetImage
ImageName=[MeasureImage]
X=(237 - ([MeterImage:W])-1)
Y=1
PreserveAspectRatio=1
Hidden=1
DynamicVariables=1
So I'm setting the image initially to something that just comes with Windows, (it can be any image) but not trying to display it, then changing it when the WebParser measure is "finished". No need, or point, to disabling / enabling the measures...
User avatar
qwerky
Posts: 156
Joined: April 10th, 2014, 12:31 am
Location: Canada

Re: Scaling and placing an image

qwerky » February 17th, 2019, 10:44 pm

Okay, thanks. I put DynamicVariables=1 on both the msrSatImageWidth and msrSatImageHeight measures, and added FinishAction=[!UpdateMeasure msrSatImageWidth][!UpdateMeasure msrSatImageHeight] to msrSatImage1. So far so good, but a couple of questions:

Is using the WebParser the correct/best way to download the satellite images?

And, as there are many satellite images which the user can rotate through, and the image will expand from thumbnail to large size when moused over, I'm wondering whether I'm using the best code for that, so I'll try to show the code here (there are six SatURLx lines; you can change that number, or pick any images you like from that page):

Code: Select all

[Variables]
thumbWidth=(#endPanelWidth#-125)    *** endPanelWidth is 237 for 1680x1050, 287 for 1920x1080, and increases for largers screens
thumbHeight=(#mainPanelHeight#-2)    *** mainPanelHeight is 128
imageWidth=900
SatURL1=https://weather.gc.ca/data/satellite/goes_nam_1070x_100.jpg
SatURL2=https://weather.gc.ca/data/satellite/ another image, and so forth
SatURLs=6

[mtrSatImage]
Meter=Image
MeasureName=msrSatImage[&msrSatRotate]
X=(#skinRight#-1-#thumbWidth#)
Y=#iconY#
W=#thumbWidth#
PreserveAspectRatio=1
DynamicVariables=1
MouseOverAction=[!SetOption mtrSatImage X "(#skinRight#-1-#imageWidth#)"][!SetOption mtrSatImage W "#imageWidth#"]
MouseLeaveAction=[!SetOption mtrSatImage X "(#skinRight#-1-#thumbWidth#)"][!SetOption mtrSatImage W "#thumbWidth#"]
LeftMouseUpAction=[!UpdateMeasure msrSatRotate]
So the mtrSatImage meter selects an image based on the msrSatRotate measure:

Code: Select all

[msrSatRotate]
Measure=Calc
UpdateDivider=-1
Formula=(msrSatRotate%#SatURLs#)+1
OnUpdateAction=[!UpdateMeter mtrSatImage]
There are six satellite image measures, numbered one through six:

Code: Select all

[msrSatImage1]
Measure=WebParser
URL=#SatURL1#
UpdateRate=#SiteUpdateRate#
Download=1
FinishAction=[!UpdateMeasure msrSatImageWidth][!UpdateMeasure msrSatImageHeight]
All of this works correctly with regard to expanding the thumbnail when moused over, and reducing it again when the mouse leaves, and in regard to rotating through the various images. But I feel that there might be a way to have only one msrSatImage measure, rather than six, and to simply update its URL; but I haven't been able to figure out how to do so.

Finally, adding the ImageSize measures:

Code: Select all

[msrSatImageWidth]
Measure=Plugin
Plugin=ImageSize
ImageName=[msrSatImage6]
Dimension=Width
UpdateDivider=-1
DynamicVariables=1

[msrSatImageHeight]
Measure=Plugin
Plugin=ImageSize
ImageName=[msrSatImage6]
Dimension=Height
UpdateDivider=-1
DynamicVariables=1
IfCondition=msrSatImageWidth >= msrSatImageHeight
IfTrueAction=[!SetOption mtrSatImage H ""][!SetOption mtrSatImage W "(Min([*msrSatImageWidth*], #thumbWidth#))"][!Update]
IfFalseAction=[!SetOption mtrSatImage W ""][!SetOption mtrSatImage H "(Min([*msrSatImageHeight*], #thumbHeight#))"][!Update]
Here I need to change the IfCondition actions, since I'm not now trying to decide between a wider or a taller image, but rather am trying to decide whether the W or H option needs to be set in order to keep the thumbnail of the image from going beyond the allotted thumbnail space.