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