
Key Required
Auto Draw Open Source
Lua Script•395 lines (12235 chars)
-- This single-file Luau library implements read/write support for the
-- `Windows Bitmap`/`device-independent bitmap` file format version 3.0.
-- License: MIT
-- offsets into a Windows Bitmap file header
local bmp_header_offset = 0
local bmp_header_filesize = 2
local bmp_header_pixel_offset = 10
local bmp_header_size = 14
local bmp_header_width = 18
local bmp_header_height = 22
local bmp_header_planes = 26
local bmp_header_bpp = 28
local bmp_header_compression = 30
local bmp_header_image_size = 34
local bmp_header_bitmask_r = 54
local bmp_header_bitmask_g = 58
local bmp_header_bitmask_b = 62
local bmp_header_bitmask_a = 66
local function get_data_size(width: number, height: number, bpp: number): number
local line_w = math.ceil(width/4)*4
return height*line_w*(bpp/8)
end
local function new_bitmap()
-- this table is returned as an interface for a Windows Bitmap to the user:
local bmp = {}
-- reading 8/16/32-bit little-endian integer values from a string
function bmp:read(offset: number): number -- read uint8
offset = math.floor(offset)
local value = self.data[offset]:byte()
return value
end
function bmp:read_word(offset: number): number -- read uint16
return self:read(offset+1)*0x100 + self:read(offset)
end
function bmp:read_dword(offset: number): number -- read uint32
return self:read(offset+3)*0x1000000 + self:read(offset+2)*0x10000 + self:read(offset+1)*0x100 + self:read(offset)
end
function bmp:read_long(offset: number): number -- read int32
local value = self:read_dword(offset)
if value >= 0x80000000 then -- bitmap format uses two's complement
value = -(value - 0x80000000)
end
return value
end
-- writing 8/16/32-bit little-endian integer values from a string
function bmp:write(offset: number, data: string) -- write uint8*
offset = math.floor(offset)
for i=1, #data do
local index = offset+i-1
local value = data:sub(i,i)
self.data[index] = value -- only update, don't create new entry
end
end
function bmp:write_word(offset: number, value: number) -- write uint16
local a = math.floor(value % 0x100)
local b = math.floor(value / 0x100)
local data = string.char(a,b)
self:write(offset, data)
end
function bmp:write_dword(offset: number, value: number) -- write uint32
local a = math.floor(value) % 0x100
local b = math.floor(value / 0x100) % 0x100
local c = math.floor(value / 0x10000) % 0x100
local d = math.floor(value / 0x1000000) % 0x100
local data = string.char(a,b,c,d)
self:write(offset, data)
end
function bmp:write_long(offset: number, value: number)
if value < 0 then
value = -value + 0x80000000
end
self:write_dword(offset, value)
end
-- read bitmap headers and parse required metadata, update self
function bmp:read_header(): (boolean, string?)
-- check the bitmap header
if self:read_word(bmp_header_offset) ~= 0x4D42 then
return false, "Bitmap magic header not found"
end
local compression = self:read_dword(bmp_header_compression)
if compression == 3 then -- BI_BITFIELDS
-- For 16bpp and 32bpp with bitfields compression
-- We'll assume standard RGBA bitmasks if not specified
self.bitmask_r = self:read_dword(bmp_header_bitmask_r)
self.bitmask_g = self:read_dword(bmp_header_bitmask_g)
self.bitmask_b = self:read_dword(bmp_header_bitmask_b)
self.bitmask_a = self:read_dword(bmp_header_bitmask_a)
elseif compression ~= 0 then -- BI_RGB
return false, "Only uncompressed or BI_BITFIELDS bitmaps supported. Is: "..tostring(compression)
end
-- get bits per pixel from the bitmap header
self.bpp = self:read_word(bmp_header_bpp)
if not (self.bpp == 24 or self.bpp == 32 or (compression == 3 and (self.bpp == 16 or self.bpp == 32))) then
return false, "Only 24bpp/32bpp bitmaps supported (or 16bpp with BI_BITFIELDS). Is: "..tostring(self.bpp)
end
-- get other required info from the bitmap header
self.pixel_offset = self:read_dword(bmp_header_pixel_offset)
self.width = self:read_long(bmp_header_width)
self.height = self:read_long(bmp_header_height)
-- calculate expected size of the data region
self.data_size = get_data_size(self.width, self.height, self.bpp)
-- if height is <0, the image data is in topdown format
self.topdown = true
if self.height < 0 then
self.topdown = false
self.height = -self.height
end
return true
end
-- write bitmap headers from self
function bmp:write_header(width: number, height: number, bpp: number): (boolean, string?)
if (width < 0) or (height < 0) or (width >= 2^31) or (height >= 2^31) then
return false, "Invalid dimensions"
end
if not (bpp == 24 or bpp == 32) then
return false, "Invalid bpp"
end
-- update expected data size
self.data_size = get_data_size(width, height, bpp)
-- Bitmap header
self:write_word(bmp_header_offset, 0x4D42)
self:write_dword(bmp_header_filesize, 54+self.data_size)
self:write_dword(bmp_header_size, 40)
self:write_word(bmp_header_planes, 1)
-- image information
self:write_dword(bmp_header_compression, 0) -- BI_RGB
self:write_word(bmp_header_bpp, bpp)
self:write_dword(bmp_header_pixel_offset, self.pixel_offset)
self:write_long(bmp_header_width, width)
self:write_dword(bmp_header_image_size, self.data_size)
if self.topdown then
self:write_long(bmp_header_height, height)
else
self:write_long(bmp_header_height, -height)
end
-- set all internal values accordingly
return self:read_header()
end
-- return the r,g,b,a[0-255] color value for a pixel by its x,y coordinates
function bmp:get_pixel(x: number, y: number): (number?, number?, number?, number?, string?)
if (x < 0) or (x >= self.width) or (y < 0) or (y >= self.height) then
return nil, nil, nil, nil, "Out of bounds"
end
-- calculate byte offset in data
local Bpp = self.bpp/8
local row_len = self.width * Bpp -- length of pixel data in bytes
local row_stride = math.ceil(row_len/4)*4 -- padded length of a row in bytes
local index = self.pixel_offset + y*row_stride + x*Bpp
if self.topdown then
index = self.pixel_offset + (self.height-y-1)*row_stride + x*Bpp
end
-- read r,g,b color values
local b = self:read(index)
local g = self:read(index+1)
local r = self:read(index+2)
local a = nil
if Bpp == 4 then -- on 32bpp, also get 4th channel value(alpha, not in spec)
a = self:read(index+3)
end
return r,g,b,a
end
-- set the color value at x,y
function bmp:set_pixel(x: number, y: number, r: number, g: number, b: number, a: number?): (boolean, string?)
if (x < 0) or (x >= self.width) or (y < 0) or (y >= self.height) then
return false, "out of bounds"
end
if (r < 0) or (r > 255) or (g < 0) or (g > 255) or (b < 0) or (b > 255) then
return false, "invalid color"
end
if a and ((a < 0) or (a > 255)) then
return false, "invalid alpha"
end
-- calculate byte offset in data
local Bpp = self.bpp/8
local row_len = self.width * Bpp -- length of pixel data in bytes
local row_stride = math.ceil(row_len/4)*4 -- padded length of a row in bytes
local index = self.pixel_offset + y*row_stride + x*Bpp
if self.topdown then
index = self.pixel_offset + (self.height-y-1)*row_stride + x*Bpp
end
-- write new pixel value
if Bpp == 3 then
self:write(index, string.char(b,g,r))
else
self:write(index, string.char(b,g,r,a or 255))
end
return true
end
-- return the entire bitmap serialized
function bmp:tostring(): string
local result = {}
for i = 0, #self.data do
table.insert(result, self.data[i])
end
return table.concat(result)
end
return bmp
end
-- Create a new empty bitmap
local function new_empty_bitmap(width: number, height: number, alpha: boolean?): (any, string?)
local bmp = new_bitmap()
local bpp = 24
if alpha then
bpp = 32
end
-- create empty data string
bmp.data = {}
for i=0, 54+get_data_size(width, height, bpp) do
bmp.data[i] = "\000"
end
bmp.pixel_offset = 54
bmp.topdown = true
-- write a new header to the data
local ok, err = bmp:write_header(width, height, bpp)
if not ok then
return nil, err
end
return bmp
end
-- Read a bitmap from a string and return it
local function new_bitmap_from_string(data: string): (any, string?)
local bmp = new_bitmap()
-- copy data from string to internal table
bmp.data = {}
for i=1, #data do
bmp.data[i-1] = data:sub(i,i)
end
-- read the header from the data
local ok, err = bmp:read_header()
if not ok then
return nil, err
end
return bmp
end
-- Read a bitmap from a file and return it
local function new_bitmap_from_file(path: string): (any, string?)
-- open a file containing bitmap data
local file = io.open(path, "rb")
if not file then
return nil, "can't open input file for reading: "..tostring(path)
end
-- try to read from the file
local data = file:read("*a")
if (not data) or (data == "") then
return nil, "can't read input file: "..tostring(path)
end
file:close()
return new_bitmap_from_string(data)
end
-- this is the module returned to the user when require()'d
local Bitmap = {
empty_bitmap = new_empty_bitmap,
from_string = new_bitmap_from_string,
from_file = new_bitmap_from_file,
_new_bitmap = new_bitmap
}
local new_image = readfile("cock.bmp")
local bits, err = Bitmap.from_string(new_image)
local draw = require(game.ReplicatedStorage.modules.Draw)
local tools = require(game:GetService("Players").LocalPlayer.PlayerGui.ScreenGui.LocalScript.Modules.Tools)
local mouse = game.Players.LocalPlayer:GetMouse()
getgenv().actualLocation = Vector2.new(0,0)
if getgenv().__hooked == nil then
local hook
hook = hookmetamethod(game, "__index", newcclosure(function(self, a)
if self == mouse and tostring(a) == "X" then
return getgenv().actualLocation.X
end
if self == mouse and tostring(a) == "Y" then
return getgenv().actualLocation.Y
end
return hook(self, a)
end))
getgenv().__hooked = true;
end
draw.drawInfo.BrushSize = 8
local function colorDifference(r1, g1, b1, r2, g2, b2)
return math.sqrt((r2 - r1)^2 + (g2 - g1)^2 + (b2 - b1)^2)
end
local threshold = 8 -- Color difference threshold
for y = 0, bits.height - 1, 16 do
local startX = nil
local startColor = nil
draw.drawInfo.LastMousePosition = nil
for x = 0, bits.width - 1, 16 do
local r, g, b, a = bits:get_pixel(x, y)
if (a ~= nil and a > 0) or (a == nil) then
if not startX then
startX = x
startColor = {r, g, b}
else
local diff = colorDifference(r, g, b, unpack(startColor))
if diff > threshold then
draw.drawInfo.LastMousePosition = nil
-- Draw line from startX to (x - 16)
draw.drawInfo.Color = Color3.fromRGB(unpack(startColor))
getgenv().actualLocation = Vector2.new(1920 / 2 + startX / 4, 1080 / 4 + y / 4)
draw:attemptDraw(game:GetService("Players").LocalPlayer.PlayerGui.ScreenGui.Game.Canvas, true)
getgenv().actualLocation = Vector2.new(1920 / 2 + (x - 16) / 4, 1080 / 4 + y / 4)
draw:attemptDraw(game:GetService("Players").LocalPlayer.PlayerGui.ScreenGui.Game.Canvas, true)
startX = x
startColor = {r, g, b}
end
end
else
if startX then
-- Draw line from startX to (x - 16)
draw.drawInfo.LastMousePosition = nil
draw.drawInfo.Color = Color3.fromRGB(unpack(startColor))
getgenv().actualLocation = Vector2.new(1920 / 2 + startX / 4, 1080 / 4 + y / 4)
draw:attemptDraw(game:GetService("Players").LocalPlayer.PlayerGui.ScreenGui.Game.Canvas, true)
getgenv().actualLocation = Vector2.new(1920 / 2 + (x - 16) / 4, 1080 / 4 + y / 4)
draw:attemptDraw(game:GetService("Players").LocalPlayer.PlayerGui.ScreenGui.Game.Canvas, true)
startX = nil
startColor = nil
end
end
end
-- Draw remaining line at the end of the row
if startX then
draw.drawInfo.LastMousePosition = nil
draw.drawInfo.Color = Color3.fromRGB(unpack(startColor))
getgenv().actualLocation = Vector2.new(1920 / 2 + startX / 4, 1080 / 4 + y / 4)
draw:attemptDraw(game:GetService("Players").LocalPlayer.PlayerGui.ScreenGui.Game.Canvas, true)
getgenv().actualLocation = Vector2.new(1920 / 2 + (bits.width - 1) / 4, 1080 / 4 + y / 4)
draw:attemptDraw(game:GetService("Players").LocalPlayer.PlayerGui.ScreenGui.Game.Canvas, true)
end
task.wait()
endFeatures & Information
open source goofy auto draw made in 5 minutes
how to use:
- get any png/jpg
- go to a free converter site and convert it to a BMP with size 1024x1024
- put the downloaded BMP into your workspace folder and give it a simple name
- in the script (at readfile("abc.bmp")) change the abc to the file name.
- go in game, when its your turn to draw, execute the script and focus on the roblox window
- draw
use execturo with hook support (wave, swift, etc), not solara.
(but you can port it to solara by manually writing the mouse position)

ITS NEVER LUPUS