diff --git a/AndroidCompat/src/main/java/android/graphics/Bitmap.java b/AndroidCompat/src/main/java/android/graphics/Bitmap.java index 1566a9923..3dbc16569 100644 --- a/AndroidCompat/src/main/java/android/graphics/Bitmap.java +++ b/AndroidCompat/src/main/java/android/graphics/Bitmap.java @@ -1,20 +1,23 @@ 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 android.annotation.ColorInt; +import android.annotation.NonNull; + import javax.imageio.IIOImage; import javax.imageio.ImageIO; import javax.imageio.ImageWriteParam; import javax.imageio.ImageWriter; import javax.imageio.stream.ImageOutputStream; +import java.awt.image.BufferedImage; +import java.awt.image.Raster; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Iterator; public final class Bitmap { - private int width; - private int height; - private BufferedImage image; + private final int width; + private final int height; + private final BufferedImage image; public Bitmap(BufferedImage image) { this.image = image; @@ -59,8 +62,8 @@ public enum Config { final int nativeInt; - private static Config sConfigs[] = { - null, ALPHA_8, null, RGB_565, ARGB_4444, ARGB_8888, RGBA_F16, HARDWARE, RGBA_1010102 + private static final Config[] sConfigs = { + null, ALPHA_8, null, RGB_565, ARGB_4444, ARGB_8888, RGBA_F16, HARDWARE, RGBA_1010102 }; Config(int ni) { @@ -72,11 +75,60 @@ static Config nativeToConfig(int ni) { } } + /** + * Common code for checking that x and y are >= 0 + * + * @param x x coordinate to ensure is >= 0 + * @param y y coordinate to ensure is >= 0 + */ + private static void checkXYSign(int x, int y) { + if (x < 0) { + throw new IllegalArgumentException("x must be >= 0"); + } + if (y < 0) { + throw new IllegalArgumentException("y must be >= 0"); + } + } + + /** + * Common code for checking that width and height are > 0 + * + * @param width width to ensure is > 0 + * @param height height to ensure is > 0 + */ + private static void checkWidthHeight(int width, int height) { + if (width <= 0) { + throw new IllegalArgumentException("width must be > 0"); + } + if (height <= 0) { + throw new IllegalArgumentException("height must be > 0"); + } + } + 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 static Bitmap createBitmap(@NonNull Bitmap source, int x, int y, int width, int height) { + checkXYSign(x, y); + checkWidthHeight(width, height); + if (x + width > source.getWidth()) { + throw new IllegalArgumentException("x + width must be <= bitmap.width()"); + } + if (y + height > source.getHeight()) { + throw new IllegalArgumentException("y + height must be <= bitmap.height()"); + } + + // Android will make a copy when creating a sub image, + // so we do the same here + BufferedImage subImage = source.image.getSubimage(x, y, width, height); + BufferedImage newImage = new BufferedImage(subImage.getWidth(), subImage.getHeight(), subImage.getType()); + newImage.setData(subImage.getData()); + + return new Bitmap(newImage); + } + public boolean compress(CompressFormat format, int quality, OutputStream stream) { if (stream == null) { throw new NullPointerException(); @@ -87,7 +139,7 @@ public boolean compress(CompressFormat format, int quality, OutputStream stream) } float qualityFloat = ((float) quality) / 100; - String formatString = ""; + String formatString; if (format == Bitmap.CompressFormat.PNG) { formatString = "png"; } else if (format == Bitmap.CompressFormat.JPEG) { @@ -100,7 +152,7 @@ public boolean compress(CompressFormat format, int quality, OutputStream stream) if (!writers.hasNext()) { throw new IllegalStateException("no image writers found for this format!"); } - ImageWriter writer = (ImageWriter) writers.next(); + ImageWriter writer = writers.next(); ImageOutputStream ios; try { @@ -111,19 +163,73 @@ public boolean compress(CompressFormat format, int quality, OutputStream stream) writer.setOutput(ios); ImageWriteParam param = writer.getDefaultWriteParam(); - if (formatString == "jpg") { + if ("jpg".equals(formatString)) { param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); param.setCompressionQuality(qualityFloat); } try { - writer.write(null, new IIOImage(image, null, null), param); - ios.close(); - writer.dispose(); + writer.write(null, new IIOImage(image, null, null), param); + ios.close(); + writer.dispose(); } catch (IOException ex) { throw new RuntimeException(ex); } return true; } + + /** + * Shared code to check for illegal arguments passed to getPixels() + * or setPixels() + * + * @param x left edge of the area of pixels to access + * @param y top edge of the area of pixels to access + * @param width width of the area of pixels to access + * @param height height of the area of pixels to access + * @param offset offset into pixels[] array + * @param stride number of elements in pixels[] between each logical row + * @param pixels array to hold the area of pixels being accessed + */ + private void checkPixelsAccess(int x, int y, int width, int height, + int offset, int stride, int[] pixels) { + checkXYSign(x, y); + if (width < 0) { + throw new IllegalArgumentException("width must be >= 0"); + } + if (height < 0) { + throw new IllegalArgumentException("height must be >= 0"); + } + if (x + width > getWidth()) { + throw new IllegalArgumentException( + "x + width must be <= bitmap.width()"); + } + if (y + height > getHeight()) { + throw new IllegalArgumentException( + "y + height must be <= bitmap.height()"); + } + if (Math.abs(stride) < width) { + throw new IllegalArgumentException("abs(stride) must be >= width"); + } + int lastScanline = offset + (height - 1) * stride; + int length = pixels.length; + if (offset < 0 || (offset + width > length) + || lastScanline < 0 + || (lastScanline + width > length)) { + throw new ArrayIndexOutOfBoundsException(); + } + } + + public void getPixels(@ColorInt int[] pixels, int offset, int stride, + int x, int y, int width, int height) { + checkPixelsAccess(x, y, width, height, offset, stride, pixels); + + Raster raster = image.getData(); + int[] rasterPixels = raster.getPixels(x, y, width, height, (int[]) null); + + for (int ht = 0; ht < height; ht++) { + int rowOffset = offset + stride * ht; + System.arraycopy(rasterPixels, ht * width, pixels, rowOffset, width); + } + } }