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