Exchanging one color for a different one

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
User avatar
leNoah
Prole
Posts: 14
Joined: Mon Feb 08, 2016 5:00 pm

Exchanging one color for a different one

Post by leNoah »

Basically what I want to do is have my images drawn in monochrome (say just black and white) and then change the two colors to custom values.

Currently the closest I can get is

Code: Select all

color1 = {252, 217, 63}
color2 = {2, 35, 132}
love.graphics.setColor(color1)
However this only works for one color, and only if it is white (which it is in my case)

So is there any way to exchange colors in this way?

Thanks in advance, Noah :)
--Home is where the heart is. Home is the ribcage.--
Santos
Party member
Posts: 384
Joined: Sat Oct 22, 2011 7:37 am

Re: Exchanging one color for a different one

Post by Santos »

Hello! :)

Here's one idea: try making two images for each image.

One image has the parts that will be one color and the other image has the parts that will be the other color.

Code: Select all

color1 = {252, 217, 63}
love.graphics.setColor(color1)
love.graphics.draw(image1, x, y)

color2 = {2, 35, 132}
love.graphics.setColor(color2)
love.graphics.draw(image2, x, y)
To make it easier to make the images, you could make the image in black and white and then use an ImageData object to take out the parts which are black and another ImageData object to take out the parts which are white and make the black bits white (so they can be colored with setColor), and then make Images from the ImageData objects.

I haven't tested this, but this is the basic idea:

Code: Select all

function love.load()
    black = love.image.newImageData('image.png')
    white = love.image.newImageData('image.png')

    black:mapPixel(function(x, y, r, g, b, a)
        if r == 255 and g == 255 and b == 255 and a == 255 then -- If the pixel is white...
            return 0, 0, 0, 0 --- ... make it invisible.
        elseif r == 0 and g == 0 and b == 0 and a == 255 then -- If the pixel is black...
            return 255, 255, 255, 0 --- ... make it white.
        else -- If the pixel is invisible, leave as is.
            return r, g, b, a
        end
    end)

    white:mapPixel(function(x, y, r, g, b, a)
        if r == 0 and g == 0 and b == 0 and a == 255 then -- If the pixel is black...
            return 0, 0, 0, 0 --- ... make it invisible.
        else
            return r, g, b, a
        end
    end)

    image1 = love.graphics.newImage(black)
    image2 = love.graphics.newImage(white)

    x = 100
    y = 100
end

function love.draw()
    color1 = {252, 217, 63}
    love.graphics.setColor(color1)
    love.graphics.draw(image1, x, y)

    color2 = {2, 35, 132}
    love.graphics.setColor(color2)
    love.graphics.draw(image2, x, y)
end
User avatar
leNoah
Prole
Posts: 14
Joined: Mon Feb 08, 2016 5:00 pm

Re: Exchanging one color for a different one

Post by leNoah »

Thanks for the response!
Yeah, I did consider this but wondered if there would be any built in way (I know you can do this with PICO-8 so I wondered if it was here too)

I wrote this function to avoid having to write the mapping everytime:

Code: Select all

function loadImage(path)
  blackLayer = love.image.newImageData(path)
  whiteLayer = love.image.newImageData(path)
    
  blackLayer:mapPixel(function(x, y, r, g, b, a)
    if r == 255 and g == 255 and b == 255 and a == 255 then
      return 0, 0, 0, 0 -- make the white pixels invisible
    elseif r == 0 and g == 0 and b == 0 and a == 255 then
        return 255 ,255, 255, 255 -- make the black pixels white
    else
      return r, g, b, a -- if it's not black or white (basically invisible cuz i won't add other colors) then just leave it
    end
  end)

  return love.graphics.newImage(whiteLayer), love.graphics.newImage(blackLayer)
end
Thanks, it works great now :)
--Home is where the heart is. Home is the ribcage.--
Xugro
Party member
Posts: 110
Joined: Wed Sep 29, 2010 8:14 pm

Re: Exchanging one color for a different one

Post by Xugro »

You can also use Blendmodes to change the colors. I attached a small example code with the replacement function. It takes an image and two colors and gives you a canvas back with the new image. But it is costly to use every frame, because it create three canvas every time.
Attachments
change_monochrome_color.love
(9.1 KiB) Downloaded 97 times
User avatar
Nixola
Inner party member
Posts: 1949
Joined: Tue Dec 06, 2011 7:11 pm
Location: Italy

Re: Exchanging one color for a different one

Post by Nixola »

Did no one mention Shaders yet?
lf = love.filesystem
ls = love.sound
la = love.audio
lp = love.physics
lt = love.thread
li = love.image
lg = love.graphics
User avatar
Nixola
Inner party member
Posts: 1949
Joined: Tue Dec 06, 2011 7:11 pm
Location: Italy

Re: Exchanging one color for a different one

Post by Nixola »

Sorry for the double post, but I think it's appropriate since four days have passed and I actually have time to provide an answer now.
This is a basic color substitution shader:

Code: Select all

  shader = love.graphics.newShader [[
    extern Image palette;

    vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords) { 
      vec4 tex_color = texture2D(texture, texture_coords);
      vec2 index = vec2(tex_color.r, 0); //get color based on red
      return texture2D(palette, index);
    }]]
It substitutes every pixel's colour by looking up a colour in a palette, using the red colour of the pixel to be substituted as x coordinate for the lookup; so, if the palette is 256x1, the amount of red in the image (0-255) directly translates to the x position of the colour in the palette.
If I had to write a shader for your specific use case, I'd just modify mine a bit:

Code: Select all

  shader = love.graphics.newShader [[
    extern Image palette;

    vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords) { 
      vec4 tex_color = texture2D(texture, texture_coords); //get the colour of the pixel which is being drawn
      vec2 index = vec2(tex_color.r, 0); //get pixel position on palette based on red
      vec4 new_color = texture2D(palette, index); //get colour of the pixel in the palette
      new_color.a = tex_color.a; //preserve the original alpha, only changing the rest of the colours;
      return new_color * color; //factor in the colour set with love.graphics.setColor
    }]]
In this case you'd need a 2x1 palette, with the two pixels being the two colours you need to draw (so, as for your example,{252, 217, 63} and {2, 35, 132}) and your monochrome image should only have pure red and pure black as colours. Transparency in your monochrome image is allowed, as the alpha channel is preserved. You should set the shader using love.graphics.setShader right before drawing your monochrome image, then re-set the default one after (by just calling the same function with no arguments), and you should use Shader:send just once, to actually provide the palette to the shader.
lf = love.filesystem
ls = love.sound
la = love.audio
lp = love.physics
lt = love.thread
li = love.image
lg = love.graphics
Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot] and 153 guests