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