/* jl2005bcd.c * * Converts raw output from Jeilin JL2005B/C/D to Bayer output. * * The code is based upon what is found in libgphoto2/camlibs/jl2005c, * Copyright (c) 2010 Theodore Kilgore * * The decompression code used is * Copyright (c) 2010 Hans de Goede * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef ANDROID #include #else #include #endif #include #include #ifdef HAVE_JPEG #include "libv4lconvert-priv.h" #include "jpeg_memsrcdest.h" #include "libv4lsyscall-priv.h" #define JPEG_HEADER_SIZE 338 #define JPEG_HEIGHT_OFFSET 94 static int find_eoi(struct v4lconvert_data *data, const unsigned char *jpeg_data, int jpeg_data_idx, int jpeg_data_size) { int i; for (i = jpeg_data_idx; i < (jpeg_data_size - 1); i++) if (jpeg_data[i] == 0xff && jpeg_data[i + 1] == 0xd9) break; if (i >= (jpeg_data_size - 1)) { V4LCONVERT_ERR("incomplete jl2005bcd frame\n"); return -1; } return i + 2; /* + 2 -> Point to after EOI marker */ } int v4lconvert_decode_jl2005bcd(struct v4lconvert_data *data, const unsigned char *src, int src_size, unsigned char *dest, int width, int height) { unsigned char jpeg_stripe[50000]; int q; struct jpeg_compress_struct cinfo; struct jpeg_decompress_struct dinfo; struct jpeg_error_mgr jcerr, jderr; JOCTET *jpeg_header = NULL; unsigned long jpeg_header_size = 0; int i, x, y, x1, y1, jpeg_data_size, jpeg_data_idx, eoi, size; /* src_size had better be bigger than 16 */ if (src_size < 16) return 1; /* The first 16 bytes of src are a raw frame header */ q = src[13] & 0x7f; if (height != src[4] << 3) { V4LCONVERT_ERR("Height is %d, not %d\n", src[4] << 3, height); return 1; } if (width != src[5] << 3) { V4LCONVERT_ERR("Width is %d, not %d\n", src[5] << 3, width); return 1; } /* * And the fun begins, first of all create a dummy jpeg, which we use * to get the headers from to feed to libjpeg when decompressing the * stripes. This way we can use libjpeg's quant table handling * (and built in default huffman tables). */ cinfo.err = jpeg_std_error (&jcerr); jpeg_create_compress (&cinfo); jpeg_mem_dest (&cinfo, &jpeg_header, &jpeg_header_size); cinfo.image_width = 16; cinfo.image_height = 16; cinfo.input_components = 3; cinfo.in_color_space = JCS_RGB; jpeg_set_defaults (&cinfo); /* Make comp[0] (which will be green) 1x2 subsampled */ cinfo.comp_info[0].h_samp_factor = 1; cinfo.comp_info[0].v_samp_factor = 2; /* Make comp[1] and [2] use huffman table and quanttable 0, as all * components use luminance settings with the jl2005c/d/e */ cinfo.comp_info[1].quant_tbl_no = 0; cinfo.comp_info[1].dc_tbl_no = 0; cinfo.comp_info[1].ac_tbl_no = 0; cinfo.comp_info[2].quant_tbl_no = 0; cinfo.comp_info[2].dc_tbl_no = 0; cinfo.comp_info[2].ac_tbl_no = 0; /* Apply the quality setting from the header */ if (q <= 0) i = 5000; else if (q <= 50) i = 5000 / q; else if (q <= 100) i = 2 * (100 - q); else i = 0; jpeg_set_linear_quality(&cinfo, i, TRUE); jpeg_start_compress (&cinfo, TRUE); while( cinfo.next_scanline < cinfo.image_height ) { JOCTET row[16 * 3]; JSAMPROW row_pointer[1] = { row }; jpeg_write_scanlines (&cinfo, row_pointer, 1); } jpeg_finish_compress (&cinfo); jpeg_destroy_compress (&cinfo); JSAMPLE green[8 * 16]; JSAMPLE red[8 * 8]; JSAMPLE blue[8 * 8]; JSAMPROW green_row_pointer[16]; JSAMPROW red_row_pointer[8]; JSAMPROW blue_row_pointer[8]; for (i = 0; i < 16; i++) green_row_pointer[i] = green + i * 8; for (i = 0; i < 8; i++) { red_row_pointer[i] = red + i * 8; blue_row_pointer[i] = blue + i * 8; } JSAMPARRAY samp_image[3] = { green_row_pointer, red_row_pointer, blue_row_pointer }; memcpy(jpeg_stripe, jpeg_header, JPEG_HEADER_SIZE); jpeg_stripe[JPEG_HEIGHT_OFFSET ] = height >> 8; jpeg_stripe[JPEG_HEIGHT_OFFSET + 1] = height; jpeg_stripe[JPEG_HEIGHT_OFFSET + 2] = 0; jpeg_stripe[JPEG_HEIGHT_OFFSET + 3] = 8; free (jpeg_header); /* Get past the raw frame header. */ src += 16; jpeg_data_size = src_size - 16; jpeg_data_idx = 0; dinfo.err = jpeg_std_error (&jderr); jpeg_create_decompress (&dinfo); for (x = 0; x < width; x += 16) { eoi = find_eoi(data, src, jpeg_data_idx, jpeg_data_size); if (eoi < 0) return eoi; size = eoi - jpeg_data_idx; if ((JPEG_HEADER_SIZE + size) > sizeof(jpeg_stripe)) { V4LCONVERT_ERR("stripe size too big %d > %zd\n", JPEG_HEADER_SIZE + size, sizeof(jpeg_stripe)); return 1; } memcpy (jpeg_stripe + JPEG_HEADER_SIZE, src + jpeg_data_idx, size); jpeg_mem_src (&dinfo, jpeg_stripe, JPEG_HEADER_SIZE + size); jpeg_read_header (&dinfo, TRUE); dinfo.raw_data_out = TRUE; #if JPEG_LIB_VERSION >= 70 dinfo.do_fancy_upsampling = FALSE; #endif jpeg_start_decompress (&dinfo); for (y = 0; y < height; y += 16) { jpeg_read_raw_data (&dinfo, samp_image, 16); for (y1 = 0; y1 < 16; y1 += 2) { for (x1 = 0; x1 < 16; x1 += 2) { dest[((y + y1 + 0) * width + x + x1 + 0)] = red[y1 * 4 + x1 / 2]; dest[((y + y1 + 0) * width + x + x1 + 1)] = green[y1 * 8 + x1 / 2]; dest[((y + y1 + 1) * width + x + x1 + 0)] = green[y1 * 8 + 8 + x1 / 2]; dest[((y + y1 + 1) * width + x + x1 + 1)] = blue[y1 * 4 + x1 / 2]; } } } jpeg_finish_decompress (&dinfo); /* Set jpeg_data_idx for the next stripe */ jpeg_data_idx = (jpeg_data_idx + size + 0x0f) & ~0x0f; } jpeg_destroy_decompress(&dinfo); return 0; } #endif /* HAVE_JPEG */