Coverage Report

Created: 2026-02-14 09:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/gcore/tiff_common.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GDAL Core
4
 * Purpose:  Common code shared between the GTiff and libertiff drivers
5
 * Author:   Even Rouault <even dot rouault at spatialys.com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "tiff_common.h"
14
15
#include <algorithm>
16
17
#include "gdal_mdreader.h"
18
19
namespace gdal
20
{
21
namespace tiff_common
22
{
23
24
/************************************************************************/
25
/*                       PrepareTIFFErrorFormat()                       */
26
/*                                                                      */
27
/*      sometimes the "module" has stuff in it that has special         */
28
/*      meaning in a printf() style format, so we try to escape it.     */
29
/*      For now we hope the only thing we have to escape is %'s.        */
30
/************************************************************************/
31
32
char *PrepareTIFFErrorFormat(const char *module, const char *fmt)
33
34
26.1M
{
35
26.1M
    const size_t nModuleSize = strlen(module);
36
26.1M
    const size_t nModFmtSize = nModuleSize * 2 + strlen(fmt) + 2;
37
26.1M
    char *pszModFmt = static_cast<char *>(CPLMalloc(nModFmtSize));
38
39
26.1M
    size_t iOut = 0;  // Used after for.
40
41
493M
    for (size_t iIn = 0; iIn < nModuleSize; ++iIn)
42
467M
    {
43
467M
        if (module[iIn] == '%')
44
26
        {
45
26
            CPLAssert(iOut < nModFmtSize - 2);
46
26
            pszModFmt[iOut++] = '%';
47
26
            pszModFmt[iOut++] = '%';
48
26
        }
49
467M
        else
50
467M
        {
51
467M
            CPLAssert(iOut < nModFmtSize - 1);
52
467M
            pszModFmt[iOut++] = module[iIn];
53
467M
        }
54
467M
    }
55
26.1M
    CPLAssert(iOut < nModFmtSize);
56
26.1M
    pszModFmt[iOut] = '\0';
57
26.1M
    strcat(pszModFmt, ":");
58
26.1M
    strcat(pszModFmt, fmt);
59
60
26.1M
    return pszModFmt;
61
26.1M
}
62
63
/************************************************************************/
64
/*                    TIFFColorMapTagToColorTable()                     */
65
/************************************************************************/
66
67
std::unique_ptr<GDALColorTable> TIFFColorMapTagToColorTable(
68
    const unsigned short *panRed, const unsigned short *panGreen,
69
    const unsigned short *panBlue, int nColorCount, int &nColorTableMultiplier,
70
    int nDefaultColorTableMultiplier, bool bNoDataSet, double dfNoDataValue)
71
2.20k
{
72
2.20k
    auto poColorTable = std::make_unique<GDALColorTable>();
73
74
2.20k
    if (nColorTableMultiplier == 0)
75
2.18k
    {
76
        // TIFF color maps are in the [0, 65535] range, so some remapping must
77
        // be done to get values in the [0, 255] range, but it is not clear
78
        // how to do that exactly. We have standardized on
79
        // using a 257 multiplication factor (https://github.com/OSGeo/gdal/commit/eeec5b62e385d53e7f2edaba7b73c7c74bc2af39)
80
        // but other software uses 256 (cf https://github.com/OSGeo/gdal/issues/10310)
81
        // Do a first pass to check if all values are multiples of 256 or 257.
82
2.18k
        bool bFoundNonZeroEntry = false;
83
2.18k
        bool bAllValuesMultipleOf256 = true;
84
2.18k
        bool bAllValuesMultipleOf257 = true;
85
2.18k
        unsigned short nMaxColor = 0;
86
103k
        for (int iColor = 0; iColor < nColorCount; ++iColor)
87
101k
        {
88
101k
            if (panRed[iColor] > 0 || panGreen[iColor] > 0 ||
89
17.4k
                panBlue[iColor] > 0)
90
92.9k
            {
91
92.9k
                bFoundNonZeroEntry = true;
92
92.9k
            }
93
101k
            if ((panRed[iColor] % 256) != 0 || (panGreen[iColor] % 256) != 0 ||
94
22.5k
                (panBlue[iColor] % 256) != 0)
95
90.7k
            {
96
90.7k
                bAllValuesMultipleOf256 = false;
97
90.7k
            }
98
101k
            if ((panRed[iColor] % 257) != 0 || (panGreen[iColor] % 257) != 0 ||
99
28.3k
                (panBlue[iColor] % 257) != 0)
100
86.2k
            {
101
86.2k
                bAllValuesMultipleOf257 = false;
102
86.2k
            }
103
104
101k
            nMaxColor = std::max(nMaxColor, panRed[iColor]);
105
101k
            nMaxColor = std::max(nMaxColor, panGreen[iColor]);
106
101k
            nMaxColor = std::max(nMaxColor, panBlue[iColor]);
107
101k
        }
108
109
2.18k
        if (nMaxColor > 0 && nMaxColor < 256)
110
346
        {
111
            // Bug 1384 - Some TIFF files are generated with color map entry
112
            // values in range 0-255 instead of 0-65535 - try to handle these
113
            // gracefully.
114
346
            nColorTableMultiplier = 1;
115
346
            CPLDebug("GTiff",
116
346
                     "TIFF ColorTable seems to be improperly scaled with "
117
346
                     "values all in [0,255] range, fixing up.");
118
346
        }
119
1.84k
        else
120
1.84k
        {
121
1.84k
            if (!bAllValuesMultipleOf256 && !bAllValuesMultipleOf257)
122
1.08k
            {
123
1.08k
                CPLDebug("GTiff",
124
1.08k
                         "The color map contains entries which are not "
125
1.08k
                         "multiple of 256 or 257, so we don't know for "
126
1.08k
                         "sure how to remap them to [0, 255]. Default to "
127
1.08k
                         "using a 257 multiplication factor");
128
1.08k
            }
129
1.84k
            nColorTableMultiplier =
130
1.84k
                (bFoundNonZeroEntry && bAllValuesMultipleOf256)
131
1.84k
                    ? 256
132
1.84k
                    : nDefaultColorTableMultiplier;
133
1.84k
        }
134
2.18k
    }
135
2.20k
    CPLAssert(nColorTableMultiplier > 0);
136
2.20k
    CPLAssert(nColorTableMultiplier <= 257);
137
433k
    for (int iColor = nColorCount - 1; iColor >= 0; iColor--)
138
431k
    {
139
431k
        const GDALColorEntry oEntry = {
140
431k
            static_cast<short>(panRed[iColor] / nColorTableMultiplier),
141
431k
            static_cast<short>(panGreen[iColor] / nColorTableMultiplier),
142
431k
            static_cast<short>(panBlue[iColor] / nColorTableMultiplier),
143
431k
            static_cast<short>(
144
431k
                bNoDataSet && static_cast<int>(dfNoDataValue) == iColor ? 0
145
431k
                                                                        : 255)};
146
147
431k
        poColorTable->SetColorEntry(iColor, &oEntry);
148
431k
    }
149
150
2.20k
    return poColorTable;
151
2.20k
}
152
153
/************************************************************************/
154
/*                      TIFFRPCTagToRPCMetadata()                       */
155
/************************************************************************/
156
157
CPLStringList TIFFRPCTagToRPCMetadata(const double adfRPC[92])
158
45
{
159
45
    CPLStringList asMD;
160
45
    asMD.SetNameValue(RPC_ERR_BIAS, CPLOPrintf("%.15g", adfRPC[0]));
161
45
    asMD.SetNameValue(RPC_ERR_RAND, CPLOPrintf("%.15g", adfRPC[1]));
162
45
    asMD.SetNameValue(RPC_LINE_OFF, CPLOPrintf("%.15g", adfRPC[2]));
163
45
    asMD.SetNameValue(RPC_SAMP_OFF, CPLOPrintf("%.15g", adfRPC[3]));
164
45
    asMD.SetNameValue(RPC_LAT_OFF, CPLOPrintf("%.15g", adfRPC[4]));
165
45
    asMD.SetNameValue(RPC_LONG_OFF, CPLOPrintf("%.15g", adfRPC[5]));
166
45
    asMD.SetNameValue(RPC_HEIGHT_OFF, CPLOPrintf("%.15g", adfRPC[6]));
167
45
    asMD.SetNameValue(RPC_LINE_SCALE, CPLOPrintf("%.15g", adfRPC[7]));
168
45
    asMD.SetNameValue(RPC_SAMP_SCALE, CPLOPrintf("%.15g", adfRPC[8]));
169
45
    asMD.SetNameValue(RPC_LAT_SCALE, CPLOPrintf("%.15g", adfRPC[9]));
170
45
    asMD.SetNameValue(RPC_LONG_SCALE, CPLOPrintf("%.15g", adfRPC[10]));
171
45
    asMD.SetNameValue(RPC_HEIGHT_SCALE, CPLOPrintf("%.15g", adfRPC[11]));
172
173
45
    CPLString osField;
174
45
    CPLString osMultiField;
175
176
945
    for (int i = 0; i < 20; ++i)
177
900
    {
178
900
        osField.Printf("%.15g", adfRPC[12 + i]);
179
900
        if (i > 0)
180
855
            osMultiField += " ";
181
45
        else
182
45
            osMultiField = "";
183
900
        osMultiField += osField;
184
900
    }
185
45
    asMD.SetNameValue(RPC_LINE_NUM_COEFF, osMultiField);
186
187
945
    for (int i = 0; i < 20; ++i)
188
900
    {
189
900
        osField.Printf("%.15g", adfRPC[32 + i]);
190
900
        if (i > 0)
191
855
            osMultiField += " ";
192
45
        else
193
45
            osMultiField = "";
194
900
        osMultiField += osField;
195
900
    }
196
45
    asMD.SetNameValue(RPC_LINE_DEN_COEFF, osMultiField);
197
198
945
    for (int i = 0; i < 20; ++i)
199
900
    {
200
900
        osField.Printf("%.15g", adfRPC[52 + i]);
201
900
        if (i > 0)
202
855
            osMultiField += " ";
203
45
        else
204
45
            osMultiField = "";
205
900
        osMultiField += osField;
206
900
    }
207
45
    asMD.SetNameValue(RPC_SAMP_NUM_COEFF, osMultiField);
208
209
945
    for (int i = 0; i < 20; ++i)
210
900
    {
211
900
        osField.Printf("%.15g", adfRPC[72 + i]);
212
900
        if (i > 0)
213
855
            osMultiField += " ";
214
45
        else
215
45
            osMultiField = "";
216
900
        osMultiField += osField;
217
900
    }
218
45
    asMD.SetNameValue(RPC_SAMP_DEN_COEFF, osMultiField);
219
45
    return asMD;
220
45
}
221
222
}  // namespace tiff_common
223
}  // namespace gdal