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