001/** 002 * Copyright (c) 2011, The University of Southampton and the individual contributors. 003 * All rights reserved. 004 * 005 * Redistribution and use in source and binary forms, with or without modification, 006 * are permitted provided that the following conditions are met: 007 * 008 * * Redistributions of source code must retain the above copyright notice, 009 * this list of conditions and the following disclaimer. 010 * 011 * * Redistributions in binary form must reproduce the above copyright notice, 012 * this list of conditions and the following disclaimer in the documentation 013 * and/or other materials provided with the distribution. 014 * 015 * * Neither the name of the University of Southampton nor the names of its 016 * contributors may be used to endorse or promote products derived from this 017 * software without specific prior written permission. 018 * 019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 029 */ 030package org.openimaj.image.renderer; 031 032import java.util.Arrays; 033 034import org.openimaj.image.FImage; 035import org.openimaj.image.ImageUtilities; 036import org.openimaj.image.MBFImage; 037 038/** 039 * {@link ImageRenderer} for {@link MBFImage} images. 040 * 041 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 042 * 043 */ 044public class MBFImageRenderer extends MultiBandRenderer<Float, MBFImage, FImage> { 045 046 /** 047 * Construct with given target image. 048 * 049 * @param targetImage 050 * the target image. 051 */ 052 public MBFImageRenderer(final MBFImage targetImage) { 053 super(targetImage); 054 } 055 056 /** 057 * Construct with given target image and rendering hints. 058 * 059 * @param targetImage 060 * the target image. 061 * @param hints 062 * the render hints 063 */ 064 public MBFImageRenderer(final MBFImage targetImage, final RenderHints hints) { 065 super(targetImage, hints); 066 } 067 068 @Override 069 public Float[] defaultBackgroundColour() { 070 return new Float[this.targetImage.numBands()]; 071 } 072 073 @Override 074 public Float[] defaultForegroundColour() { 075 final Float[] c = new Float[this.targetImage.numBands()]; 076 Arrays.fill(c, 1f); 077 return c; 078 } 079 080 /** 081 * Draw the provided image at the given coordinates. Parts of the image 082 * outside the bounds of this image will be ignored 083 * 084 * @param image 085 * Image to draw. 086 * @param x 087 * x-coordinate 088 * @param y 089 * y-coordinate 090 */ 091 @Override 092 public void drawImage(final MBFImage image, final int x, final int y) { 093 final int targetBands = this.targetImage.numBands(); 094 final int imageBands = image.numBands(); 095 096 if (targetBands == imageBands && targetBands == 3) { 097 this.drawImage3(image, x, y); 098 return; 099 } else if (targetBands < 3 || targetBands > 4 || imageBands < 3 || imageBands > 4) { 100 super.drawImage(image, x, y); 101 return; 102 } 103 104 final int stopx = Math.min(this.targetImage.getWidth(), x + image.getWidth()); 105 final int stopy = Math.min(this.targetImage.getHeight(), y + image.getHeight()); 106 final int startx = Math.max(0, x); 107 final int starty = Math.max(0, y); 108 109 final float[][][] thisPixels = new float[targetBands][][]; 110 for (int i = 0; i < thisPixels.length; i++) 111 thisPixels[i] = this.targetImage.getBand(i).pixels; 112 113 final float[][][] thatPixels = new float[imageBands][][]; 114 for (int i = 0; i < thatPixels.length; i++) 115 thatPixels[i] = image.getBand(i).pixels; 116 117 /** 118 * If either image is 4 channel then we deal with the alpha channel 119 * correctly. Basically you add together the pixel values such that the 120 * pixel on top dominates (i.e. the image being added) 121 */ 122// final float thisA = 1.0f, thatA = 1.0f, thisR, thisG, thisB, thatR, thatG, thatB, a, r, g, b; 123 if(thisPixels.length == 4 && thatPixels.length == 4){ 124 drawBothAlpha(x, y, stopx, stopy, startx, starty, thisPixels, thatPixels); 125 } else if (thisPixels.length == 4){ 126 drawThisAlpha(x, y, stopx, stopy, startx, starty, thisPixels, thatPixels); 127 } else{ 128 drawThatAlpha(x, y, stopx, stopy, startx, starty, thisPixels, thatPixels); 129 } 130 } 131 132 private void drawBothAlpha(final int x, final int y, final int stopx, 133 final int stopy, final int startx, final int starty, 134 final float[][][] thisPixels, final float[][][] thatPixels) { 135 float[] out = new float[4]; 136 for (int yy = starty; yy < stopy; yy++) { 137 final int thatY = yy - y; 138 139 for (int xx = startx; xx < stopx; xx++) { 140 141 final int thatX = xx - x; 142 float thisA = thisPixels[3][yy][xx] ; 143 float thatA = thatPixels[3][thatY][thatX] ; 144 ImageUtilities.alphaCompositePixel(out, 145 thisPixels[0][yy][xx], thisPixels[1][yy][xx], thisPixels[2][yy][xx], thisA, 146 thatPixels[0][thatY][thatX], thatPixels[1][thatY][thatX], thatPixels[2][thatY][thatX], thatA 147 ); 148 149 thisPixels[0][yy][xx] = out[0]; 150 thisPixels[1][yy][xx] = out[1]; 151 thisPixels[2][yy][xx] = out[2]; 152 thisPixels[3][yy][xx] = out[3]; 153 } 154 } 155 } 156 157 private void drawThisAlpha(final int x, final int y, final int stopx, 158 final int stopy, final int startx, final int starty, 159 final float[][][] thisPixels, final float[][][] thatPixels) { 160 float[] out = new float[4]; 161 for (int yy = starty; yy < stopy; yy++) { 162 final int thatY = yy - y; 163 164 for (int xx = startx; xx < stopx; xx++) { 165 166 final int thatX = xx - x; 167 float thisA = thisPixels[3][yy][xx] ; 168 float thatA = 1f ; 169 ImageUtilities.alphaCompositePixel(out, 170 thisPixels[0][yy][xx], thisPixels[1][yy][xx], thisPixels[2][yy][xx], thisA, 171 thatPixels[0][thatY][thatX], thatPixels[1][thatY][thatX], thatPixels[2][thatY][thatX], thatA 172 ); 173 174 thisPixels[0][yy][xx] = out[0]; 175 thisPixels[1][yy][xx] = out[1]; 176 thisPixels[2][yy][xx] = out[2]; 177 thisPixels[3][yy][xx] = out[3]; 178 } 179 } 180 } 181 182 private void drawThatAlpha(final int x, final int y, final int stopx, 183 final int stopy, final int startx, final int starty, 184 final float[][][] thisPixels, final float[][][] thatPixels) { 185 float[] out = new float[4]; 186 for (int yy = starty; yy < stopy; yy++) { 187 final int thatY = yy - y; 188 189 for (int xx = startx; xx < stopx; xx++) { 190 191 final int thatX = xx - x; 192 float thisA = 1f ; 193 float thatA = thatPixels[3][thatY][thatX] ; 194 ImageUtilities.alphaCompositePixel(out, 195 thisPixels[0][yy][xx], thisPixels[1][yy][xx], thisPixels[2][yy][xx], thisA, 196 thatPixels[0][thatY][thatX], thatPixels[1][thatY][thatX], thatPixels[2][thatY][thatX], thatA 197 ); 198 199 thisPixels[0][yy][xx] = out[0]; 200 thisPixels[1][yy][xx] = out[1]; 201 thisPixels[2][yy][xx] = out[2]; 202 } 203 } 204 } 205 206 protected void drawImage3(final MBFImage image, final int x, final int y) { 207 final int stopx = Math.max(0, Math.min(this.targetImage.getWidth(), x + image.getWidth())); 208 final int stopy = Math.max(0, Math.min(this.targetImage.getHeight(), y + image.getHeight())); 209 final int startx = Math.max(0, x); 210 final int starty = Math.max(0, y); 211 212 if (startx >= stopx || starty >= stopy) 213 return; 214 215 final float[][][] thisPixels = new float[3][][]; 216 for (int i = 0; i < thisPixels.length; i++) 217 thisPixels[i] = this.targetImage.getBand(i).pixels; 218 219 final float[][][] thatPixels = new float[3][][]; 220 for (int i = 0; i < thatPixels.length; i++) 221 thatPixels[i] = image.getBand(i).pixels; 222 223 for (int yy = starty; yy < stopy; yy++) { 224 final int thatY = yy - y; 225 226 System.arraycopy(thatPixels[0][thatY], startx - x, thisPixels[0][yy], startx, stopx - startx); 227 System.arraycopy(thatPixels[1][thatY], startx - x, thisPixels[1][yy], startx, stopx - startx); 228 System.arraycopy(thatPixels[2][thatY], startx - x, thisPixels[2][yy], startx, stopx - startx); 229 230 // for (int xx=startx; xx<stopx; xx++) 231 // { 232 // int thatX = xx - x; 233 // 234 // thisPixels[0][yy][xx] = thatPixels[0][thatY][thatX]; 235 // thisPixels[1][yy][xx] = thatPixels[1][thatY][thatX]; 236 // thisPixels[2][yy][xx] = thatPixels[2][thatY][thatX]; 237 // } 238 } 239 } 240 241 @Override 242 protected void drawHorizLine(final int x1, final int x2, final int y, Float[] col) { 243 col = this.sanitise(col); 244 if (y < 0 || y > this.targetImage.getHeight() - 1) 245 return; 246 247 final int startx = Math.max(0, Math.min(x1, x2)); 248 final int stopx = Math.min(Math.max(x1, x2), this.targetImage.getWidth() - 1); 249 final int nbands = Math.min(col.length, this.targetImage.numBands()); 250 251 for (int b = 0; b < nbands; b++) { 252 final float[][] img = this.targetImage.getBand(b).pixels; 253 final float c = col[b]; 254 255 for (int x = startx; x <= stopx; x++) { 256 img[y][x] = c; 257 } 258 } 259 } 260 261 @Override 262 protected Float[] sanitise(final Float[] colour) 263 { 264 return this.targetImage.colourSpace.sanitise(colour); 265 } 266}