diff --git a/.luacheckrc b/.luacheckrc new file mode 100644 index 0000000..bfcd139 --- /dev/null +++ b/.luacheckrc @@ -0,0 +1,47 @@ +std = "max" +files['.luacheckrc'].global = false +unused_args = false + +globals = { + "sys", + "go", + "gui", + "label", + "render", + "crash", + "sprite", + "sound", + "tilemap", + "spine", + "particlefx", + "physics", + "factory", + "collectionfactory", + "iac", + "msg", + "vmath", + "url", + "http", + "image", + "json", + "zlib", + "iap", + "push", + "facebook", + "hash", + "hash_to_hex", + "pprint", + "init", + "final", + "update", + "on_input", + "on_message", + "on_reload", + "window", + "unityads", + "socket", + "defreview", + "resource", + "buffer", + "drawpixels" +} diff --git a/drawpixels/ext.manifest b/drawpixels/ext.manifest new file mode 100755 index 0000000..9d62983 --- /dev/null +++ b/drawpixels/ext.manifest @@ -0,0 +1 @@ +name: "DrawPixels" diff --git a/drawpixels/src/drawpixels.cpp b/drawpixels/src/drawpixels.cpp new file mode 100755 index 0000000..87980da --- /dev/null +++ b/drawpixels/src/drawpixels.cpp @@ -0,0 +1,232 @@ +// Extension lib defines +#define LIB_NAME "DrawPixels" +#define MODULE_NAME "drawpixels" + +#define DLIB_LOG_DOMAIN LIB_NAME + +#include +#include + +struct BufferInfo +{ + dmBuffer::HBuffer buffer; + int width; + int height; + int colors_count; +}; + +BufferInfo buffer_info; + +static int xytoi(int x, int y, int w, int h, int bpp) { + if (x < 0) x = 0; + if (y < 0) y = 0; + if (x >= w) x = w - 1; + if (y >= h) y = h - 1; + return (y * w * bpp) + (x * bpp); +} + +static int lenght(int x1, int y1, int x2, int y2){ + int a = x1-x2; + int b = y1-y2; + return sqrt(a*a + b*b); +} + +static void read_and_validate_buffer_info(lua_State* L, int index) { + luaL_checktype(L, index, LUA_TTABLE); + lua_getfield(L, index, "buffer"); + lua_getfield(L, index, "width"); + lua_getfield(L, index, "height"); + lua_getfield(L, index, "colors_count"); + dmScript::LuaHBuffer *lua_buffer = dmScript::CheckBuffer(L, -4); + buffer_info.buffer = lua_buffer->m_Buffer; + + if (!dmBuffer::IsBufferValid(buffer_info.buffer)) { + luaL_error(L, "Buffer is invalid"); + } + + buffer_info.width = lua_tointeger(L, -3); + if (buffer_info.width == 0) { + luaL_error(L, "'width' of the buffer should be an integer and > 0"); + } + + buffer_info.height = lua_tointeger(L, -2); + if (buffer_info.height == 0) { + luaL_error(L, "'height' of the buffer should be an integer and > 0"); + } + + buffer_info.colors_count = lua_tointeger(L, -1); + if (buffer_info.colors_count == 0) { + luaL_error(L, "'colors_count' of the buffer should be an integer and > 0"); + } +} + +static int draw_circle(lua_State* L) { + int top = lua_gettop(L) + 4; + + read_and_validate_buffer_info(L, 1); + uint32_t posx = luaL_checknumber(L, 2); + uint32_t posy = luaL_checknumber(L, 3); + uint32_t size = luaL_checknumber(L, 4); + uint32_t r = luaL_checknumber(L, 5); + uint32_t g = luaL_checknumber(L, 6); + uint32_t b = luaL_checknumber(L, 7); + uint32_t a = 0; + if (lua_isnumber(L, 5) == 1) + { + a = luaL_checknumber(L, 5); + } + + uint8_t* bytes = 0; + uint32_t src_size = 0; + + dmBuffer::Result result = dmBuffer::GetBytes(buffer_info.buffer, (void**)&bytes, &src_size); + if (result != dmBuffer::RESULT_OK) { + dmLogError("Unable to get bytes from source buffer"); + return 0; + } + int newposx = 0; + int newposy = 0; + int half_size = size/2; + for(int x = -half_size; x < half_size; x++) { + for(int y = -half_size; y < half_size; y++) { + newposx = x + posx; + newposy = y + posy; + if (lenght(newposx, newposy, posx, posy) < half_size) { + int i = xytoi(newposx, newposy, buffer_info.width, buffer_info.height, buffer_info.colors_count); + bytes[i] = r; + bytes[i + 1] = g; + bytes[i + 2] = b; + if (buffer_info.colors_count == 4) { + bytes[i + 3] = a; + } + } + } + } + + assert(top == lua_gettop(L)); + return 0; +} + +static int fill_texture(lua_State* L) { + int top = lua_gettop(L) + 4; + + read_and_validate_buffer_info(L, 1); + uint32_t r = luaL_checknumber(L, 2); + uint32_t g = luaL_checknumber(L, 3); + uint32_t b = luaL_checknumber(L, 4); + uint32_t a = 0; + if (lua_isnumber(L, 5) == 1) + { + a = luaL_checknumber(L, 5); + } + + uint8_t* bytes = 0; + uint32_t src_size = 0; + + dmBuffer::Result result = dmBuffer::GetBytes(buffer_info.buffer, (void**)&bytes, &src_size); + if (result != dmBuffer::RESULT_OK) { + dmLogError("Unable to get bytes from source buffer"); + return 0; + } + for(int i = 0; i < src_size; i += buffer_info.colors_count) { + bytes[i] = r; + bytes[i + 1] = g; + bytes[i + 2] = b; + if (buffer_info.colors_count == 4) { + bytes[i + 3] = a; + } + } + + assert(top == lua_gettop(L)); + return 0; +} + +static int draw_rect(lua_State* L) { + int top = lua_gettop(L) + 4; + + read_and_validate_buffer_info(L, 1); + uint32_t posx = luaL_checknumber(L, 2); + uint32_t posy = luaL_checknumber(L, 3); + uint32_t sizex = luaL_checknumber(L, 4); + uint32_t sizey = luaL_checknumber(L, 5); + uint32_t r = luaL_checknumber(L, 6); + uint32_t g = luaL_checknumber(L, 7); + uint32_t b = luaL_checknumber(L, 8); + uint32_t a = 0; + if (lua_isnumber(L, 5) == 1) + { + a = luaL_checknumber(L, 5); + } + + uint8_t* bytes = 0; + uint32_t src_size = 0; + + dmBuffer::Result result = dmBuffer::GetBytes(buffer_info.buffer, (void**)&bytes, &src_size); + if (result != dmBuffer::RESULT_OK) { + dmLogError("Unable to get bytes from source buffer"); + return 0; + } + + int half_size_x = sizex/2; + int half_size_y = sizey/2; + int newposx = 0; + int newposy = 0; + for(int x = -half_size_x; x < half_size_x; x++) { + for(int y = -half_size_y; y < half_size_y; y++) { + newposx = x + posx; + newposy = y + posy; + int i = xytoi(newposx, newposy, buffer_info.width, buffer_info.height, buffer_info.colors_count); + bytes[i] = r; + bytes[i + 1] = g; + bytes[i + 2] = b; + if (buffer_info.colors_count == 4) { + bytes[i + 3] = a; + } + } + } + + assert(top == lua_gettop(L)); + return 0; +} + +// Functions exposed to Lua +static const luaL_reg Module_methods[] = { + {"circle", draw_circle}, + {"fill", fill_texture}, + {"rect", draw_rect}, + {0, 0} +}; + +static void LuaInit(lua_State* L) { + int top = lua_gettop(L); + // Register lua names + luaL_register(L, MODULE_NAME, Module_methods); + lua_pop(L, 1); + assert(top == lua_gettop(L)); +} + +dmExtension::Result AppInitializeImpExtension(dmExtension::AppParams* params) { + return dmExtension::RESULT_OK; +} + +dmExtension::Result InitializeImpExtension(dmExtension::Params* params) { + // Init Lua + LuaInit(params->m_L); + printf("Registered %s Extension\n", MODULE_NAME); + return dmExtension::RESULT_OK; +} + +dmExtension::Result AppFinalizeImpExtension(dmExtension::AppParams* params) { + return dmExtension::RESULT_OK; +} + +dmExtension::Result FinalizeImpExtension(dmExtension::Params* params) { + return dmExtension::RESULT_OK; +} + + +// Defold SDK uses a macro for setting up extension entry points: +// +// DM_DECLARE_EXTENSION(symbol, name, app_init, app_final, init, update, on_event, final) + +DM_DECLARE_EXTENSION(DrawPixels, LIB_NAME, AppInitializeImpExtension, AppFinalizeImpExtension, InitializeImpExtension, 0, 0, FinalizeImpExtension) diff --git a/example/canvas.script b/example/canvas.script new file mode 100755 index 0000000..8b2aaa2 --- /dev/null +++ b/example/canvas.script @@ -0,0 +1,78 @@ +function init(self) + msg.post(".", "acquire_input_focus") + + -- size of texture when scaled to nearest power of two + local width = 512 + local height = 1024 + + local our_buffer = buffer.create(width * height, {{name = hash("rgba"), type = buffer.VALUE_TYPE_UINT8, count = 4}}) + + self.buffer_info = { + buffer = our_buffer, + width = width, + height = height, + colors_count = 4 --- 3 for rgb, 4 for rgba + } + + drawpixels.fill(self.buffer_info, 128, 128, 128) + self.dirty = true + + -- drawing params + self.prev_pos = vmath.vector3() + self.resource_path = go.get("#sprite", "texture0") + self.header = { + width = width, + height = height, + type = resource.TEXTURE_TYPE_2D, + format = resource.TEXTURE_FORMAT_RGBA, + num_mip_maps = 1 + } +end + +function update(self, dt) + -- update texture if it's dirty (ie we've drawn to it) + if self.dirty then + resource.set_texture(self.resource_path, self.header, self.buffer_info.buffer) + self.dirty = false + end +end + +function on_input(self, action_id, action) + if action_id == hash("touch") then + local pos = vmath.vector3(action.x, action.y, 0) + if action.pressed then + self.drawing = true + self.touch_pos = pos + elseif action.released then + self.drawing = false + end + if self.drawing then + -- calculate the length and direction from the previous touch + -- position to the current position + local length = math.ceil(vmath.length(self.touch_pos - pos)) + local dir = vmath.normalize(self.touch_pos - pos) + if length == 0 then + length = 1 + dir = vmath.vector3(0) + end + if self.prev_pos == pos then + return false + end + self.prev_pos = self.touch_pos + -- use current tool from the previous touch position to + -- the current touch position0 + while length > 0 do + drawpixels.rect(self.buffer_info, self.touch_pos.x, self.touch_pos.y, 40, 40, 255, 255, 255, 255) + -- drawpixels.circle(self.buffer_info, self.touch_pos.x, self.touch_pos.y, 120, 255, 255, 255, 255) + self.dirty = true + self.touch_pos = self.touch_pos - dir + length = length - 1 + end + end + end +end + +function on_reload(self) + -- Add reload-handling code here + -- Remove this function if not needed +end diff --git a/main/main.collection b/example/example.collection similarity index 54% rename from main/main.collection rename to example/example.collection index 6667c3b..dde902d 100644 --- a/main/main.collection +++ b/example/example.collection @@ -1,12 +1,27 @@ name: "main" scale_along_z: 0 embedded_instances { - id: "logo" - data: "embedded_components {\n" + id: "canvas" + data: "components {\n" + " id: \"script\"\n" + " component: \"/example/canvas.script\"\n" + " position {\n" + " x: 0.0\n" + " y: 0.0\n" + " z: 0.0\n" + " }\n" + " rotation {\n" + " x: 0.0\n" + " y: 0.0\n" + " z: 0.0\n" + " w: 1.0\n" + " }\n" + "}\n" + "embedded_components {\n" " id: \"sprite\"\n" " type: \"sprite\"\n" - " data: \"tile_set: \\\"/main/logo.atlas\\\"\\n" - "default_animation: \\\"logo\\\"\\n" + " data: \"tile_set: \\\"/example/img.atlas\\\"\\n" + "default_animation: \\\"fullscreen\\\"\\n" "material: \\\"/builtins/materials/sprite.material\\\"\\n" "blend_mode: BLEND_MODE_ALPHA\\n" "\"\n" @@ -24,8 +39,8 @@ embedded_instances { "}\n" "" position { - x: 640.0 - y: 340.0 + x: 256.0 + y: 512.0 z: 0.0 } rotation { diff --git a/example/images/fullscreen.png b/example/images/fullscreen.png new file mode 100755 index 0000000..d68ba80 Binary files /dev/null and b/example/images/fullscreen.png differ diff --git a/example/img.atlas b/example/img.atlas new file mode 100644 index 0000000..b3d90ba --- /dev/null +++ b/example/img.atlas @@ -0,0 +1,6 @@ +images { + image: "/example/images/fullscreen.png" +} +margin: 0 +extrude_borders: 0 +inner_padding: 0 diff --git a/game.project b/game.project index 3b16dcd..ce861c3 100644 --- a/game.project +++ b/game.project @@ -1,17 +1,17 @@ [project] -title = My project +title = Draw Pixels version = 0.1 [bootstrap] -main_collection = /main/main.collectionc +main_collection = /example/example.collectionc [input] game_binding = /input/game.input_bindingc use_accelerometer = 0 [display] -width = 1280 -height = 720 +width = 512 +height = 1024 [physics] scale = 0.02 @@ -19,3 +19,6 @@ scale = 0.02 [script] shared_state = 1 +[graphics] +texture_profiles = /builtins/graphics/default.texture_profiles + diff --git a/input/game.input_binding b/input/game.input_binding index e69de29..b4ccf33 100644 --- a/input/game.input_binding +++ b/input/game.input_binding @@ -0,0 +1,4 @@ +mouse_trigger { + input: MOUSE_BUTTON_LEFT + action: "touch" +} diff --git a/main/images/logo.png b/main/images/logo.png deleted file mode 100644 index d76885f..0000000 Binary files a/main/images/logo.png and /dev/null differ diff --git a/main/logo.atlas b/main/logo.atlas deleted file mode 100644 index 44e703e..0000000 --- a/main/logo.atlas +++ /dev/null @@ -1,3 +0,0 @@ -images { - image: "/main/images/logo.png" -}