001    /*
002     * ImageToImageOperation
003     * 
004     * Copyright (c) 2001, 2002 Marco Schmidt.
005     * All rights reserved.
006     */
007    
008    package net.sourceforge.jiu.ops;
009    
010    import net.sourceforge.jiu.data.PixelImage;
011    import net.sourceforge.jiu.ops.MissingParameterException;
012    import net.sourceforge.jiu.ops.Operation;
013    import net.sourceforge.jiu.ops.WrongParameterException;
014    
015    /**
016     * An operation that acesses an input image and produces data for an output image.
017     * This abstract class only provides methods to get and set those images.
018     * <p>
019     * Normally, an operation creates the output image itself.
020     * However, an output image can be specified by the user with
021     * {@link #setOutputImage}.
022     * This could be done when existing image objects are to be reused.
023     * <p>
024     * An operation extending ImageToImageOperation must check if 
025     * (1) a user-defined output image is available and
026     * (2) whether that image matches the required criteria.
027     * The criteria depend on the operation - example: for an operation that
028     * rotates an image by 180 degrees, an output image must have the same resolution
029     * as the input image and be of the same type.
030     * <p>
031     * If an output image is not available (case #1), the operation must create 
032     * the matching output image itself.
033     * It should know best what is required.
034     * Very generic methods (like rotation of images by 90 degrees) must know
035     * relatively little about the image.
036     * They can make use of PixelImage.createCompatibleImage(int, int) and provide 
037     * width and height.
038     * That way, the operation works for all kinds of images, like BilevelImage,
039     * Paletted8Image, Gray8Image, RGB24Image etc.
040     * <p>
041     * If a user-provided image does not match the required criteria, an appropriate 
042     * exception (most of the time {@link WrongParameterException} will do) with a 
043     * descriptive error message must be thrown.
044     * In the example of the 90-degree rotation, the width of the output image must
045     * be equal to the height of the input image and vice versa.
046     * The types of input and output must be equal.
047     * <p>
048     * However, there are limits to the checks on user-provided output images.
049     * As an example, a generic test could not check if a paletted output image
050     * has the same palette as the input counterpart because it treats all images
051     * based on IntegerImage the same.
052     * <p>
053     * When performing an image-to-image-operation, the input image can possibly be 
054     * used as the output image.
055     * This can be done
056     * <ul>
057     * <li>if input and output are of the same type and resolution and</li>
058     * <li>if the operation needs only one input pixel to compute the output pixel
059     *  at any given position.</li>
060     * </ul>
061     * <p>
062     * Mirroring the image horizontally is an example of an operation that can be
063     * implemented that way - the operation starts at the top left and at the bottom
064     * right pixel, swaps them and proceeds one pixel to the right of the top left
065     * pixel (and one to the left of the bottom right pixel).
066     *
067     * @author Marco Schmidt
068     * @since 0.6.0
069     */
070    public abstract class ImageToImageOperation extends Operation
071    {
072            private PixelImage inputImage;
073            private PixelImage outputImage;
074            private boolean canInAndOutBeEqual;
075    
076            /**
077             * Creates an object of this class and sets input image 
078             * and output image to the argument values.
079             */
080            public ImageToImageOperation(PixelImage in, PixelImage out)
081            {
082                    super();
083                    setInputImage(in);
084                    setOutputImage(out);
085                    canInAndOutBeEqual = false;
086            }
087    
088            /**
089             * Creates an object of this class and sets the input image 
090             * to the argument value, output image to <code>null</code>.
091             */
092            public ImageToImageOperation(PixelImage in)
093            {
094                    this(in, null);
095            }
096    
097            /**
098             * Creates an object of this class and sets both input image 
099             * and output image to <code>null</code>.
100             */
101            public ImageToImageOperation()
102            {
103                    this(null, null);
104            }
105    
106            /**
107             * Returns if input and output image are allowed to be the same object.
108             * @see #setCanInputAndOutputBeEqual
109             */
110            public boolean canInputAndOutputBeEqual()
111            {
112                    return canInAndOutBeEqual;
113            }
114    
115            /**
116             * If both an input and an output image have been specified (both non-null), 
117             * this method compares their width and height properties and throws
118             * an exception if the two images do not have the same resolution.
119             * @throws WrongParameterException if input and output images exist and their 
120             *  resolutions differ
121             */
122            public void ensureImagesHaveSameResolution() throws WrongParameterException
123            {
124                    PixelImage in = getInputImage();
125                    PixelImage out = getOutputImage();
126                    if (in != null && out != null)
127                    {
128                            if (in.getWidth() != out.getWidth())
129                            {
130                                    throw new WrongParameterException("Input and output image must have the same width.");
131                            }
132                            if (in.getHeight() != out.getHeight())
133                            {
134                                    throw new WrongParameterException("Input and output image must have the same height.");
135                            }
136                    }
137            }
138    
139            /**
140             * If {@link #getInputImage} returns <code>null</code> this
141             * method throws a {@link net.sourceforge.jiu.ops.MissingParameterException}
142             * complaining that an input image is missing.
143             * @throws MissingParameterException if no input image is available
144             */
145            public void ensureInputImageIsAvailable() throws MissingParameterException
146            {
147                    if (getInputImage() == null)
148                    {
149                            throw new MissingParameterException("Input image missing.");
150                    }
151            }
152    
153            /**
154             * If an output image has been specified this method will compare
155             * its resolution with the argument resolution and throw an exception if the
156             * resolutions differ.
157             * If no output image has been specified nothing happens.
158             * @param width the horizontal pixel resolution that the output image must have
159             * @param height the vertical pixel resolution that the output image must have
160             * @throws WrongParameterException if the resolutions differ
161             */
162            public void ensureOutputImageResolution(int width, int height) throws WrongParameterException
163            {
164                    PixelImage out = getOutputImage();
165                    if (out != null)
166                    {
167                            if (out.getWidth() != width)
168                            {
169                                    throw new WrongParameterException("Output image must have width " + width + " (got: " + out.getWidth() + ").");
170                            }
171                            if (out.getHeight() != height)
172                            {
173                                    throw new WrongParameterException("Output image must have height " + height + " (got: " + out.getHeight() + ").");
174                            }
175                    }
176            }
177    
178            /**
179             * Returns the input image stored in this object.
180             * @return input image, possibly <code>null</code>
181             */
182            public PixelImage getInputImage()
183            {
184                    return inputImage;
185            }
186    
187            /**
188             * Returns the output image stored in this object.
189             * @return output image, possibly <code>null</code>
190             */
191            public PixelImage getOutputImage()
192            {
193                    return outputImage;
194            }
195    
196            /**
197             * Specify if input and output image are allowed to be the same object.
198             * @see #canInputAndOutputBeEqual
199             */
200            public void setCanInputAndOutputBeEqual(boolean newValue)
201            {
202                    canInAndOutBeEqual = newValue;
203            }
204    
205            /**
206             * Sets the input image stored in this object to the argument.
207             * Argument can be <code>null</code>.
208             * @param in the new input image of this object
209             */
210            public void setInputImage(PixelImage in)
211            {
212                    inputImage = in;
213            }
214    
215            /**
216             * Sets the output image stored in this object to the argument.
217             * Argument can be <code>null</code>.
218             * @param out the new output image of this object
219             */
220            public void setOutputImage(PixelImage out)
221            {
222                    outputImage = out;
223            }
224    }