This repository has been archived by the owner on Mar 15, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 176
/
apca-w3.ts
754 lines (626 loc) · 28.5 KB
/
apca-w3.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
///////////////////////////////////////////////////////////////////////////////
/** @preserve
///// SAPC APCA - Advanced Perceptual Contrast Algorithm
///// Beta 0.1.9 W3 • contrast function only
///// DIST: W3 • Revision date: July 3, 2022
///// Function to parse color values and determine Lc contrast
///// Copyright © 2019-2022 by Andrew Somers. All Rights Reserved.
///// LICENSE: W3 LICENSE
///// CONTACT: Please use the ISSUES or DISCUSSIONS tab at:
///// https://github.com/Myndex/SAPC-APCA/
/////
///////////////////////////////////////////////////////////////////////////////
/////
///// MINIMAL IMPORTS:
///// import { APCAcontrast, sRGBtoY, displayP3toY,
///// calcAPCA, fontLookupAPCA } from 'apca-w3';
///// import { colorParsley } from 'colorparsley';
/////
///// FORWARD CONTRAST USAGE:
///// Lc = APCAcontrast( sRGBtoY( TEXTcolor ) , sRGBtoY( BACKGNDcolor ) );
///// Where the colors are sent as an rgba array [255,255,255,1]
/////
///// Retrieving an array of font sizes for the contrast:
///// fontArray = fontLookupAPCA(Lc);
/////
///// Live Demonstrator at https://www.myndex.com/APCA/
// */
///////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/////
///// SAPC Method and APCA Algorithm
///// W3 Licensed Version: https://github.com/Myndex/apca-w3
///// GITHUB MAIN REPO: https://github.com/Myndex/SAPC-APCA
///// DEVELOPER SITE: https://git.myndex.com/
/////
///// Acknowledgments and Thanks To:
///// • This project references the research & work of M.Fairchild, R.W.Hunt,
///// Drs. Bailey/Lovie-Kitchin, G.Legge, A.Arditi, M.Stone, C.Poynton,
///// L.Arend, M.Luo, E.Burns, R.Blackwell, P.Barton, M.Brettel, and many
///// others — see refs at https://www.myndex.com/WEB/WCAG_CE17polarity
///// • Bruce Bailey of USAccessBoard for his encouragement, ideas, & feedback
///// • Chris Lilly of W3C for continued review, examination, & oversight
///// • Chris Loiselle of Oracle for getting us back on track in a pandemic
///// • The many volunteer test subjects for participating in the studies.
///// • The many early adopters, beta testers, and code/issue contributors
///// • Principal research conducted at Myndex by A.Somers.
/////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/////
///// ***** SAPC BLOCK *****
/////
///// For Evaluations, refer to this as: SAPC-8, 0.0.98G-series constant 4g
///// SAPC • S-LUV Advanced Predictive Color
/////
///// SIMPLE VERSION — Only the basic APCA contrast predictor.
/////
///// Included Extensions & Model Features in this file:
///// • SAPC-8 Core Contrast (Base APCA, non-clinical use only)
///// • G series constants, group "G-4g" using a 2.4 monitor exponent
///// • sRGB to Y, parses numeric sRGB color to luminance
///// • SoftToe black level soft clamp and flare compensation.
/////
/////
////////////////////////////////////////////////////////////////////////////////
/////
///// DISCLAIMER AND LIMITATIONS OF USE
///// APCA is an embodiment of certain suprathreshold contrast
///// prediction technologies and it is licensed to the W3 on a
///// limited basis for use in certain specific accessibility
///// guidelines for web content only. APCA may be used for
///// predicting colors for web content use without royalty.
/////
///// However, Any such license excludes other use cases
///// not related to web content. Prohibited uses include
///// medical, clinical evaluation, human safety related,
///// aerospace, transportation, military applications,
///// and uses which are not specific to web based content
///// presented on self-illuminated displays or devices.
/////
////////////////////////////////////////////////////////////////////////////////
////////// APCA 0.1.9 G 4g USAGE ///////////////////////////////////////////
///
/// The API for "APCA 0.1.9" is trivially simple.
/// Send text and background sRGB numeric values to the sRGBtoY() function,
/// and send the resulting text-Y and background-Y to the APCAcontrast function
/// it returns a signed float with the numeric Lc contrast result.
///
/// The two inputs are TEXT color and BACKGROUND color in that order.
/// Each must be a numeric NOT a string, as this simple version has
/// no string parsing utilities. EXAMPLE:
/// ________________________________________________________________________
///
/// txtColor = colorParsley(0x123456); // color of the text
/// bgColor = colorParsley(0xabcdef); // color for the background
///
/// contrastLc = APCAcontrast( sRGBtoY(txtColor) , sRGBtoY(bgColor) );
/// ________________________________________________________________________
///
/// ********** QUICK START **********
///
/// Each color must be a 24bit color (8 bit per channel) as a single integer
/// (or 0x) sRGB encoded color, i.e. White is either the integer 16777216 or
/// the hex 0xffffff. A float is returned with a positive or negative value.
/// Negative values mean light text and a dark background, positive values
/// mean dark text and a light background. 60.0, or -60.0 is a contrast
/// "sort of like" the old WCAG 2's 4.5:1. NOTE: the total range is now less
/// than ± 110, so output can be rounded to a signed INT but DO NOT output
/// an absolute value - light text on dark BG should return a negative number.
///
/// ***** IMPORTANT: Do Not Mix Up Text and Background inputs. *****
/// **************** APCA is polarity sensitive! *****************
///
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///// BEGIN APCA 0.1.9 BLOCK \/////////////////////////////////////
//// \///////////////////////////////////
/// \/////////////////////////////////
// \///////////////////////////////
///// DEPENDENCIES /////
// The following imports are not needed for the main APCA function,
// but are needed for the shortcut/alias calcAPCA(), and for the
// future invertAPCA function, which examines hue.
//// (add slash to line start for local test mode, remove before push)
/* //// LOCAL TESTING SWITCH for using test.html
import{colorParsley}from'../node_modules/colorparsley/src/colorparsley.js';
/*/ //// TOGGLE
// */ //// END LOCAL TESTING SWITCH
///// Module Scope Object Containing Constants /////
///// APCA 0.0.98G - 4g - W3 Compatible Constants
///// 𝒦 SA98G ///////////////////////////////////
const SA98G = {
mainTRC: 2.4, // 2.4 exponent for emulating actual monitor perception
// For reverseAPCA
get mainTRCencode() {
return 1 / this.mainTRC;
},
// sRGB coefficients
sRco: 0.2126729,
sGco: 0.7151522,
sBco: 0.072175,
// G-4g constants for use with 2.4 exponent
normBG: 0.56,
normTXT: 0.57,
revTXT: 0.62,
revBG: 0.65,
// G-4g Clamps and Scalers
blkThrs: 0.022,
blkClmp: 1.414,
scaleBoW: 1.14,
scaleWoB: 1.14,
loBoWoffset: 0.027,
loWoBoffset: 0.027,
deltaYmin: 0.0005,
loClip: 0.1,
///// MAGIC NUMBERS for UNCLAMP, for use with 0.022 & 1.414 /////
// Magic Numbers for reverseAPCA
mFactor: 1.9468554433171,
get mFactInv() {
return 1 / this.mFactor;
},
mOffsetIn: 0.0387393816571401,
mExpAdj: 0.283343396420869,
get mExp() {
return this.mExpAdj / this.blkClmp;
},
mOffsetOut: 0.312865795870758,
};
//////////////////////////////////////////////////////////////////////////////
////////// APCA CALCULATION FUNCTIONS \/////////////////////////////////////
////////// ƒ APCAcontrast() ////////////////////////////////////////////
export function APCAcontrast(txtY: number, bgY: number, places = -1) {
// send linear Y (luminance) for text and background.
// txtY and bgY must be between 0.0-1.0
// IMPORTANT: Do not swap, polarity is important.
const icp = [0.0, 1.1]; // input range clamp / input error check
if (isNaN(txtY) || isNaN(bgY) || Math.min(txtY, bgY) < icp[0] || Math.max(txtY, bgY) > icp[1]) {
return 0.0; // return zero on error
// return 'error'; // optional string return for error
}
////////// SAPC LOCAL VARS /////////////////////////////////////////
let SAPC = 0.0; // For raw SAPC values
let outputContrast = 0.0; // For weighted final values
let polCat = 'BoW'; // Alternate Polarity Indicator. N normal R reverse
// TUTORIAL
// Use Y for text and BG, and soft clamp black,
// return 0 for very close luminances, determine
// polarity, and calculate SAPC raw contrast
// Then scale for easy to remember levels.
// Note that reverse contrast (white text on black)
// intentionally returns a negative number
// Proper polarity is important!
////////// BLACK SOFT CLAMP ////////////////////////////////////////
// Soft clamps Y for either color if it is near black.
txtY = txtY > SA98G.blkThrs ? txtY : txtY + Math.pow(SA98G.blkThrs - txtY, SA98G.blkClmp);
bgY = bgY > SA98G.blkThrs ? bgY : bgY + Math.pow(SA98G.blkThrs - bgY, SA98G.blkClmp);
///// Return 0 Early for extremely low ∆Y
if (Math.abs(bgY - txtY) < SA98G.deltaYmin) {
return 0.0;
}
////////// APCA/SAPC CONTRAST - LOW CLIP (W3 LICENSE) ///////////////
if (bgY > txtY) {
// For normal polarity, black text on white (BoW)
// Calculate the SAPC contrast value and scale
SAPC = (Math.pow(bgY, SA98G.normBG) - Math.pow(txtY, SA98G.normTXT)) * SA98G.scaleBoW;
// Low Contrast smooth rollout to prevent polarity reversal
// and also a low-clip for very low contrasts
outputContrast = SAPC < SA98G.loClip ? 0.0 : SAPC - SA98G.loBoWoffset;
} else {
// For reverse polarity, light text on dark (WoB)
// WoB should always return negative value.
polCat = 'WoB';
SAPC = (Math.pow(bgY, SA98G.revBG) - Math.pow(txtY, SA98G.revTXT)) * SA98G.scaleWoB;
outputContrast = SAPC > -SA98G.loClip ? 0.0 : SAPC + SA98G.loWoBoffset;
}
// return Lc (lightness contrast) as a signed numeric value
// Round to the nearest whole number as string is optional.
// Rounded can be a signed INT as output will be within ± 127
// places = -1 returns signed float, 1 or more set that many places
// 0 returns rounded string, uses BoW or WoB instead of minus sign
if (places < 0) {
// Default (-1) number out, all others are strings
return outputContrast * 100.0;
} else if (places == 0) {
return Math.round(Math.abs(outputContrast) * 100.0) + '<sub>' + polCat + '</sub>';
} else if (Number.isInteger(places)) {
return (outputContrast * 100.0).toFixed(places);
} else {
return 0.0;
}
} // End APCAcontrast() // END SWITCH ////////// ƒ reverseAPCA() DEPRECATED SOON ///////////////////////////////
/* SWITCH -- WORK IN PROGRESS DO NOT USE
////////// ƒ invertAPCA() //////////////////////////////////////////////////
export function invertAPCA (
{knownColor: [128,128,128], knownType: 'bg', targetCnst: 75,
returnAs: 'object', unknownType: 'txt', hueInvert: false,
hueRange: 5, preserveSat: false }) {
//if (Math.abs(targetCnst) < 15) { return false }; // abs contrast must be > 15
let knownY = sRGBtoY (knownColor);
let unknownY = knownY, knownExp, unknownExp;
let min,max,knownLs,isBG = true;
if (knownType == 'bg' || knownType == 'background') {
knownLs = Math.pow(knownY, );
black = APCAcontrast(0,knownY);
white = APCAcontrast(1,knownY);
} else if (knownType == 'txt' || knownType == 'text') {
isBG = false;
black = APCAcontrast(knownY,0);
white = APCAcontrast(knownY,1);
} else { return false } // return false on error
///// APCA 0.0.98G - 4g - W3 Compatible Constants ////////////////////
const scale = contrast > 0 ? SA98G.scaleBoW : SA98G.scaleWoB;
const offset = contrast > 0 ? SA98G.loBoWoffset : -SA98G.loWoBoffset;
targetCnst = ( parseFloat(targetCnst) * 0.01 + offset ) / scale;
// Soft clamps Y if it is near black.
knownY = (knownY > SA98G.blkThrs) ? knownY :
knownY + Math.pow(SA98G.blkThrs - knownY, SA98G.blkClmp);
// set the known and unknown exponents
if (isBG) {
knownExp = targetCnst > 0 ? SA98G.normBG : SA98G.revBG;
unknownExp = targetCnst > 0 ? SA98G.normTXT : SA98G.revTXT;
unknownY = Math.pow( Math.pow(knownY,knownExp) - targetCnst, 1/unknownExp );
if (isNaN(unknownY)) return false;
} else if (!isBG) {
knownExp = targetCnst > 0 ? SA98G.normTXT : SA98G.revTXT;
unknownExp = targetCnst > 0 ? SA98G.normBG : SA98G.revBG;
unknownY = Math.pow(targetCnst + Math.pow(knownY,knownExp), 1/unknownExp );
if (isNaN(unknownY)) return false;
}
//return contrast +'----'+unknownY;
if (unknownY > 1.06 || unknownY < 0) { return false } // return false on overflow
// if (unknownY < 0) { return false } // return false on underflow
//unknownY = Math.max(unknownY,0.0);
// unclamp
unknownY = (unknownY > SA98G.blkThrs) ? unknownY :
(Math.pow(((unknownY + SA98G.mOffsetIn)m* SA98G.mFactor),
SA98G.mExp) * SA98G.mFactInv) - SA98G.mOffsetOut;
// unknownY - 0.22 * Math.pow(unknownY*0.5, 1/blkClmp);
unknownY = Math.max(Math.min(unknownY,1.0),0.0);
let testedCnst = (isBG) ? APCAcontrast(unknownY,knownY) :
APCAcontrast(knownY,unknownY);
if (returnAs === 'object') {
let hexB = ( Math.round(Math.pow(unknownY,SA98G.mainTRCencode) * 255)
).toString(16).padStart(2,'0');
hexB = '#' + hexB + hexB + hexB;
return {color: hexB, Lc: testedCnst, whiteLc: white, blackLc: black};
} else if (returnAs === 'hex') {
let hexB = ( Math.round(Math.pow(unknownY,SA98G.mainTRCencode) * 255)
).toString(16).padStart(2,'0');
return '#' + hexB + hexB + hexB;
} else if (returnAs === 'array') {
let colorB = Math.round(Math.pow(unknownY,SA98G.mainTRCencode) * 255);
let retUse = (knownType == 'bg') ? 'txtColor' : 'bgColor'
return [colorB,colorB,colorB,1,retUse];
} else if (returnAs === 'Y' || returnAs === 'y') {
return Math.max(0.0,unknownY);
} else { return false } // return knownY on error
}
// */ export function reverseAPCA(
contrast = 0,
knownY = 1.0,
knownType = 'bg',
returnAs = 'hex'
) {
if (Math.abs(contrast) < 9) {
return false;
} // abs contrast must be > 9
let unknownY = knownY,
knownExp,
unknownExp;
///// APCA 0.0.98G - 4g - W3 Compatible Constants ////////////////////
const scale = contrast > 0 ? SA98G.scaleBoW : SA98G.scaleWoB;
const offset = contrast > 0 ? SA98G.loBoWoffset : -SA98G.loWoBoffset;
contrast = (parseFloat(contrast) * 0.01 + offset) / scale;
// Soft clamps Y if it is near black.
knownY =
knownY > SA98G.blkThrs ? knownY : knownY + Math.pow(SA98G.blkThrs - knownY, SA98G.blkClmp);
// set the known and unknown exponents
if (knownType == 'bg' || knownType == 'background') {
knownExp = contrast > 0 ? SA98G.normBG : SA98G.revBG;
unknownExp = contrast > 0 ? SA98G.normTXT : SA98G.revTXT;
unknownY = Math.pow(Math.pow(knownY, knownExp) - contrast, 1 / unknownExp);
if (isNaN(unknownY)) return false;
} else if (knownType == 'txt' || knownType == 'text') {
knownExp = contrast > 0 ? SA98G.normTXT : SA98G.revTXT;
unknownExp = contrast > 0 ? SA98G.normBG : SA98G.revBG;
unknownY = Math.pow(contrast + Math.pow(knownY, knownExp), 1 / unknownExp);
if (isNaN(unknownY)) return false;
} else {
return false;
} // return false on error
//return contrast +'----'+unknownY;
if (unknownY > 1.06 || unknownY < 0) {
return false;
} // return false on overflow
// if (unknownY < 0) { return false } // return false on underflow
//unknownY = Math.max(unknownY,0.0);
// unclamp
unknownY =
unknownY > SA98G.blkThrs
? unknownY
: Math.pow((unknownY + SA98G.mOffsetIn) * SA98G.mFactor, SA98G.mExp) * SA98G.mFactInv -
SA98G.mOffsetOut;
// unknownY - 0.22 * Math.pow(unknownY*0.5, 1/blkClmp);
unknownY = Math.max(Math.min(unknownY, 1.0), 0.0);
if (returnAs === 'hex') {
let hexB = Math.round(Math.pow(unknownY, SA98G.mainTRCencode) * 255)
.toString(16)
.padStart(2, '0');
return '#' + hexB + hexB + hexB;
} else if (returnAs === 'color') {
let colorB = Math.round(Math.pow(unknownY, SA98G.mainTRCencode) * 255);
let retUse = knownType == 'bg' ? 'txtColor' : 'bgColor';
return [colorB, colorB, colorB, 1, retUse];
} else if (returnAs === 'Y' || returnAs === 'y') {
return Math.max(0.0, unknownY);
} else {
return false;
} // return knownY on error
}
////////// ƒ calcAPCA() /////////////////////////////////////////////
export function calcAPCA(
textColor: [number, number, number, number],
bgColor: [number, number, number, number],
places = -1,
round = true
) {
// Note that this function requires colorParsley !!
let hasAlpha = textColor[3] == 1 ? false : true;
if (hasAlpha) {
textColor = alphaBlend(textColor, bgColor, round) as [number, number, number, number];
}
return APCAcontrast(sRGBtoY(textColor), sRGBtoY(bgColor), places);
} // End calcAPCA()
//////////////////////////////////////////////////////////////////////////////
////////// ƒ fontLookupAPCA() 0.1.7 (G) \////////////////////////////////
///////// \//////////////////////////////
export function fontLookupAPCA(contrast, places = 2) {
////////////////////////////////////////////////////////////////////////////
///// CONTRAST * FONT WEIGHT & SIZE /////////////////////////////////////
// Font size interpolations. Here the chart was re-ordered to put
// the main contrast levels each on one line, instead of font size per line.
// First column is LC value, then each following column is font size by weight
// G G G G G G Public Beta 0.1.7 (G) • MAY 28 2022
// Lc values under 70 should have Lc 15 ADDED if used for body text
// All font sizes are in px and reference font is Barlow
// 999: prohibited - too low contrast
// 777: NON TEXT at this minimum weight stroke
// 666 - this is for spot text, not fluent-Things like copyright or placeholder.
// 5xx - minimum font at this weight for content, 5xx % 500 for font-size
// 4xx - minimum font at this weight for any purpose], 4xx % 400 for font-size
// MAIN FONT SIZE LOOKUP
//// ASCENDING SORTED Public Beta 0.1.7 (G) • MAY 28 2022 ////
//// Lc 45 * 0.2 = 9 which is the index for the row for Lc 45
// MAIN FONT LOOKUP May 28 2022 EXPANDED
// Sorted by Lc Value
// First row is standard weights 100-900
// First column is font size in px
// All other values are the Lc contrast
// 999 = too low. 777 = non-text and spot text only
const fontMatrixAscend = [
['Lc', 100, 200, 300, 400, 500, 600, 700, 800, 900],
[0, 999, 999, 999, 999, 999, 999, 999, 999, 999],
[10, 999, 999, 999, 999, 999, 999, 999, 999, 999],
[15, 777, 777, 777, 777, 777, 777, 777, 777, 777],
[20, 777, 777, 777, 777, 777, 777, 777, 777, 777],
[25, 777, 777, 777, 120, 120, 108, 96, 96, 96],
[30, 777, 777, 120, 108, 108, 96, 72, 72, 72],
[35, 777, 120, 108, 96, 72, 60, 48, 48, 48],
[40, 120, 108, 96, 60, 48, 42, 32, 32, 32],
[45, 108, 96, 72, 42, 32, 28, 24, 24, 24],
[50, 96, 72, 60, 32, 28, 24, 21, 21, 21],
[55, 80, 60, 48, 28, 24, 21, 18, 18, 18],
[60, 72, 48, 42, 24, 21, 18, 16, 16, 18],
[65, 68, 46, 32, 21.75, 19, 17, 15, 16, 18],
[70, 64, 44, 28, 19.5, 18, 16, 14.5, 16, 18],
[75, 60, 42, 24, 18, 16, 15, 14, 16, 18],
[80, 56, 38.25, 23, 17.25, 15.81, 14.81, 14, 16, 18],
[85, 52, 34.5, 22, 16.5, 15.625, 14.625, 14, 16, 18],
[90, 48, 32, 21, 16, 15.5, 14.5, 14, 16, 18],
[95, 45, 28, 19.5, 15.5, 15, 14, 13.5, 16, 18],
[100, 42, 26.5, 18.5, 15, 14.5, 13.5, 13, 16, 18],
[105, 39, 25, 18, 14.5, 14, 13, 12, 16, 18],
[110, 36, 24, 18, 14, 13, 12, 11, 16, 18],
[115, 34.5, 22.5, 17.25, 12.5, 11.875, 11.25, 10.625, 14.5, 16.5],
[120, 33, 21, 16.5, 11, 10.75, 10.5, 10.25, 13, 15],
[125, 32, 20, 16, 10, 10, 10, 10, 12, 14],
];
// ASCENDING SORTED Public Beta 0.1.7 (G) • MAY 28 2022 ////
// DELTA - MAIN FONT LOOKUP May 28 2022 EXPANDED
// EXPANDED Sorted by Lc Value •• DELTA
// The pre-calculated deltas of the above array
const fontDeltaAscend = [
['∆Lc', 100, 200, 300, 400, 500, 600, 700, 800, 900],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[10, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[15, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[20, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[25, 0, 0, 0, 12, 12, 12, 24, 24, 24],
[30, 0, 0, 12, 12, 36, 36, 24, 24, 24],
[35, 0, 12, 12, 36, 24, 18, 16, 16, 16],
[40, 12, 12, 24, 18, 16, 14, 8, 8, 8],
[45, 12, 24, 12, 10, 4, 4, 3, 3, 3],
[50, 16, 12, 12, 4, 4, 3, 3, 3, 3],
[55, 8, 12, 6, 4, 3, 3, 2, 2, 0],
[60, 4, 2, 10, 2.25, 2, 1, 1, 0, 0],
[65, 4, 2, 4, 2.25, 1, 1, 0.5, 0, 0],
[70, 4, 2, 4, 1.5, 2, 1, 0.5, 0, 0],
[75, 4, 3.75, 1, 0.75, 0.188, 0.188, 0, 0, 0],
[80, 4, 3.75, 1, 0.75, 0.188, 0.188, 0, 0, 0],
[85, 4, 2.5, 1, 0.5, 0.125, 0.125, 0, 0, 0],
[90, 3, 4, 1.5, 0.5, 0.5, 0.5, 0.5, 0, 0],
[95, 3, 1.5, 1, 0.5, 0.5, 0.5, 0.5, 0, 0],
[100, 3, 1.5, 0.5, 0.5, 0.5, 0.5, 1, 0, 0],
[105, 3, 1, 0, 0.5, 1, 1, 1, 0, 0],
[110, 1.5, 1.5, 0.75, 1.5, 1.125, 0.75, 0.375, 1.5, 1.5],
[115, 1.5, 1.5, 0.75, 1.5, 1.125, 0.75, 0.375, 1.5, 1.5],
[120, 1, 1, 0.5, 1, 0.75, 0.5, 0.25, 1, 1],
[125, 0, 0, 0, 0, 0, 0, 0, 0, 0],
];
// APCA CONTRAST FONT LOOKUP TABLES
// Copyright © 2022 by Myndex Research and Andrew Somers. All Rights Reserved
// Public Beta 0.1.7 (G) • MAY 28 2022
// For the following arrays, the Y axis is contrastArrayLen
// The two x axis are weightArrayLen and scoreArrayLen
// MAY 28 2022
const weightArray = [0, 100, 200, 300, 400, 500, 600, 700, 800, 900];
const weightArrayLen = weightArray.length; // X axis
let returnArray = [contrast.toFixed(places), 0, 0, 0, 0, 0, 0, 0, 0, 0];
const returnArrayLen = returnArray.length; // X axis
const contrastArrayAscend = [
'lc',
0,
10,
15,
20,
25,
30,
35,
40,
45,
50,
55,
60,
65,
70,
75,
80,
85,
90,
95,
100,
105,
110,
115,
120,
125,
];
const contrastArrayLenAsc = contrastArrayAscend.length; // Y azis
//// Lc 45 * 0.2 = 9, and 9 is the index for the row for Lc 45
let tempFont = 777;
contrast = Math.abs(contrast); // Polarity unneeded for LUT
const factor = 0.2; // 1/5 as LUT is in increments of 5
const index = contrast == 0 ? 1 : (contrast * factor) | 0; // LUT row... n|0 is bw floor
let w = 0;
// scoreAdj interpolates the needed font side per the Lc
let scoreAdj = (contrast - fontMatrixAscend[index][w]) * factor;
w++; // determines column in font matrix LUT
///////// Font and Score Interpolation \/////////////////////////////////
// populate returnArray with interpolated values
for (; w < weightArrayLen; w++) {
tempFont = fontMatrixAscend[index][w];
if (tempFont > 400) {
// declares a specific minimum for the weight.
returnArray[w] = tempFont;
} else if (contrast < 14.5) {
returnArray[w] = 999; // 999 = do not use for anything
} else if (contrast < 29.5) {
returnArray[w] = 777; // 777 = non-text only
} else {
// INTERPOLATION OF FONT SIZE
// sets level for 0.5px size increments of smaller fonts
// Note bitwise (n|0) instead of floor
tempFont > 24
? (returnArray[w] = Math.round(tempFont - fontDeltaAscend[index][w] * scoreAdj))
: (returnArray[w] = tempFont - ((2.0 * fontDeltaAscend[index][w] * scoreAdj) | 0) * 0.5);
// (n|0) is bitwise floor
}
}
///////// End Interpolation ////////////////////////////////////////////
return returnArray;
} // end fontLookupAPCA
/////////\ ///////////////////////////\
//////////\ END fontLookupAPCA() 0.1.7 (G) /////////////////////////////\
/////////////////////////////////////////////////////////////////////////////\
//////////////////////////////////////////////////////////////////////////////
////////// LUMINANCE CONVERTERS |//////////////////////////////////////////
////////// ƒ sRGBtoY() //////////////////////////////////////////////////
export function sRGBtoY(rgb = [0, 0, 0]) {
// send sRGB 8bpc (0xFFFFFF) or string
// NOTE: Currently expects 0-255
///// APCA 0.0.98G - 4g - W3 Compatible Constants ////////////////////
/*
const mainTRC = 2.4; // 2.4 exponent emulates actual monitor perception
const sRco = 0.2126729,
sGco = 0.7151522,
sBco = 0.0721750; // sRGB coefficients
*/
// Future:
// 0.2126478133913640 0.7151791475336150 0.0721730390750208
// Derived from:
// xW yW K xR yR xG yG xB yB
// 0.312720 0.329030 6504 0.640 0.330 0.300 0.600 0.150 0.060
// linearize r, g, or b then apply coefficients
// and sum then return the resulting luminance
function simpleExp(chan) {
return Math.pow(chan / 255.0, SA98G.mainTRC);
}
return (
SA98G.sRco * simpleExp(rgb[0]) + SA98G.sGco * simpleExp(rgb[1]) + SA98G.sBco * simpleExp(rgb[2])
);
} // End sRGBtoY()
////////// ƒ displayP3toY() /////////////////////////////////////////////
export function displayP3toY(rgb = [0, 0, 0]) {
// send rgba array
// NOTE: Currently Apple has the tuple as 0.0 to 1.0, NOT 255
///// APCA 0.0.98G - 4g - W3 Compatible Constants ////////////////////
const mainTRC = 2.4; // 2.4 exponent emulates actual monitor perception
// Pending evaluation, because, Apple...
const sRco = 0.228982959480578,
sGco = 0.691749262585238,
sBco = 0.0792677779341829; // displayP3 coefficients
// Derived from:
// xW yW K xR yR xG yG xB yB
// 0.312720 0.329030 6504 0.680 0.320 0.265 0.690 0.150 0.060
// linearize r, g, or b then apply coefficients
// and sum then return the resulting luminance
function simpleExp(chan) {
return Math.pow(chan, mainTRC);
}
return sRco * simpleExp(rgb[0]) + sGco * simpleExp(rgb[1]) + sBco * simpleExp(rgb[2]);
} // End displayP3toY()
////////// ƒ adobeRGBtoY() /////////////////////////////////////////////
export function adobeRGBtoY(rgb = [0, 0, 0]) {
// send rgba array
// NOTE: Currently expects 0-255
///// APCA 0.0.98G - 4g - W3 Compatible Constants ////////////////////
const mainTRC = 2.35; // 2.35 exponent emulates actual monitor perception
// Pending evaluation...
const sRco = 0.297355022711381,
sGco = 0.627372749714528,
sBco = 0.0752722275740913; // adobeRGB coefficients
// Derived from:
// xW yW K xR yR xG yG xB yB
// 0.312720 0.329030 6504 0.640 0.330 0.210 0.710 0.150 0.060
// linearize r, g, or b then apply coefficients
// and sum then return the resulting luminance
function simpleExp(chan) {
return Math.pow(chan / 255.0, mainTRC);
}
return sRco * simpleExp(rgb[0]) + sGco * simpleExp(rgb[1]) + sBco * simpleExp(rgb[2]);
} // End displayP3toY()
////////////////////////////////////////////////////////////////////////////
////////// UTILITIES \///////////////////////////////////////////////////
////////// ƒ alphaBlend() /////////////////////////////////////////////
// send rgba array for text/icon, rgb for background.
// Only foreground allows alpha of 0.0 to 1.0
// This blends using gamma encoded space (standard)
// rounded 0-255 or set round=false for number 0.0-255.0
export function alphaBlend(rgbaFG = [0, 0, 0, 1.0], rgbBG = [0, 0, 0], round = true) {
rgbaFG[3] = Math.max(Math.min(rgbaFG[3], 1.0), 0.0); // clamp alpha 0-1
let compBlend = 1.0 - rgbaFG[3];
let rgbOut = [0, 0, 0, 1]; // or just use rgbBG to retain other elements?
for (let i = 0; i < 3; i++) {
rgbOut[i] = rgbBG[i] * compBlend + rgbaFG[i] * rgbaFG[3];
if (round) rgbOut[i] = Math.min(Math.round(rgbOut[i]), 255);
}
return rgbOut;
} // End alphaBlend()
//\ ////////////////////////////////////////
///\ ////////////////////////////////////////
////\ ////////////////////////////////////////
/////\ END APCA 0.1.9 G-4g BLOCK ////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////