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