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