1 /*****************************************************************************/
3 // Written by Michael Wilber, OBOS Translation Kit Team
7 // This BTranslator based object is for opening and writing
11 // Copyright (c) 2003, OpenBeOS Project
12 // Copyright (c) 2009, Haiku, Inc. All rights reserved.
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:
21 // The above copyright notice and this permission notice shall be included
22 // in all copies or substantial portions of the Software.
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 /*****************************************************************************/
34 #include "PNGTranslator.h"
41 #define PNG_NO_PEDANTIC_WARNINGS
46 #undef B_TRANSLATION_CONTEXT
47 #define B_TRANSLATION_CONTEXT "PNGTranslator"
49 // The input formats that this translator supports.
50 static const translation_format sInputFormats
[] = {
73 "Be Bitmap Format (PNGTranslator)"
77 // The output formats that this translator supports.
78 static const translation_format sOutputFormats
[] = {
93 "Be Bitmap Format (PNGTranslator)"
97 // Default settings for the Translator
98 static const TranSetting sDefaultSettings
[] = {
99 {B_TRANSLATOR_EXT_HEADER_ONLY
, TRAN_SETTING_BOOL
, false},
100 {B_TRANSLATOR_EXT_DATA_ONLY
, TRAN_SETTING_BOOL
, false},
101 {PNG_SETTING_INTERLACE
, TRAN_SETTING_INT32
, PNG_INTERLACE_NONE
}
102 // interlacing is off by default
105 const uint32 kNumInputFormats
= sizeof(sInputFormats
) / sizeof(translation_format
);
106 const uint32 kNumOutputFormats
= sizeof(sOutputFormats
) / sizeof(translation_format
);
107 const uint32 kNumDefaultSettings
= sizeof(sDefaultSettings
) / sizeof(TranSetting
);
110 // ---------------------------------------------------------------
111 // make_nth_translator
113 // Creates a PNGTranslator object to be used by BTranslatorRoster
117 // Parameters: n, The translator to return. Since
118 // PNGTranslator only publishes one
119 // translator, it only returns a
120 // PNGTranslator if n == 0
122 // you, The image_id of the add-on that
123 // contains code (not used).
125 // flags, Has no meaning yet, should be 0.
129 // Returns: NULL if n is not zero,
130 // a new PNGTranslator if n is zero
131 // ---------------------------------------------------------------
133 make_nth_translator(int32 n
, image_id you
, uint32 flags
, ...)
136 return new PNGTranslator();
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!).
148 # define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf)
151 //// libpng Callback functions!
154 get_pio(png_structp ppng
)
156 BPositionIO
*pio
= NULL
;
157 pio
= static_cast<BPositionIO
*>(png_get_io_ptr(ppng
));
162 pngcb_read_data(png_structp ppng
, png_bytep pdata
, png_size_t length
)
164 BPositionIO
*pio
= get_pio(ppng
);
165 pio
->Read(pdata
, static_cast<size_t>(length
));
169 pngcb_write_data(png_structp ppng
, png_bytep pdata
, png_size_t length
)
171 BPositionIO
*pio
= get_pio(ppng
);
172 pio
->Write(pdata
, static_cast<size_t>(length
));
176 pngcb_flush_data(png_structp ppng
)
178 // I don't think I really need to do anything here
181 // ---------------------------------------------------------------
184 // Sets up the version info and the name of the translator so that
185 // these values can be returned when they are requested.
194 // ---------------------------------------------------------------
195 PNGTranslator::PNGTranslator()
196 : BaseTranslator(B_TRANSLATE("PNG images"),
197 B_TRANSLATE("PNG image translator"),
198 PNG_TRANSLATOR_VERSION
,
199 sInputFormats
, kNumInputFormats
,
200 sOutputFormats
, kNumOutputFormats
,
201 "PNGTranslator_Settings",
202 sDefaultSettings
, kNumDefaultSettings
,
203 B_TRANSLATOR_BITMAP
, B_PNG_FORMAT
)
207 // ---------------------------------------------------------------
219 // ---------------------------------------------------------------
220 PNGTranslator::~PNGTranslator()
225 identify_png_header(BPositionIO
*inSource
, translator_info
*outInfo
)
227 const int32 kSigSize
= 8;
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
;
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
));
248 // ---------------------------------------------------------------
251 // Examines the data from inSource and determines if it is in a
252 // format that this translator knows how to work with.
256 // Parameters: inSource, where the data to examine is
258 // inFormat, a hint about the data in inSource,
259 // it is ignored since it is only a hint
261 // ioExtension, configuration settings for the
262 // translator (not used)
264 // outInfo, information about what data is in
265 // inSource and how well this translator
266 // can handle that data is stored here
268 // outType, The format that the user wants
269 // the data in inSource to be
274 // Returns: B_NO_TRANSLATOR, if this translator can't handle
275 // the data in inSource
277 // B_ERROR, if there was an error converting the data to the host
280 // B_BAD_VALUE, if the settings in ioExtension are bad
282 // B_OK, if this translator understood the data and there were
285 // Other errors if BPositionIO::Read() returned an error value
286 // ---------------------------------------------------------------
288 PNGTranslator::DerivedIdentify(BPositionIO
*inSource
,
289 const translation_format
*inFormat
, BMessage
*ioExtension
,
290 translator_info
*outInfo
, uint32 outType
)
292 return identify_png_header(inSource
, outInfo
);
295 void throw_error(png_structp ppng
, png_const_charp error_msg
)
297 throw std::exception();
300 void alert_warning(png_structp ppng
, png_const_charp error_msg
)
303 // TODO show a BAlert?
307 PNGTranslator::translate_from_png_to_bits(BPositionIO
*inSource
,
308 BPositionIO
*outDestination
)
310 if (identify_png_header(inSource
, NULL
) != B_OK
)
311 return B_NO_TRANSLATOR
;
313 status_t result
= B_ERROR
;
314 // if a libpng errors before this is set
315 // to a different value, the above is what
316 // will be returned from this function
318 bool bheaderonly
= false, bdataonly
= false;
320 // for storing decoded PNG row data
321 uint8
**prows
= NULL
, *prow
= NULL
;
322 png_uint_32 nalloc
= 0;
324 png_structp ppng
= NULL
;
325 png_infop pinfo
= NULL
;
326 while (ppng
== NULL
) {
327 // create PNG read pointer with default error handling routines
328 ppng
= png_create_read_struct(PNG_LIBPNG_VER_STRING
, NULL
, NULL
, NULL
);
332 // alocate / init memory for image information
333 pinfo
= png_create_info_struct(ppng
);
337 // set up erorr handling to use C++ exceptions instead of setjmp
338 png_set_error_fn(ppng
, png_get_error_ptr(ppng
), throw_error
,
342 // set read callback function
343 png_set_read_fn(ppng
, static_cast<void *>(inSource
), pngcb_read_data
);
345 // Read in PNG image info
346 png_set_sig_bytes(ppng
, 8);
347 png_read_info(ppng
, pinfo
);
349 png_uint_32 width
, height
;
350 int bit_depth
, color_type
, interlace_type
;
351 png_get_IHDR(ppng
, pinfo
, &width
, &height
, &bit_depth
, &color_type
,
352 &interlace_type
, NULL
, NULL
);
354 // Setup image transformations to make converting it easier
358 png_set_strip_16(ppng
);
359 else if (bit_depth
< 8)
360 png_set_packing(ppng
);
362 if (color_type
== PNG_COLOR_TYPE_PALETTE
)
363 png_set_palette_to_rgb(ppng
);
365 if (color_type
== PNG_COLOR_TYPE_GRAY
&& bit_depth
< 8) {
366 // In order to convert from low-depth gray images to RGB,
367 // I first need to convert them to grayscale, 8 bpp
368 png_set_expand_gray_1_2_4_to_8(ppng
);
371 if (png_get_valid(ppng
, pinfo
, PNG_INFO_tRNS
)) {
372 // if there is transparency data in the
373 // PNG, but not in the form of an alpha channel
375 png_set_tRNS_to_alpha(ppng
);
378 // change RGB to BGR as it is in 'bits'
379 if (color_type
& PNG_COLOR_MASK_COLOR
)
382 // have libpng convert gray to RGB for me
383 if (color_type
== PNG_COLOR_TYPE_GRAY
||
384 color_type
== PNG_COLOR_TYPE_GRAY_ALPHA
) {
385 png_set_gray_to_rgb(ppng
);
388 if (color_type
& PNG_COLOR_MASK_ALPHA
) {
389 // if image contains an alpha channel
394 // add filler byte for images without alpha
395 // so that the pixels are 4 bytes each
396 png_set_filler(ppng
, 0xff, PNG_FILLER_AFTER
);
399 // Check that transformed PNG rowbytes matches
401 const int32 kbytes
= 4;
402 png_uint_32 rowbytes
= png_get_rowbytes(ppng
, pinfo
);
403 if (rowbytes
< kbytes
* width
)
404 rowbytes
= kbytes
* width
;
407 // Write out the data to outDestination
408 // Construct and write Be bitmap header
409 TranslatorBitmap bitsHeader
;
410 bitsHeader
.magic
= B_TRANSLATOR_BITMAP
;
411 bitsHeader
.bounds
.left
= 0;
412 bitsHeader
.bounds
.top
= 0;
413 bitsHeader
.bounds
.right
= width
- 1;
414 bitsHeader
.bounds
.bottom
= height
- 1;
415 bitsHeader
.rowBytes
= 4 * width
;
417 bitsHeader
.colors
= B_RGBA32
;
419 bitsHeader
.colors
= B_RGB32
;
420 bitsHeader
.dataSize
= bitsHeader
.rowBytes
* height
;
421 if (swap_data(B_UINT32_TYPE
, &bitsHeader
,
422 sizeof(TranslatorBitmap
), B_SWAP_HOST_TO_BENDIAN
) != B_OK
) {
426 outDestination
->Write(&bitsHeader
, sizeof(TranslatorBitmap
));
434 if (interlace_type
== PNG_INTERLACE_NONE
) {
435 // allocate buffer for storing PNG row
436 prow
= new uint8
[rowbytes
];
438 result
= B_NO_MEMORY
;
441 for (png_uint_32 i
= 0; i
< height
; i
++) {
442 png_read_row(ppng
, prow
, NULL
);
443 outDestination
->Write(prow
, width
* kbytes
);
446 // finish reading, pass NULL for info because I
447 // don't need the extra data
448 png_read_end(ppng
, NULL
);
454 // interlaced PNG image
455 prows
= new uint8
*[height
];
457 result
= B_NO_MEMORY
;
460 // allocate enough memory to store the whole image
461 for (nalloc
= 0; nalloc
< height
; nalloc
++) {
462 prows
[nalloc
] = new uint8
[rowbytes
];
468 result
= B_NO_MEMORY
;
470 png_read_image(ppng
, prows
);
472 for (png_uint_32 i
= 0; i
< height
; i
++)
473 outDestination
->Write(prows
[i
], width
* kbytes
);
476 // If png_read_end throws an exception, we still accept
477 // the image as valid.
478 png_read_end(ppng
, NULL
);
483 } catch (std::exception
& e
) {
484 // An error occured, abort decoding and cleanup
493 // delete row pointers and array of pointers to rows
496 delete[] prows
[nalloc
];
501 // free PNG handle / info structures
503 png_destroy_read_struct(&ppng
, NULL
, NULL
);
505 png_destroy_read_struct(&ppng
, &pinfo
, NULL
);
512 PNGTranslator::translate_from_png(BPositionIO
*inSource
, uint32 outType
,
513 BPositionIO
*outDestination
)
515 if (outType
== B_TRANSLATOR_BITMAP
)
516 return translate_from_png_to_bits(inSource
, outDestination
);
518 // Translate from PNG to PNG
519 translate_direct_copy(inSource
, outDestination
);
524 // Convert width pixels from pbits to PNG format, storing the
527 pix_bits_to_png(uint8
*pbits
, uint8
*ppng
, color_space fromspace
,
528 uint32 width
, const color_map
*pmap
, int32 bitsBytesPerPixel
)
530 status_t bytescopied
= 0;
535 bytescopied
= width
* bitsBytesPerPixel
;
536 memcpy(ppng
, pbits
, bytescopied
);
541 bytescopied
= width
* bitsBytesPerPixel
;
543 memcpy(ppng
, pbits
, 3);
545 pbits
+= bitsBytesPerPixel
;
550 bytescopied
= width
* 4;
563 bytescopied
= width
* 4;
565 ppng
[0] = 255 - pbits
[2];
566 ppng
[1] = 255 - pbits
[1];
567 ppng
[2] = 255 - pbits
[0];
578 bytescopied
= width
* 3;
580 comp
= 255 - pbits
[2] - pbits
[3];
581 ppng
[0] = (comp
< 0) ? 0 : comp
;
583 comp
= 255 - pbits
[1] - pbits
[3];
584 ppng
[1] = (comp
< 0) ? 0 : comp
;
586 comp
= 255 - pbits
[0] - pbits
[3];
587 ppng
[2] = (comp
< 0) ? 0 : comp
;
597 bytescopied
= width
* 3;
599 ppng
[0] = 255 - pbits
[2];
600 ppng
[1] = 255 - pbits
[1];
601 ppng
[2] = 255 - pbits
[0];
604 pbits
+= bitsBytesPerPixel
;
610 bytescopied
= width
* 3;
612 if (fromspace
== B_RGB16
)
613 val
= pbits
[0] + (pbits
[1] << 8);
615 val
= pbits
[1] + (pbits
[0] << 8);
618 ((val
& 0x1f) << 3) | ((val
& 0x1f) >> 2);
620 ((val
& 0x7e0) >> 3) | ((val
& 0x7e0) >> 9);
622 ((val
& 0xf800) >> 8) | ((val
& 0xf800) >> 13);
631 bytescopied
= width
* 3;
633 if (fromspace
== B_RGB15
)
634 val
= pbits
[0] + (pbits
[1] << 8);
636 val
= pbits
[1] + (pbits
[0] << 8);
638 ((val
& 0x1f) << 3) | ((val
& 0x1f) >> 2);
640 ((val
& 0x3e0) >> 2) | ((val
& 0x3e0) >> 7);
642 ((val
& 0x7c00) >> 7) | ((val
& 0x7c00) >> 12);
651 bytescopied
= width
* 4;
653 if (fromspace
== B_RGBA15
)
654 val
= pbits
[0] + (pbits
[1] << 8);
656 val
= pbits
[1] + (pbits
[0] << 8);
658 ((val
& 0x1f) << 3) | ((val
& 0x1f) >> 2);
660 ((val
& 0x3e0) >> 2) | ((val
& 0x3e0) >> 7);
662 ((val
& 0x7c00) >> 7) | ((val
& 0x7c00) >> 12);
663 ppng
[3] = (val
& 0x8000) ? 255 : 0;
671 bytescopied
= width
* 3;
683 bytescopied
= width
* 3;
697 bytescopied
= width
* 3;
699 c
= pmap
->color_list
[pbits
[0]];
712 memcpy(ppng
, pbits
, bytescopied
);
716 bytescopied
= B_ERROR
;
718 } // switch (fromspace)
724 PNGTranslator::translate_from_bits_to_png(BPositionIO
*inSource
,
725 BPositionIO
*outDestination
)
727 TranslatorBitmap bitsHeader
;
731 result
= identify_bits_header(inSource
, NULL
, &bitsHeader
);
735 const color_map
*pmap
= NULL
;
736 if (bitsHeader
.colors
== B_CMAP8
) {
737 pmap
= system_colors();
742 png_uint_32 width
, height
;
743 width
= static_cast<png_uint_32
>(bitsHeader
.bounds
.Width() + 1);
744 height
= static_cast<png_uint_32
>(bitsHeader
.bounds
.Height() + 1);
746 int32 pngBytesPerPixel
= 0;
747 int bit_depth
, color_type
, interlace_type
;
749 switch (bitsHeader
.colors
) {
755 pngBytesPerPixel
= 4;
756 color_type
= PNG_COLOR_TYPE_RGB_ALPHA
;
770 pngBytesPerPixel
= 3;
771 color_type
= PNG_COLOR_TYPE_RGB
;
774 // ADD SUPPORT FOR B_CMAP8 HERE (later)
777 pngBytesPerPixel
= 1;
778 color_type
= PNG_COLOR_TYPE_GRAY
;
782 return B_NO_TRANSLATOR
;
784 interlace_type
= fSettings
->SetGetInt32(PNG_SETTING_INTERLACE
);
786 int32 bitsBytesPerPixel
= 0;
787 switch (bitsHeader
.colors
) {
795 bitsBytesPerPixel
= 4;
801 bitsBytesPerPixel
= 3;
810 bitsBytesPerPixel
= 2;
815 bitsBytesPerPixel
= 1;
819 return B_NO_TRANSLATOR
;
822 uint8
*pbitsrow
= NULL
, *prow
= NULL
;
824 // image buffer for writing whole png image at once
825 uint8
**prows
= NULL
;
826 png_uint_32 nalloc
= 0;
828 png_structp ppng
= NULL
;
829 png_infop pinfo
= NULL
;
831 result
= B_NO_TRANSLATOR
;
832 while (ppng
== NULL
) {
833 // create PNG read pointer with default error handling routines
834 ppng
= png_create_write_struct(PNG_LIBPNG_VER_STRING
, NULL
,
840 // alocate / init memory for image information
841 pinfo
= png_create_info_struct(ppng
);
847 // set up erorr handling to use C++ exceptions instead of setjmp
848 png_set_error_fn(ppng
, png_get_error_ptr(ppng
), throw_error
,
852 png_set_write_fn(ppng
, static_cast<void *>(outDestination
),
853 pngcb_write_data
, pngcb_flush_data
);
855 // Allocate memory needed to buffer image data
856 pbitsrow
= new uint8
[bitsHeader
.rowBytes
];
858 result
= B_NO_MEMORY
;
861 if (interlace_type
== PNG_INTERLACE_NONE
) {
862 prow
= new uint8
[width
* pngBytesPerPixel
];
864 result
= B_NO_MEMORY
;
868 prows
= new uint8
*[height
];
870 result
= B_NO_MEMORY
;
873 // allocate enough memory to store the whole image
874 for (nalloc
= 0; nalloc
< height
; nalloc
++) {
875 prows
[nalloc
] = new uint8
[width
* pngBytesPerPixel
];
879 if (nalloc
< height
) {
880 result
= B_NO_MEMORY
;
881 // clear out rest of the pointers,
882 // so we don't call delete[] with invalid pointers
883 for (; nalloc
< height
; nalloc
++)
884 prows
[nalloc
] = NULL
;
889 // Specify image info
890 png_set_IHDR(ppng
, pinfo
, width
, height
, bit_depth
, color_type
,
891 interlace_type
, PNG_COMPRESSION_TYPE_DEFAULT
, PNG_FILTER_TYPE_DEFAULT
);
892 png_write_info(ppng
, pinfo
);
896 // write out image data
897 if (interlace_type
== PNG_INTERLACE_NONE
) {
898 for (png_uint_32 i
= 0; i
< height
; i
++) {
899 inSource
->Read(pbitsrow
, bitsHeader
.rowBytes
);
901 pix_bits_to_png(pbitsrow
, prow
, bitsHeader
.colors
, width
,
902 pmap
, bitsBytesPerPixel
);
904 png_write_row(ppng
, prow
);
907 for (png_uint_32 i
= 0; i
< height
; i
++) {
908 inSource
->Read(pbitsrow
, bitsHeader
.rowBytes
);
910 pix_bits_to_png(pbitsrow
, prows
[i
], bitsHeader
.colors
,
911 width
, pmap
, bitsBytesPerPixel
);
913 png_write_image(ppng
, prows
);
916 // If png_read_end throws an exception, we still accept
917 // the image as valid.
918 png_write_end(ppng
, NULL
);
921 } catch(std::exception
& e
) {
933 // delete row pointers and array of pointers to rows
936 delete[] prows
[nalloc
];
941 // free PNG handle / info structures
943 png_destroy_write_struct(&ppng
, NULL
);
945 png_destroy_write_struct(&ppng
, &pinfo
);
951 // ---------------------------------------------------------------
954 // Translates the data in inSource to the type outType and stores
955 // the translated data in outDestination.
959 // Parameters: inSource, the data to be translated
961 // inInfo, hint about the data in inSource (not used)
963 // ioExtension, configuration options for the
966 // outType, the type to convert inSource to
968 // outDestination, where the translated data is
971 // baseType, indicates whether inSource is in the
972 // bits format, not in the bits format or
977 // Returns: B_BAD_VALUE, if the options in ioExtension are bad
979 // B_NO_TRANSLATOR, if this translator doesn't understand the data
981 // B_ERROR, if there was an error allocating memory or converting
984 // B_OK, if all went well
985 // ---------------------------------------------------------------
987 PNGTranslator::DerivedTranslate(BPositionIO
*inSource
,
988 const translator_info
*inInfo
, BMessage
*ioExtension
, uint32 outType
,
989 BPositionIO
*outDestination
, int32 baseType
)
992 // if inSource is in bits format
993 return translate_from_bits_to_png(inSource
, outDestination
);
994 else if (baseType
== 0)
995 // if inSource is NOT in bits format
996 return translate_from_png(inSource
, outType
, outDestination
);
998 return B_NO_TRANSLATOR
;
1002 PNGTranslator::NewConfigView(TranslatorSettings
*settings
)
1004 return new PNGView(BRect(0, 0, PNG_VIEW_WIDTH
, PNG_VIEW_HEIGHT
),
1005 B_TRANSLATE("PNGTranslator Settings"), B_FOLLOW_ALL
,
1006 B_WILL_DRAW
, settings
);