001: package org.zilonis.tool.ext.aerith.g2d;
002:
003: /*
004: *
005: * Copyright 2001-2004 The Apache Software Foundation
006: *
007: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
008: * use this file except in compliance with the License. You may obtain a copy of
009: * the License at
010: *
011: * http://www.apache.org/licenses/LICENSE-2.0
012: *
013: * Unless required by applicable law or agreed to in writing, software
014: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
015: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
016: * License for the specific language governing permissions and limitations under
017: * the License.
018: *
019: */
020:
021: import java.awt.Color;
022: import java.awt.Graphics;
023: import java.awt.Graphics2D;
024: import java.awt.GraphicsConfiguration;
025: import java.awt.GraphicsEnvironment;
026: import java.awt.Insets;
027: import java.awt.RenderingHints;
028: import java.awt.Transparency;
029: import java.awt.image.BufferedImage;
030: import java.awt.image.ColorModel;
031: import java.awt.image.ComponentSampleModel;
032: import java.awt.image.DataBuffer;
033: import java.awt.image.DataBufferByte;
034: import java.awt.image.DataBufferInt;
035: import java.awt.image.SampleModel;
036: import java.awt.image.SinglePixelPackedSampleModel;
037: import java.awt.image.WritableRaster;
038: import java.io.IOException;
039: import java.net.URL;
040: import javax.imageio.ImageIO;
041: import javax.swing.JComponent;
042:
043: public class GraphicsUtil {
044: private static GraphicsConfiguration configuration = GraphicsEnvironment
045: .getLocalGraphicsEnvironment().getDefaultScreenDevice()
046: .getDefaultConfiguration();
047:
048: private GraphicsUtil() {
049: }
050:
051: public static BufferedImage createCompatibleImage(int width,
052: int height) {
053: return configuration.createCompatibleImage(width, height);
054: }
055:
056: public static BufferedImage createTranslucentCompatibleImage(
057: int width, int height) {
058: return configuration.createCompatibleImage(width, height,
059: Transparency.TRANSLUCENT);
060: }
061:
062: public static BufferedImage loadCompatibleImage(URL resource)
063: throws IOException {
064: BufferedImage image = ImageIO.read(resource);
065: return toCompatibleImage(image);
066: }
067:
068: public static BufferedImage toCompatibleImage(BufferedImage image) {
069: BufferedImage compatibleImage = configuration
070: .createCompatibleImage(image.getWidth(), image
071: .getHeight(), Transparency.TRANSLUCENT);
072: Graphics g = compatibleImage.getGraphics();
073: g.drawImage(image, 0, 0, null);
074: g.dispose();
075: return compatibleImage;
076: }
077:
078: public static BufferedImage createThumbnail(BufferedImage image,
079: int requestedThumbSize) {
080: float ratio = (float) image.getWidth()
081: / (float) image.getHeight();
082: int width = image.getWidth();
083: BufferedImage thumb = image;
084:
085: do {
086: width /= 2;
087: if (width < requestedThumbSize) {
088: width = requestedThumbSize;
089: }
090:
091: BufferedImage temp = new BufferedImage(width,
092: (int) (width / ratio), BufferedImage.TYPE_INT_ARGB);
093: Graphics2D g2 = temp.createGraphics();
094: g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
095: RenderingHints.VALUE_INTERPOLATION_BILINEAR);
096: g2.drawImage(thumb, 0, 0, temp.getWidth(),
097: temp.getHeight(), null);
098: g2.dispose();
099:
100: thumb = temp;
101: } while (width != requestedThumbSize);
102:
103: return thumb;
104: }
105:
106: public static BufferedImage createThumbnail(BufferedImage image,
107: int newWidth, int newHeight) {
108: int width = image.getWidth();
109: int height = image.getHeight();
110: BufferedImage thumb = image;
111:
112: do {
113: if (width > newWidth) {
114: width /= 2;
115: if (width < newWidth) {
116: width = newWidth;
117: }
118: }
119:
120: if (height > newHeight) {
121: height /= 2;
122: if (height < newHeight) {
123: height = newHeight;
124: }
125: }
126:
127: BufferedImage temp = new BufferedImage(width, height,
128: BufferedImage.TYPE_INT_ARGB);
129: Graphics2D g2 = temp.createGraphics();
130: g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
131: RenderingHints.VALUE_INTERPOLATION_BILINEAR);
132: g2.drawImage(thumb, 0, 0, temp.getWidth(),
133: temp.getHeight(), null);
134: g2.dispose();
135:
136: thumb = temp;
137: } while (width != newWidth || height != newHeight);
138:
139: return thumb;
140: }
141:
142: /**
143: * Create a new ColorModel with it's alpha premultiplied state matching
144: * newAlphaPreMult.
145: *
146: * @param cm
147: * The ColorModel to change the alpha premult state of.
148: * @param newAlphaPreMult
149: * The new state of alpha premult.
150: * @return A new colorModel that has isAlphaPremultiplied() equal to
151: * newAlphaPreMult.
152: */
153: public static ColorModel coerceColorModel(ColorModel cm,
154: boolean newAlphaPreMult) {
155: if (cm.isAlphaPremultiplied() == newAlphaPreMult)
156: return cm;
157:
158: // Easiest way to build proper colormodel for new Alpha state...
159: // Eventually this should switch on known ColorModel types and
160: // only fall back on this hack when the CM type is unknown.
161: WritableRaster wr = cm.createCompatibleWritableRaster(1, 1);
162: return cm.coerceData(wr, newAlphaPreMult);
163: }
164:
165: /**
166: * Coerces data within a bufferedImage to match newAlphaPreMult, Note that
167: * this can not change the colormodel of bi so you
168: *
169: * @param wr
170: * The raster to change the state of.
171: * @param cm
172: * The colormodel currently associated with data in wr.
173: * @param newAlphaPreMult
174: * The desired state of alpha Premult for raster.
175: * @return A new colormodel that matches newAlphaPreMult.
176: */
177: public static ColorModel coerceData(WritableRaster wr,
178: ColorModel cm, boolean newAlphaPreMult) {
179:
180: // System.out.println("CoerceData: " + cm.isAlphaPremultiplied() +
181: // " Out: " + newAlphaPreMult);
182: if (!cm.hasAlpha())
183: // Nothing to do no alpha channel
184: return cm;
185:
186: if (cm.isAlphaPremultiplied() == newAlphaPreMult)
187: // nothing to do alpha state matches...
188: return cm;
189:
190: // System.out.println("CoerceData: " + wr.getSampleModel());
191:
192: if (newAlphaPreMult) {
193: multiplyAlpha(wr);
194: } else {
195: divideAlpha(wr);
196: }
197:
198: return coerceColorModel(cm, newAlphaPreMult);
199: }
200:
201: public static void multiplyAlpha(WritableRaster wr) {
202: if (is_BYTE_COMP_Data(wr.getSampleModel()))
203: mult_BYTE_COMP_Data(wr);
204: else if (is_INT_PACK_Data(wr.getSampleModel(), true))
205: mult_INT_PACK_Data(wr);
206: else {
207: int[] pixel = null;
208: int bands = wr.getNumBands();
209: float norm = 1f / 255f;
210: int x0, x1, y0, y1, a, b;
211: float alpha;
212: x0 = wr.getMinX();
213: x1 = x0 + wr.getWidth();
214: y0 = wr.getMinY();
215: y1 = y0 + wr.getHeight();
216: for (int y = y0; y < y1; y++)
217: for (int x = x0; x < x1; x++) {
218: pixel = wr.getPixel(x, y, pixel);
219: a = pixel[bands - 1];
220: if ((a >= 0) && (a < 255)) {
221: alpha = a * norm;
222: for (b = 0; b < bands - 1; b++)
223: pixel[b] = (int) (pixel[b] * alpha + 0.5f);
224: wr.setPixel(x, y, pixel);
225: }
226: }
227: }
228: }
229:
230: public static void divideAlpha(WritableRaster wr) {
231: if (is_BYTE_COMP_Data(wr.getSampleModel()))
232: divide_BYTE_COMP_Data(wr);
233: else if (is_INT_PACK_Data(wr.getSampleModel(), true))
234: divide_INT_PACK_Data(wr);
235: else {
236: int x0, x1, y0, y1, a, b;
237: float ialpha;
238: int bands = wr.getNumBands();
239: int[] pixel = null;
240:
241: x0 = wr.getMinX();
242: x1 = x0 + wr.getWidth();
243: y0 = wr.getMinY();
244: y1 = y0 + wr.getHeight();
245: for (int y = y0; y < y1; y++)
246: for (int x = x0; x < x1; x++) {
247: pixel = wr.getPixel(x, y, pixel);
248: a = pixel[bands - 1];
249: if ((a > 0) && (a < 255)) {
250: ialpha = 255 / (float) a;
251: for (b = 0; b < bands - 1; b++)
252: pixel[b] = (int) (pixel[b] * ialpha + 0.5f);
253: wr.setPixel(x, y, pixel);
254: }
255: }
256: }
257: }
258:
259: public static boolean is_INT_PACK_Data(SampleModel sm,
260: boolean requireAlpha) {
261: // Check ColorModel is of type DirectColorModel
262: if (!(sm instanceof SinglePixelPackedSampleModel))
263: return false;
264:
265: // Check transfer type
266: if (sm.getDataType() != DataBuffer.TYPE_INT)
267: return false;
268:
269: SinglePixelPackedSampleModel sppsm;
270: sppsm = (SinglePixelPackedSampleModel) sm;
271:
272: int[] masks = sppsm.getBitMasks();
273: if (masks.length == 3) {
274: if (requireAlpha)
275: return false;
276: } else if (masks.length != 4)
277: return false;
278:
279: if (masks[0] != 0x00ff0000)
280: return false;
281: if (masks[1] != 0x0000ff00)
282: return false;
283: if (masks[2] != 0x000000ff)
284: return false;
285: return !((masks.length == 4) && (masks[3] != 0xff000000));
286:
287: }
288:
289: public static boolean is_BYTE_COMP_Data(SampleModel sm) {
290: // Check ColorModel is of type DirectColorModel
291: if (!(sm instanceof ComponentSampleModel))
292: return false;
293:
294: // Check transfer type
295: return sm.getDataType() == DataBuffer.TYPE_BYTE;
296:
297: }
298:
299: protected static void divide_INT_PACK_Data(WritableRaster wr) {
300: // System.out.println("Divide Int");
301:
302: SinglePixelPackedSampleModel sppsm;
303: sppsm = (SinglePixelPackedSampleModel) wr.getSampleModel();
304:
305: final int width = wr.getWidth();
306:
307: final int scanStride = sppsm.getScanlineStride();
308: DataBufferInt db = (DataBufferInt) wr.getDataBuffer();
309: final int base = (db.getOffset() + sppsm.getOffset(wr.getMinX()
310: - wr.getSampleModelTranslateX(), wr.getMinY()
311: - wr.getSampleModelTranslateY()));
312: int pixel, a, aFP;
313: // Access the pixel data array
314: final int pixels[] = db.getBankData()[0];
315: for (int y = 0; y < wr.getHeight(); y++) {
316: int sp = base + y * scanStride;
317: final int end = sp + width;
318: while (sp < end) {
319: pixel = pixels[sp];
320: a = pixel >>> 24;
321: if (a <= 0) {
322: pixels[sp] = 0x00FFFFFF;
323: } else if (a < 255) {
324: aFP = (0x00FF0000 / a);
325: pixels[sp] = ((a << 24)
326: | (((((pixel & 0xFF0000) >> 16) * aFP) & 0xFF0000))
327: | (((((pixel & 0x00FF00) >> 8) * aFP) & 0xFF0000) >> 8) | (((((pixel & 0x0000FF)) * aFP) & 0xFF0000) >> 16));
328: }
329: sp++;
330: }
331: }
332: }
333:
334: protected static void mult_INT_PACK_Data(WritableRaster wr) {
335: // System.out.println("Multiply Int: " + wr);
336:
337: SinglePixelPackedSampleModel sppsm;
338: sppsm = (SinglePixelPackedSampleModel) wr.getSampleModel();
339:
340: final int width = wr.getWidth();
341:
342: final int scanStride = sppsm.getScanlineStride();
343: DataBufferInt db = (DataBufferInt) wr.getDataBuffer();
344: final int base = (db.getOffset() + sppsm.getOffset(wr.getMinX()
345: - wr.getSampleModelTranslateX(), wr.getMinY()
346: - wr.getSampleModelTranslateY()));
347: // Access the pixel data array
348: final int pixels[] = db.getBankData()[0];
349: for (int y = 0; y < wr.getHeight(); y++) {
350: int sp = base + y * scanStride;
351: final int end = sp + width;
352: while (sp < end) {
353: int pixel = pixels[sp];
354: int a = pixel >>> 24;
355: if ((a >= 0) && (a < 255)) {
356: pixels[sp] = ((a << 24)
357: | ((((pixel & 0xFF0000) * a) >> 8) & 0xFF0000)
358: | ((((pixel & 0x00FF00) * a) >> 8) & 0x00FF00) | ((((pixel & 0x0000FF) * a) >> 8) & 0x0000FF));
359: }
360: sp++;
361: }
362: }
363: }
364:
365: protected static void divide_BYTE_COMP_Data(WritableRaster wr) {
366: // System.out.println("Multiply Int: " + wr);
367:
368: ComponentSampleModel csm;
369: csm = (ComponentSampleModel) wr.getSampleModel();
370:
371: final int width = wr.getWidth();
372:
373: final int scanStride = csm.getScanlineStride();
374: final int pixStride = csm.getPixelStride();
375: final int[] bandOff = csm.getBandOffsets();
376:
377: DataBufferByte db = (DataBufferByte) wr.getDataBuffer();
378: final int base = (db.getOffset() + csm.getOffset(wr.getMinX()
379: - wr.getSampleModelTranslateX(), wr.getMinY()
380: - wr.getSampleModelTranslateY()));
381:
382: int a = 0;
383: int aOff = bandOff[bandOff.length - 1];
384: int bands = bandOff.length - 1;
385: int b, i;
386: // Access the pixel data array
387: final byte pixels[] = db.getBankData()[0];
388: for (int y = 0; y < wr.getHeight(); y++) {
389: int sp = base + y * scanStride;
390: final int end = sp + width * pixStride;
391: while (sp < end) {
392: a = pixels[sp + aOff] & 0xFF;
393: if (a == 0) {
394: for (b = 0; b < bands; b++)
395: pixels[sp + bandOff[b]] = (byte) 0xFF;
396: } else if (a < 255) {
397: int aFP = (0x00FF0000 / a);
398: for (b = 0; b < bands; b++) {
399: i = sp + bandOff[b];
400: pixels[i] = (byte) (((pixels[i] & 0xFF) * aFP) >>> 16);
401: }
402: }
403: sp += pixStride;
404: }
405: }
406: }
407:
408: protected static void mult_BYTE_COMP_Data(WritableRaster wr) {
409: // System.out.println("Multiply Int: " + wr);
410:
411: ComponentSampleModel csm;
412: csm = (ComponentSampleModel) wr.getSampleModel();
413:
414: final int width = wr.getWidth();
415:
416: final int scanStride = csm.getScanlineStride();
417: final int pixStride = csm.getPixelStride();
418: final int[] bandOff = csm.getBandOffsets();
419:
420: DataBufferByte db = (DataBufferByte) wr.getDataBuffer();
421: final int base = (db.getOffset() + csm.getOffset(wr.getMinX()
422: - wr.getSampleModelTranslateX(), wr.getMinY()
423: - wr.getSampleModelTranslateY()));
424:
425: int a = 0;
426: int aOff = bandOff[bandOff.length - 1];
427: int bands = bandOff.length - 1;
428: int b, i;
429:
430: // Access the pixel data array
431: final byte pixels[] = db.getBankData()[0];
432: for (int y = 0; y < wr.getHeight(); y++) {
433: int sp = base + y * scanStride;
434: final int end = sp + width * pixStride;
435: while (sp < end) {
436: a = pixels[sp + aOff] & 0xFF;
437: if (a != 0xFF)
438: for (b = 0; b < bands; b++) {
439: i = sp + bandOff[b];
440: pixels[i] = (byte) (((pixels[i] & 0xFF) * a) >> 8);
441: }
442: sp += pixStride;
443: }
444: }
445: }
446:
447: public static String getColorHexString(Color c) {
448: String colString = Integer.toHexString(c.getRGB() & 0xffffff);
449: return "#000000".substring(0, 7 - colString.length()).concat(
450: colString);
451: }
452:
453: /**
454: * Draws an image on top of a component by doing a 3x3 grid stretch of the image
455: * using the specified insets.
456: */
457: public static void tileStretchPaint(Graphics g, JComponent comp,
458: BufferedImage img, Insets ins) {
459:
460: int left = ins.left;
461: int right = ins.right;
462: int top = ins.top;
463: int bottom = ins.bottom;
464:
465: // top
466: g.drawImage(img, 0, 0, left, top, 0, 0, left, top, null);
467: g.drawImage(img, left, 0, comp.getWidth() - right, top, left,
468: 0, img.getWidth() - right, top, null);
469: g.drawImage(img, comp.getWidth() - right, 0, comp.getWidth(),
470: top, img.getWidth() - right, 0, img.getWidth(), top,
471: null);
472:
473: // middle
474: g.drawImage(img, 0, top, left, comp.getHeight() - bottom, 0,
475: top, left, img.getHeight() - bottom, null);
476:
477: g.drawImage(img, left, top, comp.getWidth() - right, comp
478: .getHeight()
479: - bottom, left, top, img.getWidth() - right, img
480: .getHeight()
481: - bottom, null);
482:
483: g.drawImage(img, comp.getWidth() - right, top, comp.getWidth(),
484: comp.getHeight() - bottom, img.getWidth() - right, top,
485: img.getWidth(), img.getHeight() - bottom, null);
486:
487: // bottom
488: g.drawImage(img, 0, comp.getHeight() - bottom, left, comp
489: .getHeight(), 0, img.getHeight() - bottom, left, img
490: .getHeight(), null);
491: g
492: .drawImage(img, left, comp.getHeight() - bottom, comp
493: .getWidth()
494: - right, comp.getHeight(), left, img
495: .getHeight()
496: - bottom, img.getWidth() - right, img
497: .getHeight(), null);
498: g.drawImage(img, comp.getWidth() - right, comp.getHeight()
499: - bottom, comp.getWidth(), comp.getHeight(), img
500: .getWidth()
501: - right, img.getHeight() - bottom, img.getWidth(), img
502: .getHeight(), null);
503: }
504:
505: }
|