Last active
February 26, 2021 05:45
-
-
Save oToToT/100bc8f4fd2351fba8e2f256b40f3190 to your computer and use it in GitHub Desktop.
Convert png to webp of a whole directory
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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