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