1 #ifndef LIBHEIF_BOX_EMSCRIPTEN_H
2 #define LIBHEIF_BOX_EMSCRIPTEN_H
3 
4 #include <emscripten/bind.h>
5 
6 #include <memory>
7 #include <string>
8 #include <sstream>
9 #include <utility>
10 #include <vector>
11 
12 #include "heif.h"
13 
_heif_get_version()14 static std::string _heif_get_version()
15 {
16   return heif_get_version();
17 }
18 
_heif_context_read_from_memory(struct heif_context * context,const std::string & data)19 static struct heif_error _heif_context_read_from_memory(
20     struct heif_context* context, const std::string& data)
21 {
22   return heif_context_read_from_memory(context, data.data(), data.size(), nullptr);
23 }
24 
strided_copy(void * dest,const void * src,int width,int height,int stride)25 static void strided_copy(void* dest, const void* src, int width, int height,
26                          int stride)
27 {
28   if (width == stride) {
29     memcpy(dest, src, width * height);
30   }
31   else {
32     const uint8_t* _src = static_cast<const uint8_t*>(src);
33     uint8_t* _dest = static_cast<uint8_t*>(dest);
34     for (int y = 0; y < height; y++, _dest += width, _src += stride) {
35       memcpy(_dest, _src, stride);
36     }
37   }
38 }
39 
heif_js_context_get_image_handle(struct heif_context * context,heif_item_id id)40 static emscripten::val heif_js_context_get_image_handle(
41     struct heif_context* context, heif_item_id id)
42 {
43   emscripten::val result = emscripten::val::object();
44   if (!context) {
45     return result;
46   }
47 
48   struct heif_image_handle* handle;
49   struct heif_error err = heif_context_get_image_handle(context, id, &handle);
50   if (err.code != heif_error_Ok) {
51     return emscripten::val(err);
52   }
53 
54   return emscripten::val(handle);
55 }
56 
heif_js_context_get_list_of_top_level_image_IDs(struct heif_context * context)57 static emscripten::val heif_js_context_get_list_of_top_level_image_IDs(
58     struct heif_context* context)
59 {
60   emscripten::val result = emscripten::val::array();
61   if (!context) {
62     return result;
63   }
64 
65   int count = heif_context_get_number_of_top_level_images(context);
66   if (count <= 0) {
67     return result;
68   }
69 
70   heif_item_id* ids = (heif_item_id*) malloc(count * sizeof(heif_item_id));
71   if (!ids) {
72     struct heif_error err;
73     err.code = heif_error_Memory_allocation_error;
74     err.subcode = heif_suberror_Security_limit_exceeded;
75     return emscripten::val(err);
76   }
77 
78   int received = heif_context_get_list_of_top_level_image_IDs(context, ids, count);
79   if (!received) {
80     free(ids);
81     return result;
82   }
83 
84   for (int i = 0; i < received; i++) {
85     result.set(i, ids[i]);
86   }
87   free(ids);
88   return result;
89 }
90 
round_odd(int v)91 static int round_odd(int v) {
92   return (int) ((v / 2.0) + 0.5);
93 }
94 
heif_js_decode_image(struct heif_image_handle * handle,enum heif_colorspace colorspace,enum heif_chroma chroma)95 static emscripten::val heif_js_decode_image(struct heif_image_handle* handle,
96                                             enum heif_colorspace colorspace, enum heif_chroma chroma)
97 {
98   emscripten::val result = emscripten::val::object();
99   if (!handle) {
100     return result;
101   }
102 
103   struct heif_image* image;
104   struct heif_error err = heif_decode_image(handle, &image, colorspace, chroma, nullptr);
105   if (err.code != heif_error_Ok) {
106     return emscripten::val(err);
107   }
108 
109   result.set("is_primary", heif_image_handle_is_primary_image(handle));
110   result.set("thumbnails", heif_image_handle_get_number_of_thumbnails(handle));
111   int width = heif_image_get_width(image, heif_channel_Y);
112   result.set("width", width);
113   int height = heif_image_get_height(image, heif_channel_Y);
114   result.set("height", height);
115   std::string data;
116   result.set("chroma", heif_image_get_chroma_format(image));
117   result.set("colorspace", heif_image_get_colorspace(image));
118   switch (heif_image_get_colorspace(image)) {
119     case heif_colorspace_YCbCr: {
120       int stride_y;
121       const uint8_t* plane_y = heif_image_get_plane_readonly(image,
122                                                              heif_channel_Y, &stride_y);
123       int stride_u;
124       const uint8_t* plane_u = heif_image_get_plane_readonly(image,
125                                                              heif_channel_Cb, &stride_u);
126       int stride_v;
127       const uint8_t* plane_v = heif_image_get_plane_readonly(image,
128                                                              heif_channel_Cr, &stride_v);
129       data.resize((width * height) + (2 * round_odd(width) * round_odd(height)));
130       char* dest = const_cast<char*>(data.data());
131       strided_copy(dest, plane_y, width, height, stride_y);
132       strided_copy(dest + (width * height), plane_u,
133                    round_odd(width), round_odd(height), stride_u);
134       strided_copy(dest + (width * height) + (round_odd(width) * round_odd(height)),
135                    plane_v, round_odd(width), round_odd(height), stride_v);
136     }
137       break;
138     case heif_colorspace_RGB: {
139       assert(heif_image_get_chroma_format(image) ==
140              heif_chroma_interleaved_24bit);
141       int stride_rgb;
142       const uint8_t* plane_rgb = heif_image_get_plane_readonly(image,
143                                                                heif_channel_interleaved, &stride_rgb);
144       data.resize(width * height * 3);
145       char* dest = const_cast<char*>(data.data());
146       strided_copy(dest, plane_rgb, width * 3, height, stride_rgb);
147     }
148       break;
149     case heif_colorspace_monochrome: {
150       assert(heif_image_get_chroma_format(image) ==
151              heif_chroma_monochrome);
152       int stride_grey;
153       const uint8_t* plane_grey = heif_image_get_plane_readonly(image,
154                                                                 heif_channel_Y, &stride_grey);
155       data.resize(width * height);
156       char* dest = const_cast<char*>(data.data());
157       strided_copy(dest, plane_grey, width, height, stride_grey);
158     }
159       break;
160     default:
161       // Should never reach here.
162       break;
163   }
164   result.set("data", std::move(data));
165   heif_image_release(image);
166   return result;
167 }
168 
169 #define EXPORT_HEIF_FUNCTION(name) \
170   emscripten::function(#name, &name, emscripten::allow_raw_pointers())
171 
EMSCRIPTEN_BINDINGS(libheif)172 EMSCRIPTEN_BINDINGS(libheif) {
173     emscripten::function("heif_get_version", &_heif_get_version,
174                          emscripten::allow_raw_pointers());
175     EXPORT_HEIF_FUNCTION(heif_get_version_number);
176 
177     EXPORT_HEIF_FUNCTION(heif_context_alloc);
178     EXPORT_HEIF_FUNCTION(heif_context_free);
179     emscripten::function("heif_context_read_from_memory",
180     &_heif_context_read_from_memory, emscripten::allow_raw_pointers());
181     EXPORT_HEIF_FUNCTION(heif_context_get_number_of_top_level_images);
182     emscripten::function("heif_js_context_get_list_of_top_level_image_IDs",
183     &heif_js_context_get_list_of_top_level_image_IDs, emscripten::allow_raw_pointers());
184     emscripten::function("heif_js_context_get_image_handle",
185     &heif_js_context_get_image_handle, emscripten::allow_raw_pointers());
186     emscripten::function("heif_js_decode_image",
187     &heif_js_decode_image, emscripten::allow_raw_pointers());
188     EXPORT_HEIF_FUNCTION(heif_image_handle_release);
189 
190     emscripten::enum_<heif_error_code>("heif_error_code")
191     .value("heif_error_Ok", heif_error_Ok)
192     .value("heif_error_Input_does_not_exist", heif_error_Input_does_not_exist)
193     .value("heif_error_Invalid_input", heif_error_Invalid_input)
194     .value("heif_error_Unsupported_filetype", heif_error_Unsupported_filetype)
195     .value("heif_error_Unsupported_feature", heif_error_Unsupported_feature)
196     .value("heif_error_Usage_error", heif_error_Usage_error)
197     .value("heif_error_Memory_allocation_error", heif_error_Memory_allocation_error)
198     .value("heif_error_Decoder_plugin_error", heif_error_Decoder_plugin_error)
199     .value("heif_error_Encoder_plugin_error", heif_error_Encoder_plugin_error)
200     .value("heif_error_Encoding_error", heif_error_Encoding_error)
201     .value("heif_error_Color_profile_does_not_exist", heif_error_Color_profile_does_not_exist);
202     emscripten::enum_<heif_suberror_code>("heif_suberror_code")
203     .value("heif_suberror_Unspecified", heif_suberror_Unspecified)
204     .value("heif_suberror_Cannot_write_output_data", heif_suberror_Cannot_write_output_data)
205     .value("heif_suberror_End_of_data", heif_suberror_End_of_data)
206     .value("heif_suberror_Invalid_box_size", heif_suberror_Invalid_box_size)
207     .value("heif_suberror_No_ftyp_box", heif_suberror_No_ftyp_box)
208     .value("heif_suberror_No_idat_box", heif_suberror_No_idat_box)
209     .value("heif_suberror_No_meta_box", heif_suberror_No_meta_box)
210     .value("heif_suberror_No_hdlr_box", heif_suberror_No_hdlr_box)
211     .value("heif_suberror_No_hvcC_box", heif_suberror_No_hvcC_box)
212     .value("heif_suberror_No_pitm_box", heif_suberror_No_pitm_box)
213     .value("heif_suberror_No_ipco_box", heif_suberror_No_ipco_box)
214     .value("heif_suberror_No_ipma_box", heif_suberror_No_ipma_box)
215     .value("heif_suberror_No_iloc_box", heif_suberror_No_iloc_box)
216     .value("heif_suberror_No_iinf_box", heif_suberror_No_iinf_box)
217     .value("heif_suberror_No_iprp_box", heif_suberror_No_iprp_box)
218     .value("heif_suberror_No_iref_box", heif_suberror_No_iref_box)
219     .value("heif_suberror_No_pict_handler", heif_suberror_No_pict_handler)
220     .value("heif_suberror_Ipma_box_references_nonexisting_property", heif_suberror_Ipma_box_references_nonexisting_property)
221     .value("heif_suberror_No_properties_assigned_to_item", heif_suberror_No_properties_assigned_to_item)
222     .value("heif_suberror_No_item_data", heif_suberror_No_item_data)
223     .value("heif_suberror_Invalid_grid_data", heif_suberror_Invalid_grid_data)
224     .value("heif_suberror_Missing_grid_images", heif_suberror_Missing_grid_images)
225     .value("heif_suberror_No_av1C_box", heif_suberror_No_av1C_box)
226     .value("heif_suberror_Invalid_clean_aperture", heif_suberror_Invalid_clean_aperture)
227     .value("heif_suberror_Invalid_overlay_data", heif_suberror_Invalid_overlay_data)
228     .value("heif_suberror_Overlay_image_outside_of_canvas", heif_suberror_Overlay_image_outside_of_canvas)
229     .value("heif_suberror_Auxiliary_image_type_unspecified", heif_suberror_Auxiliary_image_type_unspecified)
230     .value("heif_suberror_No_or_invalid_primary_item", heif_suberror_No_or_invalid_primary_item)
231     .value("heif_suberror_No_infe_box", heif_suberror_No_infe_box)
232     .value("heif_suberror_Security_limit_exceeded", heif_suberror_Security_limit_exceeded)
233     .value("heif_suberror_Unknown_color_profile_type", heif_suberror_Unknown_color_profile_type)
234     .value("heif_suberror_Wrong_tile_image_chroma_format", heif_suberror_Wrong_tile_image_chroma_format)
235     .value("heif_suberror_Invalid_fractional_number", heif_suberror_Invalid_fractional_number)
236     .value("heif_suberror_Invalid_image_size", heif_suberror_Invalid_image_size)
237     .value("heif_suberror_Nonexisting_item_referenced", heif_suberror_Nonexisting_item_referenced)
238     .value("heif_suberror_Null_pointer_argument", heif_suberror_Null_pointer_argument)
239     .value("heif_suberror_Nonexisting_image_channel_referenced", heif_suberror_Nonexisting_image_channel_referenced)
240     .value("heif_suberror_Unsupported_plugin_version", heif_suberror_Unsupported_plugin_version)
241     .value("heif_suberror_Unsupported_writer_version", heif_suberror_Unsupported_writer_version)
242     .value("heif_suberror_Unsupported_parameter", heif_suberror_Unsupported_parameter)
243     .value("heif_suberror_Invalid_parameter_value", heif_suberror_Invalid_parameter_value)
244     .value("heif_suberror_Invalid_pixi_box", heif_suberror_Invalid_pixi_box)
245     .value("heif_suberror_Unsupported_codec", heif_suberror_Unsupported_codec)
246     .value("heif_suberror_Unsupported_image_type", heif_suberror_Unsupported_image_type)
247     .value("heif_suberror_Unsupported_data_version", heif_suberror_Unsupported_data_version)
248     .value("heif_suberror_Unsupported_color_conversion", heif_suberror_Unsupported_color_conversion)
249     .value("heif_suberror_Unsupported_item_construction_method", heif_suberror_Unsupported_item_construction_method)
250     .value("heif_suberror_Unsupported_bit_depth", heif_suberror_Unsupported_bit_depth);
251     emscripten::enum_<heif_compression_format>("heif_compression_format")
252     .value("heif_compression_undefined", heif_compression_undefined)
253     .value("heif_compression_HEVC", heif_compression_HEVC)
254     .value("heif_compression_AVC", heif_compression_AVC)
255     .value("heif_compression_JPEG", heif_compression_JPEG)
256     .value("heif_compression_AV1", heif_compression_AV1);
257     emscripten::enum_<heif_chroma>("heif_chroma")
258     .value("heif_chroma_undefined", heif_chroma_undefined)
259     .value("heif_chroma_monochrome", heif_chroma_monochrome)
260     .value("heif_chroma_420", heif_chroma_420)
261     .value("heif_chroma_422", heif_chroma_422)
262     .value("heif_chroma_444", heif_chroma_444)
263     .value("heif_chroma_interleaved_RGB", heif_chroma_interleaved_RGB)
264     .value("heif_chroma_interleaved_RGBA", heif_chroma_interleaved_RGBA)
265     .value("heif_chroma_interleaved_RRGGBB_BE", heif_chroma_interleaved_RRGGBB_BE)
266     .value("heif_chroma_interleaved_RRGGBBAA_BE", heif_chroma_interleaved_RRGGBBAA_BE)
267     .value("heif_chroma_interleaved_RRGGBB_LE", heif_chroma_interleaved_RRGGBB_LE)
268     .value("heif_chroma_interleaved_RRGGBBAA_LE", heif_chroma_interleaved_RRGGBBAA_LE)
269     // Aliases
270     .value("heif_chroma_interleaved_24bit", heif_chroma_interleaved_24bit)
271     .value("heif_chroma_interleaved_32bit", heif_chroma_interleaved_32bit);
272     emscripten::enum_<heif_colorspace>("heif_colorspace")
273     .value("heif_colorspace_undefined", heif_colorspace_undefined)
274     .value("heif_colorspace_YCbCr", heif_colorspace_YCbCr)
275     .value("heif_colorspace_RGB", heif_colorspace_RGB)
276     .value("heif_colorspace_monochrome", heif_colorspace_monochrome);
277     emscripten::enum_<heif_channel>("heif_channel")
278     .value("heif_channel_Y", heif_channel_Y)
279     .value("heif_channel_Cr", heif_channel_Cr)
280     .value("heif_channel_Cb", heif_channel_Cb)
281     .value("heif_channel_R", heif_channel_R)
282     .value("heif_channel_G", heif_channel_G)
283     .value("heif_channel_B", heif_channel_B)
284     .value("heif_channel_Alpha", heif_channel_Alpha)
285     .value("heif_channel_interleaved", heif_channel_interleaved);
286 
287     emscripten::class_<heif_context>("heif_context");
288     emscripten::class_<heif_image_handle>("heif_image_handle");
289     emscripten::class_<heif_image>("heif_image");
290     emscripten::value_object<heif_error>("heif_error")
291     .field("code", &heif_error::code)
292     .field("subcode", &heif_error::subcode);
293 }
294 
295 #endif  // LIBHEIF_BOX_EMSCRIPTEN_H
296