Images being upscaled in Flutter - flutter

I have images stored as blobs in SQLite. Other tools like DB Browser for SQLite show the images themselves are not upscaled.
I scaled them down from an original image with the following code.
final thumbnailData = encodeJpg(copyResize(
decodeImage(imageData),
width: 400,
interpolation: Interpolation.average
));
When displayed in Flutter they are noticably upscaled.
#override
Widget build(BuildContext context) {
return Image.memory(_getThumbnailData());
}
Image.memory() has a scale argument that defaults to 1.0. Setting it manually to be sure doesn't help either.
I have to set it to some guesstimated value like 2.0 to get the correct scale but I don't understand why and wether 2.0 is actually "unscaled" or still slightly off.
How can I tell Flutter to display the images as they are?

Flutter uses logical pixel instead of physical pixels.
Device pixels are also referred to as physical pixels. Logical pixels are also referred to as device-independent or resolution-independent pixels.
How to convert between physical pixels and logical pixels?
To convert between physical pixels and logical pixels, you can use devicePixelRatio.
The number of device pixels for each logical pixel. This number might not be a power of two. Indeed, it might not even be an integer. For example, the Nexus 6 has a device pixel ratio of 3.5.
MediaQuery.of(context).devicePixelRatio

Related

What is the use of logical pixel in flutter?

I am not interested in inside workings of logical pixels, I just want to know if flutter automatically use logical pixel
Container(
width:100,
child:...
)
Does flutter uses 100 pixel or logical pixel as width here, I can't figure it out.
You can print screen width
double kScreenWidth(BuildContext ctx) => MediaQuery.of(ctx).size.width;
you can see what it is like
Container(width: 100, ...)
logical pixels
So obvious,
What you see is what you got.
Flutter follows a simple density-based format like iOS. Assets might be 1.0x, 2.0x, 3.0x, or any other multiplier.
Flutter doesn’t have dps but there are logical pixels, which are basically the same as device-independent pixels. The so-called devicePixelRatio expresses the ratio of physical pixels in a single logical pixel.
from flutter dev doc

Flutter: How to scale text based on screen size

I have an app I'm building and I recently made the screens I have so far scale automatically based on screen size so that they can fit multiple devices and so that I can test them on my device (iPhone 7 Plus). I did that and it worked fine but then I wanted to test it on a smaller screen and see how it goes, on the iPhone 6s emulator the app had render flex overflows, after changing any sized boxes i had to set their size based on their (original size / size of container on iPhone 12 Pro Max) * current container size and it barely worked (i was building the app on the iPhone 12 Pro Max emulator). Any smaller screen sizes won't work and that is because the elements them selves don't scale because the text size is too big (e.g. text form fields and buttons don't scale down because text size remains the same).
I saw this (Flutter: How can I resize text based on device's screen size) and was going to try it as a potential solution as that is how I rescaled the containers on the Welcome/Authentication screens but wanted to see if there was an eaiser or built in solution that Flutter has that would be easier to implement.
iPhone 12 Pro Max:
Welcome/Authentication Screens: https://cln.sh/asdQyc
Settings Screen: https://cln.sh/yQSM5o
iPhone 6s:
Welcome/Authentication Screens: https://cln.sh/mZZZ2F
Settings Screen: https://cln.sh/oUss9m
Thanks for the help!
Edit: Also using the solution in Flutter: How can I resize text based on device's screen size makes the text too small but I can't make it bigger because then it would be too big on bigger screens.
You can use an if-else condition too. It worked for me when I was very specific about size of text. I used both height and width to calculate my text size, like -
if(height<A && width<B) {
size = MediaQuery.of(context).size.width*MediaQuery.of(context).size.height*(some ratio)
}
if(height>A && width<B) {
size = MediaQuery.of(context).size.width*MediaQuery.of(context).size.height*(some ratio)
}
if(height<A && width>B) {
size = MediaQuery.of(context).size.width*MediaQuery.of(context).size.height*(some ratio)
}
if(height>A && width>B) {
size = MediaQuery.of(context).size.width*MediaQuery.of(context).size.height*(some ratio)
}
I was using android, so it was a bit longer, but i-phones have very less range of varieties in sizes, so 4-5 statements would be more-than enough.
Or, if you wish to go even further, make a linear or logarithmic function by using appropriate values of size, height and width

What's the difference between AssetImage and ExactAssetImage?

I am unable to understand the difference between AssetImage and ExactAssetImage. I can use
Image(
image: AssetImage(chocolateImage),
)
Or
Image(
image: ExactAssetImage(chocolateImage),
)
with no performance or memory difference. The only advantage ExactAssetImage has is the scale property, but is that all there to it? If yes, then what's the need of AssetImage?
This is related to the images with different pixel densities you provide in your assets list.
I mean when you have provided images like this:
icons/heart.png
icons/1.5x/heart.png
icons/2.0x/heart.png
If you use AssetImage, flutter will choose between your 3 images depending on the device density pixels.
On a device with a 1.0 device pixel ratio, the image chosen would be
heart.png; on a device with a 1.3 device pixel ratio, the image chosen
would be 1.5x/heart.png.
If you use ExactAssetImage, you select the scale manually, doing somthing like:
Image(image: ExactAssetImage("your-asset",scale: 2)),
AssetImage
AssetImage fetches the image from the AssetBundle then uses the context to determine the exact image to use. Then based on the device pixel ratio and size determines the best configuration for the image this then get passed to resolve
ExactAssetImage
ExactAssetImage fetches images in a similar way while also associating a scale with the image. ExactAssetImage ignores the devices pixels ratio and size in the configuration passed to resolve.
In Conclusion
AssetImage is resolution-aware and can pick the right image based on the right device pixel ratio and size while ExactAssetImage is not. So to more directly answer your question ExactAssetImage gives you more control of memory usage as it will use the exact resolution of the image.

Is there a way to define a view's frame in terms of inches instead of points?

Given any screen resolution, is there a way that I can figure out the amount of points in an inch? For instance, if I wanted to create an NSView that was 8.5 inches by 11 inches (like a sheet of a paper), is there an algorithm that will allow me to obtain the correct point values for the frame across many different types of Macs and screen resolutions?
It's not straightforward. I'm not sure there's a good way. I can provide an approach, but I haven't confirmed that this works reliably:
First, you can use CGDisplayScreenSize() to get the screen's physical size in millimeters. You can obtain the CGDirectDisplayID for a screen from NSScreen, which you can, in turn, get from the window. Obtain the screen's deviceDescription and get the value for the "NSScreenNumber" key. That may need to be cast to CGDirectDisplayID.
The problem from there is that the display mode may not fill the screen. It could be letterboxed or pillarboxed. Or, it might be stretched. This should be fairly uncommon these days, but still possible. You can obtain the display mode using CGDisplayCopyDisplayMode(). To determine if it's stretched, you can examine its ioFlags to see if they contain the bitmask kDisplayModeStretchedFlag (declared in IOKit).
If it's stretched, the screen's frame will have to be mapped to its size in millimeters separately for the X and Y axes. You assume the screen's frame.width (in points) maps to the full physical width, and similarly for the height.
If the mode is not stretched, you'll have to check the aspect ratio of the frame and the screen physical size to see if it's letter- or pillarboxed. If the aspect ratios are very close, then it's presumably not. That case is similar to the stretched case, but the width and height mappings should be equivalent.
If the aspect ratios differ significantly, then you compare them. If the screen's physical aspect ratio is larger than the frame's, then the screen is physically wider than the mode is using (pillarboxed). So, you compute the mapping from points to millimeters from the two heights. If the physical aspect ratio is smaller than the logical one, then the mode is letterboxed and you use the widths to compute the mapping.

Get physical size of screen in flutter

I am building a flutter application that needs precise measurements of the screen in cm / inches.
According to the docs,
By definition, there are roughly 38 logical pixels per centimeter, or about 96 logical pixels per inch, of the physical display. The value returned by devicePixelRatio is ultimately obtained either from the hardware itself, the device drivers, or a hard-coded value stored in the operating system or firmware, and may be inaccurate, sometimes by a significant margin.
I have tried using these ratios in my application but they are not even close.
Is there a way to accurately calculate the dimensions of the screen?
Flutter's pixel coordinates are given in logical pixels rather than physical pixels. However, MediaQuery will give you the conversion ratio.
var mediaQuery = MediaQuery.of(context);
var physicalPixelWidth = mediaQuery.size.width * mediaQuery.devicePixelRatio;
var physicalPixelHeight = mediaQuery.size.height * mediaQuery.devicePixelRatio;
(Note that this code can only be run in a place where a BuildContext object is available, such as a build method or a StatefulWidget's companion State class.)