Updated copyright
[sixth-3d.git] / src / main / java / eu / svjatoslav / sixth / e3d / renderer / raster / texture / Texture.java
1 /*
2  * Sixth 3D engine. Copyright ©2012-2019, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of version 3 of the GNU Lesser General Public License
6  * or later as published by the Free Software Foundation.
7  *
8  */
9
10 package eu.svjatoslav.sixth.e3d.renderer.raster.texture;
11
12 import eu.svjatoslav.sixth.e3d.gui.RenderingContext;
13
14 import java.awt.*;
15 import java.awt.image.BufferedImage;
16 import java.awt.image.DataBufferByte;
17 import java.awt.image.WritableRaster;
18
19 public class Texture {
20
21     public final TextureBitmap primaryBitmap;
22     public final java.awt.Graphics2D graphics;
23     TextureBitmap[] upSampled;
24     TextureBitmap[] downSampled = new TextureBitmap[8];
25
26     public Texture(final int width, final int height, final int maxUpscale) {
27         upSampled = new TextureBitmap[maxUpscale];
28
29         final BufferedImage bufferedImage = new BufferedImage(width, height,
30                 RenderingContext.bufferedImageType);
31
32         final WritableRaster raster = bufferedImage.getRaster();
33         final DataBufferByte dbi = (DataBufferByte) raster.getDataBuffer();
34         graphics = (Graphics2D) bufferedImage.getGraphics();
35
36         graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
37                 RenderingHints.VALUE_ANTIALIAS_ON);
38
39         graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
40                 RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
41
42         primaryBitmap = new TextureBitmap(width, height, dbi.getData(), 1);
43     }
44
45     public int detectDownscaleFactorForZoom(final double zoom) {
46         double size = 1;
47         for (int i = 0; i < downSampled.length; i++) {
48             size = size / 2;
49             if (size < zoom)
50                 return i;
51         }
52
53         return downSampled.length - 1;
54     }
55
56     public int detectUpscaleFactorForZoom(final double zoom) {
57         double size = 2;
58         for (int i = 0; i < upSampled.length; i++) {
59             size = size * 2;
60             if (size > zoom)
61                 return i;
62         }
63
64         return upSampled.length - 1;
65     }
66
67     public TextureBitmap downscaleBitmap(final TextureBitmap originalBitmap) {
68         int newWidth = originalBitmap.width / 2;
69         int newHeight = originalBitmap.height / 2;
70
71         // Enforce minimum width and height
72         if (newWidth < 1)
73             newWidth = 1;
74         if (newHeight < 1)
75             newHeight = 1;
76
77         final TextureBitmap downScaled = new TextureBitmap(newWidth, newHeight,
78                 originalBitmap.multiplicationFactor / 2d);
79
80         final ColorAccumulator accumulator = new ColorAccumulator();
81
82         for (int y = 0; y < newHeight; y++)
83             for (int x = 0; x < newWidth; x++) {
84                 accumulator.reset();
85                 accumulator.accumulate(originalBitmap, x * 2, y * 2);
86                 accumulator.accumulate(originalBitmap, (x * 2) + 1, y * 2);
87                 accumulator.accumulate(originalBitmap, x * 2, (y * 2) + 1);
88                 accumulator
89                         .accumulate(originalBitmap, (x * 2) + 1, (y * 2) + 1);
90                 accumulator.storeResult(downScaled, x, y);
91             }
92
93         return downScaled;
94     }
95
96     public TextureBitmap getDownscaledBitmap(final int scaleFactor) {
97         if (downSampled[scaleFactor] == null) {
98
99             TextureBitmap largerBitmap;
100             if (scaleFactor == 0)
101                 largerBitmap = primaryBitmap;
102             else
103                 largerBitmap = getDownscaledBitmap(scaleFactor - 1);
104
105             downSampled[scaleFactor] = downscaleBitmap(largerBitmap);
106         }
107
108         return downSampled[scaleFactor];
109     }
110
111     public TextureBitmap getUpscaledBitmap(final int scaleFactor) {
112         if (upSampled[scaleFactor] == null) {
113
114             TextureBitmap smallerBitmap;
115             if (scaleFactor == 0)
116                 smallerBitmap = primaryBitmap;
117             else
118                 smallerBitmap = getUpscaledBitmap(scaleFactor - 1);
119
120             upSampled[scaleFactor] = upscaleBitmap(smallerBitmap);
121         }
122
123         return upSampled[scaleFactor];
124     }
125
126     public TextureBitmap getZoomedBitmap(final double zoomLevel) {
127
128         if (zoomLevel < 1) {
129             final int downscaleFactor = detectDownscaleFactorForZoom(zoomLevel);
130             return getDownscaledBitmap(downscaleFactor);
131         } else if (zoomLevel > 2) {
132             final int upscaleFactor = detectUpscaleFactorForZoom(zoomLevel);
133
134             if (upscaleFactor < 0)
135                 return primaryBitmap;
136
137             return getUpscaledBitmap(upscaleFactor);
138         }
139
140         // System.out.println(zoomLevel);
141         return primaryBitmap;
142     }
143
144     public void resetResampledBitmapCache() {
145         for (int i = 0; i < upSampled.length; i++)
146             upSampled[i] = null;
147
148         for (int i = 0; i < downSampled.length; i++)
149             downSampled[i] = null;
150     }
151
152     public TextureBitmap upscaleBitmap(final TextureBitmap originalBitmap) {
153         final int newWidth = originalBitmap.width * 2;
154         final int newHeight = originalBitmap.height * 2;
155
156         final TextureBitmap upScaled = new TextureBitmap(newWidth, newHeight,
157                 originalBitmap.multiplicationFactor * 2d);
158
159         final ColorAccumulator accumulator = new ColorAccumulator();
160
161         for (int y = 0; y < originalBitmap.height; y++)
162             for (int x = 0; x < originalBitmap.width; x++) {
163                 accumulator.reset();
164                 accumulator.accumulate(originalBitmap, x, y);
165                 accumulator.storeResult(upScaled, x * 2, y * 2);
166
167                 accumulator.reset();
168                 accumulator.accumulate(originalBitmap, x, y);
169                 accumulator.accumulate(originalBitmap, x + 1, y);
170                 accumulator.storeResult(upScaled, (x * 2) + 1, y * 2);
171
172                 accumulator.reset();
173                 accumulator.accumulate(originalBitmap, x, y);
174                 accumulator.accumulate(originalBitmap, x, y + 1);
175                 accumulator.storeResult(upScaled, x * 2, (y * 2) + 1);
176
177                 accumulator.reset();
178                 accumulator.accumulate(originalBitmap, x, y);
179                 accumulator.accumulate(originalBitmap, x + 1, y);
180                 accumulator.accumulate(originalBitmap, x, y + 1);
181                 accumulator.accumulate(originalBitmap, x + 1, y + 1);
182                 accumulator.storeResult(upScaled, (x * 2) + 1, (y * 2) + 1);
183             }
184
185         return upScaled;
186     }
187
188     public class ColorAccumulator {
189         public int r, g, b, a;
190         public int pixelCount = 0;
191
192         public void accumulate(final TextureBitmap bitmap, final int x,
193                                final int y) {
194             int address = bitmap.getAddress(x, y);
195
196             a += bitmap.bytes[address] & 0xff;
197             address++;
198
199             b += bitmap.bytes[address] & 0xff;
200             address++;
201
202             g += bitmap.bytes[address] & 0xff;
203             address++;
204
205             r += bitmap.bytes[address] & 0xff;
206
207             pixelCount++;
208         }
209
210         public void reset() {
211             a = 0;
212             r = 0;
213             g = 0;
214             b = 0;
215             pixelCount = 0;
216         }
217
218         public void storeResult(final TextureBitmap bitmap, final int x,
219                                 final int y) {
220             int address = bitmap.getAddress(x, y);
221
222             bitmap.bytes[address] = (byte) (a / pixelCount);
223             address++;
224
225             bitmap.bytes[address] = (byte) (b / pixelCount);
226             address++;
227
228             bitmap.bytes[address] = (byte) (g / pixelCount);
229             address++;
230
231             bitmap.bytes[address] = (byte) (r / pixelCount);
232         }
233     }
234
235 }