Java 9 ImageIO read\write gives a different result than Java 8 - javax.imageio

The following test fails on Java 9 while passes in Java 8:
#Test
public void getImage_SetValueUsingConstructor_ShouldReturnCorrectValue() throws Exception {
String base64ImageString = "iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAIAAAAmkwkpAAAAEUlEQVR42mNgQAP/wQAbBw4ANwsL9Zo6V30AAAAASUVORK5CYII=";
byte[] rawImageBytes = Base64.getDecoder().decode(base64ImageString);
ByteArrayInputStream bis = new ByteArrayInputStream(rawImageBytes);
RenderedImage image = ImageIO.read(bis);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ImageIO.write(image, "PNG", bos);
byte[] imageBytesFromImage = bos.toByteArray();
assertArrayEquals(imageBytesFromImage, rawImageBytes);
}
Java 9 output:
arrays first differed at element [42];
Expected :94
Actual :-38
Can anyone help me understand what was changed in Java 9, and is there a way to write this code so that it will work for both Java 8 & 9?

As #Holger has pointed out in the comments, it is really the test that is flawed. While identical Base64 representations will give identical images, different Base64 representations does not mean the image data is different. It could mean only that the same image data is encoded differently, and will decode to the exact same image (which is the case here).
The reason your test used to pass without error, is probably that you used the Java 8 PNGImageWriter (or earlier, it hasn't really changed much since Java 1.4), which is the writer plugin used if you do ImageIO.write(image, "PNG", output), to encode the image and created the Base64 representation from it. If you had created the Base64 representation of the bytes from a file created by a different program/library, it would almost certainly be different.
You should rewrite your test, it is however not really clear to me what you are trying to test here.
If you only care about pixel data, you could just loop over the pixels and test for equality:
BufferedImage original = ImageIO.read(..);
BufferedImage current = ImageIO.read(..);
assertEquals(original.getWidth(), current.getWidth());
assertEquals(original.getHeight(), current.getHeight());
for (int y = 0; y < original.getHeight(); y++) {
for (int x = 0; x < original.getWidth(); x++) {
assertEquals(original.getRGB(x, y), current.getRGB(x, y));
}
}
If you also need the metadata to be preserved, you also need to test for equality there. But PNG doesn't really contain much interesting metadata, so I doubt you need that.

Thanks to Holger for the comments, what I did is to decode an image from the byte array and then compare the dataBuffer of both images.
The test below passed on both Java 8 and
#Test
public void imageTest() throws Exception {
String base64ImageString = "iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAIAAAAmkwkpAAAAEUlEQVR42mNgQAP/wQAbBw4ANwsL9Zo6V30AAAAASUVORK5CYII=";
byte[] rawImageBytes = Base64.getDecoder().decode(base64ImageString);
ByteArrayInputStream bis = new ByteArrayInputStream(rawImageBytes);
RenderedImage image = ImageIO.read(bis);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ImageIO.write(image, "PNG", bos);
byte[] imageBytesFromImage = bos.toByteArray();
//assertArrayEquals(imageBytesFromImage, rawImageBytes); //fails on Java 9!
bis = new ByteArrayInputStream(imageBytesFromImage);
RenderedImage image2 = ImageIO.read(bis);
DataBuffer dbA = image.getData().getDataBuffer();
int sizeA = dbA.getSize();
DataBuffer dbB = image2.getData().getDataBuffer();
int sizeB = dbB.getSize();
// compare data-buffer objects //
assertEquals(sizeA, sizeB);
for (int i = 0; i < sizeA; i++) {
assertEquals(dbA.getElem(i), dbB.getElem(i));
}
}
The compare images code was taken from: How to compare images for similarity using java

Related

Storing Long value in byte[] in spring

Below is the code snippet:
I am trying to upload a file having long as a datatype and storing that file size in a byte array.
long fileSize = uploadedFile.getSize();
byte techGuide[] = new byte[fileSize];
I got the build error:
error: incompatible types: possible lossy conversion from long to int
Please suggest what i am missing and what should i try?
Path path = uploadedFile.toPath(); // File.toPath.
Repair of your code:
// Not needed for readAllBytes.
long fileSize = Files.size(path);
if (fileSize > Integer.MAX) {
throw new IllegalArgumentException("File too large");
}
byte[] techGuide = new byte[(int)fileSize];
New code:
byte[] techGuide = Files.readAllBytes(path);
Arrays are limited by their int index. You would need to cast the fileSize to an int (and check an overflow). However Files.readAllBytes does that for you, throwing an OutOfMemoryError of > Integer.MAX - 8.

JavaFx media bytes

I want to create a soundwave in my java programm from an mp3 file. I researched and found out, that for wav-files I need to use the AudioInputStream and calculate an byte array... From mp3-File I am using JavaFX media and media-player. Are the bytes from the Inputstream the same like from the Javafx media.getSource().getBytes(); ? An AudioInputStream cant read mp3...
Or how am I supposed to get the values for an mp3 file for soundwave?
Byte from AudioInputStream:
AudioInputStream audioInputStream;
try {
audioInputStream = AudioSystem.getAudioInputStream(next);
int frameLength = (int) audioInputStream.getFrameLength();
int frameSize = (int) audioInputStream.getFormat().getFrameSize();
byte[] bytes = new byte[frameLength * frameSize];
g2.setColor(Color.MAGENTA);
for(int p = 0; p < bytes.length; p++){
g2.fillRect(20 + (p * 3), 50, 2, bytes[p]);
}
} catch (UnsupportedAudioFileException | IOException e) {
e.printStackTrace();
}
And from JavaFX:
Media media;
MediaPlayer player;
media = new Media("blablafile");
player = new Mediaplayer(media);
byte[] bytes = media.getSource().getBytes();
The JavaFX Media API does not provide much low-level support as of Java 10. It seems to be designed with only the necessary features to play media, not manipulate it significantly.
That being said, you might want to look at AudioSpectrumListener. I can't promise it will give you what you want (I'm not familiar with computer-audio concepts) but it may allow you to create your sound-wave; at least a crude representation.
You use an AudioSpectrumListener with a MediaPlayer using the corresponding property.
If your calculations don't have to be in real time then you can do them ahead of time using:
byte[] bytes = URI.create(media.getSource()).toURL().openStream().readAllBytes();
Note that if the media is remote, however, that you will end up downloading the bytes twice; once to get the bytes for your sound-wave and again when actually playing the media with a MediaPlayer.
Also, you'll want to do the above on a background thread and not the JavaFX Application thread to avoid the possibility of freezing the UI.

How do you reverse a wav file?

I want to reverse a wav file. I am not sure how to do this however. I have read that you need to reverse the sample stream instead of the byte stream, but I am not sure what people mean by this. Thanks for the help!
A nice way to get some kind of soud manipulation going is to use the Python language + Pygame -
Pygame allows you to read the contents of a .wav file into a value array with 5 or 6 lines of program - in total. The Python language allow you to revese that with a simple expression, and you will need more 2 or 3 function calls to pygame to either save the wav file or play it back.
You cna get the latest Python at http://www.python.org , and them look for instructions on how to install modules with pip - at which point you woill be able to install pygame. Then, learn some Python basics if you don't already know, and follow Pygame's documentations at:
https://www.pygame.org/docs/ref/sndarray.html
That's right, you need to reverse the samples and not the bytes.
Here's a brief summary from this tutorial.
Grab the bytes after the metadata (usually at index 44).
Reverse the samples by using a for loop:
private static byte[] ReverseTheForwardsArrayWithOnlyAudioData(int bytesPerSample, byte[] forwardsArrayWithOnlyAudioData)
{
int length = forwardsArrayWithOnlyAudioData.Length;
byte[] reversedArrayWithOnlyAudioData = new byte[length];
int sampleIdentifier = 0;
for (int i = 0; i < length; i++)
{
if (i != 0 && i % bytesPerSample == 0)
{
sampleIdentifier += 2 * bytesPerSample;
}
int index = length - bytesPerSample - sampleIdentifier + i;
reversedArrayWithOnlyAudioData[i] = forwardsArrayWithOnlyAudioData[index];
}
return reversedArrayWithOnlyAudioData;
}

Read from file in playn

Here's a stupid question.
How do you read files in a playn game? I tried using File and Scanner like I usually do in a standard java program:
void readFromFile(){
int x;
int y;
int pixel;
int[][] board;
try{
Scanner scan = new Scanner(new File(in));
x = scan.nextInt();
y = scan.nextInt();
pixel = scan.nextInt();
Point start = new Point(scan.nextInt(), scan.nextInt());
Point dir = new Point(scan.nextInt(), scan.nextInt());
Point end = new Point(scan.nextInt(), scan.nextInt());
int antRoads = scan.nextInt();
board = new int[x][y];
for (int i = 0; i < y; i++){
for (int j = 0; j < x; j++){
board[i][j] = scan.nextInt();
}
}
lev = new Level(board, start, dir, end, antRoads, pixel, x, y);
} catch(FileNotFoundException e){
System.out.println(e);
}
}
I tested File.canRead(), canWrite() and can Execute() and they all returned false.
Am I supposed to use assetMannager().getText() or something? If that's the case can someone tell me how it works? (or what is and how ResourceCallback works?)
My goal is to have a folder named "Maps" filled with maps in regular text-format just like the standard Image folder.
Regards,
Torgeir
You cannot do normal file I/O in a PlayN game, because the games are compiled into JavaScript and run in the browser (when using the HTML5 backend), and the browser supports no file I/O (at least not the general purpose file I/O you would need for these purposes).
Browsers also do not even support the idea of a byte stream, or any sort of binary I/O (this may eventually arrive, but it will be ages before it's supported for all browsers).
So you have to use AssetManager.getText to read data files. You can encode them in JSON if you like and use PlayN.json() to decode them, or you can use your own custom string-based format.
If you don't plan to deploy using the HTML5 backend, you can create a "LevelReader" interface and implement that interface in your Android or iOS backend and make use of the native file I/O capabilities on those platforms.

C# Arabic to English Code gives me error

can some one help me to rectify this error i have am using VS2010 ASP.Net C#3.0 i found this could on net but it is not working as it give me the error as show in the Screen shot. I am not able to understand the error message
Code reference from http://weblogs.asp.net/abdullaabdelhaq/archive/2009/06/27/displaying-arabic-number.aspx
CODE :
protected void Button1_Click(object sender, System.EventArgs e)
{ //Call Function
this.Label1.Text = "Arabic Number : <b>" + TranslateNumerals(this.TextBox1.Text) + "</b>";
}
public static string TranslateNumerals(string sIn)
{
System.Text.UTF8Encoding enc = new System.Text.UTF8Encoding();
System.Text.Decoder utf8Decoder = null;
utf8Decoder = enc.GetDecoder();
dynamic sTranslated = new System.Text.StringBuilder();
char[] cTransChar = new char[2];
byte[] bytes = {217,160 };
// Start Converting characters into Arabic mode.
char[] aChars = sIn.ToCharArray();
foreach (char c in aChars)
{
if (char.IsDigit(c))
{
bytes[1] = 160 + Convert.ToInt32(char.GetNumericValue(c));
utf8Decoder.GetChars(bytes, 0, 2, cTransChar, 0);
sTranslated.Append(cTransChar[0]);
}
else
{
sTranslated.Append(c);
}
}
return sTranslated.ToString();
}
The compiler is complaining about your request to implicitly convert the result of the integer addition (160 + Convert.ToInt32...) to a byte. This is a narrowing conversion; integers have a wider range than bytes do, and the compiler wants you to acknowledge, with an explicit cast operator, that you're aware that this could produce runtime errors.
Assuming that adding 160 actually does something useful to a character, I'd advise using something like this instead.
if (c >= '0' && c <= '9')
{
bytes[i] = (byte)((int)c + 160);
}
...which will properly produce, as the i'th value of the array bytes, a byte with a value 160 greater than the char c, if c represents an ASCII digit between 0 and 9.
Don't use the function char.IsDigit, which will return true even if c is a digit outside of the ASCII digit range. I don't run into this much, but since you're explicitly writing multilingual code, you'll want to handle that case.
I haven't reviewed the rest of the post you link to, but given these two quite obvious errors, I wouldn't put much faith in the correctness of the rest of it, frankly.
You could add a cast like (byte) in front of the 160.
I'm a little confused why such a large function is required to do that.
Updated answer:
Given that there are only 10 possible numerals to convert between, you can do something like this to make it slightly more readable. I think, anyway, I don't usually code in c#, and I'm on a mac right now... feel free to bash my code.
public static string TranslateNumerals(string sIn)
{
var sb = new System.Text.StringBuilder();
char[] arabic = { '٠', '١', '٢', '٣', '٤', '٥', '٦', '٧', '٨', '٩'};
foreach (char c in sIn) {
int idx;
// if it wasn't a number, just append it, otherwise convert it
if(!Integer.tryParse("" + c, idx)) {
sb.Append(c);
} else {
sb.Append(arabic[idx]);
}
}
return sb.toString();
}
// backwards array (which appears frontwards here)
{ '٩','٨','٧','٦','٥','٤','٣', '٢', '١', '٠'};

Resources