コード例 #1
0
static void write_jpeg(struct jpeg_compress_struct * cinfo, struct ImBuf * ibuf)
{
	JSAMPLE * buffer = NULL;
	JSAMPROW row_pointer[1];
	uchar * rect;
	int x, y;
	char neogeo[128];
	ImMetaData *iptr;
	char *text;

	jpeg_start_compress(cinfo, TRUE);

	strcpy(neogeo, "NeoGeo");
	ibuf_ftype = BIG_LONG(ibuf->ftype);
	
	memcpy(neogeo + 6, &ibuf_ftype, 4);
	jpeg_write_marker(cinfo, 0xe1, (JOCTET*) neogeo, 10);

	if(ibuf->metadata) {
		/* key + max value + "Blender" */
		text= MEM_mallocN(530, "stamp info read");
		iptr= ibuf->metadata;
		while(iptr) {
			if (!strcmp (iptr->key, "None")) {
				jpeg_write_marker(cinfo, JPEG_COM, (JOCTET *) iptr->value, strlen (iptr->value) + 1);
				goto next_stamp_info;
			}

			/*
			 * The JPEG format don't support a pair "key/value"
			 * like PNG, so we "encode" the stamp in a
			 * single string:
			 *	"Blender:key:value"
			 *
			 * The first "Blender" is a simple identify to help
			 * in the read process.
			 */
			sprintf (text, "Blender:%s:%s", iptr->key, iptr->value);
			jpeg_write_marker(cinfo, JPEG_COM, (JOCTET *) text, strlen (text)+1);
next_stamp_info:
			iptr = iptr->next;
		}
		MEM_freeN(text);
	}

	row_pointer[0] =
		MEM_mallocN(sizeof(JSAMPLE) *
					 cinfo->input_components *
					 cinfo->image_width, "jpeg row_pointer");

	for(y = ibuf->y - 1; y >= 0; y--){
		rect = (uchar *) (ibuf->rect + y * ibuf->x);
		buffer = row_pointer[0];

		switch(cinfo->in_color_space){
		case JCS_RGB:
			for (x = 0; x < ibuf->x; x++) {
				*buffer++ = rect[0];
				*buffer++ = rect[1];
				*buffer++ = rect[2];
				rect += 4;
			}
			break;
		case JCS_GRAYSCALE:
			for (x = 0; x < ibuf->x; x++) {
				*buffer++ = rect[0];
				rect += 4;
			}
			break;
		case JCS_UNKNOWN:
			memcpy(buffer, rect, 4 * ibuf->x);
			break;
			/* default was missing... intentional ? */
		default:
			; /* do nothing */
		}

		jpeg_write_scanlines(cinfo, row_pointer, 1);
	}

	jpeg_finish_compress(cinfo);
	MEM_freeN(row_pointer[0]);
}
コード例 #2
0
/*!
 *  pixWriteStreamJpeg()
 *
 *      Input:  stream
 *              pix  (8 or 32 bpp)
 *              quality  (1 - 100; 75 is default value; 0 is also default)
 *              progressive (0 for baseline sequential; 1 for progressive)
 *      Return: 0 if OK, 1 on error
 *
 *  Notes:
 *      (1) Under the covers, the library transforms rgb to a
 *          luminence-chromaticity triple, each component of which is
 *          also 8 bits, and compresses that.  It uses 2 Huffman tables,
 *          a higher resolution one (with more quantization levels)
 *          for luminosity and a lower resolution one for the chromas.
 *      (2) Progressive encoding gives better compression, at the
 *          expense of slower encoding and decoding.
 *      (3) Standard chroma subsampling is 2x2 on both the U and V
 *          channels.  For highest quality, use no subsampling.  This
 *          option is set by l_jpegSetNoChromaSampling(1).
 *      (4) There are three possibilities:
 *          * Grayscale image, no colormap: compress as 8 bpp image.
 *          * rgb full color image: copy each line into the color
 *            line buffer, and compress as three 8 bpp images.
 *          * 8 bpp colormapped image: convert each line to three
 *            8 bpp line images in the color line buffer, and
 *            compress as three 8 bpp images.
 *      (5) The only valid pixel depths in leptonica are 1, 2, 4, 8, 16
 *          and 32 bpp.  However, it is possible, and in some cases desirable,
 *          to write out a jpeg file using an rgb pix that has 24 bpp.
 *          This can be created by appending the raster data for a 24 bpp
 *          image (with proper scanline padding) directly to a 24 bpp
 *          pix that was created without a data array.  See note in
 *          pixWriteStreamPng() for an example.
 */
l_int32
pixWriteStreamJpeg(FILE    *fp,
                   PIX     *pix,
                   l_int32  quality,
                   l_int32  progressive)
{
l_uint8                      byteval;
l_int32                      xres, yres;
l_int32                      i, j, k;
l_int32                      w, h, d, wpl, spp, colorflg, rowsamples;
l_int32                     *rmap, *gmap, *bmap;
l_uint32                    *ppixel, *line, *data;
JSAMPROW                     rowbuffer;
PIXCMAP                     *cmap;
struct jpeg_compress_struct  cinfo;
struct jpeg_error_mgr        jerr;
const char                  *text;

    PROCNAME("pixWriteStreamJpeg");

    if (!fp)
        return ERROR_INT("stream not open", procName, 1);
    if (!pix)
        return ERROR_INT("pix not defined", procName, 1);
    rewind(fp);

    if (setjmp(jpeg_jmpbuf)) {
        FREE(rowbuffer);
        if (colorflg == 1) {
            FREE(rmap);
            FREE(gmap);
            FREE(bmap);
        }
        return ERROR_INT("internal jpeg error", procName, 1);
    }

    rowbuffer = NULL;
    rmap = NULL;
    gmap = NULL;
    bmap = NULL;
    pixGetDimensions(pix, &w, &h, &d);
    if (d != 8 && d != 24 && d != 32)
        return ERROR_INT("bpp must be 8, 24 or 32", procName, 1);

    if (quality <= 0)
        quality = 75;  /* default */

    if (d == 32 || d == 24)
        colorflg = 2;    /* rgb; no colormap */
    else if ((cmap = pixGetColormap(pix)) == NULL)
        colorflg = 0;    /* 8 bpp grayscale; no colormap */
    else {
        colorflg = 1;    /* 8 bpp; colormap */
        pixcmapToArrays(cmap, &rmap, &gmap, &bmap);
    }

    cinfo.err = jpeg_std_error(&jerr);
    jerr.error_exit = jpeg_error_do_not_exit; /* catch error; do not exit! */

    jpeg_create_compress(&cinfo);
    jpeg_stdio_dest(&cinfo, fp);

    cinfo.image_width  = w;
    cinfo.image_height = h;

    if (colorflg == 0) {
        cinfo.input_components = 1;
        cinfo.in_color_space = JCS_GRAYSCALE;
    }
    else {  /* colorflg == 1 or 2 */
        cinfo.input_components = 3;
        cinfo.in_color_space = JCS_RGB;
    }

    jpeg_set_defaults(&cinfo);

        /* Setting optimize_coding to TRUE seems to improve compression
	 * by approx 2-4 percent, and increases comp time by approx 20%. */
    cinfo.optimize_coding = FALSE;

    xres = pixGetXRes(pix);
    yres = pixGetYRes(pix);
    if ((xres != 0) && (yres != 0)) {
        cinfo.density_unit = 1;  /* designates pixels per inch */
        cinfo.X_density = xres;
        cinfo.Y_density = yres;
    }

        /* Set the quality and progressive parameters */
    jpeg_set_quality(&cinfo, quality, TRUE);
    if (progressive) {
        jpeg_simple_progression(&cinfo);
    }

        /* Set the chroma subsampling parameters.  This is done in
         * YUV color space.  The Y (intensity) channel is never subsampled.
         * The standard subsampling is 2x2 on both the U and V channels.
         * Notation on this is confusing.  For a nice illustrations, see
         *   http://en.wikipedia.org/wiki/Chroma_subsampling
         * The standard subsampling is written as 4:2:0.
         * We allow high quality where there is no subsampling on the
         * chroma channels: denoted as 4:4:4.  */
    if (var_JPEG_NO_CHROMA_SAMPLING == 1) {
        cinfo.comp_info[0].h_samp_factor = 1;
        cinfo.comp_info[0].v_samp_factor = 1;
        cinfo.comp_info[1].h_samp_factor = 1;
        cinfo.comp_info[1].v_samp_factor = 1;
        cinfo.comp_info[2].h_samp_factor = 1;
        cinfo.comp_info[2].v_samp_factor = 1;
    }

    jpeg_start_compress(&cinfo, TRUE);

    if ((text = pixGetText(pix))) {
        jpeg_write_marker(&cinfo, JPEG_COM, (const JOCTET *)text, strlen(text));
    }

        /* Allocate row buffer */
    spp = cinfo.input_components;
    rowsamples = spp * w;
    if ((rowbuffer = (JSAMPROW)CALLOC(sizeof(JSAMPLE), rowsamples)) == NULL)
        return ERROR_INT("calloc fail for rowbuffer", procName, 1);

    data = pixGetData(pix);
    wpl  = pixGetWpl(pix);
    for (i = 0; i < h; i++) {
        line = data + i * wpl;
        if (colorflg == 0) {        /* 8 bpp gray */
            for (j = 0; j < w; j++)
                rowbuffer[j] = GET_DATA_BYTE(line, j);
        }
        else if (colorflg == 1) {  /* 8 bpp colormapped */
            for (j = 0; j < w; j++) {
                byteval = GET_DATA_BYTE(line, j);
                rowbuffer[3 * j + COLOR_RED] = rmap[byteval];
                rowbuffer[3 * j + COLOR_GREEN] = gmap[byteval];
                rowbuffer[3 * j + COLOR_BLUE] = bmap[byteval];
            }
        }
        else {  /* colorflg == 2 */
            if (d == 24) {  /* See note 4 above; special case of 24 bpp rgb */
                jpeg_write_scanlines(&cinfo, (JSAMPROW *)&line, 1);
            }
            else {  /* standard 32 bpp rgb */
                ppixel = line;
                for (j = k = 0; j < w; j++) {
                    rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_RED);
                    rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_GREEN);
                    rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_BLUE);
                    ppixel++;
                }
            }
        }
        if (d != 24)
            jpeg_write_scanlines(&cinfo, &rowbuffer, 1);
    }

    jpeg_finish_compress(&cinfo);

    FREE(rowbuffer);
    if (colorflg == 1) {
        FREE(rmap);
        FREE(gmap);
        FREE(bmap);
    }

    jpeg_destroy_compress(&cinfo);
    return 0;
}
コード例 #3
0
ファイル: cjpeg.c プロジェクト: vmdanilov/mozjpeg
int
main (int argc, char **argv)
{
  struct jpeg_compress_struct cinfo;
  struct jpeg_error_mgr jerr;
#ifdef PROGRESS_REPORT
  struct cdjpeg_progress_mgr progress;
#endif
  int file_index;
  cjpeg_source_ptr src_mgr;
  FILE * input_file;
  FILE * output_file = NULL;
  unsigned char *outbuffer = NULL;
  unsigned long outsize = 0;
  JDIMENSION num_scanlines;

  /* On Mac, fetch a command line. */
#ifdef USE_CCOMMAND
  argc = ccommand(&argv);
#endif

  progname = argv[0];
  if (progname == NULL || progname[0] == 0)
    progname = "cjpeg";         /* in case C library doesn't provide it */

  /* Initialize the JPEG compression object with default error handling. */
  cinfo.err = jpeg_std_error(&jerr);
  jpeg_create_compress(&cinfo);
  /* Add some application-specific error messages (from cderror.h) */
  jerr.addon_message_table = cdjpeg_message_table;
  jerr.first_addon_message = JMSG_FIRSTADDONCODE;
  jerr.last_addon_message = JMSG_LASTADDONCODE;

  /* Initialize JPEG parameters.
   * Much of this may be overridden later.
   * In particular, we don't yet know the input file's color space,
   * but we need to provide some value for jpeg_set_defaults() to work.
   */

  cinfo.in_color_space = JCS_RGB; /* arbitrary guess */
  jpeg_set_defaults(&cinfo);

  /* Scan command line to find file names.
   * It is convenient to use just one switch-parsing routine, but the switch
   * values read here are ignored; we will rescan the switches after opening
   * the input file.
   */

  file_index = parse_switches(&cinfo, argc, argv, 0, FALSE);

#ifdef TWO_FILE_COMMANDLINE
  if (!memdst) {
    /* Must have either -outfile switch or explicit output file name */
    if (outfilename == NULL) {
      if (file_index != argc-2) {
        fprintf(stderr, "%s: must name one input and one output file\n",
                progname);
        usage();
      }
      outfilename = argv[file_index+1];
    } else {
      if (file_index != argc-1) {
        fprintf(stderr, "%s: must name one input and one output file\n",
                progname);
        usage();
      }
    }
  }
#else
  /* Unix style: expect zero or one file name */
  if (file_index < argc-1) {
    fprintf(stderr, "%s: only one input file\n", progname);
    usage();
  }
#endif /* TWO_FILE_COMMANDLINE */

  /* Open the input file. */
  if (file_index < argc) {
    if ((input_file = fopen(argv[file_index], READ_BINARY)) == NULL) {
      fprintf(stderr, "%s: can't open %s\n", progname, argv[file_index]);
      exit(EXIT_FAILURE);
    }
  } else {
    /* default input file is stdin */
    input_file = read_stdin();
  }

  /* Open the output file. */
  if (outfilename != NULL) {
    if ((output_file = fopen(outfilename, WRITE_BINARY)) == NULL) {
      fprintf(stderr, "%s: can't open %s\n", progname, outfilename);
      exit(EXIT_FAILURE);
    }
  } else if (!memdst) {
    /* default output file is stdout */
    output_file = write_stdout();
  }

#ifdef PROGRESS_REPORT
  start_progress_monitor((j_common_ptr) &cinfo, &progress);
#endif

  /* Figure out the input file format, and set up to read it. */
  src_mgr = select_file_type(&cinfo, input_file);
  src_mgr->input_file = input_file;

  /* Read the input file header to obtain file size & colorspace. */
  (*src_mgr->start_input) (&cinfo, src_mgr);

  /* Now that we know input colorspace, fix colorspace-dependent defaults */
#if JPEG_RAW_READER
  if (!is_jpeg)
#endif
  jpeg_default_colorspace(&cinfo);

  /* Adjust default compression parameters by re-parsing the options */
  file_index = parse_switches(&cinfo, argc, argv, 0, TRUE);

  /* Specify data destination for compression */
#if JPEG_LIB_VERSION >= 80 || defined(MEM_SRCDST_SUPPORTED)
  if (memdst)
    jpeg_mem_dest(&cinfo, &outbuffer, &outsize);
  else
#endif
    jpeg_stdio_dest(&cinfo, output_file);

  /* Start compressor */
  jpeg_start_compress(&cinfo, TRUE);

  /* Copy metadata */
  if (is_jpeg) {
    jpeg_saved_marker_ptr marker;
    
    /* In the current implementation, we don't actually need to examine the
     * option flag here; we just copy everything that got saved.
     * But to avoid confusion, we do not output JFIF and Adobe APP14 markers
     * if the encoder library already wrote one.
     */
    for (marker = src_mgr->marker_list; marker != NULL; marker = marker->next) {
      if (cinfo.write_JFIF_header &&
          marker->marker == JPEG_APP0 &&
          marker->data_length >= 5 &&
          GETJOCTET(marker->data[0]) == 0x4A &&
          GETJOCTET(marker->data[1]) == 0x46 &&
          GETJOCTET(marker->data[2]) == 0x49 &&
          GETJOCTET(marker->data[3]) == 0x46 &&
          GETJOCTET(marker->data[4]) == 0)
        continue;                       /* reject duplicate JFIF */
      if (cinfo.write_Adobe_marker &&
          marker->marker == JPEG_APP0+14 &&
          marker->data_length >= 5 &&
          GETJOCTET(marker->data[0]) == 0x41 &&
          GETJOCTET(marker->data[1]) == 0x64 &&
          GETJOCTET(marker->data[2]) == 0x6F &&
          GETJOCTET(marker->data[3]) == 0x62 &&
          GETJOCTET(marker->data[4]) == 0x65)
        continue;                       /* reject duplicate Adobe */
      jpeg_write_marker(&cinfo, marker->marker, marker->data,
                        marker->data_length);
    }
  }
  
  /* Process data */
  while (cinfo.next_scanline < cinfo.image_height) {
    num_scanlines = (*src_mgr->get_pixel_rows) (&cinfo, src_mgr);
#if JPEG_RAW_READER
    if (is_jpeg)
      (void) jpeg_write_raw_data(&cinfo, src_mgr->plane_pointer, num_scanlines);
    else
#endif
    (void) jpeg_write_scanlines(&cinfo, src_mgr->buffer, num_scanlines);
  }

  /* Finish compression and release memory */
  (*src_mgr->finish_input) (&cinfo, src_mgr);
  jpeg_finish_compress(&cinfo);
  jpeg_destroy_compress(&cinfo);

  /* Close files, if we opened them */
  if (input_file != stdin)
    fclose(input_file);
  if (output_file != stdout && output_file != NULL)
    fclose(output_file);

#ifdef PROGRESS_REPORT
  end_progress_monitor((j_common_ptr) &cinfo);
#endif

  if (memdst) {
    fprintf(stderr, "Compressed size:  %lu bytes\n", outsize);
    if (outbuffer != NULL)
      free(outbuffer);
  }

  /* All done. */
  exit(jerr.num_warnings ? EXIT_WARNING : EXIT_SUCCESS);
  return 0;                     /* suppress no-return-value warnings */
}
コード例 #4
0
ファイル: JpegEncode.c プロジェクト: BrajeshKhare/Pillow
int
ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
{
    JPEGENCODERSTATE* context = (JPEGENCODERSTATE*) state->context;
    int ok;

    if (setjmp(context->error.setjmp_buffer)) {
	/* JPEG error handler */
	jpeg_destroy_compress(&context->cinfo);
	state->errcode = IMAGING_CODEC_BROKEN;
	return -1;
    }

    if (!state->state) {

	/* Setup compression context (very similar to the decoder) */
	context->cinfo.err = jpeg_std_error(&context->error.pub);
	context->error.pub.error_exit = error;
	jpeg_create_compress(&context->cinfo);
	jpeg_buffer_dest(&context->cinfo, &context->destination);

        context->extra_offset = 0;

	/* Ready to encode */
	state->state = 1;

    }

    /* Load the destination buffer */
    context->destination.pub.next_output_byte = buf;
    context->destination.pub.free_in_buffer = bytes;

    switch (state->state) {

    case 1:

	context->cinfo.image_width = state->xsize;
	context->cinfo.image_height = state->ysize;

	switch (state->bits) {
        case 8:
            context->cinfo.input_components = 1;
            context->cinfo.in_color_space = JCS_GRAYSCALE;
            break;
        case 24:
            context->cinfo.input_components = 3;
            if (strcmp(im->mode, "YCbCr") == 0)
                context->cinfo.in_color_space = JCS_YCbCr;
            else
                context->cinfo.in_color_space = JCS_RGB;
            break;
        case 32:
            context->cinfo.input_components = 4;
            context->cinfo.in_color_space = JCS_CMYK;
            break;
        default:
            state->errcode = IMAGING_CODEC_CONFIG;
            return -1;
	}

	/* Compressor configuration */
	jpeg_set_defaults(&context->cinfo);

	/* Use custom quantization tables */
	if (context->qtables) {
	    int i;
	    int quality = 100;
	    if (context->quality > 0) {
		quality = context->quality;
	    }
	    for (i = 0; i < context->qtablesLen; i++) {
		// TODO: Should add support for none baseline
		jpeg_add_quant_table(&context->cinfo, i, &context->qtables[i * DCTSIZE2],
				     quality, TRUE);
	    }
	} else if (context->quality > 0) {
	    jpeg_set_quality(&context->cinfo, context->quality, 1);
	}

	/* Set subsampling options */
	switch (context->subsampling)
	    {
	    case 0:  /* 1x1 1x1 1x1 (4:4:4) : None */
		{
		    context->cinfo.comp_info[0].h_samp_factor = 1;
		    context->cinfo.comp_info[0].v_samp_factor = 1;
		    context->cinfo.comp_info[1].h_samp_factor = 1;
		    context->cinfo.comp_info[1].v_samp_factor = 1;
		    context->cinfo.comp_info[2].h_samp_factor = 1;
		    context->cinfo.comp_info[2].v_samp_factor = 1;
		    break;
		}
	    case 1:  /* 2x1, 1x1, 1x1 (4:2:2) : Medium */
		{
		    context->cinfo.comp_info[0].h_samp_factor = 2;
		    context->cinfo.comp_info[0].v_samp_factor = 1;
		    context->cinfo.comp_info[1].h_samp_factor = 1;
		    context->cinfo.comp_info[1].v_samp_factor = 1;
		    context->cinfo.comp_info[2].h_samp_factor = 1;
		    context->cinfo.comp_info[2].v_samp_factor = 1;
		    break;
		}
	    case 2:  /* 2x2, 1x1, 1x1 (4:1:1) : High */
		{
		    context->cinfo.comp_info[0].h_samp_factor = 2;
		    context->cinfo.comp_info[0].v_samp_factor = 2;
		    context->cinfo.comp_info[1].h_samp_factor = 1;
		    context->cinfo.comp_info[1].v_samp_factor = 1;
		    context->cinfo.comp_info[2].h_samp_factor = 1;
		    context->cinfo.comp_info[2].v_samp_factor = 1;
		    break;
		}
	    default:
		{
		    /* Use the lib's default */
		    break;
		}
	    }
	if (context->progressive)
	    jpeg_simple_progression(&context->cinfo);
	context->cinfo.smoothing_factor = context->smooth;
	context->cinfo.optimize_coding = (boolean) context->optimize;
        if (context->xdpi > 0 && context->ydpi > 0) {
            context->cinfo.density_unit = 1; /* dots per inch */
            context->cinfo.X_density = context->xdpi;
            context->cinfo.Y_density = context->ydpi;
        }
	switch (context->streamtype) {
	case 1:
	    /* tables only -- not yet implemented */
	    state->errcode = IMAGING_CODEC_CONFIG;
	    return -1;
	case 2:
	    /* image only */
	    jpeg_suppress_tables(&context->cinfo, TRUE);
	    jpeg_start_compress(&context->cinfo, FALSE);
            /* suppress extra section */
            context->extra_offset = context->extra_size;
	    break;
	default:
	    /* interchange stream */
	    jpeg_start_compress(&context->cinfo, TRUE);
	    break;
	}
	state->state++;
	/* fall through */

    case 2:
        // check for exif len + 'APP1' header bytes
        if (context->rawExifLen + 5 >  context->destination.pub.free_in_buffer){
            break;
        }
        //add exif header
        if (context->rawExifLen > 0){
            jpeg_write_marker(&context->cinfo, JPEG_APP0+1,
                              (unsigned char*)context->rawExif, context->rawExifLen);
        }

	state->state++;
	/* fall through */
    case 3:

        if (context->extra) {
            /* copy extra buffer to output buffer */
            unsigned int n = context->extra_size - context->extra_offset;
            if (n > context->destination.pub.free_in_buffer)
                n = context->destination.pub.free_in_buffer;
            memcpy(context->destination.pub.next_output_byte,
                   context->extra + context->extra_offset, n);
            context->destination.pub.next_output_byte += n;
            context->destination.pub.free_in_buffer -= n;
            context->extra_offset += n;
            if (context->extra_offset >= context->extra_size)
                state->state++;
            else
                break;
        } else
	    state->state++;

    case 4:
        if (1024 > context->destination.pub.free_in_buffer){
            break;
        }

	ok = 1;
	while (state->y < state->ysize) {
	    state->shuffle(state->buffer,
			   (UINT8*) im->image[state->y + state->yoff] +
			   state->xoff * im->pixelsize, state->xsize);
	    ok = jpeg_write_scanlines(&context->cinfo, &state->buffer, 1);
	    if (ok != 1)
		break;
	    state->y++;
	}

	if (ok != 1)
	    break;
	state->state++;
	/* fall through */

    case 5:

	/* Finish compression */
	if (context->destination.pub.free_in_buffer < 100)
	    break;
	jpeg_finish_compress(&context->cinfo);

	/* Clean up */
        if (context->extra) {
            free(context->extra);
            context->extra = NULL;
        }
        if (context->rawExif) {
            free(context->rawExif);
            context->rawExif = NULL;
        }
        if (context->qtables) {
            free(context->qtables);
            context->qtables = NULL;
        }

	jpeg_destroy_compress(&context->cinfo);
	/* if (jerr.pub.num_warnings) return BROKEN; */
	state->errcode = IMAGING_CODEC_END;
	break;

    }

    /* Return number of bytes in output buffer */
    return context->destination.pub.next_output_byte - buf;

}
コード例 #5
0
ファイル: jpegio.c プロジェクト: 0ximDigital/appsScanner
/*!
 *  pixWriteStreamJpeg()
 *
 *      Input:  stream
 *              pixs  (any depth; cmap is OK)
 *              quality  (1 - 100; 75 is default value; 0 is also default)
 *              progressive (0 for baseline sequential; 1 for progressive)
 *      Return: 0 if OK, 1 on error
 *
 *  Notes:
 *      (1) Progressive encoding gives better compression, at the
 *          expense of slower encoding and decoding.
 *      (2) Standard chroma subsampling is 2x2 on both the U and V
 *          channels.  For highest quality, use no subsampling; this
 *          option is set by pixSetChromaSampling(pix, 0).
 *      (3) The only valid pixel depths in leptonica are 1, 2, 4, 8, 16
 *          and 32 bpp.  However, it is possible, and in some cases desirable,
 *          to write out a jpeg file using an rgb pix that has 24 bpp.
 *          This can be created by appending the raster data for a 24 bpp
 *          image (with proper scanline padding) directly to a 24 bpp
 *          pix that was created without a data array.
 *      (4) There are two compression paths in this function:
 *          * Grayscale image, no colormap: compress as 8 bpp image.
 *          * rgb full color image: copy each line into the color
 *            line buffer, and compress as three 8 bpp images.
 *      (5) Under the covers, the jpeg library transforms rgb to a
 *          luminance-chromaticity triple, each component of which is
 *          also 8 bits, and compresses that.  It uses 2 Huffman tables,
 *          a higher resolution one (with more quantization levels)
 *          for luminosity and a lower resolution one for the chromas.
 */
l_int32
pixWriteStreamJpeg(FILE    *fp,
                   PIX     *pixs,
                   l_int32  quality,
                   l_int32  progressive)
{
l_int32                      xres, yres;
l_int32                      i, j, k;
l_int32                      w, h, d, wpl, spp, colorflag, rowsamples;
l_uint32                    *ppixel, *line, *data;
JSAMPROW                     rowbuffer;
PIX                         *pix;
struct jpeg_compress_struct  cinfo;
struct jpeg_error_mgr        jerr;
const char                  *text;
jmp_buf                      jmpbuf;  /* must be local to the function */

    PROCNAME("pixWriteStreamJpeg");

    if (!fp)
        return ERROR_INT("stream not open", procName, 1);
    if (!pixs)
        return ERROR_INT("pixs not defined", procName, 1);
    if (quality <= 0)
        quality = 75;  /* default */

        /* If necessary, convert the pix so that it can be jpeg compressed.
         * The colormap is removed based on the source, so if the colormap
         * has only gray colors, the image will be compressed with spp = 1. */
    pixGetDimensions(pixs, &w, &h, &d);
    pix = NULL;
    if (pixGetColormap(pixs) != NULL) {
        L_INFO("removing colormap; may be better to compress losslessly\n",
               procName);
        pix = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC);
    } else if (d >= 8 && d != 16) {  /* normal case; no rewrite */
        pix = pixClone(pixs);
    } else if (d < 8 || d == 16) {
        L_INFO("converting from %d to 8 bpp\n", procName, d);
        pix = pixConvertTo8(pixs, 0);  /* 8 bpp, no cmap */
    } else {
        L_ERROR("unknown pix type with d = %d and no cmap\n", procName, d);
        return 1;
    }
    if (!pix)
        return ERROR_INT("pix not made", procName, 1);

    rewind(fp);
    rowbuffer = NULL;

        /* Modify the jpeg error handling to catch fatal errors  */
    cinfo.err = jpeg_std_error(&jerr);
    cinfo.client_data = (void *)&jmpbuf;
    jerr.error_exit = jpeg_error_catch_all_1;
    if (setjmp(jmpbuf)) {
        FREE(rowbuffer);
        pixDestroy(&pix);
        return ERROR_INT("internal jpeg error", procName, 1);
    }

        /* Initialize the jpeg structs for compression */
    jpeg_create_compress(&cinfo);
    jpeg_stdio_dest(&cinfo, fp);
    cinfo.image_width  = w;
    cinfo.image_height = h;

        /* Set the color space and number of components */
    d = pixGetDepth(pix);
    if (d == 8) {
        colorflag = 0;    /* 8 bpp grayscale; no cmap */
        cinfo.input_components = 1;
        cinfo.in_color_space = JCS_GRAYSCALE;
    } else {  /* d == 32 || d == 24 */
        colorflag = 1;    /* rgb */
        cinfo.input_components = 3;
        cinfo.in_color_space = JCS_RGB;
    }

    jpeg_set_defaults(&cinfo);

        /* Setting optimize_coding to TRUE seems to improve compression
         * by approx 2-4 percent, and increases comp time by approx 20%. */
    cinfo.optimize_coding = FALSE;

        /* Set resolution in pixels/in (density_unit: 1 = in, 2 = cm) */
    xres = pixGetXRes(pix);
    yres = pixGetYRes(pix);
    if ((xres != 0) && (yres != 0)) {
        cinfo.density_unit = 1;  /* designates pixels per inch */
        cinfo.X_density = xres;
        cinfo.Y_density = yres;
    }

        /* Set the quality and progressive parameters */
    jpeg_set_quality(&cinfo, quality, TRUE);
    if (progressive)
        jpeg_simple_progression(&cinfo);

        /* Set the chroma subsampling parameters.  This is done in
         * YUV color space.  The Y (intensity) channel is never subsampled.
         * The standard subsampling is 2x2 on both the U and V channels.
         * Notation on this is confusing.  For a nice illustrations, see
         *   http://en.wikipedia.org/wiki/Chroma_subsampling
         * The standard subsampling is written as 4:2:0.
         * We allow high quality where there is no subsampling on the
         * chroma channels: denoted as 4:4:4.  */
    if (pixs->special == L_NO_CHROMA_SAMPLING_JPEG) {
        cinfo.comp_info[0].h_samp_factor = 1;
        cinfo.comp_info[0].v_samp_factor = 1;
        cinfo.comp_info[1].h_samp_factor = 1;
        cinfo.comp_info[1].v_samp_factor = 1;
        cinfo.comp_info[2].h_samp_factor = 1;
        cinfo.comp_info[2].v_samp_factor = 1;
    }

    jpeg_start_compress(&cinfo, TRUE);

    if ((text = pixGetText(pix)))
        jpeg_write_marker(&cinfo, JPEG_COM, (const JOCTET *)text, strlen(text));

        /* Allocate row buffer */
    spp = cinfo.input_components;
    rowsamples = spp * w;
    if ((rowbuffer = (JSAMPROW)CALLOC(sizeof(JSAMPLE), rowsamples)) == NULL) {
        pixDestroy(&pix);
        return ERROR_INT("calloc fail for rowbuffer", procName, 1);
    }

    data = pixGetData(pix);
    wpl  = pixGetWpl(pix);
    for (i = 0; i < h; i++) {
        line = data + i * wpl;
        if (colorflag == 0) {        /* 8 bpp gray */
            for (j = 0; j < w; j++)
                rowbuffer[j] = GET_DATA_BYTE(line, j);
        } else {  /* colorflag == 1 */
            if (d == 24) {  /* See note 3 above; special case of 24 bpp rgb */
                jpeg_write_scanlines(&cinfo, (JSAMPROW *)&line, 1);
            } else {  /* standard 32 bpp rgb */
                ppixel = line;
                for (j = k = 0; j < w; j++) {
                    rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_RED);
                    rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_GREEN);
                    rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_BLUE);
                    ppixel++;
                }
            }
        }
        if (d != 24)
            jpeg_write_scanlines(&cinfo, &rowbuffer, 1);
    }
    jpeg_finish_compress(&cinfo);

    pixDestroy(&pix);
    FREE(rowbuffer);
    jpeg_destroy_compress(&cinfo);
    return 0;
}
コード例 #6
0
static int
write_exif( Write *write )
{
#ifdef HAVE_EXIF
	unsigned char *data;
	size_t data_length;
	unsigned int idl;
	ExifData *ed;

	/* Either parse from the embedded EXIF, or if there's none, make
	 * some fresh EXIF we can write the resolution to.
	 */
	if( vips_image_get_typeof( write->in, VIPS_META_EXIF_NAME ) ) {
		if( vips_image_get_blob( write->in, VIPS_META_EXIF_NAME, 
			(void *) &data, &data_length ) )
			return( -1 );

		if( !(ed = exif_data_new_from_data( data, data_length )) )
			return( -1 );
	}
	else  {
		ed = exif_data_new();

		exif_data_set_option( ed, 
			EXIF_DATA_OPTION_FOLLOW_SPECIFICATION );
		exif_data_set_data_type( ed, EXIF_DATA_TYPE_COMPRESSED );
		exif_data_set_byte_order( ed, EXIF_BYTE_ORDER_INTEL );
	
		/* Create the mandatory EXIF fields with default data.
		 */
		exif_data_fix( ed );
	}

	/* Update EXIF tags from the image metadata.
	 */
	vips_exif_update( ed, write->in );

	/* Update EXIF resolution from the vips image header.
	 */
	if( vips__set_exif_resolution( ed, write->in ) ) {
		exif_data_free( ed );
		return( -1 );
	}

	/* Update EXIF image dimensions from the vips image header.
	 */
	if( set_exif_dimensions( ed, write->in ) ) {
		exif_data_free( ed );
		return( -1 );
	}

	/* Update EXIF orientation from the vips image header.
	 */
	if( set_exif_orientation( ed, write->in ) ) {
		exif_data_free( ed );
		return( -1 );
	}

	/* Reserialise and write. exif_data_save_data() returns an int for some
	 * reason.
	 */
	exif_data_save_data( ed, &data, &idl );
	if( !idl ) {
		vips_error( "VipsJpeg", "%s", _( "error saving EXIF" ) );
		exif_data_free( ed );
		return( -1 );
	}
	data_length = idl;

#ifdef DEBUG
	printf( "write_exif: attaching %zd bytes of EXIF\n", data_length  );
#endif /*DEBUG*/

	exif_data_free( ed );
	jpeg_write_marker( &write->cinfo, JPEG_APP0 + 1, data, data_length );
	free( data );
#else /*!HAVE_EXIF*/
	/* No libexif ... just copy the embedded EXIF over.
	 */
	if( write_blob( write, VIPS_META_EXIF_NAME, JPEG_APP0 + 1 ) )
		return( -1 );
#endif /*!HAVE_EXIF*/

	return( 0 );
}
コード例 #7
0
ファイル: JPEG_band.cpp プロジェクト: hdfeos/gdal
CPLErr JPEG_Codec::CompressJPEG(buf_mgr &dst, buf_mgr &src)
#endif

{
    // The cinfo should stay open and reside in the DS, since it can be left initialized
    // It saves some time because it has the tables initialized
    struct jpeg_compress_struct cinfo;
    MRFJPEGStruct sJPEGStruct;
    struct jpeg_error_mgr sJErr;
    ILSize sz = img.pagesize;

    jpeg_destination_mgr jmgr;
    jmgr.next_output_byte = (JOCTET *)dst.buffer;
    jmgr.free_in_buffer = dst.size;
    jmgr.init_destination = init_or_terminate_destination;
    jmgr.empty_output_buffer = empty_output_buffer;
    jmgr.term_destination = init_or_terminate_destination;

    memset(&cinfo, 0, sizeof(cinfo));

    // Look at the source of this, some interesting tidbits
    cinfo.err = jpeg_std_error(&sJErr);
    sJErr.error_exit = errorExit;
    sJErr.emit_message = emitMessage;
    cinfo.client_data = (void *)&(sJPEGStruct);
    jpeg_create_compress(&cinfo);
    cinfo.dest = &jmgr;

    // The page specific info, size and color spaces
    cinfo.image_width = sz.x;
    cinfo.image_height = sz.y;
    cinfo.input_components = sz.c;
    switch (cinfo.input_components) {
    case 1:cinfo.in_color_space = JCS_GRAYSCALE; break;
    case 3:cinfo.in_color_space = JCS_RGB; break;  // Stored as YCbCr 4:2:0 by default
    default:
        cinfo.in_color_space = JCS_UNKNOWN; // 2, 4-10 bands
    }

    // Set all required fields and overwrite the ones we want to change
    jpeg_set_defaults(&cinfo);

    // Override certain settings
    jpeg_set_quality(&cinfo, img.quality, TRUE);
    cinfo.dct_method = JDCT_FLOAT; // Pretty fast and precise
    cinfo.optimize_coding = optimize; // Set "OPTIMIZE=TRUE" in OPTIONS, default for 12bit

    // Do we explicitly turn off the YCC color and downsampling?

    if (cinfo.in_color_space == JCS_RGB) {
        if (rgb) {  // Stored as RGB
            jpeg_set_colorspace(&cinfo, JCS_RGB);  // Huge files
        }
        else if (sameres) { // YCC, somewhat larger files with improved color spatial detail
            cinfo.comp_info[0].h_samp_factor = 1;
            cinfo.comp_info[0].v_samp_factor = 1;

            // Enabling these lines will make the color components use the same tables as Y, even larger file with slightly better color depth detail
            // cinfo.comp_info[1].quant_tbl_no = 0;
            // cinfo.comp_info[2].quant_tbl_no = 0;

            // cinfo.comp_info[1].dc_tbl_no = 0;
            // cinfo.comp_info[2].dc_tbl_no = 0;

            // cinfo.comp_info[1].ac_tbl_no = 0;
            // cinfo.comp_info[2].ac_tbl_no = 0;
        }
    }

    int linesize = cinfo.image_width * cinfo.input_components * ((cinfo.data_precision == 8) ? 1 : 2);
    JSAMPROW *rowp = (JSAMPROW *)CPLMalloc(sizeof(JSAMPROW)*sz.y);
    if (!rowp) {
        CPLError(CE_Failure, CPLE_AppDefined, "MRF: JPEG compression error");
        jpeg_destroy_compress(&cinfo);
        return CE_Failure;
    }

    for (int i = 0; i < sz.y; i++)
        rowp[i] = (JSAMPROW)(src.buffer + i*linesize);

    if (setjmp(sJPEGStruct.setjmpBuffer)) {
        CPLError(CE_Failure, CPLE_AppDefined, "MRF: JPEG compression error");
        jpeg_destroy_compress(&cinfo);
        CPLFree(rowp);
        return CE_Failure;
    }

    // Build a bitmaps of the black pixels
    // If there are any black pixels, write a compressed mask in APP3 "C3Mask" chunk

    // Mask is initialized to all pixels valid
    BitMask mask(sz.x, sz.y);
    storage_manager mbuffer = { CHUNK_NAME, CHUNK_NAME_SIZE };

    int nzeros = (cinfo.data_precision == 8) ?
        update_mask(mask, reinterpret_cast<GByte *>(src.buffer), sz.c) :
        update_mask(mask, reinterpret_cast<GUInt16 *>(src.buffer), sz.c);

    // In case we need to build a Zen chunk
    char *buffer = nullptr; 

    if (nzeros != 0) { // build the Zen chunk
        mbuffer.size = 2 * mask.size() + CHUNK_NAME_SIZE;
        buffer = reinterpret_cast<char *>(CPLMalloc(mbuffer.size));
        if (!buffer) {
            jpeg_destroy_compress(&cinfo);
            CPLFree(rowp);
            CPLError(CE_Failure, CPLE_OutOfMemory, "MRF: JPEG Zen mask compression");
            return CE_Failure;
        }

        memcpy(buffer, CHUNK_NAME, CHUNK_NAME_SIZE);
        mbuffer.buffer = buffer + CHUNK_NAME_SIZE;
        mbuffer.size -= CHUNK_NAME_SIZE;

        RLEC3Packer c3;
        mask.set_packer(&c3);
        if (!mask.store(&mbuffer)) {
            CPLError(CE_Failure, CPLE_AppDefined, "MRF: JPEG Zen mask compression");
            CPLFree(rowp);
            CPLFree(buffer);
            return CE_Failure;
        }

        // Change the buffer pointer to include the signature, on output the size is the compressed size
        mbuffer.buffer = buffer;
        mbuffer.size += CHUNK_NAME_SIZE;

        // Check that the size fits in one JPEG chunk
        if (mbuffer.size + 2 + CHUNK_NAME_SIZE > 65535) {
            // Should split it in multiple chunks, for now mark this tile as all data and emit a warning
            CPLError(CE_Warning, CPLE_NotSupported, "MRF: JPEG Zen mask too large");
            mbuffer.size = CHUNK_NAME_SIZE; // Write just the signature
        }
    }

    // Everything is ready
    jpeg_start_compress(&cinfo, TRUE);

    // Always write the Zen app chunk, App3
    jpeg_write_marker(&cinfo, JPEG_APP0 + 3,
      reinterpret_cast<JOCTET *>(mbuffer.buffer),
      static_cast<unsigned int>(mbuffer.size));

    jpeg_write_scanlines(&cinfo, rowp, sz.y);
    jpeg_finish_compress(&cinfo);
    jpeg_destroy_compress(&cinfo);

    CPLFree(rowp);
    CPLFree(buffer);     // Safe to call on null

    // Figure out the size
    dst.size -= jmgr.free_in_buffer;

    return CE_None;
}
コード例 #8
0
ファイル: gd_jpeg.cpp プロジェクト: 191919/hhvm
void gdImageJpegCtx (gdImagePtr im, gdIOCtx * outfile, int quality)
{
	struct jpeg_compress_struct cinfo;
	struct jpeg_error_mgr jerr;
	int i, j, jidx;
	/* volatile so we can gdFree it on return from longjmp */
	volatile JSAMPROW row = 0;
	JSAMPROW rowptr[1];
	jmpbuf_wrapper jmpbufw;
	JDIMENSION nlines;
	char comment[255];

	memset (&cinfo, 0, sizeof (cinfo));
	memset (&jerr, 0, sizeof (jerr));

	cinfo.err = jpeg_std_error (&jerr);
	cinfo.client_data = &jmpbufw;
	if (setjmp (jmpbufw.jmpbuf) != 0) {
		/* we're here courtesy of longjmp */
		if (row) {
			gdFree (row);
		}
		return;
	}

	cinfo.err->error_exit = fatal_jpeg_error;

	jpeg_create_compress (&cinfo);

	cinfo.image_width = im->sx;
	cinfo.image_height = im->sy;
	cinfo.input_components = 3;	/* # of color components per pixel */
	cinfo.in_color_space = JCS_RGB;	/* colorspace of input image */
	jpeg_set_defaults (&cinfo);
	if (quality >= 0) {
		jpeg_set_quality (&cinfo, quality, TRUE);
	}

	/* If user requests interlace, translate that to progressive JPEG */
	if (gdImageGetInterlaced (im)) {
		jpeg_simple_progression (&cinfo);
	}

	jpeg_gdIOCtx_dest (&cinfo, outfile);

	row = (JSAMPROW) safe_emalloc(cinfo.image_width * cinfo.input_components, sizeof(JSAMPLE), 0);
	memset(row, 0, cinfo.image_width * cinfo.input_components * sizeof(JSAMPLE));
	rowptr[0] = row;

	jpeg_start_compress (&cinfo, TRUE);

	if (quality >= 0) {
		snprintf(comment, sizeof(comment)-1, "CREATOR: gd-jpeg v%s (using IJG JPEG v%d), quality = %d\n", GD_JPEG_VERSION, JPEG_LIB_VERSION, quality);
	} else {
		snprintf(comment, sizeof(comment)-1, "CREATOR: gd-jpeg v%s (using IJG JPEG v%d), default quality\n", GD_JPEG_VERSION, JPEG_LIB_VERSION);
	}
	jpeg_write_marker (&cinfo, JPEG_COM, (unsigned char *) comment, (unsigned int) strlen (comment));
	if (im->trueColor) {

#if BITS_IN_JSAMPLE == 12
		php_gd_error("gd-jpeg: error: jpeg library was compiled for 12-bit precision. This is mostly useless, because JPEGs on the web are 8-bit and such versions of the jpeg library won't read or write them. GD doesn't support these unusual images. Edit your jmorecfg.h file to specify the correct precision and completely 'make clean' and 'make install' libjpeg again. Sorry");
		goto error;
#endif /* BITS_IN_JSAMPLE == 12 */

		for (i = 0; i < im->sy; i++) {
			for (jidx = 0, j = 0; j < im->sx; j++) {
				int val = im->tpixels[i][j];

				row[jidx++] = gdTrueColorGetRed (val);
				row[jidx++] = gdTrueColorGetGreen (val);
				row[jidx++] = gdTrueColorGetBlue (val);
			}

			nlines = jpeg_write_scanlines (&cinfo, rowptr, 1);
			if (nlines != 1) {
				php_gd_error_ex(E_WARNING, "gd_jpeg: warning: jpeg_write_scanlines returns %u -- expected 1", nlines);
			}
		}
	} else {
		for (i = 0; i < im->sy; i++) {
			for (jidx = 0, j = 0; j < im->sx; j++) {
				int idx = im->pixels[i][j];

				/* NB: Although gd RGB values are ints, their max value is
				 * 255 (see the documentation for gdImageColorAllocate())
				 * -- perfect for 8-bit JPEG encoding (which is the norm)
				 */
#if BITS_IN_JSAMPLE == 8
				row[jidx++] = im->red[idx];
				row[jidx++] = im->green[idx];
				row[jidx++] = im->blue[idx];
#elif BITS_IN_JSAMPLE == 12
				row[jidx++] = im->red[idx] << 4;
				row[jidx++] = im->green[idx] << 4;
				row[jidx++] = im->blue[idx] << 4;
#else
#error IJG JPEG library BITS_IN_JSAMPLE value must be 8 or 12
#endif
			}

			nlines = jpeg_write_scanlines (&cinfo, rowptr, 1);
			if (nlines != 1) {
				php_gd_error_ex(E_WARNING, "gd_jpeg: warning: jpeg_write_scanlines returns %u -- expected 1", nlines);
			}
		}
	}

	jpeg_finish_compress (&cinfo);
	jpeg_destroy_compress (&cinfo);
	gdFree (row);
}
コード例 #9
0
size_t Jpeg::Leanify(size_t size_leanified /*= 0*/) {
  struct jpeg_decompress_struct srcinfo;
  struct jpeg_compress_struct dstinfo;
  struct jpeg_error_mgr jsrcerr, jdsterr;

  srcinfo.err = jpeg_std_error(&jsrcerr);
  jsrcerr.error_exit = mozjpeg_error_handler;
  if (setjmp(setjmp_buffer)) {
    jpeg_destroy_compress(&dstinfo);
    jpeg_destroy_decompress(&srcinfo);

    return Format::Leanify(size_leanified);
  }

  jpeg_create_decompress(&srcinfo);

  dstinfo.err = jpeg_std_error(&jdsterr);
  jdsterr.error_exit = mozjpeg_error_handler;

  jpeg_create_compress(&dstinfo);

  if (is_verbose) {
    dstinfo.err->trace_level++;
  }
  if (is_fast) {
    jpeg_c_set_int_param(&dstinfo, JINT_COMPRESS_PROFILE, JCP_FASTEST);
  }

  /* Specify data source for decompression */
  jpeg_mem_src(&srcinfo, fp_, size_);

  // Always save exif to show warning if orientation might change.
  jpeg_save_markers(&srcinfo, JPEG_APP0 + 1, 0xFFFF);
  if (keep_icc_profile_ || keep_all_metadata_) {
    jpeg_save_markers(&srcinfo, JPEG_APP0 + 2, 0xFFFF);
  }
  if (keep_all_metadata_) {
    // Save the rest APPn markers.
    for (int i = 3; i < 16; i++)
      jpeg_save_markers(&srcinfo, JPEG_APP0 + i, 0xFFFF);
    // Save comments.
    jpeg_save_markers(&srcinfo, JPEG_COM, 0xFFFF);
  }

  (void)jpeg_read_header(&srcinfo, true);

  /* Read source file as DCT coefficients */
  auto coef_arrays = jpeg_read_coefficients(&srcinfo);

  /* Initialize destination compression parameters from source values */
  jpeg_copy_critical_parameters(&srcinfo, &dstinfo);

  // use arithmetic coding if input file is arithmetic coded
  if (srcinfo.arith_code) {
    dstinfo.arith_code = true;
    dstinfo.optimize_coding = false;
  } else {
    dstinfo.optimize_coding = true;
  }

  uint8_t* outbuffer = nullptr;
  unsigned long outsize = 0;
  /* Specify data destination for compression */
  jpeg_mem_dest(&dstinfo, &outbuffer, &outsize);

  /* Start compressor (note no image data is actually written here) */
  jpeg_write_coefficients(&dstinfo, coef_arrays);

  for (auto marker = srcinfo.marker_list; marker; marker = marker->next) {
    if (marker->marker == JPEG_APP0 + 1 && !keep_exif_ && !keep_all_metadata_) {
      // Tag number: 0x0112, data format: unsigned short(3), number of components: 1
      const uint8_t kExifOrientation[] = { 0x12, 0x01, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00 };
      const uint8_t kExifOrientationMotorola[] = { 0x01, 0x12, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01 };
      uint8_t* start = marker->data;
      uint8_t* end = start + marker->data_length;
      if (std::search(start, end, kExifOrientation, std::end(kExifOrientation)) != end ||
          std::search(start, end, kExifOrientationMotorola, std::end(kExifOrientationMotorola)) != end) {
        std::cout << "Warning: The Exif being removed contains orientation data, result image might have wrong "
                     "orientation, use --keep-exif to keep Exif."
                  << std::endl;
      }
      continue;
    }
    jpeg_write_marker(&dstinfo, marker->marker, marker->data, marker->data_length);
  }

  /* Finish compression and release memory */
  jpeg_finish_compress(&dstinfo);

  (void)jpeg_finish_decompress(&srcinfo);
  jpeg_destroy_decompress(&srcinfo);

  fp_ -= size_leanified;
  // use mozjpeg result if it's smaller than original
  if (outsize < size_) {
    memcpy(fp_, outbuffer, outsize);
    size_ = outsize;
  } else {
    memmove(fp_, fp_ + size_leanified, size_);
  }

  jpeg_destroy_compress(&dstinfo);

  return size_;
}
コード例 #10
0
ファイル: im_format_jpeg.cpp プロジェクト: Vulcanior/IUP
int imFileFormatJPEG::WriteImageInfo()
{
  this->file_color_mode = imColorModeSpace(this->user_color_mode);
  this->file_color_mode |= IM_TOPDOWN;

  if (imColorModeDepth(this->file_color_mode) > 1)
    this->file_color_mode |= IM_PACKED;

  this->file_data_type = IM_BYTE;

  /* Step 3: set parameters for compression */
  this->cinfo.image_width = this->width;   /* image width and height, in pixels */
  this->cinfo.image_height = this->height;

  this->cinfo.input_components = imColorModeDepth(this->file_color_mode);

  switch (imColorModeSpace(this->user_color_mode))
  {
  case IM_BINARY:
    this->convert_bpp = -1; // expand 1 to 255
  case IM_GRAY:
    this->cinfo.in_color_space = JCS_GRAYSCALE;
    break;
  case IM_RGB:   
    this->cinfo.in_color_space = JCS_RGB;
    break;
  case IM_CMYK:
    this->cinfo.in_color_space = JCS_CMYK;
    break;
  case IM_YCBCR:
    this->cinfo.in_color_space = JCS_YCbCr;
    break;
  default:
    this->cinfo.in_color_space = JCS_UNKNOWN;
    break;
  }

  if (setjmp(this->jerr.setjmp_buffer)) 
    return IM_ERR_ACCESS;

  jpeg_set_defaults(&this->cinfo);

  imAttribTable* attrib_table = AttribTable();

  int* auto_ycbcr = (int*)attrib_table->Get("AutoYCbCr");
  if (auto_ycbcr && *auto_ycbcr == 0 &&
      this->cinfo.in_color_space == JCS_RGB)
  {
    jpeg_set_colorspace(&this->cinfo, JCS_RGB);
  }

  int* interlaced = (int*)attrib_table->Get("Interlaced");
  if (interlaced && *interlaced)
    jpeg_simple_progression(&this->cinfo);

  int* quality = (int*)attrib_table->Get("JPEGQuality");
  if (quality)
    jpeg_set_quality(&this->cinfo, *quality, TRUE);

  char* res_unit = (char*)attrib_table->Get("ResolutionUnit");
  if (res_unit)
  {
    float* xres = (float*)attrib_table->Get("XResolution");
    float* yres = (float*)attrib_table->Get("YResolution");

    if (xres && yres)
    {
      if (imStrEqual(res_unit, "DPI"))
        this->cinfo.density_unit = 1;
      else
        this->cinfo.density_unit = 2;

      this->cinfo.X_density = (UINT16)*xres;
      this->cinfo.Y_density = (UINT16)*yres;
    }
  }

  /* Step 4: Start compressor */
  jpeg_start_compress(&this->cinfo, TRUE);

  int desc_size;
  char* desc = (char*)attrib_table->Get("Description", NULL, &desc_size);
  if (desc)
    jpeg_write_marker(&this->cinfo, JPEG_COM, (JOCTET*)desc, desc_size-1);

#ifdef USE_EXIF
  iWriteExifAttrib(attrib_table);
#endif

  return IM_ERR_NONE;
}
コード例 #11
0
static gboolean
real_save_jpeg (GdkPixbuf          *pixbuf,
		gchar             **keys,
		gchar             **values,
		GError            **error,
		gboolean            to_callback,
		FILE               *f,
		GdkPixbufSaveFunc   save_func,
		gpointer            user_data)
{
        /* FIXME error handling is broken */
        
       struct jpeg_compress_struct cinfo;
       guchar *buf = NULL;
       guchar *ptr;
       guchar *pixels = NULL;
       JSAMPROW *jbuf;
       int y = 0;
       volatile int quality = 75; /* default; must be between 0 and 100 */
       int i, j;
       int w, h = 0;
       int rowstride = 0;
       int n_channels;
       struct error_handler_data jerr;
       ToFunctionDestinationManager to_callback_destmgr;
       gchar *icc_profile = NULL;
       gchar *data;
       gint retval = TRUE;
       gsize icc_profile_size = 0;

       to_callback_destmgr.buffer = NULL;

       if (keys && *keys) {
               gchar **kiter = keys;
               gchar **viter = values;

               while (*kiter) {
                       if (strcmp (*kiter, "quality") == 0) {
                               char *endptr = NULL;
                               quality = strtol (*viter, &endptr, 10);

                               if (endptr == *viter) {
                                       g_set_error (error,
                                                    GDK_PIXBUF_ERROR,
                                                    GDK_PIXBUF_ERROR_BAD_OPTION,
                                                    _("JPEG quality must be a value between 0 and 100; value '%s' could not be parsed."),
                                                    *viter);

                                       retval = FALSE;
                                       goto cleanup;
                               }
                               
                               if (quality < 0 ||
                                   quality > 100) {
                                       /* This is a user-visible error;
                                        * lets people skip the range-checking
                                        * in their app.
                                        */
                                       g_set_error (error,
                                                    GDK_PIXBUF_ERROR,
                                                    GDK_PIXBUF_ERROR_BAD_OPTION,
                                                    _("JPEG quality must be a value between 0 and 100; value '%d' is not allowed."),
                                                    quality);

                                       retval = FALSE;
                                       goto cleanup;
                               }
                       } else if (strcmp (*kiter, "icc-profile") == 0) {
                               /* decode from base64 */
                               icc_profile = (gchar*) g_base64_decode (*viter, &icc_profile_size);
                               if (icc_profile_size < 127) {
                                       /* This is a user-visible error */
                                       g_set_error (error,
                                                    GDK_PIXBUF_ERROR,
                                                    GDK_PIXBUF_ERROR_BAD_OPTION,
                                                    _("Color profile has invalid length '%u'."),
                                                    (guint) icc_profile_size);
                                       retval = FALSE;
                                       goto cleanup;
                               }
                       } else {
                               g_warning ("Unrecognized parameter (%s) passed to JPEG saver.", *kiter);
                       }
               
                       ++kiter;
                       ++viter;
               }
       }
       
       rowstride = gdk_pixbuf_get_rowstride (pixbuf);
       n_channels = gdk_pixbuf_get_n_channels (pixbuf);

       w = gdk_pixbuf_get_width (pixbuf);
       h = gdk_pixbuf_get_height (pixbuf);
       pixels = gdk_pixbuf_get_pixels (pixbuf);

       /* Allocate a small buffer to convert image data,
	* and a larger buffer if doing to_callback save.
	*/
       buf = g_try_malloc (w * 3 * sizeof (guchar));
       if (!buf) {
	       g_set_error_literal (error,
                                    GDK_PIXBUF_ERROR,
                                    GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
                                    _("Couldn't allocate memory for loading JPEG file"));
	       retval = FALSE;
	       goto cleanup;
       }
       if (to_callback) {
	       to_callback_destmgr.buffer = g_try_malloc (TO_FUNCTION_BUF_SIZE);
	       if (!to_callback_destmgr.buffer) {
		       g_set_error_literal (error,
                                            GDK_PIXBUF_ERROR,
                                            GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
                                            _("Couldn't allocate memory for loading JPEG file"));
		       retval = FALSE;
		       goto cleanup;
	       }
       }

       /* set up error handling */
       cinfo.err = jpeg_std_error (&(jerr.pub));
       jerr.pub.error_exit = fatal_error_handler;
       jerr.pub.output_message = output_message_handler;
       jerr.error = error;
       
       if (sigsetjmp (jerr.setjmp_buffer, 1)) {
               jpeg_destroy_compress (&cinfo);
	       retval = FALSE;
	       goto cleanup;
       }

       /* setup compress params */
       jpeg_create_compress (&cinfo);
       if (to_callback) {
	       to_callback_destmgr.pub.init_destination    = to_callback_init;
	       to_callback_destmgr.pub.empty_output_buffer = to_callback_empty_output_buffer;
	       to_callback_destmgr.pub.term_destination    = to_callback_terminate;
	       to_callback_destmgr.error = error;
	       to_callback_destmgr.save_func = save_func;
	       to_callback_destmgr.user_data = user_data;
	       cinfo.dest = (struct jpeg_destination_mgr*) &to_callback_destmgr;
       } else {
	       jpeg_stdio_dest (&cinfo, f);
       }
       cinfo.image_width      = w;
       cinfo.image_height     = h;
       cinfo.input_components = 3; 
       cinfo.in_color_space   = JCS_RGB;

       /* set up jepg compression parameters */
       jpeg_set_defaults (&cinfo);
       jpeg_set_quality (&cinfo, quality, TRUE);

       jpeg_start_compress (&cinfo, TRUE);

	/* write ICC profile data */
	if (icc_profile != NULL) {
		/* optimise for the common case where only one APP2 segment is required */
		if (icc_profile_size < 0xffef) {
			data = g_new (gchar, icc_profile_size + 14);
			memcpy (data, "ICC_PROFILE\000\001\001", 14);
			memcpy (data + 14, icc_profile, icc_profile_size);
			jpeg_write_marker (&cinfo, JPEG_APP0+2, (const JOCTET *) data, icc_profile_size + 14);
			g_free (data);
		} else {
			guint segments;
			guint size = 0xffef;
			guint offset;

			segments = (guint) ceilf ((gfloat) icc_profile_size / (gfloat) 0xffef);
			data = g_new (gchar, 0xffff);
			memcpy (data, "ICC_PROFILE\000", 12);
			data[13] = segments;
			for (i=0; i<=segments; i++) {
				data[12] = i;
				offset = 0xffef * i;

				/* last segment */
				if (i == segments)
					size = icc_profile_size % 0xffef;

				memcpy (data + 14, icc_profile + offset, size);
				jpeg_write_marker (&cinfo, JPEG_APP0+2, (const JOCTET *) data, size + 14);
			}
			g_free (data);
		}
	}

       /* get the start pointer */
       ptr = pixels;
       /* go one scanline at a time... and save */
       i = 0;
       while (cinfo.next_scanline < cinfo.image_height) {
               /* convert scanline from ARGB to RGB packed */
               for (j = 0; j < w; j++)
                       memcpy (&(buf[j*3]), &(ptr[i*rowstride + j*n_channels]), 3);

               /* write scanline */
               jbuf = (JSAMPROW *)(&buf);
               jpeg_write_scanlines (&cinfo, jbuf, 1);
               i++;
               y++;

       }

       /* finish off */
       jpeg_finish_compress (&cinfo);
       jpeg_destroy_compress(&cinfo);
cleanup:
	g_free (buf);
	g_free (to_callback_destmgr.buffer);
	g_free (icc_profile);
	return retval;
}
コード例 #12
0
ファイル: gd_jpeg.c プロジェクト: cmb69/libgd
/* returns 0 on success, 1 on failure */
static int _gdImageJpegCtx(gdImagePtr im, gdIOCtx *outfile, int quality)
{
	struct jpeg_compress_struct cinfo;
	struct jpeg_error_mgr jerr;
	int i, j, jidx;
	/* volatile so we can gdFree it on return from longjmp */
	volatile JSAMPROW row = 0;
	JSAMPROW rowptr[1];
	jmpbuf_wrapper jmpbufw;
	JDIMENSION nlines;
	char comment[255];

#ifdef JPEG_DEBUG
	gd_error_ex(GD_DEBUG, "gd-jpeg: gd JPEG version %s\n", GD_JPEG_VERSION);
	gd_error_ex(GD_DEBUG, "gd-jpeg: JPEG library version %d, %d-bit sample values\n", JPEG_LIB_VERSION, BITS_IN_JSAMPLE);
	if (!im->trueColor) {
		for(i = 0; i < im->colorsTotal; i++) {
			if(!im->open[i]) {
				gd_error_ex(GD_DEBUG, "gd-jpeg: gd colormap index %d: (%d, %d, %d)\n", i, im->red[i], im->green[i], im->blue[i]);
			}
		}
	}
#endif /* JPEG_DEBUG */

	memset(&cinfo, 0, sizeof(cinfo));
	memset(&jerr, 0, sizeof(jerr));

	cinfo.err = jpeg_std_error(&jerr);
	cinfo.client_data = &jmpbufw;

	if(setjmp(jmpbufw.jmpbuf) != 0) {
		/* we're here courtesy of longjmp */
		if(row) {
			gdFree(row);
		}
		return 1;
	}

	cinfo.err->emit_message = jpeg_emit_message;
	cinfo.err->error_exit = fatal_jpeg_error;

	jpeg_create_compress(&cinfo);

	cinfo.image_width = im->sx;
	cinfo.image_height = im->sy;
	cinfo.input_components = 3; /* # of color components per pixel */
	cinfo.in_color_space = JCS_RGB; /* colorspace of input image */

	jpeg_set_defaults(&cinfo);

	cinfo.density_unit = 1;
	cinfo.X_density = im->res_x;
	cinfo.Y_density = im->res_y;

	if(quality >= 0) {
		jpeg_set_quality(&cinfo, quality, TRUE);
		if (quality >= 90) {
			cinfo.comp_info[0].h_samp_factor = 1;
			cinfo.comp_info[0].v_samp_factor = 1;
		}
	}

	/* If user requests interlace, translate that to progressive JPEG */
	if(gdImageGetInterlaced(im)) {
#ifdef JPEG_DEBUG
		gd_error_ex(GD_DEBUG, "gd-jpeg: interlace set, outputting progressive JPEG image\n");
#endif
		jpeg_simple_progression(&cinfo);
	}

	jpeg_gdIOCtx_dest(&cinfo, outfile);

	row = (JSAMPROW)gdCalloc(1, cinfo.image_width * cinfo.input_components * sizeof(JSAMPLE));
	if(row == 0) {
		gd_error("gd-jpeg: error: unable to allocate JPEG row structure: gdCalloc returns NULL\n");
		jpeg_destroy_compress(&cinfo);
		return 1;
	}

	rowptr[0] = row;

	jpeg_start_compress(&cinfo, TRUE);

	sprintf(comment, "CREATOR: gd-jpeg v%s (using IJG JPEG v%d),", GD_JPEG_VERSION, JPEG_LIB_VERSION);

	if(quality >= 0) {
		sprintf (comment + strlen(comment), " quality = %d\n", quality);
	} else {
		strcat(comment + strlen(comment), " default quality\n");
	}

	jpeg_write_marker(&cinfo, JPEG_COM, (unsigned char *) comment, (unsigned int)strlen(comment));

	if(im->trueColor) {
#if BITS_IN_JSAMPLE == 12
		gd_error(
		        "gd-jpeg: error: jpeg library was compiled for 12-bit\n"
		        "precision. This is mostly useless, because JPEGs on the web are\n"
		        "8-bit and such versions of the jpeg library won't read or write\n"
		        "them. GD doesn't support these unusual images. Edit your\n"
		        "jmorecfg.h file to specify the correct precision and completely\n"
		        "'make clean' and 'make install' libjpeg again. Sorry.\n"
		       );
		goto error;
#endif /* BITS_IN_JSAMPLE == 12 */
		for(i = 0; i < im->sy; i++) {
			for(jidx = 0, j = 0; j < im->sx; j++) {
				int val = im->tpixels[i][j];
				row[jidx++] = gdTrueColorGetRed(val);
				row[jidx++] = gdTrueColorGetGreen(val);
				row[jidx++] = gdTrueColorGetBlue(val);
			}

			nlines = jpeg_write_scanlines(&cinfo, rowptr, 1);

			if(nlines != 1) {
				gd_error("gd_jpeg: warning: jpeg_write_scanlines returns %u -- expected 1\n", nlines);
			}
		}
	} else {
		for(i = 0; i < im->sy; i++) {
			for(jidx = 0, j = 0; j < im->sx; j++) {
				int idx = im->pixels[i][j];

				/*
				 * NB: Although gd RGB values are ints, their max value is
				 * 255 (see the documentation for gdImageColorAllocate())
				 * -- perfect for 8-bit JPEG encoding (which is the norm)
				 */
#if BITS_IN_JSAMPLE == 8
				row[jidx++] = im->red[idx];
				row[jidx++] = im->green[idx];
				row[jidx++] = im->blue[idx];
#elif BITS_IN_JSAMPLE == 12
				row[jidx++] = im->red[idx] << 4;
				row[jidx++] = im->green[idx] << 4;
				row[jidx++] = im->blue[idx] << 4;
#else
#error IJG JPEG library BITS_IN_JSAMPLE value must be 8 or 12
#endif
			}

			nlines = jpeg_write_scanlines(&cinfo, rowptr, 1);
			if(nlines != 1) {
				gd_error("gd_jpeg: warning: jpeg_write_scanlines"
				         " returns %u -- expected 1\n", nlines);
			}
		}
	}

	jpeg_finish_compress(&cinfo);
	jpeg_destroy_compress(&cinfo);
	gdFree(row);
	return 0;
}
コード例 #13
0
ファイル: jpegoptim.c プロジェクト: martinmoehler/jpegoptim
void write_markers(struct jpeg_decompress_struct *dinfo,
		   struct jpeg_compress_struct *cinfo)
{
  jpeg_saved_marker_ptr mrk;
  int write_marker;

  if (!cinfo || !dinfo) fatal("invalid call to write_markers()");

  mrk=dinfo->marker_list;
  while (mrk) {
    write_marker=0;

    /* check for markers to save... */

    if (save_com && mrk->marker == JPEG_COM) 
      write_marker++;
   
    if (save_iptc && mrk->marker == IPTC_JPEG_MARKER) 
      write_marker++;
    
    if (save_exif && mrk->marker == EXIF_JPEG_MARKER &&
	!memcmp(mrk->data,EXIF_IDENT_STRING,EXIF_IDENT_STRING_SIZE)) 
      write_marker++;
    
    if (save_icc && mrk->marker == ICC_JPEG_MARKER &&
	     !memcmp(mrk->data,ICC_IDENT_STRING,ICC_IDENT_STRING_SIZE)) 
      write_marker++;
   
    if (save_xmp && mrk->marker == XMP_JPEG_MARKER &&
	     !memcmp(mrk->data,XMP_IDENT_STRING,XMP_IDENT_STRING_SIZE)) 
      write_marker++;

    if (strip_none) write_marker++;


    /* libjpeg emits some markers automatically so skip these to avoid duplicates... */

    /* skip JFIF (APP0) marker */
    if ( mrk->marker == JPEG_APP0 && mrk->data_length >= 14 &&
	 mrk->data[0] == 0x4a &&
	 mrk->data[1] == 0x46 &&
	 mrk->data[2] == 0x49 &&
	 mrk->data[3] == 0x46 &&
	 mrk->data[4] == 0x00 ) 
      write_marker=0;

    /* skip Adobe (APP14) marker */
    if ( mrk->marker == JPEG_APP0+14 && mrk->data_length >= 12 &&
	 mrk->data[0] == 0x41 &&
	 mrk->data[1] == 0x64 &&
	 mrk->data[2] == 0x6f &&
	 mrk->data[3] == 0x62 &&
	 mrk->data[4] == 0x65 ) 
      write_marker=0;

    

    if (write_marker) 
      jpeg_write_marker(cinfo,mrk->marker,mrk->data,mrk->data_length);
    
    mrk=mrk->next;
  }
}
コード例 #14
0
ファイル: jpeg.c プロジェクト: gh2k/flam3
void
write_jpeg(FILE *file, unsigned char *image, int width, int height, flam3_img_comments *fpc) {
   struct jpeg_compress_struct info;
   struct jpeg_error_mgr jerr;
   size_t i;
   char *nick = getenv("nick");
   char *url = getenv("url");
   char *id = getenv("id");
   char *ai; /* For argi */
   int jpegcom_enable = argi("enable_jpeg_comments",1);

   char nick_string[64], url_string[128], id_string[128];
   char bv_string[64],ni_string[64],rt_string[64];
   char genome_string[65536], ver_string[64];
   
   /* Create the mandatory comment strings */
   snprintf(genome_string,65536,"flam3_genome: %s",fpc->genome);
   snprintf(bv_string,64,"flam3_error_rate: %s",fpc->badvals);
   snprintf(ni_string,64,"flam3_samples: %s",fpc->numiters);
   snprintf(rt_string,64,"flam3_time: %s",fpc->rtime);
   snprintf(ver_string,64,"flam3_version: %s",flam3_version());

   info.err = jpeg_std_error(&jerr);
   jpeg_create_compress(&info);
   jpeg_stdio_dest(&info, file);
   info.in_color_space = JCS_RGB;
   info.input_components = 3;
   info.image_width = width;
   info.image_height = height;
   jpeg_set_defaults(&info);

   if (getenv("jpeg")) {
      int quality = atoi(getenv("jpeg"));
	   jpeg_set_quality(&info, quality, TRUE);
   }
   
   jpeg_start_compress(&info, TRUE);
    
   /* Write comments to jpeg */
   if (jpegcom_enable==1) {
        jpeg_write_marker(&info, JPEG_COM, (unsigned char *)ver_string, (int)strlen(ver_string));

        if (0 != nick) {
            snprintf(nick_string,64,"flam3_nickname: %s",nick);
            jpeg_write_marker(&info, JPEG_COM, (unsigned char *)nick_string, (int)strlen(nick_string));
        }

        if (0 != url) {
            snprintf(url_string,128,"flam3_url: %s",url);
            jpeg_write_marker(&info, JPEG_COM, (unsigned char *)url_string, (int)strlen(url_string));
        }
        
        if (0 != id) {
            snprintf(id_string,128,"flam3_id: %s",id);
            jpeg_write_marker(&info, JPEG_COM, (unsigned char *)id_string, (int)strlen(id_string));
        }

        jpeg_write_marker(&info, JPEG_COM, (unsigned char *)bv_string, (int)strlen(bv_string));
        jpeg_write_marker(&info, JPEG_COM, (unsigned char *)ni_string, (int)strlen(ni_string));
        jpeg_write_marker(&info, JPEG_COM, (unsigned char *)rt_string, (int)strlen(rt_string));
        jpeg_write_marker(&info, JPEG_COM, (unsigned char *)genome_string, (int)strlen(genome_string));
    }

    for (i = 0; i < height; i++) {
	JSAMPROW row_pointer[1];
	row_pointer[0] = (unsigned char *) image + (3 * width * i);
	jpeg_write_scanlines(&info, row_pointer, 1);
    }
    jpeg_finish_compress(&info);
    jpeg_destroy_compress(&info);
}
コード例 #15
0
ファイル: jpeg.c プロジェクト: CarVac/darktable
int write_image(dt_imageio_module_data_t *jpg_tmp, const char *filename, const void *in_tmp, void *exif,
                int exif_len, int imgid, int num, int total)
{
  dt_imageio_jpeg_t *jpg = (dt_imageio_jpeg_t *)jpg_tmp;
  const uint8_t *in = (const uint8_t *)in_tmp;
  struct dt_imageio_jpeg_error_mgr jerr;

  jpg->cinfo.err = jpeg_std_error(&jerr.pub);
  jerr.pub.error_exit = dt_imageio_jpeg_error_exit;
  if(setjmp(jerr.setjmp_buffer))
  {
    jpeg_destroy_compress(&(jpg->cinfo));
    return 1;
  }
  jpeg_create_compress(&(jpg->cinfo));
  FILE *f = fopen(filename, "wb");
  if(!f) return 1;
  jpeg_stdio_dest(&(jpg->cinfo), f);

  jpg->cinfo.image_width = jpg->width;
  jpg->cinfo.image_height = jpg->height;
  jpg->cinfo.input_components = 3;
  jpg->cinfo.in_color_space = JCS_RGB;
  jpeg_set_defaults(&(jpg->cinfo));
  jpeg_set_quality(&(jpg->cinfo), jpg->quality, TRUE);
  if(jpg->quality > 90) jpg->cinfo.comp_info[0].v_samp_factor = 1;
  if(jpg->quality > 92) jpg->cinfo.comp_info[0].h_samp_factor = 1;
  if(jpg->quality > 95) jpg->cinfo.dct_method = JDCT_FLOAT;
  if(jpg->quality < 50) jpg->cinfo.dct_method = JDCT_IFAST;
  if(jpg->quality < 80) jpg->cinfo.smoothing_factor = 20;
  if(jpg->quality < 60) jpg->cinfo.smoothing_factor = 40;
  if(jpg->quality < 40) jpg->cinfo.smoothing_factor = 60;
  jpg->cinfo.optimize_coding = 1;

  // according to specs density_unit = 0, X_density = 1, Y_density = 1 should be fine and valid since it
  // describes an image with unknown unit and square pixels.
  // however, some applications (like the Telekom cloud thingy) seem to be confused by that, so let's set
  // these calues to the same as stored in exiv :/
  const int resolution = dt_conf_get_int("metadata/resolution");
  if(resolution > 0)
  {
    jpg->cinfo.density_unit = 1;
    jpg->cinfo.X_density = resolution;
    jpg->cinfo.Y_density = resolution;
  }
  else
  {
    jpg->cinfo.density_unit = 0;
    jpg->cinfo.X_density = 1;
    jpg->cinfo.Y_density = 1;
  }

  jpeg_start_compress(&(jpg->cinfo), TRUE);

  if(imgid > 0)
  {
    cmsHPROFILE out_profile = dt_colorspaces_get_output_profile(imgid)->profile;
    uint32_t len = 0;
    cmsSaveProfileToMem(out_profile, 0, &len);
    if(len > 0)
    {
      unsigned char buf[len];
      cmsSaveProfileToMem(out_profile, buf, &len);
      write_icc_profile(&(jpg->cinfo), buf, len);
    }
  }

  if(exif && exif_len > 0 && exif_len < 65534)
    jpeg_write_marker(&(jpg->cinfo), JPEG_APP0 + 1, exif, exif_len);

  uint8_t row[3 * jpg->width];
  const uint8_t *buf;
  while(jpg->cinfo.next_scanline < jpg->cinfo.image_height)
  {
    JSAMPROW tmp[1];
    buf = in + (size_t)jpg->cinfo.next_scanline * jpg->cinfo.image_width * 4;
    for(int i = 0; i < jpg->width; i++)
      for(int k = 0; k < 3; k++) row[3 * i + k] = buf[4 * i + k];
    tmp[0] = row;
    jpeg_write_scanlines(&(jpg->cinfo), tmp, 1);
  }
  jpeg_finish_compress(&(jpg->cinfo));
  jpeg_destroy_compress(&(jpg->cinfo));
  fclose(f);
  return 0;
}
コード例 #16
0
ファイル: im_vips2jpeg.c プロジェクト: aturcotte/libvips
static int
write_exif( Write *write )
{
	unsigned char *data;
	size_t data_length;
	unsigned int idl;
#ifdef HAVE_EXIF
	ExifData *ed;

	/* Either parse from the embedded EXIF, or if there's none, make
	 * some fresh EXIF we can write the resolution to.
	 */
	if( im_header_get_typeof( write->in, IM_META_EXIF_NAME ) ) {
		if( im_meta_get_blob( write->in, IM_META_EXIF_NAME, 
			(void *) &data, &data_length ) )
			return( -1 );

		if( !(ed = exif_data_new_from_data( data, data_length )) )
			return( -1 );
	}
	else 
		ed = exif_data_new();

	/* Update EXIF resolution from VIPS.
	 */
	if( set_exif_resolution( ed, write->in ) ) {
		exif_data_free( ed );
		return( -1 );
	}

	/* Reserialise and write. exif_data_save_data() returns an int for some
	 * reason.
	 */
	exif_data_save_data( ed, &data, &idl );
	if( !idl ) {
		im_error( "im_jpeg2vips", "%s", _( "error saving EXIF" ) );
		exif_data_free( ed );
		return( -1 );
	}
	data_length = idl;

#ifdef DEBUG
	printf( "im_vips2jpeg: attaching %zd bytes of EXIF\n", data_length  );
#endif /*DEBUG*/

	exif_data_free( ed );
	jpeg_write_marker( &write->cinfo, JPEG_APP0 + 1, data, data_length );
	free( data );
#else /*!HAVE_EXIF*/
	/* No libexif ... just copy the embedded EXIF over.
	 */
	if( im_header_get_typeof( write->in, IM_META_EXIF_NAME ) ) {
		if( im_meta_get_blob( write->in, IM_META_EXIF_NAME, 
			(void *) &data, &data_length ) )
			return( -1 );

#ifdef DEBUG
		printf( "im_vips2jpeg: attaching %d bytes of EXIF\n", 
			data_length  );
#endif /*DEBUG*/

		jpeg_write_marker( &write->cinfo, JPEG_APP0 + 1, 
			data, data_length );
	}
#endif /*!HAVE_EXIF*/

	return( 0 );
}
コード例 #17
0
ファイル: Codec_JPEG.cpp プロジェクト: DBCTRADO/TVTest
bool SaveJPEGFile(const ImageSaveInfo *pInfo)
{
	HANDLE hFile;
	struct jpeg_compress_struct jcomp;
	JPEGErrorInfo jerrinfo;
	JPEGDestinationInfo *pDstInfo;
	int Width,Height;
	volatile JSAMPROW pBuff=NULL;
	int nBytesPerLine,y;
	const BYTE *p;

	hFile=CreateFile(pInfo->pszFileName,GENERIC_WRITE,0,NULL,
									CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
	if (hFile==INVALID_HANDLE_VALUE)
		return false;
	jcomp.err=jpeg_std_error(&jerrinfo.jerr);
	jerrinfo.jerr.error_exit=JPEGErrorExit;
	jerrinfo.jerr.output_message=JPEGErrorMessage;
	if (setjmp(jerrinfo.jmpbuf)) {
		jpeg_destroy_compress(&jcomp);
		CloseHandle(hFile);
		if (pBuff!=NULL)
			delete [] pBuff;
		return false;
	}
	jpeg_create_compress(&jcomp);
	jcomp.dest=(struct jpeg_destination_mgr*)(jcomp.mem->alloc_small)(
				(j_common_ptr)&jcomp,JPOOL_IMAGE,sizeof(JPEGDestinationInfo));
	jcomp.dest->init_destination=JPEGInitDestination;
	jcomp.dest->empty_output_buffer=JPEGEmptyOutputBuffer;
	jcomp.dest->term_destination=JPEGTermDestination;
	pDstInfo=(JPEGDestinationInfo*)jcomp.dest;
	pDstInfo->hFile=hFile;
	Width=pInfo->pbmi->bmiHeader.biWidth;
	Height=abs(pInfo->pbmi->bmiHeader.biHeight);
	jcomp.image_width=Width;
	jcomp.image_height=Height;
	jcomp.input_components=3;
	jcomp.in_color_space=JCS_RGB;
	jpeg_set_defaults(&jcomp);
	jpeg_set_quality(&jcomp,_ttoi(pInfo->pszOption),TRUE);
	jcomp.optimize_coding=TRUE;
	// Progressive
	//jpeg_simple_progression(&jcomp);
	jpeg_start_compress(&jcomp,TRUE);
	if (pInfo->pszComment!=NULL) {
		/* コメントの書き込み */
#ifndef UNICODE
		jpeg_write_marker(&jcomp,JPEG_COM,(JOCTET*)pInfo->pszComment,
												lstrlen(pInfo->pszComment));
#else
		int Length;
		LPSTR pszComment;

		Length=WideCharToMultiByte(CP_ACP,0,pInfo->pszComment,-1,NULL,0,NULL,NULL);
		pszComment=new char[Length];
		WideCharToMultiByte(CP_ACP,0,pInfo->pszComment,-1,pszComment,Length,NULL,NULL);
		jpeg_write_marker(&jcomp,JPEG_COM,(JOCTET*)pszComment,Length-1);
		delete [] pszComment;
#endif
	}
	pBuff=new JSAMPLE[jcomp.image_width*jcomp.input_components];
	nBytesPerLine=DIB_ROW_BYTES(Width,pInfo->pbmi->bmiHeader.biBitCount);
	for (y=0;y<Height;y++) {
		p=static_cast<const BYTE*>(pInfo->pBits)+
			(pInfo->pbmi->bmiHeader.biHeight>0?(Height-1-y):y)*nBytesPerLine;
		CopyToRGB24(pBuff,p,pInfo->pbmi->bmiHeader.biBitCount,
												pInfo->pbmi->bmiColors,Width);
		jpeg_write_scanlines(&jcomp,(JSAMPARRAY)&pBuff,1);
	}
	delete [] pBuff;
	pBuff=NULL;
	jpeg_finish_compress(&jcomp);
	jpeg_destroy_compress(&jcomp);
	CloseHandle(hFile);
	return true;
}
コード例 #18
0
int 
NITFWriteJPEGBlock( GDALDataset *poSrcDS, FILE *fp,
                    int nBlockXOff, int nBlockYOff,
                    int nBlockXSize, int nBlockYSize,
                    int bProgressive, int nQuality,
                    const GByte* pabyAPP6, int nRestartInterval,
                    GDALProgressFunc pfnProgress, void * pProgressData )
{
    GDALDataType eDT = poSrcDS->GetRasterBand(1)->GetRasterDataType();
#if defined(JPEG_DUAL_MODE_8_12) && !defined(NITFWriteJPEGBlock)
    if( eDT == GDT_UInt16 )
    {
        return NITFWriteJPEGBlock_12(poSrcDS, fp,
                                     nBlockXOff, nBlockYOff,
                                     nBlockXSize, nBlockYSize,
                                     bProgressive, nQuality,
                                     pabyAPP6, nRestartInterval,
                                     pfnProgress, pProgressData );
    }
#endif

    int  nBands = poSrcDS->GetRasterCount();
    int  nXSize = poSrcDS->GetRasterXSize();
    int  nYSize = poSrcDS->GetRasterYSize();
    int  anBandList[3] = {1,2,3};

/* -------------------------------------------------------------------- */
/*      Initialize JPG access to the file.                              */
/* -------------------------------------------------------------------- */
    struct jpeg_compress_struct sCInfo;
    struct jpeg_error_mgr sJErr;
    
    sCInfo.err = jpeg_std_error( &sJErr );
    jpeg_create_compress( &sCInfo );

    jpeg_vsiio_dest( &sCInfo, fp );
    
    sCInfo.image_width = nBlockXSize;
    sCInfo.image_height = nBlockYSize;
    sCInfo.input_components = nBands;

    if( nBands == 1 )
    {
        sCInfo.in_color_space = JCS_GRAYSCALE;
    }
    else
    {
        sCInfo.in_color_space = JCS_RGB;
    }

    jpeg_set_defaults( &sCInfo );
    
#if defined(JPEG_LIB_MK1_OR_12BIT)
    if( eDT == GDT_UInt16 )
    {
        sCInfo.data_precision = 12;
    }
    else
    {
        sCInfo.data_precision = 8;
    }
#endif

    GDALDataType eWorkDT;
#ifdef JPEG_LIB_MK1
    sCInfo.bits_in_jsample = sCInfo.data_precision;
    eWorkDT = GDT_UInt16; /* Always force to 16 bit for JPEG_LIB_MK1 */
#else
    eWorkDT = eDT;
#endif

    sCInfo.write_JFIF_header = FALSE;

    /* Set the restart interval */
    if (nRestartInterval < 0)
    {
        /* nRestartInterval < 0 means that we will guess the value */
        /* so we set it at the maximum allowed by MIL-STD-188-198 */
        /* that is to say the number of MCU per row-block */
        nRestartInterval = nBlockXSize / 8;
    }

    if (nRestartInterval > 0)
        sCInfo.restart_interval = nRestartInterval;

    jpeg_set_quality( &sCInfo, nQuality, TRUE );

    if( bProgressive )
        jpeg_simple_progression( &sCInfo );

    jpeg_start_compress( &sCInfo, TRUE );

/* -------------------------------------------------------------------- */
/*    Emits APP6 NITF application segment (required by MIL-STD-188-198) */
/* -------------------------------------------------------------------- */
    if (pabyAPP6)
    {
        /* 0xe6 = APP6 marker */
        jpeg_write_marker( &sCInfo, 0xe6, (const JOCTET*) pabyAPP6, 23);
    }

/* -------------------------------------------------------------------- */
/*      Loop over image, copying image data.                            */
/* -------------------------------------------------------------------- */
    GByte 	*pabyScanline;
    CPLErr      eErr = CE_None;
    int         nWorkDTSize = GDALGetDataTypeSize(eWorkDT) / 8;

    pabyScanline = (GByte *) CPLMalloc( nBands * nBlockXSize * nWorkDTSize );

    double nTotalPixels = (double)nXSize * nYSize;

    int nBlockXSizeToRead = nBlockXSize;
    if (nBlockXSize * nBlockXOff + nBlockXSize > nXSize)
    {
        nBlockXSizeToRead = nXSize - nBlockXSize * nBlockXOff;
    }
    int nBlockYSizeToRead = nBlockYSize;
    if (nBlockYSize * nBlockYOff + nBlockYSize > nYSize)
    {
        nBlockYSizeToRead = nYSize - nBlockYSize * nBlockYOff;
    }
    
    bool bClipWarn = false;
    for( int iLine = 0; iLine < nBlockYSize && eErr == CE_None; iLine++ )
    {
        JSAMPLE      *ppSamples;

        if (iLine < nBlockYSizeToRead)
        {
            eErr = poSrcDS->RasterIO( GF_Read, nBlockXSize * nBlockXOff, iLine + nBlockYSize * nBlockYOff, nBlockXSizeToRead, 1, 
                                    pabyScanline, nBlockXSizeToRead, 1, eWorkDT,
                                    nBands, anBandList, 
                                    nBands*nWorkDTSize, nBands * nBlockXSize * nWorkDTSize, nWorkDTSize );

#if !defined(JPEG_LIB_MK1_OR_12BIT)
            /* Repeat the last pixel till the end of the line */
            /* to minimize discontinuity */
            if (nBlockXSizeToRead < nBlockXSize)
            {
                for (int iBand = 0; iBand < nBands; iBand++)
                {
                    GByte bVal = pabyScanline[nBands * (nBlockXSizeToRead - 1) + iBand];
                    for(int iX = nBlockXSizeToRead; iX < nBlockXSize; iX ++)
                    {
                        pabyScanline[nBands * iX + iBand ] = bVal;
                    }
                }
            }
#endif
        }

        // clamp 16bit values to 12bit.
        if( eDT == GDT_UInt16 )
        {
            GUInt16 *panScanline = (GUInt16 *) pabyScanline;
            int iPixel;

            for( iPixel = 0; iPixel < nXSize*nBands; iPixel++ )
            {
                if( panScanline[iPixel] > 4095 )
                {
                    panScanline[iPixel] = 4095;
                    if( !bClipWarn )
                    {
                        bClipWarn = true;
                        CPLError( CE_Warning, CPLE_AppDefined,
                                  "One or more pixels clipped to fit 12bit domain for jpeg output." );
                    }
                }
            }
        }

        ppSamples = (JSAMPLE *) pabyScanline;

        if( eErr == CE_None )
            jpeg_write_scanlines( &sCInfo, &ppSamples, 1 );

        double nCurPixels = (double)nBlockYOff * nBlockYSize * nXSize +
                            (double)nBlockXOff * nBlockYSize * nBlockXSize + (iLine + 1) * nBlockXSizeToRead;
        if( eErr == CE_None 
            && !pfnProgress( nCurPixels / nTotalPixels, NULL, pProgressData ) )
        {
            eErr = CE_Failure;
            CPLError( CE_Failure, CPLE_UserInterrupt, 
                      "User terminated CreateCopy()" );
        }
    }

/* -------------------------------------------------------------------- */
/*      Cleanup and close.                                              */
/* -------------------------------------------------------------------- */
    CPLFree( pabyScanline );

    if( eErr == CE_None )
        jpeg_finish_compress( &sCInfo );
    jpeg_destroy_compress( &sCInfo );

    return eErr == CE_None;
}
コード例 #19
0
ファイル: jpeg.c プロジェクト: bdancer/blender-for-vray
static void write_jpeg(struct jpeg_compress_struct *cinfo, struct ImBuf *ibuf)
{
	JSAMPLE *buffer = NULL;
	JSAMPROW row_pointer[1];
	uchar *rect;
	int x, y;
	char neogeo[128];
	struct NeoGeo_Word *neogeo_word;
	char *text;

	jpeg_start_compress(cinfo, true);

	strcpy(neogeo, "NeoGeo");
	neogeo_word = (struct NeoGeo_Word *)(neogeo + 6);
	memset(neogeo_word, 0, sizeof(*neogeo_word));
	neogeo_word->quality = ibuf->foptions.quality;
	jpeg_write_marker(cinfo, 0xe1, (JOCTET *) neogeo, 10);
	if (ibuf->metadata) {
		IDProperty *prop;
		/* key + max value + "Blender" */
		text = MEM_mallocN(530, "stamp info read");
		for (prop = ibuf->metadata->data.group.first; prop; prop = prop->next) {
			if (prop->type == IDP_STRING) {
				int text_len;
				if (!strcmp(prop->name, "None")) {
					jpeg_write_marker(cinfo, JPEG_COM, (JOCTET *) IDP_String(prop), prop->len + 1);
				}

				/*
				 * The JPEG format don't support a pair "key/value"
				 * like PNG, so we "encode" the stamp in a
				 * single string:
				 * "Blender:key:value"
				 *
				 * The first "Blender" is a simple identify to help
				 * in the read process.
				 */
				text_len = sprintf(text, "Blender:%s:%s", prop->name, IDP_String(prop));
				jpeg_write_marker(cinfo, JPEG_COM, (JOCTET *) text, text_len + 1);
			}
		}
		MEM_freeN(text);
	}

	row_pointer[0] =
	    MEM_mallocN(sizeof(JSAMPLE) *
	                cinfo->input_components *
	                cinfo->image_width, "jpeg row_pointer");

	for (y = ibuf->y - 1; y >= 0; y--) {
		rect = (uchar *) (ibuf->rect + y * ibuf->x);
		buffer = row_pointer[0];

		switch (cinfo->in_color_space) {
			case JCS_RGB:
				for (x = 0; x < ibuf->x; x++) {
					*buffer++ = rect[0];
					*buffer++ = rect[1];
					*buffer++ = rect[2];
					rect += 4;
				}
				break;
			case JCS_GRAYSCALE:
				for (x = 0; x < ibuf->x; x++) {
					*buffer++ = rect[0];
					rect += 4;
				}
				break;
			case JCS_UNKNOWN:
				memcpy(buffer, rect, 4 * ibuf->x);
				break;
			/* default was missing... intentional ? */
			default:
				/* do nothing */
				break;
		}

		jpeg_write_scanlines(cinfo, row_pointer, 1);
	}

	jpeg_finish_compress(cinfo);
	MEM_freeN(row_pointer[0]);
}
コード例 #20
0
ファイル: jpeg.c プロジェクト: fgrollier/darktable
int
write_image (dt_imageio_jpeg_t *jpg, const char *filename, const uint8_t *in, void *exif, int exif_len, int imgid)
{
  struct dt_imageio_jpeg_error_mgr jerr;

  jpg->cinfo.err = jpeg_std_error(&jerr.pub);
  jerr.pub.error_exit = dt_imageio_jpeg_error_exit;
  if (setjmp(jerr.setjmp_buffer))
  {
    jpeg_destroy_compress(&(jpg->cinfo));
    return 1;
  }
  jpeg_create_compress(&(jpg->cinfo));
  FILE *f = fopen(filename, "wb");
  if(!f) return 1;
  jpeg_stdio_dest(&(jpg->cinfo), f);

  jpg->cinfo.image_width = jpg->width;
  jpg->cinfo.image_height = jpg->height;
  jpg->cinfo.input_components = 3;
  jpg->cinfo.in_color_space = JCS_RGB;
  jpeg_set_defaults(&(jpg->cinfo));
  jpeg_set_quality(&(jpg->cinfo), jpg->quality, TRUE);
  if(jpg->quality > 90) jpg->cinfo.comp_info[0].v_samp_factor = 1;
  if(jpg->quality > 92) jpg->cinfo.comp_info[0].h_samp_factor = 1;
  if(jpg->quality > 95) jpg->cinfo.dct_method = JDCT_FLOAT;
  if(jpg->quality < 50) jpg->cinfo.dct_method = JDCT_IFAST;
  if(jpg->quality < 80) jpg->cinfo.smoothing_factor = 20;
  if(jpg->quality < 60) jpg->cinfo.smoothing_factor = 40;
  if(jpg->quality < 40) jpg->cinfo.smoothing_factor = 60;
  jpg->cinfo.optimize_coding = 1;

  jpeg_start_compress(&(jpg->cinfo), TRUE);

  if(imgid > 0)
  {
    cmsHPROFILE out_profile = dt_colorspaces_create_output_profile(imgid);
    uint32_t len = 0;
    cmsSaveProfileToMem(out_profile, 0, &len);
    if (len > 0)
    {
      unsigned char buf[len];
      cmsSaveProfileToMem(out_profile, buf, &len);
      write_icc_profile(&(jpg->cinfo), buf, len);
    }
    dt_colorspaces_cleanup_profile(out_profile);
  }

  if(exif && exif_len > 0 && exif_len < 65534)
    jpeg_write_marker(&(jpg->cinfo), JPEG_APP0+1, exif, exif_len);

  uint8_t row[3*jpg->width];
  const uint8_t *buf;
  while(jpg->cinfo.next_scanline < jpg->cinfo.image_height)
  {
    JSAMPROW tmp[1];
    buf = in + jpg->cinfo.next_scanline * jpg->cinfo.image_width * 4;
    for(int i=0; i<jpg->width; i++) for(int k=0; k<3; k++) row[3*i+k] = buf[4*i+k];
    tmp[0] = row;
    jpeg_write_scanlines(&(jpg->cinfo), tmp, 1);
  }
  jpeg_finish_compress (&(jpg->cinfo));
  jpeg_destroy_compress(&(jpg->cinfo));
  fclose(f);
  return 0;
}
コード例 #21
0
void
gdImageJpegCtx(gdImagePtr im, gdIOCtx *outfile, int quality)
{
    struct jpeg_compress_struct cinfo;
    struct jpeg_error_mgr jerr;
    int i, j, jidx;
    /* volatile so we can gdFree it on return from longjmp */
    volatile JSAMPROW row = 0;
    JSAMPROW rowptr[1];
    jmpbuf_wrapper jmpbufw;
    JDIMENSION nlines;
    char comment[255];

#ifdef JPEG_DEBUG
    printf("gd-jpeg: gd JPEG version %s\n", GD_JPEG_VERSION);
    printf("gd-jpeg: JPEG library version %d, %d-bit sample values\n",
	   JPEG_LIB_VERSION, BITS_IN_JSAMPLE);

    for (i = 0; i < im->colorsTotal; i++) {
	if (!im->open[i])
	    printf("gd-jpeg: gd colormap index %d: (%d, %d, %d)\n", i,
		   im->red[i], im->green[i], im->blue[i]);
    }
#endif /* JPEG_DEBUG */

    memset(&cinfo, 0, sizeof(cinfo));
    memset(&jerr, 0, sizeof(jerr));

    cinfo.err = jpeg_std_error(&jerr);
    cinfo.client_data = &jmpbufw;
    if (setjmp(jmpbufw.jmpbuf) != 0) {
	/* we're here courtesy of longjmp */
	if (row)
	    gdFree(row);
	return;
    }

    cinfo.err->error_exit = fatal_jpeg_error;

    jpeg_create_compress(&cinfo);

    cinfo.image_width = im->sx;
    cinfo.image_height = im->sy;
    cinfo.input_components = 3;	/* # of color components per pixel */
    cinfo.in_color_space = JCS_RGB; /* colorspace of input image */
    jpeg_set_defaults(&cinfo);
    if (quality >= 0)
	jpeg_set_quality(&cinfo, quality, TRUE);

    /* If user requests interlace, translate that to progressive JPEG */
    if (gdImageGetInterlaced(im)) {
#ifdef JPEG_DEBUG
	printf("gd-jpeg: interlace set, outputting progressive"
	       " JPEG image\n"); 
#endif	
	jpeg_simple_progression(&cinfo);
    }

    jpeg_gdIOCtx_dest(&cinfo, outfile);

    row = (JSAMPROW)gdCalloc(1, cinfo.image_width * cinfo.input_components
			   * sizeof(JSAMPLE));
    if (row == 0) {
	fprintf(stderr, "gd-jpeg: error: unable to allocate JPEG row "
		"structure: gdCalloc returns NULL\n");
	jpeg_destroy_compress(&cinfo);
	return;
    }

    rowptr[0] = row;

    jpeg_start_compress(&cinfo, TRUE);

    sprintf(comment, "CREATOR: gd-jpeg v%s (using IJG JPEG v%d),",
	    GD_JPEG_VERSION, JPEG_LIB_VERSION);
    if (quality >= 0)
	sprintf(comment + strlen(comment), " quality = %d\n",
		quality);
    else
	strcat(comment + strlen(comment), " default quality\n");
    jpeg_write_marker(&cinfo, JPEG_COM, (unsigned char *)comment,
		      (unsigned int)strlen(comment));

    for (i = 0; i < im->sy; i++) {
	for (jidx = 0, j = 0; j < im->sx; j++) {
	    int idx = im->pixels[i][j];

	    /*
	     * NB: Although gd RGB values are ints, their max value is
	     * 255 (see the documentation for gdImageColorAllocate())
	     * -- perfect for 8-bit JPEG encoding (which is the norm)
	     */
#if BITS_IN_JSAMPLE == 8
	    row[jidx++] = im->red[idx];
	    row[jidx++] = im->green[idx];
	    row[jidx++] = im->blue[idx];
#elif BITS_IN_JSAMPLE == 12
	    row[jidx++] = im->red[idx] << 4;
	    row[jidx++] = im->green[idx] << 4;
	    row[jidx++] = im->blue[idx] << 4;
#else
#error IJG JPEG library BITS_IN_JSAMPLE value must be 8 or 12
#endif
	}

	nlines = jpeg_write_scanlines(&cinfo, rowptr, 1);
	if (nlines != 1)
	    fprintf(stderr, "gd_jpeg: warning: jpeg_write_scanlines"
		    " returns %u -- expected 1\n", nlines);
    }

    jpeg_finish_compress(&cinfo);
    jpeg_destroy_compress(&cinfo);
    gdFree(row);
}