stb_image 作为头文件库支持大量的图片文件格式,包括9种主流图片格式,而且是零依赖
编译动态链接库
1、JPEG
2、PNG
3、TGA
4、BMP
5、PSD
6、GIF
7、HDR
8、PIC
9、PNM
CMake 编译STB_image动态库 dll 文件,当然在linux 中是 so
cmake_minimum_required(VERSION 3.5)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 11)
project(stb)
include_directories(src)
set(STB_SOURCES
src/stb_image.h
src/stb_image_write.h
src/test.c
)
add_library(stb SHARED ${STB_SOURCES} )
set_target_properties( stb PROPERTIES LINKER_LANGUAGE CXX)
target_link_libraries(stb )
编译dll时候出现一个问题,使用默认的连接器,因为源码都是头文件无法识别是何种代码,无法通过cmake编译,加入下面句子后通过编译
set_target_properties( stbi PROPERTIES LINKER_LANGUAGE CXX)
将生成的libstb.dll 复制到 luajit 目录位置
linux 复制到/usr/shared 目录下,
ffi 接口调用
ffi 是自带在luajit内的,在ffi调用c api 非常方便,来看一个例子,比如要调用change_a() 函数,change_a()函数定义在test.dll中,我们只需要做如下操作:
- 在c语言环境下
int * a;
*a = 1;
void change_a(int *a){
*a = *a + 1;
}
- 在jitlua环境下
local ffi = require "ffi"
ffi.cedf([[void change_a(int *a);]])
lib = ffi.load(“libtest”) --这里加载对应的dll文件,位置要在luajit需要能访问到的路径里
ptr = ffi.new("int[1]")
--或ptr = ffi.new("int[?]",1)
lib.change_a(ptr)
print(ptr[0])
通过两步就能执行c/c++的二进制文件、甚至是fortan二进制文件,大大提升lua的实用性。输出为 libstbi.dll 动态库文件,大功告成。
stb_image ffi 实操
说了这么多,目的只要在是stbi_load() 和 stbi_image_free(),一个负责加载图像,一个负责
local ffi = require "ffi"
local lj_glfw = require("glfw")
local gllib = require"gl"
gllib.set_loader(lj_glfw)
local gl, glc, glu, glext = gllib.libraries()
ffi.cdef([[
typedef unsigned char stbi_uc;
typedef unsigned short stbi_us;
stbi_uc * stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels);
void stbi_image_free (void *retval_from_stbi_load);
]])
lib = ffi.load("libstb")
local M = {}
function M.load_image(name)
local buffered_name = ffi.string(name)
local xptr = ffi.new("int[1]")
local yptr = ffi.new("int[1]")
local chptr = ffi.new("int[1]")
local pdata = ffi.new("unsigned char *")
pdata = lib.stbi_load(buffered_name,xptr,yptr,chptr,0)
gl.glTexParameteri(glc.GL_TEXTURE_2D, glc.GL_TEXTURE_WRAP_S, glc.GL_REPEAT);
gl.glTexParameteri(glc.GL_TEXTURE_2D, glc.GL_TEXTURE_WRAP_T, glc.GL_REPEAT);
gl.glTexParameteri(glc.GL_TEXTURE_2D, glc.GL_TEXTURE_MIN_FILTER, glc.GL_LINEAR);
gl.glTexParameteri(glc.GL_TEXTURE_2D, glc.GL_TEXTURE_MAG_FILTER, glc.GL_LINEAR);
texture_ptr = ffi.new("GLuint[1]")
gl.glGenTextures(1, texture_ptr)
glext.glActiveTexture(glc.GL_TEXTURE0)
-- gl.glBindTexture(glc.GL_TEXTURE_2D, 0);
gl.glBindTexture(glc.GL_TEXTURE_2D, texture_ptr[0]);
gl.glTexImage2D(glc.GL_TEXTURE_2D, 0, glc.GL_RGB, xptr[0], yptr[0], 0, glc.GL_RGB, glc.GL_UNSIGNED_BYTE, pdata)
glext.glGenerateMipmap(glc.GL_TEXTURE_2D)
local w = xptr[0]
local h = yptr[0]
local ch = chptr[0]
local texid = texture_ptr[0]
lib.stbi_image_free(pdata)
return w,h,ch,texid
end
return M
因为stbi_load()第二、三、四、五个参数是返回在指针里的,这种情况通过ffi指针转换完成,注意参数返回的指针类型,data 是 unsigned char * 类型的数据,浮点类型贴图的需要修改代码,如果是多重贴图,记得打开相应的glActiveTexture参数,
顺道也把贴图环绕方式和Mipmap设定好,返回图形的信息如长宽是否带alpha通道,并返回texture id以备用
最终效果
texture id调用成功后,由于是第一张贴图,所以texture id返回的是1,说明贴图载入成功,可以进行模型贴图
下面是代码,几何图形一个面4个点配上uv,没有使用vbo或vao,不然又是一堆代码,而是直接用glBegin() glEnd()来写,程序不超过60行
local ffi = require("ffi")
local stb = require("stb")
local lj_glfw = require("glfw")
local gllib = require"gl"
gllib.set_loader(lj_glfw)
local gl, glc, glu, glext = gllib.libraries()
local ig = require"cimgui"
lj_glfw.init()
local window = lj_glfw.Window(700,500,"gl Triangle")
window:makeContextCurrent()
local ig_impl = ig.Imgui_Impl_glfw_opengl3() --standard
ig_impl:Init(window, true)
w,h,ch,tex_id = stb.load_image("tex.png")
print("tex_info",w,h,ch,tex_id)
gl.glEnable(glc.GL_TEXTURE_2D)
gl.glBindTexture(glc.GL_TEXTURE_2D, tex_id)
while not window:shouldClose() do
lj_glfw.pollEvents()
gl.glClearColor(0.1, 0.1, 0.1, 1.0)
gl.glClear(glc.GL_COLOR_BUFFER_BIT)
gl.glBegin(glc.GL_POLYGON)
gl.glTexCoord2f(1.0, 1.0)
gl.glVertex3f(0.5, 0.5, 0.0)
gl.glTexCoord2f(1.0, 0.0)
gl.glVertex3f(0.5, -0.5, 0.0)
gl.glTexCoord2f(0.0, 0.0)
gl.glVertex3f(-0.5, -0.5, 0.0)
gl.glTexCoord2f(0.0, 1.0)
gl.glVertex3f(-0.5, 0.5, 0.0)
gl.glEnd()
ig_impl:NewFrame()
if ig.Button"Texture" then
-- print"Hello World!!"
-- do something
end
ig_impl:Render()
window:swapBuffers()
end
ig_impl:destroy()
window:destroy()
lj_glfw.terminate()
imgui 其中亦涉及到imgui opengl或vulkan backend 的使用GL和glfw封装都比较繁琐 如何从代码自动生成ffi cedf 。有兴趣可参考大神sonoro1234 的git仓库
后续
用luajit+ffi封装效率很高 容易重构 不需要反复编译