1. 从帧缓存读取像素
bool GLUtils::saveRender(const std::string& tag,int w, int h,const char* func_name, int line){
std::vector<uint32_t> outputValues(w* h, 0);
glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, &outputValues[0]);
return savePicture(tag,w, h,outputValues.data(),std::string(func_name),line);
}
2.从EGLImageKHR 转到纹理
bool GLUtils::saveEGLImage(EGLImageKHR eglImage,const std::string& tag,int w, int h,const char* func_name, int line){
GLuint tmpTex=GLUtils::createTexture(w, h,(uint32_t)0x00000000);
glBindTexture(GL_TEXTURE_2D, tmpTex);
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, eglImage);
glBindTexture(GL_TEXTURE_2D, 0);
auto re =GLUtils::saveTexture(tmpTex,tag, w, h, func_name, line);
glDeleteTextures(1, &tmpTex);
return re;
}
3.内存的像素保存到文件或图片
bool GLUtils::savePicture(const std::string& tag,int m_width, int m_height,void *m_pixelData,std::string func_name, int line,bool isSaveAsPng){
char pngPath[200];
int64_t eventTime = std::chrono::steady_clock::now().time_since_epoch().count();
char save_path[] = DEBUG_PIC_SAVE_PATH;
if (::access(save_path, F_OK) == -1){
mkdir(save_path, S_IRWXU);
}
LOGD("access file: exist \"%d\", read \"%d\", write \"%d\"", ::access(save_path, F_OK), ::access(save_path, R_OK), ::access(save_path, W_OK));
std::string suffix = ".png";
if(!isSaveAsPng){
suffix = ".raw";
}
sprintf(pngPath, "%s%lld-%s-%s-%d%s",save_path, eventTime,func_name.c_str(), tag.c_str(), line,suffix.c_str());
if(isSaveAsPng){
return save(std::string(pngPath), m_width, m_height,m_pixelData, "png");
}else{
return saveRGBARaw(std::string(pngPath), m_width, m_height,m_pixelData);
}
}
bool GLUtils::saveRGBARaw(const std::string& fileName,int width, int height,void *m_pixelData)
{
uint32_t buffer_size = width * height * sizeof(int32_t);
int dumpFd = open(fileName.c_str(), O_WRONLY|O_CREAT|O_TRUNC, 0644);
if(dumpFd >= 0) {
write(dumpFd, m_pixelData, buffer_size);
close(dumpFd);
return false;
}
return true;
}
bool GLUtils::save(const std::string& fileName,int m_width, int m_height,void *m_pixelData, const std::string& fileFormat)
{
bool ret = false;
if (m_height > 0 && m_width > 0)
{
if (fileFormat == "png")
{
ret = stbi_write_png(fileName.c_str(), m_width, m_height, c_bytesPixel[PF_RGBA8], m_pixelData, m_width*c_bytesPixel[PF_RGBA8]);
}
if (fileFormat == "jpg")
{
ret = stbi_write_jpg(fileName.c_str(), m_width, m_height, c_bytesPixel[PF_RGBA8], m_pixelData,95);
}
}
return ret;
}
4.从纹理读取
bool GLUtils::saveTexture(GLuint tex,const std::string& tag,int w, int h,const char* func_name, int line){
uint32_t *buf= GLUtils::readPixels(tex,w,h);
auto re= savePicture(tag,w, h,buf,std::string(func_name),line);
free(buf);
return re;
}
uint32_t * GLUtils::readPixels(GLuint tex,int width,int height){
uint32_t* outbuf=createTmpPictureEmpty(width, height);;
GLuint fbo;
glGenFramebuffers(1,&fbo);
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D, tex, 0);
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE,outbuf);
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
glBindTexture(GL_TEXTURE_2D,0);
return outbuf;
}
5. GraphicBuffer或ANativeWindowBuffer保存到文件
[[maybe_unused]] static void dumpGraphicRawData2file(const native_handle_t* bufferHandle,
uint32_t width, uint32_t height,
uint32_t stride, int32_t format)
{
static int sDumpCount = 0;
if(bufferHandle != nullptr) {
int shareFd = bufferHandle->data[0];
unsigned char *srcAddr = NULL;
uint32_t buffer_size = stride * height * android::bytesPerPixel(format);
srcAddr = (unsigned char *)mmap(NULL, buffer_size, PROT_READ, MAP_SHARED, shareFd, 0);
char dumpPath[100] = "";
snprintf(dumpPath, sizeof(dumpPath), "/data/dump/buffer_%u_frame_%u_%u_%u.bin", sDumpCount++, width, height, android::bytesPerPixel(format));
int dumpFd = open(dumpPath, O_WRONLY|O_CREAT|O_TRUNC, 0644);
if(dumpFd >= 0 && srcAddr != NULL) {
write(dumpFd, srcAddr, buffer_size);
close(dumpFd);
}
munmap((void*)srcAddr, buffer_size);
}
}
auto graphicBuffer = android::GraphicBuffer::from((ANativeWindowBuffer *)output.data);
uint32_t width = graphicBuffer->getWidth();
uint32_t height = graphicBuffer->getHeight();
uint32_t stride = graphicBuffer->getStride();
int32_t format = graphicBuffer->getPixelFormat();
dumpGraphicRawData2file(graphicBuffer->getNativeBuffer()->handle, width, height, stride, format);
python批量将RGBA8888裸数据保存到png
"""
# pip install opencv-python
pip install Pillow
"""
import os,re
import shutil
from PIL import Image
import numpy as np
src=[
'D:\Out\libxrengine_gtest\dump',
]
target_dir=os.path.join('D:\Out\libxrengine_gtest\generated')
files_hz='.png'
patternz = re.compile(r'\.bin$')
def mkdir(dir):
if not os.path.exists(dir):
os.makedirs(dir,exist_ok=True)
def getDirname(pathN):
return os.path.dirname(pathN)
def getName(pathN):
return pathN[len(getDirname(pathN))+1:]
ffmpeg_exe = 'D:/software/ffmpeg-6.0-essentials_build/bin/ffmpeg.exe'
def convert(from_file,to_file):
cmd=ffmpeg_exe+' -s 640x480 -pix_fmt yuv444p10le -i ' + from_file + ' -f image2 -pix_fmt rgba32 ' + to_file
print(cmd)
os.system(cmd)
def comvert2(from_file,to_file):
with open(from_file, 'rb') as raw:
rawData = raw.read()
width = 640
height = 480
image = Image.new('RGBA', (width, height))
buf = np.frombuffer(rawData, dtype=np.uint8 )
buf.shape = ( height ,width,4)
img = Image.fromarray(buf)
img.save(to_file)
def doForFile(srcFile,dstFile):
matchz = patternz.search(srcFile)
if matchz:
comvert2(srcFile,dstFile+files_hz)
def traverse(src_path,target_path,base_src_path):
for parent,dirs,files in os.walk(src_path):
for filename in files:
srcFile=os.path.join(parent , filename)
dstFile=target_path+srcFile[len(base_src_path):]
dstPath=getDirname(dstFile)
mkdir(dstPath)
doForFile(srcFile,dstFile)
for dir_in in dirs:
traverse(dir_in,target_path,base_src_path)
def traverseDir(src_path,target_path):
new_target_path = os.path.join(target_path,getName(src_path))
print(new_target_path)
traverse(src_path,new_target_path,src_path)
def start():
for src_p in src:
traverseDir(os.path.join(src_p),target_dir)
start()
print('finish')