% Copyright (C) 2001-2023 Artifex Software, Inc.
% All Rights Reserved.
%
% This software is provided AS-IS with no warranty, either express or
% implied.
%
% This software is distributed under license and may not be copied,
% modified or distributed except as expressly authorized under the terms
% of the license contained in the file LICENSE in this distribution.
%
% Refer to licensing information at http://www.artifex.com or contact
% Artifex Software, Inc.,  39 Mesa Street, Suite 108A, San Francisco,
% CA 94129, USA, for further information.
%

% docie.ps
% Emulate CIE algorithms in PostScript.

% ---------------- Auxiliary procedures ---------------- %

/r1default [0 1] def
/r3default [0 1 0 1 0 1] def

/apply3		% <u> <v> <w> [<pu> <pv> <pw>] apply3 <u'> <v'> <w'>
 {  { 4 -1 roll exch exec } forall
 } bind def

/restrict	% <u> <min> <max> restrict <u'>
 { 3 1 roll .max .min
 } bind def

/restrict3	% <u> <v> <w> [<minu> ... <maxw>] restrict3 <u'> <v'> <w'>
 { aload pop
   7 -1 roll 3 1 roll restrict 7 1 roll
   5 -1 roll 3 1 roll restrict 5 1 roll
   restrict 3 1 roll
 } bind def

/rescale	% <u> <min> <max> rescale <u'>
 { 1 index sub 3 1 roll sub exch div 0 .max 1 .min
 } bind def

/rescale3	% <u> <v> <w> [<minu> ... <maxw>] rescale3 <u'> <v'> <w'>
 { aload pop
   7 -1 roll 3 1 roll rescale 7 1 roll
   5 -1 roll 3 1 roll rescale 5 1 roll
   rescale 3 1 roll
 } bind def

/mmult3		% <u> <v> <w> [<uu> <uv> ... <wv> <ww>] mmult3
                %   <u'> <v'> <w'>
 { 4 -1 roll dup dup 6 -1 roll dup dup 8 -1 roll dup dup
   10 -1 roll { 10 -1 roll mul } forall
                % Stack: u1 v1 w1 u2 v2 w2 u3 v3 w3
   4 -1 roll add 6 -1 roll add
                % Stack: u1 v1 u2 v2 u3 v3 w'
   7 1 roll 3 -1 roll add 4 -1 roll add
                % Stack: w' u1 u2 u3 v'
   5 1 roll add add 3 1 roll
 } bind def

/minvert3	% [<uu> <uv> ... <wv> <ww>] minvert3
                %   [<uu'> <uv'> ... <wv'> <ww'>]
 { 16 dict begin
   aload pop { I H G F E D C B A } { exch def } forall
   /coa E I mul F H mul sub def
   /cob F G mul D I mul sub def
   /coc D H mul E G mul sub def
   /det A coa mul B cob mul add C coc mul add def
   [ coa det div
     C H mul B I mul sub det div
     B F mul C E mul sub det div
     cob det div
     A I mul C G mul sub det div
     C D mul A F mul sub det div
     coc det div
     B G mul A H mul sub det div
     A E mul B D mul sub det div
   ]
   end
 } bind def

/print1
 { print dup ==
 } bind def

/print3
 { print 3 array astore dup == aload pop
 } bind def

% ---------------- Mapping to XYZ ---------------- %

/csmap		% <csdict> <l> <m> <n> csmap <csdict> <x> <y> <z>
 { 3 index /RangeLMN .knownget not { r3default } if restrict3
DOCIEDEBUG { (After RangeLMN Decode: ) print3 } if
   3 index /DecodeLMN .knownget { apply3 } if
DOCIEDEBUG { (After DecodeLMN Decode: ) print3 } if
   3 index /MatrixLMN .knownget { mmult3 } if
DOCIEDEBUG { (After MatrixLMN Decode: ) print3 } if
 } bind def

/csciea		% <csdict> <a> csciea <csdict> <x> <y> <z>
 { 1 index /RangeA .knownget not { r1default aload pop } if restrict
DOCIEDEBUG { (After RangeA Decode: ) print1 } if
   1 index /DecodeA .knownget { exec } if
DOCIEDEBUG { (After DecodeA Decode: ) print1 } if
   1 index /MatrixA .knownget
    { { 1 index mul exch } forall pop }
    { dup dup }
   ifelse
DOCIEDEBUG { (After MatrixA Decode: ) print3 } if
   csmap
 } bind def

/cscieabc	% <csdict> <a> <b> <c> cscieabc <csdict> <x> <y> <z>
 { 3 index /RangeABC .knownget not { r3default } if restrict3
DOCIEDEBUG { (After RangeABC Decode: ) print3 } if
   3 index /DecodeABC .knownget { apply3 } if
DOCIEDEBUG { (After DecodeABC Decode: ) print3 } if
   3 index /MatrixABC .knownget { mmult3 } if
DOCIEDEBUG { (After MatrixABC Decode: ) print3 } if
   csmap
 } bind def

% ---------------- Rendering from XYZ ---------------- %

/lookup3	% <rtable> <a[0..1]> <b[0..1]> <c[0..1]> lookup3
                %   <rtable> <bytes>
 { 3 -1 roll 3 index 0 get 1 sub mul
   3 -1 roll 3 index 1 get 1 sub mul
   3 -1 roll 3 index 2 get 1 sub mul
                % Stack: rtable ia ib ic
DOCIEDEBUG { (RenderTable indices: ) print3 mark 5 1 roll } if
   3 -1 roll round cvi 3 index 3 get exch get
                % Stack: rtable ib ic string
   3 -1 roll round cvi 3 index 2 get mul
                % Stack: rtable ic string ib*nc
   3 -1 roll round cvi add 2 index 4 get mul
                % Stack: rtable string index
   2 index 4 get getinterval
                % Stack: rtable bytes
DOCIEDEBUG { (RenderTable values: ) print (<) print (%stdout) (w) file 1 index writehexstring (>) = } if
 } bind def

/bpdefault [0 0 0] def

/crmap		% <csdict> <crdict> <x> <y> <z> crmap <v1> ...
 {
DOCIEDEBUG { (CIE XYZ = ) print3 } if
   3 index /MatrixPQR .knownget { mmult3 } if
DOCIEDEBUG { (After MatrixPQR: ) print3 } if
   4 index /WhitePoint get
   5 index /BlackPoint .knownget not { bpdefault } if
   5 index /WhitePoint get
   6 index /BlackPoint .knownget not { bpdefault } if
   4
     { 4 -1 roll aload pop
                        % Stack: csdict crdict x y z pt pt pt px py pz
       3 copy 12 index /MatrixPQR .knownget { mmult3 } if 6 array astore
     }
   repeat
                        % Stack: csdict crdict x y z wps+ bps+ wpd+ bpd+
   9 -1 roll pop		% get rid of csdict
   7 4 roll
   7 index /TransformPQR get
    {			% Stack: crdict wps+ bps+ wpd+ bpd+ u v w proc
      8 copy exch pop exch pop
      exec exch pop 4 -1 roll pop
    }
   forall
   7 3 roll pop pop pop pop	% get rid of White/BlackPoints
DOCIEDEBUG { (After TransformPQR: ) print3 } if
   3 index /MatrixPQR .knownget { minvert3 mmult3 } if
DOCIEDEBUG { (After MatrixPQR': ) print3 } if
   3 index /MatrixLMN .knownget { mmult3 } if
DOCIEDEBUG { (After MatrixLMN Encode: ) print3 } if
   3 index /EncodeLMN .knownget { apply3 } if
DOCIEDEBUG { (After EncodeLMN Encode: ) print3 } if
   3 index /RangeLMN .knownget not { r3default } if restrict3
DOCIEDEBUG { (After RangeLMN Encode: ) print3 } if
   3 index /MatrixABC .knownget { mmult3 } if
DOCIEDEBUG { (After MatrixABC Encode: ) print3 } if
   3 index /EncodeABC .knownget { apply3 } if
DOCIEDEBUG { (After EncodeABC Encode: ) print3 } if
   3 index /RangeABC .knownget not { r3default } if
   5 -1 roll /RenderTable .knownget
    {		% Stack: u v w ranges rtable
      5 1 roll rescale3
DOCIEDEBUG { (Rescaled ABC: ) print3 } if
                % Stack: rtable a b c
      lookup3
                % Stack: rtable bytes
      0 1 3 index 4 get 1 sub
       {	% Stack: values rtable bytes c
         2 copy get 255 div
                % Stack: values rtable bytes c v
         3 index 3 -1 roll 5 add get exec 3 1 roll
       }
      for pop pop
DOCIEDEBUG { (After RenderTableT: ) print ] dup == aload pop } if
    }
    { restrict3
DOCIEDEBUG { (After RangeABC Encode: ) print3 } if
    }
   ifelse
 } bind def

% ---------------- Top level control ---------------- %

/mapdict mark
  /CIEBasedA { 1 get exch csciea currentcolorrendering 4 1 roll crmap } bind
  /DeviceGray { pop /DefaultGray /ColorSpace findresource 1 get exch csciea currentcolorrendering 4 1 roll crmap } bind
  /CIEBasedABC { 1 get 4 1 roll cscieabc currentcolorrendering 4 1 roll crmap } bind
  /DeviceRGB { pop /DefaultRGB /ColorSpace findresource 1 get 4 1 roll cscieabc currentcolorrendering 4 1 roll crmap } bind
.dicttomark def
/mapcie		% <a> mapcie <v1> ...
                % <a> <b> <c> mapcie <v1> ...
 { currentcolorspace dup 0 get //mapdict exch get exec
 } bind def
