opengl将bmp读成像素_保存的OpenGL屏幕像素为PNG使用的libpng

bd96500e110b49cbb3cd949968f18be7.png

I've been using SOIL to save images as BMP, but it turns out that SOIL (or stbi to be more specific) saves ~5MB images (which is about 1366x768 resolution image or more) which is quite insane.

Original BMP saving code (NOTE Everything is done in the render function):

uint8_t *pixels = new uint8_t[w * h * 3];

// copy pixels from screen

glBindTexture(GL_TEXTURE_2D, screenTex);

glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, w, h);

glPixelStorei(GL_PACK_ALIGNMENT, 1);

glReadPixels(0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, (GLvoid *)pixels);

// invert pixels (stolen from SOILs source code)

for (int j = 0; j * 2 < h; ++j) {

int x = j * w * 3;

int y = (h - 1 - j) * w * 3;

for (int i = w * 3; i > 0; --i) {

uint8_t tmp = pixels[x];

pixels[x] = pixels[y];

pixels[y] = tmp;

++x;

++y;

}

}

// save the image

int err = SOIL_save_image(fileName, SOIL_SAVE_TYPE_BMP, w, h, 3, pixels);

if (err)

printf("Done\n");

else

printf("Failed\n");

Code for saving PNG:

bool save_png_libpng(const char *filename, uint8_t *pixels, int w, int h)

{

png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);

if (!png)

return false;

png_infop info = png_create_info_struct(png);

if (!info) {

png_destroy_write_struct(&png, &info);

return false;

}

FILE *fp = fopen(filename, "wb");

if (!fp) {

png_destroy_write_struct(&png, &info);

return false;

}

png_init_io(png, fp);

png_set_IHDR(png, info, w, h, 8 /* depth */, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,

PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);

png_colorp palette = (png_colorp)png_malloc(png, PNG_MAX_PALETTE_LENGTH * sizeof(png_color));

if (!palette) {

fclose(fp);

png_destroy_write_struct(&png, &info);

return false;

}

png_set_PLTE(png, info, palette, PNG_MAX_PALETTE_LENGTH);

png_write_info(png, info);

png_set_packing(png);

png_bytepp rows = (png_bytepp)png_malloc(png, h * sizeof(png_bytep));

for (int i = 0; i < h; ++i)

rows[i] = (png_bytep)(pixels + (h - i) * w * 3);

png_write_image(png, rows);

png_write_end(png, info);

png_free(png, palette);

png_destroy_write_struct(&png, &info);

fclose(fp);

delete[] rows;

return true;

}

NOTE: I have not changed any of the original code, just replaced SOIL_save_image with save_png.

The code fails in the following line:

png_write_image(png, rows)

in PNG's source code, this function fails at the highlighted line:

void PNGAPI

png_write_image(png_structrp png_ptr, png_bytepp image)

{

png_uint_32 i; /* row index */

int pass, num_pass; /* pass variables */

png_bytepp rp; /* points to current row */

if (png_ptr == NULL)

return;

png_debug(1, "in png_write_image");

#ifdef PNG_WRITE_INTERLACING_SUPPORTED

/* Initialize interlace handling. If image is not interlaced,

* this will set pass to 1

*/

num_pass = png_set_interlace_handling(png_ptr);

#else

num_pass = 1;

#endif

/* Loop through passes */

for (pass = 0; pass < num_pass; pass++)

{

/* Loop through image */

for (i = 0, rp = image; i < png_ptr->height; i++, rp++)

{

png_write_row(png_ptr, *rp); // HERE

}

}

}

png_write_row then fails here: (The code for png_write_row is quite long to post here, so if you're curious about what happens before this line, you can check out pngwrite.c in png's source code. )

/* Copy user's row into buffer, leaving room for filter byte. */

memcpy(png_ptr->row_buf + 1, row, row_info.rowbytes);

P.S: I was using exactly the same code on MinGW and it was working 100% fine, when I switched to MSVC it started failing. I'm not sure if GCC does something magically here or it's my code's fault, so I would like to know for the sake of learning.

解决方案

The following line:

rows[i] = (png_bytep)(pixels + (h - i) * w * 3);

is unforunately going past the block of memory (pixels), so the following edit fixes it:

rows[i] = (png_bytep)(pixels + (h - i - 1) * w * 3);

Quite trivial but whatever.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值