Skip to content

Instantly share code, notes, and snippets.

@oToToT
Last active February 26, 2021 05:45
Show Gist options
  • Save oToToT/100bc8f4fd2351fba8e2f256b40f3190 to your computer and use it in GitHub Desktop.
Save oToToT/100bc8f4fd2351fba8e2f256b40f3190 to your computer and use it in GitHub Desktop.
Convert png to webp of a whole directory
/*
* Compiled with:
* g++ png2webp.cpp -lwebp -lpng -lpthread -std=c++17 -O3 -march=native
* using libwebp 0.6.1 here
*/
/*
* This is a modification from the source code of cwebp and libwebp, the
* original License is stated below.
*/
/*
* Copyright (c) 2010, Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Google nor the names of its contributors may
* be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "webp/encode.h"
#include <dirent.h>
#include <iostream>
#include <mutex>
#include <queue>
#include <sstream>
#include <string>
#include <thread>
#include <png.h>
#include <setjmp.h> // note: this must be included *after* png.h
#include <stdlib.h>
#include <string.h>
static void PNGAPI error_function(png_structp png, png_const_charp error) {
if (error != NULL)
fprintf(stderr, "libpng error: %s\n", error);
longjmp(png_jmpbuf(png), 1);
}
typedef struct {
const uint8_t *data;
size_t data_size;
png_size_t offset;
} PNGReadContext;
static void ReadFunc(png_structp png_ptr, png_bytep data, png_size_t length) {
PNGReadContext *const ctx = (PNGReadContext *)png_get_io_ptr(png_ptr);
memcpy(data, ctx->data + ctx->offset, length);
ctx->offset += length;
}
void ReadPNG(const uint8_t *const data, size_t data_size,
struct WebPPicture *const pic) {
volatile png_structp png = NULL;
volatile png_infop info = NULL;
volatile png_infop end_info = NULL;
PNGReadContext context = {NULL, 0, 0};
int color_type, bit_depth, interlaced;
int has_alpha;
int num_passes;
int p;
png_uint_32 width, height, y;
int64_t stride;
uint8_t *volatile rgb = NULL;
context.data = data;
context.data_size = data_size;
png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
if (png == NULL)
goto End;
png_set_error_fn(png, 0, error_function, NULL);
if (setjmp(png_jmpbuf(png))) {
Error:
goto End;
}
info = png_create_info_struct(png);
if (info == NULL)
goto Error;
end_info = png_create_info_struct(png);
if (end_info == NULL)
goto Error;
png_set_read_fn(png, &context, ReadFunc);
png_read_info(png, info);
if (!png_get_IHDR(png, info, &width, &height, &bit_depth, &color_type,
&interlaced, NULL, NULL))
goto Error;
png_set_strip_16(png);
png_set_packing(png);
if (color_type == PNG_COLOR_TYPE_PALETTE) {
png_set_palette_to_rgb(png);
}
if (color_type == PNG_COLOR_TYPE_GRAY ||
color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
if (bit_depth < 8) {
png_set_expand_gray_1_2_4_to_8(png);
}
png_set_gray_to_rgb(png);
}
if (png_get_valid(png, info, PNG_INFO_tRNS)) {
png_set_tRNS_to_alpha(png);
has_alpha = 1;
} else {
has_alpha = !!(color_type & PNG_COLOR_MASK_ALPHA);
}
num_passes = png_set_interlace_handling(png);
png_read_update_info(png, info);
stride = (int64_t)(has_alpha ? 4 : 3) * width * sizeof(*rgb);
rgb = (uint8_t *)malloc((size_t)stride * height);
for (p = 0; p < num_passes; ++p) {
png_bytep row = rgb;
for (y = 0; y < height; ++y) {
png_read_rows(png, &row, NULL, 1);
row += stride;
}
}
png_read_end(png, end_info);
pic->width = (int)width;
pic->height = (int)height;
has_alpha ? WebPPictureImportRGBA(pic, rgb, (int)stride)
: WebPPictureImportRGB(pic, rgb, (int)stride);
End:
if (png != NULL) {
png_destroy_read_struct((png_structpp)&png, (png_infopp)&info,
(png_infopp)&end_info);
}
free(rgb);
}
std::queue<std::string> list_dir(const std::string &path) {
std::queue<std::string> files;
auto dir = opendir(path.c_str());
auto entity = readdir(dir);
while (entity != NULL) {
if (entity->d_type == DT_REG) {
std::string fn = std::string(entity->d_name);
if (fn.size() >= 4 and fn.substr(fn.size() - 4, fn.size()) == ".png") {
files.push(path + "/" + fn);
}
}
entity = readdir(dir);
}
closedir(dir);
return files;
}
static int MyWriter(const uint8_t *data, size_t data_size,
const WebPPicture *const pic) {
FILE *const out = (FILE *)pic->custom_ptr;
return data_size ? (fwrite(data, data_size, 1, out) == 1) : 1;
}
void ImgIoUtilReadFile(const char *const file_name, const uint8_t **data,
size_t *data_size) {
void *file_data;
size_t file_size;
FILE *in;
*data = NULL;
*data_size = 0;
in = fopen(file_name, "rb");
fseek(in, 0, SEEK_END);
file_size = ftell(in);
fseek(in, 0, SEEK_SET);
file_data = malloc(file_size);
fread(file_data, file_size, 1, in);
fclose(in);
*data = (uint8_t *)file_data;
*data_size = file_size;
}
static void ReadPicture(const char *const filename, WebPPicture *const pic) {
const uint8_t *data = NULL;
size_t data_size = 0;
ImgIoUtilReadFile(filename, &data, &data_size);
ReadPNG(data, data_size, pic);
free((void *)data);
}
void png2webp(std::queue<std::string> &job, std::mutex &job_lock,
const WebPConfig &config) {
while (true) {
job_lock.lock();
if (job.empty()) {
job_lock.unlock();
break;
}
auto file = job.front();
job.pop();
std::stringstream log;
log << "Processing " << file << " (" << job.size() << " left)\n";
job_lock.unlock();
std::cout << log.str();
WebPPicture picture;
WebPPictureInit(&picture);
picture.use_argb = 1;
ReadPicture(file.data(), &picture);
picture.progress_hook = NULL;
picture.writer = MyWriter;
auto out_file = file.substr(0, file.size() - 4) + ".webp";
auto out_fp = fopen(out_file.data(), "wb");
picture.custom_ptr = (void *)out_fp;
WebPEncode(&config, &picture);
fclose(out_fp);
}
}
int main(int argc, char *argv[]) {
std::string path = ".";
if (argc > 1) {
path = argv[1];
}
auto &&files = list_dir(path);
auto thrs = std::thread::hardware_concurrency();
WebPConfig config;
WebPConfigInit(&config);
WebPConfigLosslessPreset(&config, 9);
config.lossless = 1;
config.quality = 100;
config.method = 6;
config.pass = 10;
config.thread_level = 1;
std::mutex job_lock;
std::vector<std::thread> pool;
for (unsigned i = 0; i < thrs; ++i) {
pool.emplace_back(png2webp, std::ref(files), std::ref(job_lock),
std::ref(config));
}
for (auto &thr : pool) {
thr.join();
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment