// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <errno.h>
#include <fcntl.h>
#include <png.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <vector>
namespace {
void PNGError(png_struct* png, const char* string) {
fprintf(stderr, "PNG error: %s\n", string);
}
void PNGWarn(png_struct* png, const char* string) {
fprintf(stderr, "PNG warning: %s\n", string);
}
uint8_t* ReadFileToBuffer(const char* path, size_t size) {
int fd = open(path, O_RDONLY);
if (fd < 0) {
fprintf(stderr, "open %s: %s\n", path, strerror(errno));
return nullptr;
}
uint8_t* buf = new uint8_t[size];
ssize_t read_rv = read(fd, buf, size);
if (read_rv < 0) {
fprintf(stderr, "read %s: %s\n", path, strerror(errno));
delete[] buf;
close(fd);
return nullptr;
} else if (read_rv != size) {
fprintf(stderr, "read %s: expected %zu, observed %zd (file too small?)\n",
path, size, read_rv);
delete[] buf;
close(fd);
return nullptr;
}
char c;
read_rv = read(fd, &c, 1);
if (read_rv < 0) {
fprintf(stderr, "read %s: %s\n", path, strerror(errno));
delete[] buf;
close(fd);
return nullptr;
} else if (read_rv != 0) {
fprintf(stderr, "read %s: expected 0, observed %zd (file too large?)\n",
path, read_rv);
delete[] buf;
close(fd);
return nullptr;
}
if (close(fd) < 0) {
fprintf(stderr, "close %s: %s\n", path, strerror(errno));
delete[] buf;
return nullptr;
}
return buf;
}
} // namespace
bool EncodePNG(const char* input_image_path,
const char* input_mask_path,
const char* output_path,
size_t dimension) {
const size_t dimension2 = dimension * dimension;
uint8_t* input_image_buf = ReadFileToBuffer(input_image_path, dimension2 * 3);
if (!input_image_buf) {
return false;
}
uint8_t* input_mask_buf = ReadFileToBuffer(input_mask_path, dimension2);
if (!input_mask_buf) {
delete[] input_image_buf;
return false;
}
uint8_t* merged_buf = new uint8_t[dimension2 * 4];
std::vector<uint8_t*> rows;
rows.reserve(dimension);
for (size_t row = 0; row < dimension; ++row) {
rows.push_back(&merged_buf[row * dimension * 4]);
for (size_t col = 0; col < dimension; ++col) {
const size_t seq = row * dimension + col;
const uint8_t r = input_image_buf[seq];
const uint8_t g = input_image_buf[dimension2 + seq];
const uint8_t b = input_image_buf[dimension2 * 2 + seq];
const uint8_t a = input_mask_buf[seq];
merged_buf[seq * 4] = r;
merged_buf[seq * 4 + 1] = g;
merged_buf[seq * 4 + 2] = b;
merged_buf[seq * 4 + 3] = a;
}
}
delete[] input_image_buf;
delete[] input_mask_buf;
FILE* output_file = fopen(output_path, "wb");
if (!output_file) {
fprintf(stderr, "fopen %s: %s\n", output_path, strerror(errno));
delete[] merged_buf;
return false;
}
png_struct* png = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr,
PNGError, PNGWarn);
if (!png) {
fprintf(stderr, "png_create_write_struct failed\n");
fclose(output_file);
delete[] merged_buf;
return false;
}
png_info* pngi = png_create_info_struct(png);
if (!pngi) {
fprintf(stderr, "png_create_info_struct failed\n");
png_destroy_write_struct(&png, nullptr);
fclose(output_file);
delete[] merged_buf;
return false;
}
png_init_io(png, output_file);
png_set_IHDR(png, pngi, dimension, dimension, 8, PNG_COLOR_TYPE_RGB_ALPHA,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
png_write_info(png, pngi);
png_write_image(png, &rows[0]);
png_write_end(png, nullptr);
delete[] merged_buf;
if (fclose(output_file) < 0) {
fprintf(stderr, "fclose %s: %s\n", output_path, strerror(errno));
return false;
}
png_destroy_write_struct(&png, &pngi);
return true;
}