001: /*
002: * $RCSfile: ImageCanvas.java,v $
003: *
004: * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
005: *
006: * Use is subject to license terms.
007: *
008: * $Revision: 1.1 $
009: * $Date: 2005/02/11 04:57:58 $
010: * $State: Exp $
011: */
012: package javax.media.jai.widget;
013:
014: import java.awt.Canvas;
015: import java.awt.Color;
016: import java.awt.Dimension;
017: import java.awt.Graphics2D;
018: import java.awt.Graphics;
019: import java.awt.Image;
020: import java.awt.Point;
021: import java.awt.Rectangle;
022: import java.awt.color.ColorSpace;
023: import java.awt.geom.AffineTransform;
024: import java.awt.image.BufferedImage;
025: import java.awt.image.ColorModel;
026: import java.awt.image.ComponentColorModel;
027: import java.awt.image.ComponentSampleModel;
028: import java.awt.image.DataBuffer;
029: import java.awt.image.DataBufferByte;
030: import java.awt.image.MemoryImageSource;
031: import java.awt.image.PixelInterleavedSampleModel;
032: import java.awt.image.Raster;
033: import java.awt.image.RenderedImage;
034: import java.awt.image.SampleModel;
035: import java.awt.image.WritableRaster;
036: import java.awt.image.renderable.ParameterBlock;
037: import java.awt.peer.ComponentPeer;
038: import java.util.HashSet;
039: import java.util.Iterator;
040: import javax.media.jai.Interpolation;
041: import javax.media.jai.InterpolationNearest;
042: import javax.media.jai.JAI;
043: import javax.media.jai.PlanarImage;
044:
045: /**
046: * A simple output widget for a RenderedImage. ImageCanvas subclasses
047: * java.awt.Canvas, and can be used in any context that calls for a
048: * Canvas. It monitors resize and update events and automatically
049: * requests tiles from its source on demand. Any displayed area outside
050: * the image is displayed in grey.
051: *
052: * <p> There is currently no policy regarding what sorts of widgets,
053: * if any, will be part of JAI.
054: *
055: * <p> Due to the limitations of BufferedImage, only TYPE_BYTE of band
056: * 1, 2, 3, 4, and TYPE_USHORT of band 1, 2, 3 images can be displayed
057: * using this widget.
058: *
059: *
060: * <p>
061: * This class has been deprecated. The source
062: * code has been moved to the samples/widget
063: * directory. These widgets are no longer
064: * supported.
065: *
066: * @deprecated as of JAI 1.1
067: */
068: public class ImageCanvas extends Canvas {
069:
070: /** The source RenderedImage. */
071: protected RenderedImage im;
072: /** The image's SampleModel. */
073: protected SampleModel sampleModel;
074: /** The image's ColorModel or one we supply. */
075: protected ColorModel colorModel;
076:
077: /** The image's min X tile. */
078: protected int minTileX;
079: /** The image's max X tile. */
080: protected int maxTileX;
081: /** The image's min Y tile. */
082: protected int minTileY;
083: /** The image's max Y tile. */
084: protected int maxTileY;
085: /** The image's tile width. */
086: protected int tileWidth;
087: /** The image's tile height. */
088: protected int tileHeight;
089: /** The image's tile grid X offset. */
090: protected int tileGridXOffset;
091: /** The image's tile grid Y offset. */
092: protected int tileGridYOffset;
093:
094: protected int imWidth;
095: protected int imHeight;
096:
097: /** used to center image in it's container */
098: protected int padX;
099: protected int padY;
100:
101: protected boolean drawBorder = false;
102:
103: /** The pixel to display in the upper left corner or the canvas. */
104: protected int originX;
105: /** The pixel to display in the upper left corner or the canvas. */
106: protected int originY;
107: /** The width of the canvas. */
108: protected int canvasWidth = 0;
109: /** The height of the canvas. */
110: protected int canvasHeight = 0;
111:
112: private Color grayColor = new Color(192, 192, 192);
113:
114: private Color backgroundColor = null;
115:
116: /** Initializes the ImageCanvas. */
117: private synchronized void initialize() {
118: int mx = im.getMinX();
119: int my = im.getMinY();
120: if ((mx < 0) || (my < 0)) {
121: ParameterBlock pb = new ParameterBlock();
122: pb.addSource(im);
123: pb.add((float) Math.max(-mx, 0));
124: pb.add((float) Math.max(-my, 0));
125: pb.add(new InterpolationNearest());
126: im = JAI.create("translate", pb, null);
127: }
128:
129: this .sampleModel = im.getSampleModel();
130:
131: // First check whether the opimage has already set a suitable ColorModel
132: this .colorModel = im.getColorModel();
133: if (this .colorModel == null) {
134: // If not, then create one.
135: this .colorModel = PlanarImage.createColorModel(im
136: .getSampleModel());
137: if (this .colorModel == null) {
138: throw new IllegalArgumentException(JaiI18N
139: .getString("ImageCanvas0"));
140: }
141: }
142:
143: Object col = im.getProperty("background_color");
144: if (col != Image.UndefinedProperty) {
145: backgroundColor = (Color) col;
146: }
147:
148: minTileX = im.getMinTileX();
149: maxTileX = im.getMinTileX() + im.getNumXTiles() - 1;
150: minTileY = im.getMinTileY();
151: maxTileY = im.getMinTileY() + im.getNumYTiles() - 1;
152: tileWidth = im.getTileWidth();
153: tileHeight = im.getTileHeight();
154: tileGridXOffset = im.getTileGridXOffset();
155: tileGridYOffset = im.getTileGridYOffset();
156:
157: imWidth = im.getMinX() + im.getWidth();
158: imHeight = im.getMinY() + im.getHeight();
159:
160: originX = originY = 0;
161: }
162:
163: /**
164: * Constructs an ImageCanvas to display a RenderedImage.
165: *
166: * @param im a RenderedImage to be displayed.
167: * @param drawBorder true if a raised border is desired.
168: */
169: public ImageCanvas(RenderedImage im, boolean drawBorder) {
170: this .im = im;
171: this .drawBorder = drawBorder;
172: initialize();
173: }
174:
175: /**
176: * Constructs an ImageCanvas to display a RenderedImage.
177: *
178: * @param im a RenderedImage to be displayed.
179: */
180: public ImageCanvas(RenderedImage im) {
181: this (im, false);
182: }
183:
184: public void addNotify() {
185: super .addNotify();
186: initialize();
187: }
188:
189: /** Changes the source image to a new RenderedImage. */
190: public synchronized void set(RenderedImage im) {
191: this .im = im;
192: initialize();
193: repaint();
194: }
195:
196: /** Changes the pixel to set Origin at x,y */
197: public void setOrigin(int x, int y) {
198: padX = 0;
199: padY = 0;
200: originX = x;
201: originY = y;
202: repaint();
203: }
204:
205: public int getXOrigin() {
206: return originX;
207: }
208:
209: public int getYOrigin() {
210: return originY;
211: }
212:
213: public int getXPad() {
214: return padX;
215: }
216:
217: public int getYPad() {
218: return padY;
219: }
220:
221: public Dimension getMinimumSize() {
222: return new Dimension(im.getMinX() + im.getWidth()
223: + (drawBorder ? 4 : 0), im.getMinY() + im.getHeight()
224: + (drawBorder ? 4 : 0));
225: }
226:
227: public Dimension getPreferredSize() {
228: return getMinimumSize();
229: }
230:
231: public Dimension getMaximumSize() {
232: return getMinimumSize();
233: }
234:
235: /** Records a new size. Called by the AWT. */
236: public void setBounds(int x, int y, int width, int height) {
237: super .setBounds(x, y, width, height);
238: canvasWidth = width;
239: canvasHeight = height;
240:
241: padX = Math.max(
242: (canvasWidth - imWidth - (drawBorder ? 4 : 0)) / 2, 0);
243: padY = Math
244: .max(
245: (canvasHeight - imHeight - (drawBorder ? 4 : 0)) / 2,
246: 0);
247: }
248:
249: private int XtoTileX(int x) {
250: return (int) Math.floor((double) (x - tileGridXOffset)
251: / tileWidth);
252: }
253:
254: private int YtoTileY(int y) {
255: return (int) Math.floor((double) (y - tileGridYOffset)
256: / tileHeight);
257: }
258:
259: private int TileXtoX(int tx) {
260: return tx * tileWidth + tileGridXOffset;
261: }
262:
263: private int TileYtoY(int ty) {
264: return ty * tileHeight + tileGridYOffset;
265: }
266:
267: /**
268: * There is no need to erase prior to drawing, so we override the
269: * default update method to simply call paint().
270: */
271: public void update(Graphics g) {
272: paint(g);
273: }
274:
275: /**
276: * Paint the image onto a Graphics object. The painting is
277: * performed tile-by-tile, and includes a grey region covering the
278: * unused portion of image tiles as well as the general
279: * background.
280: */
281: public synchronized void paint(Graphics g) {
282: if (im == null) {
283: return;
284: }
285:
286: Graphics2D g2D = null;
287: if (g instanceof Graphics2D) {
288: g2D = (Graphics2D) g;
289: } else {
290: System.err.println(JaiI18N.getString("ImageCanvas1"));
291: return;
292: }
293:
294: Color saveColor = g2D.getColor();
295:
296: if (drawBorder) {
297: g.setColor(new Color(171, 171, 171));
298: g.draw3DRect(padX, padY, imWidth + 3, imHeight + 3, true);
299: g.draw3DRect(padX + 1, padY + 1, imWidth + 1, imHeight + 1,
300: true);
301: }
302:
303: // Get the clipping rectangle and translate it into image coordinates.
304: Rectangle clipBounds = g.getClipBounds();
305: if (clipBounds == null) {
306: clipBounds = new Rectangle(0, 0, canvasWidth, canvasHeight);
307: }
308:
309: int border = drawBorder ? 2 : 0;
310: int transX = padX + border - originX;
311: int transY = padY + border - originY;
312:
313: clipBounds.translate(-transX, -transY);
314:
315: // Determine the extent of the clipping region in tile coordinates.
316: int txmin, txmax, tymin, tymax;
317:
318: txmin = XtoTileX(clipBounds.x);
319: txmin = Math.max(txmin, minTileX);
320: txmin = Math.min(txmin, maxTileX);
321:
322: txmax = XtoTileX(clipBounds.x + clipBounds.width - 1);
323: txmax = Math.max(txmax, minTileX);
324: txmax = Math.min(txmax, maxTileX);
325:
326: tymin = YtoTileY(clipBounds.y);
327: tymin = Math.max(tymin, minTileY);
328: tymin = Math.min(tymin, maxTileY);
329:
330: tymax = YtoTileY(clipBounds.y + clipBounds.height - 1);
331: tymax = Math.max(tymax, minTileY);
332: tymax = Math.min(tymax, maxTileY);
333:
334: if (backgroundColor != null) {
335: g2D.setColor(backgroundColor);
336: } else {
337: // Draw grey over unused area
338: g2D.setColor(grayColor);
339: }
340:
341: int xmin = im.getMinX();
342: int xmax = im.getMinX() + im.getWidth();
343: int ymin = im.getMinY();
344: int ymax = im.getMinY() + im.getHeight();
345: int screenX = clipBounds.x + clipBounds.width;
346: int screenY = clipBounds.y + clipBounds.height;
347:
348: // Left
349: if (xmin > clipBounds.x) {
350: g2D.fillRect(clipBounds.x + transX, clipBounds.y + transY,
351: xmin - clipBounds.x, clipBounds.height);
352: }
353:
354: // Right
355: if (xmax < screenX) {
356: g2D.fillRect(xmax + transX, clipBounds.y + transY, screenX
357: - xmax, clipBounds.height);
358: }
359:
360: // Top
361: if (ymin > clipBounds.y) {
362: g2D.fillRect(xmin + transX, clipBounds.y + transY, xmax
363: - xmin, ymin - clipBounds.y);
364: }
365:
366: // Bottom
367: if (ymax < screenY) {
368: g2D.fillRect(xmin + transX, ymax + transY, xmax - xmin,
369: screenY - ymax);
370: }
371:
372: // needed for clipping (crop op)
373: g2D.setClip(new Rectangle(transX + im.getMinX(), transY
374: + im.getMinY(), im.getWidth(), im.getHeight()));
375:
376: // Get all tiles which overlap the clipping region.
377: Point[] tileIndices = new Point[(txmax - txmin + 1)
378: * (tymax - tymin + 1)];
379: int index = 0;
380: for (int tj = tymin; tj <= tymax; tj++) {
381: for (int ti = txmin; ti <= txmax; ti++) {
382: tileIndices[index++] = new Point(ti, tj);
383: }
384: }
385: Raster[] tiles = PlanarImage.wrapRenderedImage(im).getTiles(
386: tileIndices);
387:
388: // Loop over tiles within the clipping region
389: int numTiles = tiles.length;
390: for (int tileNum = 0; tileNum < numTiles; tileNum++) {
391: Raster tile = tiles[tileNum];
392:
393: int tx = tile.getMinX();
394: int ty = tile.getMinY();
395:
396: if (tile != null) {
397: WritableRaster wr = tile instanceof WritableRaster ? ((WritableRaster) tile)
398: .createWritableTranslatedChild(0, 0)
399: : tile.createWritableRaster(sampleModel, tile
400: .getDataBuffer(), new Point(0, 0));
401:
402: BufferedImage bi = new BufferedImage(colorModel, wr,
403: colorModel.isAlphaPremultiplied(), null);
404:
405: AffineTransform transform = AffineTransform
406: .getTranslateInstance(tx + transX, ty + transY);
407: if (backgroundColor != null) {
408: g2D.fillRect(tx + transX, ty + transY, tileWidth,
409: tileHeight);
410: }
411: g2D.drawRenderedImage(bi, transform);
412: }
413: }
414:
415: // Restore color
416: g2D.setColor(saveColor);
417: notifyPaintListeners(g2D);
418: }
419:
420: /**
421: * An interface used to notify listeners during a <code>paint</code>
422: * just after the image has been painted on the image canvas. This
423: * allows registered listeners to draw additional graphics on top
424: * of the image.
425: *
426: * @since JAI 1.1
427: */
428: public interface PaintListener {
429:
430: /**
431: * Called from <code>ImageCanvas.paint</code> just after
432: * the image has been drawn on the canvas.
433: */
434: public void paint(ImageCanvas ic, Graphics g);
435: }
436:
437: private HashSet paintListeners = new HashSet();
438:
439: /**
440: * Adds the specified <code>PaintListener</code> to the canvas.
441: *
442: * @since JAI 1.1
443: */
444: public void addPaintListener(PaintListener pl) {
445: paintListeners.add(pl);
446: }
447:
448: /**
449: * Removes the specified <code>PaintListener</code> from the canvas.
450: *
451: * @since JAI 1.1
452: */
453: public void removePaintListener(PaintListener pl) {
454: paintListeners.remove(pl);
455: }
456:
457: private void notifyPaintListeners(Graphics g) {
458:
459: Iterator it = paintListeners.iterator();
460:
461: while (it.hasNext()) {
462: ((PaintListener) it.next()).paint(this, g);
463: }
464: }
465: }
|