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}