<> <> <> <> <<>> DIRECTORY Atom USING [GetPName], Imager USING [black, Error, white], ImagerColor USING [ColorFromAtom, ColorFromGray, ColorFromRGB], ImagerColorDefs USING [ColorOperator, ColorOperatorClassRep, ColorOperatorImplRep, ColorOperatorRep, ConstantColor], ImagerColorOperator USING [Mapper, MapperRep, PixelProc], ImagerColorOperatorPrivate USING [ColorOperatorClass, ColorOperatorClassRep, ColorOperatorImpl, ColorOperatorImplRep, SampleMap, SampleMapRep], ImagerColorPrivate USING [ComponentFromColor], ImagerSample USING [GetSample, LookupPixels, NewBuffer, PutSample, Sample, SampleBuffer], Real USING [Round], RealFns USING [Power], Rope USING [Concat]; ImagerColorOperatorImpl: CEDAR PROGRAM IMPORTS Atom, Imager, ImagerColor, ImagerColorPrivate, ImagerSample, Real, RealFns, Rope EXPORTS ImagerColorOperator, ImagerColorDefs ~ BEGIN OPEN ImagerColorOperator, ImagerColorOperatorPrivate, ImagerColorDefs; <<>> Sample: TYPE ~ ImagerSample.Sample; SampleBuffer: TYPE ~ ImagerSample.SampleBuffer; ColorOperatorImpl: TYPE ~ ImagerColorOperatorPrivate.ColorOperatorImpl; ColorOperatorImplRep: PUBLIC TYPE ~ ImagerColorOperatorPrivate.ColorOperatorImplRep; ColorOperatorClass: TYPE ~ ImagerColorOperatorPrivate.ColorOperatorClass; ColorOperatorClassRep: PUBLIC TYPE ~ ImagerColorOperatorPrivate.ColorOperatorClassRep; BlackData: TYPE ~ REF ColorOperatorImplRep.black; GrayLinearData: TYPE ~ REF ColorOperatorImplRep.grayLinear; GrayDensityData: TYPE ~ REF ColorOperatorImplRep.grayDensity; GrayVisualData: TYPE ~ REF ColorOperatorImplRep.grayVisual; MapData: TYPE ~ REF ColorOperatorImplRep.map; RGBLinearData: TYPE ~ REF ColorOperatorImplRep.rgbLinear; GetColorOperatorClass: PUBLIC PROC [op: ColorOperator] RETURNS [ATOM] ~ { impl: ColorOperatorImpl ~ op.impl; class: ColorOperatorClass ~ op.class; IF class # NIL THEN RETURN [class.classID]; RETURN [WITH impl SELECT FROM data: BlackData => ( IF data.clear THEN $SampledBlackClear ELSE $SampledBlack ), data: GrayLinearData => $GrayLinear, data: GrayDensityData => $GrayDensity, data: GrayVisualData => $GrayVisual, data: MapData => $Map, data: RGBLinearData => $RGBLinear, ENDCASE => NIL]; }; GrayLinearGrayFromSample: PROC [data: GrayLinearData, s0: Sample] RETURNS [REAL] ~ { s: REAL ~ IF data.map=NIL THEN REAL[s0] ELSE data.map[s0]; f: REAL ~ (s-data.sWhite)/(data.sBlack-data.sWhite); IF f<=0 THEN RETURN[0]; IF f>=1 THEN RETURN[1]; RETURN[f]; }; GrayDensityGrayFromSample: PROC [data: GrayDensityData, s0: Sample] RETURNS [REAL] ~ { s: REAL ~ IF data.map=NIL THEN REAL[s0] ELSE data.map[s0]; d: REAL ~ ((s-data.sWhite)/(data.sBlack-data.sWhite))*data.dBlack; f: REAL ~ RealFns.Power[base: 10, exponent: -d]; IF f<=0 THEN RETURN[0]; IF f>=1 THEN RETURN[1]; RETURN[f]; }; GrayVisualGrayFromSample: PROC [data: GrayVisualData, s0: Sample] RETURNS [REAL] ~ { s: REAL ~ IF data.map=NIL THEN REAL[s0] ELSE data.map[s0]; L: REAL ~ (s-data.sBlack)/(data.sWhite-data.sBlack); Y: REAL ~ IF L<=0.09 THEN L/0.09 ELSE RealFns.Power[base: (L+0.16)/0.25, exponent: 3]; f: REAL ~ 1-0.01*Y; IF f<=0 THEN RETURN[0]; IF f>=1 THEN RETURN[1]; RETURN[f]; }; MapColorFromSample: PROC [data: MapData, s0: Sample] RETURNS [ConstantColor] ~ { RETURN[data.v[s0]]; }; ColorFromPixel: PUBLIC PROC [op: ColorOperator, pixel: PROC [i: NAT] RETURNS [Sample]] RETURNS [ConstantColor] ~ { impl: ColorOperatorImpl ~ op.impl; class: ColorOperatorClass ~ op.class; IF class # NIL AND class.ColorFromPixel # NIL THEN RETURN [class.ColorFromPixel[op, pixel]]; WITH impl SELECT FROM data: BlackData => { IF pixel[0] = 0 THEN { IF data.clear THEN RETURN [ImagerColor.ColorFromAtom[$Clear]] ELSE RETURN [Imager.white]; } ELSE RETURN [Imager.black]; }; data: GrayLinearData => { f: REAL ~ GrayLinearGrayFromSample[data, pixel[0]]; RETURN[ImagerColor.ColorFromGray[f]]; }; data: GrayDensityData => { f: REAL ~ GrayDensityGrayFromSample[data, pixel[0]]; RETURN[ImagerColor.ColorFromGray[f]]; }; data: GrayVisualData => { f: REAL ~ GrayVisualGrayFromSample[data, pixel[0]]; RETURN[ImagerColor.ColorFromGray[f]]; }; data: MapData => { RETURN[MapColorFromSample[data, pixel[0]]]; }; data: RGBLinearData => { max: REAL _ data.maxSampleValue; r: REAL _ MIN[MAX[pixel[0]/max, 0.0], 1.0]; g: REAL _ MIN[MAX[pixel[1]/max, 0.0], 1.0]; b: REAL _ MIN[MAX[pixel[2]/max, 0.0], 1.0]; RETURN[ImagerColor.ColorFromRGB[[R: r, G: g, B: b]]]; }; ENDCASE => ERROR Imager.Error[[code: $unimplemented, explanation: "Color operator has an unrecognized color model."]]; }; ColorFromSamples: PUBLIC PROC [op: ColorOperator, s0, s1, s2, s3: Sample _ 0] RETURNS [ConstantColor] ~ { pixel: PROC [i: NAT] RETURNS [Sample] ~ { check: [0..4) ~ i; RETURN[SELECT i FROM 0 => s0, 1 => s1, 2 => s2, ENDCASE => s3]; }; RETURN[ColorFromPixel[op, pixel]]; }; BlackMapPixel: PROC [mapper: Mapper, pixel: PixelProc] RETURNS [Sample] ~ { s0: [0..1] ~ pixel[0]; RETURN[IF s0=1 THEN 0 ELSE mapper.maxOut]; }; BlackMapPixels: PROC [mapper: Mapper, pixels: SampleBuffer, j: NAT _ 0, buffer: SampleBuffer, bi, bj: NAT _ 0, count: NAT] ~ { FOR k: NAT IN[0..count) DO s0: [0..1] ~ pixels.GetSample[0, j+k]; buffer.PutSample[bi, bj+k, IF s0=1 THEN 0 ELSE mapper.maxOut]; ENDLOOP; }; BlackColorModel: PUBLIC PROC[clear: BOOL _ FALSE] RETURNS [ColorOperator] ~ { data: BlackData ~ NEW[ColorOperatorImplRep.black _ [black[clear: clear]]]; RETURN[NEW[ColorOperatorRep _ [samplesPerPixel: 1, impl: data]]]; }; TableMapPixel: PROC [mapper: Mapper, pixel: PixelProc] RETURNS [Sample] ~ { tables: SampleBuffer ~ NARROW[mapper.data]; sum: Sample _ 0; FOR i: NAT IN[0..tables.iSize) DO sample: Sample ~ pixel[i]; sum _ sum+tables.GetSample[i, sample]; ENDLOOP; RETURN[sum]; }; TableMapPixels: PROC [mapper: Mapper, pixels: SampleBuffer, j: NAT _ 0, buffer: SampleBuffer, bi, bj: NAT _ 0, count: NAT] ~ { tables: SampleBuffer ~ NARROW[mapper.data]; ImagerSample.LookupPixels[tables: tables, pixels: pixels, pj: j, buffer: buffer, bi: bi, bj: bj, count: count]; }; NewMapper: PUBLIC PROC [op: ColorOperator, component: ATOM, maxIn, maxOut: Sample] RETURNS [Mapper] ~ { impl: ColorOperatorImpl ~ op.impl; class: ColorOperatorClass ~ op.class; IF class # NIL AND class.NewMapper # NIL THEN RETURN [class.NewMapper[op, component, maxIn, maxOut]]; WITH impl SELECT FROM data: BlackData => { check: [0..1] ~ maxIn; RETURN[NEW[MapperRep _ [op: op, component: component, maxIn: maxIn, maxOut: maxOut, MapPixel: BlackMapPixel, MapPixels: BlackMapPixels, data: NIL]]]; }; data: GrayLinearData => { tableSize: NAT ~ maxIn+1; table: SampleBuffer ~ ImagerSample.NewBuffer[1, tableSize]; FOR s: Sample IN[0..tableSize) DO R: REAL ~ 1.0-GrayLinearGrayFromSample[data, s]; val: Sample ~ Real.Round[R*maxOut]; table.PutSample[0, s, val]; ENDLOOP; RETURN[NEW[MapperRep _ [op: op, component: component, maxIn: maxIn, maxOut: maxOut, MapPixel: TableMapPixel, MapPixels: TableMapPixels, data: table]]]; }; data: MapData => { tableSize: NAT ~ maxIn+1; table: SampleBuffer ~ ImagerSample.NewBuffer[1, tableSize]; FOR s: Sample IN[0..tableSize) DO color: ConstantColor ~ data[s]; R: REAL ~ ImagerColorPrivate.ComponentFromColor[color, component]; val: Sample ~ Real.Round[MIN[MAX[R, 0.0], 1.0]*maxOut]; table.PutSample[0, s, val]; ENDLOOP; RETURN[NEW[MapperRep _ [op: op, component: component, maxIn: maxIn, maxOut: maxOut, MapPixel: TableMapPixel, MapPixels: TableMapPixels, data: table]]]; }; data: RGBLinearData => { scale: REAL ~ data.maxSampleValue; tableSize: NAT ~ maxIn+1; tables: SampleBuffer ~ ImagerSample.NewBuffer[3, tableSize]; factors: ARRAY [0..3) OF REAL _ ALL[0]; SELECT component FROM $Intensity => factors _ [0.30, 0.59, 0.11]; $Red => factors _ [1, 0, 0]; $Green => factors _ [0, 1, 0]; $Blue => factors _ [0, 0, 1]; ENDCASE => ERROR Imager.Error[[$unimplemented, Rope.Concat["Unimplemented color operator component: $", Atom.GetPName[component]]]]; FOR i: NAT IN [0..3) DO FOR s: Sample IN[0..tableSize) DO R: REAL ~ factors[i]*s/scale; val: Sample ~ Real.Round[MIN[MAX[R, 0.0], 1.0]*maxOut]; tables.PutSample[i, s, val]; ENDLOOP; ENDLOOP; RETURN[NEW[MapperRep _ [op: op, component: component, maxIn: maxIn, maxOut: maxOut, MapPixel: TableMapPixel, MapPixels: TableMapPixels, data: tables]]]; }; ENDCASE => ERROR Imager.Error[[$unimplemented, "Unimplemented color operator."]]; }; MapPixel: PUBLIC PROC [mapper: Mapper, pixel: PixelProc] RETURNS [Sample] ~ { RETURN mapper.MapPixel[mapper, pixel]; }; MapSamples: PUBLIC PROC [mapper: Mapper, s0, s1, s2, s3: Sample _ 0] RETURNS [Sample] ~ { pixel: PROC [i: NAT] RETURNS [Sample] ~ { n: [0..4) ~ i; RETURN[SELECT n FROM 0 => s0, 1 => s1, 2 => s2, 3 => s3, ENDCASE => ERROR]; }; RETURN mapper.MapPixel[mapper, pixel]; }; MapPixels: PUBLIC PROC [mapper: Mapper, pixels: SampleBuffer, j: NAT _ 0, buffer: SampleBuffer, bi, bj: NAT _ 0, count: NAT] ~ { mapper.MapPixels[mapper: mapper, pixels: pixels, j: j, buffer: buffer, bi: bi, bj: bj, count: count]; }; <<>> NewSampleMap: PROC [maxSampleValue: Sample, sampleMap: PROC [Sample] RETURNS [REAL]] RETURNS [map: SampleMap _ NIL] ~ { IF maxSampleValue#0 AND sampleMap#NIL THEN { size: NAT ~ maxSampleValue+1; map _ NEW[SampleMapRep[size]]; FOR s0: Sample IN[0..maxSampleValue] DO map[s0] _ sampleMap[s0] ENDLOOP; }; }; GrayLinearColorModel: PUBLIC PROC [sWhite, sBlack: REAL, maxSampleValue: Sample _ 0, sampleMap: PROC [Sample] RETURNS [REAL] _ NIL ] RETURNS [ColorOperator] ~ { map: SampleMap ~ NewSampleMap[maxSampleValue, sampleMap]; impl: GrayLinearData ~ NEW[ColorOperatorImplRep.grayLinear _ [grayLinear[ sWhite: sWhite, sBlack: sBlack, map: map]]]; RETURN[NEW[ColorOperatorRep _ [samplesPerPixel: 1, impl: impl]]]; }; GrayDensityColorModel: PUBLIC PROC [sWhite, sBlack, dBlack: REAL, maxSampleValue: Sample _ 0, sampleMap: PROC [Sample] RETURNS [REAL] _ NIL ] RETURNS [ColorOperator] ~ { map: SampleMap ~ NewSampleMap[maxSampleValue, sampleMap]; impl: GrayDensityData ~ NEW[ColorOperatorImplRep.grayDensity _ [grayDensity[ sWhite: sWhite, sBlack: sBlack, dBlack: dBlack, map: map]]]; RETURN[NEW[ColorOperatorRep _ [samplesPerPixel: 1, impl: impl]]]; }; GrayVisualColorModel: PUBLIC PROC [sWhite, sBlack: REAL, maxSampleValue: Sample _ 0, sampleMap: PROC [Sample] RETURNS [REAL] _ NIL ] RETURNS [ColorOperator] ~ { map: SampleMap ~ NewSampleMap[maxSampleValue, sampleMap]; impl: GrayVisualData ~ NEW[ColorOperatorImplRep.grayVisual _ [grayVisual[ sWhite: sWhite, sBlack: sBlack, map: map]]]; RETURN[NEW[ColorOperatorRep _ [samplesPerPixel: 1, impl: impl]]]; }; MapColorModel: PUBLIC PROC [ maxSampleValue: Sample, map: PROC [Sample] RETURNS [ConstantColor] ] RETURNS [ColorOperator] ~ { size: Sample ~ maxSampleValue+1; impl: MapData ~ NEW[ColorOperatorImplRep.map[size] _ [map[v: ]]]; FOR s0: Sample IN [0..maxSampleValue] DO impl.v[s0] _ map[s0] ENDLOOP; RETURN[NEW[ColorOperatorRep _ [samplesPerPixel: 1, impl: impl]]]; }; RGBLinearColorModel: PUBLIC PROC [maxSampleValue: Sample] RETURNS [op: ColorOperator] ~ { impl: RGBLinearData ~ NEW[ColorOperatorImplRep.rgbLinear _ [rgbLinear[maxSampleValue: maxSampleValue]]]; RETURN[NEW[ColorOperatorRep _ [samplesPerPixel: 3, impl: impl]]]; }; END.