1 : /* ***** BEGIN LICENSE BLOCK *****
2 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3 : *
4 : * The contents of this file are subject to the Mozilla Public License Version
5 : * 1.1 (the "License"); you may not use this file except in compliance with
6 : * the License. You may obtain a copy of the License at
7 : * http://www.mozilla.org/MPL/
8 : *
9 : * Software distributed under the License is distributed on an "AS IS" basis,
10 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 : * for the specific language governing rights and limitations under the
12 : * License.
13 : *
14 : * The Original Code is an implementation of a bitmap encoder.
15 : *
16 : * The Initial Developer of the Original Code is
17 : * Mozilla Foundation.
18 : * Portions created by the Initial Developer are Copyright (C) 2011
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : * Brian R. Bondy <netzen@gmail.com>
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either the GNU General Public License Version 2 or later (the "GPL"), or
26 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #include "nsCRT.h"
39 : #include "EndianMacros.h"
40 : #include "nsBMPEncoder.h"
41 : #include "prmem.h"
42 : #include "prprf.h"
43 : #include "nsString.h"
44 : #include "nsStreamUtils.h"
45 : #include "nsAutoPtr.h"
46 :
47 : using namespace mozilla;
48 :
49 24 : NS_IMPL_THREADSAFE_ISUPPORTS3(nsBMPEncoder, imgIEncoder, nsIInputStream, nsIAsyncInputStream)
50 :
51 4 : nsBMPEncoder::nsBMPEncoder() : mImageBufferStart(nsnull),
52 : mImageBufferCurr(0),
53 : mImageBufferSize(0),
54 : mImageBufferReadPoint(0),
55 : mFinished(false),
56 : mCallback(nsnull),
57 : mCallbackTarget(nsnull),
58 4 : mNotifyThreshold(0)
59 : {
60 4 : }
61 :
62 8 : nsBMPEncoder::~nsBMPEncoder()
63 : {
64 4 : if (mImageBufferStart) {
65 4 : moz_free(mImageBufferStart);
66 4 : mImageBufferStart = nsnull;
67 4 : mImageBufferCurr = nsnull;
68 : }
69 4 : }
70 :
71 : // nsBMPEncoder::InitFromData
72 : //
73 : // One output option is supported: bpp=<bpp_value>
74 : // bpp specifies the bits per pixel to use where bpp_value can be 24 or 32
75 4 : NS_IMETHODIMP nsBMPEncoder::InitFromData(const PRUint8* aData,
76 : PRUint32 aLength, // (unused,
77 : // req'd by JS)
78 : PRUint32 aWidth,
79 : PRUint32 aHeight,
80 : PRUint32 aStride,
81 : PRUint32 aInputFormat,
82 : const nsAString& aOutputOptions)
83 : {
84 : // validate input format
85 4 : if (aInputFormat != INPUT_FORMAT_RGB &&
86 : aInputFormat != INPUT_FORMAT_RGBA &&
87 : aInputFormat != INPUT_FORMAT_HOSTARGB) {
88 0 : return NS_ERROR_INVALID_ARG;
89 : }
90 :
91 : // Stride is the padded width of each row, so it better be longer
92 4 : if ((aInputFormat == INPUT_FORMAT_RGB &&
93 : aStride < aWidth * 3) ||
94 : ((aInputFormat == INPUT_FORMAT_RGBA || aInputFormat == INPUT_FORMAT_HOSTARGB) &&
95 : aStride < aWidth * 4)) {
96 0 : NS_WARNING("Invalid stride for InitFromData");
97 0 : return NS_ERROR_INVALID_ARG;
98 : }
99 :
100 : nsresult rv;
101 4 : rv = StartImageEncode(aWidth, aHeight, aInputFormat, aOutputOptions);
102 4 : if (NS_FAILED(rv)) {
103 0 : return rv;
104 : }
105 :
106 : rv = AddImageFrame(aData, aLength, aWidth, aHeight, aStride,
107 4 : aInputFormat, aOutputOptions);
108 4 : if (NS_FAILED(rv)) {
109 0 : return rv;
110 : }
111 :
112 4 : rv = EndImageEncode();
113 4 : return rv;
114 : }
115 :
116 : // Just a helper method to make it explicit in calculations that we are dealing
117 : // with bytes and not bits
118 : static inline PRUint32
119 4052 : BytesPerPixel(PRUint32 aBPP)
120 : {
121 4052 : return aBPP / 8;
122 : }
123 :
124 : // Calculates the number of padding bytes that are needed per row of image data
125 : static inline PRUint32
126 104 : PaddingBytes(PRUint32 aBPP, PRUint32 aWidth)
127 : {
128 104 : PRUint32 rowSize = aWidth * BytesPerPixel(aBPP);
129 104 : PRUint8 paddingSize = 0;
130 104 : if(rowSize % 4) {
131 0 : paddingSize = (4 - (rowSize % 4));
132 : }
133 104 : return paddingSize;
134 : }
135 :
136 : // See ::InitFromData for other info.
137 4 : NS_IMETHODIMP nsBMPEncoder::StartImageEncode(PRUint32 aWidth,
138 : PRUint32 aHeight,
139 : PRUint32 aInputFormat,
140 : const nsAString& aOutputOptions)
141 : {
142 : // can't initialize more than once
143 4 : if (mImageBufferStart || mImageBufferCurr) {
144 0 : return NS_ERROR_ALREADY_INITIALIZED;
145 : }
146 :
147 : // validate input format
148 4 : if (aInputFormat != INPUT_FORMAT_RGB &&
149 : aInputFormat != INPUT_FORMAT_RGBA &&
150 : aInputFormat != INPUT_FORMAT_HOSTARGB) {
151 0 : return NS_ERROR_INVALID_ARG;
152 : }
153 :
154 : // parse and check any provided output options
155 : PRUint32 bpp;
156 4 : nsresult rv = ParseOptions(aOutputOptions, &bpp);
157 4 : if (NS_FAILED(rv)) {
158 0 : return rv;
159 : }
160 :
161 4 : InitFileHeader(bpp, aWidth, aHeight);
162 4 : InitInfoHeader(bpp, aWidth, aHeight);
163 :
164 4 : mImageBufferSize = mBMPFileHeader.filesize;
165 4 : mImageBufferStart = static_cast<PRUint8*>(moz_malloc(mImageBufferSize));
166 4 : if (!mImageBufferStart) {
167 0 : return NS_ERROR_OUT_OF_MEMORY;
168 : }
169 4 : mImageBufferCurr = mImageBufferStart;
170 :
171 4 : EncodeFileHeader();
172 4 : EncodeInfoHeader();
173 :
174 4 : return NS_OK;
175 : }
176 :
177 : // Returns the number of bytes in the image buffer used.
178 : // For a BMP file, this is all bytes in the buffer.
179 4 : NS_IMETHODIMP nsBMPEncoder::GetImageBufferUsed(PRUint32 *aOutputSize)
180 : {
181 4 : NS_ENSURE_ARG_POINTER(aOutputSize);
182 4 : *aOutputSize = mImageBufferSize;
183 4 : return NS_OK;
184 : }
185 :
186 : // Returns a pointer to the start of the image buffer
187 4 : NS_IMETHODIMP nsBMPEncoder::GetImageBuffer(char **aOutputBuffer)
188 : {
189 4 : NS_ENSURE_ARG_POINTER(aOutputBuffer);
190 4 : *aOutputBuffer = reinterpret_cast<char*>(mImageBufferStart);
191 4 : return NS_OK;
192 : }
193 :
194 4 : NS_IMETHODIMP nsBMPEncoder::AddImageFrame(const PRUint8* aData,
195 : PRUint32 aLength, // (unused,
196 : // req'd by JS)
197 : PRUint32 aWidth,
198 : PRUint32 aHeight,
199 : PRUint32 aStride,
200 : PRUint32 aInputFormat,
201 : const nsAString& aFrameOptions)
202 : {
203 : // must be initialized
204 4 : if (!mImageBufferStart || !mImageBufferCurr) {
205 0 : return NS_ERROR_NOT_INITIALIZED;
206 : }
207 :
208 : // validate input format
209 4 : if (aInputFormat != INPUT_FORMAT_RGB &&
210 : aInputFormat != INPUT_FORMAT_RGBA &&
211 : aInputFormat != INPUT_FORMAT_HOSTARGB) {
212 0 : return NS_ERROR_INVALID_ARG;
213 : }
214 :
215 4 : static fallible_t fallible = fallible_t();
216 : nsAutoArrayPtr<PRUint8> row(new (fallible)
217 : PRUint8[mBMPInfoHeader.width *
218 12 : BytesPerPixel(mBMPInfoHeader.bpp)]);
219 4 : if (!row) {
220 0 : return NS_ERROR_OUT_OF_MEMORY;
221 : }
222 :
223 : // write each row: if we add more input formats, we may want to
224 : // generalize the conversions
225 4 : if (aInputFormat == INPUT_FORMAT_HOSTARGB) {
226 : // BMP requires RGBA with post-multiplied alpha, so we need to convert
227 100 : for (PRInt32 y = mBMPInfoHeader.height - 1; y >= 0 ; y --) {
228 96 : ConvertHostARGBRow(&aData[y * aStride], row, mBMPInfoHeader.width);
229 96 : if(mBMPInfoHeader.bpp == 24) {
230 48 : EncodeImageDataRow24(row);
231 : } else {
232 48 : EncodeImageDataRow32(row);
233 : }
234 : }
235 0 : } else if (aInputFormat == INPUT_FORMAT_RGBA) {
236 : // RBGA, but we need to strip the alpha
237 0 : for (PRInt32 y = 0; y < mBMPInfoHeader.height; y ++) {
238 0 : StripAlpha(&aData[y * aStride], row, mBMPInfoHeader.width);
239 0 : if (mBMPInfoHeader.bpp == 24) {
240 0 : EncodeImageDataRow24(row);
241 : } else {
242 0 : EncodeImageDataRow32(row);
243 : }
244 : }
245 0 : } else if (aInputFormat == INPUT_FORMAT_RGB) {
246 : // simple RBG(A), no conversion needed
247 0 : for (PRInt32 y = 0; y < mBMPInfoHeader.height; y ++) {
248 0 : if (mBMPInfoHeader.bpp == 24) {
249 0 : EncodeImageDataRow24(&aData[y * aStride]);
250 : } else {
251 0 : EncodeImageDataRow32(&aData[y * aStride]);
252 : }
253 : }
254 : } else {
255 0 : NS_NOTREACHED("Bad format type");
256 0 : return NS_ERROR_INVALID_ARG;
257 : }
258 :
259 4 : return NS_OK;
260 : }
261 :
262 :
263 4 : NS_IMETHODIMP nsBMPEncoder::EndImageEncode()
264 : {
265 : // must be initialized
266 4 : if (!mImageBufferStart || !mImageBufferCurr) {
267 0 : return NS_ERROR_NOT_INITIALIZED;
268 : }
269 :
270 4 : mFinished = true;
271 4 : NotifyListener();
272 :
273 : // if output callback can't get enough memory, it will free our buffer
274 4 : if (!mImageBufferStart || !mImageBufferCurr) {
275 0 : return NS_ERROR_OUT_OF_MEMORY;
276 : }
277 :
278 4 : 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 4 : nsBMPEncoder::ParseOptions(const nsAString& aOptions, PRUint32* bpp)
286 : {
287 : // If no parsing options just use the default of 24BPP
288 4 : if (aOptions.Length() == 0) {
289 0 : if (bpp) {
290 0 : *bpp = 24;
291 : }
292 0 : return NS_OK;
293 : }
294 :
295 : // Parse the input string into a set of name/value pairs.
296 : // From a format like: name=value;bpp=<bpp_value>;name=value
297 : // to format: [0] = name=value, [1] = bpp=<bpp_value>, [2] = name=value
298 8 : nsTArray<nsCString> nameValuePairs;
299 4 : if (!ParseString(NS_ConvertUTF16toUTF8(aOptions), ';', nameValuePairs)) {
300 0 : return NS_ERROR_INVALID_ARG;
301 : }
302 :
303 : // For each name/value pair in the set
304 8 : for (PRUint32 i = 0; i < nameValuePairs.Length(); ++i) {
305 :
306 : // Split the name value pair [0] = name, [1] = value
307 8 : nsTArray<nsCString> nameValuePair;
308 4 : if (!ParseString(nameValuePairs[i], '=', nameValuePair)) {
309 0 : return NS_ERROR_INVALID_ARG;
310 : }
311 4 : if (nameValuePair.Length() != 2) {
312 0 : return NS_ERROR_INVALID_ARG;
313 : }
314 :
315 : // Parse the bpp portion of the string name=value;bpp=<bpp_value>;name=value
316 4 : if (nameValuePair[0].Equals("bpp", nsCaseInsensitiveCStringComparator())) {
317 4 : if (nameValuePair[1].Equals("24")) {
318 2 : *bpp = 24;
319 2 : } else if (nameValuePair[1].Equals("32")) {
320 2 : *bpp = 32;
321 : } else {
322 0 : return NS_ERROR_INVALID_ARG;
323 : }
324 : }
325 : }
326 :
327 4 : return NS_OK;
328 : }
329 :
330 0 : NS_IMETHODIMP nsBMPEncoder::Close()
331 : {
332 0 : if (mImageBufferStart) {
333 0 : moz_free(mImageBufferStart);
334 0 : mImageBufferStart = nsnull;
335 0 : mImageBufferSize = 0;
336 0 : mImageBufferReadPoint = 0;
337 0 : mImageBufferCurr = nsnull;
338 : }
339 :
340 0 : return NS_OK;
341 : }
342 :
343 : // Obtains the available bytes to read
344 0 : NS_IMETHODIMP nsBMPEncoder::Available(PRUint32 *_retval)
345 : {
346 0 : if (!mImageBufferStart || !mImageBufferCurr) {
347 0 : return NS_BASE_STREAM_CLOSED;
348 : }
349 :
350 0 : *_retval = GetCurrentImageBufferOffset() - mImageBufferReadPoint;
351 0 : return NS_OK;
352 : }
353 :
354 : // [noscript] Reads bytes which are available
355 0 : NS_IMETHODIMP nsBMPEncoder::Read(char * aBuf, PRUint32 aCount,
356 : PRUint32 *_retval)
357 : {
358 0 : return ReadSegments(NS_CopySegmentToBuffer, aBuf, aCount, _retval);
359 : }
360 :
361 : // [noscript] Reads segments
362 0 : NS_IMETHODIMP nsBMPEncoder::ReadSegments(nsWriteSegmentFun aWriter,
363 : void *aClosure, PRUint32 aCount,
364 : PRUint32 *_retval)
365 : {
366 0 : PRUint32 maxCount = GetCurrentImageBufferOffset() - mImageBufferReadPoint;
367 0 : if (maxCount == 0) {
368 0 : *_retval = 0;
369 0 : return mFinished ? NS_OK : NS_BASE_STREAM_WOULD_BLOCK;
370 : }
371 :
372 0 : if (aCount > maxCount) {
373 0 : aCount = maxCount;
374 : }
375 : nsresult rv = aWriter(this, aClosure,
376 : reinterpret_cast<const char*>(mImageBufferStart +
377 : mImageBufferReadPoint),
378 0 : 0, aCount, _retval);
379 0 : if (NS_SUCCEEDED(rv)) {
380 0 : NS_ASSERTION(*_retval <= aCount, "bad write count");
381 0 : mImageBufferReadPoint += *_retval;
382 : }
383 : // errors returned from the writer end here!
384 0 : return NS_OK;
385 : }
386 :
387 : NS_IMETHODIMP
388 0 : nsBMPEncoder::IsNonBlocking(bool *_retval)
389 : {
390 0 : *_retval = true;
391 0 : return NS_OK;
392 : }
393 :
394 : NS_IMETHODIMP
395 0 : nsBMPEncoder::AsyncWait(nsIInputStreamCallback *aCallback,
396 : PRUint32 aFlags,
397 : PRUint32 aRequestedCount,
398 : nsIEventTarget *aTarget)
399 : {
400 0 : if (aFlags != 0) {
401 0 : return NS_ERROR_NOT_IMPLEMENTED;
402 : }
403 :
404 0 : if (mCallback || mCallbackTarget) {
405 0 : return NS_ERROR_UNEXPECTED;
406 : }
407 :
408 0 : mCallbackTarget = aTarget;
409 : // 0 means "any number of bytes except 0"
410 0 : mNotifyThreshold = aRequestedCount;
411 0 : if (!aRequestedCount) {
412 0 : mNotifyThreshold = 1024; // We don't want to notify incessantly
413 : }
414 :
415 : // We set the callback absolutely last, because NotifyListener uses it to
416 : // determine if someone needs to be notified. If we don't set it last,
417 : // NotifyListener might try to fire off a notification to a null target
418 : // which will generally cause non-threadsafe objects to be used off the main thread
419 0 : mCallback = aCallback;
420 :
421 : // What we are being asked for may be present already
422 0 : NotifyListener();
423 0 : return NS_OK;
424 : }
425 :
426 0 : NS_IMETHODIMP nsBMPEncoder::CloseWithStatus(nsresult aStatus)
427 : {
428 0 : return Close();
429 : }
430 :
431 : // nsBMPEncoder::ConvertHostARGBRow
432 : //
433 : // Our colors are stored with premultiplied alphas, but we need
434 : // an output with no alpha in machine-independent byte order.
435 : //
436 : void
437 96 : nsBMPEncoder::ConvertHostARGBRow(const PRUint8* aSrc, PRUint8* aDest,
438 : PRUint32 aPixelWidth)
439 : {
440 96 : int bytes = BytesPerPixel(mBMPInfoHeader.bpp);
441 :
442 96 : if (mBMPInfoHeader.bpp == 32) {
443 1328 : for (PRUint32 x = 0; x < aPixelWidth; x++) {
444 1280 : const PRUint32& pixelIn = ((const PRUint32*)(aSrc))[x];
445 1280 : PRUint8 *pixelOut = &aDest[x * bytes];
446 :
447 1280 : pixelOut[0] = (pixelIn & 0x00ff0000) >> 16;
448 1280 : pixelOut[1] = (pixelIn & 0x0000ff00) >> 8;
449 1280 : pixelOut[2] = (pixelIn & 0x000000ff) >> 0;
450 1280 : pixelOut[3] = (pixelIn & 0xff000000) >> 24;
451 : }
452 : } else {
453 1328 : for (PRUint32 x = 0; x < aPixelWidth; x++) {
454 1280 : const PRUint32& pixelIn = ((const PRUint32*)(aSrc))[x];
455 1280 : PRUint8 *pixelOut = &aDest[x * bytes];
456 :
457 1280 : pixelOut[0] = (pixelIn & 0xff0000) >> 16;
458 1280 : pixelOut[1] = (pixelIn & 0x00ff00) >> 8;
459 1280 : pixelOut[2] = (pixelIn & 0x0000ff) >> 0;
460 : }
461 : }
462 96 : }
463 :
464 : // nsBMPEncoder::StripAlpha
465 : //
466 : // Input is RGBA, output is RGB
467 : void
468 0 : nsBMPEncoder::StripAlpha(const PRUint8* aSrc, PRUint8* aDest,
469 : PRUint32 aPixelWidth)
470 : {
471 0 : for (PRUint32 x = 0; x < aPixelWidth; x ++) {
472 0 : const PRUint8* pixelIn = &aSrc[x * 4];
473 0 : PRUint8* pixelOut = &aDest[x * 3];
474 0 : pixelOut[0] = pixelIn[0];
475 0 : pixelOut[1] = pixelIn[1];
476 0 : pixelOut[2] = pixelIn[2];
477 : }
478 0 : }
479 :
480 : void
481 4 : nsBMPEncoder::NotifyListener()
482 : {
483 4 : if (mCallback &&
484 0 : (GetCurrentImageBufferOffset() - mImageBufferReadPoint >=
485 4 : mNotifyThreshold || mFinished)) {
486 0 : nsCOMPtr<nsIInputStreamCallback> callback;
487 0 : if (mCallbackTarget) {
488 0 : NS_NewInputStreamReadyEvent(getter_AddRefs(callback),
489 : mCallback,
490 0 : mCallbackTarget);
491 : } else {
492 0 : callback = mCallback;
493 : }
494 :
495 0 : NS_ASSERTION(callback, "Shouldn't fail to make the callback");
496 : // Null the callback first because OnInputStreamReady could
497 : // reenter AsyncWait
498 0 : mCallback = nsnull;
499 0 : mCallbackTarget = nsnull;
500 0 : mNotifyThreshold = 0;
501 :
502 0 : callback->OnInputStreamReady(this);
503 : }
504 4 : }
505 :
506 : // Initializes the BMP file header mBMPFileHeader to the passed in values
507 : void
508 4 : nsBMPEncoder::InitFileHeader(PRUint32 aBPP, PRUint32 aWidth, PRUint32 aHeight)
509 : {
510 4 : memset(&mBMPFileHeader, 0, sizeof(mBMPFileHeader));
511 4 : mBMPFileHeader.signature[0] = 'B';
512 4 : mBMPFileHeader.signature[1] = 'M';
513 :
514 4 : mBMPFileHeader.dataoffset = WIN_HEADER_LENGTH;
515 :
516 : // The color table is present only if BPP is <= 8
517 4 : if (aBPP <= 8) {
518 0 : PRUint32 numColors = 1 << aBPP;
519 0 : mBMPFileHeader.dataoffset += 4 * numColors;
520 0 : mBMPFileHeader.filesize = mBMPFileHeader.dataoffset + aWidth * aHeight;
521 : } else {
522 : mBMPFileHeader.filesize = mBMPFileHeader.dataoffset + (aWidth *
523 8 : BytesPerPixel(aBPP) + PaddingBytes(aBPP, aWidth)) *
524 12 : aHeight;
525 : }
526 :
527 4 : mBMPFileHeader.reserved = 0;
528 4 : mBMPFileHeader.bihsize = WIN_BIH_LENGTH;
529 4 : }
530 :
531 : // Initializes the bitmap info header mBMPInfoHeader to the passed in values
532 : void
533 4 : nsBMPEncoder::InitInfoHeader(PRUint32 aBPP, PRUint32 aWidth, PRUint32 aHeight)
534 : {
535 4 : memset(&mBMPInfoHeader, 0, sizeof(mBMPInfoHeader));
536 4 : mBMPInfoHeader.bpp = aBPP;
537 4 : mBMPInfoHeader.planes = 1;
538 4 : mBMPInfoHeader.colors = 0;
539 4 : mBMPInfoHeader.important_colors = 0;
540 4 : mBMPInfoHeader.width = aWidth;
541 4 : mBMPInfoHeader.height = aHeight;
542 4 : mBMPInfoHeader.compression = 0;
543 4 : if (aBPP <= 8) {
544 0 : mBMPInfoHeader.image_size = aWidth * aHeight;
545 : } else {
546 4 : mBMPInfoHeader.image_size = (aWidth * BytesPerPixel(aBPP) +
547 4 : PaddingBytes(aBPP, aWidth)) * aHeight;
548 : }
549 4 : mBMPInfoHeader.xppm = 0;
550 4 : mBMPInfoHeader.yppm = 0;
551 4 : }
552 :
553 : // Encodes the BMP file header mBMPFileHeader
554 : void
555 4 : nsBMPEncoder::EncodeFileHeader()
556 : {
557 4 : mozilla::image::BMPFILEHEADER littleEndianBFH = mBMPFileHeader;
558 4 : littleEndianBFH.filesize = NATIVE32_TO_LITTLE(littleEndianBFH.filesize);
559 4 : littleEndianBFH.reserved = NATIVE32_TO_LITTLE(littleEndianBFH.reserved);
560 4 : littleEndianBFH.dataoffset= NATIVE32_TO_LITTLE(littleEndianBFH.dataoffset);
561 4 : littleEndianBFH.bihsize = NATIVE32_TO_LITTLE(littleEndianBFH.bihsize);
562 :
563 : memcpy(mImageBufferCurr, &littleEndianBFH.signature,
564 4 : sizeof(littleEndianBFH.signature));
565 4 : mImageBufferCurr += sizeof(littleEndianBFH.signature);
566 : memcpy(mImageBufferCurr, &littleEndianBFH.filesize,
567 4 : sizeof(littleEndianBFH.filesize));
568 4 : mImageBufferCurr += sizeof(littleEndianBFH.filesize);
569 : memcpy(mImageBufferCurr, &littleEndianBFH.reserved,
570 4 : sizeof(littleEndianBFH.reserved));
571 4 : mImageBufferCurr += sizeof(littleEndianBFH.reserved);
572 : memcpy(mImageBufferCurr, &littleEndianBFH.dataoffset,
573 4 : sizeof(littleEndianBFH.dataoffset));
574 4 : mImageBufferCurr += sizeof(littleEndianBFH.dataoffset);
575 : memcpy(mImageBufferCurr, &littleEndianBFH.bihsize,
576 4 : sizeof(littleEndianBFH.bihsize));
577 4 : mImageBufferCurr += sizeof(littleEndianBFH.bihsize);
578 4 : }
579 :
580 : // Encodes the BMP infor header mBMPInfoHeader
581 : void
582 4 : nsBMPEncoder::EncodeInfoHeader()
583 : {
584 4 : mozilla::image::BMPINFOHEADER littleEndianmBIH = mBMPInfoHeader;
585 4 : littleEndianmBIH.width = NATIVE32_TO_LITTLE(littleEndianmBIH.width);
586 4 : littleEndianmBIH.height = NATIVE32_TO_LITTLE(littleEndianmBIH.height);
587 4 : littleEndianmBIH.planes = NATIVE16_TO_LITTLE(littleEndianmBIH.planes);
588 4 : littleEndianmBIH.bpp = NATIVE16_TO_LITTLE(littleEndianmBIH.bpp);
589 : littleEndianmBIH.compression = NATIVE32_TO_LITTLE(
590 4 : littleEndianmBIH.compression);
591 : littleEndianmBIH.image_size = NATIVE32_TO_LITTLE(
592 4 : littleEndianmBIH.image_size);
593 4 : littleEndianmBIH.xppm = NATIVE32_TO_LITTLE(littleEndianmBIH.xppm);
594 4 : littleEndianmBIH.yppm = NATIVE32_TO_LITTLE(littleEndianmBIH.yppm);
595 4 : littleEndianmBIH.colors = NATIVE32_TO_LITTLE(littleEndianmBIH.colors);
596 : littleEndianmBIH.important_colors = NATIVE32_TO_LITTLE(
597 4 : littleEndianmBIH.important_colors);
598 :
599 4 : if (mBMPFileHeader.bihsize == 12) { // OS/2 Bitmap
600 0 : memcpy(mImageBufferCurr, &littleEndianmBIH.width, 2);
601 0 : mImageBufferCurr += 2; // Uint16 in OS/2 BMPs
602 0 : memcpy(mImageBufferCurr, &littleEndianmBIH.height, 2);
603 0 : mImageBufferCurr += 2; // Uint16 in OS/2 BMPs
604 : memcpy(mImageBufferCurr, &littleEndianmBIH.planes,
605 0 : sizeof(littleEndianmBIH.planes));
606 0 : mImageBufferCurr += sizeof(littleEndianmBIH.planes);
607 : memcpy(mImageBufferCurr, &littleEndianmBIH.bpp,
608 0 : sizeof(littleEndianmBIH.bpp));
609 0 : mImageBufferCurr += sizeof(littleEndianmBIH.bpp);
610 : }
611 : else {
612 : memcpy(mImageBufferCurr, &littleEndianmBIH.width,
613 4 : sizeof(littleEndianmBIH.width));
614 4 : mImageBufferCurr += sizeof(littleEndianmBIH.width);
615 : memcpy(mImageBufferCurr, &littleEndianmBIH.height,
616 4 : sizeof(littleEndianmBIH.height));
617 4 : mImageBufferCurr += sizeof(littleEndianmBIH.height);
618 : memcpy(mImageBufferCurr, &littleEndianmBIH.planes,
619 4 : sizeof(littleEndianmBIH.planes));
620 4 : mImageBufferCurr += sizeof(littleEndianmBIH.planes);
621 : memcpy(mImageBufferCurr, &littleEndianmBIH.bpp,
622 4 : sizeof(littleEndianmBIH.bpp));
623 4 : mImageBufferCurr += sizeof(littleEndianmBIH.bpp);
624 : memcpy(mImageBufferCurr, &littleEndianmBIH.compression,
625 4 : sizeof(littleEndianmBIH.compression));
626 4 : mImageBufferCurr += sizeof(littleEndianmBIH.compression);
627 : memcpy(mImageBufferCurr, &littleEndianmBIH.image_size,
628 4 : sizeof(littleEndianmBIH.image_size));
629 4 : mImageBufferCurr += sizeof(littleEndianmBIH.image_size);
630 : memcpy(mImageBufferCurr, &littleEndianmBIH.xppm,
631 4 : sizeof(littleEndianmBIH.xppm));
632 4 : mImageBufferCurr += sizeof(littleEndianmBIH.xppm);
633 : memcpy(mImageBufferCurr, &littleEndianmBIH.yppm,
634 4 : sizeof(littleEndianmBIH.yppm));
635 4 : mImageBufferCurr += sizeof(littleEndianmBIH.yppm);
636 : memcpy(mImageBufferCurr, &littleEndianmBIH.colors,
637 4 : sizeof(littleEndianmBIH.colors));
638 4 : mImageBufferCurr += sizeof(littleEndianmBIH.colors);
639 : memcpy(mImageBufferCurr, &littleEndianmBIH.important_colors,
640 4 : sizeof(littleEndianmBIH.important_colors));
641 4 : mImageBufferCurr += sizeof(littleEndianmBIH.important_colors);
642 : }
643 4 : }
644 :
645 : // Sets a pixel in the image buffer that doesn't have alpha data
646 : static inline void
647 1280 : SetPixel24(PRUint8*& imageBufferCurr, PRUint8 aRed, PRUint8 aGreen,
648 : PRUint8 aBlue)
649 : {
650 1280 : *imageBufferCurr = aBlue;
651 1280 : *(imageBufferCurr + 1) = aGreen;
652 1280 : *(imageBufferCurr + 2) = aRed;
653 1280 : }
654 :
655 : // Sets a pixel in the image buffer with alpha data
656 : static inline void
657 1280 : SetPixel32(PRUint8*& imageBufferCurr, PRUint8 aRed, PRUint8 aGreen,
658 : PRUint8 aBlue, PRUint8 aAlpha = 0xFF)
659 : {
660 1280 : *imageBufferCurr = aBlue;
661 1280 : *(imageBufferCurr + 1) = aGreen;
662 1280 : *(imageBufferCurr + 2) = aRed;
663 1280 : *(imageBufferCurr + 3) = aAlpha;
664 1280 : }
665 :
666 : // Encodes a row of image data which does not have alpha data
667 : void
668 48 : nsBMPEncoder::EncodeImageDataRow24(const PRUint8* aData)
669 : {
670 1328 : for (PRInt32 x = 0; x < mBMPInfoHeader.width; x ++) {
671 1280 : PRUint32 pos = x * BytesPerPixel(mBMPInfoHeader.bpp);
672 1280 : SetPixel24(mImageBufferCurr, aData[pos], aData[pos + 1], aData[pos + 2]);
673 1280 : mImageBufferCurr += BytesPerPixel(mBMPInfoHeader.bpp);
674 : }
675 :
676 96 : for (PRUint32 x = 0; x < PaddingBytes(mBMPInfoHeader.bpp,
677 48 : mBMPInfoHeader.width); x ++) {
678 0 : *mImageBufferCurr++ = 0;
679 : }
680 48 : }
681 :
682 : // Encodes a row of image data which does have alpha data
683 : void
684 48 : nsBMPEncoder::EncodeImageDataRow32(const PRUint8* aData)
685 : {
686 1328 : for (PRInt32 x = 0; x < mBMPInfoHeader.width; x ++) {
687 1280 : PRUint32 pos = x * BytesPerPixel(mBMPInfoHeader.bpp);
688 2560 : SetPixel32(mImageBufferCurr, aData[pos], aData[pos + 1],
689 3840 : aData[pos + 2], aData[pos + 3]);
690 1280 : mImageBufferCurr += 4;
691 : }
692 :
693 96 : for (PRUint32 x = 0; x < PaddingBytes(mBMPInfoHeader.bpp,
694 48 : mBMPInfoHeader.width); x ++) {
695 0 : *mImageBufferCurr++ = 0;
696 : }
697 48 : }
|