I have a GUI with JavaFX. I get an image from the user. Then i run a c program and giving it the filepath to the picture. The c program processes the picture and give it back to the java program where the processed picture is displayed. Here is the relevant code:
Java:
public void runCProgram(String t)
{
try {
String command = "C:\\Users\\fendl\\CPP2\\Uebung\\fotofilter";
// Create a ProcessBuilder for the C program
ProcessBuilder processBuilder = new ProcessBuilder(command, imagePath, t);
// Redirect the output to the Java program's output
processBuilder.redirectOutput(ProcessBuilder.Redirect.INHERIT);
// Start the C program
Process process = processBuilder.start();
// Wait for the C program to finish
int exitCode = process.waitFor();
if (exitCode == 0) {
// Image processing was successful
// Read the processed image data from the C program's output stream
try (InputStream is = process.getInputStream()) {
ByteArrayOutputStream outputData = new ByteArrayOutputStream();
byte[] buffer = new byte[2056];
int bytesRead;
while ((bytesRead = is.read(buffer)) != -1) {
outputData.write(buffer, 0, bytesRead);
}
byte[] processedImageData = outputData.toByteArray();
Platform.runLater(() -> {
imageView.setImage(new Image(new ByteArrayInputStream(processedImageData)));
});
System.out.println("\n C program successfully processed the image.");
}
} else {
System.err.println("C program exited with an error code: " + exitCode);
}
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
C:
int main(int argc, char const *argv[])
{
FILE *inputFile = fopen(argv[1], "rb");
if (inputFile == NULL)
{
perror("Failed to open the file");
return 1;
}
//The first 54 bytes of a bmp file is the header
unsigned char imageHeader[54];
fread(imageHeader, sizeof(unsigned char), 54, inputFile);
if (imageHeader[0] != 'B' || imageHeader[1] != 'M')
{
perror("File is not a bmp file");
return 1;
}
int width = *(int *) &imageHeader[18];
int height = *(int *) &imageHeader[22];
int bitDepth = *(int *) &imageHeader[28];
//Normally 3 Bytes per Pixel (red, green, blue)
int bytesPerPixel = bitDepth / 8;
//Row of pixel is always a multiple of 4 in a bmp file
int dataSize = width * height* bytesPerPixel;
//If there is a color table stored in the bmp file; the next 1024 bytes is the color table
unsigned char colorTable[1024];
if (bitDepth <= 8)
{
fread(colorTable, sizeof(unsigned char), 1024, inputFile);
}
//The rest of the bytes is the data of the image (RGB values for every pixel)
unsigned char *imageData = (unsigned char *) malloc(dataSize);
fread(imageData, sizeof(unsigned char), dataSize, inputFile);
//Apply Filter
choose_filter(dataSize, bytesPerPixel, imageData, argv[2][0]);
//Write image to output stream
fwrite(imageHeader, sizeof(unsigned char), 54, stdout);
if (bitDepth <= 8)
{
fwrite(colorTable, sizeof(unsigned char), 1024, stdout);
}
fwrite(imageData, sizeof(unsigned char), dataSize, stdout);
fflush(stdout);
fclose(inputFile);
free(imageData);
return 0;
}
Problem: The problem is that the "processedImageData" in Java is empty. So there seems to be a problem with how I get the data from my c program.
When writing the image to a file and not to the output stream in C, than everything works fine and we get a correctly processed image. There are also a lot of weird symbol in my console, so the c program seems to return at least something. ChatGPT also couldn't help :(
Does anybody has any idea what the problem could be and how to fix it?
1. Move waitFor after the reading of program output.
As DuncG pointed out, you must not call a Process’s
waitFormethod before reading its output. After calling waitFor, the process is finished and there is no output to read.2. Remove call to redirectOutput.
processBuilder.redirectOutput(ProcessBuilder.Redirect.INHERIT);causes the child process to use the Java program’s standard out as its own standard out. This means the Java program has no way to read that output.What you want is:
which will ensure any errors printed by the C program appear on the Java program’s standard error.
3. Check for errors in image loading.
Instead of blindly assuming the image is correct:
You should check for errors in the image:
4. (Optional) Use readAllBytes instead of a ByteArrayOutputStream.
You don’t need a ByteArrayOutputStream. Just read the bytes using readAllBytes:
This is not strictly necessary, but it saves memory (fewer copies of the byte array), and having less code helps you eliminate your own code as a possible source of bugs.
5. Calculate dataSize according to the BMP format.
Where you are calculating dataSize, you have this comment:
But you are completely ignoring that statement.
Also, the height can be negative, as explained in the BMP format.
There are probably more compact ways to do it, but my C skills are rusty and I prefer something clear over something cryptic.
6. Calculate color table size according to the BMP format.
The BMP format does not say that the color table is always 1024 bytes. The number of colors is a 32-bit value at offset 46 in the file; if zero, the number of colors is 2ⁿ, where n is the bits per pixel.
And of course, just before your main method returns: