Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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 : #include "nsCRT.h"
7 : #include "mozilla/EndianUtils.h"
8 : #include "mozilla/UniquePtrExtensions.h"
9 : #include "nsBMPEncoder.h"
10 : #include "nsString.h"
11 : #include "nsStreamUtils.h"
12 : #include "nsTArray.h"
13 : #include "mozilla/CheckedInt.h"
14 :
15 : using namespace mozilla;
16 : using namespace mozilla::image;
17 : using namespace mozilla::image::bmp;
18 :
19 0 : NS_IMPL_ISUPPORTS(nsBMPEncoder, imgIEncoder, nsIInputStream,
20 : nsIAsyncInputStream)
21 :
22 0 : nsBMPEncoder::nsBMPEncoder() : mImageBufferStart(nullptr),
23 : mImageBufferCurr(0),
24 : mImageBufferSize(0),
25 : mImageBufferReadPoint(0),
26 : mFinished(false),
27 : mCallback(nullptr),
28 : mCallbackTarget(nullptr),
29 0 : mNotifyThreshold(0)
30 : {
31 0 : }
32 :
33 0 : nsBMPEncoder::~nsBMPEncoder()
34 : {
35 0 : if (mImageBufferStart) {
36 0 : free(mImageBufferStart);
37 0 : mImageBufferStart = nullptr;
38 0 : mImageBufferCurr = nullptr;
39 : }
40 0 : }
41 :
42 : // nsBMPEncoder::InitFromData
43 : //
44 : // One output option is supported: bpp=<bpp_value>
45 : // bpp specifies the bits per pixel to use where bpp_value can be 24 or 32
46 : NS_IMETHODIMP
47 0 : nsBMPEncoder::InitFromData(const uint8_t* aData,
48 : uint32_t aLength, // (unused, req'd by JS)
49 : uint32_t aWidth,
50 : uint32_t aHeight,
51 : uint32_t aStride,
52 : uint32_t aInputFormat,
53 : const nsAString& aOutputOptions)
54 : {
55 : // validate input format
56 0 : if (aInputFormat != INPUT_FORMAT_RGB &&
57 0 : aInputFormat != INPUT_FORMAT_RGBA &&
58 : aInputFormat != INPUT_FORMAT_HOSTARGB) {
59 0 : return NS_ERROR_INVALID_ARG;
60 : }
61 :
62 0 : CheckedInt32 check = CheckedInt32(aWidth) * 4;
63 0 : if (MOZ_UNLIKELY(!check.isValid())) {
64 0 : return NS_ERROR_INVALID_ARG;
65 : }
66 :
67 : // Stride is the padded width of each row, so it better be longer
68 0 : if ((aInputFormat == INPUT_FORMAT_RGB &&
69 0 : aStride < aWidth * 3) ||
70 0 : ((aInputFormat == INPUT_FORMAT_RGBA ||
71 0 : aInputFormat == INPUT_FORMAT_HOSTARGB) &&
72 0 : aStride < aWidth * 4)) {
73 0 : NS_WARNING("Invalid stride for InitFromData");
74 0 : return NS_ERROR_INVALID_ARG;
75 : }
76 :
77 : nsresult rv;
78 0 : rv = StartImageEncode(aWidth, aHeight, aInputFormat, aOutputOptions);
79 0 : if (NS_FAILED(rv)) {
80 0 : return rv;
81 : }
82 :
83 : rv = AddImageFrame(aData, aLength, aWidth, aHeight, aStride,
84 0 : aInputFormat, aOutputOptions);
85 0 : if (NS_FAILED(rv)) {
86 0 : return rv;
87 : }
88 :
89 0 : rv = EndImageEncode();
90 0 : return rv;
91 : }
92 :
93 : // Just a helper method to make it explicit in calculations that we are dealing
94 : // with bytes and not bits
95 : static inline uint16_t
96 0 : BytesPerPixel(uint16_t aBPP)
97 : {
98 0 : return aBPP / 8;
99 : }
100 :
101 : // Calculates the number of padding bytes that are needed per row of image data
102 : static inline uint32_t
103 0 : PaddingBytes(uint16_t aBPP, uint32_t aWidth)
104 : {
105 0 : uint32_t rowSize = aWidth * BytesPerPixel(aBPP);
106 0 : uint8_t paddingSize = 0;
107 0 : if (rowSize % 4) {
108 0 : paddingSize = (4 - (rowSize % 4));
109 : }
110 0 : return paddingSize;
111 : }
112 :
113 : // See ::InitFromData for other info.
114 : NS_IMETHODIMP
115 0 : nsBMPEncoder::StartImageEncode(uint32_t aWidth,
116 : uint32_t aHeight,
117 : uint32_t aInputFormat,
118 : const nsAString& aOutputOptions)
119 : {
120 : // can't initialize more than once
121 0 : if (mImageBufferStart || mImageBufferCurr) {
122 0 : return NS_ERROR_ALREADY_INITIALIZED;
123 : }
124 :
125 : // validate input format
126 0 : if (aInputFormat != INPUT_FORMAT_RGB &&
127 0 : aInputFormat != INPUT_FORMAT_RGBA &&
128 : aInputFormat != INPUT_FORMAT_HOSTARGB) {
129 0 : return NS_ERROR_INVALID_ARG;
130 : }
131 :
132 : // parse and check any provided output options
133 : Version version;
134 : uint16_t bpp;
135 0 : nsresult rv = ParseOptions(aOutputOptions, version, bpp);
136 0 : if (NS_FAILED(rv)) {
137 0 : return rv;
138 : }
139 0 : MOZ_ASSERT(bpp <= 32);
140 :
141 0 : rv = InitFileHeader(version, bpp, aWidth, aHeight);
142 0 : if (NS_FAILED(rv)) {
143 0 : return rv;
144 : }
145 0 : rv = InitInfoHeader(version, bpp, aWidth, aHeight);
146 0 : if (NS_FAILED(rv)) {
147 0 : return rv;
148 : }
149 :
150 0 : mImageBufferSize = mBMPFileHeader.filesize;
151 0 : mImageBufferStart = static_cast<uint8_t*>(malloc(mImageBufferSize));
152 0 : if (!mImageBufferStart) {
153 0 : return NS_ERROR_OUT_OF_MEMORY;
154 : }
155 0 : mImageBufferCurr = mImageBufferStart;
156 :
157 0 : EncodeFileHeader();
158 0 : EncodeInfoHeader();
159 :
160 0 : return NS_OK;
161 : }
162 :
163 : // Returns the number of bytes in the image buffer used.
164 : // For a BMP file, this is all bytes in the buffer.
165 : NS_IMETHODIMP
166 0 : nsBMPEncoder::GetImageBufferUsed(uint32_t* aOutputSize)
167 : {
168 0 : NS_ENSURE_ARG_POINTER(aOutputSize);
169 0 : *aOutputSize = mImageBufferSize;
170 0 : return NS_OK;
171 : }
172 :
173 : // Returns a pointer to the start of the image buffer
174 : NS_IMETHODIMP
175 0 : nsBMPEncoder::GetImageBuffer(char** aOutputBuffer)
176 : {
177 0 : NS_ENSURE_ARG_POINTER(aOutputBuffer);
178 0 : *aOutputBuffer = reinterpret_cast<char*>(mImageBufferStart);
179 0 : return NS_OK;
180 : }
181 :
182 : NS_IMETHODIMP
183 0 : nsBMPEncoder::AddImageFrame(const uint8_t* aData,
184 : uint32_t aLength, // (unused, req'd by JS)
185 : uint32_t aWidth,
186 : uint32_t aHeight,
187 : uint32_t aStride,
188 : uint32_t aInputFormat,
189 : const nsAString& aFrameOptions)
190 : {
191 : // must be initialized
192 0 : if (!mImageBufferStart || !mImageBufferCurr) {
193 0 : return NS_ERROR_NOT_INITIALIZED;
194 : }
195 :
196 : // validate input format
197 0 : if (aInputFormat != INPUT_FORMAT_RGB &&
198 0 : aInputFormat != INPUT_FORMAT_RGBA &&
199 : aInputFormat != INPUT_FORMAT_HOSTARGB) {
200 0 : return NS_ERROR_INVALID_ARG;
201 : }
202 :
203 0 : if (mBMPInfoHeader.width < 0) {
204 0 : return NS_ERROR_ILLEGAL_VALUE;
205 : }
206 :
207 : CheckedUint32 size =
208 0 : CheckedUint32(mBMPInfoHeader.width) * CheckedUint32(BytesPerPixel(mBMPInfoHeader.bpp));
209 0 : if (MOZ_UNLIKELY(!size.isValid())) {
210 0 : return NS_ERROR_FAILURE;
211 : }
212 :
213 0 : auto row = MakeUniqueFallible<uint8_t[]>(size.value());
214 0 : if (!row) {
215 0 : return NS_ERROR_OUT_OF_MEMORY;
216 : }
217 :
218 0 : CheckedUint32 check = CheckedUint32(mBMPInfoHeader.height) * aStride;
219 0 : if (MOZ_UNLIKELY(!check.isValid())) {
220 0 : return NS_ERROR_FAILURE;
221 : }
222 :
223 : // write each row: if we add more input formats, we may want to
224 : // generalize the conversions
225 0 : if (aInputFormat == INPUT_FORMAT_HOSTARGB) {
226 : // BMP requires RGBA with post-multiplied alpha, so we need to convert
227 0 : for (int32_t y = mBMPInfoHeader.height - 1; y >= 0 ; y --) {
228 0 : ConvertHostARGBRow(&aData[y * aStride], row, mBMPInfoHeader.width);
229 0 : if(mBMPInfoHeader.bpp == 24) {
230 0 : EncodeImageDataRow24(row.get());
231 : } else {
232 0 : EncodeImageDataRow32(row.get());
233 : }
234 : }
235 0 : } else if (aInputFormat == INPUT_FORMAT_RGBA) {
236 : // simple RGBA, no conversion needed
237 0 : for (int32_t y = 0; y < mBMPInfoHeader.height; y++) {
238 0 : if (mBMPInfoHeader.bpp == 24) {
239 0 : EncodeImageDataRow24(row.get());
240 : } else {
241 0 : EncodeImageDataRow32(row.get());
242 : }
243 : }
244 0 : } else if (aInputFormat == INPUT_FORMAT_RGB) {
245 : // simple RGB, no conversion needed
246 0 : for (int32_t y = 0; y < mBMPInfoHeader.height; y++) {
247 0 : if (mBMPInfoHeader.bpp == 24) {
248 0 : EncodeImageDataRow24(&aData[y * aStride]);
249 : } else {
250 0 : EncodeImageDataRow32(&aData[y * aStride]);
251 : }
252 : }
253 : } else {
254 0 : NS_NOTREACHED("Bad format type");
255 0 : return NS_ERROR_INVALID_ARG;
256 : }
257 :
258 0 : return NS_OK;
259 : }
260 :
261 :
262 : NS_IMETHODIMP
263 0 : nsBMPEncoder::EndImageEncode()
264 : {
265 : // must be initialized
266 0 : if (!mImageBufferStart || !mImageBufferCurr) {
267 0 : return NS_ERROR_NOT_INITIALIZED;
268 : }
269 :
270 0 : mFinished = true;
271 0 : NotifyListener();
272 :
273 : // if output callback can't get enough memory, it will free our buffer
274 0 : if (!mImageBufferStart || !mImageBufferCurr) {
275 0 : return NS_ERROR_OUT_OF_MEMORY;
276 : }
277 :
278 0 : return NS_OK;
279 : }
280 :
281 :
282 : // Parses the encoder options and sets the bits per pixel to use
283 : // See InitFromData for a description of the parse options
284 : nsresult
285 0 : nsBMPEncoder::ParseOptions(const nsAString& aOptions, Version& aVersionOut,
286 : uint16_t& aBppOut)
287 : {
288 0 : aVersionOut = VERSION_3;
289 0 : aBppOut = 24;
290 :
291 : // Parse the input string into a set of name/value pairs.
292 : // From a format like: name=value;bpp=<bpp_value>;name=value
293 : // to format: [0] = name=value, [1] = bpp=<bpp_value>, [2] = name=value
294 0 : nsTArray<nsCString> nameValuePairs;
295 0 : if (!ParseString(NS_ConvertUTF16toUTF8(aOptions), ';', nameValuePairs)) {
296 0 : return NS_ERROR_INVALID_ARG;
297 : }
298 :
299 : // For each name/value pair in the set
300 0 : for (uint32_t i = 0; i < nameValuePairs.Length(); ++i) {
301 :
302 : // Split the name value pair [0] = name, [1] = value
303 0 : nsTArray<nsCString> nameValuePair;
304 0 : if (!ParseString(nameValuePairs[i], '=', nameValuePair)) {
305 0 : return NS_ERROR_INVALID_ARG;
306 : }
307 0 : if (nameValuePair.Length() != 2) {
308 0 : return NS_ERROR_INVALID_ARG;
309 : }
310 :
311 : // Parse the bpp portion of the string name=value;version=<version_value>;
312 : // name=value
313 0 : if (nameValuePair[0].Equals("version",
314 0 : nsCaseInsensitiveCStringComparator())) {
315 0 : if (nameValuePair[1].EqualsLiteral("3")) {
316 0 : aVersionOut = VERSION_3;
317 0 : } else if (nameValuePair[1].EqualsLiteral("5")) {
318 0 : aVersionOut = VERSION_5;
319 : } else {
320 0 : return NS_ERROR_INVALID_ARG;
321 : }
322 : }
323 :
324 : // Parse the bpp portion of the string name=value;bpp=<bpp_value>;name=value
325 0 : if (nameValuePair[0].Equals("bpp", nsCaseInsensitiveCStringComparator())) {
326 0 : if (nameValuePair[1].EqualsLiteral("24")) {
327 0 : aBppOut = 24;
328 0 : } else if (nameValuePair[1].EqualsLiteral("32")) {
329 0 : aBppOut = 32;
330 : } else {
331 0 : return NS_ERROR_INVALID_ARG;
332 : }
333 : }
334 : }
335 :
336 0 : return NS_OK;
337 : }
338 :
339 : NS_IMETHODIMP
340 0 : nsBMPEncoder::Close()
341 : {
342 0 : if (mImageBufferStart) {
343 0 : free(mImageBufferStart);
344 0 : mImageBufferStart = nullptr;
345 0 : mImageBufferSize = 0;
346 0 : mImageBufferReadPoint = 0;
347 0 : mImageBufferCurr = nullptr;
348 : }
349 :
350 0 : return NS_OK;
351 : }
352 :
353 : // Obtains the available bytes to read
354 : NS_IMETHODIMP
355 0 : nsBMPEncoder::Available(uint64_t* _retval)
356 : {
357 0 : if (!mImageBufferStart || !mImageBufferCurr) {
358 0 : return NS_BASE_STREAM_CLOSED;
359 : }
360 :
361 0 : *_retval = GetCurrentImageBufferOffset() - mImageBufferReadPoint;
362 0 : return NS_OK;
363 : }
364 :
365 : // [noscript] Reads bytes which are available
366 : NS_IMETHODIMP
367 0 : nsBMPEncoder::Read(char* aBuf, uint32_t aCount, uint32_t* _retval)
368 : {
369 0 : return ReadSegments(NS_CopySegmentToBuffer, aBuf, aCount, _retval);
370 : }
371 :
372 : // [noscript] Reads segments
373 : NS_IMETHODIMP
374 0 : nsBMPEncoder::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
375 : uint32_t aCount, uint32_t* _retval)
376 : {
377 0 : uint32_t maxCount = GetCurrentImageBufferOffset() - mImageBufferReadPoint;
378 0 : if (maxCount == 0) {
379 0 : *_retval = 0;
380 0 : return mFinished ? NS_OK : NS_BASE_STREAM_WOULD_BLOCK;
381 : }
382 :
383 0 : if (aCount > maxCount) {
384 0 : aCount = maxCount;
385 : }
386 0 : nsresult rv = aWriter(this, aClosure,
387 0 : reinterpret_cast<const char*>(mImageBufferStart +
388 0 : mImageBufferReadPoint),
389 0 : 0, aCount, _retval);
390 0 : if (NS_SUCCEEDED(rv)) {
391 0 : NS_ASSERTION(*_retval <= aCount, "bad write count");
392 0 : mImageBufferReadPoint += *_retval;
393 : }
394 : // errors returned from the writer end here!
395 0 : return NS_OK;
396 : }
397 :
398 : NS_IMETHODIMP
399 0 : nsBMPEncoder::IsNonBlocking(bool* _retval)
400 : {
401 0 : *_retval = true;
402 0 : return NS_OK;
403 : }
404 :
405 : NS_IMETHODIMP
406 0 : nsBMPEncoder::AsyncWait(nsIInputStreamCallback* aCallback,
407 : uint32_t aFlags,
408 : uint32_t aRequestedCount,
409 : nsIEventTarget* aTarget)
410 : {
411 0 : if (aFlags != 0) {
412 0 : return NS_ERROR_NOT_IMPLEMENTED;
413 : }
414 :
415 0 : if (mCallback || mCallbackTarget) {
416 0 : return NS_ERROR_UNEXPECTED;
417 : }
418 :
419 0 : mCallbackTarget = aTarget;
420 : // 0 means "any number of bytes except 0"
421 0 : mNotifyThreshold = aRequestedCount;
422 0 : if (!aRequestedCount) {
423 0 : mNotifyThreshold = 1024; // We don't want to notify incessantly
424 : }
425 :
426 : // We set the callback absolutely last, because NotifyListener uses it to
427 : // determine if someone needs to be notified. If we don't set it last,
428 : // NotifyListener might try to fire off a notification to a null target
429 : // which will generally cause non-threadsafe objects to be used off the
430 : // main thread
431 0 : mCallback = aCallback;
432 :
433 : // What we are being asked for may be present already
434 0 : NotifyListener();
435 0 : return NS_OK;
436 : }
437 :
438 : NS_IMETHODIMP
439 0 : nsBMPEncoder::CloseWithStatus(nsresult aStatus)
440 : {
441 0 : return Close();
442 : }
443 :
444 : // nsBMPEncoder::ConvertHostARGBRow
445 : //
446 : // Our colors are stored with premultiplied alphas, but we need
447 : // an output with no alpha in machine-independent byte order.
448 : //
449 : void
450 0 : nsBMPEncoder::ConvertHostARGBRow(const uint8_t* aSrc,
451 : const UniquePtr<uint8_t[]>& aDest,
452 : uint32_t aPixelWidth)
453 : {
454 0 : uint16_t bytes = BytesPerPixel(mBMPInfoHeader.bpp);
455 :
456 0 : if (mBMPInfoHeader.bpp == 32) {
457 0 : for (uint32_t x = 0; x < aPixelWidth; x++) {
458 0 : const uint32_t& pixelIn = ((const uint32_t*)(aSrc))[x];
459 0 : uint8_t* pixelOut = &aDest[x * bytes];
460 :
461 0 : pixelOut[0] = (pixelIn & 0x00ff0000) >> 16;
462 0 : pixelOut[1] = (pixelIn & 0x0000ff00) >> 8;
463 0 : pixelOut[2] = (pixelIn & 0x000000ff) >> 0;
464 0 : pixelOut[3] = (pixelIn & 0xff000000) >> 24;
465 : }
466 : } else {
467 0 : for (uint32_t x = 0; x < aPixelWidth; x++) {
468 0 : const uint32_t& pixelIn = ((const uint32_t*)(aSrc))[x];
469 0 : uint8_t* pixelOut = &aDest[x * bytes];
470 :
471 0 : pixelOut[0] = (pixelIn & 0xff0000) >> 16;
472 0 : pixelOut[1] = (pixelIn & 0x00ff00) >> 8;
473 0 : pixelOut[2] = (pixelIn & 0x0000ff) >> 0;
474 : }
475 : }
476 0 : }
477 :
478 : void
479 0 : nsBMPEncoder::NotifyListener()
480 : {
481 0 : if (mCallback &&
482 0 : (GetCurrentImageBufferOffset() - mImageBufferReadPoint >=
483 0 : mNotifyThreshold || mFinished)) {
484 0 : nsCOMPtr<nsIInputStreamCallback> callback;
485 0 : if (mCallbackTarget) {
486 0 : callback = NS_NewInputStreamReadyEvent("nsBMPEncoder::NotifyListener",
487 0 : mCallback, mCallbackTarget);
488 : } else {
489 0 : callback = mCallback;
490 : }
491 :
492 0 : NS_ASSERTION(callback, "Shouldn't fail to make the callback");
493 : // Null the callback first because OnInputStreamReady could
494 : // reenter AsyncWait
495 0 : mCallback = nullptr;
496 0 : mCallbackTarget = nullptr;
497 0 : mNotifyThreshold = 0;
498 :
499 0 : callback->OnInputStreamReady(this);
500 : }
501 0 : }
502 :
503 : // Initializes the BMP file header mBMPFileHeader to the passed in values
504 : nsresult
505 0 : nsBMPEncoder::InitFileHeader(Version aVersion, uint16_t aBPP, uint32_t aWidth,
506 : uint32_t aHeight)
507 : {
508 0 : memset(&mBMPFileHeader, 0, sizeof(mBMPFileHeader));
509 0 : mBMPFileHeader.signature[0] = 'B';
510 0 : mBMPFileHeader.signature[1] = 'M';
511 :
512 0 : if (aVersion == VERSION_3) {
513 0 : mBMPFileHeader.dataoffset = FILE_HEADER_LENGTH + InfoHeaderLength::WIN_V3;
514 : } else { // aVersion == 5
515 0 : mBMPFileHeader.dataoffset = FILE_HEADER_LENGTH + InfoHeaderLength::WIN_V5;
516 : }
517 :
518 : // The color table is present only if BPP is <= 8
519 0 : if (aBPP <= 8) {
520 0 : uint32_t numColors = 1 << aBPP;
521 0 : mBMPFileHeader.dataoffset += 4 * numColors;
522 : CheckedUint32 filesize =
523 0 : CheckedUint32(mBMPFileHeader.dataoffset) + CheckedUint32(aWidth) * aHeight;
524 0 : if (MOZ_UNLIKELY(!filesize.isValid())) {
525 0 : return NS_ERROR_INVALID_ARG;
526 : }
527 0 : mBMPFileHeader.filesize = filesize.value();
528 : } else {
529 : CheckedUint32 filesize =
530 0 : CheckedUint32(mBMPFileHeader.dataoffset) +
531 0 : (CheckedUint32(aWidth) * BytesPerPixel(aBPP) + PaddingBytes(aBPP, aWidth)) * aHeight;
532 0 : if (MOZ_UNLIKELY(!filesize.isValid())) {
533 0 : return NS_ERROR_INVALID_ARG;
534 : }
535 0 : mBMPFileHeader.filesize = filesize.value();
536 : }
537 :
538 0 : mBMPFileHeader.reserved = 0;
539 :
540 0 : return NS_OK;
541 : }
542 :
543 : #define ENCODE(pImageBufferCurr, value) \
544 : memcpy(*pImageBufferCurr, &value, sizeof value); \
545 : *pImageBufferCurr += sizeof value;
546 :
547 : // Initializes the bitmap info header mBMPInfoHeader to the passed in values
548 : nsresult
549 0 : nsBMPEncoder::InitInfoHeader(Version aVersion, uint16_t aBPP, uint32_t aWidth,
550 : uint32_t aHeight)
551 : {
552 0 : memset(&mBMPInfoHeader, 0, sizeof(mBMPInfoHeader));
553 0 : if (aVersion == VERSION_3) {
554 0 : mBMPInfoHeader.bihsize = InfoHeaderLength::WIN_V3;
555 : } else {
556 0 : MOZ_ASSERT(aVersion == VERSION_5);
557 0 : mBMPInfoHeader.bihsize = InfoHeaderLength::WIN_V5;
558 : }
559 :
560 0 : CheckedInt32 width(aWidth);
561 0 : CheckedInt32 height(aHeight);
562 0 : if (MOZ_UNLIKELY(!width.isValid() || !height.isValid())) {
563 0 : return NS_ERROR_INVALID_ARG;
564 : }
565 0 : mBMPInfoHeader.width = width.value();
566 0 : mBMPInfoHeader.height = height.value();
567 :
568 0 : mBMPInfoHeader.planes = 1;
569 0 : mBMPInfoHeader.bpp = aBPP;
570 0 : mBMPInfoHeader.compression = 0;
571 0 : mBMPInfoHeader.colors = 0;
572 0 : mBMPInfoHeader.important_colors = 0;
573 :
574 0 : CheckedUint32 check = CheckedUint32(aWidth) * BytesPerPixel(aBPP);
575 0 : if (MOZ_UNLIKELY(!check.isValid())) {
576 0 : return NS_ERROR_INVALID_ARG;
577 : }
578 :
579 0 : if (aBPP <= 8) {
580 0 : CheckedUint32 imagesize = CheckedUint32(aWidth) * aHeight;
581 0 : if (MOZ_UNLIKELY(!imagesize.isValid())) {
582 0 : return NS_ERROR_INVALID_ARG;
583 : }
584 0 : mBMPInfoHeader.image_size = imagesize.value();
585 : } else {
586 : CheckedUint32 imagesize =
587 0 : (CheckedUint32(aWidth) * BytesPerPixel(aBPP) + PaddingBytes(aBPP, aWidth)) * CheckedUint32(aHeight);
588 0 : if (MOZ_UNLIKELY(!imagesize.isValid())) {
589 0 : return NS_ERROR_INVALID_ARG;
590 : }
591 0 : mBMPInfoHeader.image_size = imagesize.value();
592 : }
593 0 : mBMPInfoHeader.xppm = 0;
594 0 : mBMPInfoHeader.yppm = 0;
595 0 : if (aVersion >= VERSION_5) {
596 0 : mBMPInfoHeader.red_mask = 0x000000FF;
597 0 : mBMPInfoHeader.green_mask = 0x0000FF00;
598 0 : mBMPInfoHeader.blue_mask = 0x00FF0000;
599 0 : mBMPInfoHeader.alpha_mask = 0xFF000000;
600 0 : mBMPInfoHeader.color_space = V5InfoHeader::COLOR_SPACE_LCS_SRGB;
601 0 : mBMPInfoHeader.white_point.r.x = 0;
602 0 : mBMPInfoHeader.white_point.r.y = 0;
603 0 : mBMPInfoHeader.white_point.r.z = 0;
604 0 : mBMPInfoHeader.white_point.g.x = 0;
605 0 : mBMPInfoHeader.white_point.g.y = 0;
606 0 : mBMPInfoHeader.white_point.g.z = 0;
607 0 : mBMPInfoHeader.white_point.b.x = 0;
608 0 : mBMPInfoHeader.white_point.b.y = 0;
609 0 : mBMPInfoHeader.white_point.b.z = 0;
610 0 : mBMPInfoHeader.gamma_red = 0;
611 0 : mBMPInfoHeader.gamma_green = 0;
612 0 : mBMPInfoHeader.gamma_blue = 0;
613 0 : mBMPInfoHeader.intent = 0;
614 0 : mBMPInfoHeader.profile_offset = 0;
615 0 : mBMPInfoHeader.profile_size = 0;
616 0 : mBMPInfoHeader.reserved = 0;
617 : }
618 :
619 0 : return NS_OK;
620 : }
621 :
622 : // Encodes the BMP file header mBMPFileHeader
623 : void
624 0 : nsBMPEncoder::EncodeFileHeader()
625 : {
626 0 : FileHeader littleEndianBFH = mBMPFileHeader;
627 0 : NativeEndian::swapToLittleEndianInPlace(&littleEndianBFH.filesize, 1);
628 0 : NativeEndian::swapToLittleEndianInPlace(&littleEndianBFH.reserved, 1);
629 0 : NativeEndian::swapToLittleEndianInPlace(&littleEndianBFH.dataoffset, 1);
630 :
631 0 : ENCODE(&mImageBufferCurr, littleEndianBFH.signature);
632 0 : ENCODE(&mImageBufferCurr, littleEndianBFH.filesize);
633 0 : ENCODE(&mImageBufferCurr, littleEndianBFH.reserved);
634 0 : ENCODE(&mImageBufferCurr, littleEndianBFH.dataoffset);
635 0 : }
636 :
637 : // Encodes the BMP infor header mBMPInfoHeader
638 : void
639 0 : nsBMPEncoder::EncodeInfoHeader()
640 : {
641 0 : V5InfoHeader littleEndianmBIH = mBMPInfoHeader;
642 0 : NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.bihsize, 1);
643 0 : NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.width, 1);
644 0 : NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.height, 1);
645 0 : NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.planes, 1);
646 0 : NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.bpp, 1);
647 0 : NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.compression, 1);
648 0 : NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.image_size, 1);
649 0 : NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.xppm, 1);
650 0 : NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.yppm, 1);
651 0 : NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.colors, 1);
652 : NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.important_colors,
653 0 : 1);
654 0 : NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.red_mask, 1);
655 0 : NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.green_mask, 1);
656 0 : NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.blue_mask, 1);
657 0 : NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.alpha_mask, 1);
658 0 : NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.color_space, 1);
659 0 : NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.white_point.r.x, 1);
660 0 : NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.white_point.r.y, 1);
661 0 : NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.white_point.r.z, 1);
662 0 : NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.white_point.g.x, 1);
663 0 : NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.white_point.g.y, 1);
664 0 : NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.white_point.g.z, 1);
665 0 : NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.white_point.b.x, 1);
666 0 : NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.white_point.b.y, 1);
667 0 : NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.white_point.b.z, 1);
668 0 : NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.gamma_red, 1);
669 0 : NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.gamma_green, 1);
670 0 : NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.gamma_blue, 1);
671 0 : NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.intent, 1);
672 0 : NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.profile_offset, 1);
673 0 : NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.profile_size, 1);
674 :
675 0 : ENCODE(&mImageBufferCurr, littleEndianmBIH.bihsize);
676 0 : ENCODE(&mImageBufferCurr, littleEndianmBIH.width);
677 0 : ENCODE(&mImageBufferCurr, littleEndianmBIH.height);
678 0 : ENCODE(&mImageBufferCurr, littleEndianmBIH.planes);
679 0 : ENCODE(&mImageBufferCurr, littleEndianmBIH.bpp);
680 0 : ENCODE(&mImageBufferCurr, littleEndianmBIH.compression);
681 0 : ENCODE(&mImageBufferCurr, littleEndianmBIH.image_size);
682 0 : ENCODE(&mImageBufferCurr, littleEndianmBIH.xppm);
683 0 : ENCODE(&mImageBufferCurr, littleEndianmBIH.yppm);
684 0 : ENCODE(&mImageBufferCurr, littleEndianmBIH.colors);
685 0 : ENCODE(&mImageBufferCurr, littleEndianmBIH.important_colors);
686 :
687 0 : if (mBMPInfoHeader.bihsize > InfoHeaderLength::WIN_V3) {
688 0 : ENCODE(&mImageBufferCurr, littleEndianmBIH.red_mask);
689 0 : ENCODE(&mImageBufferCurr, littleEndianmBIH.green_mask);
690 0 : ENCODE(&mImageBufferCurr, littleEndianmBIH.blue_mask);
691 0 : ENCODE(&mImageBufferCurr, littleEndianmBIH.alpha_mask);
692 0 : ENCODE(&mImageBufferCurr, littleEndianmBIH.color_space);
693 0 : ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.r.x);
694 0 : ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.r.y);
695 0 : ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.r.z);
696 0 : ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.g.x);
697 0 : ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.g.y);
698 0 : ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.g.z);
699 0 : ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.b.x);
700 0 : ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.b.y);
701 0 : ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.b.z);
702 0 : ENCODE(&mImageBufferCurr, littleEndianmBIH.gamma_red);
703 0 : ENCODE(&mImageBufferCurr, littleEndianmBIH.gamma_green);
704 0 : ENCODE(&mImageBufferCurr, littleEndianmBIH.gamma_blue);
705 0 : ENCODE(&mImageBufferCurr, littleEndianmBIH.intent);
706 0 : ENCODE(&mImageBufferCurr, littleEndianmBIH.profile_offset);
707 0 : ENCODE(&mImageBufferCurr, littleEndianmBIH.profile_size);
708 0 : ENCODE(&mImageBufferCurr, littleEndianmBIH.reserved);
709 : }
710 0 : }
711 :
712 : // Sets a pixel in the image buffer that doesn't have alpha data
713 : static inline void
714 0 : SetPixel24(uint8_t*& imageBufferCurr, uint8_t aRed, uint8_t aGreen,
715 : uint8_t aBlue)
716 : {
717 0 : *imageBufferCurr = aBlue;
718 0 : *(imageBufferCurr + 1) = aGreen;
719 0 : *(imageBufferCurr + 2) = aRed;
720 0 : }
721 :
722 : // Sets a pixel in the image buffer with alpha data
723 : static inline void
724 0 : SetPixel32(uint8_t*& imageBufferCurr, uint8_t aRed, uint8_t aGreen,
725 : uint8_t aBlue, uint8_t aAlpha = 0xFF)
726 : {
727 0 : *imageBufferCurr = aBlue;
728 0 : *(imageBufferCurr + 1) = aGreen;
729 0 : *(imageBufferCurr + 2) = aRed;
730 0 : *(imageBufferCurr + 3) = aAlpha;
731 0 : }
732 :
733 : // Encodes a row of image data which does not have alpha data
734 : void
735 0 : nsBMPEncoder::EncodeImageDataRow24(const uint8_t* aData)
736 : {
737 0 : for (int32_t x = 0; x < mBMPInfoHeader.width; x++) {
738 0 : uint32_t pos = x * BytesPerPixel(mBMPInfoHeader.bpp);
739 0 : SetPixel24(mImageBufferCurr, aData[pos], aData[pos + 1], aData[pos + 2]);
740 0 : mImageBufferCurr += BytesPerPixel(mBMPInfoHeader.bpp);
741 : }
742 :
743 0 : for (uint32_t x = 0; x < PaddingBytes(mBMPInfoHeader.bpp,
744 0 : mBMPInfoHeader.width); x++) {
745 0 : *mImageBufferCurr++ = 0;
746 : }
747 0 : }
748 :
749 : // Encodes a row of image data which does have alpha data
750 : void
751 0 : nsBMPEncoder::EncodeImageDataRow32(const uint8_t* aData)
752 : {
753 0 : for (int32_t x = 0; x < mBMPInfoHeader.width; x++) {
754 0 : uint32_t pos = x * BytesPerPixel(mBMPInfoHeader.bpp);
755 0 : SetPixel32(mImageBufferCurr, aData[pos], aData[pos + 1],
756 0 : aData[pos + 2], aData[pos + 3]);
757 0 : mImageBufferCurr += 4;
758 : }
759 :
760 0 : for (uint32_t x = 0; x < PaddingBytes(mBMPInfoHeader.bpp,
761 0 : mBMPInfoHeader.width); x++) {
762 0 : *mImageBufferCurr++ = 0;
763 : }
764 0 : }
|