The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
render.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2016 by David White <[email protected]>
3  Copyright (C) 2009 - 2016 by Ignacio R. Morelle <[email protected]>
4  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
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 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 /**
17  * @file
18  * Storyscreen parts renderer.
19  * @todo Translate relevant parts to GUI2.
20  */
21 
22 #include "global.hpp"
23 #include "asserts.hpp"
24 #include "log.hpp"
25 #include "storyscreen/part.hpp"
26 #include "storyscreen/render.hpp"
27 
28 #include "image.hpp"
29 #include "language.hpp"
30 #include "sdl/rect.hpp"
31 #include "sound.hpp"
32 #include "text.hpp"
33 #include "video.hpp"
34 #include "widgets/button.hpp"
35 
36 static lg::log_domain log_engine("engine");
37 #define ERR_NG LOG_STREAM(err, log_engine)
38 #define WARN_NG LOG_STREAM(warn, log_engine)
39 #define LOG_NG LOG_STREAM(info, log_engine)
40 
41 
42 namespace {
43  int const storybox_padding = 10; // px
44 #ifdef SDL_GPU
45  int const storyshadow_a = 125;
46 #else
47  double const storyshadow_opacity = 0.5;
48 #endif
49  int const storyshadow_r = 0;
50  int const storyshadow_g = 0;
51  int const storyshadow_b = 0;
52 
53  int const titlebox_padding = 20; // px
54  int const titleshadow_padding = 5; // px
55 #ifdef SDL_GPU
56  int const titleshadow_a = 125;
57 #else
58  double const titleshadow_opacity = 0.5;
59 #endif
60  int const titleshadow_r = 0;
61  int const titleshadow_g = 0;
62  int const titleshadow_b = 0;
63 
64  int const titlebox_font_size = 20; // pt?
65  int const storybox_font_size = 14; // pt?
66 
67  Uint32 const titlebox_font_color = 0xFFFFFFFF;
68  Uint32 const storybox_font_color = 0xDDDDDDFF;
69 
70 #ifndef LOW_MEM
71  // Hard-coded path to a suitable (tileable) pic for the storytxt box border.
72  std::string const storybox_top_border_path = "dialogs/translucent54-border-top.png";
73  std::string const storybox_bottom_border_path = "dialogs/translucent54-border-bottom.png";
74 #ifndef SDL_GPU
75  void blur_area(CVideo& video, int y, int h)
76  {
77  SDL_Rect blur_rect = sdl::create_rect(0, y, screen_area().w, h);
78  surface blur = get_surface_portion(video.getSurface(), blur_rect);
79  blur = blur_surface(blur, 1, false);
80  video.blit_surface(0, y, blur);
81  }
82 #endif
83 #endif
84 }
85 
86 namespace storyscreen {
87 
88 part_ui::part_ui(part &p, CVideo& video, gui::button &next_button,
89  gui::button &back_button, gui::button&play_button)
90  : events::sdl_handler(false)
91  , p_(p)
92  , video_(video)
93  , keys_()
94  , next_button_(next_button)
95  , back_button_(back_button)
96  , play_button_(play_button)
97  , dirty_(true)
98  , ret_(NEXT), skip_(false), last_key_(false)
99  , x_scale_factor_(1.0)
100  , y_scale_factor_(1.0)
101  , base_rect_()
102 #ifdef SDL_GPU
103  , background_images_()
104  , background_positions_()
105 #else
106  , background_(nullptr)
107 #endif
108  , imgs_()
109  , has_background_(false)
110  , text_x_(200)
111  , text_y_(400)
112  , buttons_x_(0)
113  , buttons_y_(0)
114 {
115 
116 }
117 
119 {
120 #ifdef SDL_GPU
121  base_rect_.w = video_.getx();
122  base_rect_.h = video_.gety();
123  has_background_ = false;
124  bool no_base_yet = true;
125 
126  for (const background_layer& bl : p_.get_background_layers()) {
127  sdl::timage layer;
128 
129  if (!bl.file().empty()) {
130  layer = image::get_texture(bl.file());
131  }
132  has_background_ = has_background_ || !layer.null();
133  if(layer.null() || layer.width() * layer.height() == 0) {
134  continue;
135  }
136 
137  const double xscale = 1.0 * video_.getx() / layer.base_width();
138  const double yscale = 1.0 * video_.gety() / layer.base_height();
139  const bool scalev = bl.scale_vertically();
140  const bool scaleh = bl.scale_horizontally();
141  const bool keep_ratio = bl.keep_aspect_ratio();
142  const bool tileh = bl.tile_horizontally();
143  const bool tilev = bl.tile_vertically();
144 
145  double x_scale_factor = scaleh ? xscale : 1.0;
146  double y_scale_factor = scalev ? yscale : 1.0;
147 
148  if (scalev && scaleh && keep_ratio) {
149  x_scale_factor = y_scale_factor = std::min<double>(xscale, yscale);
150  } else if (keep_ratio && scaleh) {
151  x_scale_factor = y_scale_factor = xscale;
152  } else if (keep_ratio && scalev) {
153  x_scale_factor = y_scale_factor = yscale;
154  }
155 
156  layer.set_smooth_scaling(true);
157  SDL_Rect clip = sdl::create_rect(0, 0, layer.base_width(), layer.base_height());
158  if (tileh) {
159  clip.x = (layer.base_width() - video_.getx())/2;
160  clip.w = video_.getx();
161  layer.set_hwrap(GPU_WRAP_REPEAT);
162  }
163  if (tilev) {
164  clip.y = (layer.base_height() - video_.gety())/2;
165  clip.h = video_.gety();
166  layer.set_vwrap(GPU_WRAP_REPEAT);
167  }
168  layer.set_clip(clip);
169  layer.set_scale(x_scale_factor, y_scale_factor);
170 
171  SDL_Rect base_rect = sdl::create_rect(
172  (video_.getx() - layer.width()) / 2
173  , (video_.gety() - layer.height()) / 2
174  , layer.width()
175  , layer.height());
176 
177  background_images_.push_back(layer);
178  background_positions_.push_back(std::pair<int, int>(base_rect.x, base_rect.y));
179 
180  if (bl.is_base_layer() || no_base_yet) {
181  x_scale_factor_ = x_scale_factor;
182  y_scale_factor_ = y_scale_factor;
183  base_rect_ = base_rect;
184  no_base_yet = false;
185  }
186  }
187 #else
189  base_rect_.w = video_.getx();
190  base_rect_.h = video_.gety();
191  has_background_ = false;
192  bool no_base_yet = true;
193 
194  // Build background surface
195  for (const background_layer& bl : p_.get_background_layers()) {
196  surface layer;
197 
198  if(bl.file().empty() != true) {
199  layer.assign( image::get_image(bl.file()) );
200  }
201  has_background_ = has_background_ || !layer.null();
202  if(layer.null() || layer->w * layer->h == 0) {
203  continue;
204  }
205 
206  layer = make_neutral_surface(layer);
207 
208  const double xscale = 1.0 * video_.getx() / layer->w;
209  const double yscale = 1.0 * video_.gety() / layer->h;
210  const bool scalev = bl.scale_vertically();
211  const bool scaleh = bl.scale_horizontally();
212  const bool keep_ratio = bl.keep_aspect_ratio();
213 
214  double x_scale_factor = scaleh ? xscale : 1.0;
215  double y_scale_factor = scalev ? yscale : 1.0;
216 
217  if (scalev && scaleh && keep_ratio) {
218  x_scale_factor = y_scale_factor = std::min<double>(xscale, yscale);
219  } else if (keep_ratio && scaleh) {
220  x_scale_factor = y_scale_factor = xscale;
221  } else if (keep_ratio && scalev) {
222  x_scale_factor = y_scale_factor = yscale;
223  }
224 
225  layer = scale_surface(layer, static_cast<int>(layer->w*x_scale_factor), static_cast<int>(layer->h*y_scale_factor), false);
226 
227  const int tilew = bl.tile_horizontally() ? video_.getx() : layer->w;
228  const int tileh = bl.tile_vertically() ? video_.gety() : layer->h;
229 
230  layer = tile_surface(layer, tilew, tileh, false);
231 
232  SDL_Rect drect = sdl::create_rect(
233  (background_->w - layer->w) / 2
234  , (background_->h - layer->h) / 2
235  , layer->w
236  , layer->h);
237  SDL_Rect srect = sdl::create_rect(
238  0
239  , 0
240  , layer->w
241  , layer->h);
242  SDL_Rect base_rect = drect;
243 
244  // If we can't see the whole image anyways, we'll want to display the
245  // top-middle area.
246  if (drect.y < 0) {
247  drect.y = 0;
248  base_rect.y = 0;
249  }
250 
251  if (drect.x < 0) {
252  srect.x -= drect.x;
253  drect.x = 0;
254  }
255 
256  blit_surface(layer, &srect, background_, &drect);
257  ASSERT_LOG(layer.null() == false, "Oops: a storyscreen part background layer got nullptr");
258 
259  if (bl.is_base_layer() || no_base_yet) {
260  x_scale_factor_ = x_scale_factor;
261  y_scale_factor_ = y_scale_factor;
262  base_rect_ = base_rect;
263  no_base_yet = false;
264  }
265  }
266 #endif
267 }
268 
270 {
271  if(video_.getx() <= 800) {
272  text_x_ = 10;
273  buttons_x_ = video_.getx() - 100 - 20;
274  }
275  else {
276  text_x_ = 100 - 40;
277  buttons_x_ = video_.getx() - 100 - 40;
278  }
279 
280  switch(p_.story_text_location())
281  {
282  case part::BLOCK_TOP:
283  text_y_ = 0;
284  buttons_y_ = 40;
285  break;
286  case part::BLOCK_MIDDLE:
287  text_y_ = video_.gety() / 3;
288  buttons_y_ = video_.gety() / 2 + 15;
289  break;
290  default: // part::BLOCK_BOTTOM
291  text_y_ = video_.gety() - 200;
292  buttons_y_ = video_.gety() - 40;
293  break;
294  }
295 
299 }
300 
302 {
303  // Build floating image surfaces
304  for (const floating_image& fi : p_.get_floating_images()) {
305  imgs_.push_back( fi.get_render_input(x_scale_factor_, y_scale_factor_, base_rect_) );
306  }
307 }
308 
310 {
312  0, 0, video_.getx(), video_.gety(), 0, 0, 0, 1.0,
314  );
315  sdl_blit(background_, nullptr, video_.getSurface(), nullptr);
316  // Render the titlebox over the background
318 }
319 
321 {
322 #ifdef SDL_GPU
323  skip_ = false;
324  last_key_ = true;
325 
326  size_t fi_n = 0;
327  for (floating_image::render_input& ri : imgs_) {
328  const floating_image& fi = p_.get_floating_images()[fi_n];
329 
330  if(!ri.image.null()) {
331  ri.image.draw(video_, ri.rect.x, ri.rect.y);
332  video_.flip();
333  }
334 
335  if (!skip_)
336  {
337  int delay = fi.display_delay(), delay_step = 20;
338  for (int i = 0; i != (delay + delay_step - 1) / delay_step; ++i)
339  {
340  if (handle_interface()) return false;
341  if (skip_) break;
342  CVideo::delay(std::min<int>(delay_step, delay - i * delay_step));
343  }
344  }
345 
346  ++fi_n;
347  }
348 
349  return true;
350 #else
352 
353  skip_ = false;
354  last_key_ = true;
355 
356  size_t fi_n = 0;
357  for (floating_image::render_input& ri : imgs_) {
358  const floating_image& fi = p_.get_floating_images()[fi_n];
359 
360  if(!ri.image.null()) {
362  for (size_t i = 0; i <= fi_n; i++)
363  {
364  floating_image::render_input& old_ri = imgs_[i];
365  sdl_blit(old_ri.image, nullptr, video_.getSurface(), &old_ri.rect);
366  update_rect(old_ri.rect);
367  }
368  }
369 
370  if (!skip_)
371  {
372  int delay = fi.display_delay(), delay_step = 20;
373  for (int i = 0; i != (delay + delay_step - 1) / delay_step; ++i)
374  {
375  if (handle_interface()) return false;
376  if (skip_) break;
377  CVideo::delay(std::min<int>(delay_step, delay - i * delay_step));
378  }
379  }
380 
381  ++fi_n;
382  }
383 
384  return true;
385 #endif
386 }
387 
389 {
390 #ifdef SDL_GPU
391  const std::string& titletxt = p_.title();
392  if(titletxt.empty()) {
393  return;
394  }
395 
396  int titlebox_x, titlebox_y, titlebox_max_w, titlebox_max_h;
397  // We later correct these according to the storytext box location.
398  // The text box is always aligned according to the base_rect_
399  // (effective background area) at the end.
400  titlebox_x = titlebox_padding;
401  titlebox_max_w = base_rect_.w - 2*titlebox_padding;
402  titlebox_y = titlebox_padding;
403  titlebox_max_h = base_rect_.h - 2*titlebox_padding;
404 
405  font::ttext t;
406  if(!t.set_text(titletxt, true)) {
407  ERR_NG << "Text: Invalid markup in '"
408  << titletxt << "' rendered as is.\n";
409  t.set_text(titletxt, false);
410  }
411 
413  .set_font_size(titlebox_font_size)
414  .set_foreground_color(titlebox_font_color)
415  .set_maximum_width(titlebox_max_w)
416  .set_maximum_height(titlebox_max_h, true);
417  sdl::timage txttxt = t.render_as_texture();
418 
419  if(txttxt.null()) {
420  ERR_NG << "storyscreen titlebox rendering resulted in a null surface" << std::endl;
421  return;
422  }
423 
424  const int titlebox_w = txttxt.width();
425  const int titlebox_h = txttxt.height();
426 
427  switch(p_.title_text_alignment()) {
428  case part::TEXT_CENTERED:
429  titlebox_x = base_rect_.w / 2 - titlebox_w / 2 - titlebox_padding;
430  break;
431  case part::TEXT_RIGHT:
432  titlebox_x = base_rect_.w - titlebox_padding - titlebox_w;
433  break;
434  default:
435  break; // already set before
436  }
437 
438  // Translate to absolute position.
439  titlebox_x += base_rect_.x;
440  titlebox_y += base_rect_.y;
441 
442  const SDL_Rect box = sdl::create_rect(
443  titlebox_x - titleshadow_padding,
444  titlebox_y - titleshadow_padding,
445  titlebox_w + 2*titleshadow_padding,
446  titlebox_h + 2*titleshadow_padding
447  );
448 
449  sdl::fill_rect(video_, box, titleshadow_r, titleshadow_g, titleshadow_b,
450  titleshadow_a);
451 
452  video_.draw_texture(txttxt, titlebox_x, titlebox_y);
453 #else
454  const std::string& titletxt = p_.title();
455  if(titletxt.empty()) {
456  return;
457  }
458 
459  int titlebox_x, titlebox_y, titlebox_max_w, titlebox_max_h;
460  // We later correct these according to the storytext box location.
461  // The text box is always aligned according to the base_rect_
462  // (effective background area) at the end.
463  titlebox_x = titlebox_padding;
464  titlebox_max_w = base_rect_.w - 2*titlebox_padding;
465  titlebox_y = titlebox_padding;
466  titlebox_max_h = base_rect_.h - 2*titlebox_padding;
467 
468  font::ttext t;
469  if(!t.set_text(titletxt, true)) {
470  ERR_NG << "Text: Invalid markup in '"
471  << titletxt << "' rendered as is.\n";
472  t.set_text(titletxt, false);
473  }
474 
476  .set_font_size(titlebox_font_size)
477  .set_foreground_color(titlebox_font_color)
478  .set_maximum_width(titlebox_max_w)
479  .set_maximum_height(titlebox_max_h, true);
480  surface txtsurf = t.render();
481 
482  if(txtsurf.null()) {
483  ERR_NG << "storyscreen titlebox rendering resulted in a null surface" << std::endl;
484  return;
485  }
486 
487  const int titlebox_w = txtsurf->w;
488  const int titlebox_h = txtsurf->h;
489 
490  switch(p_.title_text_alignment()) {
491  case part::TEXT_CENTERED:
492  titlebox_x = base_rect_.w / 2 - titlebox_w / 2 - titlebox_padding;
493  break;
494  case part::TEXT_RIGHT:
495  titlebox_x = base_rect_.w - titlebox_padding - titlebox_w;
496  break;
497  default:
498  break; // already set before
499  }
500 
501  update_locker locker(video_);
502 
503  next_button_.hide();
504  back_button_.hide();
505  play_button_.hide();
506 
508  base_rect_.x + titlebox_x - titleshadow_padding,
509  base_rect_.y + titlebox_y - titleshadow_padding,
510  titlebox_w + 2*titleshadow_padding,
511  titlebox_h + 2*titleshadow_padding,
512  titleshadow_r, titleshadow_g, titleshadow_b,
513  titleshadow_opacity,
515  );
516 
517  video_.blit_surface(base_rect_.x + titlebox_x, base_rect_.y + titlebox_y, txtsurf);
518 
519  update_rect(
520  static_cast<size_t>(std::max(0, base_rect_.x + titlebox_x)),
521  static_cast<size_t>(std::max(0, base_rect_.y + titlebox_y)),
522  static_cast<size_t>(std::max(0, titlebox_w)),
523  static_cast<size_t>(std::max(0, titlebox_h))
524  );
525 
526  next_button_.hide(false);
527  back_button_.hide(false);
528  play_button_.hide(false);
529 
530 #endif
531 }
532 
533 #ifdef LOW_MEM
534 void part_ui::render_story_box_borders(SDL_Rect& /*update_area*/)
535 {}
536 #else
537 void part_ui::render_story_box_borders(SDL_Rect& update_area)
538 {
539 #ifdef SDL_GPU
541 
542  if(has_background_) {
543  sdl::timage border_top;
544  sdl::timage border_bottom;
545 
546  if(tbl == part::BLOCK_BOTTOM || tbl == part::BLOCK_MIDDLE) {
547  border_top = image::get_texture(storybox_top_border_path);
548  }
549 
550  if(tbl == part::BLOCK_TOP || tbl == part::BLOCK_MIDDLE) {
551  border_bottom = image::get_texture(storybox_bottom_border_path);
552  }
553 
554  //
555  // If one of those are null at this point, it means that either we
556  // don't need that border pic, or it is missing (in such case get_image()
557  // would report).
558  //
559 
560  if(border_top.null() != true) {
561  const float xscale = float(screen_area().w) / border_top.base_width();
562  border_top.set_hscale(xscale);
563  //TODO: blurring
564  video_.draw_texture(border_top, 0,
565  update_area.y - border_top.base_height());
566  }
567 
568  if(border_bottom.null() != true) {
569  const float xscale = float(screen_area().w) / border_bottom.base_width();
570  border_bottom.set_hscale(xscale);
571  //TODO: blurring
572  video_.draw_texture(border_bottom, 0,
573  update_area.y - border_top.base_height());
574  }
575 
576  }
577 #else
579 
580  if(has_background_) {
581  surface border_top = nullptr;
582  surface border_bottom = nullptr;
583 
584  if(tbl == part::BLOCK_BOTTOM || tbl == part::BLOCK_MIDDLE) {
585  border_top = image::get_image(storybox_top_border_path);
586  }
587 
588  if(tbl == part::BLOCK_TOP || tbl == part::BLOCK_MIDDLE) {
589  border_bottom = image::get_image(storybox_bottom_border_path);
590  }
591 
592  //
593  // If one of those are null at this point, it means that either we
594  // don't need that border pic, or it is missing (in such case get_image()
595  // would report).
596  //
597 
598  if(border_top.null() != true) {
599  if((border_top = scale_surface(border_top, screen_area().w, border_top->h)).null()) {
600  WARN_NG << "storyscreen got a null top border surface after rescaling" << std::endl;
601  }
602  else {
603  update_area.y -= border_top->h;
604  update_area.h += border_top->h;
605  blur_area(video_, update_area.y, border_top->h);
606  video_.blit_surface(0, update_area.y, border_top);
607  }
608  }
609 
610  if(border_bottom.null() != true) {
611  if((border_bottom = scale_surface(border_bottom, screen_area().w, border_bottom->h)).null()) {
612  WARN_NG << "storyscreen got a null bottom border surface after rescaling" << std::endl;
613  }
614  else {
615  blur_area(video_, update_area.h, border_bottom->h);
616  video_.blit_surface(0, update_area.y+update_area.h, border_bottom);
617  update_area.h += border_bottom->h;
618  }
619  }
620  }
621 #endif
622 }
623 #endif
624 
626 {
627 #ifdef SDL_GPU
628  LOG_NG << "ENTER part_ui()::render_story_box()\n";
629 
630  const std::string& storytxt = p_.text();
631  if(storytxt.empty()) {
632  video_.flip();
633  wait_for_input();
634  return;
635  }
636 
638  const int max_width = buttons_x_ - storybox_padding - text_x_;
639  const int max_height = screen_area().h - storybox_padding;
640 
641  skip_ = false;
642  last_key_ = true;
643 
644  font::ttext t;
645  if(!t.set_text(p_.text(), true)) {
646  ERR_NG << "Text: Invalid markup in '"
647  << p_.text() << "' rendered as is.\n";
648  t.set_text(p_.text(), false);
649  }
651  .set_font_size(storybox_font_size)
652  .set_foreground_color(storybox_font_color)
653  .set_maximum_width(max_width)
654  .set_maximum_height(max_height, true);
655  sdl::timage txttxt = t.render_as_texture();
656 
657  if(txttxt.null()) {
658  ERR_NG << "storyscreen text area rendering resulted in a null texture" << std::endl;
659  return;
660  }
661 
662  int fix_text_y = text_y_;
663  if(fix_text_y + 2*(storybox_padding+1) + txttxt.height() > screen_area().h && tbl != part::BLOCK_TOP) {
664  fix_text_y =
665  (screen_area().h > txttxt.height() + 1) ?
666  (std::max(0, screen_area().h - txttxt.height() - 2*(storybox_padding+1))) :
667  (0);
668  }
669  int fix_text_h;
670  switch(tbl) {
671  case part::BLOCK_TOP:
672  fix_text_h = std::max(txttxt.height() + 2*storybox_padding, screen_area().h/4);
673  break;
674  case part::BLOCK_MIDDLE:
675  fix_text_h = std::max(txttxt.height() + 2*storybox_padding, screen_area().h/3);
676  break;
677  default:
678  fix_text_h = screen_area().h - fix_text_y;
679  break;
680  }
681 
682  SDL_Rect update_area = sdl::create_rect(0
683  , fix_text_y
684  , screen_area().w
685  , fix_text_h);
686 
687  /* do */ {
688  // this should kill the tiniest flickering caused
689  // by the buttons being hidden and unhidden in this scope.
690  update_locker locker(video_);
691 
692  next_button_.hide();
693  back_button_.hide();
694  play_button_.hide();
695 
696  //TODO: blurring
697 
698  const SDL_Rect box = sdl::create_rect(0, fix_text_y, screen_area().w,
699  fix_text_h);
700 
701  sdl::fill_rect(video_, box, storyshadow_r, storyshadow_g,
702  storyshadow_b, storyshadow_a);
703 
704  render_story_box_borders(update_area); // no-op if LOW_MEM is defined
705 
706  next_button_.hide(false);
707  back_button_.hide(false);
708  play_button_.hide(false);
709  }
710 
711  // Time to do some fucking visual effect.
712  const int scan_height = 1, scan_width = txttxt.width();
713  SDL_Rect scan = sdl::create_rect(0, 0, scan_width, scan_height);
714  SDL_Rect dstrect = sdl::create_rect(text_x_, 0, scan_width, scan_height);
715  bool scan_finished = false;
716  while(true) {
717  scan_finished = scan.y >= txttxt.base_height();
718  if (!scan_finished)
719  {
720  dstrect.y = fix_text_y + scan.y + storybox_padding;
721  txttxt.set_clip(scan);
722  video_.draw_texture(txttxt, dstrect.x, dstrect.y);
723  video_.flip();
724  ++scan.y;
725  }
726  else skip_ = true;
727 
728  if (handle_interface()) break;
729 
730  if (!skip_ || scan_finished) {
731  CVideo::delay(20);
732  }
733  }
734 
735  const SDL_Rect rect = sdl::create_rect(0, 0, video_.getx(), video_.gety());
736  sdl::fill_rect(video_, rect, 0, 0, 0, SDL_ALPHA_OPAQUE);
737 #else
738 
739  LOG_NG<< "ENTER part_ui()::render_story_box()\n";
740  bool first = true;
741 
743 
744  if(p_.show_title()) {
746  }
747 
748  if(!imgs_.empty()) {
749  if(!render_floating_images()) {
750  return;
751  }
752  }
753 
754 
755  const std::string& storytxt = p_.text();
756  if(storytxt.empty()) {
757  wait_for_input();
758  return;
759  }
760 
761  skip_ = false;
762  last_key_ = true;
763  font::ttext t;
764  bool scan_finished = false;
765  SDL_Rect scan;
766  SDL_Rect dstrect;
767 
768 
769  while(true) {
770 
771  if (dirty_) {
772 
774 
775  if(p_.show_title()) {
777  }
778 
779  if(!imgs_.empty()) {
780  if(!render_floating_images()) {
781  return;
782  }
783  }
784  }
785 
787  int max_width = buttons_x_ - storybox_padding - text_x_;
788  int max_height = screen_area().h - storybox_padding;
789 
790  if(!t.set_text(p_.text(), true)) {
791  ERR_NG << "Text: Invalid markup in '"
792  << p_.text() << "' rendered as is.\n";
793  t.set_text(p_.text(), false);
794  }
796  .set_font_size(storybox_font_size)
797  .set_foreground_color(storybox_font_color)
798  .set_maximum_width(max_width)
799  .set_maximum_height(max_height, true);
800 
801  surface txtsurf = t.render();
802 
803  if(txtsurf.null()) {
804  ERR_NG << "storyscreen text area rendering resulted in a null surface" << std::endl;
805  return;
806  }
807 
808  int fix_text_y = text_y_;
809  if(fix_text_y + 2*(storybox_padding+1) + txtsurf->h > screen_area().h && tbl != part::BLOCK_TOP) {
810  fix_text_y =
811  (screen_area().h > txtsurf->h + 1) ?
812  (std::max(0, screen_area().h - txtsurf->h - 2*(storybox_padding+1))) :
813  (0);
814  }
815  int fix_text_h;
816  switch(tbl) {
817  case part::BLOCK_TOP:
818  fix_text_h = std::max(txtsurf->h + 2*storybox_padding, screen_area().h/4);
819  break;
820  case part::BLOCK_MIDDLE:
821  fix_text_h = std::max(txtsurf->h + 2*storybox_padding, screen_area().h/3);
822  break;
823  default:
824  fix_text_h = screen_area().h - fix_text_y;
825  break;
826  }
827 
828  SDL_Rect update_area = sdl::create_rect(0
829  , fix_text_y
830  , screen_area().w
831  , fix_text_h);
832 
833  /* do */{
834  // this should kill the tiniest flickering caused
835  // by the buttons being hidden and unhidden in this scope.
836  update_locker locker(video_);
837 
838  next_button_.hide();
839  back_button_.hide();
840  play_button_.hide();
841 
842 #ifndef LOW_MEM
843  if (dirty_ || first) {
844  blur_area(video_, fix_text_y, fix_text_h);
845  }
846 #endif
847  if (dirty_ || first) {
849  0, fix_text_y, screen_area().w, fix_text_h,
850  storyshadow_r, storyshadow_g, storyshadow_b,
851  storyshadow_opacity,
853  );
854  }
855 
856  render_story_box_borders(update_area); // no-op if LOW_MEM is defined
857 
858  next_button_.hide(false);
859  back_button_.hide(false);
860  play_button_.hide(false);
861  }
862 
863  if (dirty_ || first) {
864  if(!imgs_.empty() && update_area.h > 0) {
865  update_rect(update_area);
866  }
867  }
868 
869  // Time to do some visual effecta.
870  if (dirty_ || first) {
871  const int scan_height = 1, scan_width = txtsurf->w;
872  scan = sdl::create_rect(0, 0, scan_width, scan_height);
873  dstrect = sdl::create_rect(text_x_, 0, scan_width, scan_height);
874  }
875 
876  /* This needs to happen before poll for events */
877  dirty_ = false;
878  first = false;
879 
880  scan_finished = scan.y >= txtsurf->h;
881  if (!scan_finished)
882  {
883  //dstrect.x = text_x_;
884  dstrect.y = fix_text_y + scan.y + storybox_padding;
885  // NOTE: ::blit_surface() screws up with antialiasing and hinting when
886  // on backgroundless (e.g. black) screens; ttext::draw()
887  // uses it nonetheless, no idea why...
888  // Here we'll use CVideo::blit_surface() instead.
889  video_.blit_surface(dstrect.x, dstrect.y, txtsurf, &scan);
890  update_rect(dstrect);
891  ++scan.y;
892  }
893  else {
894  skip_ = true;
895  }
896 
897  if (handle_interface()) break;
898 
899  if (!skip_ || scan_finished) {
900  CVideo::delay(20);
901  }
902 
903  }
904 
906  0, 0, video_.getx(), video_.gety(), 0, 0, 0,
907  1.0, video_.getSurface()
908  );
909 #endif
910 }
911 
913 {
914  LOG_NG << "ENTER part_ui()::wait_for_input()\n";
915 
916  last_key_ = true;
917  skip_ = true;
918  while (!handle_interface()) {
919  CVideo::delay(20);
920  }
921 }
922 
924 {
925  bool result = false;
926 
927  bool next_keydown = keys_[SDLK_SPACE] || keys_[SDLK_RETURN] ||
928  keys_[SDLK_KP_ENTER] || keys_[SDLK_RIGHT];
929  bool back_keydown = keys_[SDLK_BACKSPACE] || keys_[SDLK_LEFT];
930  bool play_keydown = keys_[SDLK_ESCAPE];
931 
932  if ((next_keydown && !last_key_) || next_button_.pressed())
933  {
935  if (skip_) {
936  ret_ = NEXT;
937  result = true;
938  } else {
939  skip_ = true;
940  }
941  }
942 
943  if ((play_keydown && !last_key_) || play_button_.pressed()) {
945  ret_ = QUIT;
946  result = true;
947  }
948 
949  if ((back_keydown && !last_key_) || back_button_.pressed()) {
951  ret_ = BACK;
952  result = true;
953  }
954 
955  last_key_ = next_keydown || back_keydown || play_keydown;
956 
957  events::pump();
960  video_.flip();
961 
962  return result;
963 }
964 
966 {
967  this->prepare_background();
968  this->prepare_geometry();
969  this->prepare_floating_images();
970 
971  if(p_.music().empty() != true) {
973  }
974 
975  if(p_.sound().empty() != true) {
977  }
978 
979  join();
980  try {
982  }
983  catch(utf8::invalid_utf8_exception const&) {
984  ERR_NG << "invalid UTF-8 sequence in story text, skipping part..." << std::endl;
985  }
986 
987  leave();
988 
989  return ret_;
990 }
991 
992 void part_ui::handle_event(const SDL_Event &event)
993 {
994  if (event.type == DRAW_ALL_EVENT) {
995  dirty_ = true;
996  draw();
997  }
998 
999 }
1000 
1001 void part_ui::handle_window_event(const SDL_Event &event)
1002 {
1003 
1004  if (event.type == SDL_WINDOWEVENT &&
1005  (event.window.event == SDL_WINDOWEVENT_MAXIMIZED ||
1006  event.window.event == SDL_WINDOWEVENT_RESIZED ||
1007  event.window.event == SDL_WINDOWEVENT_EXPOSED ||
1008  event.window.event == SDL_WINDOWEVENT_RESTORED)) {
1009 
1010 
1011 
1012  this->prepare_background();
1013  this->prepare_geometry();
1014  this->prepare_floating_images();
1015  dirty_ = true;
1016  }
1017 }
1018 
1019 
1020 } // end namespace storyscreen
surface get_image(const image::locator &i_locator, TYPE type)
function to get the surface corresponding to an image.
Definition: image.cpp:878
SDL_Rect base_rect_
Definition: render.hpp:90
surface create_neutral_surface(int w, int h)
Definition: utils.cpp:150
Center of the screen.
Definition: part.hpp:254
bool null() const
Definition: utils.hpp:104
bool handle_interface()
Returns true if the user did an action.
Definition: render.cpp:923
static lg::log_domain log_engine("engine")
void render_story_box()
Definition: render.cpp:625
void fill_rect(surface &dst, SDL_Rect *dst_rect, const Uint32 color)
Fill a rectangle on a given surface.
Definition: rect.hpp:143
Storyscreen parts and floating images representation.
virtual void handle_window_event(const SDL_Event &event)
Definition: render.cpp:1001
The user pressed the go-next button.
Definition: render.hpp:48
ttext & set_font_style(const unsigned font_style)
Definition: text.cpp:418
SDL_Rect rect
Corrected rectangle for rendering surf.
Definition: part.hpp:45
BLOCK_LOCATION story_text_location() const
Retrieves the area of the screen on which the story text is displayed.
Definition: part.hpp:318
Definition: video.hpp:58
void flip()
Definition: video.cpp:496
Center on the topmost edge of the screen.
Definition: part.hpp:264
ttext & set_font_size(const unsigned font_size)
Definition: text.cpp:406
const std::string & title() const
Retrieves the story screen title.
Definition: part.hpp:298
const std::vector< background_layer > & get_background_layers() const
Retrieve background layers for this story screen.
Definition: part.hpp:333
virtual void draw()
Definition: events.hpp:68
#define WARN_NG
Definition: render.cpp:38
GLint GLint GLint GLint GLint GLint y
Definition: glew.h:1220
surface scale_surface(const surface &surf, int w, int h)
Definition: utils.cpp:443
ttext & set_maximum_width(int width)
Definition: text.cpp:446
void blit_surface(int x, int y, surface surf, SDL_Rect *srcrect=nullptr, SDL_Rect *clip_rect=nullptr)
Definition: video.cpp:290
surface get_surface_portion(const surface &src, SDL_Rect &area)
Get a portion of the screen.
Definition: utils.cpp:2335
Bottom of the screen.
Definition: part.hpp:255
void prepare_background()
Constructor implementation details.
Definition: render.cpp:118
GLdouble GLdouble t
Definition: glew.h:1366
-file util.hpp
void render_title_box()
Definition: render.cpp:388
surface & getSurface()
Definition: dummy_video.cpp:22
void play_sound(const std::string &files, channel_group group, unsigned int repeats)
Definition: sound.cpp:818
void release()
Definition: button.cpp:684
void render_background()
Definition: render.cpp:309
SDL_Rect screen_area()
Definition: video.cpp:135
void blit_surface(const surface &surf, const SDL_Rect *srcrect, surface &dst, const SDL_Rect *dstrect)
Replacement for sdl_blit.
Definition: utils.cpp:2185
GLuint64EXT * result
Definition: glew.h:10727
The user pressed the go-back button.
Definition: render.hpp:49
void draw_solid_tinted_rectangle(int x, int y, int w, int h, int r, int g, int b, double alpha, surface target)
Fills a specified rectangle area of a surface with a given color and opacity.
Definition: rect.cpp:117
GLenum GLenum GLuint GLint GLint layer
Definition: glew.h:3455
int gety() const
Definition: video.cpp:481
const std::string & music() const
Retrieves the background music.
Definition: part.hpp:308
ttext & set_maximum_height(int height, bool multiline)
Definition: text.cpp:491
Represents and contains information about image labels used in story screen parts.
Definition: part.hpp:40
virtual void hide(bool value=true)
Definition: widget.cpp:162
Storyscreen parts rendering interface.
const std::string & text() const
Retrieves the story text itself.
Definition: part.hpp:288
GLubyte GLubyte GLubyte GLubyte w
Definition: glew.h:1858
RESULT
Storyscreen result.
Definition: render.hpp:47
virtual void join()
Definition: events.cpp:173
surface background_
Definition: render.hpp:96
The user selected quit.
Definition: render.hpp:50
bool pressed()
Definition: button.cpp:770
#define ERR_NG
Definition: render.cpp:37
GLfloat GLfloat p
Definition: glew.h:12766
gui::button & play_button_
Definition: render.hpp:78
surface blur_surface(const surface &surf, int depth, bool optimize)
Cross-fades a surface.
Definition: utils.cpp:1524
void raise_draw_event()
Definition: events.cpp:565
void pump()
Definition: events.cpp:336
static const unsigned STYLE_NORMAL
The flags have the same values as the ones in SDL_TTF so it's easy to mix them for now...
Definition: text.hpp:144
Represents and contains information about a single storyscreen part.
Definition: part.hpp:244
#define DRAW_ALL_EVENT
Definition: events.hpp:29
bool render_floating_images()
Renders all floating images in sequence.
Definition: render.cpp:320
void raise_process_event()
Definition: events.cpp:539
#define ASSERT_LOG(a, b)
Definition: asserts.hpp:49
const std::string & sound() const
Retrieves a one-time-only sound effect.
Definition: part.hpp:313
Top of the screen.
Definition: part.hpp:253
surface render() const
Returns the rendered text.
Definition: text.cpp:166
Thrown by operations encountering invalid UTF-8 data.
gui::button & next_button_
Definition: render.hpp:76
int getx() const
Definition: video.cpp:472
#define LOG_NG
Definition: render.cpp:39
RESULT show()
Render and display the storyscreen, process and return user input.
Definition: render.cpp:965
GLfloat GLfloat GLfloat GLfloat h
Definition: glew.h:5910
virtual void handle_event(const SDL_Event &)
Definition: render.cpp:992
size_t i
Definition: function.cpp:1057
gui::button & back_button_
Definition: render.hpp:77
int display_delay() const
Delay before displaying, in milliseconds.
Definition: part.hpp:101
void render_story_box_borders(SDL_Rect &)
Definition: render.cpp:537
Handling of system events.
Definition: manager.hpp:42
SDL_Rect create_rect(const int x, const int y, const int w, const int h)
Creates an empty SDL_Rect.
Definition: rect.cpp:28
bool show_title() const
Whether the story screen title should be displayed or not.
Definition: part.hpp:283
Top-right corner.
Definition: part.hpp:265
Contains the SDL_Rect helper code.
cl_event event
Definition: glew.h:3070
surface tile_surface(const surface &surf, int w, int h, bool optimize)
Tile a surface.
Definition: utils.cpp:674
void play_music_repeatedly(const std::string &id)
Definition: sound.cpp:536
surface make_neutral_surface(const surface &surf)
Definition: utils.cpp:135
TEXT_ALIGNMENT title_text_alignment() const
Retrieves the alignment of the title text against the screen.
Definition: part.hpp:323
Standard logging facilities (interface).
void assign(const surface &o)
Definition: utils.hpp:83
GLint * first
Definition: glew.h:1496
static void delay(unsigned int milliseconds)
Definition: video.cpp:490
bool set_text(const std::string &text, const bool markedup)
Sets the text to render.
Definition: text.cpp:360
virtual void set_location(SDL_Rect const &rect)
Definition: widget.cpp:85
double y_scale_factor_
Definition: render.hpp:86
int width() const
Definition: widget.cpp:134
virtual void leave()
Definition: events.cpp:212
void sdl_blit(const surface &src, SDL_Rect *src_rect, surface &dst, SDL_Rect *dst_rect)
Definition: utils.hpp:112
surface image
Surface, scaled if required.
Definition: part.hpp:49
std::vector< floating_image::render_input > imgs_
Definition: render.hpp:98
part_ui(part &p, CVideo &video, gui::button &next_button, gui::button &back_button, gui::button &play_button)
Constructor.
Definition: render.cpp:88
double x_scale_factor_
Definition: render.hpp:85
void update_rect(const SDL_Rect &)
Definition: dummy_video.cpp:27
const std::vector< floating_image > & get_floating_images() const
Retrieve any associated floating images for this story screen.
Definition: part.hpp:328
GLsizei const GLcharARB ** string
Definition: glew.h:4503
Text class.
Definition: text.hpp:66
void prepare_floating_images()
Constructor implementation details.
Definition: render.cpp:301
BLOCK_LOCATION
Currently used to indicate where the text block should be placed.
Definition: part.hpp:252
void prepare_geometry()
Constructor implementation details.
Definition: render.cpp:269
ttext & set_foreground_color(const Uint32 color)
Definition: text.cpp:429