2 * Sixth 3D engine. Author: Svjatoslav Agejenko.
3 * This project is released under Creative Commons Zero (CC0) license.
5 package eu.svjatoslav.sixth.e3d.renderer.raster.texture;
7 import eu.svjatoslav.sixth.e3d.gui.RenderingContext;
10 import java.awt.image.BufferedImage;
11 import java.awt.image.DataBufferByte;
12 import java.awt.image.WritableRaster;
14 import static java.util.Arrays.fill;
16 public class Texture {
18 public final TextureBitmap primaryBitmap;
19 public final java.awt.Graphics2D graphics;
20 TextureBitmap[] upSampled;
21 TextureBitmap[] downSampled = new TextureBitmap[8];
23 public Texture(final int width, final int height, final int maxUpscale) {
24 upSampled = new TextureBitmap[maxUpscale];
26 final BufferedImage bufferedImage = new BufferedImage(width, height,
27 RenderingContext.bufferedImageType);
29 final WritableRaster raster = bufferedImage.getRaster();
30 final DataBufferByte dbi = (DataBufferByte) raster.getDataBuffer();
31 graphics = (Graphics2D) bufferedImage.getGraphics();
33 graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
34 RenderingHints.VALUE_ANTIALIAS_ON);
36 graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
37 RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
39 primaryBitmap = new TextureBitmap(width, height, dbi.getData(), 1);
42 public int detectDownscaleFactorForZoom(final double zoom) {
44 for (int i = 0; i < downSampled.length; i++) {
50 return downSampled.length - 1;
53 public int detectUpscaleFactorForZoom(final double zoom) {
55 for (int i = 0; i < upSampled.length; i++) {
61 return upSampled.length - 1;
65 * Downscale given bitmap by factor of 2.
67 * @param originalBitmap Bitmap to downscale.
68 * @return Downscaled bitmap.
70 public TextureBitmap downscaleBitmap(final TextureBitmap originalBitmap) {
71 int newWidth = originalBitmap.width / 2;
72 int newHeight = originalBitmap.height / 2;
74 // Enforce minimum width and height
80 final TextureBitmap downScaled = new TextureBitmap(newWidth, newHeight,
81 originalBitmap.multiplicationFactor / 2d);
83 final ColorAccumulator accumulator = new ColorAccumulator();
85 for (int y = 0; y < newHeight; y++)
86 for (int x = 0; x < newWidth; x++) {
88 accumulator.accumulate(originalBitmap, x * 2, y * 2);
89 accumulator.accumulate(originalBitmap, (x * 2) + 1, y * 2);
90 accumulator.accumulate(originalBitmap, x * 2, (y * 2) + 1);
92 .accumulate(originalBitmap, (x * 2) + 1, (y * 2) + 1);
93 accumulator.storeResult(downScaled, x, y);
99 public TextureBitmap getDownscaledBitmap(final int scaleFactor) {
100 if (downSampled[scaleFactor] == null) {
102 TextureBitmap largerBitmap;
103 if (scaleFactor == 0)
104 largerBitmap = primaryBitmap;
106 largerBitmap = getDownscaledBitmap(scaleFactor - 1);
108 downSampled[scaleFactor] = downscaleBitmap(largerBitmap);
111 return downSampled[scaleFactor];
115 * Returns the bitmap that should be used for rendering at the given zoom
116 * @param scaleFactor The upscale factor
119 public TextureBitmap getUpscaledBitmap(final int scaleFactor) {
120 if (upSampled[scaleFactor] == null) {
122 TextureBitmap smallerBitmap;
123 if (scaleFactor == 0)
124 smallerBitmap = primaryBitmap;
126 smallerBitmap = getUpscaledBitmap(scaleFactor - 1);
128 upSampled[scaleFactor] = upscaleBitmap(smallerBitmap);
131 return upSampled[scaleFactor];
135 * Returns the bitmap that should be used for rendering at the given zoom
136 * @param zoomLevel The zoom level
139 public TextureBitmap getZoomedBitmap(final double zoomLevel) {
142 final int downscaleFactor = detectDownscaleFactorForZoom(zoomLevel);
143 return getDownscaledBitmap(downscaleFactor);
144 } else if (zoomLevel > 2) {
145 final int upscaleFactor = detectUpscaleFactorForZoom(zoomLevel);
147 if (upscaleFactor < 0)
148 return primaryBitmap;
150 return getUpscaledBitmap(upscaleFactor);
153 // System.out.println(zoomLevel);
154 return primaryBitmap;
158 * Resets the cache of resampled bitmaps
160 public void resetResampledBitmapCache() {
161 fill(upSampled, null);
163 fill(downSampled, null);
167 * Upscales the given bitmap by a factor of 2
168 * @param originalBitmap The bitmap to upscale
169 * @return The upscaled bitmap
171 public TextureBitmap upscaleBitmap(final TextureBitmap originalBitmap) {
172 final int newWidth = originalBitmap.width * 2;
173 final int newHeight = originalBitmap.height * 2;
175 final TextureBitmap upScaled = new TextureBitmap(newWidth, newHeight,
176 originalBitmap.multiplicationFactor * 2d);
178 final ColorAccumulator accumulator = new ColorAccumulator();
180 for (int y = 0; y < originalBitmap.height; y++)
181 for (int x = 0; x < originalBitmap.width; x++) {
183 accumulator.accumulate(originalBitmap, x, y);
184 accumulator.storeResult(upScaled, x * 2, y * 2);
187 accumulator.accumulate(originalBitmap, x, y);
188 accumulator.accumulate(originalBitmap, x + 1, y);
189 accumulator.storeResult(upScaled, (x * 2) + 1, y * 2);
192 accumulator.accumulate(originalBitmap, x, y);
193 accumulator.accumulate(originalBitmap, x, y + 1);
194 accumulator.storeResult(upScaled, x * 2, (y * 2) + 1);
197 accumulator.accumulate(originalBitmap, x, y);
198 accumulator.accumulate(originalBitmap, x + 1, y);
199 accumulator.accumulate(originalBitmap, x, y + 1);
200 accumulator.accumulate(originalBitmap, x + 1, y + 1);
201 accumulator.storeResult(upScaled, (x * 2) + 1, (y * 2) + 1);
208 * A helper class that accumulates color values for a given area of a bitmap
210 public static class ColorAccumulator {
211 // Accumulated color values
212 public int r, g, b, a;
214 // Number of pixels that have been accumulated
215 public int pixelCount = 0;
218 * Accumulates the color values of the given pixel
219 * @param bitmap The bitmap
220 * @param x The x coordinate of the pixel
221 * @param y The y coordinate of the pixel
223 public void accumulate(final TextureBitmap bitmap, final int x,
225 int address = bitmap.getAddress(x, y);
227 a += bitmap.bytes[address] & 0xff;
230 b += bitmap.bytes[address] & 0xff;
233 g += bitmap.bytes[address] & 0xff;
236 r += bitmap.bytes[address] & 0xff;
242 * Resets the accumulator
244 public void reset() {
253 * Stores the accumulated color values in the given bitmap
254 * @param bitmap The bitmap
255 * @param x The x coordinate of the pixel
256 * @param y The y coordinate of the pixel
258 public void storeResult(final TextureBitmap bitmap, final int x,
260 int address = bitmap.getAddress(x, y);
262 bitmap.bytes[address] = (byte) (a / pixelCount);
265 bitmap.bytes[address] = (byte) (b / pixelCount);
268 bitmap.bytes[address] = (byte) (g / pixelCount);
271 bitmap.bytes[address] = (byte) (r / pixelCount);