├── Grassify.lua └── README.md /Grassify.lua: -------------------------------------------------------------------------------- 1 | --[[------------------------------------------------------------------------------- 2 | Aseprite script that is intended for making pixel grass out of whatever is on the 3 | current layer or selection 4 | 5 | By Thoof (@Thoof4 on twitter) 6 | -------------------------------------------------------------------------------]]-- 7 | 8 | local dlg = Dialog { title = "Grassify options" } 9 | 10 | local function grassify(image) 11 | local new_image = Image(app.activeSprite.width, app.activeSprite.height, image.colorMode) 12 | 13 | 14 | local selection = app.activeSprite.selection 15 | local offset = selection.bounds 16 | 17 | local image_offset = app.activeCel.bounds 18 | 19 | local image_selection_offset = Point(offset.x - image_offset.x, offset.y - image_offset.y) 20 | 21 | new_image:drawImage(image, Point(app.activeCel.bounds.x, app.activeCel.bounds.y)) 22 | 23 | 24 | 25 | local max_grass_length = dlg.data.grass_length 26 | local grass_coverage = dlg.data.grass_amount 27 | local randomize_grass = dlg.data.rand_length 28 | 29 | local y_offset = 1 30 | 31 | for x = 0, new_image.width - 1, 2 do 32 | 33 | for y = 0, new_image.height - 1, y_offset do 34 | local invalid_pixel = (y - y_offset) >= new_image.height or (y - y_offset) < 0 35 | 36 | if (selection.isEmpty or selection:contains(Point(x, y))) and invalid_pixel == false then 37 | local pixel = new_image:getPixel(x, y) 38 | local next_pixel = new_image:getPixel(x, y - y_offset) 39 | 40 | local rand_coverage = math.random(0, 100) 41 | 42 | local length = max_grass_length 43 | if randomize_grass then 44 | length = math.random(1, max_grass_length) 45 | end 46 | 47 | local pixel_is_transparent = (app.pixelColor.rgbaA(pixel) == 0) 48 | if image.colorMode == ColorMode.INDEXED then 49 | pixel_is_transparent = (pixel == app.activeSprite.spec.transparentColor) 50 | elseif image.colorMode == ColorMode.GRAY then 51 | pixel_is_transparent = (app.pixelColor.grayaA(pixel) == 0) 52 | end 53 | 54 | -- If we've found a spot where different colors occur 55 | if rand_coverage < grass_coverage and pixel_is_transparent == false and pixel ~= next_pixel then 56 | 57 | local y_value = y - y_offset 58 | local left_pixel = new_image:getPixel(x - 1, y_value) 59 | local right_pixel = new_image:getPixel(x + 1, y_value) 60 | 61 | local grass_length = 0 62 | 63 | while grass_length < length-1 or (left_pixel == pixel or right_pixel == pixel) do 64 | 65 | new_image:drawPixel(x, y_value, pixel) 66 | y_value = y_value - y_offset 67 | 68 | left_pixel = new_image:getPixel(x - 1, y_value) 69 | right_pixel = new_image:getPixel(x + 1, y_value) 70 | 71 | grass_length = grass_length + 1 72 | end 73 | 74 | 75 | new_image:drawPixel(x, y_value, pixel) 76 | end 77 | end 78 | end 79 | end 80 | 81 | y_offset = -1 82 | 83 | for x = 1, new_image.width, 2 do 84 | 85 | for y = new_image.height - 1, 0, y_offset do 86 | local invalid_pixel = (y - y_offset) >= new_image.height or (y - y_offset) < 0 87 | 88 | if (selection.isEmpty or selection:contains(Point(x, y))) and invalid_pixel == false then 89 | local pixel = new_image:getPixel(x, y) 90 | local next_pixel = new_image:getPixel(x, y - y_offset) 91 | 92 | local rand_coverage = math.random(0, 100) 93 | 94 | local length = max_grass_length 95 | if randomize_grass then 96 | length = math.random(1, max_grass_length) 97 | end 98 | 99 | local pixel_is_transparent = app.pixelColor.rgbaA(pixel) == 0 100 | if image.colorMode == ColorMode.INDEXED then 101 | pixel_is_transparent = (pixel == app.activeSprite.spec.transparentColor) 102 | elseif image.colorMode == ColorMode.GRAY then 103 | pixel_is_transparent = (app.pixelColor.grayaV(pixel) == 0) 104 | end 105 | 106 | local next_pixel_is_transparent = (app.pixelColor.rgbaA(next_pixel) == 0) 107 | if image.colorMode == ColorMode.INDEXED then 108 | next_pixel_is_transparent = (next_pixel == app.activeSprite.spec.transparentColor) 109 | elseif image.colorMode == ColorMode.GRAY then 110 | next_pixel_is_transparent = (app.pixelColor.grayaA(next_pixel) == 0) 111 | end 112 | 113 | 114 | 115 | -- If we've found a spot where different colors occur. For this second loop we only need to do it when the next 116 | -- pixel is transparent 117 | if rand_coverage < grass_coverage and pixel_is_transparent == false and pixel ~= next_pixel and next_pixel_is_transparent == true then 118 | local y_value = y - y_offset 119 | local left_pixel = new_image:getPixel(x - 1, y_value) 120 | local right_pixel = new_image:getPixel(x + 1, y_value) 121 | 122 | local grass_length = 0 123 | 124 | while grass_length < length - 1 or (left_pixel == pixel or right_pixel == pixel) do 125 | new_image:drawPixel(x, y_value, pixel) 126 | y_value = y_value - y_offset 127 | 128 | left_pixel = new_image:getPixel(x - 1, y_value) 129 | right_pixel = new_image:getPixel(x + 1, y_value) 130 | 131 | grass_length = grass_length + 1 132 | end 133 | 134 | 135 | new_image:drawPixel(x, y_value, pixel) 136 | end 137 | end 138 | end 139 | end 140 | 141 | 142 | 143 | return new_image 144 | end 145 | 146 | dlg:slider { 147 | id = "grass_amount", 148 | label = "Grass coverage (%): ", 149 | min = 1, 150 | max = 100, 151 | value = 100, 152 | onchange = function() 153 | 154 | end 155 | } 156 | 157 | dlg:slider { 158 | id = "grass_length", 159 | label = "Grass length (px)", 160 | min = 1, 161 | max = 5, 162 | value = 1, 163 | onchange = function() 164 | 165 | end 166 | } 167 | 168 | dlg:check { 169 | id = "rand_length", 170 | label = string, 171 | text = "Randomize length", 172 | selected = boolean, 173 | onclick = function() 174 | end 175 | } 176 | 177 | dlg:button { 178 | id = "grassify", 179 | text = "Grassify", 180 | onclick = function() 181 | app.transaction( 182 | function() 183 | app.activeCel.image = grassify(app.activeCel.image) 184 | app.activeCel.position = Point(0, 0) 185 | app.refresh() 186 | end) 187 | 188 | end 189 | } 190 | 191 | 192 | dlg:show { 193 | wait = false 194 | } 195 | 196 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # aseprite-grassify 2 | 3 | ![ezgif com-gif-maker(2)](https://user-images.githubusercontent.com/5313706/157093185-1cee4a14-3bd0-43ba-9c2e-281d86fc1251.gif) 4 | 5 | A small aseprite script that converts the current layer/selection to a grasslike texture. 6 | 7 | HOW TO USE 8 | 9 | In aseprite, go File->Scripts->Open scripts folder 10 | Download the zip file. Put Grassify.lua into the scripts folder. 11 | File->Scripts->Rescan scripts folder 12 | File->Scripts->Grassify 13 | Should be good to go! 14 | 15 | Grass coverage: The amount of grass that appears along the edge of a pixel cluster 16 | 17 | Grass length: Length of the grass in pixels 18 | 19 | Randomize length: Select this to pick random grass length between 1 and the length specified in the grass length slider 20 | 21 | 22 | 23 | --------------------------------------------------------------------------------