-
Notifications
You must be signed in to change notification settings - Fork 418
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Change the way CN1 handle device resolution to use device PPI #2414
Change the way CN1 handle device resolution to use device PPI #2414
Conversation
+ add dp and sp measurment units
Thanks for the PR. It's very well written & I like a lot of the ideas within it! It does touch a few core/sensitive pieces of functionality that can break applications all over the place with minor behavior changes. So this is a very risky commit, even more so than the previous PR. There are a lot of things I like in this commit had they been separate. While I'm not a fan of device detection code in general, the Adding the SP/DP units as an option wouldn't bother anyone so it's a decent option to add as it won't break code. I have several concerns and questions about this:
The way I see this you are trying to address several separate issues with one PR, I suggest a more iterative approach that would help us manage potential regressions/issues. I also prefer not to deprecate API's even if they aren't great. This lets us seamlessly roll out improvement without making our users rework their logic. |
Yes sorry, I probably should have make 2 different PR (but as I wrote this all at once and the SP/DP part depends on the changes made on the density one it was just more convenient for me to make a unique commit to my repository). If you want to focus first into the density changes, you can omit the Style, StyleParser and LayeredLayout files (that contain changes for SP/DP) So to make some clarifications on how this PR works:
The reason I deprecate the existing way to obtain density is that CN1 code do no longer need it and I think it is misleading to the developpers. When calling a getDeviceDensity() method, any developper would assume to obtain a density value (or factor) into PPI. Here it is not the case. Furthermore, the getDeviceDensity() method returns rough pixels/mm density estimations that could be not perfectly consistent with the more precise PPI value returned by getDeviceDPI(). So keeping the two methods for returning the density would requiere to keep them in sync and might just be a burden for the future.
It does it exactly the same way the old method was handling it. The only thing I changed is that instead of returning an estimate of the density (for the getDeviceDensity() method), the getDeviceDPI() method returns the exact device density or a really close one when this information is known. So for iOS, the exact PPI density would be returned, for android, a really close one based one one the densityDpi density bucket value (I abandonned the idea of using xdpi and ydpi as too much devices have errors on these and android now offers many DENSITY_xxx buckets so the value returned by densityDpi is quite close to the reality anyway) and for other devices, it would be a rough estimation based on screen resolution.
You mean for the iPhone part? Because there is actually 3 different densities for the iPads (I added a new test to differentiate iPad retina from other iPads) and 3 for the iPhones.
For the simulator, it is exactly the same behaviour as previously. Nothing changed |
With the iPhone there are 3 densities illustrated in the image you linked @1x, @2x & @3x. You seem to treat 3x and 2x as one density. I don't see the benefit of exposing a DPI value over the "virtual" values we currently have? I suggest breaking this PR to multiple smaller independent stages. Most of the things in it shouldn't be a problem. But messing with the DPI code for multi-images is really risky. |
No I have 3 densities for iPhone and correctly treat @2x and @3x as different densities. The benefit of exposing a DPI value rather than the current "virtual" value is that it allows the developpers to access a more precise and comprehensible value of the screen density. A developper may need this if he uses its own distance unit that he want to convert into pixels for example (imagine someone may want to use in, pt, em, css px, ex... units). Accessing a most precise as possible screen resolution value can be usefull to a developper in many ways. That is pretty much why Android increased its number of virtual DENSITY_xxx values over the years (at first there was only DENSITY_LOW, DENSITY_MEDIUM and DENSITY_HIGH). |
When we started off we didn't have accurate DPI values for many devices including Android (which as I mentioned gave bad results). We also added DPI's as we moved forward just like Google. I'd rather have a consistent predictable value for DPI (which as I mentioned, we can manipulate) than have rounding at the last minute. The use cases you describe apply to |
The Density value is already derived, in any case, from the PPI value (because no platform directly express density in pixels/mm). If I understand you correctly, what you are saying is that you want to keep the getDensity() method to choose the correct image from multiImages but agree that the getDeviceDPI() method should be used for distances (mm, dp...) to pixels conversions. But what is the point of doing this? Basically you would be converting the PPI value natively returned by the Platform (or estimated) to a rouded pixels/mm density value that you will then use to choose the correct image. It is simpler to directly choose the correct image from the PPI value (and you can still manipulate/overwritte this PPI value the same way you did with the density one if you need to). Also expressing images resolutions in PPI is much natural and clear for developpers (even if multiimages expressed with old density values would still be supported under the hood). |
Android & iOS have their own logic that separates the concept of density from the concept of which image to pick. So this is exactly how both of these operating systems work. You have constants that match a heuristic for picking the right image. This logic is delicate, it doesn't mean we shouldn't touch it but it means that every change we make to it needs to proceed with HUGE caution and a good reason because it can disrupt a lot of working code. Making stuff clearer to developers is important but that's more of a javadoc/UI aspect not so much a constants change. We do have a table showing density mapping. It might be worth exposing those details in UI within the designer. In CSS multi-image density is expressed through DPI so that's already there. In games density doesn't matter, only resolution matters so resources can be loaded with a fixed density based on game logic. I think it's implemented in the Poker demo. |
…densities system (i.e. the DENSITY_xxx constants)
OK I just performed some changes that should satisfy you I think. This allow to keep full back compatibility with the old DENSITY_xxx system (so if someone previously override getDeviceDensity() in its code or use MultiImages tagged with DENSITY_xxx values, it will work and the UI should remain unchanged) while allowing CN1 to move forward (use the PPI values as recommended density values and add support for densities in PPI in MultiImages which I think is the good direction to take) |
Thanks, I've merged it. Hopefully this won't break too much in this state. |
This was reverted due to #2429 we got multiple reports about a performance regression due to this commit. I have no idea what triggered that since it's really not obvious from the code. |
OK. Since this is happening on Android, my first guess would be that this is due to the getDeviceDPI() method into AndroidImplementation. Actually, at each method call (which can happen quite frequently as this method is used to compute distances convertions like mm to px), a new call to android metrics would be made. I don't know if this is costly (probably as this seems to be the cause of the performances issue here). So the solution would be to cache the dpi value instead of requesting it from metrics each time. So changing the code to:
might solve the problem. EDIT: I can't be 100% sure that the dpi cache implementation would solve the issue. So you should make some tests on your side before pushing it of course. |
This pull request makes changes into how CN1 handle device resolution.
It makes it use device PPI (pixels per inch) wich is kind of a standard to express screen resolution (instead of the pixels/mm values previously used by CN1 which I never saw anywhere else). It has the advantage of beeing a system of measurment familiar to developpers and to allow most precise mesures/conversions than a pixels/mm int value.
Note that I kept the source of the old getDeviceDensity() function into the CodenameOneImplementations, but I tagged it as deprecated and reduced its visibility to protected. I did that because I know you are kind of reluctant to suppress functions from the CN1 code. But, with this pull request, this getDeviceDensity() function is no longer used anywhere now so I would be in favour to completely remove it.
I also kept the DENSITY_xxx constants (into CN1Constants) for now. They are no longer used by th CN1 source code (exept into the getDeviceDensity() methods but that could be deleted) but are used by the CodenameOneDesigner (for MultipleImages). If this pull request is accepted (which I hope ;) ), it would be good to also replace usage of DENSITY_xxx into the CodenameOneDesigner with the new DPI_xxx constants (that would make more sense anyway to have each image into a MultipleImage associated to its PPI resolution rather than to its pixels/mm resolution). Note that MultipleImage indexed with the old system of pixels/mm resolution would still work with the new code though (pixels/mm are converted to PPI when we read the ressource)
Also I used the DPI acronym to express PPI, to keep consistency with the rest of the CN1 code (where you use DPI to express PPI, even if this is an abuse of language (https://en.99designs.fr/blog/tips/ppi-vs-dpi-whats-the-difference/)). But you can refactor the names I used for variables and functions to your convenience anyway
This pull request also introduce the dp and sp mesure units (https://material.io/design/layout/understanding-layout.html#pixel-density) and makes the necessary changes into Display, Style and LayeredLayout so that they can be used.
Important remark: I tested my code into the simulator and on Android but I couldn't test it on iOS as I didn't have a mac to compille at hand... So there might be some issues in the native iOS part of my modifications (especially since I really do not master objectiveC). Sorry for that