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