Skip to content

Commit 8d7791d

Browse files
authored
Merge pull request #40 from mac-comp127/int-image-pixels
Added ability to convert Images to/from 32-bit ARGB int[]
2 parents 088bcb5 + 9395cdd commit 8d7791d

File tree

3 files changed

+70
-12
lines changed

3 files changed

+70
-12
lines changed

src/edu/macalester/graphics/Image.java

Lines changed: 51 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@
1919
/**
2020
* A bitmap image that can be drawn to the screen.
2121
* <p>
22-
* An image’s {@link getPosition() position} is the upper left corner of its bounding box.
22+
* An image’s {@link #getPosition() position} is the upper left corner of its bounding box.
2323
* Its size is the size of the underying image file by default, but you can shrink it using
24-
* {@link setMaxWidth(double) setMaxWidth()} and {@link setMaxHeight(double) setMaxHeight()}.
24+
* {@link #setMaxWidth(double) setMaxWidth()} and {@link #setMaxHeight(double) setMaxHeight()}.
2525
*
2626
* @author Bret Jackson, Paul Cantrell
2727
*/
@@ -139,7 +139,7 @@ public Image(int width, int height, float[] pixels, PixelFormat format) {
139139
}
140140

141141
/**
142-
* Creates a new image using raw pixel data from the given array. This method interprets bytes
142+
* Creates a new image using raw pixel data from the given array. This interprets bytes
143143
* as unsigned: zero intensity is 0, and full intensity is 255 (but Java represents this as -1,
144144
* because the language does not have unsigned primitive types).
145145
* There is one array element per color channel, with channels interleaved
@@ -157,13 +157,23 @@ public Image(int width, int height, byte[] pixels, PixelFormat format) {
157157
this(format.makeBufferedImage(pixels, width, height));
158158
}
159159

160+
/**
161+
* Creates a new image using raw pixel data from the given array, one int per pixel. Each pixel
162+
* is in 32-bit ARGB format.
163+
*
164+
* @param width Image width in pixels
165+
* @param height Image height in pixels
166+
* @param pixels Raw pixel data. Length must exactly match the number of required samples.
167+
*/
168+
public Image(int width, int height, int[] pixels) {
169+
this(createBufferFromRawPixelData(width, height, pixels));
170+
}
171+
160172
/**
161173
* Creates a bitmap image from the given BufferedImage, positioned at (0, 0).
162174
* Note that changing the BufferedImage externally does not automatically
163175
* force it to redraw. You will need to call {@link CanvasWindow#draw()}
164176
* to see the changes.
165-
*
166-
* @param image
167177
*/
168178
public Image(BufferedImage image){
169179
this(0, 0, image);
@@ -173,12 +183,10 @@ public Image(BufferedImage image){
173183
* Creates a bitmap image from the given BufferedImage. Note that changing
174184
* the BufferedImage externally does not automatically force it to redraw.
175185
* You will need to call {@link CanvasWindow#draw()} to see the changes.
176-
*
177-
* @param image
178186
*/
179187
public Image(double x, double y, BufferedImage image){
180188
setPosition(x, y);
181-
this.path = "In memory BufferedImage@"+Integer.toHexString(image.hashCode());
189+
this.path = "In-memory BufferedImage@"+Integer.toHexString(image.hashCode());
182190
this.img = image;
183191
}
184192

@@ -282,7 +290,8 @@ private double getScaleToFit() {
282290
* produces results that correspond poorly to perceived brightness.
283291
*
284292
* @see #Image(int,int,byte[],PixelFormat)
285-
* @see #toFloatArray(PixelFormat)
293+
* @see #toFloatArray(PixelFormat)
294+
* @see #toIntArray()
286295
*/
287296
public byte[] toByteArray(PixelFormat format) {
288297
return format.makeByteArray(img);
@@ -308,6 +317,37 @@ public float[] toFloatArray(PixelFormat format) {
308317
return floats;
309318
}
310319

320+
/**
321+
* Returns the pixels in this image as an array of ints, one int per <b>pixel</b>. Pixels use
322+
* 32-bit ARGB encoding.
323+
* <p>
324+
* Note that the other methods that convert images to arrays return one array entry per
325+
* <b>color channel</b>, while this method returns one array entry <b>per pixel</b>.
326+
*
327+
* @see #Image(int,int,byte[],PixelFormat)
328+
* @see #toFloatArray(PixelFormat)
329+
*/
330+
public int[] toIntArray() {
331+
return getRawPixelData(img);
332+
}
333+
334+
private static int[] getRawPixelData(BufferedImage buf) {
335+
return buf.getRGB(0, 0, buf.getWidth(), buf.getHeight(), null, 0, buf.getWidth());
336+
}
337+
338+
private static BufferedImage createBufferFromRawPixelData(int width, int height, int[] pixels) {
339+
int expectedArrayLen = width * height;
340+
if (pixels.length != expectedArrayLen) {
341+
throw new IllegalArgumentException(
342+
"Invalid input array length: expected " + width + " w * " + height + " h = "
343+
+ expectedArrayLen + ", but got " + pixels.length);
344+
}
345+
346+
BufferedImage buf = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
347+
buf.setRGB(0, 0, width, height, pixels, 0, width);
348+
return buf;
349+
}
350+
311351
@Override
312352
protected Object getEqualityAttributes() {
313353
return List.of(path, maxWidth, maxHeight); // Not ideally performant, but life goes on
@@ -407,9 +447,9 @@ private BufferedImage makeBufferedImage(
407447
}
408448

409449
private byte[] makeByteArray(BufferedImage buf) {
410-
int width = buf.getWidth(), height = buf.getHeight();
411-
int[] rawData = buf.getRGB(0, 0, width, height, null, 0, width);
450+
int[] rawData = getRawPixelData(buf);
412451

452+
int width = buf.getWidth(), height = buf.getHeight();
413453
byte[] pixels = new byte[width * height * externalChans];
414454
int i = 0;
415455
for(int pix : rawData) {

test/edu/macalester/graphics/ImageTest.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,9 @@ void pixelsConstructorsCheckBounds() {
9393
for (var ctorCall : List.of((Executable)
9494
() -> new Image(2, 3, new float[18], Image.PixelFormat.GRAYSCALE), // too large
9595
() -> new Image(3, 2, new float[6], Image.PixelFormat.RGB), // too small
96-
() -> new Image(3, 2, new byte[18], Image.PixelFormat.ARGB) // too small
96+
() -> new Image(3, 2, new byte[18], Image.PixelFormat.ARGB), // too small
97+
() -> new Image(3, 2, new int[7]), // too large
98+
() -> new Image(3, 2, new int[5]) // too small
9799
)) {
98100
assertThrows(IllegalArgumentException.class, ctorCall);
99101
}
@@ -213,6 +215,22 @@ void imageFileProcessing() {
213215
Image.PixelFormat.RGB);
214216
}
215217

218+
@RenderingTest(width=200, height=200, modes = { PLAIN })
219+
void imageFileProcessingWithInts() {
220+
Image original = new Image(FOXFLOWER_IMAGE);
221+
int[] src = original.toIntArray();
222+
int[] dst = new int[src.length];
223+
224+
for(int i = 0; i < src.length; i++) {
225+
dst[i] = src[src.length - i - 1] ^ (i * 17);
226+
}
227+
228+
image = new Image(
229+
original.getImageWidth(),
230+
original.getImageHeight(),
231+
dst);
232+
}
233+
216234
@RenderingTest(height=200, modes = { PLAIN })
217235
void colorPixelConversionToGrayscaleArray() {
218236
Image colorImage = new Image(FOXBOT_IMAGE);
89.2 KB
Loading

0 commit comments

Comments
 (0)