/* vim:set tw=80 expandtab softtabstop=4 ts=4 sw=4: */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is the Mozilla XBM Decoder. * * The Initial Developer of the Original Code is * Christian Biesinger . * Portions created by the Initial Developer are Copyright (C) 2001 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Aaron Kaluszka * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* KNOWN BUGS: * o first #define line is assumed to be width, second height */ #include #include #include #include "nsXBMDecoder.h" #include "nsIInputStream.h" #include "nsIComponentManager.h" #include "imgILoad.h" #include "nsIProperties.h" #include "nsISupportsPrimitives.h" #if defined(XP_WIN) || defined(XP_OS2) || defined(XP_BEOS) || defined(MOZ_WIDGET_PHOTON) #define GFXFORMAT gfxIFormats::BGR_A1 #else #define USE_RGB #define GFXFORMAT gfxIFormats::RGB_A1 #endif NS_IMPL_ISUPPORTS1(nsXBMDecoder, imgIDecoder) nsXBMDecoder::nsXBMDecoder() : mBuf(nsnull), mPos(nsnull), mAlphaRow(nsnull) { } nsXBMDecoder::~nsXBMDecoder() { if (mBuf) free(mBuf); if (mAlphaRow) free(mAlphaRow); } NS_IMETHODIMP nsXBMDecoder::Init(imgILoad *aLoad) { nsresult rv; mObserver = do_QueryInterface(aLoad); mImage = do_CreateInstance("@mozilla.org/image/container;1", &rv); if (NS_FAILED(rv)) return rv; mFrame = do_CreateInstance("@mozilla.org/gfx/image/frame;2", &rv); if (NS_FAILED(rv)) return rv; aLoad->SetImage(mImage); mCurRow = mBufSize = mWidth = mHeight = 0; mState = RECV_HEADER; return NS_OK; } NS_IMETHODIMP nsXBMDecoder::Close() { mObserver->OnStopContainer(nsnull, mImage); mObserver->OnStopDecode(nsnull, NS_OK, nsnull); mObserver = nsnull; mImage = nsnull; mFrame = nsnull; if (mAlphaRow) { free(mAlphaRow); mAlphaRow = nsnull; } return NS_OK; } NS_IMETHODIMP nsXBMDecoder::Flush() { mFrame->SetMutable(PR_FALSE); return NS_OK; } NS_METHOD nsXBMDecoder::ReadSegCb(nsIInputStream* aIn, void* aClosure, const char* aFromRawSegment, PRUint32 aToOffset, PRUint32 aCount, PRUint32 *aWriteCount) { nsXBMDecoder *decoder = NS_REINTERPRET_CAST(nsXBMDecoder*, aClosure); *aWriteCount = aCount; return decoder->ProcessData(aFromRawSegment, aCount); } NS_IMETHODIMP nsXBMDecoder::WriteFrom(nsIInputStream *aInStr, PRUint32 aCount, PRUint32 *aRetval) { return aInStr->ReadSegments(ReadSegCb, this, aCount, aRetval); } nsresult nsXBMDecoder::ProcessData(const char* aData, PRUint32 aCount) { char *endPtr; // calculate the offset since the absolute position might no longer // be valid after realloc const PRPtrdiff posOffset = mPos ? (mPos - mBuf) : 0; // expand the buffer to hold the new data char* oldbuf = mBuf; PRUint32 newbufsize = mBufSize + aCount + 1; if (newbufsize < mBufSize) mBuf = nsnull; // size wrapped around, give up else mBuf = (char*)realloc(mBuf, newbufsize); if (!mBuf) { mState = RECV_DONE; if (oldbuf) free(oldbuf); return NS_ERROR_OUT_OF_MEMORY; } memcpy(mBuf + mBufSize, aData, aCount); mBufSize += aCount; mBuf[mBufSize] = 0; mPos = mBuf + posOffset; // process latest data according to current state if (mState == RECV_HEADER) { mPos = strstr(mBuf, "#define"); if (!mPos) // #define not found. return for now, waiting for more data. return NS_OK; // Convert width and height to numbers. Convert hotspot for cursor functionality, if present if (sscanf(mPos, "#define %*s %u #define %*s %u #define %*s %u #define %*s %u unsigned", &mWidth, &mHeight, &mXHotspot, &mYHotspot) == 4) mIsCursor = PR_TRUE; else if (sscanf(mPos, "#define %*s %u #define %*s %u unsigned", &mWidth, &mHeight) == 2) mIsCursor = PR_FALSE; else // No identifiers found. Return for now, waiting for more data. return NS_OK; // Check for X11 flavor if (strstr(mPos, " char ")) mIsX10 = PR_FALSE; // Check for X10 flavor else if (strstr(mPos, " short ")) mIsX10 = PR_TRUE; else // Neither identifier found. Return for now, waiting for more data. return NS_OK; mImage->Init(mWidth, mHeight, mObserver); mObserver->OnStartContainer(nsnull, mImage); nsresult rv = mFrame->Init(0, 0, mWidth, mHeight, GFXFORMAT, 24); if (NS_FAILED(rv)) return rv; if (mIsCursor) { nsCOMPtr props(do_QueryInterface(mImage)); if (props) { nsCOMPtr intwrapx = do_CreateInstance("@mozilla.org/supports-PRUint32;1"); nsCOMPtr intwrapy = do_CreateInstance("@mozilla.org/supports-PRUint32;1"); if (intwrapx && intwrapy) { intwrapx->SetData(mXHotspot); intwrapy->SetData(mYHotspot); props->Set("hotspotX", intwrapx); props->Set("hotspotY", intwrapy); } } } mImage->AppendFrame(mFrame); mObserver->OnStartFrame(nsnull, mFrame); PRUint32 bpr; mFrame->GetImageBytesPerRow(&bpr); PRUint32 abpr; mFrame->GetAlphaBytesPerRow(&abpr); mAlphaRow = (PRUint8*)malloc(abpr); if (!mAlphaRow) { mState = RECV_DONE; return NS_ERROR_OUT_OF_MEMORY; } memset(mAlphaRow, 0, abpr); mState = RECV_SEEK; mCurRow = 0; mCurCol = 0; } if (mState == RECV_SEEK) { if ((endPtr = strchr(mPos, '{')) != NULL) { mPos = endPtr+1; mState = RECV_DATA; } else { mPos = mBuf + mBufSize; return NS_OK; } } if (mState == RECV_DATA) { PRUint32 bpr; mFrame->GetImageBytesPerRow(&bpr); PRUint32 abpr; mFrame->GetAlphaBytesPerRow(&abpr); PRBool hiByte = PR_TRUE; do { PRUint32 pixel = strtoul(mPos, &endPtr, 0); if (endPtr == mPos) return NS_OK; // no number to be found - need more data if (!*endPtr) return NS_OK; // number at the end - might be missing a digit if (pixel == 0 && *endPtr == 'x') return NS_OK; // 0x at the end, actual number is missing while (*endPtr && isspace(*endPtr)) endPtr++; // skip whitespace looking for comma if (!*endPtr) { // Need more data return NS_OK; } else if (*endPtr != ',') { *endPtr = '\0'; mState = RECV_DONE; // strange character (or ending '}') } if (!mIsX10 || !hiByte) mPos = endPtr; // go to next value only when done with this one if (mIsX10) { // handle X10 flavor short values if (hiByte) pixel >>= 8; hiByte = !hiByte; } mAlphaRow[mCurCol/8] = 0; for (int i = 0; i < 8; i++) { PRUint8 val = (pixel & (1 << i)) >> i; mAlphaRow[mCurCol/8] |= val << (7 - i); } mCurCol = PR_MIN(mCurCol + 8, mWidth); if (mCurCol == mWidth || mState == RECV_DONE) { // Row finished. Set Data. mFrame->SetAlphaData(mAlphaRow, abpr, mCurRow * abpr); // nsnull gets interpreted as all-zeroes, which is what we // want mFrame->SetImageData(nsnull, bpr, mCurRow * bpr); nsIntRect r(0, mCurRow, mWidth, 1); mObserver->OnDataAvailable(nsnull, mFrame, &r); if ((mCurRow + 1) == mHeight) { mState = RECV_DONE; return mObserver->OnStopFrame(nsnull, mFrame); } mCurRow++; mCurCol = 0; } // Skip the comma NS_ASSERTION(mState != RECV_DATA || *mPos == ',' || (mIsX10 && hiByte), "Must be a comma"); if (*mPos == ',') mPos++; } while ((mState == RECV_DATA) && *mPos); } return NS_OK; }