Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: "color-merge" step of "raster pipeline"
5 : * Author: Even Rouault <even dot rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2025, Even Rouault <even dot rouault at spatialys.com>
9 : * Copyright (c) 2009, Frank Warmerdam
10 :
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "gdalalg_raster_color_merge.h"
15 :
16 : #include "cpl_conv.h"
17 : #include "gdal_priv.h"
18 :
19 : #include <algorithm>
20 : #include <limits>
21 :
22 : #if defined(__x86_64) || defined(_M_X64)
23 : #define HAVE_SSE2
24 : #endif
25 : #ifdef HAVE_SSE2
26 : #include "gdalsse_priv.h"
27 : #endif
28 :
29 : //! @cond Doxygen_Suppress
30 :
31 : #ifndef _
32 : #define _(x) (x)
33 : #endif
34 :
35 : /************************************************************************/
36 : /* GDALRasterColorMergeAlgorithm::GDALRasterColorMergeAlgorithm() */
37 : /************************************************************************/
38 :
39 39 : GDALRasterColorMergeAlgorithm::GDALRasterColorMergeAlgorithm(
40 39 : bool standaloneStep)
41 : : GDALRasterPipelineStepAlgorithm(
42 : NAME, DESCRIPTION, HELP_URL,
43 0 : ConstructorOptions()
44 39 : .SetStandaloneStep(standaloneStep)
45 39 : .SetAddDefaultArguments(false)
46 78 : .SetInputDatasetHelpMsg(_("Input RGB/RGBA raster dataset"))
47 78 : .SetInputDatasetAlias("color-input")
48 78 : .SetInputDatasetMetaVar("COLOR-INPUT")
49 117 : .SetOutputDatasetHelpMsg(_("Output RGB/RGBA raster dataset")))
50 : {
51 39 : const auto AddGrayscaleDataset = [this]()
52 : {
53 : auto &arg = AddArg("grayscale", 0, _("Grayscale dataset"),
54 78 : &m_grayScaleDataset, GDAL_OF_RASTER)
55 39 : .SetPositional()
56 39 : .SetRequired();
57 :
58 39 : SetAutoCompleteFunctionForFilename(arg, GDAL_OF_RASTER);
59 39 : };
60 :
61 39 : if (standaloneStep)
62 : {
63 20 : AddRasterInputArgs(false, false);
64 20 : AddGrayscaleDataset();
65 20 : AddProgressArg();
66 20 : AddRasterOutputArgs(false);
67 : }
68 : else
69 : {
70 19 : AddRasterHiddenInputDatasetArg();
71 19 : AddGrayscaleDataset();
72 : }
73 39 : }
74 :
75 : namespace
76 : {
77 :
78 : /************************************************************************/
79 : /* HSVMergeDataset */
80 : /************************************************************************/
81 :
82 : class HSVMergeDataset final : public GDALDataset
83 : {
84 : public:
85 : HSVMergeDataset(GDALDataset &oColorDS, GDALDataset &oGrayScaleDS);
86 :
87 1 : CPLErr GetGeoTransform(double *padfGT) override
88 : {
89 1 : return m_oColorDS.GetGeoTransform(padfGT);
90 : }
91 :
92 1 : const OGRSpatialReference *GetSpatialRef() const override
93 : {
94 1 : return m_oColorDS.GetSpatialRef();
95 : }
96 :
97 : bool AcquireSourcePixels(int nXOff, int nYOff, int nXSize, int nYSize,
98 : int nBufXSize, int nBufYSize,
99 : GDALRasterIOExtraArg *psExtraArg);
100 :
101 : protected:
102 : CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
103 : int nYSize, void *pData, int nBufXSize, int nBufYSize,
104 : GDALDataType eBufType, int nBandCount,
105 : BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
106 : GSpacing nLineSpace, GSpacing nBandSpace,
107 : GDALRasterIOExtraArg *psExtraArg) override;
108 :
109 : private:
110 : friend class HSVMergeBand;
111 : GDALDataset &m_oColorDS;
112 : GDALDataset &m_oGrayScaleDS;
113 : std::vector<std::unique_ptr<HSVMergeDataset>> m_apoOverviews{};
114 : int m_nCachedXOff = 0;
115 : int m_nCachedYOff = 0;
116 : int m_nCachedXSize = 0;
117 : int m_nCachedYSize = 0;
118 : int m_nCachedBufXSize = 0;
119 : int m_nCachedBufYSize = 0;
120 : GDALRasterIOExtraArg m_sCachedExtraArg{};
121 : std::vector<GByte> m_abyBuffer{};
122 : bool m_ioError = false;
123 : };
124 :
125 : /************************************************************************/
126 : /* rgb_to_hs() */
127 : /************************************************************************/
128 :
129 : // rgb comes in as [r,g,b] with values in the range [0,255]. The returned
130 : // values will be with hue and saturation in the range [0,1].
131 :
132 : // Derived from hsv_merge.py
133 :
134 1049920 : static void rgb_to_hs(int r, int g, int b, float *h, float *s)
135 : {
136 : int minc, maxc;
137 1049920 : if (r <= g)
138 : {
139 533825 : if (r <= b)
140 : {
141 358888 : minc = r;
142 358888 : maxc = std::max(g, b);
143 : }
144 : else /* b < r */
145 : {
146 174937 : minc = b;
147 174937 : maxc = g;
148 : }
149 : }
150 : else /* g < r */
151 : {
152 516096 : if (g <= b)
153 : {
154 349440 : minc = g;
155 349440 : maxc = std::max(r, b);
156 : }
157 : else /* b < g */
158 : {
159 166656 : minc = b;
160 166656 : maxc = r;
161 : }
162 : }
163 1049920 : const int maxc_minus_minc = maxc - minc;
164 1049920 : if (s)
165 1049920 : *s = maxc_minus_minc / static_cast<float>(std::max(1, maxc));
166 1049920 : if (h)
167 : {
168 1049920 : const float maxc_minus_minc_times_6 =
169 1049920 : maxc_minus_minc == 0 ? 1.0f : 6.0f * maxc_minus_minc;
170 1049920 : if (maxc == b)
171 358237 : *h = 4.0f / 6.0f + (r - g) / maxc_minus_minc_times_6;
172 691684 : else if (maxc == g)
173 350308 : *h = 2.0f / 6.0f + (b - r) / maxc_minus_minc_times_6;
174 : else
175 : {
176 341376 : const float tmp = (g - b) / maxc_minus_minc_times_6;
177 341376 : *h = tmp < 0.0f ? tmp + 1.0f : tmp;
178 : }
179 : }
180 1049920 : }
181 :
182 : /************************************************************************/
183 : /* choose_among() */
184 : /************************************************************************/
185 :
186 : template <typename T>
187 1576900 : static inline T choose_among(int idx, T a0, T a1, T a2, T a3, T a4, T a5)
188 : {
189 1576900 : switch (idx)
190 : {
191 262080 : case 0:
192 262080 : return a0;
193 262731 : case 1:
194 262731 : return a1;
195 264033 : case 2:
196 264033 : return a2;
197 263382 : case 3:
198 263382 : return a3;
199 262591 : case 4:
200 262591 : return a4;
201 262080 : default:
202 262080 : break;
203 : }
204 262080 : return a5;
205 : }
206 :
207 : /************************************************************************/
208 : /* hsv_to_rgb() */
209 : /************************************************************************/
210 :
211 : // hsv comes in as [h,s,v] with hue and saturation in the range [0,1],
212 : // but value in the range [0,255].
213 :
214 : // Derived from hsv_merge.py
215 :
216 1049920 : static void hsv_to_rgb(float h, float s, GByte v, GByte *r, GByte *g, GByte *b)
217 : {
218 1049920 : const int i = static_cast<int>(6.0f * h);
219 1049920 : const float f = 6.0f * h - i;
220 1049920 : const GByte p = static_cast<GByte>(v * (1.0f - s) + 0.5f);
221 1049920 : const GByte q = static_cast<GByte>(v * (1.0f - s * f) + 0.5f);
222 1049920 : const GByte t = static_cast<GByte>(v * (1.0f - s * (1.0f - f)) + 0.5f);
223 :
224 1049920 : if (r)
225 525633 : *r = choose_among(i, v, q, p, p, t, v);
226 1049920 : if (g)
227 525632 : *g = choose_among(i, t, v, v, q, p, p);
228 1049920 : if (b)
229 525632 : *b = choose_among(i, p, p, t, v, v, q);
230 1049920 : }
231 :
232 : /************************************************************************/
233 : /* XMM_RGB_to_HS() */
234 : /************************************************************************/
235 :
236 : #ifdef HAVE_SSE2
237 : static inline void
238 524288 : XMM_RGB_to_HS(const GByte *CPL_RESTRICT pInR, const GByte *CPL_RESTRICT pInG,
239 : const GByte *CPL_RESTRICT pInB, const XMMReg4Float &zero,
240 : const XMMReg4Float &one, const XMMReg4Float &six,
241 : const XMMReg4Float &two_over_six,
242 : const XMMReg4Float &four_over_six, XMMReg4Float &h,
243 : XMMReg4Float &s)
244 : {
245 524288 : const auto r = XMMReg4Float::Load4Val(pInR);
246 524288 : const auto g = XMMReg4Float::Load4Val(pInG);
247 524288 : const auto b = XMMReg4Float::Load4Val(pInB);
248 524288 : const auto minc = XMMReg4Float::Min(XMMReg4Float::Min(r, g), b);
249 524288 : const auto maxc = XMMReg4Float::Max(XMMReg4Float::Max(r, g), b);
250 524288 : const auto max_minus_min = maxc - minc;
251 524288 : s = max_minus_min / XMMReg4Float::Max(one, maxc);
252 : const auto inv_max_minus_min_times_6_0 =
253 524288 : XMMReg4Float::Ternary(XMMReg4Float::Equals(max_minus_min, zero), one,
254 524288 : six * max_minus_min)
255 524288 : .inverse();
256 524288 : const auto tmp = (g - b) * inv_max_minus_min_times_6_0;
257 524288 : h = XMMReg4Float::Ternary(
258 524288 : XMMReg4Float::Equals(maxc, b),
259 524288 : four_over_six + (r - g) * inv_max_minus_min_times_6_0,
260 524288 : XMMReg4Float::Ternary(
261 524288 : XMMReg4Float::Equals(maxc, g),
262 524288 : two_over_six + (b - r) * inv_max_minus_min_times_6_0,
263 524288 : XMMReg4Float::Ternary(XMMReg4Float::Lesser(tmp, zero), tmp + one,
264 524288 : tmp)));
265 524288 : }
266 : #endif
267 :
268 : /************************************************************************/
269 : /* patch_value_line() */
270 : /************************************************************************/
271 :
272 : static
273 : #ifdef __GNUC__
274 : __attribute__((__noinline__))
275 : #endif
276 : void
277 211 : patch_value_line(int nCount, const GByte *CPL_RESTRICT pInR,
278 : const GByte *CPL_RESTRICT pInG,
279 : const GByte *CPL_RESTRICT pInB,
280 : const GByte *CPL_RESTRICT pInGray,
281 : GByte *CPL_RESTRICT pOutR, GByte *CPL_RESTRICT pOutG,
282 : GByte *CPL_RESTRICT pOutB)
283 : {
284 211 : int i = 0;
285 : #ifdef HAVE_SSE2
286 211 : const auto zero = XMMReg4Float::Zero();
287 211 : const auto one = XMMReg4Float::Set1(1.0f);
288 211 : const auto six = XMMReg4Float::Set1(6.0f);
289 211 : const auto two_over_six = XMMReg4Float::Set1(2.0f / 6.0f);
290 211 : const auto four_over_six = two_over_six + two_over_six;
291 :
292 211 : constexpr int ELTS = 8;
293 262355 : for (; i + (ELTS - 1) < nCount; i += ELTS)
294 : {
295 262144 : XMMReg4Float h0, s0;
296 262144 : XMM_RGB_to_HS(pInR + i, pInG + i, pInB + i, zero, one, six,
297 : two_over_six, four_over_six, h0, s0);
298 262144 : XMMReg4Float h1, s1;
299 262144 : XMM_RGB_to_HS(pInR + i + ELTS / 2, pInG + i + ELTS / 2,
300 262144 : pInB + i + ELTS / 2, zero, one, six, two_over_six,
301 : four_over_six, h1, s1);
302 :
303 262144 : XMMReg4Float v0, v1;
304 262144 : XMMReg4Float::Load8Val(pInGray + i, v0, v1);
305 :
306 262144 : const auto half = XMMReg4Float::Set1(0.5f);
307 262144 : const auto six_h0 = six * h0;
308 262144 : const auto idx0 = six_h0.truncate_to_int();
309 262144 : const auto f0 = six_h0 - idx0.to_float();
310 262144 : const auto p0 = (v0 * (one - s0) + half).truncate_to_int();
311 262144 : const auto q0 = (v0 * (one - s0 * f0) + half).truncate_to_int();
312 262144 : const auto t0 = (v0 * (one - s0 * (one - f0)) + half).truncate_to_int();
313 :
314 262144 : const auto six_h1 = six * h1;
315 262144 : const auto idx1 = six_h1.truncate_to_int();
316 262144 : const auto f1 = six_h1 - idx1.to_float();
317 262144 : const auto p1 = (v1 * (one - s1) + half).truncate_to_int();
318 262144 : const auto q1 = (v1 * (one - s1 * f1) + half).truncate_to_int();
319 262144 : const auto t1 = (v1 * (one - s1 * (one - f1)) + half).truncate_to_int();
320 :
321 262144 : const auto idx = XMMReg8Byte::Pack(idx0, idx1);
322 : const auto v =
323 262144 : XMMReg8Byte::Pack(v0.truncate_to_int(), v1.truncate_to_int());
324 262144 : const auto p = XMMReg8Byte::Pack(p0, p1);
325 262144 : const auto q = XMMReg8Byte::Pack(q0, q1);
326 262144 : const auto t = XMMReg8Byte::Pack(t0, t1);
327 :
328 262144 : const auto equalsTo0 = XMMReg8Byte::Equals(idx, XMMReg8Byte::Zero());
329 262144 : const auto one8Byte = XMMReg8Byte::Set1(1);
330 262144 : const auto equalsTo1 = XMMReg8Byte::Equals(idx, one8Byte);
331 262144 : const auto two8Byte = one8Byte + one8Byte;
332 262144 : const auto equalsTo2 = XMMReg8Byte::Equals(idx, two8Byte);
333 262144 : const auto four8Byte = two8Byte + two8Byte;
334 262144 : const auto equalsTo4 = XMMReg8Byte::Equals(idx, four8Byte);
335 262144 : const auto equalsTo3 = XMMReg8Byte::Equals(idx, four8Byte - one8Byte);
336 : // clang-format off
337 262144 : if (pOutR)
338 : {
339 : const auto out_r =
340 : XMMReg8Byte::Ternary(equalsTo0, v,
341 131072 : XMMReg8Byte::Ternary(equalsTo1, q,
342 131072 : XMMReg8Byte::Ternary(XMMReg8Byte::Or(equalsTo2, equalsTo3), p,
343 262144 : XMMReg8Byte::Ternary(equalsTo4, t, v))));
344 131072 : out_r.Store8Val(pOutR + i);
345 : }
346 262144 : if (pOutG)
347 : {
348 : const auto out_g =
349 : XMMReg8Byte::Ternary(equalsTo0, t,
350 114688 : XMMReg8Byte::Ternary(XMMReg8Byte::Or(equalsTo1, equalsTo2), v,
351 229376 : XMMReg8Byte::Ternary(equalsTo3, q, p)));
352 114688 : out_g.Store8Val(pOutG + i);
353 : }
354 262144 : if (pOutB)
355 : {
356 : const auto out_b =
357 114688 : XMMReg8Byte::Ternary(XMMReg8Byte::Or(equalsTo0, equalsTo1), p,
358 114688 : XMMReg8Byte::Ternary(equalsTo2, t,
359 114688 : XMMReg8Byte::Ternary(XMMReg8Byte::Or(equalsTo3, equalsTo4),
360 114688 : v, q)));
361 114688 : out_b.Store8Val(pOutB + i);
362 : }
363 : // clang-format on
364 : }
365 : #endif
366 :
367 1556 : for (; i < nCount; ++i)
368 : {
369 : float h, s;
370 1345 : rgb_to_hs(pInR[i], pInG[i], pInB[i], &h, &s);
371 4033 : hsv_to_rgb(h, s, pInGray[i], pOutR ? pOutR + i : nullptr,
372 2688 : pOutG ? pOutG + i : nullptr, pOutB ? pOutB + i : nullptr);
373 : }
374 211 : }
375 :
376 : /************************************************************************/
377 : /* HSVMergeBand */
378 : /************************************************************************/
379 :
380 : class HSVMergeBand final : public GDALRasterBand
381 : {
382 : public:
383 40 : HSVMergeBand(HSVMergeDataset &oHSVMergeDataset, int nBandIn)
384 40 : : m_oHSVMergeDataset(oHSVMergeDataset)
385 : {
386 40 : nBand = nBandIn;
387 40 : nRasterXSize = oHSVMergeDataset.GetRasterXSize();
388 40 : nRasterYSize = oHSVMergeDataset.GetRasterYSize();
389 40 : oHSVMergeDataset.m_oColorDS.GetRasterBand(1)->GetBlockSize(
390 : &nBlockXSize, &nBlockYSize);
391 40 : eDataType = GDT_Byte;
392 40 : }
393 :
394 1 : GDALColorInterp GetColorInterpretation() override
395 : {
396 1 : return m_oHSVMergeDataset.m_oColorDS.GetRasterBand(nBand)
397 1 : ->GetColorInterpretation();
398 : }
399 :
400 17 : int GetOverviewCount() override
401 : {
402 17 : return static_cast<int>(m_oHSVMergeDataset.m_apoOverviews.size());
403 : }
404 :
405 14 : GDALRasterBand *GetOverview(int idx) override
406 : {
407 13 : return idx >= 0 && idx < GetOverviewCount()
408 27 : ? m_oHSVMergeDataset.m_apoOverviews[idx]->GetRasterBand(
409 : nBand)
410 14 : : nullptr;
411 : }
412 :
413 : protected:
414 7 : CPLErr IReadBlock(int nBlockXOff, int nBlockYOff, void *pData) override
415 : {
416 7 : int nReqXSize = 0;
417 7 : int nReqYSize = 0;
418 7 : GetActualBlockSize(nBlockXOff, nBlockYOff, &nReqXSize, &nReqYSize);
419 14 : return RasterIO(GF_Read, nBlockXOff * nBlockXSize,
420 7 : nBlockYOff * nBlockYSize, nReqXSize, nReqYSize, pData,
421 7 : nReqXSize, nReqYSize, GDT_Byte, 1, nBlockXSize,
422 14 : nullptr);
423 : }
424 :
425 : CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
426 : int nYSize, void *pData, int nBufXSize, int nBufYSize,
427 : GDALDataType eBufType, GSpacing nPixelSpace,
428 : GSpacing nLineSpace,
429 : GDALRasterIOExtraArg *psExtraArg) override;
430 :
431 : private:
432 : HSVMergeDataset &m_oHSVMergeDataset;
433 : };
434 :
435 : /************************************************************************/
436 : /* HSVMergeDataset::HSVMergeDataset() */
437 : /************************************************************************/
438 :
439 12 : HSVMergeDataset::HSVMergeDataset(GDALDataset &oColorDS,
440 12 : GDALDataset &oGrayScaleDS)
441 12 : : m_oColorDS(oColorDS), m_oGrayScaleDS(oGrayScaleDS)
442 : {
443 12 : CPLAssert(oColorDS.GetRasterCount() == 3 || oColorDS.GetRasterCount() == 4);
444 12 : CPLAssert(oColorDS.GetRasterXSize() == oGrayScaleDS.GetRasterXSize());
445 12 : CPLAssert(oColorDS.GetRasterYSize() == oGrayScaleDS.GetRasterYSize());
446 12 : nRasterXSize = oColorDS.GetRasterXSize();
447 12 : nRasterYSize = oColorDS.GetRasterYSize();
448 12 : const int nOvrCount = oGrayScaleDS.GetRasterBand(1)->GetOverviewCount();
449 12 : bool bCanCreateOvr = true;
450 52 : for (int iBand = 1; iBand <= oColorDS.GetRasterCount(); ++iBand)
451 : {
452 40 : SetBand(iBand, std::make_unique<HSVMergeBand>(*this, iBand));
453 40 : bCanCreateOvr =
454 80 : bCanCreateOvr &&
455 40 : oColorDS.GetRasterBand(iBand)->GetOverviewCount() == nOvrCount;
456 48 : for (int iOvr = 0; iOvr < nOvrCount && bCanCreateOvr; ++iOvr)
457 : {
458 : const auto poColorOvrBand =
459 8 : oColorDS.GetRasterBand(iBand)->GetOverview(iOvr);
460 : const auto poGSOvrBand =
461 8 : oGrayScaleDS.GetRasterBand(1)->GetOverview(iOvr);
462 8 : bCanCreateOvr =
463 8 : poColorOvrBand->GetDataset() != &oColorDS &&
464 8 : poColorOvrBand->GetDataset() == oColorDS.GetRasterBand(1)
465 8 : ->GetOverview(iOvr)
466 8 : ->GetDataset() &&
467 8 : poGSOvrBand->GetDataset() != &oGrayScaleDS &&
468 24 : poColorOvrBand->GetXSize() == poGSOvrBand->GetXSize() &&
469 8 : poColorOvrBand->GetYSize() == poGSOvrBand->GetYSize();
470 : }
471 : }
472 :
473 12 : SetDescription(CPLSPrintf("Merge %s with %s", m_oColorDS.GetDescription(),
474 12 : m_oGrayScaleDS.GetDescription()));
475 12 : SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
476 :
477 12 : if (bCanCreateOvr)
478 : {
479 14 : for (int iOvr = 0; iOvr < nOvrCount; ++iOvr)
480 : {
481 2 : m_apoOverviews.push_back(std::make_unique<HSVMergeDataset>(
482 2 : *(oColorDS.GetRasterBand(1)->GetOverview(iOvr)->GetDataset()),
483 2 : *(oGrayScaleDS.GetRasterBand(1)
484 2 : ->GetOverview(iOvr)
485 2 : ->GetDataset())));
486 : }
487 : }
488 12 : }
489 :
490 : /************************************************************************/
491 : /* HSVMergeDataset::AcquireSourcePixels() */
492 : /************************************************************************/
493 :
494 217 : bool HSVMergeDataset::AcquireSourcePixels(int nXOff, int nYOff, int nXSize,
495 : int nYSize, int nBufXSize,
496 : int nBufYSize,
497 : GDALRasterIOExtraArg *psExtraArg)
498 : {
499 217 : if (nXOff == m_nCachedXOff && nYOff == m_nCachedYOff &&
500 18 : nXSize == m_nCachedXSize && nYSize == m_nCachedYSize &&
501 8 : nBufXSize == m_nCachedBufXSize && nBufYSize == m_nCachedBufYSize &&
502 8 : psExtraArg->eResampleAlg == m_sCachedExtraArg.eResampleAlg &&
503 8 : psExtraArg->bFloatingPointWindowValidity ==
504 8 : m_sCachedExtraArg.bFloatingPointWindowValidity &&
505 8 : (!psExtraArg->bFloatingPointWindowValidity ||
506 0 : (psExtraArg->dfXOff == m_sCachedExtraArg.dfXOff &&
507 0 : psExtraArg->dfYOff == m_sCachedExtraArg.dfYOff &&
508 0 : psExtraArg->dfXSize == m_sCachedExtraArg.dfXSize &&
509 0 : psExtraArg->dfYSize == m_sCachedExtraArg.dfYSize)))
510 : {
511 8 : return !m_abyBuffer.empty();
512 : }
513 :
514 209 : constexpr int N_COMPS_IN_BUFFER = 4; // RGB + Grayscale
515 :
516 418 : if (static_cast<size_t>(nBufXSize) >
517 209 : std::numeric_limits<size_t>::max() / nBufYSize / N_COMPS_IN_BUFFER)
518 : {
519 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
520 : "Out of memory allocating temporary buffer");
521 0 : m_abyBuffer.clear();
522 0 : m_ioError = true;
523 0 : return false;
524 : }
525 :
526 209 : const size_t nPixelCount = static_cast<size_t>(nBufXSize) * nBufYSize;
527 : try
528 : {
529 209 : if (m_abyBuffer.size() < nPixelCount * N_COMPS_IN_BUFFER)
530 9 : m_abyBuffer.resize(nPixelCount * N_COMPS_IN_BUFFER);
531 : }
532 4 : catch (const std::exception &)
533 : {
534 4 : CPLError(CE_Failure, CPLE_OutOfMemory,
535 : "Out of memory allocating temporary buffer");
536 4 : m_abyBuffer.clear();
537 4 : m_ioError = true;
538 4 : return false;
539 : }
540 :
541 : const bool bOK =
542 205 : (m_oColorDS.RasterIO(
543 205 : GF_Read, nXOff, nYOff, nXSize, nYSize, m_abyBuffer.data(),
544 : nBufXSize, nBufYSize, GDT_Byte, 3, nullptr, 1, nBufXSize,
545 408 : static_cast<GSpacing>(nPixelCount), psExtraArg) == CE_None &&
546 406 : m_oGrayScaleDS.GetRasterBand(1)->RasterIO(
547 : GF_Read, nXOff, nYOff, nXSize, nYSize,
548 203 : m_abyBuffer.data() + nPixelCount * 3, nBufXSize, nBufYSize,
549 205 : GDT_Byte, 1, nBufXSize, psExtraArg) == CE_None);
550 205 : if (bOK)
551 : {
552 203 : m_nCachedXOff = nXOff;
553 203 : m_nCachedYOff = nYOff;
554 203 : m_nCachedXSize = nXSize;
555 203 : m_nCachedYSize = nYSize;
556 203 : m_nCachedBufXSize = nBufXSize;
557 203 : m_nCachedBufYSize = nBufYSize;
558 203 : m_sCachedExtraArg = *psExtraArg;
559 : }
560 : else
561 : {
562 2 : m_abyBuffer.clear();
563 2 : m_ioError = true;
564 : }
565 205 : return bOK;
566 : }
567 :
568 : /************************************************************************/
569 : /* HSVMergeDataset::IRasterIO() */
570 : /************************************************************************/
571 :
572 205 : CPLErr HSVMergeDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
573 : int nXSize, int nYSize, void *pData,
574 : int nBufXSize, int nBufYSize,
575 : GDALDataType eBufType, int nBandCount,
576 : BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
577 : GSpacing nLineSpace, GSpacing nBandSpace,
578 : GDALRasterIOExtraArg *psExtraArg)
579 : {
580 : // Try to pass the request to the most appropriate overview dataset.
581 205 : if (nBufXSize < nXSize && nBufYSize < nYSize)
582 : {
583 2 : int bTried = FALSE;
584 2 : const CPLErr eErr = TryOverviewRasterIO(
585 : eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
586 : eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace,
587 : nBandSpace, psExtraArg, &bTried);
588 2 : if (bTried)
589 2 : return eErr;
590 : }
591 :
592 203 : GByte *pabyDst = static_cast<GByte *>(pData);
593 203 : if (eRWFlag == GF_Read && eBufType == GDT_Byte && nBandCount == nBands &&
594 202 : panBandMap[0] == 1 && panBandMap[1] == 2 && panBandMap[2] == 3 &&
595 196 : (nBandCount == 3 || panBandMap[3] == 4) &&
596 202 : AcquireSourcePixels(nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize,
597 602 : psExtraArg) &&
598 196 : (nBandCount == 3 ||
599 392 : m_oColorDS.GetRasterBand(4)->RasterIO(
600 196 : GF_Read, nXOff, nYOff, nXSize, nYSize, pabyDst + nBandSpace * 3,
601 : nBufXSize, nBufYSize, eBufType, nPixelSpace, nLineSpace,
602 : psExtraArg) == CE_None))
603 : {
604 196 : const size_t nPixelCount = static_cast<size_t>(nBufXSize) * nBufYSize;
605 196 : const GByte *pabyR = m_abyBuffer.data();
606 196 : const GByte *pabyG = m_abyBuffer.data() + nPixelCount;
607 196 : const GByte *pabyB = m_abyBuffer.data() + nPixelCount * 2;
608 196 : const GByte *pabyGrayScale = m_abyBuffer.data() + nPixelCount * 3;
609 196 : size_t nSrcIdx = 0;
610 394 : for (int j = 0; j < nBufYSize; ++j)
611 : {
612 198 : auto nDstOffset = j * nLineSpace;
613 198 : if (nPixelSpace == 1 && nLineSpace >= nPixelSpace * nBufXSize &&
614 196 : nBandSpace >= nLineSpace * nBufYSize)
615 : {
616 196 : patch_value_line(nBufXSize, pabyR + nSrcIdx, pabyG + nSrcIdx,
617 : pabyB + nSrcIdx, pabyGrayScale + nSrcIdx,
618 196 : pabyDst + nDstOffset,
619 196 : pabyDst + nDstOffset + nBandSpace,
620 196 : pabyDst + nDstOffset + 2 * nBandSpace);
621 196 : nSrcIdx += nBufXSize;
622 : }
623 : else
624 : {
625 262146 : for (int i = 0; i < nBufXSize;
626 262144 : ++i, ++nSrcIdx, nDstOffset += nPixelSpace)
627 : {
628 : float h, s;
629 262144 : rgb_to_hs(pabyR[nSrcIdx], pabyG[nSrcIdx], pabyB[nSrcIdx],
630 : &h, &s);
631 262144 : hsv_to_rgb(h, s, pabyGrayScale[nSrcIdx],
632 262144 : &pabyDst[nDstOffset + 0 * nBandSpace],
633 262144 : &pabyDst[nDstOffset + 1 * nBandSpace],
634 262144 : &pabyDst[nDstOffset + 2 * nBandSpace]);
635 : }
636 : }
637 : }
638 :
639 196 : return CE_None;
640 : }
641 7 : else if (m_ioError)
642 : {
643 6 : return CE_Failure;
644 : }
645 : else
646 : {
647 1 : const CPLErr eErr = GDALDataset::IRasterIO(
648 : eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
649 : eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace,
650 : nBandSpace, psExtraArg);
651 1 : m_ioError = eErr != CE_None;
652 1 : return eErr;
653 : }
654 : }
655 :
656 : /************************************************************************/
657 : /* HSVMergeDataset::IRasterIO() */
658 : /************************************************************************/
659 :
660 23 : CPLErr HSVMergeBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
661 : int nXSize, int nYSize, void *pData,
662 : int nBufXSize, int nBufYSize,
663 : GDALDataType eBufType, GSpacing nPixelSpace,
664 : GSpacing nLineSpace,
665 : GDALRasterIOExtraArg *psExtraArg)
666 : {
667 : // Try to pass the request to the most appropriate overview dataset.
668 23 : if (nBufXSize < nXSize && nBufYSize < nYSize)
669 : {
670 1 : int bTried = FALSE;
671 1 : const CPLErr eErr = TryOverviewRasterIO(
672 : eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
673 : eBufType, nPixelSpace, nLineSpace, psExtraArg, &bTried);
674 1 : if (bTried)
675 1 : return eErr;
676 : }
677 :
678 22 : if (nBand >= 4)
679 : {
680 3 : return m_oHSVMergeDataset.m_oColorDS.GetRasterBand(nBand)->RasterIO(
681 : eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
682 3 : eBufType, nPixelSpace, nLineSpace, psExtraArg);
683 : }
684 34 : else if (eRWFlag == GF_Read && eBufType == GDT_Byte &&
685 15 : m_oHSVMergeDataset.AcquireSourcePixels(nXOff, nYOff, nXSize,
686 : nYSize, nBufXSize,
687 : nBufYSize, psExtraArg))
688 : {
689 15 : GByte *pabyDst = static_cast<GByte *>(pData);
690 15 : const GByte *pabyR = m_oHSVMergeDataset.m_abyBuffer.data();
691 15 : const size_t nPixelCount = static_cast<size_t>(nBufXSize) * nBufYSize;
692 : const GByte *pabyG =
693 15 : m_oHSVMergeDataset.m_abyBuffer.data() + nPixelCount;
694 : const GByte *pabyB =
695 15 : m_oHSVMergeDataset.m_abyBuffer.data() + nPixelCount * 2;
696 : const GByte *pabyGrayScale =
697 15 : m_oHSVMergeDataset.m_abyBuffer.data() + nPixelCount * 3;
698 15 : size_t nSrcIdx = 0;
699 36 : for (int j = 0; j < nBufYSize; ++j)
700 : {
701 21 : auto nDstOffset = j * nLineSpace;
702 21 : if (nPixelSpace == 1 && nLineSpace >= nPixelSpace * nBufXSize)
703 : {
704 37 : patch_value_line(nBufXSize, pabyR + nSrcIdx, pabyG + nSrcIdx,
705 : pabyB + nSrcIdx, pabyGrayScale + nSrcIdx,
706 15 : nBand == 1 ? pabyDst + nDstOffset : nullptr,
707 15 : nBand == 2 ? pabyDst + nDstOffset : nullptr,
708 15 : nBand == 3 ? pabyDst + nDstOffset : nullptr);
709 15 : nSrcIdx += nBufXSize;
710 : }
711 : else
712 : {
713 786438 : for (int i = 0; i < nBufXSize;
714 786432 : ++i, ++nSrcIdx, nDstOffset += nPixelSpace)
715 : {
716 : float h, s;
717 786432 : rgb_to_hs(pabyR[nSrcIdx], pabyG[nSrcIdx], pabyB[nSrcIdx],
718 : &h, &s);
719 786432 : if (nBand == 1)
720 : {
721 262144 : hsv_to_rgb(h, s, pabyGrayScale[nSrcIdx],
722 262144 : &pabyDst[nDstOffset], nullptr, nullptr);
723 : }
724 524288 : else if (nBand == 2)
725 : {
726 262144 : hsv_to_rgb(h, s, pabyGrayScale[nSrcIdx], nullptr,
727 262144 : &pabyDst[nDstOffset], nullptr);
728 : }
729 : else
730 : {
731 262144 : CPLAssert(nBand == 3);
732 262144 : hsv_to_rgb(h, s, pabyGrayScale[nSrcIdx], nullptr,
733 262144 : nullptr, &pabyDst[nDstOffset]);
734 : }
735 : }
736 : }
737 : }
738 :
739 15 : return CE_None;
740 : }
741 4 : else if (m_oHSVMergeDataset.m_ioError)
742 : {
743 0 : return CE_Failure;
744 : }
745 : else
746 : {
747 4 : const CPLErr eErr = GDALRasterBand::IRasterIO(
748 : eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
749 : eBufType, nPixelSpace, nLineSpace, psExtraArg);
750 4 : m_oHSVMergeDataset.m_ioError = eErr != CE_None;
751 4 : return eErr;
752 : }
753 : }
754 :
755 : } // namespace
756 :
757 : /************************************************************************/
758 : /* GDALRasterColorMergeAlgorithm::RunStep() */
759 : /************************************************************************/
760 :
761 17 : bool GDALRasterColorMergeAlgorithm::RunStep(GDALPipelineStepRunContext &)
762 : {
763 17 : auto poSrcDS = m_inputDataset[0].GetDatasetRef();
764 17 : CPLAssert(poSrcDS);
765 :
766 17 : auto poGrayScaleDS = m_grayScaleDataset.GetDatasetRef();
767 17 : CPLAssert(poGrayScaleDS);
768 :
769 32 : if ((poSrcDS->GetRasterCount() != 3 && poSrcDS->GetRasterCount() != 4) ||
770 15 : poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte)
771 : {
772 3 : ReportError(CE_Failure, CPLE_NotSupported,
773 : "Only 3 or 4-band Byte dataset supported as input");
774 3 : return false;
775 : }
776 :
777 27 : if (poGrayScaleDS->GetRasterCount() != 1 ||
778 13 : poGrayScaleDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte)
779 : {
780 2 : ReportError(CE_Failure, CPLE_NotSupported,
781 : "Only 1-band Byte dataset supported as grayscale dataset");
782 2 : return false;
783 : }
784 :
785 23 : if (poSrcDS->GetRasterXSize() != poGrayScaleDS->GetRasterXSize() ||
786 11 : poSrcDS->GetRasterYSize() != poGrayScaleDS->GetRasterYSize())
787 : {
788 2 : ReportError(CE_Failure, CPLE_IllegalArg,
789 : "Input RGB/RGBA dataset and grayscale dataset must have "
790 : "the same dimensions");
791 2 : return false;
792 : }
793 :
794 10 : m_outputDataset.Set(
795 20 : std::make_unique<HSVMergeDataset>(*poSrcDS, *poGrayScaleDS));
796 :
797 10 : return true;
798 : }
799 :
800 : GDALRasterColorMergeAlgorithmStandalone::
801 : ~GDALRasterColorMergeAlgorithmStandalone() = default;
802 :
803 : //! @endcond
|