xref: /haiku/src/add-ons/translators/png/PNGTranslator.cpp (revision 3af8011358bd4c624a0979336d48dabb466171ed)
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 *
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 *
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
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
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
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 // ---------------------------------------------------------------
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 // ---------------------------------------------------------------
220 PNGTranslator::~PNGTranslator()
221 {
222 }
223 
224 status_t
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
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 
295 void throw_error(png_structp ppng, png_const_charp error_msg)
296 {
297 	throw std::exception();
298 }
299 
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
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
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
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
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
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 *
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