]> git.sesse.net Git - ultimatescore/blob - stinger/blur.cpp
Add some code for generating a simple stinger.
[ultimatescore] / stinger / blur.cpp
1 #define NO_SDL_GLEXT 1
2
3 #define WIDTH 1280
4 #define HEIGHT 720
5
6 #include <epoxy/gl.h>
7
8 #include <SDL2/SDL.h>
9 #include <SDL2/SDL_error.h>
10 #include <SDL2/SDL_events.h>
11 #include <SDL2/SDL_image.h>
12 #include <SDL2/SDL_keyboard.h>
13 #include <SDL2/SDL_mouse.h>
14 #include <SDL2/SDL_video.h>
15
16 #include <assert.h>
17 #include <features.h>
18 #include <math.h>
19 #include <png.h>
20 #include <pngconf.h>
21 #include <setjmp.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <sys/time.h>
25 #include <time.h>
26
27 #include <movit/effect_chain.h>
28 #include <movit/flat_input.h>
29 #include <movit/init.h>
30 #include <movit/mix_effect.h>
31 #include <movit/util.h>
32
33 using namespace movit;
34
35 unsigned char result[WIDTH * HEIGHT * 4];
36
37 unsigned char *load_image(const char *filename, unsigned *w, unsigned *h)
38 {
39         SDL_Surface *img = IMG_Load(filename);
40         if (img == nullptr) {
41                 fprintf(stderr, "Load of '%s' failed\n", filename);
42                 exit(1);
43         }
44
45         SDL_PixelFormat rgba_fmt;
46         rgba_fmt.palette = nullptr;
47         rgba_fmt.BitsPerPixel = 32;
48         rgba_fmt.BytesPerPixel = 8;
49         rgba_fmt.Rloss = rgba_fmt.Gloss = rgba_fmt.Bloss = rgba_fmt.Aloss = 0;
50
51         // NOTE: Assumes little endian.
52         rgba_fmt.Rmask = 0x00ff0000;
53         rgba_fmt.Gmask = 0x0000ff00;
54         rgba_fmt.Bmask = 0x000000ff;
55         rgba_fmt.Amask = 0xff000000;
56
57         rgba_fmt.Rshift = 16;
58         rgba_fmt.Gshift = 8;
59         rgba_fmt.Bshift = 0;
60         rgba_fmt.Ashift = 24;
61
62         SDL_Surface *converted = SDL_ConvertSurface(img, &rgba_fmt, SDL_SWSURFACE);
63
64         *w = img->w;
65         *h = img->h;
66
67         SDL_FreeSurface(img);
68
69         unsigned char *crop_img = new unsigned char[1280 * 720 * 4];
70
71         int img_w = *w;
72         int img_h = *h;
73
74         int xoffs = (1280 - int(img_w)) / 2;
75         int yoffs = (720 - int(img_h)) / 2;
76
77         for (unsigned y = 0; y < 720; ++y) {
78                 unsigned char *dptr = crop_img + y * 1280 * 4;
79                 int ysrc = y - yoffs;
80                 if (ysrc < 0 || ysrc >= int(img_h)) {
81                         memset(dptr, 0, 1280 * 4);
82                         continue;
83                 }
84                 unsigned char *sptr = (unsigned char *)converted->pixels + ysrc * img_w * 4;
85                 if (img_w >= 1280) {
86                         memcpy(dptr, sptr - xoffs * 4, 1280 * 4);
87                 } else {
88                         memset(dptr, 0, 1280 * 4);
89                         memcpy(dptr + xoffs * 4, sptr, img_w * 4);
90                 }
91         }
92
93         SDL_FreeSurface(converted);
94
95         *w = 1280;
96         *h = 720;
97
98         return crop_img;
99 }
100
101 void write_png(const char *filename, unsigned char *screenbuf)
102 {
103         FILE *fp = fopen(filename, "wb");
104         png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
105         png_infop info_ptr = png_create_info_struct(png_ptr);
106         
107         if (setjmp(png_jmpbuf(png_ptr))) {
108                 fclose(fp);
109                 fprintf(stderr, "Write to %s failed; exiting.\n", filename);
110                 exit(1);
111         }
112
113         png_set_IHDR(png_ptr, info_ptr, WIDTH, HEIGHT, 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
114
115         png_bytep *row_pointers = new png_bytep[HEIGHT];
116         for (unsigned y = 0; y < HEIGHT; ++y) {
117                 row_pointers[y] = screenbuf + ((HEIGHT - y - 1) * WIDTH) * 4;
118         }
119
120         png_init_io(png_ptr, fp);
121         png_set_rows(png_ptr, info_ptr, row_pointers);
122         png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_BGR, nullptr);
123         png_destroy_write_struct(&png_ptr, &info_ptr);
124         fclose(fp);
125
126         delete[] row_pointers;
127 }
128
129 class BouncingIdentityEffect : public Effect {
130 public:
131         BouncingIdentityEffect() {}
132         std::string effect_type_id() const override { return "IdentityEffect"; }
133         std::string output_fragment_shader() override { return read_file("identity.frag"); }
134         bool needs_texture_bounce() const override { return true; }
135         AlphaHandling alpha_handling() const override { return DONT_CARE_ALPHA_TYPE; }
136 };
137
138
139 int main(int argc, char **argv)
140 {
141         if (SDL_Init(SDL_INIT_EVERYTHING) == -1) {
142                 fprintf(stderr, "SDL_Init failed: %s\n", SDL_GetError());
143                 exit(1);
144         }
145         SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
146         SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 0);
147         SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 0);
148         SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
149
150         SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
151         SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
152         SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
153         // SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
154         SDL_Window *window = SDL_CreateWindow("OpenGL window",
155                 SDL_WINDOWPOS_UNDEFINED,
156                 SDL_WINDOWPOS_UNDEFINED,
157                 WIDTH, HEIGHT,
158                 SDL_WINDOW_OPENGL);
159         SDL_GLContext context = SDL_GL_CreateContext(window);
160         assert(context != nullptr);
161
162         CHECK(init_movit("/usr/share/movit", MOVIT_DEBUG_OFF));
163
164         EffectChain chain(WIDTH, HEIGHT);
165         glViewport(0, 0, WIDTH, HEIGHT);
166
167         ImageFormat inout_format;
168         inout_format.color_space = COLORSPACE_sRGB;
169         inout_format.gamma_curve = GAMMA_sRGB;
170
171         Effect *last_effect = nullptr;
172         for (int i = 1; i < argc; ++i) {
173                 fprintf(stderr, "%s...\n", argv[i]);
174                 unsigned img_w, img_h;
175                 unsigned char *src_img = load_image(argv[i], &img_w, &img_h);
176
177                 FlatInput *input = new FlatInput(inout_format, FORMAT_BGRA_POSTMULTIPLIED_ALPHA, GL_UNSIGNED_BYTE, img_w, img_h);
178                 input->set_pixel_data(src_img);
179                 chain.add_input(input);
180
181                 if (i == 1) {
182                         last_effect = input;
183                 } else {
184                         Effect *mix_effect = chain.add_effect(new MixEffect(), last_effect, input);
185                         float z = 1.0f / i;
186                         CHECK(mix_effect->set_float("strength_first", 1.0f - z));
187                         CHECK(mix_effect->set_float("strength_second", z));
188                         last_effect = mix_effect;
189                 }
190
191                 if (i % 10 == 0) {
192                         last_effect = chain.add_effect(new BouncingIdentityEffect());
193                 }
194         }
195         
196         chain.add_output(inout_format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED);
197         chain.set_dither_bits(8);
198         chain.finalize();
199
200         // generate a PBO to hold the data we read back with glReadPixels()
201         // (Intel/DRI goes into a slow path if we don't read to PBO)
202         GLuint pbo;
203         glGenBuffers(1, &pbo);
204         glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, pbo);
205         glBufferData(GL_PIXEL_PACK_BUFFER_ARB, WIDTH * HEIGHT * 4, nullptr, GL_STREAM_READ);
206
207         chain.render_to_screen();
208                 
209         glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, pbo);
210         check_error();
211         glReadPixels(0, 0, WIDTH, HEIGHT, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, BUFFER_OFFSET(0));
212         check_error();
213
214         unsigned char *screenbuf = (unsigned char *)glMapBuffer(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY);
215         write_png("blurout.png", screenbuf);
216
217         return 0; 
218 }