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