Skip to content

Commit

Permalink
Fix a decoding issue with some color grid formats
Browse files Browse the repository at this point in the history
It is possible for a color image grid to associate the alpha images
with the individual grid items instead of using an alpha image grid.

Related to AOMediaCodec/libavif#1203
  • Loading branch information
0xC0000054 committed Nov 10, 2022
1 parent 54ae23c commit 3fe2d77
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 43 deletions.
19 changes: 19 additions & 0 deletions src/Avif Container/ImageGridInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,25 @@ public ImageGridInfo(IReadOnlyList<uint> childImageIds, ImageGridDescriptor grid
this.OutputHeight = grid.OutputHeight;
}

public ImageGridInfo(IReadOnlyList<uint> alphaImageIds, ImageGridInfo colorGridInfo)
{
if (alphaImageIds is null)
{
ExceptionUtil.ThrowArgumentNullException(nameof(alphaImageIds));
}

if (colorGridInfo is null)
{
ExceptionUtil.ThrowArgumentNullException(nameof(colorGridInfo));
}

this.ChildImageIds = alphaImageIds;
this.TileColumnCount = colorGridInfo.TileColumnCount;
this.TileRowCount = colorGridInfo.TileRowCount;
this.OutputWidth = colorGridInfo.OutputWidth;
this.OutputHeight = colorGridInfo.OutputHeight;
}

public IReadOnlyList<uint> ChildImageIds { get; }

public int TileColumnCount { get; }
Expand Down
115 changes: 72 additions & 43 deletions src/Avif Reader/AvifReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,41 @@ public AvifReader(Stream input, bool leaveOpen, PaintDotNet.AppModel.IArrayPoolS
}
else
{
this.alphaGridInfo = null;
if (this.colorGridInfo != null)
{
// Some images may associate the alpha image with the individual grid images
// instead of using a separate alpha image grid.
// See https://github.com/AOMediaCodec/libavif/issues/1203

List<uint> alphaImageIds = new List<uint>();

foreach (uint colorItem in this.colorGridInfo.ChildImageIds)
{
uint alphaItem = this.parser.GetAlphaItemId(colorItem);

if (alphaItem != 0)
{
alphaImageIds.Add(alphaItem);
}
else
{
// The first image in the grid does not have an associated alpha image
// or only some of the grid images do.
//
// The image will be treated as not having an alpha channel.
break;
}
}

if (alphaImageIds.Count == this.colorGridInfo.ChildImageIds.Count)
{
this.alphaGridInfo = new ImageGridInfo(alphaImageIds, this.colorGridInfo);
}
}
else
{
this.alphaGridInfo = null;
}
}

// The HEIF specification allows an image to have up to one color information box of each type (ICC and/or NCLX).
Expand Down Expand Up @@ -119,7 +153,7 @@ public Surface Decode()
try
{
ProcessColorImage(surface);
if (this.alphaItemId != 0)
if (this.alphaItemId != 0 || this.alphaGridInfo != null)
{
ProcessAlphaImage(surface);
}
Expand Down Expand Up @@ -268,61 +302,43 @@ private void ApplyImageTransforms(ref Surface surface)
}
}

private void CheckImageItemType(uint itemId, ImageGridInfo gridInfo, string imageName, bool checkingGridChildren = false)
private void CheckImageItemType(uint itemId, ImageGridInfo gridInfo, string imageName)
{
IItemInfoEntry entry = this.parser.TryGetItemInfoEntry(itemId);

if (entry is null)
if (gridInfo != null)
{
ExceptionUtil.ThrowFormatException($"The { imageName } image does not exist.");
IReadOnlyList<uint> childImageIds = gridInfo.ChildImageIds;

for (int i = 0; i < childImageIds.Count; i++)
{
CheckImageItemType(childImageIds[i], null, imageName);
}
}
else if (entry.ItemType != ItemInfoEntryTypes.AV01)
else
{
if (entry.ItemType == ItemInfoEntryTypes.ImageGrid)
IItemInfoEntry entry = this.parser.TryGetItemInfoEntry(itemId);

if (entry is null)
{
if (checkingGridChildren)
ExceptionUtil.ThrowFormatException($"The {imageName} image does not exist.");
}
else if (entry.ItemType != ItemInfoEntryTypes.AV01)
{
if (entry.ItemType == ItemInfoEntryTypes.ImageGrid)
{
ExceptionUtil.ThrowFormatException("Nested image grids are not supported.");
}

if (gridInfo is null)
{
ExceptionUtil.ThrowFormatException($"The { imageName } image does not have any image grid information.");
}

IReadOnlyList<uint> childImageIds = gridInfo.ChildImageIds;

for (int i = 0; i < childImageIds.Count; i++)
else
{
CheckImageItemType(childImageIds[i], null, imageName, true);
ExceptionUtil.ThrowFormatException($"The {imageName} image is not a supported format.");
}
}
else
{
ExceptionUtil.ThrowFormatException($"The { imageName } image is not a supported format.");
}
}
}

private void CheckRequiredImageProperties(uint itemId, ImageGridInfo gridInfo, string imageName)
{
IItemInfoEntry entry = this.parser.TryGetItemInfoEntry(itemId);

if (entry is null)
{
ExceptionUtil.ThrowFormatException($"The { imageName } image does not exist.");
}
else if (entry.ItemType == ItemInfoEntryTypes.AV01)
{
this.parser.ValidateRequiredImageProperties(itemId);
}
else if (entry.ItemType == ItemInfoEntryTypes.ImageGrid)
if (gridInfo != null)
{
if (gridInfo is null)
{
ExceptionUtil.ThrowFormatException($"The { imageName } image does not have any image grid information.");
}

IReadOnlyList<uint> childImageIds = gridInfo.ChildImageIds;

for (int i = 0; i < childImageIds.Count; i++)
Expand All @@ -332,7 +348,20 @@ private void CheckRequiredImageProperties(uint itemId, ImageGridInfo gridInfo, s
}
else
{
ExceptionUtil.ThrowFormatException($"The { imageName } image is not a supported format.");
IItemInfoEntry entry = this.parser.TryGetItemInfoEntry(itemId);

if (entry is null)
{
ExceptionUtil.ThrowFormatException($"The {imageName} image does not exist.");
}
else if (entry.ItemType == ItemInfoEntryTypes.AV01)
{
this.parser.ValidateRequiredImageProperties(itemId);
}
else
{
ExceptionUtil.ThrowFormatException($"The {imageName} image is not a supported format.");
}
}
}

Expand Down Expand Up @@ -404,7 +433,7 @@ private void EnsureCompressedImagesAreAV1()
{
CheckImageItemType(this.primaryItemId, this.colorGridInfo, "color");

if (this.alphaItemId != 0)
if (this.alphaItemId != 0 || this.alphaGridInfo != null)
{
CheckImageItemType(this.alphaItemId, this.alphaGridInfo, "alpha");
}
Expand All @@ -428,7 +457,7 @@ private void EnsureRequiredImagePropertiesAreSupported()
{
CheckRequiredImageProperties(this.primaryItemId, this.colorGridInfo, "color");

if (this.alphaItemId != 0)
if (this.alphaItemId != 0 || this.alphaGridInfo != null)
{
CheckRequiredImageProperties(this.alphaItemId, this.alphaGridInfo, "alpha");
}
Expand Down

0 comments on commit 3fe2d77

Please sign in to comment.