NetSurf
jpegxl.c
Go to the documentation of this file.
1/*
2 * Copyright 2023 Vincent Sanders <vince@netsurf-browser.org>
3 *
4 * This file is part of NetSurf, http://www.netsurf-browser.org/
5 *
6 * NetSurf is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * NetSurf is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19/**
20 * \file
21 * implementation of content handling for image/jpegxl
22 *
23 * This implementation uses the JXL library.
24 */
25
26#include <stdbool.h>
27#include <stdlib.h>
28#include <setjmp.h>
29#include <string.h>
30
31#include <jxl/decode.h>
32
33#include "utils/utils.h"
34#include "utils/log.h"
35#include "utils/messages.h"
36#include "netsurf/bitmap.h"
37#include "content/llcache.h"
38#include "content/content.h"
42#include "desktop/bitmap.h"
43
44#include "image/image_cache.h"
45
46#include "image/jpegxl.h"
47
48
49/**
50 * output image format
51 */
52static const JxlPixelFormat jxl_output_format = {
53 .num_channels = 4,
54 .data_type = JXL_TYPE_UINT8,
55 .endianness = JXL_LITTLE_ENDIAN,
56 .align = 0,
57};
58
59/**
60 * Content create entry point.
61 */
62static nserror
64 lwc_string *imime_type, const struct http_parameter *params,
65 llcache_handle *llcache, const char *fallback_charset,
66 bool quirks, struct content **c)
67{
68 struct content *jpeg;
69 nserror error;
70
71 jpeg = calloc(1, sizeof(struct content));
72 if (jpeg == NULL)
73 return NSERROR_NOMEM;
74
75 error = content__init(jpeg, handler, imime_type, params,
77 if (error != NSERROR_OK) {
78 free(jpeg);
79 return error;
80 }
81
82 *c = jpeg;
83
84 return NSERROR_OK;
85}
86
87/**
88 * create a bitmap from jpeg xl content.
89 */
90static struct bitmap *
92{
93 struct bitmap * bitmap = NULL;
94 JxlDecoder *jxldec;
95 JxlDecoderStatus decstatus;
96 JxlBasicInfo binfo;
97 const uint8_t *src_data;
98 size_t src_size;
99 uint8_t * output;
100 bitmap_fmt_t jxl_fmt = {
101 /** TODO: At the moment we have to set the layout to the only
102 * pixel layout that libjxl supports. It looks like they
103 * plan to add support for decoding to other layouts
104 * in the future, as shown by the TODO in the docs:
105 *
106 * https://libjxl.readthedocs.io/en/latest/api_common.html#_CPPv414JxlPixelFormat
107 */
109 .pma = bitmap_fmt.pma,
110 };
111
112 jxldec = JxlDecoderCreate(NULL);
113 if (jxldec == NULL) {
114 NSLOG(netsurf, ERROR, "Unable to allocate decoder");
115 return NULL;
116 }
117
118 decstatus = JxlDecoderSetUnpremultiplyAlpha(jxldec, !bitmap_fmt.pma);
119 if (decstatus != JXL_DEC_SUCCESS) {
120 NSLOG(netsurf, ERROR, "unable to set premultiplied alpha status: %d",
121 decstatus);
122 JxlDecoderDestroy(jxldec);
123 return NULL;
124 }
125
126 decstatus= JxlDecoderSubscribeEvents(jxldec, JXL_DEC_FULL_IMAGE);
127 if (decstatus != JXL_DEC_SUCCESS) {
128 NSLOG(netsurf, ERROR, "Unable to subscribe");
129 return NULL;
130 }
131 src_data = content__get_source_data(c, &src_size);
132
133 decstatus = JxlDecoderSetInput(jxldec, src_data, src_size);
134 if (decstatus != JXL_DEC_SUCCESS) {
135 NSLOG(netsurf, ERROR, "unable to set input");
136 return NULL;
137 }
138
139 decstatus = JxlDecoderProcessInput(jxldec);
140 if (decstatus != JXL_DEC_NEED_IMAGE_OUT_BUFFER) {
141 NSLOG(netsurf, ERROR,
142 "expected status JXL_DEC_NEED_IMAGE_OUT_BUFFER(%d) got %d",
143 JXL_DEC_NEED_IMAGE_OUT_BUFFER,
144 decstatus);
145 JxlDecoderDestroy(jxldec);
146 return NULL;
147 }
148
149 decstatus = JxlDecoderGetBasicInfo(jxldec, &binfo);
150 if (decstatus != JXL_DEC_SUCCESS) {
151 NSLOG(netsurf, ERROR, "unable to get basic info status:%d",decstatus);
152 JxlDecoderDestroy(jxldec);
153 return NULL;
154 }
155
156 /* create bitmap with appropriate opacity */
157 if (binfo.alpha_bits > 0) {
159 } else {
161 }
162 if (bitmap == NULL) {
163 /* empty bitmap could not be created */
164 JxlDecoderDestroy(jxldec);
165 return NULL;
166 }
167
168 /* ensure buffer was allocated */
169 output = guit->bitmap->get_buffer(bitmap);
170 if (output == NULL) {
171 /* bitmap with no buffer available */
173 JxlDecoderDestroy(jxldec);
174 return NULL;
175 }
176 decstatus = JxlDecoderSetImageOutBuffer(jxldec, &jxl_output_format, output, c->size);
177 if (decstatus != JXL_DEC_SUCCESS) {
178 NSLOG(netsurf, ERROR, "unable to set output buffer callback status:%d",decstatus);
180 JxlDecoderDestroy(jxldec);
181 return NULL;
182 }
183
184 decstatus = JxlDecoderProcessInput(jxldec);
185 if (decstatus != JXL_DEC_FULL_IMAGE) {
186 NSLOG(netsurf, ERROR, "did not get decode event");
188 JxlDecoderDestroy(jxldec);
189 return NULL;
190 }
191
192 JxlDecoderDestroy(jxldec);
193
196
197 return bitmap;
198}
199
200/**
201 * report failiure
202 */
203static bool jxl_report_fail(struct content *c, JxlDecoderStatus decstatus, const char *msg)
204{
205 union content_msg_data msg_data;
206 NSLOG(netsurf, ERROR, "%s decoder status:%d", msg, decstatus);
208 msg_data.errordata.errormsg = msg;
210 return false;
211}
212
213/**
214 * Convert a CONTENT_JPEGXL for display.
215 */
216static bool nsjpegxl_convert(struct content *c)
217{
218 JxlDecoder *jxldec;
219 JxlSignature decsig;
220 JxlDecoderStatus decstatus = JXL_DEC_ERROR;
221 JxlBasicInfo binfo;
222 union content_msg_data msg_data;
223 const uint8_t *data;
224 size_t size;
225 char *title;
226 size_t image_size;
227
228 /* check image header is valid and get width/height */
229 data = content__get_source_data(c, &size);
230
231 decsig = JxlSignatureCheck(data,size);
232 if ((decsig != JXL_SIG_CODESTREAM) && (decsig != JXL_SIG_CONTAINER)) {
233 NSLOG(netsurf, ERROR, "signature failed");
235 msg_data.errordata.errormsg = "Signature failed";
237 return false;
238 }
239
240 jxldec = JxlDecoderCreate(NULL);
241 if (jxldec == NULL) {
242 return jxl_report_fail(c, decstatus, "Unable to allocate decoder");
243 }
244 decstatus= JxlDecoderSubscribeEvents(jxldec, JXL_DEC_BASIC_INFO);
245 if (decstatus != JXL_DEC_SUCCESS) {
246 return jxl_report_fail(c, decstatus, "Unable to subscribe");
247 }
248 decstatus = JxlDecoderSetInput(jxldec, data,size);
249 if (decstatus != JXL_DEC_SUCCESS) {
250 return jxl_report_fail(c, decstatus, "unable to set input");
251 }
252 decstatus = JxlDecoderProcessInput(jxldec);
253 if (decstatus != JXL_DEC_BASIC_INFO) {
254 return jxl_report_fail(c, decstatus, "did not get basic info event");
255 }
256 decstatus = JxlDecoderGetBasicInfo(jxldec, &binfo);
257 if (decstatus != JXL_DEC_SUCCESS) {
258 return jxl_report_fail(c, decstatus, "unable to get basic info");
259 }
260 decstatus = JxlDecoderImageOutBufferSize(jxldec, &jxl_output_format, &image_size);
261 if (decstatus != JXL_DEC_SUCCESS) {
262 return jxl_report_fail(c, decstatus, "unable get image size");
263 }
264
265 JxlDecoderDestroy(jxldec);
266
267 NSLOG(netsurf, INFO, "got basic info size:%ld x:%d y:%d", image_size, binfo.xsize, binfo.ysize);
268
269 c->width = binfo.xsize;
270 c->height = binfo.ysize;
271 c->size = image_size;
272
274
275 /* set title text */
276 title = messages_get_buff("JPEGXLTitle",
278 c->width, c->height);
279 if (title != NULL) {
281 free(title);
282 }
283
286 content_set_status(c, ""); /* Done: update status bar */
287
288 return true;
289}
290
291
292/**
293 * Clone content.
294 */
295static nserror nsjpegxl_clone(const struct content *old, struct content **newc)
296{
297 struct content *jpegxl_c;
298 nserror error;
299
300 jpegxl_c = calloc(1, sizeof(struct content));
301 if (jpegxl_c == NULL)
302 return NSERROR_NOMEM;
303
304 error = content__clone(old, jpegxl_c);
305 if (error != NSERROR_OK) {
306 content_destroy(jpegxl_c);
307 return error;
308 }
309
310 /* re-convert if the content is ready */
311 if ((old->status == CONTENT_STATUS_READY) ||
312 (old->status == CONTENT_STATUS_DONE)) {
313 if (nsjpegxl_convert(jpegxl_c) == false) {
314 content_destroy(jpegxl_c);
316 }
317 }
318
319 *newc = jpegxl_c;
320
321 return NSERROR_OK;
322}
323
326 .data_complete = nsjpegxl_convert,
327 .destroy = image_cache_destroy,
328 .redraw = image_cache_redraw,
329 .clone = nsjpegxl_clone,
330 .get_internal = image_cache_get_internal,
332 .is_opaque = image_cache_is_opaque,
333 .no_share = false,
334};
335
336static const char *nsjpegxl_types[] = {
337 "image/jxl",
338};
339
Content handling interface.
void content_destroy(struct content *c)
Destroy and free a content.
Definition: content.c:354
void content_broadcast(struct content *c, content_msg msg, const union content_msg_data *data)
Send a message to all users.
Definition: content.c:752
void content_set_done(struct content *c)
Put a content in status CONTENT_STATUS_DONE.
Definition: content.c:299
bool content__set_title(struct content *c, const char *title)
Set title associated with content.
Definition: content.c:1090
nserror content__init(struct content *c, const content_handler *handler, lwc_string *imime_type, const struct http_parameter *params, llcache_handle *llcache, const char *fallback_charset, bool quirks)
Definition: content.c:190
const uint8_t * content__get_source_data(struct content *c, size_t *size)
Retrieve source of content.
Definition: content.c:1216
nserror content__clone(const struct content *c, struct content *nc)
Clone a content's data members.
Definition: content.c:1382
void content_set_ready(struct content *c)
Put a content in status CONTENT_STATUS_READY and unlock the content.
Definition: content.c:285
void content_set_status(struct content *c, const char *status_message)
Updates content with new status.
Definition: content.c:270
Protected interface to Content handling.
@ CONTENT_STATUS_READY
Some parts of content still being loaded, but can be displayed.
Definition: content_type.h:92
@ CONTENT_STATUS_DONE
Content has completed all processing.
Definition: content_type.h:95
@ CONTENT_MSG_ERROR
error occurred
Definition: content_type.h:122
Internal core bitmap interface.
static void bitmap_format_to_client(void *bitmap, const bitmap_fmt_t *current_fmt)
Convert a bitmap to the client bitmap format.
Definition: bitmap.h:117
nserror
Enumeration of error codes.
Definition: errors.h:29
@ NSERROR_UNKNOWN
Unknown error - DO NOT USE.
Definition: errors.h:31
@ NSERROR_NOMEM
Memory exhaustion.
Definition: errors.h:32
@ NSERROR_CLONE_FAILED
Failed to clone handle.
Definition: errors.h:37
@ NSERROR_OK
No error.
Definition: errors.h:30
struct netsurf_table * guit
The global interface table.
Definition: gui_factory.c:50
Interface to core interface table.
content_type image_cache_content_type(void)
Definition: image_cache.c:873
void * image_cache_get_internal(const struct content *c, void *context)
Definition: image_cache.c:856
bool image_cache_redraw(struct content *c, struct content_redraw_data *data, const struct rect *clip, const struct redraw_context *ctx)
Generic content redraw callback.
Definition: image_cache.c:798
void image_cache_destroy(struct content *content)
Definition: image_cache.c:841
nserror image_cache_add(struct content *content, struct bitmap *bitmap, image_cache_convert_fn *convert)
adds an image content to be cached.
Definition: image_cache.c:510
bool image_cache_is_opaque(struct content *c)
Definition: image_cache.c:862
The image content handler intermediate image cache.
Generic bitmap handling interface.
@ BITMAP_OPAQUE
image is opaque
Definition: bitmap.h:38
@ BITMAP_NONE
Definition: bitmap.h:37
@ BITMAP_LAYOUT_R8G8B8A8
Bite-wise RGBA: Byte order: 0xRR, 0xGG, 0xBB, 0xAA.
Definition: bitmap.h:50
static const JxlPixelFormat jxl_output_format
output image format
Definition: jpegxl.c:52
CONTENT_FACTORY_REGISTER_TYPES(nsjpegxl, nsjpegxl_types, nsjpegxl_content_handler)
static bool jxl_report_fail(struct content *c, JxlDecoderStatus decstatus, const char *msg)
report failiure
Definition: jpegxl.c:203
static bool nsjpegxl_convert(struct content *c)
Convert a CONTENT_JPEGXL for display.
Definition: jpegxl.c:216
static nserror nsjpegxl_clone(const struct content *old, struct content **newc)
Clone content.
Definition: jpegxl.c:295
static const char * nsjpegxl_types[]
Definition: jpegxl.c:336
static nserror nsjpegxl_create(const content_handler *handler, lwc_string *imime_type, const struct http_parameter *params, llcache_handle *llcache, const char *fallback_charset, bool quirks, struct content **c)
Content create entry point.
Definition: jpegxl.c:63
static struct bitmap * jpegxl_cache_convert(struct content *c)
create a bitmap from jpeg xl content.
Definition: jpegxl.c:91
static const content_handler nsjpegxl_content_handler
Definition: jpegxl.c:324
Content for image/jpegxl (interface).
static struct llcache_s * llcache
low level cache state
Definition: llcache.c:267
nsurl * llcache_handle_get_url(const llcache_handle *handle)
Retrieve the post-redirect URL of a low-level cache object.
Definition: llcache.c:4195
Low-level resource cache (interface)
#define NSLOG(catname, level, logmsg, args...)
Definition: log.h:116
char * messages_get_buff(const char *key,...)
Formatted message from a key in the global message hash.
Definition: messages.c:205
Localised message support (interface).
const char * nsurl_access_leaf(const nsurl *url)
Access a URL's path leaf as a string.
Interface to utility string handling.
Bitmap format specifier.
Definition: bitmap.h:95
bool pma
Premultiplied alpha.
Definition: bitmap.h:97
enum bitmap_layout layout
Colour component layout.
Definition: bitmap.h:96
RISC OS wimp toolkit bitmap.
Definition: bitmap.c:68
Content operation function table.
nserror(* create)(const struct content_handler *handler, lwc_string *imime_type, const struct http_parameter *params, struct llcache_handle *llcache, const char *fallback_charset, bool quirks, struct content **c)
Content which corresponds to a single URL.
bool quirks
Content is in quirks mode.
int height
Height dimension, if applicable.
char * fallback_charset
Fallback charset, or NULL.
int width
Width dimension, if applicable.
const struct content_handler * handler
Handler for content.
struct llcache_handle * llcache
Low-level cache object.
content_status status
Current status.
unsigned int size
Estimated size of all data associated with this content.
void(* destroy)(void *bitmap)
Destroy a bitmap.
Definition: bitmap.h:143
void *(* create)(int width, int height, enum gui_bitmap_flags flags)
Create a new bitmap.
Definition: bitmap.h:136
void(* modified)(void *bitmap)
Marks a bitmap as modified.
Definition: bitmap.h:200
unsigned char *(* get_buffer)(void *bitmap)
Get the image buffer from a bitmap.
Definition: bitmap.h:169
Representation of an HTTP parameter.
Definition: parameter.c:31
Handle to low-level cache object.
Definition: llcache.c:76
struct gui_bitmap_table * bitmap
Bitmap table.
Definition: gui_table.h:153
Extra data for some content_msg messages.
Definition: content.h:60
struct content_msg_data::@99 errordata
CONTENT_MSG_ERROR - Error from content or underlying fetch.
nserror errorcode
The error code to convey meaning.
Definition: content.h:88
const char * msg
The message to log.
Definition: content.h:68
const char * title
Definition: content.h:186
const char * errormsg
The message.
Definition: content.h:95
Interface to a number of general purpose functionality.
static nserror bitmap(const struct redraw_context *ctx, struct bitmap *bitmap, int x, int y, int width, int height, colour bg, bitmap_flags_t flags)
Plot a bitmap.
Definition: plot.c:857