Francesco Galgani
Francesco Galgani

Reputation: 6249

Saving and loading Images from the Storage in Codename One

It seems that my following code doesn't work as expected.

The following class works fine in the Simulator, but on real devices I noted that the app takes too much time to open the images: I mean that there isn't any time advantage after the first load.

Instead I expect that this class should require more time to open an image the first time, and then it should be a lot faster next times (because the scaled image is saved to the Storage).

The purpose of the EasyThread is to don't block the EDT during the scaling, because I have multiple images that in total can require a lot of seconds of cpu.

/**
 * Button useful to show the given Image at the given size.
 *
 * @author Francesco Galgani
 */
public class FixedSizeButton extends Button {

    private final int imageWidth;
    private final int imageHeight;
    private FixedSizeButton instance;
    private Image image;

    /**
     * Creates a Button displaying an Image at the given fixed size; at least
     * one of imageWidth or imageHeight must be specified.
     *
     * @param image
     * @param imageWidth in pixels, can be -1 to automatically resize
     * maintaining the aspect ratio
     * @param imageHeight in pixels, can be -1 to automatically resize
     * maintaining the aspect ratio
     */
    public FixedSizeButton(Image image, String uniqueName, int imageWidth, int imageHeight) {
        this(image, uniqueName, imageWidth, imageHeight, null);
    }

    /**
     * Creates a Button displaying an Image at the given fixed size; at least
     * one of imageWidth or imageHeight must be specified.
     *
     * @param image
     * @param imageWidth in pixels, can be -1 to automatically resize
     * maintaining the aspect ratio
     * @param imageHeight in pixels, can be -1 to automatically resize
     * maintaining the aspect ratio
     * @param uiid
     */
    public FixedSizeButton(Image image, String uniqueName, int imageWidth, int imageHeight, String uiid) {
        this(image, uniqueName, imageWidth, imageHeight, false, uiid);
    }

    /**
     * Creates a Button displaying an Image at the given fixed size; at least
     * one of imageWidth or imageHeight must be specified.
     *
     * @param image
     * @param imageWidth in pixels, can be -1 to automatically resize
     * maintaining the aspect ratio
     * @param imageHeight in pixels, can be -1 to automatically resize
     * maintaining the aspect ratio
     * @param scaledSmallerRatio force the image to maintain the aspect ratio
     * within the given dimension (it requires that both imageWidth and
     * imageHeight are specified)
     * @param uiid
     */
    public FixedSizeButton(Image image, String uniqueName, int imageWidth, int imageHeight, boolean scaledSmallerRatio, String uiid) {
        if (image == null) {
            throw new IllegalArgumentException("image cannot be null");
        }
        if (StringUtilities.isEmptyOrNull(uniqueName)) {
            throw new IllegalArgumentException("image must have an unique name");
        }
        if (imageWidth <= 0 && imageHeight <= 0) {
            throw new IllegalArgumentException("invalid imageWidth and imageHeight");
        }
        this.instance = this;
        setShowEvenIfBlank(true);
        if (uiid != null) {
            super.setUIID(uiid);
        }
        if (imageWidth < 1) {
            imageWidth = image.getWidth() * imageHeight / image.getHeight();
        } else if (imageHeight < 1) {
            imageHeight = image.getHeight() * imageWidth / image.getWidth();
        }
        if (scaledSmallerRatio) {
            float hRatio = ((float) imageHeight) / ((float) image.getHeight());
            float wRatio = ((float) imageWidth) / ((float) image.getWidth());
            if (hRatio < wRatio) {
                imageWidth = (int) (image.getWidth() * hRatio);
            } else {
                imageHeight = (int) (image.getHeight() * wRatio);
            }
        }

        this.imageWidth = imageWidth;
        this.imageHeight = imageHeight;
        String fileName = StringUtilities.replaceAll(uniqueName, ".", "_") + "_" + this.imageWidth + "_" + this.imageHeight + ".jpg";

        this.image = Image.createImage(this.imageWidth, this.imageHeight, 0xFFdddddd);
        this.setIcon(this.image);

        EasyThread scalingThread = EasyThread.start("FixedSizeButton-ScalingImg-" + fileName);
        scalingThread.run(new RunnableWithResult<Image>() {
            @Override
            public void run(SuccessCallback<Image> onSuccess) {
                try {
                    if (Storage.getInstance().exists(fileName)) {
                        Image scaledImg = Image.createImage(Storage.getInstance().createInputStream(fileName));
                        onSuccess.onSucess(scaledImg);
                    } else {
                        Image scaledImg = image.scaled(instance.imageWidth, instance.imageHeight);
                        ImageIO.getImageIO().save(scaledImg, Storage.getInstance().createOutputStream(fileName), ImageIO.FORMAT_JPEG, 0.9f);
                        onSuccess.onSucess(scaledImg);
                    }
                } catch (IOException ex) {
                    Log.e(ex);
                    SendLog.sendLogAsync();
                }
            }
        }, new SuccessCallback<Image>() {
            @Override
            public void onSucess(Image image) {
                instance.image = image;
                instance.setIcon(instance.image);
            }
        });

    }

    @Override
    public Dimension calcPreferredSize() {
        int width = imageWidth + this.getStyle().getPaddingLeftNoRTL() + this.getStyle().getPaddingRightNoRTL();
        int height = imageHeight + this.getStyle().getPaddingTop() + this.getStyle().getPaddingBottom();
        return new Dimension(width, height);
    }

    @Override
    public void setText(String text) {
        throw new IllegalStateException("Not supported");
    }

}

I guess that there is something wrong here:

if (Storage.getInstance().exists(fileName)) {
                        Image scaledImg = Image.createImage(Storage.getInstance().createInputStream(fileName));
                        onSuccess.onSucess(scaledImg);
                    } else {
                        Image scaledImg = image.scaled(instance.imageWidth, instance.imageHeight);
                        ImageIO.getImageIO().save(scaledImg, Storage.getInstance().createOutputStream(fileName), ImageIO.FORMAT_JPEG, 0.9f);
                        onSuccess.onSucess(scaledImg);
                    }

Finally, I assume that instance.setIcon(instance.image); is called in the EDT and image.scaled is called in a thread that is not the EDT: please correct me if I'm wrong.

Upvotes: 1

Views: 41

Answers (1)

Shai Almog
Shai Almog

Reputation: 52770

Creating an EasyThread is expensive. You should create one and then use the run method as you do. The success callback should be on the EDT. You can easily verify it using the isEDT() method.

I suggest adding some logging to make sure that the image is cached and loaded correctly.

Upvotes: 1

Related Questions