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);
}
}
}
}