@@ -255,6 +255,39 @@ const JZAZBZ_P: f32 = 1.7 * PQEOTF_M2;
255
255
256
256
// ### MATRICES ### {{{
257
257
258
+ /// Matrix determinant using only constant math
259
+ const fn det ( m : [ [ f32 ; 3 ] ; 3 ] ) -> f32 {
260
+ m[ 0 ] [ 0 ] * ( m[ 1 ] [ 1 ] * m[ 2 ] [ 2 ] - m[ 1 ] [ 2 ] * m[ 2 ] [ 1 ] ) - m[ 0 ] [ 1 ] * ( m[ 1 ] [ 0 ] * m[ 2 ] [ 2 ] - m[ 1 ] [ 2 ] * m[ 2 ] [ 0 ] )
261
+ + m[ 0 ] [ 2 ] * ( m[ 1 ] [ 0 ] * m[ 2 ] [ 1 ] - m[ 1 ] [ 1 ] * m[ 2 ] [ 0 ] )
262
+ }
263
+
264
+ /// Matrix inversion using only constant math
265
+ /// Panics if determinant is zero
266
+ const fn inv ( m : [ [ f32 ; 3 ] ; 3 ] ) -> [ [ f32 ; 3 ] ; 3 ] {
267
+ let d = det ( m) ;
268
+ if d == 0.0 {
269
+ panic ! ( "Matrix is singular and has no inverse" )
270
+ }
271
+
272
+ [
273
+ [
274
+ ( m[ 1 ] [ 1 ] * m[ 2 ] [ 2 ] - m[ 1 ] [ 2 ] * m[ 2 ] [ 1 ] ) / d,
275
+ ( m[ 0 ] [ 2 ] * m[ 2 ] [ 1 ] - m[ 0 ] [ 1 ] * m[ 2 ] [ 2 ] ) / d,
276
+ ( m[ 0 ] [ 1 ] * m[ 1 ] [ 2 ] - m[ 0 ] [ 2 ] * m[ 1 ] [ 1 ] ) / d,
277
+ ] ,
278
+ [
279
+ ( m[ 1 ] [ 2 ] * m[ 2 ] [ 0 ] - m[ 1 ] [ 0 ] * m[ 2 ] [ 2 ] ) / d,
280
+ ( m[ 0 ] [ 0 ] * m[ 2 ] [ 2 ] - m[ 0 ] [ 2 ] * m[ 2 ] [ 0 ] ) / d,
281
+ ( m[ 0 ] [ 2 ] * m[ 1 ] [ 0 ] - m[ 0 ] [ 0 ] * m[ 1 ] [ 2 ] ) / d,
282
+ ] ,
283
+ [
284
+ ( m[ 1 ] [ 0 ] * m[ 2 ] [ 1 ] - m[ 1 ] [ 1 ] * m[ 2 ] [ 0 ] ) / d,
285
+ ( m[ 0 ] [ 1 ] * m[ 2 ] [ 0 ] - m[ 0 ] [ 0 ] * m[ 2 ] [ 1 ] ) / d,
286
+ ( m[ 0 ] [ 0 ] * m[ 1 ] [ 1 ] - m[ 0 ] [ 1 ] * m[ 1 ] [ 0 ] ) / d,
287
+ ] ,
288
+ ]
289
+ }
290
+
258
291
/// Its easier to write matricies visually then transpose them so they can be indexed per vector
259
292
/// [X1, X2] -> [X1, Y1]
260
293
/// [Y1, Y2] [X2, Y2]
@@ -282,20 +315,6 @@ const XYZ65_MAT: [[f32; 3]; 3] = t([
282
315
[ 0.0193 , 0.1192 , 0.9505 ] ,
283
316
] ) ;
284
317
285
- // Original commonly used inverted array
286
- // const XYZ65_MAT_INV: [[f32; 3]; 3] = [
287
- // [3.2406, -1.5372, -0.4986],
288
- // [-0.9689, 1.8758, 0.0415],
289
- // [0.0557, -0.2040, 1.0570],
290
- // ];
291
-
292
- // Higher precision invert using numpy. Helps with back conversions
293
- const XYZ65_MAT_INV : [ [ f32 ; 3 ] ; 3 ] = t ( [
294
- [ 3.2406254773 , -1.5372079722 , -0.4986285987 ] ,
295
- [ -0.9689307147 , 1.8757560609 , 0.0415175238 ] ,
296
- [ 0.0557101204 , -0.2040210506 , 1.0569959423 ] ,
297
- ] ) ;
298
-
299
318
// OKLAB
300
319
// They appear to be provided already transposed for code in the blog post
301
320
const OKLAB_M1 : [ [ f32 ; 3 ] ; 3 ] = [
@@ -308,16 +327,6 @@ const OKLAB_M2: [[f32; 3]; 3] = [
308
327
[ 0.7936177850 , -2.4285922050 , 0.7827717662 ] ,
309
328
[ -0.0040720468 , 0.4505937099 , -0.8086757660 ] ,
310
329
] ;
311
- const OKLAB_M1_INV : [ [ f32 ; 3 ] ; 3 ] = [
312
- [ 1.2270138511 , -0.0405801784 , -0.0763812845 ] ,
313
- [ -0.5577999807 , 1.1122568696 , -0.4214819784 ] ,
314
- [ 0.281256149 , -0.0716766787 , 1.5861632204 ] ,
315
- ] ;
316
- const OKLAB_M2_INV : [ [ f32 ; 3 ] ; 3 ] = [
317
- [ 0.9999999985 , 1.0000000089 , 1.0000000547 ] ,
318
- [ 0.3963377922 , -0.1055613423 , -0.0894841821 ] ,
319
- [ 0.2158037581 , -0.0638541748 , -1.2914855379 ] ,
320
- ] ;
321
330
322
331
// JzAzBz
323
332
const JZAZBZ_M1 : [ [ f32 ; 3 ] ; 3 ] = t ( [
@@ -331,17 +340,6 @@ const JZAZBZ_M2: [[f32; 3]; 3] = t([
331
340
[ 0.199076 , 1.096799 , -1.295875 ] ,
332
341
] ) ;
333
342
334
- const JZAZBZ_M1_INV : [ [ f32 ; 3 ] ; 3 ] = t ( [
335
- [ 1.9242264358 , -1.0047923126 , 0.037651404 ] ,
336
- [ 0.3503167621 , 0.7264811939 , -0.0653844229 ] ,
337
- [ -0.090982811 , -0.3127282905 , 1.5227665613 ] ,
338
- ] ) ;
339
- const JZAZBZ_M2_INV : [ [ f32 ; 3 ] ; 3 ] = t ( [
340
- [ 1. , 0.1386050433 , 0.0580473162 ] ,
341
- [ 1. , -0.1386050433 , -0.0580473162 ] ,
342
- [ 1. , -0.096019242 , -0.8118918961 ] ,
343
- ] ) ;
344
-
345
343
// ICtCp
346
344
const ICTCP_M1 : [ [ f32 ; 3 ] ; 3 ] = t ( [
347
345
[ 1688. / 4096. , 2146. / 4096. , 262. / 4096. ] ,
@@ -354,16 +352,6 @@ const ICTCP_M2: [[f32; 3]; 3] = t([
354
352
[ 17933. / 4096. , -17390. / 4096. , -543. / 4096. ] ,
355
353
] ) ;
356
354
357
- const ICTCP_M1_INV : [ [ f32 ; 3 ] ; 3 ] = t ( [
358
- [ 3.4366066943 , -2.5064521187 , 0.0698454243 ] ,
359
- [ -0.7913295556 , 1.9836004518 , -0.1922708962 ] ,
360
- [ -0.0259498997 , -0.0989137147 , 1.1248636144 ] ,
361
- ] ) ;
362
- const ICTCP_M2_INV : [ [ f32 ; 3 ] ; 3 ] = t ( [
363
- [ 1. , 0.008609037 , 0.111029625 ] ,
364
- [ 1. , -0.008609037 , -0.111029625 ] ,
365
- [ 1. , 0.5600313357 , -0.320627175 ] ,
366
- ] ) ;
367
355
// ### MATRICES ### }}}
368
356
369
357
// ### TRANSFER FUNCTIONS ### {{{
@@ -1216,7 +1204,7 @@ pub fn xyz_to_lrgb<T: DType, const N: usize>(pixel: &mut [T; N])
1216
1204
where
1217
1205
Channels < N > : ValidChannels ,
1218
1206
{
1219
- [ pixel[ 0 ] , pixel[ 1 ] , pixel[ 2 ] ] = mm ( XYZ65_MAT_INV , [ pixel[ 0 ] , pixel[ 1 ] , pixel[ 2 ] ] )
1207
+ [ pixel[ 0 ] , pixel[ 1 ] , pixel[ 2 ] ] = mm ( inv ( XYZ65_MAT ) , [ pixel[ 0 ] , pixel[ 1 ] , pixel[ 2 ] ] )
1220
1208
}
1221
1209
1222
1210
/// Convert from CIE LAB to CIE XYZ.
@@ -1251,9 +1239,9 @@ pub fn oklab_to_xyz<T: DType, const N: usize>(pixel: &mut [T; N])
1251
1239
where
1252
1240
Channels < N > : ValidChannels ,
1253
1241
{
1254
- let mut lms = mm ( OKLAB_M2_INV , [ pixel[ 0 ] , pixel[ 1 ] , pixel[ 2 ] ] ) ;
1242
+ let mut lms = mm ( inv ( OKLAB_M2 ) , [ pixel[ 0 ] , pixel[ 1 ] , pixel[ 2 ] ] ) ;
1255
1243
lms. iter_mut ( ) . for_each ( |c| * c = c. powi ( 3 ) ) ;
1256
- [ pixel[ 0 ] , pixel[ 1 ] , pixel[ 2 ] ] = mm ( OKLAB_M1_INV , lms) ;
1244
+ [ pixel[ 0 ] , pixel[ 1 ] , pixel[ 2 ] ] = mm ( inv ( OKLAB_M1 ) , lms) ;
1257
1245
}
1258
1246
1259
1247
/// Convert JzAzBz to CIE XYZ
@@ -1264,7 +1252,7 @@ where
1264
1252
Channels < N > : ValidChannels ,
1265
1253
{
1266
1254
let mut lms = mm (
1267
- JZAZBZ_M2_INV ,
1255
+ inv ( JZAZBZ_M2 ) ,
1268
1256
[
1269
1257
( pixel[ 0 ] + JZAZBZ_D0 . to_dt ( ) )
1270
1258
/ ( pixel[ 0 ] + JZAZBZ_D0 . to_dt ( ) ) . fma ( T :: ff32 ( -JZAZBZ_D ) , T :: ff32 ( 1.0 + JZAZBZ_D ) ) ,
@@ -1275,7 +1263,7 @@ where
1275
1263
1276
1264
lms. iter_mut ( ) . for_each ( |c| * c = pqz_eotf ( * c) ) ;
1277
1265
1278
- [ pixel[ 0 ] , pixel[ 1 ] , pixel[ 2 ] ] = mm ( JZAZBZ_M1_INV , lms) ;
1266
+ [ pixel[ 0 ] , pixel[ 1 ] , pixel[ 2 ] ] = mm ( inv ( JZAZBZ_M1 ) , lms) ;
1279
1267
1280
1268
pixel[ 0 ] = pixel[ 2 ] . fma ( ( JZAZBZ_B - 1.0 ) . to_dt ( ) , pixel[ 0 ] ) / JZAZBZ_B . to_dt ( ) ;
1281
1269
pixel[ 1 ] = pixel[ 0 ] . fma ( ( JZAZBZ_G - 1.0 ) . to_dt ( ) , pixel[ 1 ] ) / JZAZBZ_G . to_dt ( ) ;
@@ -1297,10 +1285,10 @@ where
1297
1285
Channels < N > : ValidChannels ,
1298
1286
{
1299
1287
// lms prime
1300
- let mut lms = mm ( ICTCP_M2_INV , [ pixel[ 0 ] , pixel[ 1 ] , pixel[ 2 ] ] ) ;
1288
+ let mut lms = mm ( inv ( ICTCP_M2 ) , [ pixel[ 0 ] , pixel[ 1 ] , pixel[ 2 ] ] ) ;
1301
1289
// non-prime lms
1302
1290
lms. iter_mut ( ) . for_each ( |c| * c = pq_eotf ( * c) ) ;
1303
- [ pixel[ 0 ] , pixel[ 1 ] , pixel[ 2 ] ] = mm ( ICTCP_M1_INV , lms) ;
1291
+ [ pixel[ 0 ] , pixel[ 1 ] , pixel[ 2 ] ] = mm ( inv ( ICTCP_M1 ) , lms) ;
1304
1292
}
1305
1293
1306
1294
/// Retrieves an LAB based space from its cylindrical representation.
0 commit comments