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