1cc1dc7a3Sopenharmony_ci// SPDX-License-Identifier: Apache-2.0
2cc1dc7a3Sopenharmony_ci// ----------------------------------------------------------------------------
3cc1dc7a3Sopenharmony_ci// Copyright 2011-2023 Arm Limited
4cc1dc7a3Sopenharmony_ci//
5cc1dc7a3Sopenharmony_ci// Licensed under the Apache License, Version 2.0 (the "License"); you may not
6cc1dc7a3Sopenharmony_ci// use this file except in compliance with the License. You may obtain a copy
7cc1dc7a3Sopenharmony_ci// of the License at:
8cc1dc7a3Sopenharmony_ci//
9cc1dc7a3Sopenharmony_ci//     http://www.apache.org/licenses/LICENSE-2.0
10cc1dc7a3Sopenharmony_ci//
11cc1dc7a3Sopenharmony_ci// Unless required by applicable law or agreed to in writing, software
12cc1dc7a3Sopenharmony_ci// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13cc1dc7a3Sopenharmony_ci// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14cc1dc7a3Sopenharmony_ci// License for the specific language governing permissions and limitations
15cc1dc7a3Sopenharmony_ci// under the License.
16cc1dc7a3Sopenharmony_ci// ----------------------------------------------------------------------------
17cc1dc7a3Sopenharmony_ci
18cc1dc7a3Sopenharmony_ci/**
19cc1dc7a3Sopenharmony_ci * @brief Functions for building the implementation of stb_image and tinyexr.
20cc1dc7a3Sopenharmony_ci */
21cc1dc7a3Sopenharmony_ci
22cc1dc7a3Sopenharmony_ci#include <cstdlib>
23cc1dc7a3Sopenharmony_ci#include <cstdio>
24cc1dc7a3Sopenharmony_ci#include <fstream>
25cc1dc7a3Sopenharmony_ci#include <vector>
26cc1dc7a3Sopenharmony_ci
27cc1dc7a3Sopenharmony_ci#include "astcenccli_internal.h"
28cc1dc7a3Sopenharmony_ci
29cc1dc7a3Sopenharmony_ci// Configure the STB image write library build.
30cc1dc7a3Sopenharmony_ci#define STB_IMAGE_IMPLEMENTATION
31cc1dc7a3Sopenharmony_ci#define STB_IMAGE_WRITE_IMPLEMENTATION
32cc1dc7a3Sopenharmony_ci#define STBI_NO_GIF
33cc1dc7a3Sopenharmony_ci#define STBI_NO_PIC
34cc1dc7a3Sopenharmony_ci#define STBI_NO_PNM
35cc1dc7a3Sopenharmony_ci#define STBI_NO_PNG
36cc1dc7a3Sopenharmony_ci#define STBI_NO_PSD
37cc1dc7a3Sopenharmony_ci
38cc1dc7a3Sopenharmony_ci// Configure the TinyEXR library build.
39cc1dc7a3Sopenharmony_ci#define TINYEXR_IMPLEMENTATION
40cc1dc7a3Sopenharmony_ci
41cc1dc7a3Sopenharmony_ci// Configure the Wuffs library build.
42cc1dc7a3Sopenharmony_ci#define WUFFS_IMPLEMENTATION
43cc1dc7a3Sopenharmony_ci#define WUFFS_CONFIG__MODULES
44cc1dc7a3Sopenharmony_ci#define WUFFS_CONFIG__MODULE__ADLER32
45cc1dc7a3Sopenharmony_ci#define WUFFS_CONFIG__MODULE__BASE
46cc1dc7a3Sopenharmony_ci#define WUFFS_CONFIG__MODULE__CRC32
47cc1dc7a3Sopenharmony_ci#define WUFFS_CONFIG__MODULE__DEFLATE
48cc1dc7a3Sopenharmony_ci#define WUFFS_CONFIG__MODULE__PNG
49cc1dc7a3Sopenharmony_ci#define WUFFS_CONFIG__MODULE__ZLIB
50cc1dc7a3Sopenharmony_ci#include "wuffs-v0.3.c"
51cc1dc7a3Sopenharmony_ci
52cc1dc7a3Sopenharmony_ci// For both libraries force asserts (which can be triggered by corrupt input
53cc1dc7a3Sopenharmony_ci// images) to be handled at runtime in release builds to avoid security issues.
54cc1dc7a3Sopenharmony_ci#define STBI_ASSERT(x) astcenc_runtime_assert(x)
55cc1dc7a3Sopenharmony_ci#define TEXR_ASSERT(x) astcenc_runtime_assert(x)
56cc1dc7a3Sopenharmony_ci
57cc1dc7a3Sopenharmony_ci/**
58cc1dc7a3Sopenharmony_ci * @brief Trap image load failures and convert into a runtime error.
59cc1dc7a3Sopenharmony_ci */
60cc1dc7a3Sopenharmony_cistatic void astcenc_runtime_assert(bool condition)
61cc1dc7a3Sopenharmony_ci{
62cc1dc7a3Sopenharmony_ci    if (!condition)
63cc1dc7a3Sopenharmony_ci    {
64cc1dc7a3Sopenharmony_ci        print_error("ERROR: Corrupt input image\n");
65cc1dc7a3Sopenharmony_ci        exit(1);
66cc1dc7a3Sopenharmony_ci    }
67cc1dc7a3Sopenharmony_ci}
68cc1dc7a3Sopenharmony_ci
69cc1dc7a3Sopenharmony_ci#include "stb_image.h"
70cc1dc7a3Sopenharmony_ci#include "stb_image_write.h"
71cc1dc7a3Sopenharmony_ci#include "tinyexr.h"
72cc1dc7a3Sopenharmony_ci
73cc1dc7a3Sopenharmony_ci/**
74cc1dc7a3Sopenharmony_ci * @brief Load an image using Wuffs to provide the loader.
75cc1dc7a3Sopenharmony_ci *
76cc1dc7a3Sopenharmony_ci * @param      filename          The name of the file to load.
77cc1dc7a3Sopenharmony_ci * @param      y_flip            Should the image be vertically flipped?
78cc1dc7a3Sopenharmony_ci * @param[out] is_hdr            Is this an HDR image load?
79cc1dc7a3Sopenharmony_ci * @param[out] component_count   The number of components in the data.
80cc1dc7a3Sopenharmony_ci *
81cc1dc7a3Sopenharmony_ci * @return The loaded image data in a canonical 4 channel format, or @c nullptr on error.
82cc1dc7a3Sopenharmony_ci */
83cc1dc7a3Sopenharmony_ciastcenc_image* load_png_with_wuffs(
84cc1dc7a3Sopenharmony_ci	const char* filename,
85cc1dc7a3Sopenharmony_ci	bool y_flip,
86cc1dc7a3Sopenharmony_ci	bool& is_hdr,
87cc1dc7a3Sopenharmony_ci	unsigned int& component_count
88cc1dc7a3Sopenharmony_ci) {
89cc1dc7a3Sopenharmony_ci	is_hdr = false;
90cc1dc7a3Sopenharmony_ci	component_count = 4;
91cc1dc7a3Sopenharmony_ci
92cc1dc7a3Sopenharmony_ci	std::ifstream file(filename, std::ios::binary | std::ios::ate);
93cc1dc7a3Sopenharmony_ci	if (!file)
94cc1dc7a3Sopenharmony_ci	{
95cc1dc7a3Sopenharmony_ci		print_error("ERROR: Failed to load image %s (can't fopen)\n", filename);
96cc1dc7a3Sopenharmony_ci		return nullptr;
97cc1dc7a3Sopenharmony_ci	}
98cc1dc7a3Sopenharmony_ci
99cc1dc7a3Sopenharmony_ci	std::streamsize size = file.tellg();
100cc1dc7a3Sopenharmony_ci	file.seekg(0, std::ios::beg);
101cc1dc7a3Sopenharmony_ci
102cc1dc7a3Sopenharmony_ci	std::vector<uint8_t> buffer(size);
103cc1dc7a3Sopenharmony_ci	file.read((char*)buffer.data(), size);
104cc1dc7a3Sopenharmony_ci
105cc1dc7a3Sopenharmony_ci	wuffs_png__decoder *dec = wuffs_png__decoder__alloc();
106cc1dc7a3Sopenharmony_ci	if (!dec)
107cc1dc7a3Sopenharmony_ci	{
108cc1dc7a3Sopenharmony_ci		return nullptr;
109cc1dc7a3Sopenharmony_ci	}
110cc1dc7a3Sopenharmony_ci
111cc1dc7a3Sopenharmony_ci	wuffs_base__image_config ic;
112cc1dc7a3Sopenharmony_ci	wuffs_base__io_buffer src = wuffs_base__ptr_u8__reader(buffer.data(), size, true);
113cc1dc7a3Sopenharmony_ci	wuffs_base__status status = wuffs_png__decoder__decode_image_config(dec, &ic, &src);
114cc1dc7a3Sopenharmony_ci	if (status.repr)
115cc1dc7a3Sopenharmony_ci	{
116cc1dc7a3Sopenharmony_ci		return nullptr;
117cc1dc7a3Sopenharmony_ci	}
118cc1dc7a3Sopenharmony_ci
119cc1dc7a3Sopenharmony_ci	uint32_t dim_x = wuffs_base__pixel_config__width(&ic.pixcfg);
120cc1dc7a3Sopenharmony_ci	uint32_t dim_y = wuffs_base__pixel_config__height(&ic.pixcfg);
121cc1dc7a3Sopenharmony_ci	size_t num_pixels = dim_x * dim_y;
122cc1dc7a3Sopenharmony_ci	if (num_pixels > (SIZE_MAX / 4))
123cc1dc7a3Sopenharmony_ci	{
124cc1dc7a3Sopenharmony_ci		return nullptr;
125cc1dc7a3Sopenharmony_ci	}
126cc1dc7a3Sopenharmony_ci
127cc1dc7a3Sopenharmony_ci	// Override the image's native pixel format to be RGBA_NONPREMUL
128cc1dc7a3Sopenharmony_ci	wuffs_base__pixel_config__set(
129cc1dc7a3Sopenharmony_ci	    &ic.pixcfg,
130cc1dc7a3Sopenharmony_ci	    WUFFS_BASE__PIXEL_FORMAT__RGBA_NONPREMUL,
131cc1dc7a3Sopenharmony_ci	    WUFFS_BASE__PIXEL_SUBSAMPLING__NONE,
132cc1dc7a3Sopenharmony_ci	    dim_x, dim_y);
133cc1dc7a3Sopenharmony_ci
134cc1dc7a3Sopenharmony_ci	// Configure the work buffer
135cc1dc7a3Sopenharmony_ci	size_t workbuf_len = wuffs_png__decoder__workbuf_len(dec).max_incl;
136cc1dc7a3Sopenharmony_ci	if (workbuf_len > SIZE_MAX)
137cc1dc7a3Sopenharmony_ci	{
138cc1dc7a3Sopenharmony_ci		return nullptr;
139cc1dc7a3Sopenharmony_ci	}
140cc1dc7a3Sopenharmony_ci
141cc1dc7a3Sopenharmony_ci	wuffs_base__slice_u8 workbuf_slice = wuffs_base__make_slice_u8((uint8_t*)malloc(workbuf_len), workbuf_len);
142cc1dc7a3Sopenharmony_ci	if (!workbuf_slice.ptr)
143cc1dc7a3Sopenharmony_ci	{
144cc1dc7a3Sopenharmony_ci		return nullptr;
145cc1dc7a3Sopenharmony_ci	}
146cc1dc7a3Sopenharmony_ci
147cc1dc7a3Sopenharmony_ci	wuffs_base__slice_u8 pixbuf_slice = wuffs_base__make_slice_u8((uint8_t*)malloc(num_pixels * 4), num_pixels * 4);
148cc1dc7a3Sopenharmony_ci	if (!pixbuf_slice.ptr)
149cc1dc7a3Sopenharmony_ci	{
150cc1dc7a3Sopenharmony_ci		return nullptr;
151cc1dc7a3Sopenharmony_ci	}
152cc1dc7a3Sopenharmony_ci
153cc1dc7a3Sopenharmony_ci	wuffs_base__pixel_buffer pb;
154cc1dc7a3Sopenharmony_ci	status = wuffs_base__pixel_buffer__set_from_slice(&pb, &ic.pixcfg, pixbuf_slice);
155cc1dc7a3Sopenharmony_ci	if (status.repr)
156cc1dc7a3Sopenharmony_ci	{
157cc1dc7a3Sopenharmony_ci		return nullptr;
158cc1dc7a3Sopenharmony_ci	}
159cc1dc7a3Sopenharmony_ci
160cc1dc7a3Sopenharmony_ci	// Decode the pixels
161cc1dc7a3Sopenharmony_ci	status = wuffs_png__decoder__decode_frame(dec, &pb, &src, WUFFS_BASE__PIXEL_BLEND__SRC, workbuf_slice, NULL);
162cc1dc7a3Sopenharmony_ci	if (status.repr)
163cc1dc7a3Sopenharmony_ci	{
164cc1dc7a3Sopenharmony_ci		return nullptr;
165cc1dc7a3Sopenharmony_ci	}
166cc1dc7a3Sopenharmony_ci
167cc1dc7a3Sopenharmony_ci	astcenc_image* img = astc_img_from_unorm8x4_array(pixbuf_slice.ptr, dim_x, dim_y, y_flip);
168cc1dc7a3Sopenharmony_ci
169cc1dc7a3Sopenharmony_ci	free(pixbuf_slice.ptr);
170cc1dc7a3Sopenharmony_ci	free(workbuf_slice.ptr);
171cc1dc7a3Sopenharmony_ci	free(dec);
172cc1dc7a3Sopenharmony_ci
173cc1dc7a3Sopenharmony_ci	return img;
174cc1dc7a3Sopenharmony_ci}
175