86 lines
2.3 KiB
C
86 lines
2.3 KiB
C
#include <spng.h>
|
|
#include <math.h>
|
|
#include <stdint.h>
|
|
#include "write_png.h"
|
|
|
|
static inline double linear_to_srgb(double x){
|
|
if (x <= 0.0) return 0.0;
|
|
if (x >= 1.0) return 1.0;
|
|
if (x <= 0.0031308) return 12.92 * x;
|
|
return 1.055 * pow(x, 1.0/2.4) - 0.055;
|
|
}
|
|
|
|
int buffer_normalize_srgb(double (*buffer)[3], int W, int H) {
|
|
double max = 0;
|
|
for (int j = 0; j < H; j++) {
|
|
for (int i = 0; i < W; i++) {
|
|
for (int c = 0; c < 3; c++) {
|
|
if (max < buffer[j*W + i][c]) max = buffer[j*W + i][c];
|
|
}
|
|
}
|
|
}
|
|
if (max == 0) return 1;
|
|
for (int j = 0; j < H; j++) {
|
|
for (int i = 0; i < W; i++) {
|
|
for (int c = 0; c < 3; c++) {
|
|
buffer[j*W + i][c] /= max;
|
|
buffer[j*W+i][c] = linear_to_srgb(buffer[j*W+i][c]);
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int write_png(char *filename, double (*buffer)[3], int W, int H) {
|
|
buffer_normalize_srgb(buffer, W, H);
|
|
size_t bufsize = W*H*3;
|
|
uint8_t *img = (uint8_t *)malloc(bufsize);
|
|
if (!img) {return 1;}
|
|
|
|
for (int j = 0; j < H; j++) {
|
|
for (int i = 0; i < W; i++) {
|
|
for (int c = 0; c < 3; c++) {
|
|
int v = (int)lrint(buffer[j*W*i][c]);
|
|
if (v < 0) v = 0;
|
|
if (v > 255) v = 255;
|
|
img[j*W*3+i*3+c] = (uint8_t) v;
|
|
}
|
|
}
|
|
}
|
|
|
|
int flag = 0;
|
|
spng_ctx *ctx = spng_ctx_new(SPNG_CTX_ENCODER);
|
|
if(!ctx){ fprintf(stderr, "spng_ctx_new failed\n"); free(img); return 1; }
|
|
|
|
FILE *f = fopen(filename, "wb");
|
|
if(!f){ fprintf(stderr, "fopen failed\n"); spng_ctx_free(ctx); free(img); return 1; }
|
|
spng_set_png_file(ctx, f);
|
|
|
|
struct spng_ihdr ihdr = {0};
|
|
ihdr.width = W;
|
|
ihdr.height = H;
|
|
ihdr.bit_depth = 8;
|
|
ihdr.color_type = SPNG_COLOR_TYPE_TRUECOLOR; // RGB
|
|
ihdr.interlace_method = SPNG_INTERLACE_NONE;
|
|
ihdr.compression_method = 0;
|
|
ihdr.filter_method = 0;
|
|
if( (flag= spng_set_ihdr(ctx, &ihdr)) ){
|
|
fprintf(stderr, "spng_set_ihdr: %s\n", spng_strerror(flag));
|
|
fclose(f); spng_ctx_free(ctx); free(img); return 1;
|
|
}
|
|
|
|
#ifndef SPNG_SRGB_INTENT_PERCEPTUAL
|
|
#define SPNG_SRGB_INTENT_PERCEPTUAL 0
|
|
#endif
|
|
spng_set_srgb(ctx, SPNG_SRGB_INTENT_PERCEPTUAL);
|
|
|
|
flag = spng_encode_image(ctx, img, bufsize, SPNG_FMT_PNG, SPNG_ENCODE_FINALIZE);
|
|
if(flag){
|
|
fprintf(stderr, "spng_encode_image: %s\n", spng_strerror(flag));
|
|
}
|
|
fclose(f);
|
|
spng_ctx_free(ctx);
|
|
free(img);
|
|
return flag? 1 : 0;
|
|
}
|