From 0a748cd53b60b1e1124a83d52fce9c25772d6764 Mon Sep 17 00:00:00 2001 From: Zero Date: Mon, 5 Dec 2022 10:51:16 -0500 Subject: [PATCH] implementation of android.graphics.BitmapFactory (#460) Only what was needed is implemented, compression method is still untested. --- AndroidCompat/build.gradle.kts | 5 + .../main/java/android/graphics/Bitmap.java | 129 ++++++++++++++++++ .../java/android/graphics/BitmapFactory.java | 36 +++++ 3 files changed, 170 insertions(+) create mode 100644 AndroidCompat/src/main/java/android/graphics/Bitmap.java create mode 100644 AndroidCompat/src/main/java/android/graphics/BitmapFactory.java diff --git a/AndroidCompat/build.gradle.kts b/AndroidCompat/build.gradle.kts index 50a3a6a5..e74dec6f 100644 --- a/AndroidCompat/build.gradle.kts +++ b/AndroidCompat/build.gradle.kts @@ -25,4 +25,9 @@ dependencies { // Android version of SimpleDateFormat implementation("com.ibm.icu:icu4j:72.1") + + // OpenJDK lacks a native JPEG encoder + implementation("com.twelvemonkeys.common:common-lang:3.9.4") + implementation("com.twelvemonkeys.imageio:imageio-core:3.9.4") + implementation("com.twelvemonkeys.imageio:imageio-jpeg:3.4.1") } diff --git a/AndroidCompat/src/main/java/android/graphics/Bitmap.java b/AndroidCompat/src/main/java/android/graphics/Bitmap.java new file mode 100644 index 00000000..1566a992 --- /dev/null +++ b/AndroidCompat/src/main/java/android/graphics/Bitmap.java @@ -0,0 +1,129 @@ +package android.graphics; + +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Iterator; +import javax.imageio.IIOImage; +import javax.imageio.ImageIO; +import javax.imageio.ImageWriteParam; +import javax.imageio.ImageWriter; +import javax.imageio.stream.ImageOutputStream; + +public final class Bitmap { + private int width; + private int height; + private BufferedImage image; + + public Bitmap(BufferedImage image) { + this.image = image; + this.width = image.getWidth(); + this.height = image.getHeight(); + } + + public BufferedImage getImage() { + return image; + } + + public int getHeight() { + return height; + } + + public int getWidth() { + return width; + } + + public enum CompressFormat { + JPEG (0), + PNG (1), + WEBP (2), + WEBP_LOSSY (3), + WEBP_LOSSLESS (4); + + CompressFormat(int nativeInt) { + this.nativeInt = nativeInt; + } + + final int nativeInt; + } + + public enum Config { + ALPHA_8(1), + RGB_565(3), + ARGB_4444(4), + ARGB_8888(5), + RGBA_F16(6), + HARDWARE(7), + RGBA_1010102(8); + + final int nativeInt; + + private static Config sConfigs[] = { + null, ALPHA_8, null, RGB_565, ARGB_4444, ARGB_8888, RGBA_F16, HARDWARE, RGBA_1010102 + }; + + Config(int ni) { + this.nativeInt = ni; + } + + static Config nativeToConfig(int ni) { + return sConfigs[ni]; + } + } + + public static Bitmap createBitmap(int width, int height, Config config) { + BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + return new Bitmap(image); + } + + public boolean compress(CompressFormat format, int quality, OutputStream stream) { + if (stream == null) { + throw new NullPointerException(); + } + + if (quality < 0 || quality > 100) { + throw new IllegalArgumentException("quality must be 0..100"); + } + float qualityFloat = ((float) quality) / 100; + + String formatString = ""; + if (format == Bitmap.CompressFormat.PNG) { + formatString = "png"; + } else if (format == Bitmap.CompressFormat.JPEG) { + formatString = "jpg"; + } else { + throw new IllegalArgumentException("unsupported compression format!"); + } + + Iterator writers = ImageIO.getImageWritersByFormatName(formatString); + if (!writers.hasNext()) { + throw new IllegalStateException("no image writers found for this format!"); + } + ImageWriter writer = (ImageWriter) writers.next(); + + ImageOutputStream ios; + try { + ios = ImageIO.createImageOutputStream(stream); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + writer.setOutput(ios); + + ImageWriteParam param = writer.getDefaultWriteParam(); + if (formatString == "jpg") { + param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); + param.setCompressionQuality(qualityFloat); + } + + try { + writer.write(null, new IIOImage(image, null, null), param); + ios.close(); + writer.dispose(); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + + return true; + } +} diff --git a/AndroidCompat/src/main/java/android/graphics/BitmapFactory.java b/AndroidCompat/src/main/java/android/graphics/BitmapFactory.java new file mode 100644 index 00000000..7285f15a --- /dev/null +++ b/AndroidCompat/src/main/java/android/graphics/BitmapFactory.java @@ -0,0 +1,36 @@ +package android.graphics; + +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.io.IOException; +import javax.imageio.ImageIO; + +public class BitmapFactory { + public static Bitmap decodeStream(InputStream is) { + Bitmap bm = null; + + try { + BufferedImage bf = ImageIO.read(is); + bm = new Bitmap(bf); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + + return bm; + } + + public static Bitmap decodeByteArray(byte[] data, int offset, int length) { + Bitmap bm = null; + + ByteArrayInputStream bais = new ByteArrayInputStream(data); + try { + BufferedImage bf = ImageIO.read(bais); + bm = new Bitmap(bf); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + + return bm; + } +}