001: /*
002: * Copyright (c) 2005-2008 Substance Kirill Grouchnikov. All Rights Reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * o Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: *
010: * o Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * o Neither the name of Substance Kirill Grouchnikov nor the names of
015: * its contributors may be used to endorse or promote products derived
016: * from this software without specific prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
020: * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
021: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
022: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
025: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
026: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
027: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
028: * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029: */
030: package org.jvnet.substance.watermark;
031:
032: import java.awt.*;
033: import java.awt.image.BufferedImage;
034: import java.io.File;
035: import java.io.InputStream;
036: import java.net.URL;
037: import java.security.AccessControlException;
038:
039: import javax.imageio.IIOException;
040: import javax.imageio.ImageIO;
041: import javax.swing.JComponent;
042:
043: import org.jvnet.substance.utils.SubstanceCoreUtilities;
044: import org.jvnet.substance.utils.SubstanceConstants.ImageWatermarkKind;
045: import org.jvnet.substance.utils.params.ParamReader;
046:
047: /**
048: * Implementation of {@link SubstanceWatermark}, drawing specified image as
049: * watermark. This class is part of officially supported API.
050: *
051: * @author Kirill Grouchnikov
052: * @author Chris Hall
053: * @author Mark Haag
054: */
055: public class SubstanceImageWatermark implements SubstanceWatermark {
056: /**
057: * Watermark image (screen-sized).
058: */
059: protected static Image watermarkImage = null;
060:
061: /**
062: * Watermark image kind.
063: */
064: private static ImageWatermarkKind kind = ImageWatermarkKind.SCREEN_CENTER_SCALE;
065:
066: /**
067: * Watermark image opacity.
068: */
069: private static float opacity = 0.2f;
070:
071: /**
072: * The original image (as read from the disk / HTTP connection).
073: */
074: protected BufferedImage origImage;
075:
076: /**
077: * The original image location.
078: */
079: protected String origImageLocation;
080:
081: /**
082: * Simple constructor. Uses {@link ParamReader#getWatermarkImageProperty()}
083: * to retrieve the location of the image. Note that this constructor can
084: * throw AccessControlException if this class is used in unsecure JNLP
085: * environment. However, in this case we must have access to local disk in
086: * any case since we need to read the image.
087: *
088: * @param paramReader
089: * Substance parameter reader instance.
090: * @throws AccessControlException
091: * if this class is used in unsecure JNLP environment
092: */
093: public SubstanceImageWatermark(ParamReader paramReader)
094: throws AccessControlException {
095: this (paramReader.getWatermarkImageProperty());
096: }
097:
098: /**
099: * Creates an instance with specified image.
100: *
101: * @param imageLocation
102: * Image location. Can point to a local file or HTTP URL (needs
103: * to start with <code>http</code> in the later case).
104: */
105: public SubstanceImageWatermark(String imageLocation) {
106: if (imageLocation != null) {
107: try {
108: if (imageLocation.startsWith("http")) {
109: URL url = new URL(imageLocation);
110: BufferedImage tempImage = ImageIO.read(url);
111: this .origImage = SubstanceCoreUtilities
112: .createCompatibleImage(tempImage);
113: } else {
114: try {
115: this .origImage = SubstanceCoreUtilities
116: .createCompatibleImage(ImageIO
117: .read(new File(imageLocation)));
118: } catch (IIOException iioe) {
119: this .origImage = SubstanceCoreUtilities
120: .createCompatibleImage(ImageIO
121: .read(SubstanceImageWatermark.class
122: .getClassLoader()
123: .getResource(
124: imageLocation)));
125: }
126: }
127: } catch (Exception exc) {
128: // ignore - probably specified incorrect file
129: // or file is not image
130: exc.printStackTrace();
131: }
132: }
133: this .origImageLocation = imageLocation;
134: }
135:
136: /**
137: * Creates an instance from the specified input stream.
138: *
139: * @param inputStream
140: * Input stream.
141: */
142: public SubstanceImageWatermark(InputStream inputStream) {
143: if (inputStream != null) {
144: try {
145: BufferedImage tempImage = ImageIO.read(inputStream);
146: this .origImage = SubstanceCoreUtilities
147: .createCompatibleImage(tempImage);
148: } catch (Exception exc) {
149: // ignore - probably specified incorrect input stream
150: // or stream doesn't contain an image
151: exc.printStackTrace();
152: }
153: }
154: this .origImageLocation = null;
155: }
156:
157: /*
158: * (non-Javadoc)
159: *
160: * @see org.jvnet.substance.watermark.SubstanceWatermark#drawWatermarkImage(java.awt.Graphics,
161: * int, int, int, int)
162: */
163: public void drawWatermarkImage(Graphics graphics, Component c,
164: int x, int y, int width, int height) {
165: if (!c.isShowing())
166: return;
167: int dx = 0;
168: int dy = 0;
169:
170: Component topParent = null;
171: switch (getKind()) {
172: case SCREEN_CENTER_SCALE:
173: case SCREEN_TILE:
174: dx = c.getLocationOnScreen().x;
175: dy = c.getLocationOnScreen().y;
176: break;
177: case APP_ANCHOR:
178: case APP_TILE:
179: if (c instanceof JComponent) {
180: topParent = ((JComponent) c).getTopLevelAncestor();
181: } else {
182: Component comp = c;
183: while (comp.getParent() != null) {
184: comp = comp.getParent();
185: }
186: topParent = comp;
187: }
188: dx = c.getLocationOnScreen().x
189: - topParent.getLocationOnScreen().x;
190: dy = c.getLocationOnScreen().y
191: - topParent.getLocationOnScreen().y;
192: break;
193: case APP_CENTER:
194: if (c instanceof JComponent) {
195: topParent = ((JComponent) c).getTopLevelAncestor();
196: } else {
197: Component comp = c;
198: while (comp.getParent() != null) {
199: comp = comp.getParent();
200: }
201: topParent = comp;
202: }
203: dx = c.getLocationOnScreen().x
204: - topParent.getLocationOnScreen().x;
205: dy = c.getLocationOnScreen().y
206: - topParent.getLocationOnScreen().y;
207: dx -= (topParent.getWidth() / 2 - this .origImage.getWidth() / 2);
208: dy -= (topParent.getHeight() / 2 - this .origImage
209: .getHeight() / 2);
210: }
211:
212: graphics.drawImage(SubstanceImageWatermark.watermarkImage, x,
213: y, x + width, y + height, x + dx, y + dy, x + dx
214: + width, y + dy + height, null);
215: }
216:
217: /*
218: * (non-Javadoc)
219: *
220: * @see org.jvnet.substance.watermark.SubstanceWatermark#previewWatermark(java.awt.Graphics,
221: * int, int, int, int)
222: */
223: public void previewWatermark(Graphics g, int x, int y, int width,
224: int height) {
225: }
226:
227: /*
228: * (non-Javadoc)
229: *
230: * @see org.jvnet.substance.watermark.SubstanceWatermark#updateWatermarkImage()
231: */
232: public boolean updateWatermarkImage() {
233: if (this .origImage == null) {
234: return false;
235: }
236:
237: // fix by Chris for bug 67 - support for multiple screens
238: Rectangle virtualBounds = new Rectangle();
239: GraphicsEnvironment ge = GraphicsEnvironment
240: .getLocalGraphicsEnvironment();
241: GraphicsDevice[] gds = ge.getScreenDevices();
242: for (GraphicsDevice gd : gds) {
243: GraphicsConfiguration gc = gd.getDefaultConfiguration();
244: virtualBounds = virtualBounds.union(gc.getBounds());
245: }
246:
247: int screenWidth = virtualBounds.width;
248: int screenHeight = virtualBounds.height;
249:
250: int origImageWidth = this .origImage.getWidth();
251: int origImageHeight = this .origImage.getHeight();
252:
253: if (getKind() == ImageWatermarkKind.SCREEN_CENTER_SCALE) {
254: SubstanceImageWatermark.watermarkImage = SubstanceCoreUtilities
255: .getBlankImage(screenWidth, screenHeight);
256:
257: Graphics2D graphics = (Graphics2D) SubstanceImageWatermark.watermarkImage
258: .getGraphics().create();
259:
260: Composite comp = AlphaComposite.getInstance(
261: AlphaComposite.SRC_OVER, opacity);
262: graphics.setComposite(comp);
263:
264: // decide if need to scale or center
265: boolean isWidthFits = (origImageWidth <= screenWidth);
266: boolean isHeightFits = (origImageHeight <= screenHeight);
267:
268: // see of need to scale or simply center
269: if (isWidthFits && isHeightFits) {
270: graphics.drawImage(this .origImage,
271: (screenWidth - origImageWidth) / 2,
272: (screenHeight - origImageHeight) / 2, null);
273: graphics.dispose();
274: return true;
275: }
276: if (isWidthFits) {
277: // height doesn't fit
278: double scaleFact = (double) screenHeight
279: / (double) origImageHeight;
280: int dx = (int) (screenWidth - scaleFact
281: * origImageWidth) / 2;
282: // The following line is Mark Haag's to make the
283: // scaling less jaggy
284: graphics.setRenderingHint(
285: RenderingHints.KEY_INTERPOLATION,
286: RenderingHints.VALUE_INTERPOLATION_BICUBIC);
287: graphics.drawImage(this .origImage, dx, 0, screenWidth
288: - dx, screenHeight, 0, 0, origImageWidth,
289: origImageHeight, null);
290: graphics.dispose();
291: return true;
292: }
293: if (isHeightFits) {
294: // width doesn't fit
295: double scaleFact = (double) screenWidth
296: / (double) origImageWidth;
297: int dy = (int) (screenHeight - scaleFact
298: * origImageHeight) / 2;
299: // The following line is Mark Haag's to make the
300: // scaling less jaggy
301: graphics.setRenderingHint(
302: RenderingHints.KEY_INTERPOLATION,
303: RenderingHints.VALUE_INTERPOLATION_BICUBIC);
304: graphics.drawImage(this .origImage, 0, dy, screenWidth,
305: screenHeight - dy, 0, 0, origImageWidth,
306: origImageHeight, null);
307: graphics.dispose();
308: return true;
309: }
310: // none fits
311: double scaleFactY = (double) screenHeight
312: / (double) origImageHeight;
313: double scaleFactX = (double) screenWidth
314: / (double) origImageWidth;
315: double scaleFact = Math.min(scaleFactX, scaleFactY);
316: int dx = Math.max(0, (int) (screenWidth - scaleFact
317: * origImageWidth) / 2);
318: int dy = Math.max(0, (int) (screenHeight - scaleFact
319: * origImageHeight) / 2);
320: // The following line is Mark Haag's to make the
321: // scaling less jaggy
322: graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
323: RenderingHints.VALUE_INTERPOLATION_BICUBIC);
324: graphics.drawImage(this .origImage, dx, dy,
325: screenWidth - dx, screenHeight - dy, 0, 0,
326: origImageWidth, origImageHeight, null);
327: graphics.dispose();
328: return true;
329: }
330:
331: if ((getKind() == ImageWatermarkKind.SCREEN_TILE)
332: || (getKind() == ImageWatermarkKind.APP_TILE)) {
333: SubstanceImageWatermark.watermarkImage = SubstanceCoreUtilities
334: .getBlankImage(screenWidth, screenHeight);
335:
336: Graphics2D graphics = (Graphics2D) SubstanceImageWatermark.watermarkImage
337: .getGraphics().create();
338:
339: Composite comp = AlphaComposite.getInstance(
340: AlphaComposite.SRC_OVER, opacity);
341: graphics.setComposite(comp);
342:
343: int replicateX = 1 + screenWidth / origImageWidth;
344: int replicateY = 1 + screenHeight / origImageHeight;
345:
346: for (int i = 0; i < replicateX; i++) {
347: for (int j = 0; j < replicateY; j++) {
348: graphics
349: .drawImage(this .origImage, i
350: * origImageWidth, j
351: * origImageHeight, null);
352: }
353: }
354: graphics.dispose();
355: return true;
356: }
357:
358: if ((getKind() == ImageWatermarkKind.APP_ANCHOR)
359: || (getKind() == ImageWatermarkKind.APP_CENTER)) {
360: SubstanceImageWatermark.watermarkImage = SubstanceCoreUtilities
361: .getBlankImage(origImageWidth, origImageHeight);
362:
363: Graphics2D graphics = (Graphics2D) SubstanceImageWatermark.watermarkImage
364: .getGraphics().create();
365:
366: Composite comp = AlphaComposite.getInstance(
367: AlphaComposite.SRC_OVER, opacity);
368: graphics.setComposite(comp);
369:
370: graphics.drawImage(this .origImage, 0, 0, null);
371: graphics.dispose();
372: return true;
373: }
374:
375: return false;
376: }
377:
378: /*
379: * (non-Javadoc)
380: *
381: * @see org.jvnet.substance.watermark.SubstanceWatermark#getDisplayName()
382: */
383: public String getDisplayName() {
384: return SubstanceImageWatermark.getName();
385: }
386:
387: /**
388: * Returns the name of all watermarks of <code>this</code> class.
389: *
390: * @return The name of all watermarks of <code>this</code> class.
391: */
392: public static String getName() {
393: return "Image";
394: }
395:
396: /*
397: * (non-Javadoc)
398: *
399: * @see org.jvnet.substance.watermark.SubstanceWatermark#isDependingOnTheme()
400: */
401: public boolean isDependingOnTheme() {
402: return true;
403: }
404:
405: /*
406: * (non-Javadoc)
407: *
408: * @see org.jvnet.substance.watermark.SubstanceWatermark#dispose()
409: */
410: public void dispose() {
411: watermarkImage = null;
412: }
413:
414: /**
415: * Returns the location of the original image.
416: *
417: * @return The location of the original image.
418: */
419: public String getOrigImageLocation() {
420: return this .origImageLocation;
421: }
422:
423: /**
424: * Sets image watermark kind.
425: *
426: * @param kind
427: * Image watermark kind.
428: */
429: public static void setKind(ImageWatermarkKind kind) {
430: if (kind == null) {
431: throw new IllegalArgumentException(
432: "Can't pass null to SubstanceImageWatermark.setKind()");
433: }
434: SubstanceImageWatermark.kind = kind;
435: }
436:
437: /**
438: * Returns the image watermark kind.
439: *
440: * @return Image watermark kind.
441: */
442: public static ImageWatermarkKind getKind() {
443: return kind;
444: }
445:
446: /**
447: * Returns the image watermark opacity.
448: *
449: * @return Image watermark opacity.
450: */
451: public static float getOpacity() {
452: return opacity;
453: }
454:
455: /**
456: * Sets image watermark opacity.
457: *
458: * @param opacity
459: * Image watermark opacity.
460: * @throws IllegalArgumentException
461: * if the argument is not in 0.0-1.0 range.
462: */
463: public static void setOpacity(float opacity) {
464: if ((opacity < 0.0f) || (opacity > 1.0f)) {
465: throw new IllegalArgumentException(
466: "SubstanceImageWatermark.setOpacity() can get value in 0.0-1.0 range, was passed value "
467: + opacity);
468: }
469: SubstanceImageWatermark.opacity = opacity;
470: }
471: }
|