diff --git a/modules/libpr0n/decoders/ppm/nsPPMDecoder.cpp b/modules/libpr0n/decoders/ppm/nsPPMDecoder.cpp index 9133d2cad43d..844b8941293a 100644 --- a/modules/libpr0n/decoders/ppm/nsPPMDecoder.cpp +++ b/modules/libpr0n/decoders/ppm/nsPPMDecoder.cpp @@ -22,6 +22,16 @@ * */ +/* + * Current limitations: + * + * Doesn't handle PGM and PPM files for which the maximum sample value + * is something other than 255. + * + */ + +#include + #include "nsPPMDecoder.h" #include "nsIInputStream.h" @@ -29,6 +39,7 @@ #include "imgIContainerObserver.h" #include "nspr.h" +#include "plstr.h" #include "nsIComponentManager.h" @@ -36,20 +47,66 @@ NS_IMPL_ISUPPORTS1(nsPPMDecoder, imgIDecoder) +/* + * "F_" stands for "find", so F_TYPE means we're looking for the image type + * digit. + */ +enum ParseState { + F_P, + F_TYPE, + F_WIDTH, + F_HEIGHT, + F_MAXVAL, + F_INITIALIZE, + F_TEXTBITDATA, + F_TEXTDATA, + F_RAWDATA +}; + +/* + * What kind of white space do we skip. + */ +enum Skip { + NOTHING, + WHITESPACE, + SINGLEWHITESPACE, + TOENDOFLINE +}; + +/* + * We look up the digit after the initial 'P' in this string to check + * whether it's valid; the index is then used with the following + * constants. + */ +static char ppmTypes[] = "1231456"; + +enum { + TYPE_PBM = 0, + TYPE_PGM = 1, + TYPE_PPM = 2, + PPMTYPE = 0x3, + PPMRAW = 0x4 +}; nsPPMDecoder::nsPPMDecoder() { NS_INIT_ISUPPORTS(); - mDataReceived = 0; - mDataWritten = 0; - mDataLeft = 0; - mPrevData = nsnull; + mBuffer = nsnull; + mBufferSize = 0; + + mState = F_P; + mSkip = NOTHING; + mOldSkip = NOTHING; + mType = 0; } nsPPMDecoder::~nsPPMDecoder() { - + if (mBuffer != nsnull) + PR_Free(mBuffer); + if (mRowData != nsnull) + PR_Free(mRowData); } @@ -90,172 +147,244 @@ NS_IMETHODIMP nsPPMDecoder::Flush() return NS_ERROR_NOT_IMPLEMENTED; } -static char *__itoa(int n) -{ - char *s; - int i, j, sign, tmp; - - /* check sign and convert to positive to stringify numbers */ - if ( (sign = n) < 0) - n = -n; - i = 0; - s = (char*) malloc(sizeof(char)); - - /* grow string as needed to add numbers from powers of 10 - * down till none left - */ - do - { - s = (char*) realloc(s, (i+1)*sizeof(char)); - s[i++] = n % 10 + '0'; /* '0' or 30 is where ASCII numbers start */ - s[i] = '\0'; - } - while( (n /= 10) > 0); - - /* tack on minus sign if we found earlier that this was negative */ - if (sign < 0) - { - s = (char*) realloc(s, (i+1)*sizeof(char)); - s[i++] = '-'; - } - s[i] = '\0'; - - /* pop numbers (and sign) off of string to push back into right direction */ - for (i = 0, j = strlen(s) - 1; i < j; i++, j--) - { - tmp = s[i]; - s[i] = s[j]; - s[j] = tmp; - } - - return s; -} - - /* unsigned long writeFrom (in nsIInputStream inStr, in unsigned long count); */ NS_IMETHODIMP nsPPMDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count, PRUint32 *_retval) { nsresult rv; - char *buf = (char *)PR_Malloc(count + mDataLeft); - if (!buf) + if (mBuffer == nsnull) { + mBuffer = (char *)PR_Malloc(count); + mBufferSize = count; + } else if (mBuffer != nsnull && mBufferSize != count) { + PR_Free(mBuffer); + mBuffer = (char *)PR_Malloc(count); + mBufferSize = count; + } + if (!mBuffer) return NS_ERROR_OUT_OF_MEMORY; /* we couldn't allocate the object */ - - // read the data from the input stram... + // read the data from the input stream... PRUint32 readLen; - rv = inStr->Read(buf+mDataLeft, count, &readLen); - - PRUint32 dataLen = readLen + mDataLeft; - - if (mPrevData) { - strncpy(buf, mPrevData, mDataLeft); - PR_Free(mPrevData); - mPrevData = nsnull; - mDataLeft = 0; - } - - char *data = buf; - + rv = inStr->Read(mBuffer, count, &readLen); if (NS_FAILED(rv)) return rv; - if (mDataReceived == 0) { - + if (mState == F_P && mObserver) mObserver->OnStartDecode(nsnull, nsnull); - // Check the magic number - char type; - if ((sscanf(data, "P%c\n", &type) !=1) || (type != '6')) { - return NS_ERROR_FAILURE; + char *p = mBuffer; + char *bufferEnd = mBuffer+readLen; + char *s; + int n; + + while (p < bufferEnd) { + if (mSkip == WHITESPACE) { + while (p < bufferEnd && *p != '#' + && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')) + p++; + if (p == bufferEnd) + break; + if (*p == '#') { + mOldSkip = WHITESPACE; + mSkip = TOENDOFLINE; + continue; + } + mSkip = NOTHING; + } else if (mSkip == SINGLEWHITESPACE) { + if (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\r') + return NS_ERROR_FAILURE; + p++; + mSkip = NOTHING; + } else if (mSkip == TOENDOFLINE) { + while (p < bufferEnd && *p != '\n') + p++; + if (p == bufferEnd) + break; + mSkip = mOldSkip; + mOldSkip = NOTHING; + if (mSkip != NOTHING) + continue; } - int i = 3; - data += i; - -#if 0 - // XXX - // Ignore comments - while ((input = fgetc(f)) == '#') - fgets(junk, 512, f); - ungetc(input, f); -#endif - - // Read size - int w, h, mcv; - - if (sscanf(data, "%d %d\n%d\n", &w, &h, &mcv) != 3) { - return NS_ERROR_FAILURE; - } - char *ws = __itoa(w), *hs = __itoa(h), *mcvs = __itoa(mcv); - int j = strlen(ws) + strlen(hs) + strlen(mcvs) + 3; - data += j; -// free(ws); -// free(hs); -// free(mcvs); - - readLen -= i + j; - dataLen = readLen; // since this is the first pass, we don't have any data waiting that we need to keep track of - - mImage->Init(w, h, mObserver); - if (mObserver) - mObserver->OnStartContainer(nsnull, nsnull, mImage); - - rv = mFrame->Init(0, 0, w, h, gfxIFormats::RGB); - if (NS_FAILED(rv)) - return rv; - - mImage->AppendFrame(mFrame); - if (mObserver) - mObserver->OnStartFrame(nsnull, nsnull, mFrame); - } - - PRUint32 bpr; - nscoord width; - mFrame->GetImageBytesPerRow(&bpr); - mFrame->GetWidth(&width); - - // XXX ceil? - PRUint32 real_bpr = width * 3; - - PRUint32 i = 0; - PRUint32 rownum = mDataWritten / real_bpr; // XXX this better not have a decimal - - PRUint32 wroteLen = 0; - - if (readLen > real_bpr) { - - do { - PRUint8 *line = (PRUint8*)data + i*real_bpr; - mFrame->SetImageData(line, real_bpr, (rownum++)*bpr); - - nsRect r(0, rownum, width, 1); - mObserver->OnDataAvailable(nsnull, nsnull, mFrame, &r); - - - wroteLen += real_bpr ; - i++; - } while(dataLen >= real_bpr * (i+1)); - - } - - mDataReceived += readLen; // don't double count previous data that is in 'dataLen' - mDataWritten += wroteLen; - - PRUint32 dataLeft = dataLen - wroteLen; - - if (dataLeft > 0) { - if (mPrevData) { - mPrevData = (char *)PR_Realloc(mPrevData, mDataLeft + dataLeft); - strncpy(mPrevData + mDataLeft, data+wroteLen, dataLeft); - mDataLeft += dataLeft; - - } else { - mDataLeft = dataLeft; - mPrevData = (char *)PR_Malloc(mDataLeft); - strncpy(mPrevData, data+wroteLen, mDataLeft); + switch (mState) { + case F_P: + if (*p != 'P') + return NS_ERROR_FAILURE; + p++; + mState = F_TYPE; + break; + case F_TYPE: + if ((s = PL_strchr(ppmTypes, *p)) == NULL) + return NS_ERROR_FAILURE; + mType = s-ppmTypes; + p++; + mState = F_WIDTH; + mDigitCount = 0; + mSkip = WHITESPACE; + break; + case F_WIDTH: + case F_HEIGHT: + case F_MAXVAL: + while (mDigitCount < sizeof(mDigits) && p < bufferEnd && isdigit(*p)) + mDigits[mDigitCount++] = *p++; + if (mDigitCount == sizeof(mDigits)) + return NS_ERROR_FAILURE; // number too big + if (p == bufferEnd) + break; + if (*p == '#') { + mOldSkip = NOTHING; + mSkip = TOENDOFLINE; + break; + } + mDigits[mDigitCount] = 0; + n = strtol(mDigits, (char **)NULL, 10); + if (mState == F_WIDTH) { + mWidth = n; + mDigitCount = 0; + mState = F_HEIGHT; + mSkip = WHITESPACE; + } else if (mState == F_HEIGHT) { + mHeight = n; + mDigitCount = 0; + if ((mType & PPMTYPE) != TYPE_PBM) { + mState = F_MAXVAL; + mSkip = WHITESPACE; + } else + mState = F_INITIALIZE; + } else if (mState == F_MAXVAL) { + mMaxValue = n; + mDigitCount = 0; + mState = F_INITIALIZE; + } + break; + case F_INITIALIZE: + mImage->Init(mWidth, mHeight, mObserver); + if (mObserver) + mObserver->OnStartContainer(nsnull, nsnull, mImage); + mFrame->Init(0, 0, mWidth, mHeight, gfxIFormats::RGB); + mImage->AppendFrame(mFrame); + if (mObserver) + mObserver->OnStartFrame(nsnull, nsnull, mFrame); + mRow = 0; + mBytesPerRow = 3*mWidth; + mFrame->GetImageBytesPerRow(&mFrameBytesPerRow); + mRowData = (PRUint8 *)PR_Malloc(mFrameBytesPerRow); + mRowDataFill = 0; + if (mType & PPMRAW) { + mState = F_RAWDATA; + mSkip = SINGLEWHITESPACE; + } else if ((mType & PPMTYPE) == TYPE_PBM) { + mState = F_TEXTBITDATA; + mSkip = WHITESPACE; + } else { + mState = F_TEXTDATA; + mSkip = WHITESPACE; + mDigitCount = 0; + } + break; + case F_TEXTBITDATA: + { + PRUint8 c = *p++; + if (c == '1') { + mRowData[mRowDataFill++] = 0; + mRowData[mRowDataFill++] = 0; + mRowData[mRowDataFill++] = 0; + } else { + mRowData[mRowDataFill++] = 255; + mRowData[mRowDataFill++] = 255; + mRowData[mRowDataFill++] = 255; + } + } + mSkip = WHITESPACE; + rv = checkSendRow(); + if (NS_FAILED(rv)) return rv; + break; + case F_TEXTDATA: + while (mDigitCount < sizeof(mDigits) && p < bufferEnd && isdigit(*p)) + mDigits[mDigitCount++] = *p++; + if (mDigitCount == sizeof(mDigits)) + return NS_ERROR_FAILURE; // number too big + if (p == bufferEnd) + break; + mDigits[mDigitCount] = 0; + n = strtol(mDigits, (char **)NULL, 10); + mDigitCount = 0; + switch (mType & PPMTYPE) { + case 1: + mRowData[mRowDataFill++] = n; + mRowData[mRowDataFill++] = n; + mRowData[mRowDataFill++] = n; + break; + case 2: + mRowData[mRowDataFill++] = n; + break; + } + mSkip = WHITESPACE; + rv = checkSendRow(); + if (NS_FAILED(rv)) return rv; + break; + case F_RAWDATA: + if (mType & PPMRAW) { + switch (mType & PPMTYPE) { + case TYPE_PBM: + { + PRUint32 c = *p++; + int i = 0; + while (mRowDataFill < mBytesPerRow && i < 8) { + if (c & 0x80) { + mRowData[mRowDataFill++] = 0; + mRowData[mRowDataFill++] = 0; + mRowData[mRowDataFill++] = 0; + } else { + mRowData[mRowDataFill++] = 255; + mRowData[mRowDataFill++] = 255; + mRowData[mRowDataFill++] = 255; + } + c <<= 1; + i++; + } + } + break; + case TYPE_PGM: + { + PRUint8 c = *p++; + mRowData[mRowDataFill++] = c; + mRowData[mRowDataFill++] = c; + mRowData[mRowDataFill++] = c; + } + break; + case TYPE_PPM: + if (mMaxValue == 255) { + PRUint32 bytesInBuffer = bufferEnd-p; + PRUint32 bytesNeeded = mBytesPerRow-mRowDataFill; + PRUint32 chunk = PR_MIN(bytesInBuffer, bytesNeeded); + memcpy(mRowData+mRowDataFill, p, chunk); + p += chunk; + mRowDataFill += chunk; + } else { + mRowData[mRowDataFill++] = *p++; + } + break; + } + rv = checkSendRow(); + if (NS_FAILED(rv)) return rv; + } + break; } } - - PR_FREEIF(buf); - + return NS_OK; +} + +NS_METHOD nsPPMDecoder::checkSendRow() +{ + nsresult rv; + + if (mRowDataFill == mBytesPerRow) { + rv = mFrame->SetImageData(mRowData, mFrameBytesPerRow, mRow*mFrameBytesPerRow); + if (NS_FAILED(rv)) return rv; + nsRect r(0, mRow, mWidth, 1); + if (mObserver) + mObserver->OnDataAvailable(nsnull, nsnull, mFrame, &r); + mRow++; + mRowDataFill = 0; + } return NS_OK; } diff --git a/modules/libpr0n/decoders/ppm/nsPPMDecoder.h b/modules/libpr0n/decoders/ppm/nsPPMDecoder.h index 2c1b21cbb786..8fb31e81bc5a 100644 --- a/modules/libpr0n/decoders/ppm/nsPPMDecoder.h +++ b/modules/libpr0n/decoders/ppm/nsPPMDecoder.h @@ -51,16 +51,32 @@ public: virtual ~nsPPMDecoder(); private: + NS_METHOD checkSendRow(); + nsCOMPtr mImage; nsCOMPtr mFrame; nsCOMPtr mImageLoad; nsCOMPtr mObserver; // this is just qi'd from mRequest for speed - PRUint32 mDataReceived; - PRUint32 mDataWritten; + char *mBuffer; + PRUint32 mBufferSize; - PRUint32 mDataLeft; - char *mPrevData; + PRUint32 mState; + PRUint32 mSkip; + PRUint32 mOldSkip; + char mDigits[5]; + PRUint32 mDigitCount; + + PRUint32 mType; + nscoord mWidth; + nscoord mHeight; + PRUint32 mMaxValue; + + PRUint32 mRow; + PRUint32 mBytesPerRow; + PRUint32 mFrameBytesPerRow; + PRUint8 *mRowData; + PRUint32 mRowDataFill; }; #endif // nsPPMDecoder_h__ diff --git a/modules/libpr0n/decoders/ppm/nsPPMFactory.cpp b/modules/libpr0n/decoders/ppm/nsPPMFactory.cpp index a72ecb66b15f..563fa5693389 100644 --- a/modules/libpr0n/decoders/ppm/nsPPMFactory.cpp +++ b/modules/libpr0n/decoders/ppm/nsPPMFactory.cpp @@ -30,8 +30,16 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsPPMDecoder) -static const nsModuleComponentInfo components[] = +static nsModuleComponentInfo components[] = { + { "pbm decoder", + NS_PPMDECODER_CID, + "@mozilla.org/image/decoder;2?type=image/x-portable-bitmap", + nsPPMDecoderConstructor, }, + { "pgm decoder", + NS_PPMDECODER_CID, + "@mozilla.org/image/decoder;2?type=image/x-portable-graymap", + nsPPMDecoderConstructor, }, { "ppm decoder", NS_PPMDECODER_CID, "@mozilla.org/image/decoder;2?type=image/x-portable-pixmap", diff --git a/modules/libpr0n/src/imgLoader.cpp b/modules/libpr0n/src/imgLoader.cpp index 7a04194fbad6..b518b5f49006 100644 --- a/modules/libpr0n/src/imgLoader.cpp +++ b/modules/libpr0n/src/imgLoader.cpp @@ -622,6 +622,23 @@ nsresult imgLoader::GetMimeTypeFromContent(const char* aContents, PRUint32 aLeng else if (aLength >= 8 && !nsCRT::strncmp(aContents, "#define ", 8)) { *aContentType = nsCRT::strndup("image/x-xbitmap", 15); } + /* PBM, PGM or PPM? */ + /* These start with the letter 'P' followed by a digit from 1 through 6 + * followed by a whitespace character. + */ + else if (aLength >= 3 && ((unsigned char)aContents[0]==0x50 && + ((unsigned char)aContents[2]==0x9 || (unsigned char)aContents[2]==0xa || + (unsigned char)aContents[2]==0xd || (unsigned char)aContents[2]==0x20))) + { + unsigned char c = (unsigned char)aContents[1]; + if (c == '1' || c == '4') { + *aContentType = nsCRT::strndup("image/x-portable-bitmap", 23); + } else if (c == '2' || c == '5') { + *aContentType = nsCRT::strndup("image/x-portable-graymap", 24); + } else if (c == '3' || c == '6') { + *aContentType = nsCRT::strndup("image/x-portable-pixmap", 23); + } + } else { /* none of the above? I give up */ /* don't raise an exception, simply return null */