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;
031
032import java.awt.geom.AffineTransform;
033import java.awt.image.BufferedImage;
034import java.io.ByteArrayInputStream;
035import java.io.ByteArrayOutputStream;
036import java.io.IOException;
037import java.io.OutputStreamWriter;
038import java.util.Comparator;
039
040import org.apache.batik.svggen.SVGGraphics2DIOException;
041import org.apache.batik.transcoder.TranscoderException;
042import org.apache.batik.transcoder.TranscoderInput;
043import org.apache.batik.transcoder.TranscoderOutput;
044import org.apache.batik.transcoder.image.ImageTranscoder;
045import org.apache.batik.transcoder.image.PNGTranscoder;
046import org.openimaj.image.colour.RGBColour;
047import org.openimaj.image.renderer.ImageRenderer;
048import org.openimaj.image.renderer.RenderHints;
049import org.openimaj.image.renderer.SVGRenderHints;
050import org.openimaj.image.renderer.SVGRenderer;
051import org.openimaj.math.geometry.point.Point2dImpl;
052import org.openimaj.math.geometry.shape.Rectangle;
053
054public class SVGImage extends Image<Float[], SVGImage> {
055        private SVGRenderer renderer;
056
057        /**
058         * @param hints
059         */
060        public SVGImage(SVGRenderHints hints) {
061                this.renderer = new SVGRenderer(null, hints);
062                this.renderer.setImage(this);
063        }
064
065        private SVGImage() {
066                // TODO Auto-generated constructor stub
067        }
068
069        /**
070         * Construct an empty SVG-backed image of the given size
071         *
072         * @param w
073         *            the width
074         * @param h
075         *            the height
076         */
077        public SVGImage(int w, int h) {
078                this(new SVGRenderHints(w, h));
079        }
080
081        @Override
082        public SVGImage abs() {
083                return this;
084        }
085
086        @Override
087        public SVGImage addInplace(Image<?, ?> im) {
088                if (!(im instanceof SVGImage)) {
089                        this.renderer.drawOIImage(im);
090                } else {
091                        this.renderer.drawImage((SVGImage) im, 0, 0);
092                }
093                return null;
094        }
095
096        @Override
097        public SVGImage addInplace(Float[] num) {
098                return this;
099        }
100
101        @Override
102        public SVGImage clip(Float[] min, Float[] max) {
103                return this;
104        }
105
106        @Override
107        public SVGImage clipMax(Float[] thresh) {
108                return this;
109        }
110
111        @Override
112        public SVGImage clipMin(Float[] thresh) {
113                return this;
114        }
115
116        @Override
117        public SVGImage clone() {
118                final SVGImage svgImage = new SVGImage();
119                svgImage.renderer = new SVGRenderer(svgImage, this.renderer.getGraphics2D().create());
120                return svgImage;
121        }
122
123        @Override
124        public SVGRenderer createRenderer() {
125                return this.renderer;
126        }
127
128        @Override
129        public ImageRenderer<Float[], SVGImage> createRenderer(RenderHints options) {
130                return this.renderer;
131        }
132
133        @Override
134        public SVGImage divideInplace(Image<?, ?> im) {
135                throw new UnsupportedOperationException();
136        }
137
138        @Override
139        public SVGImage divideInplace(Float[] val) {
140                throw new UnsupportedOperationException();
141        }
142
143        @Override
144        public SVGImage extractROI(int x, int y, SVGImage img) {
145                img.renderer = new SVGRenderer(img, img.renderer.getRenderHints(), this.renderer.getGraphics2D().create(x, y,
146                                img.getWidth(), img.getHeight()));
147                return img;
148        }
149
150        @Override
151        public SVGImage extractROI(int x, int y, int w, int h) {
152                final SVGImage ret = new SVGImage(w, h);
153                return extractROI(x, y, ret);
154        }
155
156        @Override
157        public SVGImage extractCentreSubPix(float cx, float cy, SVGImage out) {
158                return extractCenter((int) cx, (int) cy, out.getWidth(), out.getHeight());
159        }
160
161        @Override
162        public SVGImage fill(Float[] colour) {
163                final SVGRenderHints hint = (SVGRenderHints) this.renderer.getRenderHints();
164                this.renderer = new SVGRenderer(hint);
165                this.renderer.drawShapeFilled(this.getBounds(), colour);
166                return this;
167        }
168
169        @Override
170        public SVGImage flipX() {
171                final AffineTransform tx = AffineTransform.getScaleInstance(-1, 1);
172                tx.translate(this.getWidth(), 0);
173                this.renderer.getGraphics2D().transform(tx);
174                return this;
175        }
176
177        @Override
178        public SVGImage flipY() {
179                final AffineTransform tx = AffineTransform.getScaleInstance(1, -1);
180                tx.translate(0, -this.getHeight());
181                this.renderer.getGraphics2D().transform(tx);
182                return this;
183        }
184
185        @Override
186        public Rectangle getContentArea() {
187                return new Rectangle(0, 0, getWidth(), getHeight());
188        }
189
190        @Override
191        public SVGImage getField(org.openimaj.image.Image.Field f) {
192                throw new UnsupportedOperationException();
193        }
194
195        @Override
196        public SVGImage getFieldCopy(org.openimaj.image.Image.Field f) {
197                throw new UnsupportedOperationException();
198        }
199
200        @Override
201        public SVGImage getFieldInterpolate(org.openimaj.image.Image.Field f) {
202                throw new UnsupportedOperationException();
203        }
204
205        @Override
206        public int getHeight() {
207                return this.renderer.getGraphics2D().getSVGCanvasSize().height;
208        }
209
210        @Override
211        public Float[] getPixel(int x, int y) {
212                throw new UnsupportedOperationException();
213        }
214
215        @Override
216        public Comparator<? super Float[]> getPixelComparator() {
217                throw new UnsupportedOperationException();
218        }
219
220        @Override
221        public Float[] getPixelInterp(double x, double y) {
222                throw new UnsupportedOperationException();
223        }
224
225        @Override
226        public Float[] getPixelInterp(double x, double y, Float[] backgroundColour) {
227                throw new UnsupportedOperationException();
228        }
229
230        @Override
231        public int getWidth() {
232                return this.renderer.getGraphics2D().getSVGCanvasSize().width;
233        }
234
235        @Override
236        public SVGImage internalCopy(SVGImage im) {
237                this.renderer = im.renderer.clone();
238                this.renderer.setImage(this);
239                return this;
240        }
241
242        @Override
243        public SVGImage internalAssign(SVGImage im) {
244                this.renderer = im.renderer;
245                return this;
246        }
247
248        @Override
249        public SVGImage internalAssign(int[] pixelData, int width, int height) {
250                throw new UnsupportedOperationException();
251        }
252
253        @Override
254        public SVGImage inverse() {
255                throw new UnsupportedOperationException();
256        }
257
258        @Override
259        public Float[] max() {
260                throw new UnsupportedOperationException();
261        }
262
263        @Override
264        public Float[] min() {
265                throw new UnsupportedOperationException();
266        }
267
268        @Override
269        public SVGImage multiplyInplace(Image<?, ?> im) {
270                throw new UnsupportedOperationException();
271        }
272
273        @Override
274        public SVGImage multiplyInplace(Float[] num) {
275                throw new UnsupportedOperationException();
276        }
277
278        @Override
279        public SVGImage newInstance(int width, int height) {
280                return new SVGImage(width, height);
281        }
282
283        @Override
284        public SVGImage normalise() {
285                throw new UnsupportedOperationException();
286        }
287
288        @Override
289        public void setPixel(int x, int y, Float[] val) {
290                this.renderer.drawPoint(new Point2dImpl(x, y), val, 1);
291        }
292
293        @Override
294        public SVGImage subtractInplace(Image<?, ?> im) {
295                throw new UnsupportedOperationException();
296        }
297
298        @Override
299        public SVGImage subtractInplace(Float[] num) {
300                throw new UnsupportedOperationException();
301        }
302
303        @Override
304        public SVGImage threshold(Float[] thresh) {
305                throw new UnsupportedOperationException();
306        }
307
308        private static class BufferedImageTranscoder extends ImageTranscoder {
309
310                private BufferedImage img;
311
312                @Override
313                public BufferedImage createImage(int w, int h) {
314                        final BufferedImage bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
315                        return bi;
316                }
317
318                @Override
319                public void writeImage(BufferedImage img, TranscoderOutput arg1)
320                                throws TranscoderException
321                {
322                        this.img = img;
323                }
324
325                public BufferedImage getBufferedImage() {
326                        return this.img;
327                }
328
329        }
330
331        @Override
332        public byte[] toByteImage() {
333                final MBFImage mbf = createMBFImage();
334                return mbf.toByteImage();
335        }
336
337        public MBFImage createMBFImage() {
338                final BufferedImageTranscoder t = new BufferedImageTranscoder();
339                t.addTranscodingHint(PNGTranscoder.KEY_WIDTH, (float) getWidth());
340                t.addTranscodingHint(PNGTranscoder.KEY_HEIGHT, (float) getHeight());
341                final ByteArrayOutputStream baos = new ByteArrayOutputStream();
342                try {
343                        this.renderer.write(new OutputStreamWriter(baos));
344                        baos.flush();
345                        baos.close();
346                        final byte[] barr = baos.toByteArray();
347                        final TranscoderInput input = new TranscoderInput(new ByteArrayInputStream(barr));
348                        t.transcode(input, null);
349                } catch (final SVGGraphics2DIOException e) {
350                } catch (final IOException e) {
351                } catch (final TranscoderException e) {
352                }
353                final MBFImage mbf = ImageUtilities.createMBFImage(t.getBufferedImage(), true);
354                return mbf;
355        }
356
357        @Override
358        public int[] toPackedARGBPixels() {
359                final MBFImage mbf = createMBFImage();
360                return mbf.toPackedARGBPixels();
361        }
362
363        @Override
364        public SVGImage zero() {
365                final SVGRenderHints hint = (SVGRenderHints) this.renderer.getRenderHints();
366                this.renderer = new SVGRenderer(hint);
367                this.renderer.drawShapeFilled(this.getBounds(), RGBColour.BLACK);
368                return this;
369        }
370
371        @Override
372        public SVGImage overlayInplace(SVGImage image, int x, int y) {
373                throw new UnsupportedOperationException();
374        }
375
376        @Override
377        public SVGImage replace(Float[] target, Float[] replacement) {
378                throw new UnsupportedOperationException();
379        }
380}