All notable changes to this project will be documented in this file. This change log follows the conventions of keepachangelog.com.
- Munsell colours:
clojure2d.color/munsell-to-Yxy(D65 by default) andclojure2d.color/munsell-to-sRGB(not clamped) clojure2d.color.munsellnamespace (port of colour-science, BSD-3-Clause):munsell-spec,munsell-to-Yxy(Illuminant C by default),munsell-valueandluminancewith:astm,:newhall,:osa,:priest,:munsell,:moon,:saunderson,:ladd,:mccamyvalue scalesclojure2d.color.whitepoints/chromatic-adaptation-transform: cone response matrices of an adaptation method, throws for an unknown oneclojure2d.color.whitepoints/cmf: color matching functions of:CIE-2,:CIE-10,:CIE-2015-2,:CIE-2015-10observersclojure2d.color.whitepoints:whitepointlookup by observer and illuminant; correlated color temperatureCCT->xy(:cie-d,:kang2002),xy->CCT(:cie-d,:mccamy,:hernandez),CCT->uv/uv->CCT(:krystek),CCT->mired,mired->CCT,daylight-locus; light source spectraplanck-law,blackbody-spectrum,illuminant-A-spectrum,illuminant-D-spectrum(ported from colour-science, checked against it)- Docstrings for every public var of
clojure2d.color.whitepoints(including the 359 white point vars) and for->XYZ-to-LMS,->LMS-to-XYZ,->RGB-to-RGB,->spectrum-to-XYZ,wavelength-to-XYZ - Docstrings for
->RGB-to-XYZ,->XYZ-to-RGB,->RGB-to-linear-RGB,->linear-RGB-to-RGB,RGB-to-RGB1,RGB1-to-RGB,XYZ-to-XYZ1,XYZ1-to-XYZ,to-sRGB(including the:sRGBcolorspace semantics),from-sRGB clojure2d.color.rgb: transfer functionsto-/from-BT709,-ROMM(ProPhoto),-SMPTE240M,-BT1886(optional display white/black luminance),-PQ(SMPTE ST 2084, 1.0 = 10000 cd/m²),-HLG(ITU-R BT.2100 OETF); RGB spaces:ITU-R-BT709,:Display-P3,:P3-D65,:DCI-P3,:SMPTE-240M,:ProPhoto-RGB(alias:ROMM-RGB)clojure2d.color.rgb:rgb-space(space lookup, throwsex-infofor an unknown name),rgb-spaces-list; docstrings for the namespace, transfer functions,rgb-data->matrix,rgb-data->map,rgbs- Data generator
utils/whitepoints/gen_resources.py(replacesutils/colors/cie.clj) and CIE D-series basis dataresources/color/illuminant_d_series_basis.edn clojure2d.color.blend: docstrings for the namespace, every per-channel mode (formula, source, argument roles),blends,blends-list,alpha-blending,blend-colors,blend-palettes,blend-gradients,hue,saturation,luminosity,color,ch0-ch2clojure2d.color.blend: per-channel modesw3c-softlight(W3C/CSS soft light),grainextract,grainmerge(GIMP),phoenix,interpolation(pegtop.net), registered inblends(blends-listchanges, seededglitchresults may differ)clojure2d.color.blend: W3C non-separable modesw3c-hue,w3c-saturation,w3c-color,w3c-luminosity(luminance-preserving, as in CSS) and whole-color modesdarker-color,lighter-colorclojure2d.color.blend/blend-fn(mode lookup by name, throwsex-infofor an unknown name) andopacity(mode mixed with the base)clojure2d.color.blend/color-blends: whole-color modes by name (HSB, W3C non-separable, darker/lighter color, channel copies);cssgram/custom-filterresolves whole-color modes through itclojure2d.color.cssgram: filtersbrannanandperpetua(gradient approximated);filter-definitions(filters as data: overlays, filter steps, approximation notes); docstrings for the namespace, every filter,filters,filters-list,custom-filter;custom-filteraccepts the W3C whole-color modes and:darker-color,:lighter-colorclojure2d.color: docstrings for the representation layer (named colors, accessors, setters, AWT colors, clamping, scaling,hue,hue-polar,format-hex,pack,black?) and theColorProtoprotocol; namespace docstring corrected for packed integers and hex stringsclojure2d.color/to-color(and every color function) accepts color names as strings, case-insensitive, for names without a namespace ("red","Dark-Blue"), andfloatarraysclojure2d.color: docstrings for the colorspace conversions (model, source, conventions, ranges;to-,from-, normalized*variants) and a convention table incolorspaces,colorspaces*,colorspaces-list;to-OSA/from-OSAdocstrings no longer swapped; docstrings for the XYZ helpers (XYZ-to-LAB,LAB-to-XYZ,XYZ-to-LUV,LUV-to-XYZ,XYZ-to-HunterLAB,HunterLAB-to-XYZ,XYZ-to-LCH,LCH-to-XYZ,XYZ-to-LCHuv,LCHuv-to-XYZ,XYZ-to-Yxy,Yxy-to-XYZ,XYZ-to-UVW,UVW-to-XYZ,XYZ-to-UCS,UCS-to-XYZ),to-luma-color-hue,from-luma-color-hueclojure2d.color: docstrings for the color differences and contrast (delta-E*,delta-C*,delta-H*,delta-E-HyAB,delta-E*-94,delta-E*-euclidean,delta-C-RGB,delta-D-HCL,delta-E*-CMC,delta-E-z,delta-E*-2000,contrast-ratio,noticeably-different?): formula, source, options and defaults, reference argument;delta-E*-euclideanhad noneclojure2d.color/contrast-ratiooption:method::wcag(default),:lstar(L* difference),:weber(signed, target on background),:michelson; Weber and Michelson on the luminance Y ofto-XYZclojure2d.color: docstrings fortemperature,temperature-names(the named temperatures),adjust-temperatureandwavelengthclojure2d.color: docstrings for the adjustments and filters (whiten,blacken,brighten,darken,saturate,desaturate,adjust,modulate,tinter,correct-luma,apply-theme,color-themes,fe-color-matrix,sepia,contrast,exposure,brightness,saturation,hue-rotate,grayscale, the color vision deficiency filters,complementary,make-LCH): sources (W3C Filter Effects, chroma.js, Machado et al. 2009, Brettel et al. 1997), formulas, rangesclojure2d.color: premultiplied alpha as an option oflerp,lerp-,lerp+,mix,average,weighted-average({:premultiplied? true}after the colorspace,:RGBfor sRGB; as CSScolor-mix()and coloraide; the hue channel is not premultiplied): transparent red and blue give blue at alpha 0.5; straight alpha stays the default;weighted-averagethrows{:alpha-weights-sum 0.0}when negative weights times alphas cancel outclojure2d.color/gradient: option:premultiplied?(premultiplied alpha, as CSS gradients and coloraide; the hue channel is not premultiplied); straight alpha stays the defaultclojure2d.color:random-color,random-palette,random-gradient,apply-themeaccept{:rng r}(a fastmath random generator) for repeatable results; every random choice (also of the no-argumentpaletteandgradient) goes through a fastmath generator (was partlyrand-nth/randof Clojure and the global fastmath generator), a fresh(fastmath.random/rng :default)per call when none is givenclojure2d.color: docstrings for palettes and gradients (gradientwith the list of built-in gradients and their sources,palette,merge-gradients,iq-gradient,gradient-cubehelix,find-gradient,find-palette,random-color,random-palette,random-gradient,paletton,hue-paletton,paletton-presets-list); paletton verified against paletton.com's own color modelclojure2d.color: docstrings for mixing and reduction (nearest-color,average,weighted-average,lerp,lerp-,lerp+,mix,mixbox,mixsub,negate,reduce-colors,resample): formulas, sources (chroma.js, Mixbox, ProfJski ArtColors), rangesclojure2d.color: CSS filtersinvertandopacity(W3C Filter Effects Level 1);cvd-filtersimulating protan / deutan (Machado et al. 2009), tritan (Brettel et al. 1997) and achromat color vision deficiency at any severity in [0, 1] (:protanomaly,:deuteranomaly,:tritanomaly,:achromatomaly), or complete loss by name (:protanopia,:deuteranopia,:tritanopia,:achromatopsia)clojure2d.color/delta-E-HyCH: HyCH color difference (Abasi, Amani Tehran, Fairchild 2020), CIEDE2000 terms with a city-block lightness term; option:textiles?
spectrumand->spectrum-to-XYZ1moved toclojure2d.color.whitepoints;clojure2d.colorkeeps aliases->spectrum-to-XYZ1/->spectrum-to-XYZ::extrapolationaccepts:constant(new default) or:zero;:trimandnilare removed (they threw under fastmath 3.0.0-alpha10) and now throwex-info; unknown observer or illuminant throwsex-info->spectrum-to-XYZ1/->spectrum-to-XYZ::interpolationnow applies to the spectrum only, default:auto(Sprague for uniform data, not-a-knot cubic spline otherwise, as colour-science); new:illuminant-interpolation(default:linear); color matching functions always use:auto- [breaking]
clojure2d.color.blend/luminocityrenamed toluminosity(no alias);cssgramcustom-filteroperator:luminocityrenamed to:luminosity clojure2d.color.cssgram: soft light and hue overlays use the W3C (CSS) modesw3c-softlight(gingham, moon, reyes, slumber, stinson) andw3c-hue(maven), as in browsers; output changes (gingham up to 2.7, maven up to 34 on the 0-255 scale)- [breaking]
clojure2d.color/color(every arity) clamps channels and alpha to[0,255](before: only the 3- and 4-argument forms);to-colorof 1- and 2-element sequences and arrays no longer clamps, like longer ones - [breaking]
clojure2d.color/lumaandrelative-lumause the Y row of theto-XYZsRGB matrix (0.2126740, 0.7151511, 0.0721750) instead of 0.212671, 0.715160, 0.072169:relative-lumaequals the Y ofto-XYZtimes 2.55; values change by up to 0.0023 (0-255), alsoto-Gray(contrast-ratiouses the WCAG weights, see below) - [breaking]
clojure2d.color/to-XYZ,from-XYZ(and every XYZ-based colorspace: LAB, LUV, LCH, HunterLAB, Yxy, UVW, UCS, IPT, IgPgTg, JAB, OSA, DIN99, LMS) use the matrices ofclojure2d.color.rgb/rgbs :sRGB, derived from the sRGB primaries andCIE-2-D65, instead of fixed Lindbloom matrices: white maps exactly to the white point (LAB a*, b* of white 0 instead of -2.7e-4); XYZ changes by up to 2.1e-4 (0-100), LAB and delta E by up to 3.1e-4 - [breaking]
clojure2d.color: nil and unknown color names are not colors:to-color,color, the channel accessors,to-awt-color(and every function converting colors) throwex-infowith{:color c}instead of returning nil or failing later (e.g.core/set-colorwith nil was silently ignored);valid-color?returns false - [breaking]
clojure2d.colorDIN99o applies the 26° hue rotation of DIN 6176 (as culori, coloraide): a, b change by up to about 20 - [breaking]
clojure2d.colorYCbCr, YUV, YIQ use BT.601 luma 0.299, 0.587, 0.114 (was 0.298839, 0.586811, 0.114350 with BT.601 chroma rows); YCbCr chroma rows exact(B - Y) / 1.772,(R - Y) / 1.402; inverses are the exact inverse matrices (round trips exact) - [breaking]
clojure2d.colorYPbPr uses the standard BT.709 scalingPb = (B - Y) / 1.8556,Pr = (R - Y) / 1.5748(Pb, Pr within 127.5; were unscaled differences) - [breaking]
clojure2d.colorOklab family (Oklab, Oklch, Okhsl, Okhsv, Okhwb) uses coloraide's high-precision matrices and gamut coefficients: grays map to a = b = 0, round trips are exact; Okhsl, Okhsv saturation changes by up to 1e-3 - [breaking]
clojure2d.color/from-HCLsolves the Sarifuddin-Missaoui Q exactly (the published inverse estimates it and round-trips within about 0.06);from-XYBandfrom-OSAuse exact inverse matrices - [breaking]
clojure2d.colorDIN99c and DIN99d apply Kuehni's correctionX' = c0 X - (c0 - 1) Z(c0 1.1 and 1.12) to the color and the white before CIELAB (Cui et al. 2002, as colour-science); the docstrings described it, the code skipped it: a, b change by up to about 21 clojure2d.colornormalizedto-X*/from-X*ranges recomputed from sampled extrema (utils/color/gen_normalized_ranges.clj) for the DIN99 family, IPT, IgPgTg, JAB, JCH, LAB, LCH, LCHuv, LUV, HunterLAB, LMS, OSA, the Oklab family, UCS, UVW, YPbPr: normalized values stay within[0,255]and reach 0 and 255- [breaking]
clojure2d.color/contrast-ratiocomputes the relative luminance as WCAG defines it (weights 0.2126, 0.7152, 0.0722) instead ofrelative-luma(Y row of theto-XYZmatrix, unchanged): values change by up to 4.9e-4 relative (blue / red 2.1500 → 2.1489, red / black 5.2535 → 5.2520), grays below 1e-7; pass/fail at 3, 4.5, 7 flips for about 1 in 20000-100000 random pairs - [breaking]
clojure2d.color/temperaturecomputes Mitchell Charity's blackbody colors (Planck spectrum, CIE 1964 10°, sRGB without adaptation) from a table uniform in mired, 500-40000 K, generated with colour-science (resources/color/blackbody_srgb.edn,utils/color/gen_temperature.py), instead of the author's curve fit of the same data: within 0.1 of the exact model (the fit: 1.2 at 2000-6500 K, about 1.8 at 1000 K, 3 at 1900-2000 K); 1000 K (255, 58.2, 0) → (255, 56.4, 0), 2000 K (255, 137.6, 16.9) → (255, 136.6, 18.1); below 1000 K the model instead of an extrapolation, 500 K (255, 0, 0) → (255, 0, 68.6); outside 500-40000 K the ends of the table (50000 K was (152.6, 186.3, 255), now the 40000 K color (155.0, 187.9, 255));adjust-temperatureand the:black-bodygradient follow - [breaking]
clojure2d.color/wavelengthreturns the hue at full brightness: negative linear sRGB channels clipped to 0, the largest channel scaled to 1 (was the clamped sRGB of the XYZ, dark at the ends of the spectrum): 380 nm (3.8, 0, 19.8) → (113.9, 0, 255), 700 nm (48.8, 0, 0) → (255, 0, 0); outside the observer range black as before - [breaking]
clojure2d.color/adjust-temperaturekeeps the CIELAB lightness of the color (lerp-; waslerp+, which gave every color the lightness of the temperature color) and clamps channels to[0,255](were not clamped): (10, 10, 10) toward 3000 K (224, 190, 160) → (34, 2, 0), (30, 60, 200) (198, 187, 260) → (75, 71, 133) - [breaking]
clojure2d.color/temperature(andadjust-temperature) throwsex-infowith{:temperature t}for an unknown name (was aClassCastException), a value that is neither a number nor a keyword, a temperature not above 0 K and NaN (0 K and negative temperatures silently gave pure red) - [breaking]
clojure2d.colorCSS filters follow W3C Filter Effects Level 1:hue-rotateblue-row coefficient 0.072 (was 0.071, grays drifted: gray 128 at 90° (128, 128, 127.9) → (128, 128, 128));saturationweights 0.213, 0.715, 0.072 of the saturate matrix (were 0.2126, 0.7152, 0.0722; (200, 50, 80) at 1.5: (255, 33.0, 78.0) → (255, 32.9, 77.9));grayscaleits own matrix with 0.2126, 0.7152, 0.0722;sepia,grayscaleamounts above 1 clamped to 1 ((sepia 2)(100, 150, 200) (255, 192.8, 67.0) → (192.5, 171.4, 133.5)) - [breaking]
clojure2d.color/whiten,blackenmove W (B) linearlyamtof the way to 1,W + amt (1 - W), or to 0 for negativeamt,W + amt W;amtclamped to [-1, 1] (wasW + amtclamped to [0, 1]): (200, 50, 80)whiten0.2 (200, 101.0, 120.8) → (200, 91.0, 112.8), (200, 150, 160)whiten-0.5 (200, 22.5, 58.0) → (200, 75.0, 100.0) - [breaking]
clojure2d.colorcolor vision deficiency filters from physiological models in linear sRGB (were matrices of colorjack's ColorMatrix via chromelens, applied to encoded sRGB, without a scientific source):protanopia,deuteranopiaMachado et al. (2009) severity 1,protanomaly,deuteranomalyseverity 0.6,tritanopiaBrettel et al. (1997) as DaltonLens,tritanomaly60 % toward it,achromatopsiathe gray of the luminance Y,achromatomalyhalfway to it; (200, 50, 80)protanopia(135.0, 133.7, 72.7) → (91.2, 88.7, 80.2),achromatopsia(98.3, 98.3, 98.3) → (108.5, 108.5, 108.5); dataresources/color/cvd_machado2009.edn,cvd_brettel1997.edn(utils/color/gen_filters.py) - [breaking]
clojure2d.color/gradientinterpolates the hue in colorspaces with a hue along the shorter arc by default (option:hue:shorter,:longer,:increasing,:decreasing,:raw, as CSS Color 4; an achromatic stop takes its neighbour's hue, per segment for:linearas coloraide; was the hue as a number):((gradient [:red :blue] {:colorspace :HSL}) 0.5)(0, 255, 0) → (255, 0, 255),((gradient [:red :gray :blue] {:colorspace :HSL}) 0.75)(64.1, 191.4, 64.1) → (64.1, 64.1, 191.4),((gradient [:orange :blue] {:colorspace :Oklch}) 0.5)(0, 175.6, 103.2) → (228.3, 38.5, 161.5); built-in:cubehelix,:warm,:coolandgradient-cubehelixkeep the hue as a number (:raw, d3); easing gradients limittto [0, 1] as the others - [breaking]
clojure2d.color/to-OSA*maps j and g by255 (0.5 + atan(x / 10) / pi)(was a triple cube root mapped linearly from sampled extrema): j and g are unbounded near the surfaceY0^(1/3) = 2/3(some dark colors,[0 0 45.6]gives j about 1e14, normalized 465), now always inside (0, 255) and reversible withfrom-OSA*(reduced precision only at the singular surface); all normalized OSA j, g values change, e.g. gray 128 (145.9, 126.8, 127.3) - [breaking]
clojure2d.color/gradientlimitstalso to the domain: outside:domainevery interpolation method gives the end colors (non-linear methods extrapolated:((gradient [:red :blue :lime] {:domain [0.2 0.5 0.6] :interpolation :cubic}) 0.9)(0, 255, 255) → (0, 255, 0)); non-numeric domain values throw{:domain d};random-colorthrows{:color-theme t}for an unknown theme (was paletton's{:preset p});palettonthrows{:hue h}for a hue that is not a number (wasNullPointerException) - [breaking]
clojure2d.color/paletteconverts a sequence of colors toVec4(returned it as given:(palette [:red])was[:red]);random-coloralways returns aVec4(a keyword in about 20 % of calls);paletton-presets-listis sorted - [breaking]
clojure2d.color/paletteandgradientthrowex-infowith{:palette k}/{:gradient k}for an unknown name (palettereturned nil,gradientnil orIllegalArgumentExceptionfor a namespaced name);gradientthrows{:domain d}for a domain that is not strictly increasing or not one value per color,{:interpolation k :colors n}for an easing with other than 2 colors (it ignored the rest) or:iqwith other than 2 colors or 4 coefficient vectors (ArityException),{:options o}for options that are not a map and{:colorspace cs}for an unknown colorspace;merge-gradientsthrows{:midpoint m}outside (0, 1) (1 gave NaN at the end);palettewith a number of colors below 1 throws{:number-of-colors n}(returned[]);palettonthrows{:preset p}for an unknown preset (returned[]) and{:type t}for an unknown scheme (IllegalArgumentException) - [breaking]
clojure2d.color/lerp(andlerp-,lerp+) in colorspaces with a hue (HSx, HWB, GLHS, HCL, LCH, LCHuv, Oklch, JCH, Okhsl, Okhsv, Okhwb, Cubehelix) interpolates the hue along the shorter arc and takes the hue of the other color for an achromatic color (CSS, chroma.js, coloraide; was the hue number interpolated linearly, gray hue 0):(lerp :red :blue :HSL 0.5)(0, 255, 0) → (255, 0, 255),(lerp :gray :blue :HSL 0.5)(64.1, 191.4, 64.1) → (64.1, 64.1, 191.4),(lerp [255 0 0] [255 0 200] :Oklch 0.5)(−1091.9, 188.6, 166.2) → (263.5, −49.7, 119.7) - [breaking]
clojure2d.color/average,weighted-averagein colorspaces with a hue use the circular mean of the hues of the chromatic colors; cancelling hues give an achromatic color (coloraide):(average [:red :magenta] :HSL)(0, 255, 127.5) → (255, 0, 127.5),(average [:red :cyan] :HSL)(127.5, 255, 0) → (127.5, 127.5, 127.5) - [breaking]
clojure2d.color/mixwith a colorspace other than:RGBinterpolates linearly in it, as chroma.jsmixin that mode (was square-root mixing of the normalized channels, out of gamut):(mix :red :blue :LAB 0.5)(217.3, −71.8, 82.2) → (201.5, −59.0, 136.5) (chroma.js #ca0088) - [breaking]
clojure2d.color/mixsubfollows ProfJski ArtColorsColorMixSub: RGB distance divided by 255 √3 (was the RGBA distance divided by 510), alpha 255:(mixsub :red :white)(255, 37.3, 37.3) → (255, 23.4, 23.4),(mixsub :blue :yellow)(17.1, 17.1, 17.1) → (0, 0, 0); with a colorspace the formula works on the normalized channels (colorspaces*, 0-255, was the raw channels):(mixsub :red :blue :LAB 0.5)(117.2, 11.9, 80.4) → (89.4, −53.4, 150.1) - [breaking]
clojure2d.color/reduce-colorsuses k-means++ by default (:fuzzy?false, about 20 times faster); accepts any color type;:rngnil lets fastmath create the generator; returns the distinct colors when there are not more of them than requested (was an error) and drops empty clusters; throwsex-infowith{:options o}for options that are not a map (wasClassCastException),{:number-of-colors n}below 1 and{:colors 0}for no colors: viridis to 3 colors (65.0, 54.8, 124.0), (35, 138, 141), (181.3, 220.5, 48.7) → (64.7, 56.0, 124.4), (35, 138, 141), (180.0, 220.2, 49.4) - [breaking]
clojure2d.colorCSS filterssepia,grayscale,saturation,contrast,exposure,invert,opacitythrowex-infowith{:amount a}for a negative amount (W3C Filter Effects: not allowed) orNaN;brightness(offset) andhue-rotateaccept any value;whiten,blackenthrow{:amount a}forNaN - [breaking]
clojure2d.color/complementarythrowsex-infowith{:colorspace cs}for a colorspace other than:PalettonHSV,:HSB,:HSV,:HSL,:HWB,:GLHS(was aNullPointerExceptionfor an unknown name, a wrong channel turned in other spaces::LCHgave (812, 608, 618));make-LCHthrows{:colorspace cs}for an unknown colorspace when created (was aNullPointerExceptionwhen called) clojure2d.color/apply-themethrowsex-infowith{:color-theme t}for an unknown theme name (was an interpolation error)- [breaking]
clojure2d.color/adjust,modulatethrowex-infowith{:colorspace cs}for an unknown colorspace (was aNullPointerException) and{:channel ch}for a channel outside 0-3 (was silently ignored) - [breaking]
clojure2d.color/noticable-different?renamed tonoticeably-different?(no alias); it throwsex-infowith{:s s :p p}for a size:snot positive or a share:poutside (0, 1] (:p0 made identical colors different) - [breaking]
clojure2d.color: deprecated aliasesdelta-c,delta-h,delta-e-cie,delta-e-cmc,delta-e-jabremoved; usedelta-C*,delta-H*,delta-E*,delta-E*-CMC,delta-E-z - [breaking]
clojure2d.colorcolor differences (delta-E*,delta-C*,delta-H*,delta-E-HyAB,delta-E*-94,delta-E*-euclidean,delta-D-HCL,delta-E*-CMC,delta-E-z,delta-E*-2000) andnoticeably-different?throwex-infowith{:colorspace cs}for an unknown:colorspace(was aNullPointerException) and{:options x}when the options are not a map (a positional colorspace or weight was silently ignored, e.g.(delta-E*-euclidean c1 c2 :YUV)measured in Oklab) - [breaking]
clojure2d.color/to-Yxy*,from-Yxy*map x from 0.15-0.64 and y from 0.06-0.60 (the sRGB range) instead of from 0: normalized x, y change by up to 60 and 26
->linear-RGB-to-RGBapplied the decoding transfer function (:from) instead of the encoding one; linear -> RGB conversions were wrong for every space->RGB-to-XYZ,->XYZ-to-RGB,->RGB-to-RGB,->RGB-to-linear-RGB,->linear-RGB-to-RGBwith an unknown space name, or a space map missing required keys, failed with aNullPointerException(some only when called); now throwex-infowith{:space k}or{:missing-keys ks}when the converter is builtclojure2d.color.rgb/rgbs:NTSC(and:ITU-R-BT470-525) used gamma 2.8; now 2.2, the assumed display gamma of ITU-R BT.470 System M; the former curve (as in colour-science) is available as:NTSC-colour- White point values (
resources/color/whitepoints.ednregenerated): clojure2d.color.cssgram/larkoverlay gray 248 instead of CSSgram's 242;willowapplied the invalid CSS::beforeoverlay (ignored by browsers), lacked the::aftercolor overlay rgb(216, 205, 203) and used contrast 0.85 instead of 0.95clojure2d.color.cssgram/custom-filter: a function step replaced all previous steps (now composed); an unknown operator or blend mode was silently ignored or replaced bynormal(now throwsex-infowith{:operator op}or{:blend mode})clojure2d.color.blend/blend-colors,blend-palettes,blend-gradientswith a mode keyword (e.g.:multiply) silently acted asnormal(the keyword was called as a lookup); keywords now resolve throughblend-fn, an unknown one throwsex-infowith{:blend mode}clojure2d.color.blend/ch0,ch1,ch2with an unknown colorspace threw aNullPointerException; nowex-infowith{:colorspace colorspace}clojure2d.color/to-colorread the CSS 4-digit hex#rgbaas a packed number (#f00agave [0 240 10 255]); now red, green, blue, alpha with doubled digitsclojure2d.color/alphaof a packed integer whose top byte is 0 returned 0 whileto-colortreats it as opaque; now 255 (sopackof such an integer keeps it opaque)- named color
:sandy-brownwas#faa460instead of the CSS#f4a460(:sandybrown) clojure2d.color/set-channel,get-channel,color-converterwith an unknown colorspace threw aNullPointerException(or returned nil); nowex-infowith{:colorspace cs}; a channel outside 0-3 was ignored byset-channeland gave NaN inget-channel, nowex-infowith{:channel ch}clojure2d.color/to-coloraccepted hex strings of 5, 7 or more digits as plain numbers and threwNumberFormatExceptionfor non-hex text; now 1, 2, 3, 4, 6 and 8 digits only, otherwiseex-infowith{:color s}clojure2d.color/possible-color?treated a vector of packed RGB integers (e.g.[0xff0000 0x00ff00]) as one color and accepted any seqable (sets, Java collections); a sequential collection or a supported array is a color only with 1-4 numbers within[-255,255].possible-palette?returned true for nil, maps and sets; a palette is now a non-empty sequential collection of possible colorsclojure2d.color/to-Okhsl,to-Okhsvgave white a saturation of 0.558 (no achromatic guard, rounded matrices); black, white and grays now have saturation 0 and hue 0;get-ST-midcoefficients had truncated digitsclojure2d.color/from-HCLreturned NaN for blackclojure2d.color/from-OSAstopped its Newton iteration on a negative residual (signed test); now the absolute residualclojure2d.color/from-JABreturned NaN for colors within rounding noise of blackclojure2d.color/from-UVW(UVW-to-XYZ) returned NaN for W* = 0; the white chromaticity is usedclojure2d.colornormalized colorspaces (to-X*) left[0,255]for some sRGB colors: hue channels were mapped from sampled maxima (e.g. 0 to 359.76 for GLHS: (78.58, 51.12, 51.12) gave 255.16), now from their whole period (0-360, -180-180 for HCL, 0-1 for Okhsl, Okhsv, Okhwb); other channels from extrema refined between grid points (DIN99 family, IPT, IgPgTg, JAB, LUV, UVW, Oklab, overshoot up to 2.3e-4; Okhsl saturation up to 1.0122, Okhsv saturation 1.0120, Okhwb whiteness down to -0.012);to-HSLsaturation of near-white tints rounded above 1 (1.0007), now at most 1clojure2d.color/palettonwith a NaN or infinite hue and the PalettonHSV conversion of NaN threwIllegalArgumentException(no matching clause); nowex-infowith{:hue h}(paletton) and NaN channels (PalettonHSV);mixboxreturned transparent black for NaN, now NaN channels;(gradient :black-body)at NaN threw, now NaN channels;to-color(and channel accessors) of a non-integer number or another unsupported type threw a protocol error, nowex-infowith{:color c}({:color type}for non-numbers)clojure2d.color/gradient:black-bodythrewex-infofortbelow -0.071 (negative temperature), now limitstto [0, 1];:rainbowwrapstoutside [0, 1] as d3interpolateRainbow(was clamped extrapolation)clojure2d.color/gradient,palette,resamplewith a one-color palette threw a fastmath error (25 palettes of the files have one color;random-gradientfailed now and then); now a constant gradient and n copies of the color; no colors throwex-infowith{:colors 0}clojure2d.color/find-palettewith a group threwClassCastException(colourlovers palettes have integer names);find-gradient,find-paletteaccept a string as the regexclojure2d.color/lerp+with two colors kept the lightness of the first color (it calledlerp-); now of the second:(lerp+ :red :blue)(194.0, 82.0, 190.8) → (134.8, 15.3, 134.4)clojure2d.color/nearest-colorthrewClassCastExceptionfor palettes of keywords, strings or vectors with the default distance and returned the input color for an empty palette; entries are now converted withto-colorfor the distance (and returned as given), an empty palette throwsex-infowith{:palette-size 0}; the 1-arity converts the palette once; NaN distances are skipped (every distance NaN throws{:distance :nan}, was the first entry)clojure2d.color/mixboxandreduce-colorsreturned fully transparent results as opaque (the ARGB int with alpha 0 was read as opaque):(mixbox [0 0 255 0] [255 255 0 0])alpha 255 → 0;reduce-colorsfinds the most frequent color without packing itclojure2d.coloraverage,weighted-average,lerp,lerp-,lerp+,mix,mixsub,reduce-colorsthrewNullPointerExceptionfor an unknown colorspace; nowex-infowith{:colorspace cs}.averageof no colors throws{:colors 0}(was a protocol error);weighted-averagethrows{:colors n :weights m}for different counts (extra colors were dropped) and{:weights-sum s}for a sum of weights that is 0 or not finite (gave (0, 0, 0, 0) or ±9.2e15);resamplethrows{:number-of-colors n}below 1 (returned[])clojure2d.color/complementaryadded 180 to the saturation channel (to the lightness for:GLHS) instead of the hue:(complementary [200 50 80] :HSV)gave (200, -35950, -28720), now (50, 200, 170); the default:PalettonHSVgave (200, 0, 40), now (92.5, 186.2, 46.6)clojure2d.color/correct-lumathrew for gradients and palettes with decreasing lightness and for small lightness reversals (fastmath 3 interpolation needs increasing samples); now both directions work, reversals up to 5 % of the total lightness change are smoothed out, larger ones throwex-infowith{:lightness :non-monotonic}; after a reversal at the end the corrected gradient still ends at the last color ([:black :white [245 245 245]]at 1: (254.9, 254.9, 254.9) → (245, 245, 245)); a palette of fewer than 2 colors throws{:lightness :non-monotonic}(was an interpolation error)- D-series
CIE-2-D50,CIE-2-D55,CIE-2-D60,CIE-2-D75,CIE-10-D50,CIE-10-D55,CIE-10-D60,CIE-10-D65,CIE-10-D75are now full precision 1 nm integrations of the CIE spectra (xy change 1e-5 to 3e-5; the former values were the CIE 15:2004 Table T.3 values, 5 nm sums over 380-780 nm, which the new values match to 4 decimals);CIE-2-D65is unchanged; RGB spaces based onCIE-2-D50(Adobe Wide Gamut, Best, Beta, ColorMatch, Don, Ekta Space PS5, ECI v2, Xtreme) change by less than 0.01 on 0-255 - computed (
*) white points recomputed with the fixed integration:*-ISO-SP*were off by about 0.24 in xy,CIE-2015-*-E*were not 1/3, other ISO 7589 entries off by about 8e-4, the rest by about 2e-7
- D-series
clojure2d.color.whitepoints/chromatic-adaptation-matrix(and->XYZ-to-XYZ) failed with aClassCastExceptionmixingRealMatrixandMat3x3:rainbowgradient returned channel values above 255 (now clamped like other gradients)->spectrum-to-XYZ1/->spectrum-to-XYZwith default options (:trimextrapolation) ornilextrapolation threw an exception->spectrum-to-XYZ1/->spectrum-to-XYZintegrated over the wrong range (the intersection or the union of the illuminant, sample and observer ranges); now over the observer's range with the illuminant and sample extended, as CIE 15 (:ISO-SP, 2015 observers with:Ewere wrong)->spectrum-to-XYZ1docstring named the spectrum key:values(the key is:value) and the converter was hinted asVec4(returnsVec3)wavelength-to-XYZ(andwavelength) used CIE 1964 10° for every observer other than:CIE-2; now supports all four observers and throws for an unknown one:cat97chromatic adaptation used a transposed Bradford matrix with a sign error (copied misprint, also in colour-science); now the Bradford matrix as defined for CMCCAT97 / CIECAM97s; the former matrix stays available as:cat97-colour(colour-science compatibility)- An unknown chromatic adaptation method in
chromatic-adaptation-matrix,->XYZ-to-XYZ,->XYZ-to-LMS,->LMS-to-XYZand->RGB-to-RGB(white points differ) failed with an unrelatedIllegalArgumentException(LMS builders only when called); now throwsex-infowith{:method k}when the converter is built clojure2d.color.blend/softburnandmsoftburn: the branch fora + b > 255used0.5 - (1 - a) / binstead of the pegtop formula1 - (1 - a) / (2 * b), so values jumped ata + b = 255clojure2d.color.blend/linearlightandmlinearlighthad the layers swapped (2 * a + b - 1); now Photoshop'sa + 2 * b - 1(same values asstamp,mstamp)clojure2d.color.blend/pinlighthad the layers swapped (clamped the blend into[2a - 1, 2a]); now clamps the base into[2b - 1, 2b]clojure2d.color.blendmodulus modes (madd,mdodge, ...) returned values in(255, 256]; now wrapped with mod 256 and capped at 255 (integer-valued results unchanged)clojure2d.color.blend/nor,nand,xnorwere missing fromblends(andblends-list); registered, soblends-listchanges and seededglitchresults may differclojure2d.color.blend/alpha-blending(andblend-colors,hue,saturation,luminosity,color,ch0-ch2) returnedNaNchannels when both colors were fully transparent; now the blended channels with alpha 0
- [breaking] all shapes creating functions moved to separate namespace:
clojure2d.core.shape
Moved to fastmath 3.x
- White point manipulation namespace
clojure2d.color.whitepoints delta-accepts input without conversion- various conversions related to XYZ and white points
- spectrum <-> XYZ converter
init-fnandclose-fncallbacks for window creation and closingradial-gradient-modeandlinear-gradient-modeadded
- [breaking]
reduce-colorsuses (fuzzy)-kmeans++ and arguments are changed - api for
delta-functions wavelengthconversion uses interpolated color matching function
delta-E*-94- error of calculatingC*var
- Refreshed palettes and gradients
quad-strip
OHTAconversion should coerce input to colorIgPgTgnormalized conversion fixedHCLconversion fixed (uncached case)- eastwood related fixes
custom-eventandfire-custom-eventto allow firing an event from the code
apply-themeto generate random color from a given color and theme definitionwhitenandblackento-XYZ1,from-XYZ1not scaled version (range from 0 to around 1)scale3 or 4 channels by a value (to easier normalize rgb), alsoscale-up,scale-down- new colorspaces from colour:
- more from
Oklabfamily:Okhsv,OkhslandOkhwb DIN99color space family::DIN99,:DIN99b,:DIN99c,:DIN99dand:DIN99oreduce-colorscan accept a distance (as function orfastmath.distanceobject),claransalgorithm is used then (instead ofxmeans).claranspath is very slow though.- path definition accepts winding rule and shape appending
- certain shapes can accept coordinates as vectors
:onrepaintrefresher type for a window, leaves repainting a window for AWT. Good when you want to diplay a static image.:positionargument for window creation, sets location of the window in desired position.mixboxpigment-based color mixing by Secret Weaponsgrid-qr-cellto allow draw grid cells inq,rcoordinate systemclj-kondoconfigs
Oklabshould use linear RGB not on sRGBUCS-OSAcolor space should have different order ofgandjchannels.IPTshould operate on not scaled XYZ- missing palettes from
:nord LDRendererwith filtering should draw on borders- [breaking]
to-lchandfrom-lchrenenamed toto-luma-color-hueandfrom-luma-color-hue#21
LDRendererrevisited- added new
to-pixelmodes:linear?(default:false), whentrueturns offlogscalingsplats?(default:false), whentrueaverages pixel's values only
vibrancyis1.0by default (was0.5)- contrast/saturation/brightness don't affect background now
- changed internal splat representation (as a number from
0.0to1.0)
- added new
show-image(and other functions inclojure2d.extra.utils) uses:onrepaintrefreshing strategy by default.
- Dependency to
smile-mklis removed.openblasseems to be enough for current functionality. This makes a package lighter (1GB -> 250MB).
- Clojure 1.11.0
shapeintroduced (all primitives have additiona shape version) + functions:bounding-box,intersects-rectangle?,contains-point?andcontains-rectangle?shape->path-defandpath-def->shapeto construct path from a definition and backtext-shapereturns shape of the textload-fontloads fonts from a fileload-image,load-svgandload-bytescan load from http URLs now- PalettonHSV, RYB, XYB and Oklab color spaces
- Dictionary of Colour Combinations - colors and palettes (prefix
:docc) - https://github.com/mattdesl/dictionary-of-colour-combinations - Color Vision Deficiency
cvd-lensfunction (a filter) mixsubsubtactive mixingadjustadds a value to color channelcomplementaryto find complementary colorrandom-colorcan select from various presets, also selects colors from palettes and known colorswavelengthreturns color from light wavelength in nmclojure2d.color.cssgramset of instagram-like filters +custom-filterfunction- many more color difference functions
:retinahint added to support high density displays (first attempt, things might not work well)
- LDRednerer is aware of alpha channel now
paletteshould return a vector in every casepathpoints should be any seq (not vector only)possible-color?should recognize String as a valid colorHWBcolorspaces fixed to ensure reversibility- canvas orientation shift
- color difference functions revisited and reorganized
load-url-image- useload-image
- fastmath deps updated
- deps updated
- deps updated
- Introduced cpt-city palettes and gradients
- Introduced paletteer palettes and gradients
- Updated fastmath and batik deps
- Refactor to load gradients and palettes dynamically
- [breaking] due to the number of palettes/gradients,
find-paletteandfind-gradientshould be used to find names.
compose-channelsapplies alpha blending by default when alpha channel processing is turned off.
- internal core optimizations (fn -> macros)
- deps updated
lerp-color interpolator
possible-color?catches gradientiq-gradientwrong interpolation between two colors
- mathematica gradients (thanks to Chris N.)
palettehelper functiontemperatureto convert black-body emissions to colorget-channel,set-channel,modulate- to manipulate channels in selected color spacerelative-luma(WCAG version)- more String as color (single letter, triplets, #rgba)
- correct-lightness for gradient and palette
- deps bumped
- pixels mutating functions are now with
! - [breaking] all blending functions moved to separate namespace and reweritten from the scratch
- [breaking] changed API for certain color functions. Especially for [[palette]] and [[gradient]]
- Signal processing - moved to fastmath
- palette/gradient presets as vars (use [[palette]] or [[gradient]] to access them)
- Closing window during
drawcan cause NPE when accessing global state. HCLis now proper HCL by Sarifuddin and Missaou (to use CIELab version use LCH)brighten/darkenandsaturate/desaturatecorresponds to chroma.js nowmixcorresponds to chroma.js nowcontrast-ratioreflects WCAG definition
:always-on-top?parameter to set window to be on toporient-canvas- axis orientation functionsfilter-channel,filter-channel-xy,blend-channelandblend-channel-xycan accept function as a parameter single parameter (acts a partial)..set-compositeandcompositeto compose drawing with canvas by Java2d, see java.awt.Composite- new window parameter,
:backgroundset panel background color - bounding box for given text
load-url-imageto handle loading from URLpattern-modeto set textured fill for shapes- Gradient density rendering
- BREAKING: by default canvas is fully transparent. Window background is set to white. This is made to match html canvas behaviour.
- BREAKING:
set-backgrounduses SrcOver (default) composite. Previously was Src.
- thi.ng/color dependency
- Bring window to top // (still doesn't work well)
- Headless mode fix
- Blur algorithm rounding (ghosting effect)
- Native color conversion
from-was clumping an input
- grid cells
- hex primitive
- quadtree segmentation now uses low-discrepancy sequence generator
- reconstruction filters spread parameter
- Hann reconstruction filter
- reconstruction filters default values
- refactored renderer, breaking change: intensity is now vibrancy and acts opposite way
- reconstruction filters errors
packcolor into 32 bit integerresizePixels- 4 new color spaces: IPT, LMS, JAB, JCH
- new palettes and gradient
- new color distance based on JAB
- list of all predefined colors, palettes and gradients have separate docs
reduce-colors- selects color from cluster usingmoderather than centroid- html-color* -> named-color*
Pixelscan be treated as sequence now (sequence of colors)reduce-colorsusing x-means clustering algorithm
iq-gradientaccepts also two colors (https://github.com/thi-ng/color/blob/master/src/gradients.org#gradient-coefficient-calculation)load-svgandtranscode-svgadded
merge-rendererscan merge any number of renderers (previously only two).iq-random-gradientalso usesiq-gradientfor two colors generated by paletton generatorrandom-gradientuses also iq-random-gradient functionfilled-with-strokeis macro now- removed dependency to
Vec2in some functions inclojure2d.core
random-gradientwill use only two interpolators (:cubic and :linear) to avoid exceed of the range
- arc and rarc (radius arc) functions
- spots overlay - number of spots heuristic changed
Please note - breaking changes!
- key-pressed? mouse-pressed? (and other event values) functions available in
draw-fn(via Window protocols) - lerp in color (to interpolate colors)
- set-stroke-custom - to set custom stoke pattern
- to-XXX* / from-XXX* are now normalized (0-255) colorspace conversion functions.
- color distances
- color interpolators
- color preset palettes (Brewer, Tableaus, Microsoft)
- color gradients
- color tweaking functions
- font-ascent
- thi.ng rgba interoperability
- project dependencies to external
generateme/fastmathlibrary - to-luma -> luma, get-hue -> hue
- hue returns angle (0 - 360)
- make-color, make-awt-color, make-canvas, make-2d-...-array changed to names without
make-prefix - make-pixels -> pixels
- make-effect* -> effect
- almost all make- in raymarching are changed to version without prefix
resize-imageandresize-canvasare now moved to ImageProto asresize- xor-mode, gradient-mode are reset with paint-mode
- to-XXX/from-XXX in colorspaces are raw conversions now
- YXY renamed to Yxy
- make-color-converter -> color-converter
- paletton-palette -> paletton
- signal processing refactored, api changed
- overlays refactored
- glitch scripts refectored, api changed
Prefix make- is reserved when function is created.
- set-font works now
- math namespaces moved to
generateme/fastmathproject - examples moved to separate repository
with-canvasis no longer threading macro (usewith-canvas->for that):setupparameter added toshow-window. It's run before displaying window. Parameters are: canvas (within context) and window. When returned value is notnilit's used as initial state. In other case:draw-stateparameter is used.- color can be represented as int (eg. in hex form like: 0x234344)
clamp255returns double, for long version calllclamp255- bezier and curve functions added
- date/time functions (year, month, day, hour, minute, sec, millis, nanos)
- new function
filled-with-stroke- to draw primitive with stroke and filled load-bytesadded- general key-event added (like mouse-event). You can now dispatch also on event type not only key char or code
- background supports transparency
- screen-width and screen-height functions
- threshold filter wrong values
drawnow displays exception when something is wrong- window canvas was in the bad size causing visual artifacts
- more accurate frame delay calculations
with-canvasis renamed towith-canvas->:highesthint added (it's:highwith Stroke Pure)- :right, :left or :center alignments for text (last parameter, default :left)
rendering-hintsis map of vectors now, it gave 25% speed gain during recreating graphics context
- Vcf303, Echo and BassTreble signal processing effects + example 16 updated
- key-released and key-typed events added
- fractal bestiary explorer - example 37
- path-bezier - bezier spline
- text and font functions
- Java2D convolution filters (via (convolve ...)), returning image
- Expectations Tests
- ArrayVec type - doubles array vector representation (for speed gain)
- Overlays API changed to image + configuration map
- Switched from clojure math to primitive math library
- FastMath bindings changed to macros for several methods
- Saving picture moved to image protocol, replaced save-canvas and save-pixels removed (call (save canvas name)).
- KeyEvent protocols
- vector scale -> set-mag
- fixes after tests
- rgb scanlines bad tint values
- subpixel accuracy for strokes included in :high canvas profile
- New overlay - crt scanlines
- Events and Window carry global state,
show-windowparameters reorganized.
- Canvas transformations (transpose, rotate, scale, push/pop matrix)
- Image protocol (get-image, width and height fns) for Window, Canvas, image, Pixels
- Mouse position protocol to Window and MouseEvent
- Nature of Code examples
- html 140 colors defined in color namespace (access by (c/to-color :name) or (set-color canvas :name))
- point and line drawing function accept Vec2 coordinates as well
- more variations
- clojure2d temporary icon
- Canvas as a type
- Window as a type (record)
- Helper functions to check if window is visible
- Helper function to get image from canvas and window
- Color manimulations refactored (awt vs vec4)
- Complex type is Vec2 now
- Session management refactored
- Draw function accepts also Window (to access mouse position)
- Examples revisited
- Signal processing optimisations - speed gain 2x
- Core:
- session management functions
- Pixels:
- accumulation bins rendering support
- Random:
- random and low discrepancy sequence generators (binding to apache commons math)
- Color:
- color converters
- palette generators (paletton style, colourlovers 200 palettes, iq generator)
- Extra:
- glitch functions - slitscan, mirror, color redutions, bleding
- raymarching - basic raymarching
- signal - signal processing for sonification (pixels to raw, raw to pixels converters, wave generators)
- Examples: currently 32 examples
- A lot of changes and huge number of code optimizations (for speed)
- Draw function behaviour
- Canvas wrapper (Graphics2d allocation and release)
- Mouse moved event
- JPanel -> java.awt.Canvas and custom paint function
- Vectors as Seq + PersistentVector enhanced (VectorProto)
- filters changed to signal ns
Bugs, bugs, bugs + optimization for speed
- Initial release
- Core:
- image load/save
- display window
- basic canvas operations
- Pixels:
- operate on pixel and channel level
- basic filter
- blending functions
- Math:
- jafama FastMath lib
- random functions based on Apache Commons Math
- noise functions based on flow-noise and joise
- statistic functions
- Complex type
- Color:
- various blending methods
- Extra, place for common functions used in glitch/generative process
- filters.clj - analog filters (for sonification)
- overlays.clj - rgb scanlines, noise, spots
- segmentation.clj - divide image into squares
- variations.clj - fractal flames variation libarary (vector fields)
- Examples: 13 examples