xref: /haiku/src/add-ons/translators/png/PNGTranslator.cpp (revision 72156a402f54ea4be9dc3e3e9704c612f7d9ad16)
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 
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_TRANSLATE_CONTEXT
47 #define B_TRANSLATE_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 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 		strcpy(outInfo->name, B_TRANSLATE("PNG image"));
242 	}
243 
244 	return B_OK;
245 }
246 
247 // ---------------------------------------------------------------
248 // DerivedIdentify
249 //
250 // Examines the data from inSource and determines if it is in a
251 // format that this translator knows how to work with.
252 //
253 // Preconditions:
254 //
255 // Parameters:	inSource,	where the data to examine is
256 //
257 //				inFormat,	a hint about the data in inSource,
258 //							it is ignored since it is only a hint
259 //
260 //				ioExtension,	configuration settings for the
261 //								translator (not used)
262 //
263 //				outInfo,	information about what data is in
264 //							inSource and how well this translator
265 //							can handle that data is stored here
266 //
267 //				outType,	The format that the user wants
268 //							the data in inSource to be
269 //							converted to
270 //
271 // Postconditions:
272 //
273 // Returns: B_NO_TRANSLATOR,	if this translator can't handle
274 //								the data in inSource
275 //
276 // B_ERROR,	if there was an error converting the data to the host
277 //			format
278 //
279 // B_BAD_VALUE, if the settings in ioExtension are bad
280 //
281 // B_OK,	if this translator understood the data and there were
282 //			no errors found
283 //
284 // Other errors if BPositionIO::Read() returned an error value
285 // ---------------------------------------------------------------
286 status_t
287 PNGTranslator::DerivedIdentify(BPositionIO *inSource,
288 	const translation_format *inFormat, BMessage *ioExtension,
289 	translator_info *outInfo, uint32 outType)
290 {
291 	return identify_png_header(inSource, outInfo);
292 }
293 
294 status_t
295 PNGTranslator::translate_from_png_to_bits(BPositionIO *inSource,
296 	BPositionIO *outDestination)
297 {
298 	if (identify_png_header(inSource, NULL) != B_OK)
299 		return B_NO_TRANSLATOR;
300 
301 	status_t result = B_ERROR;
302 		// if a libpng errors before this is set
303 		// to a different value, the above is what
304 		// will be returned from this function
305 
306 	bool bheaderonly = false, bdataonly = false;
307 
308 	// for storing decoded PNG row data
309 	uint8 **prows = NULL, *prow = NULL;
310 	png_uint_32 nalloc = 0;
311 
312 	png_structp ppng = NULL;
313 	png_infop pinfo = NULL;
314 	while (ppng == NULL) {
315 		// create PNG read pointer with default error handling routines
316 		ppng = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
317 		if (!ppng)
318 			break;
319 		// alocate / init memory for image information
320 		pinfo = png_create_info_struct(ppng);
321 		if (!pinfo)
322 			break;
323 		// set error handling
324 		if (setjmp(png_jmpbuf(ppng)))
325 			// When an error occurs in libpng, it uses
326 			// the longjmp function to continue execution
327 			// from this point
328 			break;
329 
330 		// set read callback function
331 		png_set_read_fn(ppng, static_cast<void *>(inSource), pngcb_read_data);
332 
333 		// Read in PNG image info
334 		png_set_sig_bytes(ppng, 8);
335 		png_read_info(ppng, pinfo);
336 
337 		png_uint_32 width, height;
338 		int bit_depth, color_type, interlace_type;
339 		png_get_IHDR(ppng, pinfo, &width, &height, &bit_depth, &color_type,
340 			&interlace_type, NULL, NULL);
341 
342 		// Setup image transformations to make converting it easier
343 		bool balpha = false;
344 
345 		if (bit_depth == 16)
346 			png_set_strip_16(ppng);
347 		else if (bit_depth < 8)
348 			png_set_packing(ppng);
349 
350 		if (color_type == PNG_COLOR_TYPE_PALETTE)
351 			png_set_palette_to_rgb(ppng);
352 
353 		if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
354 			// In order to convert from low-depth gray images to RGB,
355 			// I first need to convert them to grayscale, 8 bpp
356 			png_set_expand_gray_1_2_4_to_8(ppng);
357 
358 		if (png_get_valid(ppng, pinfo, PNG_INFO_tRNS)) {
359 			// if there is transparency data in the
360 			// PNG, but not in the form of an alpha channel
361 			balpha = true;
362 			png_set_tRNS_to_alpha(ppng);
363 		}
364 
365 		// change RGB to BGR as it is in 'bits'
366 		if (color_type & PNG_COLOR_MASK_COLOR)
367 			png_set_bgr(ppng);
368 
369 		// have libpng convert gray to RGB for me
370 		if (color_type == PNG_COLOR_TYPE_GRAY ||
371 			color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
372 			png_set_gray_to_rgb(ppng);
373 
374 		if (color_type & PNG_COLOR_MASK_ALPHA)
375 			// if image contains an alpha channel
376 			balpha = true;
377 
378 		if (!balpha)
379 			// add filler byte for images without alpha
380 			// so that the pixels are 4 bytes each
381 			png_set_filler(ppng, 0xff, PNG_FILLER_AFTER);
382 
383 		// Check that transformed PNG rowbytes matches
384 		// what is expected
385 		const int32 kbytes = 4;
386 		png_uint_32 rowbytes = png_get_rowbytes(ppng, pinfo);
387 		if (rowbytes < kbytes * width)
388 			rowbytes = kbytes * width;
389 
390 		if (!bdataonly) {
391 			// Write out the data to outDestination
392 			// Construct and write Be bitmap header
393 			TranslatorBitmap bitsHeader;
394 			bitsHeader.magic = B_TRANSLATOR_BITMAP;
395 			bitsHeader.bounds.left = 0;
396 			bitsHeader.bounds.top = 0;
397 			bitsHeader.bounds.right = width - 1;
398 			bitsHeader.bounds.bottom = height - 1;
399 			bitsHeader.rowBytes = 4 * width;
400 			if (balpha)
401 				bitsHeader.colors = B_RGBA32;
402 			else
403 				bitsHeader.colors = B_RGB32;
404 			bitsHeader.dataSize = bitsHeader.rowBytes * height;
405 			if (swap_data(B_UINT32_TYPE, &bitsHeader,
406 				sizeof(TranslatorBitmap), B_SWAP_HOST_TO_BENDIAN) != B_OK) {
407 				result = B_ERROR;
408 				break;
409 			}
410 			outDestination->Write(&bitsHeader, sizeof(TranslatorBitmap));
411 
412 			if (bheaderonly) {
413 				result = B_OK;
414 				break;
415 			}
416 		}
417 
418 		if (interlace_type == PNG_INTERLACE_NONE) {
419 			// allocate buffer for storing PNG row
420 			prow = new uint8[rowbytes];
421 			if (!prow) {
422 				result = B_NO_MEMORY;
423 				break;
424 			}
425 			for (png_uint_32 i = 0; i < height; i++) {
426 				png_read_row(ppng, prow, NULL);
427 				outDestination->Write(prow, width * kbytes);
428 			}
429 			result = B_OK;
430 				// Set OK status here, because, in the event of
431 				// an error, png_read_end() will longjmp to error
432 				// handler above and not execute lines below it
433 
434 			// finish reading, pass NULL for info because I
435 			// don't need the extra data
436 			png_read_end(ppng, NULL);
437 
438 			break;
439 
440 		} else {
441 			// interlaced PNG image
442 			prows = new uint8 *[height];
443 			if (!prows) {
444 				result = B_NO_MEMORY;
445 				break;
446 			}
447 			// allocate enough memory to store the whole image
448 			for (nalloc = 0; nalloc < height; nalloc++) {
449 				prows[nalloc] = new uint8[rowbytes];
450 				if (!prows[nalloc])
451 					break;
452 			}
453 
454 			if (nalloc < height)
455 				result = B_NO_MEMORY;
456 			else {
457 				png_read_image(ppng, prows);
458 
459 				for (png_uint_32 i = 0; i < height; i++)
460 					outDestination->Write(prows[i], width * kbytes);
461 				result = B_OK;
462 					// Set OK status here, because, in the event of
463 					// an error, png_read_end() will longjmp to error
464 					// handler above and not execute lines below it
465 
466 				png_read_end(ppng, NULL);
467 			}
468 
469 			break;
470 		}
471 	}
472 
473 	if (ppng) {
474 		delete[] prow;
475 		prow = NULL;
476 
477 		// delete row pointers and array of pointers to rows
478 		while (nalloc) {
479 			nalloc--;
480 			delete[] prows[nalloc];
481 		}
482 		delete[] prows;
483 		prows = NULL;
484 
485 		// free PNG handle / info structures
486 		if (!pinfo)
487 			png_destroy_read_struct(&ppng, NULL, NULL);
488 		else
489 			png_destroy_read_struct(&ppng, &pinfo, NULL);
490 	}
491 
492 	return result;
493 }
494 
495 status_t
496 PNGTranslator::translate_from_png(BPositionIO *inSource, uint32 outType,
497 	BPositionIO *outDestination)
498 {
499 	if (outType == B_TRANSLATOR_BITMAP)
500 		return translate_from_png_to_bits(inSource, outDestination);
501 	else {
502 		// Translate from PNG to PNG
503 		translate_direct_copy(inSource, outDestination);
504 		return B_OK;
505 	}
506 }
507 
508 // Convert width pixels from pbits to PNG format, storing the
509 // result in ppng
510 status_t
511 pix_bits_to_png(uint8 *pbits, uint8 *ppng, color_space fromspace,
512 	uint32 width, const color_map *pmap, int32 bitsBytesPerPixel)
513 {
514 	status_t bytescopied = 0;
515 	uint16 val;
516 
517 	switch (fromspace) {
518 		case B_RGBA32:
519 			bytescopied = width * bitsBytesPerPixel;
520 			memcpy(ppng, pbits, bytescopied);
521 			break;
522 
523 		case B_RGB32:
524 		case B_RGB24:
525 			bytescopied = width * bitsBytesPerPixel;
526 			while (width--) {
527 				memcpy(ppng, pbits, 3);
528 				ppng += 3;
529 				pbits += bitsBytesPerPixel;
530 			}
531 			break;
532 
533 		case B_RGBA32_BIG:
534 			bytescopied = width * 4;
535 			while (width--) {
536 				ppng[0] = pbits[3];
537 				ppng[1] = pbits[2];
538 				ppng[2] = pbits[1];
539 				ppng[3] = pbits[0];
540 
541 				ppng += 4;
542 				pbits += 4;
543 			}
544 			break;
545 
546 		case B_CMYA32:
547 			bytescopied = width * 4;
548 			while (width--) {
549 				ppng[0] = 255 - pbits[2];
550 				ppng[1] = 255 - pbits[1];
551 				ppng[2] = 255 - pbits[0];
552 				ppng[3] = pbits[3];
553 
554 				ppng += 4;
555 				pbits += 4;
556 			}
557 			break;
558 
559 		case B_CMYK32:
560 		{
561 			int32 comp;
562 			bytescopied = width * 3;
563 			while (width--) {
564 				comp = 255 - pbits[2] - pbits[3];
565 				ppng[0] = (comp < 0) ? 0 : comp;
566 
567 				comp = 255 - pbits[1] - pbits[3];
568 				ppng[1] = (comp < 0) ? 0 : comp;
569 
570 				comp = 255 - pbits[0] - pbits[3];
571 				ppng[2] = (comp < 0) ? 0 : comp;
572 
573 				ppng += 3;
574 				pbits += 4;
575 			}
576 			break;
577 		}
578 
579 		case B_CMY32:
580 		case B_CMY24:
581 			bytescopied = width * 3;
582 			while (width--) {
583 				ppng[0] = 255 - pbits[2];
584 				ppng[1] = 255 - pbits[1];
585 				ppng[2] = 255 - pbits[0];
586 
587 				ppng += 3;
588 				pbits += bitsBytesPerPixel;
589 			}
590 			break;
591 
592 		case B_RGB16:
593 		case B_RGB16_BIG:
594 			bytescopied = width * 3;
595 			while (width--) {
596 				if (fromspace == B_RGB16)
597 					val = pbits[0] + (pbits[1] << 8);
598 				else
599 					val = pbits[1] + (pbits[0] << 8);
600 
601 				ppng[0] =
602 					((val & 0x1f) << 3) | ((val & 0x1f) >> 2);
603 				ppng[1] =
604 					((val & 0x7e0) >> 3) | ((val & 0x7e0) >> 9);
605 				ppng[2] =
606 					((val & 0xf800) >> 8) | ((val & 0xf800) >> 13);
607 
608 				ppng += 3;
609 				pbits += 2;
610 			}
611 			break;
612 
613 		case B_RGB15:
614 		case B_RGB15_BIG:
615 			bytescopied = width * 3;
616 			while (width--) {
617 				if (fromspace == B_RGB15)
618 					val = pbits[0] + (pbits[1] << 8);
619 				else
620 					val = pbits[1] + (pbits[0] << 8);
621 				ppng[0] =
622 					((val & 0x1f) << 3) | ((val & 0x1f) >> 2);
623 				ppng[1] =
624 					((val & 0x3e0) >> 2) | ((val & 0x3e0) >> 7);
625 				ppng[2] =
626 					((val & 0x7c00) >> 7) | ((val & 0x7c00) >> 12);
627 
628 				ppng += 3;
629 				pbits += 2;
630 			}
631 			break;
632 
633 		case B_RGBA15:
634 		case B_RGBA15_BIG:
635 			bytescopied = width * 4;
636 			while (width--) {
637 				if (fromspace == B_RGBA15)
638 					val = pbits[0] + (pbits[1] << 8);
639 				else
640 					val = pbits[1] + (pbits[0] << 8);
641 				ppng[0] =
642 					((val & 0x1f) << 3) | ((val & 0x1f) >> 2);
643 				ppng[1] =
644 					((val & 0x3e0) >> 2) | ((val & 0x3e0) >> 7);
645 				ppng[2] =
646 					((val & 0x7c00) >> 7) | ((val & 0x7c00) >> 12);
647 				ppng[3] = (val & 0x8000) ? 255 : 0;
648 
649 				ppng += 4;
650 				pbits += 2;
651 			}
652 			break;
653 
654 		case B_RGB32_BIG:
655 			bytescopied = width * 3;
656 			while (width--) {
657 				ppng[0] = pbits[3];
658 				ppng[1] = pbits[2];
659 				ppng[2] = pbits[1];
660 
661 				ppng += 3;
662 				pbits += 4;
663 			}
664 			break;
665 
666 		case B_RGB24_BIG:
667 			bytescopied = width * 3;
668 			while (width--) {
669 				ppng[0] = pbits[2];
670 				ppng[1] = pbits[1];
671 				ppng[2] = pbits[0];
672 
673 				ppng += 3;
674 				pbits += 3;
675 			}
676 			break;
677 
678 		case B_CMAP8:
679 		{
680 			rgb_color c;
681 			bytescopied = width * 3;
682 			while (width--) {
683 				c = pmap->color_list[pbits[0]];
684 				ppng[0] = c.blue;
685 				ppng[1] = c.green;
686 				ppng[2] = c.red;
687 
688 				ppng += 3;
689 				pbits++;
690 			}
691 			break;
692 		}
693 
694 		case B_GRAY8:
695 			bytescopied = width;
696 			memcpy(ppng, pbits, bytescopied);
697 			break;
698 
699 		default:
700 			bytescopied = B_ERROR;
701 			break;
702 	} // switch (fromspace)
703 
704 	return bytescopied;
705 }
706 
707 status_t
708 PNGTranslator::translate_from_bits_to_png(BPositionIO *inSource,
709 	BPositionIO *outDestination)
710 {
711 	TranslatorBitmap bitsHeader;
712 
713 	status_t result;
714 
715 	result = identify_bits_header(inSource, NULL, &bitsHeader);
716 	if (result != B_OK)
717 		return result;
718 
719 	const color_map *pmap = NULL;
720 	if (bitsHeader.colors == B_CMAP8) {
721 		pmap = system_colors();
722 		if (!pmap)
723 			return B_ERROR;
724 	}
725 
726 	png_uint_32 width, height;
727 	width = static_cast<png_uint_32>(bitsHeader.bounds.Width() + 1);
728 	height = static_cast<png_uint_32>(bitsHeader.bounds.Height() + 1);
729 
730 	int32 pngBytesPerPixel = 0;
731 	int bit_depth, color_type, interlace_type;
732 	bit_depth = 8;
733 	switch (bitsHeader.colors) {
734 		case B_RGBA32:
735 		case B_RGBA32_BIG:
736 		case B_CMYA32:
737 		case B_RGBA15:
738 		case B_RGBA15_BIG:
739 			pngBytesPerPixel = 4;
740 			color_type = PNG_COLOR_TYPE_RGB_ALPHA;
741 			break;
742 
743 		case B_RGB32:
744 		case B_RGB32_BIG:
745 		case B_RGB24:
746 		case B_RGB24_BIG:
747 		case B_CMY32:
748 		case B_CMYK32:
749 		case B_CMY24:
750 		case B_RGB16:
751 		case B_RGB16_BIG:
752 		case B_RGB15:
753 		case B_RGB15_BIG:
754 			pngBytesPerPixel = 3;
755 			color_type = PNG_COLOR_TYPE_RGB;
756 			break;
757 
758 		// ADD SUPPORT FOR B_CMAP8 HERE (later)
759 
760 		case B_GRAY8:
761 			pngBytesPerPixel = 1;
762 			color_type = PNG_COLOR_TYPE_GRAY;
763 			break;
764 
765 		default:
766 			return B_NO_TRANSLATOR;
767 	}
768 	interlace_type = fSettings->SetGetInt32(PNG_SETTING_INTERLACE);
769 
770 	int32 bitsBytesPerPixel = 0;
771 	switch (bitsHeader.colors) {
772 		case B_RGBA32:
773 		case B_RGBA32_BIG:
774 		case B_RGB32:
775 		case B_RGB32_BIG:
776 		case B_CMYA32:
777 		case B_CMYK32:
778 		case B_CMY32:
779 			bitsBytesPerPixel = 4;
780 			break;
781 
782 		case B_RGB24:
783 		case B_RGB24_BIG:
784 		case B_CMY24:
785 			bitsBytesPerPixel = 3;
786 			break;
787 
788 		case B_RGB16:
789 		case B_RGB16_BIG:
790 		case B_RGBA15:
791 		case B_RGBA15_BIG:
792 		case B_RGB15:
793 		case B_RGB15_BIG:
794 			bitsBytesPerPixel = 2;
795 			break;
796 
797 		case B_GRAY8:
798 		case B_CMAP8:
799 			bitsBytesPerPixel = 1;
800 			break;
801 
802 		default:
803 			return B_NO_TRANSLATOR;
804 	};
805 
806 	uint8 *pbitsrow = NULL, *prow = NULL;
807 		// row buffers
808 	// image buffer for writing whole png image at once
809 	uint8 **prows = NULL;
810 	png_uint_32 nalloc = 0;
811 
812 	png_structp ppng = NULL;
813 	png_infop pinfo = NULL;
814 
815 	result = B_NO_TRANSLATOR;
816 	while (ppng == NULL) {
817 		// create PNG read pointer with default error handling routines
818 		ppng = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,
819 			NULL, NULL);
820 		if (!ppng) {
821 			result = B_ERROR;
822 			break;
823 		}
824 		// alocate / init memory for image information
825 		pinfo = png_create_info_struct(ppng);
826 		if (!pinfo) {
827 			result = B_ERROR;
828 			break;
829 		}
830 		// set error handling
831 		if (setjmp(png_jmpbuf(ppng))) {
832 			// When an error occurs in libpng, it uses
833 			// the longjmp function to continue execution
834 			// from this point
835 			result = B_ERROR;
836 			break;
837 		}
838 
839 		png_set_write_fn(ppng, static_cast<void *>(outDestination),
840 			pngcb_write_data, pngcb_flush_data);
841 
842 		// Allocate memory needed to buffer image data
843 		pbitsrow = new uint8[bitsHeader.rowBytes];
844 		if (!pbitsrow) {
845 			result = B_NO_MEMORY;
846 			break;
847 		}
848 		if (interlace_type == PNG_INTERLACE_NONE) {
849 			prow = new uint8[width * pngBytesPerPixel];
850 			if (!prow) {
851 				result = B_NO_MEMORY;
852 				break;
853 			}
854 		} else {
855 			prows = new uint8 *[height];
856 			if (!prows) {
857 				result = B_NO_MEMORY;
858 				break;
859 			}
860 			// allocate enough memory to store the whole image
861 			for (nalloc = 0; nalloc < height; nalloc++) {
862 				prows[nalloc] = new uint8[width * pngBytesPerPixel];
863 				if (!prows[nalloc])
864 					break;
865 			}
866 			if (nalloc < height) {
867 				result = B_NO_MEMORY;
868 				// clear out rest of the pointers,
869 				// so we don't call delete[] with invalid pointers
870 				for (; nalloc < height; nalloc++)
871 					prows[nalloc] = NULL;
872 				break;
873 			}
874 		}
875 
876 		// Specify image info
877 		png_set_IHDR(ppng, pinfo, width, height, bit_depth, color_type,
878 			interlace_type, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
879 		png_write_info(ppng, pinfo);
880 
881 		png_set_bgr(ppng);
882 
883 		// write out image data
884 		if (interlace_type == PNG_INTERLACE_NONE) {
885 			for (png_uint_32 i = 0; i < height; i++) {
886 				inSource->Read(pbitsrow, bitsHeader.rowBytes);
887 
888 				pix_bits_to_png(pbitsrow, prow, bitsHeader.colors, width,
889 					pmap, bitsBytesPerPixel);
890 
891 				png_write_row(ppng, prow);
892 			}
893 		} else {
894 			for (png_uint_32 i = 0; i < height; i++) {
895 				inSource->Read(pbitsrow, bitsHeader.rowBytes);
896 
897 				pix_bits_to_png(pbitsrow, prows[i], bitsHeader.colors, width,
898 					pmap, bitsBytesPerPixel);
899 			}
900 			png_write_image(ppng, prows);
901 		}
902 		png_write_end(ppng, NULL);
903 
904 		result = B_OK;
905 		break;
906 	}
907 
908 	if (ppng) {
909 		delete[] pbitsrow;
910 		pbitsrow = NULL;
911 
912 		delete[] prow;
913 		prow = NULL;
914 
915 		// delete row pointers and array of pointers to rows
916 		while (nalloc) {
917 			nalloc--;
918 			delete[] prows[nalloc];
919 		}
920 		delete[] prows;
921 		prows = NULL;
922 
923 		// free PNG handle / info structures
924 		if (!pinfo)
925 			png_destroy_write_struct(&ppng, NULL);
926 		else
927 			png_destroy_write_struct(&ppng, &pinfo);
928 	}
929 
930 	return result;
931 }
932 
933 // ---------------------------------------------------------------
934 // DerivedTranslate
935 //
936 // Translates the data in inSource to the type outType and stores
937 // the translated data in outDestination.
938 //
939 // Preconditions:
940 //
941 // Parameters:	inSource,	the data to be translated
942 //
943 //				inInfo,	hint about the data in inSource (not used)
944 //
945 //				ioExtension,	configuration options for the
946 //								translator
947 //
948 //				outType,	the type to convert inSource to
949 //
950 //				outDestination,	where the translated data is
951 //								put
952 //
953 //				baseType, indicates whether inSource is in the
954 //				          bits format, not in the bits format or
955 //				          is unknown
956 //
957 // Postconditions:
958 //
959 // Returns: B_BAD_VALUE, if the options in ioExtension are bad
960 //
961 // B_NO_TRANSLATOR, if this translator doesn't understand the data
962 //
963 // B_ERROR, if there was an error allocating memory or converting
964 //          data
965 //
966 // B_OK, if all went well
967 // ---------------------------------------------------------------
968 status_t
969 PNGTranslator::DerivedTranslate(BPositionIO *inSource,
970 	const translator_info *inInfo, BMessage *ioExtension, uint32 outType,
971 	BPositionIO *outDestination, int32 baseType)
972 {
973 	if (baseType == 1)
974 		// if inSource is in bits format
975 		return translate_from_bits_to_png(inSource, outDestination);
976 	else if (baseType == 0)
977 		// if inSource is NOT in bits format
978 		return translate_from_png(inSource, outType, outDestination);
979 	else
980 		return B_NO_TRANSLATOR;
981 }
982 
983 BView *
984 PNGTranslator::NewConfigView(TranslatorSettings *settings)
985 {
986 	return new PNGView(BRect(0, 0, PNG_VIEW_WIDTH, PNG_VIEW_HEIGHT),
987 		B_TRANSLATE("PNGTranslator Settings"), B_FOLLOW_ALL,
988 		B_WILL_DRAW, settings);
989 }
990 
991