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