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