Images not rendered when scaling down on specific situations

61 views
Skip to first unread message

Carlos Verdier

unread,
Jul 9, 2016, 6:52:35 AM7/9/16
to CodenameOne Discussions

Hi


I'm having a problem with images I cannot figure out, hope you can help me out…


What I'm trying to do is download and place a png scaling down if necessary to fit the screen width.

My assumption: If the height of the image is larger than the height of the device, then the image is not rendered on devices but it is on simulator with no problem.


The problematic image is 1536x4272 and 1MB.


BUT the same image scaled to 768x2136 and 714KB works fine both on simulator and devices, so my assumption might not be right and I'm totally lost. Or maybe it's a problem with the image, but then why is it working on simulator?


To scale the image I'm setting the background of a Label. As I said The code below works on simulator but not on iOS or Android.


    public void start() {
        if(current != null){
            current.show();
            return;
        }
        Form f = new Form(new BoxLayout(BoxLayout.Y_AXIS));  
        f.setScrollableY(true);
        Container loading = new Container(new BorderLayout(BorderLayout.CENTER_BEHAVIOR_CENTER_ABSOLUTE)).add(BorderLayout.CENTER, new InfiniteProgress());
        Util.downloadImageToFileSystem("https://dl.dropboxusercontent.com/u/47281022/IMGP0.png", 
            FileSystemStorage.getInstance().getAppHomePath() + FileSystemStorage.getInstance().getFileSystemSeparator() + "IMGP0", 
            new CallbackAdapter(){
                @Override
                public void onSucess(Object value) {
                    Image img = (Image) value;
                    int imgHeightDevice = (Display.getInstance().getDisplayWidth() * img.getHeight()) / img.getWidth();                            
                    Label l = new Label() {
                        @Override
                        protected Dimension calcPreferredSize() {
                            return new Dimension(Display.getInstance().getDisplayWidth(), imgHeightDevice);
                        }                            
                    };                        
                    l.getAllStyles().setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FIT);
                    l.getAllStyles().setBgImage(img);
                    f.replace(loading, l, null);
                    f.repaint();
                }
            });
        f.addComponent(loading);
        f.show();                        
        
    }



Carlos Verdier

unread,
Jul 9, 2016, 7:09:39 AM7/9/16
to CodenameOne Discussions
Update

The code below works fine, but I don't want to use because is much slower

    public void start() {
        if(current != null){
            current.show();
            return;
        }
        Form f = new Form(new BoxLayout(BoxLayout.Y_AXIS));  
        f.setScrollableY(true);
        Container loading = new Container(new BorderLayout(BorderLayout.CENTER_BEHAVIOR_CENTER_ABSOLUTE)).add(BorderLayout.CENTER, new InfiniteProgress());
        Util.downloadImageToFileSystem("https://dl.dropboxusercontent.com/u/47281022/IMGP0.png", 
            FileSystemStorage.getInstance().getAppHomePath() + FileSystemStorage.getInstance().getFileSystemSeparator() + "IMGP0", 
            new CallbackAdapter(){
                @Override
                public void onSucess(Object value) {
                    Image img = (Image) value;
                    img = img.scaledWidth(Display.getInstance().getDisplayWidth());
                    Label l = new Label(img);                        

Shai Almog

unread,
Jul 10, 2016, 12:54:23 AM7/10/16
to CodenameOne Discussions
That is weird. I notice that if I leave the app and go back to it the image appears briefly and disappears after a moment.

I think the problem is in the scale behavior fit and your hierarchy. The image tries to fit into a component whose size is unclear because it is nested in a scrollable container and has a parent that is center absolute. so the fit method goes haywire in the resize calculations.

Carlos Verdier

unread,
Jul 10, 2016, 2:47:24 PM7/10/16
to CodenameOne Discussions
I'm afraid it has nothing to do with hierarchy. Notice that the center absolute is not the parent of that container, because it is replaced here:

f.replace(loading, l, null);

I have elaborated the code a bit more to see if it can clarify something. I have resized the same image, so now we have 3 of them:

- 1536x4272
- 1536x3272
- 768x2136

I also have removed the center absolute and the scrollable container, so we can reduce possibilities.

If you build and run on device, only the first one is problematic and the rest are fine. I'm not sure it is an edge case and what is bugging me is why only that size fails, and why it shows fine on simulator and not on devices... Anyway, here is the code:

public class Prueba {   
    private Form current;
    final static String[] resolutions = new String[]{"1536x4272.png", "1536x3272.png", "768x2136.png"};   
    
    public void init(Object context) {
        Toolbar.setGlobalToolbar(true);
    }
    
    public void start() {
        if(current != null){
            current.show();
            return;
        }
        
        Form f1 = new Form(new FlowLayout(Component.CENTER));
        f1.setTransitionOutAnimator(CommonTransitions.createSlide(CommonTransitions.SLIDE_HORIZONTAL, false, 300));
        Command back = new Command("Back") {
            @Override
            public void actionPerformed(ActionEvent evt) {
                f1.showBack();
            }            
        };        
        Button b1 = new Button ("Image " + resolutions[0]);        
        Button b2 = new Button ("Image " + resolutions[1]);
        Button b3 = new Button ("Image " + resolutions[2]);
        Container c1 = new Container(new FlowLayout());
        c1.add(b1).add(b2).add(b3);
        int[] selected = new int[1];
        String[] downloadLink = new String[1];        
        b1.addActionListener((evt) -> {
            selected[0] = 0;
            downloadLink[0] = "https://dl.dropboxusercontent.com/u/47281022/IMGP0_" + resolutions[0];            
            showImage(back, selected, downloadLink);       
        });
        b2.addActionListener((evt) -> {
            selected[0] = 1;            
            downloadLink[0] = "https://dl.dropboxusercontent.com/u/47281022/IMGP0_" + resolutions[1];                            
            showImage(back, selected, downloadLink);                   
        });
        b3.addActionListener((evt) -> {
            selected[0] = 2;
            downloadLink[0] = "https://dl.dropboxusercontent.com/u/47281022/IMGP0_" + resolutions[2];  
            showImage(back, selected, downloadLink);                   
        });
        f1.add(c1);
        f1.show();
    }
    private void showImage(Command back, int[] selected, String[] downloadLink) {
        Form f2 = new Form(new BoxLayout(BoxLayout.Y_AXIS));  
        f2.setTransitionOutAnimator(CommonTransitions.createSlide(CommonTransitions.SLIDE_HORIZONTAL, false, 300));                        
        f2.setBackCommand(back);
        f2.getToolbar().addCommandToLeftBar(back);   
        f2.show();                        
        String path = FileSystemStorage.getInstance().getAppHomePath() + "IMGP0_";        
        if (FileSystemStorage.getInstance().exists(path + resolutions[selected[0]])) {
            try {
                EncodedImage img = EncodedImage.create(FileSystemStorage.getInstance().openInputStream(path + resolutions[selected[0]]));
                int imgHeightDevice = (Display.getInstance().getDisplayWidth() * img.getHeight()) / img.getWidth();                        
                Label l = new Label() {
                    @Override
                    protected Dimension calcPreferredSize() {
                        return new Dimension(Display.getInstance().getDisplayWidth(), imgHeightDevice);
                    }                            
                };                        
                l.getAllStyles().setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FIT);
                l.getAllStyles().setBgImage(img);                       
                f2.add(l);    
                f2.repaint();
            } catch (IOException io) {                
            }
        } else {
            Util.downloadImageToFileSystem(downloadLink[0], 
                path + resolutions[selected[0]], 
                new CallbackAdapter(){
                    @Override
                    public void onSucess(Object value) {
                        Image img = (Image) value;
                        int imgHeightDevice = (Display.getInstance().getDisplayWidth() * img.getHeight()) / img.getWidth();                        
                        Label l = new Label() {
                            @Override
                            protected Dimension calcPreferredSize() {
                                return new Dimension(Display.getInstance().getDisplayWidth(), imgHeightDevice);
                            }                            
                        };                        
                        l.getAllStyles().setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FIT);
                        l.getAllStyles().setBgImage(img);                       
                        f2.add(l);
                        f2.repaint();
                    }
                });
        }
        
    }
    public void stop() {
        current = Display.getInstance().getCurrent();
    }    
    public void destroy() {
    }
}


Carlos Verdier

unread,
Jul 10, 2016, 4:12:29 PM7/10/16
to CodenameOne Discussions
Update

I have tried with different sizes, and I have found the following:

- 1536x4095 -- works fine
- 1536x4096 -- shows image but has problems scrolling
- 1536x4097 -- does not show

So it seems that anything beyond 4095 is problematic whatever width I use because I have tried 768 width and had the same result above.

Hope it helps

Shai Almog

unread,
Jul 10, 2016, 11:38:34 PM7/10/16
to CodenameOne Discussions
On iOS we use textures to show images and they do have a size limit, the thing that threw me off here is the fact that this happens on Android.
I'm guessing google has similar texture upper limits for image size.

Carlos Verdier

unread,
Jul 11, 2016, 4:49:19 AM7/11/16
to CodenameOne Discussions

There are some things that makes me doubt about your explanation:


- If the image is 768x4096, and therefore does not need scaling on 768x1024 device, it shows ok on that device.


- The image also shows fine if I add this to the code.


img = img.scaledWidth(Display.getInstance().getDisplayWidth());

Right before:



l.getAllStyles().setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FIT);
l
.getAllStyles().setBgImage(img);


So it seems that there is something wrong in the process of scaling when using the background image scaled.


Can it be fixed?


If not, I guess that I'll have to scale the images on the server side. I have tried Cloudinary and works fine, but I'm not sure the free cuota is enough for me...

Shai Almog

unread,
Jul 12, 2016, 12:11:00 AM7/12/16
to CodenameOne Discussions
scaled() physically resize the image on the CPU using either our code in Java or native code.
The other approach calls drawImage(img, x, y, w, h) which goes directly to the GPU on some platforms.

I'm sure this is because of the GPU as you have pretty much proved it. 4096 is a magic power of 2 number. Textures must always be rounded to power of 2 for the GPU which is the only case where 4096 and 4097 will be so different.

Carlos Verdier

unread,
Jul 12, 2016, 4:09:31 AM7/12/16
to CodenameOne Discussions
Ok, it's clear now. 4096x4096 is the max texture size for modern devices. I didn't know that but it's something that game developers are very aware of. The solution is simple: use more small texture instead of one big.

Thank you
Reply all
Reply to author
Forward
0 new messages