Add shadows and light effects
[mercenary-reloaded.git] / src / libopengl / opengl.c
1 /* OpenGL rendering
2  *
3  * (C) 2018 by Andreas Eversberg <jolly@eversberg.eu>
4  * All Rights Reserved
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <stdio.h>
21 #include <stdint.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <math.h>
25 #include <errno.h>
26 #include "../libsdl/print.h"
27 #include "opengl.h"
28 #define GLEW_STATIC
29 #include <GL/glew.h>
30
31 #define MAX_OSD 2
32 static uint8_t *image_rgb = NULL;
33 static uint8_t *osd_rgba[MAX_OSD] = { NULL, NULL };
34 static GLuint image_name;
35 static GLuint osd_name[MAX_OSD];
36 static int image_width, image_height;
37 static int osd_width[MAX_OSD], osd_height[MAX_OSD];
38 static int texture_size;
39 static int osd_size[MAX_OSD];
40 static int flip_y;
41 static GLuint shadow_framebuffer[2] = { 0, 0 };
42 static int current_view_width = 0, current_view_height = 0;
43 static GLuint shadow_texture[2];
44 static int shadow_texture_width = 0, shadow_texture_height = 0;
45 static int shadow_multisampling;
46
47 GLuint current_framebuffer = 0;
48
49
50 /* alloc and init an image texture with size that is greater than the power of two */
51 int init_opengl_image(int _image_width, int _image_height)
52 {
53         int rc;
54
55         image_width = _image_width;
56         image_height = _image_height;
57
58         /* generate texture */
59         for (texture_size = 1; texture_size <= image_width || texture_size <= image_height; texture_size *= 2)
60                 ;
61
62         image_rgb = calloc(texture_size * texture_size, 3);
63         if (!image_rgb) {
64                 print_error("Failed to allocate texture\n");
65                 rc = -ENOMEM;
66                 goto error;
67         }
68         glShadeModel(GL_FLAT);
69         glPixelStorei(GL_UNPACK_ALIGNMENT, 1); /* bytes */
70         glGenTextures(1, &image_name);
71         glBindTexture(GL_TEXTURE_2D, image_name);
72         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
73         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
74         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture_size, texture_size, 0, GL_RGB, GL_UNSIGNED_BYTE, image_rgb);
75
76
77         return 0;
78
79 error:
80         exit_opengl();
81         return rc;
82 }
83
84 /* alloc and init an osd texture with size that is greater than the power of two */
85 int init_opengl_osd(int num, int _osd_width, int _osd_height)
86 {
87         int rc;
88
89         if (num < 0 || num >= MAX_OSD) {
90                 print_error("given OSD number out of range");
91                 rc = -ENOMEM;
92                 goto error;
93         }
94
95         osd_width[num] = _osd_width;
96         osd_height[num] = _osd_height;
97
98         /* generate texture */
99         for (osd_size[num] = 1; osd_size[num] <= osd_width[num] || osd_size[num] <= osd_height[num]; osd_size[num] *= 2)
100                 ;
101         osd_rgba[num] = calloc(osd_size[num] * osd_size[num], 4);
102         if (!osd_rgba[num]) {
103                 print_error("Failed to allocate texture\n");
104                 rc = -ENOMEM;
105                 goto error;
106         }
107         glPixelStorei(GL_UNPACK_ALIGNMENT, 1); /* bytes */
108         glGenTextures(1, &osd_name[num]);
109         glBindTexture(GL_TEXTURE_2D, osd_name[num]);
110         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
111         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
112         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, osd_size[num], osd_size[num], 0, GL_RGBA, GL_UNSIGNED_BYTE, osd_rgba[num]);
113
114         return 0;
115
116 error:
117         exit_opengl();
118         return rc;
119 }
120
121 void opengl_clear(int _flip_y)
122 {
123         flip_y = _flip_y;
124
125         /* clear screen */
126         glClearColor(0.05, 0.05, 0.05, 1.0);
127         glClear(GL_COLOR_BUFFER_BIT);
128 }
129
130 /* only set view width and heigt. (used by ovr to tell frame buffer size) */
131 void opengl_set_size(int view_width, int view_height)
132 {
133         current_view_width = view_width;
134         current_view_height = view_height;
135 }
136
137 /* set viewport for improved rendering */
138 void opengl_viewport(int view_width, int view_height, int split, int benson_at_line, double fov, double benson_size)
139 {
140         int view_x = 0, view_y = 0;
141         double factor_height = 1.0;
142         double factor_width = 1.0;
143
144         /* center view, or put it in the top half of the window */
145         if (split == 1) {
146                 view_y = view_height / 2;
147                 view_height = view_height / 2;
148         } else if (split == 2) {
149                 view_height = view_height / 2;
150         }
151
152         /* avoid division by zero, if one dimension is too small */
153         if (view_width < 1 || view_height < 1)
154                 return;
155
156         /* expand width or height, if apect of image_width:image_height does not match the view_width:view_height */
157         if (view_height * image_width > view_width * image_height)
158                 factor_height = (double)(view_height * image_width) / (double)(view_width * image_height);
159         else if (view_height * image_width < view_width * image_height)
160                 factor_width = (double)(view_width * image_height) / (double)(view_height * image_width);
161
162         current_view_width = view_width;
163         current_view_height = view_height;
164
165         /* avoid views that are too small */
166         if (view_width < 1 || view_height < 1)
167                 return;
168
169         /* projection matrix */
170         glViewport((GLsizei)view_x, (GLsizei)view_y, (GLsizei)view_width, (GLsizei)view_height);
171         glMatrixMode(GL_PROJECTION);
172         glLoadIdentity();
173
174         /* calculate field-of-view */
175         double slope = tan(fov / 360 * M_PI);
176         /* make frustum to center the view in the game view above benson */
177         double left = -slope;
178         double right = slope;
179         double benson_start_at_position = ((double)image_height - (double)benson_at_line) * benson_size;
180         double top = slope * ((double)image_height - benson_start_at_position) / (double)image_width;
181         double bottom = -slope * ((double)image_height * 2.0 - ((double)image_height - benson_start_at_position)) / (double)image_width;
182         glFrustum(left * factor_width, right * factor_width, bottom * factor_height, top * factor_height, 1.0, 5000000000.0);
183
184         glMatrixMode(GL_MODELVIEW);
185 }
186
187 /* render image or only benson */
188 void opengl_blit_image(uint8_t *rgb, int filter, int benson_at_line, int render_benson_only, double fov, double monitor_distance, double benson_size, int benson_case)
189 {
190         double texture_left = 0;
191         double texture_right = ((double)image_width) / (double)texture_size;
192         double texture_top = ((double)benson_at_line) / (double)texture_size;
193         double texture_bottom = ((double)image_height) / (double)texture_size;
194         double image_start_at_position = ((double)image_height - (double)benson_at_line) * benson_size;
195         double image_top = -((double)image_height - image_start_at_position) / (double)image_width;
196         double image_bottom = -((double)image_height * 2.0 - ((double)image_height - image_start_at_position)) / (double)image_width;
197
198         /* calculate field-of-view */
199         double slope = tan(fov / 360 * M_PI) * monitor_distance;
200
201         /* render image */
202
203         if (benson_case) {
204                 glEnable(GL_CULL_FACE);
205                 glFrontFace(GL_CW);
206                 glCullFace(GL_BACK);
207         }
208
209         glEnable(GL_TEXTURE_2D);
210         glBindTexture(GL_TEXTURE_2D, image_name);
211         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);  /* no modulation with color */
212         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (filter) ? GL_LINEAR : GL_NEAREST);
213         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (filter) ? GL_LINEAR : GL_NEAREST);
214         if (render_benson_only) {
215                 /* render benson only, and copy top line of benson to one line above, so the texture filter will not take false data above benson */
216                 rgb += image_width * (benson_at_line - 1) * 3;
217                 memcpy(rgb, rgb + image_width * 3, image_width * 3);
218                 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, benson_at_line - 1, image_width, image_height - benson_at_line - 1, GL_RGB, GL_UNSIGNED_BYTE, rgb);
219         } else {
220                 image_top = -image_top;
221                 texture_top = 0.0;
222                 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, image_width, image_height, GL_RGB, GL_UNSIGNED_BYTE, rgb);
223         }
224         glBegin(GL_QUADS);
225         glTexCoord2f(texture_left, texture_bottom);
226         glVertex3f(-benson_size * slope, image_bottom * slope, -monitor_distance);
227         glTexCoord2f(texture_right, texture_bottom);
228         glVertex3f(benson_size * slope, image_bottom * slope, -monitor_distance);
229         glTexCoord2f(texture_right, texture_top);
230         glVertex3f(benson_size * slope, image_top * slope, -monitor_distance);
231         glTexCoord2f(texture_left, texture_top);
232         glVertex3f(-benson_size * slope, image_top * slope, -monitor_distance);
233         glEnd();
234         glDisable(GL_TEXTURE_2D);
235         /* render benson case */
236         if (benson_case) {
237                 double left, right, bottom, top, near, far;
238                 left = -benson_size * slope;
239                 right = benson_size * slope;
240                 bottom = image_bottom * slope;
241                 top = image_top * slope;
242                 near = -monitor_distance;
243                 far = near - benson_size * slope / 3.0;
244                 glBegin(GL_QUADS);
245                 /* back */
246                 glColor4f(0.3, 0.3, 0.3, 1.0);
247                 glVertex3f(left, top, far);
248                 glVertex3f(right, top, far);
249                 glVertex3f(right, bottom, far);
250                 glVertex3f(left, bottom, far);
251                 /* top */
252                 glColor4f(0.2, 0.2, 0.2, 1.0);
253                 glVertex3f(left, top, near);
254                 glVertex3f(right, top, near);
255                 glVertex3f(right, top, far);
256                 glVertex3f(left, top, far);
257                 /* bottom */
258                 glColor4f(0.05, 0.05, 0.05, 1.0);
259                 glVertex3f(left, bottom, far);
260                 glVertex3f(right, bottom, far);
261                 glVertex3f(right, bottom, near);
262                 glVertex3f(left, bottom, near);
263                 /* left */
264                 glColor4f(0.1, 0.1, 0.1, 1.0);
265                 glVertex3f(left, bottom, far);
266                 glVertex3f(left, bottom, near);
267                 glVertex3f(left, top, near);
268                 glVertex3f(left, top, far);
269                 /* right */
270                 glVertex3f(right, top, far);
271                 glVertex3f(right, top, near);
272                 glVertex3f(right, bottom, near);
273                 glVertex3f(right, bottom, far);
274                 glEnd();
275         }
276
277         if (benson_case) {
278                 glDisable(GL_CULL_FACE);
279         }
280 }
281
282 /* render osd texture */
283 void opengl_blit_osd(int num, uint8_t *rgba, int filter, int benson_at_line, double fov, double monitor_distance, double benson_size, double scale_x, double scale_y, double offset_x, double offset_y)
284 {
285         double texture_left = 0.0;
286         double texture_right = (double)osd_width[num] / (double)osd_size[num];
287         double texture_top = 0.0;
288         double texture_bottom = (double)osd_height[num] / (double)osd_size[num];
289         double benson_start_at_position;
290         double osd_left = 0.0;
291         double osd_right = 1.0;
292         double osd_top = 0.0;
293         double osd_bottom = 1.0;
294         double slope, range, center;
295         if (fov) {
296                 osd_left = -1.0;
297                 osd_right = 1.0;
298                 benson_start_at_position = ((double)image_height - (double)benson_at_line) * benson_size;
299                 osd_top = ((double)image_height - benson_start_at_position) / (double)image_width;
300                 osd_bottom = -((double)image_height * 2.0 - ((double)image_height - benson_start_at_position)) / (double)image_width;
301                 /* calculate field-of-view */
302                 slope = tan(fov / 360 * M_PI) * monitor_distance;
303         }
304         range = (osd_right - osd_left) / 2.0;
305         center = (osd_right + osd_left) / 2.0;
306         osd_left = (osd_left - center) * scale_x + center + range * offset_x;
307         osd_right = (osd_right - center) * scale_x + center + range * offset_x;
308         range = (osd_bottom - osd_top) / 2.0;
309         center = (osd_bottom + osd_top) / 2.0;
310         osd_top = (osd_top - center) * scale_y + center + range * offset_y;
311         osd_bottom = (osd_bottom - center) * scale_y + center + range * offset_y;
312
313         glEnable(GL_BLEND);
314         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
315         glEnable(GL_TEXTURE_2D);
316         glBindTexture(GL_TEXTURE_2D, osd_name[num]);
317         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);  /* no modulation with color */
318         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (filter) ? GL_LINEAR : GL_NEAREST);
319         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (filter) ? GL_LINEAR : GL_NEAREST);
320         glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, osd_width[num], osd_height[num], GL_RGBA, GL_UNSIGNED_BYTE, rgba);
321         glBegin(GL_QUADS);
322         if (fov) {
323                 /* perspective viewport */
324                 glTexCoord2f(texture_left, texture_bottom);
325                 glVertex3f(osd_left * slope, osd_bottom * slope, -monitor_distance);
326                 glTexCoord2f(texture_right, texture_bottom);
327                 glVertex3f(osd_right * slope, osd_bottom * slope, -monitor_distance);
328                 glTexCoord2f(texture_right, texture_top);
329                 glVertex3f(osd_right * slope, osd_top * slope, -monitor_distance);
330                 glTexCoord2f(texture_left, texture_top);
331                 glVertex3f(osd_left * slope, osd_top * slope, -monitor_distance);
332         } else {
333                 /* orthogonal viewport */
334                 glTexCoord2f(texture_left, texture_top);
335                 glVertex3f(osd_left, osd_top, 0.0);
336                 glTexCoord2f(texture_right, texture_top);
337                 glVertex3f(osd_right, osd_top, 0.0);
338                 glTexCoord2f(texture_right, texture_bottom);
339                 glVertex3f(osd_right, osd_bottom, 0.0);
340                 glTexCoord2f(texture_left, texture_bottom);
341                 glVertex3f(osd_left, osd_bottom, 0.0);
342         }
343         glEnd();
344         glDisable(GL_TEXTURE_2D);
345         glDisable(GL_BLEND);
346 }
347
348 /* set color and opacity */
349 void opengl_render_color(double r, double g, double b, double a)
350 {
351         if (a < 1.0) {
352                 glEnable(GL_BLEND);
353                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
354                 glColor4d(r, g, b, a);
355         } else {
356                 glDisable(GL_BLEND);
357                 glColor3d(r, g, b);
358         }
359 }
360
361 /* set color and alpha */
362 void opengl_render_color_alpha(double r, double g, double b, double a)
363 {
364         glDisable(GL_BLEND);
365         glColor4d(r, g, b, a);
366 }
367
368 /* render polygon */
369 void opengl_render_polygon(double *x, double *y, double *z, int count, int cull_face)
370 {
371         int i;
372
373         if (cull_face) {
374                 glEnable(GL_CULL_FACE);
375                 glFrontFace((flip_y) ? GL_CCW : GL_CW);
376                 glCullFace(GL_BACK);
377         }
378         glBegin(GL_POLYGON);
379         for (i = 0; i < count; i++)
380                 glVertex3d(x[i], y[i], -z[i]);
381         glEnd();
382         if (cull_face)
383                 glDisable(GL_CULL_FACE);
384 }
385
386 /* render polygon, but make sure any size of it is visible */
387 void opengl_render_polygon_and_line(double *x, double *y, double *z, int count)
388 {
389         int i;
390
391         glBegin(GL_POLYGON);
392         for (i = 0; i < count; i++)
393                 glVertex3d(x[i], y[i], -z[i]);
394         glEnd();
395         glBegin(GL_LINE_LOOP);
396         for (i = 0; i < count; i++)
397                 glVertex3d(x[i], y[i], -z[i]);
398         glEnd();
399 }
400
401 /* render line */
402 void opengl_render_line(double x1, double y1, double z1, double x2, double y2, double z2, double size)
403 {
404         if (size == 0.0) {
405                 glBegin(GL_LINES);
406                 glVertex3f(x1, y1, -z1);
407                 glVertex3f(x2, y2, -z2);
408                 glEnd();
409                 return;
410         }
411 }
412
413 /* render point */
414 void opengl_render_point(double x, double y, double z, double size)
415 {
416         if (size == 0.0) {
417                 glBegin(GL_POINTS);
418                 glVertex3f(x, y, -z);
419                 glEnd();
420                 return;
421         }
422 }
423
424 int init_shadow_buffer(int multisampling)
425 {
426         shadow_multisampling = multisampling;
427
428         glGenFramebuffers(2, shadow_framebuffer);
429
430         glBindFramebuffer(GL_FRAMEBUFFER, current_framebuffer);
431
432         return 0;
433 }
434
435 /* change to shadow buffer and clear it */
436 void begin_shadow_render(void)
437 {
438         /* free texture, if size changes */
439         if (shadow_texture[0] && (current_view_width != shadow_texture_width || current_view_height != shadow_texture_height)) {
440                 glDeleteTextures(2, shadow_texture);
441                 shadow_texture[0] = 0;
442         }
443         /* generate texture for shadow rendering (two, if MSAA is used) */
444         if (!shadow_texture[0]) {
445                 glGenTextures(2, shadow_texture);
446                 shadow_texture_width = current_view_width;
447                 shadow_texture_height = current_view_height;
448                 if (shadow_multisampling > 1) {
449                         glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, shadow_texture[0]);
450                         glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, shadow_multisampling, GL_RGBA, shadow_texture_width, shadow_texture_height, 0);
451                         glBindTexture(GL_TEXTURE_2D, shadow_texture[1]);
452                         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, shadow_texture_width, shadow_texture_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
453                 } else {
454                         glBindTexture(GL_TEXTURE_2D, shadow_texture[0]);
455                         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, shadow_texture_width, shadow_texture_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
456                 }
457         }
458
459         /* switch to shadow_framebuffer now and attach the first texture */
460         glBindFramebuffer(GL_FRAMEBUFFER, shadow_framebuffer[0]);
461         if (shadow_multisampling > 1)
462                 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, shadow_texture[0], 0);
463         else
464                 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, shadow_texture[0], 0);
465         GLenum DrawBuffers[1] = {GL_COLOR_ATTACHMENT0};
466         glDrawBuffers(1, DrawBuffers);
467         if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
468                 fprintf(stderr, "failed to create shadow texture\n");
469         }
470
471         /* clear to black + transparent */
472         glClearColor(0.0, 0.0, 0.0, 0.0);
473         glClear(GL_COLOR_BUFFER_BIT);
474 }
475
476 /* apply shadow from shadow_framebuffer to current_framebuffer and switch to it */
477 void end_shadow_render(void)
478 {
479         if (shadow_multisampling > 1) {
480                 /* attach second texture to second frame buffer */
481                 glBindFramebuffer(GL_FRAMEBUFFER, shadow_framebuffer[1]);
482                 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, shadow_texture[1], 0);
483                 glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, shadow_texture[1]);
484                 /* blit from first texture to second texture (MSAA to normal texture) */
485                 glBindFramebuffer(GL_READ_FRAMEBUFFER, shadow_framebuffer[0]);
486                 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, shadow_framebuffer[1]);
487                 glBlitFramebufferEXT( 0, 0, shadow_texture_width, shadow_texture_height, 0, 0, shadow_texture_width, shadow_texture_height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
488         }
489
490         /* switch to color buffer */
491         glBindFramebuffer(GL_FRAMEBUFFER, current_framebuffer);
492
493         /* apply shadow by drawing texture to screen */
494         glEnable(GL_BLEND);
495         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
496         glColor4d(1, 1, 1, 1);
497         glMatrixMode(GL_PROJECTION);
498         glPushMatrix();
499         glLoadIdentity();
500         glOrtho(-1, 1, -1, 1, -1, 1);
501
502         glEnable(GL_TEXTURE_2D);
503         if (shadow_multisampling > 1)
504                 glBindTexture(GL_TEXTURE_2D, shadow_texture[1]);
505         else
506                 glBindTexture(GL_TEXTURE_2D, shadow_texture[0]);
507         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
508         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
509         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
510         glBegin(GL_QUADS);
511         glTexCoord2f(0, 1);
512         glVertex3f(-1, 1, 0);
513         glTexCoord2f(1, 1);
514         glVertex3f(1, 1, 0);
515         glTexCoord2f(1, 0);
516         glVertex3f(1, -1, 0);
517         glTexCoord2f(0, 0);
518         glVertex3f(-1, -1, 0);
519         glEnd();
520         glDisable(GL_TEXTURE_2D);
521
522         glPopMatrix();
523         glMatrixMode(GL_MODELVIEW);
524         glDisable(GL_BLEND);
525 }
526
527 /* free image texture */
528 void exit_opengl(void)
529 {
530         int i;
531
532         if (shadow_texture[0]) {
533                 glDeleteTextures(2, shadow_texture);
534                 shadow_texture[0] = 0;
535         }
536         if (shadow_framebuffer[0]) {
537                 glDeleteFramebuffers(2, shadow_framebuffer);
538                 shadow_framebuffer[0] = 0;
539         }
540         if (image_rgb) {
541                 free(image_rgb);
542                 image_rgb = NULL;
543         }
544         for (i = 0; i < MAX_OSD; i++) {
545                 if (osd_rgba[i]) {
546                         free(osd_rgba[i]);
547                         osd_rgba[i] = NULL;
548                 }
549         }
550 }
551