Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file Color.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278(** [Color] encodes ANSI colors.
It comes in three flavors:
- 4-bit ([Minimal])
- 8-bit ([Advanced])
- 24-bit ([Rgb]) *)openUtilmoduleGround=struct(** [Ground] encodes the information on whether a certain
color is on the foreground or the background. *)typet=|Foreground|Background(** [to_int ?bright ground] produces the corresponding leading
digit for an SGR escape sequence to be set as foreground
or background. *)letto_int?(bright:bool=false)(ground:t):int=(* We could also match only on [ground] and add 6 when
[bright] is true, but I like to avoid arithmetic code when
possible as it makes it more obscure - constants are easier
to reason about and less error prone. *)matchground,brightwith|Foreground,false->3|Background,false->4|Foreground,true->9|Background,true->10;;endletforeground=Ground.Foregroundletbackground=Ground.BackgroundmoduleMinimal=struct(** [Minimal] encodes the 8 default ANSI colors. *)typet=|Black|Red|Green|Yellow|Blue|Magenta|Cyan|White(** [to_int color] produces the corresponding ANSI SGR code
of the [color], which is a value between 0 and 7. *)letto_int:t->int=function|Black->0|Red->1|Green->2|Yellow->3|Blue->4|Magenta->5|Cyan->6|White->7;;endtypet=(* Minimal could also be a tuple, but I like the explicitness
of records. *)|Minimalof{color:Minimal.t;bright:bool}|AdvancedofInt8.t(* Here, I don't think a record is needed - the order of the
channels are literally given out by the constructor's name. *)|RgbofInt8.t*Int8.t*Int8.t(* It's handy for the user to have module-level constants for
each minimal color. *)(** Default black color. *)letblack:t=Minimal{color=Minimal.Black;bright=false}(** Default red color. *)letred:t=Minimal{color=Minimal.Red;bright=false}(** Default green color. *)letgreen:t=Minimal{color=Minimal.Green;bright=false}(** Default yellow color. *)letyellow:t=Minimal{color=Minimal.Yellow;bright=false}(** Default blue color. *)letblue:t=Minimal{color=Minimal.Blue;bright=false}(** Default magenta color. *)letmagenta:t=Minimal{color=Minimal.Magenta;bright=false}(** Default cyan color. *)letcyan:t=Minimal{color=Minimal.Cyan;bright=false}(** Default white color. *)letwhite:t=Minimal{color=Minimal.White;bright=false}(** Default bright black (gray) color. *)letbright_black:t=Minimal{color=Minimal.Black;bright=true}(** Default bright red color. *)letbright_red:t=Minimal{color=Minimal.Red;bright=true}(** Default bright green color. *)letbright_green:t=Minimal{color=Minimal.Green;bright=true}(** Default bright yellow color. *)letbright_yellow:t=Minimal{color=Minimal.Yellow;bright=true}(** Default bright blue color. *)letbright_blue:t=Minimal{color=Minimal.Blue;bright=true}(** Default bright magenta color. *)letbright_magenta:t=Minimal{color=Minimal.Magenta;bright=true}(** Default bright cyan color. *)letbright_cyan:t=Minimal{color=Minimal.Cyan;bright=true}(** Default bright white color. *)letbright_white:t=Minimal{color=Minimal.White;bright=true}(* It's also useful for the user to have module-level functions
to easily create colors without knowing the intricacies and
details of their implementation. *)(** [make_minimal ?bright value] creates a minimal color.
If [value] is not a valid color code, it returns [None].
A valid color code is an integer i where 0 <= i <= 7. *)letmake_minimal?(bright:bool=false):int->toption=function|0->Some(Minimal{color=Minimal.Black;bright})|1->Some(Minimal{color=Minimal.Red;bright})|2->Some(Minimal{color=Minimal.Green;bright})|3->Some(Minimal{color=Minimal.Yellow;bright})|4->Some(Minimal{color=Minimal.Blue;bright})|5->Some(Minimal{color=Minimal.Magenta;bright})|6->Some(Minimal{color=Minimal.Cyan;bright})|7->Some(Minimal{color=Minimal.White;bright})|_->None;;(** [make_minimal_exn ?bright value] creates a minimal color.
If [value] is not a valid color code, it raises a [Failure]
exception.
A valid color code is an integer i where 0 <= i <= 7. *)letmake_minimal_exn?(bright:bool=false)(value:int):t=matchmake_minimal~brightvaluewith|None->failwith"value must be an integer between 0 and 7 (both included)"|Somecolor->color;;(** [make_advanced value] creates an advanced color.
If [value] is not a valid color code, it returns [None].
A valid color code is an integer i where 0 <= i < 256. *)letmake_advanced(value:int):toption=Option.map(fun(value:Int8.t)->Advancedvalue)(Int8.of_intvalue);;(** [make_advanced_exn value] creates an advanced color.
If [value] is not a valid color code, it raises a [Failure]
exception.
A valid color code is an integer i where 0 <= i < 256. *)letmake_advanced_exn(value:int):t=Advanced(Int8.of_int_exnvalue)moduleChannel=struct(** Represents an RGB channel - either red, green, or blue. *)typet=|Redofint|Greenofint|Blueofint(** [name channel] returns the name of the [channel]. *)letname:t->string=function|Red_->"red"|Green_->"green"|Blue_->"blue";;(** [value channel] retreives the underlying value of the
[channel]. *)letvalue:t->int=function|Redvalue->value|Greenvalue->value|Bluevalue->value;;(** [to_int8 channel] tries to convert the [channel]'s value
into a [Int8] value.
If it fails, it returns the channel data wrapped in the
[Error] variant. This is because this function is often
applied in batch to every channel of an RGB component, so
the user can trace which channel's value was invalid. *)letto_int8(channel:t):(Int8.t,t)result=Option.to_result~none:channel(Int8.of_int(valuechannel));;(** [red value] creates a [Red] channel. *)letred(value:int):t=Redvalue(** [green value] creates a [Green] channel. *)letgreen(value:int):t=Greenvalue(** [blue value] creates a [Blue] channel. *)letblue(value:int):t=Bluevalueend(** [make_rgb red green blue] creates an RGB color.
If any of [red], [green] or [blue] is not a valid channel
value, it returns an [Error] which indicates which channel
had an invalid value.
A valid channel value is an integer i where 0 <= i < 256.
For the version that returns an [option] instead, see
[make_rgb_opt]. *)letmake_rgb(red:int)(green:int)(blue:int):(t,Channel.t)result=(* We convert each channel independently by mapping them to
some specialized type so we can extract the information of
what the first channel with an incorrect value was.
That information is used for example in [make_rgb_exn]. *)(red,green,blue)|>Triplet.mapChannel.redChannel.greenChannel.blue|>Triplet.map_uniform~func:Channel.to_int8|>Triplet.all_ok|>Result.map(fun(r,g,b)->Rgb(r,g,b));;(** [make_rgb_opt red green blue] creates an RGB color.
If any of [red], [green] or [blue] is not a valid channel
value, it returns [None].
A valid channel value is an integer i where 0 <= i < 256.
For the version that returns a result with the invalid
channel data, see [make_rgb]. *)letmake_rgb_opt(red:int)(green:int)(blue:int):toption=Result.to_option(make_rgbredgreenblue);;(** [make_rgb_exn red green blue] creates an RGB color.
If any of [red], [green] or [blue] is not a valid channel
value, it raises a [Failure] exception.
A valid channel value is an integer i where 0 <= i < 256. *)letmake_rgb_exn(red:int)(green:int)(blue:int):t=matchmake_rgbredgreenbluewith|Okcolor->color|Errorchannel->failwith(Printf.sprintf"channel %s has the incorrect value %d (must be between 0 and 255)"(Channel.namechannel)(Channel.valuechannel));;(** [to_ansi color] produces an SGR escape portion that can be
embedded in a string based on the [color]. *)letto_ansi~(ground:Ground.t):t->string=function|Minimal{color;bright}->Printf.sprintf"%d%d"(Ground.to_int~brightground)(Minimal.to_intcolor)|Advancedcolor->Printf.sprintf"%d8;5;%d"(Ground.to_intground)(Int8.to_intcolor)|Rgb(r,g,b)->Printf.sprintf"%d8;2;%d;%d;%d"(Ground.to_intground)(Int8.to_intr)(Int8.to_intg)(Int8.to_intb);;