package com.nativelibs4java.opencl; import org.bridj.Pointer; import com.nativelibs4java.opencl.CLImageFormat.ChannelDataType; import com.nativelibs4java.opencl.CLImageFormat.ChannelOrder; import static org.junit.Assert.*; import java.awt.image.BufferedImage; import org.junit.*; import java.util.Arrays; import java.util.List; import org.junit.runners.Parameterized; /** * * @author ochafik */ public class ImageTest extends AbstractCommon { public ImageTest(CLDevice device) { super(device); } @Parameterized.Parameters public static List<Object[]> getDeviceParameters() { return AbstractCommon.getDeviceParameters(); } public boolean supportsImages() { for (CLDevice device : context.getDevices()) if (device.hasImageSupport()) return true; return false; } @Test public void simpleImage2d() { if (!supportsImages()) return; long width = 100, height = 200; CLImageFormat format = formatsRead2D[0]; CLImage2D im = context.createImage2D(CLMem.Usage.InputOutput, format, width, height); assertEquals(width, im.getWidth()); assertEquals(height, im.getHeight()); assertEquals(format, im.getFormat()); } int someARGBPixelValue = 0xff123456; int someARGBGrayPixelValue = 0xffababab; int someShortGrayPixelValue = 0xf1f1; @Test public void testCreateARGBFromImage() { int width = 2, height = 2; BufferedImage im = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); im.setRGB(1, 0, someARGBPixelValue); CLImage2D clim = context.createImage2D(CLMem.Usage.InputOutput, im, true); assertEquals(width, clim.getWidth()); assertEquals(height, clim.getHeight()); assertEquals(CLImageFormat.INT_ARGB_FORMAT, clim.getFormat()); assertSameImage(im, clim.read(queue)); } static void assertSameImage(BufferedImage expected, BufferedImage real) { int width = expected.getWidth(), height = expected.getHeight(); assertEquals("Bad width", width, real.getWidth()); assertEquals("Bad height", height, real.getHeight()); for (int x = 0; x < width; x++) for (int y = 0; y < height; y++) assertEquals("Different value for pixel x = " + x + ", y = " + y, Integer.toHexString(expected.getRGB(x, y)), Integer.toHexString(real.getRGB(x, y))); } @Test public void testARGBReadWrite() { int width = 2, height = 2; CLImage2D clim = context.createImage2D(CLMem.Usage.InputOutput, CLImageFormat.INT_ARGB_FORMAT, width, height); assertEquals(width, clim.getWidth()); assertEquals(height, clim.getHeight()); BufferedImage im = clim.read(queue); assertEquals(BufferedImage.TYPE_INT_ARGB, im.getType()); int x = 0, y = 1; im.setRGB(x, y, someARGBPixelValue); clim.write(queue, im, false, true); assertSameImage(im, clim.read(queue)); } @Test public void testARGBShortGrayReadWrite() { int width = 2, height = 2; CLImage2D clim = context.createImage2D(CLMem.Usage.InputOutput, new CLImageFormat(ChannelOrder.RGBA, ChannelDataType.UnsignedInt16), width, height); assertEquals(width, clim.getWidth()); assertEquals(height, clim.getHeight()); BufferedImage im = clim.read(queue); assertEquals(BufferedImage.TYPE_USHORT_GRAY, im.getType()); int x = 0, y = 1; im.setRGB(x, y, someARGBGrayPixelValue); clim.write(queue, im, false, true); assertSameImage(im, clim.read(queue)); } @Test public void testCopyTo() { int width = 2, height = 2; BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); int value = 0xaabbccdd; image.setRGB(0, 0, value); image.setRGB(1, 1, value); CLImage2D src = context.createImage2D(CLMem.Usage.InputOutput, image, true); CLImage2D dest = context.createImage2D(CLMem.Usage.InputOutput, src.getFormat(), width, height); CLEvent e = src.copyTo(queue, dest); BufferedImage res = dest.read(queue, e); assertEquals(value, res.getRGB(0, 0)); assertEquals(0, res.getRGB(1, 0)); assertEquals(value, res.getRGB(1, 1)); assertEquals(0, res.getRGB(0, 1)); } @Test public void testMaxWidth() { if (!supportsImages()) return; context.createImage2D(CLMem.Usage.Input, formatsRead2D[0], device.getImage2DMaxWidth() - 1, 1); //long d = device.getImage3DMaxDepth(); //TODO FAILING !!! context.createInput3D(formatsRead3D[0], device.getImage3DMaxWidth() - 1, 1, 1); } @Test public void testMaxHeight() { if (!supportsImages()) return; context.createImage2D(CLMem.Usage.Input, formatsRead2D[0], 1, device.getImage2DMaxHeight() - 1); //long d = device.getImage3DMaxDepth(); //TODO FAILING !!! context.createInput3D(formatsRead3D[0], 1, device.getImage3DMaxHeight(), 1); } @Test public void testMaxDepth() { if (!supportsImages()) return; context.createImage3D(CLMem.Usage.Input, formatsRead3D[0], 1, 1, device.getImage3DMaxDepth() - 1); } /*@Test(expected=CLException.InvalidImageSize.class) public void testInvalidImageSize() { CLImage2D im = context.createImage2D(CLMem.Usage.Input, formatsRead2D[0], device.getImage2DMaxWidth() + 10, 1); }*/ public void simpleImage3d() { long width = 100, height = 200, depth = 50;//device.getImage3DMaxDepth(); CLImageFormat format = formatsRead3D[0]; CLImage3D im = context.createImage3D(CLMem.Usage.Input, format, width, height, depth); assertEquals(width, im.getWidth()); assertEquals(height, im.getHeight()); assertEquals(depth, im.getDepth()); assertEquals(format, im.getFormat()); //im.blockingMap(queue, CLMem.MapFlags.Read); } @Test public void testRGBAImageSource() { if (!supportsImages()) return; try { CLContext context = JavaCL.createBestContext(); CLQueue queue = context.createDefaultQueue(); String src = "\n" + "const sampler_t sampler = \n" + " CLK_NORMALIZED_COORDS_FALSE | \n" + " CLK_FILTER_NEAREST | \n" + " CLK_ADDRESS_CLAMP_TO_EDGE; \n" + " \n" + "__kernel void test( \n" + " __read_only image2d_t src_image, \n" + " int width, \n" + " int height, \n" + " __global float* output) \n" + "{ \n" + " int x = get_global_id(0); \n" + " int y = get_global_id(1); \n" + " int2 coord = (int2)(x, y); \n" + " float4 pixel = read_imagef(src_image, sampler, coord); \n" + " int offset = (y * width + x) * 4; \n" + " output[offset] = pixel.x; \n" + " output[offset + 1] = pixel.y; \n" + " output[offset + 2] = pixel.z; \n" + " output[offset + 3] = pixel.w; \n" + "} \n"; int width = 16, height = 16; BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); for (int x = 0; x < width; x++) for (int y = 0; y < height; y++) { int red = (x + y); int green = 2 * (x + y); int blue = (x + 2 * y); int alpha = (2 * x + y); int p = (alpha << 24) | (blue << 16) | (green << 8) | red; image.setRGB(x, y, p); } //image.setRGB(i, 0, i); //image.setRGB(i, height - 1, i); CLProgram program = context.createProgram(src).build(); //CLBuffer<Integer> cloutput = context.createBuffer(CLMem.Usage.Output, Integer.class, width * height * 4); CLBuffer<Float> cloutput = context.createBuffer(CLMem.Usage.Output, Float.class, width * height * 4); CLKernel kernel = program.createKernel("test"); ChannelDataType channelDataType = CLImageFormat.INT_ARGB_FORMAT.getChannelDataType(); for (ChannelOrder channelOrder : Arrays.asList(ChannelOrder.BGRA, ChannelOrder.RGBA)) { CLImageFormat imageFormat = new CLImageFormat(channelOrder, channelDataType); //CLImage2D climage = context.createImage2D(CLMem.Usage.Input, image, true); CLImage2D climage = context.createImage2D(CLMem.Usage.Input, imageFormat, width, height); climage.write(queue, image); kernel.setArgs( climage, width, height, cloutput ); kernel.enqueueNDRange(queue, new int[] {width, height}).waitFor(); //IntBuffer output = cloutput.read(queue); //IntBuffer output = cloutput.readBytes(queue, 0, width * height * 4 * 4).order(ByteOrder.BIG_ENDIAN).asIntBuffer(); Pointer<Float> output = cloutput.read(queue); for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { int expected = image.getRGB(x, y); float expectedRed = (x + y) / (float)0xff; float expectedGreen = (2 * (x + y)) / (float)0xff; float expectedBlue = (x + 2 * y) / (float)0xff; float expectedAlpha = (2 * x + y) / (float)0xff; int offset = (y * width + x) * 4; /*int r = output.get(offset); int g = output.get(offset + 1); int b = output.get(offset + 2); int a = output.get(offset + 3); System.out.println("(x, y) = (" + x + ", "+ y + ") : expected " + Integer.toHexString(expected) + ", r = " + Integer.toHexString(r) + ", g = " + Integer.toHexString(g) + ", b = " + Integer.toHexString(b) + ", a = " + Integer.toHexString(a)); System.out.println("(x, y) = (" + x + ", "+ y + ") : expected " + (expectedChannel / (double)0xff) + ", r = " + (r / (double)Integer.MAX_VALUE) + ", g = " + (g / (double)Integer.MAX_VALUE) + ", b = " + (b / (double)Integer.MAX_VALUE) + ", a = " + (a / (double)Integer.MAX_VALUE));*/ float px = output.get(offset); float py = output.get(offset + 1); float pz = output.get(offset + 2); float pw = output.get(offset + 3); float red, green, blue, alpha; switch (climage.getFormat().getChannelOrder()) { case RGBA: red = px; green = py; blue = pz; alpha = pw; break; case ARGB: alpha = px; red = py; green = pz; blue = pw; break; case BGRA: blue = px; green = py; red = pz; alpha = pw; break; default: assertTrue("Channel order not handled in this test : " + climage.getFormat(), false); return; } float tolerance = 0.00001f; String rgba = "(r = " + red + ", green = " + green + ", blue = " + blue + ", alpha = " + alpha + ")"; String expectedRgba = "(r = " + expectedRed + ", green = " + expectedGreen + ", blue = " + expectedBlue + ", alpha = " + expectedAlpha + ")"; assertEquals("[" + imageFormat + " format] bad red value for (x, y) = (" + x + ", " + y + ") : \n\t Got " + rgba + ", \n\tExpected " + expectedRgba, expectedRed, red, tolerance); assertEquals("[" + imageFormat + " format] bad green value for (x, y) = (" + x + ", " + y + ") : " + rgba + " expected " + expectedRgba, expectedGreen, green, tolerance); assertEquals("[" + imageFormat + " format] bad blue value for (x, y) = (" + x + ", " + y + ") : " + rgba + " expected " + expectedRgba, expectedBlue, blue, tolerance); assertEquals("[" + imageFormat + " format] bad alpha value for (x, y) = (" + x + ", " + y + ") : " + rgba + " expected " + expectedRgba, expectedAlpha, alpha, tolerance); //System.out.println("(x, y) = (" + x + ", "+ y + ") : expected " + (expectedChannel / (double)0xff) + ", r = " + (r) + ", g = " + (g) + ", b = " + (b) + ", a = " + (a)); } } } /* for (int i = 0; i < width; i++) { int value = output.get(i); //System.out.println(value); Assert.assertEquals(i, value); } * */ } catch (Exception ex) { ex.printStackTrace(); } } @Test public void testShortGrayImageSource() { if (!supportsImages()) return; // try { CLContext context = JavaCL.createBestContext(); CLQueue queue = context.createDefaultQueue(); String src = "\n" + "const sampler_t sampler = \n" + " CLK_NORMALIZED_COORDS_FALSE | \n" + " CLK_FILTER_NEAREST | \n" + " CLK_ADDRESS_CLAMP_TO_EDGE; \n" + " \n" + "__kernel void test( \n" + " __read_only image2d_t src_image, \n" + " int width, \n" + " int height, \n" + " __global float* output) \n" + "{ \n" + " int x = get_global_id(0); \n" + " int y = get_global_id(1); \n" + " int2 coord = (int2)(x, y); \n" + " float4 pixel = read_imagef(src_image, sampler, coord); \n" + " int offset = (y * width + x) * 4; \n" + " output[offset] = pixel.x; \n" + " output[offset + 1] = pixel.y; \n" + " output[offset + 2] = pixel.z; \n" + " output[offset + 3] = pixel.w; \n" + "} \n"; int width = 16, height = 16; BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_USHORT_GRAY); short[] data = new short[width * height]; for (short x = 0; x < width; x++) for (short y = 0; y < height; y++) { short value = (short) (10 * (x + y)); data[y * height + x] = value; } image.getRaster().setDataElements(0, 0, width, height, data); CLProgram program = context.createProgram(src).build(); CLBuffer<Float> cloutput = context.createBuffer(CLMem.Usage.Output, Float.class, width * height * 4); CLKernel kernel = program.createKernel("test"); List<CLImageFormat> formats = Arrays.asList(context.getSupportedImageFormats(CLMem.Flags.WriteOnly, CLMem.ObjectType.Image2D)); //System.out.println("Supported formats = " + formats); ChannelDataType channelDataType = ChannelDataType.UNormInt16; for (ChannelOrder channelOrder : Arrays.asList(ChannelOrder.RGBA)) { CLImageFormat imageFormat = new CLImageFormat(channelOrder, channelDataType); //CLImage2D climage = context.createImage2D(CLMem.Usage.Input, image, true); CLImage2D climage = context.createImage2D(CLMem.Usage.Input, imageFormat, width, height); climage.write(queue, image); kernel.setArgs( climage, width, height, cloutput ); kernel.enqueueNDRange(queue, new int[] {width, height}).waitFor(); //IntBuffer output = cloutput.read(queue); //IntBuffer output = cloutput.readBytes(queue, 0, width * height * 4 * 4).order(ByteOrder.BIG_ENDIAN).asIntBuffer(); Pointer<Float> output = cloutput.read(queue); for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { float expected = 10f * (x + y) / (float)0xffff; int offset = (y * width + x) * 4; float px = output.get(offset); float py = output.get(offset + 1); float pz = output.get(offset + 2); float pw = output.get(offset + 3); float tolerance = 0.00001f; assertEquals("[" + imageFormat + " format] different RGB components for gray image", px, py, tolerance); assertEquals("[" + imageFormat + " format] different RGB components for gray image", px, pz, tolerance); assertEquals("[" + imageFormat + " format] alpha not solid for gray image", 1f, pw, tolerance); float actual = px; assertEquals("[" + imageFormat + " format] bad value for (x, y) = (" + x + ", " + y + ") : \n\t Got " + actual + ", \n\tExpected " + expected, expected, actual, tolerance); } } } /* for (int i = 0; i < width; i++) { int value = output.get(i); //System.out.println(value); Assert.assertEquals(i, value); } * */ // } catch (Exception ex) { // ex.printStackTrace(); // } } @Ignore @Test public void testFillImage() { if (!supportsImages()) return; int width = 256, height = 256; CLImage2D clim = context.createImage2D(CLMem.Usage.InputOutput, CLImageFormat.INT_ARGB_FORMAT, width, height); int red = 100, green = 110, blue = 120, alpha = 127; int argb = alpha << 24 | red << 16 | green << 8 | blue; CLEvent e = clim.fillImage(queue, new int[] { red, green, blue, alpha }); BufferedImage im = clim.read(queue, e); //int[] rgb = im.getRGB(0, 0, width, height, null, 0, width); for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { int pix = im.getRGB(x, y); assertEquals("x = " + x + ", y = " + y, argb, pix); } } } }