001    /*
002     * Contrast
003     * 
004     * Copyright (c) 2001, 2002, 2003 Marco Schmidt.
005     * All rights reserved.
006     */
007    
008    package net.sourceforge.jiu.color.adjustment;
009    
010    import net.sourceforge.jiu.data.GrayIntegerImage;
011    import net.sourceforge.jiu.data.IntegerImage;
012    import net.sourceforge.jiu.data.Palette;
013    import net.sourceforge.jiu.data.Paletted8Image;
014    import net.sourceforge.jiu.data.RGBIntegerImage;
015    import net.sourceforge.jiu.ops.LookupTableOperation;
016    import net.sourceforge.jiu.ops.MissingParameterException;
017    import net.sourceforge.jiu.ops.WrongParameterException;
018    
019    /**
020     * Adjusts the contrast of an image.
021     * The amount of adjustment is given to the constructor as a percentage value between -100 and 100.
022     * -100 will make the resulting image middle-gray, 0 will leave it unchanged, 100 will map it to 
023     * the eight corners of the color cube.
024     * <h3>Usage example</h3>
025     * This code snippet will reduce <code>image</code>'s contrast by 40 percent.
026     * <pre>
027     * Contrast contrast = new Contrast();
028     * contrast.setInputImage(image);
029     * contrast.setContrast(-40);
030     * contrast.process();
031     * PixelImage adjustedImage = contrast.getOutputImage();
032     * </pre>
033     * @author Marco Schmidt
034     */
035    public class Contrast extends LookupTableOperation
036    {
037            private int contrast;
038    
039            private int[] createLookupTable(int numSamples, int contrast)
040            {
041                    int[] result = new int[numSamples];
042                    final int MAX = numSamples - 1;
043                    final float MID = MAX / 2.0f;
044                    for (int i = 0; i < numSamples; i++)
045                    {
046                            if (contrast < 0)
047                            {
048                                    if (i < MID)
049                                    {
050                                            result[i] = (int)(i + (MID - i) * (- contrast) / 100.0f);
051                                    }
052                                    else
053                                    {
054                                            result[i] = (int)(MID + (i - MID) * (100.0f + contrast) / 100.0f);
055                                    }
056                            }
057                            else
058                            {
059                                    if (i < MID)
060                                    {
061                                            result[i] = (int)(i * (100.0f - contrast) / 100.0f);
062                                    }
063                                    else
064                                    {
065                                            result[i] = (int)(i + (MAX - i) * contrast / 100.0f);
066                                    }
067                            }
068                    }
069                    return result;
070            }
071    
072            /**
073             * Returns the contrast adjustment value associated with this opperation.
074             * The value lies between -100 and 100 (including both values).
075             * @return contrast adjustment
076             * @see #setContrast
077             */
078            public int getContrast()
079            {
080                    return contrast;
081            }
082    
083            private void process(Paletted8Image in, Paletted8Image out)
084            {
085                    if (out == null)
086                    {
087                            out = (Paletted8Image)in.createCompatibleImage(in.getWidth(), in.getHeight());
088                    }
089                    Palette palette = out.getPalette();
090                    int numSamples = palette.getMaxValue() + 1;
091                    final int[] LUT = createLookupTable(numSamples, contrast);
092                    for (int c = 0; c < 3; c++)
093                    {
094                            for (int i = 0; i < palette.getNumEntries(); i++)
095                            {
096                                    palette.putSample(c, i, LUT[palette.getSample(c, i)]);
097                            }
098                    }
099                    for (int y = 0; y < in.getHeight(); y++)
100                    {
101                            for (int x = 0; x < in.getWidth(); x++)
102                            {
103                                    out.putSample(0, x, y, in.getSample(0, x, y));
104                            }
105                            setProgress(y, in.getHeight());
106                    }
107                    setOutputImage(out);
108            }
109    
110            public void process() throws
111                    MissingParameterException,
112                    WrongParameterException
113            {
114                    prepareImages();
115                    IntegerImage in = (IntegerImage)getInputImage();
116                    if (in instanceof GrayIntegerImage || in instanceof RGBIntegerImage)
117                    {
118                            setNumTables(in.getNumChannels());
119                            for (int channelIndex = 0; channelIndex < in.getNumChannels(); channelIndex++)
120                            {
121                                    setTable(channelIndex, createLookupTable(in.getMaxSample(channelIndex) + 1, getContrast()));
122                            }
123                            super.process();
124                    }
125                    else
126                    if (in instanceof Paletted8Image)
127                    {
128                            process((Paletted8Image)in, (Paletted8Image)getOutputImage());
129                    }
130                    else
131                    {
132                            throw new WrongParameterException("Contrast operation cannot operate on input image type: " + in.getClass());
133                    }
134            }
135    
136            /**
137             * Sets the value for contrast adjustment to be used within this operation.
138             * @param newContrast new contrast, between -100 and 100 (including both values)
139             * @throws IllegalArgumentException if the new contrast value is not in the above mentioned interval
140             * @see #getContrast
141             */
142            public void setContrast(int newContrast)
143            {
144                    if (newContrast < -100)
145                    {
146                            throw new IllegalArgumentException("Contrast must be at least -100: " + newContrast);
147                    }
148                    if (newContrast > 100)
149                    {
150                            throw new IllegalArgumentException("Contrast must be at most 100: " + newContrast);
151                    }
152                    contrast = newContrast;
153            }
154    }