LCOV - code coverage report
Current view: top level - core/bitmap - SPBitmapPng.cc (source / functions) Hit Total Coverage
Test: coverage.info Lines: 144 183 78.7 %
Date: 2024-05-12 00:16:13 Functions: 18 18 100.0 %

          Line data    Source code
       1             : /**
       2             : Copyright (c) 2022 Roman Katuntsev <sbkarr@stappler.org>
       3             : Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
       4             : 
       5             : Permission is hereby granted, free of charge, to any person obtaining a copy
       6             : of this software and associated documentation files (the "Software"), to deal
       7             : in the Software without restriction, including without limitation the rights
       8             : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       9             : copies of the Software, and to permit persons to whom the Software is
      10             : furnished to do so, subject to the following conditions:
      11             : 
      12             : The above copyright notice and this permission notice shall be included in
      13             : all copies or substantial portions of the Software.
      14             : 
      15             : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      16             : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      17             : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      18             : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      19             : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      20             : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      21             : THE SOFTWARE.
      22             : **/
      23             : 
      24             : #include "SPBitmapFormat.h"
      25             : #include "SPLog.h"
      26             : #include "SPFilesystem.h"
      27             : #include "png.h"
      28             : 
      29             : namespace STAPPLER_VERSIONIZED stappler::bitmap::png {
      30             : 
      31             : struct ReadState {
      32             :         const uint8_t *data = nullptr;
      33             :         size_t offset = 0;
      34             : };
      35             : 
      36        8554 : static bool isPng(const uint8_t * data, size_t dataLen) {
      37        8554 :     if (dataLen <= 8) {
      38           0 :         return false;
      39             :     }
      40             : 
      41             :     static const unsigned char PNG_SIGNATURE[] = {0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a};
      42        8554 :     return memcmp(PNG_SIGNATURE, data, sizeof(PNG_SIGNATURE)) == 0;
      43             : }
      44             : 
      45        1819 : static bool getPngImageSize(const io::Producer &file, StackBuffer<512> &data, uint32_t &width, uint32_t &height) {
      46        1819 :         if (isPng(data.data(), data.size()) && data.size() >= 24) {
      47         644 :                 auto reader = BytesViewNetwork(data.data() + 16, 8);
      48             : 
      49         644 :                 width = reader.readUnsigned32();
      50         644 :                 height = reader.readUnsigned32();
      51             : 
      52         644 :                 return true;
      53             :         }
      54        1175 :         return false;
      55             : }
      56             : 
      57       55712 : static void readDynamicData(png_structp pngPtr, png_bytep data, png_size_t length) {
      58       55712 :         auto state = (ReadState *)png_get_io_ptr(pngPtr);
      59       55712 :         memcpy(data, state->data + state->offset, length);
      60       55712 :         state->offset += length;
      61       55712 : }
      62             : 
      63             : struct PngReadStruct {
      64        2061 :         ~PngReadStruct() {
      65        2061 :                 if (png_ptr || info_ptr) {
      66        2061 :                         png_destroy_read_struct(png_ptr ? &png_ptr : nullptr, info_ptr ? &info_ptr : nullptr, NULL);
      67        2061 :                         png_ptr = nullptr;
      68        2061 :                         info_ptr = nullptr;
      69             :                 }
      70        2061 :         }
      71             : 
      72        2061 :         PngReadStruct() { }
      73             : 
      74        2061 :         bool init(const uint8_t *inputData, size_t size) {
      75        2061 :                 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
      76        2061 :                 if (png_ptr == NULL) {
      77           0 :                         log::error("libpng", "fail to create read struct");
      78           0 :                         return false;
      79             :                 }
      80             : 
      81        2061 :                 info_ptr = png_create_info_struct(png_ptr);
      82        2061 :                 if (info_ptr == NULL) {
      83           0 :                         log::error("libpng", "fail to create info struct");
      84           0 :                         png_destroy_read_struct(&png_ptr, NULL, NULL);
      85           0 :                         return false;
      86             :                 }
      87             : 
      88        2061 :                 if (setjmp(png_jmpbuf(png_ptr))) {
      89           0 :                         log::error("libpng", "error in processing (setjmp return)");
      90           0 :                         return false;
      91             :                 }
      92             : 
      93        2061 :                 state.data = inputData;
      94        2061 :                 state.offset = 0;
      95        2061 :                 png_set_read_fn(png_ptr,(png_voidp)&state, readDynamicData);
      96             : 
      97             : #ifdef PNG_ARM_NEON_API_SUPPORTED
      98             :                 png_set_option(png_ptr, PNG_ARM_NEON, PNG_OPTION_ON);
      99             : #endif
     100        2061 :                 png_read_info(png_ptr, info_ptr);
     101        2061 :                 return true;
     102             :         }
     103             : 
     104        2061 :         bool info(ImageInfo &info) {
     105        2061 :                 if (setjmp(png_jmpbuf(png_ptr))) {
     106           0 :                         log::error("libpng", "error in processing (setjmp return)");
     107           0 :                         return false;
     108             :                 }
     109             : 
     110        2061 :                 info.width = png_get_image_width(png_ptr, info_ptr);
     111        2061 :                 info.height = png_get_image_height(png_ptr, info_ptr);
     112             : 
     113        2061 :                 png_byte bitdepth = png_get_bit_depth(png_ptr, info_ptr);
     114        2061 :                 png_uint_32 color_type = png_get_color_type(png_ptr, info_ptr);
     115             : 
     116        2061 :                 if (color_type == PNG_COLOR_TYPE_PALETTE) {
     117           0 :                         png_set_palette_to_rgb(png_ptr);
     118             :                 }
     119        2061 :                 if (color_type == PNG_COLOR_TYPE_GRAY && bitdepth < 8) {
     120           0 :                         bitdepth = 8;
     121           0 :                         png_set_expand_gray_1_2_4_to_8(png_ptr);
     122             :                 }
     123        2061 :                 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
     124           0 :                         png_set_tRNS_to_alpha(png_ptr);
     125             :                 }
     126        2061 :                 if (bitdepth == 16) {
     127           0 :                         png_set_strip_16(png_ptr);
     128             :                 }
     129        2061 :                 if (bitdepth < 8) {
     130           0 :                         png_set_packing(png_ptr);
     131             :                 }
     132             : 
     133        2061 :                 png_read_update_info(png_ptr, info_ptr);
     134        2061 :                 bitdepth = png_get_bit_depth(png_ptr, info_ptr);
     135        2061 :                 color_type = png_get_color_type(png_ptr, info_ptr);
     136        2061 :                 auto rowbytes = png_get_rowbytes(png_ptr, info_ptr);
     137             : 
     138        2061 :                 if (color_type == PNG_COLOR_TYPE_GRAY) {
     139         354 :                         info.color = (info.color == PixelFormat::A8?PixelFormat::A8:PixelFormat::I8);
     140        1707 :                 } else if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
     141         354 :                         info.color = PixelFormat::IA88;
     142        1353 :                 } else if (color_type == PNG_COLOR_TYPE_RGB) {
     143         329 :                         info.color = PixelFormat::RGB888;
     144        1024 :                 } else if (color_type == PNG_COLOR_TYPE_RGBA) {
     145        1024 :                         info.color = PixelFormat::RGBA8888;
     146             :                 } else {
     147           0 :                         info.width = 0;
     148           0 :                         info.height = 0;
     149           0 :                         info.stride = 0;
     150           0 :                         log::format(log::Error, "libpng", "unsupported color type: %u", (unsigned int)color_type);
     151           0 :                         return false;
     152             :                 }
     153             : 
     154        2061 :                 info.stride = (uint32_t)rowbytes;
     155             : 
     156        2061 :                 if (info.color == PixelFormat::I8 || info.color == PixelFormat::RGB888) {
     157         683 :                         info.alpha = AlphaFormat::Opaque;
     158             :                 } else {
     159        1378 :                         info.alpha = AlphaFormat::Unpremultiplied;
     160             :                 }
     161             : 
     162        2061 :                 return true;
     163             :         }
     164             : 
     165        1303 :         bool load(BitmapWriter &outputData) {
     166        1303 :                 if (!info(outputData)) {
     167           0 :                         return false;
     168             :                 }
     169             : 
     170        1303 :                 if (setjmp(png_jmpbuf(png_ptr))) {
     171           0 :                         log::error("libpng", "error in processing (setjmp return)");
     172           0 :                         return false;
     173             :                 }
     174             : 
     175        1303 :                 if (outputData.getStride) {
     176          75 :                         auto rowbytes = png_get_rowbytes(png_ptr, info_ptr);
     177          75 :                         outputData.stride = max((uint32_t)outputData.getStride(outputData.target, outputData.color, outputData.width), (uint32_t)rowbytes);
     178             :                 }
     179             : 
     180        1303 :                 png_bytep row_pointers[outputData.height];
     181             : 
     182        1303 :                 auto dataLen = outputData.stride * outputData.height;
     183             : 
     184        1303 :                 outputData.resize(outputData.target, dataLen);
     185             : 
     186      401156 :                 for (uint32_t i = 0; i < outputData.height; ++i) {
     187      399853 :                         row_pointers[i] = outputData.getData(outputData.target, i * outputData.stride);
     188             :                 }
     189             : 
     190        1303 :                 png_read_image(png_ptr, row_pointers);
     191        1303 :                 png_read_end(png_ptr, nullptr);
     192        1303 :                 return true;
     193        1303 :         }
     194             : 
     195             :         ReadState state;
     196             :         png_structp png_ptr = nullptr;
     197             :         png_infop info_ptr = nullptr;
     198             : };
     199             : 
     200             : struct PngWriteStruct {
     201             :         int bit_depth = 8;
     202             :         png_structp png_ptr = nullptr;
     203             :         png_infop info_ptr = nullptr;
     204             :         bool valid = false;
     205             : 
     206             :         FILE *fp = nullptr;
     207             :         BitmapWriter *out = nullptr;
     208             : 
     209        1426 :         ~PngWriteStruct() {
     210        1426 :                 if (png_ptr != nullptr) {
     211        1426 :                         png_destroy_write_struct(&png_ptr, &info_ptr);
     212             :                 }
     213             : 
     214        1426 :                 if (fp) {
     215        1150 :                         fclose(fp);
     216             :                 }
     217        1426 :         }
     218             : 
     219        1426 :         PngWriteStruct() {
     220        1426 :                 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
     221        1426 :                 if (png_ptr == nullptr) {
     222           0 :                         log::error("libpng", "fail to create write struct");
     223           0 :                         return;
     224             :                 }
     225             : 
     226        1426 :                 info_ptr = png_create_info_struct (png_ptr);
     227        1426 :                 if (info_ptr == nullptr) {
     228           0 :                         log::error("libpng", "fail to create info struct");
     229           0 :                         return;
     230             :                 }
     231             :         }
     232             : 
     233         276 :         PngWriteStruct(BitmapWriter *v) : PngWriteStruct() {
     234         276 :                 out = v;
     235         276 :                 valid = true;
     236         276 :         }
     237             : 
     238        1150 :         PngWriteStruct(StringView filename) : PngWriteStruct() {
     239        1150 :                 fp = filesystem::native::fopen_fn(filename, "wb");
     240        1150 :                 if (!fp) {
     241           0 :                         log::error("libpng", "fail to open file '%s' to write png data", filename.data());
     242           0 :                         valid = false;
     243           0 :                         return;
     244             :                 }
     245             : 
     246        1150 :                 valid = true;
     247           0 :         }
     248             : 
     249       10794 :         static void writePngFn(png_structp png_ptr, png_bytep data, png_size_t length) {
     250       10794 :                 auto out = (BitmapWriter *)png_get_io_ptr(png_ptr);
     251       10794 :                 out->push(out->target, data, length);
     252       10794 :         }
     253             : 
     254        1426 :         bool write(const uint8_t *data, BitmapWriter &state, bool invert = false) {
     255        1426 :                 if (!valid) {
     256           0 :                         return false;
     257             :                 }
     258             : 
     259        1426 :                 if (!fp && !out) {
     260           0 :                         return false;
     261             :                 }
     262             : 
     263        1426 :                 if (setjmp (png_jmpbuf (png_ptr))) {
     264           0 :                         log::error("libpng", "error in processing (setjmp return)");
     265           0 :                         return false;
     266             :                 }
     267             : 
     268        1426 :                 if (state.stride == 0) {
     269           0 :                         state.stride = getBytesPerPixel(state.color) * state.width;
     270             :                 }
     271             : 
     272        1426 :                 int color_type = 0;
     273        1426 :                 switch (state.color) {
     274          44 :                 case PixelFormat::A8:
     275             :                 case PixelFormat::I8:
     276          44 :                         color_type = PNG_COLOR_TYPE_GRAY;
     277          44 :                         break;
     278          44 :                 case PixelFormat::IA88:
     279          44 :                         color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
     280          44 :                         break;
     281         919 :                 case PixelFormat::RGB888:
     282         919 :                         color_type = PNG_COLOR_TYPE_RGB;
     283         919 :                         break;
     284         419 :                 case PixelFormat::RGBA8888:
     285         419 :                         color_type = PNG_COLOR_TYPE_RGBA;
     286         419 :                         break;
     287           0 :                 default:
     288           0 :                         return false;
     289             :                 }
     290             : 
     291             :                 /* Set image attributes. */
     292        1426 :                 png_set_IHDR (png_ptr, info_ptr, state.width, state.height, bit_depth,
     293             :                                 color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
     294             : 
     295             :                 /* Initialize rows of PNG. */
     296        1426 :                 png_byte *row_pointers[state.height];
     297      193152 :                 for (size_t i = 0; i < state.height; ++i) {
     298      191726 :                         row_pointers[i] = (png_byte *)data + (invert ? state.stride * (state.height - i - 1) : state.stride * i);
     299             :                 }
     300             : 
     301        1426 :                 if (fp) {
     302        1150 :                         png_init_io (png_ptr, fp);
     303             :                 } else {
     304         276 :                         png_set_write_fn(png_ptr, out, &writePngFn, nullptr);
     305             :                 }
     306             : 
     307        1426 :                 png_set_rows (png_ptr, info_ptr, row_pointers);
     308        1426 :                 png_write_png (png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
     309        1426 :                 return true;
     310             :         }
     311             : };
     312             : 
     313         758 : static bool infoPng(const uint8_t *inputData, size_t size, ImageInfo &outputData) {
     314         758 :         PngReadStruct pngStruct;
     315        1516 :         return pngStruct.init(inputData, size) && pngStruct.info(outputData);
     316         758 : }
     317             : 
     318        1303 : static bool loadPng(const uint8_t *inputData, size_t size, BitmapWriter &outputData) {
     319        1303 :         PngReadStruct pngStruct;
     320        2606 :         return pngStruct.init(inputData, size) && pngStruct.load(outputData);
     321        1303 : }
     322             : 
     323        1150 : static bool savePng(StringView filename, const uint8_t *data, BitmapWriter &state, bool invert) {
     324        1150 :         PngWriteStruct s(filename);
     325        2300 :         return s.write(data, state, invert);
     326        1150 : }
     327             : 
     328         276 : static bool writePng(const uint8_t *data, BitmapWriter &state, bool invert) {
     329         276 :         PngWriteStruct s(&state);
     330         552 :         return s.write(data, state, invert);
     331         276 : }
     332             : 
     333             : }

Generated by: LCOV version 1.14