/* * sulky-modules - several general-purpose modules. * Copyright (C) 2007-2014 Joern Huxhorn * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ /* * Copyright 2007-2014 Joern Huxhorn * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package de.huxhorn.sulky.swing; import java.awt.AlphaComposite; import java.awt.Composite; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; import java.awt.GraphicsEnvironment; import java.awt.Shape; import java.awt.Transparency; import java.awt.image.BufferedImage; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.io.IOException; import java.net.URL; import javax.imageio.ImageIO; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Inspired by Filthy Rich Clients... */ public final class GraphicsUtilities { private GraphicsUtilities() {} private static GraphicsConfiguration getConfiguration() { return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration(); } public static BufferedImage createCompatibleImage(BufferedImage image) { return createCompatibleImage(image, image.getWidth(), image.getHeight()); } public static BufferedImage createCompatibleImage(BufferedImage image, int width, int height) { return getConfiguration().createCompatibleImage(width, height, image.getTransparency()); } public static BufferedImage createCompatibleImage(int width, int height, int transparency) { return getConfiguration().createCompatibleImage(width, height, transparency); } public static BufferedImage createOpaqueCompatibleImage(int width, int height) { return createCompatibleImage(width, height, Transparency.OPAQUE); } public static BufferedImage createTranslucentCompatibleImage(int width, int height) { return createCompatibleImage(width, height, Transparency.TRANSLUCENT); } public static BufferedImage toCompatibleImage(BufferedImage image) { GraphicsConfiguration gc = getConfiguration(); if(image.getColorModel().equals(gc.getColorModel())) { return image; } return createCompatibleCopy(image); } public static BufferedImage createCompatibleCopy(BufferedImage image) { BufferedImage result = createCompatibleImage(image.getWidth(), image.getHeight(), image.getTransparency()); Graphics g = result.getGraphics(); g.drawImage(image, 0, 0, null); g.dispose(); return result; } /** * Draws the highlight for the given shape. The highlight is painted using the current color of g2. * * @param g2 the graphics context. * @param s the shape. * @param size the size of the highlight. * @param opacity the opacity of the highlight. */ public static void drawHighlight(Graphics2D g2, Shape s, int size, float opacity) { final Logger logger = LoggerFactory.getLogger(GraphicsUtilities.class); if(Float.compare(opacity, 0.0f) == 0) { // return if transparent return; } Composite c = g2.getComposite(); for(int i = -size; i <= size; i++) { for(int j = -size; j <= size; j++) { float distance = i * i + j * j; float alpha = opacity; if(Float.compare(distance, 0.0f) > 0) { alpha = (1.0f / distance) * opacity * size; if(logger.isDebugEnabled()) logger.debug("Calculated alpha: {}", alpha); } if(alpha > 1.0) { if(logger.isDebugEnabled()) logger.debug("Corrected alpha: {}", alpha); alpha = 1.0f; } //g2.setComposite(AlphaComposite.SrcOver.derive(alpha)); g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha)); g2.translate(i, j); g2.fill(s); g2.translate(-i, -j); } } g2.setComposite(c); } /** * <p>Returns an array of pixels, stored as integers, from a * <code>BufferedImage</code>. The pixels are grabbed from a rectangular * area defined by a location and two dimensions. Calling this method on * an image of type different from <code>BufferedImage.TYPE_INT_ARGB</code> * and <code>BufferedImage.TYPE_INT_RGB</code> will unmanage the image.</p> * * @param img the source image * @param x the x location at which to start grabbing pixels * @param y the y location at which to start grabbing pixels * @param w the width of the rectangle of pixels to grab * @param h the height of the rectangle of pixels to grab * @param pixels a pre-allocated array of pixels of size w*h; can be null * @return <code>pixels</code> if non-null, a new array of integers * otherwise * @throws IllegalArgumentException is <code>pixels</code> is non-null and * of length < w*h */ public static int[] getPixels(BufferedImage img, int x, int y, int w, int h, int[] pixels) { if(w == 0 || h == 0) { return new int[0]; } if(pixels == null) { pixels = new int[w * h]; } else if(pixels.length < w * h) { throw new IllegalArgumentException("pixels array must have a length" + " >= w*h"); } int imageType = img.getType(); if(imageType == BufferedImage.TYPE_INT_ARGB || imageType == BufferedImage.TYPE_INT_RGB) { Raster raster = img.getRaster(); return (int[]) raster.getDataElements(x, y, w, h, pixels); } // Unmanages the image return img.getRGB(x, y, w, h, pixels, 0, w); } /** * <p>Writes a rectangular area of pixels in the destination * <code>BufferedImage</code>. Calling this method on * an image of type different from <code>BufferedImage.TYPE_INT_ARGB</code> * and <code>BufferedImage.TYPE_INT_RGB</code> will unmanage the image.</p> * * @param img the destination image * @param x the x location at which to start storing pixels * @param y the y location at which to start storing pixels * @param w the width of the rectangle of pixels to store * @param h the height of the rectangle of pixels to store * @param pixels an array of pixels, stored as integers * @throws IllegalArgumentException is <code>pixels</code> is non-null and * of length < w*h */ public static void setPixels(BufferedImage img, int x, int y, int w, int h, int[] pixels) { if(pixels == null || w == 0 || h == 0) { return; } else if(pixels.length < w * h) { throw new IllegalArgumentException("pixels array must have a length" + " >= w*h"); } int imageType = img.getType(); if(imageType == BufferedImage.TYPE_INT_ARGB || imageType == BufferedImage.TYPE_INT_RGB) { WritableRaster raster = img.getRaster(); raster.setDataElements(x, y, w, h, pixels); } else { // Unmanages the image img.setRGB(x, y, w, h, pixels, 0, w); } } public static BufferedImage loadCompatibleImage(URL resource) throws IOException { BufferedImage image = ImageIO.read(resource); return toCompatibleImage(image); } }