github.com/cellofellow/gopkg@v0.0.0-20140722061823-eec0544a62ad/image/webp/libwebp/examples/vwebp.c (about)

     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 "config.h"
    15  #endif
    16  
    17  #include <stdio.h>
    18  #include <stdlib.h>
    19  #include <string.h>
    20  
    21  #if defined(WEBP_HAVE_GL)
    22  
    23  #if defined(HAVE_GLUT_GLUT_H)
    24  #include <GLUT/glut.h>
    25  #else
    26  #include <GL/glut.h>
    27  #ifdef FREEGLUT
    28  #include <GL/freeglut.h>
    29  #endif
    30  #endif
    31  
    32  #ifdef WEBP_HAVE_QCMS
    33  #include <qcms.h>
    34  #endif
    35  
    36  #include "webp/decode.h"
    37  #include "webp/demux.h"
    38  
    39  #include "./example_util.h"
    40  
    41  #ifdef _MSC_VER
    42  #define snprintf _snprintf
    43  #endif
    44  
    45  static void Help(void);
    46  
    47  // Unfortunate global variables. Gathered into a struct for comfort.
    48  static struct {
    49    int has_animation;
    50    int has_color_profile;
    51    int done;
    52    int decoding_error;
    53    int print_info;
    54    int use_color_profile;
    55  
    56    int canvas_width, canvas_height;
    57    int loop_count;
    58    uint32_t bg_color;
    59  
    60    const char* file_name;
    61    WebPData data;
    62    WebPDecoderConfig config;
    63    const WebPDecBuffer* pic;
    64    WebPDemuxer* dmux;
    65    WebPIterator curr_frame;
    66    WebPIterator prev_frame;
    67    WebPChunkIterator iccp;
    68  } kParams;
    69  
    70  static void ClearPreviousPic(void) {
    71    WebPFreeDecBuffer((WebPDecBuffer*)kParams.pic);
    72    kParams.pic = NULL;
    73  }
    74  
    75  static void ClearParams(void) {
    76    ClearPreviousPic();
    77    WebPDataClear(&kParams.data);
    78    WebPDemuxReleaseIterator(&kParams.curr_frame);
    79    WebPDemuxReleaseIterator(&kParams.prev_frame);
    80    WebPDemuxReleaseChunkIterator(&kParams.iccp);
    81    WebPDemuxDelete(kParams.dmux);
    82    kParams.dmux = NULL;
    83  }
    84  
    85  // -----------------------------------------------------------------------------
    86  // Color profile handling
    87  static int ApplyColorProfile(const WebPData* const profile,
    88                               WebPDecBuffer* const rgba) {
    89  #ifdef WEBP_HAVE_QCMS
    90    int i, ok = 0;
    91    uint8_t* line;
    92    uint8_t major_revision;
    93    qcms_profile* input_profile = NULL;
    94    qcms_profile* output_profile = NULL;
    95    qcms_transform* transform = NULL;
    96    const qcms_data_type input_type = QCMS_DATA_RGBA_8;
    97    const qcms_data_type output_type = QCMS_DATA_RGBA_8;
    98    const qcms_intent intent = QCMS_INTENT_DEFAULT;
    99  
   100    if (profile == NULL || rgba == NULL) return 0;
   101    if (profile->bytes == NULL || profile->size < 10) return 1;
   102    major_revision = profile->bytes[8];
   103  
   104    qcms_enable_iccv4();
   105    input_profile = qcms_profile_from_memory(profile->bytes, profile->size);
   106    // qcms_profile_is_bogus() is broken with ICCv4.
   107    if (input_profile == NULL ||
   108        (major_revision < 4 && qcms_profile_is_bogus(input_profile))) {
   109      fprintf(stderr, "Color profile is bogus!\n");
   110      goto Error;
   111    }
   112  
   113    output_profile = qcms_profile_sRGB();
   114    if (output_profile == NULL) {
   115      fprintf(stderr, "Error creating output color profile!\n");
   116      goto Error;
   117    }
   118  
   119    qcms_profile_precache_output_transform(output_profile);
   120    transform = qcms_transform_create(input_profile, input_type,
   121                                      output_profile, output_type,
   122                                      intent);
   123    if (transform == NULL) {
   124      fprintf(stderr, "Error creating color transform!\n");
   125      goto Error;
   126    }
   127  
   128    line = rgba->u.RGBA.rgba;
   129    for (i = 0; i < rgba->height; ++i, line += rgba->u.RGBA.stride) {
   130      qcms_transform_data(transform, line, line, rgba->width);
   131    }
   132    ok = 1;
   133  
   134   Error:
   135    if (input_profile != NULL) qcms_profile_release(input_profile);
   136    if (output_profile != NULL) qcms_profile_release(output_profile);
   137    if (transform != NULL) qcms_transform_release(transform);
   138    return ok;
   139  #else
   140    (void)profile;
   141    (void)rgba;
   142    return 1;
   143  #endif  // WEBP_HAVE_QCMS
   144  }
   145  
   146  //------------------------------------------------------------------------------
   147  // File decoding
   148  
   149  static int Decode(void) {   // Fills kParams.curr_frame
   150    const WebPIterator* const curr = &kParams.curr_frame;
   151    WebPDecoderConfig* const config = &kParams.config;
   152    WebPDecBuffer* const output_buffer = &config->output;
   153    int ok = 0;
   154  
   155    ClearPreviousPic();
   156    output_buffer->colorspace = MODE_RGBA;
   157    ok = (WebPDecode(curr->fragment.bytes, curr->fragment.size,
   158                     config) == VP8_STATUS_OK);
   159    if (!ok) {
   160      fprintf(stderr, "Decoding of frame #%d failed!\n", curr->frame_num);
   161    } else {
   162      kParams.pic = output_buffer;
   163      if (kParams.use_color_profile) {
   164        ok = ApplyColorProfile(&kParams.iccp.chunk, output_buffer);
   165        if (!ok) {
   166          fprintf(stderr, "Applying color profile to frame #%d failed!\n",
   167                  curr->frame_num);
   168        }
   169      }
   170    }
   171    return ok;
   172  }
   173  
   174  static void decode_callback(int what) {
   175    if (what == 0 && !kParams.done) {
   176      int duration = 0;
   177      if (kParams.dmux != NULL) {
   178        WebPIterator* const curr = &kParams.curr_frame;
   179        if (!WebPDemuxNextFrame(curr)) {
   180          WebPDemuxReleaseIterator(curr);
   181          if (WebPDemuxGetFrame(kParams.dmux, 1, curr)) {
   182            --kParams.loop_count;
   183            kParams.done = (kParams.loop_count == 0);
   184          } else {
   185            kParams.decoding_error = 1;
   186            kParams.done = 1;
   187            return;
   188          }
   189        }
   190        duration = curr->duration;
   191      }
   192      if (!Decode()) {
   193        kParams.decoding_error = 1;
   194        kParams.done = 1;
   195      } else {
   196        glutPostRedisplay();
   197        glutTimerFunc(duration, decode_callback, what);
   198      }
   199    }
   200  }
   201  
   202  //------------------------------------------------------------------------------
   203  // Callbacks
   204  
   205  static void HandleKey(unsigned char key, int pos_x, int pos_y) {
   206    (void)pos_x;
   207    (void)pos_y;
   208    if (key == 'q' || key == 'Q' || key == 27 /* Esc */) {
   209  #ifdef FREEGLUT
   210      glutLeaveMainLoop();
   211  #else
   212      ClearParams();
   213      exit(0);
   214  #endif
   215    } else if (key == 'c') {
   216      if (kParams.has_color_profile && !kParams.decoding_error) {
   217        kParams.use_color_profile = 1 - kParams.use_color_profile;
   218  
   219        if (kParams.has_animation) {
   220          // Restart the completed animation to pickup the color profile change.
   221          if (kParams.done && kParams.loop_count == 0) {
   222            kParams.loop_count =
   223                (int)WebPDemuxGetI(kParams.dmux, WEBP_FF_LOOP_COUNT) + 1;
   224            kParams.done = 0;
   225            // Start the decode loop immediately.
   226            glutTimerFunc(0, decode_callback, 0);
   227          }
   228        } else {
   229          Decode();
   230          glutPostRedisplay();
   231        }
   232      }
   233    } else if (key == 'i') {
   234      kParams.print_info = 1 - kParams.print_info;
   235      glutPostRedisplay();
   236    }
   237  }
   238  
   239  static void HandleReshape(int width, int height) {
   240    // TODO(skal): proper handling of resize, esp. for large pictures.
   241    // + key control of the zoom.
   242    glViewport(0, 0, width, height);
   243    glMatrixMode(GL_PROJECTION);
   244    glLoadIdentity();
   245    glMatrixMode(GL_MODELVIEW);
   246    glLoadIdentity();
   247  }
   248  
   249  static void PrintString(const char* const text) {
   250    void* const font = GLUT_BITMAP_9_BY_15;
   251    int i;
   252    for (i = 0; text[i]; ++i) {
   253      glutBitmapCharacter(font, text[i]);
   254    }
   255  }
   256  
   257  static float GetColorf(uint32_t color, int shift) {
   258    return (color >> shift) / 255.f;
   259  }
   260  
   261  static void DrawCheckerBoard(void) {
   262    const int square_size = 8;  // must be a power of 2
   263    int x, y;
   264    GLint viewport[4];  // x, y, width, height
   265  
   266    glPushMatrix();
   267  
   268    glGetIntegerv(GL_VIEWPORT, viewport);
   269    // shift to integer coordinates with (0,0) being top-left.
   270    glOrtho(0, viewport[2], viewport[3], 0, -1, 1);
   271    for (y = 0; y < viewport[3]; y += square_size) {
   272      for (x = 0; x < viewport[2]; x += square_size) {
   273        const GLubyte color = 128 + 64 * (!((x + y) & square_size));
   274        glColor3ub(color, color, color);
   275        glRecti(x, y, x + square_size, y + square_size);
   276      }
   277    }
   278    glPopMatrix();
   279  }
   280  
   281  static void HandleDisplay(void) {
   282    const WebPDecBuffer* const pic = kParams.pic;
   283    const WebPIterator* const curr = &kParams.curr_frame;
   284    WebPIterator* const prev = &kParams.prev_frame;
   285    GLfloat xoff, yoff;
   286    if (pic == NULL) return;
   287    glPushMatrix();
   288    glPixelZoom(1, -1);
   289    xoff = (GLfloat)(2. * curr->x_offset / kParams.canvas_width);
   290    yoff = (GLfloat)(2. * curr->y_offset / kParams.canvas_height);
   291    glRasterPos2f(-1.f + xoff, 1.f - yoff);
   292    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
   293    glPixelStorei(GL_UNPACK_ROW_LENGTH, pic->u.RGBA.stride / 4);
   294  
   295    if (prev->dispose_method == WEBP_MUX_DISPOSE_BACKGROUND ||
   296        curr->blend_method == WEBP_MUX_NO_BLEND) {
   297      // TODO(later): these offsets and those above should factor in window size.
   298      //              they will be incorrect if the window is resized.
   299      // glScissor() takes window coordinates (0,0 at bottom left).
   300      int window_x, window_y;
   301      if (prev->dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) {
   302        // Clear the previous frame rectangle.
   303        window_x = prev->x_offset;
   304        window_y = kParams.canvas_height - prev->y_offset - prev->height;
   305      } else {  // curr->blend_method == WEBP_MUX_NO_BLEND.
   306        // We simulate no-blending behavior by first clearing the current frame
   307        // rectangle (to a checker-board) and then alpha-blending against it.
   308        window_x = curr->x_offset;
   309        window_y = kParams.canvas_height - curr->y_offset - curr->height;
   310      }
   311      glEnable(GL_SCISSOR_TEST);
   312      // Only update the requested area, not the whole canvas.
   313      glScissor(window_x, window_y, prev->width, prev->height);
   314  
   315      glClear(GL_COLOR_BUFFER_BIT);  // use clear color
   316      DrawCheckerBoard();
   317  
   318      glDisable(GL_SCISSOR_TEST);
   319    }
   320  
   321    *prev = *curr;
   322  
   323    glDrawPixels(pic->width, pic->height,
   324                 GL_RGBA, GL_UNSIGNED_BYTE,
   325                 (GLvoid*)pic->u.RGBA.rgba);
   326    if (kParams.print_info) {
   327      char tmp[32];
   328  
   329      glColor4f(0.90f, 0.0f, 0.90f, 1.0f);
   330      glRasterPos2f(-0.95f, 0.90f);
   331      PrintString(kParams.file_name);
   332  
   333      snprintf(tmp, sizeof(tmp), "Dimension:%d x %d", pic->width, pic->height);
   334      glColor4f(0.90f, 0.0f, 0.90f, 1.0f);
   335      glRasterPos2f(-0.95f, 0.80f);
   336      PrintString(tmp);
   337      if (curr->x_offset != 0 || curr->y_offset != 0) {
   338        snprintf(tmp, sizeof(tmp), " (offset:%d,%d)",
   339                 curr->x_offset, curr->y_offset);
   340        glRasterPos2f(-0.95f, 0.70f);
   341        PrintString(tmp);
   342      }
   343    }
   344    glPopMatrix();
   345    glFlush();
   346  }
   347  
   348  static void StartDisplay(void) {
   349    const int width = kParams.canvas_width;
   350    const int height = kParams.canvas_height;
   351    glutInitDisplayMode(GLUT_RGBA);
   352    glutInitWindowSize(width, height);
   353    glutCreateWindow("WebP viewer");
   354    glutDisplayFunc(HandleDisplay);
   355    glutIdleFunc(NULL);
   356    glutKeyboardFunc(HandleKey);
   357    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   358    glEnable(GL_BLEND);
   359    glClearColor(GetColorf(kParams.bg_color, 0),
   360                 GetColorf(kParams.bg_color, 8),
   361                 GetColorf(kParams.bg_color, 16),
   362                 GetColorf(kParams.bg_color, 24));
   363    HandleReshape(width, height);
   364    glClear(GL_COLOR_BUFFER_BIT);
   365    DrawCheckerBoard();
   366  }
   367  
   368  //------------------------------------------------------------------------------
   369  // Main
   370  
   371  static void Help(void) {
   372    printf("Usage: vwebp in_file [options]\n\n"
   373           "Decodes the WebP image file and visualize it using OpenGL\n"
   374           "Options are:\n"
   375           "  -version  .... print version number and exit.\n"
   376           "  -noicc ....... don't use the icc profile if present.\n"
   377           "  -nofancy ..... don't use the fancy YUV420 upscaler.\n"
   378           "  -nofilter .... disable in-loop filtering.\n"
   379           "  -dither <int>  dithering strength (0..100). Default=50.\n"
   380           "  -mt .......... use multi-threading.\n"
   381           "  -info ........ print info.\n"
   382           "  -h     ....... this help message.\n"
   383           "\n"
   384           "Keyboard shortcuts:\n"
   385           "  'c' ................ toggle use of color profile.\n"
   386           "  'i' ................ overlay file information.\n"
   387           "  'q' / 'Q' / ESC .... quit.\n"
   388          );
   389  }
   390  
   391  int main(int argc, char *argv[]) {
   392    int c;
   393    WebPDecoderConfig* const config = &kParams.config;
   394    WebPIterator* const curr = &kParams.curr_frame;
   395    WebPIterator* const prev = &kParams.prev_frame;
   396  
   397    if (!WebPInitDecoderConfig(config)) {
   398      fprintf(stderr, "Library version mismatch!\n");
   399      return -1;
   400    }
   401    config->options.dithering_strength = 50;
   402    kParams.use_color_profile = 1;
   403  
   404    for (c = 1; c < argc; ++c) {
   405      if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
   406        Help();
   407        return 0;
   408      } else if (!strcmp(argv[c], "-noicc")) {
   409        kParams.use_color_profile = 0;
   410      } else if (!strcmp(argv[c], "-nofancy")) {
   411        config->options.no_fancy_upsampling = 1;
   412      } else if (!strcmp(argv[c], "-nofilter")) {
   413        config->options.bypass_filtering = 1;
   414      } else if (!strcmp(argv[c], "-dither") && c + 1 < argc) {
   415        config->options.dithering_strength = strtol(argv[++c], NULL, 0);
   416      } else if (!strcmp(argv[c], "-info")) {
   417        kParams.print_info = 1;
   418      } else if (!strcmp(argv[c], "-version")) {
   419        const int dec_version = WebPGetDecoderVersion();
   420        const int dmux_version = WebPGetDemuxVersion();
   421        printf("WebP Decoder version: %d.%d.%d\nWebP Demux version: %d.%d.%d\n",
   422               (dec_version >> 16) & 0xff, (dec_version >> 8) & 0xff,
   423               dec_version & 0xff, (dmux_version >> 16) & 0xff,
   424               (dmux_version >> 8) & 0xff, dmux_version & 0xff);
   425        return 0;
   426      } else if (!strcmp(argv[c], "-mt")) {
   427        config->options.use_threads = 1;
   428      } else if (!strcmp(argv[c], "--")) {
   429        if (c < argc - 1) kParams.file_name = argv[++c];
   430        break;
   431      } else if (argv[c][0] == '-') {
   432        printf("Unknown option '%s'\n", argv[c]);
   433        Help();
   434        return -1;
   435      } else {
   436        kParams.file_name = argv[c];
   437      }
   438    }
   439  
   440    if (kParams.file_name == NULL) {
   441      printf("missing input file!!\n");
   442      Help();
   443      return 0;
   444    }
   445  
   446    if (!ExUtilReadFile(kParams.file_name,
   447                        &kParams.data.bytes, &kParams.data.size)) {
   448      goto Error;
   449    }
   450  
   451    if (!WebPGetInfo(kParams.data.bytes, kParams.data.size, NULL, NULL)) {
   452      fprintf(stderr, "Input file doesn't appear to be WebP format.\n");
   453      goto Error;
   454    }
   455  
   456    kParams.dmux = WebPDemux(&kParams.data);
   457    if (kParams.dmux == NULL) {
   458      fprintf(stderr, "Could not create demuxing object!\n");
   459      goto Error;
   460    }
   461  
   462    if (WebPDemuxGetI(kParams.dmux, WEBP_FF_FORMAT_FLAGS) & FRAGMENTS_FLAG) {
   463      fprintf(stderr, "Image fragments are not supported for now!\n");
   464      goto Error;
   465    }
   466    kParams.canvas_width = WebPDemuxGetI(kParams.dmux, WEBP_FF_CANVAS_WIDTH);
   467    kParams.canvas_height = WebPDemuxGetI(kParams.dmux, WEBP_FF_CANVAS_HEIGHT);
   468    if (kParams.print_info) {
   469      printf("Canvas: %d x %d\n", kParams.canvas_width, kParams.canvas_height);
   470    }
   471  
   472    prev->width = kParams.canvas_width;
   473    prev->height = kParams.canvas_height;
   474    prev->x_offset = prev->y_offset = 0;
   475    prev->dispose_method = WEBP_MUX_DISPOSE_BACKGROUND;
   476  
   477    memset(&kParams.iccp, 0, sizeof(kParams.iccp));
   478    kParams.has_color_profile =
   479        !!(WebPDemuxGetI(kParams.dmux, WEBP_FF_FORMAT_FLAGS) & ICCP_FLAG);
   480    if (kParams.has_color_profile) {
   481  #ifdef WEBP_HAVE_QCMS
   482      if (!WebPDemuxGetChunk(kParams.dmux, "ICCP", 1, &kParams.iccp)) goto Error;
   483      printf("VP8X: Found color profile\n");
   484  #else
   485      fprintf(stderr, "Warning: color profile present, but qcms is unavailable!\n"
   486              "Build libqcms from Mozilla or Chromium and define WEBP_HAVE_QCMS "
   487              "before building.\n");
   488  #endif
   489    }
   490  
   491    if (!WebPDemuxGetFrame(kParams.dmux, 1, curr)) goto Error;
   492  
   493    kParams.has_animation = (curr->num_frames > 1);
   494    kParams.loop_count = (int)WebPDemuxGetI(kParams.dmux, WEBP_FF_LOOP_COUNT);
   495    kParams.bg_color = WebPDemuxGetI(kParams.dmux, WEBP_FF_BACKGROUND_COLOR);
   496    printf("VP8X: Found %d images in file (loop count = %d)\n",
   497           curr->num_frames, kParams.loop_count);
   498  
   499    // Decode first frame
   500    if (!Decode()) goto Error;
   501  
   502    // Position iterator to last frame. Next call to HandleDisplay will wrap over.
   503    // We take this into account by bumping up loop_count.
   504    WebPDemuxGetFrame(kParams.dmux, 0, curr);
   505    if (kParams.loop_count) ++kParams.loop_count;
   506  
   507    // Start display (and timer)
   508    glutInit(&argc, argv);
   509  #ifdef FREEGLUT
   510    glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_CONTINUE_EXECUTION);
   511  #endif
   512    StartDisplay();
   513  
   514    if (kParams.has_animation) glutTimerFunc(0, decode_callback, 0);
   515    glutMainLoop();
   516  
   517    // Should only be reached when using FREEGLUT:
   518    ClearParams();
   519    return 0;
   520  
   521   Error:
   522    ClearParams();
   523    return -1;
   524  }
   525  
   526  #else   // !WEBP_HAVE_GL
   527  
   528  int main(int argc, const char *argv[]) {
   529    fprintf(stderr, "OpenGL support not enabled in %s.\n", argv[0]);
   530    (void)argc;
   531    return 0;
   532  }
   533  
   534  #endif
   535  
   536  //------------------------------------------------------------------------------