1:
37:
38:
39: package ;
40:
41: import ;
42: import ;
43: import ;
44: import ;
45: import ;
46: import ;
47: import ;
48: import ;
49: import ;
50: import ;
51: import ;
52: import ;
53: import ;
54: import ;
55: import ;
56: import ;
57:
58: import ;
59: import ;
60: import ;
61: import ;
62: import ;
63: import ;
64: import ;
65: import ;
66: import ;
67: import ;
68: import ;
69: import ;
70:
71: import ;
72: import ;
73:
74: public class GdkPixbufDecoder extends gnu.java.awt.image.ImageDecoder
75: {
76: static
77: {
78: if (true)
79: {
80: System.loadLibrary("gtkpeer");
81: }
82:
83: initStaticState ();
84: }
85:
86:
92: static Object pixbufLock = new Object();
93:
94: static native void initStaticState();
95: private final int native_state = GtkGenericPeer.getUniqueInteger ();
96:
97:
98: private boolean needsClose = false;
99:
100:
101: Vector curr;
102:
103:
109: private Pointer nativeDecoder;
110:
111:
112:
113: native void initState ();
114: native void pumpBytes (byte[] bytes, int len) throws IOException;
115: native void pumpDone () throws IOException;
116: native void finish (boolean needsClose);
117:
118:
122: static native void streamImage(int[] bytes, String format,
123: int width, int height,
124: boolean hasAlpha, GdkPixbufWriter writer);
125:
126:
127: static final ColorModel cm = new DirectColorModel (32, 0xff000000,
128: 0x00ff0000,
129: 0x0000ff00,
130: 0x000000ff);
131: public GdkPixbufDecoder (DataInput datainput)
132: {
133: super (datainput);
134: }
135:
136: public GdkPixbufDecoder (InputStream in)
137: {
138: super (in);
139: }
140:
141: public GdkPixbufDecoder (String filename)
142: {
143: super (filename);
144: }
145:
146: public GdkPixbufDecoder (URL url)
147: {
148: super (url);
149: }
150:
151: public GdkPixbufDecoder (byte[] imagedata, int imageoffset, int imagelength)
152: {
153: super (imagedata, imageoffset, imagelength);
154: }
155:
156:
157: void areaPrepared (int width, int height)
158: {
159:
160: if (curr == null)
161: return;
162:
163: for (int i = 0; i < curr.size (); i++)
164: {
165: ImageConsumer ic = (ImageConsumer) curr.elementAt (i);
166: ic.setDimensions (width, height);
167: ic.setColorModel (cm);
168: ic.setHints (ImageConsumer.RANDOMPIXELORDER);
169: }
170: }
171:
172:
173: void areaUpdated (int x, int y, int width, int height,
174: int pixels[], int scansize)
175: {
176: if (curr == null)
177: return;
178:
179: for (int i = 0; i < curr.size (); i++)
180: {
181: ImageConsumer ic = (ImageConsumer) curr.elementAt (i);
182: ic.setPixels (x, y, width, height, cm, pixels, 0, scansize);
183: }
184: }
185:
186:
187:
188:
189:
190:
191:
192:
193: public void produce (Vector v, InputStream is) throws IOException
194: {
195: curr = v;
196:
197: byte bytes[] = new byte[4096];
198: int len = 0;
199: synchronized(pixbufLock)
200: {
201: initState();
202: }
203: needsClose = true;
204:
205:
206: while ((len = is.read (bytes)) != -1)
207: {
208: synchronized(pixbufLock)
209: {
210: pumpBytes (bytes, len);
211: }
212: }
213:
214: synchronized(pixbufLock)
215: {
216: pumpDone();
217: }
218:
219: needsClose = false;
220:
221: for (int i = 0; i < curr.size (); i++)
222: {
223: ImageConsumer ic = (ImageConsumer) curr.elementAt (i);
224: ic.imageComplete (ImageConsumer.STATICIMAGEDONE);
225: }
226:
227: curr = null;
228: }
229:
230: public void finalize()
231: {
232: synchronized(pixbufLock)
233: {
234: finish(needsClose);
235: }
236: }
237:
238:
239: public static class ImageFormatSpec
240: {
241: public String name;
242: public boolean writable = false;
243: public ArrayList<String> mimeTypes = new ArrayList<String>();
244: public ArrayList<String> extensions = new ArrayList<String>();
245:
246: public ImageFormatSpec(String name, boolean writable)
247: {
248: this.name = name;
249: this.writable = writable;
250: }
251:
252: public synchronized void addMimeType(String m)
253: {
254: mimeTypes.add(m);
255: }
256:
257: public synchronized void addExtension(String e)
258: {
259: extensions.add(e);
260: }
261: }
262:
263: static ArrayList<ImageFormatSpec> imageFormatSpecs;
264:
265: public static ImageFormatSpec registerFormat(String name, boolean writable)
266: {
267: ImageFormatSpec ifs = new ImageFormatSpec(name, writable);
268: synchronized(GdkPixbufDecoder.class)
269: {
270: if (imageFormatSpecs == null)
271: imageFormatSpecs = new ArrayList<ImageFormatSpec>();
272: imageFormatSpecs.add(ifs);
273: }
274: return ifs;
275: }
276:
277: static String[] getFormatNames(boolean writable)
278: {
279: ArrayList<String> names = new ArrayList<String>();
280: synchronized (imageFormatSpecs)
281: {
282: Iterator<ImageFormatSpec> i = imageFormatSpecs.iterator();
283: while (i.hasNext())
284: {
285: ImageFormatSpec ifs = i.next();
286: if (writable && !ifs.writable)
287: continue;
288: names.add(ifs.name);
289:
290:
295:
296: Iterator<String> j = ifs.extensions.iterator();
297: while (j.hasNext())
298: names.add(j.next());
299: }
300: }
301: return names.toArray(new String[names.size()]);
302: }
303:
304: static String[] getFormatExtensions(boolean writable)
305: {
306: ArrayList<String> extensions = new ArrayList<String>();
307: synchronized (imageFormatSpecs)
308: {
309: Iterator<ImageFormatSpec> i = imageFormatSpecs.iterator();
310: while (i.hasNext())
311: {
312: ImageFormatSpec ifs = i.next();
313: if (writable && !ifs.writable)
314: continue;
315: Iterator<String> j = ifs.extensions.iterator();
316: while (j.hasNext())
317: extensions.add(j.next());
318: }
319: }
320: return extensions.toArray(new String[extensions.size()]);
321: }
322:
323: static String[] getFormatMimeTypes(boolean writable)
324: {
325: ArrayList<String> mimeTypes = new ArrayList<String>();
326: synchronized (imageFormatSpecs)
327: {
328: Iterator<ImageFormatSpec> i = imageFormatSpecs.iterator();
329: while (i.hasNext())
330: {
331: ImageFormatSpec ifs = i.next();
332: if (writable && !ifs.writable)
333: continue;
334: Iterator<String> j = ifs.mimeTypes.iterator();
335: while (j.hasNext())
336: mimeTypes.add(j.next());
337: }
338: }
339: return mimeTypes.toArray(new String[mimeTypes.size()]);
340: }
341:
342:
343: static String findFormatName(Object ext, boolean needWritable)
344: {
345: if (ext == null)
346: return null;
347:
348: if (!(ext instanceof String))
349: throw new IllegalArgumentException("extension is not a string");
350:
351: String str = (String) ext;
352:
353: Iterator<ImageFormatSpec> i = imageFormatSpecs.iterator();
354: while (i.hasNext())
355: {
356: ImageFormatSpec ifs = i.next();
357:
358: if (needWritable && !ifs.writable)
359: continue;
360:
361: if (ifs.name.equals(str))
362: return str;
363:
364: Iterator<String> j = ifs.extensions.iterator();
365: while (j.hasNext())
366: {
367: String extension = j.next();
368: if (extension.equals(str))
369: return ifs.name;
370: }
371:
372: j = ifs.mimeTypes.iterator();
373: while (j.hasNext())
374: {
375: String mimeType = j.next();
376: if (mimeType.equals(str))
377: return ifs.name;
378: }
379: }
380: throw new IllegalArgumentException("unknown extension '" + str + "'");
381: }
382:
383: private static GdkPixbufReaderSpi readerSpi;
384: private static GdkPixbufWriterSpi writerSpi;
385:
386: public static synchronized GdkPixbufReaderSpi getReaderSpi()
387: {
388: if (readerSpi == null)
389: readerSpi = new GdkPixbufReaderSpi();
390: return readerSpi;
391: }
392:
393: public static synchronized GdkPixbufWriterSpi getWriterSpi()
394: {
395: if (writerSpi == null)
396: writerSpi = new GdkPixbufWriterSpi();
397: return writerSpi;
398: }
399:
400: public static void registerSpis(IIORegistry reg)
401: {
402: reg.registerServiceProvider(getReaderSpi(), ImageReaderSpi.class);
403: reg.registerServiceProvider(getWriterSpi(), ImageWriterSpi.class);
404: }
405:
406: public static class GdkPixbufWriterSpi extends ImageWriterSpi
407: {
408: public GdkPixbufWriterSpi()
409: {
410: super("GdkPixbuf", "2.x",
411: GdkPixbufDecoder.getFormatNames(true),
412: GdkPixbufDecoder.getFormatExtensions(true),
413: GdkPixbufDecoder.getFormatMimeTypes(true),
414: "gnu.java.awt.peer.gtk.GdkPixbufDecoder$GdkPixbufWriter",
415: new Class[] { ImageOutputStream.class },
416: new String[] { "gnu.java.awt.peer.gtk.GdkPixbufDecoder$GdkPixbufReaderSpi" },
417: false, null, null, null, null,
418: false, null, null, null, null);
419: }
420:
421: public boolean canEncodeImage(ImageTypeSpecifier ts)
422: {
423: return true;
424: }
425:
426: public ImageWriter createWriterInstance(Object ext)
427: {
428: return new GdkPixbufWriter(this, ext);
429: }
430:
431: public String getDescription(java.util.Locale loc)
432: {
433: return "GdkPixbuf Writer SPI";
434: }
435:
436: }
437:
438: public static class GdkPixbufReaderSpi extends ImageReaderSpi
439: {
440: public GdkPixbufReaderSpi()
441: {
442: super("GdkPixbuf", "2.x",
443: GdkPixbufDecoder.getFormatNames(false),
444: GdkPixbufDecoder.getFormatExtensions(false),
445: GdkPixbufDecoder.getFormatMimeTypes(false),
446: "gnu.java.awt.peer.gtk.GdkPixbufDecoder$GdkPixbufReader",
447: new Class[] { ImageInputStream.class },
448: new String[] { "gnu.java.awt.peer.gtk.GdkPixbufDecoder$GdkPixbufWriterSpi" },
449: false, null, null, null, null,
450: false, null, null, null, null);
451: }
452:
453: public boolean canDecodeInput(Object obj)
454: {
455: return true;
456: }
457:
458: public ImageReader createReaderInstance(Object ext)
459: {
460: return new GdkPixbufReader(this, ext);
461: }
462:
463: public String getDescription(Locale loc)
464: {
465: return "GdkPixbuf Reader SPI";
466: }
467: }
468:
469: private static class GdkPixbufWriter
470: extends ImageWriter implements Runnable
471: {
472: String ext;
473: public GdkPixbufWriter(GdkPixbufWriterSpi ownerSpi, Object ext)
474: {
475: super(ownerSpi);
476: this.ext = findFormatName(ext, true);
477: }
478:
479: public IIOMetadata convertImageMetadata (IIOMetadata inData,
480: ImageTypeSpecifier imageType,
481: ImageWriteParam param)
482: {
483: return null;
484: }
485:
486: public IIOMetadata convertStreamMetadata (IIOMetadata inData,
487: ImageWriteParam param)
488: {
489: return null;
490: }
491:
492: public IIOMetadata getDefaultImageMetadata (ImageTypeSpecifier imageType,
493: ImageWriteParam param)
494: {
495: return null;
496: }
497:
498: public IIOMetadata getDefaultStreamMetadata (ImageWriteParam param)
499: {
500: return null;
501: }
502:
503: public void write (IIOMetadata streamMetadata, IIOImage i, ImageWriteParam param)
504: throws IOException
505: {
506: RenderedImage image = i.getRenderedImage();
507: Raster ras = image.getData();
508: int width = ras.getWidth();
509: int height = ras.getHeight();
510: ColorModel model = image.getColorModel();
511: int[] pixels = CairoGraphics2D.findSimpleIntegerArray (image.getColorModel(), ras);
512:
513: if (pixels == null)
514: {
515: BufferedImage img;
516: if(model != null && model.hasAlpha())
517: img = CairoSurface.getBufferedImage(width, height);
518: img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
519: int[] pix = new int[4];
520: for (int y = 0; y < height; ++y)
521: for (int x = 0; x < width; ++x)
522: img.setRGB(x, y, model.getRGB(ras.getPixel(x, y, pix)));
523: pixels = CairoGraphics2D.findSimpleIntegerArray (img.getColorModel(),
524: img.getRaster());
525: model = img.getColorModel();
526: }
527:
528: Thread workerThread = new Thread(this, "GdkPixbufWriter");
529: workerThread.start();
530: processImageStarted(1);
531: synchronized(pixbufLock)
532: {
533: streamImage(pixels, this.ext, width, height, model.hasAlpha(),
534: this);
535: }
536: synchronized(data)
537: {
538: data.add(DATADONE);
539: data.notifyAll();
540: }
541:
542: while (workerThread.isAlive())
543: {
544: try
545: {
546: workerThread.join();
547: }
548: catch (InterruptedException ioe)
549: {
550:
551: }
552: }
553:
554: if (exception != null)
555: throw exception;
556:
557: processImageComplete();
558: }
559:
560:
563: private static final Object DATADONE = new Object();
564:
565:
571: private ArrayList<Object> data = new ArrayList<Object>();
572:
573:
577: private IOException exception;
578:
579:
580: private void write(byte[] bs)
581: {
582: synchronized(data)
583: {
584: data.add(bs);
585: data.notifyAll();
586: }
587: }
588:
589: public void run()
590: {
591: boolean done = false;
592: while (!done)
593: {
594: synchronized(data)
595: {
596: while (data.isEmpty())
597: {
598: try
599: {
600: data.wait();
601: }
602: catch (InterruptedException ie)
603: {
604:
605: }
606: }
607:
608: Object o = data.remove(0);
609: if (o == DATADONE)
610: done = true;
611: else
612: {
613: DataOutput out = (DataOutput) getOutput();
614: try
615: {
616: out.write((byte[]) o);
617: }
618: catch (IOException ioe)
619: {
620:
621: if (exception == null)
622: exception = ioe;
623: }
624: }
625: }
626: }
627: }
628: }
629:
630: private static class GdkPixbufReader
631: extends ImageReader
632: implements ImageConsumer
633: {
634:
635: GdkPixbufDecoder dec;
636: BufferedImage bufferedImage;
637: ColorModel defaultModel;
638: int width;
639: int height;
640: String ext;
641:
642: public GdkPixbufReader(GdkPixbufReaderSpi ownerSpi, Object ext)
643: {
644: super(ownerSpi);
645: this.ext = findFormatName(ext, false);
646: }
647:
648: public GdkPixbufReader(GdkPixbufReaderSpi ownerSpi, Object ext,
649: GdkPixbufDecoder d)
650: {
651: this(ownerSpi, ext);
652: dec = d;
653: }
654:
655: public void setDimensions(int w, int h)
656: {
657: processImageStarted(1);
658: width = w;
659: height = h;
660: }
661:
662: public void setProperties(Hashtable props) {}
663:
664: public void setColorModel(ColorModel model)
665: {
666: defaultModel = model;
667: }
668:
669: public void setHints(int flags) {}
670:
671: public void setPixels(int x, int y, int w, int h,
672: ColorModel model, byte[] pixels,
673: int offset, int scansize)
674: {
675: }
676:
677: public void setPixels(int x, int y, int w, int h,
678: ColorModel model, int[] pixels,
679: int offset, int scansize)
680: {
681: if (model == null)
682: model = defaultModel;
683:
684: if (bufferedImage == null)
685: {
686: if(model != null && model.hasAlpha())
687: bufferedImage = new BufferedImage (width, height,
688: BufferedImage.TYPE_INT_ARGB);
689: else
690: bufferedImage = new BufferedImage (width, height,
691: BufferedImage.TYPE_INT_RGB);
692: }
693:
694: int pixels2[];
695: if (model != null)
696: {
697: pixels2 = new int[pixels.length];
698: for (int yy = 0; yy < h; yy++)
699: for (int xx = 0; xx < w; xx++)
700: {
701: int i = yy * scansize + xx;
702: pixels2[i] = model.getRGB (pixels[i]);
703: }
704: }
705: else
706: pixels2 = pixels;
707:
708: bufferedImage.setRGB (x, y, w, h, pixels2, offset, scansize);
709: processImageProgress(y / (height == 0 ? 1 : height));
710: }
711:
712: public void imageComplete(int status)
713: {
714: processImageComplete();
715: }
716:
717: public BufferedImage getBufferedImage()
718: {
719: if (bufferedImage == null && dec != null)
720: dec.startProduction (this);
721: return bufferedImage;
722: }
723:
724:
725:
726: public int getNumImages(boolean allowSearch)
727: throws IOException
728: {
729: return 1;
730: }
731:
732: public IIOMetadata getImageMetadata(int i)
733: {
734: return null;
735: }
736:
737: public IIOMetadata getStreamMetadata()
738: throws IOException
739: {
740: return null;
741: }
742:
743: public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex)
744: throws IOException
745: {
746: BufferedImage img = getBufferedImage();
747: Vector<ImageTypeSpecifier> vec = new Vector<ImageTypeSpecifier>();
748: vec.add(new ImageTypeSpecifier(img));
749: return vec.iterator();
750: }
751:
752: public int getHeight(int imageIndex)
753: throws IOException
754: {
755: return getBufferedImage().getHeight();
756: }
757:
758: public int getWidth(int imageIndex)
759: throws IOException
760: {
761: return getBufferedImage().getWidth();
762: }
763:
764: public void setInput(Object input,
765: boolean seekForwardOnly,
766: boolean ignoreMetadata)
767: {
768: super.setInput(input, seekForwardOnly, ignoreMetadata);
769: Object get = getInput();
770: if (get instanceof InputStream)
771: dec = new GdkPixbufDecoder((InputStream) get);
772: else if (get instanceof DataInput)
773: dec = new GdkPixbufDecoder((DataInput) get);
774: else
775: throw new IllegalArgumentException("input object not supported: "
776: + get);
777: }
778:
779: public BufferedImage read(int imageIndex, ImageReadParam param)
780: throws IOException
781: {
782: return getBufferedImage ();
783: }
784: }
785: }