B4J Code Snippet Image to JPEG byte array with resize/quality options

Due to @Erel's comment below, I've rewritten this in pure B4X basic. The new version should work with B4A, B4J and B4i. New version (that should be used) can be found here https://www.b4x.com/android/forum/t...byte-array-with-resize-quality-options.91774/.
B4X:
' Converts image to JPEG a byte array of the resulting JPEG. Ability to resize and adjust JPEG quality.
' Negative width and height values = %, such that -50 = 50% and -200 = 200%
' Positive width and height values = pixel dimensions
' If one value (either width or height) are 0, then the other value is proportionally
'  calculated from the first.
' If both width and height are 0 or -100, no resizing takes place
' If quality = -1, use Java's default quality
Sub ImageToJPEGByteArray(aImage As Image, width As Int, height As Int, quality As Int) As Byte()
   Dim jo As JavaObject = Me
   Return jo.RunMethod("imageToJPEGByteArray", Array As Object (aImage, width, height, quality))
End Sub

B4X:
#if Java
import java.awt.image.BufferedImage;
import java.awt.geom.AffineTransform;
import java.awt.Graphics2D;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;

import java.util.Iterator;
import java.util.Locale;

import javafx.scene.image.Image;
import javafx.embed.swing.SwingFXUtils;

import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.plugins.jpeg.JPEGImageWriteParam;
import javax.imageio.stream.ImageOutputStream;

// Image resizing
// https://stackoverflow.com/a/4205711
// https://stackoverflow.com/questions/21540378/convert-javafx-image-to-bufferedimage
// https://stackoverflow.com/a/13605411
// https://www.mkyong.com/java/how-to-convert-bufferedimage-to-byte-in-java/
// JPEG quality
// https://www.b4x.com/android/forum/threads/solved-how-to-create-jpeg-output-stream.83830/#post-531280
// http://www.java2s.com/Code/Java/2D-Graphics-GUI/WritesanimagetoanoutputstreamasaJPEGfileTheJPEGqualitycanbespecifiedinpercent.htm


// Converts image to JPEG a byte array of the resulting JPEG. Ability to resize and adjust JPEG quality.
// Negative width and height values = %, such that -50 = 50% and -200 = 200%
// Positive width and height values = pixel dimensions
// If one value (either width or height) are 0, then the other value is proportionally
//  calculated from the first.
// If both width and height are 0 or -100, no resizing takes place
// If quality = -1, use Java's default quality
public static byte[] imageToJPEGByteArray(Image aImage, int width, int height, int qualityPercent) throws IOException {
   byte[] imageBytes = new byte[0];
 
   if ((qualityPercent < -1) || (qualityPercent > 100)) {
      throw new IllegalArgumentException("Quality out of bounds!");
    }
   float quality = qualityPercent / 100f;
 
   double oldWidth = aImage.getWidth();
   double oldHeight = aImage.getHeight();
   if (oldWidth == 0 || oldHeight == 0) {
       throw new IllegalArgumentException("Source image with 0 width and/or height!");
   }
 
   boolean resize = true;
   if ((width == 0 && height == 0) || (width == -100 && height == -100)) resize = false;

   BufferedImage destImage;
   if (resize) {
       double newWidth = (double) width;
       double newHeight = (double) height;
       // Calculate new dimensions
       if (newWidth < 0) newWidth = -1 * oldWidth * newWidth / 100;
       if (newHeight < 0) newHeight = -1 * oldHeight * newHeight / 100;
       if (newWidth == 0) newWidth = oldWidth * newHeight / oldHeight; 
       if (newHeight == 0) newHeight = oldHeight * newWidth / oldWidth;
       // Convert JavaFX image to BufferedImage and transform according to new dimensions
       destImage = new BufferedImage((int) newWidth, (int) newHeight, BufferedImage.TYPE_INT_RGB);
       BufferedImage srcImage = SwingFXUtils.fromFXImage(aImage, null);
       Graphics2D g = destImage.createGraphics();
       AffineTransform at = AffineTransform.getScaleInstance(newWidth/oldWidth, newHeight/oldHeight);
       g.drawRenderedImage(srcImage, at);
       g.dispose();
   } else {
       destImage = SwingFXUtils.fromFXImage(aImage, null);
   }
 
   // Output JPEG byte array
   ByteArrayOutputStream baos = new ByteArrayOutputStream();
 
   if(qualityPercent != -1) {
       // Start to create JPEG with quality option
       ImageWriter writer = null;
       Iterator iter = ImageIO.getImageWritersByFormatName("jpg");
       if (iter.hasNext()) {
         writer = (ImageWriter) iter.next();
       }
       ImageOutputStream ios = ImageIO.createImageOutputStream(baos);
       writer.setOutput(ios); 
       ImageWriteParam iwparam = new JPEGImageWriteParam(Locale.getDefault());
       iwparam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
       iwparam.setCompressionQuality(quality);
       writer.write(null, new IIOImage(destImage, null, null), iwparam);
       ios.flush();
       writer.dispose();
       ios.close();
       // Done creating JPEG with quality option
   } else {
       // This one line below created a JPEG file without quality option
       ImageIO.write(destImage, "jpg", baos);
   }
 
   baos.flush();
   imageBytes = baos.toByteArray();
   baos.close();
 
   // Done
   return imageBytes; 
}

#End If

Example
B4X:
Dim bImage As Image
bImage.Initialize(File.DirAssets, "b.png")
Dim c() As Byte = ImageToJPEGByteArray(bImage, -50, 0, 50)
'https://www.b4x.com/android/forum/threads/b4x-bytes-to-file.70111/#content
BytesToFile(File.DirApp, "b.jpg", c)

Resources used:
Image resizing
https://stackoverflow.com/a/4205711
https://stackoverflow.com/questions/21540378/convert-javafx-image-to-bufferedimage
https://stackoverflow.com/a/13605411
https://www.mkyong.com/java/how-to-convert-bufferedimage-to-byte-in-java/

JPEG quality
https://www.b4x.com/android/forum/threads/solved-how-to-create-jpeg-output-stream.83830/#post-531280
http://www.java2s.com/Code/Java/2D-Graphics-GUI/WritesanimagetoanoutputstreamasaJPEGfileTheJPEGqualitycanbespecifiedinpercent.htm
 
Last edited:

OliverA

Expert
Licensed User
Longtime User

moster67

Expert
Licensed User
Longtime User
@OliverA: Many thanks for the java-code you provided - it was a life-saver for a library I am wrapping. Thanks!
 
Top