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