Skip to content

Commit

Permalink
GTiff/gdalwarp/COG: preserve pre-multiplied alpha information from so…
Browse files Browse the repository at this point in the history
…urce TIFF

Fixes #11377
  • Loading branch information
rouault committed Nov 28, 2024
1 parent 4cfbcf8 commit b24644d
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 5 deletions.
13 changes: 13 additions & 0 deletions apps/gdalwarp_lib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4712,6 +4712,19 @@ static GDALDatasetH GDALWarpCreateOutput(
aosCreateOptions.FetchNameValue("PHOTOMETRIC") == nullptr)
{
aosCreateOptions.SetNameValue("PHOTOMETRIC", "RGB");

// Preserve potential ALPHA=PREMULTIPLIED from source alpha band
const char *pszAlpha;
if (aosCreateOptions.FetchNameValue("ALPHA") == nullptr &&
apeColorInterpretations.size() == 4 &&
apeColorInterpretations[3] == GCI_AlphaBand &&
GDALGetRasterCount(pahSrcDS[0]) == 4 &&
(pszAlpha =
GDALGetMetadataItem(GDALGetRasterBand(pahSrcDS[0], 4),
"ALPHA", "IMAGE_STRUCTURE")))
{
aosCreateOptions.SetNameValue("ALPHA", pszAlpha);
}
}

/* The GTiff driver now supports writing band color interpretation */
Expand Down
29 changes: 29 additions & 0 deletions autotest/gcore/cog.py
Original file line number Diff line number Diff line change
Expand Up @@ -1961,3 +1961,32 @@ def test_cog_write_check_golden_file(tmp_path, src_filename, creation_options):
)
assert os.stat(src_filename).st_size == os.stat(out_filename).st_size
assert open(src_filename, "rb").read() == open(out_filename, "rb").read()


###############################################################################


def test_cog_preserve_ALPHA_PREMULTIPLIED_on_copy(tmp_vsimem):

src_filename = str(tmp_vsimem / "src.tif")
src_ds = gdal.GetDriverByName("GTiff").Create(
src_filename, 1, 1, 4, options=["ALPHA=PREMULTIPLIED", "PROFILE=BASELINE"]
)
src_ds.SetGeoTransform([500000, 1, 0, 4500000, 0, -1])
srs = osr.SpatialReference()
srs.ImportFromEPSG(32631)
src_ds.SetProjection(srs.ExportToWkt())

out_filename = str(tmp_vsimem / "out.tif")
gdal.GetDriverByName("COG").CreateCopy(
out_filename,
src_ds,
options=[
"TILING_SCHEME=GoogleMapsCompatible",
],
)
with gdal.Open(out_filename) as ds:
assert (
ds.GetRasterBand(4).GetMetadataItem("ALPHA", "IMAGE_STRUCTURE")
== "PREMULTIPLIED"
)
29 changes: 28 additions & 1 deletion autotest/gcore/tiff_write.py
Original file line number Diff line number Diff line change
Expand Up @@ -948,7 +948,7 @@ def test_tiff_write_20():


###############################################################################
# Test RGBA images with TIFFTAG_EXTRASAMPLES=EXTRASAMPLE_ASSOCALPHA
# Test RGBA images with TIFFTAG_EXTRASAMPLES=EXTRASAMPLE_UNASSOCALPHA


def test_tiff_write_21():
Expand Down Expand Up @@ -11927,3 +11927,30 @@ def test_tiff_write_check_golden_file(tmp_path, src_filename, creation_options):
)
assert os.stat(src_filename).st_size == os.stat(out_filename).st_size
assert open(src_filename, "rb").read() == open(out_filename, "rb").read()


###############################################################################
# Test preserving ALPHA=PREMULTIPLIED on copy


def test_tiff_write_preserve_ALPHA_PREMULTIPLIED_on_copy(tmp_path):

src_filename = str(tmp_path / "src.tif")
out_filename = str(tmp_path / "out.tif")
gdal.GetDriverByName("GTiff").Create(
src_filename, 1, 1, 4, options=["ALPHA=PREMULTIPLIED", "PROFILE=BASELINE"]
)
assert gdal.VSIStatL(src_filename + ".aux.xml") is None
with gdal.Open(src_filename) as src_ds:
assert (
src_ds.GetRasterBand(4).GetMetadataItem("ALPHA", "IMAGE_STRUCTURE")
== "PREMULTIPLIED"
)
gdal.GetDriverByName("GTiff").CreateCopy(
out_filename, src_ds, options=["PROFILE=BASELINE"]
)
with gdal.Open(out_filename) as out_ds:
assert (
out_ds.GetRasterBand(4).GetMetadataItem("ALPHA", "IMAGE_STRUCTURE")
== "PREMULTIPLIED"
)
18 changes: 14 additions & 4 deletions frmts/gtiff/gtiffdataset_write.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7055,10 +7055,10 @@ GDALDataset *GTiffDataset::CreateCopy(const char *pszFilename,
CPLMalloc(nExtraSamples * sizeof(uint16_t)));
memcpy(pasNewExtraSamples, extraSamples,
nExtraSamples * sizeof(uint16_t));
uint16_t nAlpha = GTiffGetAlphaValue(
CPLGetConfigOption("GTIFF_ALPHA",
CSLFetchNameValue(papszOptions, "ALPHA")),
DEFAULT_ALPHA_TYPE);
const char *pszAlpha = CPLGetConfigOption(
"GTIFF_ALPHA", CSLFetchNameValue(papszOptions, "ALPHA"));
const uint16_t nAlpha =
GTiffGetAlphaValue(pszAlpha, DEFAULT_ALPHA_TYPE);
const int nBaseSamples = l_nBands - nExtraSamples;
for (int iExtraBand = nBaseSamples + 1; iExtraBand <= l_nBands;
iExtraBand++)
Expand All @@ -7067,6 +7067,16 @@ GDALDataset *GTiffDataset::CreateCopy(const char *pszFilename,
GCI_AlphaBand)
{
pasNewExtraSamples[iExtraBand - nBaseSamples - 1] = nAlpha;
if (!pszAlpha)
{
// Use the ALPHA metadata item from the source band, when
// present, if no explicit ALPHA creation option
pasNewExtraSamples[iExtraBand - nBaseSamples - 1] =
GTiffGetAlphaValue(
poSrcDS->GetRasterBand(iExtraBand)
->GetMetadataItem("ALPHA", "IMAGE_STRUCTURE"),
nAlpha);
}
}
}
TIFFSetField(l_hTIFF, TIFFTAG_EXTRASAMPLES, nExtraSamples,
Expand Down
5 changes: 5 additions & 0 deletions frmts/gtiff/gtiffrasterband.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,12 @@ GTiffRasterBand::GTiffRasterBand(GTiffDataset *poDSIn, int nBandIn)
if (nBand > nBaseSamples && nBand - nBaseSamples - 1 < count &&
(v[nBand - nBaseSamples - 1] == EXTRASAMPLE_ASSOCALPHA ||
v[nBand - nBaseSamples - 1] == EXTRASAMPLE_UNASSALPHA))
{
if (v[nBand - nBaseSamples - 1] == EXTRASAMPLE_ASSOCALPHA)
m_oGTiffMDMD.SetMetadataItem("ALPHA", "PREMULTIPLIED",
"IMAGE_STRUCTURE");
m_eBandInterp = GCI_AlphaBand;
}
else
m_eBandInterp = GCI_Undefined;
}
Expand Down

0 comments on commit b24644d

Please sign in to comment.