QJCC homepage

biz.chitec.quarterback.swing
Class ImageEditModel

java.lang.Object
  extended bybiz.chitec.quarterback.swing.ImageEditModel

public class ImageEditModel
extends java.lang.Object

Image editing model for some basic operations: Cropping, scaling, changing format and compression level

ImageEditModel allows some basic operation on a given image file: Cropping to take only a part of the image, scaling to make the image smaller (or larger...), and changing storage type (PNG versus JPG) and JPG compression level, if applicable.

This model gets its image by the setImage(imagedata,lossyflag) method. The image data is taken verbatim from a file or any other source producing an image in JPG or PNG encoding. To get an editing result, call getImage(). To see whether any editing took place at all, call isEdited() and/or hook a listener onto the EDITED property. Images can also be loaded and stored directly between the model and a disk file.

ImageEditModel creates internally a java.awt.Image from the given data. All operations are always performed on this representation and are therefore lossless, even if output is set to, say, JPG with a very low quality level. An editor can show the data in the selected quality level on screen, but the model keeps the lossless representation internally. The first place where this quality-decreased data shows up again programmatically is the getImage() method. Therefore, during editing, it is even possible to change JPG quality level in both directions. Undo always starts again with the data given through setImage().

This class has been derived from the TinyImageEditor which featured both editor widget and model at once. As the possibility to change an image's proportions etc. can also be useful without the dialog widget, the data representation has been seperated from the user interface lateron. Note that this model is not aware of any thread changing issues as TinyImageEditor. the model works totally independent from the AWT thread. Especially, the events generated by ImageEditModel are not sent on the AWT thread! An event receiver must dispatch itself appropriately if it wants to do anything on the GUI.

As usual, the model interacts with any other parts of the program through the means of events. Note however that it does not pass the image data itself with the imageChanged events. Any interested event receiver must call the getImage() operations to receive the image data after the event has been received.

Version:
$Id: acdc108ac6c3ae45ace1a4f2eb18a0494750b8ff $
Author:
Dirk Hillbrecht 2006. Distributed under the terms of the GNU LGPL.

Field Summary
private static float DELTA
           
private  boolean edited
           
private  java.awt.image.BufferedImage losslessimage
           
private  boolean lossy
           
private  boolean lossychanged
           
private  java.awt.image.BufferedImage lossyimage
           
private  float lossyquality
           
private  byte[] originalbytes
           
private  boolean originallossy
           
private  java.beans.PropertyChangeSupport pcs
           
private  byte[] resultbytes
           
private  boolean resultchanged
           
 
Constructor Summary
ImageEditModel()
           
 
Method Summary
 void addPropertyChangeListener(java.beans.PropertyChangeListener listener)
           
 void addPropertyChangeListener(java.lang.String propertyName, java.beans.PropertyChangeListener listener)
           
 void clear()
           
private  void createLossyImage()
          Create the lossy image by reading the lossy byte stream.
private  void createResultBytes()
          Creates the result byte stream and the lossy image from the lossless image.
 void cropImage(java.awt.Rectangle croprect)
          Performs the crop operation The rectangle to be left over from the crop operation is taken from the image pane.
private  void fireEdited(boolean newval)
          Set internally whether something has been edited compared to the setImage() state.
 byte[] getImage()
          Returns the edited image as lossy or lossless byte stream.
 int getImageHeight()
          Return height of the image.
 int getImageWidth()
          Return width of the image.
 java.awt.image.BufferedImage getLossLessBufferedImage()
          Returns the internally stored lossless image object.
 java.awt.image.BufferedImage getLossyBufferedImage()
          Returns the internally stored lossy image object.
 float getLossyQuality()
          Get the lossy quality setting.
 java.beans.PropertyChangeListener[] getPropertyChangeListeners()
           
 java.beans.PropertyChangeListener[] getPropertyChangeListeners(java.lang.String propertyName)
           
 boolean hasListeners(java.lang.String propertyName)
           
 boolean isEdited()
          Returns whether the current editor content is edited in any way.
 boolean isLossy()
          Returns whether the result byte stream is lossy or not.
 void loadImage(java.lang.String filename)
          Load an image directly from a file into the edit model.
 void removePropertyChangeListener(java.beans.PropertyChangeListener listener)
           
 void removePropertyChangeListener(java.lang.String propertyName, java.beans.PropertyChangeListener listener)
           
 void rescaleImage(int newx, int newy, boolean keepratio)
          Performs the rescale operation.
private  void restoreImage()
          Restore the complete editor from the originally given image.
 void setImage(byte[] imagedatax, boolean lossyx)
          Sets the image to work on.
 void setLossy(boolean lx)
          Sets whether the output should be lossy or not
 void setLossyQuality(float lq)
          Sets the lossy quality of the output.
 int setLossyQualityBySize(int size, float maxquality)
          Set the lossy quality so that the resulting file is about the size given as parameter in bytes.
 void softenImage(float factor)
          Performs a soften operation.
 void storeImage(java.lang.String filename)
          Stores an image directly from the model into a file.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Field Detail

DELTA

private static final float DELTA
See Also:
Constant Field Values

originalbytes

private byte[] originalbytes

losslessimage

private java.awt.image.BufferedImage losslessimage

lossyimage

private java.awt.image.BufferedImage lossyimage

resultbytes

private byte[] resultbytes

originallossy

private boolean originallossy

lossy

private boolean lossy

edited

private boolean edited

resultchanged

private boolean resultchanged

lossychanged

private boolean lossychanged

lossyquality

private float lossyquality

pcs

private java.beans.PropertyChangeSupport pcs
Constructor Detail

ImageEditModel

public ImageEditModel()
Method Detail

addPropertyChangeListener

public void addPropertyChangeListener(java.beans.PropertyChangeListener listener)

addPropertyChangeListener

public void addPropertyChangeListener(java.lang.String propertyName,
                                      java.beans.PropertyChangeListener listener)

getPropertyChangeListeners

public java.beans.PropertyChangeListener[] getPropertyChangeListeners()

getPropertyChangeListeners

public java.beans.PropertyChangeListener[] getPropertyChangeListeners(java.lang.String propertyName)

hasListeners

public boolean hasListeners(java.lang.String propertyName)

removePropertyChangeListener

public void removePropertyChangeListener(java.beans.PropertyChangeListener listener)

removePropertyChangeListener

public void removePropertyChangeListener(java.lang.String propertyName,
                                         java.beans.PropertyChangeListener listener)

loadImage

public void loadImage(java.lang.String filename)
               throws java.io.IOException
Load an image directly from a file into the edit model. This method can read JPG or PNG files directly into the model. The filename must end with ".JPG" or ".PNG" (case insensitive) so the the loader can identify the file type.

Parameters:
filename - Full name of the file with the image. Must end with ".JPG" or ".PNG" (case insensitive)
Throws:
java.io.IOException - If something goes wrong with the loading.

setImage

public void setImage(byte[] imagedatax,
                     boolean lossyx)
Sets the image to work on.

Parameters:
imagedatax - The image data as byte stream (e.g. the contents of a JPG file on disk)
lossyx - Flag whether the image data should be handled as lossy (true) or lossless (false)

getImage

public byte[] getImage()
Returns the edited image as lossy or lossless byte stream. If no editing has taken place, the original byte stream is returned. Otherwise, a reencoded stream is returned containing a new image composed accordingly to lossy settings and the JPG compression level. The returned data can be handled as any byte stream representing an image, e.g. written verbatim to a disk file.

Note that this is not the method a GUI should use for representing the model on screen. For this purpose, the two getLossy/LossLessBufferedImage()-methods below should be used.

Returns:
Byte stream representing an image in JPG or PNG encoding

storeImage

public void storeImage(java.lang.String filename)
                throws java.io.IOException
Stores an image directly from the model into a file. This method can write JPG or PNG files directly from model. The filename must end with ".JPG" or ".PNG" (case insensitive) so the the writer can identify the file type.

Parameters:
filename - Full name of the file with the image. Must end with ".JPG" or ".PNG" (case insensitive)
Throws:
java.io.IOException - If something goes wrong with the storing.

clear

public void clear()

setLossy

public void setLossy(boolean lx)
Sets whether the output should be lossy or not


isLossy

public boolean isLossy()
Returns whether the result byte stream is lossy or not. This can change during editing.


isEdited

public boolean isEdited()
Returns whether the current editor content is edited in any way. This returns false directly after setting the content and after "Undo" has been pressed.

Returns:
Flag whether something has changed

setLossyQuality

public void setLossyQuality(float lq)
Sets the lossy quality of the output. Parameter is a float between 0 and 1. The higher the value, the better the quality.

Parameters:
lq - Quality between 0 and 1.

getLossyQuality

public float getLossyQuality()
Get the lossy quality setting.


getLossLessBufferedImage

public java.awt.image.BufferedImage getLossLessBufferedImage()
Returns the internally stored lossless image object. This operation is always inexpensive as it simply returns the Java image object on which the operations are happening.

Returns:
BufferedImage containing the image.

getLossyBufferedImage

public java.awt.image.BufferedImage getLossyBufferedImage()
Returns the internally stored lossy image object. This is a potentially expensive operation as the result byte stream has not only to be created but also reinterpreted to obtain the true quality of the output. If the image has been created once, it is of course cached for any further queries.

Returns:
BufferedImage containing the image in true JPEG quality

getImageWidth

public int getImageWidth()
Return width of the image. Works without creating result bytes as it takes the lossless image directly.


getImageHeight

public int getImageHeight()
Return height of the image. Works without creating result bytes as it takes the lossless image directly.


fireEdited

private void fireEdited(boolean newval)
Set internally whether something has been edited compared to the setImage() state. This method is only called internally and therefore private. Beside saving the new "edited" state, it fires a property change event if necessary. Therefore, the "edited" flag should always be changed through this method.

Parameters:
newval - The new "edited" state.

restoreImage

private void restoreImage()
Restore the complete editor from the originally given image. Called internally either at the end of setImage() or to perform the undo operation.


createResultBytes

private void createResultBytes()
Creates the result byte stream and the lossy image from the lossless image. In lossless mode, a PNG byte stream is created from the lossless image and the lossy image is set equal to the lossless image. In lossy mode, a JPG byte stream is created from the lossless image so that the lossy image can be derived from that byte stream. Mode and lossy quality are taken from the global fields. In case of any problems, empty stream and image are created.


createLossyImage

private void createLossyImage()
Create the lossy image by reading the lossy byte stream. The lossy image object is not needed for any internal operation. It is only there for any external accessor like a GUI so that it can represent the result in its true quality, e.g. as quality-reduce JPEG file.

If the current output mode is "lossless", the method simply sets the lossy image object to be the same as the lossless one.


setLossyQualityBySize

public int setLossyQualityBySize(int size,
                                 float maxquality)
Set the lossy quality so that the resulting file is about the size given as parameter in bytes. The method adjusts the quality of the lossy JPG compression algorithm so that a file is produced where the byte stream is not larger than the given amount of bytes. The algorithm works with a binary search for the quality level. It aborts if either the file size with the tested quality is between 90% of size and size, the maxquality level has been reached of the difference between the tested quality levels is smaller than or equal to 2%.

Note that setting the lossy quality this way will of course only work if after calling this method no further changes are made to the image (cropping, resizing etc.). Otherwise, the size constaint will almost certainly get lost. Therefore, this method should only be called after all other operations are done and the image will finally be saved somewhere.

Due to the iterative approach, the method might be slow. This is especially true for (pixelwise) larger images as for each loop, the JPEG file has to be encoded and this is a rather time-consuming operation.

Parameters:
size - Target file size. Derived range is between 90% and 100% of the given value.
maxquality - Maximum quality level of the target image.
Returns:
Size of the resulting image in bytes.

rescaleImage

public void rescaleImage(int newx,
                         int newy,
                         boolean keepratio)
Performs the rescale operation. Rescaling is always performed using the "smooth" algorithm. The result overwrites the current lossless image object.

Parameters:
newx - New width of the image
newy - New height of the image
keepratio - if true, ratio of original image is kept. Either x or y might be shorter then newx/newy parameter.

cropImage

public void cropImage(java.awt.Rectangle croprect)
Performs the crop operation The rectangle to be left over from the crop operation is taken from the image pane. Cropping is performed with the standard java.awt.Image capabilities. The result overwrites the current lossless image object.


softenImage

public void softenImage(float factor)
Performs a soften operation. The currently loaded image is softened, i.e. sharp edges are reduced. The given factor controls the strength of the operation, it should be considerably small for the image not to become totally blurred.

Parameters:
factor -

QJCC homepage