blob: 4e98220f5ef66ea99817d954fcdf71fcfa51192a [file] [log] [blame]
Pascal Massimino7937b402011-12-13 14:02:04 -08001// Copyright 2011 Google Inc.
2//
3// This code is licensed under the same terms as WebM:
4// Software License Agreement: http://www.webmproject.org/license/software/
5// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
6// -----------------------------------------------------------------------------
7//
8// Simple WebP file viewer.
9//
10// Compiling on linux:
11// sudo apt-get install libglut3-dev mesa-common-dev
12// gcc -o vwebp vwebp.c -O3 -lwebp -lglut -lGL
13// Compiling on Mac + XCode:
14// gcc -o vwebp vwebp.c -lwebp -framework GLUT -framework OpenGL
15//
16// Author: Skal (pascal.massimino@gmail.com)
17
18#include <stdio.h>
19#include <stdlib.h>
20#include <string.h>
21
22#include "webp/decode.h"
23
24#ifdef __APPLE__
25#include <GLUT/glut.h>
26#else
27#include <GL/glut.h>
28#ifdef FREEGLUT
29#include <GL/freeglut.h>
30#endif
31#endif
32
33// Unfortunate global variables
34static const WebPDecBuffer* kPic = NULL;
35static const char* file_name = NULL;
36static int print_info = 0;
37
38//------------------------------------------------------------------------------
39// Callbacks
40
41static void HandleKey(unsigned char key, int pos_x, int pos_y) {
42 (void)pos_x;
43 (void)pos_y;
44 if (key == 'q' || key == 'Q' || key == 27 /* Esc */) {
45#ifdef FREEGLUT
46 glutLeaveMainLoop();
47#else
48 WebPFreeDecBuffer((WebPDecBuffer*)kPic);
49 kPic = NULL;
50 exit(0);
51#endif
52 } else if (key == 'i') {
53 print_info = 1 - print_info;
54 glutPostRedisplay();
55 }
56}
57
58static void HandleReshape(int width, int height) {
59 // TODO(skal): proper handling of resize, esp. for large pictures.
60 // + key control of the zoom.
61 glViewport(0, 0, width, height);
62 glMatrixMode(GL_PROJECTION);
63 glLoadIdentity();
64 glMatrixMode(GL_MODELVIEW);
65 glLoadIdentity();
66}
67
68static void PrintString(const char* const text) {
69 void* const font = GLUT_BITMAP_9_BY_15;
70 int i;
71 for (i = 0; text[i]; ++i) {
72 glutBitmapCharacter(font, text[i]);
73 }
74}
75
76static void HandleDisplay(void) {
77 if (kPic == NULL) return;
78 glClear(GL_COLOR_BUFFER_BIT);
79 glPushMatrix();
80 glPixelZoom(1, -1);
81 glRasterPos2f(-1, 1);
82 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
83 glPixelStorei(GL_UNPACK_ROW_LENGTH, kPic->u.RGBA.stride / 4);
84 glDrawPixels(kPic->width, kPic->height, GL_RGBA, GL_UNSIGNED_BYTE,
85 (GLvoid*)kPic->u.RGBA.rgba);
86 if (print_info) {
87 char tmp[32];
88
89 glColor4f(0.0, 0.0, 0.0, 0.0);
90 glRasterPos2f(-0.95, 0.90);
91 PrintString(file_name);
92
93 snprintf(tmp, sizeof(tmp), "Dimension:%d x %d", kPic->width, kPic->height);
94 glColor4f(0.0, 0.0, 0.0, 0.0);
95 glRasterPos2f(-0.95, 0.80);
96 PrintString(tmp);
97 }
98 glFlush();
99}
100
101static void Show(const WebPDecBuffer* const pic) {
102 glutInitDisplayMode(GLUT_RGBA);
103 glutInitWindowSize(pic->width, pic->height);
104 glutCreateWindow("WebP viewer");
105 glutReshapeFunc(HandleReshape);
106 glutDisplayFunc(HandleDisplay);
107 glutIdleFunc(NULL);
108 glutKeyboardFunc(HandleKey);
109 glClearColor(0.0, 0.0, 0.0, 0.0);
110 HandleReshape(pic->width, pic->height);
111 glutMainLoop();
112}
113
114//------------------------------------------------------------------------------
115// File decoding
116
117static const char* const kStatusMessages[] = {
118 "OK", "OUT_OF_MEMORY", "INVALID_PARAM", "BITSTREAM_ERROR",
119 "UNSUPPORTED_FEATURE", "SUSPENDED", "USER_ABORT", "NOT_ENOUGH_DATA"
120};
121
122static int Decode(const char* const in_file, WebPDecoderConfig* const config) {
123 WebPDecBuffer* const output_buffer = &config->output;
124 WebPBitstreamFeatures* const bitstream = &config->input;
125 VP8StatusCode status = VP8_STATUS_OK;
126 int ok;
127 size_t data_size = 0;
128 void* data = NULL;
129 FILE* const in = fopen(in_file, "rb");
130
131 if (!in) {
132 fprintf(stderr, "cannot open input file '%s'\n", in_file);
133 return 0;
134 }
135 fseek(in, 0, SEEK_END);
136 data_size = ftell(in);
137 fseek(in, 0, SEEK_SET);
138 data = malloc(data_size);
139 if (data == NULL) return 0;
140 ok = (fread(data, data_size, 1, in) == 1);
141 fclose(in);
142 if (!ok) {
143 fprintf(stderr, "Could not read %zu bytes of data from file %s\n",
144 data_size, in_file);
145 free(data);
146 return 0;
147 }
148
149 status = WebPGetFeatures((const uint8_t*)data, data_size, bitstream);
150 if (status != VP8_STATUS_OK) {
151 goto end;
152 }
153
154 output_buffer->colorspace = MODE_RGBA;
155 status = WebPDecode((const uint8_t*)data, data_size, config);
156
157 end:
158 free(data);
159 ok = (status == VP8_STATUS_OK);
160 if (!ok) {
161 fprintf(stderr, "Decoding of %s failed.\n", in_file);
162 fprintf(stderr, "Status: %d (%s)\n", status, kStatusMessages[status]);
163 }
164 return ok;
165}
166
167//------------------------------------------------------------------------------
168// Main
169
170static void Help(void) {
171 printf("Usage: vwebp in_file [options]\n\n"
172 "Decodes the WebP image file and visualize it using OpenGL\n"
173 "Options are:\n"
174 " -version .... print version number and exit.\n"
175 " -nofancy ..... don't use the fancy YUV420 upscaler.\n"
176 " -nofilter .... disable in-loop filtering.\n"
177 " -mt .......... use multi-threading\n"
178 " -crop <x> <y> <w> <h> ... crop output with the given rectangle\n"
179 " -scale <w> <h> .......... scale the output (*after* any cropping)\n"
180 " -h ....... this help message.\n"
181 );
182}
183
184int main(int argc, char *argv[]) {
185 WebPDecoderConfig config;
186 int c;
187
188 if (!WebPInitDecoderConfig(&config)) {
189 fprintf(stderr, "Library version mismatch!\n");
190 return -1;
191 }
192
193 for (c = 1; c < argc; ++c) {
194 if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
195 Help();
196 return 0;
197 } else if (!strcmp(argv[c], "-nofancy")) {
198 config.options.no_fancy_upsampling = 1;
199 } else if (!strcmp(argv[c], "-nofilter")) {
200 config.options.bypass_filtering = 1;
201 } else if (!strcmp(argv[c], "-version")) {
202 const int version = WebPGetDecoderVersion();
203 printf("%d.%d.%d\n",
204 (version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff);
205 return 0;
206 } else if (!strcmp(argv[c], "-mt")) {
207 config.options.use_threads = 1;
208 } else if (!strcmp(argv[c], "-crop") && c < argc - 4) {
209 config.options.use_cropping = 1;
210 config.options.crop_left = strtol(argv[++c], NULL, 0);
211 config.options.crop_top = strtol(argv[++c], NULL, 0);
212 config.options.crop_width = strtol(argv[++c], NULL, 0);
213 config.options.crop_height = strtol(argv[++c], NULL, 0);
214 } else if (!strcmp(argv[c], "-scale") && c < argc - 2) {
215 config.options.use_scaling = 1;
216 config.options.scaled_width = strtol(argv[++c], NULL, 0);
217 config.options.scaled_height = strtol(argv[++c], NULL, 0);
218 } else if (argv[c][0] == '-') {
219 printf("Unknown option '%s'\n", argv[c]);
220 Help();
221 return -1;
222 } else {
223 file_name = argv[c];
224 }
225 }
226
227 if (file_name == NULL) {
228 printf("missing input file!!\n");
229 Help();
230 return -1;
231 }
232 if (!Decode(file_name, &config)) {
233 return -1;
234 }
235
236 kPic = &config.output;
237 printf("Displaying [%s]: %d x %d. Press Esc to exit, 'i' for info.\n",
238 file_name, kPic->width, kPic->height);
239
240 glutInit(&argc, argv);
241 Show(kPic);
242
243 // Should only be reached when using FREEGLUT:
244 WebPFreeDecBuffer(&config.output);
245 return 0;
246}
247
248//------------------------------------------------------------------------------