# From sRGB color space to sRGB profile: how to calculate the ICC sRGB profile primaries from the sRGB color space specifications

This page provides a step-by-step worked example of performing a Bradford chromatic adaptation to calculate the D50-adapted ICC sRGB profile red, green, and blue primaries from the unadapted red, green, blue, and white xyz values given in the sRGB color space specifications.

Written February 2014.

## Overview: From the sRGB D65 color space to the ICC D50-adapted sRGB profile

The ICC sRGB profile is made as follows:

1. The unadapted red, green, and blue XYZ values are calculated from the unadapted red, green, and blue xyz values plus the D65 source white point xyz values given in the sRGB specifications.
2. The unadapted red, green, and blue XYZ values are chromatically adapted 1 from the sRGB D65 source white point to the ICC D50 destination white point using the Bradford transformation matrix.

The rest of this page walks you step by step through the mathematics of calculating the ICC D50-adapted sRGB profile red, green, and blue XYZ primaries from the values given in the sRGB D65 color space specification.2 All of the calculations are in the downloadable sRGB specifications to ICC profile spreadsheet.

## Step by step: Adapting the sRGB D65 color space red, green, and blue primaries from the sRGB D65 source white point to the ICC D50 illuminant

### The sRGB specifications

The sRGB specifications give the values for the sRGB unadapted red, green, and blue xy values and the sRGB D65 source white point:

1. Poynton's Color FAQ, Question 17
2. Basic sRGB Math, from the Wayback archive of the original Hewlett-Packard/Microsoft sRGB website.
3. sRGB
4. Default RGB Colour Space - sRGB
5. A Standard Default Color Space for the Internet - sRGB and A Standard Default Color Space for the Internet: sRGB (essentially the same information hosted by the W3C and the ICC, respectively)

Table 1 gives the sRGB unadapted red, green, blue, and white xyz values:

Table 1: sRGB specifications unadapted red, green, blue, and D65 source white point xyz values xy values
x y z
D65 Source White Point 0.3127 0.3290 0.3583

If you do some adding and subtracting, you'll find that the z values are always equal to (1-x-y), so the z values don't add any new information. The real question is what are the right sRGB unadapted red, green, blue, and source white point Y values? The Y value for D65 is automatically 1.0 because RGB color space white points by definition are normalized to have a relative luminance of 1.0. For now, let's postpone how to calculate the sRGB unadapted red, green, and blue Y values.

### sRGB profile D65 source and D50 destination white points

To make an actual ICC profile from the xyY values given in the sRGB specification, the unadapted xyY values must be chromatically adapted from the D65 sRGB source white point to the ICC D50 profile destination white point, better known as the ICC profile D50 illuminant. The ICC V4 profile specifications give the exact values for the ICC D50 illuminant:

3.1.21
PCS illuminant
illuminant with the spectral radiance distribution of CIE illuminant D50 and nCIEXYZ X = 0,964 2, nCIEXYZ Y = 1,0, nCIEXYZ Z = 0,824 9

6.3.1
The PCS adopted white chromaticity shall be the chromaticity of the D50 illuminant defined in ISO 3640

7.2.16 PCS illuminant field (Bytes 68 to 79)
The PCS illuminant field shall contain the nCIEXYZ values X = 0,964 2, Y = 1,0 and Z = 0,824 9 encoded as an XYZNumber. . . .
NOTE These values are the nCIE XYZ values of CIE illuminant D

Table 2a gives the sRGB source and destination white points that will be used to calculate the sRGB D65 to ICC D50 Bradford chromatic adaptation matrix:

Table 2a: sRGB D65 source and ICC D50 destination white points
sRGB D65 Source White Point x y Y
0.3127 0.3290 1.0
ICC D50 Destination White Point X Y Z
0.9642 1.0000 0.8249

Notice that the sRGB D65 white point is given as xyY values, whereas the ICC specifications gives the D50 destination white point as XYZ values. Chromatic adaptation calculations are done using XYZ values rather than xyY values. So calculating the sRGB D65 to ICC D50 Bradford chromatic adaptation matrix requires converting the sRGB specifications D65 source white point values from xyY to XYZ. Fortunately, it's very simple to convert from xyY to XYZ:

X = ( x times Y )   ⁄   y

Y = Y

Z = ( ( 1 − x − y ) times Y )   ⁄   y

Table 2b gives the sRGB D65 source and ICC D50 destination white points as XYZ values:

Table 2b: sRGB D65 source and D50 destination white points as XYZ values
X Y Z
sRGB D65 Source White Point 0.950455927 1.000000000 1.089057751
ICC D50 Destination White Point 0.964200000 1.000000000 0.824900000

If you play around with the xyY to XYZ equations and the corresponding XYZ to xyY equations, you'll see that maintaining four decimal places of accuracy for values initially specified as xyY values requires considerably more than four decimal places in the resulting XYZ values; the same is also true when going the other way around.

All calculations on this page are carried out to 9 decimal places. The final results are rounded to 8 decimal places.

Doing a chromatic adaptation from a source to a destination white point requires first choosing a matrix that defines the cone response domain tranformation. Many such cone response domain transformations have been proposed. The ICC V4 specifications suggests (that is, strongly urges but doesn't actually require) using the Bradford model:

6.2 Rendering intents
6.2.1 General
The colorimetric rendering intents operate on measurement-based colorimetric values as adapted to the PCS adopted white chromaticity. This adaptation, when required, shall be indicated in the chromaticAdaptationTag. For the purposes of this ICC specifications, chromatic adaptation should be calculated using the linear Bradford model.

 0.8951 0.2664 -0.1614 -0.7502 1.7135 0.0367 0.0389 -0.0685 1.0296

Just to clarify a possibly point of confusion, the Bradford tranformation matrix isn't a chromatic adaptation matrix. Rather it's one of many mathematical transforms that have been suggested for converting XYZ values to an LMS color space that describes the "response of the three types of cones of the human eye, named after their responsivity (sensitivity) at long, medium and short wavelengths". So the Bradford transformation matrix in Table 3 above is used to calculate Bradford chromatic adaptation matrices.

The sRGB specifications don't mention anything at all about how to adapt the sRGB color space to the D50 destination white point required for ICC profiles. This is not surprising as the sRGB color space describes the ideal CRT monitor in a non-ICC-profile-color-managed context. However, the ICC recommends using Bradford adaptation and that's what LittleCMS, ArgyllCMS, Adobe, and probably all profile makers use when making the sRGB ICC profile (and also all the other standard RGB working spaces).

 1.04789 0.0229188 -0.0502161 0.0295818 0.990484 -0.0170787 -0.00925188 0.0150726 0.751678

### Unadapted red, green, and blue: from xyz to xyY

Chromatic adaptation is done using XYZ values and the sRGB specifications provide xyz values 3. Converting from xyY to XYZ is trivially easy, but does require that you have Y as well as xy. Because RGB color space white points are normalized to 1.0, the sRGB D65 source white point Y value is automatically 1.0. Now I'll show you the math for calculating the sRGB unadapted red, green, and blue Y values, equations for which can be found in the ArgyllCMS source code in the file "icc/mkDispProf.c":

Assume for the moment the unadapted sRGB red, green, and blue Y values are also equal to 1.0, producing the interim red, green, and blue xyY values in Table 5a:

Table 5a: interim sRGB unadapted red, green, blue xyY values, with Y set equal to 1.0
x y Y
red (unadapted) 0.6400 0.3300 1.0 (interim)
green (unadapted) 0.3000 0.6000 1.0 (interim)
blue (unadapted) 0.1500 0.0600 1.0 (interim)

Calculate the interim XYZ values from the assumed xyY values in Table 5a, using the xyY to XYZ equations, producing the values in Table 5b:

Table 5b: interim sRGB unadapted red, green, blue XYZ values, calculated from the interim xyY values in Table 5a
X 1.939393939 0.500000000 2.500000000
Y 1.000000000 1.000000000 1.000000000
Z 0.090909091 0.166666667 13.166666667

To calculate the real unadapted red, green, and blue Y values requires three steps. The first step is to treat the XYZ values in Table 5b as a 3x3 matrix and calculate its inverse (openoffice/libreoffic MINVERSE function). The resulting inverse matrix is shown in Table 5c:

 0.689157 -0.326908 -0.106024 -0.693173 1.34163 0.0297189 0.00401606 -0.0147256 0.0763052

The second step is to turn the sRGB D65 source white point XYZ values from Table 2b above into a 1x3 matrix, as shown in Table 5d:

 X 0.950456 1 1.08906

The third step is to multiply the 3x3 matrix in Table 5c by the 1x3 matrix in Table 5d to produce a second 1x3 matrix. The result, shown in Table 5e, is composed of the actual (as opposed to interim) unadapted red, green, and blue Y values:

 actual unadapted red Y 0.212639 0.715169 0.0721923

Given the red, green, and blue xy values and the sRGB D65 source white point xyY values, the mathematics shown above for calculating the sRGB unadapted red, green, and blue Y values seems like mathemagic to me. I think more in pictures than in numbers and if anyone knows of or can create a convincing visual explanation of why the mathemagic works, please share!

### Unadapted red, green, and blue: from xyY to XYZ

If you will recall from Section B2, I mentioned that chromatic adaptation equations use XYZ values. The goal of Sections B3 through B5 has been get to the point where we could calculate the sRGB unadapted XYZ values from the unadapted xyz values. Now that we have the sRGB unadapted red, green, and blue xyY values, it's trivial to calculate the correponding unadapted red, green, and blue XYZ values.

Table 6 shows the result of using the xyY to XYZ equations to calculate the sRGB unadapted XYZ values from the unadapted xyY values:

Table 6: sRGB unadapted red, green, and blue XYZ values calculated from the unadapted xyY values
red x 0.640000000 X 0.412390799
y 0.330000000 Y 0.212639006
Y 0.212639006 Z 0.019330819
green x 0.300000000 X 0.357584339
y 0.600000000 Y 0.715168679
Y 0.715168679 Z 0.119194780
blue x 0.150000000 X 0.180480788
y 0.060000000 Y 0.072192315
Y 0.072192315 Z 0.950532152

### Calculating the D50-adapted sRGB primaries

We're almost done! To review what we've covered so far:

1. The sRGB specifications give the sRGB unadapted red, green, and blue xy values and the sRGB D65 source white point.
2. The ICC specifications give the ICC D50 destination white point.
3. The ICC specifications also suggest using the Bradford transformation when calculating the source-to-destination chromatic adaptation matrix.
4. We calculated the sRGB D65 to ICC D50 Bradford adaptation matrix from the sRGB D65 source white point, the ICC D50 destination white point, and the Bradford transformation matrix.
5. We used mathemagic to calculate the unadapted sRGB red, green, and blue xyY values from the unadapted red, green, blue, and white xyz values given in the sRGB specification.
6. Finally, we calculated the unadapted sRGB red, green, and blue XYZ values from the unadapted red, green, and blue xyY values.

The only thing left to do is multiply the sRGB D65 to ICC D50 Bradford adaptation matrix (calculated in Section B4) by each of the unadapted red, green, and blue primaries (calculated in Section B6), in turn:

1. To calculate the ICC D50-adapted sRGB red XYZ values: multiply the D65 to D50 chromatic adaptation matrix by the sRGB unadapted red XYZ primaries from Table 6.
2. To calculate the ICC D50-adapted sRGB green XYZ values: multiply the D65 to D50 chromatic adaptation matrix by the sRGB unadapted green XYZ primaries from Table 6.
3. To calculate the ICC D50-adapted sRGB blue XYZ values: multiply the D65 to D50 chromatic adaptation matrix by the sRGB unadapted blue XYZ primaries from Table 6.

Applying the equations to calculate the D50-adapted sRGB primaries from the D65 unadapted sRGB primaries yields the following ICC sRGB profile red, green, and blue primaries:

Table 7: sRGB ICC profile XYZ primaries, adapted from sRGB D65 to ICC D50
X Y Z
Red 0.436041252 0.222484540 0.013920187
Green 0.385112911 0.716905079 0.097067239
Blue 0.143045838 0.060610381 0.713912574

## Spreadsheet calculations vs the ArgyllCMS sRGB.icm profile

### Comparing our spreadsheet-calculated values to values generated while making an sRGB profile using the ArgyllCMS mkDispProf

The ArgyllCMS version 1.6.3 source code includes the mkDispProf.c source code file, which is set up to make the sRGB ICC profile 4. Graeme Gill, author of ArgyllCMS, very kindly wrote the mkDispProf.c code as a demonstration of how to use ArgyllCMS to make ICC profiles.

The following code box shows the mkDispProf license and copyright information plus excerpts from the code, which I have modified to include additional printf statements. The sRGB red, green, blue, and white unadapted primaries are highlighted, as is the information that will be printed to the terminal screen when the modified mkDispProf is compiled and the executable is used to make the sRGB profile:

```/*
* Create an ICC V2.4 compatible matrix display profile.
*
* Author:  Graeme W. Gill
* Date:    8/2/2008
* Version: 1.00
*
* Copyright 2006 - 2014 Graeme W. Gill
*
* see the License.txt file in this directory for licensing details.
*
* Based on icc/lutest.c
*/
/* Setup the primaries */
{
/* Compute primaries as XYZ */
icmXYZNumber wrgb[4] = {  /* Primaries in Yxy from the standard */
{ 1.0, 0.3127, 0.3290 }, /* White */
{ 1.0, 0.6400, 0.3300 }, /* Red */
{ 1.0, 0.3000, 0.6000 }, /* Green */
{ 1.0, 0.1500, 0.0600 }  /* Blue */
};

double mat[3][3];
int i;

/* Convert Yxy to XYZ */
printf("\nPrint interim unadapted XYZ primaries calculated by setting Y=1.0\n");
for (i = 0; i < 4; i++) {
double v[3];

icmXYZ2Ary(v, wrgb[i]);
icmYxy2XYZ(v, v);
icmAry2XYZ(wrgb[i], v);

printf("{icmAry2XYZ: i=%d, %1.9f, %1.9f, %1.9f } \n",
i, wrgb[i].X, wrgb[i].Y, wrgb[i].Z);
}

/* Convert XYZ to normalised 3x3 matrix */
icmRGBprim2matrix(wrgb[0], wrgb[1], wrgb[2], wrgb[3], mat);

printf("\nPrint D65 sRGB white point: XYZ\n");
printf("{ %1.9f, %1.9f, %1.9f }     /* White */\n",
wrgb[0].X, wrgb[0].Y, wrgb[0].Z);

printf("{ %1.9f, %1.9f, %1.9f }, /* Red */\n"
"{ %1.9f, %1.9f, %1.9f }, /* Green */\n"
"{ %1.9f, %1.9f, %1.9f }, /* Blue */\n",
mat[0][0], mat[0][1], mat[0][2],
mat[1][0], mat[1][1], mat[1][2],
mat[2][0], mat[2][1], mat[2][2]);

[ . . .
skipping over some code
. . . ]

/* Red, Green and Blue Colorant Tags: */
{
icmXYZArray *wor, *wog, *wob;
double fromAbs[3][3];
double d50m[3][3];

/* Convert to D50 adapated */
icmMulBy3x3(d50m[0], fromAbs, mat[0]);
icmMulBy3x3(d50m[1], fromAbs, mat[1]);
icmMulBy3x3(d50m[2], fromAbs, mat[2]);

printf("\nPrint sRGB D65 to ICC D50 Chromatic Adaptation Matrix\n");
printf("{ %1.9f, %1.9f, %1.9f }, /* Red */\n"
"{ %1.9f, %1.9f, %1.9f }, /* Green */\n"
"{ %1.9f, %1.9f, %1.9f }, /* Blue */\n",
fromAbs[0][0], fromAbs[0][1], fromAbs[0][2],
fromAbs[1][0], fromAbs[1][1], fromAbs[1][2],
fromAbs[2][0], fromAbs[2][1], fromAbs[2][2]);

printf("{ %1.9f, %1.9f, %1.9f }, /* Red */\n"
"{ %1.9f, %1.9f, %1.9f }, /* Green */\n"
"{ %1.9f, %1.9f, %1.9f }, /* Blue */\n",
d50m[0][0], d50m[0][1], d50m[0][2],
d50m[1][0], d50m[1][1], d50m[1][2],
d50m[2][0], d50m[2][1], d50m[2][2]);
```

A quick comparison between the values in Table 1 from the sRGB specifications and the ArgyllCMS mkDispProf values given for icmXYZNumber wrgb[4] "Primaries in Yxy from the standard" in the above code box shows the ArgyllCMS source code uses the exact red, green, blue, and white xy values given in the sRGB specifications. And the Y values have all been set equal to 1.0, just as we did in Section B5 above.

Here are the values printed to screen after compiling mkDispProf modified to include the additional printf statements and then running the executable mkDispProf to make the "srgb.icc" profile. Clicking on the links will take you back to where we calculated the same values using the spreadsheet:

```\$mkDispProf srgb.icc
Print interim unadapted XYZ primaries calculated by setting Y=1.0
{icmAry2XYZ: i=0, 0.950455927, 1.000000000, 1.089057751 }
{icmAry2XYZ: i=1, 1.939393939, 1.000000000, 0.090909091 }
{icmAry2XYZ: i=2, 0.500000000, 1.000000000, 0.166666667 }
{icmAry2XYZ: i=3, 2.500000000, 1.000000000, 13.166666667 }

Print D65 sRGB white point: XYZ
{ 0.950455927, 1.000000000, 1.089057751 }       /* White */

{ 0.412390799, 0.212639006, 0.019330819 },      /* Red */
{ 0.357584339, 0.715168679, 0.119194780 },      /* Green */
{ 0.180480788, 0.072192315, 0.950532152 },      /* Blue */

Print sRGB D65 to ICC D50 Chromatic Adaptation Matrix
{ 1.047886003, 0.022918765, -0.050216095 },     /* Red */
{ 0.029581782, 0.990483518, -0.017078708 },     /* Green */
{ -0.009251881, 0.015072607, 0.751678134 },     /* Blue */

{ 0.436041252, 0.222484540, 0.013920187 },      /* Red */
{ 0.385112911, 0.716905079, 0.097067239 },      /* Green */
{ 0.143045838, 0.060610381, 0.713912574 },      /* Blue */

```

Comparing the values printed to screen by the mkDispProf executable while making "srgb.icc" and the corresponding values that were calculated by our spreadsheet, all the values exactly match.

### Comparing the calculated sRGB D50-adapted XYZ values to values in the "srgb.icc" profile colorant tags

To recap what's been covered so far: We used a spreadsheet to calculate the D50-adapted sRGB red, green, and blue XYZ values. I modified the mkDispProf source code to print to screen calculations made by the mkDispProf executable while making the "srgb.icc" profile. The values printed to screen by the mkDispProf executable exactly match the values we calculated using the spreadsheet.

Now let's compare the calculated adapted red, green, and blue XYZ values to the red, green, and blue XYZ values found in the actual "srgb.icc" profile produced by mkDispProf. As an important aside, the "srgb.icc" profile red, green, and blue XYZ values are the exact same XYZ values found in the ArgyllCMS sRGB.icm profile (from any recent version of ArgyllCMS including the most recent version 1.6.3, but not including the sRGB.icm profile from 1.6.1 and 1.6.2, which has very slightly different primaries).

If you want to examine the primaries in an actual ICC profile, several free and open source utilities are available, including icc_examin, which provides a gui display; the ArgyllCMS iccdump, which is a terminal utility that gives values to 6 decimal places; and iccToXml (part of iccXML), which produces an XML file with (among other information) 8-decimal-place profile red, green, and blue XYZ values.

I used iccToXml to dump the contents of "srgb.icc" to an XML file. Table 8 below gives the red, green, and blue colorant XYZ values in the XML file:

Table 8: Calculated D50-adapted sRGB primaries vs "srgb.icc" XYZ colorant values
Calculated
XYZ values
"srgb.icc"
profile
XYZ values
Absolute
difference
Red X 0.436041252 0.43603516 0.00000609
Red Y 0.222484540 0.22248840 0.00000385
Red Z 0.013920187 0.01391602 0.00000415
Green X 0.385112911 0.38511658 0.00000371
Green Y 0.716905079 0.71690369 0.00000143
Green Z 0.097067239 0.09706116 0.00000604
Blue X 0.143045838 0.14305115 0.00000528
Blue Y 0.060610381 0.06060791 0.00000245
Blue Z 0.713912574 0.71392822 0.00001564

Examining Table 8, it turns out that the calculated and actual "srgb.icc" adapted primaries only match through the fifth decimal place. The reason for the discrepancy is a funny thing called hexadecimal quantization:

### Hexadecimal quantization and ICC profiles

ICC profiles don't store XYZ values as decimal values. Rather an ICC profile's XYZ values are stored as hexadecimal values. So a certain amount of quantization (rounding) happens during the conversion from decimal values (which are given in the sRGB specifications) to hexadecimal values (which are stored in actual ICC profiles) and back to decimal (which is what utilities like iccdump, icc_examin, and iccToXml provide).

The ICC V4 specifications refer to a profile's red, green, and blue adapted XYZ primaries as colorants. A profile's red, green, and blue colorants are stored as XYZNumbers (all one word, see section 4.14 of the ICC V4 specs). As an aside, the profile illuminant, white point tag, and chad ("chromatic adaptation") tag values are also stored as XYZNumbers. According to the ICC specifciations, XYZNumbers are encoded as "s15Fixed16Number" (see Table 8, section 4.14 of the ICC V4 specs):

4.6 s15Fixed16Number
An s15Fixed16Number is a fixed signed 4-byte (32-bit) quantity which has 16 fractional bits as shown in Table 4.

The referred to Table 4 of the ICC V4 specs shows the range of decimal integer numbers that an s15Fixed16Number can accomodate. Athough I understand hexadecimal numbers, my understanding of the ICC's s15Fixed16Number encoding breaks down right here, so if you can provide a nice summary, please share!

I used the following OpenOffice/LibreOffice spreadsheet formula to convert the calculated sRGB D50-adapted red, green, and blue primaries to hexadecimal and back to decimal:

( HEX2DEC ( DEC2HEX ( number-to-be-converted * 65536; 9 ) ) ) / 65536

where 65536 is 2 raised to the 16th power. I guesstimated that 16 is the right power of 2 by trying different powers of 2 until I got to the power of 2 that most closely replicated the colorant values in the ArgyllCMS sRGB.icm profile.

Table 9: Calculated vs Hexadecimal rounded vs "srgb.icc" colorant XYZ values
Calculated
XYZ values
Rounded
"srgb.icc"
profile
XYZ values
Absolute
difference
between
Rounded and
Actual
Red X 0.436041252 0.436035156 0.43603516 0.00000000
Red Y 0.222484540 0.222473145 0.22248840 0.00001526
Red Z 0.013920187 0.013916016 0.01391602 0.00000000
Green X 0.385112911 0.385101318 0.38511658 0.00001526
Green Y 0.716905079 0.716903687 0.71690369 0.00000000
Green Z 0.097067239 0.097061157 0.09706116 0.00000000
Blue X 0.143045838 0.143035889 0.14305115 0.00001526
Blue Y 0.060610381 0.060607910 0.06060791 0.00000000
Blue Z 0.713912574 0.713897705 0.71392822 0.00003051

Examining Table 9, after the spreadsheet-calculated values have been hexadecimal-rounded, five of the actual XYZ values in the mkDispProf "srgb.icc" profile match the calculated-then-hexadecimal-rounded adapted XYZ values to 8 decimal places. The remaining actual XYZ values match the hexadecimal-rounded values through the fourth decimal place, and are actually closer to the (non-rounded) calculated values than to the hexadecimal-rounded values.

I will guess that the remaining discrepencies between the calculated and actual "srgb.icc" red, green, and blue D50-adapted XYZ values are from a combination of two factors:

1. I doubt that my rough and ready hexadecimal rounding equation fully accounts for the peculiarities of the ICC-specified s15Fixed16Number hexadecimal format account.
2. The ArgyllCMS profile-making code specifically takes hexadecimal rounding into account (unlike certain other profile making softwares), which as an extremely important aside is the reason why the ArgyllCMS code makes well-behaved, neutral profiles (again, unlike certain other profile making softwares). I didn't take into account the portions of the ArgyllCMS code that perform this important step.

In any event, it's satisfying (well, I think it is) to see our spreadsheet calculations so closely reproduce the D50-adapted "srgb.icc" colorants produced by the ArgyllCMS mkDispProf.c code.

## Conclusion

If you actually made it all the way to the bottom of this page, you deserve a great big round of applause, a medal for fortitude, and maybe a cold beer to sooth your aching head! Hopefully you also have a better understanding of the actual mathematics behind chromatic adaptation as it applies to making the ICC sRGB profile from the sRGB color space specification.

Given the specifications for any standard RGB color space, the exact same mathematical calculations are used to make the corresponding D50-adapted ICC profile. So if you are truly a glutton for punishment, make a copy of the downloadable sRGB specifications to ICC profile spreadsheet and replace the values for the sRGB color space with the corresponding values from the Reference Output Medium Metric RGB Color Space (ROMM RGB) White Paper (ProPhoto) or Adobe® RGB (1998) Color Image Encoding. For extra credit, use iccToXml to check the corresponding values in the profile distributed by your favorite vendor of free and open source profiles and see how closely the colorant tag values agree with your calculations. If there is a large discrepency, then the profile maker used the wrong source white point values, or the wrong unadapted primaries, or both 5.

Notes: