Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GeoTIFF Driver
4 : * Purpose: GDAL GeoTIFF support.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 1998, 2002, Frank Warmerdam <warmerdam@pobox.com>
9 : * Copyright (c) 2007-2015, Even Rouault <even dot rouault at spatialys dot com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "gtiffjpegoverviewds.h"
15 :
16 : #include "gtiffdataset.h"
17 : #include "gdal_priv.h"
18 :
19 : #include "tifvsi.h"
20 :
21 : /************************************************************************/
22 : /* ==================================================================== */
23 : /* GTiffJPEGOverviewBand */
24 : /* ==================================================================== */
25 : /************************************************************************/
26 :
27 : class GTiffJPEGOverviewBand final : public GDALRasterBand
28 : {
29 : public:
30 : GTiffJPEGOverviewBand(GTiffJPEGOverviewDS *poDS, int nBand);
31 :
32 : CPLErr IReadBlock(int, int, void *) override;
33 :
34 9 : GDALColorInterp GetColorInterpretation() override
35 : {
36 9 : return cpl::down_cast<GTiffJPEGOverviewDS *>(poDS)
37 9 : ->m_poParentDS->GetRasterBand(nBand)
38 9 : ->GetColorInterpretation();
39 : }
40 : };
41 :
42 : /************************************************************************/
43 : /* GTiffJPEGOverviewDS() */
44 : /************************************************************************/
45 :
46 60 : GTiffJPEGOverviewDS::GTiffJPEGOverviewDS(GTiffDataset *poParentDSIn,
47 : int nOverviewLevelIn,
48 : const void *pJPEGTable,
49 60 : int nJPEGTableSizeIn)
50 : : m_poParentDS(poParentDSIn), m_nOverviewLevel(nOverviewLevelIn),
51 60 : m_nJPEGTableSize(nJPEGTableSizeIn)
52 : {
53 60 : ShareLockWithParentDataset(poParentDSIn);
54 :
55 60 : m_osTmpFilenameJPEGTable = VSIMemGenerateHiddenFilename("jpegtable");
56 :
57 60 : const GByte abyAdobeAPP14RGB[] = {0xFF, 0xEE, 0x00, 0x0E, 0x41, 0x64,
58 : 0x6F, 0x62, 0x65, 0x00, 0x64, 0x00,
59 : 0x00, 0x00, 0x00, 0x00};
60 60 : const bool bAddAdobe =
61 105 : m_poParentDS->m_nPlanarConfig == PLANARCONFIG_CONTIG &&
62 90 : m_poParentDS->m_nPhotometric != PHOTOMETRIC_YCBCR &&
63 30 : m_poParentDS->nBands == 3;
64 60 : m_pabyJPEGTable = static_cast<GByte *>(CPLMalloc(
65 60 : m_nJPEGTableSize + (bAddAdobe ? sizeof(abyAdobeAPP14RGB) : 0)));
66 60 : memcpy(m_pabyJPEGTable, pJPEGTable, m_nJPEGTableSize);
67 60 : if (bAddAdobe)
68 : {
69 6 : memcpy(m_pabyJPEGTable + m_nJPEGTableSize, abyAdobeAPP14RGB,
70 : sizeof(abyAdobeAPP14RGB));
71 6 : m_nJPEGTableSize += sizeof(abyAdobeAPP14RGB);
72 : }
73 60 : CPL_IGNORE_RET_VAL(VSIFCloseL(VSIFileFromMemBuffer(
74 60 : m_osTmpFilenameJPEGTable, m_pabyJPEGTable, m_nJPEGTableSize, TRUE)));
75 :
76 60 : const int nScaleFactor = 1 << m_nOverviewLevel;
77 60 : nRasterXSize = DIV_ROUND_UP(m_poParentDS->nRasterXSize, nScaleFactor);
78 60 : nRasterYSize = DIV_ROUND_UP(m_poParentDS->nRasterYSize, nScaleFactor);
79 :
80 204 : for (int i = 1; i <= m_poParentDS->nBands; ++i)
81 144 : SetBand(i, new GTiffJPEGOverviewBand(this, i));
82 :
83 60 : SetMetadataItem(GDALMD_INTERLEAVE, "PIXEL", GDAL_MDD_IMAGE_STRUCTURE);
84 60 : if (m_poParentDS->m_nPhotometric == PHOTOMETRIC_YCBCR)
85 15 : SetMetadataItem(GDALMD_COMPRESSION, "YCbCr JPEG",
86 : GDAL_MDD_IMAGE_STRUCTURE);
87 : else
88 45 : SetMetadataItem(GDALMD_COMPRESSION, "JPEG", GDAL_MDD_IMAGE_STRUCTURE);
89 60 : }
90 :
91 : /************************************************************************/
92 : /* ~GTiffJPEGOverviewDS() */
93 : /************************************************************************/
94 :
95 120 : GTiffJPEGOverviewDS::~GTiffJPEGOverviewDS()
96 : {
97 60 : m_poJPEGDS.reset();
98 60 : VSIUnlink(m_osTmpFilenameJPEGTable);
99 60 : if (!m_osTmpFilename.empty())
100 50 : VSIUnlink(m_osTmpFilename);
101 120 : }
102 :
103 : /************************************************************************/
104 : /* IRasterIO() */
105 : /************************************************************************/
106 :
107 32 : CPLErr GTiffJPEGOverviewDS::IRasterIO(
108 : GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
109 : void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
110 : int nBandCount, BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
111 : GSpacing nLineSpace, GSpacing nBandSpace, GDALRasterIOExtraArg *psExtraArg)
112 :
113 : {
114 : // For non-single strip JPEG-IN-TIFF, the block based strategy will
115 : // be the most efficient one, to avoid decompressing the JPEG content
116 : // for each requested band.
117 32 : if (nBandCount > 1 &&
118 22 : m_poParentDS->m_nPlanarConfig == PLANARCONFIG_CONTIG &&
119 14 : (m_poParentDS->m_nBlockXSize < m_poParentDS->nRasterXSize ||
120 12 : m_poParentDS->m_nBlockYSize > 1))
121 : {
122 14 : return BlockBasedRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
123 : nBufXSize, nBufYSize, eBufType, nBandCount,
124 : panBandMap, nPixelSpace, nLineSpace,
125 14 : nBandSpace, psExtraArg);
126 : }
127 :
128 18 : return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
129 : nBufXSize, nBufYSize, eBufType, nBandCount,
130 : panBandMap, nPixelSpace, nLineSpace,
131 18 : nBandSpace, psExtraArg);
132 : }
133 :
134 : /************************************************************************/
135 : /* GTiffJPEGOverviewBand() */
136 : /************************************************************************/
137 :
138 144 : GTiffJPEGOverviewBand::GTiffJPEGOverviewBand(GTiffJPEGOverviewDS *poDSIn,
139 144 : int nBandIn)
140 : {
141 144 : poDS = poDSIn;
142 144 : nBand = nBandIn;
143 144 : eDataType =
144 144 : poDSIn->m_poParentDS->GetRasterBand(nBandIn)->GetRasterDataType();
145 144 : poDSIn->m_poParentDS->GetRasterBand(nBandIn)->GetBlockSize(&nBlockXSize,
146 : &nBlockYSize);
147 144 : const int nScaleFactor = 1 << poDSIn->m_nOverviewLevel;
148 144 : nBlockXSize = DIV_ROUND_UP(nBlockXSize, nScaleFactor);
149 144 : nBlockYSize = DIV_ROUND_UP(nBlockYSize, nScaleFactor);
150 144 : }
151 :
152 : /************************************************************************/
153 : /* IReadBlock() */
154 : /************************************************************************/
155 :
156 2828 : CPLErr GTiffJPEGOverviewBand::IReadBlock(int nBlockXOff, int nBlockYOff,
157 : void *pImage)
158 : {
159 2828 : GTiffJPEGOverviewDS *m_poGDS = cpl::down_cast<GTiffJPEGOverviewDS *>(poDS);
160 :
161 : // Compute the source block ID.
162 2828 : int nBlockId = 0;
163 : int nParentBlockXSize, nParentBlockYSize;
164 2828 : m_poGDS->m_poParentDS->GetRasterBand(1)->GetBlockSize(&nParentBlockXSize,
165 : &nParentBlockYSize);
166 2828 : const bool bIsSingleStripAsSplit =
167 2830 : (nParentBlockYSize == 1 &&
168 2 : m_poGDS->m_poParentDS->m_nBlockYSize != nParentBlockYSize);
169 2828 : if (!bIsSingleStripAsSplit)
170 : {
171 2826 : nBlockId =
172 2826 : nBlockYOff * m_poGDS->m_poParentDS->m_nBlocksPerRow + nBlockXOff;
173 : }
174 2828 : if (m_poGDS->m_poParentDS->m_nPlanarConfig == PLANARCONFIG_SEPARATE)
175 : {
176 688 : nBlockId += (nBand - 1) * m_poGDS->m_poParentDS->m_nBlocksPerBand;
177 : }
178 :
179 : // Make sure it is available.
180 2828 : const int nDataTypeSize = GDALGetDataTypeSizeBytes(eDataType);
181 2828 : vsi_l_offset nOffset = 0;
182 2828 : vsi_l_offset nByteCount = 0;
183 2828 : bool bErrOccurred = false;
184 2828 : if (!m_poGDS->m_poParentDS->IsBlockAvailable(nBlockId, &nOffset,
185 : &nByteCount, &bErrOccurred))
186 : {
187 64 : memset(pImage, 0,
188 64 : static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize *
189 64 : nDataTypeSize);
190 64 : if (bErrOccurred)
191 0 : return CE_Failure;
192 64 : return CE_None;
193 : }
194 :
195 2764 : const int nScaleFactor = 1 << m_poGDS->m_nOverviewLevel;
196 2764 : if (m_poGDS->m_poJPEGDS == nullptr || nBlockId != m_poGDS->m_nBlockId)
197 : {
198 2459 : if (nByteCount < 2)
199 0 : return CE_Failure;
200 2459 : nOffset += 2; // Skip leading 0xFF 0xF8.
201 2459 : nByteCount -= 2;
202 :
203 2459 : CPLString osFileToOpen;
204 2459 : m_poGDS->m_osTmpFilename = VSIMemGenerateHiddenFilename("sparse");
205 2459 : VSILFILE *fp = VSIFOpenL(m_poGDS->m_osTmpFilename, "wb+");
206 :
207 : // If the size of the JPEG strip/tile is small enough, we will
208 : // read it from the TIFF file and forge a in-memory JPEG file with
209 : // the JPEG table followed by the JPEG data.
210 2459 : const bool bInMemoryJPEGFile = nByteCount < 256 * 256;
211 2459 : if (bInMemoryJPEGFile)
212 : {
213 2448 : osFileToOpen = m_poGDS->m_osTmpFilename;
214 :
215 2448 : bool bError = false;
216 2448 : if (VSIFSeekL(fp, m_poGDS->m_nJPEGTableSize + nByteCount - 1,
217 2448 : SEEK_SET) != 0)
218 0 : bError = true;
219 2448 : char ch = 0;
220 2448 : if (!bError && VSIFWriteL(&ch, 1, 1, fp) != 1)
221 0 : bError = true;
222 : GByte *pabyBuffer =
223 2448 : VSIGetMemFileBuffer(m_poGDS->m_osTmpFilename, nullptr, FALSE);
224 2448 : memcpy(pabyBuffer, m_poGDS->m_pabyJPEGTable,
225 2448 : m_poGDS->m_nJPEGTableSize);
226 2448 : TIFF *hTIFF = m_poGDS->m_poParentDS->m_hTIFF;
227 2448 : VSILFILE *fpTIF = VSI_TIFFGetVSILFile(TIFFClientdata(hTIFF));
228 2448 : if (!bError && VSIFSeekL(fpTIF, nOffset, SEEK_SET) != 0)
229 0 : bError = true;
230 2448 : if (VSIFReadL(pabyBuffer + m_poGDS->m_nJPEGTableSize,
231 2448 : static_cast<size_t>(nByteCount), 1, fpTIF) != 1)
232 0 : bError = true;
233 2448 : if (bError)
234 : {
235 0 : CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
236 0 : return CE_Failure;
237 : }
238 : }
239 : else
240 : {
241 : // If the JPEG strip/tile is too big (e.g. a single-strip
242 : // JPEG-in-TIFF), we will use /vsisparse mechanism to make a
243 : // fake JPEG file.
244 :
245 : osFileToOpen =
246 11 : CPLSPrintf("/vsisparse/%s", m_poGDS->m_osTmpFilename.c_str());
247 :
248 11 : if (VSIFPrintfL(fp,
249 : "<VSISparseFile><SubfileRegion>"
250 : "<Filename relative='0'>%s</Filename>"
251 : "<DestinationOffset>0</DestinationOffset>"
252 : "<SourceOffset>0</SourceOffset>"
253 : "<RegionLength>%d</RegionLength>"
254 : "</SubfileRegion>"
255 : "<SubfileRegion>"
256 : "<Filename relative='0'>%s</Filename>"
257 : "<DestinationOffset>%d</DestinationOffset>"
258 : "<SourceOffset>" CPL_FRMT_GUIB "</SourceOffset>"
259 : "<RegionLength>" CPL_FRMT_GUIB "</RegionLength>"
260 : "</SubfileRegion></VSISparseFile>",
261 : m_poGDS->m_osTmpFilenameJPEGTable.c_str(),
262 11 : static_cast<int>(m_poGDS->m_nJPEGTableSize),
263 11 : m_poGDS->m_poParentDS->GetDescription(),
264 11 : static_cast<int>(m_poGDS->m_nJPEGTableSize),
265 11 : nOffset, nByteCount) < 0)
266 : {
267 0 : CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
268 0 : return CE_Failure;
269 : }
270 : }
271 2459 : CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
272 :
273 2459 : const char *const apszDrivers[] = {"JPEG", nullptr};
274 :
275 : CPLConfigOptionSetter oJPEGtoRGBSetter(
276 : "GDAL_JPEG_TO_RGB",
277 4294 : m_poGDS->m_poParentDS->m_nPlanarConfig == PLANARCONFIG_CONTIG &&
278 1835 : m_poGDS->nBands == 4
279 : ? "NO"
280 : : "YES",
281 6753 : false);
282 :
283 2459 : m_poGDS->m_poJPEGDS.reset(
284 : GDALDataset::Open(osFileToOpen, GDAL_OF_RASTER | GDAL_OF_INTERNAL,
285 : apszDrivers, nullptr, nullptr));
286 :
287 2459 : if (m_poGDS->m_poJPEGDS != nullptr)
288 : {
289 : // Force all implicit overviews to be available, even for
290 : // small tiles.
291 : CPLConfigOptionSetter oInternalOverviewsSetter(
292 2459 : "JPEG_FORCE_INTERNAL_OVERVIEWS", "YES", false);
293 2459 : GDALGetOverviewCount(
294 2459 : GDALGetRasterBand(m_poGDS->m_poJPEGDS.get(), 1));
295 :
296 2459 : m_poGDS->m_nBlockId = nBlockId;
297 : }
298 : }
299 :
300 2764 : CPLErr eErr = CE_Failure;
301 2764 : if (m_poGDS->m_poJPEGDS)
302 : {
303 2764 : GDALDataset *l_poDS = m_poGDS->m_poJPEGDS.get();
304 :
305 2764 : int nReqXOff = 0;
306 2764 : int nReqYOff = 0;
307 2764 : int nReqXSize = 0;
308 2764 : int nReqYSize = 0;
309 2764 : if (bIsSingleStripAsSplit)
310 : {
311 2 : nReqYOff = nBlockYOff * nScaleFactor;
312 2 : nReqXSize = l_poDS->GetRasterXSize();
313 2 : nReqYSize = nScaleFactor;
314 : }
315 : else
316 : {
317 2762 : if (nBlockXSize == m_poGDS->GetRasterXSize())
318 : {
319 2340 : nReqXSize = l_poDS->GetRasterXSize();
320 : }
321 : else
322 : {
323 422 : nReqXSize = nBlockXSize * nScaleFactor;
324 : }
325 2762 : nReqYSize = nBlockYSize * nScaleFactor;
326 : }
327 2764 : int nBufXSize = nBlockXSize;
328 2764 : int nBufYSize = nBlockYSize;
329 2764 : if (nBlockXOff == m_poGDS->m_poParentDS->m_nBlocksPerRow - 1)
330 : {
331 2403 : nReqXSize = m_poGDS->m_poParentDS->nRasterXSize -
332 2403 : nBlockXOff * m_poGDS->m_poParentDS->m_nBlockXSize;
333 : }
334 2764 : if (nReqXOff + nReqXSize > l_poDS->GetRasterXSize())
335 : {
336 0 : nReqXSize = l_poDS->GetRasterXSize() - nReqXOff;
337 : }
338 2764 : if (!bIsSingleStripAsSplit &&
339 2762 : nBlockYOff == m_poGDS->m_poParentDS->m_nBlocksPerColumn - 1)
340 : {
341 170 : nReqYSize = m_poGDS->m_poParentDS->nRasterYSize -
342 170 : nBlockYOff * m_poGDS->m_poParentDS->m_nBlockYSize;
343 : }
344 2764 : if (nReqYOff + nReqYSize > l_poDS->GetRasterYSize())
345 : {
346 0 : nReqYSize = l_poDS->GetRasterYSize() - nReqYOff;
347 : }
348 2764 : if (nBlockXOff * nBlockXSize > m_poGDS->GetRasterXSize() - nBufXSize)
349 : {
350 60 : memset(pImage, 0,
351 60 : static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize *
352 60 : nDataTypeSize);
353 60 : nBufXSize = m_poGDS->GetRasterXSize() - nBlockXOff * nBlockXSize;
354 : }
355 2764 : if (nBlockYOff * nBlockYSize > m_poGDS->GetRasterYSize() - nBufYSize)
356 : {
357 111 : memset(pImage, 0,
358 111 : static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize *
359 111 : nDataTypeSize);
360 111 : nBufYSize = m_poGDS->GetRasterYSize() - nBlockYOff * nBlockYSize;
361 : }
362 :
363 2764 : const int nSrcBand =
364 2764 : m_poGDS->m_poParentDS->m_nPlanarConfig == PLANARCONFIG_SEPARATE
365 2764 : ? 1
366 : : nBand;
367 2764 : if (nSrcBand <= l_poDS->GetRasterCount())
368 : {
369 5528 : eErr = l_poDS->GetRasterBand(nSrcBand)->RasterIO(
370 : GF_Read, nReqXOff, nReqYOff, nReqXSize, nReqYSize, pImage,
371 : nBufXSize, nBufYSize, eDataType, 0,
372 2764 : static_cast<GPtrDiff_t>(nBlockXSize) * nDataTypeSize, nullptr);
373 : }
374 : }
375 :
376 2764 : return eErr;
377 : }
|