├── color-compliment.lua ├── README.md ├── shade-generator.lua ├── draw-circle.lua ├── hue-generator.lua └── pico-dither.lua /color-compliment.lua: -------------------------------------------------------------------------------- 1 | -- Aseprite Script to show color compliment of given color 2 | -- Written by aquova, 2018 3 | -- https://github.com/aquova/aseprite-scripts 4 | 5 | -- Open dialog, ask user for color 6 | function userInput() 7 | local dlg = Dialog() 8 | -- Creates a starting color of black 9 | local defaultColor = Color{r=0, g=0, b=0, a=255} 10 | dlg:color{ id="color", label="Choose a color", color=defaultColor } 11 | dlg:button{ id="ok", text="OK" } 12 | dlg:button{ id="cancel", text="Cancel" } 13 | dlg:show() 14 | 15 | return dlg.data 16 | end 17 | 18 | function generateCompliment(color) 19 | local newHue = (color.hsvHue + 180) % 360 20 | local newCol = Color{h=newHue, s=color.hsvSaturation, v=color.hsvValue} 21 | return newCol 22 | end 23 | 24 | -- Generates the color gradiants and displays them 25 | function showOutput(color) 26 | local dlg = Dialog() 27 | local newColor = generateCompliment(color) 28 | dlg:color{ color=newColor } 29 | dlg:button{ id="ok", text="OK" } 30 | dlg:show() 31 | end 32 | 33 | -- Run script 34 | do 35 | local userColor = userInput() 36 | if userColor.ok then 37 | local c = Color{r=userColor.color.red, g=userColor.color.green, b=userColor.color.blue, a=userColor.color.alpha} 38 | showOutput(c) 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Aseprite-Scripts 2 | 3 | Some useful scripts for the *Aseprite* art program 4 | 5 | https://github.com/aquova/aseprite-scripts 6 | 7 | ## -- Installation -- 8 | 9 | To install, open the Scripts folder via `File->Scripts->Open Scripts Folder`. Simply move the files into that folder to install. Once there, scripts can be run via `File->Scripts->SCRIPT NAME`. You may need to use the 'Rescan Scripts Folder' option for them to show up. 10 | 11 | ## -- Scripts -- 12 | 13 | ### -- Pico Dithering -- 14 | 15 | Converts the canvas to the Pico-8 or Picotron palette, using Floyd-Steinberg dithering to achieve a more accurate image. 16 | 17 | ### -- Color Compliment -- 18 | 19 | This script asks for a single color and returns the HSV color compliment according to the color wheel. 20 | 21 | ### -- Hue Generator -- 22 | 23 | This script asks for two colors and the number of intermediate colors you wish to generate. The script will then give a window with a range of colors between the two that were given. 24 | 25 | ### -- Shade Generator -- 26 | 27 | This script will give a range of shades brighter and darker than the color that is given, ranging from black up to white. 28 | 29 | ### -- Draw Circle -- 30 | 31 | This script will draw a circle at the specified (X, Y) position at a specified radius, using the currently set foreground color. 32 | -------------------------------------------------------------------------------- /shade-generator.lua: -------------------------------------------------------------------------------- 1 | -- Aseprite Script to open dialog to create related shades 2 | -- Written by aquova, 2018 3 | -- https://github.com/aquova/aseprite-scripts 4 | 5 | -- Open dialog, ask user for a color 6 | function userInput() 7 | local dlg = Dialog() 8 | -- Creates a starting color of black 9 | local defaultColor = Color{r=0, g=0, b=0, a=255} 10 | dlg:color{ id="color1", label="Choose a color", color=defaultColor } 11 | dlg:button{ id="ok", text="OK" } 12 | dlg:button{ id="cancel", text="Cancel" } 13 | dlg:show() 14 | 15 | return dlg.data 16 | end 17 | 18 | function generateColors(color) 19 | local colors = {} 20 | for light=1,0,-0.1 do 21 | local newCol = Color{h=color.hslHue, s=color.hslSaturation, l=light} 22 | table.insert(colors, newCol) 23 | end 24 | 25 | return colors 26 | end 27 | 28 | function showOutput(color) 29 | local dlg = Dialog() 30 | local colors = generateColors(color) 31 | 32 | for i=1,#colors do 33 | dlg:newrow() 34 | dlg:color{color=colors[i]} 35 | end 36 | 37 | dlg:button{ id="ok", text="OK" } 38 | dlg:show() 39 | end 40 | 41 | -- Run script 42 | do 43 | local color = userInput() 44 | if color.ok then 45 | local userColor = Color{r=color.color1.red, g=color.color1.green, b=color.color1.blue, a=color.color1.alpha} 46 | showOutput(userColor) 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /draw-circle.lua: -------------------------------------------------------------------------------- 1 | -- Aseprite script to draw a circle at a specified (X,Y) with a given radius 2 | -- Written by aquova, 2021 3 | -- https://github.com/aquova/aseprite-scripts 4 | 5 | -- Open dialog, ask user for paramters 6 | function userInput() 7 | local dlg = Dialog() 8 | -- Create dialog parameters 9 | dlg:number{ id="x", label="X:", decimals=0 } 10 | dlg:number{ id="y", label="Y:", decimals=0 } 11 | dlg:number{ id="radius", label="Radius:", decimals=0 } 12 | dlg:button{ id="ok", text="OK" } 13 | dlg:button{ id="cancel", text="Cancel" } 14 | dlg:show() 15 | 16 | return dlg.data 17 | end 18 | 19 | -- Draws the specified circle 20 | function drawCircle(cx, cy, rad) 21 | local image = app.activeCel.image 22 | local copy = image:clone() 23 | local left = cx - rad 24 | local top = cy - rad 25 | for x = left, left + 2 * rad do 26 | for y = top, top + 2 * rad do 27 | if (x >= 0 and x < copy.width) and (y >= 0 and y < copy.height) then 28 | local dx = cx - x 29 | local dy = cy - y 30 | dx = dx^2 31 | dy = dy^2 32 | distSquared = dx + dy 33 | radSquared = rad^2 34 | if distSquared <= radSquared then 35 | copy:drawPixel(x, y, app.fgColor) 36 | end 37 | end 38 | end 39 | end 40 | app.activeCel.image:drawImage(copy) 41 | end 42 | 43 | -- Run script 44 | do 45 | local userCircle = userInput() 46 | if userCircle.ok then 47 | drawCircle(userCircle.x, userCircle.y, userCircle.radius) 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /hue-generator.lua: -------------------------------------------------------------------------------- 1 | -- Aseprite Script to open dialog to select hues between two colors 2 | -- Written by aquova, 2018 3 | -- https://github.com/aquova/aseprite-scripts 4 | 5 | -- Open dialog, ask user for two colors 6 | function userInput() 7 | local dlg = Dialog() 8 | -- Creates a starting color of black 9 | local defaultColor = Color{r=0, g=0, b=0, a=255} 10 | dlg:color{ id="color1", label="Choose two colors", color=defaultColor } 11 | dlg:color{ id="color2", color=defaultColor } 12 | dlg:slider{ id="num_hues", label="Number of colors to generate: ", min=3, max=9, value=3 } 13 | dlg:button{ id="ok", text="OK" } 14 | dlg:button{ id="cancel", text="Cancel" } 15 | dlg:show() 16 | 17 | return dlg.data 18 | end 19 | 20 | -- Generates the color gradiants and displays them 21 | function showOutput(color1, color2) 22 | local dlg = Dialog() 23 | -- Find the slopes of each component of both colors 24 | local m = { 25 | r=(color1.red - color2.red), 26 | g=(color1.green - color2.green), 27 | b=(color1.blue - color2.blue), 28 | a=(color1.alpha - color2.alpha) 29 | } 30 | 31 | for i=0,numHues do 32 | -- Linearly find the colors between the two initial colors 33 | local newRed = color1.red - math.floor(m.r * i / numHues) 34 | local newGreen = color1.green - math.floor(m.g * i / numHues) 35 | local newBlue = color1.blue - math.floor(m.b * i / numHues) 36 | local newAlpha = color1.alpha - math.floor(m.a * i / numHues) 37 | 38 | local newC = Color{r=newRed, g=newGreen, b=newBlue, a=newAlpha} 39 | -- Put every entry on a new row 40 | dlg:newrow() 41 | dlg:color{ color=newC } 42 | end 43 | dlg:button{ id="ok", text="OK" } 44 | dlg:show() 45 | end 46 | 47 | -- Run script 48 | do 49 | local color = userInput() 50 | if color.ok then 51 | -- Number of hues generated does not include initial colors 52 | numHues = color.num_hues + 1 53 | local c1 = Color{r=color.color1.red, g=color.color1.green, b=color.color1.blue, a=color.color1.alpha} 54 | local c2 = Color{r=color.color2.red, g=color.color2.green, b=color.color2.blue, a=color.color2.alpha} 55 | showOutput(c1, c2) 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /pico-dither.lua: -------------------------------------------------------------------------------- 1 | -- Aseprite script to convert an image into the Pico-8/Picotron palette with Floyd-Steinberg dithering 2 | -- Written by aquova, 2022-2024 3 | -- https://github.com/aquova/aseprite-scripts 4 | 5 | PICO8_PALETTE = { 6 | {r = 0, g = 0, b = 0}, 7 | {r = 29, g = 43, b = 83}, 8 | {r = 126, g = 37, b = 83}, 9 | {r = 0, g = 135, b = 81}, 10 | {r = 171, g = 82, b = 54}, 11 | {r = 95, g = 87, b = 79}, 12 | {r = 194, g = 195, b = 199}, 13 | {r = 255, g = 241, b = 232}, 14 | {r = 255, g = 0, b = 77}, 15 | {r = 255, g = 163, b = 0}, 16 | {r = 255, g = 236, b = 39}, 17 | {r = 0, g = 228, b = 54}, 18 | {r = 41, g = 173, b = 255}, 19 | {r = 131, g = 118, b = 156}, 20 | {r = 255, g = 119, b = 168}, 21 | {r = 255, g = 204, b = 170}, 22 | } 23 | 24 | PICOTRON_PALETTE = { 25 | {r = 0, g = 0, b = 0}, 26 | {r = 108, g = 51, b = 44}, 27 | {r = 160, g = 87, b = 61}, 28 | {r = 239, g = 139, b = 116}, 29 | {r = 247, g = 206, b = 175}, 30 | {r = 234, g = 51, b = 82}, 31 | {r = 179, g = 37, b = 77}, 32 | {r = 116, g = 44, b = 82}, 33 | {r = 69, g = 46, b = 56}, 34 | {r = 94, g = 87, b = 80}, 35 | {r = 158, g = 137, b = 123}, 36 | {r = 194, g = 195, b = 199}, 37 | {r = 253, g = 242, b = 233}, 38 | {r = 243, g = 176, b = 196}, 39 | {r = 238, g = 127, b = 167}, 40 | {r = 209, g = 48, b = 167}, 41 | {r = 32, g = 43, b = 80}, 42 | {r = 48, g = 93, b = 166}, 43 | {r = 73, g = 162, b = 160}, 44 | {r = 86, g = 170, b = 248}, 45 | {r = 133, g = 220, b = 243}, 46 | {r = 183, g = 155, b = 218}, 47 | {r = 129, g = 118, b = 153}, 48 | {r = 111, g = 80, b = 147}, 49 | {r = 39, g = 82, b = 88}, 50 | {r = 58, g = 133, b = 86}, 51 | {r = 79, g = 175, b = 92}, 52 | {r = 104, g = 225, b = 84}, 53 | {r = 165, g = 234, b = 95}, 54 | {r = 252, g = 237, b = 87}, 55 | {r = 242, g = 167, b = 59}, 56 | {r = 219, g = 114, b = 44}, 57 | } 58 | 59 | PALETTE = nil 60 | 61 | -- Prompt user for which platform they prefer 62 | function userInput() 63 | local dlg = Dialog() 64 | 65 | dlg:combobox{ 66 | id="platform", 67 | label="Which system palette to use? ", 68 | option="Pico-8", 69 | options={"Pico-8", "Picotron"}, 70 | } 71 | dlg:button{ id="ok", text="Select" } 72 | dlg:show() 73 | 74 | return dlg.data 75 | end 76 | 77 | function convertImage() 78 | -- Get the current image 79 | local img = app.activeCel.image 80 | 81 | -- Ensure image is RGBA 82 | if img.colorMode ~= ColorMode.RGB then 83 | local dlg = Dialog("Pico-8 Dithering") 84 | dlg:label{ label="Error:", text="The image must be in RGB color mode for the script to operate" } 85 | dlg:button{ text="OK" } 86 | dlg:show() 87 | return 88 | end 89 | 90 | -- Duplicate image into our buffer 91 | local copy = img:clone() 92 | 93 | -- Set specified palette 94 | local spr = app.activeSprite 95 | local pal = createPalette() 96 | spr:setPalette(pal) 97 | 98 | for y = 0, copy.height - 1 do 99 | for x = 0, copy.width - 1 do 100 | -- Iterate over every pixel, finding closest Pico-8 color 101 | local p = copy:getPixel(x, y) 102 | local old = createRgbTable(p) 103 | local closest = findClosestColor(old) 104 | local err = sub(old, closest) 105 | 106 | local packed = app.pixelColor.rgba(closest.r, closest.g, closest.b, 0xFF) 107 | copy:drawPixel(x, y, packed) 108 | 109 | -- Apply any error to neighboring pixels 110 | if (x + 1) < copy.width then 111 | applyError(copy, x + 1, y, err, 7.0 / 16.0) 112 | end 113 | 114 | if 0 <= (x - 1) and (y + 1) < copy.height then 115 | applyError(copy, x - 1, y + 1, err, 3.0 / 16.0) 116 | end 117 | 118 | if (y + 1) < copy.height then 119 | applyError(copy, x, y + 1, err, 5.0 / 16.0) 120 | end 121 | 122 | if (x + 1) < copy.width and (y + 1) < copy.height then 123 | applyError(copy, x + 1, y + 1, err, 1.0 / 16.0) 124 | end 125 | end 126 | end 127 | 128 | img:drawImage(copy) 129 | end 130 | 131 | -- Creates a new palette from the tables above 132 | function createPalette() 133 | local pal = Palette(#PALETTE) 134 | for i, v in pairs(PALETTE) do 135 | local color = app.pixelColor.rgba(v.r, v.g, v.b, 0xFF) 136 | pal:setColor(i - 1, color) 137 | end 138 | return pal 139 | end 140 | 141 | -- Uses Euclidean distance to find closest matching palette color 142 | function findClosestColor(p) 143 | local best_dist = 999999 144 | local best_idx = 0 145 | for k, v in pairs(PALETTE) do 146 | local dist = colorDist(p, v) 147 | if dist < best_dist then 148 | best_dist = dist 149 | best_idx = k 150 | end 151 | end 152 | return PALETTE[best_idx] 153 | end 154 | 155 | -- Calculates the square of Euclidean distance 156 | function colorDist(a, b) 157 | return ((a.r - b.r) ^ 2) + ((a.g - b.g) ^ 2) + ((a.b - b.b) ^ 2) 158 | end 159 | 160 | -- Converts 32-bit color value into Lua RGB table 161 | function createRgbTable(p) 162 | local r = app.pixelColor.rgbaR(p) 163 | local g = app.pixelColor.rgbaG(p) 164 | local b = app.pixelColor.rgbaB(p) 165 | 166 | return {r = r, g = g, b = b} 167 | end 168 | 169 | -- Applies Floyd-Steinberg dithering error to neighboring pixels 170 | function applyError(img, x, y, err, percent) 171 | local nr = img:getPixel(x, y) 172 | local n = createRgbTable(nr) 173 | local nc = add(n, mul(err, percent)) 174 | local np = app.pixelColor.rgba(clamp(nc.r), clamp(nc.g), clamp(nc.b), 0xFF) 175 | img:drawPixel(x, y, np) 176 | end 177 | 178 | -- Addition of two Lua RGB tables 179 | function add(a, b) 180 | return {r = a.r + b.r, g = a.g + b.g, b = a.b + b.b} 181 | end 182 | 183 | -- Subtraction of two Lua RGB tables 184 | function sub(a, b) 185 | return {r = a.r - b.r, g = a.g - b.g, b = a.b - b.b} 186 | end 187 | 188 | -- Multiplication of a Lua RGB table with a scalar 189 | function mul(a, val) 190 | return {r = a.r * val, g = a.g * val, b = a.b * val} 191 | end 192 | 193 | -- Clamps value between 0 and 255 194 | function clamp(v) 195 | if v < 0 then 196 | return 0 197 | elseif v > 0xFF then 198 | return 0xFF 199 | else 200 | return v 201 | end 202 | end 203 | 204 | do 205 | local palette = userInput() 206 | if palette.ok then 207 | local pal = palette.platform 208 | if pal == "Pico-8" then 209 | PALETTE = PICO8_PALETTE 210 | else 211 | PALETTE = PICOTRON_PALETTE 212 | end 213 | convertImage() 214 | end 215 | end 216 | --------------------------------------------------------------------------------