@@ -245,12 +245,33 @@ const JZAZBZ_P: f32 = 1.7 * PQEOTF_M2;
245
245
// ### CONSTS ### }}}
246
246
247
247
// ### MATRICES ### {{{
248
+
249
+ /// Its easier to write matricies visually then transpose them so they can be indexed per vector
250
+ /// [X1, X2] -> [X1, Y1]
251
+ /// [Y1, Y2] [X2, Y2]
252
+ const fn t ( m : [ [ f32 ; 3 ] ; 3 ] ) -> [ [ f32 ; 3 ] ; 3 ] {
253
+ [
254
+ [ m[ 0 ] [ 0 ] , m[ 1 ] [ 0 ] , m[ 2 ] [ 0 ] ] ,
255
+ [ m[ 0 ] [ 1 ] , m[ 1 ] [ 1 ] , m[ 2 ] [ 1 ] ] ,
256
+ [ m[ 0 ] [ 2 ] , m[ 1 ] [ 2 ] , m[ 2 ] [ 2 ] ] ,
257
+ ]
258
+ }
259
+
260
+ /// Matrix Multiply
261
+ fn mm < T : DType > ( m : [ [ f32 ; 3 ] ; 3 ] , p : [ T ; 3 ] ) -> [ T ; 3 ] {
262
+ [
263
+ p[ 0 ] . fma ( m[ 0 ] [ 0 ] . to_dt ( ) , p[ 1 ] . fma ( m[ 1 ] [ 0 ] . to_dt ( ) , p[ 2 ] * m[ 2 ] [ 0 ] . to_dt ( ) ) ) ,
264
+ p[ 0 ] . fma ( m[ 0 ] [ 1 ] . to_dt ( ) , p[ 1 ] . fma ( m[ 1 ] [ 1 ] . to_dt ( ) , p[ 2 ] * m[ 2 ] [ 1 ] . to_dt ( ) ) ) ,
265
+ p[ 0 ] . fma ( m[ 0 ] [ 2 ] . to_dt ( ) , p[ 1 ] . fma ( m[ 1 ] [ 2 ] . to_dt ( ) , p[ 2 ] * m[ 2 ] [ 2 ] . to_dt ( ) ) ) ,
266
+ ]
267
+ }
268
+
248
269
// CIE XYZ
249
- const XYZ65_MAT : [ [ f32 ; 3 ] ; 3 ] = [
270
+ const XYZ65_MAT : [ [ f32 ; 3 ] ; 3 ] = t ( [
250
271
[ 0.4124 , 0.3576 , 0.1805 ] ,
251
272
[ 0.2126 , 0.7152 , 0.0722 ] ,
252
273
[ 0.0193 , 0.1192 , 0.9505 ] ,
253
- ] ;
274
+ ] ) ;
254
275
255
276
// Original commonly used inverted array
256
277
// const XYZ65_MAT_INV: [[f32; 3]; 3] = [
@@ -260,13 +281,14 @@ const XYZ65_MAT: [[f32; 3]; 3] = [
260
281
// ];
261
282
262
283
// Higher precision invert using numpy. Helps with back conversions
263
- const XYZ65_MAT_INV : [ [ f32 ; 3 ] ; 3 ] = [
284
+ const XYZ65_MAT_INV : [ [ f32 ; 3 ] ; 3 ] = t ( [
264
285
[ 3.2406254773 , -1.5372079722 , -0.4986285987 ] ,
265
286
[ -0.9689307147 , 1.8757560609 , 0.0415175238 ] ,
266
287
[ 0.0557101204 , -0.2040210506 , 1.0569959423 ] ,
267
- ] ;
288
+ ] ) ;
268
289
269
290
// OKLAB
291
+ // They appear to be provided already transposed for code in the blog post
270
292
const OKLAB_M1 : [ [ f32 ; 3 ] ; 3 ] = [
271
293
[ 0.8189330101 , 0.0329845436 , 0.0482003018 ] ,
272
294
[ 0.3618667424 , 0.9293118715 , 0.2643662691 ] ,
@@ -289,68 +311,50 @@ const OKLAB_M2_INV: [[f32; 3]; 3] = [
289
311
] ;
290
312
291
313
// JzAzBz
292
- const JZAZBZ_M1 : [ [ f32 ; 3 ] ; 3 ] = [
314
+ const JZAZBZ_M1 : [ [ f32 ; 3 ] ; 3 ] = t ( [
293
315
[ 0.41478972 , 0.579999 , 0.0146480 ] ,
294
316
[ -0.2015100 , 1.120649 , 0.0531008 ] ,
295
317
[ -0.0166008 , 0.264800 , 0.6684799 ] ,
296
- ] ;
297
- const JZAZBZ_M2 : [ [ f32 ; 3 ] ; 3 ] = [
318
+ ] ) ;
319
+ const JZAZBZ_M2 : [ [ f32 ; 3 ] ; 3 ] = t ( [
298
320
[ 0.500000 , 0.500000 , 0.000000 ] ,
299
321
[ 3.524000 , -4.066708 , 0.542708 ] ,
300
322
[ 0.199076 , 1.096799 , -1.295875 ] ,
301
- ] ;
323
+ ] ) ;
302
324
303
- const JZAZBZ_M1_INV : [ [ f32 ; 3 ] ; 3 ] = [
325
+ const JZAZBZ_M1_INV : [ [ f32 ; 3 ] ; 3 ] = t ( [
304
326
[ 1.9242264358 , -1.0047923126 , 0.037651404 ] ,
305
327
[ 0.3503167621 , 0.7264811939 , -0.0653844229 ] ,
306
328
[ -0.090982811 , -0.3127282905 , 1.5227665613 ] ,
307
- ] ;
308
- const JZAZBZ_M2_INV : [ [ f32 ; 3 ] ; 3 ] = [
329
+ ] ) ;
330
+ const JZAZBZ_M2_INV : [ [ f32 ; 3 ] ; 3 ] = t ( [
309
331
[ 1. , 0.1386050433 , 0.0580473162 ] ,
310
332
[ 1. , -0.1386050433 , -0.0580473162 ] ,
311
333
[ 1. , -0.096019242 , -0.8118918961 ] ,
312
- ] ;
334
+ ] ) ;
313
335
314
336
// ICtCp
315
- const ICTCP_M1 : [ [ f32 ; 3 ] ; 3 ] = [
337
+ const ICTCP_M1 : [ [ f32 ; 3 ] ; 3 ] = t ( [
316
338
[ 1688. / 4096. , 2146. / 4096. , 262. / 4096. ] ,
317
339
[ 683. / 4096. , 2951. / 4096. , 462. / 4096. ] ,
318
340
[ 99. / 4096. , 309. / 4096. , 3688. / 4096. ] ,
319
- ] ;
320
- const ICTCP_M2 : [ [ f32 ; 3 ] ; 3 ] = [
341
+ ] ) ;
342
+ const ICTCP_M2 : [ [ f32 ; 3 ] ; 3 ] = t ( [
321
343
[ 2048. / 4096. , 2048. / 4096. , 0. / 4096. ] ,
322
344
[ 6610. / 4096. , -13613. / 4096. , 7003. / 4096. ] ,
323
345
[ 17933. / 4096. , -17390. / 4096. , -543. / 4096. ] ,
324
- ] ;
346
+ ] ) ;
325
347
326
- const ICTCP_M1_INV : [ [ f32 ; 3 ] ; 3 ] = [
348
+ const ICTCP_M1_INV : [ [ f32 ; 3 ] ; 3 ] = t ( [
327
349
[ 3.4366066943 , -2.5064521187 , 0.0698454243 ] ,
328
350
[ -0.7913295556 , 1.9836004518 , -0.1922708962 ] ,
329
351
[ -0.0259498997 , -0.0989137147 , 1.1248636144 ] ,
330
- ] ;
331
- const ICTCP_M2_INV : [ [ f32 ; 3 ] ; 3 ] = [
352
+ ] ) ;
353
+ const ICTCP_M2_INV : [ [ f32 ; 3 ] ; 3 ] = t ( [
332
354
[ 1. , 0.008609037 , 0.111029625 ] ,
333
355
[ 1. , -0.008609037 , -0.111029625 ] ,
334
356
[ 1. , 0.5600313357 , -0.320627175 ] ,
335
- ] ;
336
-
337
- /// 3 * 3x3 Matrix multiply with vector transposed, ie pixel @ matrix
338
- fn matmul3t < T : DType > ( p : [ T ; 3 ] , m : [ [ f32 ; 3 ] ; 3 ] ) -> [ T ; 3 ] {
339
- [
340
- p[ 0 ] . fma ( m[ 0 ] [ 0 ] . to_dt ( ) , p[ 1 ] . fma ( m[ 1 ] [ 0 ] . to_dt ( ) , p[ 2 ] * m[ 2 ] [ 0 ] . to_dt ( ) ) ) ,
341
- p[ 0 ] . fma ( m[ 0 ] [ 1 ] . to_dt ( ) , p[ 1 ] . fma ( m[ 1 ] [ 1 ] . to_dt ( ) , p[ 2 ] * m[ 2 ] [ 1 ] . to_dt ( ) ) ) ,
342
- p[ 0 ] . fma ( m[ 0 ] [ 2 ] . to_dt ( ) , p[ 1 ] . fma ( m[ 1 ] [ 2 ] . to_dt ( ) , p[ 2 ] * m[ 2 ] [ 2 ] . to_dt ( ) ) ) ,
343
- ]
344
- }
345
-
346
- /// Transposed 3 * 3x3 matrix multiply, ie matrix @ pixel
347
- fn matmul3 < T : DType > ( m : [ [ f32 ; 3 ] ; 3 ] , p : [ T ; 3 ] ) -> [ T ; 3 ] {
348
- [
349
- p[ 0 ] . fma ( m[ 0 ] [ 0 ] . to_dt ( ) , p[ 1 ] . fma ( m[ 0 ] [ 1 ] . to_dt ( ) , p[ 2 ] * m[ 0 ] [ 2 ] . to_dt ( ) ) ) ,
350
- p[ 0 ] . fma ( m[ 1 ] [ 0 ] . to_dt ( ) , p[ 1 ] . fma ( m[ 1 ] [ 1 ] . to_dt ( ) , p[ 2 ] * m[ 1 ] [ 2 ] . to_dt ( ) ) ) ,
351
- p[ 0 ] . fma ( m[ 2 ] [ 0 ] . to_dt ( ) , p[ 1 ] . fma ( m[ 2 ] [ 1 ] . to_dt ( ) , p[ 2 ] * m[ 2 ] [ 2 ] . to_dt ( ) ) ) ,
352
- ]
353
- }
357
+ ] ) ;
354
358
// ### MATRICES ### }}}
355
359
356
360
// ### TRANSFER FUNCTIONS ### {{{
@@ -1112,7 +1116,7 @@ pub fn lrgb_to_xyz<T: DType, const N: usize>(pixel: &mut [T; N])
1112
1116
where
1113
1117
Channels < N > : ValidChannels ,
1114
1118
{
1115
- [ pixel[ 0 ] , pixel[ 1 ] , pixel[ 2 ] ] = matmul3 ( XYZ65_MAT , [ pixel[ 0 ] , pixel[ 1 ] , pixel[ 2 ] ] )
1119
+ [ pixel[ 0 ] , pixel[ 1 ] , pixel[ 2 ] ] = mm ( XYZ65_MAT , [ pixel[ 0 ] , pixel[ 1 ] , pixel[ 2 ] ] )
1116
1120
}
1117
1121
1118
1122
/// Convert from CIE XYZ to CIE LAB.
@@ -1147,9 +1151,9 @@ pub fn xyz_to_oklab<T: DType, const N: usize>(pixel: &mut [T; N])
1147
1151
where
1148
1152
Channels < N > : ValidChannels ,
1149
1153
{
1150
- let mut lms = matmul3t ( [ pixel[ 0 ] , pixel[ 1 ] , pixel[ 2 ] ] , OKLAB_M1 ) ;
1154
+ let mut lms = mm ( OKLAB_M1 , [ pixel[ 0 ] , pixel[ 1 ] , pixel[ 2 ] ] ) ;
1151
1155
lms. iter_mut ( ) . for_each ( |c| * c = c. scbrt ( ) ) ;
1152
- [ pixel[ 0 ] , pixel[ 1 ] , pixel[ 2 ] ] = matmul3t ( lms , OKLAB_M2 ) ;
1156
+ [ pixel[ 0 ] , pixel[ 1 ] , pixel[ 2 ] ] = mm ( OKLAB_M2 , lms ) ;
1153
1157
}
1154
1158
1155
1159
/// Convert CIE XYZ to JzAzBz
@@ -1159,7 +1163,7 @@ pub fn xyz_to_jzazbz<T: DType, const N: usize>(pixel: &mut [T; N])
1159
1163
where
1160
1164
Channels < N > : ValidChannels ,
1161
1165
{
1162
- let mut lms = matmul3 (
1166
+ let mut lms = mm (
1163
1167
JZAZBZ_M1 ,
1164
1168
[
1165
1169
pixel[ 0 ] . fma ( JZAZBZ_B . to_dt ( ) , T :: ff32 ( -JZAZBZ_B + 1.0 ) * pixel[ 2 ] ) ,
@@ -1170,7 +1174,7 @@ where
1170
1174
1171
1175
lms. iter_mut ( ) . for_each ( |e| * e = pqz_oetf ( * e) ) ;
1172
1176
1173
- let lab = matmul3 ( JZAZBZ_M2 , lms) ;
1177
+ let lab = mm ( JZAZBZ_M2 , lms) ;
1174
1178
1175
1179
pixel[ 0 ] = ( T :: ff32 ( 1.0 + JZAZBZ_D ) * lab[ 0 ] ) / lab[ 0 ] . fma ( JZAZBZ_D . to_dt ( ) , 1.0 . to_dt ( ) ) - JZAZBZ_D0 . to_dt ( ) ;
1176
1180
pixel[ 1 ] = lab[ 1 ] ;
@@ -1200,10 +1204,10 @@ where
1200
1204
// };
1201
1205
// pixel.iter_mut().for_each(|c| bt2020(c));
1202
1206
1203
- let mut lms = matmul3 ( ICTCP_M1 , [ pixel[ 0 ] , pixel[ 1 ] , pixel[ 2 ] ] ) ;
1207
+ let mut lms = mm ( ICTCP_M1 , [ pixel[ 0 ] , pixel[ 1 ] , pixel[ 2 ] ] ) ;
1204
1208
// lms prime
1205
1209
lms. iter_mut ( ) . for_each ( |c| * c = pq_oetf ( * c) ) ;
1206
- [ pixel[ 0 ] , pixel[ 1 ] , pixel[ 2 ] ] = matmul3 ( ICTCP_M2 , lms) ;
1210
+ [ pixel[ 0 ] , pixel[ 1 ] , pixel[ 2 ] ] = mm ( ICTCP_M2 , lms) ;
1207
1211
}
1208
1212
1209
1213
/// Converts an LAB based space to a cylindrical representation.
@@ -1334,7 +1338,7 @@ pub fn xyz_to_lrgb<T: DType, const N: usize>(pixel: &mut [T; N])
1334
1338
where
1335
1339
Channels < N > : ValidChannels ,
1336
1340
{
1337
- [ pixel[ 0 ] , pixel[ 1 ] , pixel[ 2 ] ] = matmul3 ( XYZ65_MAT_INV , [ pixel[ 0 ] , pixel[ 1 ] , pixel[ 2 ] ] )
1341
+ [ pixel[ 0 ] , pixel[ 1 ] , pixel[ 2 ] ] = mm ( XYZ65_MAT_INV , [ pixel[ 0 ] , pixel[ 1 ] , pixel[ 2 ] ] )
1338
1342
}
1339
1343
1340
1344
/// Convert from CIE LAB to CIE XYZ.
@@ -1369,9 +1373,9 @@ pub fn oklab_to_xyz<T: DType, const N: usize>(pixel: &mut [T; N])
1369
1373
where
1370
1374
Channels < N > : ValidChannels ,
1371
1375
{
1372
- let mut lms = matmul3t ( [ pixel[ 0 ] , pixel[ 1 ] , pixel[ 2 ] ] , OKLAB_M2_INV ) ;
1376
+ let mut lms = mm ( OKLAB_M2_INV , [ pixel[ 0 ] , pixel[ 1 ] , pixel[ 2 ] ] ) ;
1373
1377
lms. iter_mut ( ) . for_each ( |c| * c = c. powi ( 3 ) ) ;
1374
- [ pixel[ 0 ] , pixel[ 1 ] , pixel[ 2 ] ] = matmul3t ( lms , OKLAB_M1_INV ) ;
1378
+ [ pixel[ 0 ] , pixel[ 1 ] , pixel[ 2 ] ] = mm ( OKLAB_M1_INV , lms ) ;
1375
1379
}
1376
1380
1377
1381
/// Convert JzAzBz to CIE XYZ
@@ -1381,7 +1385,7 @@ pub fn jzazbz_to_xyz<T: DType, const N: usize>(pixel: &mut [T; N])
1381
1385
where
1382
1386
Channels < N > : ValidChannels ,
1383
1387
{
1384
- let mut lms = matmul3 (
1388
+ let mut lms = mm (
1385
1389
JZAZBZ_M2_INV ,
1386
1390
[
1387
1391
( pixel[ 0 ] + JZAZBZ_D0 . to_dt ( ) )
@@ -1393,7 +1397,7 @@ where
1393
1397
1394
1398
lms. iter_mut ( ) . for_each ( |c| * c = pqz_eotf ( * c) ) ;
1395
1399
1396
- [ pixel[ 0 ] , pixel[ 1 ] , pixel[ 2 ] ] = matmul3 ( JZAZBZ_M1_INV , lms) ;
1400
+ [ pixel[ 0 ] , pixel[ 1 ] , pixel[ 2 ] ] = mm ( JZAZBZ_M1_INV , lms) ;
1397
1401
1398
1402
pixel[ 0 ] = pixel[ 2 ] . fma ( ( JZAZBZ_B - 1.0 ) . to_dt ( ) , pixel[ 0 ] ) / JZAZBZ_B . to_dt ( ) ;
1399
1403
pixel[ 1 ] = pixel[ 0 ] . fma ( ( JZAZBZ_G - 1.0 ) . to_dt ( ) , pixel[ 1 ] ) / JZAZBZ_G . to_dt ( ) ;
@@ -1415,10 +1419,10 @@ where
1415
1419
Channels < N > : ValidChannels ,
1416
1420
{
1417
1421
// lms prime
1418
- let mut lms = matmul3 ( ICTCP_M2_INV , [ pixel[ 0 ] , pixel[ 1 ] , pixel[ 2 ] ] ) ;
1422
+ let mut lms = mm ( ICTCP_M2_INV , [ pixel[ 0 ] , pixel[ 1 ] , pixel[ 2 ] ] ) ;
1419
1423
// non-prime lms
1420
1424
lms. iter_mut ( ) . for_each ( |c| * c = pq_eotf ( * c) ) ;
1421
- [ pixel[ 0 ] , pixel[ 1 ] , pixel[ 2 ] ] = matmul3 ( ICTCP_M1_INV , lms) ;
1425
+ [ pixel[ 0 ] , pixel[ 1 ] , pixel[ 2 ] ] = mm ( ICTCP_M1_INV , lms) ;
1422
1426
}
1423
1427
1424
1428
/// Retrieves an LAB based space from its cylindrical representation.
0 commit comments