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