<> <> <> <> <> <> <> DIRECTORY Buttons, CD, CDColors, CDIO, CDMenus, CDSequencer, NMos, ColorMap, ColorWorld, Commander, Containers, Convert, GraphicsBasic, GraphicsColor, Icons, Labels, MessageWindow, Process, Real, Rope, Sliders, Terminal, TerminalIO, UserProfile, ViewerOps, ViewerClasses, WindowManager; NMos8BitColors: CEDAR PROGRAM IMPORTS Buttons, CDColors, CDIO, CDMenus, CDSequencer, NMos, ColorMap, ColorWorld, Commander, Containers, Convert, Icons, Labels, MessageWindow, Process, Real, Rope, Sliders, Terminal, TerminalIO, UserProfile, ViewerOps, WindowManager = BEGIN <<>> <<--**Patterns**************************************>> Init8Patterns: Buttons.ButtonProc = <<--all parameter are discarded >> BEGIN green1: CARDINAL = 1; yellow: CARDINAL = 2; blue: CARDINAL = 4; pinktr: CARDINAL = 8; red: CARDINAL = 16; black: CARDINAL = 255; Checker8: PROC[col1, col2: CARDINAL] RETURNS[REF CDColors.Brick] = BEGIN RETURN[NEW[CDColors.Brick _ [col1*256+col2, col2*256+col1, col1*256+col2, col2*256+col1]]] END; DefineDimmer8: PROC[lev: CD.Layer, color: CARDINAL] = BEGIN CDColors.DefineColor[lev, Checker8[color, 0], bit8, back]; END; Full8: PROC[color: CARDINAL] RETURNS [b: REF CDColors.Brick] = BEGIN RETURN[Checker8[color, color]] END; CDColors.DefineColor[NMos.dif, Full8[green1], bit8]; CDColors.DefineColor[NMos.met, Full8[blue], bit8]; CDColors.DefineColor[NMos.met2, Full8[pinktr], bit8]; CDColors.DefineColor[NMos.pol, Full8[red], bit8]; CDColors.DefineColor[NMos.cut, Full8[black], bit8]; CDColors.DefineColor[NMos.imp, Full8[yellow], bit8]; CDColors.DefineColor[NMos.imp0, Checker8[yellow, 0], bit8]; CDColors.DefineColor[NMos.impWeak, Checker8[yellow, 0], bit8]; CDColors.DefineColor[NMos.cut2, Checker8[black, blue], bit8]; CDColors.DefineColor[NMos.bur, Checker8[black, 0], bit8]; CDColors.DefineColor[NMos.nwelCont, Checker8[green1, blue], bit8]; CDColors.DefineColor[NMos.ovg, Checker8[blue, pinktr], bit8]; CDColors.DefineColor[NMos.nwel, NEW[CDColors.Brick_[14, 13, 0, 0]], bit8]; CDColors.DefineColor[NMos.nwelCont, NEW[CDColors.Brick_[14, 14, 5, 0]], bit8]; CDColors.DefineColor[CD.backGround, NEW[CDColors.Brick _ [8, 0, 256*8, 0]], bit8]; CDColors.DefineColor[CD.highLightError, NEW[CDColors.Brick _ [255, 0, 0, 0]], bit8]; CDColors.DefineColor[CD.highLightShade, NEW[CDColors.Brick_[0, 255,0 , 255]], bit8]; DefineDimmer8[NMos.dif, green1]; DefineDimmer8[NMos.met, blue]; DefineDimmer8[NMos.met2, pinktr]; DefineDimmer8[NMos.pol, red]; DefineDimmer8[NMos.imp, yellow]; DefineDimmer8[NMos.cut, black]; DefineDimmer8[NMos.cut2, black]; <<-- no change for bur, nwelCont, pwelCont, and ovgc for this background>> END; <<>> <<--**end patterns**************************************>> Color: TYPE = RECORD[r, g, b, weight: REAL _ 0]; ColorDescRec: TYPE = ARRAY [0..7) OF Color; ColorDesc: TYPE = REF ColorDescRec; color: ColorDesc = NEW[ColorDescRec]; <<-- The weights associated with every primary layer color: think of them as the transparency of each layer. The color of an intersection (i.e. green1 over blue) is the weighted sum of the rgb of each color. These weights are also optionally loaded from profile and editable by the user.>> indLayer: CARDINAL _ 1; previndLayer: CARDINAL _ 1; -- to deselect the previous layer button <<>> CoefSlider: TYPE = RECORD [ value: Labels.Label _ NIL, select: Buttons.Button _ NIL, bar: Sliders.Slider _ NIL ]; ColorSlider: TYPE = RECORD [ value: Labels.Label _ NIL, bar: Sliders.Slider _ NIL ]; Handle: TYPE = REF AdjustColorRec; AdjustColorRec: TYPE = RECORD [ outer: Containers.Container _ NIL, coefLayer: ARRAY [0..7) OF CoefSlider, potarColor: ARRAY [0..3) OF ColorSlider, height: CARDINAL _ 0 -- height measured from the top of the container ]; DataForSpecialProc: TYPE = REF DataForSpecialProcRec; DataForSpecialProcRec: TYPE = RECORD [ my: Handle, layer: CARDINAL ]; ColorModeIs8: PROC [] RETURNS [BOOL] = INLINE BEGIN virtual: Terminal.Virtual = Terminal.Current[]; RETURN [virtual.hasColorDisplay AND ~virtual.GetColorMode[].full AND virtual.GetColorMode[].bitsPerPixelChannelA=8]; END; caption: Rope.ROPE = "nmos 8 bit color editor"; Check8BitMode: PROC [] RETURNS [ok: BOOL] = BEGIN ok _ ColorModeIs8[]; IF ~ok THEN TRUSTED{Process.Detach[FORK TerminalIO.WriteRope["Put the display in 8 bit per pixel mode first\n"]]}; END; UpdateWeightSlider: Sliders.SliderProc = BEGIN datac: DataForSpecialProc _ NARROW[clientData]; my: Handle _ datac.my; layer: CARDINAL _ datac.layer; IF ~ColorModeIs8[] THEN RETURN; color[layer].weight _ value; ComputeColors[]; Labels.Set[my.coefLayer[layer].value, Convert.RopeFromInt[Real.Fix[value*100]]]; END; UpdateColorSlider: Sliders.SliderProc = BEGIN data: DataForSpecialProc _ NARROW[clientData]; my: Handle _ data.my; layer: CARDINAL _ data.layer; IF ~ColorModeIs8[] THEN RETURN; IF indLayer = 7 THEN RETURN; -- channel hack Labels.Set[my.potarColor[layer].value, Convert.RopeFromInt[Real.Fix[value*100]]]; SELECT layer FROM 0 => color[indLayer-1].r _ value; 1 => color[indLayer-1].g _ value; 2 => color[indLayer-1].b _ value; ENDCASE=> ERROR; ComputeColors[]; END; ResetButton: Buttons.ButtonProc = BEGIN Init8Patterns[parent: parent, clientData: clientData]; ResetMapButton[parent: parent, clientData: clientData]; END; ResetAndCreateButton: Buttons.ButtonProc = BEGIN viewer: ViewerClasses.Viewer _ ViewerOps.FindViewer[caption]; IF viewer=NIL THEN { IF ColorWorld.HasMode[8] AND ~ColorModeIs8[] THEN { pos: WindowManager.ScreenPos _ left; IF Rope.Equal["right", UserProfile.Token[key: "ColorDisplay.Side"], FALSE] THEN pos _ right; WindowManager.StartColorViewers[screenPos: pos, bitsPerPixel: 8]; }; ResetButton[parent: parent]; IF mouseButton=blue THEN CreateAnInstance[]; } ELSE { <<--we do only open the viewer, >> <<--because resetting can not reset already painted sliders... >> IF viewer.iconic THEN ViewerOps.OpenIcon[viewer] ELSE MessageWindow.Append["use the color editor tool", TRUE]; }; END; ResetMapButton: Buttons.ButtonProc = <<--Resets color map;>> <<--does not touch patterns>> BEGIN UpdateAllSliders: PROC [my: Handle] = BEGIN w: Sliders.NormalizedSliderValue; FOR layer: CARDINAL IN [0..7) DO w _ color[layer].weight; Sliders.SetContents[my.coefLayer[layer].bar, 0]; Sliders.SetContents[my.coefLayer[layer].bar, w]; Labels.Set[my.coefLayer[layer].value, Convert.RopeFromInt[Real.Fix[color[layer].weight*100]]]; ENDLOOP; ButtonProcLayerInternal[0, my]; END; <<>> <<--ResetMapButton>> IF Check8BitMode[] THEN { SetUserColor[]; ComputeColors[]; IF clientData#NIL THEN UpdateAllSliders[NARROW[clientData, Handle]]; }; END; ButtonProcLayer: Buttons.ButtonProc = BEGIN data: DataForSpecialProc _ NARROW[clientData]; IF Check8BitMode[] THEN { IF data.layer = 6 THEN RETURN; -- (transistor channel hack) ButtonProcLayerInternal[data.layer, data.my]; } END; ButtonProcLayerInternal: PROC[layer: CARDINAL, my: Handle] = BEGIN SetPotar: PROC[layer: CARDINAL, my: Handle] = BEGIN IF layer = 6 THEN RETURN; -- channel hack Sliders.SetContents[my.potarColor[0].bar, color[layer].r]; Sliders.SetContents[my.potarColor[1].bar, color[layer].g]; Sliders.SetContents[my.potarColor[2].bar, color[layer].b]; Labels.Set[my.potarColor[0].value, Convert.RopeFromInt[Real.Fix[color[layer].r*100]]]; Labels.Set[my.potarColor[1].value, Convert.RopeFromInt[Real.Fix[color[layer].g*100]]]; Labels.Set[my.potarColor[2].value, Convert.RopeFromInt[Real.Fix[color[layer].b*100]]]; END; <<>> <<-- ButtonProcLayerInternal>> indLayer _ layer+1; SetPotar[layer, my]; Buttons.SetDisplayStyle[my.coefLayer[previndLayer-1].select, $BlackOnWhite]; Buttons.SetDisplayStyle[my.coefLayer[indLayer-1].select, $WhiteOnBlack]; previndLayer _ indLayer; END; CreateAnInstance: PROC[] = <<--create an icon of the tool;>> <<--does not change any color setup at initialization of the tool>> BEGIN nameLayerButton: ARRAY [0..7) OF Rope.ROPE _ ["dif", "imp", "m1", "m2", "pol", "sub", "tr"]; nameColorSlider: ARRAY [0..3) OF Rope.ROPE _ ["R", "G", "B"]; coefX: CARDINAL = 10; coefDX: CARDINAL = 25; coefDeltaX: CARDINAL = coefDX+4; coefSelectY: CARDINAL = 5; coefSelectDY: CARDINAL = 15; coefValueY: CARDINAL = coefSelectY+coefSelectDY+3; coefValueDY: CARDINAL = 15; coefBarY: CARDINAL = coefValueY+coefValueDY; firstButtonY: CARDINAL = 10; buttonDY: CARDINAL = 15; interButtonY: CARDINAL = 3; buttonX: CARDINAL = coefX+7*coefDeltaX+15; buttonDX: CARDINAL = 80; firstSliderY: CARDINAL = 10; sliderDY: CARDINAL = 20; interSliderY: CARDINAL = 5; labelX: CARDINAL = buttonX+buttonDX+10; -- R, G, B labelDX: CARDINAL = 14; label2X: CARDINAL = labelX+labelDX; -- number label2DX: CARDINAL = 30; sliderX: CARDINAL = label2X+label2DX; my: Handle = NEW[AdjustColorRec]; IF ~ColorModeIs8[] THEN TerminalIO.WriteRope["The nmos 8 bit color tool works only after you put the display in 8 bit mode\n"]; IF ViewerOps.FindViewer[caption]#NIL THEN { TerminalIO.WriteRope[caption]; TerminalIO.WriteRope[" viewer already exits\n"]; RETURN }; my.outer _ Containers.Create[[ name: caption, iconic: TRUE, icon: Icons.NewIconFromFile[CDIO.GetWorkingDirectory[NIL].Cat["Chipndale.icons"], 2], column: left, scrollable: FALSE ]]; my.height _ firstSliderY+3*(sliderDY+interSliderY); <<-- create sliders for weights>> FOR indLayer: CARDINAL IN [0..7) DO wInit: REAL _ color[indLayer].weight; my.coefLayer[indLayer].select _ Buttons.Create[ info: [ name: nameLayerButton[indLayer], iconic: TRUE, wx: coefX+indLayer*coefDeltaX, wy: coefSelectY, wh: coefSelectDY, ww: coefDX, parent: my.outer, border: TRUE], proc: ButtonProcLayer, clientData: NEW[DataForSpecialProcRec _ [my: my, layer: indLayer]] ]; my.coefLayer[indLayer].value _ Labels.Create[ [ name: Convert.RopeFromInt[Real.Fix[wInit*100]], -- initial contents iconic: TRUE, wx: coefX+indLayer*coefDeltaX, wy: coefValueY, wh: coefValueDY, ww: coefDX, parent: my.outer, border: TRUE ]]; my.coefLayer[indLayer].bar _ Sliders.Create[ info: [ name: nameLayerButton[indLayer], iconic: TRUE, parent: my.outer, wx: coefX+indLayer*coefDeltaX, wy: coefBarY, wh: - 5, --guardY ww: coefDX, border: TRUE], sliderProc: UpdateWeightSlider, orientation: vertical, value: wInit, clientData: NEW[DataForSpecialProcRec _ [my: my, layer: indLayer] ]]; Containers.ChildYBound[my.outer, my.coefLayer[indLayer].bar]; ENDLOOP; <<>> <<-- create command buttons>> [] _ Buttons.Create[ info: [name: "reset all", wx: buttonX, wy: firstButtonY, wh: buttonDY, ww: buttonDX, parent: my.outer, border: TRUE], proc: ResetButton, clientData: my, fork: TRUE ]; [] _ Buttons.Create[ info: [name: "reset map", wx: buttonX, wy: firstButtonY+buttonDY+interButtonY, wh: buttonDY, ww: buttonDX, parent: my.outer, border: TRUE], proc: ResetMapButton, clientData: my, fork: TRUE ]; [] _ Buttons.Create[ info: [name: "reset pattern", wx: buttonX, wy: firstButtonY+2*(buttonDY+interButtonY), wh: buttonDY, ww: buttonDX, parent: my.outer, border: TRUE], fork: TRUE, proc: Init8Patterns ]; <<>> <<-- create sliders for rgb colors>> FOR iColor: CARDINAL IN [0..3) DO cInit: REAL _ SELECT iColor FROM 0 => color[indLayer-1].r, 1 => color[indLayer-1].g, 2 => color[indLayer-1].b, ENDCASE => ERROR; [] _ Labels.Create[ info: [name: nameColorSlider[iColor], parent: my.outer, wx: labelX, wy: firstSliderY+iColor*(sliderDY+interSliderY), ww: labelDX, wh: sliderDY, border: TRUE ]]; my.potarColor[iColor].bar _ Sliders.Create[ info: [parent: my.outer, wx: sliderX, wy: firstSliderY+iColor*(sliderDY+interSliderY), ww: 10, wh: sliderDY], sliderProc: UpdateColorSlider, orientation: horizontal, value: cInit, clientData: NEW[DataForSpecialProcRec _ [my: my, layer: iColor]] ]; Containers.ChildXBound[my.outer, my.potarColor[iColor].bar]; my.potarColor[iColor].value _ Labels.Create[[ name: Convert.RopeFromInt[Real.Fix[cInit*100]], -- initial contents wx: label2X, wy: firstSliderY+iColor*(sliderDY+interSliderY), ww: label2DX, wh: sliderDY, parent: my.outer, border: TRUE ]]; ENDLOOP; ViewerOps.SetOpenHeight[my.outer, my.height]; -- hint our desired height ViewerOps.PaintViewer[my.outer, all]; -- reflect above change Buttons.SetDisplayStyle[my.coefLayer[0].select, $WhiteOnBlack]; END; SaturationComm: CDSequencer.CommandProc = BEGIN valueLayer: CARDINAL _ 1; IF Check8BitMode[] THEN { indLayer _ TerminalIO.RequestSelection[ label: "Layers", choice: LIST["diff", "imp", "met1", "met2", "poly", "substrate", "channel", "from profile"] ]; IF indLayer = 0 THEN RETURN[]; IF indLayer < 8 THEN BEGIN valueLayer _ TerminalIO.RequestSelection[ label: "Values", choice: LIST["Invisible", "1", "2", "3", "4", "5", "6", "7", "8", "9", "Saturated"] ]; IF valueLayer = 0 THEN RETURN[]; color[indLayer-1].weight _ Real.Float[valueLayer-1]/10; END ELSE SetUserColor[]; ComputeColors[]; }; END; ComputeColors: PROC[] = <<--takes a color setting (weights and rgb values) and sets up the device>> BEGIN SetMap: PROC[index: CARDINAL, color: Color] = { ColorMap.SetRGBColor[index, color.r, color.g, color.b] }; Adjust: PROC [value, x: REAL] RETURNS [REAL] = { RETURN[IF x>0 THEN value+((1-value)*x) ELSE value+(value)*x] }; ComposeColor: PROC[c1: Color, ind: CARDINAL] RETURNS [c: Color] = { c _ [ c1.r+color[ind].weight*color[ind].r, c1.g+color[ind].weight*color[ind].g, c1.b+color[ind].weight*color[ind].b, c1.weight+color[ind].weight] }; c: Color; indBackgnd: CARDINAL; IF ~ColorModeIs8[] THEN RETURN; <<-- we need a better way, using Basics.* or similar to access the bits of the index>> FOR i0: CARDINAL IN [0..2) DO FOR i1: CARDINAL IN [0..2) DO FOR i2: CARDINAL IN [0..2) DO FOR i3: CARDINAL IN [0..2) DO FOR i4: CARDINAL IN [0..2) DO <> c _ [0, 0, 0, 0]; <<-- compute the weighted sum of colors>> IF i0=1 THEN {c _ ComposeColor[c, 0]}; IF i1=1 THEN {c _ ComposeColor[c, 1]}; IF i2=1 THEN {c _ ComposeColor[c, 2]}; IF i3=1 THEN {c _ ComposeColor[c, 3]}; IF i4=1 THEN {c _ ComposeColor[c, 4]}; <> indBackgnd _ 5; IF c.weight=0 OR color[indBackgnd].weight>=20 THEN c _ ComposeColor[c, indBackgnd]; IF c.weight#0 THEN c _ [c.r/c.weight, c.g/c.weight, c.b/c.weight, c.weight]; <<>> <<-- force yellow on transistors>> IF i0+--i1+--i4=2 THEN { c.r _ Adjust[c.r, color[6].weight]; c.g _ Adjust[c.g, color[6].weight]; c.b _ Adjust[c.b, -color[6].weight] }; <<>> <<-- detect forbidden overlaps and display them white>> <<--IF THEN c _ [1, 1, 1, 0];>> SetMap[--32*i5+--16*i4+8*i3+4*i2+2*i1+i0, c]; <> ENDLOOP; ENDLOOP; ENDLOOP; ENDLOOP; ENDLOOP; SetMap[index: 64, color: color[6]]; -- [background] SetMap[index: 128, color: [r:1, g:1, b:0]]; -- [channels] SetMap[index: 255, color: [r:0, g:0, b:0]]; -- [black] SetMap[index: 254, color: [r:1, g:0, b:0]]; -- [pure red] SetMap[index: 253, color: [r:0, g:1, b:0]]; -- [pure green] SetMap[index: 252, color: [r:0, g:0, b:1]]; -- [pure blue] SetMap[index: 251, color: [r:1, g:1, b:0]]; -- [pure yellow] SetMap[index: 250, color: [r:1, g:1, b:1]]; -- [pure white] [] _ Terminal.SetColorCursorPresentation[Terminal.Current[], onesAreWhite]; END; SetUserColor: PUBLIC PROC [] = <<--makes a default color setup; reads user profile>> <<--does not set up the device>> BEGIN color[0].weight _ Real.Float[UserProfile.Number[key: "Chipndale.green1Weight", default: 100]]/100; color[0].r _ Real.Float[UserProfile.Number[key: "Chipndale.green1.r", default: 0]]/100; color[0].g _ Real.Float[UserProfile.Number[key: "Chipndale.green1.g", default: 100]]/100; color[0].b _ Real.Float[UserProfile.Number[key: "Chipndale.green1.b", default: 0]]/100; color[1].weight _ Real.Float[UserProfile.Number[key: "Chipndale.impWeight", default: 40]]/100; color[1].r _ Real.Float[UserProfile.Number[key: "Chipndale.imp.r", default: 100]]/100; color[1].g _ Real.Float[UserProfile.Number[key: "Chipndale.imp.g", default: 100]]/100; color[1].b _ Real.Float[UserProfile.Number[key: "Chipndale.imp.b", default: 0]]/100; color[2].weight _ Real.Float[UserProfile.Number[key: "Chipndale.blueWeight", default: 50]]/100; color[2].r _ Real.Float[UserProfile.Number[key: "Chipndale.blue.r", default: 0]]/100; color[2].g _ Real.Float[UserProfile.Number[key: "Chipndale.blue.g", default: 0]]/100; color[2].b _ Real.Float[UserProfile.Number[key: "Chipndale.blue.b", default: 100]]/100; color[3].weight _ Real.Float[UserProfile.Number[key: "Chipndale.pinkWeight", default: 50]]/100; color[3].r _ Real.Float[UserProfile.Number[key: "Chipndale.pink.r", default: 50]]/100; color[3].g _ Real.Float[UserProfile.Number[key: "Chipndale.pink.g", default: 50]]/100; color[3].b _ Real.Float[UserProfile.Number[key: "Chipndale.pink.b", default: 70]]/100; color[4].weight _ Real.Float[UserProfile.Number[key: "Chipndale.redWeight", default: 100]]/100; color[4].r _ Real.Float[UserProfile.Number[key: "Chipndale.red.r", default: 100]]/100; color[4].g _ Real.Float[UserProfile.Number[key: "Chipndale.red.g", default: 20]]/100; color[4].b _ Real.Float[UserProfile.Number[key: "Chipndale.red.b", default: 0]]/100; color[5].weight _ Real.Float[UserProfile.Number[key: "Chipndale.backgndWeight", default: 10]]/100; color[5].r _ Real.Float[UserProfile.Number[key: "Chipndale.backgnd.r", default: 40]]/100; color[5].g _ Real.Float[UserProfile.Number[key: "Chipndale.backgnd.g", default: 50]]/100; color[5].b _ Real.Float[UserProfile.Number[key: "Chipndale.backgnd.b", default: 50]]/100; color[6].weight _ Real.Float[UserProfile.Number[key: "Chipndale.channelWeight", default: 30]]/100; END; CDNmosColorCommand: Commander.CommandProc = BEGIN CreateAnInstance[]; END; NmosColorComm: PROC [comm: CDSequencer.Command] = BEGIN CreateAnInstance[]; END; Init8Patterns[NIL]; SetUserColor[]; ComputeColors[]; CDSequencer.ImplementCommand[$SaturationComm, SaturationComm, NMos.nmos, doQueue]; CDSequencer.ImplementCommand[$ColorToolN, NmosColorComm, NMos.nmos, dontQueue]; CDMenus.CreateEntry[$DisplayMenu, "nmos 8 bit tool", $ColorToolN]; [] _ Buttons.Create[info: [name: "nmos col"], proc: ResetAndCreateButton]; Commander.Register[ key: "CDNmosColor", proc: CDNmosColorCommand, doc: "nmos 8 bit color tool" ]; END.