Resolve memory issues of webp
[platform/core/uifw/dali-adaptor.git] / dali / internal / imaging / common / loader-webp.cpp
1 /*
2  * Copyright (c) 2021 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 #include <dali/internal/imaging/common/loader-webp.h>
19
20 // EXTERNAL INCLUDES
21 #ifdef DALI_WEBP_AVAILABLE
22 #include <webp/decode.h>
23 #include <webp/demux.h>
24
25 #if WEBP_DEMUX_ABI_VERSION > 0x0101
26 #define DALI_ANIMATED_WEBP_ENABLED 1
27 #endif
28 #endif
29 #include <dali/devel-api/adaptor-framework/pixel-buffer.h>
30 #include <dali/integration-api/debug.h>
31 #include <cstring>
32 #include <memory>
33
34 typedef unsigned char WebPByteType;
35
36 namespace Dali
37 {
38 namespace TizenPlatform
39 {
40 #ifdef DALI_ANIMATED_WEBP_ENABLED
41 bool ReadWebPInformation(FILE* const fp, WebPData& webPData)
42 {
43   if(fp == NULL)
44   {
45     return false;
46   }
47
48   if(fseek(fp, 0, SEEK_END) <= -1)
49   {
50     return false;
51   }
52   WebPDataInit(&webPData);
53   webPData.size = ftell(fp);
54
55   if((!fseek(fp, 0, SEEK_SET)))
56   {
57     unsigned char* WebPDataBuffer;
58     WebPDataBuffer = reinterpret_cast<WebPByteType*>(malloc(sizeof(WebPByteType) * webPData.size));
59     webPData.size  = fread(WebPDataBuffer, sizeof(WebPByteType), webPData.size, fp);
60     webPData.bytes = WebPDataBuffer;
61   }
62   else
63   {
64     return false;
65   }
66   return true;
67 }
68
69 void ReleaseResource(WebPData& webPData, WebPAnimDecoder* webPAnimDecoder)
70 {
71   free((void*)webPData.bytes);
72   webPData.bytes = nullptr;
73   WebPDataInit(&webPData);
74   if(webPAnimDecoder)
75   {
76     WebPAnimDecoderDelete(webPAnimDecoder);
77   }
78 }
79
80 #endif
81
82 bool LoadWebpHeader(const Dali::ImageLoader::Input& input, unsigned int& width, unsigned int& height)
83 {
84   FILE* const fp = input.file;
85   if(fp == NULL)
86   {
87     return false;
88   }
89
90   if(fseek(fp, 0, SEEK_END) <= -1)
91   {
92     return false;
93   }
94
95   // If the image is non-animated webp
96 #ifdef DALI_WEBP_AVAILABLE
97   size_t webPSize = ftell(fp);
98   if((!fseek(fp, 0, SEEK_SET)))
99   {
100     std::vector<uint8_t> encodedImage;
101     encodedImage.resize(webPSize, 0);
102     size_t readCount = fread(&encodedImage[0], sizeof(uint8_t), encodedImage.size(), fp);
103     if(readCount != encodedImage.size())
104     {
105       return false;
106     }
107     int32_t imageWidth, imageHeight;
108     if(WebPGetInfo(&encodedImage[0], encodedImage.size(), &imageWidth, &imageHeight))
109     {
110       width  = static_cast<uint32_t>(imageWidth);
111       height = static_cast<uint32_t>(imageHeight);
112       return true;
113     }
114   }
115 #endif
116
117   // If the image is animated webp
118 #ifdef DALI_ANIMATED_WEBP_ENABLED
119   WebPData         webPData;
120   WebPAnimDecoder* webPAnimDecoder = nullptr;
121   WebPAnimInfo     webPAnimInfo;
122   if(ReadWebPInformation(fp, webPData))
123   {
124     WebPAnimDecoderOptions webPAnimDecoderOptions;
125     WebPAnimDecoderOptionsInit(&webPAnimDecoderOptions);
126     webPAnimDecoderOptions.color_mode = MODE_RGBA;
127     webPAnimDecoder                   = WebPAnimDecoderNew(&webPData, &webPAnimDecoderOptions);
128     if(webPAnimDecoder != nullptr)
129     {
130       WebPAnimDecoderGetInfo(webPAnimDecoder, &webPAnimInfo);
131       width  = webPAnimInfo.canvas_width;
132       height = webPAnimInfo.canvas_height;
133       ReleaseResource(webPData, webPAnimDecoder);
134       return true;
135     }
136   }
137   ReleaseResource(webPData, webPAnimDecoder);
138 #endif
139   DALI_LOG_ERROR("WebP file open failed.\n");
140   return false;
141 }
142
143 bool LoadBitmapFromWebp(const Dali::ImageLoader::Input& input, Dali::Devel::PixelBuffer& bitmap)
144 {
145   FILE* const fp = input.file;
146   if(fp == NULL)
147   {
148     return false;
149   }
150
151   if(fseek(fp, 0, SEEK_END) <= -1)
152   {
153     return false;
154   }
155
156   // If the image is non-animated webp
157 #ifdef DALI_WEBP_AVAILABLE
158   size_t webPSize = ftell(fp);
159   if((!fseek(fp, 0, SEEK_SET)))
160   {
161     std::vector<uint8_t> encodedImage;
162     encodedImage.resize(webPSize, 0);
163     size_t readCount = fread(&encodedImage[0], sizeof(uint8_t), encodedImage.size(), fp);
164     if(readCount != encodedImage.size())
165     {
166       DALI_LOG_ERROR("WebP image loading failed.\n");
167       return false;
168     }
169
170     int32_t width, height;
171     if(!WebPGetInfo(&encodedImage[0], encodedImage.size(), &width, &height))
172     {
173       DALI_LOG_ERROR("Cannot retrieve WebP image size information.\n");
174       return false;
175     }
176
177     WebPBitstreamFeatures features;
178     if(VP8_STATUS_NOT_ENOUGH_DATA == WebPGetFeatures(&encodedImage[0], encodedImage.size(), &features))
179     {
180       DALI_LOG_ERROR("Cannot retrieve WebP image features.\n");
181       return false;
182     }
183
184     uint32_t      channelNumber = (features.has_alpha) ? 4 : 3;
185     Pixel::Format pixelFormat   = (channelNumber == 4) ? Pixel::RGBA8888 : Pixel::RGB888;
186     bitmap                      = Dali::Devel::PixelBuffer::New(width, height, pixelFormat);
187     uint8_t* frameBuffer        = nullptr;
188     if(channelNumber == 4)
189     {
190       frameBuffer = WebPDecodeRGBA(&encodedImage[0], encodedImage.size(), &width, &height);
191     }
192     else
193     {
194       frameBuffer = WebPDecodeRGB(&encodedImage[0], encodedImage.size(), &width, &height);
195     }
196
197     if(frameBuffer != nullptr)
198     {
199       const int32_t bufferSize = width * height * sizeof(uint8_t) * channelNumber;
200       memcpy(bitmap.GetBuffer(), frameBuffer, bufferSize);
201       free((void*)frameBuffer);
202       return true;
203     }
204   }
205 #endif
206
207   // If the image is animated webp
208 #ifdef DALI_ANIMATED_WEBP_ENABLED
209   WebPData         webPData;
210   WebPAnimDecoder* webPAnimDecoder = nullptr;
211   WebPAnimInfo     webPAnimInfo;
212   if(ReadWebPInformation(fp, webPData))
213   {
214     WebPAnimDecoderOptions webPAnimDecoderOptions;
215     WebPAnimDecoderOptionsInit(&webPAnimDecoderOptions);
216     webPAnimDecoderOptions.color_mode = MODE_RGBA;
217     webPAnimDecoder                   = WebPAnimDecoderNew(&webPData, &webPAnimDecoderOptions);
218     if(webPAnimDecoder != nullptr)
219     {
220       uint8_t* frameBuffer;
221       int      timestamp;
222       WebPAnimDecoderGetInfo(webPAnimDecoder, &webPAnimInfo);
223       WebPAnimDecoderReset(webPAnimDecoder);
224       WebPAnimDecoderGetNext(webPAnimDecoder, &frameBuffer, &timestamp);
225
226       bitmap                   = Dali::Devel::PixelBuffer::New(webPAnimInfo.canvas_width, webPAnimInfo.canvas_height, Dali::Pixel::RGBA8888);
227       const int32_t bufferSize = webPAnimInfo.canvas_width * webPAnimInfo.canvas_height * sizeof(uint32_t);
228       memcpy(bitmap.GetBuffer(), frameBuffer, bufferSize);
229       ReleaseResource(webPData, webPAnimDecoder);
230       return true;
231     }
232   }
233   ReleaseResource(webPData, webPAnimDecoder);
234 #endif
235
236   DALI_LOG_ERROR("WebP image loading failed.\n");
237   return false;
238 }
239
240 } // namespace TizenPlatform
241
242 } // namespace Dali