2014-02-18 6 views
4

SOIL을 사용하여 이미지를 BMP로 저장했지만 SOIL (또는 stbi가 더 구체적 임) ~ 5MB 이미지 (약 1366x768 해상도 이미지 이상)가 저장되는 것으로 나타났습니다.). 그것은 아주 미쳤다.libpng를 사용하여 OpenGL 화면 픽셀을 PNG로 저장

원래 BMP 절약 코드 ( 모두가 렌더링 기능에서 수행 주) :

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; 
} 

참고 :

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"); 

코드 PNG를 저장하기 위해 나는 어떤을 변경하지 않은 원래 코드는 SOIL_save_imagesave_png으로 바 꾸었습니다. 여기 실패 후

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 :

PNG의 소스 코드에서

png_write_image(png, rows)

는이 기능이 강조 표시 줄에서 실패

코드는 다음 줄에 실패 (코드에 대한 png_write_row는 여기에 게시하기에 꽤 길기 때문에이 줄 앞에 어떤 일이 발생하는지 궁금하다면 png의 소스 코드에서 pngwrite.c를 확인하십시오.)

/* Copy user's row into buffer, leaving room for filter byte. */ 
    memcpy(png_ptr->row_buf + 1, row, row_info.rowbytes); 

P.S : 저는 MinGW에서 정확히 동일한 코드를 사용하고 있었는데 MSVC로 전환했을 때 100 % 정상적으로 작동했습니다. 실패했습니다. 나는 GCC가 마술처럼 여기에서 어떤 일을하는지, 아니면 내 코드의 잘못인지 모르겠다. 그래서 나는 배우기 위해서 알고 싶다.

답변

5

다음 줄 :

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

는 unforunately 메모리 (pixels)의 블록을지나려고하고있다, 그래서 다음 편집을 해결

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

아주 사소한하지만 뭐든간에.