diff --git a/share/grafx2/scripts/ani_db_3DPalette.lua b/share/grafx2/scripts/ani_db_3DPalette.lua new file mode 100644 index 00000000..bf119e65 --- /dev/null +++ b/share/grafx2/scripts/ani_db_3DPalette.lua @@ -0,0 +1,431 @@ +--3D-Palette viwer V0.7 (HSL-models added, 3D-World added, Pen-color only cycles thru unique colors, InputBox) +--by Richard 'Dawnbringer' Fhager + +-- Mouse: Rotate Cube (Stops animation) +-- Arrow-keys: Move Cube (in 3D world) +-- F1: Start/Stop animation +-- F2: Reset +-- F3: Increase Color-Size +-- F4: Decrease Color-Size +-- F5: (Wip) Cycle thru selected PenColor (Note that only unique colors are displayed) +-- F9: RGB-space model +--F10: HSL-space model +--F11: HSLcubic-space model +-- "+" (Num): Zoom In +-- "-" (Num): Zoom Out +-- Esc: Exit script + +require("dawnbringer_lib") + + +BRIDIAG_SHOW = 1 -- Show brightness/Grayscale diagonal (1 = on, 0 = off) +ANIM = 1 -- Animation (1 = on, 0 = off) +BOX_DRK = 8 -- Darkest color used for box (0-255) +BOX_BRI = 112 -- Brightest color used for box (0-255) +COLSIZE_BASE = 26 -- Colors base size (value to adjusted by palette-size, with 2 cols maxsize is v / 1.23) + +-- +OK,RGB,HSL,HSLC,BOX_BRI,COLSIZE_BASE,SET800x600 = inputbox("3D-Palette Viever Settings", + + "1. RGB space [F9]", 1, 0,1,-1, + "2. HSL space [F10]", 0, 0,1,-1, + "3. HSL-cubic space [F11]",0, 0,1,-1, + "Box Brightness (16-255)", BOX_BRI, 16,255,0, + "Col Size (1-100) [F3/F4]", COLSIZE_BASE, 1,100,0, + "Set Screen to 800x600", 1,0,1,0 + +); +-- + +if OK then + + if SET800x600 == 1 then setpicturesize(800,600); end + + SPACE = "rgb" + FORM = "cube" + if HSL == 1 then + SPACE = "hsl" + FORM = "cylinder" + end + if HSLC == 1 then + SPACE = "hsl_cubic" + FORM = "cube" + end + + +pal = db.fixPalette(db.makePalList(256)) + +FG = getforecolor() +BG = getbackcolor() + +palcol = FG + +-- +function initColors(space) + for n = 1, #pal, 1 do + c = pal[n]; + if space == "rgb" then + cols[n] = {c[1]/128-1,c[2]/128-1,c[3]/128-1,c[4]}; + end + if space == "hsl_cubic" then + cols[n] = {} + cols[n][1] = (db.getHUE(c[1],c[2],c[3],0) / 6.0 * 255) / 128 - 1 + cols[n][2] = (db.getSaturation(c[1],c[2],c[3])) / 128 - 1 + cols[n][3] = (db.getLightness(c[1],c[2],c[3])) / 128 - 1 + cols[n][4] = c[4] + end + if space == "hsl" then + cols[n] = {} + hue = db.getHUE(c[1],c[2],c[3],0) / 6.0 * math.pi*2 + rad = db.getSaturation(c[1],c[2],c[3]) / 256 + cols[n][1] = math.cos(hue) * rad + cols[n][2] = math.sin(hue) * rad + cols[n][3] = (db.getLightness(c[1],c[2],c[3])) / 128 - 1 + cols[n][4] = c[4] + end + end +end +-- + +cols = {} -- Make points of palette colors +colz = {} -- To hold calculated points +initColors(SPACE) + + +function initPointsAndLines(form,bridiag) + if form == "cube" then + pts = {{-1,1,-1},{1,1,-1},{1,-1,-1},{-1,-1,-1}, -- The box + {-1,1, 1},{1,1, 1},{1,-1, 1},{-1,-1, 1}} + lin = {{1,2},{2,3},{3,4},{4,1},{5,6},{6,7},{7,8},{8,5},{1,5},{2,6},{3,7},{4,8}} -- Box Lines + if bridiag == 1 then lin[13] = {4,6}; end + end + if form == "cylinder" then + p = 28 + pts = {} + lin = {} + for n = 1, p, 1 do + x = math.cos(math.pi*2 / p * (n-1)) + y = math.sin(math.pi*2 / p * (n-1)) + pts[n] = {x,y,-1} + lin[n] = {n,1 + (n%p)} + pts[n + p] = {x,y,1} + lin[n + p] = {n+p,p + 1 + (n%p)} + end + lin[p*2+1] = {1,p+1} -- Red (0 degrees) + lin[p*2+2] = {p+1,p+1+math.ceil(p/2)} -- Lightness end (needs an even # of points to work) + end +end + +boxp = {} -- To hold the calculated points +initPointsAndLines(FORM,BRIDIAG_SHOW) + +w,h = getpicturesize() +CX,CY = w/2, h/2 + + + +function initAndReset() + XANG, YANG, ZANG, ZOOM, COLSIZE_ADJ, XD, YD, WORLD_X, WORLD_Y = 0,0,0,0,0,0,0,0,0 +end + +initAndReset() + +SIZE = math.min(w,h)/4 +DIST = 5 -- Distance perspective modifier, ~5 is nominal, more means "less 3D" + +CMAXSIZE = math.floor(COLSIZE_BASE / ((#pal)^0.3)) +--CMAXSIZE = 8 +CMINSIZE = 1 -- Negative values are ok. Color are never smaller than 1 pix + +BOX_LINE_DIV = 20 -- Number of colors/segments that a box-line can be divided into (depth) +BOX_DIV_MULT = BOX_LINE_DIV / (math.sqrt(3)*2) + +-- Box depth colors +box_div = {} +for n = 0, BOX_LINE_DIV-1, 1 do + c = BOX_DRK + (BOX_BRI / (BOX_LINE_DIV - 1)) * n + --box_div[BOX_LINE_DIV - n] = matchcolor(c,c,c) + box_div[BOX_LINE_DIV - n] = db.getBestPalMatchHYBRID({c,c,c},pal,0.5,true) +end + +--BOX_COL = matchcolor(80,80,80) +BKG_COL = matchcolor(0,0,0) +--CUR_COL = matchcolor(112,112,112) + + + function rotate3D(x,y,z,Xsin,Ysin,Zsin,Xcos,Ycos,Zcos) -- PrecCalced cos&sin for speed + + local x1,x2,x3,y1,y2,y3,f,xp,yp + + x1 = x + y1 = y * Xcos + z * Xsin + z1 = z * Xcos - y * Xsin + + x2 = x1 * Ycos - z1 * Ysin + y2 = y1 + z2 = x1 * Ysin + z1 * Ycos + + x3 = x2 * Zcos - y2 * Zsin + y3 = x2 * Zsin + y2 * Zcos + z3 = z2 + + return x3,y3,z3 + end + + function do3D(x,y,z,zoom,dist,Xsin,Ysin,Zsin,Xcos,Ycos,Zcos) -- PrecCalced cos&sin for speed + + local x1,x2,x3,y1,y2,y3,f,xp,yp + + x1 = x + y1 = y * Xcos + z * Xsin + z1 = z * Xcos - y * Xsin + + x2 = x1 * Ycos - z1 * Ysin + y2 = y1 + z2 = x1 * Ysin + z1 * Ycos + + x3 = x2 * Zcos - y2 * Zsin + y3 = x2 * Zsin + y2 * Zcos + z3 = z2 + + f = dist/(z3 + dist + zoom) + xp = x3 * f + yp = y3 * f + + return xp,yp,z3 + end + + +function draw3Dline(x1,y1,z1,x2,y2,z2,div,mult,depthlist) + local s,xt,yt,xd,yd,zd,xf,yf + xd = (x2 - x1) / div + yd = (y2 - y1) / div + zd = (z2 - z1) / div + xf,yf = x1,y1 + + for s = 1, div, 1 do + -- Depth assumes a 1-Box (z ranges from -sq(3) to sq(3)) + depth = math.floor(1 + (z1+zd*s + 1.732) * mult) + xt = x1 + xd*s -- + math.random()*8 + yt = y1 + yd*s -- + math.random()*8 + c = depthlist[depth] + if c == null then c = 1; end -- Something isn't perfect, error is super rare but this controls it + drawline(xf,yf,xt,yt,c) + xf = xt + yf = yt + end +end + +function killinertia() + XD = 0 + YD = 0 +end + + -- If using 1-box, z is -sq(3) to sq(3) + minz = math.sqrt(3) + totz = minz * 2 + maxrad = CMAXSIZE - CMINSIZE + +q = 0 +delay = 4 +move = 0.03 + +while 1 < 2 do + + -- Time-for-space-wiggle...or somekindof attempt + --WORLD_X = -move + --q = (q + 1) % delay + --if q < delay/2 then WORLD_X = move; end + + clearpicture(BKG_COL) + + Xsin = math.sin(XANG); Xcos = math.cos(XANG) + Ysin = math.sin(YANG); Ycos = math.cos(YANG) + Zsin = math.sin(ZANG); Zcos = math.cos(ZANG) + + -- Rotate Box points + for n = 1, #pts, 1 do + p = pts[n] + x,y,z = p[1],p[2],p[3] + XP,YP,zp = rotate3D(x,y,z,Xsin,Ysin,Zsin,Xcos,Ycos,Zcos) + boxp[n] = {XP,YP,zp} + end + + -- Rotate Colors in palette + for n = 1, #cols, 1 do + p = cols[n] + x,y,z,c = p[1],p[2],p[3],p[4] + XP,YP,zp = rotate3D(x,y,z,Xsin,Ysin,Zsin,Xcos,Ycos,Zcos) + colz[n] = {XP,YP,zp,c} + end + + ------------------------------------ + -- Control world + ------------------------------------ + + -- Calculate points anew + + -- Worldize Box points + for n = 1, #boxp, 1 do + s = SIZE + v = boxp[n] + x = v[1] + WORLD_X + y = v[2] + WORLD_Y + z = v[3] + f = DIST/(z + DIST + ZOOM) + XP = CX + x * f * s + YP = CY + y * f * s + boxp[n] = {XP,YP,z} + end + + -- Worldize Colors in palette + for n = 1, #colz, 1 do + s = SIZE + v = colz[n] + x = v[1] + WORLD_X + y = v[2] + WORLD_Y + z = v[3] + c = v[4] + f = DIST/(z + DIST + ZOOM) + XP = CX + x * f * s + YP = CY + y * f * s + colz[n] = {XP,YP,z,c} + end + + +------------------------------------- +------------------------------------- + + -- Brightness Diagonal + --if BRIDIAG_SHOW == 1 then + -- p1 = boxp[4] + -- p2 = boxp[6] + -- x1,y1,z1 = p1[1],p1[2],p1[3] + -- x2,y2,z2 = p2[1],p2[2],p2[3] + -- draw3Dline(x1,y1,z1,x2,y2,z2,BOX_LINE_DIV,BOX_DIV_MULT,box_div) + --end + + -- sort on z + db.sorti(colz,3) + + -- Draw colors + for n = #colz, 1, -1 do + p = colz[n] + XP,YP,zp,c = p[1],p[2],p[3],p[4] + + radius = CMINSIZE + maxrad - (zp+minz) / totz * maxrad + dorad = math.floor(radius - ZOOM*2 + COLSIZE_ADJ) + + if dorad >= 1 then + drawdisk(XP,YP,dorad,c) + --db.drawRectangle(XP,YP,dorad,dorad,c) + else putpicturepixel(XP,YP,c) + end + + if c == FG or c == BG then + sz = math.max(3,dorad + 3) + if c == BKG_COL then v = (c+128) % 255; c = matchcolor(v,v,v); end + db.drawRectangleLine(XP-sz,YP-sz,sz*2,sz*2,c) + end + + end -- colz + + + + -- Draw box + for n = 1, #lin, 1 do + + l = lin[n] + p1 = boxp[l[1]] + p2 = boxp[l[2]] + x1,y1,z1 = p1[1],p1[2],p1[3] + x2,y2,z2 = p2[1],p2[2],p2[3] + draw3Dline(x1,y1,z1,x2,y2,z2,BOX_LINE_DIV,BOX_DIV_MULT,box_div) + + end -- eof box + + --updatescreen(); if (waitbreak(0.00)==1) then return; end + + repeat + + old_key = key; + old_mouse_x = mouse_x; + old_mouse_y = mouse_y; + old_mouse_b = mouse_b; + + updatescreen() + moved, key, mouse_x, mouse_y, mouse_b = waitinput(0) + + if mouse_b == 1 then ANIM = 0; end + + if (key==27) then + return; + end + + if (key==282) then ANIM = (ANIM+1) % 2; end -- F1: Stop/Start Animation + if (key==283) then initAndReset(); end -- F2: Reset all values + if (key==284) then COLSIZE_ADJ = COLSIZE_ADJ + 0.5; end -- F3 + if (key==285) then COLSIZE_ADJ = COLSIZE_ADJ - 0.5; end -- F4 + + if (key==286) then + --FG = (FG + 1) % 255; + palcol = (palcol + 1) % #pal + FG = pal[palcol+1][4] + setforecolor(FG); + setcolor(0,getcolor(0)) -- Force update of palette until setforecolor() is fixed + end -- F5 + + if (key==290) then -- F9 + initColors("rgb") + initPointsAndLines("cube",BRIDIAG_SHOW) + end + if (key==291) then -- F10 + initColors("hsl") + initPointsAndLines("cylinder", 0) -- Bridiag won't show even if turned on, it's only for cube + end + if (key==292) then -- F11 + initColors("hsl_cubic") + initPointsAndLines("cube",BRIDIAG_SHOW) + end + + if (key==269) then ZOOM = ZOOM + 0.1; end + if (key==270) then ZOOM = ZOOM - 0.1; end + SPEED = math.pi / 100 + + + if (key==273) then WORLD_Y = WORLD_Y - 0.05; killinertia(); end + if (key==274) then WORLD_Y = WORLD_Y + 0.05; killinertia(); end + + if (key==276) then WORLD_X = WORLD_X - 0.05; killinertia(); end + if (key==275) then WORLD_X = WORLD_X + 0.05; killinertia(); end + + until ((mouse_b == 1 and (old_mouse_x~=mouse_x or old_mouse_y~=mouse_y)) or key~=0 or ANIM==1 or math.abs(XD)>0.01 or math.abs(YD)>0.01); + + if ANIM == 0 then + if (mouse_b==1 and (old_mouse_x~=mouse_x or old_mouse_y~=mouse_y)) then -- Inertia + XD = (mouse_y - old_mouse_y)*0.005 + YD = (mouse_x - old_mouse_x)*0.005 + else + XD = XD*0.92 + YD = YD*0.92 + end + XANG = ((XANG - XD) % (math.pi*2)); + YANG = ((YANG + YD) % (math.pi*2)); + ZANG = 0 + end + + if ANIM == 1 then + XANG = (XANG + math.pi/300) % (math.pi*2) + YANG = (YANG + math.pi/500) % (math.pi*2) + ZANG = (ZANG + math.pi/1000) % (math.pi*2) + end + + --XANG = ((CY-mouse_y) / 200 % (math.pi*2)); + --YANG = ((mouse_x - CX) / 200 % (math.pi*2)); + --ZANG = 0 + + + statusmessage("X: "..math.floor(XANG*57.3).."°, Y: "..math.floor(YANG*57.3).."°, Z: "..math.floor(ZANG*57.3).."°, Zoom: "..math.floor(-ZOOM*10).." ") +end + +end -- OK diff --git a/share/grafx2/scripts/bru_db_ApplyColor.lua b/share/grafx2/scripts/bru_db_ApplyColor.lua new file mode 100644 index 00000000..94db376e --- /dev/null +++ b/share/grafx2/scripts/bru_db_ApplyColor.lua @@ -0,0 +1,125 @@ +--BRUSH Remap: Apply PenColor +--by Richard Fhager +--http://hem.fyristorg.com/dawnbringer/ + +-- Copyright 2010 Richard Fhager +-- +-- This program is free software; you can redistribute it and/or +-- modify it under the terms of the GNU General Public License +-- as published by the Free Software Foundation; version 2 +-- of the License. See + +--dofile("dawnbringer_lib.lua") +require("dawnbringer_lib") + +OK,tin,clz,fade,amt,brikeep,falloff,nobg,briweight = inputbox("Apply PenColor 2 Brush", + + "1. Tint", 1, 0,1,-1, + "2. Colorize", 0, 0,1,-1, + "BG->FG color Fade", 0, 0,1,0, + "AMOUNT % (0-100)", 100, 0,100,0, + "Preserve Brightness", 1, 0,1,0, + "Bri/Dark FallOff", 1, 0,1,0, + "Exclude Background", 1,0,1,0, + "ColMatch Bri-Weight %", 25, 0,100,0 +); + + +if OK == true then + + function cap(v) return math.min(255,math.max(v,0)); end + + w, h = getbrushsize() + + + fg = getforecolor() + bg = getbackcolor() + fR,fG,fB = getcolor(fg) + bR,bG,bB = getcolor(bg) + + pal = db.fixPalette(db.makePalList(256)) + if nobg == 1 then + pal = db.stripIndexFromPalList(pal,bg) -- Remove background color from pallist + end + + amtA = amt / 100 + amtR = 1 - amtA + + -- Normalize Pen Color + lev = (fR+fG+fB)/3 + fR = fR - lev + fG = fG - lev + fB = fB - lev + + --------------------------------------------------- + -- Colorize (Colourant) (just apply colorbalance) + -- Tint (make grayscale and apply colorbalance) + -- + -- I think it should be the other way around since colorize is the process of adding color to B&W film... + -- But this is the what Brilliance and others call it + -- + if clz == 1 or tin == 1 then + cols = {} + for n = 0, 255, 1 do + + r,g,b = getcolor(n) + a = db.getBrightness(r,g,b) + + + mR,mG,mB = fR,fG,fB + + -- Fade between bg & fg pencolor across dark-bright + if fade == 1 then + lf = a / 255 + lr = 1 - lf + mR = bR*lr + fR*lf + mG = bG*lr + fG*lf + mB = bB*lr + fB*lf + lev = (mR+mG+mB)/3 + mR = mR - lev + mG = mG - lev + mB = mB - lev + end + + fr,fg,fb = mR,mG,mB + + + if brikeep == 1 then + -- Loose Brightness preservation (ex: applying full red to dark colors) + brin = db.getBrightness(cap(r+mR),cap(g+mG),cap(b+mB)) + itot = brin - a + fr = mR - itot + fg = mG - itot + fb = mB - itot + end + + -- Falloff (Effect weakens at dark and bright colors) + if falloff == 1 then + fo = 1 - math.abs((a - 127.5)/127.5)^2 + fr = fr * fo + fg = fg * fo + fb = fb * fo + end + + if tin == 1 then + --cols[n+1] = matchcolor((a+fr)*amtA + r*amtR, (a+fg)*amtA + g*amtR, (a+fb)*amtA + b*amtR) + cols[n+1] = db.getBestPalMatchHYBRID({(a+fr)*amtA+r*amtR, (a+fg)*amtA + g*amtR, (a+fb)*amtA + b*amtR},pal,briweight / 100,true) + end + if clz == 1 then + --cols[n+1] = matchcolor((r+fr)*amtA + r*amtR, (g+fg)*amtA + g*amtR, (b+fb)*amtA + b*amtR) + cols[n+1] = db.getBestPalMatchHYBRID({(r+fr)*amtA+r*amtR, (g+fg)*amtA + g*amtR, (b+fb)*amtA + b*amtR},pal,briweight / 100,true) + end + end + + if nobg == 1 then cols[getbackcolor()+1] = getbackcolor(); end + + for x = 0, w - 1, 1 do + for y = 0, h - 1, 1 do + putbrushpixel(x, y, cols[getbrushpixel(x,y) + 1]); + end + end +end; +-- eof Colorize & Tint +-------------------------------------------------------- + +end -- OK diff --git a/share/grafx2/scripts/libs/dawnbringer_lib.lua b/share/grafx2/scripts/libs/dawnbringer_lib.lua new file mode 100644 index 00000000..541a5cb0 --- /dev/null +++ b/share/grafx2/scripts/libs/dawnbringer_lib.lua @@ -0,0 +1,2471 @@ +--DawnBringer function library v1.1 +--** THIS IS NOT A RUNNABLE SCRIPT! ** +--by Richard Fhager +-- http://hem.fyristorg.com/dawnbringer/ +-- Email: dawnbringer@hem.utfors.se +-- MSN: annassar@hotmail.com +-- +-- Many functions in here was adopted from Evalion, a Javascript codecrafting/imageprocessing project +-- http://goto.glocalnet.net/richard_fhager/evalion/evalion.html +-- +-- +-- You may access these functions in your own scripts by loading this library, +-- just add the follwing line as one of the first instructions: +-- +-- require("dawnbringer_lib") +-- +-- or +-- +-- dofile("dawnbringer_lib.lua") +-- +-- +-- Note that the functions must be called with the full library object-name, "db.function_name..." +-- + +-- Global library object +db = {} + + +-- ************************************* +-- *** Text & Conversions *** +-- ************************************* +-- +-- + +function db.rgb2HEX(r,g,b,prefix) + local c,n,s,t,z + c = {r,g,b} + z = {"0",""} + t = "" + for n = 1, 3, 1 do + s = string.upper(string.format("%x",c[n])) + t = t..z[#s]..s + --s = tonumber(c[n],16) + --t = t..s + end + return prefix..t +end + + +-- +-- ... eof Text & Conversions ... +-- + + + +-- ************************************* +-- *** Custom Math Functions *** +-- ************************************* +-- +-- +function db.sign(v) + local s + s = 0 + if v > 0 then s = 1; end + if v < 0 then s = -1; end + return s +end +-- + +-- +function db.rotation (rot_ang,hub_x,hub_y,x,y) -- Rotate coordinates x & y relative hub + local new_ang,dist,m,xd,yd,v; m = math + xd=hub_x-x; + yd=hub_y-y; + if (not(xd==0 and yd==0)) then + v = -90; if xd < 0 then v = 90; end + new_ang = m.atan(yd/xd) - (v+rot_ang) * m.pi/180; + dist = m.sqrt(xd*xd+yd*yd); + x = hub_x - m.sin(new_ang)*dist; + y = hub_y + m.cos(new_ang)*dist; + end + return math.floor(x),math.floor(y) -- For drawing purposes +end +-- + +-- +-- ... eof Custom Math Functions ... +-- + +-- ************************************* +-- *** Fractional Scenery *** +-- ************************************* + +-- +function db.setSceneryPalette() + db.colorCigarr(10,28,true) -- 250 colors + setcolor(250, 208,48,48) + setcolor(251, 48,208,48) + setcolor(252, 48,48,208) + setcolor(253, 224,224,64) + setcolor(254, 224,64,224) + setcolor(255, 64,224,224) +end +-- + +-- +function db.star(xf,yf,sx,sy,rgb,haz,out,lum) + local n,c,dist; c={} + dist = haz + out * math.sqrt((xf-sx)^2+(yf-sy)^2); + for n = 1, 3, 1 do c[n] = (rgb[n] * lum) / dist; end + return c; +end +-- + +-- +function db.zoom(xf,yf,zoom,panx,pany) -- Zoom and Pan in a fractional coord-system + xf = (xf-0.5)/zoom + 0.5 + panx; + yf = (yf-0.5)/zoom + 0.5 + pany; + return xf,yf +end +-- + +-- +function db.rotationFrac(rot_ang,hub_x,hub_y,x,y) -- Rotate coordinates x & y relative hub + local new_ang,dist,m,xd,yd,v; m = math + xd=hub_x-x; + yd=hub_y-y; + if (not(xd==0 and yd==0)) then + v = -90; if xd < 0 then v = 90; end + new_ang = m.atan(yd/xd) - (v+rot_ang) * m.pi/180; + dist = m.sqrt(xd*xd+yd*yd); + x = hub_x - m.sin(new_ang)*dist; + y = hub_y + m.cos(new_ang)*dist; + end + return x,y +end +-- + +-- +function db.twirl(x,y,arms,trot,tpow,tang) + local b,ang,vx,vy,vr,m,deg,tw + m=math; deg=math.pi/180; tw=.5; + if (not(x==.5 and y==.5)) then + ang = m.atan((.5-y)/(.5-x)); + b = 0; if (x>.5) then b = m.pi; end + vx = .5-x; vy = .5-y; vr = m.pow(m.sqrt(vx*vx+vy*vy),tpow); + tw = .5+m.sin(-tang*deg+vr*trot+(ang + b)*arms)*.5; + end + return tw; +end +-- + +--- Alpha filters +-- +function db.alpha1(x,y,amp) -- Coord, Amplify: 0..n + local p,a,xh,yh,m + xh=0.5-x; yh=0.5-y; m = math + p = m.pow(xh*xh+yh*yh,0.7); + a = m.cos(32*m.pi*p)*m.sin(8*m.pi*(xh+yh)); + return 1 + (a * amp) +end +-- + +-- +-- ... eof Fractional Scenery ... +-- + +-- ************************************* +-- *** Custom Array Functions *** +-- ************************************* +-- +-- Ok, I don't know Lua that well (still unsure about some scopes & refs etc.) +-- And some features may not be active in Grafx2. So, some of the follwing functions +-- may exist in Lua/Grafx2...but since I'm not sure if and how they work - I'll prefer +-- to add a set of my own of known performance. + +-- +function db.newArrayInit2Dim(xs,ys,val) + local x,y,ary; ary = {} + for y = 1, ys, 1 do + ary[y] = {} + for x = 1, xs, 1 do + ary[y][x] = val + end + end + return ary +end +-- + +-- +-- Merge two arrays into a NEW one: array_c = db.newArrayMerge(array_b,array_b) +-- +function db.newArrayMerge(a,b) + local n,ary; ary = {} + for n = 1, #a, 1 do + ary[n] = a[n] + end + for n = 1, #b, 1 do + ary[n+#a] = b[n] + end + return ary +end +-- + +-- +-- Generate a copy of an array with a new value added Last +-- +function db.newArrayInsertLast(a,val) + local n,ary; ary = {} + for n = 1, #a, 1 do + ary[n] = a[n] + end + ary[#a+1] = val + return ary +end +-- + +-- +-- Generate a copy of an array with a new value added First +-- +function db.newArrayInsertFirst(a,val) + local n,ary; ary = {} + ary[1] = val + for n = 2, #a+1, 1 do + ary[n] = a[n-1] + end + return ary +end +-- + +-- +function db.ary2txt(ary) -- One & two dimensions supported [a,b] -> "a,b". [[a,b],[c,d]] -> "a-b, c-d" + local t,n,m,v + t = "" + for n = 1, #ary, 1 do + if type(ary[n]) == "table" then + t = t..ary[n][1] + for m = 2, #ary[n], 1 do + t = t.."-"..ary[n][m] + end + else t = t..ary[n]; + end + t = t..", " + end + return t +end +-- + + +-- +-- ... eof Custom Array Functions ... +-- + + +-- ************************************* +-- *** Misc. Logical Operations *** +-- ************************************* + +-- +-- palList [r,g,b,palindex] is expected only to contain unique colors +-- index = -1 --> index of list +-- +function db.makeIndexList(list,index) + local n,ilist + ilist = {} + for n = 1, #list, 1 do + if (index > 0) then ilist[n] = list[n][index]; end + if (index == -1) then ilist[n] = n; end + end + return ilist +end +-- + +-- +-- Return a list of all possible (non-same) pairs from the entries in a list +-- [a,b,c] --> [[a,b],[a,c],[b,c]] +-- (All entries are treated as unique. i.e it's only the INDEX that counts) +-- mode = 0: Only unique pairs (m = (n^2 - n)/2), [a,b] --> [[a,b]] +-- mode = 1: All pairs, i.e mirror versions as well. (m = n^2 - n), [a,b] --> [[a,b], [b,a]] +-- +function db.pairsFromList(list,mode) + local a,b,l,n,pairs + pairs = {} + l = #list + n = 1 + for a = 1, l, 1 do + for b = a+1, l, 1 do + pairs[n] = {list[a],list[b]}; n = n + 1 + if mode == 1 then pairs[n] = {list[b],list[a]}; n = n + 1; end + end + end + return pairs +end +-- + +function db.valueInArray(ary,val) + local n,res + res = false + for n = 1, #ary, 1 do + if ary[n] == val then res = true; break; end + end + return res +end + +-- RAMP specific + +-- Remove initial pair (palList) colors from pallist +function db.initiateRamp(pair,pallist,pal_index) + local n,found,plist + plist = {} + + found = 1 + for n = 1, #pallist, 1 do + if db.valueInArray(pair,pallist[n]) == false then + plist[found] = pallist[n]; found = found + 1; + end + end + + pair[pal_index] = plist -- ex: ["pal"] + + return pair -- Is now a 2 color RAMP +end +-- + +-- Remove new col entry from ramp's pallist and add it to the ramp, returns an updated ramp +-- RampList = [1,2] ["pal"] = palList = [3,4,5], addindex = 3 +-- --> [1,2,3] palList = [4,5] +function db.updateRamp(ramp,addindex,pal_index) + local n,found,pallist,plist,newramp + plist = {} + pallist = ramp[pal_index] + + -- New palList without added color to IndexList + found = 1 + for n = 1, #pallist, 1 do + if pallist[n] ~= addindex then + plist[found] = pallist[n]; found = found + 1; + end + end + + newramp = db.newArrayInsertLast(ramplist,addindex) + newramp[pal_index] = plist + + return rlist +end + +-- +-- Returns a list of all inital ramps from color pairs +-- +-- Weeds out bad pairs, attaches remaining palette colors and the first rgb-vector +-- +-- +function db.initiateRampList(pairs,pallist,pal_index,vec_index,min,maxmult,rw,gw,bw) + local n,ramplist,newpairs,accept,dist,c1,c2,max,rD,gD,bD + ramplist = {} + max = min + (142 / math.sqrt(#pallist)) * maxmult -- min ex: 8-12 + accept = 0 + + for n = 1, #pairs, 1 do + c1 = pallist[pairs[n][1]] + c2 = pallist[pairs[n][2]] + rD = c2[1] - c1[1] + gD = c2[2] - c1[2] + bD = c2[3] - c1[3] + dist = math.sqrt( (rw*rD)^2 + (gw*gD)^2 + (bw*bD)^2 ) + + if dist >= min and dist <= max then + accept = accept + 1; ramplist[accept] = db.initiateRamp(pairs[n],pallist,pal_index); + ramplist[accept][vec_index] = {rD, gD, bD, dist}; -- Add first color vector, ONLY KEEP DISTANCE? + end + end + + return ramplist +end + + +function db.findRampExpansionColors(ramp) + local clist + clist = {} + -- Calculate vectors here? + return clist +end + + +function db.findRAMPS(min_len, max_len) + local i,n,c,pallist,ramp,ramplist,pairs,spairs,palindex,vecindex,found,donelist,newlist,dones + local colorlist + palindex = "pal" + vecindex = "vector" + pallist = db.fixPalette(db.makePalList(256), 0) + pairs = db.pairsFromList(db.makeIndexList(pallist,-1), 0) + ramplist = db.initiateRampList(pairs,pallist,palindex,vecindex, 8,0.75, 0.26,0.55,0.19) + + -- MIN_LEN = 5 + -- MAX_LEN = 10 + + -- Split Ramp-build into two parts: + -- 1. Build ramps >= MIN_LEN, NONE added to 'Done' + -- 2. Run til no more ramps can be expanded or reaches MAX_LEN, ALL ramps added to 'Done' + + for i = 1, (min_len - 2), 1 do -- Assuming 2 for inital pairs (2 color ramps) + newlist = {} + found = 0 + for n = 1, #ramplist, 1 do + ramp = ramplist[n] + colorlist = db.findRampExpansionColors(ramp) -- Colors that can split the current ramp into new expanded ramps + for c = 1, #colorlist, 1 do + found = found + 1; newlist[found] = db.updateRamp(ramp,colorlist[c],palindex); -- Ramp is expanded by 1 color + end + end + ramplist = newlist + end + + + donelist = {}; dones = 0 + + repeat + newlist = {} + found = 0 + for n = 1, #ramplist, 1 do + ramp = ramplist[n] + if true == false then + found = found + 1; newlist[found] = db.updateRamp(ramp,color,palindex); + else + dones = dones + 1; donelist[dones] = ramp; + end + end + --ramplist = newlist + until found == 0 + + return #pairs.." - "..#ramplist +end + +-- +-- ... eof Misc. Logical Operations ... +-- + + +-- *************************************** +-- *** General RGB-Color Modifications *** +-- *************************************** + + +-- +function db.makeComplimentaryColor(r,g,b,brikeeplev) -- Lev: 0 = Normal, 1 = Loose, 2 = Strict + + local bri_o,bri_n,bdiff + + function cap(v) return math.max(0,math.min(v,255)); end + + bri_o = db.getBrightness(r,g,b) + r,g,b = db.shiftHUE(r,g,b,180) + + if brikeeplev > 0 then + + for n = 0, brikeeplev*3-1, 1 do -- Must iterate to reduce brightness error + bri_n = db.getBrightness(r,g,b) + bdiff = (bri_o - bri_n) / 2 * brikeeplev + r = cap(r + bdiff) + g = cap(g + bdiff) + b = cap(b + bdiff) + end + + end + + return r,g,b + +end +-- + + +-- *** Color balance *** +-- +-- bri_flag: Preserve brightness +-- loose_flag: Loose preservation restrictions for brightness and balance +-- +-- Jeez, was this a tricky sucker; color-balance is just adding and capping... +-- but trying color-balance with preserved perceptual brightness is a different monster... +-- ...so bad I could only solve it by iterative error correction. +-- +function db.ColorBalance(r,g,b,rd,gd,bd,bri_flag,loose_flag) -- preserve brightness + local rw,gw,bw,ri,gi,bi,itot,rni,gni,bni,ro,go,bo,ovscale,lev,count,rt,gt,bt,rf,gf,bf,bri + + -- Dawn 3.0, [0.26,0.55,0.19], 0-255 bri-colorscale adjust = 1.56905 + rw,gw,bw = 0.26, 0.55, 0.19 + + function cap(v) return math.min(255,math.max(v,0)); end + + bri = db.getBrightness(r,g,b) + + + -- Loose brightness & balance preservation, a good compromise. + if bri_flag == true and loose_flag == true then + + lev = (rd + gd + bd) / 3 + rd = rd - lev + gd = gd - lev + bd = bd - lev + + brin = db.getBrightness(cap(r+rd),cap(g+gd),cap(b+bd)) + itot = brin - bri + rd = rd - itot + gd = gd - itot + bd = bd - itot + + end + + + if bri_flag == true and loose_flag == false then + + itot = 255 + count = 0 + + -- Normalize (Yup, it's right only to normalize once first..cont.norm. will have some counter-effect) + lev = (rd + gd + bd) / 3 + rd = rd - lev + gd = gd - lev + bd = bd - lev + + repeat + + --messagebox("Norm:"..rd..", "..gd..", "..bd) + + -- Calculate total brightness change + -- Note: Perceptual Brightness is exponential, and can't be delta-adjusted for anything other than greyscales. + -- Although the formula for the new brightness corrected normalization level can can be derived... + -- ...it doesn't do much good since the bigger problem is overflow outside the 0-255 boundary. + -- As for now, I see no other means to solve this issue than with iterative error-correction. + + rt = r+rd + gt = g+gd + bt = b+bd + + itot = 9e99 + rni = rd + gni = gd + bni = bd + + -- We can get brightness of negative values etc. So bri-correction is put on hold until values are scaled down + if (rt>=0 and gt>=0 and bt>=0) and (rt<256 and gt<256 and bt<256) then + brin = db.getBrightness(rt,gt,bt) + itot = brin - bri + --messagebox("Bri Diff: "..itot) + -- Brightness adjusted balance + rni = rd - itot + gni = gd - itot + bni = bd - itot + end + + --messagebox("Bri Adj Bal:"..rni..", "..gni..", "..bni) + + -- Apply balance to find overflow (as fraction of the channel change) + ro = math.max( math.max((r + rni)-255,0), math.abs(math.min((r + rni),0)) ) / math.max(math.abs(rni),1) + go = math.max( math.max((g + gni)-255,0), math.abs(math.min((g + gni),0)) ) / math.max(math.abs(gni),1) + bo = math.max( math.max((b + bni)-255,0), math.abs(math.min((b + bni),0)) ) / math.max(math.abs(bni),1) + + ovscale = 1 - math.max(ro,go,bo) + + -- Scaling balances might be logically incorrect (as they can be seen as constant differences) + -- But scaling DOWN is quite harmless and I don't see how it could be done otherwise... + -- ex: +10 red, +5 blue: Scale x2 = +20 red, +10 blue -> More red over blue than ordered, a contrast behaviour. + -- +10 red, +5 blue: Scale x0.5 = +5 red, +2.5 blue -> Less of everything, but a part of the order. Harmless? + -- + rd = rni * ovscale + gd = gni * ovscale + bd = bni * ovscale + + count = count + 1 + + --messagebox("Final bal:"..rd..", "..gd..", "..bd) + + until math.abs(itot) < 1 or count > 5 + + end + + rf = r + rd + gf = g + gd + bf = b + bd + + --messagebox("Result color:"..rf..", "..gf..", "..bf) + + return rf,gf,bf +end +-- + + + +-- +-- bri_flag: Preserve brightness +-- cap_flag: Cap new color at 0-255, has a desaturating effect for large values. +-- +function db.ColorBalanceXXX(r,g,b,rd,gd,bd,bri_flag,cap_flag) -- preserve brightness + local rf,gf,bf + + if cap_flag == true then + rd = math.min(255,math.max(0, r+rd)) - r + gd = math.min(255,math.max(0, g+gd)) - g + bd = math.min(255,math.max(0, b+bd)) - b + end + + local rw,gw,bw,ri,gi,bi,itot,rni,gni,bni,ro,go,bo,ovscale + + + -- Dawn 3.0, [0.26,0.55,0.19], 0-255 bri-colorscale adjust = 1.56905 + rw,gw,bw = 0.26, 0.55, 0.19 + + if bri_flag == true then + + -- Calculate total brightness change + --ri = rd * rw + --gi = gd * gw + --bi = bd * bw + --itot = math.sqrt(ri^2 + gi^2 + bi^2) + + bri = db.getBrightness(r,g,b) + brin = db.getBrightness(r+rd,g+gd,b+bd) + itot = brin - bri + + + -- Normalized and Brightness adjusted balance + rni = rd - itot + gni = gd - itot + bni = bd - itot + + -- Apply balance to find overflow (as fraction of the channel change) + ro = math.max( math.max((r + rni)-255,0), math.abs(math.min((r + rni),0)) ) / math.max(math.abs(rni),1) + go = math.max( math.max((g + gni)-255,0), math.abs(math.min((g + gni),0)) ) / math.max(math.abs(gni),1) + bo = math.max( math.max((b + bni)-255,0), math.abs(math.min((b + bni),0)) ) / math.max(math.abs(bni),1) + + ovscale = 1 - math.max(ro,go,bo) + + rd = rni * ovscale + gd = gni * ovscale + bd = bni * ovscale + + end + + rf = r + rd + gf = g + gd + bf = b + bd + + return rf,gf,bf +end +-- + +-- +function db.getContrast(ch) -- Channel, returns fraction -1..0..1, negative for ch < 127.5 + --return math.abs((ch / 127.5) - 1) + return (ch / 127.5) - 1 +end +-- + +-- +function db.getAvgContrast(r,g,b) + return (math.abs(db.getContrast(r)) + math.abs(db.getContrast(g)) + math.abs(db.getContrast(b))) / 3 +end +-- + +-- +-- Mode = 0: Proportional - all colors reach max contrast at 100% +-- +-- Mode = 1: Linear - percentage simply added +-- +function db.changeContrastOLD(r,g,b,prc,mode) + + local m,rd,gd,bd,rv,gv,bv,rc,gc,bc,base,sign + + base = 1; sign = 1 + if prc < 0 then base = 0; sign = -1; end -- decontrast + + m = prc / 100 * sign + + -- mode 0 + rc = db.getContrast(r) + rd = (base - math.abs(rc)) * m * db.sign(rc) + rv = (rc+rd+1) * 127.5 + + gc = db.getContrast(g) + gd = (base - math.abs(gc)) * m * db.sign(gc) + gv = (gc+gd+1) * 127.5 + + bc = db.getContrast(b) + bd = (base - math.abs(bc)) * m * db.sign(bc) + bv = (bc+bd+1) * 127.5 + + return rv,gv,bv + +end +-- + +function db.changeContrast(r,g,b,prc) -- Photoshop style + + local m,rd,gd,bd,rv,gv,bv,rc,gc,bc + + m = 1 + math.pow((255 / 100 * prc),3) / (255*255) + + -- decontrast + if prc < 0 then + m = 1 - math.abs(prc)/100 + end + + rc = db.getContrast(r) + rd = rc * m + rv = (rd+1) * 127.5 + + gc = db.getContrast(g) + gd = gc * m + gv = (gd+1) * 127.5 + + bc = db.getContrast(b) + bd = bc * m + bv = (bd+1) * 127.5 + + return rv,gv,bv + +end + + + +-- +function db.getBrightness(r,g,b) -- 0-255 + local bri + --bri = (r+g+b)/3 + --bri = r*0.3 + g*0.59 + b*0.11 -- Luma Y'601 + --bri = math.sqrt((r*0.3)^2 + (g*0.59)^2 + (b*0.11)^2) -- Luma Y'601 + --bri = r*0.245 + g*0.575 + b*0.18 -- Dawn 2.0 + + bri = math.sqrt((r*0.26)^2 + (g*0.55)^2 + (b*0.19)^2) * 1.56905 -- Dawn 3.0 + return bri +end +-- + + +-- +-- Note on desaturation: These functions are all junk, the only way to desaturate +-- is to fade a color into it's corresponding greyscale. +-- + +-- +function db.desaturate(percent,r,g,b) -- V1.0 by Richard Fhager + local a,p + p = percent / 100 + a = (math.min(math.max(r,g,b),255) + math.max(math.min(r,g,b),0)) * 0.5 * p + r = r + (a-r*p) -- a+r*(1-p) + g = g + (a-g*p) + b = b + (a-b*p) + return r,g,b +end +-- + +-- +function db.desaturateA(percent,c) -- array version + local r,g,b,a + r = c[1] + g = c[2] + b = c[3] + p = percent / 100 + a = (math.min(math.max(r,g,b),255) + math.max(math.min(r,g,b),0)) * 0.5 * p + r = r + (a-r*p) + g = g + (a-g*p) + b = b + (a-b*p) + return {r,g,b} +end +-- + +-- +function db.desatAVG(desat,c) -- Desaturation, simpe average + r = c[1] + g = c[2] + b = c[3] + p = desat / 100 + a = (r+g+b)/3 + r = r + p*(a-r) + g = g + p*(a-g) + b = b + p*(a-b) + return {r,g,b} +end +-- + + +-- +function db.getSaturation(r,g,b) -- HSL + local M,m,c,s,l + M = math.max(r,g,b) + m = math.min(r,g,b) + c = (M - m)/255 + s = 0 + if c ~= 0 then + --l = (0.3*r + 0.59*g + 0.11*b)/255 -- HSLuma: Y'601 + l = (M+m)/510 -- This produces a quite "correct looking" divison of saturation + if l <= 0.5 then s = c / (2*l); end + if l > 0.5 then s = c / (2-2*l); end + end + return math.min(255,s * 255) +end +-- + +-- +function db.getTrueSaturationX(r,g,b) -- Distance from grayscale axis. Not HSV/HSL + local sat,bri + bri = (r+g+b) / 3 + sat = math.min(255, math.sqrt((r-bri)^2 + (g-bri)^2 + (b-bri)^2) * 1.224744875) + return sat +end +-- + +-- WIP. Trying to find a more natural model for estimating Saturation +-- Current: (HSL + True) / 2 +function db.getAppSaturation(r,g,b) + return math.min(255, (db.getSaturation(r,g,b) + db.getTrueSaturationX(r,g,b)) / 2) +end +-- + +-- +function db.saturate(percent,r,g,b) + local a,m,p,mc + a = (math.min(math.max(r,g,b),255) + math.max(math.min(r,g,b),0)) * 0.5 + m = math.min(255-math.max(r,g,b), math.min(r,g,b)) + p = percent * (m / 100) + mc = math.max((r-a),(g-a),(b-a)) -- Can this be derived elsewhere? + if mc ~= 0 then + r = r + (r-a) * p / mc + g = g + (g-a) * p / mc + b = b + (b-a) * p / mc + end + return r,g,b +end +-- + +-- +-- Super Saturate: Better than Photoshop etc. +-- +-- Higher than 100% power is ok +-- +function db.saturateAdv(percent,r,g,b,brikeeplev,greydamp) -- brikeep = 0 - 2 + local a,m,p,mc,bri_o,bri_n,bdiff,mx,mi,adj,q,n + function cap(v) return math.max(0,math.min(v,255)); end + mx = math.max(r,g,b) + mi = math.min(r,g,b) + bri_o = db.getBrightness(r,g,b) + a = (math.min(mx,255) + math.max(mi,0)) * 0.5 + m = math.min(255-mx, mi) + p = percent * (m / 100) + mc = math.max((r-a),(g-a),(b-a)) -- Can this be derived elsewhere? + if mc ~= 0 and m ~= 0 then + adj = math.min(1,(mx - mi) / m) -- Reduce effect on low saturation + if greydamp == false then adj = 1; end + q = p / mc * adj + r = cap( r + (r-a) * q ) + g = cap( g + (g-a) * q ) + b = cap( b + (b-a) * q ) + end + for n = 0, brikeeplev*2, 1 do -- Must iterate to reduce brightness error + bri_n = db.getBrightness(r,g,b) + bdiff = (bri_o - bri_n) / 2 * brikeeplev + r = cap(r + bdiff) + g = cap(g + bdiff) + b = cap(b + bdiff) + end + return r,g,b +end +-- + + +-- +-- Lightness: Darken / Brighten color (Argument and returnvalue is a rgb-list) +-- Rate of change is inversely proportional to the distance of the max/min. +-- i.e. all colors/channels will reach max/min at the same time (at 0 or 100 %) +-- (As opposed to 'Brightness' where all channels are changed by a constant value) +-- +function db.lightness(percent,c) + local v,r,g,b,p + r = c[1] + g = c[2] + b = c[3] + p = math.abs(percent/100) + v = 255 + if percent < 0 then v = 0; end + r = r + (v - r)*p + g = g + (v - g)*p + b = b + (v - b)*p + return {r,g,b} +end +-- + +-- +function db.changeLightness(r,g,b,percent) + local v + v = db.lightness(percent,{r,g,b}) + return v[1],v[2],v[3] +end +-- + +-- +function db.getLightness(r,g,b) -- HSL bi-hexcone + return (math.max(r,g,b) + math.min(r,g,b)) / 2 +end +-- + +-- +function db.shiftHUE(r,g,b,deg) -- V1.3 R.Fhager 2007, (Heavily derived code, hehe...) + local c,h,mi,mx,d,s,p,i,f,q,t + c = {g,b,r} + mi = math.min(r,g,b) + mx = math.max(r,g,b); v = mx; + d = mx - mi; + s = 0; if mx ~= 0 then s = d/mx; end + p = 1; if g ~= mx then p = 2; if b ~= mx then p = 0; end; end + + if s~=0 then + h=(deg/60+(6+p*2+(c[1+p]-c[1+(p+1)%3])/d))%6; + i=math.floor(h); + f=h-i; + p=v*(1-s); + q=v*(1-s*f); + t=v*(1-s*(1-f)); + c={v,q,p,p,t,v} + r = c[1+i] + g = c[1+(i+4)%6] + b = c[1+(i+2)%6] + end + + return r,g,b +end +-- + +-- +function db.getHUE(r,g,b,greytol) -- 0-6 (6.5 = Greyscale), mult. with 60 for degrees + -- 1 Color diff is roughly detected by Tolerance = 0.0078125 (Tol. incr. with lightness etc.) + local c,h,mi,mx,d,s,p,i,f,q,t + c = {g,b,r} + mi = math.min(r,g,b) + mx = math.max(r,g,b); v = mx; + d = mx - mi; + s = 0; if mx ~= 0 then s = d/mx; end + p = 1; if g ~= mx then p = 2; if b ~= mx then p = 0; end; end + + h = 6.5 -- for custom graphical purposes + if s>greytol then -- can't use >= + h=(6+p*2+(c[1+p]-c[1+(p+1)%3])/d)%6; + end + + return h +end +-- + +-- +-- ... eof RGB color modifications ... +-- + + + +-- **************************************** +-- *** Custom Color / Palette functions *** +-- **************************************** + + +-- +function db.rgbcap(r,g,b,mx,mi) + local m = math + return m.max(mi,m.min(r,mx)), m.max(mi,m.min(g,mx)), m.max(mi,m.min(b,mx)) +end +-- + +-- +function db.makePalList(cols) + local pal,n,r,g,b + pal = {} + for n = 0, cols-1, 1 do + r,g,b = getcolor(n) + pal[n+1] = {r,g,b,n} + end + return pal +end +-- + +-- +function db.makeSparePalList(cols) + local pal,n,r,g,b + pal = {} + for n = 0, cols-1, 1 do + r,g,b = getsparecolor(n) + pal[n+1] = {r,g,b,n} + end + return pal +end +-- + + +-- +-- Use to remove the black colors (marks unused colors) from palette-list +-- if it's known that no black color exists in the image. +function db.stripBlackFromPalList(pallist) + local i,u,c,dummy; i = 257 -- Do 'nothing' If using a full 256 col palette with no blacks + for u = 1, #pallist, 1 do + c = pallist[u] + if (c[1]+c[2]+c[3]) == 0 then i = u; end + end + dummy = table.remove(pallist,i) + return pallist +end +-- + +-- +function db.stripIndexFromPalList(pallist,colindex) + local i,u,c,dummy + for u = 1, #pallist, 1 do + c = pallist[u] + if c[4] == colindex then i = u; end + end + dummy = table.remove(pallist,i) + return pallist +end +-- + +-- +function db.addHSBtoPalette(pallist) + local n,hue,sat,rgb + for n=1, #pallist, 1 do + rgb = pallist[n] + pallist[n][5] = db.getHUE(rgb[1],rgb[2],rgb[3],0) + pallist[n][6] = db.getSaturation(rgb[1],rgb[2],rgb[3]) + pallist[n][7] = db.getBrightness(rgb[1],rgb[2],rgb[3]) + end + return pallist -- {r,g,b,n,bri,hue,sat} +end +-- + +-- +function db.makePalListRange(start,ends) + local pal,n,r,g,b,a + pal = {} + a = 1 + for n = start, ends, 1 do + r,g,b = getcolor(n) + pal[a] = {r,g,b,n}; a = a + 1; + end + return pal +end +-- + + +-- +function db.makePalListShade(cols,sha) -- Convert colors to less bits, colorcube operations etc. + local pal,n,r,g,b,mf,div + mf = math.floor + div = 256 / sha + pal = {} + for n = 0, cols-1, 1 do + r,g,b = getcolor(n) + pal[n+1] = {mf(r/div),mf(g/div),mf(b/div),n} + end + return pal +end +-- +-- +function db.makePalListShadeSPARE(cols,sha) -- Convert colors to less bits, colorcube operations etc. + local pal,n,r,g,b,mf,div + mf = math.floor + div = 256 / sha + pal = {} + for n = 0, cols-1, 1 do + r,g,b = getsparecolor(n) + pal[n+1] = {mf(r/div),mf(g/div),mf(b/div),n} + end + return pal +end +-- + + + +-- +function db.getColorDistance_weight(r1,g1,b1,r2,g2,b2,rw,gw,bw) + return math.sqrt( (rw*(r1-r2))^2 + (gw*(g1-g2))^2 + (bw*(b1-b2))^2 ) +end +-- + +-- +function db.getBestPalMatch(r,g,b,pal,index_flag) -- pal = [r,g,b,palindex], index_flag -> return palindex if pal is sorted or reduced + local diff,best,bestcol,cols,n,c,p + cols = #pal + bestcol = -1 + best = 9e99 + + for n=1, cols, 1 do + p = pal[n] + diff = db.getColorDistance_weight(r,g,b,p[1],p[2],p[3],0.26,0.55,0.19) * 1.569 + if diff < best then bestcol = n; best = diff; end + end + + if index_flag == true then + bestcol = pal[bestcol][4] + 1 + end + + return bestcol-1 -- palList index start at 1, image-palette at 0 +end +-- + + +-- Normally this function will return the (image)palette index of best color +-- ...but if the palette has been sorted with 'fixPalette' it will return the index +-- of the custom palList, setting index_flag will convert this value to image-palette index +-- +-- HYBRID means the colormatch is a combo of color and (perceptual)brightness +-- +-- +function db.getBestPalMatchHYBRID(rgb,pal,briweight,index_flag) -- Now correctly balanced + local diff,diffC,diffB,best,bestcol,cols,n,c,r,g,b,p,obri,pbri + cols = #pal + bestcol = -1 + best = 9e99 + + --messagebox(briweight) + + -- Note: Not secured against negative values (this algorithm is SLOW, we cannot afford it) + r = rgb[1] + g = rgb[2] + b = rgb[3] + + obri = db.getBrightness(r,g,b) -- 0-255 + + for n=1, cols, 1 do + p = pal[n] + pbri = db.getBrightness(p[1],p[2],p[3]) + diffB = math.abs(obri - pbri) + -- we need to normalize the distance by the weights + diffC = db.getColorDistance_weight(r,g,b,p[1],p[2],p[3],0.26,0.55,0.19) * 1.569 + + diff = briweight * (diffB - diffC) + diffC + if diff < best then bestcol = n; best = diff; end + end + + if index_flag == true then + bestcol = pal[bestcol][4] + 1 -- Since we detract 1 on return, God Lua is stupid + end + + return bestcol-1 -- palList index start at 1, image-palette at 0 +end +-- + + + +-- +-- Special version of Hybrid-remapping for mixPalette list +-- +-- mixpal: {score,col#1,col#2,dist,rm,gm,bm, c1_r,c1_g,c1_b, c2_r,c2_g,c2_b} +-- +-- returns: {col#1,col#2} (index of palette) +-- +function db.getBestPalMatchHybridMIX(rgb,mixpal,briweight,mixreduction) + local diff,diffC,diffB,best,bestcol,cols,n,c,r,g,b,p,obri,pbri, distmult + cols = #mixpal + bestcol = -1 + best = 9e99 + + -- We will simply add the the distance to the mix with the distance between the mixcolors and + -- employ a user tolerance to much the latter will matter. + --distmult = 255 / 9.56 / 100 * mixreduction -- 16 shades + distmult = 1.56902 / 100 * mixreduction -- 24-bit, Dawn3.0 colormodel + + -- Note: Not secured against negative values (this algorithm is SLOW, we cannot afford it) + r = rgb[1] + g = rgb[2] + b = rgb[3] + + obri = db.getBrightness(r,g,b) -- 0-255 + + for n=1, cols, 1 do + p = mixpal[n] + --pbri = db.getBrightness(p[5],p[6],p[7]) + + -- *** DawnBringer's exponetial color brightness dither resolution phenomena theorem *** + -- Bri = color value ^ 2 + -- Two adjacent pixels displayed with "normal high resolution" will NOT have the perceptual + -- brightness of the resulting mixcolor. The brightness lies closer to that of the brightest pixel. + -- Bri[(C1+C2)/2] = SQRT( (C1bri^2 + C2bri^2) / 2 ) + -- (Brightness according to Dawn-model: bri = SQRT( (r*.26)^2 + (g*.55)^2 + (b*.19)^2 ) ) + + pbri = math.sqrt((db.getBrightness(p[8],p[9],p[10])^2 + db.getBrightness(p[11],p[12],p[13])^2) / 2) + + diffB = math.abs(obri - pbri) + -- we need to normalize the distance by the weights + diffC = db.getColorDistance_weight(r,g,b,p[5],p[6],p[7],0.26,0.55,0.19) * 1.569 + p[4]*distmult + + diff = briweight * (diffB - diffC) + diffC + if diff <= best then bestcol = n; best = diff; end + end + + return {mixpal[bestcol][2], mixpal[bestcol][3]} +--return {mixpal[bestcol][2], 0} + + + +end +-- + + + +-- +function db.matchcolorHSB(h,s,b,pallist,index_flag) + -- + -- why don't we just convert HSB-diagram to RGB and do normal colormatching? + -- Not the same... + -- + local n,c,best,bestcol,pb,ph,ps,diff,huediff,huecorr,hue_adj,sat_adj,bri_adj + bestcol = -1 + best = 9e99 + + -- higher adjust means more impact (higher hue gives more interpolation ) + hue_adj = 4 + sat_adj = 0.075 + bri_adj = 2 + + huecorr = 255 / 6 -- Our Hue goes from 0.0 - 5.999 + + for n=1, #pallist, 1 do + c = pallist[n] + ph = c[5] + ps = c[6] + pb = c[7] + + huediff = math.abs(h-ph*huecorr) + if huediff > 127 then huediff = huediff - (huediff % 127) * 2; end + + --if ph == 6.5 then huediff = 0; end + + -- With less saturation, exact hue becomes less important and brightness more usefull + -- This allows for greyscales and low saturation colors to work smoothly. + huediff = huediff * (ps /255) + + diff = hue_adj*huediff^2 + (s-ps)^2 * sat_adj + (b-pb)^2 * bri_adj + + if diff <= best then bestcol = n; best = diff; end + end + + if index_flag == true then + bestcol = palList[bestcol][4] + 1 -- Since we detract 1 on return, God Lua is stupid + end + + return bestcol-1 + +end +-- + +-- +-- Used by PaletteAnalysis.lua, FindRamps(), MixColors() +-- +function db.fixPalette(pal,sortflag) -- Arrange palette & only keep unique colors + + local n,l,rgb,i,unique,bri,hue,sat,ulist,indexpal,newpal,dtot + ulist = {} + indexpal = {} + newpal = {} + local doubles; doubles = {} + + l = #pal + + unique = 1 -- ok, see how stupid lua is + dtot = 0 + for n=1, l, 1 do + rgb = pal[n]; -- actually rgbn + i = 1 + rgb[1] * 65536 + rgb[2] * 256 + rgb[3]; + bri = db.getBrightness(rgb[1],rgb[2],rgb[3]) + if indexpal[i] == nil then + indexpal[i] = rgb; ulist[unique] = {i,bri}; unique = unique+1; + else + doubles[rgb[4]] = true; -- Mark as double + dtot = dtot + 1 + end + end + + -- sort ulist + if sortflag == 1 then db.sorti(ulist,2); end -- sort by brightness + + l = #ulist + for n=1, l, 1 do + newpal[n] = indexpal[ulist[n][1]] + end + + newpal["doubles"] = doubles + newpal.double_total = dtot + + --messagebox("unique colors", unique-1) + + return newpal + +end +-- + +-- +-- InsertionSort Array, this is chaos...I'm confused and stomped...don't understand how Lua works... +-- ...sorting seem be to ok but this code is ugly... +-- Sort LO-HI +-- +-- Screwed up or confused thing here I think, perhaps lo-hi/hi-lo. This is working lo-hi but the code +-- looks like hi-lo...edit this some day +-- +function db.sorti(d,idx) + local a,j,tmp,l,e + l = #d + + for a=2, l, 1 do + tmp = d[a]; + e = a + for j=a, 2, -1 do + e = j + if d[j-1][idx] > tmp[idx] then d[j] = d[j-1]; e = j-1; else break; end; + end; + d[e] = tmp; -- WHY THE F**K CAN'T YOU READ j HERE!?! STUPID ASSUCKING LANGUAGE + + end; + --return d +end +-- + +-- +function db.drawColorspace12bit(x,y,cols,size) + local r,g,b,c,rows,row,col,s16,rx,ry,xx,yy + s16 = size*16 + rows = math.floor(16/cols) + + for g = 0, 15, 1 do + col = g % cols + row = math.floor(g / cols) + for r = 0, 15, 1 do + for b = 0, 15, 1 do + c = matchcolor(r*17,g*17,b*17) + xx = x+col*s16+r*size + yy = y+row*s16+b*size + for ry = 0, size-1, 1 do + for rx = 0, size-1, 1 do + putpicturepixel(xx+rx,yy+ry,c) + end;end + end + end + end +end +-- + +-- +function db.drawHSBdiagram(pallist,posx,posy,width,height,size,sat) + --db.addHSBtoPalette(palList) + local x,y,c + for y = 0, height-1, 1 do + for x = 0, width-1, 1 do + hue = 255/width * x + bri = 255/height * y + c = db.matchcolorHSB(hue,sat,bri,pallist,true) + db.drawRectangle(posx + x*size, posy + y*size,size,size, c) + end + end +end +-- + +-- +-- Histograms, remapping etc. +-- + +-- +function db.makeHistogram() + local n,y,x,c,w,h,list; list = {} + w, h = getpicturesize() + for n = 1, 256, 1 do list[n] = 0; end + for y = 0, h - 1, 1 do + for x = 0, w - 1, 1 do + c = getpicturepixel(x,y) + list[c+1] = list[c+1] + 1 + end + end + return list +end +-- + +-- +function db.makeSpareHistogram() + local n,y,x,c,w,h,list; list = {} + w, h = getsparepicturesize() + --w,h = 512,360 + for n = 1, 256, 1 do list[n] = 0; end + for y = 0, h - 1, 1 do + for x = 0, w - 1, 1 do + c = getsparepicturepixel(x,y) + list[c+1] = list[c+1] + 1 + end + end + return list +end +-- + + +-- +-- Makes a palette-list from only the colors (histogram) that occurs in the image +-- Assumes image/palette has not changed since histogram was created +function db.makePalListFromHistogram(hist) + local n,r,g,b,list,count + list = {} + count = 1 + for n = 1, #hist, 1 do + if hist[n] > 0 then + r,g,b = getcolor(n-1) + list[count] = {r,g,b,n-1} + count = count + 1 + end + end + return list +end +-- + +function db.makePalListFromSpareHistogram(hist) + local n,r,g,b,list,count + list = {} + count = 1 + for n = 1, #hist, 1 do + if hist[n] > 0 then + r,g,b = getsparecolor(n-1) + list[count] = {r,g,b,n-1} + count = count + 1 + end + end + return list +end +-- + + +-- +function db.remap(org) -- Working with a remap-list there's no need of reading backuppixel + --messagebox("Remapping") + local x,y,c,i,w,h,s,f,col + f = getpicturepixel + s = false + w, h = getpicturesize() + for y = 0, h - 1, 1 do + for x = 0, w - 1, 1 do + c = f(x,y) + i = org[c+1] + if i == null then i = matchcolor(getbackupcolor(getbackuppixel(x,y))); s = true; col = c; end -- Find color for a removed double + putpicturepixel(x,y,i) + end + end + 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); + end +end +-- + +-- +-- Palette DeCluster: Color-reduction by fusing similar colors into new ones, using a desired tolerance. +-- This is a method similar to Median-Cut, but more surgical. +-- +-- pallist: Palette list {r,g,b,palette_index} +-- hist: Histogram {color 0 pixels, color 1 pixels...etc} always a full 256 color list +-- crad: Cluster radius treshold in % of distance between black & white +-- A value of 0 will only remove identical colors +-- A value of 3-4 will usally fuse redundant colors without causing notice +-- prot_pow: (0..10) Protect common colors in histogram. Distances are increased by ocurrence. +-- Also gives protection to fused colors even if not using histogram (combined nominal weights) +-- pixels: Pixels in image (so protection can be calculated) +-- rw,gw,bw: Color weights (rw+gw+bw = 1, 0.33,0.33,0.33 is nominal) +-- +-- Returns: +-- a new (c)palette list {r,g,b,{original palette_indices},fused flag, histogram_weight} +-- a remap list (org) [image color + 1] = remap color (in the new palette) +function db.deCluster(pallist, hist, crad, prot_pow, pixels, rw,gw,bw) + + --messagebox(pixels) + + 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 + local worst,wtot,maxdist,maxDist,distfrac,clusterExists,clustVal,count,crad1 + local cList,cPalList,clusterList,fuseCol,orgcols,newPalList,org + + maxdist = math.sqrt(rw*rw*65025 + gw*gw*65025 + bw*bw*65025) + distfrac = 100 / maxdist + + -- Let's just make a slightly more suitable format of the pallist (List for original color(s)) + cPalList = {} + for u = 1, #pallist, 1 do + c = pallist[u] + cPalList[u] = {c[1],c[2],c[3],{c[4]},false,hist[c[4]+1]} -- r,g,b,{original colors},fuse_marker,histogram_weight + end + + --table.insert(cPalList,{255,255,0,{257},false,1}) + + clusterExists = true + while clusterExists do + clusterExists = false + clusterList = {} + + crad1 = crad + 1 -- avoid divison by zero + worst = 9999 + for a = 1, #cPalList, 1 do + c1 = cPalList[a] + r1,g1,b1 = c1[1],c1[2],c1[3] + wtot = c1[6] + cList = {a} + maxDist = 0 + for b = 1, #cPalList, 1 do + if (b ~= a) then + c2 = cPalList[b] + r2,g2,b2 = c2[1],c2[2],c2[3] + wt = c2[6] + pd = math.pow((1 + wt / pixels), prot_pow) -- Protection, increase distance + dist = db.getColorDistance_weight(r1,g1,b1,r2,g2,b2,rw,gw,bw) * distfrac * pd + if dist <= crad then + wtot = wtot + wt + table.insert(cList,b) + maxDist = math.max(dist,maxDist) + end + end + end -- b + if #cList > 1 then + clustVal = maxDist / (crad1 * #cList) * (wtot / #cList) + if clustVal < worst then + worst = clustVal + clusterList = cList + end + end + end -- a + + --t = db.ary2txt(clusterList) + --messagebox("Worst cluster is "..t) + + -- Fuse + if #clusterList > 1 then + clusterExists = true -- Run another iteration and look for more clusters + fuseCol = {0,0,0,{}} + rt,gt,bt,tot = 0,0,0,0 + for n = 1, #clusterList, 1 do + i = clusterList[n] + c = cPalList[i] + --o = c[4][1] -- Original color (always #1 in list since fused colors can't re-fuse) + o = c[4] -- Original color list + --if c[5] == true then messagebox("Re-Fusing..."); end + r,g,b = c[1],c[2],c[3] + --wt = hist[o+1] -- Org. colors are 0-255 + wt = c[6] + rt = rt + r * wt + gt = gt + g * wt + bt = bt + b * wt + tot = tot + wt + cPalList[i] = -1 -- Erase color + --table.insert(fuseCol[4],o) + orgcols = fuseCol[4] + for j = 1, #o, 1 do + table.insert(orgcols,o[j]) + end + fuseCol[4] = orgcols + end + + rt = rt / tot + gt = gt / tot + bt = bt / tot + fuseCol[1] = rt + fuseCol[2] = gt + fuseCol[3] = bt + fuseCol[5] = true -- fusecol marker + fuseCol[6] = tot + table.insert(cPalList,fuseCol) + --messagebox(#clusterList.." Colors was fused, resulting in "..rt..", "..gt..", "..bt) + newPalList = {} + for n = 1, #cPalList, 1 do + if cPalList[n] ~= -1 then + table.insert(newPalList,cPalList[n]) + --newPalList = db.newArrayInsertLast(newPalList,cPalList[n]) + end + end + cPalList = newPalList + --messagebox("Pal length: "..#cPalList) + statusmessage("DeCluster - Image colors:"..#cPalList.." "); waitbreak(0) + end -- fuse + + end -- while + + -- Create remap-list + org = {} + count = 0 + for u = 1, #cPalList, 1 do + c = cPalList[u] + for n = 1, #c[4], 1 do + i = c[4][n] + org[i+1] = count -- quick way to remap without matchcolor + end + count = count + 1 + end + + return org,cPalList + +end; -- decluster + + +-- +-- ... eof Custom Color / Palette functions ... +-- + + +-- ***************************** +-- *** Custom Draw functions *** +-- ***************************** + +-- +function db.lineTransp(x1,y1,x2,y2,c,amt) -- amt: 0-1, 1 = Full color + local n,st,m,x,y,r,g,b,r1,g1,b1,c2,org; m = math + org = 1 - amt + st = m.max(1,m.abs(x2-x1),m.abs(y2-y1)); + for n = 0, st, 1 do + x = m.floor(x1+n*(x2-x1)/st) + y = m.floor(y1+n*(y2-y1)/st) + r,g,b = getcolor(getpicturepixel(x,y)) + r1,g1,b1 = getcolor(c) + c2 = matchcolor(r1*amt+r*org, g1*amt+g*org, b1*amt+b*org) + putpicturepixel(x, y, c2 ); + end +end +-- + +-- +function db.drawBrushRectangle(x1,y1,w,h,c) + local x,y + for y = y1, y1+h-1, 1 do + for x = x1, x1+w-1, 1 do + putbrushpixel(x,y,c); + end + end +end +-- + +-- +function db.drawRectangle(x1,y1,w,h,c) + drawfilledrect(x1, y1, x1+w, y1+w, c); +end +-- + +-- +function db.drawRectangleNeg(x1,y1,w,h,c) + local x,y,xs,ys + xs = db.sign(w) + ys = db.sign(h) + if xs == 0 then xs = 1; end + if ys == 0 then ys = 1; end + for y = y1, y1+h-1, ys do + for x = x1, x1+w-1, xs do + putpicturepixel(x,y,c); + end + end +end +-- + +-- +function db.drawRectangleLine(x,y,w,h,c) + w = w-1 + h = h-1 + drawline(x,y,x+w,y,c) + drawline(x,y,x,y+h,c) + drawline(x,y+h,x+w,y+h,c) + drawline(x+w,y,x+w,y+h,c) +end +-- + + +-- +function db.drawRectangleMix(x1,y1,w,h,c1,c2) + local x,y,c,n + c = {c1,c2} + n = 0 + for y = y1, y1+h-1, 1 do + n = n + 1 + for x = x1, x1+w-1, 1 do + n = n + 1 + putpicturepixel(x,y,c[n%2+1]); + end + end +end +-- + +-- +function db.drawBrushCircle(x1,y1,r,c) -- ok, lottsa weird adjustments here, can probably be optimized... + local x,y,d + for y = 0, r*2, 1 do + for x = 0, r*2, 1 do + d = math.sqrt((x-r-0.5)^2 + (y-r-0.5)^2) + if d < r-0.25 then putbrushpixel(x1+x-r-0.5,y1+y-r-0.5,c); end + end + end +end +-- + +-- +-- Rotation in degrees +-- Step is # of line segments (more is "better") +-- a & b are axis-radius +function db.ellipse2(x,y,a,b,stp,rot,col) + local n,m=math,rad,al,sa,ca,sb,cb,ox,oy,x1,y1,ast + m = math; rad = m.pi/180; ast = rad * 360/stp; + sb = m.sin(-rot * rad); cb = m.cos(-rot * rad) + for n = 0, stp, 1 do + ox = x1; oy = y1; + sa = m.sin(ast*n) * b; ca = m.cos(ast*n) * a + x1 = x + ca * cb - sa * sb + y1 = y + ca * sb + sa * cb + if (n > 0) then drawline(ox,oy,x1,y1,col); end + end +end +-- + + + +--[[ +var ER = 0.3 +var DR = 0.15 + +ellipse(0.5*xx,0.5*yy,DR*xx,6,Math.PI*0) + +function ellipse(x,y,r,stp,rot){ + var n,deg=360,m=Math,rad=Math.PI/180,rn + var ox,oy,x1,y1,x2,y2,d1,r1 = ER * xx + + for (n=0; n<=deg; n+=stp){ + + ox = x2; oy = y2, rn = rad * n + d1 = rn - rot + x1 = x + m.sin(d1) * r + y1 = y + m.cos(d1) * r + + x2 = x1 + m.sin(-rn) * r1 + y2 = y1 + m.cos(-rn) * r1 + if (n > 0){ line_rgb(MX,[0,0,0],0,ox,oy,x2,y2) } + } +} + +} + +ellipse2(0.5*xx,0.5*yy,15,8,200,22,[0,0,0],0.5) + +function ellipse2(x,y,a,b,stp,rot,rgb,transp){ + var n,m=Math,rad=m.PI/180,al,sa,ca,sb,cb,ox,oy,x1,y1 + sb = m.sin(-rot * rad); cb = m.cos(-rot * rad) + for (n=0; n<=stp; n++){ + ox = x1; oy = y1; al = rad * 360/stp * n + sa = m.sin(al) * b; ca = m.cos(al) * a + x1 = x + ca * cb - sa * sb + y1 = y + ca * sb + sa * cb + if (n > 0){ line_rgb(MX,rgb,transp,ox,oy,x1,y1) } + } +} + + +]] + + + +function db.obliqueCube(side,x,y,r,g,b,bri) + local n,c,depth,x1,y1,x2,y2,f + + f = matchcolor + c = f(r,g,b) + cP50 = f(r+bri*0.5,g+bri*0.5,b+bri*0.5) + cP75 = f(r+bri*0.75,g+bri*0.75,b+bri*0.75) + cM50 = f(r-bri*0.5,g-bri*0.5,b-bri*0.5) + cM100 = f(r-bri,g-bri,b-bri) + + depth = math.floor(side / 2) + + for n = 0, depth-1, 1 do + drawline(x+side+n,y-1-n,x+side+n,y+side-n-1,cM50) + end + + for n = 0, depth-1, 1 do + drawline(x+n,y-1-n,x+side+n-1,y-1-n,cP50) + end + + -- / + -- + --drawline(x+side,y-1,x+side+depth-1,y-depth,c) + + -- Smoothing & Shade + + -- + -- / + --drawline(x+side,y+side-1,x+side+depth-1,y+side-depth,cM100) + + --drawline(x,y,x+side-2,y,cP75) + --drawline(x,y,x,y+side-2,cP75) + + db.drawRectangle(x,y,side,side,c) + +end + + +function db.obliqueCubeBRI(side,x,y,r,g,b,bri,pallist,briweight,index_flag) + local n,c,depth,x1,y1,x2,y2 + + --f = db.getBestPalMatchHYBRID + c = db.getBestPalMatchHYBRID({r,g,b}, pallist, briweight, index_flag) + cP50 = db.getBestPalMatchHYBRID({r+bri*0.5,g+bri*0.5,b+bri*0.5}, pallist, briweight, index_flag) + cP75 = db.getBestPalMatchHYBRID({r+bri*0.75,g+bri*0.75,b+bri*0.75}, pallist, briweight, index_flag) + cM50 = db.getBestPalMatchHYBRID({r-bri*0.5,g-bri*0.5,b-bri*0.5}, pallist, briweight, index_flag) + cM100 = db.getBestPalMatchHYBRID({r-bri,g-bri,b-bri}, pallist, briweight, index_flag) + + depth = math.floor(side / 2) + + db.drawRectangle(x,y,side,side,c) + + for n = 0, depth-1, 1 do + drawline(x+side+n,y-1-n,x+side+n,y+side-n-1,cM50) + end + + for n = 0, depth-1, 1 do + drawline(x+n,y-1-n,x+side+n-1,y-1-n,cP50) + end + + -- / + -- + drawline(x+side,y-1,x+side+depth-1,y-depth,c) + + -- Smoothing & Shade + + -- + -- / + --drawline(x+side,y+side-1,x+side+depth-1,y+side-depth,cM100) + + --drawline(x,y,x+side-2,y,cP75) + --drawline(x,y,x,y+side-2,cP75) + + +end + + +-- +-- ... eof Custom Draw functions ... +-- + + +-- ****************************** +-- *** Filters & Convolutions *** +-- ****************************** + + +function db.applyConvolution2Pic(convmx,divisor,bias,neg,amt) + 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 + + n1 = 1 + n2 = bias + if neg == 1 then + n1 = -1 + n2 = 255 + bias + end + + amtr = 1 - amt + w, h = getpicturesize() + cy = #convmx + cx = #convmx[1] + mxh = math.floor(cx / 2) + 1 + myh = math.floor(cy / 2) + 1 + + for y = 0, h-1, 1 do + for x = 0, w-1, 1 do + r,g,b = 0,0,0 + ro,go,bo = getcolor(getbackuppixel(x,y)) + div = divisor + for my = 1, cy, 1 do + for mx = 1, cx, 1 do + xp = mx-mxh + yp = my-myh + mp = convmx[my][mx] + xx = x + xp + yy = y + yp + if yy>=0 and yy=0 and xx 0 and n 0 and n0) then + x = x*px - fx + y = y*py - fy + nfrac = nfrac + spfrac + end + n = n+1 + end + --return 1 - n/i; + return 1 - nfrac/i +end +-- + + +-- +function db.mandel(x,y,l,r,o,i) -- pos. as fraction of 1, left coord, right coord, y coord, iterations + + local w,s,a,p,q,n,v,w + + s=math.abs(r-l); + + a = l + s*x; + p = a; + b = o - s*(y-0.5); + q = b; + n = 1; + v = 0; + w = 0; + + while (v+w<4 and n weakest then weakest = w; weak_i = {z,y,x}; end + end + + end;end;end + return weak_i[1],weak_i[2],weak_i[3] +end +-- + +-- +-- +-- Nearest color version: void is selected by the point that has the greatest distance +-- to the nearest color. Higher value means greater void. +-- +function db.addColor2Cube(cube,sha,r,g,b,rw,gw,bw) + local star,x,y,z,d,rd,gd,bd,cu1,cu2 + star = 0 + cube[r+1][g+1][b+1] = {false, star} + for z = 0, sha-1, 1 do + rd = (rw*(z-r))^2 + cu2 = cube[z+1] + for y = 0, sha-1, 1 do + gd = (gw*(y-g))^2 + cu1 = cu2[y+1] + for x = 0, sha-1, 1 do + + d = rd + gd + (bw*(x-b))^2 + + --cube[z+1][y+1][x+1][2] = math.min(d, cube[z+1][y+1][x+1][2]) -- Don't add, use nearest color + + cu1[x+1][2] = math.min(d, cu1[x+1][2]) + + end;end;end +end +-- + +-- Should be same as original, but not 100% verified. Using a rgb+1 trick to speed up handling +-- +function db.addColor2Cube_test(cube,sha,r,g,b,rw,gw,bw) + local star,x,y,z,d,rd,gd,bd,cu1,cu2 + star = 0 + r = r+1; g = g+1; b = b+1 + cube[r][g][b] = {false, star} + for z = 1, sha, 1 do + rd = (rw*(z-r))^2 + cu2 = cube[z] + for y = 1, sha, 1 do + gd = (gw*(y-g))^2 + cu1 = cu2[y] + for x = 1, sha, 1 do + cu1[x][2] = math.min(rd+gd+(bw*(x-b))^2, cu1[x][2]) + end;end;end +end +-- + + + +-- Create new allowed colorlines in colorspace (ramps from which colors can be picked) +function db.enableRangeColorsInCube(cube,sha,r1,g1,b1,r2,g2,b2) + + local div,r,g,b,n,rs,gs,bs + div = 256 / sha + rs = (r2 - r1) / sha / div + gs = (g2 - g1) / sha / div + bs = (b2 - b1) / sha / div + + for n = 0, sha-1, 1 do + + r = math.floor(r1/div + rs * n) + g = math.floor(g1/div + gs * n) + b = math.floor(b1/div + bs * n) + + cube[r+1][g+1][b+1][1] = true + + end +end +-- + + +function db.colorCigarr(shades,radius,fill_flag) + local s,rad,radsq,step,shalf,bas,cols,found,x,y,z,bri,con,d,n + radius = radius / 100 + step = math.floor(255 / (shades-1)) + shalf = math.floor(shades / 2) + s = shades - 1 + rad = math.floor(shades / 2 * radius) + radsq = rad^2 + + bas = 0 + cols = {} + found = 0 + + for z = 0, s, 1 do + for y = 0, s, 1 do + for x = 0, s, 1 do + + --0.26,0.55,0.19 + bri = (x + y + z ) / 3 + --bri = math.sqrt(((x*0.26)^2 + (y*0.55)^2 + (z*0.19)^2)) * 1.5609 + con = math.floor((shades - math.abs(bri - shalf)*2) * radius) + + d = math.floor(math.sqrt((bri-x)^2 + (bri-y)^2 + (bri-z)^2)) + --d = math.floor(math.sqrt(((bri-x)*0.26)^2 + ((bri-y)*0.55)^2 + ((bri-z)*0.19)^2)) * 1.5609 + + -- Filled cigarr: Less or Equal, cigarr shell: Equal + if d == con or (d < con and fill_flag) then + found = found + 1 + r = bas + x * step + g = bas + y * step + b = bas + z * step + cols[found] = {r,g,b} + end + + end; end; end + + --messagebox("Colors found: "..found.."\n\n".."Run AnalyzePalette to examine") + + for n = 0, 255, 1 do + if n < found then + c = cols[n+1] + setcolor(n,c[1],c[2],c[3]) + else + setcolor(n,0,0,0) + end + end +end -- eof colorcigarr + + +-- +-- ... eof Color Cube ... +-- + + + +-- COLORMIX -- +-- +-- Returns a list of mixcolors palette entries, that are ranked by by quality & usefulness +-- +-- This whole junk my partly locked on 16 shades (4096 colors/ 12bit palette precision) so don't use anything else... +-- +-- +function db.colormixAnalysis(sha,spare_flag,cust_dist) -- Interface + local shades,pallist,ilist,custom_max_distance + + shades = sha -- 16 is good + --messagebox(shades) + + custom_max_distance = -1 + if cust_dist ~= null then + custom_max_distance = cust_dist -- in % + end + + if spare_flag == true then -- No shades here for now + --pallist = db.makePalListShadeSPARE(256,shades) -- 16 shades so Colorcube processes is possible + pallist = db.makeSparePalList(256) + pallist = db.fixPalette(pallist,0) -- Remove doubles, No need to sort? + ilist = db.makeIndexList(pallist, -1) -- -1, use list order as index + else + pallist = db.makePalListShade(256,shades) -- 16 shades so Colorcube processes is possible + pallist = db.fixPalette(pallist,0) -- Remove doubles, No need to sort? + ilist = db.makeIndexList(pallist, -1) -- -1, use list order as index + end + + if shades > 0 then + return db.colormixAnalysisEXT(shades,pallist,ilist,custom_max_distance) -- max distance in % + end + if shades == -1 then + return db.colormixAnalysisEXTnoshade(pallist,ilist,custom_max_distance) -- max distance in % + end +end +-- +-- +function db.colormixAnalysisEXT(SHADES,pallist,ilist,custom_max_distance) -- Shades, most number of mixes returned + local n,m,c1,c2,pairs,cube,rm,gm,bm + local mix,total,found,dist,void,ideal,mini,maxi,bestmix,bestscore + + --messagebox("will now make pairs") + + pairs = db.pairsFromList(ilist,0) -- 0 for unique pairs only, pairs are entries in pallist + + --messagebox(#pairs.." will now add colors to cube") + + cube = db.initColorCube(SHADES,{true,9999}) + for n = 1, #pallist, 1 do + c1 = pallist[n] + db.addColor2Cube_test(cube,SHADES,c1[1],c1[2],c1[3],0.26,0.55,0.19) + end + + -- these values are adjusted for a 12bit palette (0-15) and perceptual weight where r+g+b = 1.0 + -- Ideal distance = 2.5 Green steps = 1.375 + -- Minimum distance = 1 Green step = 0.55 + + --messagebox("colorcube done") + + VACT = 1 + DACT = 1 + + total = 9.56 -- Max distance possible with 16 shades + ideal = 0.45 -- 1 step = 0.637 + mini = 0.35 + maxi = ideal + (total - ideal) / math.max(1, #pallist / 16) + if custom_max_distance ~= -1 then + maxi = total * (custom_max_distance / 100) + end + mix = {} + --mix[1] = {9e99,0,0,9e99,0,0,0} + bestmix = -1 + bestscore = 9e99 + found = 0 + for n = 1, #pairs, 1 do + + c1 = pallist[pairs[n][1]] + c2 = pallist[pairs[n][2]] + --0.26,0.55,0.19 + dist = db.getColorDistance_weight(c1[1],c1[2],c1[3],c2[1],c2[2],c2[3],0.26,0.55,0.19) -- Not normalized + + rm = math.floor((c1[1]+c2[1])/2) + gm = math.floor((c1[2]+c2[2])/2) + bm = math.floor((c1[3]+c2[3])/2) + + -- Mix color adjustment (perhaps less than perfect, but probably good enough) + mixbri = db.getBrightness(rm,gm,bm) + truebri = math.sqrt((db.getBrightness(c1[1],c1[2],c1[3])^2 + db.getBrightness(c2[1],c2[2],c2[3])^2) / 2) + diff = truebri - mixbri + rm = math.max(0,math.min(15,math.floor(rm + diff))) + gm = math.max(0,math.min(15,math.floor(gm + diff))) + bm = math.max(0,math.min(15,math.floor(bm + diff))) + newbri = db.getBrightness(rm,gm,bm) + delta = math.abs(newbri - truebri) + --if delta > 0.9 then + -- messagebox(pallist[pairs[n][1]][4]..", "..pallist[pairs[n][2]][4].." delta = "..delta) + --end + -- + + --rm = math.floor(math.sqrt((c1[1]^2 + c2[1]^2) / 2)) + --gm = math.floor(math.sqrt((c1[2]^2 + c2[2]^2) / 2)) + --bm = math.floor(math.sqrt((c1[3]^2 + c2[3]^2) / 2)) + + void = cube[rm+1][gm+1][bm+1][2] + + if dist >= mini and dist <= maxi then + found = found + 1 + score = ((1+DACT*(dist - ideal)^2) / (1+void*VACT)) -- Lowest is best + mix[found] = {score,pallist[pairs[n][1]][4],pallist[pairs[n][2]][4],dist,rm*SHADES,gm*SHADES,bm*SHADES,c1[1]*SHADES,c1[2]*SHADES,c1[3]*SHADES,c2[1]*SHADES,c2[2]*SHADES,c2[3]*SHADES} -- mix holds palette entry + if score < bestscore then bestscore = score; bestmix = found; end + end + + end + + + if true == false then + -- 2nd pass, add bestmix to colorspace. This reduces many similar mixes. + m = mix[bestmix] + db.addColor2Cube(cube,SHADES,m[5],m[6],m[7],0.26,0.55,0.19) + for n = 1, #mix, 1 do + if n ~= bestmix then + m = mix[n] + dist = m[4] + void = cube[m[5]+1][m[6]+1][m[7]+1][2] + score = ((1+DACT*(dist - ideal)^2) / (1+void*VACT)) + m[1] = score + end + end + end + + c1,c2 = -1,-1 + if found > 0 then + db.sorti(mix,1) + best = mix[1] + c1 = best[2] + c2 = best[3] + end + + --return found,c1,c2 + return mix,found,c1,c2 +end +-- + + +-- +-- Mixcolor without colorcube - no scoring or sorting, 24bit colors, faster... +-- +function db.colormixAnalysisEXTnoshade(pallist,ilist,custom_max_distance) + local n,m,c1,c2,pairs,cube,rm,gm,bm + local mix,total,found,dist,void,ideal,mini,maxi,bestmix,bestscore + + pairs = db.pairsFromList(ilist,0) -- 0 for unique pairs only, pairs are entries in pallist + + total = 162.53 -- Max distance possible with 24-bit palette ad Dawn3.0 color weights 162.53 + ideal = 0 + mini = 0 + maxi = ideal + (total - ideal) / math.max(1, #pallist / 16) + + if custom_max_distance ~= -1 then + maxi = total * (custom_max_distance / 100) + end + + mix = {} + found = 0 + for n = 1, #pairs, 1 do + + c1 = pallist[pairs[n][1]] + c2 = pallist[pairs[n][2]] + --0.26,0.55,0.19 + dist = db.getColorDistance_weight(c1[1],c1[2],c1[3],c2[1],c2[2],c2[3],0.26,0.55,0.19) -- Not normalized + + rm = math.floor((c1[1]+c2[1])/2) + gm = math.floor((c1[2]+c2[2])/2) + bm = math.floor((c1[3]+c2[3])/2) + + -- Mix color adjustment + mixbri = db.getBrightness(rm,gm,bm) + truebri = math.sqrt((db.getBrightness(c1[1],c1[2],c1[3])^2 + db.getBrightness(c2[1],c2[2],c2[3])^2) / 2) + diff = truebri - mixbri + rm = math.max(0,math.min(255,math.floor(rm + diff))) + gm = math.max(0,math.min(255,math.floor(gm + diff))) + bm = math.max(0,math.min(255,math.floor(bm + diff))) + newbri = db.getBrightness(rm,gm,bm) + delta = math.abs(newbri - truebri) + --if delta > 0.9 then + -- messagebox(pallist[pairs[n][1]][4]..", "..pallist[pairs[n][2]][4].." delta = "..delta) + --end + -- + + if dist >= mini and dist <= maxi then + found = found + 1 + score = 1 + mix[found] = {score,pallist[pairs[n][1]][4],pallist[pairs[n][2]][4],dist,rm,gm,bm,c1[1],c1[2],c1[3],c2[1],c2[2],c2[3]} -- mix holds palette entry + end + + end + + --messagebox(#mix) + + return mix,found,-1,-1 +end +-- + + + +-- Fuse a palettelist into an extended mix-anlysis list +function db.fusePALandMIX(pal,mix,max_score,max_dist) + local n,c,mixlist,tot,score,dist,c1,c2,rm,gm,bm + + mixlist = {} + tot = 0 + + -- {r,g,b,n} + for n = 1, #pal, 1 do + tot = tot + 1 + c = pal[n] + mixlist[tot] = {0,c[4],c[4],0,c[1],c[2],c[3],c[1],c[2],c[3],c[1],c[2],c[3]} + end + + -- {score,col#1,col#2,dist,rm,gm,bm} low score is best + for n = 1, #mix, 1 do + score = mix[n][1] + dist = mix[n][4] + if score <= max_score and dist <= max_dist then + tot = tot + 1 + mixlist[tot] = mix[n] + end + end + + return mixlist +end +-- + + +-- ******************************************** +-- *** L-system (fractal curves & "plants") *** +-- ******************************************** +-- + +-- +function db.Lsys_makeData(a) + local n,i; i = {} + for n = 1, #a, 1 do i[a[n][2]] = a[n]; end + return i +end +-- + +-- +function db.Lsys_makeSet(seed,iter,data) + local s,n,i,nset,set + set = seed + for n = 1, iter, 1 do + nset = '' + for i = 1, #set, 1 do + s = string.sub(set,i,i) + nset = nset..data[s][3] + end + set = nset + end + return set +end +-- + +function db.Lsys_draw(set,data,cx,cy,size,rot,rgb,rng,transp, speed) + local p,M,DEG,l,n,d,i,v,q,c,tx,ty,posx,posy,dval,col,w,h,s,cl,count + + if speed == nil then speed = 50; end -- speed is drawing operations per update + + function ang(d) return (d % 360 + 360) * DEG; end + + w,h = getpicturesize() + + p = 0 + M = math + DEG = math.pi/180 + l = #set + + posx={}; posy={}; dval={} + + if (rgb == null) then rgb = {0,0,0}; end + if (transp == null) then transp = 0; end + col = db.newArrayMerge(rgb,{}) + q = 255 / l + + count = 0 + for n = 1, l, 1 do + s = string.sub(set,n,n) + d = data[s] + i = d[1] + v = d[4] + + --messagebox(i) + + if (i == 'Left') then rot = rot - v; end + if (i == 'Right') then rot = rot + v; end + + if (i == 'Save') then p=p+1; posx[p] = cx; posy[p] = cy; dval[p] = rot; end + if (i == 'Load') then cx = posx[p]; cy = posy[p]; rot = dval[p]; p=p-1; end + + if (i == 'Draw') then + tx = cx + M.sin(ang(rot)) * size + ty = cy + -M.cos(ang(rot)) * size + for c = 1, 3, 1 do + if (rng[c] > 0) then col[c] = rgb[c] + (n * q) * rng[c]; end + if (rng[c] < 0) then col[c] = rgb[c] + (n * q) * rng[c]; end + end + + cl = matchcolor(col[1],col[2],col[3]) + --putpicturepixel(cx*w,cy*h,cl); + --drawline(cx*w,cy*h,tx*w,ty*h,cl) + db.lineTransp(cx*w,cy*h,tx*w,ty*h,cl,transp) + + cx = tx; cy = ty + end + count = count + 1 + if count == speed then count = 0; updatescreen(); if (waitbreak(0)==1) then return end; end + end + + return {cx,cy,rot} +end -- draw + + +-- +-- eof L-system +-- + + + + + + + + diff --git a/src/factory.c b/src/factory.c index 94f466d3..7bb7f565 100644 --- a/src/factory.c +++ b/src/factory.c @@ -348,6 +348,85 @@ int L_PutPicturePixel(lua_State* L) return 0; // no values returned for lua } + +int L_DrawLine(lua_State* L) +{ + int x1, y1, x2, y2, c; + + int nb_args = lua_gettop(L); + + LUA_ARG_LIMIT(5, "drawline"); + LUA_ARG_NUMBER(1, "drawline", x1, INT_MIN, INT_MAX); + LUA_ARG_NUMBER(2, "drawline", y1, INT_MIN, INT_MAX); + LUA_ARG_NUMBER(3, "drawline", x2, INT_MIN, INT_MAX); + LUA_ARG_NUMBER(4, "drawline", y2, INT_MIN, INT_MAX); + LUA_ARG_NUMBER(5, "drawline", c, INT_MIN, INT_MAX); + + Pixel_figure = Display_pixel; + Draw_line_general(x1, y1, x2, y2, c); + + return 0; +} + + +int L_DrawFilledRect(lua_State* L) +{ + int x1, y1, x2, y2, c; + + int nb_args = lua_gettop(L); + + LUA_ARG_LIMIT(5, "drawfilledrect"); + LUA_ARG_NUMBER(1, "drawfilledrect", x1, INT_MIN, INT_MAX); + LUA_ARG_NUMBER(2, "drawfilledrect", y1, INT_MIN, INT_MAX); + LUA_ARG_NUMBER(3, "drawfilledrect", x2, INT_MIN, INT_MAX); + LUA_ARG_NUMBER(4, "drawfilledrect", y2, INT_MIN, INT_MAX); + LUA_ARG_NUMBER(5, "drawfilledrect", c, INT_MIN, INT_MAX); + + Draw_filled_rectangle(x1, y1, x2, y2, c); + + return 0; +} + + +int L_DrawCircle(lua_State* L) +{ + int x1, y1, r, c; + + int nb_args = lua_gettop(L); + + LUA_ARG_LIMIT(4, "drawcircle"); + LUA_ARG_NUMBER(1, "drawcircle", x1, INT_MIN, INT_MAX); + LUA_ARG_NUMBER(2, "drawcircle", y1, INT_MIN, INT_MAX); + LUA_ARG_NUMBER(3, "drawcircle", r, INT_MIN, INT_MAX); + LUA_ARG_NUMBER(4, "drawcircle", c, INT_MIN, INT_MAX); + + Pixel_figure = Display_pixel; + Circle_limit = r*r; + Draw_empty_circle_general(x1, y1, r, c); + + return 0; +} + + +int L_DrawDisk(lua_State* L) +{ + int x1, y1, r, c; + + int nb_args = lua_gettop(L); + + LUA_ARG_LIMIT(4, "drawdisk"); + LUA_ARG_NUMBER(1, "drawdisk", x1, INT_MIN, INT_MAX); + LUA_ARG_NUMBER(2, "drawdisk", y1, INT_MIN, INT_MAX); + LUA_ARG_NUMBER(3, "drawdisk", r, INT_MIN, INT_MAX); + LUA_ARG_NUMBER(4, "drawdisk", c, INT_MIN, INT_MAX); + + Circle_limit = r*r; + Draw_filled_circle(x1, y1, r, c); + + return 0; +} + + int L_GetPicturePixel(lua_State* L) { int x; @@ -1308,36 +1387,54 @@ void Run_script(const char *script_subdirectory, const char *script_filename) L = lua_open(); putenv("LUA_PATH=libs\\?.lua"); + // writing and reading pixels lua_register(L,"putbrushpixel",L_PutBrushPixel); + lua_register(L,"putpicturepixel",L_PutPicturePixel); + lua_register(L, "drawline",L_DrawLine); + lua_register(L, "drawfilledrect",L_DrawFilledRect); + lua_register(L, "drawcircle",L_DrawCircle); + lua_register(L, "drawdisk",L_DrawDisk); + lua_register(L,"getbrushpixel",L_GetBrushPixel); lua_register(L,"getbrushbackuppixel",L_GetBrushBackupPixel); - lua_register(L,"putpicturepixel",L_PutPicturePixel); lua_register(L,"getpicturepixel",L_GetPicturePixel); lua_register(L,"getlayerpixel",L_GetLayerPixel); lua_register(L,"getbackuppixel",L_GetBackupPixel); + lua_register(L,"getsparelayerpixel",L_GetSpareLayerPixel); + lua_register(L,"getsparepicturepixel",L_GetSparePicturePixel); + + + // resizing stuff lua_register(L,"setbrushsize",L_SetBrushSize); lua_register(L,"setpicturesize",L_SetPictureSize); + lua_register(L,"getbrushsize",L_GetBrushSize); lua_register(L,"getpicturesize",L_GetPictureSize); + lua_register(L,"getsparepicturesize",L_GetSparePictureSize); + + // color and palette lua_register(L,"setcolor",L_SetColor); + lua_register(L,"setforecolor",L_SetForeColor); + lua_register(L,"setbackcolor",L_SetBackColor); + lua_register(L,"getcolor",L_GetColor); lua_register(L,"getbackupcolor",L_GetBackupColor); - lua_register(L,"matchcolor",L_MatchColor); lua_register(L,"getbrushtransparentcolor",L_GetBrushTransparentColor); + lua_register(L,"getsparecolor",L_GetSpareColor); + lua_register(L,"getsparetranscolor",L_GetSpareTransColor); + lua_register(L,"getforecolor",L_GetForeColor); + lua_register(L,"getbackcolor",L_GetBackColor); + lua_register(L,"gettranscolor",L_GetTransColor); + + lua_register(L,"matchcolor",L_MatchColor); + + // ui lua_register(L,"inputbox",L_InputBox); lua_register(L,"messagebox",L_MessageBox); lua_register(L,"statusmessage",L_StatusMessage); lua_register(L,"selectbox",L_SelectBox); - lua_register(L,"getforecolor",L_GetForeColor); - lua_register(L,"getbackcolor",L_GetBackColor); - lua_register(L,"setforecolor",L_SetForeColor); - lua_register(L,"setbackcolor",L_SetBackColor); - lua_register(L,"gettranscolor",L_GetTransColor); - lua_register(L,"getsparepicturesize",L_GetSparePictureSize); - lua_register(L,"getsparelayerpixel",L_GetSpareLayerPixel); - lua_register(L,"getsparepicturepixel",L_GetSparePicturePixel); - lua_register(L,"getsparecolor",L_GetSpareColor); - lua_register(L,"getsparetranscolor",L_GetSpareTransColor); + + // misc. stuff lua_register(L,"clearpicture",L_ClearPicture); lua_register(L,"wait",L_Wait); lua_register(L,"waitbreak",L_WaitBreak);