LCOV - code coverage report
Current view: top level - core/bitmap - SPBitmapGif.cc (source / functions) Hit Total Coverage
Test: coverage.info Lines: 108 127 85.0 %
Date: 2024-05-12 00:16:13 Functions: 11 11 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 "gif_lib.h"
      28             : 
      29             : namespace STAPPLER_VERSIONIZED stappler::bitmap::gif {
      30             : 
      31        2875 : static bool isGif(const uint8_t * data, size_t dataLen) {
      32        2875 :         if (dataLen <= 8) {
      33           0 :                 return false;
      34             :         }
      35             : 
      36             :         static const unsigned char GIF_SIGNATURE_1[] = {0x47, 0x49, 0x46, 0x38, 0x37, 0x61};
      37             :         static const unsigned char GIF_SIGNATURE_2[] = {0x47, 0x49, 0x46, 0x38, 0x39, 0x61};
      38        2875 :         return memcmp(GIF_SIGNATURE_1, data, sizeof(GIF_SIGNATURE_1)) == 0
      39        2875 :                         || memcmp(GIF_SIGNATURE_2, data, sizeof(GIF_SIGNATURE_2)) == 0;
      40             : }
      41             : 
      42         575 : static bool getGifImageSize(const io::Producer &file, StackBuffer<512> &data, uint32_t &width, uint32_t &height) {
      43         575 :         if (isGif(data.data(), data.size())) {
      44         375 :                 auto reader = BytesViewTemplate<Endian::Little>(data.data() + 6, 4);
      45             : 
      46         375 :                 width = reader.readUnsigned16();
      47         375 :                 height = reader.readUnsigned16();
      48             : 
      49         375 :                 return true;
      50             :         }
      51         200 :         return false;
      52             : }
      53             : 
      54      396100 : static int Gif_InputFunc(GifFileType *file, GifByteType *bytes, int count) {
      55      396100 :         auto reader = (CoderSource *)file->UserData;
      56             : 
      57      396100 :         if (count >= 0) {
      58      396100 :                 return reader->read(bytes, count);
      59             :         }
      60           0 :         return 0;
      61             : }
      62             : 
      63             : struct GifReadStruct {
      64         250 :         ~GifReadStruct() {
      65         250 :                 if (!file) {
      66           0 :                         DGifCloseFile(file, &error);
      67           0 :                         file = nullptr;
      68             :                 }
      69         250 :         }
      70             : 
      71         250 :         GifReadStruct() { }
      72             : 
      73         250 :         bool init(const uint8_t *inputData, size_t size) {
      74         250 :                 reader._data = BytesViewTemplate<Endian::Network>(inputData, size);
      75         250 :                 file = DGifOpen((void *)&reader, &Gif_InputFunc, &error);
      76             : 
      77         250 :                 if (!file || error != 0) {
      78           0 :                         log::error("GIF", "fail to open file");
      79           0 :                         return false;
      80             :                 }
      81             : 
      82         250 :                 if (DGifSlurp(file) != GIF_OK) {
      83           0 :                         log::error("GIF", "fail to read file");
      84           0 :                         return false;
      85             :                 }
      86             : 
      87         250 :                 if (file->ImageCount == 0) {
      88           0 :                         log::error("GIF", "no images found");
      89           0 :                         return false;
      90             :                 }
      91             : 
      92         250 :                 return true;
      93             :         }
      94             : 
      95         250 :         bool info(ImageInfo &outputData) {
      96         250 :                 ColorMapObject *colors =  (file->SavedImages->ImageDesc.ColorMap) ? file->SavedImages->ImageDesc.ColorMap : file->SColorMap;
      97         250 :                 if (!colors) {
      98           0 :                         log::error("GIF", "no color profile found");
      99           0 :                         return false;
     100             :                 }
     101             : 
     102       13350 :                 auto checkGrayscale = [&] (GifColorType &c) {
     103       13350 :                         return c.Red == c.Green && c.Red == c.Blue;
     104             :                 };
     105             : 
     106         250 :                 bool isGrayscale = true;
     107       13450 :                 for (size_t i = 0; i < size_t(colors->ColorCount); ++ i) {
     108       13350 :                         if (!checkGrayscale(colors->Colors[i])) {
     109         150 :                                 isGrayscale = false;
     110         150 :                                 break;
     111             :                         }
     112             :                 }
     113             : 
     114         250 :                 if (file->ExtensionBlockCount > 0) {
     115           0 :                         for (size_t i = 0; i < size_t(file->ExtensionBlockCount); ++ i) {
     116             :                                 GraphicsControlBlock GCB;
     117           0 :                                 if (DGifExtensionToGCB(file->ExtensionBlocks[i].ByteCount, file->ExtensionBlocks[i].Bytes, &GCB) == GIF_OK) {
     118           0 :                                         if (GCB.TransparentColor != NO_TRANSPARENT_COLOR) {
     119           0 :                                                 transparent = GCB.TransparentColor;
     120             :                                         }
     121             :                                 }
     122             :                         }
     123             :                 }
     124             : 
     125         250 :                 if (file->SavedImages->ExtensionBlockCount > 0) {
     126             :                         GraphicsControlBlock GCB;
     127         250 :                         if (DGifSavedExtensionToGCB(file, 0, &GCB) == GIF_OK) {
     128         250 :                                 if (GCB.TransparentColor != NO_TRANSPARENT_COLOR) {
     129         100 :                                         transparent = GCB.TransparentColor;
     130             :                                 }
     131             :                         }
     132             :                 }
     133             : 
     134         250 :                 outputData.width = file->SavedImages->ImageDesc.Width;
     135         250 :                 outputData.height = file->SavedImages->ImageDesc.Height;
     136             : 
     137         250 :                 outputData.color = (transparent != maxOf<size_t>())
     138         400 :                                 ? (isGrayscale ? PixelFormat::IA88 : PixelFormat::RGBA8888)
     139         150 :                                 : (isGrayscale ? ((outputData.color == PixelFormat::A8) ? PixelFormat::A8 : PixelFormat::I8) : PixelFormat::RGB888);
     140             : 
     141         400 :                 outputData.alpha = (transparent != maxOf<size_t>() || outputData.color == PixelFormat::A8)
     142         400 :                                 ? AlphaFormat::Unpremultiplied
     143             :                                 : AlphaFormat::Opaque;
     144             : 
     145         250 :                 outputData.stride = max(outputData.stride, (uint32_t)(outputData.width * getBytesPerPixel(outputData.color)));
     146             : 
     147         250 :                 return true;
     148             :         }
     149             : 
     150         125 :         bool load(BitmapWriter &outputData) {
     151         125 :                 if (!info(outputData)) {
     152           0 :                         return false;
     153             :                 }
     154             : 
     155         125 :                 ColorMapObject *colors =  (file->SavedImages->ImageDesc.ColorMap) ? file->SavedImages->ImageDesc.ColorMap : file->SColorMap;
     156         125 :                 if (!colors) {
     157           0 :                         log::error("GIF", "no color profile found");
     158           0 :                         return false;
     159             :                 }
     160             : 
     161         125 :                 if (outputData.getStride) {
     162         125 :                         outputData.stride = (uint32_t)outputData.getStride(outputData.target, outputData.color, outputData.width);
     163             :                 }
     164             : 
     165         125 :             auto dataLen = outputData.stride * outputData.height;
     166         125 :             outputData.resize(outputData.target, dataLen);
     167             : 
     168         125 :                 auto input = file->SavedImages->RasterBits;
     169         125 :                 auto location = outputData.getData(outputData.target, 0);
     170             : 
     171         125 :                 if (outputData.color == PixelFormat::RGB888) {
     172       15650 :                         for (size_t i = 0; i < outputData.height; ++ i) {
     173       15600 :                                 auto loc = location;
     174     4882800 :                                 for (size_t j = 0; j < outputData.width; ++ j) {
     175     4867200 :                                         auto &c = colors->Colors[input[i * outputData.width + j]];
     176     4867200 :                                         *loc++ = c.Red;
     177     4867200 :                                         *loc++ = c.Green;
     178     4867200 :                                         *loc++ = c.Blue;
     179             :                                 }
     180       15600 :                                 location += outputData.stride;
     181             :                         }
     182          75 :                 } else if (outputData.color == PixelFormat::A8 || outputData.color == PixelFormat::I8) {
     183        5650 :                         for (size_t i = 0; i < outputData.height; ++ i) {
     184        5625 :                                 auto loc = location;
     185     2255625 :                                 for (size_t j = 0; j < outputData.width; ++ j) {
     186     2250000 :                                         auto &c = colors->Colors[input[i * outputData.width + j]];
     187     2250000 :                                         *loc++ = c.Red;
     188             :                                 }
     189        5625 :                                 location += outputData.stride;
     190             :                         }
     191          75 :                 } else if (outputData.color == PixelFormat::IA88) {
     192        3775 :                         for (size_t i = 0; i < outputData.height; ++ i) {
     193        3750 :                                 auto loc = location;
     194      566250 :                                 for (size_t j = 0; j < outputData.width; ++ j) {
     195      562500 :                                         auto idx = input[i * outputData.width + j];
     196      562500 :                                         *loc++ = colors->Colors[idx].Red;
     197      562500 :                                         if (idx == transparent) {
     198      540850 :                                                 *loc++ = 0;
     199             :                                         } else {
     200       21650 :                                                 *loc++ = 255;
     201             :                                         }
     202             :                                 }
     203        3750 :                                 location += outputData.stride;
     204             :                         }
     205          25 :                 } else if (outputData.color == PixelFormat::RGBA8888) {
     206        8775 :                         for (size_t i = 0; i < outputData.height; ++ i) {
     207        8750 :                                 auto loc = location;
     208     1758750 :                                 for (size_t j = 0; j < outputData.width; ++ j) {
     209     1750000 :                                         auto idx = input[i * outputData.width + j];
     210     1750000 :                                         auto &c = colors->Colors[idx];
     211     1750000 :                                         *loc++ = c.Red;
     212     1750000 :                                         *loc++ = c.Green;
     213     1750000 :                                         *loc++ = c.Blue;
     214     1750000 :                                         if (idx == transparent) {
     215     1738600 :                                                 *loc++ = 0;
     216             :                                         } else {
     217       11400 :                                                 *loc++ = 255;
     218             :                                         }
     219             :                                 }
     220        8750 :                                 location += outputData.stride;
     221             :                         }
     222             :                 }
     223             : 
     224         125 :                 return true;
     225             :         }
     226             : 
     227             :         int error = 0;
     228             :         GifFileType *file = nullptr;
     229             :         CoderSource reader;
     230             :         size_t transparent = maxOf<size_t>();
     231             : };
     232             : 
     233         125 : SPUNUSED static bool infoGif(const uint8_t *inputData, size_t size, ImageInfo &outputData) {
     234         125 :         GifReadStruct readStruct;
     235         250 :         return readStruct.init(inputData, size) && readStruct.info(outputData);
     236         125 : }
     237             : 
     238         125 : SPUNUSED static bool loadGif(const uint8_t *inputData, size_t size, BitmapWriter &outputData) {
     239         125 :         GifReadStruct readStruct;
     240         250 :         return readStruct.init(inputData, size) && readStruct.load(outputData);
     241         125 : }
     242             : 
     243             : }

Generated by: LCOV version 1.14