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