Skip to content

Instantly share code, notes, and snippets.

@jsfan3
Last active May 4, 2020 15:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jsfan3/4d2d55f0d5136ec88d36257401d41854 to your computer and use it in GitHub Desktop.
Save jsfan3/4d2d55f0d5136ec88d36257401d41854 to your computer and use it in GitHub Desktop.
Codename One fast image scaling algorithm to generate thumbnails from captured photos. This optimization makes sense on Android and Simulator (Java SE) only. See: https://github.com/codenameone/CodenameOne/issues/3090
package net.informaticalibera.test.imagescaling;
import com.codename1.capture.Capture;
import com.codename1.components.SpanLabel;
import com.codename1.io.FileSystemStorage;
import static com.codename1.ui.CN.*;
import com.codename1.ui.Form;
import com.codename1.ui.Dialog;
import com.codename1.ui.Label;
import com.codename1.ui.plaf.UIManager;
import com.codename1.ui.util.Resources;
import com.codename1.io.Log;
import com.codename1.ui.Toolbar;
import java.io.IOException;
import com.codename1.ui.layouts.BoxLayout;
import com.codename1.io.Util;
import com.codename1.ui.Button;
import com.codename1.ui.CN;
import com.codename1.ui.EncodedImage;
import com.codename1.ui.Image;
import com.codename1.ui.events.ActionListener;
import com.codename1.ui.util.ImageIO;
import java.io.InputStream;
import java.io.OutputStream;
/**
* This file was generated by <a href="https://www.codenameone.com/">Codename
* One</a> for the purpose of building native mobile applications using Java.
*/
public class MyApplication {
private Form current;
private Resources theme;
public void init(Object context) {
// use two network threads instead of one
updateNetworkThreadCount(2);
theme = UIManager.initFirstTheme("/theme");
// Enable Toolbar on all Forms by default
Toolbar.setGlobalToolbar(true);
// Pro only feature
Log.bindCrashProtection(true);
addNetworkErrorListener(err -> {
// prevent the event from propagating
err.consume();
if (err.getError() != null) {
Log.e(err.getError());
}
Log.sendLogAsync();
Dialog.show("Connection Error", "There was a networking error in the connection to " + err.getConnectionRequest().getUrl(), "OK", null);
});
}
public void start() {
if (current != null) {
current.show();
return;
}
Form hi = new Form("Hi World", BoxLayout.y());
Button button1 = new Button("Open camera");
Button button2 = new Button("Open gallery");
SpanLabel summary = new SpanLabel();
Label label1 = new Label();
Label label2 = new Label();
Label label3 = new Label();
Label label4 = new Label();
Label label5 = new Label();
Label label6 = new Label();
hi.addAll(button1, button2, summary, label1, label2, label3, label4, label5, label6);
hi.show();
ActionListener actionEvt = evt -> {
String tempPath = (String) evt.getSource();
if (tempPath != null && FileSystemStorage.getInstance().exists(tempPath)) {
try {
// copy the temp file in the home
String capturedPhoto = FileSystemStorage.getInstance().getAppHomePath() + "captured.jpg";
InputStream temp = FileSystemStorage.getInstance().openInputStream(tempPath);
OutputStream dest = FileSystemStorage.getInstance().openOutputStream(capturedPhoto);
Util.copy(temp, dest);
InputStream source = FileSystemStorage.getInstance().openInputStream(capturedPhoto);
Image original = EncodedImage.create(source);
int newWidth = CN.convertToPixels(30);
int newHeight = newWidth * original.getHeight() / original.getWidth();
// resizes using the fastScaleImage method
long startTime1 = System.currentTimeMillis();
Image thumbnail1 = fastScaleImage(original, newWidth, newHeight);
long endTime1 = System.currentTimeMillis();
long time1 = endTime1 - startTime1;
label1.setIcon(thumbnail1);
// resizes using the Image.scale method
long startTime2 = System.currentTimeMillis();
Image thumbnail2 = original.scaled(newWidth, newHeight);
long endTime2 = System.currentTimeMillis();
long time2 = endTime2 - startTime2;
label2.setIcon(thumbnail2);
// resizes using the ImageIO.save method
long startTime3 = System.currentTimeMillis();
String thumbPath = FileSystemStorage.getInstance().getAppHomePath() + "thumb.jpg";
InputStream source3 = FileSystemStorage.getInstance().openInputStream(capturedPhoto);
OutputStream response = FileSystemStorage.getInstance().openOutputStream(thumbPath);
ImageIO.getImageIO().save(source3, response, ImageIO.FORMAT_JPEG, newWidth, newHeight, 0.9f);
long endTime3 = System.currentTimeMillis();
InputStream thumbStream = FileSystemStorage.getInstance().openInputStream(thumbPath);
Image thumbnail = EncodedImage.create(thumbStream);
long time3 = endTime3 - startTime3;
label3.setIcon(thumbnail);
// Test of make an image bigger
// resizes using the fastScaleImage method
long startTime4 = System.currentTimeMillis();
Image thumbnail4 = fastScaleImage(thumbnail, original.getWidth(), original.getHeight());
long endTime4 = System.currentTimeMillis();
long time4 = endTime4 - startTime4;
label4.setIcon(thumbnail4);
// resizes using the Image.scale method
long startTime5 = System.currentTimeMillis();
Image thumbnail5 = thumbnail.scaled(original.getWidth(), original.getHeight());
long endTime5 = System.currentTimeMillis();
long time5 = endTime5 - startTime5;
label5.setIcon(thumbnail5);
// resizes using the ImageIO.save method
long startTime6 = System.currentTimeMillis();
String newImgPath = FileSystemStorage.getInstance().getAppHomePath() + "newImg.jpg";
ImageIO.getImageIO().saveAndKeepAspect(thumbPath, newImgPath, ImageIO.FORMAT_JPEG, original.getWidth(), original.getHeight(), 0.9f, false, true);
long endTime6 = System.currentTimeMillis();
InputStream newImgStream = FileSystemStorage.getInstance().openInputStream(newImgPath);
Image thumbnail6 = EncodedImage.create(newImgStream);
long time6 = endTime6 - startTime6;
label6.setIcon(thumbnail6);
summary.setText("RESIZING TO A SMALLER IMAGE\n\nfastScaleImage time: " + time1 + "\n" + "Image.scaled() time: " + time2 + "\n" + "ImageIO.save() time: " + time3 + "\n\n"
+ "RESIZING TO A BIGGER IMAGE\n\nfastScaleImage time: " + time4 + "\n" + "Image.scaled() time: " + time5 + "\n" + "ImageIO.save() time: " + time6);
hi.revalidate();
} catch (IOException ex) {
Log.p("Error loading the photo", Log.ERROR);
Log.e(ex);
}
}
};
button1.addActionListener(l -> {
Capture.capturePhoto(actionEvt);
});
button2.addActionListener(l -> {
CN.openGallery(actionEvt, CN.GALLERY_IMAGE);
});
}
public void stop() {
current = getCurrentForm();
if (current instanceof Dialog) {
((Dialog) current).dispose();
current = getCurrentForm();
}
}
public void destroy() {
}
/**
* <p>
* Fast scaling algorithm, optimized to resize a captured photo to a
* thumbnail: in this use case, it's about 3x ~ 10x faster that
* Image.scaled() and IOImage.save() ON ANDROID AND SIMULATOR ONLY, with an
* equivalent quality. On iOS, the fastest algorithm is IOImage.save()</p>
* <p>
* CAUTION: Don't use to enlarge an image, because it will be slower than
* other methods.</p>
* <p>
* Speed comparation, see
* <a href="https://gist.github.com/jsfan3/4d2d55f0d5136ec88d36257401d41854">testing
* code</a></p>
*
*
* @param original Image
* @param newWidth in pixels
* @param newHeight in pixels
* @return new scaled Image (it will be an EncodedImage instance)
*/
public static Image fastScaleImage(Image original, int newWidth, int newHeight) {
int[] rawInput = original.getRGB(); // ARGB array
int[] rawOutput = new int[newWidth * newHeight];
int originalW = original.getWidth();
int originalH = original.getHeight();
int YD = (originalH / newHeight) * originalW - originalW;
int YR = originalH % newHeight;
int XD = originalW / newWidth;
int XR = originalW % newWidth;
int outOffset = 0;
int inOffset = 0;
for (int y = newHeight, YE = 0; y > 0; y--) {
for (int x = newWidth, XE = 0; x > 0; x--) {
rawOutput[outOffset++] = rawInput[inOffset];
inOffset += XD;
XE += XR;
if (XE >= newWidth) {
XE -= newWidth;
inOffset++;
}
}
inOffset += YD;
YE += YR;
if (YE >= newHeight) {
YE -= newHeight;
inOffset += originalW;
}
}
return EncodedImage.createFromRGB(rawOutput, newWidth, newHeight, true);
}
}
@jsfan3
Copy link
Author

jsfan3 commented May 3, 2020

screen1

screen2

screen3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment