Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. --DawnBringer function library v1.14 (some drawing replaced)
  2. --** THIS IS NOT A RUNNABLE SCRIPT! **
  3. --by Richard Fhager
  4. -- http://hem.fyristorg.com/dawnbringer/
  5. -- Email: dawnbringer@hem.utfors.se
  6. -- MSN:   annassar@hotmail.com
  7. --
  8. -- Many functions in here was adopted from Evalion, a Javascript codecrafting/imageprocessing project
  9. -- http://goto.glocalnet.net/richard_fhager/evalion/evalion.html
  10. --
  11. --
  12. -- You may access these functions in your own scripts by loading this library,
  13. -- just add the follwing line as one of the first instructions:
  14. --
  15. -- run("dawnbringer_lib")
  16. --
  17. -- or
  18. --
  19. -- run("../libs/dawnbringer_lib.lua")
  20. --
  21. --
  22. -- Note that the functions must be called with the full library object-name, "db.function_name..."
  23. --
  24.  
  25. -- Global library object
  26. db = {}
  27.  
  28.  
  29. -- *************************************
  30. -- ***      Text & Conversions       ***
  31. -- *************************************
  32. --
  33. --
  34.  
  35. function db.rgb2HEX(r,g,b,prefix)
  36.   local c,n,s,t,z
  37.   c = {r,g,b}
  38.   z = {"0",""}
  39.   t = ""
  40.   for n = 1, 3, 1 do
  41.   s = string.upper(string.format("%x",c[n]))
  42.   t = t..z[#s]..s
  43.      --s = tonumber(c[n],16)
  44.      --t = t..s
  45.   end
  46.   return prefix..t
  47. end
  48.  
  49.  
  50. --
  51. -- ... eof Text & Conversions ...
  52. --
  53.  
  54.  
  55.  
  56. -- *************************************
  57. -- ***    Custom Math Functions     ***
  58. -- *************************************
  59. --
  60. --
  61. function db.sign(v)
  62.     local s
  63.     s = 0
  64.     if v > 0 then s = 1; end
  65.     if v < 0 then s = -1; end
  66.     return s
  67. end
  68. --
  69.  
  70. function db.distance(ax,ay,bx,by) return math.sqrt((ax-bx)^2 + (ay-by)^2); end
  71.  
  72. --
  73. function db.rotation (rot_ang,hub_x,hub_y,x,y) -- Rotate coordinates x & y relative hub
  74.   local new_ang,dist,m,xd,yd,v; m = math
  75.   xd=hub_x-x;
  76.   yd=hub_y-y;
  77.   if (not(xd==0 and yd==0)) then
  78.    v = -90; if xd < 0 then v = 90; end
  79.    new_ang = m.atan(yd/xd) - (v+rot_ang) * m.pi/180;
  80.    dist = m.sqrt(xd*xd+yd*yd);
  81.    x = hub_x - m.sin(new_ang)*dist;
  82.    y = hub_y + m.cos(new_ang)*dist;
  83.   end
  84.   return math.floor(x),math.floor(y) -- For drawing purposes
  85. end
  86. --
  87.  
  88. --
  89. function db.recursiveSum(div,rlev) -- divisons per recursion,recursion levels
  90.  local s,i,m
  91.  s,m = 1,1
  92.  for i = 1, rlev, 1 do
  93.   m = m*div
  94.   s = s + m
  95.  end
  96.  return s
  97. end
  98. --
  99.  
  100. --
  101. -- ... eof Custom Math Functions ...
  102. --
  103.  
  104. -- *************************************
  105. -- ***     Fractional Scenery        ***
  106. -- *************************************
  107.  
  108. --
  109. function db.setSceneryPalette()
  110.  db.colorCigarr(10,28,true) -- 250 colors
  111.  setcolor(250, 208,48,48)
  112.  setcolor(251, 48,208,48)
  113.  setcolor(252, 48,48,208)
  114.  setcolor(253, 224,224,64)
  115.  setcolor(254, 224,64,224)
  116.  setcolor(255, 64,224,224)
  117. end
  118. --
  119.  
  120. --
  121. function db.star(xf,yf,sx,sy,rgb,haz,out,lum)
  122.  local n,c,dist; c={}
  123.  dist = haz + out * math.sqrt((xf-sx)^2+(yf-sy)^2);
  124.  for n = 1, 3, 1 do c[n] = (rgb[n] * lum) / dist; end
  125.  return c;
  126. end
  127. --
  128.  
  129. --
  130. function db.zoom(xf,yf,zoom,panx,pany) -- Zoom and Pan in a fractional coord-system
  131.   xf = (xf-0.5)/zoom + 0.5 + panx;
  132.   yf = (yf-0.5)/zoom + 0.5 + pany;
  133.   return xf,yf
  134. end
  135. --
  136.  
  137. --
  138. function db.rotationFrac(rot_ang,hub_x,hub_y,x,y) -- Rotate coordinates x & y relative hub
  139.   local new_ang,dist,m,xd,yd,v; m = math
  140.   xd=hub_x-x;
  141.   yd=hub_y-y;
  142.   if (not(xd==0 and yd==0)) then
  143.    v = -90; if xd < 0 then v = 90; end
  144.    new_ang = m.atan(yd/xd) - (v+rot_ang) * m.pi/180;
  145.    dist = m.sqrt(xd*xd+yd*yd);
  146.    x = hub_x - m.sin(new_ang)*dist;
  147.    y = hub_y + m.cos(new_ang)*dist;
  148.   end
  149.   return x,y
  150. end
  151. --
  152.  
  153. --
  154. function db.twirl(x,y,arms,trot,tpow,tang)
  155.  local b,ang,vx,vy,vr,m,deg,tw
  156.  m=math; deg=math.pi/180; tw=.5;
  157.  if (not(x==.5 and y==.5)) then
  158.   ang = m.atan((.5-y)/(.5-x));
  159.   b = 0; if (x>.5) then b = m.pi; end
  160.   vx = .5-x; vy = .5-y; vr = m.pow(m.sqrt(vx*vx+vy*vy),tpow);
  161.   tw = .5+m.sin(-tang*deg+vr*trot+(ang + b)*arms)*.5;
  162.  end
  163.  return tw;
  164. end
  165. --
  166.  
  167. --- Alpha filters
  168. --
  169. function db.alpha1(x,y,amp) -- Coord, Amplify: 0..n
  170.  local p,a,xh,yh,m
  171.  xh=0.5-x; yh=0.5-y; m = math
  172.  p = m.pow(xh*xh+yh*yh,0.7);
  173.  a = m.cos(32*m.pi*p)*m.sin(8*m.pi*(xh+yh));
  174.  return 1 + (a * amp)
  175. end
  176. --
  177.  
  178. --
  179. -- ... eof Fractional Scenery ...
  180. --
  181.  
  182. -- *************************************
  183. -- ***    Custom Array Functions     ***
  184. -- *************************************
  185. --
  186. -- Ok, I don't know Lua that well (still unsure about some scopes & refs etc.)
  187. -- And some features may not be active in Grafx2. So, some of the follwing functions
  188. -- may exist in Lua/Grafx2...but since I'm not sure if and how they work - I'll prefer
  189. -- to add a set of my own of known performance.
  190.  
  191. --
  192. function db.newArrayInit(xs,val)
  193.   local x,ary; ary = {}
  194.    for x = 1, xs, 1 do
  195.      ary[x] = val
  196.    end
  197.   return ary
  198. end
  199. --
  200.  
  201.  
  202. --
  203. function db.newArrayInit2Dim(xs,ys,val)
  204.   local x,y,ary; ary = {}
  205.   for y = 1, ys, 1 do
  206.    ary[y] = {}
  207.    for x = 1, xs, 1 do
  208.      ary[y][x] = val
  209.    end
  210.   end
  211.   return ary
  212. end
  213. --
  214.  
  215. --
  216. -- Merge two arrays into a NEW one: array_c = db.newArrayMerge(array_b,array_b)
  217. --
  218. function db.newArrayMerge(a,b)
  219.   local n,ary; ary = {}
  220.   for n = 1, #a, 1 do
  221.    ary[n] = a[n]
  222.   end
  223.   for n = 1, #b, 1 do
  224.    ary[n+#a] = b[n]
  225.   end
  226.   return ary
  227. end
  228. --
  229.  
  230. --
  231. -- Generate a copy of an array with a new value added Last
  232. --
  233. function db.newArrayInsertLast(a,val)
  234.   local n,ary; ary = {}
  235.   for n = 1, #a, 1 do
  236.    ary[n] = a[n]
  237.   end
  238.   ary[#a+1] = val
  239.   return ary
  240. end
  241. --
  242.  
  243. --
  244. -- Generate a copy of an array with a new value added First
  245. --
  246. function db.newArrayInsertFirst(a,val)
  247.   local n,ary; ary = {}
  248.   ary[1] = val
  249.   for n = 2, #a+1, 1 do
  250.    ary[n] = a[n-1]
  251.   end
  252.   return ary
  253. end
  254. --
  255.  
  256. --
  257. function db.ary2txt(ary) -- One & two dimensions supported [a,b] -> "a,b". [[a,b],[c,d]] -> "a-b, c-d"
  258.  local t,n,m,v
  259.  t = ""
  260.  for n = 1, #ary, 1 do
  261.    if type(ary[n]) == "table" then
  262.      t = t..ary[n][1]
  263.      for m = 2, #ary[n], 1 do
  264.        t = t.."-"..ary[n][m]
  265.      end
  266.    else t = t..ary[n];
  267.    end
  268.    t = t..", "
  269.  end
  270.  return t
  271. end
  272. --
  273.  
  274. --
  275. -- *** Array data manipulation ***
  276. --
  277.  
  278. --
  279. -- InsertionSort Array, this is chaos...I'm confused and stomped...don't understand how Lua works...
  280. -- ...sorting seem be to ok but this code is ugly...
  281. -- Sort LO-HI
  282. --
  283. -- Screwed up or confused thing here I think, perhaps lo-hi/hi-lo. This is working lo-hi but the code
  284. -- looks like hi-lo...edit this some day
  285. --
  286. function db.sorti(d,idx)
  287.    local a,j,tmp,l,e
  288.    l = #d
  289.  
  290.    for a=2, l, 1 do
  291.     tmp = d[a];
  292.     e = a
  293.     for j=a, 2, -1 do  
  294.       e = j
  295.       if d[j-1][idx] > tmp[idx] then d[j] = d[j-1]; e = j-1; else break; end;
  296.     end;
  297.     d[e] = tmp; -- WHY THE F**K CAN'T YOU READ j HERE!?! STUPID ASSUCKING LANGUAGE
  298.      
  299.    end;
  300.    --return d
  301. end
  302. --
  303.  
  304. --
  305. function db.shuffle(list)
  306.  local i,n,t
  307.  for n = #list, 2, -1 do
  308.   i = 1+math.floor(math.random() * n)
  309.   t = list[n]; list[n] = list[i]; list[i] = t
  310.  end
  311. end
  312. --
  313.  
  314. --
  315. -- ... eof Custom Array Functions ...
  316. --
  317.  
  318.  
  319. -- *************************************
  320. -- ***   Misc. Logical Operations    ***
  321. -- *************************************
  322.  
  323. --
  324. -- palList [r,g,b,palindex] is expected only to contain unique colors
  325. -- index = -1 --> index of list
  326. --
  327. function db.makeIndexList(list,index)
  328.   local n,ilist
  329.   ilist = {}
  330.   for n = 1, #list, 1 do
  331.    if (index > 0) then ilist[n] = list[n][index]; end
  332.    if (index == -1) then ilist[n] = n; end
  333.   end
  334.   return ilist
  335. end
  336. --
  337.  
  338. --
  339. -- Return a list of all possible (non-same) pairs from the entries in a list
  340. -- [a,b,c] --> [[a,b],[a,c],[b,c]]
  341. -- (All entries are treated as unique. i.e it's only the INDEX that counts)
  342. -- mode = 0: Only unique pairs (m = (n^2 - n)/2), [a,b] --> [[a,b]]
  343. -- mode = 1: All pairs, i.e mirror versions as well. (m = n^2 - n), [a,b] --> [[a,b], [b,a]]
  344. --
  345. function db.pairsFromList(list,mode)
  346.  local a,b,l,n,pairs
  347.  pairs = {}
  348.  l = #list
  349.  n = 1
  350.  for a = 1, l, 1 do
  351.    for b = a+1, l, 1 do
  352.      pairs[n] = {list[a],list[b]}; n = n + 1
  353.      if mode == 1 then pairs[n] = {list[b],list[a]}; n = n + 1; end
  354.    end
  355.  end
  356.  return pairs
  357. end
  358. --
  359.  
  360. function db.valueInArray(ary,val)
  361.  local n,res
  362.  res = false
  363.  for n = 1, #ary, 1 do
  364.    if ary[n] == val then res = true; break; end
  365.  end
  366.  return res
  367. end
  368.  
  369. -- RAMP specific
  370.  
  371. -- Remove initial pair (palList) colors from pallist
  372. function db.initiateRamp(pair,pallist,pal_index)
  373.   local n,found,plist
  374.   plist = {}
  375.  
  376.   found = 1
  377.   for n = 1, #pallist, 1 do
  378.    if db.valueInArray(pair,pallist[n]) == false then
  379.     plist[found] = pallist[n]; found = found + 1;
  380.    end
  381.   end
  382.  
  383.   pair[pal_index] = plist -- ex: ["pal"]
  384.  
  385.   return pair -- Is now a 2 color RAMP
  386. end
  387. --
  388.  
  389. -- Remove new col entry from ramp's pallist and add it to the ramp, returns an updated ramp
  390. -- RampList = [1,2] ["pal"] = palList = [3,4,5], addindex = 3
  391. -- --> [1,2,3] palList = [4,5]
  392. function db.updateRamp(ramp,addindex,pal_index)
  393.   local n,found,pallist,plist,newramp
  394.   plist = {}
  395.   pallist = ramp[pal_index]
  396.  
  397.   -- New palList without added color to IndexList
  398.   found = 1
  399.   for n = 1, #pallist, 1 do
  400.    if pallist[n] ~= addindex then
  401.     plist[found] = pallist[n]; found = found + 1;
  402.    end
  403.   end
  404.  
  405.   newramp = db.newArrayInsertLast(ramplist,addindex)
  406.   newramp[pal_index] = plist
  407.  
  408.   return rlist
  409. end
  410.  
  411. --
  412. -- Returns a list of all inital ramps from color pairs
  413. --
  414. -- Weeds out bad pairs, attaches remaining palette colors and the first rgb-vector
  415. --
  416. --
  417. function db.initiateRampList(pairs,pallist,pal_index,vec_index,min,maxmult,rw,gw,bw)
  418.  local n,ramplist,newpairs,accept,dist,c1,c2,max,rD,gD,bD
  419.  ramplist = {}
  420.  max = min + (142 / math.sqrt(#pallist)) * maxmult -- min ex: 8-12
  421.  accept = 0
  422.  
  423.  for n = 1, #pairs, 1 do
  424.   c1 = pallist[pairs[n][1]]
  425.   c2 = pallist[pairs[n][2]]
  426.   rD = c2[1] - c1[1]
  427.   gD = c2[2] - c1[2]
  428.   bD = c2[3] - c1[3]
  429.   dist = math.sqrt( (rw*rD)^2 + (gw*gD)^2 + (bw*bD)^2 )
  430.  
  431.   if dist >= min and dist <= max then
  432.     accept = accept + 1; ramplist[accept] = db.initiateRamp(pairs[n],pallist,pal_index);
  433.     ramplist[accept][vec_index] = {rD, gD, bD, dist}; -- Add first color vector, ONLY KEEP DISTANCE?
  434.   end
  435.  end
  436.  
  437.  return ramplist
  438. end
  439.  
  440.  
  441. function db.findRampExpansionColors(ramp)
  442.  local clist
  443.  clist = {}
  444.  -- Calculate vectors here?
  445.  return clist
  446. end
  447.  
  448.  
  449. function db.findRAMPS(min_len, max_len)
  450.  local i,n,c,pallist,ramp,ramplist,pairs,spairs,palindex,vecindex,found,donelist,newlist,dones
  451.  local colorlist
  452.  palindex = "pal"
  453.  vecindex = "vector"
  454.  pallist =  db.fixPalette(db.makePalList(256), 0)
  455.  pairs =    db.pairsFromList(db.makeIndexList(pallist,-1), 0)
  456.  ramplist = db.initiateRampList(pairs,pallist,palindex,vecindex, 8,0.75, 0.26,0.55,0.19)
  457.  
  458.  -- MIN_LEN = 5
  459.  -- MAX_LEN = 10
  460.  
  461.  -- Split Ramp-build into two parts:
  462.  -- 1. Build ramps >= MIN_LEN, NONE added to 'Done'
  463.  -- 2. Run til no more ramps can be expanded or reaches MAX_LEN, ALL ramps added to 'Done'
  464.  
  465.  for i = 1, (min_len - 2), 1 do -- Assuming 2 for inital pairs (2 color ramps)
  466.   newlist = {}
  467.   found = 0
  468.   for n = 1, #ramplist, 1 do
  469.     ramp = ramplist[n]
  470.     colorlist = db.findRampExpansionColors(ramp) -- Colors that can split the current ramp into new expanded ramps
  471.     for c = 1, #colorlist, 1 do
  472.      found = found + 1; newlist[found] = db.updateRamp(ramp,colorlist[c],palindex); -- Ramp is expanded by 1 color
  473.     end
  474.   end
  475.   ramplist = newlist
  476.  end
  477.  
  478.  
  479.  donelist = {}; dones = 0
  480.  
  481.  repeat
  482.   newlist = {}
  483.   found = 0
  484.   for n = 1, #ramplist, 1 do
  485.     ramp = ramplist[n]
  486.     if true == false then
  487.      found = found + 1; newlist[found] = db.updateRamp(ramp,color,palindex);
  488.       else
  489.       dones = dones + 1; donelist[dones] = ramp;
  490.     end
  491.   end
  492.   --ramplist = newlist
  493.  until found == 0
  494.  
  495.  return #pairs.." - "..#ramplist
  496. end
  497.  
  498. --
  499. -- ... eof Misc. Logical Operations ...
  500. --
  501.  
  502.  
  503. -- ***************************************
  504. -- *** General RGB-Color Modifications ***
  505. -- ***************************************
  506.  
  507.  
  508. --
  509. function db.makeComplementaryColor(r,g,b,brikeeplev) -- Lev: 0 = Normal, 1 = Loose, 2 = Strict
  510.  
  511.  local bri_o,bri_n,bdiff
  512.  
  513.  function cap(v) return math.max(0,math.min(v,255)); end
  514.  
  515.  bri_o = db.getBrightness(r,g,b)
  516.  r,g,b = db.shiftHUE(r,g,b,180)
  517.  
  518.  if brikeeplev > 0 then
  519.  
  520.   for n = 0, brikeeplev*3-1, 1 do -- Must iterate to reduce brightness error
  521.     bri_n = db.getBrightness(r,g,b)
  522.     bdiff = (bri_o - bri_n) / 2 * brikeeplev
  523.     r = cap(r + bdiff)
  524.     g = cap(g + bdiff)
  525.     b = cap(b + bdiff)
  526.   end
  527.  
  528.  end
  529.  
  530.  return r,g,b
  531.  
  532. end
  533. --
  534.  
  535.  
  536. -- *** Color balance ***
  537. --
  538. -- bri_flag:   Preserve brightness
  539. -- loose_flag: Loose preservation restrictions for brightness and balance
  540. --
  541. -- Jeez, was this a tricky sucker; color-balance is just adding and capping...
  542. -- but trying color-balance with preserved perceptual brightness is a different monster...
  543. -- ...so bad I could only solve it by iterative error correction.
  544. --
  545. function db.ColorBalance(r,g,b,rd,gd,bd,bri_flag,loose_flag) -- preserve brightness
  546.  local rw,gw,bw,ri,gi,bi,itot,rni,gni,bni,ro,go,bo,ovscale,lev,count,rt,gt,bt,rf,gf,bf,bri
  547.  
  548.  -- Dawn 3.0, [0.26,0.55,0.19], 0-255 bri-colorscale adjust = 1.56905
  549.  rw,gw,bw = 0.26, 0.55, 0.19
  550.  
  551.  function cap(v) return math.min(255,math.max(v,0)); end
  552.  
  553.   bri = db.getBrightness(r,g,b)
  554.  
  555.  
  556.  -- Loose brightness & balance preservation, a good compromise.
  557.  if bri_flag == true and loose_flag == true then
  558.  
  559.    lev = (rd + gd + bd) / 3
  560.    rd = rd - lev
  561.    gd = gd - lev
  562.    bd = bd - lev
  563.  
  564.    brin = db.getBrightness(cap(r+rd),cap(g+gd),cap(b+bd))
  565.    itot = brin - bri
  566.    rd = rd - itot
  567.    gd = gd - itot
  568.    bd = bd - itot
  569.  
  570.  end
  571.  
  572.  
  573.  if bri_flag == true and loose_flag == false then
  574.  
  575.   itot = 255
  576.   count = 0
  577.  
  578.    -- Normalize (Yup, it's right only to normalize once first..cont.norm. will have some counter-effect)
  579.   lev = (rd + gd + bd) / 3
  580.   rd = rd - lev
  581.   gd = gd - lev
  582.   bd = bd - lev
  583.  
  584.  repeat
  585.  
  586.   --messagebox("Norm:"..rd..", "..gd..", "..bd)
  587.  
  588.   -- Calculate total brightness change
  589.   -- Note: Perceptual Brightness is exponential, and can't be delta-adjusted for anything other than greyscales.
  590.   -- Although the formula for the new brightness corrected normalization level can can be derived...
  591.   -- ...it doesn't do much good since the bigger problem is overflow outside the 0-255 boundary.
  592.   -- As for now, I see no other means to solve this issue than with iterative error-correction.
  593.  
  594.   rt = r+rd
  595.   gt = g+gd
  596.   bt = b+bd
  597.  
  598.   itot = 9e99
  599.   rni = rd
  600.   gni = gd
  601.   bni = bd
  602.  
  603.   -- We can get brightness of negative values etc. So bri-correction is put on hold until values are scaled down
  604.   if (rt>=0 and gt>=0 and bt>=0) and (rt<256 and gt<256 and bt<256) then
  605.     brin = db.getBrightness(rt,gt,bt)
  606.     itot = brin - bri
  607.     --messagebox("Bri Diff: "..itot)
  608.     -- Brightness adjusted balance
  609.      rni = rd - itot
  610.      gni = gd - itot
  611.      bni = bd - itot
  612.   end
  613.  
  614.   --messagebox("Bri Adj Bal:"..rni..", "..gni..", "..bni)
  615.  
  616.    -- Apply balance to find overflow (as fraction of the channel change)
  617.    ro = math.max( math.max((r + rni)-255,0), math.abs(math.min((r + rni),0)) ) / math.max(math.abs(rni),1)
  618.    go = math.max( math.max((g + gni)-255,0), math.abs(math.min((g + gni),0)) ) / math.max(math.abs(gni),1)
  619.    bo = math.max( math.max((b + bni)-255,0), math.abs(math.min((b + bni),0)) ) / math.max(math.abs(bni),1)
  620.  
  621.   ovscale = 1 - math.max(ro,go,bo)
  622.  
  623.   -- Scaling balances might be logically incorrect (as they can be seen as constant differences)
  624.   -- But scaling DOWN is quite harmless and I don't see how it could be done otherwise...
  625.   -- ex: +10 red, +5 blue: Scale x2   = +20 red, +10 blue -> More red over blue than ordered, a contrast behaviour.
  626.   --     +10 red, +5 blue: Scale x0.5 = +5 red, +2.5 blue -> Less of everything, but a part of the order. Harmless?
  627.   --
  628.   rd = rni * ovscale
  629.   gd = gni * ovscale  
  630.   bd = bni * ovscale
  631.  
  632.   count = count + 1
  633.  
  634.   --messagebox("Final bal:"..rd..", "..gd..", "..bd)
  635.  
  636.  until math.abs(itot) < 1 or count > 5
  637.  
  638.  end
  639.  
  640.  rf = r + rd
  641.  gf = g + gd
  642.  bf = b + bd
  643.  
  644.  --messagebox("Result color:"..rf..", "..gf..", "..bf)
  645.  
  646.  return rf,gf,bf
  647. end
  648. --
  649.  
  650.  
  651.  
  652. --
  653. -- bri_flag: Preserve brightness
  654. -- cap_flag: Cap new color at 0-255, has a desaturating effect for large values.
  655. --
  656. function db.ColorBalanceXXX(r,g,b,rd,gd,bd,bri_flag,cap_flag) -- preserve brightness
  657.  local rf,gf,bf
  658.  
  659.  if cap_flag == true then
  660.   rd = math.min(255,math.max(0, r+rd)) - r
  661.   gd = math.min(255,math.max(0, g+gd)) - g
  662.   bd = math.min(255,math.max(0, b+bd)) - b
  663.  end
  664.  
  665.  local rw,gw,bw,ri,gi,bi,itot,rni,gni,bni,ro,go,bo,ovscale
  666.  
  667.  
  668.  -- Dawn 3.0, [0.26,0.55,0.19], 0-255 bri-colorscale adjust = 1.56905
  669.  rw,gw,bw = 0.26, 0.55, 0.19
  670.  
  671.  if bri_flag == true then
  672.  
  673.   -- Calculate total brightness change
  674.   --ri = rd * rw
  675.   --gi = gd * gw
  676.   --bi = bd * bw
  677.   --itot = math.sqrt(ri^2 + gi^2 + bi^2)
  678.  
  679.   bri  = db.getBrightness(r,g,b)
  680.   brin = db.getBrightness(r+rd,g+gd,b+bd)
  681.   itot = brin - bri
  682.  
  683.  
  684.   -- Normalized and Brightness adjusted balance
  685.   rni = rd - itot
  686.   gni = gd - itot
  687.   bni = bd - itot
  688.  
  689.   -- Apply balance to find overflow (as fraction of the channel change)
  690.   ro = math.max( math.max((r + rni)-255,0), math.abs(math.min((r + rni),0)) ) / math.max(math.abs(rni),1)
  691.   go = math.max( math.max((g + gni)-255,0), math.abs(math.min((g + gni),0)) ) / math.max(math.abs(gni),1)
  692.   bo = math.max( math.max((b + bni)-255,0), math.abs(math.min((b + bni),0)) ) / math.max(math.abs(bni),1)
  693.  
  694.   ovscale = 1 - math.max(ro,go,bo)
  695.  
  696.   rd = rni * ovscale
  697.   gd = gni * ovscale  
  698.   bd = bni * ovscale
  699.  
  700.  end
  701.  
  702.  rf = r + rd
  703.  gf = g + gd
  704.  bf = b + bd
  705.  
  706.  return rf,gf,bf
  707. end
  708. --
  709.  
  710. --
  711. function db.getContrast(ch) -- Channel, returns fraction -1..0..1, negative for ch < 127.5
  712.  --return math.abs((ch / 127.5) - 1)
  713.  return (ch / 127.5) - 1
  714. end
  715. --
  716.  
  717. --
  718. function db.getAvgContrast(r,g,b)
  719.  return (math.abs(db.getContrast(r)) + math.abs(db.getContrast(g)) + math.abs(db.getContrast(b))) / 3  
  720. end
  721. --
  722.  
  723. --
  724. -- Mode = 0: Proportional - all colors reach max contrast at 100%
  725. --
  726. -- Mode = 1: Linear - percentage simply added
  727. --
  728. function db.changeContrastOLD(r,g,b,prc,mode)
  729.  
  730.  local m,rd,gd,bd,rv,gv,bv,rc,gc,bc,base,sign
  731.  
  732.  base = 1; sign = 1
  733.  if prc < 0 then base = 0; sign = -1; end -- decontrast
  734.  
  735.  m = prc / 100 * sign
  736.  
  737.  -- mode 0
  738.  rc = db.getContrast(r)
  739.  rd = (base - math.abs(rc)) * m  * db.sign(rc)
  740.  rv = (rc+rd+1) * 127.5
  741.  
  742.  gc = db.getContrast(g)
  743.  gd = (base - math.abs(gc)) * m  * db.sign(gc)
  744.  gv = (gc+gd+1) * 127.5
  745.  
  746.  bc = db.getContrast(b)
  747.  bd = (base - math.abs(bc)) * m  * db.sign(bc)
  748.  bv = (bc+bd+1) * 127.5
  749.  
  750.  return rv,gv,bv
  751.  
  752. end
  753. --
  754.  
  755. function db.changeContrast(r,g,b,prc) -- Photoshop style
  756.  
  757.  local m,rd,gd,bd,rv,gv,bv,rc,gc,bc
  758.  
  759.  m = 1 + math.pow((255 / 100 * prc),3) / (255*255)
  760.  
  761.  -- decontrast
  762.  if prc < 0 then
  763.   m = 1 - math.abs(prc)/100
  764.  end
  765.  
  766.  rc = db.getContrast(r)
  767.  rd = rc * m
  768.  rv = (rd+1) * 127.5
  769.  
  770.  gc = db.getContrast(g)
  771.  gd = gc * m
  772.  gv = (gd+1) * 127.5
  773.  
  774.  bc = db.getContrast(b)
  775.  bd = bc * m
  776.  bv = (bd+1) * 127.5
  777.  
  778.  return rv,gv,bv
  779.  
  780. end
  781.  
  782.  
  783.  
  784. --
  785. function db.getBrightness(r,g,b) -- 0-255
  786.  local bri
  787.  --bri = (r+g+b)/3
  788.  --bri = r*0.3 + g*0.59 + b*0.11 -- Luma Y'601
  789.  --bri = math.sqrt((r*0.3)^2 + (g*0.59)^2 + (b*0.11)^2) -- Luma Y'601
  790.  --bri = r*0.245 + g*0.575 + b*0.18 -- Dawn 2.0
  791.  
  792.  bri = math.sqrt((r*0.26)^2 + (g*0.55)^2 + (b*0.19)^2) * 1.56905 -- Dawn 3.0
  793.  return bri  
  794. end
  795. --
  796.  
  797.  
  798. --
  799. -- Note on desaturation: These functions are all junk, the only way to desaturate
  800. --                       is to fade a color into it's corresponding greyscale.
  801. --
  802.  
  803. --
  804. function db.desaturate(percent,r,g,b) -- V1.0 by Richard Fhager
  805.  local a,p
  806.  p = percent / 100
  807.  a = (math.min(math.max(r,g,b),255) + math.max(math.min(r,g,b),0)) * 0.5 * p
  808.  r = r + (a-r*p) -- a+r*(1-p)
  809.  g = g + (a-g*p)
  810.  b = b + (a-b*p)
  811.  return r,g,b
  812. end
  813. --
  814.  
  815. --
  816. function db.desaturateA(percent,c) -- array version
  817.  local r,g,b,a
  818.  r = c[1]
  819.  g = c[2]
  820.  b = c[3]
  821.  p = percent / 100
  822.  a = (math.min(math.max(r,g,b),255) + math.max(math.min(r,g,b),0)) * 0.5 * p
  823.  r = r + (a-r*p)
  824.  g = g + (a-g*p)
  825.  b = b + (a-b*p)
  826.  return {r,g,b}
  827. end
  828. --
  829.  
  830. --
  831. function db.desatAVG(desat,c) -- Desaturation, simpe average
  832.  r = c[1]
  833.  g = c[2]
  834.  b = c[3]
  835.  p = desat / 100
  836.  a = (r+g+b)/3
  837.  r = r + p*(a-r)
  838.  g = g + p*(a-g)
  839.  b = b + p*(a-b)
  840.  return {r,g,b}
  841. end
  842. --
  843.  
  844.  
  845. --
  846. function db.getSaturation(r,g,b) -- HSL
  847.   local M,m,c,s,l
  848.   M = math.max(r,g,b)
  849.   m = math.min(r,g,b)
  850.   c = (M - m)/255
  851.   s = 0
  852.   if c ~= 0 then
  853.     --l = (0.3*r + 0.59*g + 0.11*b)/255 -- HSLuma: Y'601
  854.     l = (M+m)/510 -- This produces a quite "correct looking" divison of saturation
  855.     if l <= 0.5 then s = c / (2*l); end
  856.     if l  > 0.5 then s = c / (2-2*l); end
  857.   end
  858.   return math.min(255,s * 255)
  859. end
  860. --
  861.  
  862. --
  863. function db.getTrueSaturationX(r,g,b) -- Distance from grayscale axis. Not HSV/HSL
  864.  local sat,bri
  865.  bri = (r+g+b) / 3
  866.  sat = math.min(255, math.sqrt((r-bri)^2 + (g-bri)^2 + (b-bri)^2) * 1.224744875)
  867.  return sat
  868. end
  869. --
  870.  
  871. -- WIP. Trying to find a more natural model for estimating Saturation
  872. -- Current: (HSL + True) / 2
  873. function db.getAppSaturation(r,g,b)
  874.   return  math.min(255, (db.getSaturation(r,g,b) + db.getTrueSaturationX(r,g,b)) / 2)
  875. end
  876. --
  877.  
  878. --
  879. function db.saturate(percent,r,g,b)
  880.   local a,m,p,mc
  881.   a = (math.min(math.max(r,g,b),255) + math.max(math.min(r,g,b),0)) * 0.5
  882.   m = math.min(255-math.max(r,g,b), math.min(r,g,b))
  883.   p = percent * (m / 100)
  884.   mc = math.max((r-a),(g-a),(b-a)) -- Can this be derived elsewhere?
  885.   if mc ~= 0 then
  886.    r = r + (r-a) * p / mc
  887.    g = g + (g-a) * p / mc
  888.    b = b + (b-a) * p / mc
  889.   end
  890.   return r,g,b
  891. end
  892. --
  893.  
  894. --
  895. -- Super Saturate: Better than Photoshop etc.
  896. --
  897. -- Higher than 100% power is ok
  898. --
  899. function db.saturateAdv(percent,r,g,b,brikeeplev,greydamp) -- brikeep = 0 - 2
  900.   local a,m,p,mc,bri_o,bri_n,bdiff,mx,mi,adj,q,n
  901.    function cap(v) return math.max(0,math.min(v,255)); end
  902.   mx = math.max(r,g,b)
  903.   mi = math.min(r,g,b)
  904.   bri_o = db.getBrightness(r,g,b)
  905.   a = (math.min(mx,255) + math.max(mi,0)) * 0.5
  906.   m = math.min(255-mx, mi)
  907.   p = percent * (m / 100)
  908.   mc = math.max((r-a),(g-a),(b-a)) -- Can this be derived elsewhere?
  909.   if mc ~= 0 and m ~= 0 then
  910.    adj = math.min(1,(mx - mi) / m) -- Reduce effect on low saturation
  911.    if greydamp == false then adj = 1; end
  912.    q = p / mc * adj
  913.    r = cap( r + (r-a) * q )
  914.    g = cap( g + (g-a) * q )
  915.    b = cap( b + (b-a) * q )
  916.   end
  917.   for n = 0, brikeeplev*2, 1 do -- Must iterate to reduce brightness error
  918.     bri_n = db.getBrightness(r,g,b)
  919.     bdiff = (bri_o - bri_n) / 2 * brikeeplev
  920.     r = cap(r + bdiff)
  921.     g = cap(g + bdiff)
  922.     b = cap(b + bdiff)
  923.   end
  924.   return r,g,b
  925. end
  926. --
  927.  
  928.  
  929. --
  930. -- Lightness: Darken / Brighten color (Argument and returnvalue is a rgb-list)
  931. --            Rate of change is inversely proportional to the distance of the max/min.
  932. --            i.e. all colors/channels will reach max/min at the same time (at 0 or 100 %)
  933. --            (As opposed to 'Brightness' where all channels are changed by a constant value)
  934. --
  935. function db.lightness(percent,c)
  936.  local v,r,g,b,p
  937.  r = c[1]
  938.  g = c[2]
  939.  b = c[3]
  940.  p = math.abs(percent/100)
  941.  v = 255
  942.  if percent < 0 then v = 0; end
  943.  r = r + (v - r)*p
  944.  g = g + (v - g)*p
  945.  b = b + (v - b)*p
  946.  return {r,g,b}
  947. end
  948. --
  949.  
  950. --
  951. function db.changeLightness(r,g,b,percent)
  952.  local v
  953.  v = db.lightness(percent,{r,g,b})
  954.  return v[1],v[2],v[3]
  955. end
  956. --
  957.  
  958. --
  959. function db.getLightness(r,g,b) -- HSL bi-hexcone
  960.   return (math.max(r,g,b) + math.min(r,g,b)) / 2
  961. end
  962. --
  963.  
  964. --
  965. function db.shiftHUE(r,g,b,deg) -- V1.3 R.Fhager 2007, (Heavily derived code, hehe...)
  966.  local c,h,mi,mx,d,s,p,i,f,q,t
  967.  c = {g,b,r}
  968.  mi = math.min(r,g,b)
  969.  mx = math.max(r,g,b); v = mx;
  970.  d = mx - mi;
  971.  s = 0; if mx ~= 0 then s = d/mx; end
  972.  p = 1; if g ~= mx then p = 2; if b ~= mx then p = 0; end; end
  973.  
  974.  if s~=0 then
  975.   h=(deg/60+(6+p*2+(c[1+p]-c[1+(p+1)%3])/d))%6;
  976.   i=math.floor(h);
  977.   f=h-i;
  978.   p=v*(1-s);
  979.   q=v*(1-s*f);
  980.   t=v*(1-s*(1-f));
  981.   c={v,q,p,p,t,v}
  982.   r = c[1+i]
  983.   g = c[1+(i+4)%6]
  984.   b = c[1+(i+2)%6]
  985.  end
  986.  
  987.  return r,g,b
  988. end
  989. --
  990.  
  991. --
  992. function db.getHUE(r,g,b,greytol) -- 0-6 (6.5 = Greyscale), mult. with 60 for degrees
  993.  -- 1 Color diff is roughly detected by Tolerance = 0.0078125 (Tol. incr. with lightness etc.)
  994.  local c,h,mi,mx,d,s,p,i,f,q,t
  995.  c = {g,b,r}
  996.  mi = math.min(r,g,b)
  997.  mx = math.max(r,g,b); v = mx;
  998.  d = mx - mi;
  999.  s = 0; if mx ~= 0 then s = d/mx; end
  1000.  p = 1; if g ~= mx then p = 2; if b ~= mx then p = 0; end; end
  1001.  
  1002.  h = 6.5 -- for custom graphical purposes
  1003.  if s>greytol then -- can't use >=
  1004.   h=(6+p*2+(c[1+p]-c[1+(p+1)%3])/d)%6;
  1005.  end
  1006.  
  1007.  return h
  1008. end
  1009. --
  1010.  
  1011. --
  1012. -- ... eof RGB color modifications ...
  1013. --
  1014.  
  1015.  
  1016.  
  1017. -- ****************************************
  1018. -- *** Custom Color / Palette functions ***
  1019. -- ****************************************
  1020.  
  1021.  
  1022. --# of Unique colors in palette:
  1023. --#db.fixPalette(db.makePalList(256))
  1024.  
  1025. --# of Colors in Image:
  1026. --#db.makePalListFromHistogram(db.makeHistogram())
  1027.  
  1028. --# of Unique colors in Image:
  1029. --#db.fixPalette(db.makePalListFromHistogram(db.makeHistogram()))
  1030.  
  1031. --
  1032. function db.rgbcap(r,g,b,mx,mi)
  1033.  local m = math
  1034.  return m.max(mi,m.min(r,mx)), m.max(mi,m.min(g,mx)), m.max(mi,m.min(b,mx))
  1035. end
  1036. --
  1037.  
  1038. --
  1039. function db.rgbcapInt(r,g,b,mx,mi)
  1040.  local m = math
  1041.  return m.floor(m.max(mi,m.min(r,mx))), m.floor(m.max(mi,m.min(g,mx))), m.floor(m.max(mi,m.min(b,mx)))
  1042. end
  1043. --
  1044.  
  1045.  
  1046. --
  1047. function db.makePalList(cols)
  1048.  local pal,n,r,g,b
  1049.  pal = {}
  1050.  for n = 0, cols-1, 1 do
  1051.    r,g,b = getcolor(n)
  1052.    pal[n+1] = {r,g,b,n}
  1053.  end
  1054.  return pal
  1055. end
  1056. --
  1057.  
  1058. --
  1059. function db.makeSparePalList(cols)
  1060.  local pal,n,r,g,b
  1061.  pal = {}
  1062.  for n = 0, cols-1, 1 do
  1063.    r,g,b = getsparecolor(n)
  1064.    pal[n+1] = {r,g,b,n}
  1065.  end
  1066.  return pal
  1067. end
  1068. --
  1069.  
  1070.  
  1071. --
  1072. -- Use to remove the black colors (marks unused colors) from palette-list
  1073. -- if it's known that no black color exists in the image.
  1074. function db.stripBlackFromPalList(pallist)
  1075.    local i,u,c,dummy; i = 257 -- Do 'nothing' If using a full 256 col palette with no blacks
  1076.    for u = 1, #pallist, 1 do
  1077.      c = pallist[u]
  1078.      if (c[1]+c[2]+c[3]) == 0 then i = u; end
  1079.    end
  1080.    dummy = table.remove(pallist,i)
  1081.    return pallist
  1082. end
  1083. --
  1084.  
  1085. --
  1086. function db.stripIndexFromPalList(pallist,colindex)
  1087.    local i,u,c,dummy
  1088.    for u = 1, #pallist, 1 do
  1089.      c = pallist[u]
  1090.      if c[4] == colindex then i = u; end
  1091.    end
  1092.    dummy = table.remove(pallist,i)
  1093.    return pallist
  1094. end
  1095. --
  1096.  
  1097. --
  1098. function db.addHSBtoPalette(pallist)
  1099.  local n,hue,sat,rgb
  1100.  for n=1, #pallist, 1 do
  1101.    rgb = pallist[n]
  1102.    pallist[n][5] =        db.getHUE(rgb[1],rgb[2],rgb[3],0)
  1103.    pallist[n][6] = db.getSaturation(rgb[1],rgb[2],rgb[3])
  1104.    pallist[n][7] = db.getBrightness(rgb[1],rgb[2],rgb[3])
  1105.  end
  1106.  return pallist -- {r,g,b,n,bri,hue,sat}
  1107. end
  1108. --
  1109.  
  1110. --
  1111. function db.makePalListRange(start,ends)
  1112.  local pal,n,r,g,b,a
  1113.  pal = {}
  1114.  a = 1
  1115.  for n = start, ends, 1 do
  1116.    r,g,b = getcolor(n)
  1117.    pal[a] = {r,g,b,n}; a = a + 1;
  1118.  end
  1119.  return pal
  1120. end
  1121. --
  1122.  
  1123.  
  1124. --
  1125. function db.makePalListShade(cols,sha) -- Convert colors to less bits, colorcube operations etc.
  1126.  local pal,n,r,g,b,mf,div
  1127.  mf = math.floor
  1128.  div = 256 / sha
  1129.  pal = {}
  1130.  for n = 0, cols-1, 1 do
  1131.    r,g,b = getcolor(n)
  1132.    pal[n+1] = {mf(r/div),mf(g/div),mf(b/div),n}
  1133.  end
  1134.  return pal
  1135. end
  1136. --
  1137. --
  1138. function db.makePalListShadeSPARE(cols,sha) -- Convert colors to less bits, colorcube operations etc.
  1139.  local pal,n,r,g,b,mf,div
  1140.  mf = math.floor
  1141.  div = 256 / sha
  1142.  pal = {}
  1143.  for n = 0, cols-1, 1 do
  1144.    r,g,b = getsparecolor(n)
  1145.    pal[n+1] = {mf(r/div),mf(g/div),mf(b/div),n}
  1146.  end
  1147.  return pal
  1148. end
  1149. --
  1150.  
  1151.  
  1152.  
  1153. --
  1154. function db.getColorDistance_weight(r1,g1,b1,r2,g2,b2,rw,gw,bw)
  1155.  return math.sqrt( (rw*(r1-r2))^2 + (gw*(g1-g2))^2 + (bw*(b1-b2))^2 )
  1156. end
  1157. --
  1158.  
  1159. --
  1160. -- Since brightness is exponential, each channel may work as a "star" drowning the color
  1161. -- of a lesser channel. This algorithm is an approximation to adjust distances for this phenomenon.
  1162. -- Ex: Adding 32 red to black is visually obvious, but adding 64 red to full green is almost
  1163. -- impossible to detect by the naked eye.
  1164. --
  1165. -- However this isn't a complete solution so we may weigh in brightness as well...
  1166. --
  1167. -- If cv = 0 (0..1) then prox acts as ordinary perceptual colordistance
  1168. -- if bri = 1 (0..1) then distance is only that of brightness
  1169. function db.getColorDistanceProx(r1,g1,b1,r2,g2,b2,rw,gw,bw,normalizer, cv,briweight)
  1170.  local rp1,gp1,bp1,rp2,gp2,bp2,v,m1,m2,prox,bdiff; v = 2*255*255
  1171.  m1 = math.max(r1,g1,b1)
  1172.  m2 = math.max(r2,g2,b2)
  1173.  rp1 = 1 - math.sqrt((r1-m1)^2 / v) * cv
  1174.  gp1 = 1 - math.sqrt((g1-m1)^2 / v) * cv
  1175.  bp1 = 1 - math.sqrt((b1-m1)^2 / v) * cv
  1176.  
  1177.  rp2 = 1 - math.sqrt((r2-m2)^2 / v) * cv
  1178.  gp2 = 1 - math.sqrt((g2-m2)^2 / v) * cv
  1179.  bp2 = 1 - math.sqrt((b2-m2)^2 / v) * cv
  1180.  
  1181.  bdiff = math.abs(db.getBrightness(r1,g1,b1) - db.getBrightness(r2,g2,b2)) -- weights are hardcoded in function
  1182.  prox = math.sqrt( (rw*(r1*rp1-r2*rp2))^2 + (gw*(g1*gp1-g2*gp2))^2 + (bw*(b1*bp1-b2*bp2))^2 ) * normalizer
  1183.  
  1184.   return prox * (1-briweight) + bdiff * briweight
  1185. end
  1186. --
  1187.  
  1188. --
  1189. function db.getBestPalMatch(r,g,b,pal,index_flag) -- pal = [r,g,b,palindex], index_flag -> return palindex if pal is sorted or reduced
  1190.  local diff,best,bestcol,cols,n,c,p
  1191.  cols = #pal
  1192.  bestcol = -1
  1193.  best = 9e99
  1194.  
  1195.  for n=1, cols, 1 do
  1196.   p = pal[n]
  1197.   diff = db.getColorDistance_weight(r,g,b,p[1],p[2],p[3],0.26,0.55,0.19)  * 1.569
  1198.   if diff < best then bestcol = n; best = diff; end
  1199.  end
  1200.  
  1201.  if index_flag == true then
  1202.   bestcol = pal[bestcol][4] + 1
  1203.  end
  1204.  
  1205.  return bestcol-1 -- palList index start at 1, image-palette at 0
  1206. end
  1207. --
  1208.  
  1209.  
  1210. -- Normally this function will return the (image)palette index of best color
  1211. -- ...but if the palette has been sorted with 'fixPalette' it will return the index
  1212. -- of the custom palList, setting index_flag will convert this value to image-palette index
  1213. --
  1214. -- HYBRID means the colormatch is a combo of color and (perceptual)brightness
  1215. --
  1216. --
  1217. function db.getBestPalMatchHYBRID(rgb,pal,briweight,index_flag) -- Now correctly balanced
  1218.  local diff,diffC,diffB,best,bestcol,cols,n,c,r,g,b,p,obri,pbri
  1219.  cols = #pal
  1220.  bestcol = -1
  1221.  best = 9e99
  1222.  
  1223.  --messagebox(briweight)
  1224.  
  1225.  -- Note: Not secured against negative values (this algorithm is SLOW, we cannot afford it)
  1226.  r = rgb[1]
  1227.  g = rgb[2]
  1228.  b = rgb[3]
  1229.  
  1230.  obri = db.getBrightness(r,g,b) -- 0-255
  1231.  
  1232.  for n=1, cols, 1 do
  1233.   p = pal[n]
  1234.   pbri = db.getBrightness(p[1],p[2],p[3])
  1235.   diffB = math.abs(obri - pbri)
  1236.   -- we need to normalize the distance by the weights
  1237.   diffC = db.getColorDistance_weight(r,g,b,p[1],p[2],p[3],0.26,0.55,0.19)  * 1.569
  1238.  
  1239.   diff = briweight * (diffB - diffC) + diffC
  1240.   if diff < best then bestcol = n; best = diff; end
  1241.  end
  1242.  
  1243.  if index_flag == true then
  1244.   bestcol = pal[bestcol][4] + 1 -- Since we detract 1 on return, God Lua is stupid
  1245.  end
  1246.  
  1247.  return bestcol-1 -- palList index start at 1, image-palette at 0
  1248. end
  1249. --
  1250.  
  1251.  
  1252.  
  1253. --
  1254. -- Special version of Hybrid-remapping for mixPalette list
  1255. --
  1256. -- mixpal: {score,col#1,col#2,dist,rm,gm,bm, c1_r,c1_g,c1_b, c2_r,c2_g,c2_b}
  1257. --
  1258. -- returns: {col#1,col#2} (index of palette)
  1259. --
  1260. function db.getBestPalMatchHybridMIX(rgb,mixpal,briweight,mixreduction)
  1261.  local diff,diffC,diffB,best,bestcol,cols,n,c,r,g,b,p,obri,pbri, distmult
  1262.  cols = #mixpal
  1263.  bestcol = -1
  1264.  best = 9e99
  1265.  
  1266.  -- We will simply add the the distance to the mix with the distance between the mixcolors and
  1267.  -- employ a user tolerance to much the latter will matter.
  1268.   --distmult = 255 / 9.56 / 100 * mixreduction -- 16 shades
  1269.  distmult = 1.56902 / 100 * mixreduction  -- 24-bit, Dawn3.0 colormodel
  1270.  
  1271.  -- Note: Not secured against negative values (this algorithm is SLOW, we cannot afford it)
  1272.  r = rgb[1]
  1273.  g = rgb[2]
  1274.  b = rgb[3]
  1275.  
  1276.  obri = db.getBrightness(r,g,b) -- 0-255
  1277.  
  1278.  for n=1, cols, 1 do
  1279.   p = mixpal[n]
  1280.   --pbri = db.getBrightness(p[5],p[6],p[7])
  1281.  
  1282.  -- *** DawnBringer's exponetial color brightness dither resolution phenomena theorem ***
  1283.  -- Bri = color value ^ 2
  1284.  -- Two adjacent pixels displayed with "normal high resolution" will NOT have the perceptual
  1285.  -- brightness of the resulting mixcolor. The brightness lies closer to that of the brightest pixel.
  1286.  -- Bri[(C1+C2)/2] = SQRT( (C1bri^2 + C2bri^2) / 2 )
  1287.  -- (Brightness according to Dawn-model: bri = SQRT( (r*.26)^2 + (g*.55)^2 + (b*.19)^2 ) )
  1288.  
  1289.   pbri = math.sqrt((db.getBrightness(p[8],p[9],p[10])^2 + db.getBrightness(p[11],p[12],p[13])^2) / 2)
  1290.  
  1291.   diffB = math.abs(obri - pbri)
  1292.   -- we need to normalize the distance by the weights
  1293.   diffC = db.getColorDistance_weight(r,g,b,p[5],p[6],p[7],0.26,0.55,0.19)  * 1.569 + p[4]*distmult
  1294.  
  1295.   diff = briweight * (diffB - diffC) + diffC
  1296.   if diff <= best then bestcol = n; best = diff; end
  1297.  end
  1298.  
  1299.  return {mixpal[bestcol][2], mixpal[bestcol][3]}
  1300. --return {mixpal[bestcol][2], 0}
  1301.  
  1302.  
  1303.  
  1304. end
  1305. --
  1306.  
  1307.  
  1308.  
  1309. --
  1310. function db.matchcolorHSB(h,s,b,pallist,index_flag)
  1311.  --
  1312.  -- why don't we just convert HSB-diagram to RGB and do normal colormatching?
  1313.  -- Not the same...
  1314.  --
  1315.  local n,c,best,bestcol,pb,ph,ps,diff,huediff,huecorr,hue_adj,sat_adj,bri_adj
  1316.  bestcol = -1
  1317.  best = 9e99
  1318.  
  1319.  -- higher adjust means more impact (higher hue gives more interpolation )
  1320.  hue_adj = 4
  1321.  sat_adj = 0.075
  1322.  bri_adj = 2
  1323.  
  1324.  huecorr = 255 / 6 -- Our Hue goes from 0.0 - 5.999
  1325.  
  1326.  for n=1, #pallist, 1 do
  1327.   c = pallist[n]
  1328.   ph = c[5]
  1329.   ps = c[6]
  1330.   pb = c[7]
  1331.  
  1332.   huediff = math.abs(h-ph*huecorr)
  1333.   if huediff > 127 then huediff = huediff - (huediff % 127) * 2; end
  1334.  
  1335.   --if ph == 6.5 then huediff = 0; end
  1336.  
  1337.   -- With less saturation, exact hue becomes less important and brightness more usefull
  1338.   -- This allows for greyscales and low saturation colors to work smoothly.
  1339.   huediff = huediff * (ps /255)
  1340.  
  1341.   diff = hue_adj*huediff^2 + (s-ps)^2 * sat_adj + (b-pb)^2 * bri_adj
  1342.  
  1343.   if diff <= best then bestcol = n; best = diff; end
  1344.  end
  1345.  
  1346.  if index_flag == true then
  1347.   bestcol = pallist[bestcol][4] + 1 -- Since we detract 1 on return, God Lua is stupid
  1348.  end
  1349.  
  1350.  return bestcol-1
  1351.  
  1352. end
  1353. --
  1354.  
  1355. --
  1356. -- Used by PaletteAnalysis.lua, FindRamps(), MixColors() etc.
  1357. -- Assigns is used by ApplySpare script
  1358. --
  1359. function db.fixPalette(pal,sortflag) -- Arrange palette & only keep unique colors
  1360.  
  1361.  local n,l,rgb,i,unique,bri,hue,sat,ulist,indexpal,newpal,dtot
  1362.  ulist = {}
  1363.  indexpal = {}
  1364.  newpal = {}
  1365.   local doubles,assign
  1366.   doubles = {}; assign = {}
  1367.  
  1368.  l = #pal
  1369.  
  1370.  unique = 1 -- ok, see how stupid lua is
  1371.  dtot = 0
  1372.  for n=1, l, 1 do
  1373.    rgb = pal[n]; -- actually rgbn
  1374.    i = 1 + rgb[1] * 65536 + rgb[2] * 256 + rgb[3];
  1375.    bri = db.getBrightness(rgb[1],rgb[2],rgb[3])
  1376.    if indexpal[i] == nil then
  1377.       indexpal[i] = rgb; ulist[unique] = {i,bri}; unique = unique+1;
  1378.       assign[rgb[4]+1] = rgb[4] -- really n, but can we be sure?
  1379.       else
  1380.         doubles[rgb[4]] = true; -- Mark as double (This is wrong; starts at 0...but col 0 cannot be a double so...)
  1381.         dtot = dtot + 1
  1382.         assign[rgb[4]+1] = indexpal[i][4] -- Reassign removed color
  1383.    end
  1384.  end
  1385.  
  1386.  -- sort ulist
  1387.  if sortflag == 1 then db.sorti(ulist,2); end -- sort by brightness
  1388.  
  1389.  l = #ulist
  1390.  for n=1, l, 1 do
  1391.   newpal[n] = indexpal[ulist[n][1]]
  1392.  end
  1393.  
  1394.  newpal["assigns"] = assign -- Complete list of image color assigns (removed cols will point to 1st occurence)
  1395.  newpal["doubles"] = doubles
  1396.  newpal.double_total = dtot
  1397.  
  1398.   --messagebox("unique colors", unique-1)
  1399.  
  1400.  return newpal
  1401.  
  1402. end
  1403. --
  1404.  
  1405. --
  1406. function db.drawColorspace12bit(x,y,cols,size)
  1407.  local r,g,b,c,rows,row,col,s16,rx,ry,xx,yy
  1408.  s16 = size*16
  1409.  rows = math.floor(16/cols)
  1410.  
  1411.  for g = 0, 15, 1 do
  1412.   col = g % cols
  1413.   row = math.floor(g / cols)
  1414.   for r = 0, 15, 1 do
  1415.    for b = 0, 15, 1 do
  1416.     c  = matchcolor(r*17,g*17,b*17)
  1417.     xx = x+col*s16+r*size
  1418.     yy = y+row*s16+b*size
  1419.      for ry = 0, size-1, 1 do
  1420.       for rx = 0, size-1, 1 do
  1421.        putpicturepixel(xx+rx,yy+ry,c)
  1422.      end;end
  1423.    end
  1424.   end
  1425.  end
  1426. end
  1427. --
  1428.  
  1429. --
  1430. function db.drawHSBdiagram(pallist,posx,posy,width,height,size,sat)
  1431.  --db.addHSBtoPalette(palList)
  1432.  local x,y,c
  1433.  for y = 0, height-1, 1 do
  1434.   for x = 0, width-1, 1 do
  1435.    hue = 255/width  * x
  1436.    bri = 255/height * y
  1437.    c = db.matchcolorHSB(hue,sat,bri,pallist,true)
  1438.    db.drawRectangle(posx + x*size, posy + y*size,size,size, c)
  1439.   end
  1440.  end
  1441. end
  1442. --
  1443.  
  1444. --
  1445. function db.polarHSBdiagram(ox,oy,radius,pol,brilev,huelev,saturation,dark2bright_flag)
  1446.  
  1447. local pal,bstep,bstep2,hstep,hstep2,bri,hue,sx,sy,cx,cy,x,y,p1,p2,c
  1448.  
  1449. pal = db.addHSBtoPalette(db.fixPalette(db.makePalList(256)))
  1450.  
  1451. bstep = radius / (brilev + pol)
  1452. bstep2 = bstep / 2
  1453. hstep = -360 / huelev
  1454. hstep2 = hstep / 2
  1455.  
  1456. c = 255; if dark2bright_flag then c = 0; end
  1457. drawdisk(ox,oy,math.ceil(pol*bstep),matchcolor(c,c,c))
  1458.  
  1459. for y=pol, brilev+pol-1,1 do
  1460.  
  1461.  bri = (brilev - y + pol) * (256 / brilev)
  1462.  
  1463.  if dark2bright_flag then
  1464.   bri = (brilev - (brilev - y + pol)) * (256 / brilev)
  1465.  end
  1466.  
  1467.  for x=0, huelev-1,1 do
  1468.  
  1469.   hue = x * (360 / huelev) * 255/360
  1470.  
  1471.   c = db.matchcolorHSB(hue,saturation,bri,pal,true)
  1472.  
  1473.   sx = ox
  1474.   sy = oy - y*bstep
  1475.  
  1476.   cx,cy = db.rotationFrac(x*hstep,ox,oy,sx,sy)
  1477.  
  1478.   x1,y1 = db.rotation(x*hstep-hstep2,ox,oy,ox, sy-bstep2)
  1479.   x2,y2 = db.rotation(x*hstep+hstep2,ox,oy,ox, sy-bstep2)
  1480.   x3,y3 = db.rotation(x*hstep-hstep2,ox,oy,ox, sy+bstep2)
  1481.   x4,y4 = db.rotation(x*hstep+hstep2,ox,oy,ox, sy+bstep2)
  1482.  
  1483.   p1 = {{x1,y1},{x2,y2},{x3,y3}}
  1484.   p2 = {{x3,y3},{x4,y4},{x2,y2}}
  1485.  
  1486.   db.fillTriangle(p1,c,0,true,false) -- triangle, fillcol, linecol, fill, wire
  1487.   db.fillTriangle(p2,c,0,true,false)
  1488.  
  1489.  end
  1490.  updatescreen(); if (waitbreak(0)==1) then return; end
  1491. end
  1492.  
  1493. end -- polarHSB
  1494. --
  1495.  
  1496.  
  1497. --
  1498. -- Histograms, remapping etc.
  1499. --
  1500.  
  1501. --
  1502. function db.makeHistogram()
  1503.   local n,y,x,c,w,h,list; list = {}
  1504.   w, h = getpicturesize()
  1505.   for n = 1, 256, 1 do list[n] = 0; end
  1506.   for y = 0, h - 1, 1 do
  1507.     for x = 0, w - 1, 1 do
  1508.       c = getpicturepixel(x,y)
  1509.       list[c+1] = list[c+1] + 1
  1510.     end
  1511.   end
  1512.   return list
  1513. end
  1514. --
  1515.  
  1516. --
  1517. function db.makeHistogramIndexed() -- With color index so it can be sorted etc.
  1518.   local n,y,x,c,w,h,r,g,b,list; list = {}
  1519.   w, h = getpicturesize()
  1520.   for n = 1, 256, 1 do
  1521.    r,g,b = getcolor(n-1)
  1522.    list[n] = {0,n-1,r,g,b};
  1523.   end
  1524.   for y = 0, h - 1, 1 do
  1525.     for x = 0, w - 1, 1 do
  1526.       c = getpicturepixel(x,y)
  1527.       list[c+1][1] = list[c+1][1] + 1
  1528.     end
  1529.   end
  1530.   return list
  1531. end
  1532. --
  1533.  
  1534. --
  1535. function db.makeSpareHistogram()
  1536.   local n,y,x,c,w,h,list; list = {}
  1537.   w, h = getsparepicturesize()
  1538.   --w,h = 512,360
  1539.   for n = 1, 256, 1 do list[n] = 0; end
  1540.   for y = 0, h - 1, 1 do
  1541.     for x = 0, w - 1, 1 do
  1542.       c = getsparepicturepixel(x,y)
  1543.       list[c+1] = list[c+1] + 1
  1544.     end
  1545.   end
  1546.   return list
  1547. end
  1548. --
  1549.  
  1550.  
  1551. --
  1552. -- Makes a palette-list from only the colors (histogram) that occurs in the image
  1553. -- Assumes image/palette has not changed since histogram was created
  1554. function db.makePalListFromHistogram(hist)
  1555.   local n,r,g,b,list,count
  1556.   list = {}
  1557.   count = 1
  1558.   for n = 1, #hist, 1 do
  1559.     if hist[n] > 0 then
  1560.       r,g,b = getcolor(n-1)
  1561.       list[count] = {r,g,b,n-1}
  1562.       count = count + 1
  1563.     end
  1564.   end
  1565.   return list
  1566. end
  1567. --
  1568.  
  1569. function db.makePalListFromSpareHistogram(hist)
  1570.   local n,r,g,b,list,count
  1571.   list = {}
  1572.   count = 1
  1573.   for n = 1, #hist, 1 do
  1574.     if hist[n] > 0 then
  1575.       r,g,b = getsparecolor(n-1)
  1576.       list[count] = {r,g,b,n-1}
  1577.       count = count + 1
  1578.     end
  1579.   end
  1580.   return list
  1581. end
  1582. --
  1583.  
  1584.  
  1585. --
  1586. function db.remap(org) -- Working with a remap-list there's no need of reading backuppixel
  1587.   --messagebox("Remapping")
  1588.   local x,y,c,i,w,h,s,f,col
  1589.   f = getpicturepixel
  1590.   s = false
  1591.   w, h = getpicturesize()
  1592.   for y = 0, h - 1, 1 do
  1593.    for x = 0, w - 1, 1 do
  1594.     c = f(x,y)
  1595.     i = org[c+1]
  1596.     if i == null then i = matchcolor(getbackupcolor(getbackuppixel(x,y))); s = true; col = c; end -- Find color for a removed double
  1597.     putpicturepixel(x,y,i)
  1598.    end
  1599.   end
  1600.   if s then messagebox("Remapping: Not all image colors were found in remap-list (re-assign), probably due to duplicate removal. Matchcolor was used, ex: col# "..col);
  1601.   end
  1602. end
  1603. --
  1604.  
  1605. --
  1606. -- Same as db.remap but no comments
  1607. --
  1608. function db.remapImage(colassignlist) -- assignment list is optional
  1609.  local x,y,c,w,i,h,assign
  1610.  assign = false
  1611.  if colassignlist ~= null then assign = true; end
  1612.  w,h = getpicturesize()
  1613.  for y = 0, h-1, 1 do
  1614.    for x = 0, w-1, 1 do
  1615.     c = getbackuppixel(x,y)
  1616.     i = null; if assign then i = colassignlist[c+1]; end
  1617.     if not assign or i == null then
  1618.      i = matchcolor(getbackupcolor(c))
  1619.     end
  1620.     putpicturepixel(x,y,i)
  1621.  end
  1622.  end
  1623. end
  1624. --
  1625.  
  1626. --
  1627. -- Palette DeCluster: Color-reduction by fusing similar colors into new ones, using a desired tolerance.
  1628. --                    This is a method similar to Median-Cut, but more surgical.
  1629. --
  1630. -- pallist:   Palette list {r,g,b,palette_index}
  1631. -- hist:      Histogram {color 0 pixels, color 1 pixels...etc} always a full 256 color list
  1632. -- crad:      Cluster radius treshold in % of distance between black & white
  1633. --            A value of 0 will only remove identical colors
  1634. --            A value of 3-4 will usally fuse redundant colors without causing notice
  1635. -- prot_pow:  (0..10) Protect common colors in histogram. Distances are increased by ocurrence.
  1636. --            Also gives protection to fused colors even if not using histogram (combined nominal weights)
  1637. -- pixels:    Pixels in image (so protection can be calculated)
  1638. -- rw,gw,bw:  Color weights (rw+gw+bw = 1, 0.33,0.33,0.33 is nominal)
  1639. --
  1640. -- Returns:
  1641. -- a new (c)palette list {r,g,b,{original palette_indices},fused flag, histogram_weight}
  1642. -- a remap list (org) [image color + 1] = remap color (in the new palette)
  1643. function db.deCluster(pallist, hist, crad, prot_pow, pixels, rw,gw,bw)
  1644.  
  1645.  --messagebox(pixels)
  1646.  
  1647.  local u,c,a,i,o,j,n,c1,c2,r,g,b,r1,g1,b1,r2,g2,b2,wt,rt,gt,bt,tot,pd
  1648.  local worst,wtot,maxdist,maxDist,distfrac,clusterExists,clustVal,count,crad1
  1649.  local cList,cPalList,clusterList,fuseCol,orgcols,newPalList,org
  1650.  
  1651.  maxdist = math.sqrt(rw*rw*65025 + gw*gw*65025 + bw*bw*65025)
  1652.  distfrac = 100 / maxdist
  1653.  
  1654.  -- Let's just make a slightly more suitable format of the pallist (List for original color(s))
  1655.  cPalList = {}
  1656.  for u = 1, #pallist, 1 do
  1657.   c = pallist[u]
  1658.   cPalList[u] = {c[1],c[2],c[3],{c[4]},false,hist[c[4]+1]} -- r,g,b,{original colors},fuse_marker,histogram_weight
  1659.  end
  1660.  
  1661.  --table.insert(cPalList,{255,255,0,{257},false,1})
  1662.  
  1663.  clusterExists = true
  1664.  while clusterExists do
  1665.   clusterExists = false
  1666.   clusterList = {}
  1667.  
  1668.   crad1 = crad + 1 -- avoid divison by zero
  1669.   worst = 9999
  1670.   for a = 1, #cPalList, 1 do
  1671.     c1 = cPalList[a]
  1672.     r1,g1,b1 = c1[1],c1[2],c1[3]
  1673.     wtot = c1[6]
  1674.     cList = {a}
  1675.     maxDist = 0
  1676.     for b = 1, #cPalList, 1 do
  1677.       if (b ~= a) then
  1678.         c2 = cPalList[b]
  1679.         r2,g2,b2 = c2[1],c2[2],c2[3]
  1680.         wt = c2[6]
  1681.         pd = math.pow((1 + wt / pixels),  prot_pow) -- Protection, increase distance
  1682.         dist = db.getColorDistance_weight(r1,g1,b1,r2,g2,b2,rw,gw,bw) * distfrac * pd
  1683.         if dist <= crad then
  1684.            wtot = wtot + wt
  1685.           table.insert(cList,b)
  1686.           maxDist = math.max(dist,maxDist)
  1687.         end
  1688.       end
  1689.     end -- b
  1690.     if #cList > 1 then
  1691.       clustVal = maxDist / (crad1 * #cList) * (wtot / #cList)  
  1692.       if clustVal < worst then
  1693.         worst = clustVal
  1694.         clusterList = cList
  1695.       end
  1696.     end
  1697.   end -- a
  1698.  
  1699.   --t = db.ary2txt(clusterList)
  1700.   --messagebox("Worst cluster is "..t)
  1701.  
  1702.    -- Fuse
  1703.   if #clusterList > 1 then
  1704.     clusterExists = true -- Run another iteration and look for more clusters
  1705.     fuseCol = {0,0,0,{}}
  1706.     rt,gt,bt,tot = 0,0,0,0
  1707.     for n = 1, #clusterList, 1 do
  1708.      i = clusterList[n]
  1709.      c = cPalList[i]
  1710.        --o = c[4][1] -- Original color (always #1 in list since fused colors can't re-fuse)
  1711.      o = c[4] -- Original color list
  1712.      --if c[5] == true then messagebox("Re-Fusing..."); end
  1713.      r,g,b = c[1],c[2],c[3]
  1714.        --wt = hist[o+1] -- Org. colors are 0-255
  1715.      wt = c[6]
  1716.      rt = rt + r * wt
  1717.      gt = gt + g * wt
  1718.      bt = bt + b * wt
  1719.      tot = tot + wt
  1720.      cPalList[i] = -1 -- Erase color
  1721.        --table.insert(fuseCol[4],o)
  1722.      orgcols = fuseCol[4]
  1723.      for j = 1, #o, 1 do
  1724.       table.insert(orgcols,o[j])
  1725.      end
  1726.      fuseCol[4] = orgcols
  1727.     end
  1728.  
  1729.     rt = rt / tot
  1730.     gt = gt / tot
  1731.     bt = bt / tot
  1732.     fuseCol[1] = rt
  1733.     fuseCol[2] = gt
  1734.     fuseCol[3] = bt
  1735.     fuseCol[5] = true -- fusecol marker
  1736.     fuseCol[6] = tot
  1737.     table.insert(cPalList,fuseCol)
  1738.     --messagebox(#clusterList.." Colors was fused, resulting in "..rt..", "..gt..", "..bt)
  1739.     newPalList = {}
  1740.     for n = 1, #cPalList, 1 do
  1741.      if cPalList[n] ~= -1 then
  1742.        table.insert(newPalList,cPalList[n])
  1743.       --newPalList = db.newArrayInsertLast(newPalList,cPalList[n])  
  1744.      end
  1745.     end
  1746.     cPalList = newPalList
  1747.     --messagebox("Pal length: "..#cPalList)
  1748.    statusmessage("DC - Image colors: "..#cPalList.."                       "); waitbreak(0)
  1749.   end -- fuse
  1750.  
  1751.  end -- while
  1752.  
  1753.     -- Create remap-list
  1754.     org = {}
  1755.     count = 0
  1756.     for u = 1, #cPalList, 1 do
  1757.        c = cPalList[u]
  1758.        for n = 1, #c[4], 1 do
  1759.          i = c[4][n]
  1760.          org[i+1] = count -- quick way to remap without matchcolor
  1761.        end
  1762.        count = count + 1
  1763.     end
  1764.  
  1765.  return org,cPalList
  1766.  
  1767. end; -- decluster
  1768.  
  1769.  
  1770.  
  1771. -- ------------- MEDIAN CUT V1.0 ------------
  1772. --
  1773. --   256 color Palette Lua-version (converted from Evalion JS-script)
  1774. --
  1775. --   by Richard 'DawnBringer' Fhager
  1776. --
  1777. --
  1778. -- pal:    [[r,g,b,i]]  Pallist
  1779. -- cnum:                Target number of colors in reduced palette
  1780. -- (step:)   1..          Pixel picks for processing, 1 = all pixels in image, best and slowest. 2 = 25% of pixels
  1781. -- qual:   Flag         Qualitative color selection (Normal mode)
  1782. -- quant:  Flag         Quantative color/pixel selection (Histogram) 100% mean that it count as much as quality
  1783. --  (One of or both qual/quant must be selected)
  1784. -- rgbw:   [3]          RGB-weights []. Weigh the color channels. ex: [1,1.333,0.75]
  1785. -- bits:   1-8          Bits used for each color channel in palette
  1786. -- quantpow:            0..1
  1787. --
  1788. -- return: A palette! A list of [r,g,b] values
  1789. --
  1790. -- NOTE: Quantity will act as a counterforce to altered colorspace (weights)...
  1791. -- Ex: if Green is considered bigger, it will be split into more blocks
  1792. --     but each blocks will have less colors and thus less quantity.
  1793. --
  1794. -- Perceptual colorspace (rgb-weights) will generally produce the best quality of palettes, but if
  1795. -- It's desirable to preserve stronger colors, esp. in small palettes,
  1796. -- it can be good to just use nominal space: 1,1,1
  1797. --
  1798. -- Histogram may be useful to assign more colors to an object that already covers most of the image,
  1799. -- however; if the object of interest is small in relation to a large (unimportant) background, it's
  1800. -- usually best not to have any histogram at all. Histogram will dampen strong infrequent colors.
  1801.  
  1802.  
  1803. function db.medianCut(pal,cnum,qual,quant,rgbw,bits,quantpow)
  1804.  local n,x,y,xs,ys,rgb,blocklist,blocks
  1805.  local len,res,chan,diff,maxdiff,maxblock,split
  1806.  local qualnorm, quantnorm
  1807.  
  1808.  -- Normalize 256 for quality/quantity relationship
  1809.  qualnorm =  1 / math.sqrt(rgbw[1]^2 + rgbw[2]^2 + rgbw[3]^2)
  1810.  quantnorm = 256 / #pal
  1811.  
  1812.  blocklist = {}
  1813.  blocklist[1] = {}; blocks = 1
  1814.  
  1815.  for n=1, #pal, 1 do
  1816.   blocklist[1][n] = pal[n];
  1817.  end
  1818.  
  1819.  analyzeBlock(blocklist[1],qual,quant,rgbw,qualnorm,quantnorm,quantpow)
  1820.  
  1821.  failsafe = 0
  1822.  while (blocks < cnum and failsafe < 256) do
  1823.    failsafe = failsafe + 1
  1824.   maxdiff  = -1
  1825.   maxblock = -1
  1826.   for n=1, blocks, 1 do
  1827.     diff = blocklist[n].diff
  1828.     if (diff > maxdiff) then maxdiff = diff; maxblock = n; end -- maxchan is stored as .chan in block
  1829.   end
  1830.   split = splitBlock(blocklist,maxblock,qual,quant,rgbw,qualnorm,quantnorm,quantpow)
  1831.   --if (split == false){ alert("Only found " + blocks + " (24-bit) colors!"); break;  }
  1832.   blocks = #blocklist
  1833.   --status.value = "MC: " +blocks
  1834.  end -- while
  1835.  
  1836.  return blocks2Palette(blocklist,bits)
  1837.  
  1838. end
  1839. --
  1840.  
  1841. --
  1842. function blocks2Palette(blocklist,bits)
  1843.  local n,r,g,b,c,pal,block,rgb,blen,M,dB,cB,rf,gf,bf
  1844.  
  1845.  M = math
  1846.  pal = {}
  1847.  
  1848.  --bits = 1
  1849.  dB = M.pow(2,8-bits)
  1850.  cB = M.ceil(255 / (M.pow(2,bits) - 1))
  1851.  
  1852.  for n=1, #blocklist, 1 do
  1853.   block = blocklist[n]
  1854.   r,g,b = 0,0,0
  1855.   blen = #block
  1856.   for c=1, blen, 1 do
  1857.    rgb = block[c]
  1858.    r = r + rgb[1]
  1859.    g = g + rgb[2]
  1860.    b = b + rgb[3]
  1861.   end
  1862.  
  1863.   rf = M.floor(M.min(255,M.max(0,M.floor(r/blen))) / dB) * cB
  1864.   gf = M.floor(M.min(255,M.max(0,M.floor(g/blen))) / dB) * cB
  1865.   bf = M.floor(M.min(255,M.max(0,M.floor(b/blen))) / dB) * cB
  1866.  
  1867.   pal[n] = {rf, gf, bf, 0} -- col is avg. of all colors in block (index is set (to 0) for compatibility)
  1868.  end -- blocklist
  1869.  
  1870.  return pal
  1871. end
  1872. --
  1873.  
  1874. --
  1875. function analyzeBlock(block,qual,quant,rgbw,qualnorm,quantnorm,quantpow)
  1876.  local r,g,b,n,rmin,gmin,bmin,rmax,gmax,bmax,rdif,gdif,bdif,chan,d,median,diff
  1877.  local len,Mm,Mx,rgb,kv,qu
  1878.  
  1879.  Mx,Mm = math.max, math.min
  1880.  len = #block
  1881.  
  1882.  rmin,gmin,bmin = 255,255,255
  1883.  rmax,gmax,bmax = 0,0,0
  1884.  
  1885.  for n=1, len, 1 do
  1886.   rgb = block[n]
  1887.   r = rgb[1] * rgbw[1]
  1888.   g = rgb[2] * rgbw[2]
  1889.   b = rgb[3] * rgbw[3]
  1890.   --if (!isNaN(r) and !isNaN(g) and !isNaN(b)) then -- Ignore any erroneous data
  1891.    rmin = Mm(rmin,r)
  1892.    gmin = Mm(gmin,g)
  1893.    bmin = Mm(bmin,b)
  1894.    rmax = Mx(rmax,r)
  1895.    gmax = Mx(gmax,g)
  1896.    bmax = Mx(bmax,b)
  1897.   --end
  1898.  end
  1899.  
  1900.  rdif = (rmax - rmin) -- * rgbw[1]
  1901.  gdif = (gmax - gmin) -- * rgbw[2]
  1902.  bdif = (bmax - bmin) -- * rgbw[3]
  1903.  
  1904.  d = {{rmin,rdif,rmax},{gmin,gdif,gmax},{bmin,bdif,bmax}}
  1905.  
  1906.  chan = 1 -- Widest channel
  1907.  if (gdif > rdif) then chan = 2; end
  1908.  if (bdif > rdif and bdif > gdif) then chan = 3; end
  1909.  
  1910.  -- Ok, this is the average of the max/min value rather than an actual median
  1911.  -- I guess this will fill the colorspace more uniformly and perhaps select extremes to a greater extent?
  1912.  -- Which is better?
  1913.  --median = d[chan][1] + d[chan][2] / 2 -- OLD same as median with nominal weights
  1914.  
  1915.  median = (d[chan][1] + d[chan][3]) / 2
  1916.  
  1917.  -- quantity and quality are normalized to 256 (256 is the total of colors in the set for quantity)
  1918.  -- Note that, regardless of forumla, quality (distance) must always be greater in any block than quantity (colors/pixels)
  1919.  -- Coz a block may contain many of only 1 unique color, thus rendering it impossible to split if selected.
  1920.  kv = 1
  1921.  qu = 1
  1922.  if (quant) then kv = 1 + len*quantnorm*quantpow; end
  1923.  if (qual)  then qu = d[chan][2] * qualnorm; end
  1924.  diff = qu + qu*kv^2.5
  1925.  
  1926.  block.chan   = chan
  1927.  block.diff   = diff
  1928.  block.median = median
  1929.  
  1930.  return {chan,diff,median,len}
  1931.  
  1932. end
  1933. --
  1934.  
  1935. function splitBlock(blocklist,maxblock,qual,quant,rgbw,qualnorm,quantnorm,quantpow)
  1936.   local n,cmax,median,blockA,blockB,len,cB,block,rgb,res
  1937.  
  1938.   blockA,blockB = {},{}
  1939.   block = blocklist[maxblock]
  1940.  
  1941.   res = true
  1942.  
  1943.   chan   = block.chan
  1944.   median = block.median  
  1945.  
  1946.   cB = blocklist[maxblock] -- maxblock starts at 1 when called so it should not hava a +1
  1947.   len = #cB
  1948.  
  1949.   for n=1, len, 1 do
  1950.     rgb = cB[n]
  1951.     --if (rgb[chan] >= median) then blockA.push(rgb); end
  1952.     --if (rgb[chan] <  median) then blockB.push(rgb); end
  1953.     if (rgb[chan]*rgbw[chan] >= median) then table.insert(blockA,rgb); end
  1954.     if (rgb[chan]*rgbw[chan] <  median) then table.insert(blockB,rgb); end
  1955.   end
  1956.  
  1957.   blocklist[maxblock] = blockA  -- Can't be empty right?
  1958.   analyzeBlock(blocklist[maxblock],qual,quant,rgbw,qualnorm,quantnorm,quantpow)
  1959.  
  1960.   if (#blockB > 0) then
  1961.    table.insert(blocklist,blockB)
  1962.    analyzeBlock(blocklist[#blocklist],qual,quant,rgbw,qualnorm,quantnorm,quantpow) -- no -1 on blocklist
  1963.    else
  1964.     res = false
  1965.   end
  1966.  
  1967.   return res -- false = no split
  1968. end
  1969.  
  1970. ------------ eof MEDIAN CUT --------------------------
  1971.  
  1972.  
  1973. -- ------------- MEDIAN REDUX V1.0 ------------
  1974. --
  1975. --  Divide space by greatest distance of any two colors (rather than MC-method of any given channel)
  1976. --  Basically it allows colorspace to be sliced at any angles rather than the "boxing" of MC.
  1977. --
  1978. --
  1979. --   by Richard 'DawnBringer' Fhager
  1980. --
  1981. --
  1982. -- pal:    [[r,g,b,i,h]]  Pallist (h = histogram/pixelcount)
  1983. -- cnum:                Target number of colors in reduced palette
  1984. -- (step:)   1..          Pixel picks for processing, 1 = all pixels in image, best and slowest. 2 = 25% of pixels
  1985. -- qual:   Flag         Qualitative color selection (Normal mode)
  1986. -- quant:  Flag         Quantative color/pixel selection (Histogram) 100% mean that it count as much as quality
  1987. --  (One of or both qual/quant must be selected)
  1988. -- rgbw:   [3]          RGB-weights []. Weigh the color channels. ex: [0.26, 0.55, 0.19]
  1989. -- bits:   1-8          Bits used for each color channel in palette
  1990. -- quantpow: 0..1       Quantity vs Quality (put weight into histogram/pixelcount)
  1991. -- briweight: 0..1      Brightness distance weight in colordistance
  1992. -- proxweight: 0..1     Primary Proximity distance weight in colordistance (ColorTheory-WIP: compensate for brightness of individual channels, the "extra power" of primary colors)    
  1993. --
  1994. -- return: A palette! A list of [r,g,b] values
  1995. --
  1996. -- NOTE: Quantity will act as a counterforce to altered colorspace (weights)...
  1997. -- Ex: if Green is considered bigger, it will be split into more blocks
  1998. --     but each blocks will have less colors and thus less quantity.
  1999. --
  2000. -- Perceptual colorspace (rgb-weights) will generally produce the best quality of palettes, but if
  2001. -- It's desirable to preserve stronger colors, esp. in small palettes,
  2002. -- it can be good to just use nominal space: 0.33, 0.33, 0.33
  2003. --
  2004. -- Histogram may be useful to assign more colors to an object that already covers most of the image,
  2005. -- however; if the object of interest is small in relation to a large (unimportant) background, it's
  2006. -- usually best not to have any histogram at all. Histogram will dampen strong infrequent colors.
  2007.  
  2008.  
  2009. function db.medianRedux(pal,cnum,qual,quant,rgbw,bits,quantpow, briweight, proxweight) -- pal[r,g,b,i,pixelcount]
  2010.  local n,x,y,xs,ys,rgb,blocklist,blocks
  2011.  local len,res,chan,diff,maxdiff,maxblock,split
  2012.  local qualnorm, quantnorm,count
  2013.  
  2014.  blocklist = {}
  2015.  blocklist[1] = {}; blocks = 1
  2016.  
  2017.  count = 0
  2018.  for n=1, #pal, 1 do
  2019.   blocklist[1][n] = pal[n];
  2020.   count = count + pal[n][5]
  2021.  end
  2022.  
  2023.  -- Normalize 256 for quality/quantity relationship
  2024.  qualnorm =  1 / math.sqrt(rgbw[1]^2 + rgbw[2]^2 + rgbw[3]^2)
  2025.  quantnorm = 256 / count
  2026.  
  2027.  
  2028.  -- Dist table
  2029.  statusmessage("MR: Making Distance Table..."); updatescreen(); if (waitbreak(0)==1) then return; end
  2030.  local dy,c,r1,g1,b1,i1,i2
  2031.  dt = {}
  2032.  for n=1, #pal, 1 do
  2033.   c = pal[n]
  2034.   r1,g1,b1,i1 = c[1],c[2],c[3],c[4]
  2035.   dt[i1+1] = {}
  2036.   for m=1, #pal, 1 do
  2037.    dt[i1+1][pal[m][4]+1] = db.getColorDistanceProx(r1,g1,b1,pal[m][1],pal[m][2],pal[m][3],rgbw[1],rgbw[2],rgbw[3],qualnorm, proxweight, briweight) -- pri/bri
  2038.   end
  2039.  end
  2040.  --
  2041.  
  2042.  statusmessage("MR: Analyzing Block 1..."); updatescreen(); if (waitbreak(0)==1) then return; end
  2043.  r_analyzeBlock(dt,blocklist[1],qual,quant,rgbw,qualnorm,quantnorm,quantpow)
  2044.  
  2045.  statusmessage("MR: Analyzing Blocks..."); updatescreen(); if (waitbreak(0)==1) then return; end
  2046.  failsafe = 0
  2047.  while (blocks < cnum and failsafe < 256) do
  2048.    failsafe = failsafe + 1
  2049.   maxdiff  = -1
  2050.   maxblock = -1
  2051.   for n=1, blocks, 1 do
  2052.     diff = blocklist[n].diff
  2053.     if (diff > maxdiff) then maxdiff = diff; maxblock = n; end -- maxchan is stored as .chan in block
  2054.   end
  2055.   split = r_splitBlock(dt,blocklist,maxblock,qual,quant,rgbw,qualnorm,quantnorm,quantpow)
  2056.   if (split == false) then messagebox("Only found "..blocks.." (24-bit) colors!"); break; end
  2057.   blocks = #blocklist
  2058.   statusmessage("MR: "..blocks); updatescreen(); if (waitbreak(0)==1) then return; end
  2059.  end -- while
  2060.  
  2061.  return r_blocks2Palette(blocklist,bits)
  2062.  
  2063. end
  2064. --
  2065.  
  2066. --
  2067. function r_blocks2Palette(blocklist,bits)
  2068.  local n,r,g,b,c,pal,block,rgb,blen,M,dB,cB,rf,gf,bf
  2069.  
  2070.  M = math
  2071.  pal = {}
  2072.  
  2073.  --bits = 1
  2074.  dB = M.pow(2,8-bits)
  2075.  cB = M.ceil(255 / (M.pow(2,bits) - 1))
  2076.  
  2077.  for n=1, #blocklist, 1 do
  2078.   block = blocklist[n]
  2079.   r,g,b = 0,0,0
  2080.   blen = #block
  2081.   for c=1, blen, 1 do
  2082.    rgb = block[c]
  2083.    r = r + rgb[1]
  2084.    g = g + rgb[2]
  2085.    b = b + rgb[3]
  2086.   end
  2087.  
  2088.   rf = M.floor(M.min(255,M.max(0,M.floor(r/blen))) / dB) * cB
  2089.   gf = M.floor(M.min(255,M.max(0,M.floor(g/blen))) / dB) * cB
  2090.   bf = M.floor(M.min(255,M.max(0,M.floor(b/blen))) / dB) * cB
  2091.  
  2092.   pal[n] = {rf, gf, bf} -- col is avg. of all colors in block
  2093.  end -- blocklist
  2094.  
  2095.  return pal
  2096. end
  2097. --
  2098.  
  2099. --
  2100. function r_analyzeBlock(dt,block,qual,quant,rgbw,qualnorm,quantnorm,quantpow)
  2101.  local r,g,b,n,m,rmin,gmin,bmin,rmax,gmax,bmax,rdif,gdif,bdif,chan,d,median,diff
  2102.  local len,Mm,Mx,rgb,kv,qu
  2103.  local maxdist,dist,r1,g1,b1,r2,g2,b2,c1,c2,count
  2104.  
  2105.  Mx,Mm = math.max, math.min
  2106.  len = #block
  2107.  
  2108.  rmin,gmin,bmin = 255,255,255
  2109.  rmax,gmax,bmax = 0,0,0
  2110.  
  2111.  maxdist,c1,c2,count = 0,-1,-1,0
  2112.  
  2113.  for n=1, len, 1 do
  2114.    rgb1 = block[n]
  2115.    count = count + rgb1[5] -- pixelcount for color
  2116.   for m=n+1, len, 1 do
  2117.    rgb2 = block[m]
  2118.    --dist = db.getColorDistanceProx(r1,g1,b1,r2,g2,b2,0.26,0.55,0.19,1.569, 0.1, 0.25) -- pri/bri
  2119.    dist = dt[rgb1[4]+1][rgb2[4]+1]
  2120.  
  2121.    if dist > maxdist then
  2122.     maxdist = dist
  2123.     c1 = rgb1[4]+1
  2124.     c2 = rgb2[4]+1
  2125.    end
  2126.  
  2127.   end
  2128.  end
  2129.  
  2130.  -- quantity and quality are normalized to 256 (256 is the total of colors in the set for quantity)
  2131.  -- Note that, regardless of forumla, quality (distance) must always be greater in any block than quantity (colors/pixels)
  2132.  -- Coz a block may contain many of only 1 unique color, thus rendering it impossible to split if selected.
  2133.  kv = 1
  2134.  qu = 1
  2135.  if (quant) then kv = math.pow(1 + count*quantnorm*quantpow, 0.5); end
  2136.  if (qual)  then qu = maxdist * qualnorm; end
  2137.  diff = qu*(1-quantpow) + qu*kv
  2138.  
  2139.  block.chan   = -1
  2140.  block.diff   = diff
  2141.  block.median = -1
  2142.  block.c1     = c1
  2143.  block.c2     = c2
  2144.  
  2145.  return {diff,len}
  2146.  
  2147. end
  2148. --
  2149.  
  2150. function r_splitBlock(dt,blocklist,maxblock,qual,quant,rgbw,qualnorm,quantnorm,quantpow)
  2151.   local n,cmax,median,blockA,blockB,len,cB,block,rgb,res
  2152.   local c1,c2,dist1,dist2,medr,medg,medb,r1,g1,b1,r2,g2,b2,rgb1,rgb2  
  2153.  
  2154.   blockA,blockB = {},{}
  2155.   block = blocklist[maxblock]
  2156.  
  2157.   res = true
  2158.  
  2159.   --chan   = block.chan
  2160.   --median = block.median  
  2161.   c1 = block.c1
  2162.   c2 = block.c2
  2163.  
  2164.   --rgb1 = block[c1]
  2165.   --r1,g1,b1 = rgb1[1],rgb1[2],rgb1[3]
  2166.   --rgb2 = block[c2]
  2167.   --r2,g2,b2 = rgb2[1],rgb2[2],rgb2[3]
  2168.   --medr = (r1+r2)/2
  2169.   --medg = (g1+g2)/2
  2170.   --medb = (b1+b2)/2
  2171.  
  2172.   cB = blocklist[maxblock] -- maxblock starts at 1 when called so it should not hava a +1
  2173.   len = #cB
  2174.  
  2175.   if len < 2 then return false; end
  2176.  
  2177.   for n=1, len, 1 do
  2178.     rgb = cB[n]
  2179.  
  2180.      dist1 = dt[rgb[4]+1][c1]
  2181.      dist2 = dt[rgb[4]+1][c2]
  2182.  
  2183.     if (dist1 <= dist2)
  2184.      then table.insert(blockA,rgb);
  2185.     end
  2186.  
  2187.     if (dist1 > dist2) then
  2188.      table.insert(blockB,rgb);
  2189.     end
  2190.   end
  2191.  
  2192.   blocklist[maxblock] = blockA  -- Can't be empty right?
  2193.   r_analyzeBlock(dt,blocklist[maxblock],qual,quant,rgbw,qualnorm,quantnorm,quantpow)
  2194.  
  2195.   if (#blockB > 0) then
  2196.    table.insert(blocklist,blockB)
  2197.    r_analyzeBlock(dt,blocklist[#blocklist],qual,quant,rgbw,qualnorm,quantnorm,quantpow) -- no -1 on blocklist
  2198.    else
  2199.     res = false
  2200.   end
  2201.  
  2202.   return res -- false = no split
  2203. end
  2204.  
  2205. ------------ eof MEDIAN REDUX --------------------------
  2206.  
  2207.  
  2208.  
  2209.  
  2210. --
  2211. -- ... eof Custom Color / Palette functions ...
  2212. --
  2213.  
  2214.  
  2215. -- *****************************
  2216. -- *** Custom Draw functions ***
  2217. -- *****************************
  2218.  
  2219. --
  2220. function db.line(x1,y1,x2,y2,c) -- Coords should be integers or broken lines are possible
  2221.  local n,st,m,xd,yd; m = math
  2222.  st = m.max(1,m.abs(x2-x1),m.abs(y2-y1));
  2223.  xd = (x2-x1) / st
  2224.  yd = (y2-y1) / st
  2225.  for n = 0, st, 1 do
  2226.    putpicturepixel(m.floor(x1 + n*xd), m.floor(y1 + n*yd), c );
  2227.  end
  2228. end
  2229. --
  2230.  
  2231. --
  2232. function db.lineTransp(x1,y1,x2,y2,c,amt) -- amt: 0-1, 1 = Full color
  2233.  local n,st,m,x,y,r,g,b,r1,g1,b1,c2,org; m = math
  2234.  org = 1 - amt
  2235.  st = m.max(1,m.abs(x2-x1),m.abs(y2-y1));
  2236.  for n = 0, st, 1 do
  2237.    x = m.floor(x1+n*(x2-x1)/st)
  2238.    y = m.floor(y1+n*(y2-y1)/st)
  2239.    r,g,b = getcolor(getpicturepixel(x,y))
  2240.    r1,g1,b1 = getcolor(c)
  2241.    c2 = matchcolor(r1*amt+r*org, g1*amt+g*org, b1*amt+b*org)
  2242.    putpicturepixel(x, y, c2 );
  2243.  end
  2244. end
  2245. --
  2246.  
  2247. --
  2248. function db.drawBrushRectangle(x1,y1,w,h,c)
  2249.    local x,y
  2250.    for y = y1, y1+h-1, 1 do
  2251.     for x = x1, x1+w-1, 1 do
  2252.        putbrushpixel(x,y,c);
  2253.     end
  2254.    end
  2255. end
  2256. --
  2257.  
  2258. --
  2259. function db.drawRectangle(x1,y1,w,h,c)
  2260.    local x,y
  2261.    for y = y1, y1+h-1, 1 do
  2262.     for x = x1, x1+w-1, 1 do
  2263.        putpicturepixel(x,y,c);
  2264.     end
  2265.    end
  2266. end
  2267. --
  2268.  
  2269. --
  2270. function db.drawRectangleNeg(x1,y1,w,h,c)
  2271.    local x,y,xs,ys
  2272.    xs = db.sign(w)
  2273.    ys = db.sign(h)
  2274.    if xs == 0 then xs = 1; end
  2275.    if ys == 0 then ys = 1; end
  2276.    for y = y1, y1+h-1, ys do
  2277.     for x = x1, x1+w-1, xs do
  2278.        putpicturepixel(x,y,c);
  2279.     end
  2280.    end
  2281. end
  2282. --
  2283.  
  2284. --
  2285. function db.drawRectangleLine(x,y,w,h,c)
  2286.  w = w-1
  2287.  h = h-1
  2288.  db.line(x,y,x+w,y,c)
  2289.  db.line(x,y,x,y+h,c)
  2290.  db.line(x,y+h,x+w,y+h,c)
  2291.  db.line(x+w,y,x+w,y+h,c)
  2292. end
  2293. --
  2294.  
  2295.  
  2296. --
  2297. function db.drawRectangleMix(x1,y1,w,h,c1,c2)
  2298.    local x,y,c,n
  2299.    c = {c1,c2}
  2300.    n = 0
  2301.    for y = y1, y1+h-1, 1 do
  2302.      n = n + 1
  2303.     for x = x1, x1+w-1, 1 do
  2304.        n = n + 1
  2305.        putpicturepixel(x,y,c[n%2+1]);
  2306.     end
  2307.    end
  2308. end
  2309. --
  2310.  
  2311. --
  2312. function db.drawCircle(x1,y1,r,c) -- ok, lottsa weird adjustments here, can probably be optimized...
  2313.    local x,y,d,r5,r25,r2,xr5,yr5
  2314.    r5,r25,r2,xr5,yr5 = r+0.5,r-0.25,r*2, x1-r-0.5, y1-r-0.5
  2315.    for y = 0, r2, 1 do
  2316.     for x = 0, r2, 1 do
  2317.        d = math.sqrt((x-r5)^2 + (y-r5)^2)
  2318.        if d < r25 then putpicturepixel(x + xr5, y + yr5,c); end
  2319.     end
  2320.    end
  2321. end
  2322. --
  2323.  
  2324. --
  2325. function db.drawBrushCircle(x1,y1,r,c) -- ok, lottsa weird adjustments here, can probably be optimized...
  2326.    local x,y,d
  2327.    for y = 0, r*2, 1 do
  2328.     for x = 0, r*2, 1 do
  2329.        d = math.sqrt((x-r-0.5)^2 + (y-r-0.5)^2)
  2330.        if d < r-0.25 then putbrushpixel(x1+x-r-0.5,y1+y-r-0.5,c); end
  2331.     end
  2332.    end
  2333. end
  2334. --
  2335.  
  2336. --
  2337. -- Rotation in degrees
  2338. -- Step is # of line segments (more is "better")
  2339. -- a & b are axis-radius
  2340. function db.ellipse2(x,y,a,b,stp,rot,col)
  2341.  local n,m=math,rad,al,sa,ca,sb,cb,ox,oy,x1,y1,ast
  2342.  m = math; rad = m.pi/180; ast = rad * 360/stp;
  2343.  sb = m.sin(-rot * rad); cb = m.cos(-rot * rad)
  2344.  for n = 0, stp, 1 do
  2345.   ox = x1; oy = y1;
  2346.   sa = m.sin(ast*n) * b; ca = m.cos(ast*n) * a
  2347.   x1 = x + ca * cb - sa * sb
  2348.   y1 = y + ca * sb + sa * cb
  2349.   --if (n > 0) then db.line(ox,oy,x1,y1,col); end
  2350.   if (n > 0) then drawline(ox,oy,x1,y1,col); end
  2351.  end
  2352. end
  2353. --
  2354.  
  2355.  
  2356.  
  2357. --[[
  2358. var ER = 0.3
  2359. var DR = 0.15
  2360.  
  2361. ellipse(0.5*xx,0.5*yy,DR*xx,6,Math.PI*0)
  2362.  
  2363. function ellipse(x,y,r,stp,rot){
  2364.  var n,deg=360,m=Math,rad=Math.PI/180,rn
  2365.  var ox,oy,x1,y1,x2,y2,d1,r1 = ER * xx
  2366.  
  2367.  for (n=0; n<=deg; n+=stp){
  2368.  
  2369.   ox = x2; oy = y2, rn = rad * n
  2370.   d1 = rn - rot
  2371.   x1 = x + m.sin(d1) * r
  2372.   y1 = y + m.cos(d1) * r
  2373.  
  2374.   x2 = x1 + m.sin(-rn) * r1
  2375.   y2 = y1 + m.cos(-rn) * r1
  2376.   if (n > 0){ line_rgb(MX,[0,0,0],0,ox,oy,x2,y2) }
  2377.  }
  2378. }
  2379.  
  2380. }
  2381.  
  2382. ellipse2(0.5*xx,0.5*yy,15,8,200,22,[0,0,0],0.5)
  2383.  
  2384. function ellipse2(x,y,a,b,stp,rot,rgb,transp){
  2385.  var n,m=Math,rad=m.PI/180,al,sa,ca,sb,cb,ox,oy,x1,y1
  2386.  sb = m.sin(-rot * rad); cb = m.cos(-rot * rad)
  2387.  for (n=0; n<=stp; n++){
  2388.   ox = x1; oy = y1; al = rad * 360/stp * n
  2389.   sa = m.sin(al) * b; ca = m.cos(al) * a
  2390.   x1 = x + ca * cb - sa * sb
  2391.   y1 = y + ca * sb + sa * cb
  2392.   if (n > 0){ line_rgb(MX,rgb,transp,ox,oy,x1,y1) }
  2393.  }
  2394. }
  2395.  
  2396.  
  2397. ]]
  2398.  
  2399.  
  2400.  
  2401. function db.obliqueCube(side,x,y,r,g,b,bri,cols)
  2402.  local n,c,depth,x1,y1,x2,y2,f
  2403.  
  2404.  asscols = false
  2405.  if cols >= 0 and cols<250 then
  2406.   asscols = true
  2407.   c = cols;                   setcolor(cols,r,g,b);       cols = cols + 1
  2408.   cP50 = cols; q =  bri*0.5;  setcolor(cols,r+q,g+q,b+q); cols = cols + 1;
  2409.   cP75 = cols; q =  bri*0.75; setcolor(cols,r+q,g+q,b+q); cols = cols + 1;
  2410.   cM50 = cols; q = -bri*0.5;  setcolor(cols,r+q,g+q,b+q); cols = cols + 1;
  2411.   cM100= cols; q = -bri;      setcolor(cols,r+q,g+q,b+q); cols = cols + 1;
  2412.  end
  2413.  
  2414.   f = matchcolor
  2415.   if asscols == false then
  2416.    c = f(r,g,b)
  2417.    cP50 =  f(r+bri*0.5,g+bri*0.5,b+bri*0.5)
  2418.    cP75 =  f(r+bri*0.75,g+bri*0.75,b+bri*0.75)
  2419.    cM50 =  f(r-bri*0.5,g-bri*0.5,b-bri*0.5)
  2420.    cM100 = f(r-bri,g-bri,b-bri)
  2421.   end
  2422.  
  2423.  
  2424.  depth = math.floor(side / 2)
  2425.  
  2426.  for n = 0, depth-1, 1 do
  2427.   db.line(x+side+n,y-1-n,x+side+n,y+side-n-1,cM50)
  2428.   --drawline(x+side+n,y-1-n,x+side+n,y+side-n-1,cM50)
  2429.  end
  2430.  
  2431.  for n = 0, depth-1, 1 do
  2432.   db.line(x+n,y-1-n,x+side+n-1,y-1-n,cP50)
  2433.   --drawline(x+n,y-1-n,x+side+n-1,y-1-n,cP50)
  2434.  end
  2435.  
  2436.  --  /
  2437.  --  
  2438.  --db.line(x+side,y-1,x+side+depth-1,y-depth,c)
  2439.  
  2440.  -- Smoothing & Shade
  2441.  
  2442.  --
  2443.  --  /
  2444.  --db.line(x+side,y+side-1,x+side+depth-1,y+side-depth,cM100)
  2445.  
  2446.  --db.line(x,y,x+side-2,y,cP75)
  2447.  --db.line(x,y,x,y+side-2,cP75)
  2448.  
  2449.  db.drawRectangle(x,y,side,side,c)
  2450.  
  2451.  return cols
  2452.  
  2453. end
  2454.  
  2455.  
  2456. function db.obliqueCubeBRI(side,x,y,r,g,b,bri,pallist,briweight,index_flag)
  2457.  local n,c,depth,x1,y1,x2,y2
  2458.  
  2459.   --f = db.getBestPalMatchHYBRID
  2460.   c =  db.getBestPalMatchHYBRID({r,g,b},    pallist, briweight, index_flag)
  2461.   cP50 =  db.getBestPalMatchHYBRID({r+bri*0.5,g+bri*0.5,b+bri*0.5},    pallist, briweight, index_flag)
  2462.   cP75 =  db.getBestPalMatchHYBRID({r+bri*0.75,g+bri*0.75,b+bri*0.75}, pallist, briweight, index_flag)
  2463.   cM50 =  db.getBestPalMatchHYBRID({r-bri*0.5,g-bri*0.5,b-bri*0.5},    pallist, briweight, index_flag)
  2464.   cM100 = db.getBestPalMatchHYBRID({r-bri,g-bri,b-bri},                pallist, briweight, index_flag)
  2465.  
  2466.  depth = math.floor(side / 2)
  2467.  
  2468.  db.drawRectangle(x,y,side,side,c)
  2469.  
  2470.  for n = 0, depth-1, 1 do
  2471.   db.line(x+side+n,y-1-n,x+side+n,y+side-n-1,cM50)
  2472.   --drawline(x+side+n,y-1-n,x+side+n,y+side-n-1,cM50)
  2473.  end
  2474.  
  2475.  for n = 0, depth-1, 1 do
  2476.   db.line(x+n,y-1-n,x+side+n-1,y-1-n,cP50)
  2477.   --drawline(x+n,y-1-n,x+side+n-1,y-1-n,cP50)
  2478.  end
  2479.  
  2480.  --  /
  2481.  --  
  2482.  db.line(x+side,y-1,x+side+depth-1,y-depth,c)
  2483.  --drawline(x+side,y-1,x+side+depth-1,y-depth,c)
  2484.  
  2485.  
  2486.  -- Smoothing & Shade
  2487.  
  2488.  --
  2489.  --  /
  2490.  --db.line(x+side,y+side-1,x+side+depth-1,y+side-depth,cM100)
  2491.  
  2492.  --db.line(x,y,x+side-2,y,cP75)
  2493.  --db.line(x,y,x,y+side-2,cP75)
  2494.  
  2495.  
  2496. end
  2497.  
  2498.  
  2499. --
  2500. function db.fillTriangle(p,fcol,lcol,fill,wire) -- p = list of 3 points
  2501.  
  2502.  local n,x,y,x1,x2,y1,y2,xf,yf,len,mr
  2503.  
  2504.  mr = math.floor
  2505.  
  2506.  -- Convert to screen/matrix-coordinates
  2507.  --if (mode == 'percent')  then xf = xx / 100; yf = yy / 100; end
  2508.  --if (mode == 'fraction') then xf = xx; yf = yy; end
  2509.  --if (mode ~= 'absolute') then screenilizeTriangle(p,xf,yf); end
  2510.  
  2511.  if (fill) then
  2512.   local Ax,Ay,Bx,By,Cx,Cy,xd,a,b,yc,ABdy,BCdy,ABix,BCix,ACix
  2513.  
  2514.   xd = {}
  2515.  
  2516.   --sort(p,1)                    -- Find top and middle y-point
  2517.   db.sorti(p,2)  
  2518.  
  2519.   Ay = p[1][2]; Ax = p[1][1]
  2520.   By = p[2][2]; Bx = p[2][1]
  2521.   Cy = p[3][2]; Cx = p[3][1]
  2522.  
  2523.   ABdy = By - Ay
  2524.   BCdy = Cy - By
  2525.   ABix = (Bx - Ax) / ABdy
  2526.   BCix = (Cx - Bx) / BCdy
  2527.   ACix = (Cx - Ax) / (Cy - Ay)
  2528.  
  2529.   a=1; b=2;
  2530.   if (ACix < ABix) then a=2; b=1; end
  2531.   for y = 0, ABdy-1, 1 do -- Upper -1
  2532.    xd[a] = mr(Ax + ABix * y)
  2533.    xd[b] = mr(Ax + ACix * y)
  2534.    yc = y+Ay;
  2535.    for x=xd[1], xd[2], 1 do
  2536.     putpicturepixel(x,yc,fcol)
  2537.    end
  2538.   end
  2539.  
  2540.   a=1; b=2;
  2541.   if (BCix < ACix) then a=2; b=1; end
  2542.   for y = 0, BCdy, 1 do -- Lower
  2543.    xd[a] = mr(Cx - BCix * y);
  2544.    xd[b] = mr(Cx - ACix * y)
  2545.    yc = Cy-y;
  2546.    for x = xd[1], xd[2], 1 do
  2547.     putpicturepixel(x,yc,fcol)
  2548.    end
  2549.   end
  2550.  
  2551.  end -- eof fill
  2552.  
  2553.  if (wire) then
  2554.   for n = 0, 2, 1 do -- Outline
  2555.    x1 = p[n+1][1]; y1 = p[n+1][2]
  2556.    x2 = p[1 + (n+1) % 3][1]; y2 = p[1 + (n+1) % 3][2]
  2557.    --db.line(x1,y1,x2,y2,lcol)
  2558.    drawline(x1,y1,x2,y2,lcol)
  2559.   end
  2560.  end
  2561.  
  2562. end -- eof fillTriangle
  2563. --
  2564.  
  2565. --
  2566. -- ... eof Custom Draw functions ...
  2567. --
  2568.  
  2569.  
  2570. -- ******************************
  2571. -- *** Filters & Convolutions ***
  2572. -- ******************************
  2573.  
  2574.  
  2575. function db.applyConvolution2Pic(convmx,divisor,bias,neg,amt)
  2576.  local r,g,b,mx,my,cx,cy,mxh,myh,mp,rb,gb,bb,xx,yy,x,y,w,h,div,n1,n2,amtr,ro,go,bo
  2577.  
  2578.  n1 = 1
  2579.  n2 = bias
  2580.  if neg == 1 then
  2581.   n1 = -1
  2582.   n2 = 255 + bias
  2583.  end
  2584.  
  2585.  amtr = 1 - amt
  2586.  w, h = getpicturesize()
  2587.  cy = #convmx
  2588.  cx = #convmx[1]
  2589.  mxh = math.floor(cx / 2) + 1
  2590.  myh = math.floor(cy / 2) + 1
  2591.  
  2592.   for y = 0, h-1, 1 do
  2593.   for x = 0, w-1, 1 do
  2594.    r,g,b = 0,0,0
  2595.    ro,go,bo = getcolor(getbackuppixel(x,y))
  2596.    div = divisor
  2597.    for my = 1, cy, 1 do
  2598.     for mx = 1, cx, 1 do
  2599.      xp = mx-mxh
  2600.      yp = my-myh
  2601.      mp = convmx[my][mx]
  2602.      xx = x + xp
  2603.      yy = y + yp
  2604.       if yy>=0 and yy<h and xx>=0 and xx<w then
  2605.        rb,gb,bb = getcolor(getbackuppixel(xx,yy))
  2606.        r = r + rb * mp
  2607.        g = g + gb * mp
  2608.        b = b + bb * mp
  2609.         else div = div - mp -- Assumes divisor is the sum of the convolution matrix
  2610.       end
  2611.     end
  2612.    end
  2613.    r = ro*amtr + (n2 + (n1 * r) / div)*amt -- +bias
  2614.    g = go*amtr + (n2 + (n1 * g) / div)*amt
  2615.    b = bo*amtr + (n2 + (n1 * b) / div)*amt
  2616.    putpicturepixel(x,y,matchcolor(r,g,b))
  2617.  end;
  2618.   updatescreen(); if (waitbreak(0)==1) then return; end
  2619.  end;
  2620.  
  2621. end
  2622.  
  2623. --
  2624. -- ... eof Filters & Convolutions ...
  2625. --
  2626.  
  2627.  
  2628.  
  2629. -- *****************************
  2630. -- *** Fractals etc.         ***
  2631. -- *****************************
  2632.  
  2633. -- Fractal Pattern V1.0 by Richard Fhager (mod allows for wrapping)
  2634. --
  2635. -- Pattern matrix example: {{1,1,1},{1,0,1},{1,1,1}}
  2636. --
  2637. function db.pattern(x,y,p,n,i) -- coord as fraction of 1, pattern, offset(0), iterations (1-15)
  2638.  local px,py
  2639.  py = #p
  2640.  px = #p[1]
  2641.  while ((p[1+math.abs(math.floor(y*py))%py][1+math.abs(math.floor(x*px))%px]) > 0 and n<i) do
  2642.   x=x*px-math.floor(x*px);
  2643.   y=y*py-math.floor(y*py);
  2644.   n = n+1
  2645.  end
  2646.  return 1 - n/i;
  2647. end
  2648. --
  2649.  
  2650. --
  2651. function db.patternDec(x,y,p,n,i) -- coord as fraction of 1, pattern, offset(0), iterations (1-15)
  2652.  local px,py,spfrac,nfrac,fx,fy
  2653. spfrac = 1
  2654. nfrac = 0
  2655.  py = #p
  2656.  px = #p[1]
  2657.  while (spfrac > 0 and n<i) do
  2658.    fy = math.floor(math.abs(y*py))%py
  2659.    fx = math.floor(math.abs(x*px))%px
  2660.    spfrac = p[fy+1][fx+1]
  2661.    if (spfrac>0) then
  2662.     x = x*px - fx
  2663.     y = y*py - fy
  2664.     nfrac = nfrac + spfrac
  2665.    end
  2666.   n = n+1
  2667.  end
  2668.  --return 1 - n/i;
  2669.  return 1 - nfrac/i
  2670. end
  2671. --
  2672.  
  2673.  
  2674. --
  2675. function db.mandel(x,y,l,r,o,i) -- pos. as fraction of 1, left coord, right coord, y coord, iterations
  2676.  
  2677.   local w,s,a,p,q,n,v,w
  2678.  
  2679.   s=math.abs(r-l);
  2680.  
  2681.   a = l + s*x;
  2682.   p = a;
  2683.   b = o - s*(y-0.5);
  2684.   q = b;
  2685.   n = 1;
  2686.   v = 0;
  2687.   w = 0;
  2688.  
  2689.   while (v+w<4 and n<i) do n=n+1; v=p*p; w=q*q; q=2*p*q+b; p=v-w+a; end;
  2690.  
  2691.   return n
  2692. end
  2693. --
  2694.  
  2695. --
  2696. -- ... eof Fractals etc. ...
  2697. --
  2698.