1 : /* vim:set tw=80 expandtab softtabstop=4 ts=4 sw=4: */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is the Mozilla BMP Decoder.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Christian Biesinger <cbiesinger@web.de>.
19 : * Portions created by the Initial Developer are Copyright (C) 2001
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Neil Rashbrook <neil@parkwaycc.co.uk>
24 : * Bobby Holley <bobbyholley@gmail.com>
25 : * Brian R. Bondy <netzen@gmail.com>
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either the GNU General Public License Version 2 or later (the "GPL"), or
29 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 : /* I got the format description from http://www.daubnet.com/formats/BMP.html */
41 :
42 : /* This is a Cross-Platform BMP Decoder, which should work everywhere, including
43 : * Big-Endian machines like the PowerPC. */
44 :
45 : #include <stdlib.h>
46 :
47 : #include "EndianMacros.h"
48 : #include "nsBMPDecoder.h"
49 :
50 : #include "nsIInputStream.h"
51 : #include "RasterImage.h"
52 : #include "imgIContainerObserver.h"
53 : #include "ImageLogging.h"
54 :
55 : namespace mozilla {
56 : namespace image {
57 :
58 : #ifdef PR_LOGGING
59 1464 : PRLogModuleInfo *gBMPLog = PR_NewLogModule("BMPDecoder");
60 : #endif
61 :
62 : // Convert from row (1..height) to absolute line (0..height-1)
63 : #define LINE(row) ((mBIH.height < 0) ? (-mBIH.height - (row)) : ((row) - 1))
64 : #define PIXEL_OFFSET(row, col) (LINE(row) * mBIH.width + col)
65 :
66 4 : nsBMPDecoder::nsBMPDecoder(RasterImage &aImage, imgIDecoderObserver* aObserver)
67 4 : : Decoder(aImage, aObserver)
68 : {
69 4 : mColors = nsnull;
70 4 : mRow = nsnull;
71 4 : mImageData = nsnull;
72 4 : mCurPos = mPos = mNumColors = mRowBytes = 0;
73 4 : mOldLine = mCurLine = 1; // Otherwise decoder will never start
74 4 : mState = eRLEStateInitial;
75 4 : mStateData = 0;
76 4 : mLOH = WIN_HEADER_LENGTH;
77 4 : mUseAlphaData = mHaveAlphaData = false;
78 4 : }
79 :
80 12 : nsBMPDecoder::~nsBMPDecoder()
81 : {
82 4 : delete[] mColors;
83 4 : if (mRow) {
84 4 : moz_free(mRow);
85 : }
86 16 : }
87 :
88 : // Sets whether or not the BMP will use alpha data
89 : void
90 4 : nsBMPDecoder::SetUseAlphaData(bool useAlphaData)
91 : {
92 4 : mUseAlphaData = useAlphaData;
93 4 : }
94 :
95 : // Obtains the bits per pixel from the internal BIH header
96 : PRInt32
97 8 : nsBMPDecoder::GetBitsPerPixel() const
98 : {
99 8 : return mBIH.bpp;
100 : }
101 :
102 : // Obtains the width from the internal BIH header
103 : PRInt32
104 2 : nsBMPDecoder::GetWidth() const
105 : {
106 2 : return mBIH.width;
107 : }
108 :
109 : // Obtains the height from the internal BIH header
110 : PRInt32
111 1 : nsBMPDecoder::GetHeight() const
112 : {
113 1 : return mBIH.height;
114 : }
115 :
116 : // Obtains the internal output image buffer
117 : PRUint32*
118 48 : nsBMPDecoder::GetImageData()
119 : {
120 48 : return mImageData;
121 : }
122 :
123 : // Obtains the size of the compressed image resource
124 : PRInt32
125 6 : nsBMPDecoder::GetCompressedImageSize() const
126 : {
127 : // For everything except BI_RGB the header field must be defined
128 6 : if (mBIH.compression != BI_RGB) {
129 0 : return mBIH.image_size;
130 : }
131 :
132 : // mBIH.image_size isn't always filled for BI_RGB so calculate it manually
133 : // The pixel array size is calculated based on extra 4 byte boundary padding
134 6 : PRUint32 rowSize = (mBIH.bpp * mBIH.width + 7) / 8; // + 7 to round up
135 : // Pad to DWORD Boundary
136 6 : if (rowSize % 4) {
137 0 : rowSize += (4 - (rowSize % 4));
138 : }
139 :
140 : // The height should be the absolute value of what the height is in the BIH.
141 : // If positive the bitmap is stored bottom to top, otherwise top to bottom
142 6 : PRInt32 pixelArraySize = rowSize * abs(mBIH.height);
143 6 : return pixelArraySize;
144 : }
145 :
146 : // Obtains whether or not a BMP file had alpha data in its 4th byte
147 : // for 32BPP bitmaps. Only use after the bitmap has been processed.
148 : bool
149 1 : nsBMPDecoder::HasAlphaData() const
150 : {
151 1 : return mHaveAlphaData;
152 : }
153 :
154 :
155 : void
156 4 : nsBMPDecoder::FinishInternal()
157 : {
158 : // We shouldn't be called in error cases
159 4 : NS_ABORT_IF_FALSE(!HasError(), "Can't call FinishInternal on error!");
160 :
161 : // We should never make multiple frames
162 4 : NS_ABORT_IF_FALSE(GetFrameCount() <= 1, "Multiple BMP frames?");
163 :
164 : // Send notifications if appropriate
165 4 : if (!IsSizeDecode() && (GetFrameCount() == 1)) {
166 :
167 : // Invalidate
168 4 : nsIntRect r(0, 0, mBIH.width, mBIH.height);
169 4 : PostInvalidation(r);
170 :
171 4 : PostFrameStop();
172 4 : PostDecodeDone();
173 : }
174 4 : }
175 :
176 : // ----------------------------------------
177 : // Actual Data Processing
178 : // ----------------------------------------
179 :
180 0 : static void calcBitmask(PRUint32 aMask, PRUint8& aBegin, PRUint8& aLength)
181 : {
182 : // find the rightmost 1
183 : PRUint8 pos;
184 0 : bool started = false;
185 0 : aBegin = aLength = 0;
186 0 : for (pos = 0; pos <= 31; pos++) {
187 0 : if (!started && (aMask & (1 << pos))) {
188 0 : aBegin = pos;
189 0 : started = true;
190 : }
191 0 : else if (started && !(aMask & (1 << pos))) {
192 0 : aLength = pos - aBegin;
193 0 : break;
194 : }
195 : }
196 0 : }
197 :
198 0 : NS_METHOD nsBMPDecoder::CalcBitShift()
199 : {
200 : PRUint8 begin, length;
201 : // red
202 0 : calcBitmask(mBitFields.red, begin, length);
203 0 : mBitFields.redRightShift = begin;
204 0 : mBitFields.redLeftShift = 8 - length;
205 : // green
206 0 : calcBitmask(mBitFields.green, begin, length);
207 0 : mBitFields.greenRightShift = begin;
208 0 : mBitFields.greenLeftShift = 8 - length;
209 : // blue
210 0 : calcBitmask(mBitFields.blue, begin, length);
211 0 : mBitFields.blueRightShift = begin;
212 0 : mBitFields.blueLeftShift = 8 - length;
213 0 : return NS_OK;
214 : }
215 :
216 : void
217 14 : nsBMPDecoder::WriteInternal(const char* aBuffer, PRUint32 aCount)
218 : {
219 14 : NS_ABORT_IF_FALSE(!HasError(), "Shouldn't call WriteInternal after error!");
220 :
221 : // aCount=0 means EOF, mCurLine=0 means we're past end of image
222 14 : if (!aCount || !mCurLine)
223 0 : return;
224 :
225 : nsresult rv;
226 14 : if (mPos < BFH_INTERNAL_LENGTH) { /* In BITMAPFILEHEADER */
227 8 : PRUint32 toCopy = BFH_INTERNAL_LENGTH - mPos;
228 8 : if (toCopy > aCount)
229 4 : toCopy = aCount;
230 8 : memcpy(mRawBuf + mPos, aBuffer, toCopy);
231 8 : mPos += toCopy;
232 8 : aCount -= toCopy;
233 8 : aBuffer += toCopy;
234 : }
235 14 : if (mPos == BFH_INTERNAL_LENGTH) {
236 4 : ProcessFileHeader();
237 4 : if (mBFH.signature[0] != 'B' || mBFH.signature[1] != 'M') {
238 0 : PostDataError();
239 0 : return;
240 : }
241 4 : if (mBFH.bihsize == OS2_BIH_LENGTH)
242 0 : mLOH = OS2_HEADER_LENGTH;
243 : }
244 14 : if (mPos >= BFH_INTERNAL_LENGTH && mPos < mLOH) { /* In BITMAPINFOHEADER */
245 4 : PRUint32 toCopy = mLOH - mPos;
246 4 : if (toCopy > aCount)
247 0 : toCopy = aCount;
248 4 : memcpy(mRawBuf + (mPos - BFH_INTERNAL_LENGTH), aBuffer, toCopy);
249 4 : mPos += toCopy;
250 4 : aCount -= toCopy;
251 4 : aBuffer += toCopy;
252 : }
253 :
254 : // GetNumFrames is called to ensure that if at this point mPos == mLOH but
255 : // we have no data left to process, the next time WriteInternal is called
256 : // we won't enter this condition again.
257 14 : if (mPos == mLOH && GetFrameCount() == 0) {
258 4 : ProcessInfoHeader();
259 4 : PR_LOG(gBMPLog, PR_LOG_DEBUG, ("BMP is %lix%lix%lu. compression=%lu\n",
260 : mBIH.width, mBIH.height, mBIH.bpp, mBIH.compression));
261 : // Verify we support this bit depth
262 4 : if (mBIH.bpp != 1 && mBIH.bpp != 4 && mBIH.bpp != 8 &&
263 : mBIH.bpp != 16 && mBIH.bpp != 24 && mBIH.bpp != 32) {
264 0 : PostDataError();
265 0 : return;
266 : }
267 :
268 : // BMPs with negative width are invalid
269 : // Reject extremely wide images to keep the math sane
270 4 : const PRInt32 k64KWidth = 0x0000FFFF;
271 4 : if (mBIH.width < 0 || mBIH.width > k64KWidth) {
272 0 : PostDataError();
273 0 : return;
274 : }
275 :
276 4 : PRUint32 real_height = (mBIH.height > 0) ? mBIH.height : -mBIH.height;
277 :
278 : // Post our size to the superclass
279 4 : PostSize(mBIH.width, real_height);
280 4 : if (HasError()) {
281 : // Setting the size lead to an error; this can happen when for example
282 : // a multipart channel sends an image of a different size.
283 0 : return;
284 : }
285 :
286 : // We have the size. If we're doing a size decode, we got what
287 : // we came for.
288 4 : if (IsSizeDecode())
289 0 : return;
290 :
291 : // We're doing a real decode.
292 4 : mOldLine = mCurLine = real_height;
293 :
294 4 : if (mBIH.bpp <= 8) {
295 3 : mNumColors = 1 << mBIH.bpp;
296 3 : if (mBIH.colors && mBIH.colors < mNumColors)
297 0 : mNumColors = mBIH.colors;
298 :
299 : // Always allocate 256 even though mNumColors might be smaller
300 3 : mColors = new colorTable[256];
301 3 : memset(mColors, 0, 256 * sizeof(colorTable));
302 : }
303 1 : else if (mBIH.compression != BI_BITFIELDS && mBIH.bpp == 16) {
304 : // Use default 5-5-5 format
305 0 : mBitFields.red = 0x7C00;
306 0 : mBitFields.green = 0x03E0;
307 0 : mBitFields.blue = 0x001F;
308 0 : CalcBitShift();
309 : }
310 :
311 : // Make sure we have a valid value for our supported compression modes
312 : // before adding the frame
313 4 : if (mBIH.compression != BI_RGB && mBIH.compression != BI_RLE8 &&
314 : mBIH.compression != BI_RLE4 && mBIH.compression != BI_BITFIELDS) {
315 0 : PostDataError();
316 0 : return;
317 : }
318 :
319 : // If we have RLE4 or RLE8 or BI_ALPHABITFIELDS, then ensure we
320 : // have valid BPP values before adding the frame
321 4 : if (mBIH.compression == BI_RLE8 && mBIH.bpp != 8) {
322 0 : PR_LOG(gBMPLog, PR_LOG_DEBUG,
323 : ("BMP RLE8 compression only supports 8 bits per pixel\n"));
324 0 : PostDataError();
325 0 : return;
326 : }
327 4 : if (mBIH.compression == BI_RLE4 && mBIH.bpp != 4 && mBIH.bpp != 1) {
328 0 : PR_LOG(gBMPLog, PR_LOG_DEBUG,
329 : ("BMP RLE4 compression only supports 4 bits per pixel\n"));
330 0 : PostDataError();
331 0 : return;
332 : }
333 4 : if (mBIH.compression == BI_ALPHABITFIELDS &&
334 : mBIH.bpp != 16 && mBIH.bpp != 32) {
335 0 : PR_LOG(gBMPLog, PR_LOG_DEBUG,
336 : ("BMP ALPHABITFIELDS only supports 16 or 32 bits per pixel\n"));
337 0 : PostDataError();
338 0 : return;
339 : }
340 :
341 : PRUint32 imageLength;
342 4 : if (mBIH.compression == BI_RLE8 || mBIH.compression == BI_RLE4 ||
343 : mBIH.compression == BI_ALPHABITFIELDS) {
344 : rv = mImage.EnsureFrame(0, 0, 0, mBIH.width, real_height,
345 : gfxASurface::ImageFormatARGB32,
346 0 : (PRUint8**)&mImageData, &imageLength);
347 : } else {
348 : // mRow is not used for RLE encoded images
349 4 : mRow = (PRUint8*)moz_malloc((mBIH.width * mBIH.bpp) / 8 + 4);
350 : // + 4 because the line is padded to a 4 bit boundary, but I don't want
351 : // to make exact calculations here, that's unnecessary.
352 : // Also, it compensates rounding error.
353 4 : if (!mRow) {
354 0 : PostDecoderError(NS_ERROR_OUT_OF_MEMORY);
355 0 : return;
356 : }
357 :
358 4 : if (mUseAlphaData) {
359 : rv = mImage.EnsureFrame(0, 0, 0, mBIH.width, real_height,
360 : gfxASurface::ImageFormatARGB32,
361 4 : (PRUint8**)&mImageData, &imageLength);
362 : } else {
363 : rv = mImage.EnsureFrame(0, 0, 0, mBIH.width, real_height,
364 : gfxASurface::ImageFormatRGB24,
365 0 : (PRUint8**)&mImageData, &imageLength);
366 : }
367 : }
368 4 : if (NS_FAILED(rv) || !mImageData) {
369 0 : PostDecoderError(NS_ERROR_FAILURE);
370 0 : return;
371 : }
372 :
373 : // Prepare for transparency
374 4 : if ((mBIH.compression == BI_RLE8) || (mBIH.compression == BI_RLE4)) {
375 : // Clear the image, as the RLE may jump over areas
376 0 : memset(mImageData, 0, imageLength);
377 : }
378 :
379 : // Tell the superclass we're starting a frame
380 4 : PostFrameStart();
381 : }
382 :
383 14 : if (mColors && mPos >= mLOH) {
384 : // OS/2 Bitmaps have no padding byte
385 8 : PRUint8 bytesPerColor = (mBFH.bihsize == OS2_BIH_LENGTH) ? 3 : 4;
386 8 : if (mPos < (mLOH + mNumColors * bytesPerColor)) {
387 : // Number of bytes already received
388 8 : PRUint32 colorBytes = mPos - mLOH;
389 : // Color which is currently received
390 8 : PRUint8 colorNum = colorBytes / bytesPerColor;
391 8 : PRUint8 at = colorBytes % bytesPerColor;
392 3088 : while (aCount && (mPos < (mLOH + mNumColors * bytesPerColor))) {
393 3072 : switch (at) {
394 : case 0:
395 768 : mColors[colorNum].blue = *aBuffer;
396 768 : break;
397 : case 1:
398 768 : mColors[colorNum].green = *aBuffer;
399 768 : break;
400 : case 2:
401 768 : mColors[colorNum].red = *aBuffer;
402 : // If there is no padding byte, increment the color index
403 : // since we're done with the current color.
404 768 : if (bytesPerColor == 3)
405 0 : colorNum++;
406 768 : break;
407 : case 3:
408 : // This is a padding byte only in Windows BMPs. Increment
409 : // the color index since we're done with the current color.
410 768 : colorNum++;
411 768 : break;
412 : }
413 3072 : mPos++; aBuffer++; aCount--;
414 3072 : at = (at + 1) % bytesPerColor;
415 : }
416 8 : }
417 : }
418 6 : else if (aCount && mBIH.compression == BI_BITFIELDS && mPos < (WIN_HEADER_LENGTH + BITFIELD_LENGTH)) {
419 : // If compression is used, this is a windows bitmap, hence we can
420 : // use WIN_HEADER_LENGTH instead of mLOH
421 0 : PRUint32 toCopy = (WIN_HEADER_LENGTH + BITFIELD_LENGTH) - mPos;
422 0 : if (toCopy > aCount)
423 0 : toCopy = aCount;
424 0 : memcpy(mRawBuf + (mPos - WIN_HEADER_LENGTH), aBuffer, toCopy);
425 0 : mPos += toCopy;
426 0 : aBuffer += toCopy;
427 0 : aCount -= toCopy;
428 : }
429 14 : if (mPos == WIN_HEADER_LENGTH + BITFIELD_LENGTH &&
430 : mBIH.compression == BI_BITFIELDS) {
431 0 : mBitFields.red = LITTLE_TO_NATIVE32(*(PRUint32*)mRawBuf);
432 0 : mBitFields.green = LITTLE_TO_NATIVE32(*(PRUint32*)(mRawBuf + 4));
433 0 : mBitFields.blue = LITTLE_TO_NATIVE32(*(PRUint32*)(mRawBuf + 8));
434 0 : CalcBitShift();
435 : }
436 28 : while (aCount && (mPos < mBFH.dataoffset)) { // Skip whatever is between header and data
437 0 : mPos++; aBuffer++; aCount--;
438 : }
439 14 : if (aCount && ++mPos >= mBFH.dataoffset) {
440 : // Need to increment mPos, else we might get to mPos==mLOH again
441 : // From now on, mPos is irrelevant
442 4 : if (!mBIH.compression || mBIH.compression == BI_BITFIELDS) {
443 4 : PRUint32 rowSize = (mBIH.bpp * mBIH.width + 7) / 8; // + 7 to round up
444 4 : if (rowSize % 4) {
445 0 : rowSize += (4 - (rowSize % 4)); // Pad to DWORD Boundary
446 : }
447 : PRUint32 toCopy;
448 60 : do {
449 64 : toCopy = rowSize - mRowBytes;
450 64 : if (toCopy) {
451 64 : if (toCopy > aCount)
452 0 : toCopy = aCount;
453 64 : memcpy(mRow + mRowBytes, aBuffer, toCopy);
454 64 : aCount -= toCopy;
455 64 : aBuffer += toCopy;
456 64 : mRowBytes += toCopy;
457 : }
458 64 : if (rowSize == mRowBytes) {
459 : // Collected a whole row into mRow, process it
460 64 : PRUint8* p = mRow;
461 64 : PRUint32* d = mImageData + PIXEL_OFFSET(mCurLine, 0);
462 64 : PRUint32 lpos = mBIH.width;
463 64 : switch (mBIH.bpp) {
464 : case 1:
465 0 : while (lpos > 0) {
466 : PRInt8 bit;
467 : PRUint8 idx;
468 0 : for (bit = 7; bit >= 0 && lpos > 0; bit--) {
469 0 : idx = (*p >> bit) & 1;
470 0 : SetPixel(d, idx, mColors);
471 0 : --lpos;
472 : }
473 0 : ++p;
474 : }
475 0 : break;
476 : case 4:
477 0 : while (lpos > 0) {
478 0 : Set4BitPixel(d, *p, lpos, mColors);
479 0 : ++p;
480 : }
481 0 : break;
482 : case 8:
483 864 : while (lpos > 0) {
484 768 : SetPixel(d, *p, mColors);
485 768 : --lpos;
486 768 : ++p;
487 : }
488 48 : break;
489 : case 16:
490 0 : while (lpos > 0) {
491 0 : PRUint16 val = LITTLE_TO_NATIVE16(*(PRUint16*)p);
492 : SetPixel(d,
493 : (val & mBitFields.red) >> mBitFields.redRightShift << mBitFields.redLeftShift,
494 : (val & mBitFields.green) >> mBitFields.greenRightShift << mBitFields.greenLeftShift,
495 0 : (val & mBitFields.blue) >> mBitFields.blueRightShift << mBitFields.blueLeftShift);
496 0 : --lpos;
497 0 : p+=2;
498 : }
499 0 : break;
500 : case 24:
501 0 : while (lpos > 0) {
502 0 : SetPixel(d, p[2], p[1], p[0]);
503 0 : p += 2;
504 0 : --lpos;
505 0 : ++p;
506 : }
507 0 : break;
508 : case 32:
509 288 : while (lpos > 0) {
510 256 : if (mUseAlphaData) {
511 256 : if (!mHaveAlphaData && p[3]) {
512 : // Non-zero alpha byte detected! Clear previous
513 : // pixels that we have already processed.
514 : // This works because we know that if we
515 : // are reaching here then the alpha data in byte
516 : // 4 has been right all along. And we know it
517 : // has been set to 0 the whole time, so that
518 : // means that everything is transparent so far.
519 2 : memset(mImageData + (mCurLine - 1) * GetWidth(), 0,
520 1 : (GetHeight() - mCurLine + 1) *
521 3 : GetWidth() * sizeof(PRUint32));
522 1 : mHaveAlphaData = true;
523 : }
524 256 : SetPixel(d, p[2], p[1], p[0], mHaveAlphaData ? p[3] : 0xFF);
525 : } else {
526 0 : SetPixel(d, p[2], p[1], p[0]);
527 : }
528 256 : p += 4;
529 256 : --lpos;
530 : }
531 16 : break;
532 : default:
533 0 : NS_NOTREACHED("Unsupported color depth, but earlier check didn't catch it");
534 : }
535 64 : mCurLine --;
536 64 : if (mCurLine == 0) { // Finished last line
537 4 : break;
538 : }
539 60 : mRowBytes = 0;
540 :
541 : }
542 4 : } while (aCount > 0);
543 : }
544 0 : else if ((mBIH.compression == BI_RLE8) || (mBIH.compression == BI_RLE4)) {
545 0 : if (((mBIH.compression == BI_RLE8) && (mBIH.bpp != 8)) ||
546 : ((mBIH.compression == BI_RLE4) && (mBIH.bpp != 4) && (mBIH.bpp != 1))) {
547 0 : PR_LOG(gBMPLog, PR_LOG_DEBUG, ("BMP RLE8/RLE4 compression only supports 8/4 bits per pixel\n"));
548 0 : PostDataError();
549 0 : return;
550 : }
551 :
552 0 : while (aCount > 0) {
553 : PRUint8 byte;
554 :
555 0 : switch(mState) {
556 : case eRLEStateInitial:
557 0 : mStateData = (PRUint8)*aBuffer++;
558 0 : aCount--;
559 :
560 0 : mState = eRLEStateNeedSecondEscapeByte;
561 0 : continue;
562 :
563 : case eRLEStateNeedSecondEscapeByte:
564 0 : byte = *aBuffer++;
565 0 : aCount--;
566 0 : if (mStateData != RLE_ESCAPE) { // encoded mode
567 : // Encoded mode consists of two bytes:
568 : // the first byte (mStateData) specifies the
569 : // number of consecutive pixels to be drawn
570 : // using the color index contained in
571 : // the second byte
572 : // Work around bitmaps that specify too many pixels
573 0 : mState = eRLEStateInitial;
574 0 : PRUint32 pixelsNeeded = NS_MIN<PRUint32>(mBIH.width - mCurPos, mStateData);
575 0 : if (pixelsNeeded) {
576 0 : PRUint32* d = mImageData + PIXEL_OFFSET(mCurLine, mCurPos);
577 0 : mCurPos += pixelsNeeded;
578 0 : if (mBIH.compression == BI_RLE8) {
579 0 : do {
580 0 : SetPixel(d, byte, mColors);
581 0 : pixelsNeeded --;
582 : } while (pixelsNeeded);
583 : } else {
584 0 : do {
585 0 : Set4BitPixel(d, byte, pixelsNeeded, mColors);
586 : } while (pixelsNeeded);
587 : }
588 : }
589 0 : continue;
590 : }
591 :
592 0 : switch(byte) {
593 : case RLE_ESCAPE_EOL:
594 : // End of Line: Go to next row
595 0 : mCurLine --;
596 0 : mCurPos = 0;
597 0 : mState = eRLEStateInitial;
598 0 : break;
599 :
600 : case RLE_ESCAPE_EOF: // EndOfFile
601 0 : mCurPos = mCurLine = 0;
602 0 : break;
603 :
604 : case RLE_ESCAPE_DELTA:
605 0 : mState = eRLEStateNeedXDelta;
606 0 : continue;
607 :
608 : default : // absolute mode
609 : // Save the number of pixels to read
610 0 : mStateData = byte;
611 0 : if (mCurPos + mStateData > (PRUint32)mBIH.width) {
612 : // We can work around bitmaps that specify one
613 : // pixel too many, but only if their width is odd.
614 0 : mStateData -= mBIH.width & 1;
615 0 : if (mCurPos + mStateData > (PRUint32)mBIH.width) {
616 0 : PostDataError();
617 0 : return;
618 : }
619 : }
620 :
621 : // See if we will need to skip a byte
622 : // to word align the pixel data
623 : // mStateData is a number of pixels
624 : // so allow for the RLE compression type
625 : // Pixels RLE8=1 RLE4=2
626 : // 1 Pad Pad
627 : // 2 No Pad
628 : // 3 Pad No
629 : // 4 No No
630 0 : if (((mStateData - 1) & mBIH.compression) != 0)
631 0 : mState = eRLEStateAbsoluteMode;
632 : else
633 0 : mState = eRLEStateAbsoluteModePadded;
634 0 : continue;
635 : }
636 0 : break;
637 :
638 : case eRLEStateNeedXDelta:
639 : // Handle the XDelta and proceed to get Y Delta
640 0 : byte = *aBuffer++;
641 0 : aCount--;
642 0 : mCurPos += byte;
643 0 : if (mCurPos > mBIH.width)
644 0 : mCurPos = mBIH.width;
645 :
646 0 : mState = eRLEStateNeedYDelta;
647 0 : continue;
648 :
649 : case eRLEStateNeedYDelta:
650 : // Get the Y Delta and then "handle" the move
651 0 : byte = *aBuffer++;
652 0 : aCount--;
653 0 : mState = eRLEStateInitial;
654 0 : mCurLine -= NS_MIN<PRInt32>(byte, mCurLine);
655 0 : break;
656 :
657 : case eRLEStateAbsoluteMode: // Absolute Mode
658 : case eRLEStateAbsoluteModePadded:
659 0 : if (mStateData) {
660 : // In absolute mode, the second byte (mStateData)
661 : // represents the number of pixels
662 : // that follow, each of which contains
663 : // the color index of a single pixel.
664 0 : PRUint32* d = mImageData + PIXEL_OFFSET(mCurLine, mCurPos);
665 0 : PRUint32* oldPos = d;
666 0 : if (mBIH.compression == BI_RLE8) {
667 0 : while (aCount > 0 && mStateData > 0) {
668 0 : byte = *aBuffer++;
669 0 : aCount--;
670 0 : SetPixel(d, byte, mColors);
671 0 : mStateData--;
672 : }
673 : } else {
674 0 : while (aCount > 0 && mStateData > 0) {
675 0 : byte = *aBuffer++;
676 0 : aCount--;
677 0 : Set4BitPixel(d, byte, mStateData, mColors);
678 : }
679 : }
680 0 : mCurPos += d - oldPos;
681 : }
682 :
683 0 : if (mStateData == 0) {
684 : // In absolute mode, each run must
685 : // be aligned on a word boundary
686 :
687 0 : if (mState == eRLEStateAbsoluteMode) { // Word Aligned
688 0 : mState = eRLEStateInitial;
689 0 : } else if (aCount > 0) { // Not word Aligned
690 : // "next" byte is just a padding byte
691 : // so "move" past it and we can continue
692 0 : aBuffer++;
693 0 : aCount--;
694 0 : mState = eRLEStateInitial;
695 : }
696 : }
697 : // else state is still eRLEStateAbsoluteMode
698 0 : continue;
699 :
700 : default :
701 0 : NS_ABORT_IF_FALSE(0, "BMP RLE decompression: unknown state!");
702 0 : PostDecoderError(NS_ERROR_UNEXPECTED);
703 0 : return;
704 : }
705 : // Because of the use of the continue statement
706 : // we only get here for eol, eof or y delta
707 0 : if (mCurLine == 0) { // Finished last line
708 0 : break;
709 : }
710 : }
711 : }
712 : }
713 :
714 14 : const PRUint32 rows = mOldLine - mCurLine;
715 14 : if (rows) {
716 :
717 : // Invalidate
718 : nsIntRect r(0, mBIH.height < 0 ? -mBIH.height - mOldLine : mCurLine,
719 4 : mBIH.width, rows);
720 4 : PostInvalidation(r);
721 :
722 4 : mOldLine = mCurLine;
723 : }
724 :
725 14 : return;
726 : }
727 :
728 4 : void nsBMPDecoder::ProcessFileHeader()
729 : {
730 4 : memset(&mBFH, 0, sizeof(mBFH));
731 4 : memcpy(&mBFH.signature, mRawBuf, sizeof(mBFH.signature));
732 4 : memcpy(&mBFH.filesize, mRawBuf + 2, sizeof(mBFH.filesize));
733 4 : memcpy(&mBFH.reserved, mRawBuf + 6, sizeof(mBFH.reserved));
734 4 : memcpy(&mBFH.dataoffset, mRawBuf + 10, sizeof(mBFH.dataoffset));
735 4 : memcpy(&mBFH.bihsize, mRawBuf + 14, sizeof(mBFH.bihsize));
736 :
737 : // Now correct the endianness of the header
738 4 : mBFH.filesize = LITTLE_TO_NATIVE32(mBFH.filesize);
739 4 : mBFH.dataoffset = LITTLE_TO_NATIVE32(mBFH.dataoffset);
740 4 : mBFH.bihsize = LITTLE_TO_NATIVE32(mBFH.bihsize);
741 4 : }
742 :
743 4 : void nsBMPDecoder::ProcessInfoHeader()
744 : {
745 4 : memset(&mBIH, 0, sizeof(mBIH));
746 4 : if (mBFH.bihsize == 12) { // OS/2 Bitmap
747 0 : memcpy(&mBIH.width, mRawBuf, 2);
748 0 : memcpy(&mBIH.height, mRawBuf + 2, 2);
749 0 : memcpy(&mBIH.planes, mRawBuf + 4, sizeof(mBIH.planes));
750 0 : memcpy(&mBIH.bpp, mRawBuf + 6, sizeof(mBIH.bpp));
751 : }
752 : else {
753 4 : memcpy(&mBIH.width, mRawBuf, sizeof(mBIH.width));
754 4 : memcpy(&mBIH.height, mRawBuf + 4, sizeof(mBIH.height));
755 4 : memcpy(&mBIH.planes, mRawBuf + 8, sizeof(mBIH.planes));
756 4 : memcpy(&mBIH.bpp, mRawBuf + 10, sizeof(mBIH.bpp));
757 4 : memcpy(&mBIH.compression, mRawBuf + 12, sizeof(mBIH.compression));
758 4 : memcpy(&mBIH.image_size, mRawBuf + 16, sizeof(mBIH.image_size));
759 4 : memcpy(&mBIH.xppm, mRawBuf + 20, sizeof(mBIH.xppm));
760 4 : memcpy(&mBIH.yppm, mRawBuf + 24, sizeof(mBIH.yppm));
761 4 : memcpy(&mBIH.colors, mRawBuf + 28, sizeof(mBIH.colors));
762 4 : memcpy(&mBIH.important_colors, mRawBuf + 32, sizeof(mBIH.important_colors));
763 : }
764 :
765 : // Convert endianness
766 4 : mBIH.width = LITTLE_TO_NATIVE32(mBIH.width);
767 4 : mBIH.height = LITTLE_TO_NATIVE32(mBIH.height);
768 4 : mBIH.planes = LITTLE_TO_NATIVE16(mBIH.planes);
769 4 : mBIH.bpp = LITTLE_TO_NATIVE16(mBIH.bpp);
770 :
771 4 : mBIH.compression = LITTLE_TO_NATIVE32(mBIH.compression);
772 4 : mBIH.image_size = LITTLE_TO_NATIVE32(mBIH.image_size);
773 4 : mBIH.xppm = LITTLE_TO_NATIVE32(mBIH.xppm);
774 4 : mBIH.yppm = LITTLE_TO_NATIVE32(mBIH.yppm);
775 4 : mBIH.colors = LITTLE_TO_NATIVE32(mBIH.colors);
776 4 : mBIH.important_colors = LITTLE_TO_NATIVE32(mBIH.important_colors);
777 4 : }
778 :
779 : } // namespace image
780 4392 : } // namespace mozilla
|