Game Accessibility Library logo SourceForge.net Logo
Game Accessibility Suite: CAT/CATImage.cpp Source File

CATImage.cpp

Go to the documentation of this file.
00001 /// \file    CATImage.cpp
00002 /// \brief   PNG image class
00003 /// \ingroup CAT
00004 ///
00005 /// Copyright (c) 2003-2008 by Michael Ellison.
00006 /// See COPYING.txt for the \ref gaslicense License (MIT License).
00007 ///
00008 // $Author: mikeellison $
00009 // $Date: 2008-01-21 08:33:12 -0600 (Mon, 21 Jan 2008) $
00010 // $Revision:   $
00011 // $NoKeywords: $
00012 
00013 #include <memory.h>
00014 #include "png.h"
00015 #include "CATImage.h"
00016 #include "CATStreamFile.h"
00017 
00018 const CATInt32 kBytesPerPixel = 4;
00019 
00020 //------------------------------------------------------------------------
00021 // CreateImage creates an image.
00022 //
00023 // Use this instead of new to create image objects.
00024 // If width and height are non-zero, creates the appropriate memory for
00025 // the image and adds a reference. Otherwise, does not create memory 
00026 // buffer or add reference.
00027 //
00028 // Call CATImage::ReleaseImage() on the returned image when done.
00029 //
00030 //    image - uninitialized image ptr. Set on return.
00031 //    width - desired width of image in pixels.
00032 //    height - desired height of image in pixels.
00033 //    init - if true, image data is set to 0.
00034 //    transparent - if false, sets all alpha channels 
00035 //                      to 255 (opaque). Otherwise, sets the
00036 //                      alpha channels to 0 (transparent).      
00037 //                      Ignored if init == false.
00038 //------------------------------------------------------------------------
00039 CATResult CATImage::CreateImage(   CATImage*&      image,
00040                                  CATInt32            width,
00041                                  CATInt32            height,
00042                                  bool           init,
00043                                  bool           transparent)
00044 {
00045    image = 0;
00046 
00047    try
00048    {
00049       image = new CATImage();
00050    }
00051    catch(...)
00052    {
00053       image = 0;
00054    }
00055 
00056    if (image == 0)
00057    {
00058       return CATRESULT(CAT_ERR_OUT_OF_MEMORY);
00059    }
00060 
00061    if ((width != 0) && (height != 0))
00062    {
00063       return image->Create(width, height, init, transparent);
00064    }
00065    
00066    return CAT_SUCCESS;
00067 }
00068 
00069 //---------------------------------------------------------------------------
00070 // ReleaseImage decrements the reference count of an image and
00071 // will free the image if it hits zero.
00072 // It may also free parent images if the specified
00073 // image holds the last reference to a parent.
00074 //
00075 // image - pointer to image to release. Is set to NULL
00076 //         if last reference was deleted.
00077 //---------------------------------------------------------------------------
00078 CATResult CATImage::ReleaseImage( CATImage*& image)
00079 {
00080    CATASSERT(image != 0, "Invalid image released!");
00081 
00082    if (image != 0)
00083    {     
00084       // DecRef also decrements parent(s)
00085       if (image->fRefCount <= 1)
00086       {
00087          // Delete if reference count hit zero.
00088          delete image;
00089 
00090          // Null caller's pointer since it's invalid now.
00091          image = 0;
00092       }
00093       else
00094       {
00095          // If we have other references, just decrement our count.
00096          image->DecRef();
00097       }
00098       
00099       return CAT_SUCCESS;
00100    }
00101    return CATRESULT(CAT_ERR_IMAGE_NULL);
00102    
00103 }
00104 
00105 
00106 
00107 //---------------------------------------------------------------------------
00108 // CreateSub creates a sub-image of the specified parent.
00109 //
00110 // dstImage should not be instantiated prior to calling CreateSub.
00111 // Sub image is returned in dstImage.
00112 //
00113 // The sub image references the parent's fData
00114 // member, and the parent image must not be deleted before you
00115 // are done using the sub image.
00116 //
00117 // You may create a sub image of a sub image, ad infinum. It'll
00118 // handle the offsets.  Call CATImage::ReleaseImage() when done.
00119 //
00120 //        orgImg - parent image to derive subimage from.
00121 //        dstImg - uninitialized image ptr. Set on return.
00122 //        xOffset - relative x offset for top-left of sub image.
00123 //        yOffset - relative y offset for top-left of sub image.
00124 //        width - width of sub image.
00125 //        height - height of sub image.
00126 //---------------------------------------------------------------------------
00127 CATResult CATImage::CreateSub      (  const CATImage*    orgImg,
00128                                  CATImage*&         dstImg,
00129                                  CATInt32               xOffset,
00130                                  CATInt32               yOffset,
00131                                  CATInt32               width,
00132                                  CATInt32               height)
00133 {
00134    dstImg = 0;
00135    
00136    CATASSERT(xOffset >= 0, "XOffset must be >= 0");
00137    CATASSERT(yOffset >= 0, "YOffset must be >= 0");
00138    
00139    CATASSERT(xOffset + width  <= orgImg->fWidth,  
00140             "Invalid sub image width");
00141    
00142    CATASSERT(yOffset + height <= orgImg->fHeight, 
00143             "Invalid sub image height");
00144 
00145    CATASSERT(orgImg->fData != 0, "Parent image is invalid.");
00146 
00147    // Bail on invalid parameters (same checks as asserts above)
00148    if ((xOffset < 0) || (yOffset < 0)        ||
00149        (xOffset + width  > orgImg->fWidth)      ||
00150        (yOffset + height > orgImg->fHeight)     ||
00151        (orgImg->fData == 0))
00152    {
00153       return CATRESULT(CAT_ERR_IMAGE_INVALID_SUB_POSITION);
00154    }
00155 
00156    // Create an uninitialized image object
00157    CATResult result;
00158    if (CATFAILED(result = 
00159          CreateImage(dstImg, 0, 0, false)))
00160    {
00161       return result;
00162    }
00163 
00164    
00165    // Point it at parent and store options
00166    dstImg->fOwnData     = false;
00167    dstImg->fParentImage = (CATImage*)orgImg;
00168    dstImg->fData        = orgImg->fData;
00169 
00170    // Remember these are just width and height of the sub image. To retrieve
00171    // the width and height of the fData data buffer, use AbsWidth() and
00172    // AbsHeight().
00173    dstImg->fWidth       = width;
00174    dstImg->fHeight      = height;
00175 
00176    // Remember on the x and y offsets that these are
00177    // relative to the parent only - to get the absolute x and y offsets
00178    // within the fData data buffer, use XOffsetAbs() and YOffsetAbs().
00179    dstImg->fXOffset     = xOffset;
00180    dstImg->fYOffset     = yOffset;
00181    
00182    // Add reference to ourself and our parents.  CreateImage() did not do this
00183    // previously, since width and height were set to 0.
00184    dstImg->AddRef();
00185 
00186    return CAT_SUCCESS;
00187 }
00188 
00189 //---------------------------------------------------------------------------
00190 // CopyImage() creates a new image of the same type as srcImg
00191 // and stores it in dstImg.  The data from srcImg is
00192 // copied into a buffer owned by dstImg. Caller must call
00193 // CATImage::ReleaseImage() on the returned image when done.
00194 //
00195 //       srcImg - source image to copy from
00196 //       dstImg - uninitizlied image ptr. Contains copy
00197 //                 of srcImg on return.
00198 //---------------------------------------------------------------------------
00199 CATResult CATImage::CopyImage      (  const CATImage*    srcImg,
00200                                  CATImage*&         dstImg)
00201 {
00202    dstImg = 0;
00203    CATASSERT(srcImg != 0, "Can't copy a null image.");
00204    if (srcImg == 0)
00205    {
00206       return CATRESULT(CAT_ERR_IMAGE_NULL);
00207    }
00208 
00209    // Simply copy - make full copy of source 
00210    return CopyImage( srcImg, 
00211                      dstImg, 
00212                      srcImg->XOffsetRel(), 
00213                      srcImg->YOffsetRel(),
00214                      srcImg->Width(),
00215                      srcImg->Height());
00216 }
00217 //---------------------------------------------------------------------------
00218 // CopyImage() creates a new image of the same type as srcImg
00219 // and stores it in dstImg.  The data from srcImg is
00220 // copied into a buffer owned by dstImg. Caller must call
00221 // CATImage::ReleaseImage() on the returned image when done.
00222 //
00223 // This version allows you to specify offsets, width, and
00224 // height of the data to copy into a new image.
00225 // 
00226 //        srcImg - source image to copy from
00227 //        dstImg - uninitialized image ptr. Contains copy
00228 //                 of srcImg on return.
00229 //        xOffset - relative x offset for top-left of copy.
00230 //        yOffset - relative y offset for top-left of copy.
00231 //        width - width of destination image.
00232 //        height - height of destination image.
00233 //---------------------------------------------------------------------------
00234 CATResult CATImage::CopyImage      (  const CATImage*    srcImg,
00235                                  CATImage*&         dstImg,
00236                                  CATInt32               xOffset,
00237                                  CATInt32               yOffset,
00238                                  CATInt32               width,
00239                                  CATInt32               height)
00240 {
00241    dstImg = 0;
00242    CATASSERT(srcImg != 0, "Can't copy a null image.");
00243    if (srcImg == 0)
00244    {
00245       return CATRESULT(CAT_ERR_IMAGE_NULL);
00246    }
00247    
00248    CATASSERT(xOffset >= 0, "XOffset must be >= 0");
00249    CATASSERT(yOffset >= 0, "YOffset must be >= 0");
00250    CATASSERT(xOffset + width  <= srcImg->fWidth,  "Invalid offset/width");
00251    CATASSERT(yOffset + height <= srcImg->fHeight, "Invalid offset/height");
00252    CATASSERT(srcImg->fData != 0, "Source image is invalid.");
00253 
00254    // Bail on invalid parameters (same checks as asserts above)
00255    if ((xOffset < 0) || (yOffset < 0)        ||
00256        (xOffset + width  > srcImg->fWidth)      ||
00257        (yOffset + height > srcImg->fHeight)     ||
00258        (srcImg->fData == 0))
00259    {
00260       return CATRESULT(CAT_ERR_IMAGE_INVALID_SUB_POSITION);
00261    }
00262 
00263    // Create an image object and allocate space for memory.
00264    CATResult result;
00265    if (CATFAILED(result = CreateImage(  dstImg, 
00266                                        width, 
00267                                        height, 
00268                                        false)))
00269    {
00270       return result;
00271    }
00272 
00273    // Copy image buffer...
00274    CATInt32 y;
00275    
00276    // Offset into line for source buffer in bytes
00277    CATInt32 srcLineOffset = 
00278          (srcImg->XOffsetAbs() + xOffset) * kBytesPerPixel;
00279    
00280    // current position of start of buffer in source
00281    unsigned char* srcPtr = 
00282             srcImg->fData + srcLineOffset + 
00283             ((srcImg->YOffsetAbs() + yOffset) * srcImg->AbsWidth() * kBytesPerPixel);
00284 
00285    unsigned char* dstPtr = dstImg->fData;
00286 
00287    for (y = 0; y < height; y++)
00288    {
00289       // Copy one row at a time into our new image
00290       memcpy(  dstPtr,
00291                srcPtr,
00292                width * kBytesPerPixel );
00293 
00294       // step to next line in source
00295       srcPtr += srcImg->AbsWidth() * kBytesPerPixel;
00296       
00297       // and in destination....
00298       dstPtr += width * kBytesPerPixel;
00299    }
00300 
00301    return CAT_SUCCESS;
00302 }
00303 
00304 //---------------------------------------------------------------------------
00305 // Increment reference count
00306 //---------------------------------------------------------------------------
00307 unsigned long  CATImage::AddRef()
00308 {
00309    // Increment parent reference count
00310    if (fParentImage != 0)
00311    {
00312       fParentImage->AddRef();
00313    }
00314 
00315    fRefCount++;
00316    return fRefCount;
00317 }
00318 
00319 //---------------------------------------------------------------------------
00320 // Decrement reference count
00321 //---------------------------------------------------------------------------
00322 unsigned long  CATImage::DecRef()
00323 {
00324    if (fParentImage != 0)
00325    {
00326       // If parent is non-null, release our ref count for it.
00327       // This may delete it.
00328       ReleaseImage(fParentImage);
00329       
00330       // If parent was deleted, then our ref count should be 1    
00331       if (fParentImage == 0)
00332       {
00333          // Make sure that if we deleted parent, we're going to delete here
00334          CATASSERT(fRefCount == 1, "Deleted parent, but we're not done here!");
00335       }
00336    }
00337 
00338    CATASSERT(fRefCount != 0, "Decrementing reference count too far!");
00339    if (fRefCount == 0)
00340       return fRefCount;
00341 
00342    fRefCount--;
00343    return fRefCount;
00344 }
00345 
00346 //---------------------------------------------------------------------------
00347 // Protected constructor
00348 //---------------------------------------------------------------------------
00349 CATImage::CATImage()
00350 {
00351    fOwnData       = false;
00352    fData          = 0;
00353    fWidth         = 0;
00354    fHeight        = 0;   
00355    fXOffset       = 0;
00356    fYOffset       = 0;
00357    fParentImage   = 0;
00358    fRefCount      = 0;
00359 }
00360 
00361 //---------------------------------------------------------------------------
00362 // Protected destructor
00363 //---------------------------------------------------------------------------
00364 CATImage::~CATImage()
00365 {
00366    // Dec and check our reference count - make sure we're not in here when
00367    // we shouldn't be.
00368    if (fData)
00369       DecRef();
00370 
00371    CATASSERT(fRefCount == 0, "Destructor called while active!");
00372 
00373    if (fOwnData)
00374    {
00375       if (fData != 0)
00376       {
00377          delete [] fData;
00378          fData = 0;
00379       }
00380    }
00381 }
00382 
00383 
00384 //---------------------------------------------------------------------------
00385 // Create
00386 //    Creates the buffer for the image. If init is true,
00387 //    the data is initialized to 0. Otherwise, it's whatever
00388 //    happened to be there.
00389 //---------------------------------------------------------------------------
00390 CATResult CATImage::Create(CATInt32 width, CATInt32 height, bool init, bool transparent)
00391 {
00392    CATResult result = CAT_SUCCESS;
00393 
00394    if ( (width == 0) || (height == 0) )
00395    {
00396       return CATRESULT(CAT_ERR_IMAGE_INVALID_SIZE);
00397    }
00398 
00399     if (fData != 0)
00400     {
00401         delete [] fData;
00402     }
00403    
00404    fData = new unsigned char[width * height * kBytesPerPixel];
00405    if (fData == 0)
00406    {
00407       return CATRESULT(CAT_ERR_OUT_OF_MEMORY);
00408    }
00409    fOwnData = true;
00410 
00411    fWidth   = width;
00412    fHeight  = height;
00413 
00414    if (init)
00415       this->Clear(transparent);
00416 
00417    this->AddRef();
00418    
00419    return CAT_SUCCESS;
00420 }
00421 
00422 //---------------------------------------------------------------------------
00423 // Clear
00424 //    Clears the image data to 0
00425 //---------------------------------------------------------------------------
00426 CATResult CATImage::Clear(bool transparent)
00427 {
00428    CATASSERT(fData != 0, "Can't clear an image unless it's been created.");
00429    if (fData == 0)
00430    {
00431       return CATRESULT(CAT_ERR_IMAGE_MUST_INITIALIZE);
00432    }
00433 
00434    // Offset into line for source buffer in bytes
00435    CATInt32 dstOffX = this->XOffsetAbs() * kBytesPerPixel;   
00436    // Absolute length of line in fData in bytes ( >= fWidth * 3)
00437    CATInt32 dstLineLength = this->AbsWidth()   * kBytesPerPixel;   
00438    // Distance from end of one line to beginning of the next in bytes.
00439    CATInt32 dstStep   = dstLineLength - (fWidth * kBytesPerPixel);
00440 
00441    // current position of start of buffer in source
00442    unsigned char* dstPtr = fData + (YOffsetAbs() * dstLineLength);
00443  
00444    unsigned char alpha = (transparent?0:255);
00445 
00446    // Endian-neutral way to set it up
00447    *(CATUInt32 *)dstPtr = 0;
00448    dstPtr[3] = alpha;
00449 
00450    // Get the val we'll copy in for clear into a reg/var
00451    CATUInt32 val = *(CATUInt32*)dstPtr;
00452 
00453    for (CATInt32 y = 0; y < fHeight; y++)
00454    {      
00455       for (CATInt32 x = 0; x < fWidth; x++)
00456       {
00457          *(CATUInt32*)dstPtr = val;
00458          dstPtr += kBytesPerPixel;         
00459       }
00460 
00461       dstPtr += dstStep;   
00462    }
00463 
00464    return CAT_SUCCESS;
00465 }
00466 
00467 
00468 //---------------------------------------------------------------------------
00469 // FillRect() fills an area to a specific color.  If the alpha channel of
00470 // the color is != 255, then fills w/alpha (e.g. blends color w/bg)
00471 //
00472 // \param rect  - rectangle to fill
00473 // \param color - color to fill to
00474 // \return CATResult - CAT_SUCCESS on success.
00475 //---------------------------------------------------------------------------
00476 CATResult CATImage::FillRect(const CATRect& rect, const CATColor& color)
00477 {
00478    CATResult result = CAT_SUCCESS;
00479    CATRect   imageRect(0,0,fWidth,fHeight);
00480 
00481    CATASSERT(color.a != 0, "Uhm... filling with 0 alpha doesn't do much.");
00482 
00483    if (!imageRect.Inside(rect))
00484    {
00485       CATASSERT(false,"Rect must be contained by the image.");
00486       return CAT_ERR_IMAGE_FILL_OUT_OF_BOUNDS;
00487    }
00488 
00489    CATASSERT(fData != 0, "Image must be created first!");
00490    if (fData == 0)
00491    {
00492       return CATRESULT(CAT_ERR_IMAGE_MUST_INITIALIZE);
00493    }
00494 
00495    // Now fill the image in on all four channels.
00496    CATInt32 x,y;
00497 
00498    // Offset into line for source buffer in bytes
00499    CATInt32 dstOffX = (this->XOffsetAbs() + rect.left) * kBytesPerPixel;   
00500    
00501    // Absolute length of line in fData in bytes ( >= fWidth * 3)
00502    CATInt32 dstLineLength = this->AbsWidth() * kBytesPerPixel;   
00503    
00504    // Distance from end of one line to beginning of the next in bytes.
00505    CATInt32 dstStep   = dstLineLength - (rect.Width() * kBytesPerPixel);
00506    
00507    // current position of start of buffer in source
00508    unsigned char* dstPtr = fData + 
00509                            dstOffX + 
00510                            ((YOffsetAbs() + rect.top) * dstLineLength);
00511 
00512 
00513    CATASSERT(kBytesPerPixel == 4, "Woops.");
00514 
00515    // If we're not totally opaque, then alpha blend fill color
00516    if (color.a != 255)
00517    {
00518       // Loop through image performing fill
00519       for (y = 0; y < rect.Height(); y++)
00520       {      
00521          for (x = 0; x < rect.Width(); x++)
00522          {            
00523             // Blend the r,g,b channels. Alpha remains the same for destination image.         
00524             dstPtr[0] =  (CATUInt8)(((CATInt32)(color.r   * color.a) +
00525                                   (CATInt32)(dstPtr[0] * (255 - color.a))) / 255);
00526 
00527             dstPtr[1] =  (CATUInt8)(((CATInt32)(color.g   * color.a) +
00528                                   (CATInt32)(dstPtr[1] * (255 - color.a))) / 255);
00529 
00530             dstPtr[2] =  (CATUInt8)(((CATInt32)(color.b   * color.a) +
00531                                   (CATInt32)(dstPtr[2] * (255 - color.a))) / 255);
00532 
00533             dstPtr+=4;
00534          }         
00535          dstPtr += dstStep;
00536       }
00537 
00538    }
00539    else
00540    {
00541       // Totally opaque - just overwrite.
00542 
00543       // Loop through image performing merge
00544       for (y = 0; y < rect.Height(); y++)
00545       {      
00546          for (x = 0; x < rect.Width(); x++)
00547          {         
00548             dstPtr[0] = color.r;
00549             dstPtr[1] = color.g;
00550             dstPtr[2] = color.b;
00551             
00552             dstPtr += 4;
00553          }
00554 
00555          dstPtr += dstStep;
00556       }
00557    }
00558    return result;
00559 }
00560 
00561 
00562 //---------------------------------------------------------------------------
00563 // XOffsetRel
00564 //    Returns relative x offset within parent image.
00565 //---------------------------------------------------------------------------
00566 CATInt32 CATImage::XOffsetRel () const
00567 {
00568    return fXOffset;
00569 }
00570 
00571 //---------------------------------------------------------------------------
00572 // YOffsetRel
00573 //    Returns relative y offset within parent image.
00574 //---------------------------------------------------------------------------
00575 CATInt32 CATImage::YOffsetRel () const
00576 {
00577    return fYOffset;
00578 }
00579 
00580 //---------------------------------------------------------------------------
00581 // XOffsetAbs
00582 //    Returns absolute X offset within fData
00583 //---------------------------------------------------------------------------
00584 CATInt32 CATImage::XOffsetAbs () const
00585 {
00586    CATInt32 absOffset = fXOffset;
00587    
00588    // Recursively add all parent offsets into ours to find real offset
00589    // within fData.
00590    if (fParentImage != 0)
00591    {
00592       absOffset += fParentImage->XOffsetAbs();
00593    }
00594    
00595    return absOffset;
00596 }
00597 
00598 //---------------------------------------------------------------------------
00599 // YOffsetAbs
00600 //    Returns absolute Y offset within fData
00601 //---------------------------------------------------------------------------
00602 CATInt32 CATImage::YOffsetAbs () const
00603 {
00604    CATInt32 absOffset = fYOffset;
00605 
00606    // Recursively add all parent offsets into ours to find real offset
00607    // within fData.
00608    if (fParentImage != 0)
00609    {
00610       absOffset += fParentImage->YOffsetAbs();
00611    }
00612    
00613    return absOffset;
00614 }
00615 //---------------------------------------------------------------------------
00616 // AbsWidth
00617 //    Returns the absolute width of the fData image buffer
00618 //---------------------------------------------------------------------------
00619 CATInt32 CATImage::AbsWidth   () const
00620 {
00621    CATInt32 absWidth = fWidth;
00622    if (fParentImage != 0)
00623    {
00624       absWidth = fParentImage->AbsWidth();
00625    }
00626 
00627    return absWidth;
00628 }
00629 
00630 //---------------------------------------------------------------------------
00631 // AbsHeight
00632 //    Returns the absolute height of the fData image buffer
00633 //---------------------------------------------------------------------------
00634 CATInt32 CATImage::AbsHeight  () const
00635 {
00636    CATInt32 absHeight = fHeight;
00637    if (fParentImage != 0)
00638    {
00639       absHeight = fParentImage->AbsHeight();
00640    }
00641 
00642    return absHeight;
00643 }
00644 
00645 //---------------------------------------------------------------------------
00646 // Width()
00647 //    Retrieve the width of the image in pixels
00648 //---------------------------------------------------------------------------
00649 CATInt32 CATImage::Width() const
00650 {
00651    return fWidth;
00652 }
00653 
00654 //---------------------------------------------------------------------------
00655 // Height()
00656 //    Retrieve the height of the image in pixels
00657 //---------------------------------------------------------------------------
00658 CATInt32 CATImage::Height() const
00659 {
00660    return fHeight;
00661 }
00662 
00663 //---------------------------------------------------------------------------
00664 // Size()
00665 //    Retrieve the size of the image in bytes
00666 //---------------------------------------------------------------------------
00667 CATInt32 CATImage::Size() const
00668 {
00669    return fWidth * fHeight * kBytesPerPixel;
00670 }
00671 
00672 //---------------------------------------------------------------------------
00673 // AbsSize()
00674 //    Retrieve the absolute size of the root image in bytes
00675 //---------------------------------------------------------------------------
00676 CATInt32 CATImage::AbsSize() const
00677 {
00678    return AbsWidth() * AbsHeight() * kBytesPerPixel;
00679 }
00680 
00681 //---------------------------------------------------------------------------
00682 // IsImageRoot()
00683 //    Checks if an image is the root image. Returns true if so.
00684 //    Otherwise, it is a sub image of another image.
00685 //---------------------------------------------------------------------------
00686 bool CATImage::IsImageRoot() const
00687 {
00688    bool isRoot = fOwnData;
00689    
00690    CATASSERT(isRoot == (fParentImage == 0), 
00691       "Root image should not have a parent image, sub images must.");
00692 
00693    return isRoot;
00694 }
00695 
00696 //---------------------------------------------------------------------------
00697 // GetRawDataPtr()
00698 //    Retrieve the image data ptr
00699 //
00700 //    Remember that this may be a sub image, in which case the data ptr 
00701 //    returned is to the raw buffer of a parent image.
00702 //
00703 //    So, any time you access the raw data you should use the
00704 //    XOffsetAbs, YOffsetAbs, AbsWidth, and AbsHeight functions
00705 //    when calculating your pointers into the data.
00706 //
00707 //    Also remember that if you modify a sub image, you'll be modifying
00708 //    the parent image and any other overlapping sub images.  If you
00709 //    want to muck with an image without messing up any others, use
00710 //    CATImage::CopyImage() first to get a base image that owns its own
00711 //    data.
00712 //
00713 //    Note that the xOffset and yOffset are relative to parent. They will
00714 //    be calculated at runtime if you call this function.      
00715 //---------------------------------------------------------------------------
00716 unsigned char* CATImage::GetRawDataPtr() const
00717 {
00718    return fData;
00719 }
00720 
00721 
00722 //---------------------------------------------------------------------------
00723 // Because of the factory style setup, we aren't going to support
00724 // copy operators. So it's protected, and just asserts.
00725 // Use CreateCopy() or CreateSub() instead depending on whether you
00726 // want a deep or shallow copy of the object.
00727 //---------------------------------------------------------------------------
00728 CATImage& CATImage::operator=(const CATImage& img)
00729 {
00730    CATASSERT(false,"operator= not supported for CATImage.");
00731    return *this;
00732 }
00733 
00734 //---------------------------------------------------------------------------
00735 // SetSubPosition
00736 //    Move the ROI of the image within its parent.
00737 //    Will return CAT_ERR_IMAGE_OPERATION_INVALID_ON_ROOT if you try
00738 //    to use this on a root image instead of a sub image.
00739 //---------------------------------------------------------------------------
00740 CATResult CATImage::SetSubPosition (  CATInt32      newXOffset,
00741                                  CATInt32      newYOffset,
00742                                  CATInt32      newWidth,
00743                                  CATInt32      newHeight )
00744 {
00745    
00746    // Cannot set offsets or modify width/height on the root image.
00747    CATASSERT(IsImageRoot() == false, 
00748       "Cannot SetSubPosition on a root image.");   
00749    
00750    if (IsImageRoot())
00751    {     
00752       return CATRESULT(CAT_ERR_IMAGE_OPERATION_INVALID_ON_ROOT);
00753    }
00754 
00755    // Perform sanity checks on values.
00756    CATASSERT(newXOffset >= 0, "XOffset must be >= 0");
00757    CATASSERT(newYOffset >= 0, "YOffset must be >= 0");
00758    
00759    CATASSERT(newXOffset + newWidth  <= fParentImage->fWidth,  
00760             "Invalid sub image width");
00761    
00762    CATASSERT(newYOffset + newHeight <= fParentImage->fHeight, 
00763             "Invalid sub image height");
00764 
00765    CATASSERT(fParentImage->fData != 0, "Parent image is invalid.");
00766 
00767    // Bail on invalid parameters (same checks as asserts above)
00768    if ((newXOffset < 0) || (newYOffset < 0)        ||
00769        (newXOffset + newWidth  > fParentImage->fWidth)      ||
00770        (newYOffset + newHeight > fParentImage->fHeight)     ||
00771        (fParentImage->fData == 0))
00772    {
00773       return CATRESULT(CAT_ERR_IMAGE_INVALID_SUB_POSITION);
00774    }
00775 
00776    // Okay, position is valid. Make the change.
00777    fXOffset = newXOffset;
00778    fYOffset = newYOffset;
00779    fWidth   = newWidth;
00780    fHeight  = newHeight;
00781 
00782    return   CAT_SUCCESS;
00783 }
00784 
00785 
00786 //---------------------------------------------------------------------------
00787 // GetPixel() retrieves the red, green, blue, and alpha values for a 
00788 // specified pixel.
00789 //
00790 // This is for convenience and prototyping - for high-speed image
00791 // processing you'll need to work more directly with the image
00792 // buffer.
00793 //
00794 //
00795 // \param x - x position within the image of the pixel
00796 // \param y - y position within the image of the pixel
00797 // \param r - receives the red value of the pixel
00798 // \param g - receives the green value of the pixel
00799 // \param b - receives the blue value of the pixel
00800 // \param a - receives the alpha value of the pixel
00801 //
00802 // \return CATResult result code.  CAT_SUCCESS on success.
00803 // \sa SetPixel()
00804 //---------------------------------------------------------------------------
00805 
00806 CATResult CATImage::GetPixel(   CATInt32              x,
00807                               CATInt32              y,
00808                               CATColor&         color) const
00809 {
00810    return GetPixel(x, y, color.r, color.g, color.b, color.a);
00811 }
00812 
00813 CATResult CATImage::GetPixel(   CATInt32              x,
00814                               CATInt32              y,
00815                               unsigned char&   r,
00816                               unsigned char&   g,
00817                               unsigned char&   b,
00818                               unsigned char&   a) const
00819 {
00820    CATResult result = CAT_SUCCESS;
00821    
00822    CATASSERT(fData != 0, "Image must be created first!");
00823    if (fData == 0)
00824    {
00825       return CATRESULT(CAT_ERR_IMAGE_EMPTY);
00826    }
00827 
00828    // Bounds check coordinates
00829    CATASSERT(( (x >= 0) || (x < fWidth)), "X position is out of bounds!");
00830    CATASSERT(( (y >= 0) || (y < fHeight)), "Y position is out of bounds!");
00831 
00832    if ( (x < 0) || (x >= fWidth))
00833    {
00834       return CATRESULT(CAT_ERR_IMAGE_OUT_OF_RANGE);
00835    }
00836 
00837    if ( (y < 0) || (y >= fHeight))
00838    {
00839       return CATRESULT(CAT_ERR_IMAGE_OUT_OF_RANGE);
00840    }
00841       
00842    
00843    // Offset of pixel on x axis in image data
00844    CATInt32 lineOffset = (this->XOffsetAbs() + x) * kBytesPerPixel;
00845 
00846    // Absolute length of line in fData in bytes ( >= fWidth * GetBytesPerPixel())
00847    CATInt32 lineLength = this->AbsWidth() * kBytesPerPixel;
00848    
00849    // current position of start of buffer in source
00850    unsigned char* curPtr = fData + lineOffset + 
00851                            ((YOffsetAbs() + y) * lineLength);
00852 
00853    // pull out pixels
00854    r = curPtr[0];
00855    g = curPtr[1];
00856    b = curPtr[2];
00857    a = curPtr[3];
00858          
00859    return result;
00860 }
00861 //---------------------------------------------------------------------------
00862 // SetPixel() sets the red, green, blue, and alpha pixel values
00863 // for a pixel
00864 //
00865 // This is for convenience and prototyping - for high-speed image
00866 // processing you'll need to work more directly with the image
00867 // buffer.
00868 //
00869 // \param x - x position within the image of the pixel
00870 // \param y - y position within the image of the pixel
00871 // \param r - red value of the pixel
00872 // \param g - green value of the pixel
00873 // \param b - blue value of the pixel
00874 // \param a - alpha value of the pixel
00875 //
00876 // \return CATResult result code.  CAT_SUCCESS on success.
00877 // \sa GetPixel()
00878 //---------------------------------------------------------------------------
00879 CATResult CATImage::SetPixel(   CATInt32              x,
00880                               CATInt32              y,
00881                               const CATColor&   color)
00882 {
00883    return SetPixel(x, y, color.r, color.g, color.b, color.a);
00884 }
00885 
00886 CATResult CATImage::SetPixel (        CATInt32               x,
00887                                     CATInt32               y,
00888                                     unsigned char     r,
00889                                     unsigned char     g,
00890                                     unsigned char     b,
00891                                     unsigned char     a)
00892 {
00893    CATResult result = CAT_SUCCESS;
00894    
00895    CATASSERT(fData != 0, "Image must be created first!");
00896    if (fData == 0)
00897    {
00898       return CATRESULT(CAT_ERR_IMAGE_EMPTY);
00899    }
00900 
00901    // Bounds check coordinates
00902    CATASSERT(( (x >= 0) || (x < fWidth)), "X position is out of bounds!");
00903    CATASSERT(( (y >= 0) || (y < fHeight)), "Y position is out of bounds!");
00904 
00905    if ( (x < 0) || (x >= fWidth))
00906    {
00907       return CATRESULT(CAT_ERR_IMAGE_OUT_OF_RANGE);
00908    }
00909 
00910    if ( (y < 0) || (y >= fHeight))
00911    {
00912       return CATRESULT(CAT_ERR_IMAGE_OUT_OF_RANGE);
00913    }
00914    
00915    // Offset of pixel on x axis in image data
00916    CATInt32 lineOffset = (this->XOffsetAbs() + x) * kBytesPerPixel;
00917 
00918    // Absolute length of line in fData in bytes ( >= fWidth * GetBytesPerPixel())
00919    CATInt32 lineLength = this->AbsWidth() * kBytesPerPixel;
00920    
00921    // current position of start of buffer in source
00922    unsigned char* curPtr = fData + lineOffset + 
00923                            ((YOffsetAbs() + y) * lineLength);
00924    
00925    // Store
00926    curPtr[0] = r;
00927    curPtr[1] = g;
00928    curPtr[2] = b;
00929    curPtr[3] = a;
00930          
00931    return result;
00932 }
00933 
00934 //---------------------------------------------------------------------------
00935 // CopyOver() copies from another image over the current image
00936 // at the specified offsets for the specified width and height.
00937 //
00938 // If no width/height parameters are given (or are 0), the entire 
00939 // width and/or height of the source image is used.
00940 //
00941 // Clipping is not performed. However, if the copy would go out
00942 // of bounds, an error will be returned and the copy will not
00943 // be performed.
00944 //
00945 // \param srcImage - source image to copy from
00946 // \param dstOffsetX - x offset in dest image to copy to
00947 // \param dstOffsetY - y offset in dest image to copy to
00948 // \param srcOffsetX - x offset in src image to copy from
00949 // \param srcOffsetY - y offset in src image to copy from
00950 // \param width    - width to copy in
00951 // \param height   - height to copy in
00952 // \return CATResult - CAT_SUCCESS on success.
00953 //---------------------------------------------------------------------------
00954 CATResult  CATImage::CopyOver (    const CATImage*    srcImg,
00955                                  CATInt32               dstOffsetX,
00956                                  CATInt32               dstOffsetY,
00957                                  CATInt32               srcOffsetX,
00958                                  CATInt32               srcOffsetY,
00959                                  CATInt32               width,
00960                                  CATInt32               height)
00961 {
00962    CATResult result = CAT_SUCCESS;
00963    CATASSERT(srcImg != 0, "Null source image.");
00964    
00965    // Bail if image is null
00966    if (srcImg == 0)
00967    {
00968       return CATRESULT(CAT_ERR_IMAGE_NULL);
00969    }
00970 
00971    // If the width/height are 0, set them to source size
00972    if (width == 0)
00973    {
00974       width = srcImg->Width();
00975    }  
00976    
00977    if (height == 0)
00978    {
00979       height = srcImg->Height();
00980    } 
00981 
00982    CATASSERT(width != 0, "Width still null in image copy");
00983    CATASSERT(height != 0, "Height still null in image copy");
00984    if ((width == 0) || (height == 0))
00985    {
00986       return CATRESULT(CAT_ERR_IMAGE_NULL);
00987    }
00988 
00989    CATASSERT(width + dstOffsetX <= fWidth, "Out of bounds in image copy");
00990    CATASSERT(height + dstOffsetY <= fHeight, "Out of bounds in image copy");
00991    if ( (width + dstOffsetX > fWidth) ||
00992          (height + dstOffsetY > fHeight))
00993    {
00994       return CATRESULT(CAT_ERR_IMAGE_OVERLAY_OUT_OF_BOUNDS);
00995    }
00996 
00997    // Now copy the image in on all four channels.
00998    CATInt32 y;
00999     // CATInt32 x
01000    CATASSERT(fData != 0, "Image must be created first!");
01001    if (fData == 0)
01002    {
01003       return CATRESULT(CAT_ERR_IMAGE_MUST_INITIALIZE);
01004    }
01005 
01006 
01007    // Offset into line for source buffer in bytes
01008    CATInt32 dstOffX = (this->XOffsetAbs() + dstOffsetX) * kBytesPerPixel;   
01009    
01010    // Absolute length of line in fData in bytes ( >= fWidth * 3)
01011    CATInt32 dstLineLength = this->AbsWidth()   * kBytesPerPixel;   
01012    
01013    // Distance from end of one line to beginning of the next in bytes.
01014    CATInt32 dstStep   = dstLineLength - (width * kBytesPerPixel);
01015    
01016    // current position of start of buffer in source
01017    unsigned char* dstPtr = fData + 
01018                            dstOffX + 
01019                            ((YOffsetAbs() + dstOffsetY) * dstLineLength);
01020 
01021 
01022    // as above for source
01023    CATInt32 srcOffX           = (srcImg->XOffsetAbs() + srcOffsetX) * kBytesPerPixel;   
01024    CATInt32 srcLineLength     = srcImg->AbsWidth()   * kBytesPerPixel;   
01025    CATInt32 srcStep           = srcLineLength - (width * kBytesPerPixel);
01026    unsigned char* srcPtr = srcImg->fData + 
01027                            srcOffX + 
01028                            ((srcImg->YOffsetAbs() + srcOffsetY) * srcLineLength);
01029 
01030    bool alphaUnused = true;
01031 
01032    CATASSERT(kBytesPerPixel == 4, "Woops.");
01033 
01034    // Loop through image performing merge
01035    
01036     CATUInt32 lineWidth = width*4;
01037 
01038     for (y = 0; y < height; y++)
01039    {     
01040         memcpy(dstPtr, srcPtr,lineWidth);
01041       dstPtr += dstStep + lineWidth;
01042       srcPtr += srcStep + lineWidth;
01043    }
01044    return result;
01045 }
01046 
01047 //---------------------------------------------------------------------------
01048 // Overlay() merges from another image over the current image
01049 // at the specified offsets for the specified width and height.
01050 //
01051 // If no width/height parameters are given (or are 0), the entire 
01052 // width and/or height of the source image is used.
01053 //
01054 // Clipping is not performed. However, if the copy would go out
01055 // of bounds, an error will be returned and the copy will not
01056 // be performed.
01057 //
01058 // The alpha channel is used for the merge operation from the
01059 // source image only.  The destination's alpha remains 
01060 // unaffected - the alpha is used for merging the colors only.
01061 //
01062 // \param srcImage - source image to copy from
01063 // \param xOffset  - offset in dest image to copy to
01064 // \param yOffset  - offset in dest image to copy to
01065 // \param width    - width to copy in
01066 // \param height   - height to copy in
01067 // \return CATResult - CAT_SUCCESS on success.
01068 //---------------------------------------------------------------------------
01069 CATResult  CATImage::Overlay(   const CATImage*    srcImg,
01070                               CATInt32               dstOffsetX,
01071                               CATInt32               dstOffsetY,
01072                               CATInt32               srcOffsetX,
01073                               CATInt32               srcOffsetY,
01074                               CATInt32               width,
01075                               CATInt32               height)
01076 {
01077    CATResult result = CAT_SUCCESS;
01078    CATASSERT(srcImg != 0, "Null source image.");
01079    
01080    // Bail if image is null
01081    if (srcImg == 0)
01082    {
01083       return CATRESULT(CAT_ERR_IMAGE_NULL);
01084    }
01085 
01086    // If the width/height are 0, set them to source size
01087    if (width == 0)
01088    {
01089       width = srcImg->Width();
01090    }
01091    
01092    if (height == 0)
01093    {
01094       height = srcImg->Height();
01095    } 
01096 
01097    CATASSERT(width != 0, "Width still null in image copy");
01098    CATASSERT(height != 0, "Height still null in image copy");
01099    if ((width == 0) || (height == 0))
01100    {
01101       return CATRESULT(CAT_ERR_IMAGE_NULL);
01102    }
01103 
01104    CATASSERT(width + dstOffsetX <= fWidth, "Out of bounds in image copy");
01105    CATASSERT(height + dstOffsetY <= fHeight, "Out of bounds in image copy");
01106    if ( (width + dstOffsetX > fWidth) ||
01107          (height + dstOffsetY > fHeight))
01108    {
01109       return CATRESULT(CAT_ERR_IMAGE_OVERLAY_OUT_OF_BOUNDS);
01110    }
01111 
01112    if ( (width + dstOffsetX > fWidth) ||
01113          (height + dstOffsetY > fHeight))
01114    {
01115       return CATRESULT(CAT_ERR_IMAGE_OVERLAY_OUT_OF_BOUNDS);
01116    }
01117 
01118    CATASSERT(width + srcOffsetX <= srcImg->Width(), "Out of bounds in image copy");
01119    CATASSERT(height + srcOffsetY <= srcImg->Height(), "Out of bounds in image copy");
01120    if (width + srcOffsetX > srcImg->Width())
01121    {
01122       width = srcImg->Width() - srcOffsetX;
01123    }
01124    if (height + srcOffsetY > srcImg->Height())
01125    {
01126       height = srcImg->Height() - srcOffsetY;
01127    }
01128 
01129 
01130    // Now copy the image in on all four channels.
01131    CATInt32 x,y;
01132    CATASSERT(fData != 0, "Image must be created first!");
01133    if (fData == 0)
01134    {
01135       return CATRESULT(CAT_ERR_IMAGE_MUST_INITIALIZE);
01136    }
01137 
01138 
01139    // Offset into line for source buffer in bytes
01140    CATInt32 dstOffX = (this->XOffsetAbs() + dstOffsetX) * kBytesPerPixel;   
01141    
01142    // Absolute length of line in fData in bytes ( >= fWidth * 3)
01143    CATInt32 dstLineLength = this->AbsWidth()   * kBytesPerPixel;   
01144    
01145    // Distance from end of one line to beginning of the next in bytes.
01146    CATInt32 dstStep   = dstLineLength - (width * kBytesPerPixel);
01147    
01148    // current position of start of buffer in source
01149    unsigned char* dstPtr = fData + 
01150                            dstOffX + 
01151                            ((YOffsetAbs() + dstOffsetY) * dstLineLength);
01152 
01153 
01154    // as above for source
01155    CATInt32 srcOffX           = (srcImg->XOffsetAbs() + srcOffsetX) * 
01156                            kBytesPerPixel;   
01157 
01158    CATInt32 srcLineLength     = srcImg->AbsWidth()   * kBytesPerPixel;   
01159    CATInt32 srcStep           = srcLineLength - (width * kBytesPerPixel);
01160    unsigned char* srcPtr = srcImg->fData + 
01161                            srcOffX + 
01162                            ((srcImg->YOffsetAbs() + 
01163                               srcOffsetY) * srcLineLength);
01164 
01165    // Loop through image performing merge.
01166    // If this turns out to be a bottleneck, 
01167    // convert to SIMD on win32 and equiv on Mac.
01168    for (y = 0; y < height; y++)
01169    {      
01170       for (x = 0; x < width; x++)
01171       {       
01172          CATInt32 i;  
01173          unsigned char alpha = srcPtr[3];
01174 
01175          // skip multiplication steps for totally transparent or opaque alpha.
01176          if (alpha == 0)
01177          {
01178             srcPtr +=4;
01179             dstPtr +=4;
01180             continue;
01181          }
01182          else if (alpha == 255)
01183          {
01184             for (i=0; i<3; i++)
01185             {
01186                *dstPtr = *srcPtr;
01187                dstPtr++;
01188                srcPtr++;
01189             }
01190             dstPtr ++;
01191             srcPtr ++;
01192             continue;
01193          }
01194          
01195          // Blend the r,g,b channels. Alpha remains the same for destination image.         
01196          for (i=0; i < 3; i++)
01197          {
01198                 *dstPtr = (CATUInt8)((( alpha * (CATInt32)( *srcPtr - *dstPtr) ) + ((CATInt32)(*dstPtr)<<8) ) >> 8);
01199 /*
01200             *dstPtr =  (CATUInt8)((( (CATInt32)(*srcPtr) * alpha)  + 
01201                         ( (CATInt32)(*dstPtr) * (255 - alpha) )) / 255);
01202 */
01203             dstPtr++;
01204             srcPtr++;
01205          }
01206          
01207          srcPtr++;
01208          dstPtr++;
01209       }
01210 
01211       dstPtr += dstStep;
01212       srcPtr += srcStep;
01213    }
01214 
01215     
01216 
01217 
01218    return result;
01219 }
01220 
01221 //---------------------------------------------------------------------------
01222 /// Load() loads an image from a file.
01223 ///
01224 /// Currently, only .PNG is supported.
01225 ///
01226 /// Call CATImage::ReleaseImage() when done with the returned image.
01227 ///
01228 /// \param stream - an opened stream to load the image from.
01229 ///                 The stream should be positioned at the 
01230 ///                 start of the image.
01231 ///
01232 /// \param image - image ref set to image from stream on success.
01233 /// \return CATResult - CAT_SUCCESS on success.
01234 //---------------------------------------------------------------------------
01235 CATResult CATImage::Load           (  CATStream*         stream,
01236                                     CATImage*&         image)
01237 {   
01238    image = 0;
01239 
01240    CATResult result = CAT_SUCCESS;
01241 
01242    CATASSERT(stream != 0, "Stream must be created and opened first!");
01243    CATASSERT(stream->IsOpen(), "Stream must be created and opened first!");
01244    if ((stream == 0) || (stream->IsOpen() == false))
01245    {
01246       return CATRESULT(CAT_ERR_INVALID_PARAM);
01247    }
01248 
01249    image = new CATImage();
01250 
01251 
01252    png_structp png_ptr;
01253    png_infop info_ptr;
01254    png_uint_32 width, height;  
01255 
01256    try
01257    {
01258       // Create the base PNG pointer and set the error handlers.
01259       png_ptr = png_create_read_struct(   PNG_LIBPNG_VER_STRING,
01260                                           image, 
01261                                           PNGError, 
01262                                           PNGWarning);
01263 
01264       if (png_ptr == NULL)
01265       {
01266          delete image;
01267          image = 0;
01268          return CATRESULT(CAT_ERR_PNG_ERROR_CREATING_READ);
01269       }
01270 
01271       // Create the info struct   
01272       info_ptr = png_create_info_struct(png_ptr);
01273    
01274 
01275       if (info_ptr == NULL)
01276       {
01277          png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);       
01278          delete image;
01279          image = 0;
01280          return CATRESULT(CAT_ERR_PNG_ERROR_CREATING_READ);
01281       }
01282 
01283       // Set read callback and pass stream* as user_io_ptr
01284       png_set_read_fn(png_ptr, (void *)stream, PNGRead);
01285 
01286       // Convert png to 32-bit RGBA regardless of what it is
01287       png_set_expand(png_ptr);
01288       png_set_palette_to_rgb(png_ptr);
01289       png_set_tRNS_to_alpha(png_ptr);
01290       png_set_gray_to_rgb(png_ptr);
01291 
01292       // Read in the .png file
01293       png_read_png(png_ptr, info_ptr,  PNG_TRANSFORM_SHIFT, png_voidp_NULL);
01294 
01295 
01296       // Get pointer to row data
01297       unsigned char** rows = png_get_rows(png_ptr, info_ptr);
01298 
01299       // Get width / height
01300       width = png_get_image_width(png_ptr, info_ptr);
01301       height = png_get_image_height(png_ptr, info_ptr);
01302 
01303       CATUInt32 y,x;
01304 
01305    
01306       // Since we're creating a new image here, no worries about offsets and padding -
01307       // the image data is contiguous. Just copy row by row into our image
01308       image->Create(width, height, true, false);
01309       unsigned char* rawData = image->GetRawDataPtr();
01310 
01311       CATInt32 channels = png_get_channels(png_ptr,info_ptr);
01312 
01313       if (channels == 4)
01314       {
01315          for (y=0; y < height; y++)
01316          {
01317             memcpy(  rawData + (y * width * kBytesPerPixel),
01318                      rows[y],
01319                      width * kBytesPerPixel);
01320          }
01321       }
01322       else if (channels == 3)
01323       {
01324          for (y=0; y < height; y++)
01325          {
01326             unsigned char* linePtr = rawData + (y * width * kBytesPerPixel);            
01327             unsigned char* srcPtr = rows[y];
01328 
01329             for (x = 0; x < width; x++)
01330             {
01331                *linePtr = *srcPtr; linePtr++; srcPtr++;
01332                *linePtr = *srcPtr; linePtr++; srcPtr++;
01333                *linePtr = *srcPtr; linePtr++; srcPtr++;
01334                *linePtr = 255;
01335                linePtr++;
01336             }
01337          }
01338       }
01339       else
01340       {
01341          CATASSERT(false,"Unsupported number of channels in .PNG!");
01342          result = CATRESULTFILE(CAT_ERR_PNG_UNSUPPORTED_FORMAT,stream->GetName());
01343       }
01344    
01345       // Clean up
01346       png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);        
01347 
01348       return result;
01349    }
01350    // Catch errors that were thrown from PNGError()
01351    catch (CATResult& thrownRes)
01352    {
01353       result = thrownRes;
01354       if (image != 0)
01355       {
01356          delete image;
01357          image = 0;
01358       }
01359       png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
01360 
01361       // Tack on filename to result if we're using result classes.      
01362       return CATRESULTFILE(thrownRes, stream->GetName());
01363    }
01364 
01365 }
01366 
01367 //---------------------------------------------------------------------------
01368 /// Save() saves an image to a file.
01369 ///
01370 /// Currently, only .PNG format is supported.
01371 ///
01372 /// \param stream - an opened stream to save the image to.
01373 ///                 The stream should be positioned to the location
01374 ///                 to save the image to, and it must be writeable.
01375 ///
01376 /// \param image - ptr to image to save.
01377 /// \return CATResult - CAT_SUCCESS on success.
01378 //---------------------------------------------------------------------------
01379 
01380 
01381 CATResult CATImage::Save(  const CATWChar*  filename,
01382                            CATImage*        image)
01383 {
01384     CATResult result = CAT_SUCCESS;
01385     CATStreamFile outFile;
01386     if (CATFAILED(result = outFile.Open(filename,CATStream::READ_WRITE_CREATE_TRUNC)))
01387     {
01388         return result;
01389     }
01390 
01391     result = Save(&outFile, image);
01392 
01393     outFile.Close();
01394 
01395     return result;
01396 }
01397 
01398 CATResult CATImage::Save             (  CATStream*         stream,
01399                                         CATImage*          image,
01400                                         CATIMAGEFORMAT     imageFormat)
01401 {
01402    CATResult result = CAT_SUCCESS;
01403    unsigned char** row_pointers = 0;
01404 
01405    CATASSERT(imageFormat == CATIMAGE_PNG_RGBA32, "Only 32-bit RGBA .PNG's are supported.");
01406    if (imageFormat != CATIMAGE_PNG_RGBA32)
01407    {
01408       return CATRESULT(CAT_ERR_IMAGE_UNKNOWN_FORMAT);
01409    }
01410 
01411    CATASSERT(stream != 0, "Stream must be created and opened first!");
01412    CATASSERT(stream->IsOpen(), "Stream must be created and opened first!");
01413    if ((stream == 0) || (stream->IsOpen() == false))
01414    {
01415       return CATRESULT(CAT_ERR_INVALID_PARAM);
01416    }
01417 
01418    CATASSERT(image != 0, "Image must be valid for save.");   
01419    if ((image == 0) || (image->fData == 0))
01420    {
01421       return CATRESULT(CAT_ERR_INVALID_PARAM);
01422    }
01423 
01424    png_structp png_ptr;
01425    png_infop info_ptr;
01426 
01427    try
01428    {
01429       png_ptr = png_create_write_struct(  PNG_LIBPNG_VER_STRING,
01430                                           image, 
01431                                           PNGError,
01432                                           PNGWarning);
01433 
01434       if (png_ptr == NULL)
01435       {
01436          return CATRESULT(CAT_ERR_PNG_ERROR_CREATING_WRITE);
01437       }
01438 
01439       // Create infostruct
01440       info_ptr = png_create_info_struct(png_ptr);
01441       if (info_ptr == NULL)
01442       {      
01443          png_destroy_write_struct(&png_ptr,  png_infopp_NULL);
01444          return CATRESULT(CAT_ERR_PNG_ERROR_CREATING_WRITE);
01445       }
01446 
01447       // Set write callbacks
01448       png_set_write_fn( png_ptr, (void*)stream, 
01449                         PNGWrite,
01450                         PNGFlush);      
01451 
01452       
01453       try
01454       {
01455          row_pointers = new unsigned char*[image->fHeight];
01456       }
01457       catch (...)
01458       {
01459          row_pointers = 0;
01460          // Throw CATResult to function catch
01461          throw CATRESULT(CAT_ERR_OUT_OF_MEMORY);
01462       }
01463 
01464       for (CATInt32 y = 0; y < image->fHeight; y++)
01465       {
01466          row_pointers[y] = image->fData + (image->XOffsetAbs() * kBytesPerPixel) +
01467                            (( (y + image->YOffsetAbs()) * image->AbsWidth()) 
01468                               * kBytesPerPixel);
01469       }
01470 
01471       png_set_IHDR(  png_ptr,
01472                      info_ptr,
01473                      image->fWidth,
01474                      image->fHeight,
01475                      8,
01476                      PNG_COLOR_TYPE_RGB_ALPHA,
01477                      PNG_INTERLACE_NONE,
01478                      PNG_COMPRESSION_TYPE_BASE,
01479                      PNG_FILTER_TYPE_BASE);
01480 
01481       png_set_rows(png_ptr,info_ptr,row_pointers);
01482 
01483       png_write_png(png_ptr, info_ptr, 0, png_voidp_NULL);
01484 
01485       delete [] row_pointers;
01486    }
01487    catch (CATResult& thrownRes)
01488    {            
01489       if (row_pointers)
01490       {
01491          delete [] row_pointers;
01492       }
01493 
01494       result = thrownRes;
01495       // Tack on filename to result if we're using result classes.
01496       result = CATRESULTFILE(result,stream->GetName());      
01497    }
01498 
01499    // Clean up
01500    png_destroy_write_struct(&png_ptr, &info_ptr);      
01501 
01502    return result;
01503 }
01504 
01505 //---------------------------------------------------------------------------
01506 // Debugging func - saves image to disk as a .png
01507 //---------------------------------------------------------------------------
01508 CATResult CATImage::DbgSave( const CATWChar* filename)
01509 {
01510    // Create file    
01511    CATResult result = CAT_SUCCESS;
01512    CATStreamFile file;
01513    if (CATFAILED(result = file.Open(filename, CATStream::READ_WRITE_CREATE_TRUNC)))
01514    {
01515       return result;
01516    }
01517 
01518    result = this->Save(&file,this);
01519 
01520    file.Close();
01521    
01522    return result;
01523 }
01524 
01525 //------------------------------------------------------------------------
01526 // PNG utility functions
01527 // These are mainly callbacks required by the PNG library.
01528 //
01529 // Functions which may call into these need to catch CATResult&'s.
01530 //------------------------------------------------------------------------
01531 
01532 
01533 //------------------------------------------------------------------------
01534 // PNGRead() reads from the current stream into the buffer
01535 // provided by libpng.
01536 //
01537 // You shouldn't need to call this directly - these are called
01538 // by libpng only.
01539 //
01540 // \param png_ptr - structure for the .png library. The CATStream*
01541 //                  for the current stream is in png_ptr->io_ptr.
01542 // \param data    - target buffer
01543 // \param length  - number of bytes to read.
01544 // \return none. Throws a CATRESULT on error.
01545 // \sa Load()
01546 //---------------------------------------------------------------------------
01547 void CATImage::PNGRead(     png_structp       png_ptr,
01548                            png_bytep         data, 
01549                            png_size_t        length)
01550 {
01551    CATStream* userStream = (CATStream*)png_ptr->io_ptr;   
01552    CATUInt32 amountRead = (CATUInt32)length;
01553    CATResult result = userStream->Read(data,amountRead);
01554    if (CATFAILED(result))
01555    {
01556       throw(result);
01557    }
01558 }
01559 
01560 //------------------------------------------------------------------------
01561 // PNGWrite() writes to the current stream from the buffer
01562 // provided by libpng.
01563 //
01564 // You shouldn't need to call this directly - these are called
01565 // by libpng only.
01566 //
01567 // \param png_ptr - structure for the .png library. The CATStream*
01568 //                  for the current stream is in png_ptr->io_ptr.
01569 // \param data    - source buffer
01570 // \param length  - number of bytes to write.
01571 // \return none. Throws a CATRESULT on error.
01572 // \sa Save()
01573 //---------------------------------------------------------------------------
01574 void CATImage::PNGWrite(    png_structp       png_ptr,
01575                            png_bytep         data, 
01576                            png_size_t        length)
01577 {
01578    CATStream* userStream = (CATStream*)png_ptr->io_ptr;   
01579    CATResult result = userStream->Write(data,(CATUInt32)length);
01580    if (CATFAILED(result))
01581    {
01582       throw(result);
01583    }
01584 }
01585 
01586 //------------------------------------------------------------------------
01587 // PNGFlush() flushes the current stream for libpng.
01588 //
01589 // You shouldn't need to call this directly - these are called
01590 // by libpng only.
01591 //
01592 // \param png_ptr - structure for the .png library. The CATStream*
01593 //                  for the current stream is in png_ptr->io_ptr.
01594 // \return none. Throws a CATRESULT on error.
01595 // \sa Load(), Save()
01596 //---------------------------------------------------------------------------
01597 void CATImage::PNGFlush(    png_structp       png_ptr)
01598 {
01599    CATStream* userStream = (CATStream*)png_ptr->io_ptr;      
01600 }
01601 
01602 //------------------------------------------------------------------------
01603 // PNGError() is called by libpng when a fatal error occurs.
01604 //
01605 // This just throws a CATResult when called.
01606 //
01607 // \param png_ptr - structure for .png lib.
01608 // \param error_msg - ASCIIZ message reporting the error.
01609 //---------------------------------------------------------------------------
01610 void CATImage::PNGError(    png_structp       png_ptr,
01611                   png_const_charp   error_msg)
01612 {
01613    throw(CATRESULTDESC(CAT_ERR_PNG_CORRUPT,error_msg));
01614 }
01615 
01616 
01617 //------------------------------------------------------------------------
01618 // PNGWarning() is called by libpng when a recoverable error occurs.
01619 //
01620 // This just throws a CATResult when called.
01621 //
01622 // \param png_ptr - structure for .png lib.
01623 // \param warning_msg - ASCIIZ message reporting the warning.
01624 //---------------------------------------------------------------------------
01625 void CATImage::PNGWarning(  png_structp       png_ptr,
01626                   png_const_charp   warning_msg)
01627 {
01628    throw(CATRESULTDESC(CAT_ERR_PNG_WARNING,warning_msg));
01629 }
01630 
01631 //---------------------------------------------------------------------------
01632 // CopyOutBGR() copies the RGB data from a rectangle
01633 // into a buffer and flips the Red and Blue channels.  
01634 // The buffer must be width*height*3
01635 // bytes in size.
01636 //
01637 // \param rgbBuf - raw rgb buffer of width/height size
01638 // \param offsetX - x offset to copy from
01639 // \param offsetY - y offset to copy from
01640 // \param width    - width to copy 
01641 // \param height   - height to copy 
01642 // \param paddedWidth - padded width of buffer
01643 // \return CATResult - CAT_SUCCESS on success.
01644 //---------------------------------------------------------------------------
01645 CATResult  CATImage::CopyOutBGR (  CATUInt8*              rgbBuf,
01646                                  CATInt32               offsetX,
01647                                  CATInt32               offsetY,
01648                                  CATInt32               width,
01649                                  CATInt32               height,
01650                                  CATInt32               widthBytes)
01651 {
01652    CATASSERT(width != 0, "Width null");
01653    CATASSERT(height != 0, "Height null");
01654    CATASSERT(rgbBuf != 0, "Null buffer passed in.");
01655    if ((width == 0) || (height == 0) || (rgbBuf == 0))
01656    {
01657       return CATRESULT(CAT_ERR_IMAGE_OVERLAY_OUT_OF_BOUNDS);
01658    }
01659 
01660    CATASSERT(width + offsetX <= fWidth, "Out of bounds in image copy");
01661    CATASSERT(height + offsetY <= fHeight, "Out of bounds in image copy");
01662    if ( (width + offsetX > fWidth) ||
01663         (height + offsetY > fHeight))
01664    {
01665       return CATRESULT(CAT_ERR_IMAGE_OVERLAY_OUT_OF_BOUNDS);
01666    }
01667 
01668    // Now copy the image to the buffer
01669    CATInt32 x,y;
01670    CATASSERT(fData != 0, "Image must be created first!");
01671    if (fData == 0)
01672    {
01673       return CATRESULT(CAT_ERR_IMAGE_MUST_INITIALIZE);
01674    }
01675 
01676 
01677    // Offset into line for source buffer in bytes
01678    CATInt32 srcOffX = (this->XOffsetAbs() + offsetX) * kBytesPerPixel;   
01679    
01680    // Absolute length of line in fData in bytes ( >= fWidth * 3)
01681    CATInt32 srcLineLength = this->AbsWidth()   * kBytesPerPixel;   
01682    
01683    // Distance from end of one line to beginning of the next in bytes.
01684    CATInt32 srcStep   = srcLineLength - (width * kBytesPerPixel);
01685    
01686    // current position of start of buffer in source
01687    unsigned char* srcPtr = fData + 
01688                            srcOffX + 
01689                            ((YOffsetAbs() + offsetY) * srcLineLength);
01690 
01691 
01692    unsigned char* dstPtr = rgbBuf;
01693    CATInt32 dstStep = widthBytes - width*3;
01694 
01695     CATASSERT(kBytesPerPixel == 4, "Making assumptions on pixel size right now...");
01696 
01697    // Loop through image performing merge
01698    for (y = 0; y < height; y++)
01699    {      
01700         CATUInt32* srcDwPtr = (CATUInt32*)srcPtr;
01701 
01702       for (x = 0; x < width; x++)
01703       {
01704             CATUInt32 curPixel = *srcDwPtr;
01705             srcDwPtr++;
01706             *dstPtr = (CATUInt8)(curPixel >> 16);
01707             dstPtr++;
01708             *dstPtr = (CATUInt8)(curPixel >> 8);
01709             dstPtr++;
01710             *dstPtr = (CATUInt8)(curPixel);
01711             dstPtr++;
01712             
01713             
01714             /*
01715          dstPtr[0] = srcPtr[2];
01716          dstPtr[1] = srcPtr[1];
01717          dstPtr[2] = srcPtr[0];
01718          
01719          srcPtr += kBytesPerPixel;
01720          dstPtr += 3;
01721             */
01722 
01723 
01724             
01725       }
01726 
01727       srcPtr += srcStep + width*4;
01728       dstPtr += dstStep;
01729    }
01730    
01731    return CAT_SUCCESS;
01732 }
01733 
01734 unsigned long CATImage::GetRefCount()
01735 {
01736    return this->fRefCount;
01737 }
01738 
01739 
01740 CATResult CATImage::CreateImageFromDIB( CATImage*&      image,
01741                                                     HBITMAP         dibSection)
01742 {
01743     CATResult result = CAT_SUCCESS;
01744    image = 0;
01745 
01746    try
01747    {
01748       image = new CATImage();
01749    }
01750    catch(...)
01751    {
01752       image = 0;
01753    }
01754 
01755    if (image == 0)
01756    {
01757       return CATRESULT(CAT_ERR_OUT_OF_MEMORY);
01758    }
01759 
01760     if ((dibSection == 0) || (dibSection == INVALID_HANDLE_VALUE))
01761     {
01762         return CATRESULT(CAT_ERR_INVALID_PARAM);
01763     }
01764 
01765     BITMAP bmpInfo;
01766     memset(&bmpInfo,0,sizeof(bmpInfo));
01767     ::GetObject( dibSection, sizeof(BITMAP), &bmpInfo);
01768 
01769     CATInt32 width,height;
01770     width       = bmpInfo.bmWidth;
01771     height  = bmpInfo.bmHeight;
01772 
01773     bool upsideDown = (height > 0);
01774     if (!upsideDown)
01775     {
01776         height = -height;
01777     }
01778 
01779    if ((width != 0) && (height != 0))
01780    {
01781       result = image->Create(width, height, false, false);
01782    }
01783 
01784     if (CATFAILED(result))
01785     {
01786         return result;
01787     }
01788 
01789     unsigned char* rawPtr = image->GetRawDataPtr();
01790 
01791     CATASSERT(bmpInfo.bmBitsPixel == 24, "Only 24-bit images are currently supported.");
01792     
01793     if ((bmpInfo.bmBits != 0) && (bmpInfo.bmBitsPixel == 24))
01794     {           
01795         unsigned char* dstPtr = 0;
01796         unsigned char* srcPtr = 0;
01797 
01798         for (CATInt32 y = 0; y < height; y++)
01799         {
01800             dstPtr = rawPtr + (y*image->AbsWidth()*4);
01801             srcPtr = (unsigned char*)bmpInfo.bmBits;
01802             
01803             if (upsideDown)
01804                 srcPtr += ((height-1) - y) * bmpInfo.bmWidthBytes;
01805             else
01806                 srcPtr += (y*bmpInfo.bmWidthBytes);
01807 
01808 
01809             for (CATInt32 x = 0; x < width; x++)
01810             {
01811                 // BGR to RGB
01812                 *dstPtr = srcPtr[2]; dstPtr++;              
01813                 *dstPtr = srcPtr[1]; dstPtr++;              
01814                 *dstPtr = srcPtr[0];    dstPtr++; 
01815                                 
01816                 // alpha
01817                 *dstPtr = 0xff;
01818                 dstPtr++;   
01819                 
01820                 srcPtr += 3;
01821             }
01822         }
01823     }
01824 
01825     return result;
01826 }

Generated on Mon Feb 11 04:09:43 2008 for Game Accessibility Suite by doxygen 1.5.4