aboutsummaryrefslogblamecommitdiffstats
path: root/common.h
blob: 12c86a2a16918a06e41fb8aea2073e115a828124 (plain) (tree)































































































































































































































































































































                                                                                                                                         
                                                     
                                    

                                           















                                                                          
                                                         
                                   

                                           


































































































                                                                                                   
                                                                                     

                                                      


                                              








































                                                                                             
/* See LICENSE file for copyright and license details. */

#if defined(__GNUC__) && !defined(__clang__)
# pragma GCC diagnostic ignored "-Wfloat-equal"
#elif defined(__clang__)
# pragma clang diagnostic ignored "-Wcomma"
# pragma clang diagnostic ignored "-Wfloat-equal"
# pragma clang diagnostic ignored "-Wvla"
# pragma clang diagnostic ignored "-Wtautological-compare"
# pragma clang diagnostic ignored "-Wcovered-switch-default"
# pragma clang diagnostic ignored "-Wfloat-conversion"
# pragma clang diagnostic ignored "-Wconversion"
# pragma clang diagnostic ignored "-Wdouble-promotion"
# pragma clang diagnostic ignored "-Wswitch-enum"
# pragma clang diagnostic ignored "-Wcast-align"
#endif



#define PI          D(3.14159265358979323846)
#define PI2         (2 * D(3.14159265358979323846))
#define WASDIV0(X)  (xisinf(X) || xisnan(X))

#define REGULAR(S, T)    ((T) <= (S)->TRANSITION ? (S)->SLOPE * (T) : (1 + (S)->OFFSET) * xpow((T), 1 / (S)->GAMMA) - (S)->OFFSET)
#define INVREGULAR(S, T) ((T) <= (S)->TRANSITIONINV ? (T) / (S)->SLOPE : xpow(((T) + (S)->OFFSET) / (1 + (S)->OFFSET), (S)->GAMMA))

#define XTRANSFORM(X, Y, Z, A, B, C, R1C1, R1C2, R1C3, R2C1, R2C2, R2C3, R3C1, R3C2, R3C3)\
	do {\
		TYPE a__ = (A), b__ = (B), c__ = (C);\
		(X) = (R1C1) * a__ + (R1C2) * b__ + (R1C3) * c__;\
		(Y) = (R2C1) * a__ + (R2C2) * b__ + (R2C3) * c__;\
		(Z) = (R3C1) * a__ + (R3C2) * b__ + (R3C3) * c__;\
	} while (0)

#define TRANSFORM(X, Y, Z, A, B, C, R1C1, R1C2, R1C3, R2C1, R2C2, R2C3, R3C1, R3C2, R3C3)\
	XTRANSFORM((X), (Y), (Z), (A), (B), (C),\
	           D(R1C1), D(R1C2), D(R1C3),\
	           D(R2C1), D(R2C2), D(R2C3),\
	           D(R3C1), D(R3C2), D(R3C3))



#define SLOPE transfer.regular.slope
#define TRANSITIONINV transfer.regular.transitioninv
#define TRANSITION transfer.regular.transition
#define GAMMA transfer.regular.gamma
#define OFFSET transfer.regular.offset
#define TO_ENCODED_RED transfer.custom.to_encoded_red
#define TO_DECODED_RED transfer.custom.to_decoded_red
#define TO_ENCODED_GREEN transfer.custom.to_encoded_green
#define TO_DECODED_GREEN transfer.custom.to_decoded_green
#define TO_ENCODED_BLUE transfer.custom.to_encoded_blue
#define TO_DECODED_BLUE transfer.custom.to_decoded_blue



#define CIEXYZ_TO_RGB(FROM_X, FROM_Y, FROM_Z, TO_R, TO_G, TO_B, TO_MINV)\
	XTRANSFORM((TO_R), (TO_G), (TO_B), (FROM_X), (FROM_Y), (FROM_Z),\
	           (TO_MINV)[0][0], (TO_MINV)[0][1], (TO_MINV)[0][2],\
	           (TO_MINV)[1][0], (TO_MINV)[1][1], (TO_MINV)[1][2],\
	           (TO_MINV)[2][0], (TO_MINV)[2][1], (TO_MINV)[2][2])

#define CIEXYZ_TO_SRGB(FROM_X, FROM_Y, FROM_Z, TO_R, TO_G, TO_B)\
	TRANSFORM((TO_R), (TO_G), (TO_B), (FROM_X), (FROM_Y), (FROM_Z),\
	           3.240446254647737500675930277794, -1.537134761820080575134284117667, -0.498530193022728718155178739835,\
	          -0.969266606244679751469561779231,  1.876011959788370209167851498933,  0.041556042214430065351304932619,\
	           0.055643503564352832235773149705, -0.204026179735960239147729566866,  1.057226567722703292062647051353)

#define YIQ_TO_SRGB(FROM_Y, FROM_I, FROM_Q, TO_R, TO_G, TO_B)\
	TRANSFORM((TO_R), (TO_G), (TO_B), (FROM_Y), (FROM_I), (FROM_Q),\
	          1.,  0.95629483232089407263032398986979387700557708740234,  0.62102512544472865396727456754888407886028289794922,\
	          1., -0.27212147408397735492968649850809015333652496337891, -0.64738095351761570928061928498209454119205474853516,\
	          1., -1.10698990856712820018969978264067322015762329101562,  1.70461497549882934343656870623817667365074157714844)
	/* ⎛1    1000 cos 33° / 877                                  1000 sin 33° / 877                               ⎞
	   ⎜1    9500 sin 33° / 24067 - 299000 * cos 33° / 514799    -9500 cos 33° / 24067 - 299000 * sin 33° / 514799⎟
	   ⎝1    -250 sin 33° / 123                                  250 cos 33° / 123                                ⎠
	*/

#define YDBDR_TO_SRGB(FROM_Y, FROM_DB, FROM_DR, TO_R, TO_G, TO_B)\
	TRANSFORM((TO_R), (TO_G), (TO_B), (FROM_Y), (FROM_DB), (FROM_DR),\
	          1.,  0.000092303716148, -0.525912630661865,\
	          1., -0.129132898890509,  0.267899328207599,\
	          1.,  0.664679059978955, -0.000079202543533)

#define YPBPR_TO_SRGB(FROM_Y, FROM_PB, FROM_PR, TO_R, TO_G, TO_B)\
	do {\
		TYPE y__ = (FROM_Y);\
		TYPE r__ = (FROM_PR) + y__;\
		TYPE b__ = (FROM_PB) + y__;\
		(TO_R) = r__;\
		(TO_B) = b__;\
		(TO_G) = (y__ - r__ * D(0.2126) - b__ * D(0.0722)) / D(0.7152);\
	} while (0)

#define YUV_TO_SRGB(FROM_Y, FROM_U, FROM_V, TO_R, TO_G, TO_B)\
	TRANSFORM((TO_R), (TO_G), (TO_B), (FROM_Y), (FROM_U), (FROM_V),\
	          1.,  0.00028328010485821202317155420580263580632163211703,  1.14070449590558520291949662350816652178764343261719,\
	          1., -0.39630886669497211727275498560629785060882568359375, -0.58107364288228224857846271333983168005943298339844,\
	          1.,  2.03990003507541306504435851820744574069976806640625,  0.00017179031692307700847528739718228507626918144524)

#define YCGCO_TO_SRGB(FROM_Y, FROM_CG, FROM_CO, TO_R, TO_G, TO_B)\
	TRANSFORM((TO_R), (TO_G), (TO_B), (FROM_Y), (FROM_CG), (FROM_CO),\
	           1., -1.,  1.,\
	           1.,  1.,  0.,\
	           1., -1., -1.)

#define CIEXYZ_TO_CIEXYY(FROM_X, FROM_Y, FROM_Z, TO_X, TO_Y, TO_YY)\
	do {\
		TYPE x__ = (FROM_X), y__ = (FROM_Y), z__ = (FROM_Z);\
		TYPE s__ = x__ + y__ + z__;\
		x__ = x__ / s__;\
		s__ = y__ / s__;\
		if (WASDIV0(x__) || WASDIV0(s__))\
			x__ = s__ = 0;\
		(TO_X) = x__;\
		(TO_Y) = s__;\
		(TO_YY) = y__;\
	} while (0)

#define RGB_TO_CIEXYZ(FROM_R, FROM_G, FROM_B, TO_X, TO_Y, TO_Z, FROM_M)\
	XTRANSFORM((TO_X), (TO_Y), (TO_Z), (FROM_R), (FROM_G), (FROM_B),\
	           (FROM_M)[0][0], (FROM_M)[0][1], (FROM_M)[0][2],\
	           (FROM_M)[1][0], (FROM_M)[1][1], (FROM_M)[1][2],\
	           (FROM_M)[2][0], (FROM_M)[2][1], (FROM_M)[2][2])

#define SRGB_TO_CIEXYZ(FROM_R, FROM_G, FROM_B, TO_X, TO_Y, TO_Z)\
	TRANSFORM((TO_X), (TO_Y), (TO_Z), (FROM_R), (FROM_G), (FROM_B),\
	          0.412457445582367576708548995157, 0.357575865245515878143578447634, 0.180437247826399665973085006954,\
	          0.212673370378408277403536885686, 0.715151730491031756287156895269, 0.072174899130559869164791564344,\
	          0.019333942761673460208893260415, 0.119191955081838593666354597644, 0.950302838552371742508739771438)

#define CIEXYY_TO_CIEXYZ(FROM_X, FROM_Y, FROM_YY, TO_X, TO_Y, TO_Z)\
	do {\
		TYPE x__ = (FROM_X), y__ = (FROM_Y), Y__ = (FROM_YY);\
		TYPE Yy__ = Y__ / y__;\
		if (WASDIV0(Yy__)) {\
			(TO_X) = (TO_Y) = (TO_Z) = Y__;\
		} else {\
			(TO_X) = x__ * Yy__;\
			(TO_Y) = Y__;\
			(TO_Z) = (1 - x__ - y__) * Yy__;\
		}\
	} while (0)

#define CIELAB_TO_CIEXYZ(FROM_L, FROM_A, FROM_B, TO_X, TO_Y, TO_Z)\
	do {\
		TYPE Y__ = ((FROM_L) + 16) / 116;\
		TYPE X__ = Y__ + (FROM_A) / 500;\
		TYPE Z__ = Y__ - (FROM_B) / 200;\
		(TO_X) = cielab_finv(X__) * D(0.95047);\
		(TO_Y) = cielab_finv(Y__);\
		(TO_Z) = cielab_finv(Z__) * D(1.08883);\
	} while (0)

#define CIELUV_TO_CIEXYZ(FROM_L, FROM_U, FROM_V, TO_X, TO_Y, TO_Z, FROM_WHITE_X, FROM_WHITE_Y, FROM_WHITE_Z)\
	do {\
		TYPE L__ = (FROM_L), X__ = (FROM_WHITE_X), Y__ = (FROM_WHITE_Y), Z__ = (FROM_WHITE_Z);\
		TYPE L13__ = L__ * 13;\
		TYPE t__ = X__ + 15 * Y__ + 3 * Z__;\
		TYPE u__ = (FROM_U) / L13__ + 4 * X__ / t__;\
		TYPE v__ = (FROM_V) / L13__ + 9 * Y__ / t__;\
		if (L__ <= 8) {\
			Y__ *= L__ * 27 / 24389;\
		} else {\
			L__ = (L__ + 16) / 116;\
			Y__ *= L__ * L__ * L__;\
		}\
		(TO_X) = D(2.25) * Y__ * u__ / v__;\
		(TO_Y) = Y__;\
		(TO_Z) = Y__ * (3 / v__ - D(0.75) * u__ / v__ - 5);\
	} while (0)

#define YIQ_TO_CIEXYZ(FROM_Y, FROM_I, FROM_Q, TO_X, TO_Y, TO_Z)\
	TRANSFORM((TO_X), (TO_Y), (TO_Z), (FROM_Y), (FROM_I), (FROM_Q),\
	           0.95047055865428309306963683411595411598682403564453,\
	           0.09738463974178063198294807989441324025392532348633,\
	           0.33223466706854809515903070860076695680618286132812,\
	           0.99999999999999988897769753748434595763683319091797,\
	          -0.07112658301916767455974621725545148365199565887451,\
	          -0.20786968876398304040264974901219829916954040527344,\
	           1.08882873639588373393394249433185905218124389648438,\
	          -1.06592139332461721679123911599162966012954711914062,\
	           1.55474471255181900808395312196807935833930969238281)

#define YDBDR_TO_CIEXYZ(FROM_Y, FROM_DB, FROM_DR, TO_X, TO_Y, TO_Z)\
	TRANSFORM((TO_X), (TO_Y), (TO_Z), (FROM_Y), (FROM_DB), (FROM_DR),\
	           0.95047055865428309306963683411595411598682403564453,\
	           0.07379612357298576119646327242662664502859115600586,\
	          -0.12113653724874726136384595065464964136481285095215,\
	           0.99999999999999988897769753748434595763683319091797,\
	          -0.04435684145428285540813106990754022262990474700928,\
	           0.07973534004202506575431641522300196811556816101074,\
	           1.08882873639588373393394249433185905218124389648438,\
	           0.61625657933494271123464613992837257683277130126953,\
	           0.02168821359337728266192257819966471288353204727173)

#define YUV_TO_CIEXYZ(FROM_Y, FROM_U, FROM_V, TO_X, TO_Y, TO_Z)\
	TRANSFORM((TO_X), (TO_Y), (TO_Z), (FROM_Y), (FROM_U), (FROM_V),\
	           0.95047055865428309306963683411595411598682403564453,\
	           0.22648030324549334180161963558930438011884689331055,\
	           0.26274514929253273143316960158699657768011093139648,\
	           0.99999999999999988897769753748434595763683319091797,\
	          -0.13613114642319409930415474718756740912795066833496,\
	          -0.17294595255115238763288232348713790997862815856934,\
	           1.08882873639588373393394249433185905218124389648438,\
	           1.89129144197893928058817891724174842238426208496094,\
	          -0.04704173528403532422714761196402832865715026855469)

#define YPBPR_TO_CIEXYZ(FROM_Y, FROM_PB, FROM_PR, TO_X, TO_Y, TO_Z)\
	TRANSFORM((TO_X), (TO_Y), (TO_Z), (FROM_Y), (FROM_PB), (FROM_PR),\
	           0.95047055865428298204733437160030007362365722656250,\
	           0.14433968424876231217979238863335922360420227050781,\
	           0.30616461986760712399302519770571961998939514160156,\
	           0.99999999999999977795539507496869191527366638183594,\
	          -0.00002022802471486617736928792510298080742359161377,\
	           0.00008771894888734421691367515450110659003257751465,\
	           1.08882873639588373393394249433185905218124389648438,\
	           0.93827031735982591165168287261622026562690734863281,\
	          -0.01609699914324668607035206946420657914131879806519)

#define YCGCO_TO_CIEXYZ(FROM_Y, FROM_CG, FROM_CO, TO_X, TO_Y, TO_Z)\
	TRANSFORM((TO_X), (TO_Y), (TO_Z), (FROM_Y), (FROM_CG), (FROM_CO),\
	           0.95047055865428309306963683411595411598682403564453,\
	          -0.23531882816325136453805555447615915909409523010254,\
	           0.23202019775596791073546398820326430723071098327637,\
	           0.99999999999999988897769753748434595763683319091797,\
	           0.43030346098206362359661625305307097733020782470703,\
	           0.14049847124784842211653312915586866438388824462891,\
	           1.08882873639588373393394249433185905218124389648438,\
	          -0.85044482623220662986796014592982828617095947265625,\
	          -0.93096889579069830311652822274481877684593200683594)

#define CIE1960UCS_TO_CIEXYZ(FROM_U, FROM_V, FROM_Y, TO_X, TO_Y, TO_Z)\
	do {\
		TYPE u__ = (FROM_U), v__ = (FROM_V), Y__ = (FROM_Y);\
		(TO_X) = D(1.5) * Y__ * u__ / v__;\
		(TO_Y) = Y__;\
		(TO_Z) = (4 * Y__ - Y__ * u__ - 10 * Y__ * v__) / (2 * v__);\
	} while (0)

#define CIEXYZ_TO_CIELAB(FROM_X, FROM_Y, FROM_Z, TO_L, TO_A, TO_B)\
	do {\
		TYPE X__ = (FROM_X), Y__ = (FROM_Y), Z__ = (FROM_Z);\
		X__ /= D(0.95047);\
		Z__ /= D(1.08883);\
		Y__ = cielab_f(Y__);\
		(TO_L) = 116 * Y__ - 16;\
		(TO_A) = 500 * (cielab_f(X__) - Y__);\
		(TO_B) = 200 * (Y__ - cielab_f(Z__));\
	} while (0)

#define CIEXYZ_TO_CIELUV(FROM_X, FROM_Y, FROM_Z, TO_L, TO_U, TO_V, TO_WHITE_X, TO_WHITE_Y, TO_WHITE_Z)\
	do {\
		TYPE WX__ = (TO_WHITE_X), WY__ = (TO_WHITE_Y), WZ__ = (TO_WHITE_Z);\
		TYPE X__ = (FROM_X), Y__ = (FROM_Y), Z__ = (FROM_Z);\
		TYPE L2__, L__, u__, v__, t__;\
		t__ = WX__ + 15 * WY__ + 3 * WZ__;\
		u__ = 4 * WX__ / t__;\
		v__ = 9 * WY__ / t__;\
		t__ = X__ + 15 * Y__ + 3 * Z__;\
		u__ = 4 * X__ / t__ - u__;\
		v__ = 9 * Y__ / t__ - v__;\
		L__ = Y__ / WY__;\
		L2__ = L__ * 24389;\
		L__ = L2__ <= 216 ? L2__ / 27 : xcbrt(L__) * 116 - 16;\
		(TO_L) = L__;\
		L__ *= 13;\
		(TO_U) = u__ * L__;\
		(TO_V) = v__ * L__;\
	} while (0)

#define CIELCHUV_TO_CIELUV(FROM_L, FROM_C, FROM_H, TO_L, TO_U, TO_V)\
	do {\
		TYPE C__ = (FROM_C), h__ = (FROM_H);\
		(TO_L) = (FROM_L);\
		(TO_U) = C__ * xcos(h__);\
		(TO_V) = C__ * xsin(h__);\
	} while (0)

#define CIELUV_TO_CIELCHUV(FROM_L, FROM_U, FROM_V, TO_L, TO_C, TO_H, TO_ONE_REVOLUTION)\
	do {\
		TYPE u__ = (FROM_U), v__ = (FROM_V), rev__ = (TO_ONE_REVOLUTION), h__;\
		(TO_L) = (FROM_L);\
		(TO_C) = xsqrt(u__ * u__ + v__ * v__);\
		h__ = xatan2(v__, u__) / PI2 * rev__;\
		if (!WASDIV0(h__) && (h__ < 0))\
			h__ += rev__;\
		(TO_H) = h__;\
	} while (0)

#define SRGB_TO_YIQ(FROM_R, FROM_G, FROM_B, TO_Y, TO_I, TO_Q)\
	TRANSFORM((TO_Y), (TO_I), (TO_Q), (FROM_R), (FROM_G), (FROM_B),\
	           0.299, 0.587, 0.114,\
	           0.59571613491277464191853141528554260730743408203125, /* (0.877 cos 33°)(1 - 0.299) - (0.492 sin 33°)(-0.299) */\
	          -0.27445283783925644716106262421817518770694732666016, /* (0.877 cos 33°)(-0.587)    - (0.492 sin 33°)(-0.587) */\
	          -0.32126329707351808373516632855171337723731994628906, /* (0.877 cos 33°)(-0.114)    - (0.492 sin 33°)(1 - 0.114) */\
	           0.21145640212011795888713550084503367543220520019531, /* (0.877 sin 33°)(1 - 0.299) + (0.492 cos 33°)(-0.299) */\
	          -0.52259104529161115593183239980135113000869750976562, /* (0.877 sin 33°)(-0.587)    + (0.492 cos 33°)(-0.587) */\
	           0.31113464317149330806699936147197149693965911865234) /* (0.877 sin 33°)(-0.114)    + (0.492 cos 33°)(1 - 0.114) */

#define CIEXYZ_TO_YIQ(FROM_X, FROM_Y, FROM_Z, TO_Y, TO_I, TO_Q)\
	TRANSFORM((TO_Y), (TO_I), (TO_Q), (FROM_X), (FROM_Y), (FROM_Z),\
	           0.40627729168038273499519164033699780702590942382812,\
	           0.61835674212166968910509012857801280915737152099609,\
	          -0.00414330221353725880462093300593551248311996459961,\
	           2.17852787350219845308174626552499830722808837890625,\
	          -1.36502666214454104753883711964590474963188171386719,\
	          -0.64803574634025240541745915834326297044754028320312,\
	           1.20905577682138853923277110880007967352867126464844,\
	          -1.36890364998339797431015085749095305800437927246094,\
	           0.20180559439597040016778350945969577878713607788086)

#define SRGB_TO_YDBDR(FROM_R, FROM_G, FROM_B, TO_Y, TO_DB, TO_DR)\
	TRANSFORM((TO_Y), (TO_DB), (TO_DR), (FROM_R), (FROM_G), (FROM_B),\
	           0.299,  0.587, 0.114,\
	          -0.450, -0.883, 1.333,\
	          -1.333,  1.116, 0.217)

#define YUV_TO_YDBDR(FROM_Y, FROM_U, FROM_V, TO_Y, TO_DB, TO_DR)\
	do {\
		TYPE U__ = (FROM_U), V__ = (FROM_V);\
		(TO_Y)  = (FROM_Y);\
		(TO_DB) = U__ *  D(3.069);\
		(TO_DR) = V__ * D(-2.169);\
	} while (0)

#define CIEXYZ_TO_YDBDR(FROM_X, FROM_Y, FROM_Z, TO_Y, TO_DB, TO_DR)\
	TRANSFORM((TO_Y), (TO_DB), (TO_DR), (FROM_X), (FROM_Y), (FROM_Z),\
	           0.40627729168038273499519164033699780702590942382812,\
	           0.61835674212166968910509012857801280915737152099609,\
	          -0.00414330221353725880462093300593551248311996459961,\
	          -0.52816561102614745237815441214479506015777587890625,\
	          -1.23677481526212962315014465275453403592109680175781,\
	           1.59692761635924940222253098909277468919754028320312,\
	          -5.38914174974103143966885909321717917919158935546875,\
	           4.09835630362728497999569299281574785709381103515625,\
	           0.94033545560642795013706063400604762136936187744141)

#define YDBDR_TO_YUV(FROM_Y, FROM_DB, FROM_DR, TO_Y, TO_U, TO_V)\
	do {\
		TYPE DB__ = (FROM_DB), DR__ = (FROM_DR);\
		(TO_Y) = (FROM_Y);\
		(TO_U) = DB__ /  D(3.069);\
		(TO_V) = DR__ / D(-2.169);\
	} while (0)

#define SRGB_TO_YUV(FROM_R, FROM_G, FROM_B, TO_Y, TO_U, TO_V)\
	TRANSFORM((TO_Y), (TO_U), (TO_V), (FROM_R), (FROM_G), (FROM_B),\
	           0.29899999999999998800959133404830936342477798461914,\
	           0.58699999999999996624922005139524117112159729003906,\
	           0.11400000000000000410782519111307919956743717193604,\
	          -0.14662756598240470062854967636667424812912940979004,\
	          -0.28771586836102963635752871596196200698614120483398,\
	           0.43434343434343436474165400795754976570606231689453,\
	           0.61456892577224520035628074765554629266262054443359,\
	          -0.51452282157676354490405401520547457039356231689453,\
	          -0.10004610419548178035231700278018251992762088775635)

#define CIEXYZ_TO_YUV(FROM_X, FROM_Y, FROM_Z, TO_Y, TO_U, TO_V)\
	TRANSFORM((TO_Y), (TO_U), (TO_V), (FROM_X), (FROM_Y), (FROM_Z),\
	           0.40627729168038273499519164033699780702590942382812,\
	           0.61835674212166968910509012857801280915737152099609,\
	          -0.00414330221353725880462093300593551248311996459961,\
	          -0.17209697328971895746718701047939248383045196533203,\
	          -0.40298951295605395239718404809536878019571304321289,\
	           0.52034135430408912093014350830344483256340026855469,\
	           2.48462044709130047692724474472925066947937011718750,\
	          -1.88951420176453876997868519538315013051033020019531,\
	          -0.43353409663735725798616726933687459677457809448242)

#define SRGB_TO_YPBPR(FROM_R, FROM_G, FROM_B, TO_Y, TO_PB, TO_PR)\
	do {\
		TYPE R__ = (FROM_R), G__ = (FROM_G), B__ = (FROM_B);\
		(TO_Y)  = R__ * D(0.2126) +\
		          G__ * D(0.7152) +\
		          B__ * D(0.0722);\
		(TO_PB) = B__ - to->Y;\
		(TO_PR) = R__ - to->Y;\
	 } while (0)

#define CIEXYZ_TO_YPBPR(FROM_X, FROM_Y, FROM_Z, TO_Y, TO_PB, TO_PR)\
	TRANSFORM((TO_Y), (TO_PB), (TO_PR), (FROM_X), (FROM_Y), (FROM_Z),\
	          -0.00028314209073960778378920011277841695118695497513,\
	           1.00019821310075673892470149439759552478790283203125,\
	           0.00006512054470741990286342115723527967929840087891,\
	           0.05592664565509243568275365987574332393705844879150,\
	          -1.20422439283671711685030913940863683819770812988281,\
	           1.05716144717799576113748116767965257167816162109375,\
	           3.24072939673847715269516811531502753496170043945312,\
	          -2.53733297492083753610359053709544241428375244140625,\
	          -0.49859531356743613805804216099204495549201965332031)

#define SRGB_TO_YCGCO(FROM_R, FROM_G, FROM_B, TO_Y, TO_CG, TO_CO)\
	TRANSFORM((TO_Y), (TO_CG), (TO_CO), (FROM_R), (FROM_G), (FROM_B),\
		   0.25, 0.50,  0.25,\
		  -0.25, 0.50, -0.25,\
		   0.50, 0.,   -0.50)

#define CIEXYZ_TO_YCGCO(FROM_X, FROM_Y, FROM_Z, TO_Y, TO_CG, TO_CO)\
	TRANSFORM((TO_Y), (TO_CG), (TO_CO), (FROM_X), (FROM_Y), (FROM_Z),\
	           0.33938913643068269188063368346774950623512268066406,\
	           0.50271574450517486631895280879689380526542663574219,\
	           0.16045211478220866574417868832824751734733581542969,\
	          -1.30865574267536244335019546269904822111129760742188,\
	           1.37329621528319534284889869013568386435508728027344,\
	          -0.11889607256777862120955546743061859160661697387695,\
	           1.59240137554169236544510113162687048316001892089844,\
	          -0.66655429104206020962664069884340278804302215576172,\
	          -0.77787838037271606062006412685150280594825744628906)

#define CIEUVW_TO_CIE1960UCS(FROM_U, FROM_V, FROM_W, TO_U, TO_V, TO_Y, FROM_U0, FROM_V0)\
	do {\
		TYPE U__ = (FROM_U), V__ = (FROM_V), W__ = (FROM_W), Y__;\
		Y__ = (W__ + 17) / 25;\
		Y__ *= Y__ * Y__;\
		W__ *= 13;\
		(TO_U) = U__ / W__ + (FROM_U0);\
		(TO_V) = V__ / W__ + (FROM_V0);\
		(TO_Y) = Y__;\
	} while (0)

#define CIEXYZ_TO_CIE1960UCS(FROM_X, FROM_Y, FROM_Z, TO_U, TO_V, TO_Y)\
	do {\
		TYPE X__ = (FROM_X), Y__ = (FROM_Y), w__;\
		w__ = X__ + 15 * Y__ + 3 * (FROM_Z);\
		(TO_U) = 4 * X__ / w__;\
		(TO_V) = 6 * Y__ / w__;\
		(TO_Y) = Y__;\
	} while (0)

#define CIEUVW_TO_CIEUVW(FROM_U, FROM_V, FROM_W, TO_U, TO_V, TO_W, FROM_U0, FROM_V0, TO_U0, TO_V0)\
	do {\
		TYPE U__ = (FROM_U), V__ = (FROM_V), W__ = (FROM_W);\
		TYPE w__ = W__ * 13;\
		U__ += w__ * ((FROM_U0) - (TO_U0));\
		V__ += w__ * ((FROM_V0) - (TO_V0));\
		(TO_U) = U__;\
		(TO_V) = V__;\
		(TO_W) = W__;\
	} while (0)

#define CIE1960UCS_TO_CIEUVW(FROM_U, FROM_V, FROM_Y, TO_U, TO_V, TO_W, TO_U0, TO_V0)\
	do {\
		TYPE U__ = (FROM_U), V__ = (FROM_V), u0__ = (TO_U0), v0__ = (TO_V0);\
		TYPE Y__ = 25 * xcbrt((FROM_Y)) - 17;\
		TYPE w__ = Y__ * 13;\
		(to->U) = w__ * (U__ - u0__);\
		(to->V) = w__ * (V__ - v0__);\
		(to->W) = Y__;\
	} while (0)



static inline TYPE
srgb_encode(TYPE t)
{
	TYPE sign = 1;
	if (t < 0) {
		t = -t;
		sign = -1;
	}
	t = t <= D(0.0031306684425217108) ? D(12.92) * t
		: D(1.055) * xpow(t, 1 / D(2.4)) - D(0.055);
	return t * sign;
}

static inline TYPE
srgb_decode(TYPE t)
{
	TYPE sign = 1;
	if (t < 0) {
		t = -t;
		sign = -1;
	}
	t = t <= D(0.0031306684425217108) * D(12.92) ? t / D(12.92)
		: xpow((t + D(0.055)) / D(1.055), D(2.4));
	return t * sign;
}

static inline TYPE
cielab_finv(TYPE t)
{
	return (t > D(6.) / D(29.)) ? t * t * t : (t - D(4.) / D(29.)) * 108 / 841;
}

static inline TYPE
cielab_f(TYPE t)
{
	return (t > D(216.) / D(24389.)) ? xcbrt(t) : t * D(841.) / D(108.) + D(4.) / D(29.);
}