1 /*****************************************************************************/
2 // PNGTranslator
3 // Written by Michael Wilber, Haiku Translation Kit Team
4 //
5 // PNGTranslator.cpp
6 //
7 // This BTranslator based object is for opening and writing
8 // PNG images.
9 //
10 //
11 // Copyright (c) 2003, Haiku Project
12 // Copyright (c) 2009, Haiku, Inc. All rights reserved.
13 //
14 // Permission is hereby granted, free of charge, to any person obtaining a
15 // copy of this software and associated documentation files (the "Software"),
16 // to deal in the Software without restriction, including without limitation
17 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
18 // and/or sell copies of the Software, and to permit persons to whom the
19 // Software is furnished to do so, subject to the following conditions:
20 //
21 // The above copyright notice and this permission notice shall be included
22 // in all copies or substantial portions of the Software.
23 //
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
25 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
27 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
30 // DEALINGS IN THE SOFTWARE.
31 /*****************************************************************************/
32
33
34 #include "PNGTranslator.h"
35
36 #include <stdio.h>
37 #include <string.h>
38
39 #include <Catalog.h>
40 #include <OS.h>
41 #define PNG_NO_PEDANTIC_WARNINGS
42 #include <png.h>
43
44 #include "PNGView.h"
45
46 #undef B_TRANSLATION_CONTEXT
47 #define B_TRANSLATION_CONTEXT "PNGTranslator"
48
49 // The input formats that this translator supports.
50 static const translation_format sInputFormats[] = {
51 {
52 B_PNG_FORMAT,
53 B_TRANSLATOR_BITMAP,
54 PNG_IN_QUALITY,
55 PNG_IN_CAPABILITY,
56 "image/png",
57 "PNG image"
58 },
59 {
60 B_PNG_FORMAT,
61 B_TRANSLATOR_BITMAP,
62 PNG_IN_QUALITY,
63 PNG_IN_CAPABILITY,
64 "image/x-png",
65 "PNG image"
66 },
67 {
68 B_TRANSLATOR_BITMAP,
69 B_TRANSLATOR_BITMAP,
70 BBT_IN_QUALITY,
71 BBT_IN_CAPABILITY,
72 "image/x-be-bitmap",
73 "Be Bitmap Format (PNGTranslator)"
74 }
75 };
76
77 // The output formats that this translator supports.
78 static const translation_format sOutputFormats[] = {
79 {
80 B_PNG_FORMAT,
81 B_TRANSLATOR_BITMAP,
82 PNG_OUT_QUALITY,
83 PNG_OUT_CAPABILITY,
84 "image/png",
85 "PNG image"
86 },
87 {
88 B_TRANSLATOR_BITMAP,
89 B_TRANSLATOR_BITMAP,
90 BBT_OUT_QUALITY,
91 BBT_OUT_CAPABILITY,
92 "image/x-be-bitmap",
93 "Be Bitmap Format (PNGTranslator)"
94 }
95 };
96
97 // Default settings for the Translator
98 static const TranSetting sDefaultSettings[] = {
99 {B_TRANSLATOR_EXT_HEADER_ONLY, TRAN_SETTING_BOOL, false},
100 {B_TRANSLATOR_EXT_DATA_ONLY, TRAN_SETTING_BOOL, false},
101 {PNG_SETTING_INTERLACE, TRAN_SETTING_INT32, PNG_INTERLACE_NONE}
102 // interlacing is off by default
103 };
104
105 const uint32 kNumInputFormats = sizeof(sInputFormats) / sizeof(translation_format);
106 const uint32 kNumOutputFormats = sizeof(sOutputFormats) / sizeof(translation_format);
107 const uint32 kNumDefaultSettings = sizeof(sDefaultSettings) / sizeof(TranSetting);
108
109
110 // ---------------------------------------------------------------
111 // make_nth_translator
112 //
113 // Creates a PNGTranslator object to be used by BTranslatorRoster
114 //
115 // Preconditions:
116 //
117 // Parameters: n, The translator to return. Since
118 // PNGTranslator only publishes one
119 // translator, it only returns a
120 // PNGTranslator if n == 0
121 //
122 // you, The image_id of the add-on that
123 // contains code (not used).
124 //
125 // flags, Has no meaning yet, should be 0.
126 //
127 // Postconditions:
128 //
129 // Returns: NULL if n is not zero,
130 // a new PNGTranslator if n is zero
131 // ---------------------------------------------------------------
132 BTranslator *
make_nth_translator(int32 n,image_id you,uint32 flags,...)133 make_nth_translator(int32 n, image_id you, uint32 flags, ...)
134 {
135 if (!n)
136 return new(std::nothrow) PNGTranslator();
137 else
138 return NULL;
139 }
140
141 /* The png_jmpbuf() macro, used in error handling, became available in
142 * libpng version 1.0.6. If you want to be able to run your code with older
143 * versions of libpng, you must define the macro yourself (but only if it
144 * is not already defined by libpng!).
145 */
146
147 #ifndef png_jmpbuf
148 # define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf)
149 #endif
150
151 //// libpng Callback functions!
152
153 BPositionIO *
get_pio(png_structp ppng)154 get_pio(png_structp ppng)
155 {
156 BPositionIO *pio = NULL;
157 pio = static_cast<BPositionIO *>(png_get_io_ptr(ppng));
158 return pio;
159 }
160
161 void
pngcb_read_data(png_structp ppng,png_bytep pdata,png_size_t length)162 pngcb_read_data(png_structp ppng, png_bytep pdata, png_size_t length)
163 {
164 BPositionIO *pio = get_pio(ppng);
165 pio->Read(pdata, static_cast<size_t>(length));
166 }
167
168 void
pngcb_write_data(png_structp ppng,png_bytep pdata,png_size_t length)169 pngcb_write_data(png_structp ppng, png_bytep pdata, png_size_t length)
170 {
171 BPositionIO *pio = get_pio(ppng);
172 pio->Write(pdata, static_cast<size_t>(length));
173 }
174
175 void
pngcb_flush_data(png_structp ppng)176 pngcb_flush_data(png_structp ppng)
177 {
178 // I don't think I really need to do anything here
179 }
180
181 // ---------------------------------------------------------------
182 // Constructor
183 //
184 // Sets up the version info and the name of the translator so that
185 // these values can be returned when they are requested.
186 //
187 // Preconditions:
188 //
189 // Parameters:
190 //
191 // Postconditions:
192 //
193 // Returns:
194 // ---------------------------------------------------------------
PNGTranslator()195 PNGTranslator::PNGTranslator()
196 : BaseTranslator(B_TRANSLATE("PNG images"),
197 B_TRANSLATE("PNG image translator"),
198 PNG_TRANSLATOR_VERSION,
199 sInputFormats, kNumInputFormats,
200 sOutputFormats, kNumOutputFormats,
201 "PNGTranslator_Settings",
202 sDefaultSettings, kNumDefaultSettings,
203 B_TRANSLATOR_BITMAP, B_PNG_FORMAT)
204 {
205 }
206
207 // ---------------------------------------------------------------
208 // Destructor
209 //
210 // Does nothing
211 //
212 // Preconditions:
213 //
214 // Parameters:
215 //
216 // Postconditions:
217 //
218 // Returns:
219 // ---------------------------------------------------------------
~PNGTranslator()220 PNGTranslator::~PNGTranslator()
221 {
222 }
223
224 status_t
identify_png_header(BPositionIO * inSource,translator_info * outInfo)225 identify_png_header(BPositionIO *inSource, translator_info *outInfo)
226 {
227 const int32 kSigSize = 8;
228 uint8 buf[kSigSize];
229 if (inSource->Read(buf, kSigSize) != kSigSize)
230 return B_NO_TRANSLATOR;
231 if (png_sig_cmp(buf, 0, kSigSize))
232 // if first 8 bytes of stream don't match PNG signature bail
233 return B_NO_TRANSLATOR;
234
235 if (outInfo) {
236 outInfo->type = B_PNG_FORMAT;
237 outInfo->group = B_TRANSLATOR_BITMAP;
238 outInfo->quality = PNG_IN_QUALITY;
239 outInfo->capability = PNG_IN_CAPABILITY;
240 strcpy(outInfo->MIME, "image/png");
241 strlcpy(outInfo->name, B_TRANSLATE("PNG image"),
242 sizeof(outInfo->name));
243 }
244
245 return B_OK;
246 }
247
248 // ---------------------------------------------------------------
249 // DerivedIdentify
250 //
251 // Examines the data from inSource and determines if it is in a
252 // format that this translator knows how to work with.
253 //
254 // Preconditions:
255 //
256 // Parameters: inSource, where the data to examine is
257 //
258 // inFormat, a hint about the data in inSource,
259 // it is ignored since it is only a hint
260 //
261 // ioExtension, configuration settings for the
262 // translator (not used)
263 //
264 // outInfo, information about what data is in
265 // inSource and how well this translator
266 // can handle that data is stored here
267 //
268 // outType, The format that the user wants
269 // the data in inSource to be
270 // converted to
271 //
272 // Postconditions:
273 //
274 // Returns: B_NO_TRANSLATOR, if this translator can't handle
275 // the data in inSource
276 //
277 // B_ERROR, if there was an error converting the data to the host
278 // format
279 //
280 // B_BAD_VALUE, if the settings in ioExtension are bad
281 //
282 // B_OK, if this translator understood the data and there were
283 // no errors found
284 //
285 // Other errors if BPositionIO::Read() returned an error value
286 // ---------------------------------------------------------------
287 status_t
DerivedIdentify(BPositionIO * inSource,const translation_format * inFormat,BMessage * ioExtension,translator_info * outInfo,uint32 outType)288 PNGTranslator::DerivedIdentify(BPositionIO *inSource,
289 const translation_format *inFormat, BMessage *ioExtension,
290 translator_info *outInfo, uint32 outType)
291 {
292 return identify_png_header(inSource, outInfo);
293 }
294
throw_error(png_structp ppng,png_const_charp error_msg)295 void throw_error(png_structp ppng, png_const_charp error_msg)
296 {
297 throw std::exception();
298 }
299
alert_warning(png_structp ppng,png_const_charp error_msg)300 void alert_warning(png_structp ppng, png_const_charp error_msg)
301 {
302 // These are only warnings and the image can still be decoded. We have no
303 // way to convey this to the calling app using the current translator API,
304 // so the warnings are just ignored.
305 }
306
307 status_t
translate_from_png_to_bits(BPositionIO * inSource,BPositionIO * outDestination)308 PNGTranslator::translate_from_png_to_bits(BPositionIO *inSource,
309 BPositionIO *outDestination)
310 {
311 if (identify_png_header(inSource, NULL) != B_OK)
312 return B_NO_TRANSLATOR;
313
314 status_t result = B_ERROR;
315 // if a libpng errors before this is set
316 // to a different value, the above is what
317 // will be returned from this function
318
319 bool bheaderonly = false, bdataonly = false;
320
321 // for storing decoded PNG row data
322 uint8 **prows = NULL, *prow = NULL;
323 png_uint_32 nalloc = 0;
324
325 png_structp ppng = NULL;
326 png_infop pinfo = NULL;
327 while (ppng == NULL) {
328 // create PNG read pointer with default error handling routines
329 ppng = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
330 if (!ppng)
331 break;
332
333 // alocate / init memory for image information
334 pinfo = png_create_info_struct(ppng);
335 if (!pinfo)
336 break;
337
338 // set up erorr handling to use C++ exceptions instead of setjmp
339 png_set_error_fn(ppng, png_get_error_ptr(ppng), throw_error,
340 alert_warning);
341
342 try {
343 // set read callback function
344 png_set_read_fn(ppng, static_cast<void *>(inSource), pngcb_read_data);
345
346 // Read in PNG image info
347 png_set_sig_bytes(ppng, 8);
348 png_read_info(ppng, pinfo);
349
350 png_uint_32 width, height;
351 int bit_depth, color_type, interlace_type;
352 png_get_IHDR(ppng, pinfo, &width, &height, &bit_depth, &color_type,
353 &interlace_type, NULL, NULL);
354
355 // Setup image transformations to make converting it easier
356 bool balpha = false;
357
358 if (bit_depth == 16)
359 png_set_strip_16(ppng);
360 else if (bit_depth < 8)
361 png_set_packing(ppng);
362
363 if (color_type == PNG_COLOR_TYPE_PALETTE)
364 png_set_palette_to_rgb(ppng);
365
366 if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
367 // In order to convert from low-depth gray images to RGB,
368 // I first need to convert them to grayscale, 8 bpp
369 png_set_expand_gray_1_2_4_to_8(ppng);
370 }
371
372 if (png_get_valid(ppng, pinfo, PNG_INFO_tRNS)) {
373 // if there is transparency data in the
374 // PNG, but not in the form of an alpha channel
375 balpha = true;
376 png_set_tRNS_to_alpha(ppng);
377 }
378
379 // change RGB to BGR as it is in 'bits'
380 if (color_type & PNG_COLOR_MASK_COLOR)
381 png_set_bgr(ppng);
382
383 // have libpng convert gray to RGB for me
384 if (color_type == PNG_COLOR_TYPE_GRAY ||
385 color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
386 png_set_gray_to_rgb(ppng);
387 }
388
389 if (color_type & PNG_COLOR_MASK_ALPHA) {
390 // if image contains an alpha channel
391 balpha = true;
392 }
393
394 if (!balpha) {
395 // add filler byte for images without alpha
396 // so that the pixels are 4 bytes each
397 png_set_filler(ppng, 0xff, PNG_FILLER_AFTER);
398 }
399
400 // Check that transformed PNG rowbytes matches
401 // what is expected
402 const int32 kbytes = 4;
403 png_uint_32 rowbytes = png_get_rowbytes(ppng, pinfo);
404 if (rowbytes < kbytes * width)
405 rowbytes = kbytes * width;
406
407 if (!bdataonly) {
408 // Write out the data to outDestination
409 // Construct and write Be bitmap header
410 TranslatorBitmap bitsHeader;
411 bitsHeader.magic = B_TRANSLATOR_BITMAP;
412 bitsHeader.bounds.left = 0;
413 bitsHeader.bounds.top = 0;
414 bitsHeader.bounds.right = width - 1;
415 bitsHeader.bounds.bottom = height - 1;
416 bitsHeader.rowBytes = 4 * width;
417 if (balpha)
418 bitsHeader.colors = B_RGBA32;
419 else
420 bitsHeader.colors = B_RGB32;
421 bitsHeader.dataSize = bitsHeader.rowBytes * height;
422 if (swap_data(B_UINT32_TYPE, &bitsHeader,
423 sizeof(TranslatorBitmap), B_SWAP_HOST_TO_BENDIAN) != B_OK) {
424 result = B_ERROR;
425 break;
426 }
427 outDestination->Write(&bitsHeader, sizeof(TranslatorBitmap));
428
429 if (bheaderonly) {
430 result = B_OK;
431 break;
432 }
433 }
434
435 if (interlace_type == PNG_INTERLACE_NONE) {
436 // allocate buffer for storing PNG row
437 prow = new(std::nothrow) uint8[rowbytes];
438 if (!prow) {
439 result = B_NO_MEMORY;
440 break;
441 }
442 for (png_uint_32 i = 0; i < height; i++) {
443 png_read_row(ppng, prow, NULL);
444 outDestination->Write(prow, width * kbytes);
445 }
446
447 // finish reading, pass NULL for info because I
448 // don't need the extra data
449 png_read_end(ppng, NULL);
450 result = B_OK;
451
452 break;
453
454 } else {
455 // interlaced PNG image
456 prows = new(std::nothrow) uint8 *[height];
457 if (!prows) {
458 result = B_NO_MEMORY;
459 break;
460 }
461 // allocate enough memory to store the whole image
462 for (nalloc = 0; nalloc < height; nalloc++) {
463 prows[nalloc] = new(std::nothrow) uint8[rowbytes];
464 if (!prows[nalloc])
465 break;
466 }
467
468 if (nalloc < height)
469 result = B_NO_MEMORY;
470 else {
471 png_read_image(ppng, prows);
472
473 for (png_uint_32 i = 0; i < height; i++)
474 outDestination->Write(prows[i], width * kbytes);
475
476 result = B_OK;
477 // If png_read_end throws an exception, we still accept
478 // the image as valid.
479 png_read_end(ppng, NULL);
480 }
481
482 break;
483 }
484 } catch (std::exception& e) {
485 // An error occured, abort decoding and cleanup
486 break;
487 }
488 }
489
490 if (ppng) {
491 delete[] prow;
492 prow = NULL;
493
494 // delete row pointers and array of pointers to rows
495 while (nalloc) {
496 nalloc--;
497 delete[] prows[nalloc];
498 }
499 delete[] prows;
500 prows = NULL;
501
502 // free PNG handle / info structures
503 if (!pinfo)
504 png_destroy_read_struct(&ppng, NULL, NULL);
505 else
506 png_destroy_read_struct(&ppng, &pinfo, NULL);
507 }
508
509 return result;
510 }
511
512 status_t
translate_from_png(BPositionIO * inSource,uint32 outType,BPositionIO * outDestination)513 PNGTranslator::translate_from_png(BPositionIO *inSource, uint32 outType,
514 BPositionIO *outDestination)
515 {
516 if (outType == B_TRANSLATOR_BITMAP)
517 return translate_from_png_to_bits(inSource, outDestination);
518 else {
519 // Translate from PNG to PNG
520 translate_direct_copy(inSource, outDestination);
521 return B_OK;
522 }
523 }
524
525 // Convert width pixels from pbits to PNG format, storing the
526 // result in ppng
527 status_t
pix_bits_to_png(uint8 * pbits,uint8 * ppng,color_space fromspace,uint32 width,const color_map * pmap,int32 bitsBytesPerPixel)528 pix_bits_to_png(uint8 *pbits, uint8 *ppng, color_space fromspace,
529 uint32 width, const color_map *pmap, int32 bitsBytesPerPixel)
530 {
531 status_t bytescopied = 0;
532 uint16 val;
533
534 switch (fromspace) {
535 case B_RGBA32:
536 bytescopied = width * bitsBytesPerPixel;
537 memcpy(ppng, pbits, bytescopied);
538 break;
539
540 case B_RGB32:
541 case B_RGB24:
542 bytescopied = width * bitsBytesPerPixel;
543 while (width--) {
544 memcpy(ppng, pbits, 3);
545 ppng += 3;
546 pbits += bitsBytesPerPixel;
547 }
548 break;
549
550 case B_RGBA32_BIG:
551 bytescopied = width * 4;
552 while (width--) {
553 ppng[0] = pbits[3];
554 ppng[1] = pbits[2];
555 ppng[2] = pbits[1];
556 ppng[3] = pbits[0];
557
558 ppng += 4;
559 pbits += 4;
560 }
561 break;
562
563 case B_CMYA32:
564 bytescopied = width * 4;
565 while (width--) {
566 ppng[0] = 255 - pbits[2];
567 ppng[1] = 255 - pbits[1];
568 ppng[2] = 255 - pbits[0];
569 ppng[3] = pbits[3];
570
571 ppng += 4;
572 pbits += 4;
573 }
574 break;
575
576 case B_CMYK32:
577 {
578 int32 comp;
579 bytescopied = width * 3;
580 while (width--) {
581 comp = 255 - pbits[2] - pbits[3];
582 ppng[0] = (comp < 0) ? 0 : comp;
583
584 comp = 255 - pbits[1] - pbits[3];
585 ppng[1] = (comp < 0) ? 0 : comp;
586
587 comp = 255 - pbits[0] - pbits[3];
588 ppng[2] = (comp < 0) ? 0 : comp;
589
590 ppng += 3;
591 pbits += 4;
592 }
593 break;
594 }
595
596 case B_CMY32:
597 case B_CMY24:
598 bytescopied = width * 3;
599 while (width--) {
600 ppng[0] = 255 - pbits[2];
601 ppng[1] = 255 - pbits[1];
602 ppng[2] = 255 - pbits[0];
603
604 ppng += 3;
605 pbits += bitsBytesPerPixel;
606 }
607 break;
608
609 case B_RGB16:
610 case B_RGB16_BIG:
611 bytescopied = width * 3;
612 while (width--) {
613 if (fromspace == B_RGB16)
614 val = pbits[0] + (pbits[1] << 8);
615 else
616 val = pbits[1] + (pbits[0] << 8);
617
618 ppng[0] =
619 ((val & 0x1f) << 3) | ((val & 0x1f) >> 2);
620 ppng[1] =
621 ((val & 0x7e0) >> 3) | ((val & 0x7e0) >> 9);
622 ppng[2] =
623 ((val & 0xf800) >> 8) | ((val & 0xf800) >> 13);
624
625 ppng += 3;
626 pbits += 2;
627 }
628 break;
629
630 case B_RGB15:
631 case B_RGB15_BIG:
632 bytescopied = width * 3;
633 while (width--) {
634 if (fromspace == B_RGB15)
635 val = pbits[0] + (pbits[1] << 8);
636 else
637 val = pbits[1] + (pbits[0] << 8);
638 ppng[0] =
639 ((val & 0x1f) << 3) | ((val & 0x1f) >> 2);
640 ppng[1] =
641 ((val & 0x3e0) >> 2) | ((val & 0x3e0) >> 7);
642 ppng[2] =
643 ((val & 0x7c00) >> 7) | ((val & 0x7c00) >> 12);
644
645 ppng += 3;
646 pbits += 2;
647 }
648 break;
649
650 case B_RGBA15:
651 case B_RGBA15_BIG:
652 bytescopied = width * 4;
653 while (width--) {
654 if (fromspace == B_RGBA15)
655 val = pbits[0] + (pbits[1] << 8);
656 else
657 val = pbits[1] + (pbits[0] << 8);
658 ppng[0] =
659 ((val & 0x1f) << 3) | ((val & 0x1f) >> 2);
660 ppng[1] =
661 ((val & 0x3e0) >> 2) | ((val & 0x3e0) >> 7);
662 ppng[2] =
663 ((val & 0x7c00) >> 7) | ((val & 0x7c00) >> 12);
664 ppng[3] = (val & 0x8000) ? 255 : 0;
665
666 ppng += 4;
667 pbits += 2;
668 }
669 break;
670
671 case B_RGB32_BIG:
672 bytescopied = width * 3;
673 while (width--) {
674 ppng[0] = pbits[3];
675 ppng[1] = pbits[2];
676 ppng[2] = pbits[1];
677
678 ppng += 3;
679 pbits += 4;
680 }
681 break;
682
683 case B_RGB24_BIG:
684 bytescopied = width * 3;
685 while (width--) {
686 ppng[0] = pbits[2];
687 ppng[1] = pbits[1];
688 ppng[2] = pbits[0];
689
690 ppng += 3;
691 pbits += 3;
692 }
693 break;
694
695 case B_CMAP8:
696 {
697 rgb_color c;
698 bytescopied = width * 3;
699 while (width--) {
700 c = pmap->color_list[pbits[0]];
701 ppng[0] = c.blue;
702 ppng[1] = c.green;
703 ppng[2] = c.red;
704
705 ppng += 3;
706 pbits++;
707 }
708 break;
709 }
710
711 case B_GRAY8:
712 bytescopied = width;
713 memcpy(ppng, pbits, bytescopied);
714 break;
715
716 default:
717 bytescopied = B_ERROR;
718 break;
719 } // switch (fromspace)
720
721 return bytescopied;
722 }
723
724 status_t
translate_from_bits_to_png(BPositionIO * inSource,BPositionIO * outDestination)725 PNGTranslator::translate_from_bits_to_png(BPositionIO *inSource,
726 BPositionIO *outDestination)
727 {
728 TranslatorBitmap bitsHeader;
729
730 status_t result;
731
732 result = identify_bits_header(inSource, NULL, &bitsHeader);
733 if (result != B_OK)
734 return result;
735
736 const color_map *pmap = NULL;
737 if (bitsHeader.colors == B_CMAP8) {
738 pmap = system_colors();
739 if (!pmap)
740 return B_ERROR;
741 }
742
743 png_uint_32 width, height;
744 width = static_cast<png_uint_32>(bitsHeader.bounds.Width() + 1);
745 height = static_cast<png_uint_32>(bitsHeader.bounds.Height() + 1);
746
747 int32 pngBytesPerPixel = 0;
748 int bit_depth, color_type, interlace_type;
749 bit_depth = 8;
750 switch (bitsHeader.colors) {
751 case B_RGBA32:
752 case B_RGBA32_BIG:
753 case B_CMYA32:
754 case B_RGBA15:
755 case B_RGBA15_BIG:
756 pngBytesPerPixel = 4;
757 color_type = PNG_COLOR_TYPE_RGB_ALPHA;
758 break;
759
760 case B_RGB32:
761 case B_RGB32_BIG:
762 case B_RGB24:
763 case B_RGB24_BIG:
764 case B_CMY32:
765 case B_CMYK32:
766 case B_CMY24:
767 case B_RGB16:
768 case B_RGB16_BIG:
769 case B_RGB15:
770 case B_RGB15_BIG:
771 pngBytesPerPixel = 3;
772 color_type = PNG_COLOR_TYPE_RGB;
773 break;
774
775 // ADD SUPPORT FOR B_CMAP8 HERE (later)
776
777 case B_GRAY8:
778 pngBytesPerPixel = 1;
779 color_type = PNG_COLOR_TYPE_GRAY;
780 break;
781
782 default:
783 return B_NO_TRANSLATOR;
784 }
785 interlace_type = fSettings->SetGetInt32(PNG_SETTING_INTERLACE);
786
787 int32 bitsBytesPerPixel = 0;
788 switch (bitsHeader.colors) {
789 case B_RGBA32:
790 case B_RGBA32_BIG:
791 case B_RGB32:
792 case B_RGB32_BIG:
793 case B_CMYA32:
794 case B_CMYK32:
795 case B_CMY32:
796 bitsBytesPerPixel = 4;
797 break;
798
799 case B_RGB24:
800 case B_RGB24_BIG:
801 case B_CMY24:
802 bitsBytesPerPixel = 3;
803 break;
804
805 case B_RGB16:
806 case B_RGB16_BIG:
807 case B_RGBA15:
808 case B_RGBA15_BIG:
809 case B_RGB15:
810 case B_RGB15_BIG:
811 bitsBytesPerPixel = 2;
812 break;
813
814 case B_GRAY8:
815 case B_CMAP8:
816 bitsBytesPerPixel = 1;
817 break;
818
819 default:
820 return B_NO_TRANSLATOR;
821 };
822
823 uint8 *pbitsrow = NULL, *prow = NULL;
824 // row buffers
825 // image buffer for writing whole png image at once
826 uint8 **prows = NULL;
827 png_uint_32 nalloc = 0;
828
829 png_structp ppng = NULL;
830 png_infop pinfo = NULL;
831
832 result = B_NO_TRANSLATOR;
833 while (ppng == NULL) {
834 // create PNG read pointer with default error handling routines
835 ppng = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,
836 NULL, NULL);
837 if (!ppng) {
838 result = B_ERROR;
839 break;
840 }
841 // alocate / init memory for image information
842 pinfo = png_create_info_struct(ppng);
843 if (!pinfo) {
844 result = B_ERROR;
845 break;
846 }
847
848 // set up erorr handling to use C++ exceptions instead of setjmp
849 png_set_error_fn(ppng, png_get_error_ptr(ppng), throw_error,
850 alert_warning);
851
852 try {
853 png_set_write_fn(ppng, static_cast<void *>(outDestination),
854 pngcb_write_data, pngcb_flush_data);
855
856 // Allocate memory needed to buffer image data
857 pbitsrow = new(std::nothrow) uint8[bitsHeader.rowBytes];
858 if (!pbitsrow) {
859 result = B_NO_MEMORY;
860 break;
861 }
862 if (interlace_type == PNG_INTERLACE_NONE) {
863 prow = new(std::nothrow) uint8[width * pngBytesPerPixel];
864 if (!prow) {
865 result = B_NO_MEMORY;
866 break;
867 }
868 } else {
869 prows = new(std::nothrow) uint8 *[height];
870 if (!prows) {
871 result = B_NO_MEMORY;
872 break;
873 }
874 // allocate enough memory to store the whole image
875 for (nalloc = 0; nalloc < height; nalloc++) {
876 prows[nalloc] =
877 new(std::nothrow) uint8[width * pngBytesPerPixel];
878 if (!prows[nalloc])
879 break;
880 }
881 if (nalloc < height) {
882 result = B_NO_MEMORY;
883 // clear out rest of the pointers,
884 // so we don't call delete[] with invalid pointers
885 for (; nalloc < height; nalloc++)
886 prows[nalloc] = NULL;
887 break;
888 }
889 }
890
891 // Specify image info
892 png_set_IHDR(ppng, pinfo, width, height, bit_depth, color_type,
893 interlace_type, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
894 png_write_info(ppng, pinfo);
895
896 png_set_bgr(ppng);
897
898 // write out image data
899 if (interlace_type == PNG_INTERLACE_NONE) {
900 for (png_uint_32 i = 0; i < height; i++) {
901 inSource->Read(pbitsrow, bitsHeader.rowBytes);
902
903 pix_bits_to_png(pbitsrow, prow, bitsHeader.colors, width,
904 pmap, bitsBytesPerPixel);
905
906 png_write_row(ppng, prow);
907 }
908 } else {
909 for (png_uint_32 i = 0; i < height; i++) {
910 inSource->Read(pbitsrow, bitsHeader.rowBytes);
911
912 pix_bits_to_png(pbitsrow, prows[i], bitsHeader.colors,
913 width, pmap, bitsBytesPerPixel);
914 }
915 png_write_image(ppng, prows);
916 }
917 result = B_OK;
918 // If png_read_end throws an exception, we still accept
919 // the image as valid.
920 png_write_end(ppng, NULL);
921
922 break;
923 } catch(std::exception& e) {
924 break;
925 }
926 }
927
928 if (ppng) {
929 delete[] pbitsrow;
930 pbitsrow = NULL;
931
932 delete[] prow;
933 prow = NULL;
934
935 // delete row pointers and array of pointers to rows
936 while (nalloc) {
937 nalloc--;
938 delete[] prows[nalloc];
939 }
940 delete[] prows;
941 prows = NULL;
942
943 // free PNG handle / info structures
944 if (!pinfo)
945 png_destroy_write_struct(&ppng, NULL);
946 else
947 png_destroy_write_struct(&ppng, &pinfo);
948 }
949
950 return result;
951 }
952
953 // ---------------------------------------------------------------
954 // DerivedTranslate
955 //
956 // Translates the data in inSource to the type outType and stores
957 // the translated data in outDestination.
958 //
959 // Preconditions:
960 //
961 // Parameters: inSource, the data to be translated
962 //
963 // inInfo, hint about the data in inSource (not used)
964 //
965 // ioExtension, configuration options for the
966 // translator
967 //
968 // outType, the type to convert inSource to
969 //
970 // outDestination, where the translated data is
971 // put
972 //
973 // baseType, indicates whether inSource is in the
974 // bits format, not in the bits format or
975 // is unknown
976 //
977 // Postconditions:
978 //
979 // Returns: B_BAD_VALUE, if the options in ioExtension are bad
980 //
981 // B_NO_TRANSLATOR, if this translator doesn't understand the data
982 //
983 // B_ERROR, if there was an error allocating memory or converting
984 // data
985 //
986 // B_OK, if all went well
987 // ---------------------------------------------------------------
988 status_t
DerivedTranslate(BPositionIO * inSource,const translator_info * inInfo,BMessage * ioExtension,uint32 outType,BPositionIO * outDestination,int32 baseType)989 PNGTranslator::DerivedTranslate(BPositionIO *inSource,
990 const translator_info *inInfo, BMessage *ioExtension, uint32 outType,
991 BPositionIO *outDestination, int32 baseType)
992 {
993 if (baseType == 1)
994 // if inSource is in bits format
995 return translate_from_bits_to_png(inSource, outDestination);
996 else if (baseType == 0)
997 // if inSource is NOT in bits format
998 return translate_from_png(inSource, outType, outDestination);
999 else
1000 return B_NO_TRANSLATOR;
1001 }
1002
1003 BView *
NewConfigView(TranslatorSettings * settings)1004 PNGTranslator::NewConfigView(TranslatorSettings *settings)
1005 {
1006 return new(std::nothrow) PNGView(BRect(0, 0, PNG_VIEW_WIDTH, PNG_VIEW_HEIGHT),
1007 B_TRANSLATE("PNGTranslator Settings"), B_FOLLOW_ALL,
1008 B_WILL_DRAW, settings);
1009 }
1010
1011