/* * Copyright (c) 2009-2012 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of 'jMonkeyEngine' nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.jme3.scene.plugins.blender.textures.blending; import com.jme3.math.FastMath; import com.jme3.scene.plugins.blender.BlenderContext; import com.jme3.scene.plugins.blender.textures.TexturePixel; import com.jme3.scene.plugins.blender.textures.io.PixelIOFactory; import com.jme3.scene.plugins.blender.textures.io.PixelInputOutput; import com.jme3.texture.Image; import com.jme3.texture.Image.Format; import com.jme3.texture.image.ColorSpace; import com.jme3.util.BufferUtils; import java.nio.ByteBuffer; import java.util.ArrayList; /** * <p> * The class that is responsible for blending the following texture types: * </p> * <ul> * <li>RGBA8</li> * <li>ABGR8</li> * <li>BGR8</li> * <li>RGB8</li> * </ul> * * <p> * Not yet supported (but will be): * </p> * <ul> * <li>ARGB4444</li> * <li>RGB10</li> * <li>RGB111110F</li> * <li>RGB16</li> * <li>RGB16F</li> * <li>RGB16F_to_RGB111110F</li> * <li>RGB16F_to_RGB9E5</li> * <li>RGB32F</li> * <li>RGB565</li> * <li>RGB5A1</li> * <li>RGB9E5</li> * <li>RGBA16</li> * <li>RGBA16F</li> * </ul> * * @author Marcin Roguski (Kaelthas) */ public class TextureBlenderAWT extends AbstractTextureBlender { public TextureBlenderAWT(int flag, boolean negateTexture, int blendType, float[] materialColor, float[] color, float blendFactor) { super(flag, negateTexture, blendType, materialColor, color, blendFactor); } @Override public Image blend(Image image, Image baseImage, BlenderContext blenderContext) { this.prepareImagesForBlending(image, baseImage); float[] pixelColor = new float[] { color[0], color[1], color[2], 1.0f }; Format format = image.getFormat(); PixelInputOutput basePixelIO = null, pixelReader = PixelIOFactory.getPixelIO(format); TexturePixel basePixel = null, pixel = new TexturePixel(); float[] materialColor = this.materialColor; if (baseImage != null) { basePixelIO = PixelIOFactory.getPixelIO(baseImage.getFormat()); materialColor = new float[this.materialColor.length]; basePixel = new TexturePixel(); } int width = image.getWidth(); int height = image.getHeight(); int depth = image.getDepth(); if (depth == 0) { depth = 1; } int bytesPerPixel = image.getFormat().getBitsPerPixel() >> 3; ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(depth); float[] resultPixel = new float[4]; for (int dataLayerIndex = 0; dataLayerIndex < depth; ++dataLayerIndex) { ByteBuffer data = image.getData(dataLayerIndex); data.rewind(); int imagePixelCount = data.limit() / bytesPerPixel; ByteBuffer newData = BufferUtils.createByteBuffer(imagePixelCount * 4); int dataIndex = 0, x = 0, y = 0, index = 0; while (index < data.limit()) { // getting the proper material color if the base texture is applied if (basePixelIO != null) { basePixelIO.read(baseImage, dataLayerIndex, basePixel, x, y); basePixel.toRGBA(materialColor); ++x; if (x >= width) { x = 0; ++y; } } // reading the current texture's pixel pixelReader.read(image, dataLayerIndex, pixel, index); index += bytesPerPixel; pixel.toRGBA(pixelColor); if (negateTexture) { pixel.negate(); } this.blendPixel(resultPixel, materialColor, pixelColor, blenderContext); newData.put(dataIndex++, (byte) (resultPixel[0] * 255.0f)); newData.put(dataIndex++, (byte) (resultPixel[1] * 255.0f)); newData.put(dataIndex++, (byte) (resultPixel[2] * 255.0f)); newData.put(dataIndex++, (byte) (pixelColor[3] * 255.0f)); } dataArray.add(newData); } Image result = depth > 1 ? new Image(Format.RGBA8, width, height, depth, dataArray, ColorSpace.Linear) : new Image(Format.RGBA8, width, height, dataArray.get(0), ColorSpace.Linear); if (image.getMipMapSizes() != null) { result.setMipMapSizes(image.getMipMapSizes().clone()); } return result; } /** * This method blends the single pixel depending on the blending type. * * @param result * the result pixel * @param materialColor * the material color * @param pixelColor * the pixel color * @param blenderContext * the blender context */ protected void blendPixel(float[] result, float[] materialColor, float[] pixelColor, BlenderContext blenderContext) { float blendFactor = this.blendFactor * pixelColor[3]; float oneMinusFactor = 1.0f - blendFactor, col; switch (blendType) { case MTEX_BLEND: result[0] = blendFactor * pixelColor[0] + oneMinusFactor * materialColor[0]; result[1] = blendFactor * pixelColor[1] + oneMinusFactor * materialColor[1]; result[2] = blendFactor * pixelColor[2] + oneMinusFactor * materialColor[2]; break; case MTEX_MUL: result[0] = (oneMinusFactor + blendFactor * materialColor[0]) * pixelColor[0]; result[1] = (oneMinusFactor + blendFactor * materialColor[1]) * pixelColor[1]; result[2] = (oneMinusFactor + blendFactor * materialColor[2]) * pixelColor[2]; break; case MTEX_DIV: if (pixelColor[0] != 0.0) { result[0] = (oneMinusFactor * materialColor[0] + blendFactor * materialColor[0] / pixelColor[0]) * 0.5f; } if (pixelColor[1] != 0.0) { result[1] = (oneMinusFactor * materialColor[1] + blendFactor * materialColor[1] / pixelColor[1]) * 0.5f; } if (pixelColor[2] != 0.0) { result[2] = (oneMinusFactor * materialColor[2] + blendFactor * materialColor[2] / pixelColor[2]) * 0.5f; } break; case MTEX_SCREEN: result[0] = 1.0f - (oneMinusFactor + blendFactor * (1.0f - materialColor[0])) * (1.0f - pixelColor[0]); result[1] = 1.0f - (oneMinusFactor + blendFactor * (1.0f - materialColor[1])) * (1.0f - pixelColor[1]); result[2] = 1.0f - (oneMinusFactor + blendFactor * (1.0f - materialColor[2])) * (1.0f - pixelColor[2]); break; case MTEX_OVERLAY: if (materialColor[0] < 0.5f) { result[0] = pixelColor[0] * (oneMinusFactor + 2.0f * blendFactor * materialColor[0]); } else { result[0] = 1.0f - (oneMinusFactor + 2.0f * blendFactor * (1.0f - materialColor[0])) * (1.0f - pixelColor[0]); } if (materialColor[1] < 0.5f) { result[1] = pixelColor[1] * (oneMinusFactor + 2.0f * blendFactor * materialColor[1]); } else { result[1] = 1.0f - (oneMinusFactor + 2.0f * blendFactor * (1.0f - materialColor[1])) * (1.0f - pixelColor[1]); } if (materialColor[2] < 0.5f) { result[2] = pixelColor[2] * (oneMinusFactor + 2.0f * blendFactor * materialColor[2]); } else { result[2] = 1.0f - (oneMinusFactor + 2.0f * blendFactor * (1.0f - materialColor[2])) * (1.0f - pixelColor[2]); } break; case MTEX_SUB: result[0] = materialColor[0] - blendFactor * pixelColor[0]; result[1] = materialColor[1] - blendFactor * pixelColor[1]; result[2] = materialColor[2] - blendFactor * pixelColor[2]; result[0] = FastMath.clamp(result[0], 0.0f, 1.0f); result[1] = FastMath.clamp(result[1], 0.0f, 1.0f); result[2] = FastMath.clamp(result[2], 0.0f, 1.0f); break; case MTEX_ADD: result[0] = (blendFactor * pixelColor[0] + materialColor[0]) * 0.5f; result[1] = (blendFactor * pixelColor[1] + materialColor[1]) * 0.5f; result[2] = (blendFactor * pixelColor[2] + materialColor[2]) * 0.5f; break; case MTEX_DIFF: result[0] = oneMinusFactor * materialColor[0] + blendFactor * Math.abs(materialColor[0] - pixelColor[0]); result[1] = oneMinusFactor * materialColor[1] + blendFactor * Math.abs(materialColor[1] - pixelColor[1]); result[2] = oneMinusFactor * materialColor[2] + blendFactor * Math.abs(materialColor[2] - pixelColor[2]); break; case MTEX_DARK: col = blendFactor * pixelColor[0]; result[0] = col < materialColor[0] ? col : materialColor[0]; col = blendFactor * pixelColor[1]; result[1] = col < materialColor[1] ? col : materialColor[1]; col = blendFactor * pixelColor[2]; result[2] = col < materialColor[2] ? col : materialColor[2]; break; case MTEX_LIGHT: col = blendFactor * pixelColor[0]; result[0] = col > materialColor[0] ? col : materialColor[0]; col = blendFactor * pixelColor[1]; result[1] = col > materialColor[1] ? col : materialColor[1]; col = blendFactor * pixelColor[2]; result[2] = col > materialColor[2] ? col : materialColor[2]; break; case MTEX_BLEND_HUE: case MTEX_BLEND_SAT: case MTEX_BLEND_VAL: case MTEX_BLEND_COLOR: System.arraycopy(materialColor, 0, result, 0, 3); this.blendHSV(blendType, result, blendFactor, pixelColor, blenderContext); break; default: throw new IllegalStateException("Unknown blend type: " + blendType); } } }