AWARE [SYSTEMS] Imaging expertise for the Delphi developer
AWare Systems, Imaging expertise for the Delphi developer, Home TIFF and LibTiff Mailing List Archive

LibTiff Mailing List

TIFF and LibTiff Mailing List Archive
January 2012

Previous Thread
Next Thread

Previous by Thread
Next by Thread

Previous by Date
Next by Date

Contact

The TIFF Mailing List Homepage
This list is run by Frank Warmerdam
Archive maintained by AWare Systems



Valid HTML 4.01!



2012.01.07 02:39 "resubmit tiff_ojpeg patch (2011/6/8)", by Ryan Wong

This is a repost of the patch, originally sent on 2011/6/8, for reading
certain tif_ojpeg files from a certain software vendor.
 
I repost this because the earlier patch was not in unified diff format,
making it difficult to apply. In order to address the issue of
TIFFTAG_JPEGIFOFFSET not pointing to the start of any JPEG marker (that is,
the first byte being not 0xFF), the tif_ojpeg.c was extensively modified to
test both TIFFTAG_STRIPOFFSETS and TIFFTAG_JPEGIFOFFSET, in addition to a
limited range of marker searching. It is also modified to deal with
incorrect metadata in the file. The patch is applicable to 3.9.5 only.
Regards,rwong_002@hotmail.com 


--- libtiff-3_9_5_r227596\source\libtiff\tif_dirread.c	2012-01-06 17:40:59.693689100 -0800
+++ libtiff-3_9_5\source\libtiff\tif_dirread.c	2012-01-06 17:50:58.475760000 -0800
@@ -77,12 +77,13 @@
 
 	int n;
 	TIFFDirectory* td;
 	TIFFDirEntry *dp, *dir = NULL;
 	uint16 iv;
 	uint32 v;
+	uint16 iv2[2];
 	const TIFFFieldInfo* fip;
 	size_t fix;
 	uint16 dircount;
 	uint16 previous_tag = 0;
 	int diroutoforderwarning = 0, compressionknown = 0;
 	int haveunknowntags = 0;
@@ -641,12 +642,22 @@
 				 * by spec.  Assume correct SamplesPerPixel value of 1.
 				 */
 				if (!TIFFSetField(tif,TIFFTAG_SAMPLESPERPIXEL,1))
 					goto bad;
 			}
 		}
+		/*
+		 * If there is any disagreement between the TIFF header and JPEG datastream,
+		 * the values from the JPEG datastream should be used. Calling TIFFGetField
+		 * will trigger the OJPEG handler to perform a cross-check, which will fix 
+		 * the TIFF directory fields.
+		 */
+		TIFFGetField(tif,TIFFTAG_BITSPERSAMPLE, &iv);
+		TIFFGetField(tif,TIFFTAG_SAMPLESPERPIXEL, &iv);
+		TIFFGetField(tif,TIFFTAG_PHOTOMETRIC, &iv);
+		TIFFGetField(tif,TIFFTAG_YCBCRSUBSAMPLING, iv2 + 0, iv2 + 1);
 	}
 	/*
 	 * Verify Palette image has a Colormap.
 	 */
 	if (td->td_photometric == PHOTOMETRIC_PALETTE &&
 	    !TIFFFieldSet(tif, FIELD_COLORMAP)) {

--- libtiff-3_9_5_r227596\source\libtiff\tif_ojpeg.c	2012-01-06
17:40:59.772697000 -0800
+++ libtiff-3_9_5\source\libtiff\tif_ojpeg.c	2012-01-06 17:50:58.428760000
-0800
@@ -332,12 +332,13 @@
 static int OJPEGReadHeaderInfo(TIFF* tif);
 static int OJPEGReadSecondarySos(TIFF* tif, tsample_t s);
 static int OJPEGWriteHeaderInfo(TIFF* tif);
 static void OJPEGLibjpegSessionAbort(TIFF* tif);
 
 static int OJPEGReadHeaderInfoSec(TIFF* tif);
+static int OJPEGReadHeaderInfoSecStream(TIFF* tif);
 static int OJPEGReadHeaderInfoSecStreamDri(TIFF* tif);
 static int OJPEGReadHeaderInfoSecStreamDqt(TIFF* tif);
 static int OJPEGReadHeaderInfoSecStreamDht(TIFF* tif);
 static int OJPEGReadHeaderInfoSecStreamSof(TIFF* tif, uint8 marker_id);
 static int OJPEGReadHeaderInfoSecStreamSos(TIFF* tif);
 static int OJPEGReadHeaderInfoSecTablesQTable(TIFF* tif);
@@ -458,12 +459,27 @@
 		case TIFFTAG_JPEGIFOFFSET:
 			*va_arg(ap,uint32*)=(uint32)sp->jpeg_interchange_format;
 			break;
 		case TIFFTAG_JPEGIFBYTECOUNT:
 			*va_arg(ap,uint32*)=(uint32)sp->jpeg_interchange_format_length;
 			break;
+		case TIFFTAG_BITSPERSAMPLE:
+			if (sp->subsamplingcorrect_done==0)
+				OJPEGSubsamplingCorrect(tif);
+			return (*sp->vgetparent)(tif,tag,ap);
+			break;
+		case TIFFTAG_SAMPLESPERPIXEL:
+			if (sp->subsamplingcorrect_done==0)
+				OJPEGSubsamplingCorrect(tif);
+			return (*sp->vgetparent)(tif,tag,ap);
+			break;
+		case TIFFTAG_PHOTOMETRIC:
+			if (sp->subsamplingcorrect_done==0)
+				OJPEGSubsamplingCorrect(tif);
+			return (*sp->vgetparent)(tif,tag,ap);
+			break;
 		case TIFFTAG_YCBCRSUBSAMPLING:
 			if (sp->subsamplingcorrect_done==0)
 				OJPEGSubsamplingCorrect(tif);
 			*va_arg(ap,uint16*)=(uint16)sp->subsampling_hor;
 			*va_arg(ap,uint16*)=(uint16)sp->subsampling_ver;
 			break;
@@ -562,12 +578,17 @@
 		case TIFFTAG_JPEGPROC:
 			sp->jpeg_proc=(uint8)va_arg(ap,uint32);
 			break;
 		case TIFFTAG_JPEGRESTARTINTERVAL:
 			sp->restart_interval=(uint16)va_arg(ap,uint32);
 			break;
+		case TIFFTAG_BITSPERSAMPLE:
+			// TIFF IFD contains a BitsPerSample value 
+			// inconsistent with the OJPEG stream.
+			tif->tif_dir.td_bitspersample = (uint16)8;
+			break;
 		default:
 			return (*sp->vsetparent)(tif,tag,ap);
 	}
 	TIFFSetFieldBit(tif,_TIFFFieldWithTag(tif,tag)->field_bit);
 	tif->tif_flags|=TIFF_DIRTYDIRECT;
 	return(1);
@@ -839,19 +860,22 @@
 static void
 OJPEGPostDecode(TIFF* tif, tidata_t buf, tsize_t cc)
 {
 	OJPEGState* sp=(OJPEGState*)tif->tif_data;
 	(void)buf;
 	(void)cc;
+	if (tif->tif_row % sp->lines_per_strile == 0)
+	{
 	sp->write_curstrile++;
 	if (sp->write_curstrile%tif->tif_dir.td_stripsperimage==0)
 	{
 		assert(sp->libjpeg_session_active!=0);
 		OJPEGLibjpegSessionAbort(tif);
 		sp->writeheader_done=0;
 	}
+	}
 }
 
 static int
 OJPEGSetupEncode(TIFF* tif)
 {
 	static const char module[]="OJPEGSetupEncode";
@@ -937,41 +961,114 @@
 OJPEGSubsamplingCorrect(TIFF* tif)
 {
 	static const char module[]="OJPEGSubsamplingCorrect";
 	OJPEGState* sp=(OJPEGState*)tif->tif_data;
 	uint8 mh;
 	uint8 mv;
+	uint16 bps;
+	uint16 spp;
+	int r;
 	assert(sp->subsamplingcorrect_done==0);
-	if ((tif->tif_dir.td_samplesperpixel!=3) ||
((tif->tif_dir.td_photometric!=PHOTOMETRIC_YCBCR) &&
-	    (tif->tif_dir.td_photometric!=PHOTOMETRIC_ITULAB)))
+
+	/*  
+	 *  Originally, this function is intended to only handle inconsistencies
in the 
+	 *  YCbCrSubsampling tag. We now extend it so that it can fix several
other 
+	 *  inconsistencies seen in images generated by one of the library
vendors.
+	 *
+	 *  Because of this, OJPEGReadHeaderInfoSec will be called regardless of 
+	 *  the values of SamplesPerPixel or Photometric.
+	 *
+	 *  The OJPEGState flags "subsamplingcorrect" and
"subsamplingcorrect_done" are still
+	 *  used to indicate the execution state of this function. This function
only need to
+	 *  be run once per TIFF directory.
+	 */
+
+	/* Backing up old values from TIFF header. */
+
+	bps = tif->tif_dir.td_bitspersample;
+	spp = tif->tif_dir.td_samplesperpixel;
+	mh=sp->subsampling_hor;
+	mv=sp->subsampling_ver;
+
+	/* Process the JPEG SOFn header. This is being done in the
"subsamplingcorrect mode" 
+	 * which causes ceretain steps to be skipped.
+	 */
+	sp->subsamplingcorrect_done=1;
+	sp->subsamplingcorrect=1;
+	r = OJPEGReadHeaderInfoSec(tif);
+	sp->subsamplingcorrect=0;
+	if (r==0)
 	{
+		/* OJPEGReadHeaderInfoSec failed. However, if the TIFF header had correct
value,
+		 * the image might still be able to decode. Thus, restore fields which
might be 
+		 * modified by OJPEGReadHeaderInfoSec.
+		 */
+		if (sp->samples_per_pixel == 0)
+			sp->samples_per_pixel = (uint8) spp;
+		return; 
+	}
+
+	if (sp->samples_per_pixel != 0 && sp->samples_per_pixel != spp)
+	{
+		TIFFWarningExt(tif->tif_clientdata,module,"SamplesPerPixel tag inside
JPEG data does not match SamplesPerPixel tag values [%d]; assuming
SamplesPerPixel inside JPEG data is correct.", sp->samples_per_pixel);
+		tif->tif_dir.td_samplesperpixel = sp->samples_per_pixel;
+	}
+
+	if (sp->samples_per_pixel == 1)
+	{ 
+		if ((tif->tif_dir.td_photometric != PHOTOMETRIC_MINISWHITE) &&
(tif->tif_dir.td_photometric != PHOTOMETRIC_MINISBLACK) &&
+			(tif->tif_dir.td_photometric != PHOTOMETRIC_LOGL))
+		{
+			TIFFWarningExt(tif->tif_clientdata,module,"Value of photometric tag is
not appropriate for this SamplesPerPixel");
+			tif->tif_dir.td_photometric = PHOTOMETRIC_MINISBLACK;
+		}
+
 		if (sp->subsampling_tag!=0)
-			TIFFWarningExt(tif->tif_clientdata,module,"Subsampling tag not
appropriate for this Photometric and/or SamplesPerPixel");
+			TIFFWarningExt(tif->tif_clientdata,module,"Subsampling tag not
appropriate for this SamplesPerPixel");
+	}
+	else if (sp->samples_per_pixel == 3)
+	{
+		//... LASERFICHE / SNOWBOUND ...//
+		if (tif->tif_dir.td_photometric == PHOTOMETRIC_MINISWHITE ||
+			tif->tif_dir.td_photometric == PHOTOMETRIC_MINISBLACK ||
+			tif->tif_dir.td_photometric == PHOTOMETRIC_PALETTE ||
+			tif->tif_dir.td_photometric == PHOTOMETRIC_MASK ||
+			tif->tif_dir.td_photometric == PHOTOMETRIC_SEPARATED || 
+			tif->tif_dir.td_photometric == PHOTOMETRIC_LOGL)
+		{
+			// These photometrics cannot work with SamplesPerPixel == 3. 
+			// Since it is already verified that the JPEG stream has 3 channels, 
+			// we must set photometric to a default value that can handle 3
channels.
+			TIFFWarningExt(tif->tif_clientdata,module,"Photometric tag %d not
appropriate for this SamplesPerPixel", sp->samples_per_pixel);
+			tif->tif_dir.td_photometric = PHOTOMETRIC_YCBCR;
+		}
+		if ((tif->tif_dir.td_photometric != PHOTOMETRIC_YCBCR) && 
+			(tif->tif_dir.td_photometric != PHOTOMETRIC_RGB) && 
+			(tif->tif_dir.td_photometric != PHOTOMETRIC_ITULAB))
+		{
+			if (sp->subsampling_tag!=0)
+				TIFFWarningExt(tif->tif_clientdata,module,"Subsampling tag not
appropriate for this Photometric");
 		sp->subsampling_hor=1;
 		sp->subsampling_ver=1;
 		sp->subsampling_force_desubsampling_inside_decompression=0;
 	}
 	else
 	{
-		sp->subsamplingcorrect_done=1;
-		mh=sp->subsampling_hor;
-		mv=sp->subsampling_ver;
-		sp->subsamplingcorrect=1;
-		OJPEGReadHeaderInfoSec(tif);
 		if (sp->subsampling_force_desubsampling_inside_decompression!=0)
 		{
 			sp->subsampling_hor=1;
 			sp->subsampling_ver=1;
 		}
-		sp->subsamplingcorrect=0;
 		if (((sp->subsampling_hor!=mh) || (sp->subsampling_ver!=mv)) &&
 		(sp->subsampling_force_desubsampling_inside_decompression==0))
 		{
 			if (sp->subsampling_tag==0)
 				TIFFWarningExt(tif->tif_clientdata,module,"Subsampling tag is not set,
 				yet subsampling inside JPEG data [%d,%d] does not match default values
 				[2,2]; assuming subsampling inside JPEG data is
 				correct",sp->subsampling_hor,sp->subsampling_ver);
 			else
 				TIFFWarningExt(tif->tif_clientdata,module,"Subsampling inside JPEG data
 				[%d,%d] does not match subsampling tag values [%d,%d]; assuming
 				subsampling inside JPEG data is
 				correct",sp->subsampling_hor,sp->subsampling_ver,mh,mv);
+				tif->tif_dir.td_ycbcrsubsampling[0] = sp->subsampling_hor;
+				tif->tif_dir.td_ycbcrsubsampling[1] = sp->subsampling_ver;
 		}
 		if (sp->subsampling_force_desubsampling_inside_decompression!=0)
 		{
 			if (sp->subsampling_tag==0)
 				TIFFWarningExt(tif->tif_clientdata,module,"Subsampling tag is not set,
 				yet subsampling inside JPEG data does not match default values [2,2]
 				(nor any other values allowed in TIFF); assuming subsampling inside
 				JPEG data is correct and desubsampling inside JPEG decompression");
 			else
@@ -980,12 +1077,17 @@
 		if (sp->subsampling_force_desubsampling_inside_decompression==0)
 		{
 			if (sp->subsampling_hor<sp->subsampling_ver)
 				TIFFWarningExt(tif->tif_clientdata,module,"Subsampling values [%d,%d]
 				are not allowed in TIFF",sp->subsampling_hor,sp->subsampling_ver);
 		}
 	}
+	}
+	else
+	{
+		// TODO: handle 4-channel JPEG.
+	}
 	sp->subsamplingcorrect_done=1;
 }
 
 static int
 OJPEGReadHeaderInfo(TIFF* tif)
 {
@@ -1201,14 +1303,12 @@
 static int
 OJPEGReadHeaderInfoSec(TIFF* tif)
 {
 	static const char module[]="OJPEGReadHeaderInfoSec";
 	OJPEGState* sp=(OJPEGState*)tif->tif_data;
 	uint8 m;
-	uint16 n;
-	uint8 o;
 	if (sp->file_size==0)
 		sp->file_size=TIFFGetFileSize(tif);
 	if (sp->jpeg_interchange_format!=0)
 	{
 		if (sp->jpeg_interchange_format>=sp->file_size)
 		{
@@ -1223,12 +1323,45 @@
 	}
 	sp->in_buffer_source=osibsNotSetYet;
 	sp->in_buffer_next_strile=0;
 	sp->in_buffer_strile_count=tif->tif_dir.td_nstrips;   
 	sp->in_buffer_file_togo=0;
 	sp->in_buffer_togo=0;
+
+	/* 
+	 * During subsampling correct or first time header cross-check, 
+	 * need to check both jpeg_interchange_format and strip_offset.
+	 */
+	if (OJPEGReadBytePeek(sp,&m)!=0 &&
+		m == 255)
+	{
+		return OJPEGReadHeaderInfoSecStream(tif);
+	}
+	/* jpeg_interchange_format did not point to a JPEG Marker. 
+	 */
+	sp->in_buffer_source=osibsStrile; // Fallback to use strip_offset. 
+	sp->in_buffer_next_strile=0;
+	sp->in_buffer_strile_count=tif->tif_dir.td_nstrips;   
+	sp->in_buffer_file_togo=0;
+	sp->in_buffer_togo=0;
+	if (OJPEGReadBytePeek(sp,&m)==0 ||
+		m != 255)
+	{
+		// Failed; do something.
+	}
+	return OJPEGReadHeaderInfoSecStream(tif);
+}
+
+static int
+OJPEGReadHeaderInfoSecStream(TIFF* tif)
+{
+	static const char module[]="OJPEGReadHeaderInfoSec";
+	OJPEGState* sp=(OJPEGState*)tif->tif_data;
+	uint8 m;
+	uint16 n;
+	uint8 o;
 	do
 	{
 		if (OJPEGReadBytePeek(sp,&m)==0)
 			return(0);
 		if (m!=255)
 			break;
@@ -1512,13 +1645,18 @@
 	{
 		if (sp->subsamplingcorrect==0)
 			TIFFErrorExt(tif->tif_clientdata,module,"Corrupt SOF marker in JPEG
 			data");
 		return(0);
 	}
 	n=m/3;
-	if (sp->subsamplingcorrect==0)
+	if (sp->subsamplingcorrect!=0)
+	{
+		if (n!=sp->samples_per_pixel)
+			sp->samples_per_pixel = n;
+	}
+	else
 	{
 		if (n!=sp->samples_per_pixel)
 		{
 			TIFFErrorExt(tif->tif_clientdata,module,"JPEG compressed data indicates
 			unexpected number of samples");
 			return(0);
 		}
@@ -1585,12 +1723,18 @@
 		if (sp->subsamplingcorrect!=0)
 		{
 			if (q==0)
 			{
 				sp->subsampling_hor=(o>>4);
 				sp->subsampling_ver=(o&15);
+				if (n == 1 && (sp->subsampling_hor != 1 || sp->subsampling_ver != 1))
+				{
+					TIFFWarningExt(tif->tif_clientdata,module,"JPEG compressed data
indicates unexpected subsampling values for grayscale; ignored");
+					sp->subsampling_hor = 1;
+					sp->subsampling_ver = 1;
+				}
 				if (((sp->subsampling_hor!=1) && (sp->subsampling_hor!=2) &&
 				(sp->subsampling_hor!=4)) ||
 					((sp->subsampling_ver!=1) && (sp->subsampling_ver!=2) &&
 					(sp->subsampling_ver!=4)))
 					sp->subsampling_force_desubsampling_inside_decompression=1;
 			}
 			else
 			{
@@ -1602,13 +1746,18 @@
 		{
 			sp->sof_hv[q]=o;
 			if (sp->subsampling_force_desubsampling_inside_decompression==0)
 			{
 				if (q==0)
 				{
-					if (o!=((sp->subsampling_hor<<4)|sp->subsampling_ver))
+					if (n == 1 && o != 17)
+					{
+						TIFFWarningExt(tif->tif_clientdata,module,"JPEG compressed data
indicates unexpected subsampling values for grayscale; ignored");
+						sp->sof_hv[q] = 17;
+					}
+					else if (o!=((sp->subsampling_hor<<4)|sp->subsampling_ver))
 					{
 						TIFFErrorExt(tif->tif_clientdata,module,"JPEG compressed data
 						indicates unexpected subsampling values");
 						return(0);
 					}
 				}
 				else