mirror of
https://github.com/webmproject/libwebp.git
synced 2024-12-27 06:08:21 +01:00
009562b403
The graphical bug happens when there is a frame disposed to background color, followed by another frame that does not blend, and their areas don't fully overlap. Only the previous frame clears its part of the viewport. The fix consists in clearing the screen for the previous and the current frame if needed. Change-Id: I3425cf7297f0c7b2cf13a3a61b517cc0b1c031d8
626 lines
20 KiB
C
626 lines
20 KiB
C
// Copyright 2011 Google Inc. All Rights Reserved.
|
|
//
|
|
// Use of this source code is governed by a BSD-style license
|
|
// that can be found in the COPYING file in the root of the source
|
|
// tree. An additional intellectual property rights grant can be found
|
|
// in the file PATENTS. All contributing project authors may
|
|
// be found in the AUTHORS file in the root of the source tree.
|
|
// -----------------------------------------------------------------------------
|
|
//
|
|
// Simple OpenGL-based WebP file viewer.
|
|
//
|
|
// Author: Skal (pascal.massimino@gmail.com)
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "webp/config.h"
|
|
#endif
|
|
|
|
#if defined(__unix__) || defined(__CYGWIN__)
|
|
#define _POSIX_C_SOURCE 200112L // for setenv
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#if defined(WEBP_HAVE_GL)
|
|
|
|
#if defined(HAVE_GLUT_GLUT_H)
|
|
#include <GLUT/glut.h>
|
|
#else
|
|
#include <GL/glut.h>
|
|
#ifdef FREEGLUT
|
|
#include <GL/freeglut.h>
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef WEBP_HAVE_QCMS
|
|
#include <qcms.h>
|
|
#endif
|
|
|
|
#include "webp/decode.h"
|
|
#include "webp/demux.h"
|
|
|
|
#include "../examples/example_util.h"
|
|
#include "../imageio/imageio_util.h"
|
|
|
|
#if defined(_MSC_VER) && _MSC_VER < 1900
|
|
#define snprintf _snprintf
|
|
#endif
|
|
|
|
// 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 only_deltas;
|
|
int use_color_profile;
|
|
int draw_anim_background_color;
|
|
|
|
int canvas_width, canvas_height;
|
|
int loop_count;
|
|
uint32_t bg_color;
|
|
|
|
const char* file_name;
|
|
WebPData data;
|
|
WebPDecoderConfig config;
|
|
const WebPDecBuffer* pic;
|
|
WebPDemuxer* dmux;
|
|
WebPIterator curr_frame;
|
|
WebPIterator prev_frame;
|
|
WebPChunkIterator iccp;
|
|
int viewport_width, viewport_height;
|
|
} kParams;
|
|
|
|
static void ClearPreviousPic(void) {
|
|
WebPFreeDecBuffer((WebPDecBuffer*)kParams.pic);
|
|
kParams.pic = NULL;
|
|
}
|
|
|
|
static void ClearParams(void) {
|
|
ClearPreviousPic();
|
|
WebPDataClear(&kParams.data);
|
|
WebPDemuxReleaseIterator(&kParams.curr_frame);
|
|
WebPDemuxReleaseIterator(&kParams.prev_frame);
|
|
WebPDemuxReleaseChunkIterator(&kParams.iccp);
|
|
WebPDemuxDelete(kParams.dmux);
|
|
kParams.dmux = NULL;
|
|
}
|
|
|
|
// Sets the previous frame to the dimensions of the canvas and has it dispose
|
|
// to background to cause the canvas to be cleared.
|
|
static void ClearPreviousFrame(void) {
|
|
WebPIterator* const prev = &kParams.prev_frame;
|
|
prev->width = kParams.canvas_width;
|
|
prev->height = kParams.canvas_height;
|
|
prev->x_offset = prev->y_offset = 0;
|
|
prev->dispose_method = WEBP_MUX_DISPOSE_BACKGROUND;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// 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.curr_frame
|
|
const WebPIterator* const curr = &kParams.curr_frame;
|
|
WebPDecoderConfig* const config = &kParams.config;
|
|
WebPDecBuffer* const output_buffer = &config->output;
|
|
int ok = 0;
|
|
|
|
ClearPreviousPic();
|
|
output_buffer->colorspace = MODE_RGBA;
|
|
ok = (WebPDecode(curr->fragment.bytes, curr->fragment.size,
|
|
config) == VP8_STATUS_OK);
|
|
if (!ok) {
|
|
fprintf(stderr, "Decoding of frame #%d failed!\n", curr->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",
|
|
curr->frame_num);
|
|
}
|
|
}
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
static void decode_callback(int what) {
|
|
if (what == 0 && !kParams.done) {
|
|
int duration = 0;
|
|
if (kParams.dmux != NULL) {
|
|
WebPIterator* const curr = &kParams.curr_frame;
|
|
if (!WebPDemuxNextFrame(curr)) {
|
|
WebPDemuxReleaseIterator(curr);
|
|
if (WebPDemuxGetFrame(kParams.dmux, 1, curr)) {
|
|
--kParams.loop_count;
|
|
kParams.done = (kParams.loop_count == 0);
|
|
if (kParams.done) return;
|
|
ClearPreviousFrame();
|
|
} else {
|
|
kParams.decoding_error = 1;
|
|
kParams.done = 1;
|
|
return;
|
|
}
|
|
}
|
|
duration = curr->duration;
|
|
// Behavior copied from Chrome, cf:
|
|
// https://cs.chromium.org/chromium/src/third_party/WebKit/Source/
|
|
// platform/graphics/DeferredImageDecoder.cpp?
|
|
// rcl=b4c33049f096cd283f32be9a58b9a9e768227c26&l=246
|
|
if (duration <= 10) duration = 100;
|
|
}
|
|
if (!Decode()) {
|
|
kParams.decoding_error = 1;
|
|
kParams.done = 1;
|
|
} else {
|
|
glutPostRedisplay();
|
|
glutTimerFunc(duration, decode_callback, what);
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Callbacks
|
|
|
|
static void HandleKey(unsigned char key, int pos_x, int pos_y) {
|
|
// Note: rescaling the window or toggling some features during an animation
|
|
// generates visual artifacts. This is not fixed because refreshing the frame
|
|
// may require rendering the whole animation from start till current frame.
|
|
(void)pos_x;
|
|
(void)pos_y;
|
|
if (key == 'q' || key == 'Q' || key == 27 /* Esc */) {
|
|
#ifdef FREEGLUT
|
|
glutLeaveMainLoop();
|
|
#else
|
|
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 == 'b') {
|
|
kParams.draw_anim_background_color = 1 - kParams.draw_anim_background_color;
|
|
if (!kParams.has_animation) ClearPreviousFrame();
|
|
glutPostRedisplay();
|
|
} else if (key == 'i') {
|
|
kParams.print_info = 1 - kParams.print_info;
|
|
if (!kParams.has_animation) ClearPreviousFrame();
|
|
glutPostRedisplay();
|
|
} else if (key == 'd') {
|
|
kParams.only_deltas = 1 - kParams.only_deltas;
|
|
glutPostRedisplay();
|
|
}
|
|
}
|
|
|
|
static void HandleReshape(int width, int height) {
|
|
// Note: reshape doesn't preserve aspect ratio, and might
|
|
// be handling larger-than-screen pictures incorrectly.
|
|
glViewport(0, 0, width, height);
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glLoadIdentity();
|
|
kParams.viewport_width = width;
|
|
kParams.viewport_height = height;
|
|
if (!kParams.has_animation) ClearPreviousFrame();
|
|
}
|
|
|
|
static void PrintString(const char* const text) {
|
|
void* const font = GLUT_BITMAP_9_BY_15;
|
|
int i;
|
|
for (i = 0; text[i]; ++i) {
|
|
glutBitmapCharacter(font, text[i]);
|
|
}
|
|
}
|
|
|
|
static float GetColorf(uint32_t color, int shift) {
|
|
return ((color >> shift) & 0xff) / 255.f;
|
|
}
|
|
|
|
static void DrawCheckerBoard(void) {
|
|
const int square_size = 8; // must be a power of 2
|
|
int x, y;
|
|
GLint viewport[4]; // x, y, width, height
|
|
|
|
glPushMatrix();
|
|
|
|
glGetIntegerv(GL_VIEWPORT, viewport);
|
|
// shift to integer coordinates with (0,0) being top-left.
|
|
glOrtho(0, viewport[2], viewport[3], 0, -1, 1);
|
|
for (y = 0; y < viewport[3]; y += square_size) {
|
|
for (x = 0; x < viewport[2]; x += square_size) {
|
|
const GLubyte color = 128 + 64 * (!((x + y) & square_size));
|
|
glColor3ub(color, color, color);
|
|
glRecti(x, y, x + square_size, y + square_size);
|
|
}
|
|
}
|
|
glPopMatrix();
|
|
}
|
|
|
|
static void DrawBackground(void) {
|
|
// Whole window cleared with clear color, checkerboard rendered on top of it.
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
DrawCheckerBoard();
|
|
|
|
// ANIM background color rendered (blend) on top. Default is white for still
|
|
// images (without ANIM chunk). glClear() can't be used for that (no blend).
|
|
if (kParams.draw_anim_background_color) {
|
|
glPushMatrix();
|
|
glLoadIdentity();
|
|
glColor4f(GetColorf(kParams.bg_color, 16), // BGRA from spec
|
|
GetColorf(kParams.bg_color, 8),
|
|
GetColorf(kParams.bg_color, 0),
|
|
GetColorf(kParams.bg_color, 24));
|
|
glRecti(-1, -1, +1, +1);
|
|
glPopMatrix();
|
|
}
|
|
}
|
|
|
|
// Draw background in a scissored rectangle.
|
|
static void DrawBackgroundScissored(int window_x, int window_y, int frame_w,
|
|
int frame_h) {
|
|
// Only update the requested area, not the whole canvas.
|
|
window_x = window_x * kParams.viewport_width / kParams.canvas_width;
|
|
window_y = window_y * kParams.viewport_height / kParams.canvas_height;
|
|
frame_w = frame_w * kParams.viewport_width / kParams.canvas_width;
|
|
frame_h = frame_h * kParams.viewport_height / kParams.canvas_height;
|
|
|
|
// glScissor() takes window coordinates (0,0 at bottom left).
|
|
window_y = kParams.viewport_height - window_y - frame_h;
|
|
|
|
glEnable(GL_SCISSOR_TEST);
|
|
glScissor(window_x, window_y, frame_w, frame_h);
|
|
DrawBackground();
|
|
glDisable(GL_SCISSOR_TEST);
|
|
}
|
|
|
|
static void HandleDisplay(void) {
|
|
const WebPDecBuffer* const pic = kParams.pic;
|
|
const WebPIterator* const curr = &kParams.curr_frame;
|
|
WebPIterator* const prev = &kParams.prev_frame;
|
|
GLfloat xoff, yoff;
|
|
if (pic == NULL) return;
|
|
glPushMatrix();
|
|
glPixelZoom((GLfloat)(+1. / kParams.canvas_width * kParams.viewport_width),
|
|
(GLfloat)(-1. / kParams.canvas_height * kParams.viewport_height));
|
|
xoff = (GLfloat)(2. * curr->x_offset / kParams.canvas_width);
|
|
yoff = (GLfloat)(2. * curr->y_offset / kParams.canvas_height);
|
|
glRasterPos2f(-1.f + xoff, 1.f - yoff);
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, pic->u.RGBA.stride / 4);
|
|
|
|
if (kParams.only_deltas) {
|
|
DrawBackground();
|
|
} else {
|
|
// The rectangle of the previous frame might be different than the current
|
|
// frame, so we may need to DrawBackgroundScissored for both.
|
|
if (prev->dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) {
|
|
// Clear the previous frame rectangle.
|
|
DrawBackgroundScissored(prev->x_offset, prev->y_offset, prev->width,
|
|
prev->height);
|
|
}
|
|
if (curr->blend_method == WEBP_MUX_NO_BLEND) {
|
|
// We simulate no-blending behavior by first clearing the current frame
|
|
// rectangle and then alpha-blending against it.
|
|
DrawBackgroundScissored(curr->x_offset, curr->y_offset, curr->width,
|
|
curr->height);
|
|
}
|
|
}
|
|
|
|
*prev = *curr;
|
|
|
|
glDrawPixels(pic->width, pic->height,
|
|
GL_RGBA, GL_UNSIGNED_BYTE,
|
|
(GLvoid*)pic->u.RGBA.rgba);
|
|
if (kParams.print_info) {
|
|
char tmp[32];
|
|
|
|
glColor4f(0.90f, 0.0f, 0.90f, 1.0f);
|
|
glRasterPos2f(-0.95f, 0.90f);
|
|
PrintString(kParams.file_name);
|
|
|
|
snprintf(tmp, sizeof(tmp), "Dimension:%d x %d", pic->width, pic->height);
|
|
glColor4f(0.90f, 0.0f, 0.90f, 1.0f);
|
|
glRasterPos2f(-0.95f, 0.80f);
|
|
PrintString(tmp);
|
|
if (curr->x_offset != 0 || curr->y_offset != 0) {
|
|
snprintf(tmp, sizeof(tmp), " (offset:%d,%d)",
|
|
curr->x_offset, curr->y_offset);
|
|
glRasterPos2f(-0.95f, 0.70f);
|
|
PrintString(tmp);
|
|
}
|
|
}
|
|
glPopMatrix();
|
|
#if defined(__APPLE__) || defined(_WIN32)
|
|
glFlush();
|
|
#else
|
|
glutSwapBuffers();
|
|
#endif
|
|
}
|
|
|
|
static void StartDisplay(void) {
|
|
const int width = kParams.canvas_width;
|
|
const int height = kParams.canvas_height;
|
|
// TODO(webp:365) GLUT_DOUBLE results in flickering / old frames to be
|
|
// partially displayed with animated webp + alpha.
|
|
#if defined(__APPLE__) || defined(_WIN32)
|
|
glutInitDisplayMode(GLUT_RGBA);
|
|
#else
|
|
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
|
|
#endif
|
|
glutInitWindowSize(width, height);
|
|
glutCreateWindow("WebP viewer");
|
|
glutDisplayFunc(HandleDisplay);
|
|
glutReshapeFunc(HandleReshape);
|
|
glutIdleFunc(NULL);
|
|
glutKeyboardFunc(HandleKey);
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
glEnable(GL_BLEND);
|
|
glClearColor(0, 0, 0, 0); // window will be cleared to black (no blend)
|
|
DrawBackground();
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Main
|
|
|
|
static void Help(void) {
|
|
printf(
|
|
"Usage: vwebp in_file [options]\n\n"
|
|
"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"
|
|
" -dither <int> dithering strength (0..100), default=50\n"
|
|
" -noalphadither disable alpha plane dithering\n"
|
|
" -usebgcolor .. display background color\n"
|
|
" -mt .......... use multi-threading\n"
|
|
" -info ........ print info\n"
|
|
" -h ........... this help message\n"
|
|
"\n"
|
|
"Keyboard shortcuts:\n"
|
|
" 'c' ................ toggle use of color profile\n"
|
|
" 'b' ................ toggle background color display\n"
|
|
" 'i' ................ overlay file information\n"
|
|
" 'd' ................ disable blending & disposal (debug)\n"
|
|
" 'q' / 'Q' / ESC .... quit\n");
|
|
}
|
|
|
|
int main(int argc, char *argv[]) {
|
|
int c;
|
|
WebPDecoderConfig* const config = &kParams.config;
|
|
WebPIterator* const curr = &kParams.curr_frame;
|
|
|
|
if (!WebPInitDecoderConfig(config)) {
|
|
fprintf(stderr, "Library version mismatch!\n");
|
|
return -1;
|
|
}
|
|
config->options.dithering_strength = 50;
|
|
config->options.alpha_dithering_strength = 100;
|
|
kParams.use_color_profile = 1;
|
|
// Background color hidden by default to see transparent areas.
|
|
kParams.draw_anim_background_color = 0;
|
|
|
|
for (c = 1; c < argc; ++c) {
|
|
int parse_error = 0;
|
|
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")) {
|
|
config->options.bypass_filtering = 1;
|
|
} else if (!strcmp(argv[c], "-noalphadither")) {
|
|
config->options.alpha_dithering_strength = 0;
|
|
} else if (!strcmp(argv[c], "-usebgcolor")) {
|
|
kParams.draw_anim_background_color = 1;
|
|
} else if (!strcmp(argv[c], "-dither") && c + 1 < argc) {
|
|
config->options.dithering_strength =
|
|
ExUtilGetInt(argv[++c], 0, &parse_error);
|
|
} else if (!strcmp(argv[c], "-info")) {
|
|
kParams.print_info = 1;
|
|
} else if (!strcmp(argv[c], "-version")) {
|
|
const int dec_version = WebPGetDecoderVersion();
|
|
const int dmux_version = WebPGetDemuxVersion();
|
|
printf("WebP Decoder version: %d.%d.%d\nWebP Demux version: %d.%d.%d\n",
|
|
(dec_version >> 16) & 0xff, (dec_version >> 8) & 0xff,
|
|
dec_version & 0xff, (dmux_version >> 16) & 0xff,
|
|
(dmux_version >> 8) & 0xff, dmux_version & 0xff);
|
|
return 0;
|
|
} else if (!strcmp(argv[c], "-mt")) {
|
|
config->options.use_threads = 1;
|
|
} else if (!strcmp(argv[c], "--")) {
|
|
if (c < argc - 1) kParams.file_name = argv[++c];
|
|
break;
|
|
} else if (argv[c][0] == '-') {
|
|
printf("Unknown option '%s'\n", argv[c]);
|
|
Help();
|
|
return -1;
|
|
} else {
|
|
kParams.file_name = argv[c];
|
|
}
|
|
|
|
if (parse_error) {
|
|
Help();
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (kParams.file_name == NULL) {
|
|
printf("missing input file!!\n");
|
|
Help();
|
|
return 0;
|
|
}
|
|
|
|
if (!ImgIoUtilReadFile(kParams.file_name,
|
|
&kParams.data.bytes, &kParams.data.size)) {
|
|
goto Error;
|
|
}
|
|
|
|
if (!WebPGetInfo(kParams.data.bytes, kParams.data.size, NULL, NULL)) {
|
|
fprintf(stderr, "Input file doesn't appear to be WebP format.\n");
|
|
goto Error;
|
|
}
|
|
|
|
kParams.dmux = WebPDemux(&kParams.data);
|
|
if (kParams.dmux == NULL) {
|
|
fprintf(stderr, "Could not create demuxing object!\n");
|
|
goto Error;
|
|
}
|
|
|
|
kParams.canvas_width = WebPDemuxGetI(kParams.dmux, WEBP_FF_CANVAS_WIDTH);
|
|
kParams.canvas_height = WebPDemuxGetI(kParams.dmux, WEBP_FF_CANVAS_HEIGHT);
|
|
if (kParams.print_info) {
|
|
printf("Canvas: %d x %d\n", kParams.canvas_width, kParams.canvas_height);
|
|
}
|
|
|
|
ClearPreviousFrame();
|
|
|
|
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, curr)) goto Error;
|
|
|
|
kParams.has_animation = (curr->num_frames > 1);
|
|
kParams.loop_count = (int)WebPDemuxGetI(kParams.dmux, WEBP_FF_LOOP_COUNT);
|
|
kParams.bg_color = WebPDemuxGetI(kParams.dmux, WEBP_FF_BACKGROUND_COLOR);
|
|
printf("VP8X: Found %d images in file (loop count = %d)\n",
|
|
curr->num_frames, kParams.loop_count);
|
|
|
|
// Decode first frame
|
|
if (!Decode()) goto Error;
|
|
|
|
// Position iterator to last frame. Next call to HandleDisplay will wrap over.
|
|
// We take this into account by bumping up loop_count.
|
|
WebPDemuxGetFrame(kParams.dmux, 0, curr);
|
|
if (kParams.loop_count) ++kParams.loop_count;
|
|
|
|
#if defined(__unix__) || defined(__CYGWIN__)
|
|
// Work around GLUT compositor bug.
|
|
// https://bugs.launchpad.net/ubuntu/+source/freeglut/+bug/369891
|
|
setenv("XLIB_SKIP_ARGB_VISUALS", "1", 1);
|
|
#endif
|
|
|
|
// Start display (and timer)
|
|
glutInit(&argc, argv);
|
|
#ifdef FREEGLUT
|
|
glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_CONTINUE_EXECUTION);
|
|
#endif
|
|
StartDisplay();
|
|
|
|
if (kParams.has_animation) glutTimerFunc(0, decode_callback, 0);
|
|
glutMainLoop();
|
|
|
|
// Should only be reached when using FREEGLUT:
|
|
ClearParams();
|
|
return 0;
|
|
|
|
Error:
|
|
ClearParams();
|
|
return -1;
|
|
}
|
|
|
|
#else // !WEBP_HAVE_GL
|
|
|
|
int main(int argc, const char *argv[]) {
|
|
fprintf(stderr, "OpenGL support not enabled in %s.\n", argv[0]);
|
|
(void)argc;
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
//------------------------------------------------------------------------------
|