-
Notifications
You must be signed in to change notification settings - Fork 7.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
stb_image_write: support for 16 bit png #1726
base: master
Are you sure you want to change the base?
Conversation
For single channel 16 bit png
What's left
Why not fix it
This fix is still valuable
|
Releasing this wouldn't make any sense in practice because of the width=height restriction. It may be true that gamedev textures are this way, but (a) a more common use for this library is screenshots, which aren't square, and (b) more importantly it's too weird a restriction. End users won't read the documentation and notice the limitation and would report it as a bug, and we'd have to say it's not a bug, and they'd be annoyed. We either fully support 16-bit writing, or not at all. |
Two solution
|
It should definitely be a separate function so it can be typesafe (take |
Conclusion
Proveint main()
{
char name[32];
for (int i = 8; i < 1000; )
{
for (int j = 8; j < 1000;)
{
sprintf_s(name, "my_16bit_%dx%d.png", i,j);
unsigned short* us_pixel = new unsigned short[i * j];
DrawJuliaSet(us_pixel, 255,i,j);
stbi_write_png(name, i, j, 1, us_pixel, i * sizeof(unsigned short));
sprintf_s(name, "my_8bit_%dx%d.png", i, j);
unsigned char* uc_pixel = new unsigned char[i * j];
DrawJuliaSet(uc_pixel, 255, i, j);
stbi_write_png(name, i, j, 1, uc_pixel, i * sizeof(unsigned char));
sprintf_s(name, "my_16bit_n3_%dx%d.png", i, j);
unsigned short* us_pixel3 = new unsigned short[i * j * 3];
DrawColor(us_pixel3, 255, i, j);
stbi_write_png(name, i, j, 3, us_pixel3, i * sizeof(unsigned short) * 3);
sprintf_s(name, "my_8bit_n3_%dx%d.png", i, j);
unsigned char* uc_pixel3 = new unsigned char[i * j * 3];
DrawColor(uc_pixel3, 255, i, j);
stbi_write_png(name, i, j, 3, uc_pixel3, i * sizeof(unsigned char) * 3);
i = i + 15;
j = j + 15;
}
}
return 0;
}
16bit-channel3.mp4
Why does they support
Overthrow my assumption
|
Ok, that's good, but you can't change the API the way you have to infer the size of the pixels from the stride. Stride already has a different meaning. The stride describes the number of bytes between successive rows of an image, sometimes called the pitch; it's not the length of a row. For example, if you have a 1024x1024 1-channel 1-byte image and you want to write out a 512x512 subregion of it, you call the writer with a pointer to the top left corner of the subregion, and you specify the stride as 1024, because that's the distance between rows of the 512x512 subimage in memory. So you still need a separate interface for writing out 16-bit. |
Also, you can test the 16-bit output by using stb_image to read the 16-bit PNGs and verify they're the same pixels that you wrote out. |
I have tested images file from 8*8 to 1280 *1280
Seperate Function Done
final-test.mp4More info
FYI
#define STB_IMAGE_WRITE_IMPLEMENTATION
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image_write.h"
#include "stb_image.h"
#include "stdio.h"
#include "png.h"
#define PNG_BYTES_TO_CHECK 4 //libpng检查文件是否为png所需要BYTE数目
//https://www.shadertoy.com/view/wtBBWd
template<typename T>
void DrawJuliaSet(T* pImage, int nMax, int WID, int HGT){
float cx = -0.8f, cy = 0.2f;
for (int i = 0; i < WID; i++) {
for (int j = 0; j < HGT; j++) {
float zx = float(i) / float(HGT) - 1.0f, zy = float(j) / float(HGT) - 0.5f;
pImage[(i + j * WID)] = 0;
while (sqrtf(zx * zx + zy * zy) < float(HGT) && pImage[(i + j * WID)] < nMax){
float temp = zx * zx - zy * zy + cx;
zy = 2.0f * zx * zy + cy;
zx = temp;
pImage[(i + j * WID)]++;
}
}
}
}
template<typename T>
void DrawColor(T* pImage, int nMax, int WID, int HGT) {
for (int i = 0; i < HGT; i++) {
for (int j = 0; j < WID; j++) {
pImage[(j + i * WID) * 3 + 0] = T(float(j) / float(WID - 1) * float(nMax));
pImage[(j + i * WID) * 3 + 1] = T(float(i) / float(HGT - 1) * float(nMax));
pImage[(j + i * WID) * 3 + 2] = 0;
}
}
}
void ReadPng(const char* pFileName)
{
//初始化各种结构
png_structp png_ptr = { 0 };
png_infop info_ptr = { 0 };
char buf[PNG_BYTES_TO_CHECK] = { 0 };
int temp0 = 0;
//创建png数据结构体
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
//创建png文件信息结构体
info_ptr = png_create_info_struct(png_ptr);
setjmp(png_jmpbuf(png_ptr));
//检测是否为png文件
FILE* m_pFile = fopen(pFileName, "rb");
if (NULL == m_pFile)
{
return;
}
temp0 = (int)fread(buf, 1, PNG_BYTES_TO_CHECK, m_pFile); //读取四个字节判断是否为Png文件
temp0 = png_sig_cmp((png_const_bytep)buf, (png_size_t)0, PNG_BYTES_TO_CHECK);
if (temp0 != 0)
{
return;
}
//开始读文件
rewind(m_pFile); //重置文件指针
png_init_io(png_ptr, m_pFile); //初始化png数据结构体
png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND, 0); //初始化png文件信息结构体
//获取宽度,高度,位深,颜色类型
int color_type;
int nChannel = png_get_channels(png_ptr, info_ptr); //获取通道数
color_type = png_get_color_type(png_ptr, info_ptr); //颜色类型
int nPicWid = png_get_image_width(png_ptr, info_ptr);
int nPicHgt = png_get_image_height(png_ptr, info_ptr);
int nPicPitch = png_get_rowbytes(png_ptr, info_ptr);
int nPicDepth = png_get_bit_depth(png_ptr, info_ptr) / 8;
int nSize = static_cast<long long>(nPicPitch) * static_cast<long long>(nPicHgt); // 计算图片的总像素点数量
png_bytepp row_pointers;// row_pointers里边就是rgba数据
row_pointers = png_get_rows(png_ptr, info_ptr);
long long temp = 0;
if (nChannel == 3 || color_type == PNG_COLOR_TYPE_RGB)//每个像素点占3个字节内存
{
//由于png文件解析之后的数据按照RGB排列,所以这里直接赋值即可
for (int i = 0; i < nPicHgt; i++)
{
for (int j = 0; j < nPicWid * 3; j = j + 3)
{
unsigned short* pData = (unsigned short*)(row_pointers[i] + j * nPicDepth); //存储解析后的数据
printf("%d %d %d|", pData[0], pData[1], pData[2]);
}
printf("\n");
printf("--------------next line-------------\n");
}
}
//删除数据占用的内存
png_destroy_read_struct(&png_ptr, &info_ptr, 0);
fclose(m_pFile);
}
int main()
{
char name[32];
for (int i = 8; i <= 1280; )
{
for (int j = 8; j <= 1280;)
{
sprintf_s(name, "my_16bit_%dx%d.png", i,j);
unsigned short* us_pixel = new unsigned short[i * j];
DrawJuliaSet(us_pixel, 65535,i,j);
stbi_write_png_16(name, i, j, 1, us_pixel, i * sizeof(unsigned short));
sprintf_s(name, "my_8bit_%dx%d.png", i, j);
unsigned char* uc_pixel = new unsigned char[i * j];
DrawJuliaSet(uc_pixel, 255, i, j);
stbi_write_png(name, i, j, 1, uc_pixel, i * sizeof(unsigned char));
sprintf_s(name, "my_16bit_n3_%dx%d.png", i, j);
unsigned short* us_pixel3 = new unsigned short[i * j * 3];
DrawColor(us_pixel3, 255, i, j);
stbi_write_png_16(name, i, j, 3, us_pixel3, i * sizeof(unsigned short) * 3);
sprintf_s(name, "my_8bit_n3_%dx%d.png", i, j);
unsigned char* uc_pixel3 = new unsigned char[i * j * 3];
DrawColor(uc_pixel3, 255, i, j);
stbi_write_png(name, i, j, 3, uc_pixel3, i * sizeof(unsigned char) * 3);
i = i + 15;
j = j + 15;
printf("--------------%dX%d Done-------------\n", i,j);
}
}
printf("****************test for stb*********************\n");
int w, h, n;
auto pData = stbi_load_16("my_16bit_n3_8x8.png", &w, &h, &n, 3);
for (int i = 0; i < h; i++)
{
for (int j = 0; j < w; j++)
{
printf("%d %d %d|", pData[3 * (j + i * w) + 0], pData[3 * (j + i * w) + 1], pData[3 * (j + i * w) + 2]);
}
printf("\n");
printf("--------------next line-------------\n");
}
printf("****************test for libpng*********************\n");
ReadPng("my_16bit_n3_8x8.png");
return 0;
}
|
Background
Why is this neccessary
1.mp4
Bug reproduce
More test