Advertisement
Guest User

Converison d'image pour Thomson

a guest
Jul 28th, 2013
131
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #/bin/perl
  2. #
  3. # Conversion d'image au format MAP thomson.
  4. #
  5. # Usage: perl <script.pl> <image>.<ext> <image>.<ext> ...
  6. # ou     find <dir> -name '*.gif' | perl <script.pl> (entrée des images par l'entrée standard)
  7. # ==> produit x<image>.map (image thomson) et x<image>.gif (version visible ailleurs que thomson).
  8. #
  9. # Ce script est complètement expérimental et change tout le temps. Beaucoup de code est inutilisé
  10. # ou commenté du fait des évolution succéssives. Il n'est pas écrit pour être utilisé en production.
  11. # Un jour j'en ferais une version belle et propre en créant un plugin XnView.
  12. #
  13. # (c) Samuel DEVULDER 2007-2013
  14. #
  15.  
  16. #use Graphics::MagickXX;
  17. use Image::Magick;
  18.  
  19. $SIG{'INT'} = 'DEFAULT';
  20. $SIG{'CHLD'} = 'IGNORE';
  21.  
  22. # suppression du buffer pour l'affichage en sortie
  23. #$| = 1;
  24.  
  25. # variables globale
  26. $glb_magick = Image::Magick->new;
  27. $glb_to7pal = 2;       # 2 = palette TO7, 1 = TO7/70, 0 = TO9
  28. $glb_maxcol = $glb_to7pal>1?8:16;      # nb total de couls
  29. $glb_lab    = 0;       # distance couleur cielab
  30. $glb_dith   = 0;       # avec 3 ca donne des images pas mal colorées!
  31. $glb_gamma  = 2.20; #1/0.45;
  32. $glb_clean  = 0.2;
  33. $glb_att    = 1; #.85;
  34.    
  35. # error dispersion matrix. Index represents:
  36. #    X 3
  37. #  0 1 2
  38. @glb_err = (0.000, 0.000, 0.000, 0.000) if 1;     # no dith
  39. @glb_err = (0.200, 0.700, 0.100, 0.000) if 0;     # nice lines
  40. @glb_err = (0.062, 0.312, 0.187, 0.437) if 0;     # floyd steinberg
  41. @glb_err = (0.187, 0.312, 0.062, 0.437) if 1;     # floyd steinberg
  42. @glb_err = (0.000, 0.500, 0.000, 0.500) if 0;     # simple
  43. @glb_err = (0.000, 1.000, 0.000, 0.000) if 0;
  44. @glb_err = (0.100, 0.500, 0.100, 0.300) if 0;
  45. @glb_err = (0.300, 0.500, 0.100, 0.100) if 0;
  46. @glb_err = (0.250, 0.500, 0.125, 0.125) if 0;
  47. @glb_err = (0.500, 0.000, 0.500, 0.000) if 0;     # motifs inca
  48. @glb_err = (0.200, 0.500, 0.100, 0.200) if 0;     # motifs inca
  49. @glb_err = (0.000, 0.400, 0.400, 0.200) if 0;     # motifs inca
  50. @glb_err = (0.250, 0.250, 0.000, 0.500) if 0;     # sierra 2-4a
  51. @glb_err = (0.333, 0.334, 0.000, 0.333) if 0;
  52. @glb_err = (0.233, 0.333, 0.234, 0.200) if 0;     # à voir
  53. @glb_err = (0.233, 0.367, 0.200, 0.200) if 1*0;     # à voir
  54.  
  55. @glb_err = (0.250, 0.500, 0.250, 0.000) if 0;
  56.  
  57. @glb_err = (0.200, 0.233, 0.367, 0.200) if 0;     # serpente
  58. @glb_err = (0.250, 0.500, 0.250, 0.000) if 0;
  59.  
  60. @glb_err = (0.100, 0.500, 0.000, 0.400) if 0;     # simple (horiz)
  61. @glb_err = (0.025, 0.125, 0.050, 0.125) if 0;     # permet d'avoir des plages de couleurs constantes . Ca rend plutot pas mal pour les jeux videos.
  62. @glb_err = (0.050, 0.125, 0.050, 0.125) if 0;     # permet d'avoir des plages de couleurs constantes . Ca rend plutot pas mal pour les jeux videos.
  63. @glb_err = (0.100, 0.150, 0.050, 0.200) if 0;     # fs attenue (pas mal pour les jeux)
  64. @glb_err = (0.125, 0.250, 0.125, 0.250) if 0;     # modified atkinson
  65.  
  66. @mat = (
  67.     [ 0,48,12,60, 3,51,15,63],
  68.     [32,16,44,28,35,19,47,31],
  69.     [ 8,56, 4,52,11,59, 7,55],
  70.     [40,24,36,20,43,27,39,23],
  71.     [ 2,50,14,62, 1,49,13,61],
  72.     [34,18,46,30,33,17,45,29],
  73.     [10,58, 6,54, 9,57, 5,53],
  74.     [42,26,38,22,41,25,37,21]);
  75.  
  76. $mat_y = 1+$#mat;
  77. $mat_x = 1+$#{$mat[0]};
  78. $max = 0;
  79. for $y (0..$mat_y-1) {
  80.   for $x (0..$mat_x-1) {
  81.     ++$mat[$y][$x];
  82.     $max = $mat[$y][$x] if $mat[$y][$x]>$max;
  83.   }
  84. }
  85. for $y (0..$mat_y-1) {
  86.   for $x (0..$mat_x-1) {
  87.     $mat[$y][$x] /= $max;
  88.   }
  89. }
  90.    
  91. # construit les maps pour la multiplication
  92. for($i = -256; $i<256; ++$i) {$glb_map0[$i & 0x1ff] = xint($i * $glb_err[0] * &glb_att($i)) & 0x1ff;}
  93. for($i = -256; $i<256; ++$i) {$glb_map1[$i & 0x1ff] = xint($i * $glb_err[1] * &glb_att($i)) & 0x1ff;}
  94. for($i = -256; $i<256; ++$i) {$glb_map2[$i & 0x1ff] = xint($i * $glb_err[2] * &glb_att($i)) & 0x1ff;}
  95. for($i = -256; $i<256; ++$i) {$glb_map3[$i & 0x1ff] = xint($i * $glb_err[3] * &glb_att($i)) & 0x1ff;}
  96. for($i = -256; $i<256; ++$i) {$glb_sqr [$i & 0x1ff] = $i * $i;}
  97. $glb_err0 = $glb_err[0]>0;
  98. $glb_err1 = $glb_err[1]>0;
  99. $glb_err2 = $glb_err[2]>0;
  100. $glb_err3 = $glb_err[3]>0;
  101.  
  102. # limit error
  103. $clamp = -48;
  104. for($i = -256; $i<256; ++$i) {$glb_clamp[$i & 0x1ff] = ($i< $clamp ? $clamp : $i) & 0x1ff;}
  105.  
  106. # map une intensité entre 0..255 vers l'intensité produite par le circuit EFxxx le plus proche (16 valeurs)
  107. @ef_vals = (0, 39, 74, 101, 122, 140, 157, 171, 185, 195, 206, 216, 227, 237, 248, 255) if 1;
  108.  
  109. # eval perso
  110. @ef_vals = (0,78,116,138,157,171,182,187,205,215,222,229,238,244,249,255) if 0;
  111. @ef_vals = (0,51,91,117,142,161,172,187,199,210,220,227,236,244,248,255) if 1;
  112.  
  113. # ef TEO
  114. @ef_vals = (0, 100, 127, 142, 163, 179, 191,203, 215, 223, 231, 239, 243, 247, 251, 255) if 1;
  115. @ef_vals = (0, 127, 169, 188, 198, 205, 212, 219, 223, 227, 232, 239, 243, 247, 251, 255) if 0; # eval prehisto
  116. @ef_vals = (0, 174, 192, 203, 211, 218, 224, 229, 233, 237, 240, 244, 247, 249, 252, 255) if 0; # prehisto 2
  117. @ef_vals = (0, 169, 188, 200, 209, 216, 222, 227, 232, 236, 239, 243, 246, 249, 252, 255) if 0; # prehisto 3
  118. @ef_vals = (0, 153, 175, 189, 199, 207, 215, 221, 227, 232, 236, 241, 245, 248, 252, 255) if 0; # prehisto 4
  119.  
  120. @intens = @ef_vals;
  121. #@intens = (0, 16, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192, 218, 224, 240, 255);
  122. #@intens = (0, 66, 96, 120, 138, 153, 168, 180, 192, 201, 210, 219, 228, 237, 246, 252);
  123. #@intens = (0, 32, 66, 98, 128, 152, 170, 185, 200, 212, 224, 233, 245, 255);
  124. #@intens = (0, 60, 98, 128, 152, 170, 185, 200, 212, 224, 233, 245, 255);
  125. @intens = (0, 32, 64, 96, 128, 160, 192, 255) if 0;
  126. @intens = (0, 16, 32, 48, 64, 96, 128, 160, 192, 224, 255) if 0;
  127. @intens = (0, 32, 64, 128, 255) if 0;
  128. @intens = (0, 50, 100, 200, 255) if 0; ## <== pas mal
  129. @intens = (0, 32, 64, 128, 192, 255) if 0;
  130. @intens = (0, 33, 66, 99, 133, 166, 200, 255) if 0;
  131. @intens = (0, 98, 128, 152, 170,
  132.     185, #200,
  133.     212, #224,
  134.     233, #241,
  135.     251) if 0;
  136. @intens = (0, 45, 98, 185, 255) if 0;
  137. @intens = (0, 16, 32, 64, 128, 192, 224, 240, 255) if 0; ##  
  138. @intens = (0, 32, 64, 128, 192, 224, 255) if 0;
  139. @intens = (0, 16, 32, 64, 128, 255) if 0;
  140.  
  141. # basse luminosité
  142. @intens = (0, 39, 74, 122, 195, 227, 255) if 0; # ajustement aux niveaux thomson
  143. @intens = (0, 39, 74, 122, 195, 227, 248) if 0; # ajustement aux niveaux thomson
  144. @intens = (0, 39, 101, 195, 255) if 0;
  145. @intens = (0, 39, 74, 122, 185, 216, 255) if 0; # ajustement aux niveaux thomson
  146.  
  147. # 0 64 128 192 256~
  148. # 0 32 64 96 128 160 192 224 256
  149. @intens = (0, 78, 116, 138, 157, 187, 222, 238, 255) if 0;
  150. @intens = (0, 78, 116, 157, 195, 222, 255) if 0;
  151. @intens = (0, 78, 138, 222, 255) if 0;
  152. @intens = (0, 78, 138, 157, 255) if 0;
  153. @intens = (0, 78, 157, 244) if 0;
  154. @intens = (0, 138, 255) if 0;
  155. @intens = (0, 51, 91, 117, 161, 187, 227, 255) if 0;
  156. @intens = (0, 42, 84, 126, 168, 210, 255) if 0;
  157. @intens = (0, 51, 102, 153, 204, 255) if 0;
  158. @intens = (0, 16, 32, 64, 128, 192, 224, 240, 255) if 0;
  159. @intens = (0, 100, 127, 142, 179, 215, 255) if 0;
  160.  
  161. # equi reparti
  162. @intens = (0, 16, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192, 208, 224, 240, 255) if 0;
  163. @intens = (0, 32, 64, 96, 128, 160, 192, 224, 255) if 0;
  164. @intens = (0, 48, 96, 144, 192, 255) if 0;
  165.  
  166. if($glb_gamma) {
  167.     #print join(",", @intens), "\n";
  168.     foreach (@intens)  {$_ = &gamma($_);}
  169.     #print join(",", @intens), "\n";
  170.     foreach (@ef_vals) {$_ = &gamma($_);}
  171. }
  172.  
  173. # remap des intens
  174. for($i=0; $i<=$#intens; ++$i) {
  175.     my($z) = 0;
  176.     for($j=0, $m=1e30; $j<=$#ef_vals; ++$j) {
  177.         next if $ef_vals[$j]<0;
  178.         $k = $intens[$i] - $ef_vals[$j]; $k = -$k if $k<0;
  179.         if($k<$m) {$m=$k; $z = $ef_vals[$j];}
  180.     }
  181.     $intens[$i] = $z;
  182. }
  183.  
  184. # mapping des intensités
  185. @map_ef = ();
  186. for($i=0; $i<256; ++$i) {  
  187.     for($j=0, $m=1e30; $j<=$#intens; ++$j) {
  188.         next if $intens[$j]<0;
  189.         $k = $i - $intens[$j]; $k = -$k if $k<0;
  190.         if($k<$m) {$m=$k; $map_ef[$i] = $intens[$j];}
  191.     }
  192.     for($j=0; $j<=$#intens && $intens[$j]<=$i; ++$j) {
  193.         next if $intens[$j]<0;
  194.         $map_ef2[$i] = $intens[$j];
  195.     }
  196. }
  197. #@map_ef = ();
  198.  
  199.  
  200. # analyse des fichiers en argments
  201. @files = @ARGV;
  202.  
  203. # si aucun fichier, alors on les prends depuis l'entrée standard
  204. if(!@files) {
  205.     while(<>) {
  206.         chomp;
  207.         next if /chlgdls/;
  208.         y%\\%/%;
  209.         s%^([\S]):%/cygdrive/$1%;
  210.         push(@files, $_);
  211.     }
  212. }
  213.  
  214. # extension supportées
  215. $supported_ext = "\.(gif|pnm|png|jpg|jpeg|ps)";
  216. # fichier a effacer pour stopper le prog
  217. $stopme = ".stop_me";
  218. open(f, ">$stopme"); close(f);
  219.  
  220. #&start_wd;
  221.  
  222. # traitement de tous les fichiers
  223. $cpt = 0;
  224. foreach $in (@files) {
  225.     last if ! -e "$stopme";
  226.     next unless $in =~ /$supported_ext$/i;
  227.  
  228.     ++$cpt;
  229.  
  230.     next if $in eq "-";
  231.     #next if $in =~ /ord/;
  232.     next if $in =~ /6846/;
  233.  
  234.     $out = $in; $out=~s/$supported_ext$/.gif/i; $out=~s/.*[\/\\]//;
  235.     $out = "x$out";
  236.  
  237.     print $cpt,"/",1+$#files," $in => $out\n";
  238.    
  239.     &reset_wd;
  240.    
  241.     next if -e $out;
  242.  
  243.     # lecture
  244.     my(@px) = &read_image($in);
  245.    
  246.     @px = &cleanup(@px) if 1;
  247.    
  248.     # creation palette 16 couls (passage par une globale pour simplifier le code)
  249.     @glb_pal = &find_palette($glb_maxcol, @px);
  250.    
  251.     #&print_pal(@glb_pal);
  252.  
  253.     # precalc distance entre les couleurs de la palette
  254.     $glb_dist = ();
  255.     for($i=0; $i<$glb_maxcol; ++$i) {
  256.         $glb_dist[$i + $i*$glb_maxcol] = 0;
  257.         for($j = 0; $j<$i; ++$j) {
  258.             $glb_dist[$j + $i*$glb_maxcol] = $glb_dist[$i + $j*$glb_maxcol] = &irgb_dist($glb_pal[$i], $glb_pal[$j]);
  259.         }
  260.     }
  261.    
  262.     if(0) {
  263.         $px[1] = &rgb2irgb(1,1,1);
  264.         $px[2] = &rgb2irgb(1,1,1);
  265.         $px[3] = &rgb2irgb(1,1,1);
  266.         $px[321] = &rgb2irgb(1,1,1);
  267.         $px[322] = &rgb2irgb(1,1,1);
  268.         $px[323] = &rgb2irgb(1,1,1);
  269.     }
  270.    
  271.     if(0) {
  272.         # pour tester : dither sans contrainte
  273.         my(@p2) = @px;
  274.         for($p=$y=0; $y<200; ++$y) {
  275.             for($x=0; $x<320; ++$x) {
  276.                 $p = 320*$y+$x;
  277.                 my($rvb) = $p2[$p] = &irgb_map($p2[$p], \@glb_clamp);
  278.                 for($i=0, $dm=1e30; $i<$glb_maxcol; ++$i) {
  279.                     $d  = &irgb_dist($rvb, $glb_pal[$i]);
  280.                     print &irgb2hex($glb_pal[$i]), " $i => $d\n" if 0;
  281.                     if($d<$dm) {$dm = $d; $p2[$p] = $glb_pal[$i];}
  282.                 }
  283.                 print "$x,$y : ", &irgb2hex($rvb), "=>", &irgb2hex($p2[$p]), " $dm\n\n" if 0;
  284.                 $rvb = &irgb_sub($rvb, $p2[$p]);
  285.                 #print " /_\\ = ", &irgb2hex($rvb), "\n";
  286.                 $p2[$p + 319] = &irgb_add_cln($p2[$p + 319], &irgb_map($rvb, \@glb_map0)) if $glb_err0 && $y<199 && $x>0;
  287.                 $p2[$p + 320] = &irgb_add_cln($p2[$p + 320], &irgb_map($rvb, \@glb_map1)) if $glb_err1 && $y<199;
  288.                 $p2[$p + 321] = &irgb_add_cln($p2[$p + 321], &irgb_map($rvb, \@glb_map2)) if $glb_err2 && $y<199 && $x<319;
  289.                 $p2[$p + 001] = &irgb_add_cln($p2[$p + 001], &irgb_map($rvb, \@glb_map3)) if $glb_err3 &&           $x<319;
  290.             }
  291.         }
  292.         &write_image("${out}.gif", @p2);
  293.     }
  294.  
  295.     my(%palx);
  296.         foreach $i (@px) {$palx{$i} = 1;last if length(keys %palx)>2;}
  297.     $mono = length(keys %palx)<3;
  298.     %palx = ();
  299.    
  300.     # process image
  301.     my($p, $y, $x) = (0,0,0);
  302.     for($y=0; $y<200; ++$y) {
  303.         print "\r> ", int($y/2), "%  ";
  304.         for($x=0; $x<320; $x+=8) {
  305.             $p = $y * 320 + $x;
  306.             #for($i=0; $i<8; ++$i) {$px[$p+$i] = &irgb_map($px[$p+$i], \@glb_clamp);}
  307.             for($i=0; $i<8; ++$i) {$px[$p+$i] = &irgb_sat($px[$p+$i]);}
  308.             my($forme, $fond) = &couple6(@px[$p..$p+7]);   
  309.             #print "===> ", &irgb2hex($forme), " ", &irgb2hex($fond),"\n";
  310.             for($i=0; $i<8; ++$i, ++$p) {
  311.                 my($rvb) = &irgb_sat($px[$p]);
  312.                 #$rvb = &irgb_add_cln($rvb, &rgb2irgb(rand(0.02),rand(0.02),rand(0.02))) if 0; #($i+$y) & 8;
  313.                 # meilleur couleur approchante
  314.                 $px[$p] = (&irgb_dist($forme, $rvb) < &irgb_dist($fond, $rvb)) ? $forme : $fond;
  315.                 #print $i,"::", &irgb2hex($rvb),"=>",&irgb2hex($px[$p]),"\n";
  316.                 #for($dm = 1e30, $k = 0; $k<$glb_maxcol; ++$k) {if(($d = &irgb_dist($rvb, $glb_pal[$k])) < $dm) {$dm = $d; $px[$p] = $glb_pal[$k];}};
  317.                
  318.                 #if(($px[$p] & 0xff) > 0x40) {
  319.                 #    print &irgb2hex($px[$p]),"\n", &irgb2hex($rvb), " f=", &irgb2hex($forme), ":", &irgb_dist($forme, $rvb)," F=", &irgb2hex($fond),":",&irgb_dist($fond,$rvb),"\n";
  320.                 #}
  321.                 #if(($px[$p] & 0xff) < 0x80) {
  322.                 #    print &irgb2hex($px[$p]),"\n", &irgb2hex($rvb), " f=", &irgb2hex($forme), ":", &irgb_dist($forme, $rvb)," F=", &irgb2hex($fond),":",&irgb_dist($fond,$rvb),"\n";
  323.                 #}
  324.                
  325.                 # diffusion d'erreur
  326.                 if(!$mono) {
  327.                     #print " p=",&irgb2hex($rvb);
  328.                     $rvb = &irgb_sub($rvb, $px[$p]);
  329.                     #print " q=", &irgb2hex($px[$p]), " d=", &irgb2hex($rvb);
  330.                     #print " m=", irgb2hex(&irgb_map($rvb, \@glb_map1)), " n=", &irgb2hex($px[$p+320]), " X=", &irgb2hex(&irgb_uadd($px[$p + 320], &irgb_map($rvb, \@glb_map1))), "\n";
  331.                     $px[$p + 319] = &irgb_add_cln($px[$p + 319], &irgb_map($rvb, \@glb_map0)) if $glb_err0 && $y<199 && ($x+$i)>0;
  332.                     $px[$p + 320] = &irgb_add_cln($px[$p + 320], &irgb_map($rvb, \@glb_map1)) if $glb_err1 && $y<199;
  333.                     $px[$p + 321] = &irgb_add_cln($px[$p + 321], &irgb_map($rvb, \@glb_map2)) if $glb_err2 && $y<199 && ($x+$i)<319;
  334.                     $px[$p + 001] = &irgb_add_cln($px[$p + 001], &irgb_map($rvb, \@glb_map3)) if $glb_err3 &&           ($x+$i)<319;
  335.                 }
  336.                 # pour voir les limites octets
  337.                 $px[$p] = $i&1? $forme : $fond if 0;
  338.                 $px[$p] ^= 0x0ff3fcff if $i==0 && 0;
  339.             }
  340.         }
  341.         $| = 1; print "\r"; $| = 0;
  342.     }
  343.     %dist_cache = ();
  344.    
  345.     # ecriture des pixels et lecture
  346.     #$out =~ s/.gif$/.c16.gif/;
  347.     &write_image($out, @px);
  348.    
  349.     $out =~ s/.gif$//;
  350.     #&write_map("$out.mpa", 1, @px);
  351.     #&write_map("$out.mpb", 2, @px);
  352.     &write_map("$out.map", 3, @px);
  353.    
  354.     sleep(1);
  355. }
  356. unlink($stopme);
  357.  
  358. if(0) {
  359.     %m = ();
  360.     foreach $out (<rgb/*.MAP>) {
  361.         open(IN, "cygpath -w -s \"$out\" |"); $zz = <IN>; chomp($zz); close(IN);
  362.         $zz=~y/~\\/_\//;
  363.         $m{$out} = $zz;
  364.     }
  365.     foreach $out (keys %m) {
  366.         rename($out, $m{$out});
  367.     }
  368. }
  369.  
  370. sub glb_att {
  371.     return 1;
  372.     my($i) = @_;
  373.     $i = -$i if $i<0;
  374.     return 1 if $i>32;
  375.     return .5*(1+$i/32)/2;
  376. }
  377.  
  378. sub print_pal {
  379.     my(@pal) = @_;
  380.     my($i, @t);
  381.     foreach $i (@pal) {
  382.         my($r) = ($i>>20) & 255;
  383.         my($g) = ($i>>10) & 255;
  384.         my($b) = ($i>>00) & 255;
  385.        
  386.         push(@t, sprintf("%3d,%3d,%3d", $r, $g, $b));
  387.     }
  388.     for $i (sort(@t)) {
  389.         print "$i\n";
  390.     }
  391. }
  392. # retourne la palette TO7/70
  393. sub to770_palette {
  394.     return (
  395.         &rgb2irgb(0.0000,0.0000,0.0000), &rgb2irgb(1.0000,0.0000,0.0000),
  396.         &rgb2irgb(0.0000,1.0000,0.0000), &rgb2irgb(1.0000,1.0000,0.0000),
  397.         &rgb2irgb(0.0000,0.0000,1.0000), &rgb2irgb(1.0000,0.0000,1.0000),
  398.         &rgb2irgb(0.0000,1.0000,1.0000), &rgb2irgb(1.0000,1.0000,1.0000),
  399.         &rgb2irgb(0.4375,0.4375,0.4375), &rgb2irgb(0.6250,0.1875,0.1875),
  400.         &rgb2irgb(0.1875,0.6250,0.1875), &rgb2irgb(0.6250,0.6250,0.1875),
  401.         &rgb2irgb(0.1875,0.1875,0.6250), &rgb2irgb(0.6250,0.1875,0.6250),
  402.         &rgb2irgb(0.4375,0.8750,0.8750), &rgb2irgb(0.5375,0.6875,0.0000)
  403.     ) if 0; # pas de gamma
  404.     return (
  405.         &rgb2irgb(0.0000,0.0000,0.0000), &rgb2irgb(1.0000,0.0000,0.0000),
  406.         &rgb2irgb(0.0000,1.0000,0.0000), &rgb2irgb(1.0000,1.0000,0.0000),
  407.         &rgb2irgb(0.0000,0.0000,1.0000), &rgb2irgb(1.0000,0.0000,1.0000),
  408.         &rgb2irgb(0.0000,1.0000,1.0000), &rgb2irgb(1.0000,1.0000,1.0000),
  409.         &rgb8irgb(212, 212, 212), &rgb8irgb(242, 152, 152),
  410.         &rgb8irgb(152, 242, 152), &rgb8irgb(242, 242, 152),
  411.         &rgb8irgb(152, 152, 242), &rgb8irgb(242, 152, 242),
  412.         &rgb8irgb(212, 255, 255), &rgb8irgb(255, 211,   1),  
  413.     ) if 1; # gamma
  414.     return (
  415.         &rgb2irgb(0.0000,0.0000,0.0000), &rgb2irgb(1.0000,0.0000,0.0000),
  416.         &rgb2irgb(0.0000,1.0000,0.0000), &rgb2irgb(1.0000,1.0000,0.0000),
  417.         &rgb2irgb(0.0000,0.0000,1.0000), &rgb2irgb(1.0000,0.0000,1.0000),
  418.         &rgb2irgb(0.0000,1.0000,1.0000), &rgb2irgb(1.0000,1.0000,1.0000),
  419.         &rgb2irgb(0.6980,0.6980,0.6980), &rgb2irgb(0.8320,0.4640,0.4640),
  420.         &rgb2irgb(0.4640,0.8320,0.4640), &rgb2irgb(0.8320,0.8320,0.4640),
  421.         &rgb2irgb(0.4640,0.4640,0.8320), &rgb2irgb(0.8320,0.4640,0.8320),
  422.         &rgb2irgb(0.6980,0.9680,0.9680), &rgb2irgb(0.8710,0.4640,0.0000)
  423.     ); # gamma
  424. }
  425.  
  426. sub rgb8irgb {
  427.     return &rgb2irgb($_[0]/255.0, $_[1]/255.0, $_[2]/255.0);
  428. }
  429.  
  430. sub test_niveaux {
  431.     my (@args) = @_;
  432.    
  433.     my($dither) = 0;
  434.    
  435.     # args=(seuil, niveaux..., -max, pixels...)
  436.     my($seuil, $max, $t, @niv, @px, @pal) = 0;
  437.    
  438.     foreach $t (@args) {
  439.         if($seuil==0) {
  440.             $seuil = $t;
  441.             $max = 0;
  442.         } elsif($max==0) {
  443.             if($t>=0) {
  444.                 push(@niv, $map_ef[$t]);
  445.             } else {
  446.                 $max = -$t;
  447.                 for($t=0; $t<256; ++$t) {
  448.                     my($m, $d, $n); $m = 1e30;
  449.                     foreach $n (@niv) {$d = $n - $t; $d = -$d if $d<0; if($d<$m) {$m = $d; $pal[$t] = $n;}}
  450.                 }
  451.             }
  452.         } else {
  453.             push(@px, $t) if 1;
  454.             push(@px, ((($map_ef[$t>>20]<<10) + $map_ef[($t>>10) & 0xff])<<10) + $map_ef[$t & 0xff]) if 0;
  455.         }
  456.     }
  457.     my($w, $h) = (320, 200);
  458.     ($w, $h) = (160, 200) if 160*200==1+$#px;
  459.     ($w, $h) = (160, 100) if 160*100==1+$#px;
  460.     ($w, $h) = (80, 100)  if 80*100==1+$#px;
  461.     ($w, $h) = (80, 50)   if 80*50==1+$#px;
  462.     $seuil = $seuil*$seuil*$w*$h;
  463.    
  464.     # color reduce
  465.     @niv = ();
  466.     my($x, $y, $p, $m, $d, @out);
  467.     if($dither) {
  468.         my @tmp = @px;
  469.         for($y=0, $p=0; $y<$h; ++$y) {
  470.             for($x=0; $x<$w; ++$x, ++$p) {
  471.                 $rvb = $px[$p];
  472.                 my ($r,$v,$b) = ($pal[$rvb>>20], $pal[($rvb>>10) & 0xff], $pal[$rvb & 0xff]);
  473.                 push(@niv, $r, $v, $b);
  474.                 $px[$p] = ($r<<20) + ($v<<10) + $b;
  475.                 $rvb = &fs_diff($rvb, $px[$p]);
  476.                 $px[$p + $w - 1] = &fs_prop($px[$p + $w - 1], $rvb, \@glb_map0) if $glb_err0 && $y<$h-1 && $x>0;
  477.                 $px[$p + $w + 0] = &fs_prop($px[$p + $w + 0], $rvb, \@glb_map1) if $glb_err1 && $y<$h-1;
  478.                 $px[$p + $w + 1] = &fs_prop($px[$p + $w + 1], $rvb, \@glb_map2) if $glb_err2 && $y<$h-1 && $x<$w-1;
  479.                 $px[$p + 000001] = &fs_prop($px[$p + 000001], $rvb, \@glb_map3) if $glb_err3 &&            $x<$w-1;
  480.             }
  481.         }
  482.         @px = @tmp;
  483.     } else {
  484.         foreach $t (@px) {push(@niv, $pal[$t>>20], $pal[($t>>10) & 0xff], $pal[$t & 0xff]);}
  485.     }
  486.     open(OUT,">.toto2.pnm"); print OUT "P6\n$w $h\n255\n", pack('C*', @niv), "\n"; close(OUT);
  487.     @$glb_magick = ();
  488.     $glb_magick->Set(page=>"$wx$h+0+0");
  489.     $glb_magick->Read(".toto2.pnm");
  490.     $glb_magick->Write(".toto3.png");
  491.     unlink(".toto2.pnm");
  492.    
  493.     $glb_magick->Quantize(colors=>$max, colorspace=>"RGB", treedepth=>0, dither=>"False");
  494.     $glb_magick->Write(".toto4.png");
  495.     @niv = $glb_magick->GetPixels(map=>"RGB", height=>$h, normalize=>"True");
  496.     my(%pal, $rvb);
  497.     for($t=$#niv+1; ($t-=3)>=0;) {
  498.         $rvb = &rgb2irgb(@niv[$t..$t+2]);
  499.         $rvb = ((($pal[$rvb>>20]<<10) + $pal[($rvb>>10) & 0xff])<<10) + $pal[$rvb & 0xff];
  500.         $pal{$rvb} = 1;
  501.     }
  502.     @niv = (keys(%pal), (0) x $max)[0..($max-1)];
  503.    
  504.     # dither & calcul d'erreur
  505.     my($err) = 0;
  506.     my($cache, %cache) = 1;
  507.     for($y=$p=0; $err < $seuil && $y<$h; ++$y) {
  508.         for($x=0; $err < $seuil && $x<$w; ++$x, ++$p) {
  509.             $rvb = $px[$p];
  510.             # on trouve le niv le plus proche
  511.             $t = $cache{$rvb} if $cache;
  512.             if(!$cache || !defined($t)) {
  513.                 $m = 1e30; foreach $t (@niv) {$d = &irgb_dist($t, $rvb); if($d<$m) {$m = $d;$px[$p] = $t;}}
  514.                 $cache{$rvb} = $px[$p] if $cache;
  515.             } else {
  516.                 $m = &irgb_dist($t, $rvb);
  517.                 $px[$p] = $t;
  518.             }
  519.             push(@out, $px[$p]>>20, ($px[$p]>>10)&255, $px[$p]&255);
  520.             $err += &sq($m);
  521.             if($dither) {
  522.                 $rvb = &fs_diff($rvb, $px[$p]);
  523.                 $px[$p + $w - 1] = &fs_prop($px[$p + $w - 1], $rvb, \@glb_map0) if $glb_err0 && $y<$h-1 && $x>0;
  524.                 $px[$p + $w + 0] = &fs_prop($px[$p + $w + 0], $rvb, \@glb_map1) if $glb_err1 && $y<$h-1;
  525.                 $px[$p + $w + 1] = &fs_prop($px[$p + $w + 1], $rvb, \@glb_map2) if $glb_err2 && $y<$h-1 && $x<$w-1;
  526.                 $px[$p + 000001] = &fs_prop($px[$p + 000001], $rvb, \@glb_map3) if $glb_err3 &&            $x<$w-1;
  527.             }
  528.         }
  529.     }
  530.    
  531.     if($err < $seuil) {
  532.         open(OUT,">.toto2.pnm"); print OUT "P6\n$w $h\n255\n", pack('C*', @out), "\n"; close(OUT);
  533.         @$glb_magick = ();
  534.         $glb_magick->Set(page=>"$wx$h+0+0");
  535.         $glb_magick->Read(".toto2.pnm");
  536.         $glb_magick->Write(".toto2.png");
  537.         unlink(".toto2.pnm");
  538.     }
  539.    
  540.     # fini
  541.     $glb_magick->Set(page=>"320x200+0+0");
  542.     sleep(0.5);
  543.     return (sprintf("%.05f", sqrt($err/$w/$h)), @niv);
  544. }
  545.  
  546. # calcul d'une palette de 16 couleurs
  547. sub find_palette_orig {
  548.     my($max, @px) = @_;
  549.  
  550.     # cas TO7
  551.     return &to770_palette if $glb_to7pal;
  552.    
  553.     # vrai algo
  554.     my($mask) = 0x0f03c0f0; $mask = -1;
  555.    
  556.     # si l'image a suffisament peu de couleurs alors on retourne la palette de l'image
  557.     # directement
  558.     my($i, %pal);
  559.     foreach $i (@px) {
  560.         $pal{$i & $mask} = 1;
  561.         last if length(keys %pal)>$max;
  562.     }
  563.     my(@t) = keys(%pal);
  564.     return @t if $#t<$max;
  565.     %pal = ();
  566.    
  567.     # sinon on quantifie l'image:
  568.    
  569.     #return &xxx_palette($max, @px) if $#map_ef>=0;
  570.    
  571.     # on remap l'image aux niveau produits par les puces thomson!
  572.     if($#map_ef>=0) {
  573.         @t = ();
  574.         my($x, $y, $p, $rvb, $r, $v, $b);
  575.         for($y=0, $p=0; $y<200; ++$y) {
  576.             for($x=0; $x<320; ++$x, ++$p) {
  577.                 $rvb = $px[$p];
  578.         $r=$map_ef[$rvb>>20]; $v=$map_ef[($rvb>>10) & 0xff]; $b=$map_ef[$rvb & 0xff];
  579.                 push(@t, &ammag($r), &ammag($v), &ammag($b));
  580.                 #push(@t, $r=($rvb>>20), $v=(($rvb>>10) & 0xff), $b=($rvb & 0xff));
  581.                 if(1) {
  582.                     $px[$p] = ((($r<<10)+$v)<<10)+$b;
  583.                     $rvb = &irgb_sub($rvb, $px[$p]);
  584.                     $px[$p + 319] = &irgb_uadd($px[$p + 319], &irgb_map($rvb, \@glb_map0)) if $glb_err0 && $y<199 && $x>0;
  585.                     $px[$p + 320] = &irgb_uadd($px[$p + 320], &irgb_map($rvb, \@glb_map1)) if $glb_err1 && $y<199;
  586.                     $px[$p + 321] = &irgb_uadd($px[$p + 321], &irgb_map($rvb, \@glb_map2)) if $glb_err2 && $y<199 && $x<319;
  587.                     $px[$p + 001] = &irgb_uadd($px[$p + 001], &irgb_map($rvb, \@glb_map3)) if $glb_err3 &&           $x<319;
  588.                 }
  589.             }
  590.         }
  591.         open(OUT,">.toto2.pnm"); print OUT "P6\n320 200\n255\n", pack('C*', @t), "\n"; close(OUT);
  592.         @$glb_magick = ();
  593.         $glb_magick->Read(".toto2.pnm");
  594.         #$glb_magick->Resize(geometry=>"160x100!");
  595.         #$glb_magick->Resize(geometry=>"320x200!");
  596.         $glb_magick->Write(".toto2.png");
  597.         #$glb_magick->Read(".toto2.png");
  598.         unlink(".toto2.pnm");
  599.     }
  600.  
  601.     if(0) { #recherche
  602.         # sinon on quantifie l'image:
  603.     my($c, $err, @pal, $e, @p) = (0, 1e30);
  604.    
  605.     # on divise le nombre de pixels par 4
  606.     if(1) {
  607.         my($x, $y, $p, @t);
  608.         for($p=$y=0; $y<200; $y+=2, $p+=320) {
  609.             for($x=0; $x<320; $x+=2, $p+=2) {
  610.                 push(@t, &irgb_avg(&irgb_avg($px[$p], $px[$p+1]), &irgb_avg($px[$p+320], $px[$p+321])));
  611.             }
  612.         }
  613.         @px = @t; @t = ();
  614.         if(0) {
  615.             for($p=$y=0; $y<100; $y+=1, $p+=0) {
  616.                 for($x=0; $x<160; $x+=2, $p+=2) {
  617.                     push(@t, &irgb_avg($px[$p], $px[$p+1]));
  618.                 }
  619.             }
  620.             @px = @t; @t = ();
  621.             if(0) {
  622.                 for($p=$y=0; $y<100; $y+=2, $p+=80) {
  623.                     for($x=0; $x<80; $x+=1, $p+=1) {
  624.                         push(@t, &irgb_avg($px[$p], $px[$p+80]));
  625.                     }
  626.                 }
  627.                 @px = @t; @t = ();
  628.             }
  629.         }
  630.     }
  631.    
  632.     # 0
  633.     ($e, @p) = &test_niveaux($err, @ef_vals, -$max, @px);
  634.     print $c++,"=$e"; if($e < $err) {@pal = @p; $err = $e; print "*";} print "\n";
  635.  
  636.     # 1
  637.     #($e, @p) = &test_niveaux($err, (0, 16, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192, 208, 224, 240, 255), -$max, @px);
  638.     ($e, @p) = &test_niveaux($err, (0, 50, 100, 150, 200, 250), -$max, @px);
  639.     print $c++,"=$e"; if($e < $err) {@pal = @p; $err = $e; print "*";} print "\n";
  640.  
  641.     # 2
  642.     ($e, @p) = &test_niveaux($err, (0, 32, 64, 96, 128, 160, 192, 224, 255), -$max, @px);
  643.     print $c++,"=$e"; if($e < $err) {@pal = @p; $err = $e; print "*";} print "\n";
  644.  
  645.     # 3
  646.     ($e, @p) = &test_niveaux($err, (0, 50, 100, 200, 255), -$max, @px);
  647.     print $c++,"=$e"; if($e < $err) {@pal = @p; $err = $e; print "*";} print "\n";
  648.    
  649.     # 4
  650.     ($e, @p) = &test_niveaux($err, (0, 100, 140, 180, 200, 220, 240, 255), -$max, @px);
  651.     print $c++,"=$e"; if($e < $err) {@pal = @p; $err = $e; print "*";} print "\n";
  652.  
  653.     # 5
  654.     ($e, @p) = &test_niveaux($err, (0, 32, 64, 128, 192, 224, 255), -$max, @px);
  655.     print $c++,"=$e"; if($e < $err) {@pal = @p; $err = $e; print "*";} print "\n";
  656.  
  657.     # 6
  658.     ($e, @p) = &test_niveaux($err, (0, 16, 32, 48, 80, 112, 144, 208, 208, 240, 255), -$max, @px);
  659.     print $c++,"=$e"; if($e < $err) {@pal = @p; $err = $e; print "*";} print "\n";
  660.  
  661.     # 7
  662.     ($e, @p) = &test_niveaux($err, (0, 16, 32, 64, 128, 192, 224, 240, 255), -$max, @px);
  663.     print $c++,"=$e"; if($e < $err) {@pal = @p; $err = $e; print "*";} print "\n";
  664.    
  665.     # 8
  666.     ($e, @p) = &test_niveaux($err, (0, 64, 128, 192, 255), -$max, @px);
  667.     print $c++,"=$e"; if($e < $err) {@pal = @p; $err = $e; print "*";} print "\n";
  668.  
  669.     # 9
  670.     ($e, @p) = &test_niveaux($err, (0, 48, 96, 144, 192, 255), -$max, @px);
  671.     print $c++,"=$e"; if($e < $err) {@pal = @p; $err = $e; print "*";} print "\n";
  672.    
  673.     # 10
  674.     ($e, @p) = &test_niveaux($err, (0, 96, 112, 128, 144, 192, 255), -$max, @px);
  675.     print $c++,"=$e"; if($e < $err) {@pal = @p; $err = $e; print "*";} print "\n";
  676.  
  677.     # 11
  678.     ($e, @p) = &test_niveaux($err, (0, 128, 255), -$max, @px);
  679.     print $c++,"=$e"; if($e < $err) {@pal = @p; $err = $e; print "*";} print "\n";
  680.  
  681.     # 12
  682.     ($e, @p) = &test_niveaux($err, (0, 39, 101, 195, 255), -$max, @px);
  683.     print $c++,"=$e"; if($e < $err) {@pal = @p; $err = $e; print "*";} print "\n";
  684.    
  685.     return @pal;
  686.     }
  687.  
  688.    
  689.    
  690.     my($colorspace) = "CMYK";  
  691.     #$colorspace="HSV";
  692.     $colorspace = "RGB";
  693.     #$colorspace="YUV";
  694.     $glb_magick->AdaptiveResize(geometry=>"80x200!") if 0;
  695.     $glb_magick->Posterize(levels=>16, dither=>"False") if 0;
  696.     $glb_magick->Posterize(levels=>6, dither=>"False") if 0;
  697.     $glb_magick->Posterize(levels=>4, dither=>"False") if 0;
  698.     $glb_magick->Posterize(levels=>3, dither=>"False") if 0;
  699.     # pas mal du tout:
  700.     $glb_magick->Quantize(colors=>$max, colorspace=>$colorspace, treedepth=>0, dither=>"False");
  701.     @t = $glb_magick->GetPixels(map=>"RGB", height=>200, normalize=>"True");
  702.     for($i=$#t+1; ($i-=3)>=0;) {
  703.         $rvb = &rgb2irgb(@t[$i..$i+2]);
  704.         #$rvb = ((($map_ef2[$rvb>>20]<<10) + $map_ef2[($rvb>>10) & 0xff])<<10) + $map_ef2[$rvb & 0xff] if $#map_ef>=0;
  705.         $rvb = ((($map_ef[$rvb>>20]<<10) + $map_ef[($rvb>>10) & 0xff])<<10) + $map_ef[$rvb & 0xff] if $#map_ef>=0;
  706.         $pal{$rvb & $mask} = 1;
  707.     }
  708.     @t = (keys(%pal), (0) x $max)[0..($max-1)];
  709.     #foreach $t (@t) {  print &irgb2hex($t), "\n"; }
  710.     return @t;
  711. }
  712.  
  713. # calcul d'une palette de 16 couleurs
  714. sub find_palette {
  715.     my($max, @px) = @_;
  716.  
  717.     # cas TO7
  718.     return &to770_palette if $glb_to7pal;
  719.    
  720.     # vrai algo
  721.     my($mask) = 0x0f03c0f0; $mask = -1;
  722.    
  723.     # si l'image a suffisament peu de couleurs alors on retourne la palette de l'image
  724.     # directement
  725.     my($i, %pal);
  726.     foreach $i (@px) {
  727.         $pal{$i & $mask} = 1;
  728.         last if length(keys %pal)>$max;
  729.     }
  730.     my(@t) = keys(%pal);
  731.     #for $t (@t) {
  732.     #       print &irgb2hex($t), "  = ", $pal{$t},"\n";
  733.     #}
  734.     return @t if $#t<$max;
  735.     %pal = ();
  736.    
  737.     # sinon on quantifie l'image:
  738.     my($use_dith) = 1;
  739.     my($alt) = 0;
  740.    
  741.     #return &xxx_palette($max, @px) if $#map_ef>=0;
  742.    
  743.     # on remap l'image aux niveau produits par les puces thomson!
  744.     if($#map_ef>=0) {
  745.         @t = simple_dither($use_dith, @px) unless $alt;
  746.         @t = prox_dither  ($use_dith, @px) if $alt;
  747.     }
  748.    
  749.     # idee par groupe de $w pixels on sature les valeurs RGB avec
  750.     # les min/max ontenus pour ce groupe. L'idee est de réduire
  751.     # la disperssion des couleurs
  752.     if(1) {
  753.         my($w) = 8;
  754.         for($i=0; $i<=$#t; $i+=3*$w) {
  755.             my($r,$v,$b) = (1,1,1);
  756.             my($R,$V,$B) = (0,0,0);
  757.             my($j);
  758.             for($j=$i; $j<$i+$w*3; $j+=3) {
  759.                 $r = $t[$j+0] if $t[$j+0]<$r;
  760.                 $v = $t[$j+1] if $t[$j+1]<$v;
  761.                 $b = $t[$j+2] if $t[$j+2]<$b;
  762.                 $R = $t[$j+0] if $t[$j+0]>$R;
  763.                 $V = $t[$j+1] if $t[$j+1]>$V;
  764.                 $B = $t[$j+2] if $t[$j+2]>$B;
  765.             }
  766.             my($t) = 0.5;
  767.             for($j=$i; $j<$i+$w*3; $j+=3) {
  768.                 $t[$j+0] = $t[$j+0] < (1-$t)*$r + $t*$R ? $r : $R;
  769.                 $t[$j+1] = $t[$j+1] < (1-$t)*$v + $t*$V ? $v : $V;
  770.                 $t[$j+2] = $t[$j+2] < (1-$t)*$b + $t*$B ? $b : $B;
  771.             }
  772.         }
  773.     }
  774.    
  775.     # on réduit à 64 couls
  776.     $glb_magick->Quantize(colors=>($alt?48:24)*0+64*1+128*0+256*0, colorspace=>"RGB", treedepth=>0, dither=>($use_dith && !$alt & 0?"True":"False"));
  777.     $glb_magick->Write("toto3.gif");
  778.     @t = $glb_magick->GetPixels(map=>"RGB", height=>200, normalize=>"True");
  779.    
  780.     # on comptabilise les couleurs renormalisées au format Thomson
  781.     %pal = ();
  782.     $pal{0} = 1+$#t;
  783.     for($i=$#t+1; ($i-=3)>=0;) {
  784.         $rvb = &rgb2irgb(@t[$i..$i+2]);
  785.         #$rvb = ((($map_ef2[$rvb>>20]<<10) + $map_ef2[($rvb>>10) & 0xff])<<10) + $map_ef2[$rvb & 0xff] if $#map_ef>=0;
  786.         $rvb = ((($map_ef[$rvb>>20]<<10) + $map_ef[($rvb>>10) & 0xff])<<10) + $map_ef[$rvb & 0xff] if $#map_ef>=0;
  787.         ++$pal{$rvb & $mask};
  788.     }
  789.    
  790.     # on trie par frequence
  791.     my(@cpt) = (sort { $pal{$b} - $pal{$a} } keys %pal);   
  792.    
  793.     # selection par popularité?
  794.     return  (@cpt, (0) x $max)[0..($max-1)] if 0;
  795.    
  796.     # affichage des stats
  797.     my($dbg) = 0;
  798.     if($dbg) {
  799.         for $t (@cpt) {
  800.             print &irgb2hex($t), "  = ", $pal{$t},"\n";
  801.         }
  802.     }
  803.  
  804.     # on coupe les couls sous-représentées
  805.     my($thr) = 8;
  806.     @t = @cpt; @cpt = ();
  807.     for $t (@t) {
  808.         push(@cpt, $t) if $pal{$t} >= $thr;
  809.     }
  810.        
  811.     # on prend la couleur la plus frequente, puis la plus loin de celle là jusqu'à 10 couls ensuite une fois sur 2 on prend la plus ancienne
  812.     @t = ();
  813.     push(@t, shift(@cpt));
  814.     while($#t < $max && $#cpt>=0) {
  815.         #print "\n\n";
  816.         #for $t (@t) {
  817.         #   print &irgb2hex($t), "  = ", $pal{$t},"\n";
  818.         #}
  819.         if($#t < 10 || ($#t & 1)) {
  820.             #print "\n\n\n$#t, plus loin\n";
  821.             $i = &find_furthest(\@t, \@cpt);
  822.             push(@t, splice(@cpt, $i, 1, ()));
  823.         } else {
  824.             #print "\n\n\n$#t, plus freq";
  825.             # on prends la plus frequente
  826.             push(@t, shift(@cpt));
  827.         }
  828.     }
  829.    
  830.     # on complète avec des zero
  831.     @t = (@t, (0) x $max)[0..($max-1)];
  832.     $dbg = 0;
  833.     if($dbg) {
  834.         print "\n\n";foreach $t (@t) {
  835.             my($r) = &ammag(($t>>20) & 0x1ff);
  836.             my($g) = &ammag(($t>>10) & 0x1ff);
  837.             my($b) = &ammag(($t>>00) & 0x1ff);
  838.             print &irgb2hex($t), " = ", $pal{$t}, " ", $r,",",$g,",",$b," ",$t,"\n";
  839.         }
  840.     }
  841.    
  842.     return @t;
  843. }
  844.  
  845. # dithering simple sans contraintes de proximité
  846. sub simple_dither {
  847.     my($use_dith, @px) = @_;
  848.    
  849.     my($x, $y, $p, $rvb, $r, $v, $b, @t);
  850.     for($y=0, $p=0; $y<200; ++$y) {
  851.         for($x=0; $x<320; ++$x, ++$p) {
  852.             $rvb = $px[$p];
  853.             $r=$map_ef[$rvb>>20]; $v=$map_ef[($rvb>>10) & 0xff]; $b=$map_ef[$rvb & 0xff];
  854.             push(@t, &ammag($r), &ammag($v), &ammag($b));
  855.             #push(@t, $r=($rvb>>20), $v=(($rvb>>10) & 0xff), $b=($rvb & 0xff));
  856.             if($use_dith) {
  857.                 $px[$p] = ((($r<<10)+$v)<<10)+$b;
  858.                 $rvb = &irgb_sub($rvb, $px[$p]);
  859.                 $px[$p + 319] = &irgb_uadd($px[$p + 319], &irgb_map($rvb, \@glb_map0)) if $glb_err0 && $y<199 && $x>0;
  860.                 $px[$p + 320] = &irgb_uadd($px[$p + 320], &irgb_map($rvb, \@glb_map1)) if $glb_err1 && $y<199;
  861.                 $px[$p + 321] = &irgb_uadd($px[$p + 321], &irgb_map($rvb, \@glb_map2)) if $glb_err2 && $y<199 && $x<319;
  862.                 $px[$p + 001] = &irgb_uadd($px[$p + 001], &irgb_map($rvb, \@glb_map3)) if $glb_err3 &&           $x<319;
  863.             }
  864.         }
  865.     }
  866.     open(OUT,">.toto2.pnm"); print OUT "P6\n320 200\n255\n", pack('C*', @t), "\n"; close(OUT);
  867.     @$glb_magick = ();
  868.     $glb_magick->Read(".toto2.pnm");
  869.     unlink(".toto2.pnm");
  870.    
  871.     $glb_magick->Write("toto2_.png");
  872.    
  873.     return @t;
  874. }
  875.  
  876. # dither sans contraintes de couleurs, mais avec contrainte de proximité
  877. sub prox_dither {
  878.     my($use_dith, @px) = @_;
  879.    
  880.     my($x, $y, $fond, $forme, $i, $p, $rvb, $r, $v, $b, @t);
  881.     for($y=$p=0; $y<200; ++$y) {
  882.         for($x=0; $x<320; $x += 8) {
  883.             ($fond, $forme) = &prox_couple(@px[$p..$p+7]);
  884.             for($i = 0; $i<8; ++$i, ++$p) {
  885.                 $rvb = $px[$p];
  886.                 $rvb = &irgb_dist($fond, $rvb) < &irgb_dist($forme, $rvb) ? $fond : $forme;
  887.                 $r=$map_ef[$rvb>>20]; $v=$map_ef[($rvb>>10) & 0xff]; $b=$map_ef[$rvb & 0xff];
  888.                 push(@t, &ammag($r), &ammag($v), &ammag($b));
  889.                 if($use_dither | 1) {
  890.                     #$px[$p] = ((($r<<10)+$v)<<10)+$b;
  891.                     $rvb = &irgb_sub($px[$p], $rvb);
  892.                     $px[$p + 319] = &irgb_uadd($px[$p + 319], &irgb_map($rvb, \@glb_map0)) if $glb_err0 && $y<199 && $x+$i>0;
  893.                     $px[$p + 320] = &irgb_uadd($px[$p + 320], &irgb_map($rvb, \@glb_map1)) if $glb_err1 && $y<199;
  894.                     $px[$p + 321] = &irgb_uadd($px[$p + 321], &irgb_map($rvb, \@glb_map2)) if $glb_err2 && $y<199 && $x+$i<319;
  895.                     $px[$p + 001] = &irgb_uadd($px[$p + 001], &irgb_map($rvb, \@glb_map3)) if $glb_err3 &&           $x+$i<319;
  896.                 }
  897.             }
  898.         }
  899.     }
  900.     open(OUT,">.toto2.pnm"); print OUT "P6\n320 200\n255\n", pack('C*', @t), "\n"; close(OUT);
  901.     @$glb_magick = ();
  902.     $glb_magick->Read(".toto2.pnm");
  903.     #$glb_magick->Write(".toto2.png");
  904.     unlink(".toto2.pnm");
  905.    
  906.     return @t;
  907. }
  908.  
  909. sub prox_couple {
  910.     my(@octet) = @_;
  911.    
  912.     my($i, $im, $j, $jm, $d, $dm, $rgb, $r, $g, $b, %cpt, @px);
  913.        
  914.     # dither de l'octet sans contraintes pour extraire les stats
  915.     @px = (@octet);
  916.     for($i=0; $i<8; ++$i) {
  917.         $rgb = $px[$i];
  918.         $r=$map_ef[$rgb>>20]; $v=$map_ef[($rgb>>10) & 0xff]; $b=$map_ef[$rgb & 0xff];
  919.         ++$cpt{$px[$i] = ((($r<<10)+$v)<<10)+$b};
  920.         $px[$i+1] = &irgb_add_cln($px[$i+1], &irgb_map(&irgb_sub($rgb, $px[$i]), \@glb_map3)) if $i<7;
  921.     }
  922.    
  923.     # on trie par frequence
  924.     my(@cpt) = (sort { $cpt{$b} - $cpt{$a} } keys %cpt);
  925.    
  926.     #print "\n\n";
  927.     #foreach $t (@octet) {
  928.     #   print &irgb2hex($t), " ";
  929.     #}
  930.     #print "\n\n";
  931.     #foreach $t (@cpt) {
  932.     #   print &irgb2hex($t), " = ", $cpt{$t}, "\n";
  933.     #}
  934.  
  935.     # 1 ou 2 couls utilisées: pas de probs
  936.     if($#cpt<=1) {
  937.         # on s'assure qu'on en a au moins 2
  938.         $cpt[1] = $cpt[0] if $#cpt==0;
  939.  
  940.         return ($cpt[1], $cpt[0]);
  941.     }
  942.    
  943.     # les 2 couls principales couvrent 7 pixels sur les 8, on les gardes, tant pis pour la mintorité
  944.     if($cpt{$cpt[0]} + $cpt{$cpt[1]} >= 6) {
  945.         return ($cpt[1], $cpt[0]);        
  946.     }
  947.    
  948.     # si la 1ere couvre 4 pixels, on prend comme 2eme celle qui fait le moins d'err
  949.     if($cpt{$cpt[0]} >= 6) {
  950.         $dm = 1e30;
  951.         for($i=1; $i<=$#cpt; ++$i) {
  952.             @px = (@octet);
  953.             for($d = $j = 0; $j<8 && $d<$dm; ++$j) {
  954.                 $d1 = &irgb_dist($cpt[0], $px[$j]);
  955.                 $d2 = &irgb_dist($cpt[$i],$px[$j]);
  956.                 if($d1 < $d2) {$d += &sq($d1); $rgb = $cpt[0];} else {$d += &sq($d2); $rgb = $cpt[$i];}
  957.                 $px[$j+1] = &irgb_add_cln($px[$j+1], &irgb_map(&irgb_sub($px[$j], $rgb), \@glb_map3)) if $glb_err3 && $j<7;
  958.             }
  959.             if($d < $dm) {$dm = $d; $im = $i;}
  960.         }
  961.         return ($cpt[$im], $cpt[0]);
  962.     }
  963.  
  964.     # sinon tester tous les couple avec dither
  965.     my($r, $rm);
  966.     $dm = 1e30;
  967.     for($i=0; $i<=$#cpt; ++$i) {
  968.         for($j=0; $j<$i; ++$j) {
  969.             @px = (@octet);
  970.             for($r = $d = $k = 0; $k<8 && $d<$dm; ++$k) {
  971.                 $di = &irgb_dist($cpt[$i], $px[$k]);
  972.                 $dj = &irgb_dist($cpt[$j], $px[$k]);
  973.                 if($di <= $dj) {$r |= 1; $rgb = $cpt[$i]; $d += &sq($di);} else {$r |= 2; $rgb = $cpt[$j]; $d += &sq($dj);}
  974.                 $px[$k+1] = &irgb_add_cln($px[$k+1], &irgb_map(&irgb_sub($px[$k], $rgb), \@glb_map3)) if $glb_err3 && $k<7;
  975.             }
  976.             if($d < $dm) {$rm = $r; $dm = $d; $im = $i; $jm = $j}
  977.         }
  978.     }
  979.     return ($cpt[$im], $cpt[$jm]);
  980. }
  981.  
  982. sub find_furthest {
  983.     my ($set, $cols) = @_;
  984.     my ($d, $dm, $i, $im);
  985.     for($i = $#{$cols}, $dm = $im = 0; $i>=0; --$i) {
  986.         $d = &set_dist($cols->[$i], $set);
  987.         #print "$i ", &irgb2hex($cols->[$i])," ==> $d, $dm\n";
  988.         if($d > $dm) {$dm = $d; $im = $i; #print"^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n";
  989.         }
  990.     }
  991.     #print "*** ", &irgb2hex($cols->[$im])," $im ($dm)\n";
  992.     return $im;
  993. }
  994.  
  995. sub set_dist {
  996.     my($rgb, $set) = @_;
  997.     my($dm, $d, $col) = 1e30;
  998.     my $dc = 0;
  999.     foreach $col (@$set) {
  1000.         $d = &irgb_dist($rgb, $col);
  1001.         $dc += $d;
  1002.         #print &irgb2hex($rgb),",",&irgb2hex($col), "====$d\n";
  1003.         if($d<$dm) {$dm = $d;}
  1004.     }
  1005.     return $dm; # + $dc;
  1006. }
  1007.  
  1008. # sauvegarde de l'image
  1009. sub write_image {
  1010.     my($file, @px) = @_;
  1011.    
  1012.     # on replace tout entre 0 et 255
  1013.     my($t, $c, @p);
  1014.     foreach $t (@px) {
  1015.         my($b) = $t & 0x100 ? 0 : $t & 0xff; $t >>= 10;
  1016.         my($v) = $t & 0x100 ? 0 : $t & 0xff; $t >>= 10;
  1017.         my($r) = $t & 0x100 ? 0 : $t & 0xff;
  1018.         if(0        && $#map_ef>=0) {
  1019.             $r = $map_ef2[$r];
  1020.             $v = $map_ef2[$v];
  1021.             $b = $map_ef2[$b];
  1022.         }
  1023.     push(@p, &ammag($r), &ammag($v), &ammag($b)); #, $r, $v, $b);
  1024.     }
  1025.     # on passe par un fichier temporaire
  1026.     open(OUT,">.toto.pnm"); print OUT "P6\n320 200\n255\n", pack('C*', @p), "\n"; close(OUT);
  1027.     #open(OUT, ">.toto.pnm"); print OUT "P6\n640 400\n255\n"; for($t = 0; $t<=$#p; $t+=640*3) {print OUT pack('C*', @p[$t..$t+640*3-1], @p[$t..$t+640*3-1]);} print OUT "\n"; close(OUT);
  1028.     @$glb_magick = ();
  1029.     $glb_magick->Read(".toto.pnm");
  1030.     $glb_magick->Set(page=>"320x200+0+0");
  1031. #    $glb_magick->Set(page=>"640x400+0+0");
  1032.     #$glb_magick->Gamma(gamma=>1.2);
  1033.     #$glb_magick->Resize(geometry=>"640x400!");
  1034.     #$glb_magick->Border(width=>"320",height=>"100",color=>"black");    
  1035.     #unlink(".toto.pnm");
  1036.  
  1037.     # sauvegarde
  1038.     $glb_magick->Write($file);
  1039. }
  1040.  
  1041. # gamma / normalize / sigmoidal
  1042. # 0 = orig / Linear / off
  1043. # 1 = orig / Linear / on
  1044. # 2 = orig / Normalize / off
  1045. # 3 = orig / Normalize / on
  1046. # 4 = gamma / Linear / off
  1047. # 5 = gamma / Linear / on
  1048. # 6 = gamma / Normalize / off
  1049. # 7 = gamma / Normalize / on
  1050.  
  1051. # lit une image au format 320 x 200 sous la forme r10v10b10
  1052. sub read_image {
  1053.     my($file) = @_;
  1054.  
  1055.     @$glb_magick = ();     
  1056.     my($x) = $glb_magick->Read($file);
  1057.     warn "$x" if "$x";
  1058.  
  1059.     # formattage en 320x200 si necessaire
  1060.     $glb_magick->Enhance();
  1061.     $glb_magick->Normalize(); #
  1062.     #$glb_magick->LinearStretch('black-point'=>0, 'white-point'=>1);
  1063.     #$glb_magick->Contrast(sharpen=>"True");
  1064.     #$glb_magick->Set(antialias=>"True");
  1065.     $glb_magick->SigmoidalContrast(contrast=>2);
  1066.     $glb_magick->AdaptiveResize(geometry=>"320x200", filter=>"lanczos", blur=>1);
  1067.     $glb_magick->Border(width=>"320",height=>"100",color=>"black");
  1068.     #  $glb_magick->Blur(1);
  1069.     #  $glb_magick->OilPaint(2);
  1070.     $glb_magick->Set(gravity=>"Center");
  1071.     #   $glb_magick->Crop(geometry=>"320x200!", gravity=>"center");
  1072.     $glb_magick->Crop(geometry=>"320x200!");
  1073.     $glb_magick->Set(page=>"320x200+0+0");
  1074.     $glb_magick->Resize(geometry=>"320x200!", filter=>"lanczos", blur=>1);
  1075.     #$glb_magick->ReduceNoise(radius=>0);
  1076.     #$glb_magick->Gamma(gamma=>0.8) if $glb_to7pal;
  1077.     #$glb_magick->Gamma(gamma=>0.45);
  1078.     #$glb_magick->AdaptiveSharpen(radius=>3);
  1079.     #$glb_magick->AdaptiveBlur(radius=>4);
  1080.     #$glb_magick->Contrast(sharpen=>"True");
  1081.     #$glb_magick->Evaluate(operator=>"Multiply", value=>"0.9");
  1082.  
  1083.     #$glb_magick->Quantize(colors=>$glb_maxcol, colorspace=>"CYMK", dither=>"True");
  1084.     #$glb_magick->OrderedDither(threshold=>"h4x4", channel=>"RGB");
  1085.     if($glb_dith>=2) {
  1086.         #dither en 16 couls
  1087.         my(@t) = $glb_magick->GetPixels(map=>"RGB", height=>200, normalize=>"True");
  1088.         my($i, $j, $t, $p, @p);
  1089.         my($m) = $glb_dith-1;
  1090.         for($j=$p=0; $j<200; ++$j) {
  1091.             for($i=0; $i<320; ++$i) {
  1092.                 $t = $t[$p] * $m; ++$t if $t<$m && $t - int($t)>=$mat[$i % $mat_x][$j % $mat_y]; $t[$p++] = (int($t)*255)/$m;
  1093.                 $t = $t[$p] * $m; ++$t if $t<$m && $t - int($t)>=$mat[$i % $mat_x][$j % $mat_y]; $t[$p++] = (int($t)*255)/$m;
  1094.                 $t = $t[$p] * $m; ++$t if $t<$m && $t - int($t)>=$mat[$i % $mat_x][$j % $mat_y]; $t[$p++] = (int($t)*255)/$m;
  1095.             }
  1096.         }    
  1097.         open(OUT,">.toto.pnm"); print OUT "P6\n320 200\n255\n", pack('C*', @t), "\n"; close(OUT);
  1098.         @$glb_magick = ();
  1099.         $glb_magick->Read(".toto.pnm");
  1100.         $glb_magick->Write(".toto.png");
  1101.         unlink(".toto.pnm");
  1102.     }
  1103.     my(@t) = $glb_magick->GetPixels(map=>"RGB", height=>200, normalize=>"True");
  1104.     my($i, @px);
  1105.     for($i = 0; $i<$#t; $i += 3) {
  1106.         push(@px, &rgb2irgb($t[$i], $t[$i+1], $t[$i+2]));
  1107.     }
  1108.    
  1109.     #$glb_magick->Write("totof.png");
  1110.    
  1111.     return @px;
  1112. }
  1113.  
  1114. sub ammag {
  1115.     return $_[0] unless $glb_gamma;
  1116.     my $t = $_[0]/255;
  1117.     #if($t<=0.018) {$t = 4.5*$t;} else {$t = 1.099*($t**(1/$glb_gamma))-0.099;}
  1118.     $t = $t**(1/$glb_gamma);
  1119.     return xint(255*$t);
  1120. }
  1121.  
  1122. sub gamma {
  1123.     return $_[0] unless $glb_gamma;
  1124.     my $t = $_[0]/255;
  1125.     #if($t<=0.081) {$t = $t/4.5;} else {$t = (($t+0.099)/1.099)**$glb_gamma;}
  1126.     $t = $t**$glb_gamma;
  1127.     return xint($t*255); #**1.2; #**1.4;
  1128. }
  1129.  
  1130. # affichage
  1131. sub irgb2hex {
  1132.     my($irgb) = @_;
  1133.     my($s) = "";
  1134.     if($irgb & 0x100) {$s = sprintf("-%02x$s", (($irgb ^ 0x1ff)&0xff) + 1);} else {$s = sprintf("+%02x$s", $irgb & 0xff);} $irgb >>= 10;
  1135.     if($irgb & 0x100) {$s = sprintf("-%02x$s", (($irgb ^ 0x1ff)&0xff) + 1);} else {$s = sprintf("+%02x$s", $irgb & 0xff);} $irgb >>= 10;
  1136.     if($irgb & 0x100) {$s = sprintf("-%02x$s", (($irgb ^ 0x1ff)&0xff) + 1);} else {$s = sprintf("+%02x$s", $irgb & 0xff);} $irgb >>= 10;
  1137.     return $s;
  1138. }
  1139.  
  1140. # addition d'une valeur irgb signée .. inclu saturation -256 +255
  1141. sub irgb_add {
  1142.     my($irgb1, $irgb2) = @_;
  1143.    
  1144.     my($r) = $irgb1 + $irgb2;
  1145.     my($o) = (($irgb1 & 0x0ff3fcff) + ($irgb2 & 0x0ff3fcff)) ^ ($r>>1);
  1146.     $r = ($r & ~0x000003ff) | (0x00000100 - (($r & 0x00000100)>>8)) if $o & 0x00000100;
  1147.     $r = ($r & ~0x000ffc00) | (0x00040000 - (($r & 0x00040000)>>8)) if $o & 0x00040000;
  1148.     $r = ($r & ~0x3ff00000) | (0x10000000 - (($r & 0x10000000)>>8)) if $o & 0x10000000;
  1149.     return $r & 0x1ff7fdff if 1; # saturation -256 et +255
  1150. }  
  1151.  
  1152. sub irgb_add_cln {
  1153.     my($t) = &irgb_add(@_);
  1154.    
  1155.     return $t;
  1156.    
  1157.     my($r, $g, $b);
  1158.  
  1159.     $r = ($t>>00) & 0x1FF;
  1160.     $g = ($t>>10) & 0x1FF;
  1161.     $b = ($t>>20) & 0x1FF;
  1162.  
  1163.     $r = ($r^0x1FF)+1 if $r & 0x100;
  1164.     $g = ($g^0x1FF)+1 if $g & 0x100;
  1165.     $b = ($b^0x1FF)+1 if $b & 0x100;
  1166.  
  1167.     my($M) = $r;
  1168.     $M = $g if $g>$M;
  1169.     $M = $b if $b>$M;
  1170.    
  1171.     $M/=4.2;
  1172.    
  1173.     $t &= ~(0x1FF<<00) if $r<$M;
  1174.     $t &= ~(0x1FF<<10) if $g<$M;
  1175.     $t &= ~(0x1FF<<20) if $b<$M;
  1176.    
  1177.     return $t;
  1178. }
  1179.  
  1180. # addition d'une valeur irgb signée .. inclu saturation 0 +255
  1181. sub irgb_uadd {
  1182.     return &irgb_sat(&irgb_add(@_));
  1183. }
  1184.  
  1185. # sature les irgb<0 à 0
  1186. sub irgb_sat {
  1187.     my($irgb) = @_;
  1188.    
  1189.     return (((0x10040100 - (($irgb & 0x10040100)>>8)) ^ 0xff3fcff) & $irgb) & 0xff3fcff;
  1190. }  
  1191.  
  1192. # soustraction de deux valeurs irgb>=0 (pas de satur)
  1193. sub irgb_sub {
  1194.     my($rgb1, $rgb2) = @_;
  1195.     return (($rgb1 | 0x20080200) - $rgb2) & 0x1ff7fdff;
  1196. }
  1197.  
  1198. sub irgb_sub2 {
  1199.     my($rgb1, $rgb2) = @_;
  1200.     my($t) = &irgb_sub(@_);
  1201.    
  1202.     my($r) = ($t>>00) & 0x1FF;
  1203.     my($g) = ($t>>10) & 0x1FF;
  1204.     my($b) = ($t>>20) & 0x1FF;
  1205.    
  1206.     $r = ($r^0x1FF)+1 if $r & 0x100;
  1207.     $g = ($g^0x1FF)+1 if $g & 0x100;
  1208.     $b = ($b^0x1FF)+1 if $b & 0x100;
  1209.    
  1210.     my($m) = ($r+$g+$b)/3;
  1211.     $r = 0 if $r<$m;
  1212.     $g = 0 if $g<$m;
  1213.     $b = 0 if $b<$m;
  1214.  
  1215.     $r = 0x1ff&(-$r) if $t & 0x100;
  1216.     $g = 0x1ff&(-$g) if $t & 0x100<<10;
  1217.     $b = 0x1ff&(-$b) if $t & 0x100<<20;
  1218.     return ((($b<<10)|$g)<<10)|$r;
  1219. }
  1220.  
  1221. # valeur opposée
  1222. sub irgb_neg {
  1223.     my($rgb) = @_;
  1224.     return (0x20080200 - $rgb) & 0x1ff7fdff;
  1225. }
  1226.  
  1227. # module du vecteur irgb
  1228. sub irgb_module {
  1229.     my($rgb) = @_;
  1230.     my($d);
  1231.     $d  = $glb_sqr[0x1ff & $rgb]; $rgb >>= 10;
  1232.     $d += $glb_sqr[0x1ff & $rgb]; $rgb >>= 10;
  1233.     $d += $glb_sqr[0x1ff & $rgb];
  1234.     return sqrt($d);
  1235. }
  1236.  
  1237. # applique une table sur un irgb (en gros ca sert pour les multiplications par des constantes)
  1238. sub irgb_map {
  1239.     my($rgb, $map) = @_;
  1240.     my($r);
  1241.     $r  = $map->[$rgb & 0x1ff];     $rgb >>= 10;
  1242.     $r |= $map->[$rgb & 0x1ff]<<10; $rgb >>= 10;
  1243.     $r |= $map->[$rgb]<<20;
  1244.     return $r;
  1245. }
  1246.  
  1247. sub irgb_cln {
  1248.     my($t) = @_;
  1249.    
  1250.     my($r) = ($t>>00) & 0x1FF;
  1251.     my($g) = ($t>>10) & 0x1FF;
  1252.     my($b) = ($t>>20) & 0x1FF;
  1253.    
  1254.     $r = ($r^0x1FF)+1 if $r & 0x100;
  1255.     $g = ($g^0x1FF)+1 if $g & 0x100;
  1256.     $b = ($b^0x1FF)+1 if $b & 0x100;
  1257.    
  1258.     #print "$r $g $b => ";
  1259.    
  1260.     my($m) = ($r+$g+$b)/4;
  1261.     $r = 0 if $r<$m;
  1262.     $g = 0 if $g<$m;
  1263.     $b = 0 if $b<$m;
  1264.    
  1265.     #print "$r $g $b     ";
  1266.  
  1267.     $r = 0x1ff&(-$r) if $t & 0x100;
  1268.     $g = 0x1ff&(-$g) if $t & 0x100<<10;
  1269.     $b = 0x1ff&(-$b) if $t & 0x100<<20;
  1270.     return ((($b<<10)|$g)<<10)|$r;
  1271. }
  1272.  
  1273. # rgb (0..1) vers irgb
  1274. sub rgb2irgb {
  1275.     my(@rgb) = @_;
  1276.     my($t);
  1277.     if($glb_gamma) {
  1278.         $rgb[0] = &gamma($rgb[0]*255)/255;
  1279.         $rgb[1] = &gamma($rgb[1]*255)/255;
  1280.         $rgb[2] = &gamma($rgb[2]*255)/255;
  1281.     }
  1282.     $t = (int(255*$rgb[0]) & 0x1ff);
  1283.     $t = (int(255*$rgb[1]) & 0x1ff) | ($t<<10);
  1284.     $t = (int(255*$rgb[2]) & 0x1ff) | ($t<<10);
  1285.     return $t;
  1286. }
  1287.  
  1288. # irgb vers rgb (0..1). si la composante est negative, elle est clampée à 0
  1289. sub irgb2rgb {
  1290.     my($t) = @_;
  1291.    
  1292.     my($b) = ($t & 0x100) ? 0 : ($t & 255)/255.0; $t >>= 10;
  1293.     my($v) = ($t & 0x100) ? 0 : ($t & 255)/255.0; $t >>= 10;
  1294.     my($r) = ($t & 0x100) ? 0 : ($t & 255)/255.0;
  1295.    
  1296.     return ($r, $v, $b);
  1297. }
  1298.  
  1299. # moyenne de 2 couleurs >= 0
  1300. sub irgb_avg {
  1301.     my($rgb1, $rgb2) = @_;
  1302.     return (($rgb1 + $rgb2 + 0x100401) & ~0x20180601)>>1;
  1303. }
  1304.  
  1305. # rgb (0..1) vers xyz
  1306. sub rgb2xyz {
  1307.     my($r, $v, $b) =  @_;
  1308.     return (0.618*$r + 0.177*$v + 0.205*$b,
  1309.         0.299*$r + 0.587*$v + 0.114*$b,
  1310.                            0.056*$v + 0.944*$b);
  1311. }
  1312.  
  1313. # xyz vers cie lab
  1314. sub xyz2lab {
  1315.     my($x, $y, $z) = @_;
  1316.     #my($xn, $yn, $zn) = &rgb2xyz(1,1,1); $x /= $xn; $y /= $yn; $z /= $zn;
  1317.     my($l,$a,$b);
  1318.     if($y>0.008856) {
  1319.         $l = 116*($y ** 0.33333333333333) - 16;
  1320.     } else {
  1321.         $l = 903*$y;
  1322.     }
  1323.     $a = 500*(&f_lab($x) - &f_lab($y));
  1324.     $b = 200*(&f_lab($y) - &f_lab($z));
  1325.     return ($l,$a,$b);
  1326. }
  1327.  
  1328. sub f_lab {
  1329.     my($v) = @_;
  1330.    
  1331.     if($v>0.008856) {
  1332.         return $v ** 0.333333333333333;
  1333.     } else {
  1334.         return 7.787*$v + 16/116.0;
  1335.     }
  1336. }
  1337.  
  1338. # rgb vers lab
  1339. sub rgb2lab {
  1340.     return &xyz2lab(&rgb2xyz(@_));
  1341. }
  1342.  
  1343. # approximated CIE formula from http://www.compuphase.com/cmetric.htm#GAMMA
  1344. sub irgb_cie_dist_fast {
  1345.     my($rgb1, $rgb2) = @_;
  1346.        
  1347.     my($rmean) = (($rgb1 + $rgb2) >> 21) & 0x1ff;
  1348.     my($rgb) = &irgb_sub($rgb1, $rgb2);
  1349.    
  1350.     $d  = ($glb_sqr[0x1ff & $rgb] * (512 + $rmean)) >> 8; $rgb >>= 10;
  1351.     $d +=  $glb_sqr[0x1ff & $rgb] * 4; $rgb >>= 10;
  1352.     $d += ($glb_sqr[0x1ff & $rgb] * (767 - $rmean)) >> 8;
  1353.     return sqrt($d);
  1354. }
  1355.  
  1356. # calcule la distance entre les deux couleurs r10g10b10
  1357. sub irgb_dist {
  1358.     my($rgb1, $rgb2) = @_;
  1359.     #die &irgb2hex($rgb1) if $rgb1 & 0x10040100;
  1360.     #die &irgb2hex($rgb2) if $rgb2 & 0x10040100;
  1361.     if($glb_lab) {
  1362.         return &irgb_cie_dist_fast($rgb1, $rgb2);
  1363.         #my($k) = $rgb1."_".$rgb2;
  1364.         my($d); # = $dist_cache{$k};
  1365.         #if(!defined $d) {
  1366.         my($r1, $g1, $b1) = &xyz2lab(&rgb2xyz(&irgb2rgb($rgb1)));
  1367.         my($r2, $g2, $b2) = &xyz2lab(&rgb2xyz(&irgb2rgb($rgb2)));
  1368.        
  1369.         $r1 -= $r2; $g1 -= $g2; $b1 -= $b2;
  1370.         $d = sqrt($r1*$r1 + $g1*$g1 + $b1*$b1);
  1371.         #$dist_cache{$k} = $d;
  1372.         #}
  1373.         return $d;
  1374.     } else {
  1375.         return &irgb_module(&irgb_sub($rgb1, $rgb2));
  1376.     }
  1377. }
  1378.  
  1379. # retourne le couple forme/fond pour un octet donné
  1380. sub couple {
  1381.     my(@octet) = @_;
  1382.    
  1383.     return &couple2(@octet) if 0;
  1384.     return &couple3(@octet) if 0;
  1385.     return &couple4(@octet) if 1;
  1386.    
  1387.     # on commence un dither classique mais horizontal de l'octet
  1388.     my($i, $im, $j, $jm, $d, $dm, $rgb, @octet_pal);
  1389.     $#octet_pal = 7;
  1390.     for($i=0; $i<8; ++$i) {
  1391.         $rgb = $octet[$i];
  1392.         # on trouve la coul la plus proche
  1393.         $dm = 1e30;
  1394.         for($j=0; $j<$glb_maxcol; ++$j) {
  1395.             $d = &irgb_dist($glb_pal[$j], $rgb);
  1396.             if($d<$dm) {$dm = $d; $octet_pal[$i] = $j;}
  1397.         }
  1398.         # on propage l'erreur
  1399.         #$qq = &irgb_map(&irgb_sub($octet[$i], $glb_pal[$octet_pal[$i]]), \@glb_map3);
  1400.         #print &irgb2hex($octet[$i]),",",&irgb2hex($glb_pal[$octet_pal[$i]])," e=",&irgb2hex(&irgb_sub($octet[$i], $glb_pal[$octet_pal[$i]])), " " if $qq;
  1401.         #print "m=",&irgb2hex($qq)," " if $qq;
  1402.         #print &irgb2hex($octet[$i + 1]), " => " if $qq;
  1403.         $octet[$i+1] = &irgb_add_cln($octet[$i+1], &irgb_map(&irgb_sub($rgb, $glb_pal[$octet_pal[$i]]), \@glb_map3)) if $i<7;
  1404.         #print &irgb2hex($octet[$i + 1]), "\n" if $qq;
  1405.     }
  1406.    
  1407.     # ensuite on trouve le couple qui conduit au minimum d'erreur
  1408.     $dm = 1e30; my($t, @line);
  1409.     for($i=0; $i<$glb_maxcol; ++$i) {
  1410.         for($j=0; $j<$i; ++$j) {
  1411.             $d  = &couple_dist($i, $j, $octet_pal[0]);
  1412.             $d += &couple_dist($i, $j, $octet_pal[1])  if $d<$dm;
  1413.             $d += &couple_dist($i, $j, $octet_pal[2])  if $d<$dm;
  1414.             $d += &couple_dist($i, $j, $octet_pal[3])  if $d<$dm;
  1415.             $d += &couple_dist($i, $j, $octet_pal[4])  if $d<$dm;
  1416.             $d += &couple_dist($i, $j, $octet_pal[5])  if $d<$dm;
  1417.             $d += &couple_dist($i, $j, $octet_pal[6])  if $d<$dm;
  1418.             $d += &couple_dist($i, $j, $octet_pal[7])  if $d<$dm;
  1419.            
  1420.             if($d<$dm) {$dm = $d; $im = $i; $jm = $j;}
  1421.         }
  1422.     }
  1423.    
  1424.     return ($glb_pal[$im], $glb_pal[$jm]);
  1425. }
  1426.  
  1427. sub couple_dist {
  1428.     my($forme, $fond, $pixel) = @_;
  1429.    
  1430.     my($t, $a, $b) = $pixel*$glb_maxcol;
  1431.     return ($a=$glb_dist[$t + $forme]) < ($b=$glb_dist[$t + $fond]) ? $a : $b;
  1432. }
  1433.  
  1434. sub couple_dist_sq {
  1435.     my($t) = &couple_dist(@_);
  1436.     return $t*$t;
  1437. }
  1438.  
  1439. sub couple2 {
  1440.     my(@octet) = @_;
  1441.     my($d, $dm, $im, $jm); $dm = 1e30;
  1442.     for($i=0; $i<$glb_maxcol; ++$i) {
  1443.         for($j=0; $j<$i; ++$j) {
  1444.             $d  = &couple2_dist($i, $j, $octet[0]);
  1445.             $d += &couple2_dist($i, $j, $octet[1])  if $d<$dm;
  1446.             $d += &couple2_dist($i, $j, $octet[2])  if $d<$dm;
  1447.             $d += &couple2_dist($i, $j, $octet[3])  if $d<$dm;
  1448.             $d += &couple2_dist($i, $j, $octet[4])  if $d<$dm;
  1449.             $d += &couple2_dist($i, $j, $octet[5])  if $d<$dm;
  1450.             $d += &couple2_dist($i, $j, $octet[6])  if $d<$dm;
  1451.             $d += &couple2_dist($i, $j, $octet[7])  if $d<$dm;
  1452.            
  1453.             if($d<$dm) {$dm = $d; $im = $i; $jm = $j;}
  1454.         }
  1455.     }
  1456.     return ($glb_pal[$im], $glb_pal[$jm]);    
  1457. }
  1458.  
  1459. sub couple2_dist {
  1460.     my($forme, $fond, $pixel) = @_;
  1461.     my($a,$b);
  1462.     return ($a=&irgb_dist($glb_pal[$forme], $pixel)) < ($b=&irgb_dist($glb_pal[$fond], $pixel)) ? $a : $b;
  1463. }
  1464.  
  1465. sub couple2_dist_sq {
  1466.     my($t) = &couple2_dist(@_);
  1467.     return $t * $t;
  1468. }
  1469.  
  1470. # retourne le couple forme/fond pour un octet donné
  1471. sub couple3 {
  1472.     my(@octet) = @_;
  1473.    
  1474.     # on commence un dither classique mais horizontal de l'octet
  1475.     my($i, $im, $j, $jm, $d, $dm, $rgb, @octet_pal);
  1476.     $#octet_pal = 7;
  1477.     for($i=0; $i<8; ++$i) {
  1478.         $rgb = $octet[$i];
  1479.         # on trouve la coul la plus proche
  1480.         $dm = 1e30;
  1481.         for($j=0; $j<$glb_maxcol; ++$j) {
  1482.             $d = &irgb_dist($glb_pal[$j], $rgb);
  1483.             if($d<$dm) {$dm = $d; $octet_pal[$i] = $j;}
  1484.         }
  1485.         # on propage l'erreur
  1486.         #$qq = &irgb_map(&irgb_sub($octet[$i], $glb_pal[$octet_pal[$i]]), \@glb_map3);
  1487.         #print &irgb2hex($octet[$i]),",",&irgb2hex($glb_pal[$octet_pal[$i]])," e=",&irgb2hex(&irgb_sub($octet[$i], $glb_pal[$octet_pal[$i]])), " " if $qq;
  1488.         #print "m=",&irgb2hex($qq)," " if $qq;
  1489.         #print &irgb2hex($octet[$i + 1]), " => " if $qq;
  1490.         $octet[$i+1] = &irgb_uadd($octet[$i+1], &irgb_map(&irgb_sub($rgb, $glb_pal[$octet_pal[$i]]), \@glb_map3)) if $i<7;
  1491.         #print &irgb2hex($octet[$i + 1]), "\n" if $qq;
  1492.     }
  1493.    
  1494.     #la couleur fond est la couleur la plus choisie par octet_pal[i]
  1495.     my(@cpt) = (0) x 8;
  1496.     $i = -1;
  1497.     foreach $j (@octet_pal) {if(++$cpt[$j] > $i) {$i = $cpt[$j]; $im = $j;}}
  1498.        
  1499.     # ensuite on trouve le couple qui conduit au minimum d'erreur
  1500.     $dm = 1e30; my($t, @line);
  1501.     for($j=0; $j<$glb_maxcol; ++$j) {
  1502.         $d  = &couple_dist($im, $j, $octet_pal[0]);
  1503.         $d += &couple_dist($im, $j, $octet_pal[1])  if $d<$dm;
  1504.         $d += &couple_dist($im, $j, $octet_pal[2])  if $d<$dm;
  1505.         $d += &couple_dist($im, $j, $octet_pal[3])  if $d<$dm;
  1506.         $d += &couple_dist($im, $j, $octet_pal[4])  if $d<$dm;
  1507.         $d += &couple_dist($im, $j, $octet_pal[5])  if $d<$dm;
  1508.         $d += &couple_dist($im, $j, $octet_pal[6])  if $d<$dm;
  1509.         $d += &couple_dist($im, $j, $octet_pal[7])  if $d<$dm;
  1510.          
  1511.         if($d<$dm) {$dm = $d; $jm = $j;}
  1512.     }
  1513.    
  1514.     return ($glb_pal[$im], $glb_pal[$jm]);
  1515. }
  1516.  
  1517. sub couple4 {
  1518.     return &couple5(@_) if 0;
  1519.  
  1520.     my(@octet) = @_;
  1521.    
  1522.     # on commence un dither classique mais horizontal de l'octet
  1523.     my($i, $im, $j, $jm, $d, $dm, $rgb, @octet_pal);
  1524.     $#octet_pal = 7;
  1525.     for($i=0; $i<8; ++$i) {
  1526.         $rgb = $octet[$i];
  1527.         # on trouve la coul la plus proche
  1528.         $dm = 1e30;
  1529.         for($j=0; $j<$glb_maxcol; ++$j) {
  1530.             $d = &irgb_dist($glb_pal[$j], $rgb);
  1531.             if($d<$dm) {$dm = $d; $octet_pal[$i] = $j;}
  1532.         }
  1533.         # on propage l'erreur
  1534.         #$qq = &irgb_map(&irgb_sub($octet[$i], $glb_pal[$octet_pal[$i]]), \@glb_map3);
  1535.         #print &irgb2hex($octet[$i]),",",&irgb2hex($glb_pal[$octet_pal[$i]])," e=",&irgb2hex(&irgb_sub($octet[$i], $glb_pal[$octet_pal[$i]])), " " if $qq;
  1536.         #print "m=",&irgb2hex($qq)," " if $qq;
  1537.         #print &irgb2hex($octet[$i + 1]), " => " if $qq;
  1538.         $octet[$i+1] = &irgb_uadd($octet[$i+1], &irgb_map(&irgb_sub($rgb, $glb_pal[$octet_pal[$i]]), \@glb_map3)) if $i<7;
  1539.         #print &irgb2hex($octet[$i + 1]), "\n" if $qq;
  1540.     }
  1541.    
  1542.     # comptage des occurences
  1543.     $dm = -1; my(@cpt) = (0) x $glb_maxcol; my($filt_cpt) = 0;
  1544.     foreach $j (@octet_pal) {if(++$cpt[$j] > $dm) {$dm = $cpt[$jm = $j];}}
  1545.     if($dm >= 8) {
  1546.         $im = 0;
  1547.     } elsif($dm >= 4) {
  1548.         # une couleur domine de loin: on la prend en fond. On cherche
  1549.         # alors la forme qui minimise l'erreur sur l'octet.
  1550.         $dm = 1e30; $im = 0;
  1551.         for($i = 0; $i < $glb_maxcol; ++$i) {
  1552.             next unless $cpt[$i]>0 || $filt_cpt;
  1553.             $d  = &couple_dist($i, $jm, $octet_pal[0]);
  1554.             $d += &couple_dist($i, $jm, $octet_pal[1])  if $d<$dm;
  1555.             $d += &couple_dist($i, $jm, $octet_pal[2])  if $d<$dm;
  1556.             $d += &couple_dist($i, $jm, $octet_pal[3])  if $d<$dm;
  1557.             $d += &couple_dist($i, $jm, $octet_pal[4])  if $d<$dm;
  1558.             $d += &couple_dist($i, $jm, $octet_pal[5])  if $d<$dm;
  1559.             $d += &couple_dist($i, $jm, $octet_pal[6])  if $d<$dm;
  1560.             $d += &couple_dist($i, $jm, $octet_pal[7])  if $d<$dm;
  1561.            
  1562.             if($d<$dm) {$dm = $d; $im = $i;}
  1563.         }
  1564.     } else {
  1565.         # sinon on essaye tous les couples sans dither
  1566.         return &couple2(@_) if 0;
  1567.         # avec dither
  1568.         return &couple2(@octet) if 0;
  1569.         $dm = 1e30; $im = 0;
  1570.         for($i=0; $i<$glb_maxcol; ++$i) {
  1571.             next unless $cpt[$i]>0 || $filt_cpt || 1;
  1572.             for($j=0; $j<$i; ++$j) {
  1573.                 next unless $cpt[$j]>0 || $filt_cpt;
  1574.                 $d  = &couple_dist($i, $j, $octet_pal[0]);
  1575.                 $d += &couple_dist($i, $j, $octet_pal[1])  if $d<$dm;
  1576.                 $d += &couple_dist($i, $j, $octet_pal[2])  if $d<$dm;
  1577.                 $d += &couple_dist($i, $j, $octet_pal[3])  if $d<$dm;
  1578.                 $d += &couple_dist($i, $j, $octet_pal[4])  if $d<$dm;
  1579.                 $d += &couple_dist($i, $j, $octet_pal[5])  if $d<$dm;
  1580.                 $d += &couple_dist($i, $j, $octet_pal[6])  if $d<$dm;
  1581.                 $d += &couple_dist($i, $j, $octet_pal[7])  if $d<$dm;
  1582.            
  1583.                 if($d<$dm) {$dm = $d; $im = $i; $jm = $j;}
  1584.             }
  1585.         }
  1586.     }
  1587.    
  1588.     return ($glb_pal[$im], $glb_pal[$jm]);
  1589. }
  1590.  
  1591. sub couple5__ {
  1592.     my(@octet) = @_;
  1593.    
  1594.     # calcul de la coul moyenne sur l'octet
  1595.     my(@moy);
  1596.     $moy[0] = (($octet[0] + $octet[1])>>1) & 0xff3fcff;
  1597.     $moy[1] = (($octet[2] + $octet[3])>>1) & 0xff3fcff;
  1598.     $moy[2] = (($octet[4] + $octet[5])>>1) & 0xff3fcff;
  1599.     $moy[3] = (($octet[6] + $octet[7])>>1) & 0xff3fcff;
  1600.    
  1601.     $moy[0] = (($moy[0] + $moy[1])>>1) & 0xff3fcff;
  1602.     $moy[1] = (($moy[2] + $moy[3])>>1) & 0xff3fcff;
  1603.  
  1604.     # le fond = le plus proche de la moyenne
  1605.     my($j, $jm, $d, $dm);
  1606.     for($dm=1e30, $j=0; $j<$glb_maxcol; ++$j) {
  1607.         if(($d = &irgb_dist($glb_pal[$j], $moy[0]))<$dm) {$dm =$d; $jm = $j;}
  1608.     }
  1609.     for($dm=1e30, $i=0; $i<$glb_maxcol; ++$i) {
  1610.         if(($d = &irgb_dist($glb_pal[$i], $moy[1]))<$dm) {$dm =$d; $im = $i;}
  1611.     }
  1612.    
  1613.     return ($glb_pal[$im], $glb_pal[$jm]);
  1614. }
  1615.  
  1616. sub couple5_ {
  1617.     my(@octet) = @_;
  1618.    
  1619.     # on commence un dither classique mais horizontal de l'octet
  1620.     my($i, $im, $j, $jm, $d, $dm, $rgb, @octet_pal);
  1621.     my(@dist) = (0) x ($glb_maxcol * 8);
  1622.    
  1623.     $#octet_pal = 7;
  1624.     for($i=0; $i<8; ++$i) {
  1625.         $rgb = $octet[$i];
  1626.         for($dm=1e30, $j=0; $j<$glb_maxcol; ++$j) {
  1627.             $d = &irgb_dist($glb_pal[$j], $rgb);
  1628.             if($d<$dm) {$dm = $d; $octet[$i] = $glb_pal[$octet_pal[$i] = $j];}
  1629.         }
  1630.         # $octet[$i+1] = &irgb_uadd($octet[$i+1], &irgb_map(&irgb_sub($rgb, $glb_pal[$octet_pal[$i]]), \@glb_map3)) if $i<7;
  1631.     }    
  1632.     # comptage des occurences
  1633.     $dm = -1; my(@cpt) = (0) x $glb_maxcol; my($filt_cpt) = 0;
  1634.     foreach $j (@octet_pal) {if(++$cpt[$j] > $dm) {$dm = $cpt[$jm = $j];}}
  1635.     if($dm >= 8) {
  1636.         $im = 0;
  1637.         print "*";
  1638.     } elsif($dm >= 0) {
  1639.         # une couleur domine de loin: on la prend en fond. On cherche
  1640.         # alors la forme qui minimise l'erreur sur l'octet d'origine
  1641.         $dm = 1e30; $im = 0; my($p) = 0;
  1642.         for($i = 0; $i < $glb_maxcol; ++$i) {
  1643.             $d  = $dist[$p++];
  1644.             $d += $dist[$p++];
  1645.             $d += $dist[$p++];
  1646.             $d += $dist[$p++];
  1647.             $d += $dist[$p++];
  1648.             $d += $dist[$p++];
  1649.             $d += $dist[$p++];
  1650.             $d += $dist[$p++];
  1651.             if($d<$dm) {$dm = $d; $im = $i;}
  1652.         }
  1653.         print "#";
  1654.     } else {
  1655.         # on regroupe les pixels 2 par 2, on trouve le plus proche dans la palette
  1656.         @octet_pal = (0) x 4;
  1657.         for($i=0; $i<4; ++$i) {
  1658.             $rgb = (($octet[$i*2] + $octet[$i*2+1])>>1) & 0xff3fcff;
  1659.             for($dm=1e30, $j=0; $j<$glb_maxcol; ++$j) {
  1660.                 $dist[$j*4 + $i] = $d = &irgb_dist($glb_pal[$j], $rgb);
  1661.                 if($d<$dm) {$dm = $d; $octet_pal[$i] = $j;}
  1662.             }
  1663.         }
  1664.         # comptage des occurences
  1665.         $dm = -1; my(@cpt) = (0) x $glb_maxcol; my($filt_cpt) = 0;
  1666.         foreach $j (@octet_pal) {if(++$cpt[$j] > $dm) {$dm = $cpt[$jm = $j];}}
  1667.         if($dm >= 2) {
  1668.             $dm = 1e30; $im = 0; my($p) = $jm*4;
  1669.             for($i = 0; $i < $glb_maxcol; ++$i) {
  1670.                 $p = $i * 4;
  1671.                 $d  = $dist[$p++];
  1672.                 $d += $dist[$p++] if $d < $dm;
  1673.                 $d += $dist[$p++] if $d < $dm;
  1674.                 $d += $dist[$p  ] if $d < $dm;
  1675.                 if($d<$dm) {$dm = $d; $im = $i;}
  1676.             }
  1677.             print ":";
  1678.         } else {
  1679.             couple2(@octet) if 0;
  1680.             $dm = 1e30; $im = 0;
  1681.             for($i=0; $i<$glb_maxcol; ++$i) {
  1682.                 for($j=0; $j<$i; ++$j) {
  1683.                     $d  = &couple_dist($i, $j, $octet_pal[0]);
  1684.                     $d += &couple_dist($i, $j, $octet_pal[1])  if $d<$dm;
  1685.                     $d += &couple_dist($i, $j, $octet_pal[2])  if $d<$dm;
  1686.                     $d += &couple_dist($i, $j, $octet_pal[3])  if $d<$dm;
  1687.            
  1688.                     if($d<$dm) {$dm = $d; $im = $i; $jm = $j;}
  1689.                 }
  1690.             }
  1691.             print ".";
  1692.         }
  1693.     }
  1694.     print sprintf("%x%x ", $im, $jm);
  1695.     return ($glb_pal[$im], $glb_pal[$jm]);
  1696. }
  1697.  
  1698. sub couple5 {
  1699.     my(@octet) = @_;
  1700.  
  1701.     my($i, $j, $rgb, $dm, @px);
  1702.    
  1703.     if(0) {
  1704.         # horiz dither first
  1705.         for($i=0; $i<8; ++$i) {
  1706.             $rgb = $octet[$i];
  1707.             for($j=0, $dm=1e30; $j<$glb_maxcol; ++$j) {
  1708.                 $d = &irgb_dist($glb_pal[$j], $rgb);
  1709.                 if($d<$dm) {$dm = $d; $octet[$i] = $glb_pal[$j];}
  1710.             }
  1711.             $octet[$i+1] = &irgb_uadd($octet[$i+1], &irgb_map(&irgb_sub($rgb, $octet[$i]), \@glb_map3)) if $i<7;
  1712.         }
  1713.     }
  1714.        
  1715.     foreach $j (@octet) {my @t = &irgb2rgb($j); push(@px, $t[0]*255, $t[1]*255, $t[2]*255);}
  1716.    
  1717.     my(@mean1) = (
  1718.         ($px[0] + $px[3] + $px[6] + $px[9]) / 4,
  1719.         ($px[1] + $px[4] + $px[7] + $px[10]) / 4,
  1720.         ($px[2] + $px[5] + $px[8] + $px[11]) / 4,
  1721.         ($px[12] + $px[15] + $px[18] + $px[21]) / 4,
  1722.         ($px[13] + $px[16] + $px[19] + $px[22]) / 4,
  1723.         ($px[14] + $px[17] + $px[20] + $px[23]) / 4,
  1724.     );
  1725.     my($d1, $d2, @mean2);
  1726.    
  1727.     # on trouve les deux clusters
  1728.     while(1) {
  1729.         #print join(",", @mean1),"\n";
  1730.         @mean2 = (0,0,0,0,0,0); $d1 = $d2 = 0;
  1731.         for($i=0; $i<8; ++$i) {
  1732.             @rgb = @px[($i*3)..($i*3+2)];
  1733.             if(&rgbdist(@mean1[0..2], @rgb) < &rgbdist(@mean1[3..5], @rgb)) {
  1734.                 ++$d1; $mean2[0] += $rgb[0]; $mean2[1] += $rgb[1]; $mean2[2] += $rgb[2];
  1735.             } else {
  1736.                 ++$d2; $mean2[3] += $rgb[0]; $mean2[4] += $rgb[1]; $mean2[5] += $rgb[2];
  1737.             }
  1738.         }
  1739.         # si un cluster est vide, on repart pour un tour
  1740.         if($d1 == 0) {
  1741.             # on trouve le point le plus eloigné  de l'autre centre
  1742.             @mean2 = (127, 127, 127, @mean2[3..5]);
  1743.         } elsif($d2 == 0) {
  1744.             @mean2 = (@mean2[0..2], 127, 127, 128);
  1745.         }
  1746.        
  1747.         $d1 = 1 unless $d1>0; $d2 = 1 unless $d2>0;
  1748.         $mean2[0] = int($mean2[0] / $d1); $mean2[1] = int($mean2[1] / $d1); $mean2[2] = int($mean2[2] / $d1);
  1749.         $mean2[3] = int($mean2[3] / $d2); $mean2[4] = int($mean2[4] / $d2); $mean2[5] = int($mean2[5] / $d2);
  1750.  
  1751.         last if $mean2[0]==$mean1[0] && $mean2[1]==$mean1[1] && $mean2[2]==$mean1[2] && $mean2[3]==$mean1[3] && $mean2[4]==$mean1[4] && $mean2[5]==$mean1[5];
  1752.         @mean1 = @mean2;
  1753.     }
  1754.  
  1755.     #print join(",", @mean1),"\n";
  1756.     my($mean) = ((($mean1[0]<<10) + $mean1[1])<<10) + $mean1[2];
  1757.     for($dm=1e30, $i=0; $i<$glb_maxcol; ++$i) {
  1758.         $d = &irgb_dist($mean, $glb_pal[$i]);
  1759.         if($d<$dm) {$dm = $d; $im = $i;}
  1760.     }
  1761.     $mean = ((($mean1[3]<<10) + $mean1[4])<<10) + $mean1[5];
  1762.     for($dm=1e30, $i=0; $i<$glb_maxcol; ++$i) {
  1763.         $d = &irgb_dist($mean, $glb_pal[$i]);
  1764.         if($d<$dm) {$dm = $d; $jm = $i;}
  1765.     }
  1766.     #print &irgb2hex($glb_pal[$im]), " ", &irgb2hex($glb_pal[$jm]),"\n";
  1767.    
  1768.     return ($glb_pal[$im], $glb_pal[$jm]);
  1769. }
  1770.  
  1771. sub rgbdist {
  1772.     my($r1, $g1, $b1, $r2, $g2, $b2) = @_;
  1773.     $r1 -= $r2;
  1774.     $g1 -= $g2;
  1775.     $b1 -= $b2;
  1776.     return sqrt($r1*$r1 + $g1*$g1 + $b1*$b1);
  1777. }
  1778.  
  1779. sub cleanup {
  1780.     return @_ if 0;
  1781.     my($thr) = $glb_clean;
  1782.     return @_ unless $thr>0;
  1783.     my(@r, $i, $t);
  1784.     my(@t) = @_;
  1785.    
  1786.     if(1) {
  1787.         # les composantes bien trop faibles sont eliminées
  1788.         @r = @t; @t = ();
  1789.         for $i (@r) {
  1790.             my($r) = ($i>>00) & 0xFF;
  1791.             my($g) = ($i>>10) & 0xFF;
  1792.             my($b) = ($i>>20) & 0xFF;
  1793.             my($M) = $r;
  1794.             $M = $g if $g>$M;
  1795.             $M = $b if $b>$M;
  1796.             my($m) = $r;
  1797.             $m = $g if $g<$m;
  1798.             $m = $b if $b<$m;
  1799.             my($l)  = 0.299*$r + 0.587*$g + 0.114*$b;
  1800.             #$m = $m*3 + $r + $g + $b;
  1801.             #$m /= 16;
  1802.             if(0) {
  1803.                 $M /= 4.2; #4.2; # pas mal
  1804.                 #$m = 255/8 if $m>255/8;
  1805.            
  1806.                 #while(($r<$m && $g<$m) || ($r<$m && $b<$m) || ($g<$m && $b<$m)) {$m/=1.05; last if $m<1e-3;}
  1807.                 if($l<38 && $m<$M) {
  1808.                     my($t) = ($m + $M)>>1;
  1809.                     $r = 0 if $r<=$t;
  1810.                     $g = 0 if $g<=$t;
  1811.                     $b = 0 if $b<=$t;
  1812.                     #$r = 0 if $r < $m;
  1813.                     #$g = 0 if $g < $m;
  1814.                     #$b = 0 if $b < $m;
  1815.                     #if($g<$m)    {$g=0;}
  1816.                     #elsif($r<$m) {$r=0;}
  1817.                     #elsif($b<$m) {$b=0;}
  1818.                 }
  1819.             } elsif(1) {
  1820.                 $M /= 4.2; #4.2; # pas mal
  1821.                 if($m<$M) {
  1822.                     my($t) = $M;
  1823.                     $r = 0 if $r<=$t;
  1824.                     $g = 0 if $g<=$t;
  1825.                     $b = 0 if $b<=$t;
  1826.                     #$r = 0 if $r < $m;
  1827.                     #$g = 0 if $g < $m;
  1828.                     #$b = 0 if $b < $m;
  1829.                     #if($g<$m)    {$g=0;}
  1830.                     #elsif($r<$m) {$r=0;}
  1831.                     #elsif($b<$m) {$b=0;}
  1832.                 }
  1833.             } elsif(0) {
  1834.                 my($n) = $r;
  1835.                 $n = $g if $g<$n;
  1836.                 $n = $b if $b<$n;
  1837.                 if($n<$m/8) {
  1838.                     $r = 0 if $r <= $n*2;
  1839.                     $g = 0 if $g <= $n*2;
  1840.                     $b = 0 if $b <= $n*2;
  1841.                 }
  1842.             }
  1843.             push(@t, ((($b<<10)|$g)<<10)|$r);
  1844.         }
  1845.     }
  1846.  
  1847.     if(1) {
  1848.         # on elimine les composantes plus faibles que 10% du max
  1849.         @r = @t; @t = ();
  1850.         for($i=0; $i<=$#r; $i+=8) {
  1851.             my($maxr, $maxv, $maxb) = (0, 0, 0);
  1852.             my($minr, $minv, $minb) = (1, 1, 1);
  1853.             my($rgb, @rgb);
  1854.             my(@o) = @r[$i..$i+7];
  1855.             for $rgb (@o) {
  1856.                 @rgb = &irgb2rgb($rgb);
  1857.                 $maxr = $rgb[0] if $rgb[0] > $maxr;
  1858.                 $maxv = $rgb[1] if $rgb[1] > $maxv;
  1859.                 $maxb = $rgb[2] if $rgb[2] > $maxb;
  1860.                 $minr = $rgb[0] if $rgb[0] < $minr;
  1861.                 $minv = $rgb[1] if $rgb[1] < $minv;
  1862.                 $minb = $rgb[2] if $rgb[2] < $minb;
  1863.             }
  1864.             $maxr = (1-$thr)*$minr + $thr*$maxr;
  1865.             $maxv = (1-$thr)*$minv + $thr*$maxv;
  1866.             $maxb = (1-$thr)*$minb + $thr*$maxb;
  1867.             for $rgb (@o) {
  1868.                 @rgb = &irgb2rgb($rgb);
  1869.                 $rgb[0] = $minr if $rgb[0] < $maxr;
  1870.                 $rgb[1] = $minv if $rgb[1] < $maxv;
  1871.                 $rgb[2] = $minb if $rgb[2] < $maxb;
  1872.                 #$rgb[0] = $maxr if $rgb[0] > $maxr;
  1873.                 #$rgb[1] = $maxv if $rgb[1] > $maxv;
  1874.                 #$rgb[2] = $maxb if $rgb[2] > $maxb;
  1875.                 $t  = int($rgb[0]*255)&0x1ff; $t<<=10;
  1876.                 $t += int($rgb[1]*255)&0x1ff; $t<<=10;
  1877.                 $t += int($rgb[2]*255)&0x1ff;
  1878.                 push(@t, $t);
  1879.             }
  1880.         }
  1881.     }
  1882.    
  1883.     if(0) {
  1884.         my($min, $max) = (32,128);
  1885.         my($mi2) = $min>>1;
  1886.         my($p, $x, $y);
  1887.         for($p=$y=0; $y<200; ++$y) {
  1888.             for($x=0; $x<320; ++$x) {
  1889.                 $p = 320*$y+$x;
  1890.                 my($rvb) = $t[$p];
  1891.                 my($r) = ($rvb>>00) & 0x1FF;
  1892.                 my($g) = ($rvb>>10) & 0x1FF;
  1893.                 my($b) = ($rvb>>20) & 0x1FF;
  1894.                 $r=0 if $r&0x100;
  1895.                 $g=0 if $g&0x100;
  1896.                 $b=0 if $b&0x100;
  1897.                 my($m) = $r; $m = $g if $g>$m; $m = $b if $b>$m; $m=$max+1;
  1898.                 $r = ($r<$mi2 ? 0:$min) if $r<$min && $m>$max;
  1899.                 $g = ($g<$mi2 ? 0:$min) if $g<$min && $m>$max;
  1900.                 $b = ($b<$mi2 ? 0:$min) if $b<$min && $m>$max;
  1901.                 $t[$p] = ((($b<<10)|$g)<<10)|$r;      
  1902.                 $rvb = &irgb_sub($rvb, $t[$p]);
  1903.                 #print " /_\\ = ", &irgb2hex($rvb), "\n";
  1904.                 $t[$p + 319] = &irgb_add_cln($t[$p + 319], &irgb_map($rvb, \@glb_map0)) if $glb_err0 && $y<199 && $x>0;
  1905.                 $t[$p + 320] = &irgb_add_cln($t[$p + 320], &irgb_map($rvb, \@glb_map1)) if $glb_err1 && $y<199;
  1906.                 $t[$p + 321] = &irgb_add_cln($t[$p + 321], &irgb_map($rvb, \@glb_map2)) if $glb_err2 && $y<199 && $x<319;
  1907.                 $t[$p + 001] = &irgb_add_cln($t[$p + 001], &irgb_map($rvb, \@glb_map3)) if $glb_err3 &&           $x<319;
  1908.             }
  1909.         }
  1910.     }
  1911.  
  1912.     &write_image("zzz.png", @t);
  1913.        
  1914.     return @t;
  1915. }
  1916.  
  1917. sub couple6 {
  1918.     my(@octet) = @_;
  1919.    
  1920.     my($dbg) = 0;
  1921.    
  1922.     my($i, $im, $j, $jm, $d, $dm, $rgb, %cpt);
  1923.     my(@px) = (@octet);
  1924.    
  1925.     # dither de l'octet
  1926.     for($i=0; $i<8; ++$i) {
  1927.         $rgb = $px[$i];
  1928.         for($dm=1e30, $jm=$j=0; $j<$glb_maxcol; ++$j) {
  1929.             $d = &irgb_dist($glb_pal[$j], $rgb);
  1930.         #print "$j => $d :: $jm\n";
  1931.             if($d<$dm) {$dm = $d; $jm = $j;}
  1932.         }
  1933.         ++$cpt{$jm};
  1934.     $px[$i+1] = &irgb_add_cln($px[$i+1], &irgb_map(&irgb_sub($rgb, $glb_pal[$jm]), \@glb_map3)) if $i<7;
  1935.     }
  1936.    
  1937.     my(@cpt) = (sort { $cpt{$b} - $cpt{$a} } keys %cpt);
  1938.    
  1939.     if($dbg) {
  1940.     print "\n\n";
  1941.     for $t (@octet) {
  1942.         print &irgb2hex($t), " ";
  1943.     }
  1944.     print "\n\n";
  1945.     for $t (@cpt) {
  1946.         print &irgb2hex($glb_pal[$t]), " ", $cpt{$t}, "\n";
  1947.     }
  1948.     }
  1949.  
  1950.     # 1 ou 2 couls utilisées: pas de probs
  1951.     if($#cpt<=1) {
  1952.         print " ";
  1953.         # on s'assure qu'on en a au moins 2
  1954.         $cpt{$cpt[1] = 0} = 0 if $#cpt==0;
  1955.  
  1956.         return ($glb_pal[$cpt[1]], $glb_pal[$cpt[0]]);
  1957.     }
  1958.    
  1959.     # les 2 couls principales couvrent 7 pixels sur les 8
  1960.     if($cpt{$cpt[0]} + $cpt{$cpt[1]} >= 6) {
  1961.         print ".";
  1962.         return ($glb_pal[$cpt[1]], $glb_pal[$cpt[0]]);        
  1963.     }
  1964.  
  1965.     #return (0x0ff3fcff,0);
  1966.    
  1967.     if(0) {
  1968.         # on fusionne les couleurs voisines
  1969.         $rgb = 0x0c0300c0;
  1970.         for($i=$#cpt; $i>=0; --$i) {
  1971.             for($j=$i-1; $j>=0; --$j) {
  1972.                 if(($rgb & $glb_pal[$cpt[$i]]) == ($rgb & $glb_pal[$cpt[$j]])) {
  1973.                     $cpt{$cpt[$j]} += $cpt{$cpt[$i]};
  1974.                     delete $cpt{$cpt[$i]}; print "*";
  1975.                     @cpt = (@cpt[0..$i-1], @cpt[$i+1..$#cpt]);
  1976.                     last;
  1977.                 }
  1978.             }
  1979.         }
  1980.    
  1981.         @cpt = (sort { $cpt{$b} - $cpt{$a} } keys %cpt);
  1982.    
  1983.         # 1 ou 2 couls utilisées: pas de probs
  1984.         if($#cpt<=1) {
  1985.             print "_";
  1986.             # on s'assure qu'on en a au moins 2
  1987.             $cpt{$cpt[1] = 0} = 0 if $#cpt==0;
  1988.  
  1989.             return ($glb_pal[$cpt[1]], $glb_pal[$cpt[0]]);
  1990.         }
  1991.    
  1992.         # les 2 couls principales couvrent 7 pixels sur les 8
  1993.         if($cpt{$cpt[0]} + $cpt{$cpt[1]} >= 7) {
  1994.             print ":";
  1995.             return ($glb_pal[$cpt[1]], $glb_pal[$cpt[0]]);        
  1996.         }
  1997.     }
  1998.    
  1999.     # si la 1ere couvre 4 pixels, alors on prends la 2eme qui fait le moins d'err
  2000.     if($cpt{$cpt[0]} >= 6) {
  2001.         $jm = $cpt[0];
  2002.         for($dm=1e30, $i=0; $i<$glb_maxcol; ++$i) {
  2003.             next if $i==$jm;
  2004.             #next unless defined $cpt{$i};
  2005.         $delta = 0;
  2006.             $d = 0; @px = (@octet);
  2007.             if(0) {
  2008.                 foreach $j (@px) {$d += $glb_dist[$i*$glb_maxcol + $j] if $j!=$jm;}
  2009.             } elsif(0) {
  2010.                 for($j = 0; $j<8 && $d<$dm; ++$j) {$d += &couple2_dist_sq($i, $jm, $octet[$j]);}
  2011.             } else {
  2012.                 for($j = 0; $j<8 && $d<$dm; ++$j) {
  2013.                     $d1 = &irgb_dist($glb_pal[$i ], $px[$j]);
  2014.                     $d2 = &irgb_dist($glb_pal[$jm], $px[$j]);
  2015.             if($d1 < $d2) {$d += &sq($d1); $rgb = $glb_pal[$i];} else {$d += &sq($d2); $rgb = $glb_pal[$jm];}
  2016.                     $px[$j+1] = &irgb_add_cln($px[$j+1], &irgb_map(&irgb_sub($px[$j], $rgb), \@glb_map3)) if $glb_err3 && $j<7;
  2017.                 }
  2018.         #$d += &irgb_module($delta);
  2019.             }
  2020.             if($d < $dm) {$dm = $d; $im = $i;}
  2021.         }
  2022.         print "o";
  2023.         return ($glb_pal[$im], $glb_pal[$jm]);
  2024.     }
  2025.    
  2026.     # TODO: prendre les couleurs les moins utilisées.. reduire leur résolution et voir si on peut les mapper
  2027.     # sur l'une des couleurs plus utilisée
  2028.    
  2029.     # fusionner les couleurs les plus proches: seuil = 1/16 au début, puis 1/8 après
  2030.    
  2031.     if(0) {
  2032.         # reduire la resolution de la palette de 0..255 à 0..7 pour merger les couleurs proches
  2033.         %cpt = (); @px = @octet; $msk = 0x0e0380e0;
  2034.         for($i=0; $i<8; ++$i) {
  2035.             $rgb = $px[$i] & $msk;
  2036.             for($dm=1e30, $jm=$j=0; $j<$glb_maxcol; ++$j) {
  2037.                 $d = &irgb_dist($glb_pal[$j] & $msk, $rgb);
  2038.                 if($d<$dm) {$dm = $d; $jm = $j;}
  2039.             }
  2040.             ++$cpt{$px[$i] = $jm};
  2041.             $px[$i+1] = &irgb_uadd($px[$i+1], &irgb_map(&irgb_sub($rgb, $glb_pal[$jm] & $msk), \@glb_map3)) if $glb_err3 &&  $i<7;
  2042.         }
  2043.         @cpt = (sort { $cpt{$b} - $cpt{$a} } keys %cpt);
  2044.  
  2045.         if($#cpt<=1) {
  2046.             # on s'assure qu'on en a au moins 2
  2047.             $cpt{$cpt[1] = 0} = 0 if $#cpt==0;
  2048.             print "_";
  2049.             return ($glb_pal[$cpt[1]], $glb_pal[$cpt[0]]);
  2050.         }
  2051.    
  2052.         # les 2 couls principales couvrent 7 pixels sur les 8 (plus strict)
  2053.         if($cpt{$cpt[0]} + $cpt{$cpt[1]} >= 7) {
  2054.             print ":";
  2055.             return ($glb_pal[$cpt[1]], $glb_pal[$cpt[0]]);        
  2056.         }
  2057.     }
  2058.    
  2059.     if(0) {
  2060.         # utilisation de rayons
  2061.         for($dm=1e30, $i=0; $i<$glb_maxcol; ++$i) {
  2062.             for($j=0; $j<$glb_maxcol; ++$j) {
  2063.                 @px = @octet; $d = 0; $ir = $jr = 0;
  2064.                 for($k = 0; $k<8 && $d<$dm; ++$k) {
  2065.        
  2066.                     $di = &irgb_dist($glb_pal[$i], $px[$k]);
  2067.                     $dj = &irgb_dist($glb_pal[$j], $px[$k]);
  2068.                     if($di <= $dj) {
  2069.                         $ir = $di if $di>$ir;
  2070.                         $rgb = $glb_pal[$i];
  2071.                     } else {
  2072.                         $jr = $dj if $dj>$jr;
  2073.                         $rgb = $glb_pal[$j];
  2074.                     }
  2075.                     $d = $ir + $jr;
  2076.                     $px[$i+1] = &irgb_uadd($px[$i+1], &irgb_map(&irgb_sub($px[$k], $rgb), \@glb_map3)) if $glb_err3 && $i<7;
  2077.                 }
  2078.                 if($d < $dm) {$dm = $d; $im = $i; $jm = $j}
  2079.             }
  2080.         }
  2081.         print "#";
  2082.         return ($glb_pal[$im], $glb_pal[$jm]);
  2083.     }
  2084.        
  2085.     # sinon tester tous les couple avec dither
  2086.     my($r, $rm);
  2087.     for($dm=1e30, $i=0; $i<$glb_maxcol; ++$i) {
  2088.         #next unless defined $cpt{$i};
  2089.         for($j=0; $j<$i; ++$j) {
  2090.             next unless defined $cpt{$j};
  2091.             @px = (@octet);
  2092.             #print $i,",",$j, "##", &irgb2hex($glb_pal[$i])," ",&irgb2hex($glb_pal[$j]),"\n";
  2093.         #for($k=0; $k<8; ++$k) {print " ", &irgb2hex($px[$k]);} print "\n";
  2094.         for($r = $d = $k = 0; $k<8 && $d<$dm; ++$k) {
  2095.         $di = &irgb_dist($glb_pal[$i], $px[$k]);
  2096.                 $dj = &irgb_dist($glb_pal[$j], $px[$k]);
  2097.                 if($di <= $dj) {
  2098.                     $r |= 1;
  2099.                     $rgb = $glb_pal[$i];
  2100.             $d += &sq($di);
  2101.                 } else {
  2102.                     $r |= 2;
  2103.                     $rgb = $glb_pal[$j];
  2104.             $d += &sq($dj);
  2105.                 }
  2106.         #print $k,"->", &irgb2hex($octet[$k]), ":", &irgb2hex($rgb),"=",$d,"\n";
  2107.                 $px[$k+1] = &irgb_add_cln($px[$k+1], &irgb_map(&irgb_sub($px[$k], $rgb), \@glb_map3)) if $glb_err3 && $k<7;
  2108.             }
  2109.         #print "DDDDD ",irgb2hex($delta),"\n";
  2110.         #$d += &irgb_module($delta);
  2111.         #print $i,",",$j, "==", &irgb2hex($glb_pal[$i])," ",&irgb2hex($glb_pal[$j])," == ",$d," (",$dm,")\n";
  2112.             if($d < $dm) {$rm = $r; $dm = $d; $im = $i; $jm = $j}
  2113.         }
  2114.     }
  2115.    
  2116.     if($rm == 3) {
  2117.         print "#";
  2118.     #print "==> ", $im, ",", $jm, " ",&irgb2hex($glb_pal[$im])," ",&irgb2hex($glb_pal[$jm]),"\n";
  2119.         return ($glb_pal[$im], $glb_pal[$jm]);
  2120.     }
  2121.    
  2122.     # si en fait on a qu'une seule couleur reele (parce que la palette
  2123.     # n'est pas assez discriminante par exemple), alors on prend la couleur
  2124.     # la plus frequente, et on cherche la coul realisant la plus petite erreur
  2125.     if($rm != 3) {
  2126.         $jm = $cpt[0];
  2127.         for($dm=1e30, $i=0; $i<$glb_maxcol; ++$i) {
  2128.             next if $i==$jm;
  2129.             @px = (@octet);
  2130.             for($d = $j = 0; $j<8 && $d<$dm; ++$j) {
  2131.                 $d1 = &irgb_dist($glb_pal[$i ], $px[$j]);
  2132.                 $d2 = &irgb_dist($glb_pal[$jm], $px[$j]);
  2133.                 if($d1<$d2) {$rgb = $glb_pal[$i]; $d += &sq($d1);} else {$rgb = $glb_pal[$jm]; $d += &sq($d2);}
  2134.                 $px[$j+1] = &irgb_add_cln($px[$j+1], &irgb_map(&irgb_sub($px[$j], $rgb), \@glb_map3)) if $glb_err3 && $j<7;
  2135.             }
  2136.             if($d < $dm) {$dm = $d; $im = $i;}
  2137.         }
  2138.         #print $cpt{$cpt[0]}," $dm\n";
  2139.         return ($glb_pal[$im], $glb_pal[$jm]);
  2140.     }
  2141.    
  2142.    
  2143.     if(0) {
  2144.     print "#$dm\n";
  2145.    
  2146.     ### on vérifie que le couple est bien utilisé
  2147.     @px = (@octet); $r = 0;
  2148.     for($k = 0; $k<8; ++$k) {
  2149.         $di = &irgb_dist($glb_pal[$im], $px[$k]);
  2150.         $dj = &irgb_dist($glb_pal[$jm], $px[$k]);
  2151.         if($di <= $dj) {
  2152.                     $rgb = $glb_pal[$im];
  2153.                     $r |= 1;
  2154.         } else {
  2155.                     $rgb = $glb_pal[$jm];
  2156.                     $r |= 2;
  2157.         }
  2158.         $px[$k+1] = &irgb_add_cln($px[$k+1], &irgb_map(&irgb_sub($px[$k], $rgb), \@glb_map3)) if $glb_err3 && $k<7;
  2159.     }
  2160.    
  2161.     if($r!=3) {
  2162.         print "\n\n";
  2163.         for($dm=1e30, $i=0; $i<$glb_maxcol; ++$i) {
  2164.             for($j=0; $j<=$i; ++$j) {
  2165.                 @px = (@octet);
  2166.                 for($d = $k = 0; $k<8 && $d<$dm; ++$k) {
  2167.                     $di = &irgb_dist($glb_pal[$i], $px[$k]);
  2168.                     $dj = &irgb_dist($glb_pal[$j], $px[$k]);
  2169.                     if($di <= $dj) {
  2170.                         $d += &sq($di);
  2171.                         $rgb = $glb_pal[$i];
  2172.                     } else {
  2173.                         $d += &sq($dj);
  2174.                         $rgb = $glb_pal[$j];
  2175.                     }
  2176.                     $px[$k+1] = &irgb_add_cln($px[$k+1], &irgb_map(&irgb_sub($px[$k], $rgb), \@glb_map3)) if $glb_err3 && $k<7;
  2177.                 }
  2178.                 print "$i,$j ==> $d\n";
  2179.                 if($d < $dm) {print "^^^\n"; $dm = $d; $im = $i; $jm = $j}
  2180.             }
  2181.         }
  2182.         print "\n\n";
  2183.         @px = (@octet); $r = 0;
  2184.         for($d = $k = 0; $k<8; ++$k) {
  2185.             $di = &irgb_dist($glb_pal[$im], $px[$k]);
  2186.             $dj = &irgb_dist($glb_pal[$jm], $px[$k]);
  2187.             if($di <= $dj) {
  2188.                 $d += $di;
  2189.                 $rgb = $glb_pal[$im];
  2190.                 $r |= 1;
  2191.             } else {
  2192.                 $rgb = $glb_pal[$jm];
  2193.                 $r |= 2;
  2194.             }
  2195.             print &irgb2hex($px[$k]),"=>$di,$dj=>",&irgb2hex($rgb),"\n";
  2196.             $px[$k+1] = &irgb_add_cln($px[$k+1], &irgb_map(&irgb_sub($px[$k], $rgb), \@glb_map3)) if $glb_err3 && $k<7;
  2197.         }
  2198.         die;
  2199.     }
  2200.     }
  2201.  
  2202.     print "#";
  2203.     return ($glb_pal[$im], $glb_pal[$jm]);
  2204. }
  2205.  
  2206. sub sq {
  2207.     return $_[0]*$_[0];
  2208. }
  2209.  
  2210. sub xint {
  2211.     if(0) {
  2212.     # round to even?
  2213.     my($t) = @_;
  2214.     # round to even
  2215.     my($halfway) = int($t*2)==$t*2;
  2216.     if($t>=0) {$t = int($t + 0.5);} else {$t = int($t - 0.5);}
  2217.     if($halfway) {$t = int($t/2)*2;}
  2218.     return $t;
  2219.     }
  2220.  
  2221.     return   int($_[0]);
  2222.     return   int(0.5 + $_[0]) if $_[0]>=0;
  2223.     return - int(0.5 - $_[0]);
  2224. }
  2225.  
  2226. sub write_map {
  2227.     my($name, $ram_ab, @px) = @_;
  2228.    
  2229.     my($i, $t);
  2230.    
  2231.     # récupération de la palette RGB
  2232.     my(%pal);
  2233.     foreach $i (@px) {++$pal{$i};}  
  2234.     #my(@t) = (sort { $pal{$b} - $pal{$a} } keys %pal);
  2235.     my(@t) = (sort { &irgb_module($a) - &irgb_module($b) } keys %pal);
  2236.     die "trop de couleurs" if $#t>15;
  2237.     @t = (@t, (0) x 15)[0..15];
  2238.    
  2239.     # pour le tour on utilise la couleur la plus sombre
  2240.     @t = ($t[0], $t[15], @t[1..14]);
  2241.     my($tour) = 0;
  2242.     for($i=0; $i<15; ++$i)  {
  2243.         $tour = $i if &irgb_module($t[$i])<&irgb_module($t[$tour]);
  2244.     }
  2245.    
  2246.     # pour que l'afficheur de préhisto marche, il faut que le tour soit
  2247.     # d'indexe fixe. On fait en sorte que ce soit toujours 0
  2248.     if($tour != 0) {
  2249.     my($t) = $t[$tour];
  2250.     $t[$tour] = $t[0];
  2251.     $t[0] = $t;
  2252.     $tour = 0;
  2253.     }
  2254.    
  2255.     # conversion de la palette vers les intensités thomson
  2256.     my(@pal, %rgb2pal);
  2257.     if($glb_to7pal) {
  2258.         @t = &to770_palette;
  2259.         for($i=0; $i<=$#t; ++$i) {$rgb2pal{$t[$i]} = $i;}
  2260.     } elsif($#map_ef>=0) {
  2261.         foreach $i (@t) {
  2262.             $rgb2pal{$i} = ($#pal + 1)>>1;
  2263.            
  2264.             #print &irgb2hex($i),":";
  2265.             my($v, $j, $d, $m, $p);
  2266.            
  2267.             $v = $i & 255; $i>>=10; #print "$v ";
  2268.             for($m=1e30, $j = 0; $j<=$#ef_vals; ++$j) {
  2269.                 $d = $ef_vals[$j] - $v; $d = -$d if $d<0;
  2270.                 if($d<$m) {$m = $d; $p = $j;}
  2271.             }
  2272.             my($b) = $p;
  2273.             $v = $i & 255; $i>>=10; #print "$v ";
  2274.             for($m=1e30, $j = 0; $j<=$#ef_vals; ++$j) {
  2275.                 $d = $ef_vals[$j] - $v; $d = -$d if $d<0;
  2276.                 if($d<$m) {$m = $d; $p = $j;}
  2277.             }
  2278.             my($g) = $p;
  2279.             $v = $i & 255; $i>>=10; #print "$v ";
  2280.             for($m=1e30, $j = 0; $j<=$#ef_vals; ++$j) {
  2281.                 $d = $ef_vals[$j] - $v; $d = -$d if $d<0;
  2282.                 if($d<$m) {$m = $d; $p = $j;}
  2283.             }
  2284.             my($r) = $p;
  2285.            
  2286.             push(@pal, $b, $g*16 + $r);
  2287.             #print sprintf("%x%x%x,", $b,$g,$r);
  2288.         }
  2289.     } else {
  2290.         foreach $i (@t) {
  2291.             $rgb2pal{$i} = ($#pal + 1)>>1;
  2292.  
  2293.             my($r, $g, $b) = &irgb2rgb($i);
  2294.             push(@pal, int($b*15), int($g*15)*16 + int($r*15));
  2295.         }
  2296.     }
  2297.     @t = ();
  2298.    
  2299.     # construction rama / ramb
  2300.     my(@rama, @ramb); $white = &rgb2irgb(1,1,1);
  2301.     my($idx, @cols) = (0) x 81;
  2302.     for($i=0; $i<$#px; $i += 8) {
  2303.         my(@octet) = @px[$i..($i+7)];
  2304.         # on trouve les deux couleurs
  2305.         my(%col) = ();
  2306.         foreach $t (@octet) {$col{$t} = 1;}
  2307.         @t = keys %col;
  2308.         die "trop de couleur pour l'octet" if $#t>1;
  2309.    
  2310.     # cas special pour la neige
  2311.     @t = ($white,0) if $#t==0 && $t[0]==$white;
  2312.     @t = ($white,$t[0]) if $#t==0;
  2313.    
  2314.         # 1 seule couleur.. on essaye de récuperer les couleurs de la ligne d'avant si possible
  2315.         if($#t==0) {
  2316.             if($t[0] == $cols[$idx]) {
  2317.                 $t[1] = $cols[$idx+1];
  2318.             } elsif($t[0] == $cols[$idx+1]) {
  2319.                 $t[1] = $cols[$idx];
  2320.             } else {
  2321.                 $t[1] = 7;
  2322.             }
  2323.         }
  2324.    
  2325.         @cols[$idx..$idx+1] = @t;
  2326.         $idx=0 if ($idx+=2)==80;
  2327.  
  2328.         my($forme, $fond) = ($t[0], $t[1]);
  2329.    
  2330.         %col = ();
  2331.         # pour l'instant
  2332.         $t = 0;
  2333.         for($j=0; $j<8; ++$j) {
  2334.             $t += (128>>$j) if $octet[$j]!=$fond;
  2335.         }
  2336.    
  2337.         $forme = $rgb2pal{$forme};
  2338.         $fond = $rgb2pal{$fond};
  2339.         # pour favoriser les répétitions en ramb, on fait $forme>=$fond
  2340.         if($forme >= $fond) {
  2341.         push(@rama, $t);
  2342.             push(@ramb, ($forme * 8 + ($fond&7) + ($fond & 8)*16) ^ (128+64));
  2343.         } else {
  2344.             push(@rama, $t^255);
  2345.             push(@ramb, ($fond * 8 + ($forme&7) + ($forme & 8)*16) ^ (128+64));
  2346.         }
  2347.     }
  2348.    
  2349.     # compression à proprement parler
  2350.     my(@data);
  2351.     push(@data,
  2352.         # bm 40
  2353.         0,
  2354.         # ncols-1
  2355.         39,
  2356.         # nlines-1
  2357.         24);
  2358.     push(@data,
  2359.         # ram a
  2360.         &to7_comp(@rama),
  2361.         0, 0) if $ram_ab & 1;
  2362.     push(@data,
  2363.         # ram b
  2364.         &to7_comp(@ramb),
  2365.         0, 0) if $ram_ab & 2;
  2366.    
  2367.     # if faut aligner l'extension sur une addr paire
  2368.     push(@data, 0) unless $#data & 1;
  2369.    
  2370.     push(@data,
  2371.         # to-snap
  2372.         0, 0, 0, $tour, 0, 0, @pal, 0xa5, 0x5a) unless $glb_to7pal;
  2373.  
  2374.     # ecriture fichier binaire
  2375.     open(OUT, ">$name");
  2376.     print OUT pack('C*', 0, int((1+$#data)/256), (1+$#data)&255, 0, 0);
  2377.     print OUT pack('C*', @data);
  2378.     print OUT pack('C*', 255, 0, 0, 0, 0);
  2379.     close(OUT);
  2380. }
  2381.  
  2382. sub to7_comp {
  2383.     my(@data) = @_;
  2384.     my(@result, @partial);
  2385.    
  2386.     for(my $p=0; $p<8000; ++$p) {
  2387.         # on lit car num fois
  2388.         my($num, $car) = (1, $data[($p % 200)*40 + int($p/200)]);
  2389.         while($num<255 && $p+1<8000 && $data[(($p+1) % 200)*40 + int(($p+1)/200)]==$car) {++$p; ++$num;}
  2390.         my($default) = 1;
  2391.         if($#partial>$[) {
  2392.             # 01 aa 01 bb ==> 00 02 aa bb
  2393.             if($default && $num==1 && $partial[0]==1) {@partial = (00, 02, $partial[1], $car); $default = 0;}
  2394.             # 00 n xx xx xx 01 bb ==> 00 n+1 xx xx xx bb
  2395.             if($default && $num==1 && $partial[0]==0 && $partial[1]<255) {push(@partial, $car); $partial[1]++; $default = 0;}
  2396.             # 00 n xx xx xx 02 bb ==> 00 n+2 xx xx xx bb bb (pas utile mais sert quand combiné à la regle ci-dessus)
  2397.             if($default && $num==2 && $partial[0]==0 && $partial[1]<254) {push(@partial, $car, $car); $partial[1]+=2; $default = 0;}
  2398.             # 01 aa 02 bb ==> 00 03 aa bb bb
  2399.         }        
  2400.         if($default) {push(@result, @partial); @partial = ($num, $car);}
  2401.     }
  2402.     push(@result, @partial);
  2403.    
  2404.     return @result;
  2405. }
  2406.  
  2407. sub wd_file {
  2408.     return ".watchdog";
  2409. }
  2410.  
  2411. sub reset_wd {
  2412.     unlink &wd_file;
  2413. }
  2414.  
  2415. sub start_wd {
  2416.     my($pause) = 300;
  2417.     my($child) = fork;
  2418.     die "fork failed" unless defined $child;
  2419.     return unless $child;
  2420.     while(1) {
  2421.         sleep($pause);
  2422.         my($f) = &wd_file;
  2423.         if(-f $f) {
  2424.             &reset_wd;
  2425.             kill 9, $child;
  2426.             die "Watch dog detected inactivity for $pause sec, exiting";
  2427.         } else {
  2428.             open(WDFILE,">$f");close(WDFILE);
  2429.         }
  2430.     }
  2431. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement