/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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. */ /** * @author Denis M. Kishenko */ package com.google.code.appengine.awt; import org.apache.harmony.awt.gl.AwtImageBackdoorAccessor; import com.google.code.appengine.awt.PaintContext; import com.google.code.appengine.awt.TexturePaintContext; import com.google.code.appengine.awt.geom.AffineTransform; import com.google.code.appengine.awt.geom.Point2D; import com.google.code.appengine.awt.geom.Rectangle2D; import com.google.code.appengine.awt.image.BufferedImage; import com.google.code.appengine.awt.image.ColorModel; import com.google.code.appengine.awt.image.DataBuffer; import com.google.code.appengine.awt.image.Raster; import com.google.code.appengine.awt.image.WritableRaster; class TexturePaintContext implements PaintContext { /** * The ColorModel object of destination raster */ ColorModel cm; /** * The BufferedImage object used as texture */ BufferedImage img; /** * The Rectangle2D bounds of texture piece to be painted */ Rectangle2D anchor; /** * The paint transformation */ AffineTransform t; /** * The AwtImageBackdoorAccessor object to communicate with image DataBuffer */ AwtImageBackdoorAccessor access; /** * The source DataBuffer object of texture image */ DataBuffer srcBuf; /** * The destination DataBuffer object of output rester */ DataBuffer dstBuf; /** * The source WritableRaster object of texture image */ WritableRaster srcRaster; /** * The destination WritableRaster object of texture image */ WritableRaster dstRaster; /** * The width of the texture image */ int srcWidth; /** * The height of the texture image */ int srcHeight; /** * The temporary pre-calculated temporary values */ int sx, sy, hx, hy, vx, vy; int m00, m01, m10, m11; int imgW, imgH; int px, py; /** * The integer array of weight components for bilinear interpolation */ int[] weight = new int[4]; /** * The temporary values */ int[] value = new int[4]; static class IntSimple extends TexturePaintContext { /** * Constructs a new IntSimple.TexturePaintContext works with DataBufferInt rasters. * This is simple paint context uses NEAREST NEIGHBOUR interpolation. * @param img - the BufferedImage object used as texture * @param anchor - the Rectangle2D bounds of texture piece to be painted * @param t - the AffineTransform applied to texture painting */ public IntSimple(BufferedImage img, Rectangle2D anchor, AffineTransform t) { super(img, anchor, t); } @Override public Raster getRaster(int dstX, int dstY, int dstWidth, int dstHeight) { prepare(dstX, dstY, dstWidth, dstHeight); int[] src = access.getDataInt(srcBuf); int[] dst = access.getDataInt(dstBuf); int k = 0; for(int j = 0; j < dstHeight; j++) { for(int i = 0; i < dstWidth; i++) { dst[k++] = src[(sx >> 8) + (sy >> 8) * srcWidth]; sx = check(sx + hx, imgW); sy = check(sy + hy, imgH); } sx = check(sx + vx, imgW); sy = check(sy + vy, imgH); } return dstRaster; } } static class ByteSimple extends TexturePaintContext { /** * Constructs a new ByteSimple.TexturePaintContext works with DataBufferByte rasters. * This is simple paint context uses NEAREST NEIGHBOUR interpolation. * @param img - the BufferedImage object used as texture * @param anchor - the Rectangle2D bounds of texture piece to be painted * @param t - the AffineTransform applied to texture painting */ public ByteSimple(BufferedImage img, Rectangle2D anchor, AffineTransform t) { super(img, anchor, t); } @Override public Raster getRaster(int dstX, int dstY, int dstWidth, int dstHeight) { prepare(dstX, dstY, dstWidth, dstHeight); byte[] src = access.getDataByte(srcBuf); byte[] dst = access.getDataByte(dstBuf); int k = 0; for(int j = 0; j < dstHeight; j++) { for(int i = 0; i < dstWidth; i++) { dst[k++] = src[(sx >> 8) + (sy >> 8) * srcWidth]; sx = check(sx + hx, imgW); sy = check(sy + hy, imgH); } sx = check(sx + vx, imgW); sy = check(sy + vy, imgH); } return dstRaster; } } static class ShortSimple extends TexturePaintContext { /** * Constructs a new ShortSimple.TexturePaintContext works with DataBufferShort rasters. * This is simple paint context uses NEAREST NEIGHBOUR interpolation. * @param img - the BufferedImage object used as texture * @param anchor - the Rectangle2D bounds of texture piece to be painted * @param t - the AffineTransform applied to texture painting */ public ShortSimple(BufferedImage img, Rectangle2D anchor, AffineTransform t) { super(img, anchor, t); } @Override public Raster getRaster(int dstX, int dstY, int dstWidth, int dstHeight) { prepare(dstX, dstY, dstWidth, dstHeight); short[] src = access.getDataUShort(srcBuf); short[] dst = access.getDataUShort(dstBuf); int k = 0; for(int j = 0; j < dstHeight; j++) { for(int i = 0; i < dstWidth; i++) { dst[k++] = src[(sx >> 8) + (sy >> 8) * srcWidth]; sx = check(sx + hx, imgW); sy = check(sy + hy, imgH); } sx = check(sx + vx, imgW); sy = check(sy + vy, imgH); } return dstRaster; } } static class CommonSimple extends TexturePaintContext { /** * Constructs a new CommonSimple.TexturePaintContext works with any raster type. * This is simple paint context uses NEAREST NEIGHBOUR interpolation. * @param img - the BufferedImage object used as texture * @param anchor - the Rectangle2D bounds of texture piece to be painted * @param t - the AffineTransform applied to texture painting */ public CommonSimple(BufferedImage img, Rectangle2D anchor, AffineTransform t) { super(img, anchor, t); } @Override public Raster getRaster(int dstX, int dstY, int dstWidth, int dstHeight) { prepare(dstX, dstY, dstWidth, dstHeight); for(int j = 0; j < dstHeight; j++) { for(int i = 0; i < dstWidth; i++) { dstRaster.setDataElements(dstX + i, dstY + j, srcRaster.getDataElements(sx >> 8, sy >> 8, null)); sx = check(sx + hx, imgW); sy = check(sy + hy, imgH); } sx = check(sx + vx, imgW); sy = check(sy + vy, imgH); } return dstRaster; } } static class IntBilinear extends TexturePaintContext { /** * Constructs a new IntSimple.TexturePaintContext works with DataBufferInt rasters. * This paint context uses BILINEAR interpolation. * @param img - the BufferedImage object used as texture * @param anchor - the Rectangle2D bounds of texture piece to be painted * @param t - the AffineTransform applied to texture painting */ public IntBilinear(BufferedImage img, Rectangle2D anchor, AffineTransform t) { super(img, anchor, t); } @Override public Raster getRaster(int dstX, int dstY, int dstWidth, int dstHeight) { prepare(dstX, dstY, dstWidth, dstHeight); int[] src = access.getDataInt(srcBuf); int[] dst = access.getDataInt(dstBuf); int k = 0; for(int j = 0; j < dstHeight; j++) { for(int i = 0; i < dstWidth; i++) { int wx1 = sx & 0xFF; int wy1 = sy & 0xFF; int wx0 = 0xFF - wx1; int wy0 = 0xFF - wy1; weight[0] = wx0 * wy0; weight[1] = wx1 * wy0; weight[2] = wx0 * wy1; weight[3] = wx1 * wy1; int x0 = sx >> 8; int y0 = sy >> 8; int x1 = check(x0 + 1, srcWidth); int y1 = check(y0 + 1, srcHeight); y0 *= srcWidth; y1 *= srcWidth; value[0] = src[x0 + y0]; value[1] = src[x1 + y0]; value[2] = src[x0 + y1]; value[3] = src[x1 + y1]; int color = 0; for(int n = 0; n < 32; n += 8) { int comp = 0; for(int m = 0; m < 4; m++) { comp += ((value[m] >> n) & 0xFF) * weight[m]; } color |= (comp >> 16) << n; } dst[k++] = color; sx = check(sx + hx, imgW); sy = check(sy + hy, imgH); } sx = check(sx + vx, imgW); sy = check(sy + vy, imgH); } return dstRaster; } } static class ByteBilinear extends TexturePaintContext { /** * Constructs a new ByteSimple.TexturePaintContext works with DataBufferByte rasters. * This paint context uses BILINEAR interpolation. * @param img - the BufferedImage object used as texture * @param anchor - the Rectangle2D bounds of texture piece to be painted * @param t - the AffineTransform applied to texture painting */ public ByteBilinear(BufferedImage img, Rectangle2D anchor, AffineTransform t) { super(img, anchor, t); } @Override public Raster getRaster(int dstX, int dstY, int dstWidth, int dstHeight) { prepare(dstX, dstY, dstWidth, dstHeight); byte[] src = access.getDataByte(srcBuf); byte[] dst = access.getDataByte(dstBuf); int k = 0; for(int j = 0; j < dstHeight; j++) { for(int i = 0; i < dstWidth; i++) { int wx1 = sx & 0xFF; int wy1 = sy & 0xFF; int wx0 = 0xFF - wx1; int wy0 = 0xFF - wy1; weight[0] = wx0 * wy0; weight[1] = wx1 * wy0; weight[2] = wx0 * wy1; weight[3] = wx1 * wy1; int x0 = sx >> 8; int y0 = sy >> 8; int x1 = check(x0 + 1, srcWidth); int y1 = check(y0 + 1, srcHeight); y0 *= dstWidth; y1 *= dstWidth; value[0] = src[x0 + y0]; value[1] = src[x1 + y0]; value[2] = src[x0 + y1]; value[3] = src[x1 + y1]; int comp = 0; for(int m = 0; m < 4; m++) { comp += value[m] * weight[m]; } dst[k++] = (byte)(comp >> 16); sx = check(sx + hx, imgW); sy = check(sy + hy, imgH); } sx = check(sx + vx, imgW); sy = check(sy + vy, imgH); } return dstRaster; } } static class ShortBilinear extends TexturePaintContext { /** * Constructs a new ShortSimple.TexturePaintContext works with DataBufferShort rasters. * This paint context uses BILINEAR interpolation. * @param img - the BufferedImage object used as texture * @param anchor - the Rectangle2D bounds of texture piece to be painted * @param t - the AffineTransform applied to texture painting */ public ShortBilinear(BufferedImage img, Rectangle2D anchor, AffineTransform t) { super(img, anchor, t); } @Override public Raster getRaster(int dstX, int dstY, int dstWidth, int dstHeight) { prepare(dstX, dstY, dstWidth, dstHeight); short[] src = access.getDataUShort(srcBuf); short[] dst = access.getDataUShort(dstBuf); int k = 0; for(int j = 0; j < dstHeight; j++) { for(int i = 0; i < dstWidth; i++) { int wx1 = sx & 0xFF; int wy1 = sy & 0xFF; int wx0 = 0xFF - wx1; int wy0 = 0xFF - wy1; weight[0] = wx0 * wy0; weight[1] = wx1 * wy0; weight[2] = wx0 * wy1; weight[3] = wx1 * wy1; int x0 = sx >> 8; int y0 = sy >> 8; int x1 = check(x0 + 1, srcWidth); int y1 = check(y0 + 1, srcHeight); y0 *= dstWidth; y1 *= dstWidth; value[0] = src[x0 + y0]; value[1] = src[x1 + y0]; value[2] = src[x0 + y1]; value[3] = src[x1 + y1]; short color = 0; for(int n = 0; n < 16; n += 8) { int comp = 0; for(int m = 0; m < 4; m++) { comp += ((value[m] >> n) & 0xFF) * weight[m]; } color |= (comp >> 16) << n; } dst[k++] = color; sx = check(sx + hx, imgW); sy = check(sy + hy, imgH); } sx = check(sx + vx, imgW); sy = check(sy + vy, imgH); } return dstRaster; } } static class CommonBilinear extends TexturePaintContext { /** * Constructs a new CommonSimple.TexturePaintContext works with any raster type. * This paint context uses BILINEAR interpolation. * @param img - the BufferedImage object used as texture * @param anchor - the Rectangle2D bounds of texture piece to be painted * @param t - the AffineTransform applied to texture painting */ public CommonBilinear(BufferedImage img, Rectangle2D anchor, AffineTransform t) { super(img, anchor, t); } @Override public Raster getRaster(int dstX, int dstY, int dstWidth, int dstHeight) { prepare(dstX, dstY, dstWidth, dstHeight); for(int j = 0; j < dstHeight; j++) { for(int i = 0; i < dstWidth; i++) { int wx1 = sx & 0xFF; int wy1 = sy & 0xFF; int wx0 = 0xFF - wx1; int wy0 = 0xFF - wy1; weight[0] = wx0 * wy0; weight[1] = wx1 * wy0; weight[2] = wx0 * wy1; weight[3] = wx1 * wy1; int x0 = sx >> 8; int y0 = sy >> 8; int x1 = check(x0 + 1, srcWidth); int y1 = check(y0 + 1, srcHeight); value[0] = cm.getRGB(srcRaster.getDataElements(x0, y0, null)); value[1] = cm.getRGB(srcRaster.getDataElements(x1, y0, null)); value[2] = cm.getRGB(srcRaster.getDataElements(x0, y1, null)); value[3] = cm.getRGB(srcRaster.getDataElements(x1, y1, null)); int color = 0; for(int n = 0; n < 32; n += 8) { int comp = 0; for(int m = 0; m < 4; m++) { comp += ((value[m] >> n) & 0xFF) * weight[m]; } color |= (comp >> 16) << n; } dstRaster.setDataElements(dstX + i, dstY + j, cm.getDataElements(color, null)); sx = check(sx + hx, imgW); sy = check(sy + hy, imgH); } sx = check(sx + vx, imgW); sy = check(sy + vy, imgH); } return dstRaster; } } public TexturePaintContext(BufferedImage img, Rectangle2D anchor, AffineTransform t) { this.cm = img.getColorModel(); this.img = img; this.anchor = anchor; this.t = t; srcWidth = img.getWidth(); srcHeight = img.getHeight(); imgW = srcWidth << 8; imgH = srcHeight << 8; double det = t.getDeterminant(); double multW = imgW / (anchor.getWidth() * det); double multH = -imgH / (anchor.getHeight() * det); m11 = (int)(t.getScaleY() * multW); m01 = (int)(t.getShearX() * multW); m00 = (int)(t.getScaleX() * multH); m10 = (int)(t.getShearY() * multH); Point2D p = t.transform(new Point2D.Double(anchor.getX(), anchor.getY()), null); px = (int)p.getX(); py = (int)p.getY(); hx = check2(m11, imgW); hy = check2(m10, imgH); srcRaster = img.getRaster(); srcBuf = srcRaster.getDataBuffer(); access = AwtImageBackdoorAccessor.getInstance(); } /** * Prepares pre-calculated values */ void prepare(int dstX, int dstY, int dstWidth, int dstHeight) { vx = check2(- m01 - m11 * dstWidth, imgW); vy = check2(- m00 - m10 * dstWidth, imgH); int dx = dstX - px; int dy = dstY - py; sx = check2(dx * m11 - dy * m01, imgW); sy = check2(dx * m10 - dy * m00, imgH); dstRaster = cm.createCompatibleWritableRaster(dstWidth, dstHeight); dstBuf = dstRaster.getDataBuffer(); } public void dispose() { } public ColorModel getColorModel() { return cm; } /** * Checks point overrun of texture anchor */ int check(int value, int max) { if (value >= max) { return value - max; } return value; } /** * Checks point overrun of texture anchor */ int check2(int value, int max) { value = value % max; return value < 0 ? max + value : value; } public Raster getRaster(int dstX, int dstY, int dstWidth, int dstHeight) { return dstRaster; } }