|
Author
|
Give me all your tricks for minimizing jar file size (Read 72636 times)
|
Atem
Member
 
Posts: 61

|
i am creating memory images with uncompressed idat. I've tested it on nokia6630 and se630. it is working. i would recommend using them. it opens up creating procedural images of any type. like rotating images, zooming images, perspective images, warping images etc. as far as your phones memory handles them.
i go for it :-)
|
|
|
|
|
Logged
|
|
|
|
Glen
Guest
|
I've been messing with this for a few days, and I've come to the conclusion, you DONT need to compress the IDAT chunk at all. This will obviously speed up the loading of images, as the IDAT chunk can be created using raw uncompressed data. The code will then not have to go through the wasted compress / decompress cycle.
If works on all the phones I've tried, but I need to test on other phones to make sure they work ok.
If someone wants the png to try I can mail it to them. I'm not sure how to add attachments here.
glen at acepocket dot com
I'll stick a little demo together and put up a link to the jad / jar when I get a chance.
Glen.
|
|
|
|
|
Logged
|
|
|
|
Claymore
Global Moderator
J2me god
    
Posts: 4978

Quantity != Quality
|
That's certainly very interesting to know!
Should hopefully bring about some interesting techniques for handling images.
|
|
|
|
|
Logged
|
|
|
|
pascal
Global Moderator
J2me god
    
Posts: 3034
|
have you also tried it with multiple images? cause I'm kinda curious how the compression is within the jar.. it might actually be better then using many different png's (if you combine the raw png's in a single binary file that is)
interesting
|
|
|
|
|
Logged
|
|
|
|
Glen
Guest
|
Hi,
The uncompressed png's when stuck into a jar file come out quite a bit smaller than the same standard png's in the jar file.
I'm currently writing a class file which will dynamically create an image from raw data. I intend to let the community have it to play with. I should have it finished today. I'll post a link when I'm happy with it.
I'm still battling with learning j2me and OO programming in general. I tend to be an unruly C / ASM programmer and j2me and OOP is quite a lesson in writing tidy code. You wouldn't think I'd been coding games for 25 years 
Glen.
|
|
|
|
|
Logged
|
|
|
|
|
|
Atem
Member
 
Posts: 61

|
multiple images:
you don't need to put images with the same color palette into a large image. as this is not working on all devices, because the size of the largest possible images is different.
with memory created images, you can save the palette once and create multiple images with the same palette by just saving the idat of the png.
an other interesting options opens up: you can save images as big as you want. without worying about device types. if the image in the binary is too large for the device, the loader can split up the idat and create several images out of it. you can change your sprite-class to accomplish drawing several images instead of one
|
|
|
|
|
Logged
|
|
|
|
Glen
Full Member
  
Posts: 106

I'm not a llama!
|
Hi,
Ok, its complete and appear to work.
Please feel free to comment or improve the code.
// Dynamic PNG Creator // Author : Glen Cook // Version : 1.0 Beta // Date : 17th June 2005 // Company : AcePocket // Email : glen (at) acepocket (dot) com
// Usage // width = width of image // height = height of image // palette = number of colours in raw palette data // idata = byte array of 1 byte per pixel indexed image data // pal = byte array of RGB triplets // trans = palette index for transparency colour, -1 for no transparency // flips = 0-Normal 1:X-Flip 2:Y-Flip 3:XY-Flip // palOffset = offset to start of palette within byte array // imgOffset = offset to start of image within byte array
import javax.microedition.lcdui.*;
class Png { int[] crc_table; int dataptr; int crcfrom; int pngsize; byte[] data; byte[] PNG_header={ -119,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a, 0x00,0x00,0x00,0x0d,0x49,0x48,0x44,0x52, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x08,0x03,0x00,0x00,0x00}; byte[] PNG_End={0x00,0x00,0x00,0x00,0x49,0x45,0x4e,0x44,-82, 0x42,0x60,-126};
Image create(int width, int height, int palette, byte[] idata, byte[] pal, int trans, int flips, int palOffset, int imgOffset) { int i; pngsize = palette*3 + (width+1)*height + 80; if(trans>-1) pngsize += 13 + trans; data = new byte[pngsize]; make_crc_table (); // Create PNG Header for(i=0;i<PNG_header.length;i++) data[i] = PNG_header[i]; data[19] = (byte)width; data[23] = (byte)height; dataptr=29; writeInt(calc_crc(data, 12, 17)); writeInt(palette*3); crcfrom=dataptr; writeHeader("PLTE"); for(i=0;i<palette*3;i++) data[dataptr++]=pal[i+palOffset]; writeInt(calc_crc(data, crcfrom, dataptr - crcfrom)); if(trans>-1) { // Write tRNS Length writeInt(trans+1); // Mark start of CRC check crcfrom=dataptr; // Write tRNS Chunk text writeHeader("tRNS"); // Create Transparency data for(i=0;i<trans;i++) data[dataptr++]=(byte)0xff; // Mark the transparent palette data[dataptr++]=0; // Calc CRC on tRNS Block writeInt(calc_crc(data, crcfrom, dataptr - crcfrom)); } int compsize = (width+1) * height; // Write IDAT Length writeInt(compsize+11); crcfrom=dataptr; // Write IDAT Chunk text writeHeader("IDAT"); data[dataptr++]=(byte)0x78; // PNG compression flags data[dataptr++]=(byte)0xda; // PNG compression flags data[dataptr++]=(byte)0x01; // PNG final block / No compression
// Write data length (little endian) data[dataptr++]=(byte)(compsize & 0xff); data[dataptr++]=(byte)((compsize>>>8) & 0xff); // Write inverse length data[dataptr]=(byte)(~data[dataptr-2]); dataptr++; data[dataptr]=(byte)(~data[dataptr-2]); dataptr++; int adlerstart = dataptr; switch(flips) { case 0:
i=imgOffset; for(int y=0;y<height;y++) { data[dataptr++]=0; // No Filter flag for each line
for(int x=0;x<width;x++) data[dataptr++]=idata[i++]; } break; case 1: for(int y=0;y<height;y++) { i=(y+1)*width-1+imgOffset; data[dataptr++]=0; // No Filter flag for each line for(int x=0;x<width;x++) data[dataptr++]=idata[i--]; } break; case 2: for(int y=0;y<height;y++) { i=(height-y-1)*width+imgOffset; data[dataptr++]=0; // No Filter flag for each line for(int x=0;x<width;x++) data[dataptr++]=idata[i++]; } break; case 3: for(int y=0;y<height;y++) { i=(height-y)*width-1+imgOffset; data[dataptr++]=0; // No Filter flag for each line for(int x=0;x<width;x++) data[dataptr++]=idata[i--]; } break; } int adler1=1; int adler2=0;
for(i=0;i<compsize;i++) { adler1=adler1+((int)data[adlerstart+i]&0xff); adler2=adler1+adler2; adler1%=65521; adler2%=65521; } data[dataptr++]=(byte)((adler2>>8)&0xff); data[dataptr++]=(byte)(adler2&0xff); data[dataptr++]=(byte)((adler1>>8)&0xff); data[dataptr++]=(byte)(adler1&0xff); writeInt(calc_crc(data, crcfrom, dataptr - crcfrom)); // Finish off the PNG with the IEND chunk for(i=0;i<PNG_End.length;i++) data[dataptr++] = PNG_End[i]; return Image.createImage (data,0, dataptr); } void writeInt(long crc) { data[dataptr++]=(byte)((crc>>>24)&255); data[dataptr++]=(byte)((crc>>>16)&255); data[dataptr++]=(byte)((crc>>>8)&255); data[dataptr++]=(byte)(crc&255); } void writeHeader(String t) { for(int i=0;i<4;i++) data[dataptr++]=(byte)t.charAt (i); } void make_crc_table () { crc_table = new int[256]; for (int n = 0; n < 256; n++) { int c = n; for (int k = 8; --k >= 0; ) { if ((c & 1) != 0) c = 0xedb88320 ^ (c >>> 1); else c = c >>> 1; } crc_table[n] = c; } } long calc_crc (byte[] buf, int off, int len) { int c = ~0; while (--len >= 0) c = crc_table[(c ^ buf[off++]) & 0xff] ^ (c >>> 8); return (long) ~c & 0xffffffffL; } }
|
|
|
|
« Last Edit: June 18, 2005, 03:50:05 pm by Glen »
|
Logged
|
|
|
|
Glen
Full Member
  
Posts: 106

I'm not a llama!
|
The code above seems to be working fine.
I recommend moving the create_crc_table into a constructor and then setting data to null before returning from the function.
No comments yet though, has anyone tried it yet?
Glen.
|
|
|
|
|
Logged
|
|
|
|
pascal
Global Moderator
J2me god
    
Posts: 3034
|
haven't tried it yet (no project code to try it in at the moment) .. will try it once I have some actual code around it
|
|
|
|
|
Logged
|
|
|
|
Wex Viator
Full Member
  
Posts: 157

Rather mad
|
Maybe this compression lark should be made into a competition! Given a standard set of game-type images (splash screen / menus / avatars / maps / icons) see who can get it down as small as possible. 
There's also the uncompression / rebuilding speed-factor though. Which is very important.
The game we're currently developing is weighing in at 140K! It's very graphical... even with a lot of the tips that have been mentioned here getting it below 100K might be tough.
I'm currently putting together a number of different crush-methods, if I find something incredible then I'll let you guys know. For now, here's some possible interesting data on just-PNGs-in-a-JAR testing:
Individual compressed PNGs: Jar size is 17.2K Individual uncompressed PNGs: Jar size is 16.7K Binaray-type file using compressed PNGs: Jar size is 11.2K Binaray-type file using uncompressed PNGs: Jar size is 7.62K
The PNGs I'm compressing are 19 frames of a character avatar. These values basically just enforce what's been said/debated already, but facts are always nice .
Something I'm pondering is when saving the frames, it might well be better to just save the changes from the previous frame. This would hopefully result in a lot of zero-bytes where no change has occured... anyone tried anything like this? This wouldn't help with images that are very different, or varying in size, but for animations...
Another thing that comes to mind is loading in different palettes to make the same data appear totally different. Something some modders (like myself) did in GTA1 to include multiple car skins in one car file:
 
Note: these aren't mine, but good examples of how altering the palette can give more effects than just changing blue to red, so long as it's done right. Figured it's worth mentioning.
|
|
|
|
|
Logged
|
|
|
|
loppy
Senior. Member
   
Posts: 351

Clarity Games
|
Is there any such concept in J2ME as palettes? It would be great to be able to manipulate the colours of a canvas and would give developers a lot of options for reducing the amount of memory needed for graphics e.g. animation through manipulation of a canvas palette as opposed to using a range of images. But I would've thought that palettes would've been too device specific to have been implemented in J2ME.
|
|
|
|
|
Logged
|
|
|
|
Claymore
Global Moderator
J2me god
    
Posts: 4978

Quantity != Quality
|
I am assuming you mean palettes like the ones used in GBAs? No, they don't exist in that sense.
|
|
|
|
|
Logged
|
|
|
|
Wex Viator
Full Member
  
Posts: 157

Rather mad
|
Unfourtunately you can only manipulate the palette at the creation time of the image, not directly on the fly. Same goes for any of the image data.
|
|
|
|
|
Logged
|
|
|
|
tsmith
New user

Posts: 26

w00t
|
Wow, this discussion has really taken off!
To emphasize what has been said earlier: DEFLATE supports uncompressed streams, and the PNG format supports DEFLATE, so any PNG-conformant phone (which means all J2ME phones) can read PNGs with uncompressed streams via the Image.createImage(byte [], int, int) method.
I used this technique in a game that is currently on deck on 80+ handsets, so I can assure you that no phones have any trouble with it 
A tip when writing your PNG generating code: write a command line version as well (i.e. as a regular java app) and check its output with pngcheck. Pngcheck is very good for debugging PNGs.
Cheers, Tim
|
|
|
|
|
Logged
|
|
|
|
|