Hello @Aryszin,
My exploration of this…
There is a GitHub issue here:
Source code here:
Give it a moment to find the line when opening!
TGA Specifications here:
I suggest you download it to view.
I modified the source code and providing an example to “reverse” the image on loading (toggle between two choices with mouse press):
// Loading a TGA and selecting how to "reverse" image (2 choices)
// glv
// 2023-03-23
// Source code was was used on example.
//https://github.com/processing/processing/issues/1682
//https://github.com/DEAKSoftware/Truevision-TGA/blob/master/truevision-tga.pdf
//https://github.com/processing/processing4/blob/0c02c66f9e2fdf2fa13d796a473f4acc35154517/core/src/processing/core/PImage.java
PImage img0, img1, img2;
boolean toggle = true;
int org = 0;
String url;
void setup()
{
size(400, 200);
url = "http://learningprocessing.com/code/assets/sunflower.jpg"; //This can also be your image
img0 = loadImage(url);
// Save image with Processing
img0.save("/data/test.tga");
textSize(24);
textAlign(CENTER, CENTER);
toggle = !toggle;
// try\catch because myloadTGA() throws exception
try {
InputStream input = createInput("/data/test.tga");
img1 = myloadTGA(input);
}
catch(IOException e)
{
System.out.println("!");
}
noLoop();
}
void draw()
{
image(img0, 0, 0);
text("BEFORE", width/4, 10) ;
image(img1, 200, 0);
text("AFTER", 3*width/4, 10) ;
}
void mousePressed()
{
toggle = !toggle;
// try\catch because myloadTGA() throws exception
try {
InputStream input = createInput("/data/test.tga");
img1 = myloadTGA(input);
}
catch(IOException e)
{
System.out.println("!");
}
redraw();
}
//******************************************************************
// Source is:
//https://github.com/processing/processing4/blob/0c02c66f9e2fdf2fa13d796a473f4acc35154517/core/src/processing/core/PImage.java#L2806
// see //glv comments for changes
/**
* Targa image loader for RLE-compressed TGA files.
* <p>
* Rewritten for 0115 to read/write RLE-encoded targa images.
* For 0125, non-RLE encoded images are now supported, along with
* images whose y-order is reversed (which is standard for TGA files).
* <p>
* A version of this function is in MovieMaker.java. Any fixes here
* should be applied over in MovieMaker as well.
* <p>
* Known issue with RLE encoding and odd behavior in some apps:
* https://github.com/processing/processing/issues/2096
* Please help!
*/
PImage myloadTGA(InputStream input) throws IOException { // ignore
byte[] header = new byte[18];
int offset = 0;
do {
int count = input.read(header, offset, header.length - offset);
if (count == -1) return null;
offset += count;
} while (offset < 18);
/*
header[2] image type code
2 (0x02) - Uncompressed, RGB images.
3 (0x03) - Uncompressed, black and white images.
10 (0x0A) - Run-length encoded RGB images.
11 (0x0B) - Compressed, black and white images. (grayscale?)
header[16] is the bit depth (8, 24, 32)
header[17] image descriptor (packed bits)
0x20 is 32 = origin upper-left
0x28 is 32 + 8 = origin upper-left + 32 bits
7 6 5 4 3 2 1 0
128 64 32 16 8 4 2 1
*/
int format = 0;
if (((header[2] == 3) || (header[2] == 11)) && // B&W, plus RLE or not
(header[16] == 8) && // 8 bits
((header[17] == 0x8) || (header[17] == 0x28))) { // origin, 32 bit
format = ALPHA;
} else if (((header[2] == 2) || (header[2] == 10)) && // RGB, RLE or not
(header[16] == 24) && // 24 bits
((header[17] == 0x20) || (header[17] == 0))) { // origin
format = RGB;
} else if (((header[2] == 2) || (header[2] == 10)) &&
(header[16] == 32) &&
((header[17] == 0x8) || (header[17] == 0x28))) { // origin, 32
format = ARGB;
}
if (format == 0) {
System.err.println("Unknown .tga file format");
return null;
}
int w = ((header[13] & 0xff) << 8) + (header[12] & 0xff);
int h = ((header[15] & 0xff) << 8) + (header[14] & 0xff);
PImage outgoing = new PImage(w, h, format);
//********************************* glv edit *********************************
// where "reversed" means upper-left corner (normal for most of
// the modernized world, but "reversed" for the tga spec)
// https://github.com/processing/processing/issues/1682
boolean reversed = false;
if(toggle == true)
reversed = (header[17] & 0x20) != 0;
else if(toggle == false)
reversed = (header[17] & 0x20) == 0;
println("header[17] =", hex(header[17] & 0x20, 2), reversed);
//****************************************************************************
if ((header[2] == 2) || (header[2] == 3)) { // not RLE encoded
if (reversed) {
int index = (h-1) * w;
switch (format) {
case ALPHA:
for (int y = h-1; y >= 0; y--) {
for (int x = 0; x < w; x++) {
outgoing.pixels[index + x] = input.read();
}
index -= w;
}
break;
case RGB:
for (int y = h-1; y >= 0; y--) {
for (int x = 0; x < w; x++) {
outgoing.pixels[index + x] =
input.read() | (input.read() << 8) | (input.read() << 16) |
0xff000000;
}
index -= w;
}
break;
case ARGB:
for (int y = h-1; y >= 0; y--) {
for (int x = 0; x < w; x++) {
outgoing.pixels[index + x] =
input.read() | (input.read() << 8) | (input.read() << 16) |
(input.read() << 24);
}
index -= w;
}
}
} else { // not reversed
int count = w * h;
switch (format) {
case ALPHA:
for (int i = 0; i < count; i++) {
outgoing.pixels[i] = input.read();
}
break;
case RGB:
for (int i = 0; i < count; i++) {
outgoing.pixels[i] =
input.read() | (input.read() << 8) | (input.read() << 16) |
0xff000000;
}
break;
case ARGB:
for (int i = 0; i < count; i++) {
outgoing.pixels[i] =
input.read() | (input.read() << 8) | (input.read() << 16) |
(input.read() << 24);
}
break;
}
}
} else { // header[2] is 10 or 11
int index = 0;
int[] px = outgoing.pixels;
while (index < px.length) {
int num = input.read();
boolean isRLE = (num & 0x80) != 0;
if (isRLE) {
num -= 127; // (num & 0x7F) + 1
//********************************* glv edit *********************************
// glv This was giving me issues with errors; I suspect the IDE and NOT the code
//int pixel = 0;
//pixel = switch (format)
// {
// case ALPHA -> input.read();
// case RGB -> 0xFF000000 |
// input.read() | (input.read() << 8) | (input.read() << 16);
// case ARGB -> input.read() |
// (input.read() << 8) | (input.read() << 16) | (input.read() << 24);
// default -> 0;
// };
// glv This worked! Borrowed form Processing 3 source
int pixel = 0;
switch (format) {
case ALPHA:
pixel = input.read();
break;
case RGB:
pixel = 0xFF000000 |
input.read() | (input.read() << 8) | (input.read() << 16);
//(input.read() << 16) | (input.read() << 8) | input.read();
break;
case ARGB:
pixel = input.read() |
(input.read() << 8) | (input.read() << 16) | (input.read() << 24);
break;
}
//****************************************************************************
for (int i = 0; i < num; i++) {
px[index++] = pixel;
if (index == px.length) break;
}
} else { // write up to 127 bytes as uncompressed
num += 1;
switch (format) {
case ALPHA:
for (int i = 0; i < num; i++) {
px[index++] = input.read();
}
break;
case RGB:
for (int i = 0; i < num; i++) {
px[index++] = 0xFF000000 |
input.read() | (input.read() << 8) | (input.read() << 16);
//(input.read() << 16) | (input.read() << 8) | input.read();
}
break;
case ARGB:
for (int i = 0; i < num; i++) {
px[index++] = input.read() | //(input.read() << 24) |
(input.read() << 8) | (input.read() << 16) | (input.read() << 24);
//(input.read() << 16) | (input.read() << 8) | input.read();
}
break;
}
}
}
if (!reversed) {
int[] temp = new int[w];
for (int y = 0; y < h/2; y++) {
int z = (h-1) - y;
System.arraycopy(px, y*w, temp, 0, w);
System.arraycopy(px, z*w, px, y*w, w);
System.arraycopy(temp, 0, px, z*w, w);
}
}
}
input.close();
return outgoing;
}
This can easily be modified to make a custom method for your needs.
:)