/* * Modification History * * 2000-December-21 Jason Rohrer * Created. * * 2001-January-6 Jason Rohrer * Fixed a bug in filter( ChannelFilter * ). * Set mSelection to NULL by default. * * 2001-January-10 Jason Rohrer * Made class serializable. * * 2001-January-15 Jason Rohrer * Made copy() not-virtual, so it can be overridden by subclasses * while allowing pointer type to determine which function * implementation is invoked. * * 2001-January-31 Jason Rohrer * Fixed a bug in copy(). * * 2001-February-3 Jason Rohrer * Updated serialization code to use new interfaces. * * 2001-February-4 Jason Rohrer * Rewrote the serialization code to send the image across as a byte * array with one byte per channel. This will reduce the transfer size by * a factor of 8. Keeping images in double format is convennient for image * creation, but the added quality never affects the end user anyway, so * there's no point in sending the extra data to a stream. * Removed an unused array allocation. * * 2005-February-21 Jason Rohrer * Made destructor virtual to avoid compiler warnings. * * 2006-August-25 Jason Rohrer * Made zero init of pixels optional (for speed). * * 2008-September-25 Jason Rohrer * Added a sub-image function and setting/getting color functions. */ #ifndef IMAGE_INCLUDED #define IMAGE_INCLUDED #include #include #include "ChannelFilter.h" #include "Color.h" #include "minorGems/io/Serializable.h" /** * A multi-channel, double-valued image. * * Is Serializable. Note that a serialized image doesn't have a selection. * * @author Jason Rohrer */ class Image : public Serializable { public: /** * Constructs an image. * * @param inWidth width of image in pixels. * @param inHeight height of image in pixels. * @param inNumChannels number of channels in image. * @param inStartPixelsAtZero true to initialize all pixels * to zero, or false to leave default memory values (garbage) * in place (pixels must be initialized by caller in this case). * Defaults to true. */ Image( int inWidth, int inHeight, int inNumChannels, char inStartPixelsAtZero = true ); virtual ~Image(); // gets the dimensions of this image. virtual long getWidth(); virtual long getHeight(); virtual long getNumChannels(); /** * Gets the values of a particular channel. * * Values are not copied. * * @param inChannel the channel to get. * * @return the values of the specified channel in row-major order. */ virtual double *getChannel( int inChannel ); /** * Gets the 3- or 4-channel color value at a given location in the * image. * * @param inIndex the image index. * * @return a color object. */ virtual Color getColor( int inIndex ); /** * Sets the 3- or 4-channel color value at a given location in the * image. * * @param inIndex the image index. * @param inColor the new color to set. */ virtual void setColor( int inIndex, Color inColor ); /** * Selects a region of the image. Default is a clear selection, * which means all regions of image are affected by an applied * filter. * * @param inSelection the image to use as the selection mask. * Values of 0 indicate pixels that are not selection, and 1 * indicate pixels that are selected, with selection amount * varying linearly between 0 and 1. * If inSelection is a single channel, then that channel is * used as a selection mask for all channels in this image. * If inSelection contains the same number of channels as this * image, then the corresponding channels of inSelection are * are used to mask each channel of this image. * If inSelection contains a number of channels different * from the number in this image, the first channel of inSelection * is used to mask each channel in this image. * * Note that inSelection is not copied or destroyed by this class. * Thus, modifying inSelection after calling setSelection will * modify the selection in this image. */ virtual void setSelection( Image *inSelection ); /** * Gets the selection for this image. * * @return the selection for this image. Returns NULL * if there is no selection. Must not be destroyed * by caller before calling clearSelection. */ virtual Image *getSelection(); /** * Clears the selection. Effectively selects the entire image. */ virtual void clearSelection(); /** * Applies a filter to the selected region of * a specified channel of this image. * * @param inFilter the filter to apply. * @param inChannel the channel to filter. */ virtual void filter( ChannelFilter *inFilter, int inChannel ); /** * Applies a filter to the selected region of * all channels of this image. * * @param inFilter the filter to apply. */ virtual void filter( ChannelFilter *inFilter ); /** * Copies the selected region of this image. Not virtual, * so can be overridden by subclasses while allowing pointer * type to determine which function implementation is invoked. * * @return a new image with the same number of channels * as this image, each containing the selected region * from each corresponding channel of this image. Unselected * regions are set to black. Returned image has no selection. */ Image *copy(); /** * Pastes the selected region from another image into * the selected region of this image. * * @param inImage the image to paste. Let c be the number * of channels in this image, and cp be the number * of channels in the image being pasted. * If ccp, then only the first cp channels * of this image are pasted into. */ virtual void paste( Image *inImage ); /** * Copies the data from the selected region of a channel. * * @param inChannel the channel to copy. * * @return a copy of the channel data. Must be destroyed * by the caller. */ virtual double *copyChannel( int inChannel ); /** * Pastes channel data into the selected region of a specified channel. * * @param inChannelData an array containing the channel * data to be pasted. Must be destroyed by caller. * @param inChannel the channel to paste into. */ virtual void pasteChannel( double *inChannelData, int inChannel ); /** * Gets the mask for a specified channel. * * @param inChannel the channel to get a mask for. * * @return the mask data for the specified channel. * If selection has the same number of channels as this image * then a different mask is returned for each channel. Otherwise, * the first channel from the selection is returned as the * mask for every channel. Returns NULL if there is no selection. */ virtual double *getChannelSelection( int inChannel ); /** * Extracts a smaller sub-image from this image. * * Ignores current selection. * * @param inStartX, inStartY, inWidth, inHeight * coordinates for the top left corner pixel of the sub-image * and the width and height of the sub-image. * * @return the sub-image as a new image. Must be destoryed by caller. */ Image *getSubImage( int inStartX, int inStartY, int inWidth, int inHeight ); // implement the Serializable interface virtual int serialize( OutputStream *inOutputStream ); virtual int deserialize( InputStream *inInputStream ); protected: long mWide, mHigh, mNumPixels, mNumChannels; double **mChannels; // NULL if nothing selected. Image *mSelection; /** * Pastes masked channel data into the selected region of a * specified channel. * * @param inChannelData an array containing the channel * data to be pasted. Must be destroyed by caller. * @param inMask the selection mask to use for passed-in channel. * Set to NULL for no mask. * @param inChannel the channel to paste into. */ virtual void pasteChannel( double *inChannelData, double *inMask, int inChannel ); }; inline Image::Image( int inWidth, int inHeight, int inNumChannels, char inStartPixelsAtZero ) : mWide( inWidth ), mHigh( inHeight ), mNumPixels( inWidth * inHeight ), mNumChannels( inNumChannels ), mChannels( new double*[inNumChannels] ), mSelection( NULL ) { // initialize all channels for( int i=0; iapply( mChannels[ inChannel ], mWide, mHigh ); } else { // part of image selected // turn selection off and filter channel entirely Image *tempSelection = mSelection; mSelection = NULL; // filter a copy of the channel double *filteredChannel = copyChannel( inChannel ); inFilter->apply( filteredChannel, mWide, mHigh ); // now paste filtered channel back into selected region mSelection = tempSelection; pasteChannel( filteredChannel, inChannel ); } } inline void Image::filter( ChannelFilter *inFilter ) { for( int i=0; ipaste( this ); return copiedImage; } inline void Image::paste( Image *inImage ) { // copy paste in the min number of channels only int numChannelsToPaste = mNumChannels; if( numChannelsToPaste > inImage->getNumChannels() ) { numChannelsToPaste = inImage->getNumChannels(); } for( int i=0; igetChannel(i), inImage->getChannelSelection(i), i ); } } inline double *Image::copyChannel( int inChannel ) { // first, copy the channel double *copiedChannel = new double[mNumPixels]; memcpy( copiedChannel, mChannels[inChannel], sizeof( double ) * mNumPixels ); if( mSelection != NULL ) { // apply selection to copied channel double *selection = getChannelSelection( inChannel ); // scale copied channel with selection for( int i=0; igetNumChannels() == mNumChannels ) { // use separate selection for each channel return mSelection->getChannel( inChannel ); } else { // use first channel of selection for all channels return mSelection->getChannel( 0 ); } } } inline void Image::pasteChannel( double *inChannelData, int inChannel ) { pasteChannel( inChannelData, NULL, inChannel ); } // We've abstracted away the complexity in the other fuctions, // but it all seemed to filter down into this function, which // is very messy. inline void Image::pasteChannel( double *inChannelData, double *inMask, int inChannel ) { double *thisChannel = mChannels[inChannel]; if( mSelection != NULL ) { // scale incoming data with this selection double *selection = getChannelSelection(inChannel); if( inMask != NULL ) { // scale incoming data with both masks for( int i=0; igetChannel( c ); double *sourceChannel = mChannels[c]; int destY=0; for( int y=inStartY; ywriteLong( mWide ); numBytes += inOutputStream->writeLong( mHigh ); // then output number of channels numBytes += inOutputStream->writeLong( mNumChannels ); // now output each channel for( int i=0; iwriteDouble( mChannels[i][p] ); byteArray[p] = (unsigned char)( mChannels[i][p] * 255 ); } numBytes += inOutputStream->write( byteArray, mNumPixels ); delete [] byteArray; } return numBytes; } inline int Image::deserialize( InputStream *inInputStream ) { int i; // first delete old image channels for( i=0; ireadLong( &mWide ); numBytes += inInputStream->readLong( &mHigh ); mNumPixels = mWide * mHigh; // then input number of channels numBytes += inInputStream->readLong( &mNumChannels ); mChannels = new double*[mNumChannels]; // now input each channel for( i=0; iread( byteArray, mNumPixels ); // convert each byte to an 8-bit double pixel for( int p=0; preadDouble( &( mChannels[i][p] ) ); mChannels[i][p] = (double)( byteArray[p] ) / 255.0; } delete [] byteArray; } return numBytes; } #endif