首 页 | 新 闻 | Symbian | Windows Mobile| J2ME | 下载中心 | 游戏策划 | 购书指南 | 移动开发视频教程
您现在的位置: 开发视界 >> Symbian英文资料 >> Graphics >> 文章正文
Image loading and color reduction
作者:佚名    文章来源:不详    更新时间:2006-5-7 11:13:47

Here is a small example of how to perform the following steps in a common graphics using program requiring efficient access to image data:

  load images from a file (jpg/png/gif for instance) with the Series 60 image conversion framework
  synchronize the process from outside so that you get a blocking method for image loading
  perform simple color reduction to convert the image into an indexed bitmap with 256-color palette of 12bit colors

Note: this example uses nested scheduling (nested CActiveScheduler::Start(), CActiveScheduler::Stop()). This approach might be hazardous if misused, but if used carefully it is a simple approach and works very well.

Below are the source files for the two classes CImageLoader and CTexture; CTexture is basically just an image with data, palette and height/width, but since I use this code in my 3D texture mapper directly, it is called texture. The source is commented and should be pretty simple to understand. Also it is not intended for straight copy-pasteing, rather than to learn from. AND it is copyrighted.

Happy hacking!


texture.h:


#ifndef __TEXTURE_H
#define __TEXTURE_H

#include <e32base.h>

/**
* Represents a texture used by the texture mapping routines.
*/
class CTexture : public CBase
{
   public:
       /*!
         @function NewL
 
         @discussion Constructs a CTexture object
         @param aWidth texture width (must be power of 2 from 3 to 10, thus giving
                width range of 8 to 1024)
                  @param aHeight texture height
                  @param aWidthShift equal to log2(aWidth)
                  @param aPalette palette information. this is copied into a local buffer
                  @param aData indexed texture data with indices pointing to the palette.
                                 This is not copied to a local buffer, so the original buffer must
                                 not be released before destroying this object.
         */
                static CTexture * NewL(TInt aWidth, TInt aHeight, TInt aWidthShift,
                                                           TUint16 *aPalette, TUint8 *aData);
       ~CTexture();

       TInt GetWidth() { return iWidth; }
       TInt GetHeight() { return iHeight; }
       TInt GetWidthShift() { return iWidthShift; }
       TUint16 * GetPalette() { return iPalette; }
       TUint8 * GetData() { return iData; }

   private:
       CTexture();
                void ConstructL(TInt aWidth, TInt aHeight, TInt aWidthShift,
                                                TUint16 *aPalette,
                                                TUint8 *aData);
       TInt iWidth;
       TInt iHeight;
       TInt iWidthShift;
       TUint16 *iPalette;
       TUint8 *iData;
};


#endif

texture.cpp:


#include "texture.h"

CTexture * CTexture::NewL(TInt aWidth, TInt aHeight, TInt aWidthShift,
                                                  TUint16 *aPalette, TUint8 *aData)
{
        CTexture *texture = new (ELeave) CTexture();
        CleanupStack::PushL(texture);
        texture->ConstructL(aWidth, aHeight, aWidthShift,
                                            aPalette, aData);
        CleanupStack::Pop(); // texture

        return texture;
}

void CTexture::ConstructL(TInt aWidth, TInt aHeight, TInt aWidthShift,
                                                  TUint16 *aPalette, TUint8 *aData)
{
   iWidth = aWidth;
   iHeight = aHeight;
   iWidthShift = aWidthShift;
   iData = aData;

        // construct & copy palette to a local pointer
        iPalette = (TUint16 *)User::AllocL(256 * sizeof(TUint16));
        Mem::Copy(iPalette, aPalette, 256 * sizeof(TUint16));
}

CTexture::CTexture()
{
}

CTexture::~CTexture()
{
   User::Free(iData);
   User::Free(iPalette);
}

imageloader.h:


#ifndef __IMAGELOADER_H
#define __IMAGELOADER_H

#include <e32base.h>
#include <MdaImageConverter.h>
#include <fbs.h>

#include "texture.h"

/**
* Color frequency array item.
*/
struct FreqItem {
        TUint16 freq;
        TUint16 color;
};

/**
* Utility methods for loading images.
*/
class CImageLoader : public CActive, public MMdaImageUtilObserver
{
   public:
       /**
        * Error codes for texture loading
        */
       enum TTextureLoadingError { KBadImageWidth = 1666001 };

       /*!
         @function LoadTextureL
 
         @discussion Loads a image file and converts it into a CTexture object
         @param aFilename filename of the image file
         */
       static CTexture * LoadTextureL(const TDesC &aFilename);

       // from MMdaImageUtilObserver
       virtual void MiuoOpenComplete(TInt aError);
            virtual void MiuoConvertComplete(TInt aError);
            virtual void MiuoCreateComplete(TInt aError);

                // from CActive
       void RunL();
       void DoCancel();
   private:
       CImageLoader(const TDesC *aFilename);
       ~CImageLoader();
       void ReadImageL();
       void CreateTexture();
       void SortFreqTable(TInt aLeft, TInt aRight);
                TUint8 FindNearestColor(TUint16 aColor, TInt aPaletteSize);

       TDesC *iFilename;
       CMdaImageFileToBitmapUtility *iConverter;
       RTimer *iTimer;
       CTexture *iTexture;
       CFbsBitmap *iBitmap;
       TInt iErrorCode;
                FreqItem *iFreqTable;
};

#endif

imageloader.cpp:


#include <e32math.h>

#include "imageloader.h"

#define IMAGEREAD_TIMEOUT 5 * 1000 * 1000

//////////////////////////////////////
// CImageLoader
//////////////////////////////////////

void CImageLoader::DoCancel()
{
   // cancel the timer    
   iTimer->Cancel();
   iTimer->Close();

   // end blocking in ReadTextureL()
   CActiveScheduler::Stop();
}

void CImageLoader::RunL()
{
   // timer timeout - loading failed
   Cancel();
   iTimer->Close();
}

CTexture * CImageLoader::LoadTextureL(const TDesC &aFilename)
{
   // construct new loader instance
   CImageLoader *loader = new (ELeave) CImageLoader(&aFilename);
   CleanupStack::PushL(loader);

   // add the loader to active scheduler
   CActiveScheduler::Add(loader);    

   // perform operations for reading and converting the image
   loader->ReadImageL();

   // start a nested scheduling; blocks until CActiveScheduler::Stop()
   // is called in DoCancel()
   CActiveScheduler::Start();

   // if error, leave with correct error code
   if( loader->iTexture == NULL ) {
       // instance will be destroyed by the cleanupstack
       User::Leave(loader->iErrorCode);
   }

   // get a local copy of the instance's created texture
   // if error(s) in process, this will be NULL
   CTexture *texture = loader->iTexture;

   // deallocate the instance
   CleanupStack::PopAndDestroy();

   // return the created texture
   return texture;
}

CImageLoader::CImageLoader(const TDesC *aFilename)
   : CActive(CActive::EPriorityStandard)
{
   // make a local copy of the filename
   iFilename = aFilename->Alloc();
}

CImageLoader::~CImageLoader()
{
   RDebug::Print(_L("CImageLoader::~CImageLoader()"));

   // deallocate all data
   delete iFilename;
   delete iTimer;
   delete iConverter;
   delete iBitmap;
}

// performs the actual reading and conversion of the image
void CImageLoader::ReadImageL()
{
   // reset the texture
   iTexture = NULL;

   // create + initialize operation timeout timer
   iTimer = new RTimer();
   iTimer->CreateLocal();

   // set timeout for the image read + conversion process
   iTimer->After(iStatus, IMAGEREAD_TIMEOUT);
   SetActive();

   // start loading the image
        iConverter = CMdaImageFileToBitmapUtility::NewL(*this);
        iConverter->OpenL(*iFilename);
}

// called when OpenL() finishes
void CImageLoader::MiuoOpenComplete(TInt aError)
{
   if( aError != KErrNone ) {
       iErrorCode = aError;
       Cancel();
       return;
   }

        TFrameInfo info;
        iConverter->FrameInfo(0, info);
       
        // create a bitmap to write into
        iBitmap = new (ELeave) CFbsBitmap();
        TInt rc = iBitmap->Create(info.iOverallSizeInPixels, EColor4K);
        if( rc != KErrNone )
        {
       iErrorCode = rc;
       Cancel();
       return;
   }

        // convert the gif into a bitmap
        TRAPD(error, iConverter->ConvertL(*iBitmap));
       
        // handle the error
   if( error != KErrNone) {
       iErrorCode = error;
       Cancel();
       return;
        }
}

// called when ConvertL() finishes
void CImageLoader::MiuoConvertComplete(TInt aError)
{
   if( aError != KErrNone ) {
       iErrorCode = aError;
       Cancel();
       return;
   }

   // create the iTexture out of the bitmap data
   CreateTexture();

   // cancel the timeout timer to end blocking in LoadTextureL()
   Cancel();
}

// quicksorts the color frequency table
void CImageLoader::SortFreqTable(TInt aLeft, TInt aRight)
{
        TInt qleft = aLeft;
        TInt qright = aRight;
        TInt qpivot        = iFreqTable[(qleft + qright) >> 1].freq;

        do {
                while( (iFreqTable[qleft].freq > qpivot) && (qleft < aRight) ) {
                        qleft++;
                }

                while( (qpivot > iFreqTable[qright].freq) && (qright > aLeft) ) {
                        qright--;
                }

                if( qleft <= qright ) {
                        // swap elements
                        TUint16 tmp = iFreqTable[qleft].freq;
                        iFreqTable[qleft].freq = iFreqTable[qright].freq;
                        iFreqTable[qright].freq = tmp;
                        tmp = iFreqTable[qleft].color;
                        iFreqTable[qleft].color = iFreqTable[qright].color;
                        iFreqTable[qright].color = tmp;

                        qleft++;
                        qright--;
                }
        } while( qleft <= qright );
       
        // recursively sort left side
        if( aLeft < qright ) SortFreqTable(aLeft, qright);

        // recursively sort right side
        if( qleft < aRight ) SortFreqTable(qleft, aRight);
}

// find color from the first 256 entries with smallest cubic difference
// component wise for the given 12bit color
inline TUint8 CImageLoader::FindNearestColor(TUint16 aColor, TInt aPaletteSize)
{
        TInt index = -1;
        TUint difference = 0xffffffff;

   // extract original color components
        TUint red0 = (aColor >> 8) & 0xf;
        TUint green0 = (aColor >> 4) & 0xf;
        TUint blue0 = aColor & 0xf;

        for( TInt i = 0; i < aPaletteSize; i++ ) {
                TUint16 color = iFreqTable[i].color;

       // extract components of palette color
                TUint red = (color >> 8) & 0xf;
                TUint green = (color >> 4) & 0xf;
                TUint blue = color & 0xf;

       // calculate cubic difference
                TUint diff = ((red0 - red) * (red0 - red)) +
                                         ((green0 - green) * (green0 - green)) +
                                         ((blue0 - blue) * (blue0 - blue));

                // try early-out if exact match
                if( diff == 0 ) {
                        return (TUint8)i;
                }

                if( diff < difference ) {
                        difference = diff;
                        index = i;
                }
        }

        return (TUint8)index;
}

void CImageLoader::CreateTexture()
{
   // verify bitmap size; its width must be a power of 2 (8 to 1024)
   TSize imagesize = iBitmap->SizeInPixels();
   TInt widthshift = 0;

   // find correct shift value by calculating log2(widht). since there is no
   // log2() we'll use log2(x) = log10(x) / log10(2)
   TReal res1, res2, val = (TReal)imagesize.iWidth, two = 2.0;
   Math::Log(res1, val);
   Math::Log(res2, two);

   // must be an integer; check that result has fractional part of 0
   res1 = res1 / res2;
   Math::Frac(res2, res1);
   if( (res2 == 0.0) && (res1 > 3.0) ) {
       widthshift = (TInt)res1;
   } else {
       iErrorCode = KBadImageWidth;
       return;
   }

   // allocate memory for the indexed texture data    
   TInt texture_data_size = imagesize.iWidth * imagesize.iHeight;
   TUint8 *data = (TUint8 *)User::Alloc(texture_data_size);
   if( data == NULL ) {
       iErrorCode = KErrNoMemory;
       return;
   }

        // allocate color frequency array
        iFreqTable = (FreqItem *)User::Alloc(4096 * sizeof(FreqItem));
        if( iFreqTable == NULL ) {
       iErrorCode = KErrNoMemory;
                User::Free(data);
       return;
   }
        Mem::FillZ(iFreqTable, 4096 * sizeof(FreqItem));

   // extract bitmap header
   SEpocBitmapHeader hdr = iBitmap->Header();

   // calculate start address of the actual bitmap data
   TInt data_start = (TInt)iBitmap->DataAddress();
   TInt datasize = hdr.iBitmapSize - hdr.iStructSize;

        // calculate color frequencies
   TUint16 *p = (TUint16 *)data_start;
        datasize >>= 1;
   for( TInt i = 0; i < datasize; i++ ) {
       TUint16 color = *p++;
                iFreqTable[color].color = color;
                if( iFreqTable[color].freq < 0xffff ) {
                        iFreqTable[color].freq++;
                }
        }

        //calculate num. distinct colors
   TInt num_colors = 0;
   for( i = 0; i < 4096; i++ ) {
                if( iFreqTable[i].freq > 0 ) {
                        num_colors++;
                }
        }

        // sort the color frequency array
        SortFreqTable(0, 4095);       

        // find nearest match for each color in the bitmap from the first
        // 256 entries of the palette
   p = (TUint16 *)data_start;
        for( i = 0; i < datasize; i++ ) {
                TUint16 color = *p++;
                data[i] = FindNearestColor(color, 256);
        }

        // copy the palette data
        TUint16 palette[256];
        for( i = 0; i < 256; i++ ) {
                palette[i] = iFreqTable[i].color;
        }

        // deallocate frequency table
        User::Free(iFreqTable);
        iFreqTable = NULL;

   // create the texture object
   TRAPD(error, (iTexture = CTexture::NewL(imagesize.iWidth, imagesize.iHeight,
                                           widthshift, palette, data)));
   if( error != KErrNone ) {
       User::Free(data);
       iErrorCode = error;
       return;
   }
}

// not used
void CImageLoader::MiuoCreateComplete(TInt /*aError*/)
{
}
相关文章:
Hashtable implementation for Symbian
Using Extended Notifiers
Using inline assembler within Symbian C++
Multichannel Sound using CMdaAudioOutputStream (v1.1)
Playing a WAV file
T, C, R, M classes
Creation of a high score table (Part II): saving to a file store
How to convert between TDes8 and TDes16