MultiResolutionCachedImage.java revision 10517:e50dfa1c0902
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 super.getWidth(observer);
90    }
91
92    @Override
93    public int getHeight(ImageObserver observer) {
94        updateInfo(observer, ImageObserver.HEIGHT);
95        return super.getHeight(observer);
96    }
97
98    @Override
99    public Object getProperty(String name, ImageObserver observer) {
100        updateInfo(observer, ImageObserver.PROPERTIES);
101        return super.getProperty(name, observer);
102    }
103
104    @Override
105    protected Image getBaseImage() {
106        return getResolutionVariant(baseImageWidth, baseImageHeight);
107    }
108
109    private void updateInfo(ImageObserver observer, int info) {
110        availableInfo |= (observer == null) ? ImageObserver.ALLBITS : info;
111    }
112
113    private static int getInfo(Image image) {
114        if (image instanceof ToolkitImage) {
115            return ((ToolkitImage) image).getImageRep().check(
116                    (img, infoflags, x, y, w, h) -> false);
117        }
118        return 0;
119    }
120
121    private static void preload(Image image, int availableInfo) {
122        if (availableInfo != 0 && image instanceof ToolkitImage) {
123            ((ToolkitImage) image).preload(new ImageObserver() {
124                int flags = availableInfo;
125
126                @Override
127                public boolean imageUpdate(Image img, int infoflags,
128                        int x, int y, int width, int height) {
129                    flags &= ~infoflags;
130                    return (flags != 0) && ((infoflags
131                            & (ImageObserver.ERROR | ImageObserver.ABORT)) == 0);
132                }
133            });
134        }
135    }
136
137    private static class ImageCacheKey implements ImageCache.PixelsKey {
138
139        private final int pixelCount;
140        private final int hash;
141
142        private final int w;
143        private final int h;
144        private final Image baseImage;
145
146        ImageCacheKey(final Image baseImage,
147                final int w, final int h) {
148            this.baseImage = baseImage;
149            this.w = w;
150            this.h = h;
151            this.pixelCount = w * h;
152            hash = hash();
153        }
154
155        @Override
156        public int getPixelCount() {
157            return pixelCount;
158        }
159
160        private int hash() {
161            int hash = baseImage.hashCode();
162            hash = 31 * hash + w;
163            hash = 31 * hash + h;
164            return hash;
165        }
166
167        @Override
168        public int hashCode() {
169            return hash;
170        }
171
172        @Override
173        public boolean equals(Object obj) {
174            if (obj instanceof ImageCacheKey) {
175                ImageCacheKey key = (ImageCacheKey) obj;
176                return baseImage == key.baseImage && w == key.w && h == key.h;
177            }
178            return false;
179        }
180    }
181}