Difference between revisions of "Gradients"

(Better version with Mesh)
(More fix)
 
(2 intermediate revisions by 2 users not shown)
Line 105: Line 105:
 
function love.load()
 
function love.load()
 
     rainbow = gradientMesh("horizontal",
 
     rainbow = gradientMesh("horizontal",
         {255, 0, 0};
+
         {255, 0, 0},
         {255, 255, 0};
+
         {255, 255, 0},
         {0, 255, 0};
+
         {0, 255, 0},
         {0, 255, 255};
+
         {0, 255, 255},
         {0, 0, 255};
+
         {0, 0, 255},
         {255, 0, 0};
+
         {255, 0, 0}
 
     )
 
     )
 
end
 
end
Line 123: Line 123:
  
 
local rainbow = gradient {
 
local rainbow = gradient {
     direction = 'horizontal';
+
     direction = 'horizontal',
     {255, 0, 0};
+
     {255, 0, 0},
     {255, 255, 0};
+
     {255, 255, 0},
     {0, 255, 0};
+
     {0, 255, 0},
     {0, 255, 255};
+
     {0, 255, 255},
     {0, 0, 255};
+
     {0, 0, 255},
     {255, 0, 0};
+
     {255, 0, 0}
 
}
 
}
  
 
function love.draw()
 
function love.draw()
     drawinrect(rainbow, love.graphics.getWidth() / 2 - 50, love.graphics.getHeight() / 2 - 50, 100)
+
     drawinrect(rainbow, love.graphics.getWidth() / 2 - 50, love.graphics.getHeight() / 2 - 50, 100, 100)
 
end
 
end
 
</source>
 
</source>
Line 141: Line 141:
  
 
local greyscale = gradient {
 
local greyscale = gradient {
     direction = 'vertical';
+
     direction = 'vertical',
     {0, 0, 0};
+
     {0, 0, 0},
     {255, 255, 255};
+
     {255, 255, 255}
 
}
 
}
  

Latest revision as of 15:47, 24 April 2024

gradient (Mesh variant)

Synopsis

mesh = gradientMesh( direction, color1, color2, ... )

Creates a gradient object (actually Mesh) from a table of colors to be evenly spaced throughout the gradient. This is better version of gradient function below.

Arguments

string direction
Gradient direction (horizontal or vertical).
table color1
First color table.
table color2
Second color table.
table ...
Additional color table.

Returns

Mesh mesh
The gradient object.

Source

-- Color multipler
local COLOR_MUL = love._version >= "11.0" and 1 or 255

function gradientMesh(dir, ...)
    -- Check for direction
    local isHorizontal = true
    if dir == "vertical" then
        isHorizontal = false
    elseif dir ~= "horizontal" then
        error("bad argument #1 to 'gradient' (invalid value)", 2)
    end

    -- Check for colors
    local colorLen = select("#", ...)
    if colorLen < 2 then
        error("color list is less than two", 2)
    end

    -- Generate mesh
    local meshData = {}
    if isHorizontal then
        for i = 1, colorLen do
            local color = select(i, ...)
            local x = (i - 1) / (colorLen - 1)

            meshData[#meshData + 1] = {x, 1, x, 1, color[1], color[2], color[3], color[4] or (1 * COLOR_MUL)}
            meshData[#meshData + 1] = {x, 0, x, 0, color[1], color[2], color[3], color[4] or (1 * COLOR_MUL)}
        end
    else
        for i = 1, colorLen do
            local color = select(i, ...)
            local y = (i - 1) / (colorLen - 1)

            meshData[#meshData + 1] = {1, y, 1, y, color[1], color[2], color[3], color[4] or (1 * COLOR_MUL)}
            meshData[#meshData + 1] = {0, y, 0, y, color[1], color[2], color[3], color[4] or (1 * COLOR_MUL)}
        end
    end

    -- Resulting Mesh has 1x1 image size
    return love.graphics.newMesh(meshData, "strip", "static")
end

Notes

The resulting Mesh has dimensions of 1x1. Scaling factor in love.graphics.draw determines the size of the gradient. drawinrect function below is not needed (can't be used really) when using this variant.

gradient (Image variant)

Synopsis

gradient {color1, color2, color3, ..., direction = 'horizontal' or 'vertical'}

Creates a gradient object (really just an Image) from a table of colors to be evenly spaced throughout the gradient, in the direction specified by the "direction" key of the parameter table.

Source

function gradient(colors)
    local direction = colors.direction or "horizontal"
    if direction == "horizontal" then
        direction = true
    elseif direction == "vertical" then
        direction = false
    else
        error("Invalid direction '" .. tostring(direction) .. "' for gradient.  Horizontal or vertical expected.")
    end
    local result = love.image.newImageData(direction and 1 or #colors, direction and #colors or 1)
    for i, color in ipairs(colors) do
        local x, y
        if direction then
            x, y = 0, i - 1
        else
            x, y = i - 1, 0
        end
        result:setPixel(x, y, color[1], color[2], color[3], color[4] or 255)
    end
    result = love.graphics.newImage(result)
    result:setFilter('linear', 'linear')
    return result
end

drawinrect

Synopsis

drawinrect(img, x, y, w, h, r, ox, oy, kx, ky)

A convenience function to draw scaled images in a rectangle of absolute size (rather than with a size relative to the size of the image, which is what love.graphics.draw() does). Useful for gradients, because you will almost always want to draw them scaled, and you don't want their bounds to change if the number of colors does.

Source

function drawinrect(img, x, y, w, h, r, ox, oy, kx, ky)
    return -- tail call for a little extra bit of efficiency
    love.graphics.draw(img, x, y, r, w / img:getWidth(), h / img:getHeight(), ox, oy, kx, ky)
end

Examples

drawing horizontal rainbow that fills the entire screen using Mesh variant

-- assume you already have gradientMesh function
local rainbow

function love.load()
    rainbow = gradientMesh("horizontal",
        {255, 0, 0},
        {255, 255, 0},
        {0, 255, 0},
        {0, 255, 255},
        {0, 0, 255},
        {255, 0, 0}
    )
end

function love.draw()
    love.graphics.draw(rainbow, 0, 0, 0, love.graphics.getDimensions())
end

draw a 100x100 horizontal rainbow (that is, red on top, purple on bottom) in the center of the screen

require "gradient"

local rainbow = gradient {
    direction = 'horizontal',
    {255, 0, 0},
    {255, 255, 0},
    {0, 255, 0},
    {0, 255, 255},
    {0, 0, 255},
    {255, 0, 0}
}

function love.draw()
    drawinrect(rainbow, love.graphics.getWidth() / 2 - 50, love.graphics.getHeight() / 2 - 50, 100, 100)
end

draw a vertical greyscale spectrum (that is, black on left, white on right) filling the whole screen

require "gradient"

local greyscale = gradient {
    direction = 'vertical',
    {0, 0, 0},
    {255, 255, 255}
}

function love.draw()
    drawinrect(greyscale, 0, 0, love.graphics.getWidth(), love.graphics.getHeight())
end