go-libwebp

Experimental translation from libwebp to Go source.
Log | Files | Refs | README | LICENSE

vwebp.c (21673B)


      1 // Copyright 2011 Google Inc. All Rights Reserved.
      2 //
      3 // Use of this source code is governed by a BSD-style license
      4 // that can be found in the COPYING file in the root of the source
      5 // tree. An additional intellectual property rights grant can be found
      6 // in the file PATENTS. All contributing project authors may
      7 // be found in the AUTHORS file in the root of the source tree.
      8 // -----------------------------------------------------------------------------
      9 //
     10 //  Simple OpenGL-based WebP file viewer.
     11 //
     12 // Author: Skal (pascal.massimino@gmail.com)
     13 #ifdef HAVE_CONFIG_H
     14 #include "webp/config.h"
     15 #endif
     16 
     17 #if defined(__unix__) || defined(__CYGWIN__)
     18 #define _POSIX_C_SOURCE 200112L  // for setenv
     19 #endif
     20 
     21 #include <assert.h>
     22 #include <stdio.h>
     23 #include <stdlib.h>
     24 #include <string.h>
     25 
     26 #if defined(WEBP_HAVE_GL)
     27 
     28 #if defined(HAVE_GLUT_GLUT_H)
     29 #include <GLUT/glut.h>
     30 #else
     31 #include <GL/glut.h>
     32 #ifdef FREEGLUT
     33 #include <GL/freeglut.h>
     34 #endif
     35 #endif
     36 
     37 #ifdef WEBP_HAVE_QCMS
     38 #include <qcms.h>
     39 #endif
     40 
     41 #include "webp/decode.h"
     42 #include "webp/demux.h"
     43 
     44 #include "../examples/example_util.h"
     45 #include "../imageio/imageio_util.h"
     46 #include "./unicode.h"
     47 
     48 #if defined(_MSC_VER) && _MSC_VER < 1900
     49 #define snprintf _snprintf
     50 #endif
     51 
     52 // Unfortunate global variables. Gathered into a struct for comfort.
     53 static struct {
     54   int has_animation;
     55   int has_color_profile;
     56   int done;
     57   int decoding_error;
     58   int print_info;
     59   int only_deltas;
     60   int use_color_profile;
     61   int draw_anim_background_color;
     62 
     63   int canvas_width, canvas_height;
     64   int loop_count;
     65   uint32_t bg_color;
     66 
     67   const char* file_name;
     68   WebPData data;
     69   WebPDecoderConfig config;
     70   const WebPDecBuffer* pic;
     71   WebPDemuxer* dmux;
     72   WebPIterator curr_frame;
     73   WebPIterator prev_frame;
     74   WebPChunkIterator iccp;
     75   int viewport_width, viewport_height;
     76 } kParams;
     77 
     78 static void ClearPreviousPic(void) {
     79   WebPFreeDecBuffer((WebPDecBuffer*)kParams.pic);
     80   kParams.pic = NULL;
     81 }
     82 
     83 static void ClearParams(void) {
     84   ClearPreviousPic();
     85   WebPDataClear(&kParams.data);
     86   WebPDemuxReleaseIterator(&kParams.curr_frame);
     87   WebPDemuxReleaseIterator(&kParams.prev_frame);
     88   WebPDemuxReleaseChunkIterator(&kParams.iccp);
     89   WebPDemuxDelete(kParams.dmux);
     90   kParams.dmux = NULL;
     91 }
     92 
     93 // Sets the previous frame to the dimensions of the canvas and has it dispose
     94 // to background to cause the canvas to be cleared.
     95 static void ClearPreviousFrame(void) {
     96   WebPIterator* const prev = &kParams.prev_frame;
     97   prev->width = kParams.canvas_width;
     98   prev->height = kParams.canvas_height;
     99   prev->x_offset = prev->y_offset = 0;
    100   prev->dispose_method = WEBP_MUX_DISPOSE_BACKGROUND;
    101 }
    102 
    103 // -----------------------------------------------------------------------------
    104 // Color profile handling
    105 static int ApplyColorProfile(const WebPData* const profile,
    106                              WebPDecBuffer* const rgba) {
    107 #ifdef WEBP_HAVE_QCMS
    108   int i, ok = 0;
    109   uint8_t* line;
    110   uint8_t major_revision;
    111   qcms_profile* input_profile = NULL;
    112   qcms_profile* output_profile = NULL;
    113   qcms_transform* transform = NULL;
    114   const qcms_data_type input_type = QCMS_DATA_RGBA_8;
    115   const qcms_data_type output_type = QCMS_DATA_RGBA_8;
    116   const qcms_intent intent = QCMS_INTENT_DEFAULT;
    117 
    118   if (profile == NULL || rgba == NULL) return 0;
    119   if (profile->bytes == NULL || profile->size < 10) return 1;
    120   major_revision = profile->bytes[8];
    121 
    122   qcms_enable_iccv4();
    123   input_profile = qcms_profile_from_memory(profile->bytes, profile->size);
    124   // qcms_profile_is_bogus() is broken with ICCv4.
    125   if (input_profile == NULL ||
    126       (major_revision < 4 && qcms_profile_is_bogus(input_profile))) {
    127     fprintf(stderr, "Color profile is bogus!\n");
    128     goto Error;
    129   }
    130 
    131   output_profile = qcms_profile_sRGB();
    132   if (output_profile == NULL) {
    133     fprintf(stderr, "Error creating output color profile!\n");
    134     goto Error;
    135   }
    136 
    137   qcms_profile_precache_output_transform(output_profile);
    138   transform = qcms_transform_create(input_profile, input_type,
    139                                     output_profile, output_type,
    140                                     intent);
    141   if (transform == NULL) {
    142     fprintf(stderr, "Error creating color transform!\n");
    143     goto Error;
    144   }
    145 
    146   line = rgba->u.RGBA.rgba;
    147   for (i = 0; i < rgba->height; ++i, line += rgba->u.RGBA.stride) {
    148     qcms_transform_data(transform, line, line, rgba->width);
    149   }
    150   ok = 1;
    151 
    152  Error:
    153   if (input_profile != NULL) qcms_profile_release(input_profile);
    154   if (output_profile != NULL) qcms_profile_release(output_profile);
    155   if (transform != NULL) qcms_transform_release(transform);
    156   return ok;
    157 #else
    158   (void)profile;
    159   (void)rgba;
    160   return 1;
    161 #endif  // WEBP_HAVE_QCMS
    162 }
    163 
    164 //------------------------------------------------------------------------------
    165 // File decoding
    166 
    167 static int Decode(void) {   // Fills kParams.curr_frame
    168   const WebPIterator* const curr = &kParams.curr_frame;
    169   WebPDecoderConfig* const config = &kParams.config;
    170   WebPDecBuffer* const output_buffer = &config->output;
    171   int ok = 0;
    172 
    173   ClearPreviousPic();
    174   output_buffer->colorspace = MODE_RGBA;
    175   ok = (WebPDecode(curr->fragment.bytes, curr->fragment.size,
    176                    config) == VP8_STATUS_OK);
    177   if (!ok) {
    178     fprintf(stderr, "Decoding of frame #%d failed!\n", curr->frame_num);
    179   } else {
    180     kParams.pic = output_buffer;
    181     if (kParams.use_color_profile) {
    182       ok = ApplyColorProfile(&kParams.iccp.chunk, output_buffer);
    183       if (!ok) {
    184         fprintf(stderr, "Applying color profile to frame #%d failed!\n",
    185                 curr->frame_num);
    186       }
    187     }
    188   }
    189   return ok;
    190 }
    191 
    192 static void decode_callback(int what) {
    193   if (what == 0 && !kParams.done) {
    194     int duration = 0;
    195     if (kParams.dmux != NULL) {
    196       WebPIterator* const curr = &kParams.curr_frame;
    197       if (!WebPDemuxNextFrame(curr)) {
    198         WebPDemuxReleaseIterator(curr);
    199         if (WebPDemuxGetFrame(kParams.dmux, 1, curr)) {
    200           --kParams.loop_count;
    201           kParams.done = (kParams.loop_count == 0);
    202           if (kParams.done) return;
    203           ClearPreviousFrame();
    204         } else {
    205           kParams.decoding_error = 1;
    206           kParams.done = 1;
    207           return;
    208         }
    209       }
    210       duration = curr->duration;
    211       // Behavior copied from Chrome, cf:
    212       // https://cs.chromium.org/chromium/src/third_party/WebKit/Source/
    213       // platform/graphics/DeferredImageDecoder.cpp?
    214       // rcl=b4c33049f096cd283f32be9a58b9a9e768227c26&l=246
    215       if (duration <= 10) duration = 100;
    216     }
    217     if (!Decode()) {
    218       kParams.decoding_error = 1;
    219       kParams.done = 1;
    220     } else {
    221       glutPostRedisplay();
    222       glutTimerFunc(duration, decode_callback, what);
    223     }
    224   }
    225 }
    226 
    227 //------------------------------------------------------------------------------
    228 // Callbacks
    229 
    230 static void HandleKey(unsigned char key, int pos_x, int pos_y) {
    231   // Note: rescaling the window or toggling some features during an animation
    232   // generates visual artifacts. This is not fixed because refreshing the frame
    233   // may require rendering the whole animation from start till current frame.
    234   (void)pos_x;
    235   (void)pos_y;
    236   if (key == 'q' || key == 'Q' || key == 27 /* Esc */) {
    237 #ifdef FREEGLUT
    238     glutLeaveMainLoop();
    239 #else
    240     ClearParams();
    241     exit(0);
    242 #endif
    243   } else if (key == 'c') {
    244     if (kParams.has_color_profile && !kParams.decoding_error) {
    245       kParams.use_color_profile = 1 - kParams.use_color_profile;
    246 
    247       if (kParams.has_animation) {
    248         // Restart the completed animation to pickup the color profile change.
    249         if (kParams.done && kParams.loop_count == 0) {
    250           kParams.loop_count =
    251               (int)WebPDemuxGetI(kParams.dmux, WEBP_FF_LOOP_COUNT) + 1;
    252           kParams.done = 0;
    253           // Start the decode loop immediately.
    254           glutTimerFunc(0, decode_callback, 0);
    255         }
    256       } else {
    257         Decode();
    258         glutPostRedisplay();
    259       }
    260     }
    261   } else if (key == 'b') {
    262     kParams.draw_anim_background_color = 1 - kParams.draw_anim_background_color;
    263     if (!kParams.has_animation) ClearPreviousFrame();
    264     glutPostRedisplay();
    265   } else if (key == 'i') {
    266     kParams.print_info = 1 - kParams.print_info;
    267     if (!kParams.has_animation) ClearPreviousFrame();
    268     glutPostRedisplay();
    269   } else if (key == 'd') {
    270     kParams.only_deltas = 1 - kParams.only_deltas;
    271     glutPostRedisplay();
    272   }
    273 }
    274 
    275 static void HandleReshape(int width, int height) {
    276   // Note: reshape doesn't preserve aspect ratio, and might
    277   // be handling larger-than-screen pictures incorrectly.
    278   glViewport(0, 0, width, height);
    279   glMatrixMode(GL_PROJECTION);
    280   glLoadIdentity();
    281   glMatrixMode(GL_MODELVIEW);
    282   glLoadIdentity();
    283   kParams.viewport_width = width;
    284   kParams.viewport_height = height;
    285   if (!kParams.has_animation) ClearPreviousFrame();
    286 }
    287 
    288 static void PrintString(const char* const text) {
    289   void* const font = GLUT_BITMAP_9_BY_15;
    290   int i;
    291   for (i = 0; text[i]; ++i) {
    292     glutBitmapCharacter(font, text[i]);
    293   }
    294 }
    295 
    296 static void PrintStringW(const char* const text) {
    297 #if defined(_WIN32) && defined(_UNICODE)
    298   void* const font = GLUT_BITMAP_9_BY_15;
    299   const W_CHAR* const wtext = (const W_CHAR*)text;
    300   int i;
    301   for (i = 0; wtext[i]; ++i) {
    302     glutBitmapCharacter(font, wtext[i]);
    303   }
    304 #else
    305   PrintString(text);
    306 #endif
    307 }
    308 
    309 static float GetColorf(uint32_t color, int shift) {
    310   return ((color >> shift) & 0xff) / 255.f;
    311 }
    312 
    313 static void DrawCheckerBoard(void) {
    314   const int square_size = 8;  // must be a power of 2
    315   int x, y;
    316   GLint viewport[4];  // x, y, width, height
    317 
    318   glPushMatrix();
    319 
    320   glGetIntegerv(GL_VIEWPORT, viewport);
    321   // shift to integer coordinates with (0,0) being top-left.
    322   glOrtho(0, viewport[2], viewport[3], 0, -1, 1);
    323   for (y = 0; y < viewport[3]; y += square_size) {
    324     for (x = 0; x < viewport[2]; x += square_size) {
    325       const GLubyte color = 128 + 64 * (!((x + y) & square_size));
    326       glColor3ub(color, color, color);
    327       glRecti(x, y, x + square_size, y + square_size);
    328     }
    329   }
    330   glPopMatrix();
    331 }
    332 
    333 static void DrawBackground(void) {
    334   // Whole window cleared with clear color, checkerboard rendered on top of it.
    335   glClear(GL_COLOR_BUFFER_BIT);
    336   DrawCheckerBoard();
    337 
    338   // ANIM background color rendered (blend) on top. Default is white for still
    339   // images (without ANIM chunk). glClear() can't be used for that (no blend).
    340   if (kParams.draw_anim_background_color) {
    341     glPushMatrix();
    342     glLoadIdentity();
    343     glColor4f(GetColorf(kParams.bg_color, 16),  // BGRA from spec
    344               GetColorf(kParams.bg_color, 8),
    345               GetColorf(kParams.bg_color, 0),
    346               GetColorf(kParams.bg_color, 24));
    347     glRecti(-1, -1, +1, +1);
    348     glPopMatrix();
    349   }
    350 }
    351 
    352 // Draw background in a scissored rectangle.
    353 static void DrawBackgroundScissored(int window_x, int window_y, int frame_w,
    354                                     int frame_h) {
    355   // Only update the requested area, not the whole canvas.
    356   window_x = window_x * kParams.viewport_width / kParams.canvas_width;
    357   window_y = window_y * kParams.viewport_height / kParams.canvas_height;
    358   frame_w = frame_w * kParams.viewport_width / kParams.canvas_width;
    359   frame_h = frame_h * kParams.viewport_height / kParams.canvas_height;
    360 
    361   // glScissor() takes window coordinates (0,0 at bottom left).
    362   window_y = kParams.viewport_height - window_y - frame_h;
    363 
    364   glEnable(GL_SCISSOR_TEST);
    365   glScissor(window_x, window_y, frame_w, frame_h);
    366   DrawBackground();
    367   glDisable(GL_SCISSOR_TEST);
    368 }
    369 
    370 static void HandleDisplay(void) {
    371   const WebPDecBuffer* const pic = kParams.pic;
    372   const WebPIterator* const curr = &kParams.curr_frame;
    373   WebPIterator* const prev = &kParams.prev_frame;
    374   GLfloat xoff, yoff;
    375   if (pic == NULL) return;
    376   glPushMatrix();
    377   glPixelZoom((GLfloat)(+1. / kParams.canvas_width * kParams.viewport_width),
    378               (GLfloat)(-1. / kParams.canvas_height * kParams.viewport_height));
    379   xoff = (GLfloat)(2. * curr->x_offset / kParams.canvas_width);
    380   yoff = (GLfloat)(2. * curr->y_offset / kParams.canvas_height);
    381   glRasterPos2f(-1.f + xoff, 1.f - yoff);
    382   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    383   glPixelStorei(GL_UNPACK_ROW_LENGTH, pic->u.RGBA.stride / 4);
    384 
    385   if (kParams.only_deltas) {
    386     DrawBackground();
    387   } else {
    388     // The rectangle of the previous frame might be different than the current
    389     // frame, so we may need to DrawBackgroundScissored for both.
    390     if (prev->dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) {
    391       // Clear the previous frame rectangle.
    392       DrawBackgroundScissored(prev->x_offset, prev->y_offset, prev->width,
    393                               prev->height);
    394     }
    395     if (curr->blend_method == WEBP_MUX_NO_BLEND) {
    396       // We simulate no-blending behavior by first clearing the current frame
    397       // rectangle and then alpha-blending against it.
    398       DrawBackgroundScissored(curr->x_offset, curr->y_offset, curr->width,
    399                               curr->height);
    400     }
    401   }
    402 
    403   *prev = *curr;
    404 
    405   glDrawPixels(pic->width, pic->height,
    406                GL_RGBA, GL_UNSIGNED_BYTE,
    407                (GLvoid*)pic->u.RGBA.rgba);
    408   if (kParams.print_info) {
    409     char tmp[32];
    410 
    411     glColor4f(0.90f, 0.0f, 0.90f, 1.0f);
    412     glRasterPos2f(-0.95f, 0.90f);
    413     PrintStringW(kParams.file_name);
    414 
    415     snprintf(tmp, sizeof(tmp), "Dimension:%d x %d", pic->width, pic->height);
    416     glColor4f(0.90f, 0.0f, 0.90f, 1.0f);
    417     glRasterPos2f(-0.95f, 0.80f);
    418     PrintString(tmp);
    419     if (curr->x_offset != 0 || curr->y_offset != 0) {
    420       snprintf(tmp, sizeof(tmp), " (offset:%d,%d)",
    421                curr->x_offset, curr->y_offset);
    422       glRasterPos2f(-0.95f, 0.70f);
    423       PrintString(tmp);
    424     }
    425   }
    426   glPopMatrix();
    427 #if defined(__APPLE__) || defined(_WIN32)
    428   glFlush();
    429 #else
    430   glutSwapBuffers();
    431 #endif
    432 }
    433 
    434 static void StartDisplay(const char* filename) {
    435   int width = kParams.canvas_width;
    436   int height = kParams.canvas_height;
    437   int screen_width, screen_height;
    438   const char viewername[] = " - WebP viewer";
    439   // max linux file len + viewername string
    440   char title[4096 + sizeof(viewername)] = "";
    441   // TODO(webp:365) GLUT_DOUBLE results in flickering / old frames to be
    442   // partially displayed with animated webp + alpha.
    443 #if defined(__APPLE__) || defined(_WIN32)
    444   glutInitDisplayMode(GLUT_RGBA);
    445 #else
    446   glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
    447 #endif
    448   screen_width = glutGet(GLUT_SCREEN_WIDTH);
    449   screen_height = glutGet(GLUT_SCREEN_HEIGHT);
    450   if (width > screen_width || height > screen_height) {
    451     if (width > screen_width) {
    452       height = (height * screen_width + width - 1) / width;
    453       width = screen_width;
    454     }
    455     if (height > screen_height) {
    456       width = (width * screen_height + height - 1) / height;
    457       height = screen_height;
    458     }
    459   }
    460   snprintf(title, sizeof(title), "%s%s", filename, viewername);
    461   glutInitWindowSize(width, height);
    462   glutCreateWindow(title);
    463   glutDisplayFunc(HandleDisplay);
    464   glutReshapeFunc(HandleReshape);
    465   glutIdleFunc(NULL);
    466   glutKeyboardFunc(HandleKey);
    467   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    468   glEnable(GL_BLEND);
    469   glClearColor(0, 0, 0, 0);  // window will be cleared to black (no blend)
    470   DrawBackground();
    471 }
    472 
    473 //------------------------------------------------------------------------------
    474 // Main
    475 
    476 static void Help(void) {
    477   printf(
    478       "Usage: vwebp in_file [options]\n\n"
    479       "Decodes the WebP image file and visualize it using OpenGL\n"
    480       "Options are:\n"
    481       "  -version ..... print version number and exit\n"
    482       "  -noicc ....... don't use the icc profile if present\n"
    483       "  -nofancy ..... don't use the fancy YUV420 upscaler\n"
    484       "  -nofilter .... disable in-loop filtering\n"
    485       "  -dither <int>  dithering strength (0..100), default=50\n"
    486       "  -noalphadither disable alpha plane dithering\n"
    487       "  -usebgcolor .. display background color\n"
    488       "  -mt .......... use multi-threading\n"
    489       "  -info ........ print info\n"
    490       "  -h ........... this help message\n"
    491       "\n"
    492       "Keyboard shortcuts:\n"
    493       "  'c' ................ toggle use of color profile\n"
    494       "  'b' ................ toggle background color display\n"
    495       "  'i' ................ overlay file information\n"
    496       "  'd' ................ disable blending & disposal (debug)\n"
    497       "  'q' / 'Q' / ESC .... quit\n");
    498 }
    499 
    500 int main(int argc, char* argv[]) {
    501   int c, file_name_argv_index = 1;
    502   WebPDecoderConfig* const config = &kParams.config;
    503   WebPIterator* const curr = &kParams.curr_frame;
    504 
    505   INIT_WARGV(argc, argv);
    506 
    507   if (!WebPInitDecoderConfig(config)) {
    508     fprintf(stderr, "Library version mismatch!\n");
    509     FREE_WARGV_AND_RETURN(EXIT_FAILURE);
    510   }
    511   config->options.dithering_strength = 50;
    512   config->options.alpha_dithering_strength = 100;
    513   kParams.use_color_profile = 1;
    514   // Background color hidden by default to see transparent areas.
    515   kParams.draw_anim_background_color = 0;
    516 
    517   for (c = 1; c < argc; ++c) {
    518     int parse_error = 0;
    519     if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
    520       Help();
    521       FREE_WARGV_AND_RETURN(EXIT_SUCCESS);
    522     } else if (!strcmp(argv[c], "-noicc")) {
    523       kParams.use_color_profile = 0;
    524     } else if (!strcmp(argv[c], "-nofancy")) {
    525       config->options.no_fancy_upsampling = 1;
    526     } else if (!strcmp(argv[c], "-nofilter")) {
    527       config->options.bypass_filtering = 1;
    528     } else if (!strcmp(argv[c], "-noalphadither")) {
    529       config->options.alpha_dithering_strength = 0;
    530     } else if (!strcmp(argv[c], "-usebgcolor")) {
    531       kParams.draw_anim_background_color = 1;
    532     } else if (!strcmp(argv[c], "-dither") && c + 1 < argc) {
    533       config->options.dithering_strength =
    534           ExUtilGetInt(argv[++c], 0, &parse_error);
    535     } else if (!strcmp(argv[c], "-info")) {
    536       kParams.print_info = 1;
    537     } else if (!strcmp(argv[c], "-version")) {
    538       const int dec_version = WebPGetDecoderVersion();
    539       const int dmux_version = WebPGetDemuxVersion();
    540       printf("WebP Decoder version: %d.%d.%d\nWebP Demux version: %d.%d.%d\n",
    541              (dec_version >> 16) & 0xff, (dec_version >> 8) & 0xff,
    542              dec_version & 0xff, (dmux_version >> 16) & 0xff,
    543              (dmux_version >> 8) & 0xff, dmux_version & 0xff);
    544       FREE_WARGV_AND_RETURN(EXIT_SUCCESS);
    545     } else if (!strcmp(argv[c], "-mt")) {
    546       config->options.use_threads = 1;
    547     } else if (!strcmp(argv[c], "--")) {
    548       if (c < argc - 1) {
    549         kParams.file_name = (const char*)GET_WARGV(argv, ++c);
    550         file_name_argv_index = c;
    551       }
    552       break;
    553     } else if (argv[c][0] == '-') {
    554       printf("Unknown option '%s'\n", argv[c]);
    555       Help();
    556       FREE_WARGV_AND_RETURN(EXIT_FAILURE);
    557     } else {
    558       kParams.file_name = (const char*)GET_WARGV(argv, c);
    559       file_name_argv_index = c;
    560     }
    561 
    562     if (parse_error) {
    563       Help();
    564       FREE_WARGV_AND_RETURN(EXIT_FAILURE);
    565     }
    566   }
    567 
    568   if (kParams.file_name == NULL) {
    569     printf("missing input file!!\n");
    570     Help();
    571     FREE_WARGV_AND_RETURN(EXIT_FAILURE);
    572   }
    573 
    574   if (!ImgIoUtilReadFile(kParams.file_name,
    575                          &kParams.data.bytes, &kParams.data.size)) {
    576     goto Error;
    577   }
    578 
    579   if (!WebPGetInfo(kParams.data.bytes, kParams.data.size, NULL, NULL)) {
    580     fprintf(stderr, "Input file doesn't appear to be WebP format.\n");
    581     goto Error;
    582   }
    583 
    584   kParams.dmux = WebPDemux(&kParams.data);
    585   if (kParams.dmux == NULL) {
    586     fprintf(stderr, "Could not create demuxing object!\n");
    587     goto Error;
    588   }
    589 
    590   kParams.canvas_width = WebPDemuxGetI(kParams.dmux, WEBP_FF_CANVAS_WIDTH);
    591   kParams.canvas_height = WebPDemuxGetI(kParams.dmux, WEBP_FF_CANVAS_HEIGHT);
    592   if (kParams.print_info) {
    593     printf("Canvas: %d x %d\n", kParams.canvas_width, kParams.canvas_height);
    594   }
    595 
    596   ClearPreviousFrame();
    597 
    598   memset(&kParams.iccp, 0, sizeof(kParams.iccp));
    599   kParams.has_color_profile =
    600       !!(WebPDemuxGetI(kParams.dmux, WEBP_FF_FORMAT_FLAGS) & ICCP_FLAG);
    601   if (kParams.has_color_profile) {
    602 #ifdef WEBP_HAVE_QCMS
    603     if (!WebPDemuxGetChunk(kParams.dmux, "ICCP", 1, &kParams.iccp)) goto Error;
    604     printf("VP8X: Found color profile\n");
    605 #else
    606     fprintf(stderr, "Warning: color profile present, but qcms is unavailable!\n"
    607             "Build libqcms from Mozilla or Chromium and define WEBP_HAVE_QCMS "
    608             "before building.\n");
    609 #endif
    610   }
    611 
    612   if (!WebPDemuxGetFrame(kParams.dmux, 1, curr)) goto Error;
    613 
    614   kParams.has_animation = (curr->num_frames > 1);
    615   kParams.loop_count = (int)WebPDemuxGetI(kParams.dmux, WEBP_FF_LOOP_COUNT);
    616   kParams.bg_color = WebPDemuxGetI(kParams.dmux, WEBP_FF_BACKGROUND_COLOR);
    617   printf("VP8X: Found %d images in file (loop count = %d)\n",
    618          curr->num_frames, kParams.loop_count);
    619 
    620   // Decode first frame
    621   if (!Decode()) goto Error;
    622 
    623   // Position iterator to last frame. Next call to HandleDisplay will wrap over.
    624   // We take this into account by bumping up loop_count.
    625   if (!WebPDemuxGetFrame(kParams.dmux, 0, curr)) goto Error;
    626   if (kParams.loop_count) ++kParams.loop_count;
    627 
    628 #if defined(__unix__) || defined(__CYGWIN__)
    629   // Work around GLUT compositor bug.
    630   // https://bugs.launchpad.net/ubuntu/+source/freeglut/+bug/369891
    631   setenv("XLIB_SKIP_ARGB_VISUALS", "1", 1);
    632 #endif
    633 
    634   // Start display (and timer)
    635   glutInit(&argc, argv);
    636 #ifdef FREEGLUT
    637   glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_CONTINUE_EXECUTION);
    638 #endif
    639   StartDisplay(argv[file_name_argv_index]);
    640 
    641   if (kParams.has_animation) glutTimerFunc(0, decode_callback, 0);
    642   glutMainLoop();
    643 
    644   // Should only be reached when using FREEGLUT:
    645   ClearParams();
    646   FREE_WARGV_AND_RETURN(EXIT_SUCCESS);
    647 
    648  Error:
    649   ClearParams();
    650   FREE_WARGV_AND_RETURN(EXIT_FAILURE);
    651 }
    652 
    653 #else   // !WEBP_HAVE_GL
    654 
    655 int main(int argc, const char* argv[]) {
    656   fprintf(stderr, "OpenGL support not enabled in %s.\n", argv[0]);
    657   (void)argc;
    658   return EXIT_FAILURE;
    659 }
    660 
    661 #endif
    662 
    663 //------------------------------------------------------------------------------