Line data Source code
1 : /**********************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: Dataset that modifies the orientation of an underlying dataset
5 : * Author: Even Rouault, <even dot rouault at spatialys dot com>
6 : *
7 : **********************************************************************
8 : * Copyright (c) 2022, Even Rouault, <even dot rouault at spatialys dot com>
9 : *
10 : * Permission is hereby granted, free of charge, to any person obtaining a
11 : * copy of this software and associated documentation files (the "Software"),
12 : * to deal in the Software without restriction, including without limitation
13 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 : * and/or sell copies of the Software, and to permit persons to whom the
15 : * Software is furnished to do so, subject to the following conditions:
16 : *
17 : * The above copyright notice and this permission notice shall be included
18 : * in all copies or substantial portions of the Software.
19 : *
20 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 : * DEALINGS IN THE SOFTWARE.
27 : ****************************************************************************/
28 :
29 : #include "gdalorienteddataset.h"
30 :
31 : #include "gdal_utils.h"
32 :
33 : #include <algorithm>
34 :
35 : //! @cond Doxygen_Suppress
36 :
37 : /************************************************************************/
38 : /* GDALOrientedRasterBand */
39 : /************************************************************************/
40 :
41 : class GDALOrientedRasterBand : public GDALRasterBand
42 : {
43 : GDALRasterBand *m_poSrcBand;
44 : std::unique_ptr<GDALDataset> m_poCacheDS{};
45 :
46 : GDALOrientedRasterBand(const GDALOrientedRasterBand &) = delete;
47 : GDALOrientedRasterBand &operator=(const GDALOrientedRasterBand &) = delete;
48 :
49 : protected:
50 : CPLErr IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage) override;
51 :
52 : public:
53 : GDALOrientedRasterBand(GDALOrientedDataset *poDSIn, int nBandIn);
54 :
55 7 : GDALColorInterp GetColorInterpretation() override
56 : {
57 7 : return m_poSrcBand->GetColorInterpretation();
58 : }
59 : };
60 :
61 : /************************************************************************/
62 : /* GDALOrientedRasterBand() */
63 : /************************************************************************/
64 :
65 14 : GDALOrientedRasterBand::GDALOrientedRasterBand(GDALOrientedDataset *poDSIn,
66 14 : int nBandIn)
67 14 : : m_poSrcBand(poDSIn->m_poSrcDS->GetRasterBand(nBandIn))
68 : {
69 14 : poDS = poDSIn;
70 14 : eDataType = m_poSrcBand->GetRasterDataType();
71 14 : if (poDSIn->m_eOrigin == GDALOrientedDataset::Origin::TOP_LEFT)
72 : {
73 0 : m_poSrcBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
74 : }
75 : else
76 : {
77 14 : nBlockXSize = poDS->GetRasterXSize();
78 14 : nBlockYSize = 1;
79 : }
80 14 : }
81 :
82 : /************************************************************************/
83 : /* FlipLineHorizontally() */
84 : /************************************************************************/
85 :
86 40 : static void FlipLineHorizontally(void *pLine, int nDTSize, int nBlockXSize)
87 : {
88 40 : switch (nDTSize)
89 : {
90 40 : case 1:
91 : {
92 40 : GByte *pabyLine = static_cast<GByte *>(pLine);
93 80 : for (int iX = 0; iX < nBlockXSize / 2; ++iX)
94 : {
95 40 : std::swap(pabyLine[iX], pabyLine[nBlockXSize - 1 - iX]);
96 : }
97 40 : break;
98 : }
99 :
100 0 : default:
101 : {
102 0 : GByte *pabyLine = static_cast<GByte *>(pLine);
103 0 : std::vector<GByte> abyTemp(nDTSize);
104 0 : for (int iX = 0; iX < nBlockXSize / 2; ++iX)
105 : {
106 0 : memcpy(&abyTemp[0], pabyLine + iX * nDTSize, nDTSize);
107 0 : memcpy(pabyLine + iX * nDTSize,
108 0 : pabyLine + (nBlockXSize - 1 - iX) * nDTSize, nDTSize);
109 0 : memcpy(pabyLine + (nBlockXSize - 1 - iX) * nDTSize, &abyTemp[0],
110 : nDTSize);
111 : }
112 0 : break;
113 : }
114 : }
115 40 : }
116 :
117 : /************************************************************************/
118 : /* IReadBlock() */
119 : /************************************************************************/
120 :
121 70 : CPLErr GDALOrientedRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
122 : void *pImage)
123 : {
124 70 : auto l_poDS = cpl::down_cast<GDALOrientedDataset *>(poDS);
125 :
126 70 : const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
127 70 : if (m_poCacheDS == nullptr &&
128 92 : l_poDS->m_eOrigin != GDALOrientedDataset::Origin::TOP_LEFT &&
129 22 : l_poDS->m_eOrigin != GDALOrientedDataset::Origin::TOP_RIGHT)
130 : {
131 12 : auto poGTiffDrv = GetGDALDriverManager()->GetDriverByName("GTiff");
132 12 : CPLStringList aosOptions;
133 12 : aosOptions.AddString("-f");
134 12 : aosOptions.AddString(poGTiffDrv ? "GTiff" : "MEM");
135 12 : aosOptions.AddString("-b");
136 12 : aosOptions.AddString(CPLSPrintf("%d", nBand));
137 12 : if (poGTiffDrv)
138 : {
139 12 : aosOptions.AddString("-co");
140 12 : aosOptions.AddString("TILED=YES");
141 : }
142 12 : std::string osTmpName;
143 12 : if (poGTiffDrv)
144 : {
145 12 : if (static_cast<GIntBig>(nRasterXSize) * nRasterYSize * nDTSize >
146 : 10 * 1024 * 1024)
147 : {
148 0 : osTmpName = CPLGenerateTempFilename(nullptr);
149 : }
150 : else
151 : {
152 : osTmpName =
153 12 : CPLSPrintf("/vsimem/_gdalorienteddataset/%p.tif", this);
154 : }
155 : }
156 : GDALTranslateOptions *psOptions =
157 12 : GDALTranslateOptionsNew(aosOptions.List(), nullptr);
158 12 : if (psOptions == nullptr)
159 0 : return CE_Failure;
160 12 : GDALDatasetH hOutDS = GDALTranslate(
161 : osTmpName.c_str(), GDALDataset::ToHandle(l_poDS->m_poSrcDS),
162 : psOptions, nullptr);
163 12 : GDALTranslateOptionsFree(psOptions);
164 12 : if (hOutDS == nullptr)
165 0 : return CE_Failure;
166 12 : m_poCacheDS.reset(GDALDataset::FromHandle(hOutDS));
167 12 : m_poCacheDS->MarkSuppressOnClose();
168 : }
169 :
170 70 : CPLErr eErr = CE_None;
171 70 : switch (l_poDS->m_eOrigin)
172 : {
173 0 : case GDALOrientedDataset::Origin::TOP_LEFT:
174 : {
175 0 : eErr = m_poSrcBand->ReadBlock(nBlockXOff, nBlockYOff, pImage);
176 0 : break;
177 : }
178 :
179 10 : case GDALOrientedDataset::Origin::TOP_RIGHT:
180 : {
181 10 : CPLAssert(nBlockXSize == nRasterXSize);
182 10 : CPLAssert(nBlockYSize == 1);
183 10 : if (m_poSrcBand->RasterIO(GF_Read, 0, nBlockYOff, nRasterXSize, 1,
184 : pImage, nRasterXSize, 1, eDataType, 0, 0,
185 10 : nullptr) != CE_None)
186 : {
187 0 : return CE_Failure;
188 : }
189 10 : FlipLineHorizontally(pImage, nDTSize, nBlockXSize);
190 10 : break;
191 : }
192 :
193 20 : case GDALOrientedDataset::Origin::BOT_RIGHT:
194 : case GDALOrientedDataset::Origin::BOT_LEFT:
195 : {
196 20 : CPLAssert(nBlockXSize == nRasterXSize);
197 20 : CPLAssert(nBlockYSize == 1);
198 20 : if (m_poCacheDS->GetRasterBand(1)->RasterIO(
199 20 : GF_Read, 0, nRasterYSize - 1 - nBlockYOff, nRasterXSize, 1,
200 : pImage, nRasterXSize, 1, eDataType, 0, 0,
201 20 : nullptr) != CE_None)
202 : {
203 0 : return CE_Failure;
204 : }
205 20 : if (l_poDS->m_eOrigin == GDALOrientedDataset::Origin::BOT_RIGHT)
206 10 : FlipLineHorizontally(pImage, nDTSize, nBlockXSize);
207 20 : break;
208 : }
209 :
210 20 : case GDALOrientedDataset::Origin::LEFT_TOP:
211 : case GDALOrientedDataset::Origin::RIGHT_TOP:
212 : {
213 20 : CPLAssert(nBlockXSize == nRasterXSize);
214 20 : CPLAssert(nBlockYSize == 1);
215 20 : if (m_poCacheDS->GetRasterBand(1)->RasterIO(
216 : GF_Read, nBlockYOff, 0, 1, nRasterXSize, pImage, 1,
217 20 : nRasterXSize, eDataType, 0, 0, nullptr) != CE_None)
218 : {
219 0 : return CE_Failure;
220 : }
221 20 : if (l_poDS->m_eOrigin == GDALOrientedDataset::Origin::RIGHT_TOP)
222 10 : FlipLineHorizontally(pImage, nDTSize, nBlockXSize);
223 20 : break;
224 : }
225 :
226 20 : case GDALOrientedDataset::Origin::RIGHT_BOT:
227 : case GDALOrientedDataset::Origin::LEFT_BOT:
228 : {
229 20 : CPLAssert(nBlockXSize == nRasterXSize);
230 20 : CPLAssert(nBlockYSize == 1);
231 20 : if (m_poCacheDS->GetRasterBand(1)->RasterIO(
232 20 : GF_Read, nRasterYSize - 1 - nBlockYOff, 0, 1, nRasterXSize,
233 : pImage, 1, nRasterXSize, eDataType, 0, 0,
234 20 : nullptr) != CE_None)
235 : {
236 0 : return CE_Failure;
237 : }
238 20 : if (l_poDS->m_eOrigin == GDALOrientedDataset::Origin::RIGHT_BOT)
239 10 : FlipLineHorizontally(pImage, nDTSize, nBlockXSize);
240 20 : break;
241 : }
242 : }
243 :
244 70 : return eErr;
245 : }
246 :
247 : /************************************************************************/
248 : /* GDALOrientedDataset() */
249 : /************************************************************************/
250 :
251 14 : GDALOrientedDataset::GDALOrientedDataset(GDALDataset *poSrcDataset,
252 14 : Origin eOrigin)
253 14 : : m_poSrcDS(poSrcDataset), m_eOrigin(eOrigin)
254 : {
255 14 : switch (eOrigin)
256 : {
257 6 : case GDALOrientedDataset::Origin::TOP_LEFT:
258 : case GDALOrientedDataset::Origin::TOP_RIGHT:
259 : case GDALOrientedDataset::Origin::BOT_RIGHT:
260 : case GDALOrientedDataset::Origin::BOT_LEFT:
261 : {
262 6 : nRasterXSize = poSrcDataset->GetRasterXSize();
263 6 : nRasterYSize = poSrcDataset->GetRasterYSize();
264 6 : break;
265 : }
266 :
267 8 : case GDALOrientedDataset::Origin::LEFT_TOP:
268 : case GDALOrientedDataset::Origin::RIGHT_TOP:
269 : case GDALOrientedDataset::Origin::RIGHT_BOT:
270 : case GDALOrientedDataset::Origin::LEFT_BOT:
271 : {
272 : // Permute (x, y)
273 8 : nRasterXSize = poSrcDataset->GetRasterYSize();
274 8 : nRasterYSize = poSrcDataset->GetRasterXSize();
275 8 : break;
276 : }
277 : }
278 :
279 14 : const int nSrcBands = poSrcDataset->GetRasterCount();
280 28 : for (int i = 1; i <= nSrcBands; ++i)
281 : {
282 14 : SetBand(i, new GDALOrientedRasterBand(this, i));
283 : }
284 14 : }
285 :
286 : /************************************************************************/
287 : /* GDALOrientedDataset() */
288 : /************************************************************************/
289 :
290 14 : GDALOrientedDataset::GDALOrientedDataset(
291 14 : std::unique_ptr<GDALDataset> &&poSrcDataset, Origin eOrigin)
292 14 : : GDALOrientedDataset(poSrcDataset.get(), eOrigin)
293 : {
294 : // cppcheck-suppress useInitializationList
295 14 : m_poSrcDSHolder = std::move(poSrcDataset);
296 14 : }
297 :
298 : /************************************************************************/
299 : /* GetMetadata() */
300 : /************************************************************************/
301 :
302 28 : char **GDALOrientedDataset::GetMetadata(const char *pszDomain)
303 : {
304 28 : if (pszDomain == nullptr || pszDomain[0] == '\0')
305 : {
306 14 : if (m_aosSrcMD.empty())
307 : {
308 7 : m_aosSrcMD.Assign(CSLDuplicate(m_poSrcDS->GetMetadata(pszDomain)));
309 : const char *pszOrientation =
310 7 : m_aosSrcMD.FetchNameValue("EXIF_Orientation");
311 7 : if (pszOrientation)
312 : {
313 : m_aosSrcMD.SetNameValue("original_EXIF_Orientation",
314 7 : pszOrientation);
315 7 : m_aosSrcMD.SetNameValue("EXIF_Orientation", nullptr);
316 : }
317 : }
318 14 : return m_aosSrcMD.List();
319 : }
320 14 : if (EQUAL(pszDomain, "EXIF"))
321 : {
322 14 : if (m_aosSrcMD_EXIF.empty())
323 : {
324 : m_aosSrcMD_EXIF.Assign(
325 7 : CSLDuplicate(m_poSrcDS->GetMetadata(pszDomain)));
326 : const char *pszOrientation =
327 7 : m_aosSrcMD_EXIF.FetchNameValue("EXIF_Orientation");
328 7 : if (pszOrientation)
329 : {
330 : m_aosSrcMD_EXIF.SetNameValue("original_EXIF_Orientation",
331 7 : pszOrientation);
332 7 : m_aosSrcMD_EXIF.SetNameValue("EXIF_Orientation", nullptr);
333 : }
334 : }
335 14 : return m_aosSrcMD_EXIF.List();
336 : }
337 0 : return m_poSrcDS->GetMetadata(pszDomain);
338 : }
339 :
340 : /************************************************************************/
341 : /* GetMetadataItem() */
342 : /************************************************************************/
343 :
344 28 : const char *GDALOrientedDataset::GetMetadataItem(const char *pszName,
345 : const char *pszDomain)
346 : {
347 28 : return CSLFetchNameValue(GetMetadata(pszDomain), pszName);
348 : }
349 :
350 : //! @endcond
|