From c4d8bba5cafc478a7d907cfd2944e1a05720423b Mon Sep 17 00:00:00 2001 From: Constantin Piber <59023762+cpiber@users.noreply.github.com> Date: Sun, 11 Jan 2026 22:48:07 +0100 Subject: [PATCH] Bitmap: Allow pixel-based access (#1855) --- .../main/java/android/graphics/Bitmap.java | 23 ++++++++++ .../main/java/android/graphics/Canvas.java | 43 ++++++++++++++++++- .../src/main/java/android/graphics/Paint.java | 7 ++- 3 files changed, 70 insertions(+), 3 deletions(-) diff --git a/AndroidCompat/src/main/java/android/graphics/Bitmap.java b/AndroidCompat/src/main/java/android/graphics/Bitmap.java index 07ace14e..f4b7872f 100644 --- a/AndroidCompat/src/main/java/android/graphics/Bitmap.java +++ b/AndroidCompat/src/main/java/android/graphics/Bitmap.java @@ -315,6 +315,23 @@ public final class Bitmap { } } + /** + * Shared code to check for illegal arguments passed to getPixel() + * or setPixel() + * + * @param x x coordinate of the pixel + * @param y y coordinate of the pixel + */ + private void checkPixelAccess(int x, int y) { + checkXYSign(x, y); + if (x >= getWidth()) { + throw new IllegalArgumentException("x must be < bitmap.width()"); + } + if (y >= getHeight()) { + throw new IllegalArgumentException("y must be < bitmap.height()"); + } + } + 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); @@ -322,6 +339,12 @@ public final class Bitmap { image.getRGB(x, y, width, height, pixels, offset, stride); } + @ColorInt + public int getPixel(int x, int y) { + checkPixelAccess(x, y); + return image.getRGB(x, y); + } + public void eraseColor(int c) { java.awt.Color color = Color.valueOf(c).toJavaColor(); Graphics2D graphics = image.createGraphics(); diff --git a/AndroidCompat/src/main/java/android/graphics/Canvas.java b/AndroidCompat/src/main/java/android/graphics/Canvas.java index 92384a04..ef7e3e54 100644 --- a/AndroidCompat/src/main/java/android/graphics/Canvas.java +++ b/AndroidCompat/src/main/java/android/graphics/Canvas.java @@ -13,6 +13,7 @@ import java.awt.Shape; import java.awt.font.TextAttribute; import java.awt.font.GlyphVector; import java.awt.geom.AffineTransform; +import java.awt.geom.Ellipse2D; import java.awt.image.BufferedImage; import java.text.AttributedString; import java.util.ArrayList; @@ -182,11 +183,21 @@ public final class Canvas { canvas.fillRect(0, 0, canvasImage.getWidth(), canvasImage.getHeight()); } + public void drawPoint(float x, float y, Paint paint) { + applyPaint(paint); + Shape shape = paintToShape(paint, x, y); + if (paint.getStyle() == Paint.Style.FILL) { + canvas.fill(shape); + } else { + canvas.draw(shape); + } + } + private void applyPaint(Paint paint) { canvas.setFont(paint.getTypeface().getFont()); java.awt.Color color = Color.valueOf(paint.getColorLong()).toJavaColor(); canvas.setColor(color); - canvas.setStroke(new BasicStroke(paint.getStrokeWidth(), BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); + canvas.setStroke(new BasicStroke(paint.getStrokeWidth(), paintToStrokeCap(paint), BasicStroke.JOIN_ROUND)); if (paint.isAntiAlias()) { canvas.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); } else { @@ -199,4 +210,34 @@ public final class Canvas { } // TODO: use more from paint? } + + private static int paintToStrokeCap(Paint paint) { + switch (paint.getStrokeCap()) { + case BUTT: + return BasicStroke.CAP_BUTT; + case SQUARE: + return BasicStroke.CAP_SQUARE; + case ROUND: + return BasicStroke.CAP_ROUND; + default: + throw new UnsupportedOperationException("Stroke cap " + paint.getStrokeCap() + " not supported"); + } + } + + private static Shape paintToShape(Paint paint, float x, float y) { + int width = (int)(paint.getStrokeWidth() * 2); + if (width <= 0) width = 1; + int upLeftX = (int) (x - (float) width / 2); + int upLeftY = (int) (y - (float) width / 2); + switch (paint.getStrokeCap()) { + case BUTT: + return new Rectangle((int)x, (int)y, 1, 1); + case SQUARE: + return new Rectangle(upLeftX, upLeftY, width, width); + case ROUND: + return new Ellipse2D.Float(upLeftX, upLeftY, width, width); + default: + throw new UnsupportedOperationException("Stroke cap " + paint.getStrokeCap() + " not supported"); + } + } } diff --git a/AndroidCompat/src/main/java/android/graphics/Paint.java b/AndroidCompat/src/main/java/android/graphics/Paint.java index da2a61d6..3e6dc5fa 100644 --- a/AndroidCompat/src/main/java/android/graphics/Paint.java +++ b/AndroidCompat/src/main/java/android/graphics/Paint.java @@ -67,6 +67,7 @@ public class Paint { private int mFlags; private Style mStyle = Style.FILL; private float mStrokeWidth = 1.0f; + private Cap mStrokeCap = Cap.BUTT; private Typeface mTypeface = Typeface.DEFAULT; private static final Object sCacheLock = new Object(); @@ -280,6 +281,7 @@ public class Paint { mStyle = Style.FILL; mStrokeWidth = 1.0f; + mStrokeCap = Cap.BUTT; mTypeface = Typeface.DEFAULT; setFlags(ANTI_ALIAS_FLAG); } @@ -316,6 +318,7 @@ public class Paint { mFlags = paint.mFlags; mStyle = paint.mStyle; mStrokeWidth = paint.mStrokeWidth; + mStrokeCap = paint.mStrokeCap; mTypeface = paint.mTypeface; } @@ -526,11 +529,11 @@ public class Paint { } public Cap getStrokeCap() { - throw new RuntimeException("Stub!"); + return mStrokeCap; } public void setStrokeCap(Cap cap) { - throw new RuntimeException("Stub!"); + mStrokeCap = cap; } public Join getStrokeJoin() {