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 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "gdalorienteddataset.h"
14 :
15 : #include "gdal_utils.h"
16 :
17 : #include <algorithm>
18 :
19 : //! @cond Doxygen_Suppress
20 :
21 : /************************************************************************/
22 : /* GDALOrientedRasterBand */
23 : /************************************************************************/
24 :
25 : class GDALOrientedRasterBand : public GDALRasterBand
26 : {
27 : GDALRasterBand *m_poSrcBand;
28 : std::unique_ptr<GDALDataset> m_poCacheDS{};
29 :
30 : GDALOrientedRasterBand(const GDALOrientedRasterBand &) = delete;
31 : GDALOrientedRasterBand &operator=(const GDALOrientedRasterBand &) = delete;
32 :
33 : protected:
34 : CPLErr IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage) override;
35 :
36 : public:
37 : GDALOrientedRasterBand(GDALOrientedDataset *poDSIn, int nBandIn);
38 :
39 7 : GDALColorInterp GetColorInterpretation() override
40 : {
41 7 : return m_poSrcBand->GetColorInterpretation();
42 : }
43 : };
44 :
45 : /************************************************************************/
46 : /* GDALOrientedRasterBand() */
47 : /************************************************************************/
48 :
49 14 : GDALOrientedRasterBand::GDALOrientedRasterBand(GDALOrientedDataset *poDSIn,
50 14 : int nBandIn)
51 14 : : m_poSrcBand(poDSIn->m_poSrcDS->GetRasterBand(nBandIn))
52 : {
53 14 : poDS = poDSIn;
54 14 : eDataType = m_poSrcBand->GetRasterDataType();
55 14 : if (poDSIn->m_eOrigin == GDALOrientedDataset::Origin::TOP_LEFT)
56 : {
57 0 : m_poSrcBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
58 : }
59 : else
60 : {
61 14 : nBlockXSize = poDS->GetRasterXSize();
62 14 : nBlockYSize = 1;
63 : }
64 14 : }
65 :
66 : /************************************************************************/
67 : /* FlipLineHorizontally() */
68 : /************************************************************************/
69 :
70 40 : static void FlipLineHorizontally(void *pLine, int nDTSize, int nBlockXSize)
71 : {
72 40 : switch (nDTSize)
73 : {
74 40 : case 1:
75 : {
76 40 : GByte *pabyLine = static_cast<GByte *>(pLine);
77 80 : for (int iX = 0; iX < nBlockXSize / 2; ++iX)
78 : {
79 40 : std::swap(pabyLine[iX], pabyLine[nBlockXSize - 1 - iX]);
80 : }
81 40 : break;
82 : }
83 :
84 0 : default:
85 : {
86 0 : GByte *pabyLine = static_cast<GByte *>(pLine);
87 0 : std::vector<GByte> abyTemp(nDTSize);
88 0 : for (int iX = 0; iX < nBlockXSize / 2; ++iX)
89 : {
90 0 : memcpy(&abyTemp[0], pabyLine + iX * nDTSize, nDTSize);
91 0 : memcpy(pabyLine + iX * nDTSize,
92 0 : pabyLine + (nBlockXSize - 1 - iX) * nDTSize, nDTSize);
93 0 : memcpy(pabyLine + (nBlockXSize - 1 - iX) * nDTSize, &abyTemp[0],
94 : nDTSize);
95 : }
96 0 : break;
97 : }
98 : }
99 40 : }
100 :
101 : /************************************************************************/
102 : /* IReadBlock() */
103 : /************************************************************************/
104 :
105 70 : CPLErr GDALOrientedRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
106 : void *pImage)
107 : {
108 70 : auto l_poDS = cpl::down_cast<GDALOrientedDataset *>(poDS);
109 :
110 70 : const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
111 70 : if (m_poCacheDS == nullptr &&
112 92 : l_poDS->m_eOrigin != GDALOrientedDataset::Origin::TOP_LEFT &&
113 22 : l_poDS->m_eOrigin != GDALOrientedDataset::Origin::TOP_RIGHT)
114 : {
115 12 : auto poGTiffDrv = GetGDALDriverManager()->GetDriverByName("GTiff");
116 12 : CPLStringList aosOptions;
117 12 : aosOptions.AddString("-f");
118 12 : aosOptions.AddString(poGTiffDrv ? "GTiff" : "MEM");
119 12 : aosOptions.AddString("-b");
120 12 : aosOptions.AddString(CPLSPrintf("%d", nBand));
121 12 : if (poGTiffDrv)
122 : {
123 12 : aosOptions.AddString("-co");
124 12 : aosOptions.AddString("TILED=YES");
125 : }
126 12 : std::string osTmpName;
127 12 : if (poGTiffDrv)
128 : {
129 12 : if (static_cast<GIntBig>(nRasterXSize) * nRasterYSize * nDTSize >
130 : 10 * 1024 * 1024)
131 : {
132 0 : osTmpName = CPLGenerateTempFilename(nullptr);
133 : }
134 : else
135 : {
136 12 : osTmpName = VSIMemGenerateHiddenFilename(nullptr);
137 : }
138 : }
139 : GDALTranslateOptions *psOptions =
140 12 : GDALTranslateOptionsNew(aosOptions.List(), nullptr);
141 12 : if (psOptions == nullptr)
142 0 : return CE_Failure;
143 12 : GDALDatasetH hOutDS = GDALTranslate(
144 : osTmpName.c_str(), GDALDataset::ToHandle(l_poDS->m_poSrcDS),
145 : psOptions, nullptr);
146 12 : GDALTranslateOptionsFree(psOptions);
147 12 : if (hOutDS == nullptr)
148 0 : return CE_Failure;
149 12 : m_poCacheDS.reset(GDALDataset::FromHandle(hOutDS));
150 12 : m_poCacheDS->MarkSuppressOnClose();
151 : }
152 :
153 70 : CPLErr eErr = CE_None;
154 70 : switch (l_poDS->m_eOrigin)
155 : {
156 0 : case GDALOrientedDataset::Origin::TOP_LEFT:
157 : {
158 0 : eErr = m_poSrcBand->ReadBlock(nBlockXOff, nBlockYOff, pImage);
159 0 : break;
160 : }
161 :
162 10 : case GDALOrientedDataset::Origin::TOP_RIGHT:
163 : {
164 10 : CPLAssert(nBlockXSize == nRasterXSize);
165 10 : CPLAssert(nBlockYSize == 1);
166 10 : if (m_poSrcBand->RasterIO(GF_Read, 0, nBlockYOff, nRasterXSize, 1,
167 : pImage, nRasterXSize, 1, eDataType, 0, 0,
168 10 : nullptr) != CE_None)
169 : {
170 0 : return CE_Failure;
171 : }
172 10 : FlipLineHorizontally(pImage, nDTSize, nBlockXSize);
173 10 : break;
174 : }
175 :
176 20 : case GDALOrientedDataset::Origin::BOT_RIGHT:
177 : case GDALOrientedDataset::Origin::BOT_LEFT:
178 : {
179 20 : CPLAssert(nBlockXSize == nRasterXSize);
180 20 : CPLAssert(nBlockYSize == 1);
181 20 : if (m_poCacheDS->GetRasterBand(1)->RasterIO(
182 20 : GF_Read, 0, nRasterYSize - 1 - nBlockYOff, nRasterXSize, 1,
183 : pImage, nRasterXSize, 1, eDataType, 0, 0,
184 20 : nullptr) != CE_None)
185 : {
186 0 : return CE_Failure;
187 : }
188 20 : if (l_poDS->m_eOrigin == GDALOrientedDataset::Origin::BOT_RIGHT)
189 10 : FlipLineHorizontally(pImage, nDTSize, nBlockXSize);
190 20 : break;
191 : }
192 :
193 20 : case GDALOrientedDataset::Origin::LEFT_TOP:
194 : case GDALOrientedDataset::Origin::RIGHT_TOP:
195 : {
196 20 : CPLAssert(nBlockXSize == nRasterXSize);
197 20 : CPLAssert(nBlockYSize == 1);
198 20 : if (m_poCacheDS->GetRasterBand(1)->RasterIO(
199 : GF_Read, nBlockYOff, 0, 1, nRasterXSize, pImage, 1,
200 20 : nRasterXSize, eDataType, 0, 0, nullptr) != CE_None)
201 : {
202 0 : return CE_Failure;
203 : }
204 20 : if (l_poDS->m_eOrigin == GDALOrientedDataset::Origin::RIGHT_TOP)
205 10 : FlipLineHorizontally(pImage, nDTSize, nBlockXSize);
206 20 : break;
207 : }
208 :
209 20 : case GDALOrientedDataset::Origin::RIGHT_BOT:
210 : case GDALOrientedDataset::Origin::LEFT_BOT:
211 : {
212 20 : CPLAssert(nBlockXSize == nRasterXSize);
213 20 : CPLAssert(nBlockYSize == 1);
214 20 : if (m_poCacheDS->GetRasterBand(1)->RasterIO(
215 20 : GF_Read, nRasterYSize - 1 - nBlockYOff, 0, 1, nRasterXSize,
216 : pImage, 1, nRasterXSize, eDataType, 0, 0,
217 20 : nullptr) != CE_None)
218 : {
219 0 : return CE_Failure;
220 : }
221 20 : if (l_poDS->m_eOrigin == GDALOrientedDataset::Origin::RIGHT_BOT)
222 10 : FlipLineHorizontally(pImage, nDTSize, nBlockXSize);
223 20 : break;
224 : }
225 : }
226 :
227 70 : return eErr;
228 : }
229 :
230 : /************************************************************************/
231 : /* GDALOrientedDataset() */
232 : /************************************************************************/
233 :
234 14 : GDALOrientedDataset::GDALOrientedDataset(GDALDataset *poSrcDataset,
235 14 : Origin eOrigin)
236 14 : : m_poSrcDS(poSrcDataset), m_eOrigin(eOrigin)
237 : {
238 14 : switch (eOrigin)
239 : {
240 6 : case GDALOrientedDataset::Origin::TOP_LEFT:
241 : case GDALOrientedDataset::Origin::TOP_RIGHT:
242 : case GDALOrientedDataset::Origin::BOT_RIGHT:
243 : case GDALOrientedDataset::Origin::BOT_LEFT:
244 : {
245 6 : nRasterXSize = poSrcDataset->GetRasterXSize();
246 6 : nRasterYSize = poSrcDataset->GetRasterYSize();
247 6 : break;
248 : }
249 :
250 8 : case GDALOrientedDataset::Origin::LEFT_TOP:
251 : case GDALOrientedDataset::Origin::RIGHT_TOP:
252 : case GDALOrientedDataset::Origin::RIGHT_BOT:
253 : case GDALOrientedDataset::Origin::LEFT_BOT:
254 : {
255 : // Permute (x, y)
256 8 : nRasterXSize = poSrcDataset->GetRasterYSize();
257 8 : nRasterYSize = poSrcDataset->GetRasterXSize();
258 8 : break;
259 : }
260 : }
261 :
262 14 : const int nSrcBands = poSrcDataset->GetRasterCount();
263 28 : for (int i = 1; i <= nSrcBands; ++i)
264 : {
265 14 : SetBand(i, new GDALOrientedRasterBand(this, i));
266 : }
267 14 : }
268 :
269 : /************************************************************************/
270 : /* GDALOrientedDataset() */
271 : /************************************************************************/
272 :
273 14 : GDALOrientedDataset::GDALOrientedDataset(
274 14 : std::unique_ptr<GDALDataset> &&poSrcDataset, Origin eOrigin)
275 14 : : GDALOrientedDataset(poSrcDataset.get(), eOrigin)
276 : {
277 : // cppcheck-suppress useInitializationList
278 14 : m_poSrcDSHolder = std::move(poSrcDataset);
279 14 : }
280 :
281 : /************************************************************************/
282 : /* GetMetadata() */
283 : /************************************************************************/
284 :
285 28 : char **GDALOrientedDataset::GetMetadata(const char *pszDomain)
286 : {
287 28 : if (pszDomain == nullptr || pszDomain[0] == '\0')
288 : {
289 14 : if (m_aosSrcMD.empty())
290 : {
291 7 : m_aosSrcMD.Assign(CSLDuplicate(m_poSrcDS->GetMetadata(pszDomain)));
292 : const char *pszOrientation =
293 7 : m_aosSrcMD.FetchNameValue("EXIF_Orientation");
294 7 : if (pszOrientation)
295 : {
296 : m_aosSrcMD.SetNameValue("original_EXIF_Orientation",
297 7 : pszOrientation);
298 7 : m_aosSrcMD.SetNameValue("EXIF_Orientation", nullptr);
299 : }
300 : }
301 14 : return m_aosSrcMD.List();
302 : }
303 14 : if (EQUAL(pszDomain, "EXIF"))
304 : {
305 14 : if (m_aosSrcMD_EXIF.empty())
306 : {
307 : m_aosSrcMD_EXIF.Assign(
308 7 : CSLDuplicate(m_poSrcDS->GetMetadata(pszDomain)));
309 : const char *pszOrientation =
310 7 : m_aosSrcMD_EXIF.FetchNameValue("EXIF_Orientation");
311 7 : if (pszOrientation)
312 : {
313 : m_aosSrcMD_EXIF.SetNameValue("original_EXIF_Orientation",
314 7 : pszOrientation);
315 7 : m_aosSrcMD_EXIF.SetNameValue("EXIF_Orientation", nullptr);
316 : }
317 : }
318 14 : return m_aosSrcMD_EXIF.List();
319 : }
320 0 : return m_poSrcDS->GetMetadata(pszDomain);
321 : }
322 :
323 : /************************************************************************/
324 : /* GetMetadataItem() */
325 : /************************************************************************/
326 :
327 28 : const char *GDALOrientedDataset::GetMetadataItem(const char *pszName,
328 : const char *pszDomain)
329 : {
330 28 : return CSLFetchNameValue(GetMetadata(pszDomain), pszName);
331 : }
332 :
333 : //! @endcond
|