/* JPEG and PNG content handler using MontaukOS's stb_image decoder. */ #include #include #include #include #include #include #include #include "utils/errors.h" #include "utils/nsurl.h" #include "utils/utils.h" #include "netsurf/bitmap.h" #include "content/llcache.h" #include "content/content.h" #include "content/content_protected.h" #include "content/content_factory.h" #include "desktop/gui_internal.h" #include "desktop/bitmap.h" #include "image/image_cache.h" #include "image/jpeg.h" static nserror nsjpeg_create(const content_handler *handler, lwc_string *mime, const struct http_parameter *params, llcache_handle *llcache, const char *fallback_charset, bool quirks, struct content **result) { struct content *image = calloc(1, sizeof(*image)); if (image == NULL) return NSERROR_NOMEM; nserror err = content__init(image, handler, mime, params, llcache, fallback_charset, quirks); if (err != NSERROR_OK) { free(image); return err; } *result = image; return NSERROR_OK; } static struct bitmap *montauk_image_decode(struct content *content) { size_t size; const uint8_t *source = content__get_source_data(content, &size); if (source == NULL || size == 0 || size > INT_MAX) return NULL; int width, height, channels; if (!stbi_info_from_memory(source, (int)size, &width, &height, &channels) || width <= 0 || height <= 0 || width > 8192 || height > 8192 || (size_t)width * height > 32u * 1024u * 1024u) return NULL; unsigned char *rgba = stbi_load_from_memory(source, (int)size, &width, &height, &channels, 4); if (rgba == NULL || width <= 0 || height <= 0) return NULL; struct bitmap *bitmap = guit->bitmap->create(width, height, BITMAP_NONE); if (bitmap == NULL) { stbi_image_free(rgba); return NULL; } uint8_t *dest = guit->bitmap->get_buffer(bitmap); size_t stride = guit->bitmap->get_rowstride(bitmap); if (dest == NULL) { guit->bitmap->destroy(bitmap); stbi_image_free(rgba); return NULL; } bool opaque = true; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { const uint8_t *src = rgba + ((size_t)y * width + x) * 4; uint8_t *out = dest + (size_t)y * stride + (size_t)x * 4; out[bitmap_layout.r] = src[0]; out[bitmap_layout.g] = src[1]; out[bitmap_layout.b] = src[2]; out[bitmap_layout.a] = src[3]; if (src[3] != 255) opaque = false; } } stbi_image_free(rgba); guit->bitmap->set_opaque(bitmap, opaque); guit->bitmap->modified(bitmap); return bitmap; } static bool nsjpeg_convert(struct content *content) { size_t size; const uint8_t *source = content__get_source_data(content, &size); int width, height, channels; if (source == NULL || size == 0 || size > INT_MAX || !stbi_info_from_memory(source, (int)size, &width, &height, &channels) || width <= 0 || height <= 0 || width > 8192 || height > 8192 || (size_t)width * height > 32u * 1024u * 1024u) { union content_msg_data message; message.errordata.errorcode = NSERROR_INVALID; message.errordata.errormsg = "Invalid JPEG or PNG image"; content_broadcast(content, CONTENT_MSG_ERROR, &message); return false; } content->width = width; content->height = height; content->size = (size_t)width * height * 4; image_cache_add(content, NULL, montauk_image_decode); char title[96]; snprintf(title, sizeof(title), "Image (%d x %d)", width, height); content__set_title(content, strdup(title)); content_set_ready(content); content_set_done(content); content_set_status(content, ""); return true; } static nserror nsjpeg_clone(const struct content *old, struct content **result) { struct content *copy = calloc(1, sizeof(*copy)); if (copy == NULL) return NSERROR_NOMEM; nserror err = content__clone(old, copy); if (err != NSERROR_OK) { content_destroy(copy); return err; } if ((old->status == CONTENT_STATUS_READY || old->status == CONTENT_STATUS_DONE) && !nsjpeg_convert(copy)) { content_destroy(copy); return NSERROR_CLONE_FAILED; } *result = copy; return NSERROR_OK; } static const content_handler montauk_image_handler = { .create = nsjpeg_create, .data_complete = nsjpeg_convert, .destroy = image_cache_destroy, .redraw = image_cache_redraw, .clone = nsjpeg_clone, .get_internal = image_cache_get_internal, .type = image_cache_content_type, .is_opaque = image_cache_is_opaque, .no_share = false, }; static const char *montauk_image_types[] = { "image/jpeg", "image/jpg", "image/pjpeg", "image/png" }; CONTENT_FACTORY_REGISTER_TYPES(nsjpeg, montauk_image_types, montauk_image_handler);