native.c - vx32 - Local 9vx git repository for patches.
(HTM) git clone git://r-36.net/vx32
(DIR) Log
(DIR) Files
(DIR) Refs
---
native.c (4835B)
---
1
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <stdarg.h>
5 #include <unistd.h>
6 #include <setjmp.h>
7 #include <assert.h>
8 #include <errno.h>
9
10 #include <vxa/vxa.h>
11 #include <vxa/codec.h>
12
13 #include "cdjpeg.h"
14 #include "native.h"
15
16
17 char vxacodec_name[] = "jpeg";
18
19
20 // Defined in djpeg-bin.c, generated from djpeg by bin2c.pl
21 extern const uint8_t vxa_djpeg_data[];
22 extern const int vxa_djpeg_length;
23
24
25 int vxacodec_init(vxacodec *c, vxaio *io)
26 {
27 c->decoder = vxa_djpeg_data;
28 c->decodersize = vxa_djpeg_length;
29
30 return VXA_RC_OK;
31 }
32
33 int vxacodec_encode(struct vxacodec *c, struct vxaio *io)
34 {
35 return vxa_error(io, VXA_RC_WRONG_FORMAT,
36 "jpeg codec currently doesn't support compression");
37 }
38
39 static void error_exit(j_common_ptr cinfo)
40 {
41 struct client_data *cdata = cinfo->client_data;
42
43 // Jump back to the original entrypoint with an error return
44 longjmp(cdata->errjmp, 1);
45 }
46
47 static void output_message(j_common_ptr cinfo)
48 {
49 struct client_data *cdata = cinfo->client_data;
50
51 /* Create the message */
52 char buffer[JMSG_LENGTH_MAX];
53 cinfo->err->format_message(cinfo, buffer);
54
55 /* Stuff it into the vxaio struct */
56 vxa_error(cdata->io, VXA_RC_CORRUPT_DATA, "%s", buffer);
57 }
58
59 static void init_source(j_decompress_ptr cinfo)
60 {
61 // Nothing to do
62 }
63
64 static boolean fill_input_buffer(j_decompress_ptr cinfo)
65 {
66 struct client_data *cdata = cinfo->client_data;
67
68 ssize_t inlen = cdata->io->readf(cdata->io, cdata->inbuf, BUFSIZE);
69 if (inlen == 0)
70 vxa_error(cdata->io, VXA_RC_CORRUPT_DATA,
71 "compressed JPEG stream is truncated");
72 if (inlen <= 0)
73 longjmp(cdata->errjmp, 1);
74
75 cinfo->src->next_input_byte = cdata->inbuf;
76 cinfo->src->bytes_in_buffer = inlen;
77
78 return TRUE;
79 }
80
81 static void skip_input_data(j_decompress_ptr cinfo, long num_bytes)
82 {
83 struct client_data *cdata = cinfo->client_data;
84
85 while (num_bytes > cinfo->src->bytes_in_buffer) {
86 num_bytes -= cinfo->src->bytes_in_buffer;
87 fill_input_buffer(cinfo);
88 }
89 cinfo->src->next_input_byte += num_bytes;
90 cinfo->src->bytes_in_buffer -= num_bytes;
91 }
92
93 static void term_source(j_decompress_ptr cinfo)
94 {
95 // Nothing to do
96 }
97
98 void vxajpeg_flush(struct client_data *cd)
99 {
100 if (cd->outpos == 0)
101 return;
102
103 ssize_t rc = cd->io->writef(cd->io, cd->outbuf, cd->outpos);
104 if (rc < 0)
105 longjmp(cd->errjmp, 1);
106 cd->outpos = 0;
107 }
108
109 int vxacodec_decode(struct vxacodec *c, struct vxaio *io)
110 {
111 // Save the vital args in a client_data struct for our callbacks
112 struct client_data cdata;
113 cdata.io = io;
114 cdata.inbuf = NULL;
115
116 // Set up the JPEG library error handler struct
117 struct jpeg_error_mgr jerr;
118 memset(&jerr, 0, sizeof(jerr));
119 jpeg_std_error(&jerr);
120 jerr.error_exit = error_exit;
121 jerr.output_message = output_message;
122
123 // Setup the JPEG input source
124 struct jpeg_source_mgr jsrc;
125 memset(&jsrc, 0, sizeof(jsrc));
126 jsrc.init_source = init_source;
127 jsrc.fill_input_buffer = fill_input_buffer;
128 jsrc.skip_input_data = skip_input_data;
129 jsrc.resync_to_restart = jpeg_resync_to_restart;
130 jsrc.term_source = term_source;
131
132 // Set up the main decompression state struct
133 struct jpeg_decompress_struct cinfo;
134 memset(&cinfo, 0, sizeof(cinfo));
135 cinfo.client_data = &cdata;
136 cinfo.err = &jerr;
137 cinfo.src = &jsrc;
138
139 // Set up a JMP_BUF for error_exit() to longjmp to on fatal errors. */
140 if (setjmp(cdata.errjmp)) {
141 jpeg_destroy_decompress(&cinfo);
142 if (cdata.inbuf != NULL)
143 free(cdata.inbuf);
144 return io->errcode;
145 }
146
147 // Allocate the input and output buffers
148 cdata.inbuf = malloc(BUFSIZE*2);
149 if (cdata.inbuf == NULL)
150 return vxa_error(io, VXA_RC_NO_MEMORY,
151 "no memory for JPEG decoder input buffer");
152 cdata.outbuf = cdata.inbuf + BUFSIZE;
153 cdata.outpos = 0;
154
155 // Initialize the JPEG decoder
156 jpeg_create_decompress(&cinfo);
157 (void) jpeg_read_header(&cinfo, TRUE);
158
159 djpeg_dest_ptr dest_mgr = jinit_write_bmp(&cinfo, FALSE);
160 dest_mgr->output_file = (FILE*)&cdata; // XX hack hack hack
161
162 (void) jpeg_start_decompress(&cinfo);
163 dest_mgr->start_output(&cinfo, dest_mgr);
164
165 /* Process data */
166 while (cinfo.output_scanline < cinfo.output_height) {
167 JDIMENSION num_scanlines = jpeg_read_scanlines(
168 &cinfo, dest_mgr->buffer,
169 dest_mgr->buffer_height);
170 dest_mgr->put_pixel_rows(&cinfo, dest_mgr, num_scanlines);
171 }
172
173 dest_mgr->finish_output(&cinfo, dest_mgr);
174
175 vxajpeg_flush(&cdata);
176
177 (void) jpeg_finish_decompress(&cinfo);
178 jpeg_destroy_decompress(&cinfo);
179
180 free(cdata.inbuf);
181
182 return VXA_RC_OK;
183 }
184
185 int vxacodec_recognize(struct vxacodec *c, struct vxaio *io,
186 const void *header, size_t size)
187 {
188 const uint8_t *inp = header;
189 if (size >= 4 &&
190 inp[0] == 0xff &&
191 inp[1] == 0xd8 && // SOI
192 inp[2] == 0xff &&
193 inp[3] >= 0xe0) { // APPx
194 io->method = VXA_M_JPEG;
195 return vxa_error(io, VXA_RC_COMPRESSED,
196 "input already compressed in JPEG format");
197 }
198
199 return vxa_error(io, VXA_RC_WRONG_FORMAT,
200 "JPEG codec currently can't compress");
201 }
202