Java-Gaming.org    
Featured games (81)
games approved by the League of Dukes
Games in Showcase (487)
Games in Android Showcase (112)
games submitted by our members
Games in WIP (553)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  Easy readable PNG decoder  (Read 11118 times)
0 Members and 1 Guest are viewing this topic.
Offline DzzD
« Posted 2011-05-09 21:37:06 »

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
152  
153  
154  
155  
156  
157  
158  
159  
160  
161  
162  
163  
164  
165  
166  
167  
168  
169  
170  
171  
172  
173  
174  
175  
176  
177  
178  
179  
180  
181  
182  
183  
184  
185  
186  
187  
188  
189  
190  
191  
192  
193  
194  
195  
196  
197  
198  
199  
200  
201  
202  
203  
204  
205  
206  
207  
208  
209  
210  
211  
212  
213  
214  
215  
216  
217  
218  
219  
220  
221  
222  
223  
224  
225  
226  
227  
228  
229  
230  
231  
232  
233  
234  
235  
236  
237  
238  
239  
240  
241  
242  
243  
244  
245  
246  
247  
248  
249  
250  
251  
252  
253  
254  
255  
256  
257  
258  
259  
260  
261  
262  
263  
264  
265  
266  
267  
268  
269  
270  
271  
272  
273  
274  
275  
276  
277  
278  
279  
280  
281  
282  
283  
284  
285  
286  
287  
288  
289  
290  
291  
292  
293  
294  
295  
296  
297  
298  
299  
300  
301  
302  
303  
304  
305  
306  
307  
308  
309  
310  
311  
312  
313  
314  
315  
316  
317  
318  
319  
320  
321  
322  
323  
324  
325  
326  
327  
328  
329  
330  
331  
332  
333  
334  
335  
336  
337  
338  
339  
340  
341  
342  
343  
344  
345  
346  
347  
348  
349  
350  
351  
352  
353  
354  
355  
356  
357  
358  
359  
360  
361  
362  
363  
364  
365  
366  
367  
368  
369  
370  
371  
372  
373  
374  
375  
376  
377  
378  
379  
380  
381  
382  
383  
384  
385  
386  
387  
388  
389  
390  
391  
392  
393  
394  
395  
396  
397  
398  
399  
400  
401  
402  
403  
404  
405  
406  
407  
408  
409  
410  
411  
412  
413  
414  
415  
416  
417  
418  
419  
420  
421  
422  
423  
424  
425  
426  
427  
428  
429  
430  
431  
432  
433  
434  
435  
436  
437  
438  
439  
440  
441  
442  
443  
444  
445  
446  
447  
448  
449  
450  
451  
452  
453  
454  
455  
456  
457  
458  
459  
460  
461  
462  
463  
464  
465  
466  
467  
468  
469  
470  
471  
472  
473  
474  
475  
476  
477  
478  
479  
480  
481  
482  
483  
484  
485  
486  
487  
488  
489  
490  
491  
492  
493  
494  
495  
496  
497  
498  
499  
500  
501  
502  
503  
504  
505  
506  
507  
508  
509  
510  
511  
512  
513  
514  
515  
516  
517  
518  
519  
520  
521  
522  
523  
524  
525  
526  
527  
528  
529  
530  
531  
532  
533  
534  
535  
536  
537  
538  
539  
540  
541  
542  
543  
544  
545  
546  
547  
548  
549  
550  
551  
552  
553  
554  
555  
556  
557  
558  
559  
560  
561  
562  
563  
564  
565  
566  
567  
568  
569  
570  
571  
572  
573  
574  
575  
576  
577  
578  
579  
580  
581  
582  
583  
584  
585  
586  
587  
588  
589  
590  
591  
592  
593  
594  
595  
596  
597  
598  
599  
600  
601  
602  
603  
604  
605  
606  
607  
608  
609  
610  
611  
612  
613  
614  
615  
616  
617  
618  
619  
620  
621  
622  
623  
624  
625  
626  
627  
628  
public class PNGDecoder
{
  /**
  * Inpute stream for raw PNG data
  */

    private InputStream in;
   
     
    /**
     * Define PNG file SIG
     */

    public static final byte[] PNG_STREAM_SIG = new byte[] {-119, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13};
     
     
    /**
     * Constant for color type
     */

    public final static int COLOR_GREY=0;
    public final static int COLOR_TRUE=2;
    public final static int COLOR_INDEX=3;
    public final static int COLOR_GREY_ALPHA=4;
    public final static int COLOR_TRUE_ALPHA=6;
   
   
    /**
     * filter type
     */

    public final static int FILTER_NONE=0;
    public final static int FILTER_SUB=1;
    public final static int FILTER_UP=2;
    public final static int FILTER_AVG=3;
    public final static int FILTER_PAETH=4;    
   
   
    /**
     * Define critical chunk
     */

    public final static int CHUNK_IHDR = bytesToInt("IHDR".getBytes());
    public final static int CHUNK_PLTE = 1347179589;//bytesToInt("PLTE".getBytes());
   public final static int CHUNK_IDAT = 1229209940;//bytesToInt("IDAT".getBytes());
   public final static int CHUNK_IEND = bytesToInt("IEND".getBytes());
   
    //@TODO: we may add extended chunk
   
     //Log.log("CHUNK_IEND="+CHUNK_IEND); //Uncomment to get a chunk int value to use in switch...case statement


    /**
     * Constructs a PNGDecoder object.
     *
     * @param in input stream to read PNG image from.
     */
   
    public PNGDecoder(InputStream in)
    {
        this.in = in;
    }
   
   
   /**
    * Reads a byte from input stream.
    *
    * @return byte read
    */

    private byte read() throws IOException
    {
        byte b = (byte)in.read();
        return(b);
    }


   /**
    * Reads a int from input stream/
    *
    * @return int read
    */

    private int readInt() throws IOException
    {
        byte b[] = read(4);
        return(((b[0]&0xff)<<24) +
               ((b[1]&0xff)<<16) +
               ((b[2]&0xff)<<8) +
               ((b[3]&0xff)));
    }


   /**
    * Reads n bytes from input stream.
    *
    * @param count number of bytes to read
    *
    * @return bytes array with values read
    */

    private byte[] read(int count) throws IOException
    {
        byte[] result = new byte[count];
        for(int i = 0; i < count; i++)
        {
            result[i] = read();
        }
        return result;
    }
   

   /**
    * Compare two bytes array
    *
    * @param b1 input byte array 1
    * @param b2 input byte array 2
    *
    * @return true if each entries of arrays are equals or false if array differs
    */

    private boolean compare(byte[] b1, byte[] b2)
    {
        if(b1.length != b2.length)
        {
            return false;
        }
       
        for(int i = 0; i < b1.length; i++)
        {
            if(b1[i] != b2[i])
            {
                return false;
            }
        }
        return true;
    }
 
   
    /**
    * Convert 4 bytes array to int
    */

   private static int bytesToInt(byte[] b)
   {
      int r=b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3];
         
      return r;
   }
   
   
    /**
    * Convert int to a 4 character string
    */

   private static String intToString(int i)
   {
      byte b[]=new byte[4];
      b[0]=(byte)(i>>24);
      b[1]=(byte)(i>>16);
      b[2]=(byte)(i>>8);
      b[3]=(byte)(i);
      return new String(b);
   }  

   /**
    * Skip the requested numbers of bytes from the input stream
    *
    * @param bytes number of bytes to skip
    */

   public void skipFully(long bytes) throws IOException
   {
      while(bytes > 0)
      {
         long skipped = in.skip(bytes);
         if(skipped <= 0)
          throw new EOFException();
         bytes -= skipped;
      }
   }
   
   /**
    * Decodes image from the input stream
    *
    * @return Image object
    */

    public Image decodeImage() throws IOException
     {
        RGBArrayImage rgb=this.decodeRGBArrayImage();
        rgb.updateMIS();
        return rgb.getImage();
       
     }
   
    /**
     * Decodes image from the input stream.
     *
     * @return RGBArrayImage object
     */

    public RGBArrayImage decodeRGBArrayImage() throws IOException
    {
       /**
        * Read PNG SIG
        */

      byte sig[] = read(12);
     
      if(!compare(sig, PNG_STREAM_SIG))
         throw(new RuntimeException("Wrong SIG ID " + new String(sig)));
     
      /**
       * Read PNG HEADER
       */

      int chunkIdHeader=readInt();
       
      if(chunkIdHeader!=CHUNK_IHDR)
         throw(new RuntimeException("Wrong HEADER ID " + intToString(chunkIdHeader)));
         
      int width = readInt();
        int height = readInt();  
        int bitDepth = read();
        int colorType = read();
        int compressionMethod = read();
        int filterMethod = read();
        int interlaceMethod = read();
       
        //Read CRC => may verify here
       int crc=readInt();
       
         
        Inflater inflater = new Inflater();
        int chunkId;
        int totalImageSize=(width*height*8)/bitDepth;
       
        /*
         * Prepare buffer for uncompressed image data
         */

      switch(colorType)
      {
         case COLOR_GREY_ALPHA :
            totalImageSize*=2;
         break;
         
         case COLOR_TRUE :
            totalImageSize*=3;
         break;
         
         case COLOR_TRUE_ALPHA :
            totalImageSize*=4;
         break;
      }
        byte imageData[]=new byte[totalImageSize+height];   //Add height for the filter type value at each scanline
       
       
       
        /**
         * Now we wil read all other chunks in a loop ( and ignoring extended or unkwnow chunk )
         */

        do
        {
           int size=readInt();   //Read chunk size
          chunkId=readInt();   //Read chunk ID
         
           
           //Decode chunk data
          switch(chunkId)
           {
              /**
               * Skip PLTE chunk
               */

              case CHUNK_PLTE :
                 //@TODO: to be implemented
                skipFully(size);
              break;
             
              /**
               * IDAT Chunk (may be repeated multiple times, but should be consecutive, pff... why for ? )
               */

               case CHUNK_IDAT :
                  byte b[]=new byte[size];
                  in.read(b);  
                  inflater.setInput(b);
                  try
                  {
                  inflater.inflate(imageData,inflater.getTotalOut(),imageData.length-inflater.getTotalOut());
               }
               catch(DataFormatException dfe)
               {
                  throw new IOException(dfe.getMessage());
               }
               break;
             
              /**
               * Skip unknow chunk
               */

              default :
                 skipFully(size);
           }
           
           
           //Read CRC => @TODO: We may verify CRC of current chunk here
          crc=readInt();
        }
        while(chunkId!=CHUNK_IEND);
       
       
        /**
         * And now it is time to decode & unfilter the image
         */

         RGBArrayImage result;
         if(colorType==COLOR_GREY_ALPHA || colorType==COLOR_TRUE_ALPHA)
            result=new RGBArrayImage(width, height,true);
         else
            result=new RGBArrayImage(width, height,false);
           
         int srcByte=0;
         int dstPixel=0;
         int filterType;
         int pixels[]=result.getPixels();
         
         switch(colorType)
         {
            case COLOR_INDEX :
              //@TODO: to be implemented
              Log.log("Decoding of this color type is not yet implemented");
            break;
           
            case COLOR_GREY :
              for(int y=0;y<height;y++)
               {
                 filterType=imageData[srcByte++];
                  for(int x=0;x<width;x++)
                  {
                     int grey=imageData[srcByte]&0xFF;
                     grey|=(grey<<8) | (grey<<16);
                     pixels[dstPixel++]=grey;
                     srcByte+=1;
                  }
                  filter(filterType,pixels,y,width);
               }
            break;
           
            case COLOR_GREY_ALPHA :
              for(int y=0;y<height;y++)
               {
                 filterType=imageData[srcByte++];
                  for(int x=0;x<width;x++)
                  {
                     int grey=imageData[srcByte]&0xFF;
                     grey|=(grey<<8) | (grey<<16);
                     int alpha=(imageData[srcByte+1]&0xFF)<<24;
                     pixels[dstPixel++]=grey | alpha;
                     srcByte+=2;
                  }
                  filter(filterType,pixels,y,width);
               }
            break;
           
            case COLOR_TRUE :
               for(int y=0;y<height;y++)
               {
                 filterType=imageData[srcByte++];
                  for(int x=0;x<width;x++)
                  {
                     pixels[dstPixel++]=((imageData[srcByte]&0xFF)<<16) | ((imageData[srcByte+1]&0xFF)<<8) | (imageData[srcByte+2]&0xFF);
                     srcByte+=3;
                  }
                  filter(filterType,pixels,y,width);
               }
            break;
           
            case COLOR_TRUE_ALPHA :
              for(int y=0;y<height;y++)
              {
                 filterType=imageData[srcByte++];
                  for(int x=0;x<width;x++)
                  {
                     pixels[dstPixel++]=((imageData[srcByte]&0xFF)<<16) | ((imageData[srcByte+1]&0xFF)<<8) | (imageData[srcByte+2]&0xFF) | ((imageData[srcByte+3]&0xFF)<<24);
                     srcByte+=4;
                  }
                  filter(filterType,pixels,y,width);
              }
            break;            
           
            default :
               Log.log("Decoding of this color type is not yet implemented and unknown...");
            break;
           
           
         }
     
        return result;
    }
   
   
    /**
     * Unfilter a PNG image row.
     *
     * @param filterType filter type for this row
     * @param pixels pixels array of the image
     * @param y line number to unfilter
     * @param width width of the row
     */

    private void filter(int filterType,int pixels[],int y,int width)
    {
       int ofsRow=y*width;
      /**
       * Perform filtering if any
       */

      switch(filterType)
      {
         case FILTER_NONE:
         break;
         case FILTER_SUB:
         {
            int AA=0;
            int RA=0;
            int GA=0;
            int BA=0;
            for(int n=0;n<width;n++)
            {
               int pixel=pixels[ofsRow+n];
               int A=(pixel>>24)&0xFF;
               int R=(pixel>>16)&0xFF;
               int G=(pixel>>8)&0xFF;
               int B=pixel&0xFF;
               
               A+=AA;
               R+=RA;
               G+=GA;
               B+=BA;
               
               A&=0xFF;
               R&=0xFF;
               G&=0xFF;
               B&=0xFF;
               
               pixels[ofsRow+n]=(A<<24) |  (R<<16) | (G<<8) | B;
               
               AA=A;
               RA=R;
               GA=G;
               BA=B;
            }
         }
         break;
         case FILTER_UP:
         {
            int ofsRowB=ofsRow-width;
            for(int n=0;n<width;n++)
            {
               int pixelB=0;
               
               if(y>0)
                  pixelB=pixels[ofsRowB+n];
               
               int AB=(pixelB>>24)&0xFF;
               int RB=(pixelB>>16)&0xFF;
               int GB=(pixelB>>8)&0xFF;
               int BB=pixelB&0xFF;
               
               int pixel=pixels[ofsRow+n];
               int A=(pixel>>24)&0xFF;
               int R=(pixel>>16)&0xFF;
               int G=(pixel>>8)&0xFF;
               int B=pixel&0xFF;
               
               A+=AB;
               R+=RB;
               G+=GB;
               B+=BB;
               
               A&=0xFF;
               R&=0xFF;
               G&=0xFF;
               B&=0xFF;
               
               pixels[ofsRow+n]=(A<<24) | (R<<16) | (G<<8) | B;
            }
         }
         break;
         case FILTER_AVG:
         {
            int AA=0;
            int RA=0;
            int GA=0;
            int BA=0;
           
         
            int ofsRowB=ofsRow-width;
            for(int n=0;n<width;n++)
            {
               int pixelB=0;
               if(y>0)
                  pixelB=pixels[ofsRowB+n];
               int AB=(pixelB>>24)&0xFF;
               int RB=(pixelB>>16)&0xFF;
               int GB=(pixelB>>8)&0xFF;
               int BB=pixelB&0xFF;
               
               int pixel=pixels[ofsRow+n];
               int A=(pixel>>24)&0xFF;
               int R=(pixel>>16)&0xFF;
               int G=(pixel>>8)&0xFF;
               int B=pixel&0xFF;
               
               A+=(AB+AA)>>1;
               R+=(RB+RA)>>1;
               G+=(GB+GA)>>1;
               B+=(BB+BA)>>1;
               
               A&=0xFF;
               R&=0xFF;
               G&=0xFF;
               B&=0xFF;
               
               pixels[ofsRow+n]=(A<<24) |(R<<16) | (G<<8) | B;
               
               AA=A;
               RA=R;
               GA=G;
               BA=B;
            }
         }
         break;
         case FILTER_PAETH:
         {
            int AA=0;
            int RA=0;
            int GA=0;
            int BA=0;
           
            int AC=0;
            int RC=0;
            int GC=0;
            int BC=0;
           
         
            int ofsRowB=ofsRow-width;
            for(int n=0;n<width;n++)
            {
               int pixelB=0;
               if(y>0)
                  pixelB=pixels[ofsRowB+n];
               int AB=(pixelB>>24)&0xFF;
               int RB=(pixelB>>16)&0xFF;
               int GB=(pixelB>>8)&0xFF;
               int BB=pixelB&0xFF;
               
               int pixel=pixels[ofsRow+n];
               int A=(pixel>>24)&0xFF;
               int R=(pixel>>16)&0xFF;
               int G=(pixel>>8)&0xFF;
               int B=pixel&0xFF;
               
               int PA=AA+AB-AC;
               int PAA=Math.abs(PA-AA);
               int PAB=Math.abs(PA-AB);
               int PAC=Math.abs(PA-AC);
               if(PAA<=PAB && PAA<=PAC)
                  A+=AA;
               else
               {
                  if(PAB<=PAC)
                     A+=AB;
                  else
                     A+=AC;
               }
               
               
               int PR=RA+RB-RC;
               int PRA=Math.abs(PR-RA);
               int PRB=Math.abs(PR-RB);
               int PRC=Math.abs(PR-RC);
               if(PRA<=PRB && PRA<=PRC)
                  R+=RA;
               else
               {
                  if(PRB<=PRC)
                     R+=RB;
                  else
                     R+=RC;
               }
               
               
               int PG=GA+GB-GC;
               int PGA=Math.abs(PG-GA);
               int PGB=Math.abs(PG-GB);
               int PGC=Math.abs(PG-GC);
               if(PGA<=PGB && PGA<=PGC)
                  G+=GA;
               else
               {
                  if(PGB<=PGC)
                     G+=GB;
                  else
                     G+=GC;
               }
               
               int PB=BA+BB-BC;
               int PBA=Math.abs(PB-BA);
               int PBB=Math.abs(PB-BB);
               int PBC=Math.abs(PB-BC);
               if(PBA<=PBB && PBA<=PBC)
                  B+=BA;
               else
               {
                  if(PBB<=PBC)
                     B+=BB;
                  else
                     B+=BC;
               }
               
               A&=0xFF;
               R&=0xFF;
               G&=0xFF;
               B&=0xFF;
               
               pixels[ofsRow+n]=(A<<24) | (R<<16) | (G<<8) | B;
               
               AA=A;
               RA=R;
               GA=G;
               BA=B;
               
               AC=AB;
               RC=RB;
               GC=GB;
               BC=BB;
               
               
            }
         }
         break;
         default:
            Log.log("Decoding of this filter type is not yet implemented and unknown..." + filterType);
         break;
      }
       
    }
}


NB: RGBArrayImage class is just a pixel array like class

Offline Matthias

Senior Member


Medals: 3
Projects: 1


TWL - Themable Widget Library


« Reply #1 - Posted 2011-05-15 12:02:25 »

Well - I wouldn't call this code readable Smiley

And I would say my PNG decoder is even more feature complete and has documentation Smiley
Offline DzzD
« Reply #2 - Posted 2011-05-15 12:27:14 »

I did not find any one at the time I wrote it (last year http://www.java-gaming.org/topics/pure-java-png-loader-with-efficient-clear-code/22041/view.html)

yours looks nice and more complete too, anyway I dont find it "easily readable", the one I posted above is short and IMO really easy to "read" even by someone who dont understand anything on PNG, but it is more a base to anyone wanting to write its own, I mean I know it is incomplete in many ways, I just wanted to give "base code"

for example showing where the int chunk value come from :
public final static int CHUNK_PLTE = 1347179589;//bytesToInt("PLTE".getBytes());

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Abuse

JGO Knight


Medals: 12


falling into the abyss of reality


« Reply #3 - Posted 2011-05-15 12:55:22 »

'P' << 24 | 'L' << 16 | 'T' << 8 | 'E';

Best of both worlds.

Make Elite IV:Dangerous happen! Pledge your backing at KICKSTARTER here! https://dl.dropbox.com/u/54785909/EliteIVsmaller.png
Offline ra4king

JGO Kernel


Medals: 345
Projects: 2
Exp: 5 years


I'm the King!


« Reply #4 - Posted 2011-05-15 18:18:44 »

@Abuse: Win!

Offline DzzD
« Reply #5 - Posted 2011-05-15 18:36:08 »

yes ^^

Offline Abuse

JGO Knight


Medals: 12


falling into the abyss of reality


« Reply #6 - Posted 2011-05-15 19:16:19 »

I've got piles of code for manipulating png data; and although most of it was written by me, I don't own it so obviously can't release it =(

It was also born in the world of j2me, so tends to solve more esoteric problems specific to the j2me platform - of less use in the desktop environment. Though given the constraints of J2ME, it did tend to be compact & resource efficient - and IMO readable.

Tasks such as palette manipulation, image rescaling, png construction from raw pixel data, chunk replacement, image rotation, crc regeneration etc - all applied to raw png data to work around API limitations and/or phone specific implementation bugs.

Writing it was great fun at the time; for some reason I like writing utility code that conforms to strict, well documented specifications. I guess clear requirements & concrete objectives are every developers dream.

Make Elite IV:Dangerous happen! Pledge your backing at KICKSTARTER here! https://dl.dropbox.com/u/54785909/EliteIVsmaller.png
Offline ra4king

JGO Kernel


Medals: 345
Projects: 2
Exp: 5 years


I'm the King!


« Reply #7 - Posted 2011-05-15 23:51:09 »

@Abuse
Yes clear concise code requirements are a dream because they are somewhat easy. You know what you need to achieve and how the code should run.
This is also why game development is hard, because you only know what it should vaguely look like, but you are still experimenting with and figuring out which different code architectures and game designs should be used.

Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

TehJavaDev (13 views)
2014-08-28 18:26:30

CopyableCougar4 (25 views)
2014-08-22 19:31:30

atombrot (38 views)
2014-08-19 09:29:53

Tekkerue (31 views)
2014-08-16 06:45:27

Tekkerue (32 views)
2014-08-16 06:22:17

Tekkerue (19 views)
2014-08-16 06:20:21

Tekkerue (29 views)
2014-08-16 06:12:11

Rayexar (66 views)
2014-08-11 02:49:23

BurntPizza (42 views)
2014-08-09 21:09:32

BurntPizza (34 views)
2014-08-08 02:01:56
List of Learning Resources
by Longor1996
2014-08-16 10:40:00

List of Learning Resources
by SilverTiger
2014-08-05 19:33:27

Resources for WIP games
by CogWheelz
2014-08-01 16:20:17

Resources for WIP games
by CogWheelz
2014-08-01 16:19:50

List of Learning Resources
by SilverTiger
2014-07-31 16:29:50

List of Learning Resources
by SilverTiger
2014-07-31 16:26:06

List of Learning Resources
by SilverTiger
2014-07-31 11:54:12

HotSpot Options
by dleskov
2014-07-08 01:59:08
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!