CMake プロジェクトの C++ アプリで webp 形式の読み書きに libwebp で対応することはじめ
概要
「せんぱい! FLIF っていうのすごいらしいですね!!!!うちで作ってるアプリで扱っている PNG や JPG の内部キャッシュもこの FLIF 形式にトランスコードしたら・・・」
っ『LGPLv3』
そのプロダクトでは LGPLv3 ライセンスのライブラリーを使っちゃうといろいろと問題が起きるからだめー、でも webp なら使ってみてもイイヨヽ(´ー`)ノ
そういうわけでせんぱいは今日もこそこそ定時退社DAYでオシゴトを終わった後にこっそりと Qiita に記事を書くのであった。(†0)
CMake のプロジェクトを libwebp に ExternalProject で対応しよう
さっそく↓こんなのを書いて CMakeLists.txt
から include
して ExternalProject
で管理してみよう:
libwebp.cmake
cmake_minimum_required( VERSION 3.2 )
include_directories(${CMAKE_CURRENT_BINARY_DIR}/include)
link_directories(${CMAKE_CURRENT_BINARY_DIR}/lib)
include( ExternalProject )
ExternalProject_Add( external_libwebp
# https://github.com/webmproject/libwebp
# http://www.webmproject.org/
GIT_REPOSITORY [email protected]:webmproject/libwebp.git
# v0.5.0 タグなどには cmake が無くて少し手間が増えるのでとりあえず master
GIT_TAG master
# WEBP 系のオプションは webp リポジトリーの CMakeLists.txt 冒頭にわかりやすく整理されているよ
# おまけツールの cwebp, dwebp コマンドのビルドを on にする場合は libjpeg が必要になるのでとりあえず off にしておくよ
CMAKE_ARGS -DWEBP_BUILD_CWEBP=off
-DWEBP_BUILD_DWEBP=off
-DWEBP_EXPERIMENTAL_FEATURES=on
-DWEBP_FORCE_ALIGNED=on
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}
-DCMAKE_COMPILER_IS_GNUCXX=${CMAKE_COMPILER_IS_GNUCXX}
-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
-DCMAKE_C_FLAGS=${GL_BINDING_C_FLAGS}
# cmake -> ninja -> install ターゲットなどしたいところだけどカスタムプリフィックスの挙動がおかしいようなので install だけごりごり書いておく
INSTALL_COMMAND
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_BINARY_DIR}/external/libwebp/src/external_libwebp/src/webp ${CMAKE_CURRENT_BINARY_DIR}/include/webp
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_BINARY_DIR}/external/libwebp/src/external_libwebp-build/include ${CMAKE_CURRENT_BINARY_DIR}/include
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_BINARY_DIR}/external/libwebp/src/external_libwebp-build/libwebp.a ${CMAKE_CURRENT_BINARY_DIR}/lib/libwebp.a
# cwebp, dwebp もビルドする時は付ける
#COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_BINARY_DIR}/external/libwebp/src/external_libwebp-build/cwebp.exe ${CMAKE_CURRENT_BINARY_DIR}/bin/cwebp.exe
#COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_BINARY_DIR}/external/libwebp/src/external_libwebp-build/dwebp.exe ${CMAKE_CURRENT_BINARY_DIR}/bin/dwebp.exe
)
あとはライブラリーを使いたいアプリの add_dependencies
に external_libwebp
、 target_link_libraries
に webp
を追加すればOK。
webp 形式のデータを C++ ソースのアプリで生成してみる
#include <webp/decode.h>
#include <webp/encode.h>
#include <webp/types.h>
#include <cstdint>
#include <iostream>
#include <fstream>
#include <vector>
auto main() -> int
{
// メモリーに 256 pixels * 256 pixels * 4 elements な変換元の入力画像データを適当に作るよ
constexpr auto in_width = 256;
constexpr auto in_height = 256;
constexpr auto in_elements = 4;
std::vector< std::uint8_t > in( in_width * in_height * in_elements );
for ( int y = 0 ; y < in_height; ++y )
for ( int x = 0 ; x < in_width; ++x )
{
in[ y * in_width * in_elements + x * in_elements + 0] = x;
in[ y * in_width * in_elements + x * in_elements + 1] = 0;
in[ y * in_width * in_elements + x * in_elements + 2] = y;
in[ y * in_width * in_elements + x * in_elements + 3] = 255;
}
// webp 形式に変換した結果をこれに入れるよ
std::uint8_t* out_data = nullptr;
auto out_size = 0ull;
constexpr auto in_stride = in_width * in_elements;
const auto write_file = [&]( const auto& filename )
{
std::ofstream o( filename, std::ios::binary );
o.write( reinterpret_cast< const char* >( out_data ), out_size );
if ( o.bad() )
return EXIT_FAILURE;
};
#ifndef LOSSLESS
// ロッシーで最高品質の webp に変換してファイルを出力してみる
{
constexpr auto out_quality_factor = 100.0f;
out_size = WebPEncodeRGBA
( in.data(), in_width, in_height, in_stride
, out_quality_factor
, &out_data
);
write_file( "lossy-100.webp" );
}
#else
// ロスレスで最高品質の webp に変換してファイルを出力してみる
{
out_size = WebPEncodeLosslessRGBA
( in.data(), in_width, in_height, in_stride
, &out_data
);
write_file( "lossless.webp" );
}
#endif
}
webp 形式のデータを C++ アプリで読み出してみる
#include <webp/decode.h>
#include <webp/encode.h>
#include <webp/types.h>
#include <cstdint>
#include <iostream>
#include <fstream>
#include <vector>
auto main() -> int
{
std::ifstream i( "hoge.lossless.webp", std::ios::binary );
std::vector< std::uint8_t > in
( ( std::istreambuf_iterator< char >( i ) )
, ( std::istreambuf_iterator< char >( ) )
);
// 対象のバイナリーデータが webp 形式か確認(†1)
if ( WebPGetInfo( in.data(), in.size(), nullptr, nullptr ) == 0 )
return EXIT_FAILURE;
int width = 0;
int height = 0;
const auto deleter = []( auto* p ){ free( p ); };
const std::unique_ptr< std::uint8_t, decltype( deleter ) > decoded_data
( WebPDecodeRGBA( in.data(), in.size(), &width, &height )
, deleter
);
);
/* 読みだした画像でお好みの処理をどうぞ */
}
だそく
#include <webp/decode.h>
#include <webp/encode.h>
#include <webp/types.h>
#include <cstdint>
#include <iostream>
#include <fstream>
#include <vector>
auto main() -> int
{
// メモリーに 256 pixels * 256 pixels * 4 elements な変換元の入力画像データを適当に作るよ
constexpr auto in_width = 256;
constexpr auto in_height = 256;
constexpr auto in_elements = 4;
std::vector< std::uint8_t > in( in_width * in_height * in_elements );
for ( int y = 0 ; y < in_height; ++y )
for ( int x = 0 ; x < in_width; ++x )
{
in[ y * in_width * in_elements + x * in_elements + 0] = x;
in[ y * in_width * in_elements + x * in_elements + 1] = 0;
in[ y * in_width * in_elements + x * in_elements + 2] = y;
in[ y * in_width * in_elements + x * in_elements + 3] = 255;
}
// webp 形式に変換した結果をこれに入れるよ
std::uint8_t* out_data = nullptr;
auto out_size = 0ull;
constexpr auto in_stride = in_width * in_elements;
const auto write_file = [&]( const auto& filename )
{
std::ofstream o( filename, std::ios::binary );
o.write( reinterpret_cast< const char* >( out_data ), out_size );
if ( o.bad() )
return EXIT_FAILURE;
};
#ifndef LOSSLESS
// ロッシーで最高品質の webp に変換してファイルを出力してみる
{
constexpr auto out_quality_factor = 100.0f;
out_size = WebPEncodeRGBA
( in.data(), in_width, in_height, in_stride
, out_quality_factor
, &out_data
);
write_file( "lossy-100.webp" );
}
#else
// ロスレスで最高品質の webp に変換してファイルを出力してみる
{
out_size = WebPEncodeLosslessRGBA
( in.data(), in_width, in_height, in_stride
, &out_data
);
write_file( "lossless.webp" );
}
#endif
}
#include <webp/decode.h>
#include <webp/encode.h>
#include <webp/types.h>
#include <cstdint>
#include <iostream>
#include <fstream>
#include <vector>
auto main() -> int
{
std::ifstream i( "hoge.lossless.webp", std::ios::binary );
std::vector< std::uint8_t > in
( ( std::istreambuf_iterator< char >( i ) )
, ( std::istreambuf_iterator< char >( ) )
);
// 対象のバイナリーデータが webp 形式か確認(†1)
if ( WebPGetInfo( in.data(), in.size(), nullptr, nullptr ) == 0 )
return EXIT_FAILURE;
int width = 0;
int height = 0;
const auto deleter = []( auto* p ){ free( p ); };
const std::unique_ptr< std::uint8_t, decltype( deleter ) > decoded_data
( WebPDecodeRGBA( in.data(), in.size(), &width, &height )
, deleter
);
);
/* 読みだした画像でお好みの処理をどうぞ */
}
だそく
libwebp は他にも扱い易い API が用意されていて楽でよいです。(†Reference)
ちなみに、今回生成した webp 形式の画像の元にしたデータと同じものを paint.net で生成してみたところ、以下の様なファイルサイズの結果が得られました。
type | bits/elem. | quality | file size[KB] |
---|---|---|---|
BMP | 32 | n/a | 193 |
BMP | 8 | n/a | 66 |
PNG | 32 | n/a | 42 |
PNG | 24 | n/a | 40 |
PNG | 8 | n/a | 20 |
JPG | 24 | 100 | 15 |
DDS | DXT1 | 33 | |
WEBP | 32 | 100 | 3 |
WEBP | 32 | Lossless | 1 |
webp さん優秀ヽ(´ー`)ノ
註
- †0 : 記事冒頭の概要本文中のどうでもよさそうな表現はフィクションです。
- †1 :
WebPGetInfo
は必要最小限のヘッダー部分しか見ていなくて、ファイル構造のヘッダー部の "RIFF"
や "WEBPVP8L"
を改変すると 0
を返すけれど、ヘッダー構造上 null であるべき場所が 1 だったりとかの程度ではデータ異常とは検出しないみたい。バイナリーエディターで遊んで見るとよい。
Reference
WebPGetInfo
は必要最小限のヘッダー部分しか見ていなくて、ファイル構造のヘッダー部の "RIFF"
や "WEBPVP8L"
を改変すると 0
を返すけれど、ヘッダー構造上 null であるべき場所が 1 だったりとかの程度ではデータ異常とは検出しないみたい。バイナリーエディターで遊んで見るとよい。Author And Source
この問題について(CMake プロジェクトの C++ アプリで webp 形式の読み書きに libwebp で対応することはじめ), 我々は、より多くの情報をここで見つけました https://qiita.com/usagi/items/38589f87a7a88ad1ddcb著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .