mirror of
https://github.com/rn10950/RetroZilla.git
synced 2024-11-10 18:00:15 +01:00
310 lines
9.8 KiB
C++
310 lines
9.8 KiB
C++
/* 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 <cbiesinger@web.de>.
|
|
* Portions created by the Initial Developer are Copyright (C) 2001
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Aaron Kaluszka <ask@swva.net>
|
|
*
|
|
* 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 <string.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
|
|
#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<nsIProperties> props(do_QueryInterface(mImage));
|
|
if (props) {
|
|
nsCOMPtr<nsISupportsPRUint32> intwrapx = do_CreateInstance("@mozilla.org/supports-PRUint32;1");
|
|
nsCOMPtr<nsISupportsPRUint32> 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;
|
|
}
|
|
|
|
|