Merge "vwebp: add color profile support" into 0.3.0

This commit is contained in:
James Zern 2013-03-16 11:58:19 -07:00 committed by Gerrit Code Review
commit 096a8e321d
2 changed files with 207 additions and 54 deletions

42
README
View File

@ -267,8 +267,48 @@ a decoded WebP file. It's not yet integrated in the automake or makefile.unix
build system, but you can try to manually compile it using the recommendations
at the top of the source file.
Usage: 'vwebp my_picture.webp'
Usage: vwebp in_file [options]
Decodes the WebP image file and visualize it using OpenGL
Options are:
-version .... print version number and exit.
-noicc ....... don't use the icc profile if present.
-nofancy ..... don't use the fancy YUV420 upscaler.
-nofilter .... disable in-loop filtering.
-mt .......... use multi-threading.
-info ........ print info.
-h ....... this help message.
Keyboard shortcuts:
'c' ................ toggle use of color profile.
'i' ................ overlay file information.
'q' / 'Q' / ESC .... quit.
Building:
---------
Prerequisites:
1) OpenGL & OpenGL Utility Toolkit (GLUT)
Linux:
$ sudo apt-get install freeglut3-dev mesa-common-dev
Mac + XCode:
- These libraries should be available in the OpenGL / GLUT frameworks.
Windows:
http://freeglut.sourceforge.net/index.php#download
2) (Optional) qcms (Quick Color Management System)
i. Download qcms from Mozilla / Chromium:
http://hg.mozilla.org/mozilla-central/file/0e7639e3bdfb/gfx/qcms
http://src.chromium.org/viewvc/chrome/trunk/src/third_party/qcms
ii. Build and archive the source files as libqcms.a / qcms.lib
iii. Update makefile.unix / Makefile.vc
a) Define WEBP_HAVE_QCMS
b) Update include / library paths to reference the qcms directory.
Build using makefile.unix / Makefile.vc:
$ make -f makefile.unix examples/vwebp
> nmake /f Makefile.vc CFG=release-static \
../obj/x64/release-static/bin/vwebp.exe
Encoding API:
=============

View File

@ -8,7 +8,7 @@
// Simple WebP file viewer.
//
// Compiling on linux:
// sudo apt-get install libglut3-dev mesa-common-dev
// sudo apt-get install freeglut3-dev mesa-common-dev
// gcc -o vwebp vwebp.c -O3 -lwebp -lwebpmux -lglut -lGL -lpthread -lm
// Compiling on Mac + XCode:
// gcc -o vwebp vwebp.c -lwebp -lwebpmux -framework GLUT -framework OpenGL
@ -19,9 +19,6 @@
#include <stdlib.h>
#include <string.h>
#include "webp/decode.h"
#include "webp/demux.h"
#ifdef __APPLE__
#include <GLUT/glut.h>
#else
@ -31,6 +28,13 @@
#endif
#endif
#ifdef WEBP_HAVE_QCMS
#include <qcms.h>
#endif
#include "webp/decode.h"
#include "webp/demux.h"
#include "./example_util.h"
#ifdef _MSC_VER
@ -42,9 +46,11 @@ static void Help(void);
// Unfortunate global variables. Gathered into a struct for comfort.
static struct {
int has_animation;
int has_color_profile;
int done;
int decoding_error;
int print_info;
int use_color_profile;
int canvas_width, canvas_height;
int loop_count;
@ -56,6 +62,7 @@ static struct {
const WebPDecBuffer* pic;
WebPDemuxer* dmux;
WebPIterator frameiter;
WebPChunkIterator iccp;
} kParams;
static void ClearPreviousPic(void) {
@ -67,10 +74,128 @@ static void ClearParams(void) {
ClearPreviousPic();
WebPDataClear(&kParams.data);
WebPDemuxReleaseIterator(&kParams.frameiter);
WebPDemuxReleaseChunkIterator(&kParams.iccp);
WebPDemuxDelete(kParams.dmux);
kParams.dmux = NULL;
}
// -----------------------------------------------------------------------------
// Color profile handling
static int ApplyColorProfile(const WebPData* const profile,
WebPDecBuffer* const rgba) {
#ifdef WEBP_HAVE_QCMS
int i, ok = 0;
uint8_t* line;
uint8_t major_revision;
qcms_profile* input_profile = NULL;
qcms_profile* output_profile = NULL;
qcms_transform* transform = NULL;
const qcms_data_type input_type = QCMS_DATA_RGBA_8;
const qcms_data_type output_type = QCMS_DATA_RGBA_8;
const qcms_intent intent = QCMS_INTENT_DEFAULT;
if (profile == NULL || rgba == NULL) return 0;
if (profile->bytes == NULL || profile->size < 10) return 1;
major_revision = profile->bytes[8];
qcms_enable_iccv4();
input_profile = qcms_profile_from_memory(profile->bytes, profile->size);
// qcms_profile_is_bogus() is broken with ICCv4.
if (input_profile == NULL ||
(major_revision < 4 && qcms_profile_is_bogus(input_profile))) {
fprintf(stderr, "Color profile is bogus!\n");
goto Error;
}
output_profile = qcms_profile_sRGB();
if (output_profile == NULL) {
fprintf(stderr, "Error creating output color profile!\n");
goto Error;
}
qcms_profile_precache_output_transform(output_profile);
transform = qcms_transform_create(input_profile, input_type,
output_profile, output_type,
intent);
if (transform == NULL) {
fprintf(stderr, "Error creating color transform!\n");
goto Error;
}
line = rgba->u.RGBA.rgba;
for (i = 0; i < rgba->height; ++i, line += rgba->u.RGBA.stride) {
qcms_transform_data(transform, line, line, rgba->width);
}
ok = 1;
Error:
if (input_profile != NULL) qcms_profile_release(input_profile);
if (output_profile != NULL) qcms_profile_release(output_profile);
if (transform != NULL) qcms_transform_release(transform);
return ok;
#else
(void)profile;
(void)rgba;
return 1;
#endif // WEBP_HAVE_QCMS
}
//------------------------------------------------------------------------------
// File decoding
static int Decode(void) { // Fills kParams.frameiter
const WebPIterator* const iter = &kParams.frameiter;
WebPDecoderConfig* const config = kParams.config;
WebPDecBuffer* const output_buffer = &config->output;
int ok = 0;
ClearPreviousPic();
output_buffer->colorspace = MODE_RGBA;
ok = (WebPDecode(iter->fragment.bytes, iter->fragment.size,
config) == VP8_STATUS_OK);
if (!ok) {
fprintf(stderr, "Decoding of frame #%d failed!\n", iter->frame_num);
} else {
kParams.pic = output_buffer;
if (kParams.use_color_profile) {
ok = ApplyColorProfile(&kParams.iccp.chunk, output_buffer);
if (!ok) {
fprintf(stderr, "Applying color profile to frame #%d failed!\n",
iter->frame_num);
}
}
}
return ok;
}
static void decode_callback(int what) {
if (what == 0 && !kParams.done) {
int duration = 0;
if (kParams.dmux != NULL) {
WebPIterator* const iter = &kParams.frameiter;
if (!WebPDemuxNextFrame(iter)) {
WebPDemuxReleaseIterator(iter);
if (WebPDemuxGetFrame(kParams.dmux, 1, iter)) {
--kParams.loop_count;
kParams.done = (kParams.loop_count == 0);
} else {
kParams.decoding_error = 1;
kParams.done = 1;
return;
}
}
duration = iter->duration;
}
if (!Decode()) {
kParams.decoding_error = 1;
kParams.done = 1;
} else {
glutPostRedisplay();
glutTimerFunc(duration, decode_callback, what);
}
}
}
//------------------------------------------------------------------------------
// Callbacks
@ -84,6 +209,24 @@ static void HandleKey(unsigned char key, int pos_x, int pos_y) {
ClearParams();
exit(0);
#endif
} else if (key == 'c') {
if (kParams.has_color_profile && !kParams.decoding_error) {
kParams.use_color_profile = 1 - kParams.use_color_profile;
if (kParams.has_animation) {
// Restart the completed animation to pickup the color profile change.
if (kParams.done && kParams.loop_count == 0) {
kParams.loop_count =
(int)WebPDemuxGetI(kParams.dmux, WEBP_FF_LOOP_COUNT) + 1;
kParams.done = 0;
// Start the decode loop immediately.
glutTimerFunc(0, decode_callback, 0);
}
} else {
Decode();
glutPostRedisplay();
}
}
} else if (key == 'i') {
kParams.print_info = 1 - kParams.print_info;
glutPostRedisplay();
@ -192,55 +335,6 @@ static void StartDisplay(void) {
DrawCheckerBoard();
}
//------------------------------------------------------------------------------
// File decoding
static int Decode(void) { // Fills kParams.frameiter
const WebPIterator* const iter = &kParams.frameiter;
WebPDecoderConfig* const config = kParams.config;
WebPDecBuffer* const output_buffer = &config->output;
int ok = 0;
ClearPreviousPic();
output_buffer->colorspace = MODE_RGBA;
ok = (WebPDecode(iter->fragment.bytes, iter->fragment.size,
config) == VP8_STATUS_OK);
if (!ok) {
fprintf(stderr, "Decoding of frame #%d failed!\n", iter->frame_num);
} else {
kParams.pic = output_buffer;
}
return ok;
}
static void decode_callback(int what) {
if (what == 0 && !kParams.done) {
int duration = 0;
if (kParams.dmux != NULL) {
WebPIterator* const iter = &kParams.frameiter;
if (!WebPDemuxNextFrame(iter)) {
WebPDemuxReleaseIterator(iter);
if (WebPDemuxGetFrame(kParams.dmux, 1, iter)) {
--kParams.loop_count;
kParams.done = (kParams.loop_count == 0);
} else {
kParams.decoding_error = 1;
kParams.done = 1;
return;
}
}
duration = iter->duration;
}
if (!Decode()) {
kParams.decoding_error = 1;
kParams.done = 1;
} else {
glutPostRedisplay();
glutTimerFunc(duration, decode_callback, what);
}
}
}
//------------------------------------------------------------------------------
// Main
@ -249,6 +343,7 @@ static void Help(void) {
"Decodes the WebP image file and visualize it using OpenGL\n"
"Options are:\n"
" -version .... print version number and exit.\n"
" -noicc ....... don't use the icc profile if present.\n"
" -nofancy ..... don't use the fancy YUV420 upscaler.\n"
" -nofilter .... disable in-loop filtering.\n"
" -mt .......... use multi-threading.\n"
@ -256,6 +351,7 @@ static void Help(void) {
" -h ....... this help message.\n"
"\n"
"Keyboard shortcuts:\n"
" 'c' ................ toggle use of color profile.\n"
" 'i' ................ overlay file information.\n"
" 'q' / 'Q' / ESC .... quit.\n"
);
@ -270,11 +366,14 @@ int main(int argc, char *argv[]) {
return -1;
}
kParams.config = &config;
kParams.use_color_profile = 1;
for (c = 1; c < argc; ++c) {
if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
Help();
return 0;
} else if (!strcmp(argv[c], "-noicc")) {
kParams.use_color_profile = 0;
} else if (!strcmp(argv[c], "-nofancy")) {
config.options.no_fancy_upsampling = 1;
} else if (!strcmp(argv[c], "-nofilter")) {
@ -327,6 +426,20 @@ int main(int argc, char *argv[]) {
printf("Canvas: %d x %d\n", kParams.canvas_width, kParams.canvas_height);
}
memset(&kParams.iccp, 0, sizeof(kParams.iccp));
kParams.has_color_profile =
!!(WebPDemuxGetI(kParams.dmux, WEBP_FF_FORMAT_FLAGS) & ICCP_FLAG);
if (kParams.has_color_profile) {
#ifdef WEBP_HAVE_QCMS
if (!WebPDemuxGetChunk(kParams.dmux, "ICCP", 1, &kParams.iccp)) goto Error;
printf("VP8X: Found color profile\n");
#else
fprintf(stderr, "Warning: color profile present, but qcms is unavailable!\n"
"Build libqcms from Mozilla or Chromium and define WEBP_HAVE_QCMS "
"before building.\n");
#endif
}
if (!WebPDemuxGetFrame(kParams.dmux, 1, &kParams.frameiter)) goto Error;
kParams.has_animation = (kParams.frameiter.num_frames > 1);