| AWARE [SYSTEMS] | Imaging expertise for the Delphi developer | |||||||
![]() |
TIFF and LibTiff Mailing List Archive | |||||||
LibTiff Mailing List
TIFF and LibTiff Mailing List Archive Contact
The TIFF Mailing List Homepage |
2012.01.07 02:39 "resubmit tiff_ojpeg patch (2011/6/8)", by Ryan WongThis 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
|
|||||||