/* * Copyright 2014 MovingBlocks * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.terasology.rendering.assets.texture; import com.google.common.primitives.UnsignedBytes; import org.terasology.assets.ResourceUrn; import org.terasology.engine.TerasologyConstants; import org.terasology.math.geom.Rect2i; import org.terasology.naming.Name; import org.terasology.rendering.nui.Color; import java.awt.image.BufferedImage; import java.nio.ByteBuffer; public final class TextureUtil { public static final Name COLOR_RESOURCE_NAME = new Name("Color"); public static final Name NOISE_RESOURCE_NAME = new Name("Noise"); private TextureUtil() { } /** * Returns a AssetUri which represents a Texture of that color. * * @param color including alpha, of the texture to represent. * @return an asset Uri for the texture */ public static ResourceUrn getTextureUriForColor(Color color) { return new ResourceUrn(TerasologyConstants.ENGINE_MODULE, COLOR_RESOURCE_NAME, new Name(getColorName(color))); } /** * Returns a AssetUri which represents a Texture that contains white noise * * @param size the size of the texture (both width and height) * @param seed the seed value for the noise generator * @param min the minimum noise value (can be lower than 0 and will be clamped then) * @param max the minimum noise value (can be larger than 255 and will be clamped then) * @return an asset Uri for the texture */ public static ResourceUrn getTextureUriForWhiteNoise(int size, long seed, int min, int max) { String name = String.format("%s.%d.%d.%d.%d", "white", size, seed, min, max); return new ResourceUrn(TerasologyConstants.ENGINE_MODULE, NOISE_RESOURCE_NAME, new Name(name)); } private static String getColorName(Color color) { StringBuilder builder = new StringBuilder(); appendColorName(builder, color); return builder.toString(); } /** * Method to append the color string hex representation back to a string buffer. * Package-only access as it is for internal use in ColorTextureAssetResolver, * but should be here for maintenance with the color-to-color-string code. * * @param sb StringBuilder into which to append name * @param color represented by hexColorName */ private static void appendColorName(StringBuilder sb, Color color) { int red = color.r(); if (red < 16) { sb.append('0'); } sb.append(Integer.toHexString(red)); int green = color.g(); if (green < 16) { sb.append('0'); } sb.append(Integer.toHexString(green)); int blue = color.b(); if (blue < 16) { sb.append('0'); } sb.append(Integer.toHexString(blue)); int alpha = color.a(); if (alpha < 16) { sb.append('0'); } sb.append(Integer.toHexString(alpha)); } /** * Method to convert the color string hex representation back to a color. * Package-only access as it is for internal use in ColorTextureAssetResolver, * but should be here for maintenance with the color-to-color-string code. * * @param hexColorName RRGGBBAA in lower-case hex notation * @return color represented by hexColorName */ static/* package-only */Color getColorForColorName(String hexColorName) { if (hexColorName.length() != 8) { // TODO: we should probably log a warning in this case. return null; } String redString = hexColorName.substring(0, 2); String greenString = hexColorName.substring(2, 4); String blueString = hexColorName.substring(4, 6); String alphaString = hexColorName.substring(6); int red = Integer.parseInt(redString, 16); int green = Integer.parseInt(greenString, 16); int blue = Integer.parseInt(blueString, 16); int alpha = Integer.parseInt(alphaString, 16); return new Color(red, green, blue, alpha); } public static BufferedImage convertToImage(TextureRegion textureRegion) { final int width = textureRegion.getWidth(); final int height = textureRegion.getHeight(); final Rect2i pixelRegion = textureRegion.getPixelRegion(); final Texture texture = textureRegion.getTexture(); ByteBuffer textureBytes = texture.getData().getBuffers()[0]; int stride = texture.getWidth() * 4; int posX = pixelRegion.minX(); int posY = pixelRegion.minY(); BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { int r = UnsignedBytes.toInt(textureBytes.get((posY + y) * stride + (posX + x) * 4)); int g = UnsignedBytes.toInt(textureBytes.get((posY + y) * stride + (posX + x) * 4 + 1)); int b = UnsignedBytes.toInt(textureBytes.get((posY + y) * stride + (posX + x) * 4 + 2)); int a = UnsignedBytes.toInt(textureBytes.get((posY + y) * stride + (posX + x) * 4 + 3)); int argb = (a << 24) + (r << 16) + (g << 8) + b; image.setRGB(x, y, argb); } } return image; } /** * Converts a BufferedImage into a ByteBuffer based on 32-bit values * in RGBA byte order * * @param image any type of BufferedImage * @return a ByteBuffer that contains the data in RGBA byte order */ public static ByteBuffer convertToByteBuffer(BufferedImage image) { int width = image.getWidth(); int height = image.getHeight(); ByteBuffer data = ByteBuffer.allocateDirect(4 * width * height); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { int argb = image.getRGB(x, y); int r = (argb >> 16) & 0xFF; int g = (argb >> 8) & 0xFF; int b = argb & 0xFF; int a = (argb >> 24) & 0xFF; data.put(UnsignedBytes.checkedCast(r)); data.put(UnsignedBytes.checkedCast(g)); data.put(UnsignedBytes.checkedCast(b)); data.put(UnsignedBytes.checkedCast(a)); } } data.rewind(); return data; } }