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