001: /*
002:  * Version Information:
003:  *
004:  * 0.01  19-Jul-2002     initial release Marc Schoenefeld
005:  * http://www.beauchamp.de/cbm
006:  */
007: 
008: import java.io.*; 
009: import java.awt.image.*;
010: import java.awt.*; 
011: import java.awt.Image;
012: import java.util.zip.*;
013: 
014: class BeauchampEncoder
015: {
016:     public static final boolean ENCODE_ALPHA = true;
017:     public static final boolean NO_ALPHA = false;
018:     public static final int FILTER_NONE = 0;
019:     public static final int FILTER_SUB = 1;
020:     public static final int FILTER_UP = 2;
021:     public static final int FILTER_LAST = 2;
022:     protected byte pngBytes[];
023:     protected byte priorRow[];
024:     protected byte leftBytes[];
025:     protected int width;
026:     protected int height;
027:     protected int bytePos;
028:     protected int maxPos;
029:     protected int hdrPos;
030:     protected int dataPos;
031:     protected int endPos;
032:     protected CRC32 crc;
033:     protected long crcValue;
034:     protected boolean encodeAlpha;
035:     protected int filter;
036:     protected int bytesPerPixel;
037:     protected int compressionLevel;
038:     public BeauchampEncoder()
039:     {
040:         this(null, false, 0, 0);
041:     }
042: 
043:     public BeauchampEncoder(BufferedImage bufferedimage)
044:     {
045:         this(bufferedimage, false, 0, 0);
046:     }
047: 
048:     public BeauchampEncoder(BufferedImage bufferedimage, boolean flag)
049:     {
050:         this(bufferedimage, flag, 0, 0);
051:     }
052: 
053:     public BeauchampEncoder(BufferedImage bufferedimage, boolean flag, int i)
054:     {
055:         this(bufferedimage, flag, i, 0);
056:     }
057: 
058:     public BeauchampEncoder(BufferedImage bufferedimage, boolean flag, int i, int j)
059:     {
060:             crc = new CRC32();
061:         encodeAlpha = flag;
062:         setFilter(i);
063:         if(j >= 0 && j <= 9)
064:             compressionLevel = j;
065:         image = bufferedimage;
066:         
067:         //compressionLevel = j;
068:     }
069: 
070:     protected boolean establishStorageInfo()
071:     {
072:         wRaster = image.getRaster();
073:         int i = wRaster.getNumDataElements();
074:         tType = wRaster.getTransferType();
075:         if(tType == 0 && i == 4 || tType == 3 && i == 1)
076:             bytesPerPixel = encodeAlpha ? 4 : 3;
077:         else
078:         if(tType == 0 && i == 1)
079:         {
080:             bytesPerPixel = 1;
081:             encodeAlpha = false;
082:         } else
083:         {
084:             return false;
085:         }
086:         return true;
087:     }
088: 
089:     public byte[] pngEncode()
090:     {
091:         return pngEncode(encodeAlpha);
092:     }
093: 
094:     public byte[] pngEncode(boolean flag)
095:     {
096:         byte abyte0[] = {
097:             -119, 80, 78, 71, 13, 10, 26, 10
098:         };
099:         if(image == null)
100:             return null;
101:         width = image.getWidth(null);
102:         height = image.getHeight(null);
103:         image = image;
104:         if(!establishStorageInfo())
105:             return null;
106:         pngBytes = new byte[(width + 1) * height * 3 + 200];
107:         maxPos = 0;
108:         bytePos = writeBytes(abyte0, 0);
109:         hdrPos = bytePos;
110:         writeHeader();
111:         dataPos = bytePos;
112:         if(writeImageData())
113:         {
114:             writeEnd();
115:             pngBytes = resizeByteArray(pngBytes, maxPos);
116:         } else
117:         {
118:             pngBytes = null;
119:         }
120:         return pngBytes;
121:     }
122: 
123:     public void setImage(BufferedImage bufferedimage)
124:     {
125:         image = bufferedimage;
126:         pngBytes = null;
127:     }
128: 
129:     protected void writeHeader()
130:     {
131:         int i = bytePos = writeInt4(13, bytePos);
132:         bytePos = writeString("IHDR", bytePos);
133:         width = image.getWidth(null);
134:         height = image.getHeight(null);
135:         bytePos = writeInt4(width, bytePos);
136:         bytePos = writeInt4(height, bytePos);
137:         bytePos = writeByte(8, bytePos);
138:         if(bytesPerPixel != 1)
139:             bytePos = writeByte(encodeAlpha ? 6 : 2, bytePos);
140:         else
141:             bytePos = writeByte(3, bytePos);
142:         bytePos = writeByte(0, bytePos);
143:         bytePos = writeByte(0, bytePos);
144:         bytePos = writeByte(0, bytePos);
145:         crc.reset();
146:         crc.update(pngBytes, i, bytePos - i);
147:         crcValue = crc.getValue();
148:         bytePos = writeInt4((int)crcValue, bytePos);
149:     }
150: 
151:     protected boolean writeImageData()
152:     {
153:         int i = height;
154:         int j = 0;
155:         Deflater deflater = new Deflater(compressionLevel);
156:         ByteArrayOutputStream bytearrayoutputstream = new ByteArrayOutputStream(1024);
157:         DeflaterOutputStream deflateroutputstream = new DeflaterOutputStream(bytearrayoutputstream, deflater);
158:         if(bytesPerPixel == 1)
159:             writePalette((IndexColorModel)image.getColorModel());
160:         try
161:         {
162:             int k;
163:             for(; i > 0; i -= k)
164:             {
165:                 k = Math.min(32767 / (width * (bytesPerPixel + 1)), i);
166:                 byte abyte0[] = new byte[width * k * bytesPerPixel + k];
167:                 if(filter == 1)
168:                     leftBytes = new byte[16];
169:                 if(filter == 2)
170:                     priorRow = new byte[width * bytesPerPixel];
171:                 byte abyte2[];
172:                 int ai[];
173:                 if(tType == 0)
174:                 {
175:                     abyte2 = (byte[])wRaster.getDataElements(0, j, width, k, null);
176:                     ai = null;
177:                 } else
178:                 {
179:                     ai = (int[])wRaster.getDataElements(0, j, width, k, null);
180:                     abyte2 = null;
181:                 }
182:                 int l = 0;
183:                 int j1 = 0;
184:                 int i1 = 1;
185:                 for(int l1 = 0; l1 < width * k; l1++)
186:                 {
187:                     if(l1 % width == 0)
188:                     {
189:                         abyte0[l++] = (byte)filter;
190:                         i1 = l;
191:                     }
192:                     if(bytesPerPixel == 1)
193:                         abyte0[l++] = abyte2[j1++];
194:                     else
195:                     if(tType == 0)
196:                     {
197:                         abyte0[l++] = abyte2[j1++];
198:                         abyte0[l++] = abyte2[j1++];
199:                         abyte0[l++] = abyte2[j1++];
200:                         if(encodeAlpha)
201:                             abyte0[l++] = abyte2[j1++];
202:                         else
203:                             j1++;
204:                     } else
205:                     {
206:                         abyte0[l++] = (byte)(ai[j1] >> 16 & 0xff);
207:                         abyte0[l++] = (byte)(ai[j1] >> 8 & 0xff);
208:                         abyte0[l++] = (byte)(ai[j1] & 0xff);
209:                         if(encodeAlpha)
210:                             abyte0[l++] = (byte)(ai[j1] >> 24 & 0xff);
211:                         j1++;
212:                     }
213:                     if(l1 % width == width - 1 && filter != 0)
214:                     {
215:                         if(filter == 1)
216:                             filterSub(abyte0, i1, width);
217:                         if(filter == 2)
218:                             filterUp(abyte0, i1, width);
219:                     }
220:                 }
221: 
222:                 deflateroutputstream.write(abyte0, 0, l);
223:                 j += k;
224:             }
225: 
226:             deflateroutputstream.close();
227:             byte abyte1[] = bytearrayoutputstream.toByteArray();
228:             int k1 = abyte1.length;
229:             crc.reset();
230:             bytePos = writeInt4(k1, bytePos);
231:             bytePos = writeString("IDAT", bytePos);
232:             crc.update("IDAT".getBytes());
233:             bytePos = writeBytes(abyte1, k1, bytePos);
234:             crc.update(abyte1, 0, k1);
235:             crcValue = crc.getValue();
236:             bytePos = writeInt4((int)crcValue, bytePos);
237:             deflater.finish();
238:             return true;
239:         }
240:         catch(IOException ioexception)
241:         {
242:             System.err.println(ioexception.toString());
243:         }
244:         return false;
245:     }
246: 
247:     protected void writePalette(IndexColorModel indexcolormodel)
248:     {
249:         byte abyte0[] = new byte[256];
250:         byte abyte1[] = new byte[256];
251:         byte abyte2[] = new byte[256];
252:         byte abyte3[] = new byte[768];
253:         indexcolormodel.getReds(abyte0);
254:         indexcolormodel.getGreens(abyte1);
255:         indexcolormodel.getBlues(abyte2);
256:         for(int i = 0; i < 256; i++)
257:         {
258:             abyte3[i * 3] = abyte0[i];
259:             abyte3[i * 3 + 1] = abyte1[i];
260:             abyte3[i * 3 + 2] = abyte2[i];
261:         }
262: 
263:         bytePos = writeInt4(768, bytePos);
264:         bytePos = writeString("PLTE", bytePos);
265:         crc.reset();
266:         crc.update("PLTE".getBytes());
267:         bytePos = writeBytes(abyte3, bytePos);
268:         crc.update(abyte3);
269:         crcValue = crc.getValue();
270:         bytePos = writeInt4((int)crcValue, bytePos);
271:     }
272: 
273:         protected void filterSub(byte abyte0[], int i, int j)
274:     {
275:         int l = bytesPerPixel;
276:         int i1 = i + l;
277:         int j1 = j * bytesPerPixel;
278:         int k1 = l;
279:         int l1 = 0;
280:         for(int k = i1; k < i + j1; k++)
281:         {
282:             leftBytes[k1] = abyte0[k];
283:             abyte0[k] = (byte)((abyte0[k] - leftBytes[l1]) % 256);
284:             k1 = (k1 + 1) % 15;
285:             l1 = (l1 + 1) % 15;
286:         }
287: 
288:     }
289: 
290:     protected void filterUp(byte abyte0[], int i, int j)
291:     {
292:         int l = j * bytesPerPixel;
293:         for(int k = 0; k < l; k++)
294:         {
295:             byte byte0 = abyte0[i + k];
296:             abyte0[i + k] = (byte)((abyte0[i + k] - priorRow[k]) % 256);
297:             priorRow[k] = byte0;
298:         }
299: 
300:     }
301: 
302:     public int getCompressionLevel()
303:     {
304:         return compressionLevel;
305:     }
306: 
307:     public boolean getEncodeAlpha()
308:     {
309:         return encodeAlpha;
310:     }
311: 
312:     public int getFilter()
313:     {
314:         return filter;
315:     }
316: 
317: 
318:     protected byte[] resizeByteArray(byte abyte0[], int i)
319:     {
320:         byte abyte1[] = new byte[i];
321:         int j = abyte0.length;
322:         System.arraycopy(abyte0, 0, abyte1, 0, Math.min(j, i));
323:         return abyte1;
324:     }
325: 
326:     public void setCompressionLevel(int i)
327:     {
328:         if(i >= 0 && i <= 9)
329:             compressionLevel = i;
330:     }
331: 
332:     public void setEncodeAlpha(boolean flag)
333:     {
334:         encodeAlpha = flag;
335:     }
336: 
337:     public void setFilter(int i)
338:     {
339:         filter = 0;
340:         if(i <= 2)
341:             filter = i;
342:     }
343: 
344:     protected int writeByte(int i, int j)
345:     {
346:         byte abyte0[] = {
347:             (byte)i
348:         };
349:         return writeBytes(abyte0, j);
350:     }
351: 
352:     protected int writeBytes(byte abyte0[], int i)
353:     {
354:         maxPos = Math.max(maxPos, i + abyte0.length);
355:         if(abyte0.length + i > pngBytes.length)
356:             pngBytes = resizeByteArray(pngBytes, pngBytes.length + Math.max(1000, abyte0.length));
357:         System.arraycopy(abyte0, 0, pngBytes, i, abyte0.length);
358:         return i + abyte0.length;
359:     }
360: 
361:     protected int writeBytes(byte abyte0[], int i, int j)
362:     {
363:         maxPos = Math.max(maxPos, j + i);
364:         if(i + j > pngBytes.length)
365:             pngBytes = resizeByteArray(pngBytes, pngBytes.length + Math.max(1000, i));
366:         System.arraycopy(abyte0, 0, pngBytes, j, i);
367:         return j + i;
368:     }
369: 
370:     protected void writeEnd()
371:     {
372:         bytePos = writeInt4(0, bytePos);
373:         bytePos = writeString("IEND", bytePos);
374:         crc.reset();
375:         crc.update("IEND".getBytes());
376:         crcValue = crc.getValue();
377:         bytePos = writeInt4((int)crcValue, bytePos);
378:     }
379: 
380:     protected int writeInt2(int i, int j)
381:     {
382:         byte abyte0[] = {
383:             (byte)(i >> 8 & 0xff), (byte)(i & 0xff)
384:         };
385:         return writeBytes(abyte0, j);
386:     }
387: 
388:     protected int writeInt4(int i, int j)
389:     {
390:         byte abyte0[] = {
391:             (byte)(i >> 24 & 0xff), (byte)(i >> 16 & 0xff), (byte)(i >> 8 & 0xff), (byte)(i & 0xff)
392:         };
393:         return writeBytes(abyte0, j);
394:     }
395: 
396:     protected int writeString(String s, int i)
397:     {
398:         return writeBytes(s.getBytes(), i);
399:     }
400: 
401: 
402:     protected BufferedImage image;
403:     protected WritableRaster wRaster;
404:     protected int tType;
405: }
406: 
407: 
408: public final class BeauchampKoala2PNG {
409: 
410:      final byte unused[] =new byte[2];
411:      final byte bitmap[] =new byte[8000];
412:      final byte mcolor[][] =new byte [25][40];
413:      final byte color[][] = new byte[25][40];
414:      final byte background[] =new byte[1]; 
415: 
416: 
417:     static final Color[] c64Colors = new Color[] {
418:         new Color(           0x00, 0x00, 0x00),
419:         new Color(                  0xff, 0xff, 0xff),
420:         new Color(           0x88, 0x00, 0x00),
421:         new Color(           0xaa, 0xff, 0xee),
422:         new Color(           0xcc, 0x44, 0xcc),
423:         new Color(           0x00, 0xcc, 0x55),
424:         new Color(           0x00, 0x00, 0xaa),
425:         new Color(           0xee, 0xee, 0x77),
426:         new Color(           0xdd, 0x88, 0x55),
427:         new Color(           0x66, 0x44, 0x00),
428:         new Color(           0xff, 0x77, 0x77),
429:         new Color(           0x33, 0x33, 0x33),
430:         new Color(           0x77, 0x77, 0x77),
431:         new Color(           0xaa, 0xff, 0x66),
432:         new Color(           0x00, 0x88, 0xff),
433:         new Color(           0xbb, 0xbb, 0xbb)
434: };
435: 
436:     public static final int koala_width= 320;
437:     public static final int koala_height= 200;
438:     public static final int koala_colors=16;
439: 
440:     static byte low_nibble(byte x) {
441:         return (byte)((x) & 0x0f); 
442:     }
443: 
444:     static byte high_nibble(byte x) {
445:         return (byte )(((x) >> 4)  & 0x0f); 
446:     }
447: 
448:     byte getColor(byte row, byte col, int index) {
449:         //        System.err.println("row:"+row+"/col:"+col+"/index:"+index); 
450:         if (index == 0) {
451:             return low_nibble(background[0]);
452:         }
453:         else if (index == 1) {
454:             return high_nibble(mcolor[row][col]);
455:         }
456:         else if (index == 2) {
457:             return low_nibble(mcolor[row][col]);
458:         }
459:         else 
460:             return low_nibble(color[row][col]);
461:     }
462: 
463:     byte[] convertFile(String theFile) {
464:         try {
465:         int pixelDepth=24; 
466:         FileInputStream inFile = new FileInputStream(theFile);
467:         
468:         int read = inFile.read(unused); 
469:         read = inFile.read(bitmap); 
470: 
471:         for (int i = 0; i < 25; i++) {
472:             read = inFile.read(mcolor[i]); 
473:         }
474: 
475:         for (int i = 0; i < 25; i++) {
476:             read = inFile.read(color[i]); 
477:         }
478: 
479:         read = inFile.read(background); 
480:         
481:         BufferedImage myImage = 
482:             new BufferedImage(
483:                               koala_width, 
484:                               koala_height, 
485:                               (pixelDepth == 8) ? BufferedImage.TYPE_BYTE_INDEXED :   BufferedImage.TYPE_4BYTE_ABGR );
486:         Graphics g;
487:         
488:         g = myImage.getGraphics();
489:         
490:         g.fillRect(0,0,koala_width,koala_height);
491:         
492:         int row = 0;
493:         int col = 0 ; 
494:         int p = 0; 
495:         for (row = 0; row < 200; row++) {
496:             int row8 = row / 8; 
497:         
498: 
499:             int pos = (row8) *320 + row % 8; 
500:         
501:             int x  = 0;
502:             byte index = 0; 
503:             byte oldindex = -1; 
504:             for (col = 0; col < 40; col++) {
505:                 p = bitmap[pos]; 
506:                 if (p < 0) {
507:                     p +=256;
508:                 }
509: 
510:                 for (int i = 0; i < 4; i++) {
511:                     //    System.err.println(">>"+p); 
512:                     oldindex = index;
513:                     index = getColor((byte)(row8), (byte)col, (p & 3));
514:                     //                    if (index != oldindex) {
515:                         g.setColor(c64Colors[index]); 
516:                         //                    }
517:                     g.drawLine(x+6-i*2,row,x+6-i*2+1,row);
518:                     p >>= 2;
519:                 }
520:                 
521:                 x += 8;
522:                 pos += 8;
523:             }
524:         }
525: 
526:         int compressionLevel=9; 
527: 
528:         byte pngBytes[]; 
529:         int filter = 0;         
530:         boolean encodeAlpha= false; 
531:         BeauchampEncoder png =  new BeauchampEncoder( 
532:                                            myImage,
533:                                            (encodeAlpha) ? BeauchampEncoder.ENCODE_ALPHA : BeauchampEncoder.NO_ALPHA,
534:                                            filter, compressionLevel );
535:         pngBytes = png.pngEncode(); 
536: 
537:         return pngBytes; 
538:         
539:     }
540:     catch (Exception e) {
541:         e.printStackTrace(); 
542:         return null;
543: 
544:     }
545: 
546:     }
547: 
548:     public static void main (String[] args) {
549:             try {
550:         if (args.length < 2) {
551:             System.out.print( new String(new BeauchampKoala2PNG().convertFile(args[0])) ); 
552:             System.exit(0);
553:         }
554:         else {
555:             FileOutputStream outfile = new FileOutputStream(args[1]);
556:             outfile.write(new BeauchampKoala2PNG().convertFile(args[0]) ); 
557:             System.exit(0);
558:             }
559:         }        
560:         catch (IndexOutOfBoundsException e) {
561:                               System.err.println("Parameters: [1] koa-file [2] png-file"); 
562:                        System.err.println("http://www.beauchamp.de/cbm"); 
563:                 System.err.println("You forgot to specify the filenames !"); 
564:         }
565:         catch (Exception e) {
566:                               System.err.println("Parameters: [1] koa-file [2] png-file"); 
567:                        System.err.println("http://www.beauchamp.de/cbm"); 
568:                 System.err.println("Some other exception occured!"); 
569:                 e.printStackTrace(System.err); 
570:         }
571: 
572:     }
573: 
574: }
575: 
576: 
577: 
578: