1 /* 2 * Copyright (c) 1995, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.awt.image; 27 28 import java.awt.Image; 29 import java.awt.image.ImageFilter; 30 import java.awt.image.ImageConsumer; 31 import java.awt.image.ImageProducer; 32 import java.util.Hashtable; 33 import java.awt.image.ColorModel; 34 import java.util.Arrays; 35 import java.util.Collections; 36 import java.util.LinkedList; 37 import java.util.List; 38 import java.util.stream.Collectors; 39 import sun.awt.image.MultiResolutionToolkitImage; 40 41 /** 42 * This class is an implementation of the ImageProducer interface which 43 * takes an existing image and a filter object and uses them to produce 44 * image data for a new filtered version of the original image. 45 * Here is an example which filters an image by swapping the red and 46 * blue components: 47 * <pre> 48 * 49 * Image src = getImage("doc:///demo/images/duke/T1.gif"); 50 * ImageFilter colorfilter = new RedBlueSwapFilter(); 51 * Image img = createImage(new FilteredImageSource(src.getSource(), 52 * colorfilter)); 53 * 54 * </pre> 55 * 56 * @see ImageProducer 57 * 58 * @author Jim Graham 59 */ 60 public class FilteredImageSource implements ImageProducer { 61 ImageProducer src; 62 ImageFilter filter; 63 64 /** 65 * Constructs an ImageProducer object from an existing ImageProducer 66 * and a filter object. 67 * @param orig the specified {@code ImageProducer} 68 * @param imgf the specified {@code ImageFilter} 69 * @see ImageFilter 70 * @see java.awt.Component#createImage 71 */ 72 public FilteredImageSource(ImageProducer orig, ImageFilter imgf) { 73 src = orig; 74 filter = imgf; 75 } 76 77 private Hashtable<ImageConsumer, ImageFilter> proxies; 78 79 /** 80 * Adds the specified {@code ImageConsumer} 81 * to the list of consumers interested in data for the filtered image. 82 * An instance of the original {@code ImageFilter} 83 * is created 84 * (using the filter's {@code getFilterInstance} method) 85 * to manipulate the image data 86 * for the specified {@code ImageConsumer}. 87 * The newly created filter instance 88 * is then passed to the {@code addConsumer} method 89 * of the original {@code ImageProducer}. 90 * 91 * <p> 92 * This method is public as a side effect 93 * of this class implementing 94 * the {@code ImageProducer} interface. 95 * It should not be called from user code, 96 * and its behavior if called from user code is unspecified. 97 * 98 * @param ic the consumer for the filtered image 99 * @see ImageConsumer 100 */ 101 public synchronized void addConsumer(ImageConsumer ic) { 102 if (proxies == null) { 103 proxies = new Hashtable<>(); 104 } 105 if (!proxies.containsKey(ic)) { 106 ImageFilter imgf = filter.getFilterInstance(ic); 107 proxies.put(ic, imgf); 108 src.addConsumer(imgf); 109 } 110 } 111 112 /** 113 * Determines whether an ImageConsumer is on the list of consumers 114 * currently interested in data for this image. 115 * 116 * <p> 117 * This method is public as a side effect 118 * of this class implementing 119 * the {@code ImageProducer} interface. 120 * It should not be called from user code, 121 * and its behavior if called from user code is unspecified. 122 * 123 * @param ic the specified {@code ImageConsumer} 124 * @return true if the ImageConsumer is on the list; false otherwise 125 * @see ImageConsumer 126 */ 127 public synchronized boolean isConsumer(ImageConsumer ic) { 128 return (proxies != null && proxies.containsKey(ic)); 129 } 130 131 /** 132 * Removes an ImageConsumer from the list of consumers interested in 133 * data for this image. 134 * 135 * <p> 136 * This method is public as a side effect 137 * of this class implementing 138 * the {@code ImageProducer} interface. 139 * It should not be called from user code, 140 * and its behavior if called from user code is unspecified. 141 * 142 * @see ImageConsumer 143 */ 144 public synchronized void removeConsumer(ImageConsumer ic) { 145 if (proxies != null) { 146 ImageFilter imgf = proxies.get(ic); 147 if (imgf != null) { 148 src.removeConsumer(imgf); 149 proxies.remove(ic); 150 if (proxies.isEmpty()) { 151 proxies = null; 152 } 153 } 154 } 155 } 156 157 /** 158 * Starts production of the filtered image. 159 * If the specified {@code ImageConsumer} 160 * isn't already a consumer of the filtered image, 161 * an instance of the original {@code ImageFilter} 162 * is created 163 * (using the filter's {@code getFilterInstance} method) 164 * to manipulate the image data 165 * for the {@code ImageConsumer}. 166 * The filter instance for the {@code ImageConsumer} 167 * is then passed to the {@code startProduction} method 168 * of the original {@code ImageProducer}. 169 * 170 * <p> 171 * This method is public as a side effect 172 * of this class implementing 173 * the {@code ImageProducer} interface. 174 * It should not be called from user code, 175 * and its behavior if called from user code is unspecified. 176 * 177 * @param ic the consumer for the filtered image 178 * @see ImageConsumer 179 */ 180 public void startProduction(ImageConsumer ic) { 181 if (proxies == null) { 182 proxies = new Hashtable<>(); 183 } 184 ImageFilter imgf = proxies.get(ic); 185 if (imgf == null) { 186 imgf = filter.getFilterInstance(ic); 187 proxies.put(ic, imgf); 188 } 189 src.startProduction(imgf); 190 } 191 192 /** 193 * Requests that a given ImageConsumer have the image data delivered 194 * one more time in top-down, left-right order. The request is 195 * handed to the ImageFilter for further processing, since the 196 * ability to preserve the pixel ordering depends on the filter. 197 * 198 * <p> 199 * This method is public as a side effect 200 * of this class implementing 201 * the {@code ImageProducer} interface. 202 * It should not be called from user code, 203 * and its behavior if called from user code is unspecified. 204 * 205 * @see ImageConsumer 206 */ 207 public void requestTopDownLeftRightResend(ImageConsumer ic) { 208 if (proxies != null) { 209 ImageFilter imgf = proxies.get(ic); 210 if (imgf != null) { 211 imgf.resendTopDownLeftRight(src); 212 } 213 } 214 } 215 216 @Override 217 public boolean isMultiResolutionImageProducer() { 218 return src.isMultiResolutionImageProducer(); 219 } 220 221 @Override 222 public List<ResolutionVariantItem<ImageProducer>> getResolutionVariantItems() { 223 return src.getResolutionVariantItems().stream().map( 224 (rvItem) -> new ResolutionVariantItem<ImageProducer>( 225 new FilteredImageSource(rvItem.getValue(), 226 filter.getScaledFilterInstance( 227 rvItem.getScaleX(), 228 rvItem.getScaleY())), 229 rvItem.getScaleX(), rvItem.getScaleY())) 230 .collect(Collectors.toList()); 231 } 232 }