Blob Blame History Raw
From 7b9ee388ca510e818dc9f248c8cd6c2ffe40fa94 Mon Sep 17 00:00:00 2001
From: Hans de Goede <hdegoede@redhat.com>
Date: Mon, 23 Apr 2012 23:03:40 +0200
Subject: [PATCH 04/12] libv4lconvert: Dynamic quantization tables for Pixart
 JPEG
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Inspired by a patch from Jean-Fran├žois Moine <moinejf@free.fr>, I've spend
4 days on a row investigating (through trial and error) Pixart's JPEG
compression. This patch accumulates what I've learned from this, giving 2
important improvements:
1) Support for properly decompressing pac7302 generated images where some
   of the MCU-s are compressed with a lower quality / higher quantization
   values
2) Proper chrominance quantization tables for Pixart JPEG, getting rid of
   the sometimes horribly over saturation our decompression code was causing

The support for dynamic quantization tables this patch adds also allows us to
enable lower compression ratios in the kernel driver when running at a lower
framerate, resulting in better image quality.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
 lib/libv4lconvert/tinyjpeg-internal.h |    1 +
 lib/libv4lconvert/tinyjpeg.c          |   77 ++++++++++++++++++++++-----------
 2 files changed, 53 insertions(+), 25 deletions(-)

diff --git a/lib/libv4lconvert/tinyjpeg-internal.h b/lib/libv4lconvert/tinyjpeg-internal.h
index 702a2a2..4041251 100644
--- a/lib/libv4lconvert/tinyjpeg-internal.h
+++ b/lib/libv4lconvert/tinyjpeg-internal.h
@@ -103,6 +103,7 @@ struct jdec_private {
 #if SANITY_CHECK
 	unsigned int current_cid;			/* For planar JPEG */
 #endif
+	unsigned char marker;			/* for PJPG (Pixart JPEG) */
 
 	/* Temp space used after the IDCT to store each components */
 	uint8_t Y[64 * 4], Cr[64], Cb[64];
diff --git a/lib/libv4lconvert/tinyjpeg.c b/lib/libv4lconvert/tinyjpeg.c
index 756ad9c..dd77d0f 100644
--- a/lib/libv4lconvert/tinyjpeg.c
+++ b/lib/libv4lconvert/tinyjpeg.c
@@ -205,9 +205,11 @@ static const unsigned char val_ac_chrominance[] = {
 	0xf9, 0xfa
 };
 
-const unsigned char pixart_quantization[][64] = { {
-		0x07, 0x07, 0x08, 0x0a, 0x09, 0x07, 0x0d, 0x0b,
-		0x0c, 0x0d, 0x11, 0x10, 0x0f, 0x12, 0x17, 0x27,
+/* Standard JPEG quantization tables from Annex K of the JPEG standard.
+   Note unlike in Annex K the entries here are in zigzag order! */
+const unsigned char standard_quantization[][64] = { {
+		0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e,
+		0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28,
 		0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25,
 		0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33,
 		0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44,
@@ -1376,25 +1378,57 @@ static void decode_MCU_2x1_3planes(struct jdec_private *priv)
 	IDCT(&priv->component_infos[cCr], priv->Cr, 8);
 }
 
+static void build_quantization_table(float *qtable, const unsigned char *ref_table);
+
 static void pixart_decode_MCU_2x1_3planes(struct jdec_private *priv)
 {
 	unsigned char marker;
 
-	look_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, 8, marker);
-	/* I think the marker indicates which quantization table to use, iow
-	   a Pixart JPEG may have a different quantization table per MCU, most
-	   MCU's have 0x44 as marker for which our special Pixart quantization
-	   tables are correct. Unfortunately with a 7302 some blocks also have 0x48,
-	   and sometimes even other values. As 0x48 is quite common too, we really
-	   need to find out the correct table for that, as currently the blocks
-	   with a 0x48 marker look wrong. During normal operation the marker stays
-	   within the range below, if it gets out of this range we're most likely
-	   decoding garbage */
-	if (marker < 0x20 || marker > 0x7f) {
-		snprintf(priv->error_string, sizeof(priv->error_string),
-				"Pixart JPEG error: invalid MCU marker: 0x%02x\n",
-				(unsigned int)marker);
-		longjmp(priv->jump_state, -EIO);
+	look_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream,
+		   8, marker);
+
+	/* Pixart JPEG MCU-s are preceded by a marker indicating the quality
+	   setting with which the MCU is compressed, IOW the MCU-s may have a
+	   different quantization table per MCU. So if the marker changes we
+	   need to rebuild the quantization tables. */
+	if (marker != priv->marker) {
+		int i, j, comp;
+		unsigned char qt[64];
+		/* These values have been found by trial and error and seem to
+		   work reasonably. Markers with index 0 - 7 are never
+		   generated by the hardware, so they are likely wrong. */
+		const int qfactor[32] = {
+			 10,   12,  14,  16,  18,  20,  22,  24,
+			 25,   30,  35,  40,  45,  50,  55,  60,
+			 64,   68,  80,  90, 100, 120, 140, 160,
+			180,  200, 220, 240, 260, 280, 300, 320
+		};
+
+		i = (marker & 0x7c) >> 2; /* Bits 0 and 1 are always 0 */
+		comp = qfactor[i];
+
+		/* And another special Pixart feature, the DC quantization
+		   factor is fixed! */
+		qt[0] = 7; 
+		for (i = 1; i < 64; i++) {
+			j = (standard_quantization[0][i] * comp + 50) / 100;
+			qt[i] = (j < 255) ? j : 255;
+		}
+		build_quantization_table(priv->Q_tables[0], qt);
+
+		/* And yet another Pixart JPEG special feature, Pixart JPEG
+		   uses the luminance table for chrominance too! Either
+		   as is or with all values multiplied by 2, this is encoded
+		   in bit 7 of the marker. */
+		if (!(marker & 0x80)) {
+			for (i = 0; i < 64; i++) {
+				j = qt[i] * 2;
+				qt[i] = (j < 255) ? j : 255;
+			}
+		}
+		build_quantization_table(priv->Q_tables[1], qt);
+
+		priv->marker = marker;
 	}
 	skip_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, 8);
 
@@ -1944,13 +1978,6 @@ static int parse_JFIF(struct jdec_private *priv, const unsigned char *stream)
 			(!dqt_marker_found && !(priv->flags & TINYJPEG_FLAGS_PIXART_JPEG)))
 		goto bogus_jpeg_format;
 
-	if (priv->flags & TINYJPEG_FLAGS_PIXART_JPEG) {
-		if (!priv->default_huffman_table_initialized) {
-			build_quantization_table(priv->Q_tables[0], pixart_quantization[0]);
-			build_quantization_table(priv->Q_tables[1], pixart_quantization[1]);
-		}
-	}
-
 	if (!dht_marker_found) {
 		trace("No Huffman table loaded, using the default one\n");
 		if (build_default_huffman_tables(priv))
-- 
1.7.10