Getting original size of image synchronously (naturalWidth/…Height can’t be used for this anymore)


#1

TL;DR

There should be a way in JS to (synchronously) get real width and height of an already loaded image not affected by width and height attributes or by anything else.

The naturalWidth and naturalHeight properties cannot be used for that anymore since those are now affected by irreversible inconsistent implicit calculations for unknown practical purpose.

Details

The naturalWidth and naturalHeight properties were already providing exactly this ability until it has been decided by WHATWG that a sort of correction should be applied to those values by dividing in corresponding pixel ratio.

The correction is applied to images specified via the srcset attribute. Even worse, resulting values are inaccurate and therefore original values cannot even be restored as is.

For example, for the same 100×100 image:

<img    src="example.png"    alt="" />
<img srcset="example.png 3x" alt="" />

naturalWidth is 100 in the first case and 33 in the second one.

We could check if the image has the srcset attribute and multiply (or not) naturalWidth / naturalHeight by corresponding pixel ratio conditionally. But:

  • to determine the pixel ratio, we are forced to parse srcset (oh my…) (please correct me if I’m wrong);

  • srcset value may be invalid;

  • there is a precision issue (33*3 is 99, not the original 100, so information is irreversibly lost).

So the story would get too complicated given that we are trying just to obtain the exact value that browser already knows directly and precisely.

So now we are again (as before introducing naturalWidth and naturalHeight) forced to use a redundant asynchronous new Image request and then read the dummy-image’s width and height once it’s loaded, instead of just reading the real dimensions of the already loaded image synchronously and directly.

As a result, some new properties should probably be introduced like realWidth and realHeight for the purpose of getting the real original size of image not corrupted by any redundant implicit calculations. In other words, the new properties should return exactly the same values that are returned respectively by width and height properties of an image created via new Image.

Thanks.


#2

Hi,

You can see the reasoning for current natual{Width,Height} behavior in this IRC discussion. They are defined (and always were defined) as CSS pixels, and therefore should represent the image’s intrinsic dimensions in those pixels, rather than in “physical” ones.

You could probably make a solid case for an API that enables you to retrieve an image’s density, which combined with natural{Width,Height} might give you what you need.

Could you please describe your use case for knowing the “physical” image dimensions?


#3

Thanks for the link, unfortunately the IRC log doesn’t make it more clear what was the real practical purpose of making naturalWidth / naturalHeight returning an altered value. A much better API would probably be just to introduce a property like currentPixelRatio so that those who need this could on their own divide the unaltered naturalWidth / naturalHeight value in the currentPixelRatio value instead of directly returning irreversibly altered value.

For example, I need to know real image size for my SmartUpscale Firefox extension (fwiw, powered by WebExtensions API) that disables blur (by applying image-rendering: -moz-crisp-edges) for images displayed at integer zoom levels (useful primarily for HiDPI displays such as 4K monitors), e. g. when each image pixel can be represented as a group of exactly 2×2 or 3×3 absolutely identical physical pixels, so blur is unneeded and just unreasonably decreases sharpness.


#4

On systems that have non-integral DPRs (including non-integral zoom levels) that may never be true, so that is not a universal use case.


#5

Hello, Ashley. If I understand you correctly, you are talking about pages with viewport different from width=device-width on mobile devices. I’m aware of that, and that’s not a reason not to apply smart upscaling on all other pages in mobile browsers at default zoom and on absolutely all pages in desktop browsers at OS-level zooms like 200% (often used with 4K monitors) or 300%. Fwiw, dynamic zoom changes are detected by my extension as well, though for touch-powered zoom such detection is indirect and limited until Resize Observers and Visual Viewport API are available.

Also, I plan to use integer-ratio non-blurry upscaling (with scaled-image size chosen by extension dynamically, see my web demo for how such upscaling should look like) for image opened via direct URL in a separate tab once ability to override image-view page is available in WebExtensions API. This is applicable regardless of whether device is mobile.

Also, we probably don’t actually need a special usecase here at all, we already had the feature available via naturalWidth and naturalHeight properties, those have just been corrupted after srcset has been introduced (btw, I have no idea what’s the practical reason to alter the values aside from the purely theoretical “it’s defined in CSS pixels”). What were usecases for naturalWidth and naturalHeight?

Moreover, we can obtain the required values anyway by creating a new Image (and fortunately, that ability cannot be changed in the future), it just doesn’t make sense to load already loaded image and get asynchronously something actually available immediately.


#6

I’ve seen Android tablets with a DPR of something like 2.333387, plus I often browse with Chrome zoomed to 110%. I guess your use case is interesting to users with certain configurations, but I don’t see it as a general-purpose case. I think it would be more compelling to suggest general-purpose use cases, since people are understandably skeptical about introducing new web platform features for one feature for a subset of users.

Despite that, it sounds like naturalWidth and naturalHeight return the same thing that getBoundingClientRect does. Am I right about that? If so it would be an odd feature…


#7

Based on my experience, default fractional DPR is only applied to webpages without width=device-width value in the viewport meta element (that value is recommended for responsive/adaptive layouts since otherwise they work not as expected).

For example, on Samsung Galaxy Tab S 10.5 tablet (with physical resolution of 2560×1600), viewport width in Firefox is 980 without the meta (so the effective DPR is about 2.6122 instead of 2), and 1280 with the meta (so the effective DPR is exactly 2). Fwiw, formal DPR (returned by window.devicePixelRatio) is 2 in both cases.

If you often browse the web at a fractional zoom, image quality probably doesn’t matter much for you, so you’re just not in target audience of my extension, that’s perfectly ok and does not make the extension less useful for those interested in high image quality.

By the way, unlike Chrome, Firefox allows to change text size (with images in particular not zoomed) instead of performing full-page zoom, that can be enabled via the “View → Zoom → Zoom Text Only” checkbox.

Consider the proposed feature as a noncorrupted version of existing naturalWidth and naturalHeight, so no extra usecases are needed over usecases for naturalWidth and naturalHeight.

width and height properties of the object returned by getBoundingClientRect() are similar to offsetWidth and offsetHeight which include paddings and borders and cause reflow while naturalWidth and naturalHeight have nothing to do with styles and just return dimensions of the image itself regardless of its styling on the page.


#8

An official comment by e.g. @tabatkins would be appreciated. Thanks.