Tests - not correct - to remove

This commit is contained in:
BobLd 2023-04-24 18:14:02 +01:00
parent 2c5cb69a64
commit af7aefb096
4 changed files with 311 additions and 186 deletions

View File

@ -17,13 +17,13 @@
Directory.CreateDirectory(OutputFolder);
}
[Fact]
public void IccXyzTest()
{
var path = IntegrationHelpers.GetSpecificTestDocumentPath("xyztest.pdf");
using (var document = PdfDocument.Open(path))
{
// page 1
var page1 = document.GetPage(1);
var paths = page1.ExperimentalAccess.Paths
.OrderBy(p => p.GetBoundingRectangle().Value.Centroid.X)
@ -80,6 +80,52 @@
}
}
[Fact]
public void IccLabProfile8bitLUT()
{
var path = IntegrationHelpers.GetSpecificTestDocumentPath("icc-lab-8bit.pdf");
using (var document = PdfDocument.Open(path))
{
var page1 = document.GetPage(1);
var paths = page1.ExperimentalAccess.Paths
.OrderBy(p => p.GetBoundingRectangle().Value.Centroid.X)
.ThenBy(p => p.GetBoundingRectangle().Value.Centroid.Y)
.ToArray();
var grouped = page1.ExperimentalAccess.Paths
.GroupBy(p => p.GetBoundingRectangle().Value.Centroid.X)
.ToDictionary(kvp => kvp.Key, kvp => kvp);
/*
* If all the profiles were rendered correctly, for the 3 "Lab" files we'd expect to see:
* stripe 1: lab(0 0 0) - black
* stripe 2: lab(100% 0 0) - white
* stripe 3: lab(75% 0 50) - mustard (#CEB759)
* stripe 4: lab(75% 50 0) - pink (#FF92BB)
*/
var black = grouped[30].Single().FillColor;
var white = grouped[60].Single().FillColor;
var mustard = grouped[90].Single().FillColor; // rgb(206,183,89)
var pink = grouped[120].Single().FillColor; // rgb(255,146,187)
AssertColor(206, 183, 89, mustard);
AssertColor(255, 146, 187, pink);
}
}
private void AssertColor(byte r, byte g, byte b, IColor color)
{
var rgb = color.ToRGBValues();
var rAct = ConvertToByte(rgb.r);
var gAct = ConvertToByte(rgb.g);
var bAct = ConvertToByte(rgb.b);
Assert.Equal(r, rAct);
Assert.Equal(g, gAct);
Assert.Equal(b, bAct);
}
[Fact]
public void IccLabProfileMatrixv4()
{
@ -422,7 +468,7 @@
private static byte ConvertToByte(decimal componentValue)
{
var rounded = Math.Round(componentValue * 255, MidpointRounding.AwayFromZero);
var rounded = Math.Round(componentValue * 255m, MidpointRounding.AwayFromZero);
return (byte)rounded;
}
}

View File

@ -1161,7 +1161,7 @@
return transformed;
}
private static double g(double x)
internal static double g(double x)
{
if (x > 6.0 / 29.0)
{
@ -1355,7 +1355,7 @@
return new double[] { 0, 0, 0 }; // Black RGB
}
if (Profile.TryProcess(values, out double[] xyz) && xyz.Length == 3)
if (Profile.TryProcessToPcs(values, null, out double[] xyz) && xyz.Length == 3) // No rendering intent for now
{
double x = xyz[0];
double y = xyz[1];
@ -1395,7 +1395,44 @@
return RGBColor.Black;
}
if (Profile.TryProcess(values, out double[] xyz) && xyz.Length == 3)
if (values.Any(x => x > 1.0))
{
//values[0] /= 100.0;
//values[1] = (values[1] + 127.0) / 255.0;
//values[2] = (values[2] + 127.0) / 255.0;
for (int i = 0; i < values.Length; i++)
{
values[i] /= 100.0;
}
}
if (Profile.Header.ColourSpace == IccColourSpaceType.CIELABorPCSLAB)
{
//values[0] /= 100.0;
//values[1] = (values[1] + 127.0) / 256.0;
//values[2] = (values[2] + 127.0) / 256.0;
// Component Ranges: L*: [0 100]; a* and b*: [128 127]
//double b = PdfFunction.ClipToRange(values[1], -128.0, 127.0);
//double c = PdfFunction.ClipToRange(values[2], -128.0, 127.0);
//double M = (values[0] + 16.0) / 116.0;
//double L = M + (b / 500.0);
//double N = M - (c / 200.0);
//IccXyz referenceWhite = Profile.Header.nCIEXYZ;
//values[0] = LabColorSpaceDetails.g(L) * referenceWhite.X; // X
//values[1] = LabColorSpaceDetails.g(M) * referenceWhite.Y; // Y
//values[2] = LabColorSpaceDetails.g(N) * referenceWhite.Z; // Z
//var labColor = labColorSpaceDetails.GetColor(values[0], values[1], values[2]);
//values[0] =
}
if (Profile.TryProcessToPcs(values, null, out double[] xyz) && xyz.Length == 3) // No rendering intent for now
{
double x = xyz[0];
double y = xyz[1];
@ -1408,7 +1445,7 @@
}
else
{
return labColorSpaceDetails.GetColor(x, y, z);
return labColorSpaceDetails.GetColor(x * 100.0, y * 255.0 - 127.0, z * 255.0 - 127.0);
}
}
}
@ -1437,7 +1474,8 @@
int outputSize = (int)(decoded.Count * outputCount / (double)NumberOfColorComponents);
var transformed = new byte[outputSize];
Parallel.For(0, decoded.Count / NumberOfColorComponents, i =>
//Parallel.For(0, decoded.Count / NumberOfColorComponents, i =>
for (int i = 0; i < decoded.Count / NumberOfColorComponents; i++)
{
double[] comps = new double[NumberOfColorComponents];
for (int n = 0; n < NumberOfColorComponents; n++)
@ -1450,7 +1488,8 @@
{
transformed[i * outputCount + c] = ConvertToByte(colors[c]);
}
});
}
//);
return transformed;
}
@ -1512,4 +1551,4 @@
throw new InvalidOperationException("UnsupportedColorSpaceDetails");
}
}
}
}

View File

@ -29,7 +29,13 @@ namespace IccProfileNet
index += w;
}
return clut[(int)index];
int indexInt = (int)index;
if (indexInt > clut.Length - 1)
{
//indexInt = clut.Length - 1;
}
return clut[indexInt];
}
internal static double[] Lookup(double[] input, double[][] clut, int clutGridPoints)
@ -40,10 +46,17 @@ namespace IccProfileNet
double index = 0;
for (int i = 0; i < input.Length; i++)
{
index += input[i] * Math.Pow(clutGridPoints, input.Length - i);
double pow = Math.Pow(clutGridPoints, input.Length - i);
index += input[i] * pow;
}
return clut[(int)index];
int indexInt = (int)index;
if (indexInt > clut.Length - 1)
{
//indexInt = indexInt % clut.Length;
}
return clut[indexInt];
}
/// <summary>

View File

@ -144,209 +144,236 @@ namespace IccProfileNet
}
}
public bool TryProcessOld(double[] input, out double[] output)
private bool TryProcessGrayTRC(double[] input, out double[] output)
{
throw new NotImplementedException($"Unsuported ICC profile v{Header.VersionMajor}");
// 8.3.4 Monochrome Input profiles
// 8.4.4 Monochrome Display profiles
// 8.5.3 Monochrome Output profiles
if (Tags.TryGetValue(IccTags.GrayTRCTag, out var kTrcTag) && kTrcTag is IccBaseCurveType kTrc)
{
double v = kTrc.Process(input.Single());
output = new double[] { v, v, v };
return true;
}
//switch (Header.VersionMajor)
//{
// case 2:
// return TryProcess2(input, out output);
// case 4:
// return TryProcess4(input, out output);
// default:
// throw new NotImplementedException($"Unsuported ICC profile v{Header.VersionMajor}");
//}
output = null;
return false;
}
public bool TryProcess(double[] input, out double[] output)
private bool TryProcessTRCMatrix(double[] input, out double[] output)
{
try
// 8.3.3 Three-component matrix-based Input profiles
// 8.4.3 Three-component matrix-based Display profiles
// See p197 of Wiley book
if (Tags.TryGetValue(IccTags.RedMatrixColumnTag, out var rmcTag) && rmcTag is IccXyzType rmc &&
Tags.TryGetValue(IccTags.GreenMatrixColumnTag, out var gmcTag) && gmcTag is IccXyzType gmc &&
Tags.TryGetValue(IccTags.BlueMatrixColumnTag, out var bmcTag) && bmcTag is IccXyzType bmc &&
Tags.TryGetValue(IccTags.RedTRCTag, out var rTrcTag) && rTrcTag is IccBaseCurveType rTrc &&
Tags.TryGetValue(IccTags.GreenTRCTag, out var gTrcTag) && gTrcTag is IccBaseCurveType gTrc &&
Tags.TryGetValue(IccTags.BlueTRCTag, out var bTrcTag) && bTrcTag is IccBaseCurveType bTrc)
{
// Optional
// Tags.TryGetValue(IccTags.ChromaticAdaptationTag, out var caTag) && caTag is IccS15Fixed16ArrayType ca
double channel1 = input[0];
double channel2 = input[1];
double channel3 = input[2];
double lR = rTrc.Process(channel1);
double lG = gTrc.Process(channel2);
double lB = bTrc.Process(channel3);
double cX = (rmc.Xyz.X * lR) + (gmc.Xyz.X * lG) + (bmc.Xyz.X * lB);
double cY = (rmc.Xyz.Y * lR) + (gmc.Xyz.Y * lG) + (bmc.Xyz.Y * lB);
double cZ = (rmc.Xyz.Z * lR) + (gmc.Xyz.Z * lG) + (bmc.Xyz.Z * lB);
output = new double[] { cX, cY, cZ };
return true;
}
output = null;
return false;
}
/// <summary>
/// Process from Device space to PCS.
/// <para>
/// A to B.
/// </para>
/// </summary>
public bool TryProcessToPcs(double[] input, IccRenderingIntent? renderingIntent, out double[] output)
{
// See Table 25 — Profile type/profile tag and defined rendering intents
//try
//{
if (TryProcessGrayTRC(input, out output))
{
return true;
}
if (TryProcessTRCMatrix(input, out output))
{
return true;
}
if (renderingIntent == null)
{
// use profile rendering intent
renderingIntent = Header.RenderingIntent;
}
switch (Header.ProfileClass)
{
case IccProfileClass.Input:
{
// 8.3 Input profiles
// 8.3.2 N-component LUT-based Input profiles
if (Tags.TryGetValue(IccTags.AToB0Tag, out var lutAB0TagI) &&
lutAB0TagI is IccLutABType lutAb0I)
{
//output = lutAB.Process(input, Header);
//return true;
}
// 8.3.3 Three-component matrix-based Input profiles
if (Tags.TryGetValue(IccTags.RedMatrixColumnTag, out var rmcTagI) &&
Tags.TryGetValue(IccTags.RedTRCTag, out var rTrcTagI))
{
// Check other taggs
}
// 8.3.4 Monochrome Input profiles
if (Tags.TryGetValue(IccTags.GrayTRCTag, out var kTrcTag) && kTrcTag is IccBaseCurveType kTrc)
{
double v = kTrc.Process(input.Single());
output = new double[] { v, v, v };
return true;
}
break;
}
case IccProfileClass.Display:
{
// 8.4 Display profiles
// 8.4.2 N-Component LUT-based Display profilesS
if (Tags.TryGetValue(IccTags.AToB0Tag, out var lutAB0TagD) && lutAB0TagD is IccLutABType lutAb0D &&
Tags.TryGetValue(IccTags.BToA0Tag, out var lutBA0TagD) && lutBA0TagD is IccLutABType lutBa0D)
{
output = lutBa0D.Process(input, Header);
return true;
}
// 8.4.3 Three-component matrix-based Display profiles
// See p197 of Wiley book
if (Tags.TryGetValue(IccTags.RedMatrixColumnTag, out var rmcTag) && rmcTag is IccXyzType rmc &&
Tags.TryGetValue(IccTags.GreenMatrixColumnTag, out var gmcTag) && gmcTag is IccXyzType gmc &&
Tags.TryGetValue(IccTags.BlueMatrixColumnTag, out var bmcTag) && bmcTag is IccXyzType bmc &&
Tags.TryGetValue(IccTags.RedTRCTag, out var rTrcTag) && rTrcTag is IccBaseCurveType rTrc &&
Tags.TryGetValue(IccTags.GreenTRCTag, out var gTrcTag) && gTrcTag is IccBaseCurveType gTrc &&
Tags.TryGetValue(IccTags.BlueTRCTag, out var bTrcTag) && bTrcTag is IccBaseCurveType bTrc)
{
// Optional
// Tags.TryGetValue(IccTags.ChromaticAdaptationTag, out var caTag) && caTag is IccS15Fixed16ArrayType ca
double channel1 = input[0];
double channel2 = input[1];
double channel3 = input[2];
double lR = rTrc.Process(channel1);
double lG = gTrc.Process(channel2);
double lB = bTrc.Process(channel3);
double cX = rmc.Xyz.X * lR + gmc.Xyz.X * lG + bmc.Xyz.X * lB;
double cY = rmc.Xyz.Y * lR + gmc.Xyz.Y * lG + bmc.Xyz.Y * lB;
double cZ = rmc.Xyz.Z * lR + gmc.Xyz.Z * lG + bmc.Xyz.Z * lB;
output = new double[] { cX, cY, cZ };
return true;
}
// 8.4.4 Monochrome Display profiles
if (Tags.TryGetValue(IccTags.GrayTRCTag, out var kTrcTag) && kTrcTag is IccBaseCurveType kTrc)
{
double v = kTrc.Process(input.Single());
output = new double[] { v, v, v };
return true;
}
break;
}
case IccProfileClass.Output:
{
// 8.5.2 N-component LUT-based Output profiles
if (Tags.TryGetValue(IccTags.AToB0Tag, out var lutAB0Tag) && lutAB0Tag is IIccClutType lutAb0 &&
Tags.TryGetValue(IccTags.AToB1Tag, out var lutAB1Tag) && lutAB1Tag is IIccClutType lutAb1 &&
Tags.TryGetValue(IccTags.AToB2Tag, out var lutAB2Tag) && lutAB2Tag is IIccClutType lutAb2 &&
Tags.TryGetValue(IccTags.BToA0Tag, out var lutBA0Tag) && lutBA0Tag is IIccClutType lutBa0 &&
Tags.TryGetValue(IccTags.BToA1Tag, out var lutBA1Tag) && lutBA1Tag is IIccClutType lutBa1 &&
Tags.TryGetValue(IccTags.BToA2Tag, out var lutBA2Tag) && lutBA2Tag is IIccClutType lutBa2 &&
Tags.TryGetValue(IccTags.GamutTag, out var gamutTag))
{
// Optional??
// Tags.TryGetValue(IccTags.ColorantTableTag, out var colorantTableTag)
switch (Header.RenderingIntent)
{
case IccRenderingIntent.Perceptual:
output = lutAb0.Process(input, Header);
return true;
case IccRenderingIntent.MediaRelativeColorimetric:
output = lutAb1.Process(input, Header);
return true;
case IccRenderingIntent.Saturation:
output = lutAb2.Process(input, Header);
return true;
}
}
// 8.5.3 Monochrome Output profiles
if (Tags.TryGetValue(IccTags.GrayTRCTag, out var tag))
{
}
break;
}
case IccProfileClass.DeviceLink:
{
// TODO
break;
}
case IccProfileClass.ColorSpace:
{
// 8.7 ColorSpace profile
if (Tags.TryGetValue(IccTags.AToB0Tag, out var lutAB0Tag) &&
Tags.TryGetValue(IccTags.BToA0Tag, out var lutBA0Tag))
// 8.3.2 N-component LUT-based Input profiles
// 8.4.2 N-Component LUT-based Display profiles
// 8.5.2 N-component LUT-based Output profiles
if (renderingIntent == IccRenderingIntent.Perceptual && Tags.TryGetValue(IccTags.AToB0Tag, out var lutAB0Tag)
&& lutAB0Tag is IIccClutType lutAb0)
{
// TODO - check
if (lutAB0Tag is IccLutABType lutAb0)
{
output = lutAb0.Process(input, Header);
return true;
}
else if (lutAB0Tag is IccBaseLutType lutAb0bis)
{
output = lutAb0bis.Process(input, Header);
return true;
}
else
{
throw new Exception("");
}
output = lutAb0.Process(input, Header);
return true;
}
else if (renderingIntent == IccRenderingIntent.MediaRelativeColorimetric && Tags.TryGetValue(IccTags.AToB1Tag, out var lutAB1Tag)
&& lutAB1Tag is IIccClutType lutAb1)
{
output = lutAb1.Process(input, Header);
return true;
}
else if (renderingIntent == IccRenderingIntent.Saturation && Tags.TryGetValue(IccTags.AToB2Tag, out var lutAB2Tag)
&& lutAB2Tag is IIccClutType lutAb2)
{
output = lutAb2.Process(input, Header);
return true;
}
else if (Tags.TryGetValue(IccTags.AToB0Tag, out var lutAB0TagDefault) && lutAB0TagDefault is IIccClutType lutAb0Default)
{
output = lutAb0Default.Process(input, Header);
return true;
}
break;
}
case IccProfileClass.Abstract:
case IccProfileClass.DeviceLink:
case IccProfileClass.NamedColor: // undefined actually
{
if (Tags.TryGetValue(IccTags.AToB0Tag, out var lutAB0Tag))
// TODO - use IIccClutType instead?
if (Tags.TryGetValue(IccTags.AToB0Tag, out var lutAB0Tag) && lutAB0Tag is IIccClutType lutAb0)
{
// TODO - check
if (lutAB0Tag is IccLutABType lutAb0)
{
output = lutAb0.Process(input, Header);
return true;
}
else if (lutAB0Tag is IccBaseLutType lutAb0bis)
{
output = lutAb0bis.Process(input, Header);
return true;
}
else
{
throw new Exception("");
}
output = lutAb0.Process(input, Header);
return true;
}
break;
}
case IccProfileClass.NamedColor:
default:
throw new ArgumentOutOfRangeException("TODO");
}
//}
//catch (Exception ex)
//{
// // Ignore
// System.Diagnostics.Debug.WriteLine(ex);
// output = null;
// return false;
//}
output = null;
return false;
}
/// <summary>
/// Process from PCS to Device space.
/// <para>
/// B to A.
/// </para>
/// </summary>
public bool TryProcessFromPcs(double[] input, IccRenderingIntent? renderingIntent, out double[] output)
{
// TODO
// See Table 25 — Profile type/profile tag and defined rendering intents
try
{
if (TryProcessGrayTRC(input, out output)) // TODO - Need inversion
{
return true;
}
if (TryProcessTRCMatrix(input, out output)) // TODO - Need inversion
{
return true;
}
if (renderingIntent == null)
{
// use profile rendering intent
renderingIntent = Header.RenderingIntent;
}
switch (Header.ProfileClass)
{
case IccProfileClass.Input:
case IccProfileClass.Display:
case IccProfileClass.Output:
case IccProfileClass.ColorSpace:
{
// TODO
// 8.3.2 N-component LUT-based Input profiles
// 8.4.2 N-Component LUT-based Display profiles
// 8.5.2 N-component LUT-based Output profiles
if (Tags.TryGetValue(IccTags.BToA0Tag, out var lutBA0Tag) && lutBA0Tag is IIccClutType lutBa0 &&
Tags.TryGetValue(IccTags.BToA1Tag, out var lutBA1Tag) && lutBA1Tag is IIccClutType lutBa1 &&
Tags.TryGetValue(IccTags.BToA2Tag, out var lutBA2Tag) && lutBA2Tag is IIccClutType lutBa2)
{
// Optional??
// Tags.TryGetValue(IccTags.GamutTag, out var gamutTag)
// Tags.TryGetValue(IccTags.ColorantTableTag, out var colorantTableTag)
switch (renderingIntent)
{
case IccRenderingIntent.Perceptual:
output = lutBa0.Process(input, Header);
return true;
case IccRenderingIntent.MediaRelativeColorimetric:
output = lutBa1.Process(input, Header);
return true;
case IccRenderingIntent.Saturation:
output = lutBa2.Process(input, Header);
return true;
default:
output = lutBa0.Process(input, Header);
return true;
}
}
break;
}
case IccProfileClass.Abstract:
case IccProfileClass.DeviceLink:
case IccProfileClass.NamedColor: // undefined actually
{
if (Tags.TryGetValue(IccTags.BToA0Tag, out var lutBA0Tag) && lutBA0Tag is IccLutABType lutBa0)
{
output = lutBa0.Process(input, Header);
return true;
}
break;
}
default:
throw new ArgumentOutOfRangeException();
throw new ArgumentOutOfRangeException("TODO");
}
}
catch (Exception)
@ -366,4 +393,4 @@ namespace IccProfileNet
return $"ICC Profile v{Header}";
}
}
}
}