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