Back to index

lightning-sunbird  0.9+nobinonly
nsXBMDecoder.cpp
Go to the documentation of this file.
00001 /* vim:set tw=80 expandtab softtabstop=4 ts=4 sw=4: */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is the Mozilla XBM Decoder.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Christian Biesinger <cbiesinger@web.de>.
00019  * Portions created by the Initial Developer are Copyright (C) 2001
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Aaron Kaluszka <ask@swva.net>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 /* KNOWN BUGS:
00040  * o first #define line is assumed to be width, second height */
00041 
00042 #include <string.h>
00043 #include <stdlib.h>
00044 #include <ctype.h>
00045 
00046 #include "nsXBMDecoder.h"
00047 
00048 #include "nsIInputStream.h"
00049 #include "nsIComponentManager.h"
00050 
00051 #include "imgILoad.h"
00052 
00053 #include "nsIProperties.h"
00054 #include "nsISupportsPrimitives.h"
00055 
00056 #if defined(XP_WIN) || defined(XP_OS2) || defined(XP_BEOS) || defined(MOZ_WIDGET_PHOTON)
00057 #define GFXFORMAT gfxIFormats::BGR_A1
00058 #else
00059 #define USE_RGB
00060 #define GFXFORMAT gfxIFormats::RGB_A1
00061 #endif
00062 
00063 NS_IMPL_ISUPPORTS1(nsXBMDecoder, imgIDecoder)
00064 
00065 nsXBMDecoder::nsXBMDecoder() : mBuf(nsnull), mPos(nsnull), mAlphaRow(nsnull)
00066 {
00067 }
00068 
00069 nsXBMDecoder::~nsXBMDecoder()
00070 {
00071     if (mBuf)
00072         free(mBuf);
00073 
00074     if (mAlphaRow)
00075         free(mAlphaRow);
00076 }
00077 
00078 NS_IMETHODIMP nsXBMDecoder::Init(imgILoad *aLoad)
00079 {
00080     nsresult rv;
00081     mObserver = do_QueryInterface(aLoad);
00082 
00083     mImage = do_CreateInstance("@mozilla.org/image/container;1", &rv);
00084     if (NS_FAILED(rv))
00085         return rv;
00086 
00087     mFrame = do_CreateInstance("@mozilla.org/gfx/image/frame;2", &rv);
00088     if (NS_FAILED(rv))
00089         return rv;
00090 
00091     aLoad->SetImage(mImage);
00092 
00093     mCurRow = mBufSize = mWidth = mHeight = 0;
00094     mState = RECV_HEADER;
00095 
00096     return NS_OK;
00097 }
00098 
00099 NS_IMETHODIMP nsXBMDecoder::Close()
00100 {
00101     mObserver->OnStopContainer(nsnull, mImage);
00102     mObserver->OnStopDecode(nsnull, NS_OK, nsnull);
00103     mObserver = nsnull;
00104     mImage = nsnull;
00105     mFrame = nsnull;
00106 
00107     if (mAlphaRow) {
00108         free(mAlphaRow);
00109         mAlphaRow = nsnull;
00110     }
00111 
00112     return NS_OK;
00113 }
00114 
00115 NS_IMETHODIMP nsXBMDecoder::Flush()
00116 {
00117     mFrame->SetMutable(PR_FALSE);
00118     return NS_OK;
00119 }
00120 
00121 NS_METHOD nsXBMDecoder::ReadSegCb(nsIInputStream* aIn, void* aClosure,
00122                              const char* aFromRawSegment, PRUint32 aToOffset,
00123                              PRUint32 aCount, PRUint32 *aWriteCount) {
00124     nsXBMDecoder *decoder = NS_REINTERPRET_CAST(nsXBMDecoder*, aClosure);
00125     *aWriteCount = aCount;
00126     return decoder->ProcessData(aFromRawSegment, aCount);
00127 }
00128 
00129 NS_IMETHODIMP nsXBMDecoder::WriteFrom(nsIInputStream *aInStr, PRUint32 aCount, PRUint32 *aRetval)
00130 {
00131     return aInStr->ReadSegments(ReadSegCb, this, aCount, aRetval);
00132 }
00133 
00134 nsresult nsXBMDecoder::ProcessData(const char* aData, PRUint32 aCount) {
00135     char *endPtr;
00136     // calculate the offset since the absolute position might no longer
00137     // be valid after realloc
00138     const PRPtrdiff posOffset = mPos ? (mPos - mBuf) : 0;
00139 
00140     // expand the buffer to hold the new data
00141     char* oldbuf = mBuf;
00142     PRUint32 newbufsize = mBufSize + aCount + 1;
00143     if (newbufsize < mBufSize)
00144         mBuf = nsnull;  // size wrapped around, give up
00145     else
00146         mBuf = (char*)realloc(mBuf, newbufsize);
00147 
00148     if (!mBuf) {
00149         mState = RECV_DONE;
00150         if (oldbuf)
00151             free(oldbuf);
00152         return NS_ERROR_OUT_OF_MEMORY;
00153     }
00154     memcpy(mBuf + mBufSize, aData, aCount);
00155     mBufSize += aCount;
00156     mBuf[mBufSize] = 0;
00157     mPos = mBuf + posOffset;
00158 
00159     // process latest data according to current state
00160     if (mState == RECV_HEADER) {
00161         mPos = strstr(mBuf, "#define");
00162         if (!mPos)
00163             // #define not found. return for now, waiting for more data.
00164             return NS_OK;
00165 
00166         // Convert width and height to numbers.  Convert hotspot for cursor functionality, if present
00167         if (sscanf(mPos, "#define %*s %u #define %*s %u #define %*s %u #define %*s %u unsigned", &mWidth, &mHeight, &mXHotspot, &mYHotspot) == 4)
00168             mIsCursor = PR_TRUE;
00169         else if (sscanf(mPos, "#define %*s %u #define %*s %u unsigned", &mWidth, &mHeight) == 2)
00170             mIsCursor = PR_FALSE;
00171         else
00172              // No identifiers found.  Return for now, waiting for more data.
00173             return NS_OK;
00174 
00175         // Check for X11 flavor
00176         if (strstr(mPos, " char "))
00177             mIsX10 = PR_FALSE;
00178         // Check for X10 flavor
00179         else if (strstr(mPos, " short "))
00180             mIsX10 = PR_TRUE;
00181         else
00182             // Neither identifier found.  Return for now, waiting for more data.
00183             return NS_OK;
00184 
00185         mImage->Init(mWidth, mHeight, mObserver);
00186         mObserver->OnStartContainer(nsnull, mImage);
00187 
00188         nsresult rv = mFrame->Init(0, 0, mWidth, mHeight, GFXFORMAT, 24);
00189         if (NS_FAILED(rv))
00190             return rv;
00191 
00192         if (mIsCursor) {
00193             nsCOMPtr<nsIProperties> props(do_QueryInterface(mImage));
00194             if (props) {
00195                 nsCOMPtr<nsISupportsPRUint32> intwrapx = do_CreateInstance("@mozilla.org/supports-PRUint32;1");
00196                 nsCOMPtr<nsISupportsPRUint32> intwrapy = do_CreateInstance("@mozilla.org/supports-PRUint32;1");
00197 
00198                 if (intwrapx && intwrapy) {
00199                     intwrapx->SetData(mXHotspot);
00200                     intwrapy->SetData(mYHotspot);
00201 
00202                     props->Set("hotspotX", intwrapx);
00203                     props->Set("hotspotY", intwrapy);
00204                 }
00205             }
00206         }
00207 
00208         mImage->AppendFrame(mFrame);
00209         mObserver->OnStartFrame(nsnull, mFrame);
00210 
00211         PRUint32 bpr;
00212         mFrame->GetImageBytesPerRow(&bpr);
00213         PRUint32 abpr;
00214         mFrame->GetAlphaBytesPerRow(&abpr);
00215 
00216         mAlphaRow = (PRUint8*)malloc(abpr);
00217         if (!mAlphaRow) {
00218           mState = RECV_DONE;
00219           return NS_ERROR_OUT_OF_MEMORY;
00220         }
00221 
00222         memset(mAlphaRow, 0, abpr);
00223 
00224         mState = RECV_SEEK;
00225 
00226         mCurRow = 0;
00227         mCurCol = 0;
00228 
00229     }
00230     if (mState == RECV_SEEK) {
00231         if ((endPtr = strchr(mPos, '{')) != NULL) {
00232             mPos = endPtr+1;
00233             mState = RECV_DATA;
00234         } else {
00235             mPos = mBuf + mBufSize;
00236             return NS_OK;
00237         }
00238     }
00239     if (mState == RECV_DATA) {
00240         PRUint32 bpr;
00241         mFrame->GetImageBytesPerRow(&bpr);
00242         PRUint32 abpr;
00243         mFrame->GetAlphaBytesPerRow(&abpr);
00244         PRBool hiByte = PR_TRUE;
00245 
00246         do {
00247             PRUint32 pixel = strtoul(mPos, &endPtr, 0);
00248             if (endPtr == mPos)
00249                 return NS_OK;   // no number to be found - need more data
00250             if (!*endPtr)
00251                 return NS_OK;   // number at the end - might be missing a digit
00252             if (pixel == 0 && *endPtr == 'x')
00253                 return NS_OK;   // 0x at the end, actual number is missing
00254             while (*endPtr && isspace(*endPtr))
00255                 endPtr++;       // skip whitespace looking for comma
00256 
00257             if (!*endPtr) {
00258                 // Need more data
00259                 return NS_OK;
00260             } else if (*endPtr != ',') {
00261                 *endPtr = '\0';
00262                 mState = RECV_DONE;  // strange character (or ending '}')
00263             }
00264             if (!mIsX10 || !hiByte)
00265                 mPos = endPtr; // go to next value only when done with this one
00266             if (mIsX10) {
00267                 // handle X10 flavor short values
00268                 if (hiByte)
00269                     pixel >>= 8;
00270                 hiByte = !hiByte;
00271             }
00272 
00273             mAlphaRow[mCurCol/8] = 0;
00274             for (int i = 0; i < 8; i++) {
00275                 PRUint8 val = (pixel & (1 << i)) >> i;
00276                 mAlphaRow[mCurCol/8] |= val << (7 - i);
00277             }
00278 
00279             mCurCol = PR_MIN(mCurCol + 8, mWidth);
00280             if (mCurCol == mWidth || mState == RECV_DONE) {
00281                 // Row finished. Set Data.
00282                 mFrame->SetAlphaData(mAlphaRow, abpr, mCurRow * abpr);
00283                 // nsnull gets interpreted as all-zeroes, which is what we
00284                 // want
00285                 mFrame->SetImageData(nsnull, bpr, mCurRow * bpr);
00286                 nsIntRect r(0, mCurRow, mWidth, 1);
00287                 mObserver->OnDataAvailable(nsnull, mFrame, &r);
00288 
00289                 if ((mCurRow + 1) == mHeight) {
00290                     mState = RECV_DONE;
00291                     return mObserver->OnStopFrame(nsnull, mFrame);
00292                 }
00293                 mCurRow++;
00294                 mCurCol = 0;
00295             }
00296 
00297             // Skip the comma
00298             NS_ASSERTION(mState != RECV_DATA || *mPos == ',' ||
00299                          (mIsX10 && hiByte),
00300                          "Must be a comma");
00301             if (*mPos == ',')
00302                 mPos++;
00303         } while ((mState == RECV_DATA) && *mPos);
00304     }
00305 
00306     return NS_OK;
00307 }
00308 
00309