|
20 | 20 |
|
21 | 21 | #include "image_helpers.h" |
22 | 22 |
|
| 23 | +#include <stdio.h> |
| 24 | +#include <stdlib.h> |
| 25 | + |
| 26 | +#include <defaultatoms.h> |
| 27 | +#include <globalcontext.h> |
| 28 | +#include <memory.h> |
| 29 | +#include <term.h> |
| 30 | +#include <utils.h> |
| 31 | + |
23 | 32 | #include "spng.h" |
24 | 33 |
|
| 34 | +static void send_decoded_image(term ref, term pid, const void *data, size_t size, Context *ctx) |
| 35 | +{ |
| 36 | + // TODO: change return format to {ok, binary} |
| 37 | + BEGIN_WITH_STACK_HEAP(TUPLE_SIZE(2) + REF_SIZE + term_binary_heap_size(size), heap); |
| 38 | + |
| 39 | + term return_tuple = term_alloc_tuple(2, &heap); |
| 40 | + term_put_tuple_element(return_tuple, 0, ref); |
| 41 | + term_put_tuple_element(return_tuple, 1, term_from_literal_binary(data, size, &heap, ctx->global)); |
| 42 | + |
| 43 | + int local_process_id = term_to_local_process_id(pid); |
| 44 | + globalcontext_send_message(ctx->global, local_process_id, return_tuple); |
| 45 | + |
| 46 | + END_WITH_STACK_HEAP(heap, ctx->global) |
| 47 | +} |
| 48 | + |
| 49 | +static void send_load_image_error(term ref, term pid, term reason, Context *ctx) |
| 50 | +{ |
| 51 | + BEGIN_WITH_STACK_HEAP(TUPLE_SIZE(2) + TUPLE_SIZE(2) + REF_SIZE, heap); |
| 52 | + |
| 53 | + term error_tuple = term_alloc_tuple(2, &heap); |
| 54 | + term_put_tuple_element(error_tuple, 0, ERROR_ATOM); |
| 55 | + term_put_tuple_element(error_tuple, 1, reason); |
| 56 | + |
| 57 | + term return_tuple = term_alloc_tuple(2, &heap); |
| 58 | + term_put_tuple_element(return_tuple, 0, ref); |
| 59 | + term_put_tuple_element(return_tuple, 1, error_tuple); |
| 60 | + |
| 61 | + int local_process_id = term_to_local_process_id(pid); |
| 62 | + globalcontext_send_message(ctx->global, local_process_id, return_tuple); |
| 63 | + |
| 64 | + END_WITH_STACK_HEAP(heap, ctx->global) |
| 65 | +} |
| 66 | + |
| 67 | +static term invalid_image_reason(Context *ctx) |
| 68 | +{ |
| 69 | + term reason = globalcontext_make_atom(ctx->global, "\xD" |
| 70 | + "invalid_image"); |
| 71 | + if (UNLIKELY(term_is_invalid_term(reason))) { |
| 72 | + return ERROR_ATOM; |
| 73 | + } |
| 74 | + return reason; |
| 75 | +} |
| 76 | + |
25 | 77 | void handle_load_image(term req, term ref, term pid, Context *ctx) |
26 | 78 | { |
| 79 | + if (UNLIKELY(term_get_tuple_arity(req) < 2)) { |
| 80 | + fprintf(stderr, "handle_load_image: missing image argument\n"); |
| 81 | + send_load_image_error(ref, pid, BADARG_ATOM, ctx); |
| 82 | + return; |
| 83 | + } |
| 84 | + |
27 | 85 | term image_bin = term_get_tuple_element(req, 1); |
| 86 | + if (UNLIKELY(!term_is_binary(image_bin))) { |
| 87 | + fprintf(stderr, "handle_load_image: image argument is not a binary\n"); |
| 88 | + send_load_image_error(ref, pid, BADARG_ATOM, ctx); |
| 89 | + return; |
| 90 | + } |
| 91 | + |
28 | 92 | const void *buf = term_binary_data(image_bin); |
29 | 93 | size_t buf_size = term_binary_size(image_bin); |
30 | 94 |
|
31 | 95 | spng_ctx *png_ctx = spng_ctx_new(0); |
32 | | - spng_set_png_buffer(png_ctx, buf, buf_size); |
| 96 | + if (UNLIKELY(!png_ctx)) { |
| 97 | + fprintf(stderr, "handle_load_image: spng_ctx_new failed\n"); |
| 98 | + send_load_image_error(ref, pid, OUT_OF_MEMORY_ATOM, ctx); |
| 99 | + return; |
| 100 | + } |
| 101 | + |
| 102 | + int ret = spng_set_png_buffer(png_ctx, buf, buf_size); |
| 103 | + if (UNLIKELY(ret != SPNG_OK)) { |
| 104 | + fprintf(stderr, "handle_load_image: spng_set_png_buffer failed: %s\n", spng_strerror(ret)); |
| 105 | + spng_ctx_free(png_ctx); |
| 106 | + send_load_image_error(ref, pid, invalid_image_reason(ctx), ctx); |
| 107 | + return; |
| 108 | + } |
33 | 109 |
|
34 | 110 | size_t out_size; |
35 | | - spng_decoded_image_size(png_ctx, SPNG_FMT_RGBA8, &out_size); |
| 111 | + ret = spng_decoded_image_size(png_ctx, SPNG_FMT_RGBA8, &out_size); |
| 112 | + if (UNLIKELY(ret != SPNG_OK)) { |
| 113 | + fprintf(stderr, "handle_load_image: spng_decoded_image_size failed: %s\n", spng_strerror(ret)); |
| 114 | + spng_ctx_free(png_ctx); |
| 115 | + send_load_image_error(ref, pid, invalid_image_reason(ctx), ctx); |
| 116 | + return; |
| 117 | + } |
36 | 118 |
|
37 | 119 | void *out = malloc(out_size); |
38 | | - spng_decode_image(png_ctx, out, out_size, SPNG_FMT_RGBA8, 0); |
| 120 | + if (UNLIKELY(!out)) { |
| 121 | + fprintf(stderr, "handle_load_image: cannot allocate %zu bytes for decoded image\n", out_size); |
| 122 | + spng_ctx_free(png_ctx); |
| 123 | + send_load_image_error(ref, pid, OUT_OF_MEMORY_ATOM, ctx); |
| 124 | + return; |
| 125 | + } |
39 | 126 |
|
| 127 | + ret = spng_decode_image(png_ctx, out, out_size, SPNG_FMT_RGBA8, 0); |
40 | 128 | spng_ctx_free(png_ctx); |
| 129 | + if (UNLIKELY(ret != SPNG_OK)) { |
| 130 | + fprintf(stderr, "handle_load_image: spng_decode_image failed: %s\n", spng_strerror(ret)); |
| 131 | + free(out); |
| 132 | + send_load_image_error(ref, pid, invalid_image_reason(ctx), ctx); |
| 133 | + return; |
| 134 | + } |
41 | 135 |
|
42 | | - // term_binary_heap_size(out_size) is usually less than 100 bytes |
43 | | - BEGIN_WITH_STACK_HEAP(TUPLE_SIZE(3) + term_binary_heap_size(out_size), heap); |
44 | | - |
45 | | - term return_tuple = term_alloc_tuple(2, &heap); |
46 | | - term_put_tuple_element(return_tuple, 0, ref); |
47 | | - term_put_tuple_element(return_tuple, 1, term_from_literal_binary(out, out_size, &heap, ctx->global)); |
| 136 | + send_decoded_image(ref, pid, out, out_size, ctx); |
48 | 137 |
|
49 | 138 | free(out); |
50 | | - |
51 | | - int local_process_id = term_to_local_process_id(pid); |
52 | | - globalcontext_send_message(ctx->global, local_process_id, return_tuple); |
53 | | - |
54 | | - END_WITH_STACK_HEAP(heap, ctx->global) |
55 | 139 | } |
0 commit comments