blob: ec5f5834c182e1bb0e7adcef0b0f8f8c6168cac6 [file] [log] [blame]
James Zernad1e1632012-01-06 14:49:06 -08001// Copyright 2011 Google Inc. All Rights Reserved.
Pascal Massimino7937b402011-12-13 14:02:04 -08002//
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
Pascal Massimino861a5b72012-05-09 00:41:12 -070012// gcc -o vwebp vwebp.c -O3 -lwebp -lwebpmux -lglut -lGL -lpthread -lm
Pascal Massimino7937b402011-12-13 14:02:04 -080013// Compiling on Mac + XCode:
Pascal Massimino861a5b72012-05-09 00:41:12 -070014// gcc -o vwebp vwebp.c -lwebp -lwebpmux -framework GLUT -framework OpenGL
Pascal Massimino7937b402011-12-13 14:02:04 -080015//
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"
Pascal Massimino861a5b72012-05-09 00:41:12 -070023#include "webp/mux.h"
Pascal Massimino7937b402011-12-13 14:02:04 -080024
25#ifdef __APPLE__
26#include <GLUT/glut.h>
27#else
28#include <GL/glut.h>
29#ifdef FREEGLUT
30#include <GL/freeglut.h>
31#endif
32#endif
33
James Zern061263a2012-05-11 16:00:57 -070034#include "./example_util.h"
35
James Zern91179542011-12-16 19:30:33 -080036#ifdef _MSC_VER
37#define snprintf _snprintf
38#endif
39
Pascal Massimino861a5b72012-05-09 00:41:12 -070040static void Help(void);
41
42// Unfortunate global variables. Gathered into a struct for comfort.
43static struct {
44 int has_animation;
45 int done;
46 int decoding_error;
47 int print_info;
48
Urvang Joshi6808e692012-07-06 11:35:36 +053049 int loop_count;
Pascal Massimino861a5b72012-05-09 00:41:12 -070050
51 const char* file_name;
52 WebPData data;
Pascal Massimino861a5b72012-05-09 00:41:12 -070053 WebPDecoderConfig* config;
54 const WebPDecBuffer* pic;
James Zernd7a5ac82012-09-26 23:44:59 -070055 WebPDemuxer* dmux;
56 WebPIterator frameiter;
57} kParams;
Pascal Massimino861a5b72012-05-09 00:41:12 -070058
59static void ClearPreviousPic(void) {
60 WebPFreeDecBuffer((WebPDecBuffer*)kParams.pic);
61 kParams.pic = NULL;
62}
63
Pascal Massimino861a5b72012-05-09 00:41:12 -070064static void ClearParams(void) {
65 ClearPreviousPic();
Urvang Joshif3bab8e2012-06-07 17:19:02 +053066 WebPDataClear(&kParams.data);
James Zernd7a5ac82012-09-26 23:44:59 -070067 WebPDemuxReleaseIterator(&kParams.frameiter);
68 WebPDemuxDelete(kParams.dmux);
69 kParams.dmux = NULL;
Pascal Massimino861a5b72012-05-09 00:41:12 -070070}
Pascal Massimino7937b402011-12-13 14:02:04 -080071
72//------------------------------------------------------------------------------
73// Callbacks
74
75static void HandleKey(unsigned char key, int pos_x, int pos_y) {
76 (void)pos_x;
77 (void)pos_y;
78 if (key == 'q' || key == 'Q' || key == 27 /* Esc */) {
79#ifdef FREEGLUT
80 glutLeaveMainLoop();
81#else
Pascal Massimino861a5b72012-05-09 00:41:12 -070082 ClearParams();
Pascal Massimino7937b402011-12-13 14:02:04 -080083 exit(0);
84#endif
85 } else if (key == 'i') {
Pascal Massimino861a5b72012-05-09 00:41:12 -070086 kParams.print_info = 1 - kParams.print_info;
Pascal Massimino7937b402011-12-13 14:02:04 -080087 glutPostRedisplay();
88 }
89}
90
91static void HandleReshape(int width, int height) {
92 // TODO(skal): proper handling of resize, esp. for large pictures.
93 // + key control of the zoom.
94 glViewport(0, 0, width, height);
95 glMatrixMode(GL_PROJECTION);
96 glLoadIdentity();
97 glMatrixMode(GL_MODELVIEW);
98 glLoadIdentity();
99}
100
101static void PrintString(const char* const text) {
102 void* const font = GLUT_BITMAP_9_BY_15;
103 int i;
104 for (i = 0; text[i]; ++i) {
105 glutBitmapCharacter(font, text[i]);
106 }
107}
108
James Zerna73b8972012-07-09 23:01:52 -0700109static void DrawCheckerBoard(void) {
110 const int square_size = 8; // must be a power of 2
111 int x, y;
112 GLint viewport[4]; // x, y, width, height
113
114 glPushMatrix();
115
116 glGetIntegerv(GL_VIEWPORT, viewport);
117 // shift to integer coordinates with (0,0) being top-left.
118 glOrtho(0, viewport[2], viewport[3], 0, -1, 1);
119 for (y = 0; y < viewport[3]; y += square_size) {
120 for (x = 0; x < viewport[2]; x += square_size) {
121 const GLubyte color = 128 + 64 * (!((x + y) & square_size));
122 glColor3ub(color, color, color);
123 glRecti(x, y, x + square_size, y + square_size);
124 }
125 }
126 glPopMatrix();
127}
128
Pascal Massimino7937b402011-12-13 14:02:04 -0800129static void HandleDisplay(void) {
Pascal Massimino861a5b72012-05-09 00:41:12 -0700130 const WebPDecBuffer* pic = kParams.pic;
131 if (pic == NULL) return;
Pascal Massimino7937b402011-12-13 14:02:04 -0800132 glClear(GL_COLOR_BUFFER_BIT);
133 glPushMatrix();
134 glPixelZoom(1, -1);
135 glRasterPos2f(-1, 1);
136 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
Pascal Massimino861a5b72012-05-09 00:41:12 -0700137 glPixelStorei(GL_UNPACK_ROW_LENGTH, pic->u.RGBA.stride / 4);
James Zerna73b8972012-07-09 23:01:52 -0700138 DrawCheckerBoard();
Pascal Massimino861a5b72012-05-09 00:41:12 -0700139 glDrawPixels(pic->width, pic->height,
140 GL_RGBA, GL_UNSIGNED_BYTE,
141 (GLvoid*)pic->u.RGBA.rgba);
142 if (kParams.print_info) {
Pascal Massimino7937b402011-12-13 14:02:04 -0800143 char tmp[32];
144
James Zernb35c07d2012-07-09 22:36:58 -0700145 glColor4f(0.0, 0.0, 0.0, 1.0);
James Zern91179542011-12-16 19:30:33 -0800146 glRasterPos2f(-0.95f, 0.90f);
Pascal Massimino861a5b72012-05-09 00:41:12 -0700147 PrintString(kParams.file_name);
Pascal Massimino7937b402011-12-13 14:02:04 -0800148
Pascal Massimino861a5b72012-05-09 00:41:12 -0700149 snprintf(tmp, sizeof(tmp), "Dimension:%d x %d", pic->width, pic->height);
James Zernb35c07d2012-07-09 22:36:58 -0700150 glColor4f(0.0, 0.0, 0.0, 1.0);
James Zern91179542011-12-16 19:30:33 -0800151 glRasterPos2f(-0.95f, 0.80f);
Pascal Massimino7937b402011-12-13 14:02:04 -0800152 PrintString(tmp);
153 }
James Zerna73b8972012-07-09 23:01:52 -0700154 glPopMatrix();
Pascal Massimino7937b402011-12-13 14:02:04 -0800155 glFlush();
156}
157
Pascal Massimino861a5b72012-05-09 00:41:12 -0700158static void StartDisplay(const WebPDecBuffer* const pic) {
Pascal Massimino7937b402011-12-13 14:02:04 -0800159 glutInitDisplayMode(GLUT_RGBA);
160 glutInitWindowSize(pic->width, pic->height);
161 glutCreateWindow("WebP viewer");
Pascal Massimino7937b402011-12-13 14:02:04 -0800162 glutDisplayFunc(HandleDisplay);
163 glutIdleFunc(NULL);
164 glutKeyboardFunc(HandleKey);
Urvang Joshi08220102012-06-20 11:18:35 -0700165 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
166 glEnable(GL_BLEND);
Pascal Massimino7937b402011-12-13 14:02:04 -0800167 glClearColor(0.0, 0.0, 0.0, 0.0);
168 HandleReshape(pic->width, pic->height);
Pascal Massimino7937b402011-12-13 14:02:04 -0800169}
170
171//------------------------------------------------------------------------------
172// File decoding
173
James Zernd7a5ac82012-09-26 23:44:59 -0700174static int Decode(int* const duration) {
175 const WebPIterator* const iter = &kParams.frameiter;
Pascal Massimino861a5b72012-05-09 00:41:12 -0700176 WebPDecoderConfig* const config = kParams.config;
Pascal Massimino861a5b72012-05-09 00:41:12 -0700177 WebPDecBuffer* const output_buffer = &config->output;
178 int ok = 0;
179
180 ClearPreviousPic();
Urvang Joshia0770722012-10-30 14:54:46 -0700181 if (iter->x_offset != 0 || iter->y_offset != 0) {
James Zernd7a5ac82012-09-26 23:44:59 -0700182 fprintf(stderr,
183 "Frame offsets not yet supported! Forcing offset to 0,0\n");
Pascal Massimino7937b402011-12-13 14:02:04 -0800184 }
Pascal Massimino7937b402011-12-13 14:02:04 -0800185 output_buffer->colorspace = MODE_RGBA;
Urvang Joshia00a3da2012-10-31 17:49:15 -0700186 ok = (WebPDecode(iter->fragment.bytes, iter->fragment.size,
James Zernd7a5ac82012-09-26 23:44:59 -0700187 config) == VP8_STATUS_OK);
Pascal Massimino7937b402011-12-13 14:02:04 -0800188 if (!ok) {
Urvang Joshia0770722012-10-30 14:54:46 -0700189 fprintf(stderr, "Decoding of frame #%d failed!\n", iter->frame_num);
Pascal Massimino861a5b72012-05-09 00:41:12 -0700190 } else {
Urvang Joshia0770722012-10-30 14:54:46 -0700191 *duration = iter->duration;
Pascal Massimino861a5b72012-05-09 00:41:12 -0700192 kParams.pic = output_buffer;
Pascal Massimino7937b402011-12-13 14:02:04 -0800193 }
194 return ok;
195}
196
Pascal Massimino861a5b72012-05-09 00:41:12 -0700197static void decode_callback(int what) {
198 if (what == 0 && !kParams.done) {
Urvang Joshi6808e692012-07-06 11:35:36 +0530199 int duration = 0;
James Zernd7a5ac82012-09-26 23:44:59 -0700200 if (kParams.dmux != NULL) {
201 if (!Decode(&duration)) {
Pascal Massimino861a5b72012-05-09 00:41:12 -0700202 kParams.decoding_error = 1;
203 kParams.done = 1;
204 } else {
James Zernd7a5ac82012-09-26 23:44:59 -0700205 WebPIterator* const iter = &kParams.frameiter;
206 if (!WebPDemuxNextFrame(iter)) {
207 WebPDemuxReleaseIterator(iter);
208 if (WebPDemuxGetFrame(kParams.dmux, 1, iter)) {
209 --kParams.loop_count;
210 kParams.done = (kParams.loop_count == 0);
211 } else {
212 kParams.decoding_error = 1;
213 kParams.done = 1;
214 }
Pascal Massimino861a5b72012-05-09 00:41:12 -0700215 }
216 }
217 }
218 glutPostRedisplay();
219 glutTimerFunc(duration, decode_callback, what);
220 }
221}
222
Pascal Massimino7937b402011-12-13 14:02:04 -0800223//------------------------------------------------------------------------------
224// Main
225
226static void Help(void) {
227 printf("Usage: vwebp in_file [options]\n\n"
228 "Decodes the WebP image file and visualize it using OpenGL\n"
229 "Options are:\n"
230 " -version .... print version number and exit.\n"
231 " -nofancy ..... don't use the fancy YUV420 upscaler.\n"
232 " -nofilter .... disable in-loop filtering.\n"
233 " -mt .......... use multi-threading\n"
234 " -crop <x> <y> <w> <h> ... crop output with the given rectangle\n"
235 " -scale <w> <h> .......... scale the output (*after* any cropping)\n"
236 " -h ....... this help message.\n"
237 );
238}
239
240int main(int argc, char *argv[]) {
241 WebPDecoderConfig config;
242 int c;
243
244 if (!WebPInitDecoderConfig(&config)) {
245 fprintf(stderr, "Library version mismatch!\n");
246 return -1;
247 }
Pascal Massimino861a5b72012-05-09 00:41:12 -0700248 kParams.config = &config;
Pascal Massimino7937b402011-12-13 14:02:04 -0800249
250 for (c = 1; c < argc; ++c) {
251 if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
252 Help();
253 return 0;
254 } else if (!strcmp(argv[c], "-nofancy")) {
255 config.options.no_fancy_upsampling = 1;
256 } else if (!strcmp(argv[c], "-nofilter")) {
257 config.options.bypass_filtering = 1;
258 } else if (!strcmp(argv[c], "-version")) {
259 const int version = WebPGetDecoderVersion();
260 printf("%d.%d.%d\n",
261 (version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff);
262 return 0;
263 } else if (!strcmp(argv[c], "-mt")) {
264 config.options.use_threads = 1;
265 } else if (!strcmp(argv[c], "-crop") && c < argc - 4) {
266 config.options.use_cropping = 1;
267 config.options.crop_left = strtol(argv[++c], NULL, 0);
268 config.options.crop_top = strtol(argv[++c], NULL, 0);
269 config.options.crop_width = strtol(argv[++c], NULL, 0);
270 config.options.crop_height = strtol(argv[++c], NULL, 0);
271 } else if (!strcmp(argv[c], "-scale") && c < argc - 2) {
272 config.options.use_scaling = 1;
273 config.options.scaled_width = strtol(argv[++c], NULL, 0);
274 config.options.scaled_height = strtol(argv[++c], NULL, 0);
275 } else if (argv[c][0] == '-') {
276 printf("Unknown option '%s'\n", argv[c]);
277 Help();
278 return -1;
279 } else {
Pascal Massimino861a5b72012-05-09 00:41:12 -0700280 kParams.file_name = argv[c];
Pascal Massimino7937b402011-12-13 14:02:04 -0800281 }
282 }
283
James Zern061263a2012-05-11 16:00:57 -0700284 if (kParams.file_name == NULL) {
285 printf("missing input file!!\n");
286 Help();
287 return 0;
288 }
289
290 if (!ExUtilReadFile(kParams.file_name,
Urvang Joshia0770722012-10-30 14:54:46 -0700291 &kParams.data.bytes, &kParams.data.size)) {
James Zern061263a2012-05-11 16:00:57 -0700292 goto Error;
293 }
Pascal Massimino861a5b72012-05-09 00:41:12 -0700294
James Zernd7a5ac82012-09-26 23:44:59 -0700295 kParams.dmux = WebPDemux(&kParams.data);
296 if (kParams.dmux == NULL) {
Pascal Massimino861a5b72012-05-09 00:41:12 -0700297 fprintf(stderr, "Could not create demuxing object!\n");
298 goto Error;
Pascal Massimino7937b402011-12-13 14:02:04 -0800299 }
300
Urvang Joshia00a3da2012-10-31 17:49:15 -0700301 if (WebPDemuxGetI(kParams.dmux, WEBP_FF_FORMAT_FLAGS) & FRAGMENTS_FLAG) {
302 fprintf(stderr, "Image fragments are not supported for now!\n");
Pascal Massimino861a5b72012-05-09 00:41:12 -0700303 goto Error;
304 }
Pascal Massimino7937b402011-12-13 14:02:04 -0800305
James Zernd7a5ac82012-09-26 23:44:59 -0700306 if (!WebPDemuxGetFrame(kParams.dmux, 1, &kParams.frameiter)) goto Error;
Pascal Massimino861a5b72012-05-09 00:41:12 -0700307
Urvang Joshia0770722012-10-30 14:54:46 -0700308 kParams.has_animation = (kParams.frameiter.num_frames > 1);
James Zernd7a5ac82012-09-26 23:44:59 -0700309 kParams.loop_count = (int)WebPDemuxGetI(kParams.dmux, WEBP_FF_LOOP_COUNT);
310 printf("VP8X: Found %d images in file (loop count = %d)\n",
Urvang Joshia0770722012-10-30 14:54:46 -0700311 kParams.frameiter.num_frames, kParams.loop_count);
Pascal Massimino861a5b72012-05-09 00:41:12 -0700312
313 // Decode first frame
314 {
Urvang Joshi6808e692012-07-06 11:35:36 +0530315 int duration;
James Zernd7a5ac82012-09-26 23:44:59 -0700316 if (!Decode(&duration)) goto Error;
Pascal Massimino861a5b72012-05-09 00:41:12 -0700317 }
318
319 // Start display (and timer)
Pascal Massimino7937b402011-12-13 14:02:04 -0800320 glutInit(&argc, argv);
James Zern880fd982012-05-25 14:53:46 -0700321#ifdef FREEGLUT
322 glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_CONTINUE_EXECUTION);
323#endif
Pascal Massimino861a5b72012-05-09 00:41:12 -0700324 StartDisplay(kParams.pic);
325 if (kParams.has_animation) glutTimerFunc(0, decode_callback, 0);
326 glutMainLoop();
Pascal Massimino7937b402011-12-13 14:02:04 -0800327
328 // Should only be reached when using FREEGLUT:
Pascal Massimino861a5b72012-05-09 00:41:12 -0700329 ClearParams();
Pascal Massimino7937b402011-12-13 14:02:04 -0800330 return 0;
Pascal Massimino861a5b72012-05-09 00:41:12 -0700331
332 Error:
333 ClearParams();
334 return -1;
Pascal Massimino7937b402011-12-13 14:02:04 -0800335}
336
337//------------------------------------------------------------------------------