+#ifdef CHECK_JPEG_YCBCR_SUBSAMPLING
+
+#define JPEG_MARKER_SOF0 0xC0
+#define JPEG_MARKER_SOF1 0xC1
+#define JPEG_MARKER_SOF3 0xC3
+#define JPEG_MARKER_DHT 0xC4
+#define JPEG_MARKER_SOI 0xD8
+#define JPEG_MARKER_SOS 0xDA
+#define JPEG_MARKER_DQT 0xDB
+#define JPEG_MARKER_DRI 0xDD
+#define JPEG_MARKER_APP0 0xE0
+#define JPEG_MARKER_COM 0xFE
+struct JPEGFixupTagsSubsamplingData
+{
+ TIFF* tif;
+ void* buffer;
+ uint32 buffersize;
+ uint8* buffercurrentbyte;
+ uint32 bufferbytesleft;
+ uint64 fileoffset;
+ uint64 filebytesleft;
+ uint8 filepositioned;
+};
+static void JPEGFixupTagsSubsampling(TIFF* tif);
+static int JPEGFixupTagsSubsamplingSec(struct JPEGFixupTagsSubsamplingData* data);
+static int JPEGFixupTagsSubsamplingReadByte(struct JPEGFixupTagsSubsamplingData* data, uint8* result);
+static int JPEGFixupTagsSubsamplingReadWord(struct JPEGFixupTagsSubsamplingData* data, uint16* result);
+static void JPEGFixupTagsSubsamplingSkip(struct JPEGFixupTagsSubsamplingData* data, uint16 skiplength);
+
+#endif
+
+static int
+JPEGFixupTags(TIFF* tif)
+{
+#ifdef CHECK_JPEG_YCBCR_SUBSAMPLING
+ if ((tif->tif_dir.td_photometric==PHOTOMETRIC_YCBCR)&&
+ (tif->tif_dir.td_planarconfig==PLANARCONFIG_CONTIG)&&
+ (tif->tif_dir.td_samplesperpixel==3))
+ JPEGFixupTagsSubsampling(tif);
+#endif
+
+ return(1);
+}
+
+#ifdef CHECK_JPEG_YCBCR_SUBSAMPLING
+
+static void
+JPEGFixupTagsSubsampling(TIFF* tif)
+{
+ /*
+ * Some JPEG-in-TIFF produces do not emit the YCBCRSUBSAMPLING values in
+ * the TIFF tags, but still use non-default (2,2) values within the jpeg
+ * data stream itself. In order for TIFF applications to work properly
+ * - for instance to get the strip buffer size right - it is imperative
+ * that the subsampling be available before we start reading the image
+ * data normally. This function will attempt to analyze the first strip in
+ * order to get the sampling values from the jpeg data stream.
+ *
+ * Note that JPEGPreDeocode() will produce a fairly loud warning when the
+ * discovered sampling does not match the default sampling (2,2) or whatever
+ * was actually in the tiff tags.
+ *
+ * See the bug in bugzilla for details:
+ *
+ * http://bugzilla.remotesensing.org/show_bug.cgi?id=168
+ *
+ * Frank Warmerdam, July 2002
+ * Joris Van Damme, May 2007
+ */
+ static const char module[] = "JPEGFixupTagsSubsampling";
+ struct JPEGFixupTagsSubsamplingData m;
+
+ _TIFFFillStriles( tif );
+
+ if( tif->tif_dir.td_stripbytecount == NULL
+ || tif->tif_dir.td_stripbytecount[0] == 0 )
+ {
+ /* Do not even try to check if the first strip/tile does not
+ yet exist, as occurs when GDAL has created a new NULL file
+ for instance. */
+ return;
+ }
+
+ m.tif=tif;
+ m.buffersize=2048;
+ m.buffer=_TIFFmalloc(m.buffersize);
+ if (m.buffer==NULL)
+ {
+ TIFFWarningExt(tif->tif_clientdata,module,
+ "Unable to allocate memory for auto-correcting of subsampling values; auto-correcting skipped");
+ return;
+ }
+ m.buffercurrentbyte=NULL;
+ m.bufferbytesleft=0;
+ m.fileoffset=tif->tif_dir.td_stripoffset[0];
+ m.filepositioned=0;
+ m.filebytesleft=tif->tif_dir.td_stripbytecount[0];
+ if (!JPEGFixupTagsSubsamplingSec(&m))
+ TIFFWarningExt(tif->tif_clientdata,module,
+ "Unable to auto-correct subsampling values, likely corrupt JPEG compressed data in first strip/tile; auto-correcting skipped");
+ _TIFFfree(m.buffer);
+}
+
+static int
+JPEGFixupTagsSubsamplingSec(struct JPEGFixupTagsSubsamplingData* data)
+{
+ static const char module[] = "JPEGFixupTagsSubsamplingSec";
+ uint8 m;
+ while (1)
+ {
+ while (1)
+ {
+ if (!JPEGFixupTagsSubsamplingReadByte(data,&m))
+ return(0);
+ if (m==255)
+ break;
+ }
+ while (1)
+ {
+ if (!JPEGFixupTagsSubsamplingReadByte(data,&m))
+ return(0);
+ if (m!=255)
+ break;
+ }
+ switch (m)
+ {
+ case JPEG_MARKER_SOI:
+ /* this type of marker has no data and should be skipped */
+ break;
+ case JPEG_MARKER_COM:
+ case JPEG_MARKER_APP0:
+ case JPEG_MARKER_APP0+1:
+ case JPEG_MARKER_APP0+2:
+ case JPEG_MARKER_APP0+3:
+ case JPEG_MARKER_APP0+4:
+ case JPEG_MARKER_APP0+5:
+ case JPEG_MARKER_APP0+6:
+ case JPEG_MARKER_APP0+7:
+ case JPEG_MARKER_APP0+8:
+ case JPEG_MARKER_APP0+9:
+ case JPEG_MARKER_APP0+10:
+ case JPEG_MARKER_APP0+11:
+ case JPEG_MARKER_APP0+12:
+ case JPEG_MARKER_APP0+13:
+ case JPEG_MARKER_APP0+14:
+ case JPEG_MARKER_APP0+15:
+ case JPEG_MARKER_DQT:
+ case JPEG_MARKER_SOS:
+ case JPEG_MARKER_DHT:
+ case JPEG_MARKER_DRI:
+ /* this type of marker has data, but it has no use to us and should be skipped */
+ {
+ uint16 n;
+ if (!JPEGFixupTagsSubsamplingReadWord(data,&n))
+ return(0);
+ if (n<2)
+ return(0);
+ n-=2;
+ if (n>0)
+ JPEGFixupTagsSubsamplingSkip(data,n);
+ }
+ break;
+ case JPEG_MARKER_SOF0:
+ case JPEG_MARKER_SOF1:
+ /* this marker contains the subsampling factors we're scanning for */
+ {
+ uint16 n;
+ uint16 o;
+ uint8 p;
+ uint8 ph,pv;
+ if (!JPEGFixupTagsSubsamplingReadWord(data,&n))
+ return(0);
+ if (n!=8+data->tif->tif_dir.td_samplesperpixel*3)
+ return(0);
+ JPEGFixupTagsSubsamplingSkip(data,7);
+ if (!JPEGFixupTagsSubsamplingReadByte(data,&p))
+ return(0);
+ ph=(p>>4);
+ pv=(p&15);
+ JPEGFixupTagsSubsamplingSkip(data,1);
+ for (o=1; o<data->tif->tif_dir.td_samplesperpixel; o++)
+ {
+ JPEGFixupTagsSubsamplingSkip(data,1);
+ if (!JPEGFixupTagsSubsamplingReadByte(data,&p))
+ return(0);
+ if (p!=0x11)
+ {
+ TIFFWarningExt(data->tif->tif_clientdata,module,
+ "Subsampling values inside JPEG compressed data have no TIFF equivalent, auto-correction of TIFF subsampling values failed");
+ return(1);
+ }
+ JPEGFixupTagsSubsamplingSkip(data,1);
+ }
+ if (((ph!=1)&&(ph!=2)&&(ph!=4))||((pv!=1)&&(pv!=2)&&(pv!=4)))
+ {
+ TIFFWarningExt(data->tif->tif_clientdata,module,
+ "Subsampling values inside JPEG compressed data have no TIFF equivalent, auto-correction of TIFF subsampling values failed");
+ return(1);
+ }
+ if ((ph!=data->tif->tif_dir.td_ycbcrsubsampling[0])||(pv!=data->tif->tif_dir.td_ycbcrsubsampling[1]))
+ {
+ TIFFWarningExt(data->tif->tif_clientdata,module,
+ "Auto-corrected former TIFF subsampling values [%d,%d] to match subsampling values inside JPEG compressed data [%d,%d]",
+ (int)data->tif->tif_dir.td_ycbcrsubsampling[0],
+ (int)data->tif->tif_dir.td_ycbcrsubsampling[1],
+ (int)ph,(int)pv);
+ data->tif->tif_dir.td_ycbcrsubsampling[0]=ph;
+ data->tif->tif_dir.td_ycbcrsubsampling[1]=pv;
+ }
+ }
+ return(1);
+ default:
+ return(0);
+ }
+ }
+}
+
+static int
+JPEGFixupTagsSubsamplingReadByte(struct JPEGFixupTagsSubsamplingData* data, uint8* result)
+{
+ if (data->bufferbytesleft==0)
+ {
+ uint32 m;
+ if (data->filebytesleft==0)
+ return(0);
+ if (!data->filepositioned)
+ {
+ TIFFSeekFile(data->tif,data->fileoffset,SEEK_SET);
+ data->filepositioned=1;
+ }
+ m=data->buffersize;
+ if ((uint64)m>data->filebytesleft)
+ m=(uint32)data->filebytesleft;
+ assert(m<0x80000000UL);
+ if (TIFFReadFile(data->tif,data->buffer,(tmsize_t)m)!=(tmsize_t)m)
+ return(0);
+ data->buffercurrentbyte=data->buffer;
+ data->bufferbytesleft=m;
+ data->fileoffset+=m;
+ data->filebytesleft-=m;
+ }
+ *result=*data->buffercurrentbyte;
+ data->buffercurrentbyte++;
+ data->bufferbytesleft--;
+ return(1);
+}
+
+static int
+JPEGFixupTagsSubsamplingReadWord(struct JPEGFixupTagsSubsamplingData* data, uint16* result)
+{
+ uint8 ma;
+ uint8 mb;
+ if (!JPEGFixupTagsSubsamplingReadByte(data,&ma))
+ return(0);
+ if (!JPEGFixupTagsSubsamplingReadByte(data,&mb))
+ return(0);
+ *result=(ma<<8)|mb;
+ return(1);
+}
+
+static void
+JPEGFixupTagsSubsamplingSkip(struct JPEGFixupTagsSubsamplingData* data, uint16 skiplength)
+{
+ if ((uint32)skiplength<=data->bufferbytesleft)
+ {
+ data->buffercurrentbyte+=skiplength;
+ data->bufferbytesleft-=skiplength;
+ }
+ else
+ {
+ uint16 m;
+ m=skiplength-data->bufferbytesleft;
+ if (m<=data->filebytesleft)
+ {
+ data->bufferbytesleft=0;
+ data->fileoffset+=m;
+ data->filebytesleft-=m;
+ data->filepositioned=0;
+ }
+ else
+ {
+ data->bufferbytesleft=0;
+ data->filebytesleft=0;
+ }
+ }
+}
+
+#endif
+
+