|
1 /* vim:set tw=80 expandtab softtabstop=4 ts=4 sw=4: */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 /* This is a Cross-Platform ICO Decoder, which should work everywhere, including |
|
7 * Big-Endian machines like the PowerPC. */ |
|
8 |
|
9 #include <stdlib.h> |
|
10 |
|
11 #include "mozilla/Endian.h" |
|
12 #include "nsICODecoder.h" |
|
13 |
|
14 #include "RasterImage.h" |
|
15 |
|
16 namespace mozilla { |
|
17 namespace image { |
|
18 |
|
19 #define ICONCOUNTOFFSET 4 |
|
20 #define DIRENTRYOFFSET 6 |
|
21 #define BITMAPINFOSIZE 40 |
|
22 #define PREFICONSIZE 16 |
|
23 |
|
24 // ---------------------------------------- |
|
25 // Actual Data Processing |
|
26 // ---------------------------------------- |
|
27 |
|
28 uint32_t |
|
29 nsICODecoder::CalcAlphaRowSize() |
|
30 { |
|
31 // Calculate rowsize in DWORD's and then return in # of bytes |
|
32 uint32_t rowSize = (GetRealWidth() + 31) / 32; // + 31 to round up |
|
33 return rowSize * 4; // Return rowSize in bytes |
|
34 } |
|
35 |
|
36 // Obtains the number of colors from the bits per pixel |
|
37 uint16_t |
|
38 nsICODecoder::GetNumColors() |
|
39 { |
|
40 uint16_t numColors = 0; |
|
41 if (mBPP <= 8) { |
|
42 switch (mBPP) { |
|
43 case 1: |
|
44 numColors = 2; |
|
45 break; |
|
46 case 4: |
|
47 numColors = 16; |
|
48 break; |
|
49 case 8: |
|
50 numColors = 256; |
|
51 break; |
|
52 default: |
|
53 numColors = (uint16_t)-1; |
|
54 } |
|
55 } |
|
56 return numColors; |
|
57 } |
|
58 |
|
59 |
|
60 nsICODecoder::nsICODecoder(RasterImage &aImage) |
|
61 : Decoder(aImage) |
|
62 { |
|
63 mPos = mImageOffset = mCurrIcon = mNumIcons = mBPP = mRowBytes = 0; |
|
64 mIsPNG = false; |
|
65 mRow = nullptr; |
|
66 mOldLine = mCurLine = 1; // Otherwise decoder will never start |
|
67 } |
|
68 |
|
69 nsICODecoder::~nsICODecoder() |
|
70 { |
|
71 if (mRow) { |
|
72 moz_free(mRow); |
|
73 } |
|
74 } |
|
75 |
|
76 void |
|
77 nsICODecoder::FinishInternal() |
|
78 { |
|
79 // We shouldn't be called in error cases |
|
80 NS_ABORT_IF_FALSE(!HasError(), "Shouldn't call FinishInternal after error!"); |
|
81 |
|
82 // Finish the internally used decoder as well |
|
83 if (mContainedDecoder) { |
|
84 mContainedDecoder->FinishSharedDecoder(); |
|
85 mDecodeDone = mContainedDecoder->GetDecodeDone(); |
|
86 } |
|
87 } |
|
88 |
|
89 // Returns a buffer filled with the bitmap file header in little endian: |
|
90 // Signature 2 bytes 'BM' |
|
91 // FileSize 4 bytes File size in bytes |
|
92 // reserved 4 bytes unused (=0) |
|
93 // DataOffset 4 bytes File offset to Raster Data |
|
94 // Returns true if successful |
|
95 bool nsICODecoder::FillBitmapFileHeaderBuffer(int8_t *bfh) |
|
96 { |
|
97 memset(bfh, 0, 14); |
|
98 bfh[0] = 'B'; |
|
99 bfh[1] = 'M'; |
|
100 int32_t dataOffset = 0; |
|
101 int32_t fileSize = 0; |
|
102 dataOffset = BFH_LENGTH + BITMAPINFOSIZE; |
|
103 |
|
104 // The color table is present only if BPP is <= 8 |
|
105 if (mDirEntry.mBitCount <= 8) { |
|
106 uint16_t numColors = GetNumColors(); |
|
107 if (numColors == (uint16_t)-1) { |
|
108 return false; |
|
109 } |
|
110 dataOffset += 4 * numColors; |
|
111 fileSize = dataOffset + GetRealWidth() * GetRealHeight(); |
|
112 } else { |
|
113 fileSize = dataOffset + (mDirEntry.mBitCount * GetRealWidth() * |
|
114 GetRealHeight()) / 8; |
|
115 } |
|
116 |
|
117 NativeEndian::swapToLittleEndianInPlace(&fileSize, 1); |
|
118 memcpy(bfh + 2, &fileSize, sizeof(fileSize)); |
|
119 NativeEndian::swapToLittleEndianInPlace(&dataOffset, 1); |
|
120 memcpy(bfh + 10, &dataOffset, sizeof(dataOffset)); |
|
121 return true; |
|
122 } |
|
123 |
|
124 // A BMP inside of an ICO has *2 height because of the AND mask |
|
125 // that follows the actual bitmap. The BMP shouldn't know about |
|
126 // this difference though. |
|
127 bool |
|
128 nsICODecoder::FixBitmapHeight(int8_t *bih) |
|
129 { |
|
130 // Get the height from the BMP file information header |
|
131 int32_t height; |
|
132 memcpy(&height, bih + 8, sizeof(height)); |
|
133 NativeEndian::swapFromLittleEndianInPlace(&height, 1); |
|
134 // BMPs can be stored inverted by having a negative height |
|
135 height = abs(height); |
|
136 |
|
137 // The bitmap height is by definition * 2 what it should be to account for |
|
138 // the 'AND mask'. It is * 2 even if the `AND mask` is not present. |
|
139 height /= 2; |
|
140 |
|
141 if (height > 256) { |
|
142 return false; |
|
143 } |
|
144 |
|
145 // We should always trust the height from the bitmap itself instead of |
|
146 // the ICO height. So fix the ICO height. |
|
147 if (height == 256) { |
|
148 mDirEntry.mHeight = 0; |
|
149 } else { |
|
150 mDirEntry.mHeight = (int8_t)height; |
|
151 } |
|
152 |
|
153 // Fix the BMP height in the BIH so that the BMP decoder can work properly |
|
154 NativeEndian::swapToLittleEndianInPlace(&height, 1); |
|
155 memcpy(bih + 8, &height, sizeof(height)); |
|
156 return true; |
|
157 } |
|
158 |
|
159 // We should always trust the contained resource for the width |
|
160 // information over our own information. |
|
161 bool |
|
162 nsICODecoder::FixBitmapWidth(int8_t *bih) |
|
163 { |
|
164 // Get the width from the BMP file information header |
|
165 int32_t width; |
|
166 memcpy(&width, bih + 4, sizeof(width)); |
|
167 NativeEndian::swapFromLittleEndianInPlace(&width, 1); |
|
168 if (width > 256) { |
|
169 return false; |
|
170 } |
|
171 |
|
172 // We should always trust the width from the bitmap itself instead of |
|
173 // the ICO width. |
|
174 if (width == 256) { |
|
175 mDirEntry.mWidth = 0; |
|
176 } else { |
|
177 mDirEntry.mWidth = (int8_t)width; |
|
178 } |
|
179 return true; |
|
180 } |
|
181 |
|
182 // The BMP information header's bits per pixel should be trusted |
|
183 // more than what we have. Usually the ICO's BPP is set to 0 |
|
184 int32_t |
|
185 nsICODecoder::ExtractBPPFromBitmap(int8_t *bih) |
|
186 { |
|
187 int32_t bitsPerPixel; |
|
188 memcpy(&bitsPerPixel, bih + 14, sizeof(bitsPerPixel)); |
|
189 NativeEndian::swapFromLittleEndianInPlace(&bitsPerPixel, 1); |
|
190 return bitsPerPixel; |
|
191 } |
|
192 |
|
193 int32_t |
|
194 nsICODecoder::ExtractBIHSizeFromBitmap(int8_t *bih) |
|
195 { |
|
196 int32_t headerSize; |
|
197 memcpy(&headerSize, bih, sizeof(headerSize)); |
|
198 NativeEndian::swapFromLittleEndianInPlace(&headerSize, 1); |
|
199 return headerSize; |
|
200 } |
|
201 |
|
202 void |
|
203 nsICODecoder::SetHotSpotIfCursor() { |
|
204 if (!mIsCursor) { |
|
205 return; |
|
206 } |
|
207 |
|
208 mImageMetadata.SetHotspot(mDirEntry.mXHotspot, mDirEntry.mYHotspot); |
|
209 } |
|
210 |
|
211 void |
|
212 nsICODecoder::WriteInternal(const char* aBuffer, uint32_t aCount, DecodeStrategy aStrategy) |
|
213 { |
|
214 NS_ABORT_IF_FALSE(!HasError(), "Shouldn't call WriteInternal after error!"); |
|
215 |
|
216 if (!aCount) { |
|
217 if (mContainedDecoder) { |
|
218 WriteToContainedDecoder(aBuffer, aCount, aStrategy); |
|
219 } |
|
220 return; |
|
221 } |
|
222 |
|
223 while (aCount && (mPos < ICONCOUNTOFFSET)) { // Skip to the # of icons. |
|
224 if (mPos == 2) { // if the third byte is 1: This is an icon, 2: a cursor |
|
225 if ((*aBuffer != 1) && (*aBuffer != 2)) { |
|
226 PostDataError(); |
|
227 return; |
|
228 } |
|
229 mIsCursor = (*aBuffer == 2); |
|
230 } |
|
231 mPos++; aBuffer++; aCount--; |
|
232 } |
|
233 |
|
234 if (mPos == ICONCOUNTOFFSET && aCount >= 2) { |
|
235 mNumIcons = LittleEndian::readUint16(reinterpret_cast<const uint16_t*>(aBuffer)); |
|
236 aBuffer += 2; |
|
237 mPos += 2; |
|
238 aCount -= 2; |
|
239 } |
|
240 |
|
241 if (mNumIcons == 0) |
|
242 return; // Nothing to do. |
|
243 |
|
244 uint16_t colorDepth = 0; |
|
245 nsIntSize prefSize = mImage.GetRequestedResolution(); |
|
246 if (prefSize.width == 0 && prefSize.height == 0) { |
|
247 prefSize.SizeTo(PREFICONSIZE, PREFICONSIZE); |
|
248 } |
|
249 |
|
250 // A measure of the difference in size between the entry we've found |
|
251 // and the requested size. We will choose the smallest image that is |
|
252 // >= requested size (i.e. we assume it's better to downscale a larger |
|
253 // icon than to upscale a smaller one). |
|
254 int32_t diff = INT_MIN; |
|
255 |
|
256 // Loop through each entry's dir entry |
|
257 while (mCurrIcon < mNumIcons) { |
|
258 if (mPos >= DIRENTRYOFFSET + (mCurrIcon * sizeof(mDirEntryArray)) && |
|
259 mPos < DIRENTRYOFFSET + ((mCurrIcon + 1) * sizeof(mDirEntryArray))) { |
|
260 uint32_t toCopy = sizeof(mDirEntryArray) - |
|
261 (mPos - DIRENTRYOFFSET - mCurrIcon * sizeof(mDirEntryArray)); |
|
262 if (toCopy > aCount) { |
|
263 toCopy = aCount; |
|
264 } |
|
265 memcpy(mDirEntryArray + sizeof(mDirEntryArray) - toCopy, aBuffer, toCopy); |
|
266 mPos += toCopy; |
|
267 aCount -= toCopy; |
|
268 aBuffer += toCopy; |
|
269 } |
|
270 if (aCount == 0) |
|
271 return; // Need more data |
|
272 |
|
273 IconDirEntry e; |
|
274 if (mPos == (DIRENTRYOFFSET + ICODIRENTRYSIZE) + |
|
275 (mCurrIcon * sizeof(mDirEntryArray))) { |
|
276 mCurrIcon++; |
|
277 ProcessDirEntry(e); |
|
278 // We can't use GetRealWidth and GetRealHeight here because those operate |
|
279 // on mDirEntry, here we are going through each item in the directory. |
|
280 // Calculate the delta between this image's size and the desired size, |
|
281 // so we can see if it is better than our current-best option. |
|
282 // In the case of several equally-good images, we use the last one. |
|
283 int32_t delta = (e.mWidth == 0 ? 256 : e.mWidth) - prefSize.width + |
|
284 (e.mHeight == 0 ? 256 : e.mHeight) - prefSize.height; |
|
285 if (e.mBitCount >= colorDepth && |
|
286 ((diff < 0 && delta >= diff) || (delta >= 0 && delta <= diff))) { |
|
287 diff = delta; |
|
288 mImageOffset = e.mImageOffset; |
|
289 |
|
290 // ensure mImageOffset is >= size of the direntry headers (bug #245631) |
|
291 uint32_t minImageOffset = DIRENTRYOFFSET + |
|
292 mNumIcons * sizeof(mDirEntryArray); |
|
293 if (mImageOffset < minImageOffset) { |
|
294 PostDataError(); |
|
295 return; |
|
296 } |
|
297 |
|
298 colorDepth = e.mBitCount; |
|
299 memcpy(&mDirEntry, &e, sizeof(IconDirEntry)); |
|
300 } |
|
301 } |
|
302 } |
|
303 |
|
304 if (mPos < mImageOffset) { |
|
305 // Skip to (or at least towards) the desired image offset |
|
306 uint32_t toSkip = mImageOffset - mPos; |
|
307 if (toSkip > aCount) |
|
308 toSkip = aCount; |
|
309 |
|
310 mPos += toSkip; |
|
311 aBuffer += toSkip; |
|
312 aCount -= toSkip; |
|
313 } |
|
314 |
|
315 // If we are within the first PNGSIGNATURESIZE bytes of the image data, |
|
316 // then we have either a BMP or a PNG. We use the first PNGSIGNATURESIZE |
|
317 // bytes to determine which one we have. |
|
318 if (mCurrIcon == mNumIcons && mPos >= mImageOffset && |
|
319 mPos < mImageOffset + PNGSIGNATURESIZE) |
|
320 { |
|
321 uint32_t toCopy = PNGSIGNATURESIZE - (mPos - mImageOffset); |
|
322 if (toCopy > aCount) { |
|
323 toCopy = aCount; |
|
324 } |
|
325 |
|
326 memcpy(mSignature + (mPos - mImageOffset), aBuffer, toCopy); |
|
327 mPos += toCopy; |
|
328 aCount -= toCopy; |
|
329 aBuffer += toCopy; |
|
330 |
|
331 mIsPNG = !memcmp(mSignature, nsPNGDecoder::pngSignatureBytes, |
|
332 PNGSIGNATURESIZE); |
|
333 if (mIsPNG) { |
|
334 mContainedDecoder = new nsPNGDecoder(mImage); |
|
335 mContainedDecoder->SetObserver(mObserver); |
|
336 mContainedDecoder->SetSizeDecode(IsSizeDecode()); |
|
337 mContainedDecoder->InitSharedDecoder(mImageData, mImageDataLength, |
|
338 mColormap, mColormapSize, |
|
339 mCurrentFrame); |
|
340 if (!WriteToContainedDecoder(mSignature, PNGSIGNATURESIZE, aStrategy)) { |
|
341 return; |
|
342 } |
|
343 } |
|
344 } |
|
345 |
|
346 // If we have a PNG, let the PNG decoder do all of the rest of the work |
|
347 if (mIsPNG && mContainedDecoder && mPos >= mImageOffset + PNGSIGNATURESIZE) { |
|
348 if (!WriteToContainedDecoder(aBuffer, aCount, aStrategy)) { |
|
349 return; |
|
350 } |
|
351 |
|
352 if (!HasSize() && mContainedDecoder->HasSize()) { |
|
353 PostSize(mContainedDecoder->GetImageMetadata().GetWidth(), |
|
354 mContainedDecoder->GetImageMetadata().GetHeight()); |
|
355 } |
|
356 |
|
357 mPos += aCount; |
|
358 aBuffer += aCount; |
|
359 aCount = 0; |
|
360 |
|
361 // Raymond Chen says that 32bpp only are valid PNG ICOs |
|
362 // http://blogs.msdn.com/b/oldnewthing/archive/2010/10/22/10079192.aspx |
|
363 if (!IsSizeDecode() && |
|
364 !static_cast<nsPNGDecoder*>(mContainedDecoder.get())->IsValidICO()) { |
|
365 PostDataError(); |
|
366 } |
|
367 return; |
|
368 } |
|
369 |
|
370 // We've processed all of the icon dir entries and are within the |
|
371 // bitmap info size |
|
372 if (!mIsPNG && mCurrIcon == mNumIcons && mPos >= mImageOffset && |
|
373 mPos >= mImageOffset + PNGSIGNATURESIZE && |
|
374 mPos < mImageOffset + BITMAPINFOSIZE) { |
|
375 |
|
376 // As we were decoding, we did not know if we had a PNG signature or the |
|
377 // start of a bitmap information header. At this point we know we had |
|
378 // a bitmap information header and not a PNG signature, so fill the bitmap |
|
379 // information header with the data it should already have. |
|
380 memcpy(mBIHraw, mSignature, PNGSIGNATURESIZE); |
|
381 |
|
382 // We've found the icon. |
|
383 uint32_t toCopy = sizeof(mBIHraw) - (mPos - mImageOffset); |
|
384 if (toCopy > aCount) |
|
385 toCopy = aCount; |
|
386 |
|
387 memcpy(mBIHraw + (mPos - mImageOffset), aBuffer, toCopy); |
|
388 mPos += toCopy; |
|
389 aCount -= toCopy; |
|
390 aBuffer += toCopy; |
|
391 } |
|
392 |
|
393 // If we have a BMP inside the ICO and we have read the BIH header |
|
394 if (!mIsPNG && mPos == mImageOffset + BITMAPINFOSIZE) { |
|
395 |
|
396 // Make sure we have a sane value for the bitmap information header |
|
397 int32_t bihSize = ExtractBIHSizeFromBitmap(reinterpret_cast<int8_t*>(mBIHraw)); |
|
398 if (bihSize != BITMAPINFOSIZE) { |
|
399 PostDataError(); |
|
400 return; |
|
401 } |
|
402 // We are extracting the BPP from the BIH header as it should be trusted |
|
403 // over the one we have from the icon header |
|
404 mBPP = ExtractBPPFromBitmap(reinterpret_cast<int8_t*>(mBIHraw)); |
|
405 |
|
406 // Init the bitmap decoder which will do most of the work for us |
|
407 // It will do everything except the AND mask which isn't present in bitmaps |
|
408 // bmpDecoder is for local scope ease, it will be freed by mContainedDecoder |
|
409 nsBMPDecoder *bmpDecoder = new nsBMPDecoder(mImage); |
|
410 mContainedDecoder = bmpDecoder; |
|
411 bmpDecoder->SetUseAlphaData(true); |
|
412 mContainedDecoder->SetObserver(mObserver); |
|
413 mContainedDecoder->SetSizeDecode(IsSizeDecode()); |
|
414 mContainedDecoder->InitSharedDecoder(mImageData, mImageDataLength, |
|
415 mColormap, mColormapSize, |
|
416 mCurrentFrame); |
|
417 |
|
418 // The ICO format when containing a BMP does not include the 14 byte |
|
419 // bitmap file header. To use the code of the BMP decoder we need to |
|
420 // generate this header ourselves and feed it to the BMP decoder. |
|
421 int8_t bfhBuffer[BMPFILEHEADERSIZE]; |
|
422 if (!FillBitmapFileHeaderBuffer(bfhBuffer)) { |
|
423 PostDataError(); |
|
424 return; |
|
425 } |
|
426 if (!WriteToContainedDecoder((const char*)bfhBuffer, sizeof(bfhBuffer), aStrategy)) { |
|
427 return; |
|
428 } |
|
429 |
|
430 // Setup the cursor hot spot if one is present |
|
431 SetHotSpotIfCursor(); |
|
432 |
|
433 // Fix the ICO height from the BIH. |
|
434 // Fix the height on the BIH to be /2 so our BMP decoder will understand. |
|
435 if (!FixBitmapHeight(reinterpret_cast<int8_t*>(mBIHraw))) { |
|
436 PostDataError(); |
|
437 return; |
|
438 } |
|
439 |
|
440 // Fix the ICO width from the BIH. |
|
441 if (!FixBitmapWidth(reinterpret_cast<int8_t*>(mBIHraw))) { |
|
442 PostDataError(); |
|
443 return; |
|
444 } |
|
445 |
|
446 // Write out the BMP's bitmap info header |
|
447 if (!WriteToContainedDecoder(mBIHraw, sizeof(mBIHraw), aStrategy)) { |
|
448 return; |
|
449 } |
|
450 |
|
451 PostSize(mContainedDecoder->GetImageMetadata().GetWidth(), |
|
452 mContainedDecoder->GetImageMetadata().GetHeight()); |
|
453 |
|
454 // We have the size. If we're doing a size decode, we got what |
|
455 // we came for. |
|
456 if (IsSizeDecode()) |
|
457 return; |
|
458 |
|
459 // Sometimes the ICO BPP header field is not filled out |
|
460 // so we should trust the contained resource over our own |
|
461 // information. |
|
462 mBPP = bmpDecoder->GetBitsPerPixel(); |
|
463 |
|
464 // Check to make sure we have valid color settings |
|
465 uint16_t numColors = GetNumColors(); |
|
466 if (numColors == (uint16_t)-1) { |
|
467 PostDataError(); |
|
468 return; |
|
469 } |
|
470 } |
|
471 |
|
472 // If we have a BMP |
|
473 if (!mIsPNG && mContainedDecoder && mPos >= mImageOffset + BITMAPINFOSIZE) { |
|
474 uint16_t numColors = GetNumColors(); |
|
475 if (numColors == (uint16_t)-1) { |
|
476 PostDataError(); |
|
477 return; |
|
478 } |
|
479 // Feed the actual image data (not including headers) into the BMP decoder |
|
480 uint32_t bmpDataOffset = mDirEntry.mImageOffset + BITMAPINFOSIZE; |
|
481 uint32_t bmpDataEnd = mDirEntry.mImageOffset + BITMAPINFOSIZE + |
|
482 static_cast<nsBMPDecoder*>(mContainedDecoder.get())->GetCompressedImageSize() + |
|
483 4 * numColors; |
|
484 |
|
485 // If we are feeding in the core image data, but we have not yet |
|
486 // reached the ICO's 'AND buffer mask' |
|
487 if (mPos >= bmpDataOffset && mPos < bmpDataEnd) { |
|
488 |
|
489 // Figure out how much data the BMP decoder wants |
|
490 uint32_t toFeed = bmpDataEnd - mPos; |
|
491 if (toFeed > aCount) { |
|
492 toFeed = aCount; |
|
493 } |
|
494 |
|
495 if (!WriteToContainedDecoder(aBuffer, toFeed, aStrategy)) { |
|
496 return; |
|
497 } |
|
498 |
|
499 mPos += toFeed; |
|
500 aCount -= toFeed; |
|
501 aBuffer += toFeed; |
|
502 } |
|
503 |
|
504 // If the bitmap is fully processed, treat any left over data as the ICO's |
|
505 // 'AND buffer mask' which appears after the bitmap resource. |
|
506 if (!mIsPNG && mPos >= bmpDataEnd) { |
|
507 // There may be an optional AND bit mask after the data. This is |
|
508 // only used if the alpha data is not already set. The alpha data |
|
509 // is used for 32bpp bitmaps as per the comment in ICODecoder.h |
|
510 // The alpha mask should be checked in all other cases. |
|
511 if (static_cast<nsBMPDecoder*>(mContainedDecoder.get())->GetBitsPerPixel() != 32 || |
|
512 !static_cast<nsBMPDecoder*>(mContainedDecoder.get())->HasAlphaData()) { |
|
513 uint32_t rowSize = ((GetRealWidth() + 31) / 32) * 4; // + 31 to round up |
|
514 if (mPos == bmpDataEnd) { |
|
515 mPos++; |
|
516 mRowBytes = 0; |
|
517 mCurLine = GetRealHeight(); |
|
518 mRow = (uint8_t*)moz_realloc(mRow, rowSize); |
|
519 if (!mRow) { |
|
520 PostDecoderError(NS_ERROR_OUT_OF_MEMORY); |
|
521 return; |
|
522 } |
|
523 } |
|
524 |
|
525 // Ensure memory has been allocated before decoding. |
|
526 NS_ABORT_IF_FALSE(mRow, "mRow is null"); |
|
527 if (!mRow) { |
|
528 PostDataError(); |
|
529 return; |
|
530 } |
|
531 |
|
532 while (mCurLine > 0 && aCount > 0) { |
|
533 uint32_t toCopy = std::min(rowSize - mRowBytes, aCount); |
|
534 if (toCopy) { |
|
535 memcpy(mRow + mRowBytes, aBuffer, toCopy); |
|
536 aCount -= toCopy; |
|
537 aBuffer += toCopy; |
|
538 mRowBytes += toCopy; |
|
539 } |
|
540 if (rowSize == mRowBytes) { |
|
541 mCurLine--; |
|
542 mRowBytes = 0; |
|
543 |
|
544 uint32_t* imageData = |
|
545 static_cast<nsBMPDecoder*>(mContainedDecoder.get())->GetImageData(); |
|
546 if (!imageData) { |
|
547 PostDataError(); |
|
548 return; |
|
549 } |
|
550 uint32_t* decoded = imageData + mCurLine * GetRealWidth(); |
|
551 uint32_t* decoded_end = decoded + GetRealWidth(); |
|
552 uint8_t* p = mRow, *p_end = mRow + rowSize; |
|
553 while (p < p_end) { |
|
554 uint8_t idx = *p++; |
|
555 for (uint8_t bit = 0x80; bit && decoded<decoded_end; bit >>= 1) { |
|
556 // Clear pixel completely for transparency. |
|
557 if (idx & bit) { |
|
558 *decoded = 0; |
|
559 } |
|
560 decoded++; |
|
561 } |
|
562 } |
|
563 } |
|
564 } |
|
565 } |
|
566 } |
|
567 } |
|
568 } |
|
569 |
|
570 bool |
|
571 nsICODecoder::WriteToContainedDecoder(const char* aBuffer, uint32_t aCount, DecodeStrategy aStrategy) |
|
572 { |
|
573 mContainedDecoder->Write(aBuffer, aCount, aStrategy); |
|
574 if (mContainedDecoder->HasDataError()) { |
|
575 mDataError = mContainedDecoder->HasDataError(); |
|
576 } |
|
577 if (mContainedDecoder->HasDecoderError()) { |
|
578 PostDecoderError(mContainedDecoder->GetDecoderError()); |
|
579 } |
|
580 return !HasError(); |
|
581 } |
|
582 |
|
583 void |
|
584 nsICODecoder::ProcessDirEntry(IconDirEntry& aTarget) |
|
585 { |
|
586 memset(&aTarget, 0, sizeof(aTarget)); |
|
587 memcpy(&aTarget.mWidth, mDirEntryArray, sizeof(aTarget.mWidth)); |
|
588 memcpy(&aTarget.mHeight, mDirEntryArray + 1, sizeof(aTarget.mHeight)); |
|
589 memcpy(&aTarget.mColorCount, mDirEntryArray + 2, sizeof(aTarget.mColorCount)); |
|
590 memcpy(&aTarget.mReserved, mDirEntryArray + 3, sizeof(aTarget.mReserved)); |
|
591 memcpy(&aTarget.mPlanes, mDirEntryArray + 4, sizeof(aTarget.mPlanes)); |
|
592 aTarget.mPlanes = LittleEndian::readUint16(&aTarget.mPlanes); |
|
593 memcpy(&aTarget.mBitCount, mDirEntryArray + 6, sizeof(aTarget.mBitCount)); |
|
594 aTarget.mBitCount = LittleEndian::readUint16(&aTarget.mBitCount); |
|
595 memcpy(&aTarget.mBytesInRes, mDirEntryArray + 8, sizeof(aTarget.mBytesInRes)); |
|
596 aTarget.mBytesInRes = LittleEndian::readUint32(&aTarget.mBytesInRes); |
|
597 memcpy(&aTarget.mImageOffset, mDirEntryArray + 12, |
|
598 sizeof(aTarget.mImageOffset)); |
|
599 aTarget.mImageOffset = LittleEndian::readUint32(&aTarget.mImageOffset); |
|
600 } |
|
601 |
|
602 bool |
|
603 nsICODecoder::NeedsNewFrame() const |
|
604 { |
|
605 if (mContainedDecoder) { |
|
606 return mContainedDecoder->NeedsNewFrame(); |
|
607 } |
|
608 |
|
609 return Decoder::NeedsNewFrame(); |
|
610 } |
|
611 |
|
612 nsresult |
|
613 nsICODecoder::AllocateFrame() |
|
614 { |
|
615 if (mContainedDecoder) { |
|
616 nsresult rv = mContainedDecoder->AllocateFrame(); |
|
617 mCurrentFrame = mContainedDecoder->GetCurrentFrame(); |
|
618 return rv; |
|
619 } |
|
620 |
|
621 return Decoder::AllocateFrame(); |
|
622 } |
|
623 |
|
624 } // namespace image |
|
625 } // namespace mozilla |