• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "image/loaders/image_loader_stb_image.h"
17 #if defined(USE_STB_IMAGE) && (USE_STB_IMAGE == 1)
18 #include <cstddef>
19 #include <cstdint>
20 #include <limits>
21 
22 //
23 // Enabling only formats that are actually used.
24 // Others available:
25 //        STBI_ONLY_BMP
26 //        STBI_ONLY_PSD
27 //        STBI_ONLY_TGA
28 //        STBI_ONLY_GIF
29 //        STBI_ONLY_HDR
30 //        STBI_ONLY_PIC
31 //        STBI_ONLY_PNM   (.ppm and .pgm)
32 #define STBI_ONLY_JPEG
33 #define STBI_ONLY_PNG
34 
35 // Without this a few gif functions are still included.
36 #define STBI_NO_GIF
37 
38 // Currently we always load from memory so disabling support for loading directly from a file.
39 #define STBI_NO_STDIO
40 
41 // This makes the stb_image implementation local to this compilation unit
42 // (So it wont interfere if someone else is using stb_image too).
43 #define STB_IMAGE_STATIC
44 #define STB_IMAGE_IMPLEMENTATION
45 
46 // On x86, SSE2 will automatically be used when available based on a run-time
47 // test; if not, the generic C versions are used as a fall-back. On ARM targets,
48 // the typical path is to have separate builds for NEON and non-NEON devices
49 // Therefore, the NEON support is toggled by a build flag: define STBI_NEON to
50 // get NEON loops
51 //
52 // Assuming that NEON is supported by our target devices.
53 // (on NDK r21 and newer NEON is enabled by default anyway).
54 #if defined(__arm__) || defined(__aarch64__)
55 #define STBI_NEON
56 #endif
57 
58 #include <stb_image.h>
59 
60 #include <base/containers/array_view.h>
61 #include <base/containers/string.h>
62 #include <base/containers/string_view.h>
63 #include <base/containers/type_traits.h>
64 #include <base/containers/unique_ptr.h>
65 #include <base/math/mathf.h>
66 #include <base/namespace.h>
67 #include <base/util/formats.h>
68 #include <core/image/intf_image_container.h>
69 #include <core/image/intf_image_loader_manager.h>
70 #include <core/io/intf_file.h>
71 #include <core/log.h>
72 #include <core/namespace.h>
73 
74 #include "image/image_loader_manager.h"
75 
76 CORE_BEGIN_NAMESPACE()
77 namespace {
78 using BASE_NS::array_view;
79 using BASE_NS::Format;
80 using BASE_NS::make_unique;
81 using BASE_NS::move;
82 using BASE_NS::string;
83 using BASE_NS::string_view;
84 using BASE_NS::unique_ptr;
85 using BASE_NS::vector;
86 using BASE_NS::Math::pow;
87 using BASE_NS::Math::round;
88 
89 // NOTE: Reading the stb error code is NOT THREADSAFE.
90 // Enable this if you really need to know the error message.
91 constexpr const bool CORE_ENABLE_STB_NON_THREADSAFE_ERROR_MSG = false;
92 // On desktop typical dimension limit for GPU images is 16k. On mobile even less.
93 constexpr const int32_t MAX_DIMENSIONS = 16384;
94 
FreeStbImageBytes(void * imageBytes)95 void FreeStbImageBytes(void* imageBytes)
96 {
97     stbi_image_free(imageBytes);
98 }
99 
100 uint8_t g_sRgbPremultiplyLookup[256u * 256u] = { 0 };
101 
InitializeSRGBTable()102 void InitializeSRGBTable()
103 {
104     // Generate lookup table to premultiply sRGB encoded image in linear space and reencoding it to sRGB
105     // Formulas from https://en.wikipedia.org/wiki/SRGB
106     for (uint32_t a = 0; a < 256u; a++) {
107         const float alpha = static_cast<float>(a) / 255.f;
108         for (uint32_t sRGB = 0; sRGB < 256u; sRGB++) {
109             float color = static_cast<float>(sRGB) / 255.f;
110             if (color <= 0.04045f) {
111                 color *= (1.f / 12.92f);
112             } else {
113                 color = pow((color + 0.055f) * (1.f / 1.055f), 2.4f);
114             }
115             float premultiplied = color * alpha;
116             if (premultiplied <= 0.0031308f) {
117                 premultiplied *= 12.92f;
118             } else {
119                 premultiplied = 1.055f * pow(premultiplied, 1.f / 2.4f) - 0.055f;
120             }
121             g_sRgbPremultiplyLookup[a * 256u + sRGB] = static_cast<uint8_t>(round(premultiplied * 255.f));
122         }
123     }
124 }
125 
PremultiplyAlpha(uint8_t * imageBytes,uint32_t width,uint32_t height,uint32_t channelCount,uint32_t bytesPerChannel,bool linear)126 bool PremultiplyAlpha(
127     uint8_t* imageBytes, uint32_t width, uint32_t height, uint32_t channelCount, uint32_t bytesPerChannel, bool linear)
128 {
129     // Only need to process images with color and alpha data. I.e. RGBA or grayscale + alpha.
130     if (channelCount != 4u && channelCount != 2u) {
131         return true;
132     }
133 
134     const uint32_t pixelCount = width * height;
135 
136     if (bytesPerChannel == 1) {
137         if (linear) {
138             uint8_t* img = imageBytes;
139             for (uint32_t i = 0; i < pixelCount; i++) {
140                 // We know the alpha value is always last.
141                 uint32_t alpha = img[channelCount - 1];
142                 for (uint32_t j = 0; j < channelCount - 1; j++) {
143                     *img = static_cast<uint8_t>(*img * alpha / 0xff);
144                     img++;
145                 }
146                 img++; // Skip over the alpha value.
147             }
148         } else {
149             if (g_sRgbPremultiplyLookup[256u * 256u - 1] == 0) {
150                 InitializeSRGBTable();
151             }
152             uint8_t* img = imageBytes;
153             for (uint32_t i = 0; i < pixelCount; i++) {
154                 uint8_t* p = &g_sRgbPremultiplyLookup[img[channelCount - 1] * 256u];
155                 for (uint32_t j = 0; j < channelCount - 1; j++) {
156                     *img = p[*img];
157                     img++;
158                 }
159                 img++;
160             }
161         }
162     } else if (bytesPerChannel == 2u) {
163         // Same for 16 bits per channel images.
164         auto* img = reinterpret_cast<uint16_t*>(imageBytes);
165         for (uint32_t i = 0; i < pixelCount; i++) {
166             uint32_t alpha = img[channelCount - 1];
167             for (uint32_t j = 0; j < channelCount - 1; j++) {
168                 *img = static_cast<uint16_t>(*img * alpha / 0xffff);
169                 img++;
170             }
171             img++;
172         }
173     } else {
174         CORE_LOG_E("Format not supported.");
175         return false;
176     }
177     return true;
178 }
179 
180 using StbImagePtr = unique_ptr<void, decltype(&FreeStbImageBytes)>;
181 } // namespace
182 
183 class StbImage final : public IImageContainer {
184 public:
StbImage()185     StbImage() : IImageContainer(), imageDesc_(), imageBuffer_() {}
186 
187     using Ptr = BASE_NS::unique_ptr<StbImage, Deleter>;
188 
GetImageDesc() const189     const ImageDesc& GetImageDesc() const override
190     {
191         return imageDesc_;
192     }
193 
GetData() const194     array_view<const uint8_t> GetData() const override
195     {
196         return { static_cast<const uint8_t*>(imageBytes_.get()), imageBytesLength_ };
197     }
198 
GetBufferImageCopies() const199     array_view<const SubImageDesc> GetBufferImageCopies() const override
200     {
201         return { &imageBuffer_, 1 };
202     }
203 
ResolveFormat(uint32_t loadFlags,uint32_t componentCount,bool is16bpc)204     static constexpr Format ResolveFormat(uint32_t loadFlags, uint32_t componentCount, bool is16bpc)
205     {
206         Format format {};
207         const bool forceLinear = (loadFlags & IImageLoaderManager::IMAGE_LOADER_FORCE_LINEAR_RGB_BIT) != 0;
208 
209         switch (componentCount) {
210             case 1u:
211                 format = is16bpc ? Format::BASE_FORMAT_R16_UNORM
212                                  : (forceLinear ? Format::BASE_FORMAT_R8_UNORM : Format::BASE_FORMAT_R8_SRGB);
213                 break;
214             case 2u:
215                 format = is16bpc ? Format::BASE_FORMAT_R16G16_UNORM
216                                  : (forceLinear ? Format::BASE_FORMAT_R8G8_UNORM : Format::BASE_FORMAT_R8G8_SRGB);
217                 break;
218             case 3u:
219                 format = is16bpc ? Format::BASE_FORMAT_R16G16B16_UNORM
220                                  : (forceLinear ? Format::BASE_FORMAT_R8G8B8_UNORM : Format::BASE_FORMAT_R8G8B8_SRGB);
221                 break;
222             case 4u:
223                 format = is16bpc
224                              ? Format::BASE_FORMAT_R16G16B16A16_UNORM
225                              : (forceLinear ? Format::BASE_FORMAT_R8G8B8A8_UNORM : Format::BASE_FORMAT_R8G8B8A8_SRGB);
226                 break;
227             default:
228                 format = Format::BASE_FORMAT_UNDEFINED;
229                 break;
230         }
231 
232         return format;
233     }
234 
ResolveImageDesc(Format format,uint32_t imageWidth,uint32_t imageHeight,uint32_t bitsPerPixel,bool generateMips,bool isPremultiplied)235     constexpr static ImageDesc ResolveImageDesc(Format format, uint32_t imageWidth, uint32_t imageHeight,
236         uint32_t bitsPerPixel, bool generateMips, bool isPremultiplied)
237     {
238         uint32_t mipCount = 1;
239 
240         // 1D images not supported with stb loader
241         constexpr ImageType imageType = ImageType::TYPE_2D;
242         constexpr ImageViewType imageViewType = ImageViewType::VIEW_TYPE_2D;
243 
244         uint32_t imageFlags = isPremultiplied ? ImageFlags::FLAGS_PREMULTIPLIED_ALPHA_BIT : 0;
245         if (generateMips) {
246             imageFlags |= ImageFlags::FLAGS_REQUESTING_MIPMAPS_BIT;
247             uint32_t mipsize = (imageWidth > imageHeight) ? imageWidth : imageHeight;
248             mipCount = 0;
249             while (mipsize > 0) {
250                 mipCount++;
251                 mipsize >>= 1;
252             }
253         }
254 
255         return ImageDesc {
256             imageFlags,   // imageFlags
257             1,            // blockPixelWidth
258             1,            // blockPixelHeight
259             1,            // blockPixelDepth
260             bitsPerPixel, // bitsPerBlock
261 
262             imageType,     // imageType
263             imageViewType, // imageViewType
264             format,        // format
265 
266             static_cast<uint32_t>(imageWidth),  // width
267             static_cast<uint32_t>(imageHeight), // height
268             1,                                  // depth
269 
270             mipCount, // mipCount
271             1,        // layerCount
272         };
273     }
274 
CreateImage(StbImagePtr imageBytes,uint32_t imageWidth,uint32_t imageHeight,uint32_t componentCount,uint32_t loadFlags,bool is16bpc)275     static ImageLoaderManager::LoadResult CreateImage(StbImagePtr imageBytes, uint32_t imageWidth, uint32_t imageHeight,
276         uint32_t componentCount, uint32_t loadFlags, bool is16bpc)
277     {
278         auto image = StbImage::Ptr(new StbImage);
279         if (!image) {
280             return ImageLoaderManager::ResultFailure("Loading image failed.");
281         }
282 
283         // Premultiply alpha if requested.
284         bool isPremultiplied = false;
285         if ((loadFlags & IImageLoaderManager::IMAGE_LOADER_METADATA_ONLY) == 0) {
286             if ((loadFlags & IImageLoaderManager::IMAGE_LOADER_PREMULTIPLY_ALPHA) != 0) {
287                 const uint32_t bytesPerChannel = is16bpc ? 2u : 1u;
288                 const bool forceLinear = (loadFlags & IImageLoaderManager::IMAGE_LOADER_FORCE_LINEAR_RGB_BIT) != 0;
289                 isPremultiplied = PremultiplyAlpha(static_cast<uint8_t*>(imageBytes.get()), imageWidth, imageHeight,
290                     componentCount, bytesPerChannel, forceLinear);
291             }
292         }
293 
294         const Format format = ResolveFormat(loadFlags, componentCount, is16bpc);
295 
296         const bool generateMips = (loadFlags & IImageLoaderManager::IMAGE_LOADER_GENERATE_MIPS) != 0;
297         const uint32_t bytesPerComponent = is16bpc ? 2u : 1u;
298         const uint32_t bitsPerPixel = bytesPerComponent * componentCount * 8u;
299 
300         image->imageDesc_ =
301             ResolveImageDesc(format, imageWidth, imageHeight, bitsPerPixel, generateMips, isPremultiplied);
302         image->imageBytes_ = BASE_NS::move(imageBytes);
303         image->imageBytesLength_ = imageWidth * imageHeight * componentCount * bytesPerComponent;
304 
305         image->imageBuffer_ = SubImageDesc {
306             0,           // uint32_t bufferOffset
307             imageWidth,  // uint32_t bufferRowLength
308             imageHeight, // uint32_t bufferImageHeight
309 
310             0, // uint32_t mipLevel
311             1, // uint32_t layerCount
312 
313             static_cast<uint32_t>(imageWidth),
314             static_cast<uint32_t>(imageHeight),
315             1,
316         };
317 
318         return ImageLoaderManager::ResultSuccess(CORE_NS::move(image));
319     }
320 
321     struct Info {
322         int width;
323         int height;
324         int componentCount;
325         bool is16bpc;
326     };
327 
LoadFromMemory(array_view<const uint8_t> imageFileBytes,uint32_t loadFlags,Info & info)328     static StbImagePtr LoadFromMemory(array_view<const uint8_t> imageFileBytes, uint32_t loadFlags, Info& info)
329     {
330         StbImagePtr imageBytes = nullptr;
331         // Convert 3 channels to 4 because 3 channel textures are not always supported.
332         // Also convert 2 channel (grayscale + alpha) to 4 because r + a in not supported.
333         int requestedComponentCount = (info.componentCount == 3 || info.componentCount == 2) ? 4 : 0;
334 
335         // Force grayscale if requested.
336         if ((loadFlags & IImageLoaderManager::IMAGE_LOADER_FORCE_GRAYSCALE_BIT) != 0) {
337             requestedComponentCount = 1;
338         }
339 
340         // Load the image byte data.
341         if (info.is16bpc) {
342             auto image = stbi_load_16_from_memory(imageFileBytes.data(), static_cast<int>(imageFileBytes.size()),
343                 &info.width, &info.height, &info.componentCount, requestedComponentCount);
344             imageBytes = { image, FreeStbImageBytes };
345         } else {
346             auto image = stbi_load_from_memory(imageFileBytes.data(), static_cast<int>(imageFileBytes.size()),
347                 &info.width, &info.height, &info.componentCount, requestedComponentCount);
348             imageBytes = { image, FreeStbImageBytes };
349         }
350 
351         if (requestedComponentCount != 0) {
352             info.componentCount = requestedComponentCount;
353         }
354         return imageBytes;
355     }
356 
357     // Actual stb_image loading implementation.
Load(array_view<const uint8_t> imageFileBytes,uint32_t loadFlags)358     static ImageLoaderManager::LoadResult Load(array_view<const uint8_t> imageFileBytes, uint32_t loadFlags)
359     {
360         if (imageFileBytes.empty()) {
361             return ImageLoaderManager::ResultFailure("Input data must not be null.");
362         }
363 
364         // Load the image info without decoding the image data
365         // (Just to check what the image format is so we can convert if necessary).
366         Info info {};
367 
368         const int result = stbi_info_from_memory(imageFileBytes.data(), static_cast<int>(imageFileBytes.size()),
369             &info.width, &info.height, &info.componentCount);
370 
371         info.is16bpc =
372             (stbi_is_16_bit_from_memory(imageFileBytes.data(), static_cast<int>(imageFileBytes.size())) != 0);
373 
374         // Not supporting hdr images via stb_image.
375 #if !defined(NDEBUG)
376         if (stbi_is_hdr_from_memory(imageFileBytes.data(), static_cast<int>(imageFileBytes.size()))) {
377             CORE_LOG_D("HDR format detected.");
378         }
379 #endif
380         StbImagePtr imageBytes = nullptr;
381         if (result) {
382             if ((info.width > MAX_DIMENSIONS) || (info.height > MAX_DIMENSIONS)) {
383                 return ImageLoaderManager::ResultFailure("Image dimensions too big");
384             }
385             if ((loadFlags & IImageLoaderManager::IMAGE_LOADER_METADATA_ONLY) == 0) {
386                 imageBytes = LoadFromMemory(imageFileBytes, loadFlags, info);
387                 // Flip vertically if requested.
388                 if (imageBytes && (loadFlags & IImageLoaderManager::IMAGE_LOADER_FLIP_VERTICALLY_BIT) != 0) {
389                     stbi__vertical_flip(imageBytes.get(), info.width, info.height, info.componentCount);
390                 }
391             } else {
392                 imageBytes = { nullptr, FreeStbImageBytes };
393             }
394         }
395 
396         if (!result || (((loadFlags & IImageLoaderManager::IMAGE_LOADER_METADATA_ONLY) == 0) && !imageBytes)) {
397             if constexpr (CORE_ENABLE_STB_NON_THREADSAFE_ERROR_MSG) {
398                 const string errorString = string_view("Loading image failed: ") + string_view(stbi_failure_reason());
399                 return ImageLoaderManager::ResultFailure(errorString);
400             }
401             return ImageLoaderManager::ResultFailure("Loading image failed");
402         }
403 
404         // Success. Populate the image info and image data object.
405         return CreateImage(CORE_NS::move(imageBytes), static_cast<uint32_t>(info.width),
406             static_cast<uint32_t>(info.height), static_cast<uint32_t>(info.componentCount), loadFlags, info.is16bpc);
407     }
408 
409 protected:
Destroy()410     void Destroy() override
411     {
412         delete this;
413     }
414 
415 private:
416     ImageDesc imageDesc_;
417     SubImageDesc imageBuffer_;
418 
419     StbImagePtr imageBytes_;
420     size_t imageBytesLength_ = 0;
421 };
422 
423 class ImageLoaderStbImage final : public ImageLoaderManager::IImageLoader {
424 public:
425     // Inherited via ImageManager::IImageLoader
Load(IFile & file,uint32_t loadFlags) const426     ImageLoaderManager::LoadResult Load(IFile& file, uint32_t loadFlags) const override
427     {
428         const uint64_t byteLength = file.GetLength();
429         // stb_image uses int for file sizes. Don't even try to read a file if the size does not fit to int.
430         if (byteLength > static_cast<uint64_t>(std::numeric_limits<int>::max())) {
431             return ImageLoaderManager::ResultFailure("File too big to read.");
432         }
433 
434         // Read the file to a buffer.
435         unique_ptr<uint8_t[]> buffer = make_unique<uint8_t[]>(static_cast<size_t>(byteLength));
436         const uint64_t read = file.Read(buffer.get(), byteLength);
437         if (read != byteLength) {
438             return ImageLoaderManager::ResultFailure("Reading file failed.");
439         }
440 
441         return StbImage::Load(array_view<const uint8_t>(buffer.get(), static_cast<size_t>(byteLength)), loadFlags);
442     }
443 
Load(array_view<const uint8_t> imageFileBytes,uint32_t loadFlags) const444     ImageLoaderManager::LoadResult Load(array_view<const uint8_t> imageFileBytes, uint32_t loadFlags) const override
445     {
446         // stb_image uses int for file sizes. Don't even try to read a file if the size does not fit to int.
447         // Not writing a test for this :)
448         if (imageFileBytes.size() > static_cast<size_t>(std::numeric_limits<int>::max())) {
449             return ImageLoaderManager::ResultFailure("Data too big to read.");
450         }
451 
452         return StbImage::Load(imageFileBytes, loadFlags);
453     }
454 
CanLoad(array_view<const uint8_t> imageFileBytes) const455     bool CanLoad(array_view<const uint8_t> imageFileBytes) const override
456     {
457         // stb_image uses int for file sizes. Don't even try to read a file if the size does not fit to int.
458         // Not writing a test for this :)
459         if (imageFileBytes.size() > static_cast<size_t>(std::numeric_limits<int>::max())) {
460             return false;
461         }
462 
463         // Check for PNG
464         if ((imageFileBytes.size() >= 8) && imageFileBytes[0] == 137 && imageFileBytes[1] == 80 && // 8,137,80 : param
465             imageFileBytes[2] == 78 && imageFileBytes[3] == 71 && imageFileBytes[4] == 13 && // 2,78,3,71,4,13 : param
466             imageFileBytes[5] == 10 && imageFileBytes[6] == 26 && imageFileBytes[7] == 10) { // 5,10,6,26,7,10 : param
467             return true;
468         }
469 
470         // Check for JPEG / JFIF / Exif / ICC_PROFILE tag
471         if ((imageFileBytes.size() >= 10) && imageFileBytes[0] == 0xff && imageFileBytes[1] == 0xd8 &&
472             imageFileBytes[2] == 0xff && // 2 : idx
473             ((imageFileBytes[3] == 0xe0 && imageFileBytes[6] == 'J' && imageFileBytes[7] == 'F' && // 3,6,7: idx
474                 imageFileBytes[8] == 'I' && imageFileBytes[9] == 'F') || // 8,9 :idx JFIF
475                 (imageFileBytes[3] == 0xe1 && imageFileBytes[6] == 'E' && imageFileBytes[7] == 'x' && // 3,6,7 : idx
476                     imageFileBytes[8] == 'i' && imageFileBytes[9] == 'f') || // 8,9 : idx Exif
477                 (imageFileBytes[3] == 0xe2 && imageFileBytes[6] == 'I' && imageFileBytes[7] == 'C' && // 3,6,7 : idx
478                     imageFileBytes[8] == 'C' && imageFileBytes[9] == '_'))) { // 8,9 : idx ICC_PROFILE
479             return true;
480         }
481 
482         return false;
483     }
484 
485     // No animation support
LoadAnimatedImage(IFile &,uint32_t)486     ImageLoaderManager::LoadAnimatedResult LoadAnimatedImage(IFile& /* file */, uint32_t /* loadFlags */) override
487     {
488         return ImageLoaderManager::ResultFailureAnimated("Animation not supported.");
489     }
490 
LoadAnimatedImage(array_view<const uint8_t>,uint32_t)491     ImageLoaderManager::LoadAnimatedResult LoadAnimatedImage(
492         array_view<const uint8_t> /* imageFileBytes */, uint32_t /* loadFlags */) override
493     {
494         return ImageLoaderManager::ResultFailureAnimated("Animation not supported.");
495     }
496 
GetSupportedTypes() const497     vector<IImageLoaderManager::ImageType> GetSupportedTypes() const override
498     {
499         return { std::begin(STB_IMAGE_TYPES), std::end(STB_IMAGE_TYPES) };
500     }
501 
502 protected:
Destroy()503     void Destroy() override
504     {
505         delete this;
506     }
507 };
508 
CreateImageLoaderStbImage(PluginToken)509 IImageLoaderManager::IImageLoader::Ptr CreateImageLoaderStbImage(PluginToken)
510 {
511     return ImageLoaderManager::IImageLoader::Ptr { new ImageLoaderStbImage() };
512 }
513 CORE_END_NAMESPACE()
514 #endif // defined(USE_STB_IMAGE) && (USE_STB_IMAGE == 1)
515