The Independent JPEG Group's JPEG software v5
diff --git a/jccolor.c b/jccolor.c
index 0da5e1b..4ab3d7e 100644
--- a/jccolor.c
+++ b/jccolor.c
@@ -1,19 +1,28 @@
 /*
  * jccolor.c
  *
- * Copyright (C) 1991, 1992, Thomas G. Lane.
+ * Copyright (C) 1991-1994, Thomas G. Lane.
  * This file is part of the Independent JPEG Group's software.
  * For conditions of distribution and use, see the accompanying README file.
  *
  * This file contains input colorspace conversion routines.
- * These routines are invoked via the methods get_sample_rows
- * and colorin_init/term.
  */
 
+#define JPEG_INTERNALS
 #include "jinclude.h"
+#include "jpeglib.h"
 
 
-static JSAMPARRAY pixel_row;	/* Workspace for a pixel row in input format */
+/* Private subobject */
+
+typedef struct {
+  struct jpeg_color_converter pub; /* public fields */
+
+  /* Private state for RGB->YCC conversion */
+  INT32 * rgb_ycc_tab;		/* => table for RGB to YCbCr conversion */
+} my_color_converter;
+
+typedef my_color_converter * my_cconvert_ptr;
 
 
 /**************** RGB -> YCbCr conversion: most common case **************/
@@ -41,11 +50,7 @@
  * in the tables to save adding them separately in the inner loop.
  */
 
-#ifdef SIXTEEN_BIT_SAMPLES
-#define SCALEBITS	14	/* avoid overflow */
-#else
-#define SCALEBITS	16	/* speedier right-shift on some machines */
-#endif
+#define SCALEBITS	16	/* speediest right-shift on some machines */
 #define ONE_HALF	((INT32) 1 << (SCALEBITS-1))
 #define FIX(x)		((INT32) ((x) * (1L<<SCALEBITS) + 0.5))
 
@@ -55,7 +60,6 @@
  * machines (more than can hold all eight addresses, anyway).
  */
 
-static INT32 * rgb_ycc_tab;	/* => table for RGB to YCbCr conversion */
 #define R_Y_OFF		0			/* offset to R => Y section */
 #define G_Y_OFF		(1*(MAXJSAMPLE+1))	/* offset to G => Y section */
 #define B_Y_OFF		(2*(MAXJSAMPLE+1))	/* etc. */
@@ -69,21 +73,20 @@
 
 
 /*
- * Initialize for colorspace conversion.
+ * Initialize for RGB->YCC colorspace conversion.
  */
 
 METHODDEF void
-rgb_ycc_init (compress_info_ptr cinfo)
+rgb_ycc_start (j_compress_ptr cinfo)
 {
+  my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert;
+  INT32 * rgb_ycc_tab;
   INT32 i;
 
-  /* Allocate a workspace for the result of get_input_row. */
-  pixel_row = (*cinfo->emethods->alloc_small_sarray)
-		(cinfo->image_width, (long) cinfo->input_components);
-
   /* Allocate and fill in the conversion tables. */
-  rgb_ycc_tab = (INT32 *) (*cinfo->emethods->alloc_small)
-				(TABLE_SIZE * SIZEOF(INT32));
+  cconvert->rgb_ycc_tab = rgb_ycc_tab = (INT32 *)
+    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				(TABLE_SIZE * SIZEOF(INT32)));
 
   for (i = 0; i <= MAXJSAMPLE; i++) {
     rgb_ycc_tab[i+R_Y_OFF] = FIX(0.29900) * i;
@@ -102,40 +105,41 @@
 
 
 /*
- * Fetch some rows of pixels from get_input_row and convert to the
- * JPEG colorspace.
+ * Convert some rows of samples to the JPEG colorspace.
+ *
+ * Note that we change from the application's interleaved-pixel format
+ * to our internal noninterleaved, one-plane-per-component format.
+ * The input buffer is therefore three times as wide as the output buffer.
+ *
+ * A starting row offset is provided only for the output buffer.  The caller
+ * can easily adjust the passed input_buf value to accommodate any row
+ * offset required on that side.
  */
 
 METHODDEF void
-get_rgb_ycc_rows (compress_info_ptr cinfo,
-		  int rows_to_read, JSAMPIMAGE image_data)
+rgb_ycc_convert (j_compress_ptr cinfo,
+		 JSAMPARRAY input_buf, JSAMPIMAGE output_buf,
+		 JDIMENSION output_row, int num_rows)
 {
-#ifdef SIXTEEN_BIT_SAMPLES
-  register UINT16 r, g, b;
-#else
+  my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert;
   register int r, g, b;
-#endif
-  register INT32 * ctab = rgb_ycc_tab;
-  register JSAMPROW inptr0, inptr1, inptr2;
+  register INT32 * ctab = cconvert->rgb_ycc_tab;
+  register JSAMPROW inptr;
   register JSAMPROW outptr0, outptr1, outptr2;
-  register long col;
-  long width = cinfo->image_width;
-  int row;
+  register JDIMENSION col;
+  JDIMENSION num_cols = cinfo->image_width;
 
-  for (row = 0; row < rows_to_read; row++) {
-    /* Read one row from the source file */
-    (*cinfo->methods->get_input_row) (cinfo, pixel_row);
-    /* Convert colorspace */
-    inptr0 = pixel_row[0];
-    inptr1 = pixel_row[1];
-    inptr2 = pixel_row[2];
-    outptr0 = image_data[0][row];
-    outptr1 = image_data[1][row];
-    outptr2 = image_data[2][row];
-    for (col = 0; col < width; col++) {
-      r = GETJSAMPLE(inptr0[col]);
-      g = GETJSAMPLE(inptr1[col]);
-      b = GETJSAMPLE(inptr2[col]);
+  while (--num_rows >= 0) {
+    inptr = *input_buf++;
+    outptr0 = output_buf[0][output_row];
+    outptr1 = output_buf[1][output_row];
+    outptr2 = output_buf[2][output_row];
+    output_row++;
+    for (col = 0; col < num_cols; col++) {
+      r = GETJSAMPLE(inptr[RGB_RED]);
+      g = GETJSAMPLE(inptr[RGB_GREEN]);
+      b = GETJSAMPLE(inptr[RGB_BLUE]);
+      inptr += RGB_PIXELSIZE;
       /* If the inputs are 0..MAXJSAMPLE, the outputs of these equations
        * must be too; we do not need an explicit range-limiting operation.
        * Hence the value being shifted is never negative, and we don't
@@ -162,46 +166,34 @@
 
 
 /*
- * Fetch some rows of pixels from get_input_row and convert to the
- * JPEG colorspace.
+ * Convert some rows of samples to the JPEG colorspace.
  * This version handles RGB->grayscale conversion, which is the same
  * as the RGB->Y portion of RGB->YCbCr.
- * We assume rgb_ycc_init has been called (we only use the Y tables).
+ * We assume rgb_ycc_start has been called (we only use the Y tables).
  */
 
 METHODDEF void
-get_rgb_gray_rows (compress_info_ptr cinfo,
-		   int rows_to_read, JSAMPIMAGE image_data)
+rgb_gray_convert (j_compress_ptr cinfo,
+		  JSAMPARRAY input_buf, JSAMPIMAGE output_buf,
+		  JDIMENSION output_row, int num_rows)
 {
-#ifdef SIXTEEN_BIT_SAMPLES
-  register UINT16 r, g, b;
-#else
+  my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert;
   register int r, g, b;
-#endif
-  register INT32 * ctab = rgb_ycc_tab;
-  register JSAMPROW inptr0, inptr1, inptr2;
+  register INT32 * ctab = cconvert->rgb_ycc_tab;
+  register JSAMPROW inptr;
   register JSAMPROW outptr;
-  register long col;
-  long width = cinfo->image_width;
-  int row;
+  register JDIMENSION col;
+  JDIMENSION num_cols = cinfo->image_width;
 
-  for (row = 0; row < rows_to_read; row++) {
-    /* Read one row from the source file */
-    (*cinfo->methods->get_input_row) (cinfo, pixel_row);
-    /* Convert colorspace */
-    inptr0 = pixel_row[0];
-    inptr1 = pixel_row[1];
-    inptr2 = pixel_row[2];
-    outptr = image_data[0][row];
-    for (col = 0; col < width; col++) {
-      r = GETJSAMPLE(inptr0[col]);
-      g = GETJSAMPLE(inptr1[col]);
-      b = GETJSAMPLE(inptr2[col]);
-      /* If the inputs are 0..MAXJSAMPLE, the outputs of these equations
-       * must be too; we do not need an explicit range-limiting operation.
-       * Hence the value being shifted is never negative, and we don't
-       * need the general RIGHT_SHIFT macro.
-       */
+  while (--num_rows >= 0) {
+    inptr = *input_buf++;
+    outptr = output_buf[0][output_row];
+    output_row++;
+    for (col = 0; col < num_cols; col++) {
+      r = GETJSAMPLE(inptr[RGB_RED]);
+      g = GETJSAMPLE(inptr[RGB_GREEN]);
+      b = GETJSAMPLE(inptr[RGB_BLUE]);
+      inptr += RGB_PIXELSIZE;
       /* Y */
       outptr[col] = (JSAMPLE)
 		((ctab[r+R_Y_OFF] + ctab[g+G_Y_OFF] + ctab[b+B_Y_OFF])
@@ -212,150 +204,246 @@
 
 
 /*
- * Initialize for colorspace conversion.
+ * Convert some rows of samples to the JPEG colorspace.
+ * This version handles Adobe-style CMYK->YCCK conversion,
+ * where we convert R=1-C, G=1-M, and B=1-Y to YCbCr using the same
+ * conversion as above, while passing K (black) unchanged.
+ * We assume rgb_ycc_start has been called.
  */
 
 METHODDEF void
-colorin_init (compress_info_ptr cinfo)
+cmyk_ycck_convert (j_compress_ptr cinfo,
+		   JSAMPARRAY input_buf, JSAMPIMAGE output_buf,
+		   JDIMENSION output_row, int num_rows)
 {
-  /* Allocate a workspace for the result of get_input_row. */
-  pixel_row = (*cinfo->emethods->alloc_small_sarray)
-		(cinfo->image_width, (long) cinfo->input_components);
-}
+  my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert;
+  register int r, g, b;
+  register INT32 * ctab = cconvert->rgb_ycc_tab;
+  register JSAMPROW inptr;
+  register JSAMPROW outptr0, outptr1, outptr2, outptr3;
+  register JDIMENSION col;
+  JDIMENSION num_cols = cinfo->image_width;
 
-
-/*
- * Fetch some rows of pixels from get_input_row and convert to the
- * JPEG colorspace.
- * This version handles grayscale output with no conversion.
- * The source can be either plain grayscale or YCbCr (since Y == gray).
- */
-
-METHODDEF void
-get_grayscale_rows (compress_info_ptr cinfo,
-		    int rows_to_read, JSAMPIMAGE image_data)
-{
-  int row;
-
-  for (row = 0; row < rows_to_read; row++) {
-    /* Read one row from the source file */
-    (*cinfo->methods->get_input_row) (cinfo, pixel_row);
-    /* Convert colorspace (gamma mapping needed here) */
-    jcopy_sample_rows(pixel_row, 0, image_data[0], row,
-		      1, cinfo->image_width);
-  }
-}
-
-
-/*
- * Fetch some rows of pixels from get_input_row and convert to the
- * JPEG colorspace.
- * This version handles multi-component colorspaces without conversion.
- */
-
-METHODDEF void
-get_noconvert_rows (compress_info_ptr cinfo,
-		    int rows_to_read, JSAMPIMAGE image_data)
-{
-  int row, ci;
-
-  for (row = 0; row < rows_to_read; row++) {
-    /* Read one row from the source file */
-    (*cinfo->methods->get_input_row) (cinfo, pixel_row);
-    /* Convert colorspace (gamma mapping needed here) */
-    for (ci = 0; ci < cinfo->input_components; ci++) {
-      jcopy_sample_rows(pixel_row, ci, image_data[ci], row,
-			1, cinfo->image_width);
+  while (--num_rows >= 0) {
+    inptr = *input_buf++;
+    outptr0 = output_buf[0][output_row];
+    outptr1 = output_buf[1][output_row];
+    outptr2 = output_buf[2][output_row];
+    outptr3 = output_buf[3][output_row];
+    output_row++;
+    for (col = 0; col < num_cols; col++) {
+      r = MAXJSAMPLE - GETJSAMPLE(inptr[0]);
+      g = MAXJSAMPLE - GETJSAMPLE(inptr[1]);
+      b = MAXJSAMPLE - GETJSAMPLE(inptr[2]);
+      /* K passes through as-is */
+      outptr3[col] = inptr[3];	/* don't need GETJSAMPLE here */
+      inptr += 4;
+      /* If the inputs are 0..MAXJSAMPLE, the outputs of these equations
+       * must be too; we do not need an explicit range-limiting operation.
+       * Hence the value being shifted is never negative, and we don't
+       * need the general RIGHT_SHIFT macro.
+       */
+      /* Y */
+      outptr0[col] = (JSAMPLE)
+		((ctab[r+R_Y_OFF] + ctab[g+G_Y_OFF] + ctab[b+B_Y_OFF])
+		 >> SCALEBITS);
+      /* Cb */
+      outptr1[col] = (JSAMPLE)
+		((ctab[r+R_CB_OFF] + ctab[g+G_CB_OFF] + ctab[b+B_CB_OFF])
+		 >> SCALEBITS);
+      /* Cr */
+      outptr2[col] = (JSAMPLE)
+		((ctab[r+R_CR_OFF] + ctab[g+G_CR_OFF] + ctab[b+B_CR_OFF])
+		 >> SCALEBITS);
     }
   }
 }
 
 
 /*
- * Finish up at the end of the file.
+ * Convert some rows of samples to the JPEG colorspace.
+ * This version handles grayscale output with no conversion.
+ * The source can be either plain grayscale or YCbCr (since Y == gray).
  */
 
 METHODDEF void
-colorin_term (compress_info_ptr cinfo)
+grayscale_convert (j_compress_ptr cinfo,
+		   JSAMPARRAY input_buf, JSAMPIMAGE output_buf,
+		   JDIMENSION output_row, int num_rows)
 {
-  /* no work (we let free_all release the workspace) */
+  register JSAMPROW inptr;
+  register JSAMPROW outptr;
+  register JDIMENSION col;
+  JDIMENSION num_cols = cinfo->image_width;
+  int instride = cinfo->input_components;
+
+  while (--num_rows >= 0) {
+    inptr = *input_buf++;
+    outptr = output_buf[0][output_row];
+    output_row++;
+    for (col = 0; col < num_cols; col++) {
+      outptr[col] = inptr[0];	/* don't need GETJSAMPLE() here */
+      inptr += instride;
+    }
+  }
 }
 
 
 /*
- * The method selection routine for input colorspace conversion.
+ * Convert some rows of samples to the JPEG colorspace.
+ * This version handles multi-component colorspaces without conversion.
+ * We assume input_components == num_components.
+ */
+
+METHODDEF void
+null_convert (j_compress_ptr cinfo,
+	      JSAMPARRAY input_buf, JSAMPIMAGE output_buf,
+	      JDIMENSION output_row, int num_rows)
+{
+  register JSAMPROW inptr;
+  register JSAMPROW outptr;
+  register JDIMENSION col;
+  register int ci;
+  int nc = cinfo->num_components;
+  JDIMENSION num_cols = cinfo->image_width;
+
+  while (--num_rows >= 0) {
+    /* It seems fastest to make a separate pass for each component. */
+    for (ci = 0; ci < nc; ci++) {
+      inptr = *input_buf;
+      outptr = output_buf[ci][output_row];
+      for (col = 0; col < num_cols; col++) {
+	outptr[col] = inptr[ci]; /* don't need GETJSAMPLE() here */
+	inptr += nc;
+      }
+    }
+    input_buf++;
+    output_row++;
+  }
+}
+
+
+/*
+ * Empty method for start_pass.
+ */
+
+METHODDEF void
+null_method (j_compress_ptr cinfo)
+{
+  /* no work needed */
+}
+
+
+/*
+ * Module initialization routine for input colorspace conversion.
  */
 
 GLOBAL void
-jselccolor (compress_info_ptr cinfo)
+jinit_color_converter (j_compress_ptr cinfo)
 {
+  my_cconvert_ptr cconvert;
+
+  cconvert = (my_cconvert_ptr)
+    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				SIZEOF(my_color_converter));
+  cinfo->cconvert = (struct jpeg_color_converter *) cconvert;
+  /* set start_pass to null method until we find out differently */
+  cconvert->pub.start_pass = null_method;
+
   /* Make sure input_components agrees with in_color_space */
   switch (cinfo->in_color_space) {
-  case CS_GRAYSCALE:
+  case JCS_GRAYSCALE:
     if (cinfo->input_components != 1)
-      ERREXIT(cinfo->emethods, "Bogus input colorspace");
+      ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE);
     break;
 
-  case CS_RGB:
-  case CS_YCbCr:
-  case CS_YIQ:
+  case JCS_RGB:
+#if RGB_PIXELSIZE != 3
+    if (cinfo->input_components != RGB_PIXELSIZE)
+      ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE);
+    break;
+#endif /* else share code with YCbCr */
+
+  case JCS_YCbCr:
     if (cinfo->input_components != 3)
-      ERREXIT(cinfo->emethods, "Bogus input colorspace");
+      ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE);
     break;
 
-  case CS_CMYK:
+  case JCS_CMYK:
+  case JCS_YCCK:
     if (cinfo->input_components != 4)
-      ERREXIT(cinfo->emethods, "Bogus input colorspace");
+      ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE);
     break;
 
-  default:
-    ERREXIT(cinfo->emethods, "Unsupported input colorspace");
+  default:			/* JCS_UNKNOWN can be anything */
+    if (cinfo->input_components < 1)
+      ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE);
     break;
   }
 
-  /* Standard init/term methods (may override below) */
-  cinfo->methods->colorin_init = colorin_init;
-  cinfo->methods->colorin_term = colorin_term;
-
   /* Check num_components, set conversion method based on requested space */
   switch (cinfo->jpeg_color_space) {
-  case CS_GRAYSCALE:
+  case JCS_GRAYSCALE:
     if (cinfo->num_components != 1)
-      ERREXIT(cinfo->emethods, "Bogus JPEG colorspace");
-    if (cinfo->in_color_space == CS_GRAYSCALE)
-      cinfo->methods->get_sample_rows = get_grayscale_rows;
-    else if (cinfo->in_color_space == CS_RGB) {
-      cinfo->methods->colorin_init = rgb_ycc_init;
-      cinfo->methods->get_sample_rows = get_rgb_gray_rows;
-    } else if (cinfo->in_color_space == CS_YCbCr)
-      cinfo->methods->get_sample_rows = get_grayscale_rows;
+      ERREXIT(cinfo, JERR_BAD_J_COLORSPACE);
+    if (cinfo->in_color_space == JCS_GRAYSCALE)
+      cconvert->pub.color_convert = grayscale_convert;
+    else if (cinfo->in_color_space == JCS_RGB) {
+      cconvert->pub.start_pass = rgb_ycc_start;
+      cconvert->pub.color_convert = rgb_gray_convert;
+    } else if (cinfo->in_color_space == JCS_YCbCr)
+      cconvert->pub.color_convert = grayscale_convert;
     else
-      ERREXIT(cinfo->emethods, "Unsupported color conversion request");
+      ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL);
     break;
 
-  case CS_YCbCr:
+  case JCS_RGB:
     if (cinfo->num_components != 3)
-      ERREXIT(cinfo->emethods, "Bogus JPEG colorspace");
-    if (cinfo->in_color_space == CS_RGB) {
-      cinfo->methods->colorin_init = rgb_ycc_init;
-      cinfo->methods->get_sample_rows = get_rgb_ycc_rows;
-    } else if (cinfo->in_color_space == CS_YCbCr)
-      cinfo->methods->get_sample_rows = get_noconvert_rows;
+      ERREXIT(cinfo, JERR_BAD_J_COLORSPACE);
+    if (cinfo->in_color_space == JCS_RGB && RGB_PIXELSIZE == 3)
+      cconvert->pub.color_convert = null_convert;
     else
-      ERREXIT(cinfo->emethods, "Unsupported color conversion request");
+      ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL);
     break;
 
-  case CS_CMYK:
+  case JCS_YCbCr:
+    if (cinfo->num_components != 3)
+      ERREXIT(cinfo, JERR_BAD_J_COLORSPACE);
+    if (cinfo->in_color_space == JCS_RGB) {
+      cconvert->pub.start_pass = rgb_ycc_start;
+      cconvert->pub.color_convert = rgb_ycc_convert;
+    } else if (cinfo->in_color_space == JCS_YCbCr)
+      cconvert->pub.color_convert = null_convert;
+    else
+      ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL);
+    break;
+
+  case JCS_CMYK:
     if (cinfo->num_components != 4)
-      ERREXIT(cinfo->emethods, "Bogus JPEG colorspace");
-    if (cinfo->in_color_space == CS_CMYK)
-      cinfo->methods->get_sample_rows = get_noconvert_rows;
+      ERREXIT(cinfo, JERR_BAD_J_COLORSPACE);
+    if (cinfo->in_color_space == JCS_CMYK)
+      cconvert->pub.color_convert = null_convert;
     else
-      ERREXIT(cinfo->emethods, "Unsupported color conversion request");
+      ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL);
     break;
 
-  default:
-    ERREXIT(cinfo->emethods, "Unsupported JPEG colorspace");
+  case JCS_YCCK:
+    if (cinfo->num_components != 4)
+      ERREXIT(cinfo, JERR_BAD_J_COLORSPACE);
+    if (cinfo->in_color_space == JCS_CMYK) {
+      cconvert->pub.start_pass = rgb_ycc_start;
+      cconvert->pub.color_convert = cmyk_ycck_convert;
+    } else if (cinfo->in_color_space == JCS_YCCK)
+      cconvert->pub.color_convert = null_convert;
+    else
+      ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL);
+    break;
+
+  default:			/* allow null conversion of JCS_UNKNOWN */
+    if (cinfo->jpeg_color_space != cinfo->in_color_space ||
+	cinfo->num_components != cinfo->input_components)
+      ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL);
+    cconvert->pub.color_convert = null_convert;
     break;
   }
 }