// Take a buffer containing a jpeg and return an optimized jpeg int optimizeJPEG(unsigned char *inputbuffer, unsigned long inputsize, unsigned char **outputbuffer, unsigned long *outputsize, int quality) { jvirt_barray_ptr *coef_arrays = NULL; JSAMPARRAY buf = NULL; struct jpeg_decompress_struct dinfo; struct jpeg_compress_struct cinfo; int j; int all_normal = 1; int all_progressive = 0; if (quality > 100) quality = 100; /* initialize decompression object */ dinfo.err = jpeg_std_error(&jderr.pub); jpeg_create_decompress(&dinfo); jderr.pub.error_exit=my_error_exit; jderr.pub.output_message=my_output_message; /* initialize compression object */ cinfo.err = jpeg_std_error(&jcerr.pub); jpeg_create_compress(&cinfo); jcerr.pub.error_exit=my_error_exit; jcerr.pub.output_message=my_output_message; /* setup error handling for decompress */ if (setjmp(jderr.setjmp_buffer)) { jpeg_abort_decompress(&dinfo); jpeg_destroy_decompress(&dinfo); jpeg_abort_compress(&cinfo); jpeg_destroy_compress(&cinfo); if (buf) { for (j=0;j<dinfo.output_height;j++) free(buf[j]); free(buf); buf=NULL; } outputsize = 0; outputbuffer = NULL; return 2; } /* prepare to decompress */ jpeg_mem_src(&dinfo, inputbuffer, inputsize); if (jpeg_read_header(&dinfo, TRUE) != JPEG_HEADER_OK) { return 2; } jpeg_mem_dest(&cinfo, outputbuffer, outputsize); printf("Proc: Image is %d by %d with %d components target quality:%d\n", dinfo.output_width, dinfo.output_height, dinfo.output_components, quality); if (quality>-1 ) { jpeg_start_decompress(&dinfo); buf = malloc(sizeof(JSAMPROW)*dinfo.output_height); if (!buf) { return 2; } for (j=0;j<dinfo.output_height;j++) { buf[j]=malloc(sizeof(JSAMPLE)*dinfo.output_width* dinfo.out_color_components); if (!buf[j]) return 2; } while (dinfo.output_scanline < dinfo.output_height) { jpeg_read_scanlines(&dinfo,&buf[dinfo.output_scanline], dinfo.output_height-dinfo.output_scanline); } } else { coef_arrays = jpeg_read_coefficients(&dinfo); } if (setjmp(jcerr.setjmp_buffer)) { jpeg_abort_compress(&cinfo); jpeg_abort_decompress(&dinfo); printf(" [Compress ERROR]\n"); if (buf) { for (j=0;j<dinfo.output_height;j++) free(buf[j]); free(buf); buf=NULL; } outputsize = 0; return 2; } if (quality>-1) { cinfo.in_color_space=dinfo.out_color_space; cinfo.input_components=dinfo.output_components; cinfo.image_width=dinfo.image_width; cinfo.image_height=dinfo.image_height; jpeg_set_defaults(&cinfo); jpeg_set_quality(&cinfo,quality,TRUE); if ( (dinfo.progressive_mode || all_progressive) && !all_normal ) jpeg_simple_progression(&cinfo); cinfo.optimize_coding = TRUE; j=0; jpeg_start_compress(&cinfo,TRUE); /* write image */ while (cinfo.next_scanline < cinfo.image_height) { jpeg_write_scanlines(&cinfo,&buf[cinfo.next_scanline], dinfo.output_height); } } else { jpeg_copy_critical_parameters(&dinfo, &cinfo); cinfo.optimize_coding = TRUE; jpeg_write_coefficients(&cinfo, coef_arrays); } jpeg_finish_compress(&cinfo); jpeg_finish_decompress(&dinfo); jpeg_destroy_decompress(&dinfo); jpeg_destroy_compress(&cinfo); return 0; }
static ImBuf *ibJpegImageFromCinfo(struct jpeg_decompress_struct *cinfo, int flags) { JSAMPARRAY row_pointer; JSAMPLE *buffer = NULL; int row_stride; int x, y, depth, r, g, b, k; struct ImBuf *ibuf = NULL; uchar *rect; jpeg_saved_marker_ptr marker; char *str, *key, *value; /* install own app1 handler */ ibuf_quality = jpeg_default_quality; jpeg_set_marker_processor(cinfo, 0xe1, handle_app1); cinfo->dct_method = JDCT_FLOAT; jpeg_save_markers(cinfo, JPEG_COM, 0xffff); if (jpeg_read_header(cinfo, false) == JPEG_HEADER_OK) { x = cinfo->image_width; y = cinfo->image_height; depth = cinfo->num_components; if (cinfo->jpeg_color_space == JCS_YCCK) cinfo->out_color_space = JCS_CMYK; jpeg_start_decompress(cinfo); if (flags & IB_test) { jpeg_abort_decompress(cinfo); ibuf = IMB_allocImBuf(x, y, 8 * depth, 0); } else if ((ibuf = IMB_allocImBuf(x, y, 8 * depth, IB_rect)) == NULL) { jpeg_abort_decompress(cinfo); } else { row_stride = cinfo->output_width * depth; row_pointer = (*cinfo->mem->alloc_sarray)((j_common_ptr) cinfo, JPOOL_IMAGE, row_stride, 1); for (y = ibuf->y - 1; y >= 0; y--) { jpeg_read_scanlines(cinfo, row_pointer, 1); rect = (uchar *) (ibuf->rect + y * ibuf->x); buffer = row_pointer[0]; switch (depth) { case 1: for (x = ibuf->x; x > 0; x--) { rect[3] = 255; rect[0] = rect[1] = rect[2] = *buffer++; rect += 4; } break; case 3: for (x = ibuf->x; x > 0; x--) { rect[3] = 255; rect[0] = *buffer++; rect[1] = *buffer++; rect[2] = *buffer++; rect += 4; } break; case 4: for (x = ibuf->x; x > 0; x--) { r = *buffer++; g = *buffer++; b = *buffer++; k = *buffer++; r = (r * k) / 255; g = (g * k) / 255; b = (b * k) / 255; rect[3] = 255; rect[2] = b; rect[1] = g; rect[0] = r; rect += 4; } break; } } marker = cinfo->marker_list; while (marker) { if (marker->marker != JPEG_COM) goto next_stamp_marker; /* * JPEG marker strings are not null-terminated, * create a null-terminated copy before going further */ str = BLI_strdupn((char *)marker->data, marker->data_length); /* * Because JPEG format don't support the * pair "key/value" like PNG, we store the * stampinfo in a single "encode" string: * "Blender:key:value" * * That is why we need split it to the * common key/value here. */ if (!STREQLEN(str, "Blender", 7)) { /* * Maybe the file have text that * we don't know "what it's", in that * case we keep the text (with a * key "None"). * This is only for don't "lose" * the information when we write * it back to disk. */ IMB_metadata_ensure(&ibuf->metadata); IMB_metadata_set_field(ibuf->metadata, "None", str); ibuf->flags |= IB_metadata; MEM_freeN(str); goto next_stamp_marker; } key = strchr(str, ':'); /* * A little paranoid, but the file maybe * is broken... and a "extra" check is better * then segfault ;) */ if (!key) { MEM_freeN(str); goto next_stamp_marker; } key++; value = strchr(key, ':'); if (!value) { MEM_freeN(str); goto next_stamp_marker; } *value = '\0'; /* need finish the key string */ value++; IMB_metadata_ensure(&ibuf->metadata); IMB_metadata_set_field(ibuf->metadata, key, value); ibuf->flags |= IB_metadata; MEM_freeN(str); next_stamp_marker: marker = marker->next; } jpeg_finish_decompress(cinfo); } jpeg_destroy((j_common_ptr) cinfo); if (ibuf) { ibuf->ftype = IMB_FTYPE_JPG; ibuf->foptions.quality = MIN2(ibuf_quality, 100); } } return(ibuf); }
/* main context */ static gboolean mjpeg_decoder_decode_frame(gpointer video_decoder) { MJpegDecoder *decoder = (MJpegDecoder*)video_decoder; gboolean back_compat = decoder->base.stream->channel->priv->peer_hdr.major_version == 1; JDIMENSION width, height; uint8_t *dest; uint8_t *lines[4]; jpeg_read_header(&decoder->mjpeg_cinfo, 1); width = decoder->mjpeg_cinfo.image_width; height = decoder->mjpeg_cinfo.image_height; if (decoder->out_size < width * height * 4) { g_free(decoder->out_frame); decoder->out_size = width * height * 4; decoder->out_frame = g_malloc(decoder->out_size); } dest = decoder->out_frame; #ifdef JCS_EXTENSIONS // requires jpeg-turbo if (back_compat) decoder->mjpeg_cinfo.out_color_space = JCS_EXT_RGBX; else decoder->mjpeg_cinfo.out_color_space = JCS_EXT_BGRX; #else #warning "You should consider building with libjpeg-turbo" decoder->mjpeg_cinfo.out_color_space = JCS_RGB; #endif #ifndef SPICE_QUALITY decoder->mjpeg_cinfo.dct_method = JDCT_IFAST; decoder->mjpeg_cinfo.do_fancy_upsampling = FALSE; decoder->mjpeg_cinfo.do_block_smoothing = FALSE; decoder->mjpeg_cinfo.dither_mode = JDITHER_ORDERED; #endif // TODO: in theory should check cinfo.output_height match with our height jpeg_start_decompress(&decoder->mjpeg_cinfo); /* rec_outbuf_height is the recommended size of the output buffer we * pass to libjpeg for optimum performance */ if (decoder->mjpeg_cinfo.rec_outbuf_height > G_N_ELEMENTS(lines)) { jpeg_abort_decompress(&decoder->mjpeg_cinfo); g_return_val_if_reached(G_SOURCE_REMOVE); } while (decoder->mjpeg_cinfo.output_scanline < decoder->mjpeg_cinfo.output_height) { /* only used when JCS_EXTENSIONS is undefined */ G_GNUC_UNUSED unsigned int lines_read; for (unsigned int j = 0; j < decoder->mjpeg_cinfo.rec_outbuf_height; j++) { lines[j] = dest; #ifdef JCS_EXTENSIONS dest += 4 * width; #else dest += 3 * width; #endif } lines_read = jpeg_read_scanlines(&decoder->mjpeg_cinfo, lines, decoder->mjpeg_cinfo.rec_outbuf_height); #ifndef JCS_EXTENSIONS { uint8_t *s = lines[0]; uint32_t *d = SPICE_ALIGNED_CAST(uint32_t *, s); if (back_compat) { for (unsigned int j = lines_read * width; j > 0; ) { j -= 1; // reverse order, bad for cache? d[j] = s[j * 3 + 0] | s[j * 3 + 1] << 8 | s[j * 3 + 2] << 16; } } else { for (unsigned int j = lines_read * width; j > 0; ) { j -= 1; // reverse order, bad for cache? d[j] = s[j * 3 + 0] << 16 | s[j * 3 + 1] << 8 | s[j * 3 + 2]; } } } #endif dest = &(decoder->out_frame[decoder->mjpeg_cinfo.output_scanline * width * 4]); } jpeg_finish_decompress(&decoder->mjpeg_cinfo); /* Display the frame and dispose of it */ stream_display_frame(decoder->base.stream, decoder->cur_frame, width, height, SPICE_UNKNOWN_STRIDE, decoder->out_frame); free_spice_frame(decoder->cur_frame); decoder->cur_frame = NULL; decoder->timer_id = 0; /* Schedule the next frame */ mjpeg_decoder_schedule(decoder); return G_SOURCE_REMOVE; }
uint8_t* ImageDecoder::decodeJPEGImpl(jpeg_source_mgr *src, jpeg_source_mgr *headerTables, uint32_t* width, uint32_t* height, bool* hasAlpha) { struct jpeg_decompress_struct cinfo; struct error_mgr err; cinfo.err = jpeg_std_error(&err); err.error_exit = error_exit; if (setjmp(err.jmpBuf)) { return NULL; } jpeg_create_decompress(&cinfo); if (headerTables) { cinfo.src = headerTables; jpeg_read_header(&cinfo, FALSE); } cinfo.src = src; if (headerTables) { // Must call init_source manually after switching src src->init_source(&cinfo); } jpeg_read_header(&cinfo, TRUE); #ifdef JCS_EXTENSIONS //JCS_EXT_XRGB is a fast decoder that outputs alpha channel, //but is only available on libjpeg-turbo cinfo.out_color_space = JCS_EXT_XRGB; cinfo.output_components = 4; #endif jpeg_start_decompress(&cinfo); *width = cinfo.output_width; *height = cinfo.output_height; if(cinfo.num_components != 3) { LOG(LOG_NOT_IMPLEMENTED,"Only RGB JPEG's are supported"); /* TODO: is this the right thing for aborting? */ jpeg_abort_decompress(&cinfo); jpeg_destroy_decompress(&cinfo); return NULL; } assert(cinfo.output_components == 3 || cinfo.output_components == 4); *hasAlpha = (cinfo.output_components == 4); int rowstride = cinfo.output_width * cinfo.output_components; JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE, rowstride, 1); uint8_t* outData = new uint8_t[cinfo.output_height * rowstride]; /* read one scanline at a time */ int y=0; while (cinfo.output_scanline < cinfo.output_height) { jpeg_read_scanlines(&cinfo, buffer, 1); memcpy(&outData[y*rowstride], buffer[0], rowstride); y++; } jpeg_finish_decompress(&cinfo); jpeg_destroy_decompress(&cinfo); return outData; }
static YOPTIMIZE_SPEED int decompress_jpeg(struct jpeg_decompress_struct *cinfo, struct jpeg_compress_struct *cinfoout, JCOPY_OPTION copyoption, Vbitmap *vbitmap, YmagineFormatOptions *options) { int scanlines; int nlines; int totallines; int j; int scalenum = -1; JSAMPARRAY buffer; Vrect srcrect; Vrect destrect; size_t row_stride; Transformer *transformer; PixelShader *shader = NULL; int iwidth, iheight; float sharpen = 0.0f; if (vbitmap == NULL && cinfoout == NULL) { /* No output specified */ return 0; } if (options == NULL) { /* Options argument is mandatory */ return 0; } iwidth = cinfo->image_width; iheight = cinfo->image_height; if (YmaginePrepareTransform(vbitmap, options, iwidth, iheight, &srcrect, &destrect) != YMAGINE_OK) { return 0; } shader = options->pixelshader; sharpen = options->sharpen; /* Define if image can be pre-subsampled by a ratio n/8 (n=1..7) */ scalenum = GetScaleNum(destrect.width, destrect.height, srcrect.width, srcrect.height, options->scalemode); if (scalenum > 0 && scalenum < 8) { cinfo->scale_num = scalenum; cinfo->scale_denom = 8; } /* Compute actual output dimension for image returned by decoder */ jpeg_calc_output_dimensions(cinfo); #if YMAGINE_DEBUG_JPEG ALOGD("src=%dx%d@%d,%d dst=%dx%d@%d,%d", srcrect.width, srcrect.height, srcrect.x, srcrect.y, destrect.width, destrect.height, destrect.x, destrect.y); ALOGD("size: %dx%d req: %dx%d %s -> scale: %d/%d output: %dx%d components: %d", iwidth, iheight, destrect.width, destrect.height, Ymagine_scaleModeStr(options->scalemode), cinfo->scale_num, cinfo->scale_denom, cinfo->output_width, cinfo->output_height, cinfo->output_components); #endif /* Scale the crop region to reflect scaling ratio applied by JPEG decoder */ if (cinfo->image_width != cinfo->output_width) { srcrect.x = (srcrect.x * cinfo->output_width) / cinfo->image_width; srcrect.width = (srcrect.width * cinfo->output_width) / cinfo->image_width; } if (cinfo->image_height != cinfo->output_height) { srcrect.y = (srcrect.y * cinfo->output_height) / cinfo->image_height; srcrect.height = (srcrect.height * cinfo->output_height) / cinfo->image_height; } /* Number of scan lines to handle per pass. Making it larger actually doesn't help much */ row_stride = cinfo->output_width * cinfo->output_components; scanlines = (32 * 1024) / row_stride; if (scanlines < 1) { scanlines = 1; } if (scanlines > cinfo->output_height) { scanlines = cinfo->output_height; } #if YMAGINE_DEBUG_JPEG ALOGD("BITMAP @(%d,%d) %dx%d bpp=%d -> @(%dx%d) %dx%d (%d lines)", srcrect.x, srcrect.y, srcrect.width, srcrect.height, JpegPixelSize(cinfo->out_color_space), destrect.x, destrect.y, destrect.width, destrect.height, scanlines); #endif /* Resize encoder */ if (cinfoout != NULL) { cinfoout->image_width = destrect.width; cinfoout->image_height = destrect.height; jpeg_start_compress(cinfoout, TRUE); if (copyoption != JCOPYOPT_NONE) { /* Copy to the output file any extra markers that we want to preserve */ jcopy_markers_execute(cinfo, cinfoout, copyoption); } } /* Resize target bitmap */ if (vbitmap != NULL) { if (options->resizable) { destrect.x = 0; destrect.y = 0; if (VbitmapResize(vbitmap, destrect.width, destrect.height) != YMAGINE_OK) { return 0; } } if (VbitmapType(vbitmap) == VBITMAP_NONE) { /* Decode bounds only, return positive number (number of lines) on success */ return VbitmapHeight(vbitmap); } } if (!jpeg_start_decompress(cinfo)) { if (cinfoout != NULL) { jpeg_abort_compress(cinfoout); } return 0; } buffer = (JSAMPARRAY) (*cinfo->mem->alloc_sarray)((j_common_ptr) cinfo, JPOOL_IMAGE, row_stride, scanlines); if (buffer == NULL) { if (cinfoout != NULL) { jpeg_abort_compress(cinfoout); } jpeg_abort_decompress(cinfo); return 0; } totallines = 0; transformer = TransformerCreate(); if (transformer != NULL) { TransformerSetScale(transformer, cinfo->output_width, cinfo->output_height, destrect.width, destrect.height); TransformerSetRegion(transformer, srcrect.x, srcrect.y, srcrect.width, srcrect.height); if (vbitmap != NULL) { TransformerSetMode(transformer, JpegPixelMode(cinfo->out_color_space), VbitmapColormode(vbitmap)); TransformerSetBitmap(transformer, vbitmap, destrect.x, destrect.y); } else { TransformerSetMode(transformer, JpegPixelMode(cinfo->out_color_space), JpegPixelMode(cinfoout->in_color_space)); TransformerSetWriter(transformer, JpegWriter, cinfoout); } TransformerSetShader(transformer, shader); TransformerSetSharpen(transformer, sharpen); } while (transformer != NULL && cinfo->output_scanline < cinfo->output_height) { nlines = jpeg_read_scanlines(cinfo, buffer, scanlines); if (nlines <= 0) { /* Decoding error */ ALOGD("decoding error (nlines=%d)", nlines); break; } for (j = 0; j < nlines; j++) { if (TransformerPush(transformer, (const char*) buffer[j]) != YMAGINE_OK) { TransformerRelease(transformer); transformer = NULL; break; } totallines++; } } /* Clean up */ if (transformer != NULL) { TransformerRelease(transformer); } if (cinfo->output_scanline > 0 && cinfo->output_scanline == cinfo->output_height) { /* Do normal cleanup if whole image has been read and decoded */ jpeg_finish_decompress(cinfo); if (cinfoout != NULL) { jpeg_finish_compress(cinfoout); } } else { /* else early abort */ jpeg_abort_decompress(cinfo); if (cinfoout != NULL) { jpeg_abort_compress(cinfoout); } totallines = 0; } return totallines; }
uint8_t* ImageDecoder::decodeJPEGImpl(jpeg_source_mgr *src, jpeg_source_mgr *headerTables, uint32_t* width, uint32_t* height, bool* hasAlpha) { struct jpeg_decompress_struct cinfo; struct error_mgr err; cinfo.err = jpeg_std_error(&err); err.error_exit = error_exit; if (setjmp(err.jmpBuf)) { return NULL; } jpeg_create_decompress(&cinfo); if (headerTables) cinfo.src = headerTables; else cinfo.src = src; //DefineBits tag may contain "abbreviated datastreams" (as //they are called in the libjpeg documentation), i.e. streams //with only compression tables and no image data. The first //jpeg_read_header accepts table-only streams. int headerStatus = jpeg_read_header(&cinfo, FALSE); if (headerTables) { // Must call init_source manually after switching src // Check this. Doesn't jpeg_read_header call // init_source anyway? cinfo.src = src; src->init_source(&cinfo); } //If the first jpeg_read_header got tables-only datastream, //a second call is needed to read the real image header. if (headerStatus == JPEG_HEADER_TABLES_ONLY) jpeg_read_header(&cinfo, TRUE); #ifdef JCS_EXTENSIONS //JCS_EXT_XRGB is a fast decoder that outputs alpha channel, //but is only available on libjpeg-turbo cinfo.out_color_space = JCS_EXT_XRGB; cinfo.output_components = 4; #endif jpeg_start_decompress(&cinfo); *width = cinfo.output_width; *height = cinfo.output_height; if(cinfo.num_components != 3) { LOG(LOG_NOT_IMPLEMENTED,"Only RGB JPEG's are supported"); /* TODO: is this the right thing for aborting? */ jpeg_abort_decompress(&cinfo); jpeg_destroy_decompress(&cinfo); return NULL; } assert(cinfo.output_components == 3 || cinfo.output_components == 4); *hasAlpha = (cinfo.output_components == 4); int rowstride = cinfo.output_width * cinfo.output_components; JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE, rowstride, 1); uint8_t* outData = new uint8_t[cinfo.output_height * rowstride]; /* read one scanline at a time */ int y=0; while (cinfo.output_scanline < cinfo.output_height) { jpeg_read_scanlines(&cinfo, buffer, 1); memcpy(&outData[y*rowstride], buffer[0], rowstride); y++; } jpeg_finish_decompress(&cinfo); jpeg_destroy_decompress(&cinfo); return outData; }
static GstFlowReturn gst_jpeg_dec_handle_frame (GstVideoDecoder * bdec, GstVideoCodecFrame * frame) { GstFlowReturn ret = GST_FLOW_OK; GstJpegDec *dec = (GstJpegDec *) bdec; GstVideoFrame vframe; gint width, height; gint r_h, r_v; guint code, hdr_ok; gboolean need_unmap = TRUE; GstVideoCodecState *state = NULL; dec->current_frame = frame; gst_buffer_map (frame->input_buffer, &dec->current_frame_map, GST_MAP_READ); gst_jpeg_dec_fill_input_buffer (&dec->cinfo); if (setjmp (dec->jerr.setjmp_buffer)) { code = dec->jerr.pub.msg_code; if (code == JERR_INPUT_EOF) { GST_DEBUG ("jpeg input EOF error, we probably need more data"); goto need_more_data; } goto decode_error; } /* read header */ hdr_ok = jpeg_read_header (&dec->cinfo, TRUE); if (G_UNLIKELY (hdr_ok != JPEG_HEADER_OK)) { GST_WARNING_OBJECT (dec, "reading the header failed, %d", hdr_ok); } GST_LOG_OBJECT (dec, "num_components=%d", dec->cinfo.num_components); GST_LOG_OBJECT (dec, "jpeg_color_space=%d", dec->cinfo.jpeg_color_space); if (!dec->cinfo.num_components || !dec->cinfo.comp_info) goto components_not_supported; r_h = dec->cinfo.comp_info[0].h_samp_factor; r_v = dec->cinfo.comp_info[0].v_samp_factor; GST_LOG_OBJECT (dec, "r_h = %d, r_v = %d", r_h, r_v); if (dec->cinfo.num_components > 3) goto components_not_supported; /* verify color space expectation to avoid going *boom* or bogus output */ if (dec->cinfo.jpeg_color_space != JCS_YCbCr && dec->cinfo.jpeg_color_space != JCS_GRAYSCALE && dec->cinfo.jpeg_color_space != JCS_RGB) goto unsupported_colorspace; #ifndef GST_DISABLE_GST_DEBUG { gint i; for (i = 0; i < dec->cinfo.num_components; ++i) { GST_LOG_OBJECT (dec, "[%d] h_samp_factor=%d, v_samp_factor=%d, cid=%d", i, dec->cinfo.comp_info[i].h_samp_factor, dec->cinfo.comp_info[i].v_samp_factor, dec->cinfo.comp_info[i].component_id); } } #endif /* prepare for raw output */ dec->cinfo.do_fancy_upsampling = FALSE; dec->cinfo.do_block_smoothing = FALSE; dec->cinfo.out_color_space = dec->cinfo.jpeg_color_space; dec->cinfo.dct_method = dec->idct_method; dec->cinfo.raw_data_out = TRUE; GST_LOG_OBJECT (dec, "starting decompress"); guarantee_huff_tables (&dec->cinfo); if (!jpeg_start_decompress (&dec->cinfo)) { GST_WARNING_OBJECT (dec, "failed to start decompression cycle"); } /* sanity checks to get safe and reasonable output */ switch (dec->cinfo.jpeg_color_space) { case JCS_GRAYSCALE: if (dec->cinfo.num_components != 1) goto invalid_yuvrgbgrayscale; break; case JCS_RGB: if (dec->cinfo.num_components != 3 || dec->cinfo.max_v_samp_factor > 1 || dec->cinfo.max_h_samp_factor > 1) goto invalid_yuvrgbgrayscale; break; case JCS_YCbCr: if (dec->cinfo.num_components != 3 || r_v > 2 || r_v < dec->cinfo.comp_info[0].v_samp_factor || r_v < dec->cinfo.comp_info[1].v_samp_factor || r_h < dec->cinfo.comp_info[0].h_samp_factor || r_h < dec->cinfo.comp_info[1].h_samp_factor) goto invalid_yuvrgbgrayscale; break; default: g_assert_not_reached (); break; } width = dec->cinfo.output_width; height = dec->cinfo.output_height; if (G_UNLIKELY (width < MIN_WIDTH || width > MAX_WIDTH || height < MIN_HEIGHT || height > MAX_HEIGHT)) goto wrong_size; gst_jpeg_dec_negotiate (dec, width, height, dec->cinfo.jpeg_color_space); state = gst_video_decoder_get_output_state (bdec); ret = gst_video_decoder_allocate_output_frame (bdec, frame); if (G_UNLIKELY (ret != GST_FLOW_OK)) goto alloc_failed; if (!gst_video_frame_map (&vframe, &state->info, frame->output_buffer, GST_MAP_READWRITE)) goto alloc_failed; GST_LOG_OBJECT (dec, "width %d, height %d", width, height); if (dec->cinfo.jpeg_color_space == JCS_RGB) { gst_jpeg_dec_decode_rgb (dec, &vframe); } else if (dec->cinfo.jpeg_color_space == JCS_GRAYSCALE) { gst_jpeg_dec_decode_grayscale (dec, &vframe); } else { GST_LOG_OBJECT (dec, "decompressing (reqired scanline buffer height = %u)", dec->cinfo.rec_outbuf_height); /* For some widths jpeglib requires more horizontal padding than I420 * provides. In those cases we need to decode into separate buffers and then * copy over the data into our final picture buffer, otherwise jpeglib might * write over the end of a line into the beginning of the next line, * resulting in blocky artifacts on the left side of the picture. */ if (G_UNLIKELY (width % (dec->cinfo.max_h_samp_factor * DCTSIZE) != 0 || dec->cinfo.comp_info[0].h_samp_factor != 2 || dec->cinfo.comp_info[1].h_samp_factor != 1 || dec->cinfo.comp_info[2].h_samp_factor != 1)) { GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, dec, "indirect decoding using extra buffer copy"); gst_jpeg_dec_decode_indirect (dec, &vframe, r_v, r_h, dec->cinfo.num_components); } else { ret = gst_jpeg_dec_decode_direct (dec, &vframe); if (G_UNLIKELY (ret != GST_FLOW_OK)) goto decode_direct_failed; } } gst_video_frame_unmap (&vframe); GST_LOG_OBJECT (dec, "decompressing finished"); jpeg_finish_decompress (&dec->cinfo); gst_buffer_unmap (frame->input_buffer, &dec->current_frame_map); ret = gst_video_decoder_finish_frame (bdec, frame); need_unmap = FALSE; done: exit: if (need_unmap) gst_buffer_unmap (frame->input_buffer, &dec->current_frame_map); if (state) gst_video_codec_state_unref (state); return ret; /* special cases */ need_more_data: { GST_LOG_OBJECT (dec, "we need more data"); ret = GST_FLOW_OK; goto exit; } /* ERRORS */ wrong_size: { GST_VIDEO_DECODER_ERROR (dec, 1, STREAM, DECODE, (_("Failed to decode JPEG image")), ("Picture is too small or too big (%ux%u)", width, height), ret); ret = GST_FLOW_ERROR; goto done; } decode_error: { gchar err_msg[JMSG_LENGTH_MAX]; dec->jerr.pub.format_message ((j_common_ptr) (&dec->cinfo), err_msg); GST_VIDEO_DECODER_ERROR (dec, 1, STREAM, DECODE, (_("Failed to decode JPEG image")), ("Decode error #%u: %s", code, err_msg), ret); gst_buffer_unmap (frame->input_buffer, &dec->current_frame_map); gst_video_decoder_drop_frame (bdec, frame); need_unmap = FALSE; jpeg_abort_decompress (&dec->cinfo); goto done; } decode_direct_failed: { /* already posted an error message */ jpeg_abort_decompress (&dec->cinfo); goto done; } alloc_failed: { const gchar *reason; reason = gst_flow_get_name (ret); GST_DEBUG_OBJECT (dec, "failed to alloc buffer, reason %s", reason); /* Reset for next time */ jpeg_abort_decompress (&dec->cinfo); if (ret != GST_FLOW_EOS && ret != GST_FLOW_FLUSHING && ret != GST_FLOW_NOT_LINKED) { GST_VIDEO_DECODER_ERROR (dec, 1, STREAM, DECODE, (_("Failed to decode JPEG image")), ("Buffer allocation failed, reason: %s", reason), ret); jpeg_abort_decompress (&dec->cinfo); } goto exit; } components_not_supported: { GST_VIDEO_DECODER_ERROR (dec, 1, STREAM, DECODE, (_("Failed to decode JPEG image")), ("number of components not supported: %d (max 3)", dec->cinfo.num_components), ret); jpeg_abort_decompress (&dec->cinfo); goto done; } unsupported_colorspace: { GST_VIDEO_DECODER_ERROR (dec, 1, STREAM, DECODE, (_("Failed to decode JPEG image")), ("Picture has unknown or unsupported colourspace"), ret); jpeg_abort_decompress (&dec->cinfo); goto done; } invalid_yuvrgbgrayscale: { GST_VIDEO_DECODER_ERROR (dec, 1, STREAM, DECODE, (_("Failed to decode JPEG image")), ("Picture is corrupt or unhandled YUV/RGB/grayscale layout"), ret); jpeg_abort_decompress (&dec->cinfo); goto done; } }
static ImBuf * ibJpegImageFromCinfo(struct jpeg_decompress_struct * cinfo, int flags) { JSAMPARRAY row_pointer; JSAMPLE * buffer = NULL; int row_stride; int x, y, depth, r, g, b, k; struct ImBuf * ibuf = NULL; uchar * rect; jpeg_saved_marker_ptr marker; char *str, *key, *value; /* install own app1 handler */ ibuf_ftype = 0; jpeg_set_marker_processor(cinfo, 0xe1, handle_app1); cinfo->dct_method = JDCT_FLOAT; jpeg_save_markers(cinfo, JPEG_COM, 0xffff); if (jpeg_read_header(cinfo, FALSE) == JPEG_HEADER_OK) { x = cinfo->image_width; y = cinfo->image_height; depth = cinfo->num_components; if (cinfo->jpeg_color_space == JCS_YCCK) cinfo->out_color_space = JCS_CMYK; jpeg_start_decompress(cinfo); if (ibuf_ftype == 0) { ibuf_ftype = JPG_STD; if (cinfo->max_v_samp_factor == 1) { if (cinfo->max_h_samp_factor == 1) ibuf_ftype = JPG_MAX; else ibuf_ftype = JPG_VID; } } if (flags & IB_test) { jpeg_abort_decompress(cinfo); ibuf = IMB_allocImBuf(x, y, 8 * depth, 0); } else if ((ibuf = IMB_allocImBuf(x, y, 8 * depth, IB_rect)) == NULL) { jpeg_abort_decompress(cinfo); } else { row_stride = cinfo->output_width * depth; row_pointer = (*cinfo->mem->alloc_sarray) ((j_common_ptr) cinfo, JPOOL_IMAGE, row_stride, 1); for (y = ibuf->y - 1; y >= 0; y--) { jpeg_read_scanlines(cinfo, row_pointer, 1); rect = (uchar *) (ibuf->rect + y * ibuf->x); buffer = row_pointer[0]; switch(depth) { case 1: for (x=ibuf->x; x >0; x--) { rect[3] = 255; rect[0] = rect[1] = rect[2] = *buffer++; rect += 4; } break; case 3: for (x=ibuf->x; x >0; x--) { rect[3] = 255; rect[0] = *buffer++; rect[1] = *buffer++; rect[2] = *buffer++; rect += 4; } break; case 4: for (x=ibuf->x; x >0; x--) { r = *buffer++; g = *buffer++; b = *buffer++; k = *buffer++; k = 255 - k; r -= k; if (r & 0xffffff00) { if (r < 0) r = 0; else r = 255; } g -= k; if (g & 0xffffff00) { if (g < 0) g = 0; else g = 255; } b -= k; if (b & 0xffffff00) { if (b < 0) b = 0; else b = 255; } rect[3] = 255 - k; rect[2] = b; rect[1] = g; rect[0] = r; rect += 4; } } } marker= cinfo->marker_list; while(marker) { if(marker->marker != JPEG_COM) goto next_stamp_marker; /* * Because JPEG format don't support the * pair "key/value" like PNG, we store the * stampinfo in a single "encode" string: * "Blender:key:value" * * That is why we need split it to the * common key/value here. */ if(strncmp((char *) marker->data, "Blender", 7)) { /* * Maybe the file have text that * we don't know "what it's", in that * case we keep the text (with a * key "None"). * This is only for don't "lose" * the information when we write * it back to disk. */ IMB_metadata_add_field(ibuf, "None", (char *) marker->data); ibuf->flags |= IB_metadata; goto next_stamp_marker; } str = BLI_strdup ((char *) marker->data); key = strchr (str, ':'); /* * A little paranoid, but the file maybe * is broken... and a "extra" check is better * that a segfaul ;) */ if (!key) { MEM_freeN(str); goto next_stamp_marker; } key++; value = strchr (key, ':'); if (!value) { MEM_freeN(str); goto next_stamp_marker; } *value = '\0'; /* need finish the key string */ value++; IMB_metadata_add_field(ibuf, key, value); ibuf->flags |= IB_metadata; MEM_freeN(str); next_stamp_marker: marker= marker->next; } jpeg_finish_decompress(cinfo); } jpeg_destroy((j_common_ptr) cinfo); if(ibuf) { ibuf->ftype = ibuf_ftype; ibuf->profile = IB_PROFILE_SRGB; } } return(ibuf); }
DLLEXPORT int DLLCALL tjDecompress(tjhandle h, unsigned char *srcbuf, unsigned long size, unsigned char *dstbuf, int width, int pitch, int height, int ps, int flags) { int i, row, retval=0; JSAMPROW *row_pointer=NULL, *outbuf[MAX_COMPONENTS]; int cw[MAX_COMPONENTS], ch[MAX_COMPONENTS], iw[MAX_COMPONENTS], tmpbufsize=0, usetmpbuf=0, th[MAX_COMPONENTS]; JSAMPLE *_tmpbuf=NULL; JSAMPROW *tmpbuf[MAX_COMPONENTS]; checkhandle(h); for(i=0; i<MAX_COMPONENTS; i++) { tmpbuf[i]=NULL; outbuf[i]=NULL; } if(srcbuf==NULL || size<=0 || dstbuf==NULL || width<=0 || pitch<0 || height<=0) _throw("Invalid argument in tjDecompress()"); if(ps!=3 && ps!=4 && ps!=1) _throw("This decompressor can only handle 24-bit and 32-bit RGB or 8-bit grayscale output"); if(!j->initd) _throw("Instance has not been initialized for decompression"); if(pitch==0) pitch=width*ps; if(flags&TJ_FORCEMMX) putenv("JSIMD_FORCEMMX=1"); else if(flags&TJ_FORCESSE) putenv("JSIMD_FORCESSE=1"); else if(flags&TJ_FORCESSE2) putenv("JSIMD_FORCESSE2=1"); if(setjmp(j->jerr.jb)) { // this will execute if LIBJPEG has an error retval=-1; goto bailout; } j->jsms.bytes_in_buffer = size; j->jsms.next_input_byte = srcbuf; jpeg_read_header(&j->dinfo, TRUE); if(flags&TJ_YUV) { j_decompress_ptr dinfo=&j->dinfo; JSAMPLE *ptr=dstbuf; for(i=0; i<dinfo->num_components; i++) { jpeg_component_info *compptr=&dinfo->comp_info[i]; int ih; iw[i]=compptr->width_in_blocks*DCTSIZE; ih=compptr->height_in_blocks*DCTSIZE; cw[i]=PAD(dinfo->image_width, dinfo->max_h_samp_factor) *compptr->h_samp_factor/dinfo->max_h_samp_factor; ch[i]=PAD(dinfo->image_height, dinfo->max_v_samp_factor) *compptr->v_samp_factor/dinfo->max_v_samp_factor; if(iw[i]!=cw[i] || ih!=ch[i]) usetmpbuf=1; th[i]=compptr->v_samp_factor*DCTSIZE; tmpbufsize+=iw[i]*th[i]; if((outbuf[i]=(JSAMPROW *)malloc(sizeof(JSAMPROW)*ch[i]))==NULL) _throw("Memory allocation failed in tjDecompress()"); for(row=0; row<ch[i]; row++) { outbuf[i][row]=ptr; ptr+=PAD(cw[i], 4); } } if(usetmpbuf) { if((_tmpbuf=(JSAMPLE *)malloc(sizeof(JSAMPLE)*tmpbufsize))==NULL) _throw("Memory allocation failed in tjDecompress()"); ptr=_tmpbuf; for(i=0; i<dinfo->num_components; i++) { if((tmpbuf[i]=(JSAMPROW *)malloc(sizeof(JSAMPROW)*th[i]))==NULL) _throw("Memory allocation failed in tjDecompress()"); for(row=0; row<th[i]; row++) { tmpbuf[i][row]=ptr; ptr+=iw[i]; } } } } else { if((row_pointer=(JSAMPROW *)malloc(sizeof(JSAMPROW)*height))==NULL) _throw("Memory allocation failed in tjDecompress()"); for(i=0; i<height; i++) { if(flags&TJ_BOTTOMUP) row_pointer[i]= &dstbuf[(height-i-1)*pitch]; else row_pointer[i]= &dstbuf[i*pitch]; } } if(ps==1) j->dinfo.out_color_space = JCS_GRAYSCALE; #if JCS_EXTENSIONS==1 else j->dinfo.out_color_space = JCS_EXT_RGB; if(ps==3 && (flags&TJ_BGR)) j->dinfo.out_color_space = JCS_EXT_BGR; else if(ps==4 && !(flags&TJ_BGR) && !(flags&TJ_ALPHAFIRST)) j->dinfo.out_color_space = JCS_EXT_RGBX; else if(ps==4 && (flags&TJ_BGR) && !(flags&TJ_ALPHAFIRST)) j->dinfo.out_color_space = JCS_EXT_BGRX; else if(ps==4 && (flags&TJ_BGR) && (flags&TJ_ALPHAFIRST)) j->dinfo.out_color_space = JCS_EXT_XBGR; else if(ps==4 && !(flags&TJ_BGR) && (flags&TJ_ALPHAFIRST)) j->dinfo.out_color_space = JCS_EXT_XRGB; #else #error "TurboJPEG requires JPEG colorspace extensions" #endif if(flags&TJ_FASTUPSAMPLE) j->dinfo.do_fancy_upsampling=FALSE; if(flags&TJ_YUV) j->dinfo.raw_data_out=TRUE; jpeg_start_decompress(&j->dinfo); if(flags&TJ_YUV) { j_decompress_ptr dinfo=&j->dinfo; for(row=0; row<dinfo->output_height; row+=dinfo->max_v_samp_factor*DCTSIZE) { JSAMPARRAY yuvptr[MAX_COMPONENTS]; int crow[MAX_COMPONENTS]; for(i=0; i<dinfo->num_components; i++) { jpeg_component_info *compptr=&dinfo->comp_info[i]; crow[i]=row*compptr->v_samp_factor/dinfo->max_v_samp_factor; if(usetmpbuf) yuvptr[i]=tmpbuf[i]; else yuvptr[i]=&outbuf[i][crow[i]]; } jpeg_read_raw_data(dinfo, yuvptr, dinfo->max_v_samp_factor*DCTSIZE); if(usetmpbuf) { int j; for(i=0; i<dinfo->num_components; i++) { for(j=0; j<min(th[i], ch[i]-crow[i]); j++) { memcpy(outbuf[i][crow[i]+j], tmpbuf[i][j], cw[i]); } } } } } else { while(j->dinfo.output_scanline<j->dinfo.output_height) { jpeg_read_scanlines(&j->dinfo, &row_pointer[j->dinfo.output_scanline], j->dinfo.output_height-j->dinfo.output_scanline); } } jpeg_finish_decompress(&j->dinfo); bailout: if(j->dinfo.global_state>DSTATE_START) jpeg_abort_decompress(&j->dinfo); for(i=0; i<MAX_COMPONENTS; i++) { if(tmpbuf[i]) free(tmpbuf[i]); if(outbuf[i]) free(outbuf[i]); } if(_tmpbuf) free(_tmpbuf); if(row_pointer) free(row_pointer); return retval; }
int main(int argc, char **argv) { JSAMPARRAY buf = malloc(sizeof(JSAMPROW)*BUF_LINES); jpeg_saved_marker_ptr exif_marker, cmarker; MD5_CTX *MD5 = malloc(sizeof(MD5_CTX)); volatile int i; int c,j,lines_read, err_count; unsigned char ch; char namebuf[1024]; long fs; unsigned char *md5buf,digest[16]; char digest_text[33]; global_total_errors=0; if (rcsid); /* to keep compiler from not complaining about rcsid */ cinfo.err = jpeg_std_error(&jerr.pub); jpeg_create_decompress(&cinfo); jerr.pub.error_exit=my_error_exit; jerr.pub.output_message=my_output_message; if (!buf || !MD5) no_memory(); if (argc<2) { if (quiet_mode < 2) fprintf(stderr,"jpeginfo: file arguments missing\n" "Try 'jpeginfo " "--help" "' for more information.\n"); exit(1); } /* parse command line parameters */ while(1) { opt_index=0; if ( (c=getopt_long(argc,argv,"livVdcChqm:f:5", long_options,&opt_index)) == -1) break; switch (c) { case 'm': if (!strcasecmp(optarg,"all")) del_mode=0; else if (!strcasecmp(optarg,"erronly")) del_mode=1; else if (!quiet_mode) fprintf(stderr,"Unknown parameter for -m, --mode.\n"); break; case 'f': if (!strcmp(optarg,"-")) listfile=stdin; else if ((listfile=fopen(optarg,"r"))==NULL) { fprintf(stderr,"Cannot open file '%s'.\n",optarg); exit(2); } input_from_file=1; break; case 'v': verbose_mode=1; break; case 'V': fprintf(stderr,"jpeginfo v" VERSION " " HOST_TYPE "\nCopyright (c) Timo Kokkonen, 1995-2002.\n"); exit(0); case 'd': delete_mode=1; break; case 'c': check_mode=1; break; case 'h': p_usage(); break; case 'q': quiet_mode++; break; case 'l': list_mode=1; break; case 'i': longinfo_mode=1; break; case '5': md5_mode=1; break; case 'C': com_mode=1; break; case '?': break; default: if (!quiet_mode) fprintf(stderr,"jpeginfo: error parsing parameters.\n"); } } if (delete_mode && verbose_mode && !quiet_mode) fprintf(stderr,"jpeginfo: delete mode enabled (%s)\n", !del_mode?"normal":"errors only"); i=1; do { if (input_from_file) { if (!fgetstr(namebuf,sizeof(namebuf),listfile)) break; current=namebuf; } else current=argv[i]; if (current[0]==0) continue; if (current[0]=='-' && !input_from_file) continue; if (setjmp(jerr.setjmp_buffer)) { jpeg_abort_decompress(&cinfo); fclose(infile); if (list_mode && quiet_mode < 2) printf(" %s",current); if (quiet_mode < 2) printf(" [ERROR]\n"); if (delete_mode) delete_file(current,verbose_mode,quiet_mode); continue; } if ((infile=fopen(current,"r"))==NULL) { if (!quiet_mode) fprintf(stderr, "jpeginfo: can't open '%s'\n", current); continue; } if (is_dir(infile)) { fclose(infile); if (verbose_mode) printf("directory: %s skipped\n",current); continue; } fs=filesize(infile); if (md5_mode) { md5buf=malloc(fs); if (!md5buf) no_memory(); fread(md5buf,1,fs,infile); rewind(infile); MD5Init(MD5); MD5Update(MD5,md5buf,fs); MD5Final(digest,MD5); md2str(digest,digest_text); free(md5buf); } if (!list_mode && quiet_mode < 2) printf("%s ",current); global_error_counter=0; err_count=jerr.pub.num_warnings; if (com_mode) jpeg_save_markers(&cinfo, JPEG_COM, 0xffff); jpeg_save_markers(&cinfo, EXIF_JPEG_MARKER, 0xffff); jpeg_stdio_src(&cinfo, infile); jpeg_read_header(&cinfo, TRUE); /* check for Exif marker */ exif_marker=NULL; cmarker=cinfo.marker_list; while (cmarker) { if (cmarker->marker == EXIF_JPEG_MARKER) { if (!memcmp(cmarker->data,EXIF_IDENT_STRING,6)) exif_marker=cmarker; } cmarker=cmarker->next; } if (quiet_mode < 2) { printf("%4d x %-4d %2dbit ",(int)cinfo.image_width, (int)cinfo.image_height,(int)cinfo.num_components*8); if (exif_marker) printf("Exif "); else if (cinfo.saw_JFIF_marker) printf("JFIF "); else if (cinfo.saw_Adobe_marker) printf("Adobe "); else printf("n/a "); if (longinfo_mode) { printf("%s %s",(cinfo.progressive_mode?"Progressive":"Normal"), (cinfo.arith_code?"Arithmetic":"Huffman") ); if (cinfo.density_unit==1||cinfo.density_unit==2) printf(",%ddp%c",MIN(cinfo.X_density,cinfo.Y_density), (cinfo.density_unit==1?'i':'c') ); if (cinfo.CCIR601_sampling) printf(",CCIR601"); printf(" %7ld ",fs); } else printf("%c %7ld ",(cinfo.progressive_mode?'P':'N'),fs); if (md5_mode) printf("%s ",digest_text); if (list_mode) printf("%s ",current); if (com_mode) { cmarker=cinfo.marker_list; while (cmarker) { if (cmarker->marker == JPEG_COM) { printf("\""); for (j=0;j<cmarker->data_length;j++) { ch = cmarker->data[j]; if (ch < 32 || iscntrl(ch)) continue; printf("%c",cmarker->data[j]); } printf("\" "); } cmarker=cmarker->next; } } } if (check_mode) { cinfo.out_color_space=JCS_GRAYSCALE; /* to speed up the process... */ cinfo.scale_denom = 8; cinfo.scale_num = 1; jpeg_start_decompress(&cinfo); for (j=0;j<BUF_LINES;j++) { buf[j]=malloc(sizeof(JSAMPLE)*cinfo.output_width* cinfo.out_color_components); if (!buf[j]) no_memory(); } while (cinfo.output_scanline < cinfo.output_height) { lines_read = jpeg_read_scanlines(&cinfo, buf,BUF_LINES); } jpeg_finish_decompress(&cinfo); for(j=0;j<BUF_LINES;j++) free(buf[j]); if (!global_error_counter) { if (quiet_mode < 2) printf(" [OK]\n"); } else { if (quiet_mode < 2) printf(" [WARNING]\n"); if (delete_mode && !del_mode) delete_file(current,verbose_mode,quiet_mode); } } else { /* !check_mode */ if (quiet_mode < 2) printf("\n"); jpeg_abort_decompress(&cinfo); } fclose(infile); } while (++i<argc || input_from_file); jpeg_destroy_decompress(&cinfo); free(buf); free(MD5); return (global_total_errors>0?1:0); /* return 1 if any errors found file(s) we checked */ }
int main(int argc, char **argv) { struct jpeg_decompress_struct dinfo; struct jpeg_compress_struct cinfo; struct my_error_mgr jcerr,jderr; JSAMPARRAY buf = NULL; jvirt_barray_ptr *coef_arrays = NULL; char marker_str[256]; char tmpfilename[MAXPATHLEN],tmpdir[MAXPATHLEN]; char newname[MAXPATHLEN], dest_path[MAXPATHLEN]; volatile int i; int c,j, tmpfd, searchcount, searchdone; int opt_index = 0; long insize = 0, outsize = 0, lastsize = 0; int oldquality; double ratio; struct stat file_stat; jpeg_saved_marker_ptr cmarker; unsigned char *outbuffer = NULL; size_t outbuffersize; char *outfname = NULL; FILE *infile = NULL, *outfile = NULL; int marker_in_count, marker_in_size; int compress_err_count = 0; int decompress_err_count = 0; long average_count = 0; double average_rate = 0.0, total_save = 0.0; if (rcsid) ; /* so compiler won't complain about "unused" rcsid string */ umask(077); signal(SIGINT,own_signal_handler); signal(SIGTERM,own_signal_handler); /* initialize decompression object */ dinfo.err = jpeg_std_error(&jderr.pub); jpeg_create_decompress(&dinfo); jderr.pub.error_exit=my_error_exit; jderr.pub.output_message=my_output_message; jderr.jump_set = 0; /* initialize compression object */ cinfo.err = jpeg_std_error(&jcerr.pub); jpeg_create_compress(&cinfo); jcerr.pub.error_exit=my_error_exit; jcerr.pub.output_message=my_output_message; jcerr.jump_set = 0; if (argc<2) { if (!quiet_mode) fprintf(stderr,PROGRAMNAME ": file arguments missing\n" "Try '" PROGRAMNAME " --help' for more information.\n"); exit(1); } /* parse command line parameters */ while(1) { opt_index=0; if ((c=getopt_long(argc,argv,"d:hm:nstqvfVpPoT:S:b",long_options,&opt_index)) == -1) break; switch (c) { case 'm': { int tmpvar; if (sscanf(optarg,"%d",&tmpvar) == 1) { quality=tmpvar; if (quality < 0) quality=0; if (quality > 100) quality=100; } else fatal("invalid argument for -m, --max"); } break; case 'd': if (realpath(optarg,dest_path)==NULL || !is_directory(dest_path)) { fatal("invalid argument for option -d, --dest"); } strncat(dest_path,DIR_SEPARATOR_S,sizeof(dest_path)-strlen(dest_path)-1); if (verbose_mode) fprintf(stderr,"Destination directory: %s\n",dest_path); dest=1; break; case 'v': verbose_mode++; break; case 'h': print_usage(); exit(0); break; case 'q': quiet_mode=1; break; case 't': totals_mode=1; break; case 'n': noaction=1; break; case 'f': force=1; break; case 'b': csv=1; quiet_mode=1; break; case '?': break; case 'V': print_version(); exit(0); break; case 'o': overwrite_mode=1; break; case 'p': preserve_mode=1; break; case 'P': preserve_perms=1; break; case 's': save_exif=0; save_iptc=0; save_com=0; save_icc=0; save_xmp=0; break; case 'T': { int tmpvar; if (sscanf(optarg,"%d",&tmpvar) == 1) { threshold=tmpvar; if (threshold < 0) threshold=0; if (threshold > 100) threshold=100; } else fatal("invalid argument for -T, --threshold"); } break; case 'S': { unsigned int tmpvar; if (sscanf(optarg,"%u",&tmpvar) == 1) { if (tmpvar > 0 && tmpvar < 100 && optarg[strlen(optarg)-1] == '%' ) { target_size=-tmpvar; } else { target_size=tmpvar; } quality=100; } else fatal("invalid argument for -S, --size"); } break; } } /* check for '-' option indicating input is from stdin... */ i=1; while (argv[i]) { if (argv[i][0]=='-' && argv[i][1]==0) stdin_mode=1; i++; } if (stdin_mode) { stdout_mode=1; force=1; } if (stdout_mode) { logs_to_stdout=0; } if (all_normal && all_progressive) fatal("cannot specify both --all-normal and --all-progressive"); if (verbose_mode) { if (quality>=0 && target_size==0) fprintf(stderr,"Image quality limit set to: %d\n",quality); if (threshold>=0) fprintf(stderr,"Compression threshold (%%) set to: %d\n",threshold); if (all_normal) fprintf(stderr,"All output files will be non-progressive\n"); if (all_progressive) fprintf(stderr,"All output files will be progressive\n"); if (target_size > 0) fprintf(stderr,"Target size for output files set to: %u Kbytes.\n", target_size); if (target_size < 0) fprintf(stderr,"Target size for output files set to: %u%%\n", -target_size); } /* loop to process the input files */ i=1; do { if (stdin_mode) { infile=stdin; } else { if (!argv[i][0]) continue; if (argv[i][0]=='-') continue; if (strlen(argv[i]) >= MAXPATHLEN) { warn("skipping too long filename: %s",argv[i]); continue; } if (!noaction) { /* generate tmp dir & new filename */ if (dest) { STRNCPY(tmpdir,dest_path,sizeof(tmpdir)); STRNCPY(newname,dest_path,sizeof(newname)); if (!splitname(argv[i],tmpfilename,sizeof(tmpfilename))) fatal("splitname() failed for: %s",argv[i]); strncat(newname,tmpfilename,sizeof(newname)-strlen(newname)-1); } else { if (!splitdir(argv[i],tmpdir,sizeof(tmpdir))) fatal("splitdir() failed for: %s",argv[i]); STRNCPY(newname,argv[i],sizeof(newname)); } } retry_point: if (!is_file(argv[i],&file_stat)) { if (is_directory(argv[i])) warn("skipping directory: %s",argv[i]); else warn("skipping special file: %s",argv[i]); continue; } if ((infile=fopen(argv[i],"rb"))==NULL) { warn("cannot open file: %s", argv[i]); continue; } } if (setjmp(jderr.setjmp_buffer)) { /* error handler for decompress */ jpeg_abort_decompress(&dinfo); fclose(infile); if (buf) FREE_LINE_BUF(buf,dinfo.output_height); if (!quiet_mode || csv) fprintf(LOG_FH,csv ? ",,,,,error\n" : " [ERROR]\n"); decompress_err_count++; jderr.jump_set=0; continue; } else { jderr.jump_set=1; } if (!retry && (!quiet_mode || csv)) { fprintf(LOG_FH,csv ? "%s," : "%s ",(stdin_mode?"stdin":argv[i])); fflush(LOG_FH); } /* prepare to decompress */ global_error_counter=0; jpeg_save_markers(&dinfo, JPEG_COM, 0xffff); for (j=0;j<=15;j++) jpeg_save_markers(&dinfo, JPEG_APP0+j, 0xffff); jpeg_stdio_src(&dinfo, infile); jpeg_read_header(&dinfo, TRUE); /* check for Exif/IPTC/ICC/XMP markers */ marker_str[0]=0; marker_in_count=0; marker_in_size=0; cmarker=dinfo.marker_list; while (cmarker) { marker_in_count++; marker_in_size+=cmarker->data_length; if (cmarker->marker == EXIF_JPEG_MARKER && !memcmp(cmarker->data,EXIF_IDENT_STRING,EXIF_IDENT_STRING_SIZE)) strncat(marker_str,"Exif ",sizeof(marker_str)-strlen(marker_str)-1); if (cmarker->marker == IPTC_JPEG_MARKER) strncat(marker_str,"IPTC ",sizeof(marker_str)-strlen(marker_str)-1); if (cmarker->marker == ICC_JPEG_MARKER && !memcmp(cmarker->data,ICC_IDENT_STRING,ICC_IDENT_STRING_SIZE)) strncat(marker_str,"ICC ",sizeof(marker_str)-strlen(marker_str)-1); if (cmarker->marker == XMP_JPEG_MARKER && !memcmp(cmarker->data,XMP_IDENT_STRING,XMP_IDENT_STRING_SIZE)) strncat(marker_str,"XMP ",sizeof(marker_str)-strlen(marker_str)-1); cmarker=cmarker->next; } if (verbose_mode > 1) fprintf(LOG_FH,"%d markers found in input file (total size %d bytes)\n", marker_in_count,marker_in_size); if (!retry && (!quiet_mode || csv)) { fprintf(LOG_FH,csv ? "%dx%d,%dbit,%c," : "%dx%d %dbit %c ",(int)dinfo.image_width, (int)dinfo.image_height,(int)dinfo.num_components*8, (dinfo.progressive_mode?'P':'N')); if (!csv) { fprintf(LOG_FH,"%s",marker_str); if (dinfo.saw_Adobe_marker) fprintf(LOG_FH,"Adobe "); if (dinfo.saw_JFIF_marker) fprintf(LOG_FH,"JFIF "); } fflush(LOG_FH); } if ((insize=file_size(infile)) < 0) fatal("failed to stat() input file"); /* decompress the file */ if (quality>=0 && !retry) { jpeg_start_decompress(&dinfo); /* allocate line buffer to store the decompressed image */ buf = malloc(sizeof(JSAMPROW)*dinfo.output_height); if (!buf) fatal("not enough memory"); for (j=0;j<dinfo.output_height;j++) { buf[j]=malloc(sizeof(JSAMPLE)*dinfo.output_width* dinfo.out_color_components); if (!buf[j]) fatal("not enough memory"); } while (dinfo.output_scanline < dinfo.output_height) { jpeg_read_scanlines(&dinfo,&buf[dinfo.output_scanline], dinfo.output_height-dinfo.output_scanline); } } else { coef_arrays = jpeg_read_coefficients(&dinfo); } if (!retry && !quiet_mode) { if (global_error_counter==0) fprintf(LOG_FH," [OK] "); else fprintf(LOG_FH," [WARNING] "); fflush(LOG_FH); } fclose(infile); infile=NULL; if (dest && !noaction) { if (file_exists(newname) && !overwrite_mode) { warn("target file already exists: %s\n",newname); jpeg_abort_decompress(&dinfo); if (buf) FREE_LINE_BUF(buf,dinfo.output_height); continue; } } if (setjmp(jcerr.setjmp_buffer)) { /* error handler for compress failures */ jpeg_abort_compress(&cinfo); jpeg_abort_decompress(&dinfo); if (!quiet_mode) fprintf(LOG_FH," [Compress ERROR]\n"); if (buf) FREE_LINE_BUF(buf,dinfo.output_height); compress_err_count++; jcerr.jump_set=0; continue; } else { jcerr.jump_set=1; } lastsize = 0; searchcount = 0; searchdone = 0; oldquality = 200; binary_search_loop: /* allocate memory buffer that should be large enough to store the output JPEG... */ if (outbuffer) free(outbuffer); outbuffersize=insize + 32768; outbuffer=malloc(outbuffersize); if (!outbuffer) fatal("not enough memory"); /* setup custom "destination manager" for libjpeg to write to our buffer */ jpeg_memory_dest(&cinfo, &outbuffer, &outbuffersize, 65536); if (quality>=0 && !retry) { /* lossy "optimization" ... */ cinfo.in_color_space=dinfo.out_color_space; cinfo.input_components=dinfo.output_components; cinfo.image_width=dinfo.image_width; cinfo.image_height=dinfo.image_height; jpeg_set_defaults(&cinfo); jpeg_set_quality(&cinfo,quality,TRUE); if ( (dinfo.progressive_mode || all_progressive) && !all_normal ) jpeg_simple_progression(&cinfo); cinfo.optimize_coding = TRUE; j=0; jpeg_start_compress(&cinfo,TRUE); /* write markers */ write_markers(&dinfo,&cinfo); /* write image */ while (cinfo.next_scanline < cinfo.image_height) { jpeg_write_scanlines(&cinfo,&buf[cinfo.next_scanline], dinfo.output_height); } } else { /* lossless "optimization" ... */ jpeg_copy_critical_parameters(&dinfo, &cinfo); if ( (dinfo.progressive_mode || all_progressive) && !all_normal ) jpeg_simple_progression(&cinfo); cinfo.optimize_coding = TRUE; /* write image */ jpeg_write_coefficients(&cinfo, coef_arrays); /* write markers */ write_markers(&dinfo,&cinfo); } jpeg_finish_compress(&cinfo); outsize=outbuffersize; if (target_size != 0 && !retry) { /* perform (binary) search to try to reach target file size... */ long osize = outsize/1024; long isize = insize/1024; long tsize = target_size; if (tsize < 0) { tsize=((-target_size)*insize/100)/1024; if (tsize < 1) tsize=1; } if (osize == tsize || searchdone || searchcount >= 8 || tsize > isize) { if (searchdone < 42 && lastsize > 0) { if (abs(osize-tsize) > abs(lastsize-tsize)) { if (verbose_mode) fprintf(LOG_FH,"(revert to %d)",oldquality); searchdone=42; quality=oldquality; goto binary_search_loop; } } if (verbose_mode) fprintf(LOG_FH," "); } else { int newquality; int dif = round(abs(oldquality-quality)/2.0); if (osize > tsize) { newquality=quality-dif; if (dif < 1) { newquality--; searchdone=1; } if (newquality < 0) { newquality=0; searchdone=2; } } else { newquality=quality+dif; if (dif < 1) { newquality++; searchdone=3; } if (newquality > 100) { newquality=100; searchdone=4; } } oldquality=quality; quality=newquality; if (verbose_mode) fprintf(LOG_FH,"(try %d)",quality); lastsize=osize; searchcount++; goto binary_search_loop; } } if (buf) FREE_LINE_BUF(buf,dinfo.output_height); jpeg_finish_decompress(&dinfo); if (quality>=0 && outsize>=insize && !retry && !stdin_mode) { if (verbose_mode) fprintf(LOG_FH,"(retry w/lossless) "); retry=1; goto retry_point; } retry=0; ratio=(insize-outsize)*100.0/insize; if (!quiet_mode || csv) fprintf(LOG_FH,csv ? "%ld,%ld,%0.2f," : "%ld --> %ld bytes (%0.2f%%), ",insize,outsize,ratio); average_count++; average_rate+=(ratio<0 ? 0.0 : ratio); if ((outsize < insize && ratio >= threshold) || force) { total_save+=(insize-outsize)/1024.0; if (!quiet_mode || csv) fprintf(LOG_FH,csv ? "optimized\n" : "optimized.\n"); if (noaction) continue; if (stdout_mode) { outfname=NULL; if (fwrite(outbuffer,outbuffersize,1,stdout) != 1) fatal("write failed to stdout"); } else { if (preserve_perms && !dest) { /* make backup of the original file */ snprintf(tmpfilename,sizeof(tmpfilename),"%s.jpegoptim.bak",newname); if (verbose_mode > 1 && !quiet_mode) fprintf(LOG_FH,"creating backup of original image as: %s\n",tmpfilename); if (file_exists(tmpfilename)) fatal("backup file already exists: %s",tmpfilename); if (copy_file(newname,tmpfilename)) fatal("failed to create backup of original file"); if ((outfile=fopen(newname,"wb"))==NULL) fatal("error opening output file: %s", newname); outfname=newname; } else { #ifdef HAVE_MKSTEMPS /* rely on mkstemps() to create us temporary file safely... */ snprintf(tmpfilename,sizeof(tmpfilename), "%sjpegoptim-%d-%d.XXXXXX.tmp", tmpdir, (int)getuid(), (int)getpid()); if ((tmpfd = mkstemps(tmpfilename,4)) < 0) fatal("error creating temp file: mkstemps() failed"); if ((outfile=fdopen(tmpfd,"wb"))==NULL) #else /* if platform is missing mkstemps(), try to create at least somewhat "safe" temp file... */ snprintf(tmpfilename,sizeof(tmpfilename), "%sjpegoptim-%d-%d.%d.tmp", tmpdir, (int)getuid(), (int)getpid(),time(NULL)); tmpfd=0; if ((outfile=fopen(tmpfilename,"wb"))==NULL) #endif fatal("error opening temporary file: %s",tmpfilename); outfname=tmpfilename; } if (verbose_mode > 1 && !quiet_mode) fprintf(LOG_FH,"writing %lu bytes to file: %s\n", (long unsigned int)outbuffersize, outfname); if (fwrite(outbuffer,outbuffersize,1,outfile) != 1) fatal("write failed to file: %s", outfname); fclose(outfile); } if (outfname) { if (preserve_mode) { /* preserve file modification time */ struct utimbuf time_save; time_save.actime=file_stat.st_atime; time_save.modtime=file_stat.st_mtime; if (utime(outfname,&time_save) != 0) warn("failed to reset output file time/date"); } if (preserve_perms && !dest) { /* original file was already replaced, remove backup... */ if (delete_file(tmpfilename)) warn("failed to remove backup file: %s",tmpfilename); } else { /* make temp file to be the original file... */ /* preserve file mode */ if (chmod(outfname,(file_stat.st_mode & 0777)) != 0) warn("failed to set output file mode"); /* preserve file group (and owner if run by root) */ if (chown(outfname, (geteuid()==0 ? file_stat.st_uid : -1), file_stat.st_gid) != 0) warn("failed to reset output file group/owner"); if (verbose_mode > 1 && !quiet_mode) fprintf(LOG_FH,"renaming: %s to %s\n",outfname,newname); if (rename_file(outfname,newname)) fatal("cannot rename temp file"); } } } else { if (!quiet_mode || csv) fprintf(LOG_FH,csv ? "skipped\n" : "skipped.\n"); } } while (++i<argc && !stdin_mode); if (totals_mode && !quiet_mode) fprintf(LOG_FH,"Average ""compression"" (%ld files): %0.2f%% (%0.0fk)\n", average_count, average_rate/average_count, total_save); jpeg_destroy_decompress(&dinfo); jpeg_destroy_compress(&cinfo); return (decompress_err_count > 0 || compress_err_count > 0 ? 1 : 0);; }
int v4lconvert_decode_jpeg_libjpeg(struct v4lconvert_data *data, unsigned char *src, int src_size, unsigned char *dest, struct v4l2_format *fmt, unsigned int dest_pix_fmt) { unsigned int width = fmt->fmt.pix.width; unsigned int height = fmt->fmt.pix.height; int result = 0; /* libjpeg errors before decoding the first line should signal EAGAIN */ data->jerr_errno = EAGAIN; result = setjmp(data->jerr_jmp_state); if (result) { if (data->cinfo_initialized) jpeg_abort_decompress(&data->cinfo); errno = result; return -1; } init_libjpeg_cinfo(data); jpeg_mem_src(&data->cinfo, src, src_size); jpeg_read_header(&data->cinfo, TRUE); if (data->cinfo.image_width != width || data->cinfo.image_height != height) { V4LCONVERT_ERR("unexpected width / height in JPEG header" "expected: %ux%u, header: %ux%u\n", width, height, data->cinfo.image_width, data->cinfo.image_height); errno = EIO; return -1; } if (data->cinfo.num_components != 3) { V4LCONVERT_ERR("unexpected no components in JPEG: %d\n", data->cinfo.num_components); errno = EIO; return -1; } if (dest_pix_fmt == V4L2_PIX_FMT_RGB24 || dest_pix_fmt == V4L2_PIX_FMT_BGR24) { JSAMPROW row_pointer[1]; #ifdef JCS_EXTENSIONS if (dest_pix_fmt == V4L2_PIX_FMT_BGR24) data->cinfo.out_color_space = JCS_EXT_BGR; #endif row_pointer[0] = dest; jpeg_start_decompress(&data->cinfo); /* Make libjpeg errors report that we've got some data */ data->jerr_errno = EPIPE; while (data->cinfo.output_scanline < height) { jpeg_read_scanlines(&data->cinfo, row_pointer, 1); row_pointer[0] += 3 * width; } jpeg_finish_decompress(&data->cinfo); #ifndef JCS_EXTENSIONS if (dest_pix_fmt == V4L2_PIX_FMT_BGR24) v4lconvert_swap_rgb(dest, dest, width, height); #endif } else { int h_samp, v_samp; unsigned char *udest, *vdest; if (data->cinfo.max_h_samp_factor == 2 && data->cinfo.cur_comp_info[0]->h_samp_factor == 2 && data->cinfo.cur_comp_info[1]->h_samp_factor == 1 && data->cinfo.cur_comp_info[2]->h_samp_factor == 1) { h_samp = 2; #if 0 /* HDG: untested, disable for now */ } else if (data->cinfo.max_h_samp_factor == 1 && data->cinfo.cur_comp_info[0]->h_samp_factor == 1 && data->cinfo.cur_comp_info[1]->h_samp_factor == 1 && data->cinfo.cur_comp_info[2]->h_samp_factor == 1) { h_samp = 1; #endif } else { fprintf(stderr, "libv4lconvert: unsupported jpeg h-sampling " "factors %d:%d:%d, please report this to " "[email protected]\n", data->cinfo.cur_comp_info[0]->h_samp_factor, data->cinfo.cur_comp_info[1]->h_samp_factor, data->cinfo.cur_comp_info[2]->h_samp_factor); errno = EOPNOTSUPP; return -1; } if (data->cinfo.max_v_samp_factor == 2 && data->cinfo.cur_comp_info[0]->v_samp_factor == 2 && data->cinfo.cur_comp_info[1]->v_samp_factor == 1 && data->cinfo.cur_comp_info[2]->v_samp_factor == 1) { v_samp = 2; } else if (data->cinfo.max_v_samp_factor == 1 && data->cinfo.cur_comp_info[0]->v_samp_factor == 1 && data->cinfo.cur_comp_info[1]->v_samp_factor == 1 && data->cinfo.cur_comp_info[2]->v_samp_factor == 1) { v_samp = 1; } else { fprintf(stderr, "libv4lconvert: unsupported jpeg v-sampling " "factors %d:%d:%d, please report this to " "[email protected]\n", data->cinfo.cur_comp_info[0]->v_samp_factor, data->cinfo.cur_comp_info[1]->v_samp_factor, data->cinfo.cur_comp_info[2]->v_samp_factor); errno = EOPNOTSUPP; return -1; } /* We don't want any padding as that may overflow our dest */ if (width % (8 * h_samp) || height % (8 * v_samp)) { V4LCONVERT_ERR( "resolution is not a multiple of dctsize"); errno = EIO; return -1; } if (dest_pix_fmt == V4L2_PIX_FMT_YVU420) { vdest = dest + width * height; udest = vdest + (width * height) / 4; } else { udest = dest + width * height; vdest = udest + (width * height) / 4; } data->cinfo.raw_data_out = TRUE; data->cinfo.do_fancy_upsampling = FALSE; jpeg_start_decompress(&data->cinfo); /* Make libjpeg errors report that we've got some data */ data->jerr_errno = EPIPE; if (h_samp == 1) { result = decode_libjpeg_h_samp1(data, dest, udest, vdest, v_samp); } else { result = decode_libjpeg_h_samp2(data, dest, udest, vdest, v_samp); } if (result) jpeg_abort_decompress(&data->cinfo); else jpeg_finish_decompress(&data->cinfo); } return result; }