We closed this forum 18 June 2010. It has served us well since 2005 as the ALPHA forum did before it from 2002 to 2005. New discussions are ongoing at the new URL http://forum.processing.org. You'll need to sign up and get a new user account. We're sorry about that inconvenience, but we think it's better in the long run. The content on this forum will remain online.
IndexProgramming Questions & HelpPrograms › RLE compression for TGA output
Page Index Toggle Pages: 1
RLE compression for TGA output (Read 2146 times)
RLE compression for TGA output
Jun 8th, 2005, 7:57am
 
Is it possible to enable RLE compression for TGA (TARGA) images saved with the save() or saveFrame() methods?

thanks,
jared
Re: RLE compression for TGA output
Reply #1 - Jun 8th, 2005, 5:09pm
 
not currently. the java 1.4 imageio stuff would probably do it (or write to another compressed format), but would require a little hacking around to get the pixels and write to the file. i think elsewhere on the board there was info about this (or maybe on the alpha board).
Re: RLE compression for TGA output
Reply #2 - Jun 8th, 2005, 5:36pm
 
RLE compression, huh! Smiley after a quick consultation of this Targa spec i modified the code to use this simple type of compression. because the maximum chunk length is only 128 pixels, don't expect a huge compression ratio even  if your image uses large regions with flat colours:

Code:
void setup() {
size(200,200);
PImage img=loadImage("ndsm.jpg");
println(saveTGA_RLE(img,"tga0.tga"));
}

boolean saveTGA_RLE(PImage img, String fileName) {
try {
int[] pixels=img.pixels;
int width=img.width;
int height=img.height;
OutputStream output = new BufferedOutputStream(
new FileOutputStream(fileName),
32768
);

byte header[] = new byte[18];

// set file header info (18 bytes)

// RGB + RLE Compression
header[2] = 0x02 + 0x08;
header[12] = (byte) (width & 0xff);
header[13] = (byte) (width >> 8);
header[14] = (byte) (height & 0xff);
header[15] = (byte) (height >> 8);
header[16] = 32; // bits per pixel
header[17] = 0x08 + 0x20; // bits per colour component + origin at top-left

output.write(header);

int maxLen = height * width;
int index = 0;
int col, prevCol;
int[] currChunk = new int[128];

while (index < maxLen) {
boolean isRLE = false;
currChunk[0] = col = pixels[index];
int rle = 1;
// try to find repeating bytes (min. len = 2 pixels)
// maximum chunk size is 128 pixels
while (index + rle < maxLen) {
if (col != pixels[index + rle] || rle == 128) {
isRLE = (rle > 1); // set flag for RLE chunk
break;
}
rle++;
}
// write compressed chunk (RLE+colour)
if (isRLE) {
output.write(128 | (rle - 1));
output.write(col & 0xff);
output.write(col >> 8 & 0xff);
output.write(col >> 16 & 0xff);
output.write(col >>> 24 & 0xff);
}
else {
// build uncompressed chunk
rle = 1; //reset scan offset
while (index + rle < maxLen) {
if ((col != pixels[index + rle] && rle < 128) || rle < 3) {
currChunk[rle] = col = pixels[index + rle];
}
else {
// check if the exit condition was the start of a repeating colour
if (col == pixels[index + rle]) rle -= 2;
break;
}
rle++;
}
// write uncompressed chunk
output.write(rle - 1);
for (int i = 0; i < rle; i++) {
col = currChunk[i];
output.write(col & 0xff);
output.write(col >> 8 & 0xff);
output.write(col >> 16 & 0xff);
output.write(col >>> 24 & 0xff);
}
}
// update scan offset
index += rle;
}
output.flush();
return true;
}
catch (IOException e) {
e.printStackTrace();
return false;
}
}


will write to ben about updating this directly in PImage...
Re: RLE compression for TGA output
Reply #3 - Jun 8th, 2005, 6:35pm
 
bless you toxi. i dug up the tga spec to take a look and found some general code to do it, but that's as far as i got.

any downside to this being the default when saving .tga?
Re: RLE compression for TGA output
Reply #4 - Jun 8th, 2005, 7:07pm
 
no real downside i know of. it is marginally slower, but the saving in terms of filesize can be quite big (depending on how "busy" the image is). for example the image below gets compressed to 61% (385KB vs. 640K) i have tested reading the exported images with photoshop and QT and they're doing just fine. i'll send you the updated PImage in a minute and you can take it from there! Smiley

...
Re: RLE compression for TGA output
Reply #5 - Jun 8th, 2005, 11:13pm
 
now integrated for rev 92.. toxi is the hero.
Re: RLE compression for TGA output
Reply #6 - Jun 9th, 2005, 1:56am
 
Props to toxi! My hard drive is gonna thank you!
Re: RLE compression for TGA output
Reply #7 - Jun 9th, 2005, 11:01pm
 
Incredible!  Thank you so much Toxi.

jared

Re: RLE compression for TGA output
Reply #8 - Jun 10th, 2005, 1:07pm
 
pleasure! btw. if you're out to save even more filesize and don't require the alpha channel, apply the patches below to only save 24bit images (and so saving yet another 25%)

1) change the lines:
Code:
header[16] = 32; // bits per pixel
header[17] = 0x08 + 0x20;

into:
Code:
header[16] = 24; // bits per pixel
header[17] = 0x00 + 0x20; // no alpha channel bits

2) remove/comment out both instances of this line:
Code:
output.write(col >>> 24 & 0xff); 


good luck!
Re: RLE compression for TGA output
Reply #9 - Jun 10th, 2005, 4:30pm
 
now integrated into PImage so it'll be all set for 92 (or anyone who's using the cvs version)
Re: RLE compression for TGA output
Reply #10 - Jun 10th, 2005, 4:41pm
 
in fact, is there a simple way to do 8 bit so we can save ALPHA format images too (or save things as grayscale?)
Re: RLE compression for TGA output
Reply #11 - Jun 10th, 2005, 4:44pm
 
inspecting now... shouldn't be too hard for grayscale, but will have to pass for proper colour quantization and palette creation!
Re: RLE compression for TGA output
Reply #12 - Jun 10th, 2005, 6:21pm
 
oh no, nothing that complex.. i just mean when we have a single channel image, is it just a matter of a couple of bits? let's make the assumption that the data is already 8 bit (grayscale or an alpha channel, say) can that be written just as simply?

it was simply that i realized that we have 3 image types, and right now the save() command in PImage is assuming everything is ARGB, so you're gonna get some weird results if you (for some reason) try to save an ALPHA format image, or try to write an RGB image that doesn't have its alpha channel set opaque (often the case). so the 24 bit patch fixes that part, was just wondering if there's a similar 8 bit patch.
Re: RLE compression for TGA output
Reply #13 - Jun 10th, 2005, 7:54pm
 
understood! though i'm not sure if always exporting w/o alpha channel is quite right. the nice thing about Targa is that you did get hold of the alpha... i just found another more detailed format spec and there's another mode mentioned, specifically intended for 8bit grayscale. yeah!

so here's an updated version which takes into account the format of a PImage object and uses the appropriate bit depth to write the TGA file:

Code:
void setup() {
size(400,400);
background(255,0,128);
for(int x=0; x<width; x+=5) {
stroke(255.0*x/400.0);
line(x,0,width,height);
line(0,height,width-x,height-x);
}
loadPixels();
// try one of those...
g.format=ARGB;
//g.format=RGB;
//g.format=ALPHA;
saveTGA_RLE(g,"targa_test.tga");
}

// new version to takes image format setting into account
// ALPHA images written as 8bit grayscale (uses lowest byte)
// RGB -> 24bits
// RGBA -> 32bits
// all versions are RLE compressed

boolean saveTGA_RLE(PImage img, String fileName) {
try {
int[] pixels=img.pixels;
int width=img.width;
int height=img.height;
OutputStream output = new BufferedOutputStream(
new FileOutputStream(fileName),
32768
);

byte header[] = new byte[18];

// save ALPHA images as 8bit grayscale
if (ALPHA==img.format) {
header[2] = 0x0B;
header[16] = 0x08;
header[17] = 0x28;
}
else {
header[2] = 0x0A;
// use 32bit colour for ARGB
// else 24bit for RGB formatted images
if (ARGB==img.format) {
header[16] = 32;
header[17] = 0x28;
}
else {
header[16] = 24;
header[17] = 0x20;
}
}
// set image dimensions lo-hi byte order
header[12] = (byte) (width & 0xff);
header[13] = (byte) (width >> 8);
header[14] = (byte) (height & 0xff);
header[15] = (byte) (height >> 8);

output.write(header);

int maxLen = height * width;
int index = 0;
int col, prevCol;
int[] currChunk = new int[128];

// 8bit image exporter is in separate loop
// to avoid excessive conditionals...
if (ALPHA==img.format) {
while (index < maxLen) {
boolean isRLE = false;
int rle = 1;
currChunk[0] = col = pixels[index] & 0xff;
while (index + rle < maxLen) {
if (col != (pixels[index + rle]&0xff) || rle == 128) {
isRLE = (rle > 1);
break;
}
rle++;
}
if (isRLE) {
output.write(0x80 | (rle - 1));
output.write(col);
}
else {
rle = 1;
while (index + rle < maxLen) {
int cscan=pixels[index + rle]&0xff;
if ((col != cscan && rle < 128) || rle < 3) {
currChunk[rle] = col = cscan;
}
else {
if (col == cscan) rle -= 2;
break;
}
rle++;
}
output.write(rle - 1);
for (int i = 0; i < rle; i++) output.write(currChunk[i]);
}
index += rle;
}
}
else {
// export 24/32 bit TARGA
while (index < maxLen) {
boolean isRLE = false;
currChunk[0] = col = pixels[index];
int rle = 1;
// try to find repeating bytes (min. len = 2 pixels)
// maximum chunk size is 128 pixels
while (index + rle < maxLen) {
if (col != pixels[index + rle] || rle == 128) {
isRLE = (rle > 1); // set flag for RLE chunk
break;
}
rle++;
}
if (isRLE) {
output.write(128 | (rle - 1));
output.write(col & 0xff);
output.write(col >> 8 & 0xff);
output.write(col >> 16 & 0xff);
if (ARGB==img.format) output.write(col >>> 24 & 0xff);
}
else {
rle = 1;
while (index + rle < maxLen) {
if ((col != pixels[index + rle] && rle < 128) || rle < 3) {
currChunk[rle] = col = pixels[index + rle];
}
else {
// check if the exit condition was the start of a repeating colour
if (col == pixels[index + rle]) rle -= 2;
break;
}
rle++;
}
// write uncompressed chunk
output.write(rle - 1);
if (ARGB==img.format) {
for (int i = 0; i < rle; i++) {
col = currChunk[i];
output.write(col & 0xff);
output.write(col >> 8 & 0xff);
output.write(col >> 16 & 0xff);
output.write(col >>> 24 & 0xff);
}
}
else {
for (int i = 0; i < rle; i++) {
col = currChunk[i];
output.write(col & 0xff);
output.write(col >> 8 & 0xff);
output.write(col >> 16 & 0xff);
}
}
}
index += rle;
}
}

output.flush();
return true;
}
catch (IOException e) {
e.printStackTrace();
return false;
}
}


good weekend all! :)
Re: RLE compression for TGA output
Reply #14 - Jun 10th, 2005, 9:45pm
 
right, in cvs i had it flipping between RGB and ARGB as necessary, but in doing so had realized that we don't have a good option for single channel (format == ALPHA, or maybe grayscale if we ever support that).

but hey, this is even easier and i'll just apply it. Wink
Page Index Toggle Pages: 1