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
117 * @param scaleFactor The upscale factor
120 public TextureBitmap getUpscaledBitmap(final int scaleFactor) {
121 if (upSampled[scaleFactor] == null) {
123 TextureBitmap smallerBitmap;
124 if (scaleFactor == 0)
125 smallerBitmap = primaryBitmap;
127 smallerBitmap = getUpscaledBitmap(scaleFactor - 1);
129 upSampled[scaleFactor] = upscaleBitmap(smallerBitmap);
132 return upSampled[scaleFactor];
136 * Returns the bitmap that should be used for rendering at the given zoom
138 * @param zoomLevel The zoom level
141 public TextureBitmap getZoomedBitmap(final double zoomLevel) {
144 final int downscaleFactor = detectDownscaleFactorForZoom(zoomLevel);
145 return getDownscaledBitmap(downscaleFactor);
146 } else if (zoomLevel > 2) {
147 final int upscaleFactor = detectUpscaleFactorForZoom(zoomLevel);
149 if (upscaleFactor < 0)
150 return primaryBitmap;
152 return getUpscaledBitmap(upscaleFactor);
155 // System.out.println(zoomLevel);
156 return primaryBitmap;
160 * Resets the cache of resampled bitmaps
162 public void resetResampledBitmapCache() {
163 fill(upSampled, null);
165 fill(downSampled, null);
169 * Upscales the given bitmap by a factor of 2
171 * @param originalBitmap The bitmap to upscale
172 * @return The upscaled bitmap
174 public TextureBitmap upscaleBitmap(final TextureBitmap originalBitmap) {
175 final int newWidth = originalBitmap.width * 2;
176 final int newHeight = originalBitmap.height * 2;
178 final TextureBitmap upScaled = new TextureBitmap(newWidth, newHeight,
179 originalBitmap.multiplicationFactor * 2d);
181 final ColorAccumulator accumulator = new ColorAccumulator();
183 for (int y = 0; y < originalBitmap.height; y++)
184 for (int x = 0; x < originalBitmap.width; x++) {
186 accumulator.accumulate(originalBitmap, x, y);
187 accumulator.storeResult(upScaled, x * 2, y * 2);
190 accumulator.accumulate(originalBitmap, x, y);
191 accumulator.accumulate(originalBitmap, x + 1, y);
192 accumulator.storeResult(upScaled, (x * 2) + 1, y * 2);
195 accumulator.accumulate(originalBitmap, x, y);
196 accumulator.accumulate(originalBitmap, x, y + 1);
197 accumulator.storeResult(upScaled, x * 2, (y * 2) + 1);
200 accumulator.accumulate(originalBitmap, x, y);
201 accumulator.accumulate(originalBitmap, x + 1, y);
202 accumulator.accumulate(originalBitmap, x, y + 1);
203 accumulator.accumulate(originalBitmap, x + 1, y + 1);
204 accumulator.storeResult(upScaled, (x * 2) + 1, (y * 2) + 1);
211 * A helper class that accumulates color values for a given area of a bitmap
213 public static class ColorAccumulator {
214 // Accumulated color values
215 public int r, g, b, a;
217 // Number of pixels that have been accumulated
218 public int pixelCount = 0;
221 * Accumulates the color values of the given pixel
223 * @param bitmap The bitmap
224 * @param x The x coordinate of the pixel
225 * @param y The y coordinate of the pixel
227 public void accumulate(final TextureBitmap bitmap, final int x,
229 int address = bitmap.getAddress(x, y);
231 a += bitmap.bytes[address] & 0xff;
234 b += bitmap.bytes[address] & 0xff;
237 g += bitmap.bytes[address] & 0xff;
240 r += bitmap.bytes[address] & 0xff;
246 * Resets the accumulator
248 public void reset() {
257 * Stores the accumulated color values in the given bitmap
259 * @param bitmap The bitmap
260 * @param x The x coordinate of the pixel
261 * @param y The y coordinate of the pixel
263 public void storeResult(final TextureBitmap bitmap, final int x,
265 int address = bitmap.getAddress(x, y);
267 bitmap.bytes[address] = (byte) (a / pixelCount);
270 bitmap.bytes[address] = (byte) (b / pixelCount);
273 bitmap.bytes[address] = (byte) (g / pixelCount);
276 bitmap.bytes[address] = (byte) (r / pixelCount);