Line data Source code
1 : /*
2 : * Copyright (c) 2002-2012, California Institute of Technology.
3 : * All rights reserved. Based on Government Sponsored Research under contracts
4 : * NAS7-1407 and/or NAS7-03001. Redistribution and use in source and binary
5 : * forms, with or without modification, are permitted provided that the
6 : * following conditions are met:
7 : * 1. Redistributions of source code must retain the above copyright notice,
8 : * this list of conditions and the following disclaimer.
9 : * 2. Redistributions in binary form must reproduce the above copyright
10 : * notice, this list of conditions and the following disclaimer in the
11 : * documentation and/or other materials provided with the distribution.
12 : * 3. Neither the name of the California Institute of Technology (Caltech),
13 : * its operating division the Jet Propulsion Laboratory (JPL), the National
14 : * Aeronautics and Space Administration (NASA), nor the names of its
15 : * contributors may be used to endorse or promote products derived from this
16 : * software without specific prior written permission.
17 : *
18 : * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 : * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 : * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 : * ARE DISCLAIMED. IN NO EVENT SHALL THE CALIFORNIA INSTITUTE OF TECHNOLOGY BE
22 : * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 : * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 : * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 : * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 : * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 : * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 : * POSSIBILITY OF SUCH DAMAGE.
29 : *
30 : * Copyright 2014-2021 Esri
31 : *
32 : * Licensed under the Apache License, Version 2.0 (the "License");
33 : * you may not use this file except in compliance with the License.
34 : * You may obtain a copy of the License at
35 : *
36 : * http://www.apache.org/licenses/LICENSE-2.0
37 : *
38 : * Unless required by applicable law or agreed to in writing, software
39 : * distributed under the License is distributed on an "AS IS" BASIS,
40 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
41 : * See the License for the specific language governing permissions and
42 : * limitations under the License.
43 : *
44 : * TIFF band
45 : * TIFF page compression and decompression functions
46 : *
47 : * Author: Lucian Plesea, lplesea esri.com
48 : *
49 : */
50 :
51 : #include "marfa.h"
52 :
53 : #include "gdal_driver.h"
54 : #include "gdal_drivermanager.h"
55 :
56 : NAMESPACE_MRF_START
57 :
58 : // Returns a unique filename
59 10 : static CPLString uniq_memfname(const char *prefix)
60 : {
61 : // Define MRF_LOCAL_TMP to use local files instead of RAM
62 : // #define MRF_LOCAL_TMP
63 : #if defined(MRF_LOCAL_TMP)
64 : return CPLGenerateTempFilenameSafe(prefix);
65 : #else
66 10 : return VSIMemGenerateHiddenFilename(prefix);
67 : #endif
68 : }
69 :
70 : //
71 : // Uses GDAL to create a temporary TIF file, using the band create options
72 : // copies the content to the destination buffer then erases the temp TIF
73 : //
74 5 : static CPLErr CompressTIF(buf_mgr &dst, const buf_mgr &src, const ILImage &img,
75 : char **papszOptions)
76 : {
77 : CPLErr ret;
78 5 : GDALDriver *poTiffDriver = GetGDALDriverManager()->GetDriverByName("GTiff");
79 : VSIStatBufL statb;
80 10 : CPLString fname = uniq_memfname("mrf_tif_write");
81 :
82 : GDALDataset *poTiff =
83 5 : poTiffDriver->Create(fname, img.pagesize.x, img.pagesize.y,
84 5 : img.pagesize.c, img.dt, papszOptions);
85 :
86 5 : if (nullptr == poTiff)
87 0 : return CE_Failure;
88 :
89 : // Write directly to avoid double caching in GDAL
90 : // Unfortunately not possible for multiple bands
91 5 : if (img.pagesize.c == 1)
92 5 : ret = poTiff->GetRasterBand(1)->WriteBlock(0, 0, src.buffer);
93 : else
94 : ret =
95 0 : poTiff->RasterIO(GF_Write, 0, 0, img.pagesize.x, img.pagesize.y,
96 0 : src.buffer, img.pagesize.x, img.pagesize.y, img.dt,
97 0 : img.pagesize.c, nullptr, 0, 0, 0, nullptr);
98 5 : if (CE_None != ret)
99 0 : return ret;
100 5 : GDALClose(poTiff);
101 :
102 : // Check that we can read the file
103 5 : if (VSIStatL(fname, &statb))
104 : {
105 0 : CPLError(CE_Failure, CPLE_AppDefined, "MRF: TIFF, can't stat %s",
106 : fname.c_str());
107 0 : return CE_Failure;
108 : }
109 :
110 5 : if (static_cast<size_t>(statb.st_size) > dst.size)
111 : {
112 0 : CPLError(CE_Failure, CPLE_AppDefined,
113 : "MRF: TIFF, Tiff generated is too large");
114 0 : return CE_Failure;
115 : }
116 :
117 5 : VSILFILE *pf = VSIFOpenL(fname, "rb");
118 5 : if (pf == nullptr)
119 : {
120 0 : CPLError(CE_Failure, CPLE_AppDefined, "MRF: TIFF, can't open %s",
121 : fname.c_str());
122 0 : return CE_Failure;
123 : }
124 :
125 5 : VSIFReadL(dst.buffer, static_cast<size_t>(statb.st_size), 1, pf);
126 5 : dst.size = static_cast<size_t>(statb.st_size);
127 5 : VSIFCloseL(pf);
128 5 : VSIUnlink(fname);
129 :
130 5 : return CE_None;
131 : }
132 :
133 : // Read from a RAM Tiff. This is rather generic
134 : // cppcheck-suppress constParameterReference
135 5 : static CPLErr DecompressTIF(buf_mgr &dst, const buf_mgr &src,
136 : const ILImage &img)
137 : {
138 10 : CPLString fname = uniq_memfname("mrf_tif_read");
139 : VSILFILE *fp =
140 5 : VSIFileFromMemBuffer(fname, (GByte *)(src.buffer), src.size, false);
141 : // Comes back opened, but we can't use it
142 5 : if (nullptr == fp)
143 : {
144 0 : CPLError(CE_Failure, CPLE_AppDefined,
145 : "MRF: TIFF, can't open %s as a temp file", fname.c_str());
146 0 : return CE_Failure;
147 : }
148 5 : VSIFCloseL(fp);
149 :
150 : static const char *const apszAllowedDrivers[] = {"GTiff", nullptr};
151 5 : GDALDataset *poTiff = GDALDataset::FromHandle(GDALOpenEx(
152 : fname, GDAL_OF_RASTER, apszAllowedDrivers, nullptr, nullptr));
153 :
154 5 : if (poTiff == nullptr || 0 == poTiff->GetRasterCount())
155 : {
156 0 : CPLError(CE_Failure, CPLE_AppDefined,
157 : "MRF: Can't open page as a raster Tiff");
158 0 : GDALClose(poTiff); // safe to call on nullptr
159 0 : VSIUnlink(fname);
160 0 : return CE_Failure;
161 : }
162 :
163 5 : const GDALDataType eGTiffDT = poTiff->GetRasterBand(1)->GetRasterDataType();
164 5 : const int nDTSize = GDALGetDataTypeSizeBytes(eGTiffDT);
165 5 : if (poTiff->GetRasterXSize() != img.pagesize.x ||
166 5 : poTiff->GetRasterYSize() != img.pagesize.y ||
167 15 : poTiff->GetRasterCount() != img.pagesize.c || img.dt != eGTiffDT ||
168 5 : static_cast<size_t>(img.pagesize.x) * img.pagesize.y * nDTSize *
169 5 : img.pagesize.c !=
170 5 : dst.size)
171 : {
172 0 : CPLError(CE_Failure, CPLE_AppDefined,
173 : "MRF: TIFF tile inconsistent with MRF parameters");
174 0 : GDALClose(poTiff);
175 0 : VSIUnlink(fname);
176 0 : return CE_Failure;
177 : }
178 :
179 : CPLErr ret;
180 : // Bypass the GDAL caching if single band and block size is right
181 5 : int nBlockXSize = 0, nBlockYSize = 0;
182 5 : poTiff->GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
183 :
184 : // Allow for TIFF blocks to be larger than MRF page size, but not in
185 : // huge proportion, to avoid later attempts at allocating a lot of memory
186 5 : if ((nBlockXSize > 4096 && nBlockXSize > img.pagesize.x) ||
187 5 : (nBlockYSize > 4096 && nBlockYSize > img.pagesize.y))
188 : {
189 0 : CPLError(CE_Failure, CPLE_AppDefined,
190 : "MRF: TIFF block size inconsistent with MRF parameters");
191 0 : GDALClose(poTiff);
192 0 : VSIUnlink(fname);
193 0 : return CE_Failure;
194 : }
195 :
196 5 : if (img.pagesize.c == 1 && nBlockXSize == img.pagesize.x &&
197 5 : nBlockYSize == img.pagesize.y)
198 5 : ret = poTiff->GetRasterBand(1)->ReadBlock(0, 0, dst.buffer);
199 : else
200 0 : ret = poTiff->RasterIO(
201 0 : GF_Read, 0, 0, img.pagesize.x, img.pagesize.y, dst.buffer,
202 0 : img.pagesize.x, img.pagesize.y, img.dt, img.pagesize.c, nullptr,
203 0 : static_cast<GSpacing>(nDTSize) * img.pagesize.c,
204 0 : static_cast<GSpacing>(nDTSize) * img.pagesize.c * img.pagesize.x,
205 : nDTSize, nullptr);
206 :
207 5 : GDALClose(poTiff);
208 5 : VSIUnlink(fname);
209 5 : return ret;
210 : }
211 :
212 5 : CPLErr TIF_Band::Decompress(buf_mgr &dst, buf_mgr &src)
213 : {
214 5 : return DecompressTIF(dst, src, img);
215 : }
216 :
217 5 : CPLErr TIF_Band::Compress(buf_mgr &dst, buf_mgr &src)
218 : {
219 5 : return CompressTIF(dst, src, img, papszOptions);
220 : }
221 :
222 15 : TIF_Band::TIF_Band(MRFDataset *pDS, const ILImage &image, int b, int level)
223 15 : : MRFRasterBand(pDS, image, b, int(level))
224 : {
225 : // Increase the page buffer by 1K in case Tiff expands data
226 15 : pDS->SetPBufferSize(image.pageSizeBytes + 1024);
227 :
228 : // Static create options for TIFF tiles
229 15 : papszOptions = CSLAddNameValue(nullptr, "COMPRESS", "DEFLATE");
230 15 : papszOptions = CSLAddNameValue(papszOptions, "TILED", "Yes");
231 15 : papszOptions = CSLAddNameValue(papszOptions, "BLOCKXSIZE",
232 30 : CPLOPrintf("%d", img.pagesize.x));
233 15 : papszOptions = CSLAddNameValue(papszOptions, "BLOCKYSIZE",
234 30 : CPLOPrintf("%d", img.pagesize.y));
235 15 : int q = img.quality / 10;
236 : // Move down so the default 85 quality maps to ZLEVEL 6. This makes the max
237 : // ZLEVEL 8, which is fine.
238 15 : if (q > 2)
239 15 : q -= 2;
240 15 : if (q == 0) // TIF does not accept ZLEVEL of zero
241 0 : q = 6;
242 15 : papszOptions = CSLAddNameValue(papszOptions, "ZLEVEL", CPLOPrintf("%d", q));
243 15 : }
244 :
245 30 : TIF_Band::~TIF_Band()
246 : {
247 15 : CSLDestroy(papszOptions);
248 30 : }
249 :
250 : NAMESPACE_MRF_END
|