Crosshair Studio

@itzchill / crosshair-studiov1.5.9Public

Overlay crosshair for games and apps that have none.

Files

reticle_png.luau3.9 KB

-- Runtime anti-aliased crosshair rasterizer. Returns one RGBA PNG string.
local png = require("png")
local SS = 2
local W, H = 160, 160
local HW, HH = W * SS, H * SS
local CENTER = W / 2
local SQRT2 = math.sqrt(2)

local function color(hex)
  local s = tostring(hex or "#ffffff"):gsub("#", "")
  if #s < 6 then return 255, 255, 255, 255 end
  local r = tonumber(s:sub(1, 2), 16) or 255
  local g = tonumber(s:sub(3, 4), 16) or 255
  local b = tonumber(s:sub(5, 6), 16) or 255
  local a = #s >= 8 and (tonumber(s:sub(7, 8), 16) or 255) or 255
  return r, g, b, a
end

local function blend(buf, i, r, g, b, a)
  local inv = 1 - a
  buf[i] = math.floor(r * a + (buf[i] or 0) * inv + 0.5)
  buf[i + 1] = math.floor(g * a + (buf[i + 1] or 0) * inv + 0.5)
  buf[i + 2] = math.floor(b * a + (buf[i + 2] or 0) * inv + 0.5)
  buf[i + 3] = math.floor(255 * a + (buf[i + 3] or 0) * inv + 0.5)
end

local function in_shape(shape, x, y, gap, length, thickness, dot, dot_shape, extra)
  local t = thickness + extra * 2
  local len = length + extra * 2
  local ax, ay = math.abs(x), math.abs(y)
  if shape == "plus" or shape == "t" then
    if ax <= t / 2 and ay >= gap and ay <= gap + len then
      return y < 0 or shape == "plus"
    end
    if shape == "t" and y > 0 then return false end
    if ay <= t / 2 and ax >= gap and ax <= gap + len then return true end
  elseif shape == "circle" then
    local radius = gap + len
    local r = math.sqrt(x * x + y * y)
    if r >= radius - t / 2 and r <= radius + t / 2 then return true end
  elseif shape == "x" then
    local d = math.abs(ax - ay) / SQRT2
    local along = math.max(ax, ay)
    if d <= t / 2 and along >= gap and along <= gap + len then return true end
  end
  if shape == "dot" then
    if dot <= 0 then return false end
    local size = dot + extra
    if dot_shape == "square" then return ax <= size / 2 and ay <= size / 2 end
    return x * x + y * y <= (size / 2) ^ 2
  end
  if shape ~= "dot" and dot > 0 then
    local size = dot + extra
    if dot_shape == "square" then
      if ax <= size / 2 and ay <= size / 2 then return true end
    elseif x * x + y * y <= (size / 2) ^ 2 then return true end
  end
  return false
end

local function draw_pass(buf, shape, gap, length, thickness, dot, dot_shape, extra, r, g, b, alpha)
  local a = alpha / 255
  for y = 0, HH - 1 do
    local ly = (y + 0.5) / SS - CENTER
    for x = 0, HW - 1 do
      if in_shape(shape, (x + 0.5) / SS - CENTER, ly, gap, length, thickness, dot, dot_shape, extra) then
        blend(buf, (y * HW + x) * 4 + 1, r, g, b, a)
      end
    end
  end
end

local function average(buf, x, y)
  local r, g, b, a = 0, 0, 0, 0
  for oy = 0, SS - 1 do
    for ox = 0, SS - 1 do
      local p = ((y * SS + oy) * HW + (x * SS + ox)) * 4 + 1
      r += buf[p] or 0; g += buf[p + 1] or 0; b += buf[p + 2] or 0; a += buf[p + 3] or 0
    end
  end
  local n = SS * SS
  return math.floor(r / n), math.floor(g / n), math.floor(b / n), math.floor(a / n)
end

return function(shape, main, outline_col, outline, glow, gap, length, thickness, dot, dot_shape, opacity)
  local cr, cg, cb, ca = color(main)
  local orr, og, ob, oa = color(outline_col)
  local buf = {}
  local op = math.max(0, math.min(1, tonumber(opacity) or 1))
  if glow > 0 then
    draw_pass(buf, shape, gap, length, thickness, dot, dot_shape, outline + glow,
      cr, cg, cb, math.floor(ca * 0.16 * op))
  end
  if outline > 0 then
    draw_pass(buf, shape, gap, length, thickness, dot, dot_shape, outline,
      orr, og, ob, math.floor(oa * op))
  end
  draw_pass(buf, shape, gap, length, thickness, dot, dot_shape, 0,
    cr, cg, cb, math.floor(ca * op))
  local out = {}
  for y = 0, H - 1 do
    for x = 0, W - 1 do
      local r, g, b, a = average(buf, x, y)
      out[#out + 1] = string.char(r, g, b, a)
    end
  end
  return png(W, H, table.concat(out))
end

136 files, 165.2 KB. 130 have no text preview.