
Palette Swap On Löve 2D
Without Using Shaders
Date:Changing the color of an image can be a very useful way to reuse a resource
without increasing the disk size of the game. It is possible to change the color
of images using the capabilities of ImageData
to access the pixels
directly, either to read the colors or to change them.

The algorithm consists of loading our image into memory as an object of
type ImageData
, which we will not modify and will only use as an
"map" to know which color we are supposed to use, which is why we will
call it image_map
.
We also create an image of the same dimensions where we will write all the
changes we make, and we will call this image_data
1

We need to load an image that contains the palettes to be used,
which will be palette_data
, where each horizontal strip one pixel
high is a palette:

lua
image_map = love.image.newImageData('ima.png')
image_data = love.image.newImageData( image_map:getWidth(), image_map:getHeight())
palette_data = love.image.newImageData('palette.png')
We must also calculate the maximum number of palettes
(max_palettes
) that we can use, which will be equal to
the vertical size of the image where we store the palettes.
lua
max_palettes = palette_data:getHeight()
Now we create a table where we will see which color in the first row of the
palette image corresponds to each column that makes up the first palette.
We will call this table look_up_color_table
. We will create a
string that will serve as a id
to quickly identify each color.
lua
look_up_color_table = {}
local col = palette_data:getWidth()
local i = 0
while i < col do
local r,g,b,a = palette_data:getPixel(i,0)
-- The id is created using a hexadecimal-like value
-- El id es creado usando un valor similar al hexadecimal
local id = ("%X_%X_%X_%X"):format(
math.floor((o_r)*255),
math.floor((o_g)*255),
math.floor((o_b)*255),
math.floor((o_a)*255)
)
look_up_color_table[id] = i
i=i+1
end
We set the palette we want to use and change the colors of image_data
.
lua
use_palette = 1
changePalete()
What happens inside the function changePalete()
,
is that we call the mapPixel()
method of the ImageData
object. mapPixel()
allows us to send a function that will be
executed for each of the pixels that make up the object, in this case,
the function with which we change the colors.
Then, since objects of type ImageData
cannot be drawn,
we create an image to be able to draw on the screen, and we tell it that we want it
to be drawn sharply.
lua
function changePalete()
image_data:mapPixel(changeColors)
image = love.graphics.newImage(image_data)
image:setFilter('nearest','nearest')
end
Now, it is in the function changeColors()
where everything happens.

First, we receive the coordinates (x,y) of the color we want to change,
we look up what color it is in image_map
with the method
getPixel(x,y)
, then we transform that color into
an id
, with which we access the table look_up_color_table
,
which returns us a column number. With the number of use_palette
which is equal to the row number, and already knowing the column,
we take that color from our palette, and return it to be placed into the
coordinates (x,y) in image_data
lua
function changeColors(x, y, r,g,b,a)
local o_r,o_g,o_b,o_a = image_map:getPixel(x,y)
local id = ("%X_%X_%X_%X"):format(
math.floor((o_r)*255),
math.floor((o_g)*255),
math.floor((o_b)*255),
math.floor((o_a)*255)
)
--check if the color exist on the table
if look_up_color_table[id] then
--if exist, then get the color on the palette
local col = look_up_color_table[id]
local row = use_palette-1
return palette_data:getPixel(col,row)
end
--else, do nothing, return the original color
return r, g, b, a
end
Each time we change the palette of the image, we only need to set which is the corresponding row and change it.
lua
use_palette = 3
changePalete()
Note, a disadvantage of this method without shaders is the fact that changing the palettes, the larger the area to be changed, it will be exponentially slower
That would be all, you can download the file with the source code .zip right here and check it out for yourself. You can use the arrows to switch between palettes.