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