MultiResolutionCachedImage.java revision 12677:a4299d47bd00
1/*
2 * Copyright (c) 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 */
25package sun.awt.image;
26
27import java.awt.Dimension;
28import java.awt.Image;
29import java.awt.geom.Dimension2D;
30import java.awt.image.ImageObserver;
31import java.util.Arrays;
32import java.util.List;
33import java.util.function.Function;
34import java.util.function.BiFunction;
35import java.util.stream.Collectors;
36
37public class MultiResolutionCachedImage extends AbstractMultiResolutionImage {
38
39    private final int baseImageWidth;
40    private final int baseImageHeight;
41    private final Dimension2D[] sizes;
42    private final BiFunction<Integer, Integer, Image> mapper;
43    private int availableInfo;
44
45    public MultiResolutionCachedImage(int baseImageWidth, int baseImageHeight,
46            BiFunction<Integer, Integer, Image> mapper) {
47        this(baseImageWidth, baseImageHeight, new Dimension[]{new Dimension(
48            baseImageWidth, baseImageHeight)
49        }, mapper);
50    }
51
52    public MultiResolutionCachedImage(int baseImageWidth, int baseImageHeight,
53            Dimension2D[] sizes, BiFunction<Integer, Integer, Image> mapper) {
54        this.baseImageWidth = baseImageWidth;
55        this.baseImageHeight = baseImageHeight;
56        this.sizes = (sizes == null) ? null : Arrays.copyOf(sizes, sizes.length);
57        this.mapper = mapper;
58    }
59
60    @Override
61    public Image getResolutionVariant(int width, int height) {
62        ImageCache cache = ImageCache.getInstance();
63        ImageCacheKey key = new ImageCacheKey(this, width, height);
64        Image resolutionVariant = cache.getImage(key);
65        if (resolutionVariant == null) {
66            resolutionVariant = mapper.apply(width, height);
67            cache.setImage(key, resolutionVariant);
68        }
69        preload(resolutionVariant, availableInfo);
70        return resolutionVariant;
71    }
72
73    @Override
74    public List<Image> getResolutionVariants() {
75        return Arrays.stream(sizes).map((Function<Dimension2D, Image>) size
76                -> getResolutionVariant((int) size.getWidth(),
77                        (int) size.getHeight())).collect(Collectors.toList());
78    }
79
80    public MultiResolutionCachedImage map(Function<Image, Image> mapper) {
81        return new MultiResolutionCachedImage(baseImageWidth, baseImageHeight,
82                sizes, (width, height) ->
83                        mapper.apply(getResolutionVariant(width, height)));
84    }
85
86    @Override
87    public int getWidth(ImageObserver observer) {
88        updateInfo(observer, ImageObserver.WIDTH);
89        return baseImageWidth;
90    }
91
92    @Override
93    public int getHeight(ImageObserver observer) {
94        updateInfo(observer, ImageObserver.HEIGHT);
95        return baseImageHeight;
96    }
97
98    @Override
99    public Object getProperty(String name, ImageObserver observer) {
100        updateInfo(observer, ImageObserver.PROPERTIES);
101        return Image.UndefinedProperty;
102    }
103
104    @Override
105    public Image getScaledInstance(int width, int height, int hints) {
106        return getResolutionVariant(width, height);
107    }
108
109    @Override
110    protected Image getBaseImage() {
111        return getResolutionVariant(baseImageWidth, baseImageHeight);
112    }
113
114    private void updateInfo(ImageObserver observer, int info) {
115        availableInfo |= (observer == null) ? ImageObserver.ALLBITS : info;
116    }
117
118    private static int getInfo(Image image) {
119        if (image instanceof ToolkitImage) {
120            return ((ToolkitImage) image).getImageRep().check(
121                    (img, infoflags, x, y, w, h) -> false);
122        }
123        return 0;
124    }
125
126    private static void preload(Image image, int availableInfo) {
127        if (availableInfo != 0 && image instanceof ToolkitImage) {
128            ((ToolkitImage) image).preload(new ImageObserver() {
129                int flags = availableInfo;
130
131                @Override
132                public boolean imageUpdate(Image img, int infoflags,
133                        int x, int y, int width, int height) {
134                    flags &= ~infoflags;
135                    return (flags != 0) && ((infoflags
136                            & (ImageObserver.ERROR | ImageObserver.ABORT)) == 0);
137                }
138            });
139        }
140    }
141
142    private static class ImageCacheKey implements ImageCache.PixelsKey {
143
144        private final int pixelCount;
145        private final int hash;
146
147        private final int w;
148        private final int h;
149        private final Image baseImage;
150
151        ImageCacheKey(final Image baseImage,
152                final int w, final int h) {
153            this.baseImage = baseImage;
154            this.w = w;
155            this.h = h;
156            this.pixelCount = w * h;
157            hash = hash();
158        }
159
160        @Override
161        public int getPixelCount() {
162            return pixelCount;
163        }
164
165        private int hash() {
166            int hash = baseImage.hashCode();
167            hash = 31 * hash + w;
168            hash = 31 * hash + h;
169            return hash;
170        }
171
172        @Override
173        public int hashCode() {
174            return hash;
175        }
176
177        @Override
178        public boolean equals(Object obj) {
179            if (obj instanceof ImageCacheKey) {
180                ImageCacheKey key = (ImageCacheKey) obj;
181                return baseImage == key.baseImage && w == key.w && h == key.h;
182            }
183            return false;
184        }
185    }
186}
187