Skip to content
This repository has been archived by the owner on May 6, 2024. It is now read-only.

Commit

Permalink
Transparency blending
Browse files Browse the repository at this point in the history
  • Loading branch information
cxmeel committed May 29, 2022
1 parent af9dfd1 commit b932ad8
Show file tree
Hide file tree
Showing 9 changed files with 160 additions and 10 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## [1.3.1]

- Implemented transparency blending:
- This returns a new Color3 that acts as if it was overlaid on a background color with a given alpha.
- Added `Hex.fromHexRGBA`, which converts a hex string with an alpha channel to a Color3.

## [1.3.0]

### Added
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@rbxts/colour-utils",
"version": "1.3.0",
"version": "1.3.1",
"description": "Colour manipulation utility for Roblox",
"homepage": "https://github.com/csqrl/colour-utils",
"main": "src/init.lua",
Expand Down
20 changes: 20 additions & 0 deletions src/Blend/Transparency.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
--[=[
@function Transparency
@within Blend
@param background Color3 -- The background colour.
@param foreground Color3 -- The foreground colour.
@param transparency number -- The transparency value.
@return Color3 -- The resulting colour.
]=]
local function applyTransparency(background: Color3, foreground: Color3, transparency: number): Color3
local alpha = 1 - transparency

local red = foreground.R * alpha + background.R * transparency
local green = foreground.G * alpha + background.G * transparency
local blue = foreground.B * alpha + background.B * transparency

return Color3.new(red, green, blue)
end

return applyTransparency
41 changes: 41 additions & 0 deletions src/Blend/Transparency.spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
local BasicallyIdentical = require(script.Parent.Parent._Util.BasicallyIdentical)

return function()
local Filter = require(script.Parent.Transparency)

local FOREGROUND = Color3.fromHex("#FF6B00")
local BACKGROUND = Color3.fromHex("#252525")

local RESULTS = {
[0] = FOREGROUND,
[13] = Color3.fromHex("#E36204"),
[50] = Color3.fromHex("#914712"),
[94] = Color3.fromHex("#322922"),
[100] = Color3.fromHex("#252525"),
}

it("throws if arguments are incorrect types", function()
expect(function()
Filter(true)
end).to.throw()

expect(function()
Filter(BACKGROUND, true)
end).to.throw()

expect(function()
Filter(BACKGROUND, FOREGROUND, true)
end).to.throw()
end)

for transparencyPercent, result in pairs(RESULTS) do
local testName = string.format("correctly applies filter with transparency %d%%", transparencyPercent)

it(testName, function()
local transparency = transparencyPercent / 100
local filterResult = Filter(BACKGROUND, FOREGROUND, transparency)

expect(BasicallyIdentical(result, filterResult)).to.equal(true)
end)
end
end
1 change: 1 addition & 0 deletions src/Blend/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ return {
Overlay = require(script.Overlay),
Dodge = require(script.Dodge),
Burn = require(script.Burn),
Transparency = require(script.Transparency),
}
39 changes: 39 additions & 0 deletions src/Hex/init.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
local Assert = require(script.Parent._Util.Assert)
local ClampColour = require(script.Parent._Util.ClampColour)
local Transparency = require(script.Parent.Blend.Transparency)

local sub = string.sub
local gsub = string.gsub
Expand All @@ -9,6 +10,7 @@ local split = string.split

local HEX_EXCLUDE_PATTERN = "[^A-Fa-f0-9]"
local HEX_FORMAT_PATTERN = "%.2x%.2x%.2x"
local BACKGROUND_BASE_COLOUR = Color3.new()

--[=[
@function fromHex
Expand Down Expand Up @@ -50,6 +52,42 @@ local function FromHex(hex: string): Color3
return ClampColour(Color3.fromRGB(red, green, blue))
end

--[=[
@function fromHexRGBA
@within Hex
Creates a Color3 from a hex string with an alpha value. The background
doesn't need to be specified, but the resulting Color3 will vary
depending on the colour of the background, so it's recommended to
specify a background unless `Color3.new()` is what you want.
If the hex string is less than 8 characters, it will be passed to
`fromHex` and the resulting Color3 will be returned without transparency
applied.
Hex strings longer than 8 characters will be truncated to 8 characters
will be accepted. If the hex string is longer than 8 characters, the
last two characters will be used as the alpha value.
@param hex string -- The hex string to convert.
@param background Color3? -- The background colour (defaults to black).
@return Color3 -- The resulting Color3.
]=]
local function FromHexRGBA(hex: string, background: Color3?): Color3
Assert.typeOf("FromHexRGBA", "hex", "string", hex)

hex = gsub(hex, HEX_EXCLUDE_PATTERN, "")

if #hex < 8 then
return FromHex(hex)
end

local transparency = 1 - (tonumber(sub(hex, -2), 16) / 255)
local colour = FromHex(sub(hex, 1, -3))

return Transparency(colour, background or BACKGROUND_BASE_COLOUR, transparency)
end

--[=[
@function toHex
@within Hex
Expand Down Expand Up @@ -81,5 +119,6 @@ end
]=]
return {
fromHex = FromHex,
fromHexRGBA = FromHexRGBA,
toHex = ToHex,
}
25 changes: 25 additions & 0 deletions src/Hex/init.spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,29 @@ return function()
expect(BasicallyIdentical(Color3.new(), Hex.fromHex(""))).to.equal(true)
end)
end)

describe("fromHexRGBA(...)", function()
it("throws if argument is not a string", function()
expect(pcall(Hex.fromHexRGBA, true)).to.equal(false)
end)

it("converts an alpha hex code to Color3", function()
-- white fg, transparency 50%, black bg
expect(BasicallyIdentical(Color3.fromHex("#808080"), Hex.fromHexRGBA("#FFFFFF80"))).to.equal(true)

-- blue fg, transparency 50%, white bg
expect(BasicallyIdentical(Color3.fromHex("#80D1FF"), Hex.fromHexRGBA("#00A2FF80", Color3.new(1, 1, 1)))).to.equal(
true
)

-- blue fg, transparency 50%, black bg
expect(BasicallyIdentical(Color3.fromHex("#005180"), Hex.fromHexRGBA("#00A2FF80"))).to.equal(true)

-- blue fg, transparency 0%, black bg
expect(BasicallyIdentical(Color3.fromHex("#00A2FF"), Hex.fromHexRGBA("#00A2FF00"))).to.equal(true)

-- blue fg, transparency 100%, black bg
expect(BasicallyIdentical(Color3.new(), Hex.fromHexRGBA("#00A2FFFF"))).to.equal(true)
end)
end)
end
34 changes: 26 additions & 8 deletions typings/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,14 +116,19 @@ declare namespace ColourUtils {

namespace Hex {
/**
* Converts a hex string into a Color3. This method accepts hex strings of any length (but will only respect the first 6 characters; with or without a preceding hash (#)
* Converts a hex string into a Color3. This method accepts hex strings of any length (but will only respect the first 6 characters); with or without a preceding hash (#)
* @param {string} hex - A hex colour string
* @returns {Color3}
*/
function fromHex(hex: string): Color3

/**
* Converts a Color3 into a hex value. Note that this method does not prepend a hash (#) to the beginning of the string
* Converts an RGBA hex string into a Color3. This method accepts hex strings of any length (the last two characters will be treated as the alpha value); with or without a preceding hash (#)
* @param colour
*/

/**
* Converts a Color3 into a hex value. Note that this method does not prepend a hash (#) to the beginning of the string, and the result is always lowercase
* @param {Color3} colour - A Color3 to convert to hex
* @returns {string}
*/
Expand Down Expand Up @@ -240,7 +245,7 @@ declare namespace ColourUtils {

/**
* Simulate colour blindness on a Color3
* @param {Color3} colour- The Color3 to simulate colour blindness on
* @param {Color3} colour - The Color3 to simulate colour blindness on
* @param {number} blinder - The type of colour blindness to simulate
* @returns {Color3} A resulting Color3 from the simulation
*/
Expand All @@ -250,43 +255,56 @@ declare namespace ColourUtils {
namespace Blend {
/**
* Apply burn blend to two Color3s
* @param {Color3} background- The bottom Color3 to apply blend to
* @param {Color3} background - The bottom Color3 to apply blend to
* @param {Color3} foreground - The top Color3 to apply blend to
* @returns {Color3} A resulting Color3 from applying the blend
*/
function Burn(background: Color3, foreground: Color3): Color3

/**
* Apply dodge blend to two Color3s
* @param {Color3} background- The bottom Color3 to apply blend to
* @param {Color3} background - The bottom Color3 to apply blend to
* @param {Color3} foreground - The top Color3 to apply blend to
* @returns {Color3} A resulting Color3 from applying the blend
*/
function Dodge(background: Color3, foreground: Color3): Color3

/**
* Apply multiply blend to two Color3s
* @param {Color3} background- The bottom Color3 to apply blend to
* @param {Color3} background - The bottom Color3 to apply blend to
* @param {Color3} foreground - The top Color3 to apply blend to
* @returns {Color3} A resulting Color3 from applying the blend
*/
function Multiply(background: Color3, foreground: Color3): Color3

/**
* Apply overlay blend to two Color3s
* @param {Color3} background- The bottom Color3 to apply blend to
* @param {Color3} background - The bottom Color3 to apply blend to
* @param {Color3} foreground - The top Color3 to apply blend to
* @returns {Color3} A resulting Color3 from applying the blend
*/
function Overlay(background: Color3, foreground: Color3): Color3

/**
* Apply screen blend to two Color3s
* @param {Color3} background- The bottom Color3 to apply blend to
* @param {Color3} background - The bottom Color3 to apply blend to
* @param {Color3} foreground - The top Color3 to apply blend to
* @returns {Color3} A resulting Color3 from applying the blend
*/
function Screen(background: Color3, foreground: Color3): Color3

/**
* Apply transparency to a Color3, based on the background colour
* @param {Color3} background - The bottom Color3 to apply blend to
* @param {Color3} foreground - The top Color3 to apply blend to
* @param {number} transparency - The transparency to apply (0-1)
* @returns {Color3} A resulting Color3 from applying the blend
*/
function Transparency(
background: Color3,
foreground: Color3,
transparency: number
): Color3
}

namespace Palette {
Expand Down
2 changes: 1 addition & 1 deletion wally.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "csqrl/colour-utils"
description = "Colour manipulation utility for Roblox"
version = "1.3.0"
version = "1.3.1"
license = "MIT"
authors = ["csqrl (https://github.com/csqrl)"]
registry = "https://github.com/UpliftGames/wally-index"
Expand Down

0 comments on commit b932ad8

Please sign in to comment.