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
);
296 PNGTranslator::translate_from_png_to_bits(BPositionIO
*inSource
,
297 BPositionIO
*outDestination
)
299 if (identify_png_header(inSource
, NULL
) != B_OK
)
300 return B_NO_TRANSLATOR
;
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
307 bool bheaderonly
= false, bdataonly
= false;
309 // for storing decoded PNG row data
310 uint8
**prows
= NULL
, *prow
= NULL
;
311 png_uint_32 nalloc
= 0;
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
);
320 // alocate / init memory for image information
321 pinfo
= png_create_info_struct(ppng
);
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
331 // set read callback function
332 png_set_read_fn(ppng
, static_cast<void *>(inSource
), pngcb_read_data
);
334 // Read in PNG image info
335 png_set_sig_bytes(ppng
, 8);
336 png_read_info(ppng
, pinfo
);
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
);
343 // Setup image transformations to make converting it easier
347 png_set_strip_16(ppng
);
348 else if (bit_depth
< 8)
349 png_set_packing(ppng
);
351 if (color_type
== PNG_COLOR_TYPE_PALETTE
)
352 png_set_palette_to_rgb(ppng
);
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
);
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
363 png_set_tRNS_to_alpha(ppng
);
366 // change RGB to BGR as it is in 'bits'
367 if (color_type
& PNG_COLOR_MASK_COLOR
)
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
);
375 if (color_type
& PNG_COLOR_MASK_ALPHA
)
376 // if image contains an alpha channel
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
);
384 // Check that transformed PNG rowbytes matches
386 const int32 kbytes
= 4;
387 png_uint_32 rowbytes
= png_get_rowbytes(ppng
, pinfo
);
388 if (rowbytes
< kbytes
* width
)
389 rowbytes
= kbytes
* width
;
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
;
402 bitsHeader
.colors
= B_RGBA32
;
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
) {
411 outDestination
->Write(&bitsHeader
, sizeof(TranslatorBitmap
));
419 if (interlace_type
== PNG_INTERLACE_NONE
) {
420 // allocate buffer for storing PNG row
421 prow
= new uint8
[rowbytes
];
423 result
= B_NO_MEMORY
;
426 for (png_uint_32 i
= 0; i
< height
; i
++) {
427 png_read_row(ppng
, prow
, NULL
);
428 outDestination
->Write(prow
, width
* kbytes
);
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
435 // finish reading, pass NULL for info because I
436 // don't need the extra data
437 png_read_end(ppng
, NULL
);
442 // interlaced PNG image
443 prows
= new uint8
*[height
];
445 result
= B_NO_MEMORY
;
448 // allocate enough memory to store the whole image
449 for (nalloc
= 0; nalloc
< height
; nalloc
++) {
450 prows
[nalloc
] = new uint8
[rowbytes
];
456 result
= B_NO_MEMORY
;
458 png_read_image(ppng
, prows
);
460 for (png_uint_32 i
= 0; i
< height
; i
++)
461 outDestination
->Write(prows
[i
], width
* kbytes
);
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
467 png_read_end(ppng
, NULL
);
478 // delete row pointers and array of pointers to rows
481 delete[] prows
[nalloc
];
486 // free PNG handle / info structures
488 png_destroy_read_struct(&ppng
, NULL
, NULL
);
490 png_destroy_read_struct(&ppng
, &pinfo
, NULL
);
497 PNGTranslator::translate_from_png(BPositionIO
*inSource
, uint32 outType
,
498 BPositionIO
*outDestination
)
500 if (outType
== B_TRANSLATOR_BITMAP
)
501 return translate_from_png_to_bits(inSource
, outDestination
);
503 // Translate from PNG to PNG
504 translate_direct_copy(inSource
, outDestination
);
509 // Convert width pixels from pbits to PNG format, storing the
512 pix_bits_to_png(uint8
*pbits
, uint8
*ppng
, color_space fromspace
,
513 uint32 width
, const color_map
*pmap
, int32 bitsBytesPerPixel
)
515 status_t bytescopied
= 0;
520 bytescopied
= width
* bitsBytesPerPixel
;
521 memcpy(ppng
, pbits
, bytescopied
);
526 bytescopied
= width
* bitsBytesPerPixel
;
528 memcpy(ppng
, pbits
, 3);
530 pbits
+= bitsBytesPerPixel
;
535 bytescopied
= width
* 4;
548 bytescopied
= width
* 4;
550 ppng
[0] = 255 - pbits
[2];
551 ppng
[1] = 255 - pbits
[1];
552 ppng
[2] = 255 - pbits
[0];
563 bytescopied
= width
* 3;
565 comp
= 255 - pbits
[2] - pbits
[3];
566 ppng
[0] = (comp
< 0) ? 0 : comp
;
568 comp
= 255 - pbits
[1] - pbits
[3];
569 ppng
[1] = (comp
< 0) ? 0 : comp
;
571 comp
= 255 - pbits
[0] - pbits
[3];
572 ppng
[2] = (comp
< 0) ? 0 : comp
;
582 bytescopied
= width
* 3;
584 ppng
[0] = 255 - pbits
[2];
585 ppng
[1] = 255 - pbits
[1];
586 ppng
[2] = 255 - pbits
[0];
589 pbits
+= bitsBytesPerPixel
;
595 bytescopied
= width
* 3;
597 if (fromspace
== B_RGB16
)
598 val
= pbits
[0] + (pbits
[1] << 8);
600 val
= pbits
[1] + (pbits
[0] << 8);
603 ((val
& 0x1f) << 3) | ((val
& 0x1f) >> 2);
605 ((val
& 0x7e0) >> 3) | ((val
& 0x7e0) >> 9);
607 ((val
& 0xf800) >> 8) | ((val
& 0xf800) >> 13);
616 bytescopied
= width
* 3;
618 if (fromspace
== B_RGB15
)
619 val
= pbits
[0] + (pbits
[1] << 8);
621 val
= pbits
[1] + (pbits
[0] << 8);
623 ((val
& 0x1f) << 3) | ((val
& 0x1f) >> 2);
625 ((val
& 0x3e0) >> 2) | ((val
& 0x3e0) >> 7);
627 ((val
& 0x7c00) >> 7) | ((val
& 0x7c00) >> 12);
636 bytescopied
= width
* 4;
638 if (fromspace
== B_RGBA15
)
639 val
= pbits
[0] + (pbits
[1] << 8);
641 val
= pbits
[1] + (pbits
[0] << 8);
643 ((val
& 0x1f) << 3) | ((val
& 0x1f) >> 2);
645 ((val
& 0x3e0) >> 2) | ((val
& 0x3e0) >> 7);
647 ((val
& 0x7c00) >> 7) | ((val
& 0x7c00) >> 12);
648 ppng
[3] = (val
& 0x8000) ? 255 : 0;
656 bytescopied
= width
* 3;
668 bytescopied
= width
* 3;
682 bytescopied
= width
* 3;
684 c
= pmap
->color_list
[pbits
[0]];
697 memcpy(ppng
, pbits
, bytescopied
);
701 bytescopied
= B_ERROR
;
703 } // switch (fromspace)
709 PNGTranslator::translate_from_bits_to_png(BPositionIO
*inSource
,
710 BPositionIO
*outDestination
)
712 TranslatorBitmap bitsHeader
;
716 result
= identify_bits_header(inSource
, NULL
, &bitsHeader
);
720 const color_map
*pmap
= NULL
;
721 if (bitsHeader
.colors
== B_CMAP8
) {
722 pmap
= system_colors();
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);
731 int32 pngBytesPerPixel
= 0;
732 int bit_depth
, color_type
, interlace_type
;
734 switch (bitsHeader
.colors
) {
740 pngBytesPerPixel
= 4;
741 color_type
= PNG_COLOR_TYPE_RGB_ALPHA
;
755 pngBytesPerPixel
= 3;
756 color_type
= PNG_COLOR_TYPE_RGB
;
759 // ADD SUPPORT FOR B_CMAP8 HERE (later)
762 pngBytesPerPixel
= 1;
763 color_type
= PNG_COLOR_TYPE_GRAY
;
767 return B_NO_TRANSLATOR
;
769 interlace_type
= fSettings
->SetGetInt32(PNG_SETTING_INTERLACE
);
771 int32 bitsBytesPerPixel
= 0;
772 switch (bitsHeader
.colors
) {
780 bitsBytesPerPixel
= 4;
786 bitsBytesPerPixel
= 3;
795 bitsBytesPerPixel
= 2;
800 bitsBytesPerPixel
= 1;
804 return B_NO_TRANSLATOR
;
807 uint8
*pbitsrow
= NULL
, *prow
= NULL
;
809 // image buffer for writing whole png image at once
810 uint8
**prows
= NULL
;
811 png_uint_32 nalloc
= 0;
813 png_structp ppng
= NULL
;
814 png_infop pinfo
= NULL
;
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
,
825 // alocate / init memory for image information
826 pinfo
= png_create_info_struct(ppng
);
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
840 png_set_write_fn(ppng
, static_cast<void *>(outDestination
),
841 pngcb_write_data
, pngcb_flush_data
);
843 // Allocate memory needed to buffer image data
844 pbitsrow
= new uint8
[bitsHeader
.rowBytes
];
846 result
= B_NO_MEMORY
;
849 if (interlace_type
== PNG_INTERLACE_NONE
) {
850 prow
= new uint8
[width
* pngBytesPerPixel
];
852 result
= B_NO_MEMORY
;
856 prows
= new uint8
*[height
];
858 result
= B_NO_MEMORY
;
861 // allocate enough memory to store the whole image
862 for (nalloc
= 0; nalloc
< height
; nalloc
++) {
863 prows
[nalloc
] = new uint8
[width
* pngBytesPerPixel
];
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
;
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
);
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
);
889 pix_bits_to_png(pbitsrow
, prow
, bitsHeader
.colors
, width
,
890 pmap
, bitsBytesPerPixel
);
892 png_write_row(ppng
, prow
);
895 for (png_uint_32 i
= 0; i
< height
; i
++) {
896 inSource
->Read(pbitsrow
, bitsHeader
.rowBytes
);
898 pix_bits_to_png(pbitsrow
, prows
[i
], bitsHeader
.colors
, width
,
899 pmap
, bitsBytesPerPixel
);
901 png_write_image(ppng
, prows
);
903 png_write_end(ppng
, NULL
);
916 // delete row pointers and array of pointers to rows
919 delete[] prows
[nalloc
];
924 // free PNG handle / info structures
926 png_destroy_write_struct(&ppng
, NULL
);
928 png_destroy_write_struct(&ppng
, &pinfo
);
934 // ---------------------------------------------------------------
937 // Translates the data in inSource to the type outType and stores
938 // the translated data in outDestination.
942 // Parameters: inSource, the data to be translated
944 // inInfo, hint about the data in inSource (not used)
946 // ioExtension, configuration options for the
949 // outType, the type to convert inSource to
951 // outDestination, where the translated data is
954 // baseType, indicates whether inSource is in the
955 // bits format, not in the bits format or
960 // Returns: B_BAD_VALUE, if the options in ioExtension are bad
962 // B_NO_TRANSLATOR, if this translator doesn't understand the data
964 // B_ERROR, if there was an error allocating memory or converting
967 // B_OK, if all went well
968 // ---------------------------------------------------------------
970 PNGTranslator::DerivedTranslate(BPositionIO
*inSource
,
971 const translator_info
*inInfo
, BMessage
*ioExtension
, uint32 outType
,
972 BPositionIO
*outDestination
, int32 baseType
)
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
);
981 return B_NO_TRANSLATOR
;
985 PNGTranslator::NewConfigView(TranslatorSettings
*settings
)
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
);