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