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