1 /* RetroArch - A frontend for libretro.
2 * Copyright (C) 2011-2017 - Daniel De Matteis
3 * Copyright (C) 2014-2017 - Jean-André Santoni
4 * Copyright (C) 2016-2019 - Brad Parker
5 * Copyright (C) 2018 - Alfredo Monclús
6 * Copyright (C) 2018-2020 - natinusala
7 *
8 * RetroArch is free software: you can redistribute it and/or modify it under the terms
9 * of the GNU General Public License as published by the Free Software Found-
10 * ation, either version 3 of the License, or (at your option) any later version.
11 *
12 * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
13 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 * PURPOSE. See the GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along with RetroArch.
17 * If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "ozone.h"
21 #include "ozone_display.h"
22 #include "ozone_theme.h"
23
24 #include <string/stdstring.h>
25 #include <file/file_path.h>
26 #include <encodings/utf.h>
27 #include <lists/string_list.h>
28
29 #include "../../../gfx/gfx_animation.h"
30
31 #include "../../../input/input_osk.h"
32
33 static void ozone_cursor_animation_cb(void *userdata);
34
ozone_animate_cursor(ozone_handle_t * ozone,float * dst,float * target)35 static void ozone_animate_cursor(ozone_handle_t *ozone,
36 float *dst, float *target)
37 {
38 int i;
39 gfx_animation_ctx_entry_t entry;
40
41 entry.easing_enum = EASING_OUT_QUAD;
42 entry.tag = (uintptr_t)&ozone_default_theme;
43 entry.duration = ANIMATION_CURSOR_PULSE;
44 entry.userdata = ozone;
45
46 for (i = 0; i < 16; i++)
47 {
48 if (i == 3 || i == 7 || i == 11 || i == 15)
49 continue;
50
51 if (i == 14)
52 entry.cb = ozone_cursor_animation_cb;
53 else
54 entry.cb = NULL;
55
56 entry.subject = &dst[i];
57 entry.target_value = target[i];
58
59 gfx_animation_push(&entry);
60 }
61 }
62
ozone_cursor_animation_cb(void * userdata)63 static void ozone_cursor_animation_cb(void *userdata)
64 {
65 float *target = NULL;
66 ozone_handle_t *ozone = (ozone_handle_t*) userdata;
67
68 switch (ozone->theme_dynamic_cursor_state)
69 {
70 case 0:
71 target = ozone->theme->cursor_border_1;
72 break;
73 case 1:
74 target = ozone->theme->cursor_border_0;
75 break;
76 }
77
78 ozone->theme_dynamic_cursor_state =
79 (ozone->theme_dynamic_cursor_state + 1) % 2;
80
81 ozone_animate_cursor(ozone, ozone->theme_dynamic.cursor_border, target);
82 }
83
ozone_draw_cursor_slice(ozone_handle_t * ozone,gfx_display_t * p_disp,void * userdata,unsigned video_width,unsigned video_height,int x_offset,unsigned width,unsigned height,size_t y,float alpha)84 static void ozone_draw_cursor_slice(
85 ozone_handle_t *ozone,
86 gfx_display_t *p_disp,
87 void *userdata,
88 unsigned video_width,
89 unsigned video_height,
90 int x_offset,
91 unsigned width, unsigned height,
92 size_t y, float alpha)
93 {
94 float scale_factor = ozone->last_scale_factor;
95 int slice_x = x_offset - 12 * scale_factor;
96 int slice_y = (int)y + 8 * scale_factor;
97 unsigned slice_new_w = width + (24 + 1) * scale_factor;
98 unsigned slice_new_h = height + 20 * scale_factor;
99 gfx_display_ctx_driver_t
100 *dispctx = p_disp->dispctx;
101 static float
102 last_alpha = 0.0f;
103
104 if (alpha != last_alpha)
105 {
106 gfx_display_set_alpha(ozone->theme_dynamic.cursor_alpha, alpha);
107 gfx_display_set_alpha(ozone->theme_dynamic.cursor_border, alpha);
108 last_alpha = alpha;
109 }
110
111 if (dispctx && dispctx->blend_begin)
112 dispctx->blend_begin(userdata);
113
114 /* Cursor without border */
115 gfx_display_draw_texture_slice(
116 p_disp,
117 userdata,
118 video_width,
119 video_height,
120 slice_x,
121 slice_y,
122 80, 80,
123 slice_new_w,
124 slice_new_h,
125 video_width, video_height,
126 ozone->theme_dynamic.cursor_alpha,
127 20, scale_factor,
128 ozone->theme->textures[OZONE_THEME_TEXTURE_CURSOR_NO_BORDER]
129 );
130
131 /* Tainted border */
132 gfx_display_draw_texture_slice(
133 p_disp,
134 userdata,
135 video_width,
136 video_height,
137 slice_x,
138 slice_y,
139 80, 80,
140 slice_new_w,
141 slice_new_h,
142 video_width, video_height,
143 ozone->theme_dynamic.cursor_border,
144 20, scale_factor,
145 ozone->textures[OZONE_TEXTURE_CURSOR_BORDER]
146 );
147
148 if (dispctx && dispctx->blend_end)
149 dispctx->blend_end(userdata);
150 }
151
ozone_draw_cursor_fallback(ozone_handle_t * ozone,gfx_display_t * p_disp,void * userdata,unsigned video_width,unsigned video_height,int x_offset,unsigned width,unsigned height,size_t y,float alpha)152 static void ozone_draw_cursor_fallback(
153 ozone_handle_t *ozone,
154 gfx_display_t *p_disp,
155 void *userdata,
156 unsigned video_width,
157 unsigned video_height,
158 int x_offset,
159 unsigned width, unsigned height,
160 size_t y, float alpha)
161 {
162 static float last_alpha = 0.0f;
163
164 if (alpha != last_alpha)
165 {
166 gfx_display_set_alpha(ozone->theme_dynamic.selection_border, alpha);
167 gfx_display_set_alpha(ozone->theme_dynamic.selection, alpha);
168 last_alpha = alpha;
169 }
170
171 /* Fill */
172 gfx_display_draw_quad(
173 p_disp,
174 userdata,
175 video_width,
176 video_height,
177 x_offset,
178 (int)y,
179 width,
180 height - ozone->dimensions.spacer_3px,
181 video_width,
182 video_height,
183 ozone->theme_dynamic.selection);
184
185 /* Borders (can't do one single quad because of alpha) */
186
187 /* Top */
188 gfx_display_draw_quad(
189 p_disp,
190 userdata,
191 video_width,
192 video_height,
193 x_offset - ozone->dimensions.spacer_3px,
194 (int)(y - ozone->dimensions.spacer_3px),
195 width + ozone->dimensions.spacer_3px * 2,
196 ozone->dimensions.spacer_3px,
197 video_width,
198 video_height,
199 ozone->theme_dynamic.selection_border);
200
201 /* Bottom */
202 gfx_display_draw_quad(
203 p_disp,
204 userdata,
205 video_width,
206 video_height,
207 x_offset - ozone->dimensions.spacer_3px,
208 (int)(y + height - ozone->dimensions.spacer_3px),
209 width + ozone->dimensions.spacer_3px * 2,
210 ozone->dimensions.spacer_3px,
211 video_width,
212 video_height,
213 ozone->theme_dynamic.selection_border);
214
215 /* Left */
216 gfx_display_draw_quad(
217 p_disp,
218 userdata,
219 video_width,
220 video_height,
221 (int)(x_offset - ozone->dimensions.spacer_3px),
222 (int)y,
223 ozone->dimensions.spacer_3px,
224 height - ozone->dimensions.spacer_3px,
225 video_width,
226 video_height,
227 ozone->theme_dynamic.selection_border);
228
229 /* Right */
230 gfx_display_draw_quad(
231 p_disp,
232 userdata,
233 video_width,
234 video_height,
235 x_offset + width,
236 (int)y,
237 ozone->dimensions.spacer_3px,
238 height - ozone->dimensions.spacer_3px,
239 video_width,
240 video_height,
241 ozone->theme_dynamic.selection_border);
242 }
243
244
245
ozone_restart_cursor_animation(ozone_handle_t * ozone)246 void ozone_restart_cursor_animation(ozone_handle_t *ozone)
247 {
248 uintptr_t tag = (uintptr_t) &ozone_default_theme;
249
250 if (!ozone->has_all_assets)
251 return;
252
253 ozone->theme_dynamic_cursor_state = 1;
254 memcpy(ozone->theme_dynamic.cursor_border,
255 ozone->theme->cursor_border_0,
256 sizeof(ozone->theme_dynamic.cursor_border));
257 gfx_animation_kill_by_tag(&tag);
258
259 ozone_animate_cursor(ozone,
260 ozone->theme_dynamic.cursor_border,
261 ozone->theme->cursor_border_1);
262 }
263
ozone_draw_cursor(ozone_handle_t * ozone,gfx_display_t * p_disp,void * userdata,unsigned video_width,unsigned video_height,int x_offset,unsigned width,unsigned height,size_t y,float alpha)264 void ozone_draw_cursor(
265 ozone_handle_t *ozone,
266 gfx_display_t *p_disp,
267 void *userdata,
268 unsigned video_width,
269 unsigned video_height,
270 int x_offset,
271 unsigned width, unsigned height,
272 size_t y, float alpha)
273 {
274 int new_x = x_offset;
275 size_t new_y = y;
276
277 /* Apply wiggle animation if needed */
278 if (ozone->cursor_wiggle_state.wiggling)
279 ozone_apply_cursor_wiggle_offset(ozone, &new_x, &new_y);
280
281 /* Draw the cursor */
282 if (ozone->has_all_assets)
283 ozone_draw_cursor_slice(ozone,
284 p_disp,
285 userdata,
286 video_width, video_height,
287 new_x, width, height, new_y, alpha);
288 else
289 ozone_draw_cursor_fallback(ozone,
290 p_disp,
291 userdata,
292 video_width,
293 video_height,
294 new_x, width, height, new_y, alpha);
295 }
296
ozone_draw_icon(gfx_display_t * p_disp,void * userdata,unsigned video_width,unsigned video_height,unsigned icon_width,unsigned icon_height,uintptr_t texture,float x,float y,unsigned width,unsigned height,float rotation,float scale_factor,float * color)297 void ozone_draw_icon(
298 gfx_display_t *p_disp,
299 void *userdata,
300 unsigned video_width,
301 unsigned video_height,
302 unsigned icon_width,
303 unsigned icon_height,
304 uintptr_t texture,
305 float x, float y,
306 unsigned width, unsigned height,
307 float rotation, float scale_factor,
308 float *color)
309 {
310 gfx_display_ctx_rotate_draw_t rotate_draw;
311 gfx_display_ctx_draw_t draw;
312 struct video_coords coords;
313 math_matrix_4x4 mymat;
314 gfx_display_ctx_driver_t
315 *dispctx = p_disp->dispctx;
316
317 rotate_draw.matrix = &mymat;
318 rotate_draw.rotation = rotation;
319 rotate_draw.scale_x = scale_factor;
320 rotate_draw.scale_y = scale_factor;
321 rotate_draw.scale_z = 1;
322 rotate_draw.scale_enable = true;
323
324 gfx_display_rotate_z(p_disp, &rotate_draw, userdata);
325
326 coords.vertices = 4;
327 coords.vertex = NULL;
328 coords.tex_coord = NULL;
329 coords.lut_tex_coord = NULL;
330 coords.color = (const float*)color;
331
332 draw.x = x;
333 draw.y = height - y - icon_height;
334 draw.width = icon_width;
335 draw.height = icon_height;
336 draw.scale_factor = scale_factor;
337 draw.rotation = rotation;
338 draw.coords = &coords;
339 draw.matrix_data = &mymat;
340 draw.texture = texture;
341 draw.prim_type = GFX_DISPLAY_PRIM_TRIANGLESTRIP;
342 draw.pipeline_id = 0;
343
344 if (draw.height > 0 && draw.width > 0)
345 dispctx->draw(&draw, userdata, video_width, video_height);
346 }
347
ozone_draw_backdrop(void * userdata,void * disp_data,unsigned video_width,unsigned video_height,float alpha)348 void ozone_draw_backdrop(
349 void *userdata,
350 void *disp_data,
351 unsigned video_width,
352 unsigned video_height,
353 float alpha)
354 {
355 static float ozone_backdrop[16] = {
356 0.00, 0.00, 0.00, 0.75,
357 0.00, 0.00, 0.00, 0.75,
358 0.00, 0.00, 0.00, 0.75,
359 0.00, 0.00, 0.00, 0.75,
360 };
361 static float last_alpha = 0.0f;
362
363 /* TODO: Replace this backdrop by a blur shader
364 * on the whole screen if available */
365 if (alpha != last_alpha)
366 {
367 gfx_display_set_alpha(ozone_backdrop, alpha);
368 last_alpha = alpha;
369 }
370
371 gfx_display_draw_quad(
372 (gfx_display_t*)disp_data,
373 userdata,
374 video_width,
375 video_height,
376 0,
377 0,
378 video_width,
379 video_height,
380 video_width,
381 video_height,
382 ozone_backdrop);
383 }
384
ozone_draw_osk(ozone_handle_t * ozone,void * userdata,void * disp_userdata,unsigned video_width,unsigned video_height,const char * label,const char * str)385 void ozone_draw_osk(ozone_handle_t *ozone,
386 void *userdata,
387 void *disp_userdata,
388 unsigned video_width,
389 unsigned video_height,
390 const char *label, const char *str)
391 {
392 unsigned i;
393 char message[2048];
394 gfx_display_t *p_disp = (gfx_display_t*)disp_userdata;
395 const char *text = str;
396 unsigned text_color = 0xffffffff;
397 static float ozone_osk_backdrop[16] = {
398 0.00, 0.00, 0.00, 0.15,
399 0.00, 0.00, 0.00, 0.15,
400 0.00, 0.00, 0.00, 0.15,
401 0.00, 0.00, 0.00, 0.15,
402 };
403 static retro_time_t last_time = 0;
404 struct string_list list = {0};
405 float scale_factor = ozone->last_scale_factor;
406 unsigned margin = 75 * scale_factor;
407 unsigned padding = 10 * scale_factor;
408 unsigned bottom_end = video_height / 2;
409 unsigned y_offset = 0;
410 bool draw_placeholder = string_is_empty(str);
411 retro_time_t current_time = menu_driver_get_current_time();
412
413 if (current_time - last_time >= INTERVAL_OSK_CURSOR)
414 {
415 ozone->osk_cursor = !ozone->osk_cursor;
416 last_time = current_time;
417 }
418
419 /* Border */
420 /* Top */
421 gfx_display_draw_quad(
422 p_disp,
423 userdata,
424 video_width,
425 video_height,
426 margin,
427 margin,
428 video_width - margin*2,
429 ozone->dimensions.spacer_1px,
430 video_width,
431 video_height,
432 ozone->theme->entries_border);
433
434 /* Bottom */
435 gfx_display_draw_quad(
436 p_disp,
437 userdata,
438 video_width,
439 video_height,
440 margin,
441 bottom_end - margin,
442 video_width - margin*2,
443 ozone->dimensions.spacer_1px,
444 video_width,
445 video_height,
446 ozone->theme->entries_border);
447
448 /* Left */
449 gfx_display_draw_quad(
450 p_disp,
451 userdata,
452 video_width,
453 video_height,
454 margin,
455 margin,
456 ozone->dimensions.spacer_1px,
457 bottom_end - margin*2,
458 video_width,
459 video_height,
460 ozone->theme->entries_border);
461
462 /* Right */
463 gfx_display_draw_quad(
464 p_disp,
465 userdata,
466 video_width,
467 video_height,
468 video_width - margin,
469 margin,
470 ozone->dimensions.spacer_1px,
471 bottom_end - margin*2,
472 video_width,
473 video_height,
474 ozone->theme->entries_border);
475
476 /* Backdrop */
477 /* TODO: Remove the backdrop if blur shader is available */
478 gfx_display_draw_quad(
479 p_disp,
480 userdata,
481 video_width,
482 video_height,
483 margin + ozone->dimensions.spacer_1px,
484 margin + ozone->dimensions.spacer_1px,
485 video_width - margin*2 - ozone->dimensions.spacer_2px,
486 bottom_end - margin*2 - ozone->dimensions.spacer_2px,
487 video_width,
488 video_height,
489 ozone_osk_backdrop);
490
491 /* Placeholder & text*/
492 if (draw_placeholder)
493 {
494 text = label;
495 text_color = ozone_theme_light.text_sublabel_rgba;
496 }
497
498 (ozone->word_wrap)(message, sizeof(message), text,
499 (video_width - margin*2 - padding*2) / ozone->fonts.entries_label.glyph_width,
500 ozone->fonts.entries_label.wideglyph_width, 0);
501
502 string_list_initialize(&list);
503 string_split_noalloc(&list, message, "\n");
504
505 for (i = 0; i < list.size; i++)
506 {
507 const char *msg = list.elems[i].data;
508
509 gfx_display_draw_text(
510 ozone->fonts.entries_label.font,
511 msg,
512 margin + padding * 2, /* x */
513 margin + padding +
514 ozone->fonts.entries_label.line_height
515 + y_offset, /* y */
516 video_width, video_height,
517 text_color,
518 TEXT_ALIGN_LEFT,
519 1.0f,
520 false,
521 1.0f,
522 false);
523
524 /* Cursor */
525 if (i == list.size - 1)
526 {
527 if (ozone->osk_cursor)
528 {
529 unsigned cursor_x = draw_placeholder
530 ? 0
531 : font_driver_get_message_width(
532 ozone->fonts.entries_label.font, msg,
533 (unsigned)strlen(msg), 1);
534 gfx_display_draw_quad(
535 p_disp,
536 userdata,
537 video_width,
538 video_height,
539 margin
540 + padding * 2
541 + cursor_x,
542 margin
543 + padding
544 + y_offset
545 + ozone->fonts.entries_label.line_height
546 - ozone->fonts.entries_label.line_ascender
547 + ozone->dimensions.spacer_3px,
548 ozone->dimensions.spacer_1px,
549 ozone->fonts.entries_label.line_ascender,
550 video_width,
551 video_height,
552 ozone->pure_white);
553 }
554 }
555 else
556 y_offset += 25 * scale_factor;
557 }
558
559 /* Keyboard */
560 gfx_display_draw_keyboard(
561 p_disp,
562 userdata,
563 video_width,
564 video_height,
565 ozone->theme->textures[OZONE_THEME_TEXTURE_CURSOR_STATIC],
566 ozone->fonts.entries_label.font,
567 input_event_get_osk_grid(),
568 input_event_get_osk_ptr(),
569 ozone->theme->text_rgba);
570
571 string_list_deinitialize(&list);
572 }
573
ozone_draw_messagebox(ozone_handle_t * ozone,gfx_display_t * p_disp,void * userdata,unsigned video_width,unsigned video_height,const char * message)574 void ozone_draw_messagebox(
575 ozone_handle_t *ozone,
576 gfx_display_t *p_disp,
577 void *userdata,
578 unsigned video_width,
579 unsigned video_height,
580 const char *message)
581 {
582 unsigned i, y_position;
583 char wrapped_message[MENU_SUBLABEL_MAX_LENGTH];
584 int x, y, longest_width = 0;
585 int usable_width = 0;
586 struct string_list list = {0};
587 float scale_factor = 0.0f;
588 unsigned width = video_width;
589 unsigned height = video_height;
590 gfx_display_ctx_driver_t
591 *dispctx = p_disp->dispctx;
592
593 wrapped_message[0] = '\0';
594
595 /* Sanity check */
596 if (string_is_empty(message) ||
597 !ozone->fonts.footer.font)
598 return;
599
600 scale_factor = ozone->last_scale_factor;
601 usable_width = (int)width - (48 * 8 * scale_factor);
602
603 if (usable_width < 1)
604 return;
605
606 /* Split message into lines */
607 (ozone->word_wrap)(
608 wrapped_message, sizeof(wrapped_message), message,
609 usable_width / (int)ozone->fonts.footer.glyph_width,
610 ozone->fonts.footer.wideglyph_width, 0);
611
612 string_list_initialize(&list);
613 if (
614 !string_split_noalloc(&list, wrapped_message, "\n")
615 || list.elems == 0)
616 {
617 string_list_deinitialize(&list);
618 return;
619 }
620
621 y_position = height / 2;
622 if (menu_input_dialog_get_display_kb())
623 y_position = height / 4;
624
625 x = width / 2;
626 y = y_position - (list.size
627 * ozone->fonts.footer.line_height) / 2;
628
629 /* find the longest line width */
630 for (i = 0; i < list.size; i++)
631 {
632 const char *msg = list.elems[i].data;
633
634 if (!string_is_empty(msg))
635 {
636 int width = font_driver_get_message_width(
637 ozone->fonts.footer.font, msg, (unsigned)strlen(msg), 1);
638
639 if (width > longest_width)
640 longest_width = width;
641 }
642 }
643
644 gfx_display_set_alpha(ozone->theme_dynamic.message_background, ozone->animations.messagebox_alpha);
645
646 if (dispctx && dispctx->blend_begin)
647 dispctx->blend_begin(userdata);
648
649 /* Avoid drawing a black box if there's no assets */
650 if (ozone->has_all_assets)
651 {
652 /* Note: The fact that we use a texture slice here
653 * makes things very messy
654 * > The actual size and offset of a texture slice
655 * is quite 'loose', and depends upon source image
656 * size, draw size and scale factor... */
657 unsigned slice_new_w = longest_width + 48 * 2 * scale_factor;
658 unsigned slice_new_h = ozone->fonts.footer.line_height * (list.size + 2);
659 int slice_x = x - longest_width/2 - 48 * scale_factor;
660 int slice_y = y - ozone->fonts.footer.line_height +
661 ((slice_new_h >= 256)
662 ? (16.0f * scale_factor)
663 : (16.0f * ((float)slice_new_h / 256.0f)));
664
665 gfx_display_draw_texture_slice(
666 p_disp,
667 userdata,
668 video_width,
669 video_height,
670 slice_x,
671 slice_y,
672 256, 256,
673 slice_new_w,
674 slice_new_h,
675 width, height,
676 ozone->theme_dynamic.message_background,
677 16, scale_factor,
678 ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_DIALOG_SLICE]
679 );
680 }
681
682 for (i = 0; i < list.size; i++)
683 {
684 const char *msg = list.elems[i].data;
685
686 if (msg)
687 gfx_display_draw_text(
688 ozone->fonts.footer.font,
689 msg,
690 x - longest_width/2.0,
691 y + (i * ozone->fonts.footer.line_height) +
692 ozone->fonts.footer.line_ascender,
693 width,
694 height,
695 COLOR_TEXT_ALPHA(ozone->theme->text_rgba, (uint32_t)(ozone->animations.messagebox_alpha*255.0f)),
696 TEXT_ALIGN_LEFT,
697 1.0f,
698 false,
699 1.0f,
700 false);
701 }
702
703 string_list_deinitialize(&list);
704 }
705
ozone_draw_fullscreen_thumbnails(ozone_handle_t * ozone,void * userdata,void * disp_userdata,unsigned video_width,unsigned video_height)706 void ozone_draw_fullscreen_thumbnails(
707 ozone_handle_t *ozone,
708 void *userdata,
709 void *disp_userdata,
710 unsigned video_width,
711 unsigned video_height)
712 {
713 /* Check whether fullscreen thumbnails are visible */
714 if (ozone->animations.fullscreen_thumbnail_alpha > 0.0f)
715 {
716 /* Note: right thumbnail is drawn at the top
717 * in the sidebar, so it becomes the *left*
718 * thumbnail when viewed fullscreen */
719 gfx_thumbnail_t *right_thumbnail = &ozone->thumbnails.left;
720 gfx_thumbnail_t *left_thumbnail = &ozone->thumbnails.right;
721 unsigned width = video_width;
722 unsigned height = video_height;
723 int view_width = (int)width;
724 gfx_display_t *p_disp = (gfx_display_t*)disp_userdata;
725
726 int view_height = (int)height - ozone->dimensions.header_height - ozone->dimensions.footer_height - ozone->dimensions.spacer_1px;
727 int thumbnail_margin = ozone->dimensions.fullscreen_thumbnail_padding;
728 bool show_right_thumbnail = false;
729 bool show_left_thumbnail = false;
730 unsigned num_thumbnails = 0;
731 float right_thumbnail_draw_width = 0.0f;
732 float right_thumbnail_draw_height = 0.0f;
733 float left_thumbnail_draw_width = 0.0f;
734 float left_thumbnail_draw_height = 0.0f;
735 float background_alpha = 0.85f;
736 static float background_color[16] = {
737 0.0f, 0.0f, 0.0f, 1.0f,
738 0.0f, 0.0f, 0.0f, 1.0f,
739 0.0f, 0.0f, 0.0f, 1.0f,
740 0.0f, 0.0f, 0.0f, 1.0f,
741 };
742 int frame_width = (int)((float)thumbnail_margin / 3.0f);
743 float frame_color[16];
744 float separator_color[16];
745 int thumbnail_box_width;
746 int thumbnail_box_height;
747 int right_thumbnail_x;
748 int left_thumbnail_x;
749 int thumbnail_y;
750
751 /* Sanity check: Return immediately if this is
752 * a menu without thumbnails and we are not currently
753 * 'fading out' the fullscreen thumbnail view */
754 if (!ozone->fullscreen_thumbnails_available &&
755 ozone->show_fullscreen_thumbnails)
756 goto error;
757
758 /* Safety check: ensure that current
759 * selection matches the entry selected when
760 * fullscreen thumbnails were enabled
761 * > Note that we exclude this check if we are
762 * currently viewing the quick menu and the
763 * thumbnail view is fading out. This enables
764 * a smooth transition if the user presses
765 * RetroPad A or keyboard 'return' to enter the
766 * quick menu while fullscreen thumbnails are
767 * being displayed */
768 if (((size_t)ozone->selection != ozone->fullscreen_thumbnail_selection) &&
769 (!ozone->is_quick_menu || ozone->show_fullscreen_thumbnails))
770 goto error;
771
772 /* Sanity check: Return immediately if the view
773 * width/height is < 1 */
774 if ((view_width < 1) || (view_height < 1))
775 goto error;
776
777 /* Get number of 'active' thumbnails */
778 show_right_thumbnail = (right_thumbnail->status == GFX_THUMBNAIL_STATUS_AVAILABLE);
779 show_left_thumbnail = (left_thumbnail->status == GFX_THUMBNAIL_STATUS_AVAILABLE);
780
781 if (show_right_thumbnail)
782 num_thumbnails++;
783
784 if (show_left_thumbnail)
785 num_thumbnails++;
786
787 /* Do nothing if both thumbnails are missing
788 * > Note: Baring inexplicable internal errors, this
789 * can never happen... */
790 if (num_thumbnails < 1)
791 goto error;
792
793 /* Get base thumbnail dimensions + draw positions */
794
795 /* > Thumbnail bounding box height + y position
796 * are fixed */
797 thumbnail_box_height = view_height - (thumbnail_margin * 2);
798 thumbnail_y = ozone->dimensions.header_height + thumbnail_margin + ozone->dimensions.spacer_1px;
799
800 /* Thumbnail bounding box width and x position
801 * depend upon number of active thumbnails */
802 if (num_thumbnails == 2)
803 {
804 thumbnail_box_width = (view_width - (thumbnail_margin * 3) - frame_width) >> 1;
805 left_thumbnail_x = thumbnail_margin;
806 right_thumbnail_x = left_thumbnail_x + thumbnail_box_width + frame_width + thumbnail_margin;
807 }
808 else
809 {
810 thumbnail_box_width = view_width - (thumbnail_margin * 2);
811 left_thumbnail_x = thumbnail_margin;
812 right_thumbnail_x = left_thumbnail_x;
813 }
814
815 /* Sanity check */
816 if ((thumbnail_box_width < 1) ||
817 (thumbnail_box_height < 1))
818 goto error;
819
820 /* Get thumbnail draw dimensions
821 * > Note: The following code is a bit awkward, since
822 * we have to do things in a very specific order
823 * - i.e. we cannot determine proper thumbnail
824 * layout until we have thumbnail draw dimensions.
825 * and we cannot get draw dimensions until we have
826 * the bounding box dimensions... */
827 if (show_right_thumbnail)
828 {
829 gfx_thumbnail_get_draw_dimensions(
830 right_thumbnail,
831 thumbnail_box_width, thumbnail_box_height, 1.0f,
832 &right_thumbnail_draw_width, &right_thumbnail_draw_height);
833
834 /* Sanity check */
835 if ((right_thumbnail_draw_width <= 0.0f) ||
836 (right_thumbnail_draw_height <= 0.0f))
837 goto error;
838 }
839
840 if (show_left_thumbnail)
841 {
842 gfx_thumbnail_get_draw_dimensions(
843 left_thumbnail,
844 thumbnail_box_width, thumbnail_box_height, 1.0f,
845 &left_thumbnail_draw_width, &left_thumbnail_draw_height);
846
847 /* Sanity check */
848 if ((left_thumbnail_draw_width <= 0.0f) ||
849 (left_thumbnail_draw_height <= 0.0f))
850 goto error;
851 }
852
853 /* Adjust thumbnail draw positions to achieve
854 * uniform appearance (accounting for actual
855 * draw dimensions...) */
856 if (num_thumbnails == 2)
857 {
858 int left_padding = (thumbnail_box_width - (int)left_thumbnail_draw_width) >> 1;
859 int right_padding = (thumbnail_box_width - (int)right_thumbnail_draw_width) >> 1;
860
861 /* Move thumbnails as close together as possible,
862 * and horizontally centre the resultant 'block'
863 * of images */
864 left_thumbnail_x += right_padding;
865 right_thumbnail_x -= left_padding;
866 }
867
868 /* Set colour values */
869
870 /* > Background */
871 gfx_display_set_alpha(
872 background_color,
873 background_alpha * ozone->animations.fullscreen_thumbnail_alpha);
874
875 /* > Separators */
876 memcpy(separator_color, ozone->theme->header_footer_separator, sizeof(separator_color));
877 gfx_display_set_alpha(
878 separator_color, ozone->animations.fullscreen_thumbnail_alpha);
879
880 /* > Thumbnail frame */
881 memcpy(frame_color, ozone->theme->sidebar_background, sizeof(frame_color));
882 gfx_display_set_alpha(
883 frame_color, ozone->animations.fullscreen_thumbnail_alpha);
884
885 /* Darken background */
886 gfx_display_draw_quad(
887 p_disp,
888 userdata,
889 video_width,
890 video_height,
891 0,
892 ozone->dimensions.header_height + ozone->dimensions.spacer_1px,
893 width,
894 (unsigned)view_height,
895 width,
896 height,
897 background_color);
898
899 /* Draw full-width separators */
900 gfx_display_draw_quad(
901 p_disp,
902 userdata,
903 video_width,
904 video_height,
905 0,
906 ozone->dimensions.header_height,
907 width,
908 ozone->dimensions.spacer_1px,
909 width,
910 height,
911 separator_color);
912
913 gfx_display_draw_quad(
914 p_disp,
915 userdata,
916 video_width,
917 video_height,
918 0,
919 height - ozone->dimensions.footer_height,
920 width,
921 ozone->dimensions.spacer_1px,
922 width,
923 height,
924 separator_color);
925
926 /* Draw thumbnails */
927
928 /* > Right */
929 if (show_right_thumbnail)
930 {
931 /* Background */
932 gfx_display_draw_quad(
933 p_disp,
934 userdata,
935 video_width,
936 video_height,
937 right_thumbnail_x - frame_width +
938 ((thumbnail_box_width - (int)right_thumbnail_draw_width) >> 1),
939 thumbnail_y - frame_width +
940 ((thumbnail_box_height - (int)right_thumbnail_draw_height) >> 1),
941 (unsigned)right_thumbnail_draw_width + (frame_width << 1),
942 (unsigned)right_thumbnail_draw_height + (frame_width << 1),
943 width,
944 height,
945 frame_color);
946
947 /* Thumbnail */
948 gfx_thumbnail_draw(
949 userdata,
950 video_width,
951 video_height,
952 right_thumbnail,
953 right_thumbnail_x,
954 thumbnail_y,
955 (unsigned)thumbnail_box_width,
956 (unsigned)thumbnail_box_height,
957 GFX_THUMBNAIL_ALIGN_CENTRE,
958 ozone->animations.fullscreen_thumbnail_alpha,
959 1.0f,
960 NULL);
961 }
962
963 /* > Left */
964 if (show_left_thumbnail)
965 {
966 /* Background */
967 gfx_display_draw_quad(
968 p_disp,
969 userdata,
970 video_width,
971 video_height,
972 left_thumbnail_x - frame_width +
973 ((thumbnail_box_width - (int)left_thumbnail_draw_width) >> 1),
974 thumbnail_y - frame_width +
975 ((thumbnail_box_height - (int)left_thumbnail_draw_height) >> 1),
976 (unsigned)left_thumbnail_draw_width + (frame_width << 1),
977 (unsigned)left_thumbnail_draw_height + (frame_width << 1),
978 width,
979 height,
980 frame_color);
981
982 /* Thumbnail */
983 gfx_thumbnail_draw(
984 userdata,
985 video_width,
986 video_height,
987 left_thumbnail,
988 left_thumbnail_x,
989 thumbnail_y,
990 (unsigned)thumbnail_box_width,
991 (unsigned)thumbnail_box_height,
992 GFX_THUMBNAIL_ALIGN_CENTRE,
993 ozone->animations.fullscreen_thumbnail_alpha,
994 1.0f,
995 NULL);
996 }
997 }
998
999 return;
1000
1001 error:
1002 /* If fullscreen thumbnails are enabled at
1003 * this point, must disable them immediately... */
1004 if (ozone->show_fullscreen_thumbnails)
1005 ozone_hide_fullscreen_thumbnails(ozone, false);
1006 }
1007