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 : NAMESPACE_MRF_START
53 :
54 : // Returns a unique filename
55 10 : static CPLString uniq_memfname(const char *prefix)
56 : {
57 : // Define MRF_LOCAL_TMP to use local files instead of RAM
58 : // #define MRF_LOCAL_TMP
59 : #if defined(MRF_LOCAL_TMP)
60 : return CPLGenerateTempFilename(prefix);
61 : #else
62 10 : return VSIMemGenerateHiddenFilename(prefix);
63 : #endif
64 : }
65 :
66 : //
67 : // Uses GDAL to create a temporary TIF file, using the band create options
68 : // copies the content to the destination buffer then erases the temp TIF
69 : //
70 5 : static CPLErr CompressTIF(buf_mgr &dst, const buf_mgr &src, const ILImage &img,
71 : char **papszOptions)
72 : {
73 : CPLErr ret;
74 5 : GDALDriver *poTiffDriver = GetGDALDriverManager()->GetDriverByName("GTiff");
75 : VSIStatBufL statb;
76 10 : CPLString fname = uniq_memfname("mrf_tif_write");
77 :
78 : GDALDataset *poTiff =
79 5 : poTiffDriver->Create(fname, img.pagesize.x, img.pagesize.y,
80 5 : img.pagesize.c, img.dt, papszOptions);
81 :
82 5 : if (nullptr == poTiff)
83 0 : return CE_Failure;
84 :
85 : // Write directly to avoid double caching in GDAL
86 : // Unfortunately not possible for multiple bands
87 5 : if (img.pagesize.c == 1)
88 5 : ret = poTiff->GetRasterBand(1)->WriteBlock(0, 0, src.buffer);
89 : else
90 : ret =
91 0 : poTiff->RasterIO(GF_Write, 0, 0, img.pagesize.x, img.pagesize.y,
92 0 : src.buffer, img.pagesize.x, img.pagesize.y, img.dt,
93 0 : img.pagesize.c, nullptr, 0, 0, 0, nullptr);
94 5 : if (CE_None != ret)
95 0 : return ret;
96 5 : GDALClose(poTiff);
97 :
98 : // Check that we can read the file
99 5 : if (VSIStatL(fname, &statb))
100 : {
101 0 : CPLError(CE_Failure, CPLE_AppDefined, "MRF: TIFF, can't stat %s",
102 : fname.c_str());
103 0 : return CE_Failure;
104 : }
105 :
106 5 : if (static_cast<size_t>(statb.st_size) > dst.size)
107 : {
108 0 : CPLError(CE_Failure, CPLE_AppDefined,
109 : "MRF: TIFF, Tiff generated is too large");
110 0 : return CE_Failure;
111 : }
112 :
113 5 : VSILFILE *pf = VSIFOpenL(fname, "rb");
114 5 : if (pf == nullptr)
115 : {
116 0 : CPLError(CE_Failure, CPLE_AppDefined, "MRF: TIFF, can't open %s",
117 : fname.c_str());
118 0 : return CE_Failure;
119 : }
120 :
121 5 : VSIFReadL(dst.buffer, static_cast<size_t>(statb.st_size), 1, pf);
122 5 : dst.size = static_cast<size_t>(statb.st_size);
123 5 : VSIFCloseL(pf);
124 5 : VSIUnlink(fname);
125 :
126 5 : return CE_None;
127 : }
128 :
129 : // Read from a RAM Tiff. This is rather generic
130 : // cppcheck-suppress constParameterReference
131 5 : static CPLErr DecompressTIF(buf_mgr &dst, const buf_mgr &src,
132 : const ILImage &img)
133 : {
134 10 : CPLString fname = uniq_memfname("mrf_tif_read");
135 : VSILFILE *fp =
136 5 : VSIFileFromMemBuffer(fname, (GByte *)(src.buffer), src.size, false);
137 : // Comes back opened, but we can't use it
138 5 : if (nullptr == fp)
139 : {
140 0 : CPLError(CE_Failure, CPLE_AppDefined,
141 : "MRF: TIFF, can't open %s as a temp file", fname.c_str());
142 0 : return CE_Failure;
143 : }
144 5 : VSIFCloseL(fp);
145 :
146 : static const char *const apszAllowedDrivers[] = {"GTiff", nullptr};
147 5 : GDALDataset *poTiff = GDALDataset::FromHandle(GDALOpenEx(
148 : fname, GDAL_OF_RASTER, apszAllowedDrivers, nullptr, nullptr));
149 :
150 5 : if (poTiff == nullptr || 0 == poTiff->GetRasterCount())
151 : {
152 0 : CPLError(CE_Failure, CPLE_AppDefined,
153 : "MRF: Can't open page as a raster Tiff");
154 0 : GDALClose(poTiff); // safe to call on nullptr
155 0 : VSIUnlink(fname);
156 0 : return CE_Failure;
157 : }
158 :
159 5 : const GDALDataType eGTiffDT = poTiff->GetRasterBand(1)->GetRasterDataType();
160 5 : const int nDTSize = GDALGetDataTypeSizeBytes(eGTiffDT);
161 5 : if (poTiff->GetRasterXSize() != img.pagesize.x ||
162 5 : poTiff->GetRasterYSize() != img.pagesize.y ||
163 15 : poTiff->GetRasterCount() != img.pagesize.c || img.dt != eGTiffDT ||
164 5 : static_cast<size_t>(img.pagesize.x) * img.pagesize.y * nDTSize *
165 5 : img.pagesize.c !=
166 5 : dst.size)
167 : {
168 0 : CPLError(CE_Failure, CPLE_AppDefined,
169 : "MRF: TIFF tile inconsistent with MRF parameters");
170 0 : GDALClose(poTiff);
171 0 : VSIUnlink(fname);
172 0 : return CE_Failure;
173 : }
174 :
175 : CPLErr ret;
176 : // Bypass the GDAL caching if single band and block size is right
177 5 : int nBlockXSize = 0, nBlockYSize = 0;
178 5 : poTiff->GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
179 :
180 : // Allow for TIFF blocks to be larger than MRF page size, but not in
181 : // huge proportion, to avoid later attempts at allocating a lot of memory
182 5 : if ((nBlockXSize > 4096 && nBlockXSize > img.pagesize.x) ||
183 5 : (nBlockYSize > 4096 && nBlockYSize > img.pagesize.y))
184 : {
185 0 : CPLError(CE_Failure, CPLE_AppDefined,
186 : "MRF: TIFF block size inconsistent with MRF parameters");
187 0 : GDALClose(poTiff);
188 0 : VSIUnlink(fname);
189 0 : return CE_Failure;
190 : }
191 :
192 5 : if (img.pagesize.c == 1 && nBlockXSize == img.pagesize.x &&
193 5 : nBlockYSize == img.pagesize.y)
194 5 : ret = poTiff->GetRasterBand(1)->ReadBlock(0, 0, dst.buffer);
195 : else
196 0 : ret = poTiff->RasterIO(
197 0 : GF_Read, 0, 0, img.pagesize.x, img.pagesize.y, dst.buffer,
198 0 : img.pagesize.x, img.pagesize.y, img.dt, img.pagesize.c, nullptr,
199 0 : static_cast<GSpacing>(nDTSize) * img.pagesize.c,
200 0 : static_cast<GSpacing>(nDTSize) * img.pagesize.c * img.pagesize.x,
201 : nDTSize, nullptr);
202 :
203 5 : GDALClose(poTiff);
204 5 : VSIUnlink(fname);
205 5 : return ret;
206 : }
207 :
208 5 : CPLErr TIF_Band::Decompress(buf_mgr &dst, buf_mgr &src)
209 : {
210 5 : return DecompressTIF(dst, src, img);
211 : }
212 :
213 5 : CPLErr TIF_Band::Compress(buf_mgr &dst, buf_mgr &src)
214 : {
215 5 : return CompressTIF(dst, src, img, papszOptions);
216 : }
217 :
218 15 : TIF_Band::TIF_Band(MRFDataset *pDS, const ILImage &image, int b, int level)
219 15 : : MRFRasterBand(pDS, image, b, int(level))
220 : {
221 : // Increase the page buffer by 1K in case Tiff expands data
222 15 : pDS->SetPBufferSize(image.pageSizeBytes + 1024);
223 :
224 : // Static create options for TIFF tiles
225 15 : papszOptions = CSLAddNameValue(nullptr, "COMPRESS", "DEFLATE");
226 15 : papszOptions = CSLAddNameValue(papszOptions, "TILED", "Yes");
227 15 : papszOptions = CSLAddNameValue(papszOptions, "BLOCKXSIZE",
228 30 : CPLOPrintf("%d", img.pagesize.x));
229 15 : papszOptions = CSLAddNameValue(papszOptions, "BLOCKYSIZE",
230 30 : CPLOPrintf("%d", img.pagesize.y));
231 15 : int q = img.quality / 10;
232 : // Move down so the default 85 quality maps to ZLEVEL 6. This makes the max
233 : // ZLEVEL 8, which is fine.
234 15 : if (q > 2)
235 15 : q -= 2;
236 15 : if (q == 0) // TIF does not accept ZLEVEL of zero
237 0 : q = 6;
238 15 : papszOptions = CSLAddNameValue(papszOptions, "ZLEVEL", CPLOPrintf("%d", q));
239 15 : }
240 :
241 30 : TIF_Band::~TIF_Band()
242 : {
243 15 : CSLDestroy(papszOptions);
244 30 : }
245 :
246 : NAMESPACE_MRF_END
|