LiVES  3.2.0
pangotext.c
Go to the documentation of this file.
1 // pangotext.c
2 // text handling code
3 // (c) A. Penkov 2010
4 // (c) G. Finch 2010 - 2020
5 // pieces of code taken and modified from scribbler.c
6 // released under the GNU GPL 3 or later
7 // see file COPYING or www.gnu.org for details
8 
9 #include "main.h"
10 #include "pangotext.h"
11 #include "effects-weed.h"
12 
13 #ifdef GUI_GTK
14 #include <pango/pangocairo.h>
15 static int font_cmp(const void *p1, const void *p2);
16 #endif
17 
18 
19 static void fill_bckg(lives_painter_t *cr, double x, double y, double dx, double dy) {
21  lives_painter_rectangle(cr, x, y, dx, dy);
24 }
25 
26 
27 static void getxypos(LingoLayout *layout, int *px, int *py, int width, int height, boolean cent, double *pw, double *ph) {
28  // calc coords of text, text will fit so it goes to bottom. Set cent to center text.
29 
30  // width and height are frame width / height in pixels
31  // py, px : return locations for x,y
32  // pw, ph : return locations for pango width, height
33 
34  int w_, h_;
35  double d;
36 
37  // get size of layout
38  lingo_layout_get_size(layout, &w_, &h_);
39 
40  // scale width, height to pixels
41  d = ((double)h_) / (double)LINGO_SCALE;
42  if (ph) *ph = d;
43 
44  // ypos (adjusted so text goes to bottom)
45  if (py) *py = height - (int) * ph;
46 
47  d = ((double)w_) / (double)LINGO_SCALE;
48  if (pw) *pw = d;
49 
50  if (px) *px = cent ? (width - (int)d) >> 1 : 0.;
51 }
52 
53 
54 static char *rewrap_text(char *text) {
55  // find the longest line and move the last word to the following line
56  // if there is no following line we add one
57  // if there are no spaces in the line we truncate
58  size_t maxlen = 0;
59 
60  char **lines;
61  char *jtext, *tmp;
62 #ifdef REFLOW_TEXT
63  char *first, *second;
64  register int j;
65 #endif
66  size_t ll;
67  boolean needs_nl = FALSE;
68  int numlines, maxline = -1;
69  register int i;
70 
71  if (!text || !(*text)) return NULL;
72 
73  jtext = lives_strdup("");
74  numlines = get_token_count(text, '\n');
75  lines = lives_strsplit(text, "\n", numlines);
76 
77  for (i = 0; i < numlines; i++) {
78  if ((ll = lives_strlen(lines[i])) > maxlen) {
79  maxlen = ll;
80  maxline = i;
81  }
82  }
83  if (maxlen < 2) {
84  lives_strfreev(lines);
85  return jtext;
86  }
87  for (i = 0; i < numlines; i++) {
88  if (i == maxline) {
89 #ifdef REFLOW_TEXT
90  for (j = maxlen - 1; j > 0; j--) {
91  // skip the final character - if it's a space we aren't going to move it yet
92  // if it's not a space we aren't going to move it yet
93  if (lines[i][j - 1] == ' ') {
94  // up to and including space
95  first = lives_strndup(lines[i], j);
96  // after space
97  second = lines[i] + j;
98  tmp = lives_strdup_printf("%s%s%s\n%s", jtext, needs_nl ? "\n" : "", first, second);
99  lives_free(first);
100  lives_free(jtext);
101  jtext = tmp;
102  needs_nl = FALSE;
103  break;
104  }
105  // no space in line, truncate last char
106  lines[i][maxlen - 1] = 0;
107  if (maxlen > 3)
108  lives_snprintf(&lines[i][maxlen - 4], 4, "%s", "...");
109  needs_nl = TRUE;
110  }
111 #endif
112  //g_print("maxlen %ld for %s\n", maxlen, lines[i]);
113  lines[i][maxlen - 1] = 0;
114  if (maxlen > 5)
115  lives_snprintf(&lines[i][maxlen - 4], 4, "%s", "...");
116  //g_print("Trying with %s\n", lines[i]);
117  }
118  tmp = lives_strdup_printf("%s%s%s", jtext, needs_nl ? "\n" : "", lines[i]);
119  lives_free(jtext);
120  jtext = tmp;
121  needs_nl = TRUE;
122  }
123  lives_strfreev(lines);
124  return jtext;
125 }
126 
127 
128 static char *remove_first_line(char *text) {
129  int i;
130  size_t tlen = lives_strlen(text);
131  for (i = 0; i < tlen; i++) {
132  if (text[i] == '\n') return lives_strdup(text + i + 1);
133  }
134  return NULL;
135 }
136 
137 
138 static char *deparagraph(char *text) {
139  char *xtext = text;
140  size_t tlen;
141  int i, j = 0, nlcnt = 0;
142 
143  for (i = 0; text[i]; i++) {
144  if (text[i] == '\n') {
145  if (!text[i + 1] || text[i + 1] == '\n') nlcnt++;
146  }
147  }
148  tlen = i;
149  if (nlcnt) {
150  tlen += nlcnt;
151  xtext = lives_malloc(tlen + 1);
152  for (i = 0; text[i]; i++) {
153  xtext[j++] = text[i];
154  if (text[i] == '\n') {
155  if (!text[i + 1] || text[i + 1] == '\n') xtext[j++] = ' ';
156  }
157  }
158  xtext[j] = 0;
159  }
160  if (text != xtext) lives_free(text);
161  return xtext;
162 }
163 
164 
165 void layout_to_lives_painter(LingoLayout *layout, lives_painter_t *cr, lives_text_mode_t mode, lives_colRGBA64_t *fg,
166  lives_colRGBA64_t *bg, int dwidth, int dheight, double x_bg, double y_bg, double x_text, double y_text) {
167  double b_alpha = 1.;
168  double f_alpha = 1.;
169 
170  if (bg) b_alpha = (double)bg->alpha / 65535.;
171  if (fg) f_alpha = (double)fg->alpha / 65535.;
172 
173  if (!cr) return;
174 
175  switch (mode) {
177  lingo_layout_set_text(layout, "", -1);
179  lives_painter_set_source_rgba(cr, (double)bg->red / 65535., (double)bg->green / 65535., (double)bg->blue / 65535., b_alpha);
180  fill_bckg(cr, x_bg, y_bg, dwidth, dheight);
181  break;
182  default:
183  break;
184  }
185 
187  lives_painter_move_to(cr, x_text, y_text);
188  lives_painter_set_source_rgba(cr, (double)fg->red / 65535., (double)fg->green / 65535., (double)fg->blue / 65535., f_alpha);
189 }
190 
191 
192 //#define DEBUG_MSGS
193 LingoLayout *layout_nth_message_at_bottom(int n, int width, int height, LiVESWidget *widget, int *linecount) {
194  // create a layout, using text properties for widget
195  //
196  // nth message in mainw->messages should be at the bottom
197  // or if there are insufficient messages then we render from message 0
198 
199  // also we want to justify text, splitting on words so that it fits width
200 
201 #ifdef GUI_GTK
202  LingoLayout *layout;
203  LingoContext *ctx;
204  weed_plant_t *msg;
205 
206  char *readytext, *testtext = NULL, *newtext = NULL, *tmp, *xx;
207  size_t ll;
208  weed_error_t error;
209 
210  int w = 0, h = 0, pw;
211  int totlines = 0;
212  int whint = 0;
213  int slen;
214 
215  boolean heightdone = FALSE;
216  boolean needs_newline = FALSE;
217 
218  if (width < 32 || height < MIN_MSGBAR_HEIGHT) return NULL;
219 
220  ctx = lives_widget_create_lingo_context(widget);
221  if (!ctx || !LINGO_IS_CONTEXT(ctx)) return NULL;
222 
223  layout = lingo_layout_new(ctx);
224  if (!layout || !LINGO_IS_LAYOUT(layout)) {
226  return NULL;
227  }
228 
229  readytext = lives_strdup("");
230 
231  msg = get_nth_info_message(n);
232  if (!msg) return NULL;
233  newtext = weed_get_string_value(msg, WEED_LEAF_LIVES_MESSAGE_STRING, &error);
234  if (error != WEED_SUCCESS) return NULL;
235  if (!newtext) return NULL;
236  slen = (int)lives_strlen(newtext);
237  if (slen > 0 && newtext[slen - 1] == '\n') {
238  newtext[--slen] = 0;
239  }
240  totlines = get_token_count(newtext, '\n') + 1;
241 #ifdef DEBUG_MSGS
242  g_print("Got msg:%s\ntotal is now %d lines\n", newtext, totlines);
243 #endif
244  if (msg == mainw->msg_list) msg = NULL;
245  else {
246  msg = weed_get_plantptr_value(msg, WEED_LEAF_PREVIOUS, &error);
247  if (error != WEED_SUCCESS) return NULL;
248  }
249 
250 #ifdef DEBUG_MSGS
251  g_print("Want msg number %d at bottom\n%s", n, weed_get_string_value(msg, WEED_LEAF_LIVES_MESSAGE_STRING, &error));
252 #endif
253 
254  while (1) {
255  if (!newtext) {
256  if (!msg) break;
257  newtext = weed_get_string_value(msg, WEED_LEAF_LIVES_MESSAGE_STRING, &error);
258  if (error != WEED_SUCCESS) break;
259  if (!newtext) break;
260  totlines += get_token_count(newtext, '\n');
261 #ifdef DEBUG_MSGS
262  g_print("Got msg:%s\ntotal is now %d lines\n", newtext, totlines);
263 #endif
264  if (msg == mainw->msg_list) msg = NULL;
265  else {
266  msg = weed_get_plantptr_value(msg, WEED_LEAF_PREVIOUS, &error);
267  if (error != WEED_SUCCESS) break;
268  }
269  }
270 
271  if (testtext) lives_free(testtext);
272  testtext = lives_strdup_printf("%s%s%s", newtext, needs_newline ? "\n" : "", readytext);
273  needs_newline = TRUE;
274  lingo_layout_set_text(layout, "", -1);
276  layout = lingo_layout_new(ctx);
277  testtext = deparagraph(testtext);
278  lingo_layout_set_text(layout, testtext, -1);
279  lingo_layout_get_size(layout, &w, &h);
280 
281  h /= LINGO_SCALE;
282  w /= LINGO_SCALE;
283 
284 #ifdef DEBUG_MSGS
285  g_print("Sizes %d %d window, %d %d layout ()\n", width, height, w, h);
286 #endif
287 
288  if (h > height) {
289 #ifdef DEBUG_MSGS
290  g_print("Too high !\n");
291 #endif
292 
293  //#ifdef MUST_FIT
294  while (h > height) {
295  // text was too high, start removing lines from the top until it fits
296  tmp = remove_first_line(newtext);
297  lives_free(newtext);
298  newtext = tmp;
299  totlines--;
300  if (!newtext) break; // no more to remove, we are done !
301  //#ifdef DEBUG_MSGS
302  g_print("Retry with (%d) |%s|\n", totlines, newtext);
303  //#endif
304  lives_free(testtext);
305  testtext = lives_strdup_printf("%s%s", newtext, readytext);
306 #ifdef DEBUG_MSGS
307  g_print("Testing with:%s:\n", testtext);
308 #endif
309  lingo_layout_set_text(layout, "", -1);
311  layout = lingo_layout_new(ctx);
312  lingo_layout_set_width(layout, width * LINGO_SCALE);
313  testtext = deparagraph(testtext);
314  lingo_layout_set_text(layout, testtext, -1);
315  lingo_layout_get_size(layout, NULL, &h);
316  h /= LINGO_SCALE;
317  }
318  //#endif
319  heightdone = TRUE;
320  }
321 
322  // height was ok, now let's check the width
323  if (0 && w > width) {
324  int jumpval = 1, dirn = -1, tjump = 0;
325  //double nscale = 2.;
326  // text was too wide
327 #ifdef DEBUG_MSGS
328  g_print("Too wide !!!\n");
329 #endif
330  //while (1) {
331  while (0) {
332  totlines -= get_token_count(newtext, '\n');
333  slen = (int)lives_strlen(newtext);
334  // for now we just truncate and elipsise lines
335  tjump = dirn * jumpval;
336  /* if (tjump >= slen && dirn == -1) { */
337  /* jumpval = slen / 2; */
338  /* tjump = dirn * jumpval; */
339  /* } */
340  /* g_print("pt b %d %d %d\n", tjump, dirn, jumpval); */
341 
343  if (whint == 0 || whint + 4 > slen) {
344  xx = lives_strndup(newtext, slen + tjump);
345  } else {
346  xx = lives_strndup(newtext, whint + 4 + tjump);
347  }
348  tmp = rewrap_text(xx);
349  lives_free(xx);
350 #ifdef DEBUG_MSGS
351  g_print("Retry with (%d) |%s|\n", totlines, xx);
352 #endif
353  if (!tmp) break;
354  // check width again, just looking at new part
355  lingo_layout_set_text(layout, "", -1);
357  layout = lingo_layout_new(ctx);
359  tmp = deparagraph(tmp);
360  lingo_layout_set_text(layout, tmp, -1);
361  lingo_layout_get_size(layout, &pw, NULL);
362  w = pw / LINGO_SCALE;
363  if (w >= width) {
364  //dirn = -1;
365  jumpval++;
366  if (whint <= 0 || (ll = (int)lives_strlen(tmp)) < whint) whint = ll;
367  } else {
368  break;
369  }
370  /* if (jumpval == 1) break; */
371  /* dirn = 1; */
372  /* nscale = 0.5; */
373  /* } */
374  /* if (jumpval > 1) jumpval = (int)(jumpval * nscale + .9); */
375  lives_free(newtext);
376  newtext = tmp;
377  }
378 #ifdef DEBUG_MSGS
379  g_print("Width OK now\n");
380 #endif
381  lives_free(newtext);
382  newtext = tmp;
383  totlines += get_token_count(newtext, '\n');
384  // width is OK again, need to recheck height
385  heightdone = FALSE;
386  }
387 
388  lives_free(newtext);
389  newtext = NULL;
390  lives_free(readytext);
391  readytext = testtext;
392  testtext = NULL;
393 #ifdef DEBUG_MSGS
394  g_print("|%s| passed size tests\n", readytext);
395 #endif
396  if (heightdone) break;
398  }
399 
400  // result is now in readytext
401  //lingo_layout_set_text(layout, readytext, -1);
402 
403  if (linecount) *linecount = totlines;
404 
405 #ifdef DEBUG_MSGS
406  g_print("|%s| FINAL !!\n", readytext);
407 #endif
408  lives_free(readytext);
410  return layout;
411 #endif
412  return NULL;
413 }
414 
415 
416 char **get_font_list(void) {
417  register int i;
418  char **font_list = NULL;
419 #ifdef GUI_GTK
420  PangoContext *ctx;
421  ctx = gdk_pango_context_get();
422  if (ctx) {
423  PangoFontMap *pfm;
424  pfm = pango_context_get_font_map(ctx);
425  if (pfm) {
426  int num = 0;
427  PangoFontFamily **pff = NULL;
428  pango_font_map_list_families(pfm, &pff, &num);
429  if (num > 0) {
430  font_list = (char **)lives_malloc((num + 1) * sizeof(char *));
431  if (font_list) {
432  for (i = 0; i < num; ++i)
433  font_list[i] = lives_strdup(pango_font_family_get_name(pff[i]));
434  font_list[num] = NULL;
435  qsort(font_list, num, sizeof(char *), font_cmp);
436  }
437  }
438  lives_free(pff);
439  }
440  }
441 #endif
442 
443 #ifdef GUI_QT
444  QFontDatabase qfd;
445  QStringList qsl = qfd.families();
446  font_list = (char **)lives_malloc((qsl.size() + 1) * sizeof(char *));
447  for (i = 0; i < qsl.size(); i++) {
448  font_list[i] = lives_strdup(qsl.at(i).toUtf8().constData());
449  }
450 #endif
451 
452  return font_list;
453 }
454 
455 
456 static int font_cmp(const void *p1, const void *p2) {
457  const char *s1 = (const char *)(*(char **)p1);
458  const char *s2 = (const char *)(*(char **)p2);
459  char *u1 = lives_utf8_casefold(s1, -1);
460  char *u2 = lives_utf8_casefold(s2, -1);
461  int ret = lives_strcmp_ordered(u1, u2);
462  lives_free(u1);
463  lives_free(u2);
464  return ret;
465 }
466 
467 
468 LingoLayout *render_text_to_cr(LiVESWidget *widget, lives_painter_t *cr, const char *text, const char *fontname,
469  double size, lives_text_mode_t mode, lives_colRGBA64_t *fg, lives_colRGBA64_t *bg,
470  boolean center, boolean rising, double *top, int *offs_x, int dwidth, int *dheight) {
471  // fontname may be eg. "Sans"
472 
473  // size is in device units, i.e. pixels
474 
475  // ypos:
476  // if "rising" is TRUE, text will be aligned to fit to bottom
477  // if "rising" is FALSE, "top" (0.0 -> 1.0) is used
478 
479  // xpos:
480  // aligned to left (offs_x), unless "center" is TRUE
481 
482  LingoFontDescription *font = NULL;
483  LingoLayout *layout;
484 
485  int x_pos = 0, y_pos = 0;
486  double lwidth = (double)dwidth, lheight = (double)(*dheight);
487 
488  if (!cr) return NULL;
489 
490 #ifdef GUI_GTK
491  if (widget) {
492  LingoContext *ctx = gtk_widget_get_pango_context(widget);
493  layout = lingo_layout_new(ctx);
494  } else {
495  layout = pango_cairo_create_layout(cr);
496  if (!layout) return NULL;
497 
498  font = lingo_font_description_new();
499  pango_font_description_set_family(font, fontname);
500  pango_font_description_set_absolute_size(font, size * LINGO_SCALE);
501  pango_layout_set_font_description(layout, font);
502  }
503 
504  lingo_layout_set_markup(layout, text, -1);
505 #endif
506 
507  if (center) lingo_layout_set_alignment(layout, LINGO_ALIGN_CENTER);
508  else lingo_layout_set_alignment(layout, LINGO_ALIGN_LEFT);
509 
510  getxypos(layout, &x_pos, &y_pos, dwidth, *dheight, center, &lwidth, &lheight);
511  if (lwidth > dwidth) {
512  lingo_layout_set_width(layout, dwidth * LINGO_SCALE);
514  x_pos = y_pos = 0;
515  lwidth = (double)dwidth;
516  lheight = (double)(*dheight);
517  getxypos(layout, &x_pos, &y_pos, dwidth, *dheight, center, &lwidth, &lheight);
518  }
519 
520  if (!rising) y_pos = (double) * dheight * *top;
521  if (!center) {
522  x_pos += *offs_x;
523  *offs_x = x_pos;
524  }
525 
526  /* lives_painter_new_path(cr);
527  lives_painter_rectangle(cr,offs_x,0,width,height);
528  lives_painter_clip(cr);*/
529 
530  if (font) lingo_font_description_free(font);
531 
532  if (mode == LIVES_TEXT_MODE_PRECALCULATE) {
533  *dheight = lheight;
534  return layout;
535  }
536 
537  layout_to_lives_painter(layout, cr, mode, fg, bg, lwidth, lheight, x_pos, y_pos, x_pos, y_pos);
538 
539  return layout;
540 }
541 
542 
543 LIVES_GLOBAL_INLINE weed_plant_t *render_text_overlay(weed_layer_t *layer, const char *text) {
544  if (!text) return layer;
545  else {
546  lives_colRGBA64_t col_white = lives_rgba_col_new(65535, 65535, 65535, 65535);
547  lives_colRGBA64_t col_black_a = lives_rgba_col_new(0, 0, 0, SUB_OPACITY);
548  int size = weed_layer_get_width(layer) / 32;
549  const char *font = "Sans";
550  boolean fake_gamma = FALSE;
551 
552  if (prefs->apply_gamma) {
553  // leave as linear gamma maybe
554  if (weed_layer_get_gamma(layer) == WEED_GAMMA_LINEAR) {
555  // stops it getting converted
556  weed_layer_set_gamma(layer, WEED_GAMMA_SRGB);
557  fake_gamma = TRUE;
558  }
559  }
560 
561  layer = render_text_to_layer(layer, text, font, size,
562  LIVES_TEXT_MODE_FOREGROUND_AND_BACKGROUND, &col_white, &col_black_a, TRUE, FALSE, 0.1);
563  if (fake_gamma)
564  weed_set_int_value(layer, WEED_LEAF_GAMMA_TYPE, WEED_GAMMA_LINEAR);
565  }
566  return layer;
567 }
568 
569 
570 weed_plant_t *render_text_to_layer(weed_layer_t *layer, const char *text, const char *fontname,
571  double size, lives_text_mode_t mode, lives_colRGBA64_t *fg_col, lives_colRGBA64_t *bg_col,
572  boolean center, boolean rising, double top) {
573  // render text to layer and return a new layer, which may have a new "rowstrides", "width" and/or "current_palette"
574  // original layer pixel_data is freed in the process and should not be re-used
575 
576  lives_painter_t *cr = NULL;
577  LingoLayout *layout;
578  weed_layer_t *test_layer, *layer_slice;
579  uint8_t *src, *pd;
580  int row = weed_layer_get_rowstride(layer);
581  double ztop = 0.;
582  int pal = weed_layer_get_palette(layer), opal = pal;
583  int width = weed_layer_get_width(layer);
584  int height = weed_layer_get_height(layer);
585  int lheight = height;
586  int gamma = WEED_GAMMA_UNKNOWN, offsx = 0;
587 
588  if (weed_palette_is_rgb(pal)) {
589  int ppal = LIVES_PAINTER_COLOR_PALETTE(capable->byte_order), oppal = ppal;
590  int ipsize = pixel_size(pal);
591  int opsize = pixel_size(ppal);
592  // test first to get the layout coords
593  gamma = weed_layer_get_gamma(layer);
594 
595  lheight = height;
596  weed_layer_set_height(layer, 4);
597 
598  test_layer = weed_layer_copy(NULL, layer);
599 
600  if (ipsize == opsize) {
601  weed_layer_set_palette(test_layer, ppal);
602  } else {
603  if (consider_swapping(&pal, &ppal)) {
604  if (ppal == oppal) {
605  weed_layer_set_palette(test_layer, pal);
606  } else ppal = oppal;
607  pal = opal;
608  }
609  }
610 
611  weed_layer_set_height(layer, height);
612  cr = layer_to_lives_painter(test_layer);
613  layout = render_text_to_cr(NULL, cr, text, fontname, size, LIVES_TEXT_MODE_PRECALCULATE,
614  fg_col, bg_col, center, rising, &top, &offsx, width, &lheight);
615  if (LIVES_IS_WIDGET_OBJECT(layout)) lives_widget_object_unref(layout);
616 
617  weed_layer_free(test_layer);
618 
620  if (top * height + lheight < height) {
621  uint8_t *xsrc;
622  boolean rbswapped = FALSE;
623 
624  // adjust pixel_data and height, then copy-by-ref to layer_slice
626  xsrc = src + (int)(top * height) * row;
628  weed_layer_set_height(layer, lheight);
629 
630  layer_slice = weed_layer_new(WEED_LAYER_TYPE_VIDEO);
631  weed_layer_copy(layer_slice, layer);
632  weed_leaf_set_flagbits(layer_slice, WEED_LEAF_PIXEL_DATA, LIVES_FLAG_MAINTAIN_VALUE);
633 
634  // restore original values
635  weed_layer_set_height(layer, height);
637 
638  if (consider_swapping(&pal, &ppal)) {
639  if (ppal == oppal) {
640  lives_colRGBA64_t col;
641  rbswapped = TRUE;
642  weed_layer_set_palette(layer_slice, pal);
643  col.red = fg_col->red;
644  fg_col->red = fg_col->blue;
645  fg_col->blue = col.red;
646  col.red = bg_col->red;
647  bg_col->red = bg_col->blue;
648  bg_col->blue = col.red;
649  }
650  }
651 
652  cr = layer_to_lives_painter(layer_slice);
653  layout = render_text_to_cr(NULL, cr, text, fontname, size, mode, fg_col, bg_col, center, FALSE, &ztop, &offsx, width, &height);
654  if (layout && LINGO_IS_LAYOUT(layout)) {
655  lingo_painter_show_layout(cr, layout);
657  }
658  // frees pd
659  lives_painter_to_layer(cr, layer_slice);
662 
663  convert_layer_palette(layer_slice, pal, 0);
664  weed_leaf_clear_flagbits(layer_slice, WEED_LEAF_PIXEL_DATA, LIVES_FLAG_MAINTAIN_VALUE);
665 
666  pd = weed_layer_get_pixel_data_packed(layer_slice);
667  if (pd != xsrc) lives_memcpy(src + (int)(top * height) * row, pd, lheight * row);
668  else weed_layer_nullify_pixel_data(layer_slice);
669  weed_layer_free(layer_slice);
670 
671  if (rbswapped) {
672  lives_colRGBA64_t col;
673  col.red = fg_col->red;
674  fg_col->red = fg_col->blue;
675  fg_col->blue = col.red;
676  col.red = bg_col->red;
677  bg_col->red = bg_col->blue;
678  bg_col->blue = col.red;
679  }
680  }
681  }
682 
683  if (!cr) {
684  cr = layer_to_lives_painter(layer);
685  if (!cr) return layer;
686  layout = render_text_to_cr(NULL, cr, text, fontname, size, mode, fg_col, bg_col, center, rising, &top, &offsx, width, &height);
687  if (layout && LINGO_IS_LAYOUT(layout)) {
688  lingo_painter_show_layout(cr, layout);
689  if (layout) lives_widget_object_unref(layout);
690  }
691  lives_painter_to_layer(cr, layer);
692  }
693  if (gamma != WEED_GAMMA_UNKNOWN)
694  weed_layer_set_gamma(layer, gamma);
695  return layer;
696 }
697 
698 static const char *cr_str = "\x0D";
699 static const char *lf_str = "\x0A";
700 
701 //
702 // read appropriate text for subtitle file (.srt)
703 //
704 static char *srt_read_text(int fd, lives_subtitle_t *title) {
705  char *poslf = NULL;
706  char *poscr = NULL;
707  char *ret = NULL;
708  char data[32768];
709 
710  if (fd < 0 || !title) return NULL;
711 
713 
714  while (lives_read_buffered(fd, data, sizeof(data) - 1, TRUE) > 0) {
715  // remove \n \r
716  poslf = strstr(data, lf_str);
717  if (poslf) *poslf = '\0';
718  poscr = strstr(data, cr_str);
719  if (poscr) *poscr = '\0';
720  if (!(*data)) break;
721  if (!ret) ret = lives_strdup(data);
722  else {
723  char *tmp = lives_strconcat(ret, "\n", data, NULL);
724  ret = tmp;
725  lives_free(ret);
726  }
727  }
728 
729  return ret;
730 }
731 
732 
733 static char *sub_read_text(int fd, lives_subtitle_t *title) {
734  char *poslf = NULL;
735  char *poscr = NULL;
736  char *ret = NULL;
737  char *retmore = NULL;
738  char data[32768];
739  size_t curlen, retlen;
740 
741  if (fd < 0 || !title) return NULL;
742 
744 
745  while (lives_read_buffered(fd, data, sizeof(data) - 1, TRUE) > 0) {
746  // remove \n \r
747  poslf = strstr(data, lf_str);
748  if (poslf) *poslf = '\0';
749  poscr = strstr(data, cr_str);
750  if (poscr) *poscr = '\0';
751  curlen = lives_strlen(data);
752  if (!curlen) break;
753  if (!ret) {
754  ret = subst(data, "[br]", "\n");
755  if (!ret) return NULL;
756  retlen = lives_strlen(ret) + 1;
757  } else {
758  retmore = subst(data, "[br]", "\n");
759  if (!retmore) return NULL;
760  curlen = lives_strlen(retmore);
761  if (!curlen) break;
762  retlen += curlen + 1;
763  ret = (char *)lives_realloc(ret, retlen);
764  if (ret) {
765  strcat(ret, "\n");
766  strcat(ret, retmore);
767  lives_free(retmore);
768  } else {
769  lives_free(retmore);
770  return NULL;
771  }
772  }
773  }
774 
775  return ret;
776 }
777 
778 
779 static boolean srt_parse_file(lives_clip_t *sfile) {
780  lives_subtitle_t *node = NULL;
781  lives_subtitle_t *index_prev = NULL;
782  char data[32768];
783  int fd = sfile->subt->tfile;
784 
785  while (!lives_read_buffered_eof(fd)) {
786  char *poslf = NULL, *poscr = NULL;
787  double starttime, endtime;
788  int hstart, mstart, sstart, fstart;
789  int hend, mend, send, fend;
790  int i;
791 
792  //
793  // data contains subtitle number
794  //
795 
796  if (lives_read_buffered(fd, data, 32768, TRUE) < 12) {
797  // EOF
798  //lives_freep((void **)&sfile->subt->text);
799  //sfile->subt->current = NULL;
800  //sub_get_last_time(sfile->subt);
801  return FALSE;
802  }
803  //
804  // data contains time range
805  //
806  // remove \n \r
807  poslf = strstr(data, lf_str);
808  if (poslf)
809  *poslf = '\0';
810  poscr = strstr(data, cr_str);
811  if (poscr)
812  *poscr = '\0';
813 
814  // try to parse it (time range)
815  i = sscanf(data, "%d:%d:%d,%d --> %d:%d:%d,%d", \
816  &hstart, &mstart, &sstart, &fstart, \
817  &hend, &mend, &send, &fend);
818  if (i == 8) {
819  // parsing ok
820  starttime = hstart * 3600 + mstart * 60 + sstart + fstart / 1000.;
821  endtime = hend * 3600 + mend * 60 + send + fend / 1000.;
823  if (node) {
824  node->start_time = starttime;
825  node->end_time = endtime;
826  node->style = NULL;
827  node->next = NULL;
828  node->prev = (lives_subtitle_t *)index_prev;
829  node->textpos = lives_buffered_offset(fd);
830  if (index_prev)
831  index_prev->next = (lives_subtitle_t *)node;
832  else
833  sfile->subt->first = node;
834  sfile->subt->last = index_prev = (lives_subtitle_t *)node;
835  }
836  while (lives_read_buffered(fd, data, 32768, TRUE) > 0) {
837  // read the text and final empty line
838  // remove \n \r
839  poslf = strstr(data, lf_str);
840  if (poslf)
841  *poslf = '\0';
842  poscr = strstr(data, cr_str);
843  if (poscr)
844  *poscr = '\0';
845 
846  if (!(*data)) break;
847  } // end while
848  } else return FALSE;
849  }
850  return TRUE;
851 }
852 
853 
854 static boolean sub_parse_file(lives_clip_t *sfile) {
855  lives_subtitle_t *node = NULL;
856  lives_subtitle_t *index_prev = NULL;
857  char data[32768];
858  int fd = sfile->subt->tfile;
859  boolean starttext = FALSE;
860 
861  while (lives_read_buffered(fd, data, 32768, TRUE) > 0) {
862  char *poslf = NULL, *poscr = NULL;
863  int hstart, mstart, sstart, fstart;
864  int hend, mend, send, fend;
865  int i;
866  double starttime, endtime;
867 
868  if (!strncmp(data, "[SUBTITLE]", 10)) {
869  starttext = TRUE;
870  }
871 
872  if (!starttext) {
873  if (!strncmp(data, "[DELAY]", 7)) {
874  sfile->subt->offset = atoi(data + 7);
875  }
876  continue;
877  }
878 
879  //
880  // data contains time range
881  //
882  // remove \n \r
883  poslf = strstr(data, lf_str);
884  if (poslf)
885  *poslf = '\0';
886  poscr = strstr(data, cr_str);
887  if (poscr)
888  *poscr = '\0';
889 
890  // try to parse it (time range)
891  i = sscanf(data, "%d:%d:%d.%d,%d:%d:%d.%d", \
892  &hstart, &mstart, &sstart, &fstart, \
893  &hend, &mend, &send, &fend);
894  if (i == 8) {
895  // parsing ok
896  starttime = hstart * 3600 + mstart * 60 + sstart + fstart / 100.;
897  endtime = hend * 3600 + mend * 60 + send + fend / 100.;
899  if (node) {
900  node->start_time = starttime;
901  node->end_time = endtime;
902  node->style = NULL;
903  node->next = NULL;
904  node->prev = (lives_subtitle_t *)index_prev;
905  node->textpos = lives_buffered_offset(fd);
906  if (index_prev)
907  index_prev->next = (lives_subtitle_t *)node;
908  else
909  sfile->subt->first = node;
910  index_prev = (lives_subtitle_t *)node;
911  }
912  while (lives_read_buffered(fd, data, 32768, TRUE) > 0) {
913  // read the text and final empty line
914  // remove \n \r
915  poslf = strstr(data, lf_str);
916  if (poslf)
917  *poslf = '\0';
918  poscr = strstr(data, cr_str);
919  if (poscr)
920  *poscr = '\0';
921 
922  if (!(*data)) break;
923  } // end while
924  } else return FALSE;
925  }
926  return TRUE;
927 }
928 
929 
930 boolean get_subt_text(lives_clip_t *sfile, double xtime) {
931  lives_subtitle_t *curr = NULL;
932 
933  if (!sfile || !sfile->subt) return FALSE;
934 
935  curr = sfile->subt->current;
936  if (curr) {
937  // continue showing existing text
938  if (curr->start_time <= xtime && curr->end_time >= xtime) {
939  if (!sfile->subt->text) {
940  if (sfile->subt->type == SUBTITLE_TYPE_SRT) {
941  char *tmp = srt_read_text(sfile->subt->tfile, curr);
942  sfile->subt->text = lives_charset_convert(tmp, LIVES_CHARSET_UTF8, SRT_DEF_CHARSET);
943  lives_free(tmp);
944  } else if (sfile->subt->type == SUBTITLE_TYPE_SUB) sfile->subt->text = sub_read_text(sfile->subt->tfile, curr);
945  }
946  return TRUE;
947  }
948  }
949 
950  lives_freep((void **)&sfile->subt->text);
951 
952  if (xtime < sfile->subt->first->start_time || xtime > sfile->subt->last->end_time) {
953  sfile->subt->current = NULL;
954  return TRUE;
955  }
956 
957  if (!curr) curr = sfile->subt->first;
958 
959  if (xtime > curr->end_time) while (curr->end_time < xtime) curr = curr->next;
960  if (xtime < curr->start_time && xtime <= curr->prev->end_time) while (curr->start_time > xtime) curr = curr->prev;
961 
962  sfile->subt->current = curr;
963 
964  if (curr->start_time <= xtime && curr->end_time >= xtime) {
965  if (sfile->subt->type == SUBTITLE_TYPE_SRT) {
966  char *tmp = srt_read_text(sfile->subt->tfile, curr);
967  sfile->subt->text = lives_charset_convert(tmp, LIVES_CHARSET_UTF8, SRT_DEF_CHARSET);
968  lives_free(tmp);
969  } else if (sfile->subt->type == SUBTITLE_TYPE_SUB) sfile->subt->text = sub_read_text(sfile->subt->tfile, curr);
970  }
971 
972  return TRUE;
973 }
974 
975 
977  if (!sfile) return;
978  if (!sfile->subt) return;
979  if (sfile->subt->tfile >= 0) lives_close_buffered(sfile->subt->tfile);
980 
981  // remove subt->first entries
982  while (sfile->subt->first) {
983  lives_subtitle_t *to_delete = sfile->subt->first;
984 
985  sfile->subt->first = (lives_subtitle_t *)sfile->subt->first->next;
986 
987  lives_freep((void **)&to_delete->style);
988  lives_freep((void **)&to_delete);
989  }
990 
991  lives_freep((void **)&sfile->subt->text);
992  lives_freep((void **)&sfile->subt);
993 }
994 
995 
996 boolean subtitles_init(lives_clip_t *sfile, char *fname, lives_subtitle_type_t subtype) {
997  // fname is the name of the subtitle file
998  int fd;
999 
1000  if (!sfile) return FALSE;
1001  if (sfile->subt) subtitles_free(sfile);
1002  sfile->subt = NULL;
1003 
1004  if ((fd = lives_open_buffered_rdonly(fname)) < 0) return FALSE;
1005 
1007  sfile->subt->tfile = fd;
1008  sfile->subt->current = sfile->subt->first = NULL;
1009  sfile->subt->text = NULL;
1010  sfile->subt->last_time = -1.;
1011  sfile->subt->type = subtype;
1012  sfile->subt->offset = 0;
1013  if (subtype == SUBTITLE_TYPE_SRT) srt_parse_file(sfile);
1014  if (subtype == SUBTITLE_TYPE_SUB) sub_parse_file(sfile);
1015  return TRUE;
1016 }
1017 
1018 
1019 static void parse_double_time(double tim, int *ph, int *pmin, int *psec, int *pmsec, int fix) {
1020  int ntim = (int)tim;
1021  int h, m, s, ms;
1022 
1023  h = ntim / 3600;
1024  m = (ntim - h * 3600) / 60;
1025  s = (ntim - h * 3600 - m * 60);
1026  if (fix == 3) ms = (int)((tim - ntim) * 1000.0 + .5);
1027  else ms = (int)((tim - ntim) * 100.0 + .5); // hundredths
1028  if (ph)
1029  *ph = h;
1030  if (pmin)
1031  *pmin = m;
1032  if (psec)
1033  *psec = s;
1034  if (pmsec)
1035  *pmsec = ms;
1036 }
1037 
1038 
1039 boolean save_srt_subtitles(lives_clip_t *sfile, double start_time, double end_time, double offset_time, const char *filename) {
1040  lives_subtitles_t *subt = NULL;
1041  int64_t savepos = 0;
1042  int fd, num_saves;
1043  lives_subtitle_t *ptr = NULL;
1044 
1045  if (!sfile) return FALSE;
1046  subt = sfile->subt;
1047  if (!subt) return FALSE;
1048  if (subt->last_time <= -1.)
1049  get_subt_text(sfile, end_time);
1050  if (subt->last_time <= -1.)
1051  savepos = lives_buffered_offset(subt->tfile);
1052 
1053  // save the contents
1054  fd = lives_create_buffered(filename, DEF_FILE_PERMS);
1055  if (fd < 0) return FALSE;
1056  num_saves = 0;
1057  ptr = subt->first;
1058  while (ptr) {
1059  char *text = NULL;
1060  if (ptr->start_time < end_time && ptr->end_time >= start_time) {
1061  text = srt_read_text(subt->tfile, ptr);
1062  if (text) {
1063  int h, m, s, ms;
1064  double dtim;
1065 
1066  if (num_saves > 0) lives_write_buffered(fd, "\n", 1, TRUE);
1067 
1068  dtim = ptr->start_time;
1069  if (dtim < start_time) dtim = start_time;
1070  dtim += offset_time;
1071 
1072  parse_double_time(dtim, &h, &m, &s, &ms, 3);
1073  lives_buffered_write_printf(fd, TRUE, "%02d:%02d:%02d,%03d\n", h, m, s, ms);
1074 
1075  dtim = ptr->end_time;
1076  if (dtim > end_time) dtim = end_time;
1077  dtim += offset_time;
1078 
1079  parse_double_time(dtim, &h, &m, &s, &ms, 3);
1080  lives_buffered_write_printf(fd, TRUE, "%02d:%02d:%02d,%03d\n", h, m, s, ms);
1081 
1082  lives_write_buffered(fd, text, lives_strlen(text), TRUE);
1083  lives_free(text);
1084  }
1085  } else if (ptr->start_time >= end_time) break;
1086  ptr = (lives_subtitle_t *)ptr->next;
1087  }
1088 
1090 
1091  if (!num_saves) // don't keep the empty file
1092  lives_rm(filename);
1093 
1094  if (subt->last_time <= -1.)
1096 
1097  return TRUE;
1098 }
1099 
1100 
1101 boolean save_sub_subtitles(lives_clip_t *sfile, double start_time, double end_time, double offset_time, const char *filename) {
1102  lives_subtitles_t *subt = NULL;
1103  int64_t savepos = 0;
1104  int fd, num_saves;
1105  lives_subtitle_t *ptr = NULL;
1106 
1107  if (!sfile)
1108  return FALSE;
1109  subt = sfile->subt;
1110  if (!subt)
1111  return FALSE;
1112  if (subt->last_time <= -1.)
1113  get_subt_text(sfile, end_time);
1114  if (subt->last_time <= -1.)
1115  savepos = lives_buffered_offset(subt->tfile);
1116 
1117  // save the contents
1118  fd = lives_create_buffered(filename, DEF_FILE_PERMS);
1119  if (fd < 0) return FALSE;
1120  num_saves = 0;
1121  ptr = subt->first;
1122 
1123  lives_buffered_write_printf(fd, TRUE, "[INFORMATION]\n");
1124  lives_buffered_write_printf(fd, TRUE, "[TITLE] %s\n", sfile->title);
1125  lives_buffered_write_printf(fd, TRUE, "[AUTHOR] %s\n", sfile->author);
1126  lives_buffered_write_printf(fd, TRUE, "[SOURCE]\n");
1127  lives_buffered_write_printf(fd, TRUE, "[FILEPATH]\n");
1128  lives_buffered_write_printf(fd, TRUE, "[DELAY] 0\n");
1129  lives_buffered_write_printf(fd, TRUE, "[COMMENT] %s\n", sfile->comment);
1130  lives_buffered_write_printf(fd, TRUE, "[END INFORMATION]\n");
1131  lives_buffered_write_printf(fd, TRUE, "[SUBTITLE]\n");
1132 
1133  while (ptr) {
1134  char *text = NULL;
1135  char *br_text = NULL;
1136  if (ptr->start_time < end_time && ptr->end_time >= start_time) {
1137  text = sub_read_text(subt->tfile, ptr);
1138  if (text) {
1139  int h, m, s, ms;
1140  double dtim;
1141  size_t ll = lives_strlen(text) - 1;
1142  if (text[ll] == '\n') text[ll] = 0;
1143 
1144  br_text = subst(text, "\n", "[br]");
1145  if (br_text) {
1146  if (num_saves > 0) lives_write_buffered(fd, "\n", 1, TRUE);
1147 
1148  dtim = ptr->start_time;
1149  if (dtim < start_time) dtim = start_time;
1150  dtim += offset_time;
1151 
1152  parse_double_time(dtim, &h, &m, &s, &ms, 2);
1153  lives_buffered_write_printf(fd, TRUE, "%02d:%02d:%02d.%02d,", h, m, s, ms);
1154 
1155  dtim = ptr->end_time;
1156  if (dtim > end_time) dtim = end_time;
1157  dtim += offset_time;
1158 
1159  parse_double_time(dtim, &h, &m, &s, &ms, 2);
1160  lives_buffered_write_printf(fd, TRUE, "%02d:%02d:%02d.%02d\n", h, m, s, ms);
1161  lives_buffered_write_printf(fd, TRUE, "%s\n", br_text);
1162  lives_free(br_text);
1163  num_saves++;
1164  }
1165  lives_free(text);
1166  }
1167  } else if (ptr->start_time >= end_time) break;
1168  ptr = (lives_subtitle_t *)ptr->next;
1169  }
1170 
1172  if (!num_saves) // don't keep the empty file
1173  lives_rm(filename);
1174 
1175  if (subt->last_time <= -1.)
1177 
1178  return TRUE;
1179 }
1180 
1181 
1182 boolean lives_parse_font_string(const char *string, char **font, int *size, char **stretch,
1183  char **style, char **weight) {
1184  if (!string) return FALSE;
1185  else {
1186  int numtok = get_token_count(string, ' ') ;
1187  char **array = lives_strsplit(string, " ", numtok);
1188  //int xsize = -1;
1189  if (font) {
1190  *font = 0;
1191  for (int i = 0; i < numtok - 1; i++) {
1192  char *tmp;
1193  if (*font) {
1194  tmp = lives_strdup_printf("%s %s", *font, array[i]);
1195  lives_free(*font);
1196  *font = tmp;
1197  } else *font = lives_strdup(array[i]);
1198  }
1199  }
1200  if (size && numtok > 1 && atoi(array[numtok - 1])) *size = atoi(array[numtok - 1]);
1201  if (font && !atoi(array[numtok - 1])) {
1202  if (*font) {
1203  char *tmp = lives_strdup_printf("%s %s", *font, array[numtok - 1]);
1204  lives_free(*font);
1205  *font = tmp;
1206  } else *font = lives_strdup(array[numtok - 1]);
1207  }
1208  lives_strfreev(array);
1209  }
1210  return TRUE;
1211 }
lives_freep
boolean lives_freep(void **ptr)
Definition: utils.c:1411
LIVES_GLOBAL_INLINE
#define LIVES_GLOBAL_INLINE
Definition: main.h:239
lives_subtitle_t::style
lives_subtitle_style_t * style
for future use
Definition: pangotext.h:32
lives_subtitle_t::textpos
long textpos
Definition: pangotext.h:33
lives_subtitle_type_t
lives_subtitle_type_t
Definition: pangotext.h:15
layout_nth_message_at_bottom
LingoLayout * layout_nth_message_at_bottom(int n, int width, int height, LiVESWidget *widget, int *linecount)
Definition: pangotext.c:193
lives_free
#define lives_free
Definition: machinestate.h:52
LIVES_FLAG_MAINTAIN_VALUE
#define LIVES_FLAG_MAINTAIN_VALUE
soft flag, like immutable / deletable for host
Definition: effects-weed.h:109
lives_malloc
#define lives_malloc
Definition: machinestate.h:46
lives_read_buffered_eof
boolean lives_read_buffered_eof(int fd)
Definition: utils.c:1170
get_font_list
char ** get_font_list(void)
Definition: pangotext.c:416
weed_layer_new
LIVES_GLOBAL_INLINE weed_layer_t * weed_layer_new(int layer_type)
Definition: colourspace.c:9655
lives_colRGBA64_t::alpha
uint16_t alpha
Definition: main.h:326
get_subt_text
boolean get_subt_text(lives_clip_t *sfile, double xtime)
Definition: pangotext.c:930
lives_realloc
#define lives_realloc
Definition: machinestate.h:49
lives_subtitle_t::prev
xlives_subtitle_t * prev
for future use
Definition: pangotext.h:34
weed_layer_get_width
LIVES_GLOBAL_INLINE int weed_layer_get_width(weed_layer_t *layer)
Definition: colourspace.c:13941
lives_lseek_buffered_rdonly_absolute
off_t lives_lseek_buffered_rdonly_absolute(int fd, off_t offset)
Definition: utils.c:907
prefs
_prefs * prefs
Definition: preferences.h:847
pixel_size
LIVES_GLOBAL_INLINE size_t pixel_size(int pal)
Definition: colourspace.c:1391
lives_subtitles_t::first
lives_subtitle_t * first
Definition: pangotext.h:43
LIVES_CHARSET_UTF8
#define LIVES_CHARSET_UTF8
Definition: pangotext.h:76
weed_layer_get_height
LIVES_GLOBAL_INLINE int weed_layer_get_height(weed_layer_t *layer)
Definition: colourspace.c:13953
get_token_count
size_t get_token_count(const char *string, int delim)
Definition: utils.c:5430
render_text_to_cr
LingoLayout * render_text_to_cr(LiVESWidget *widget, lives_painter_t *cr, const char *text, const char *fontname, double size, lives_text_mode_t mode, lives_colRGBA64_t *fg, lives_colRGBA64_t *bg, boolean center, boolean rising, double *top, int *offs_x, int dwidth, int *dheight)
Definition: pangotext.c:468
weed_layer_nullify_pixel_data
LIVES_GLOBAL_INLINE weed_layer_t * weed_layer_nullify_pixel_data(weed_layer_t *layer)
Definition: colourspace.c:9753
_prefs::apply_gamma
boolean apply_gamma
Definition: preferences.h:451
lives_open_buffered_rdonly
int lives_open_buffered_rdonly(const char *pathname)
Definition: utils.c:636
weed_layer_set_height
LIVES_GLOBAL_INLINE weed_layer_t * weed_layer_set_height(weed_layer_t *layer, int height)
Definition: colourspace.c:9724
WEED_LAYER_TYPE_VIDEO
#define WEED_LAYER_TYPE_VIDEO
Definition: colourspace.h:221
weed_layer_set_palette
LIVES_GLOBAL_INLINE weed_layer_t * weed_layer_set_palette(weed_layer_t *layer, int palette)
functions all return the input layer for convenience; no checking for valid values is done if layer i...
Definition: colourspace.c:9777
TRUE
#define TRUE
Definition: videoplugin.h:59
lives_colRGBA64_t::green
uint16_t green
Definition: main.h:324
lives_subtitles_t::last_time
double last_time
Definition: pangotext.h:46
lives_read_buffered
ssize_t lives_read_buffered(int fd, void *buf, ssize_t count, boolean allow_less)
Definition: utils.c:924
lives_strcmp_ordered
LIVES_GLOBAL_INLINE int lives_strcmp_ordered(const char *st1, const char *st2)
Definition: machinestate.c:1531
mainwindow::msg_list
weed_plant_t * msg_list
Definition: mainwindow.h:1729
weed_palette_is_rgb
LIVES_GLOBAL_INLINE boolean weed_palette_is_rgb(int pal)
Definition: colourspace.c:1448
lives_colRGBA64_t::blue
uint16_t blue
Definition: main.h:325
capable
capability * capable
Definition: main.h:627
SUBTITLE_TYPE_SUB
@ SUBTITLE_TYPE_SUB
Definition: pangotext.h:18
lives_painter_move_to
WIDGET_HELPER_GLOBAL_INLINE boolean lives_painter_move_to(lives_painter_t *cr, double x, double y)
Definition: widget-helper.c:520
weed_layer_get_palette
LIVES_GLOBAL_INLINE int weed_layer_get_palette(weed_layer_t *layer)
Definition: colourspace.c:13977
weed_layer_set_pixel_data_packed
LIVES_GLOBAL_INLINE weed_layer_t * weed_layer_set_pixel_data_packed(weed_layer_t *layer, void *pixel_data)
Definition: colourspace.c:9746
SUB_OPACITY
#define SUB_OPACITY
Definition: pangotext.h:13
weed_layer_get_pixel_data_packed
LIVES_GLOBAL_INLINE uint8_t * weed_layer_get_pixel_data_packed(weed_layer_t *layer)
Definition: colourspace.c:13915
lives_subtitles_t::tfile
int tfile
Definition: pangotext.h:40
lives_buffered_offset
off_t lives_buffered_offset(int fd)
Definition: utils.c:1364
get_nth_info_message
weed_plant_t * get_nth_info_message(int n)
Definition: utils.c:2304
lives_close_buffered
int lives_close_buffered(int fd)
Definition: utils.c:716
lives_widget_object_ref_sink
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_object_ref_sink(livespointer object)
Definition: widget-helper.c:845
error
error("LSD_RANDFUNC(ptr, size) must be defined")
lives_clip_t::title
char title[1024]
Definition: main.h:919
weed_layer_get_gamma
int weed_layer_get_gamma(weed_layer_t *layer)
Definition: colourspace.c:12002
weed_leaf_clear_flagbits
WEED_GLOBAL_INLINE uint32_t weed_leaf_clear_flagbits(weed_plant_t *plant, const char *leaf, uint32_t flagbits)
~value ANDed with flags
Definition: weed-effects-utils.c:58
lives_painter_new_path
WIDGET_HELPER_GLOBAL_INLINE boolean lives_painter_new_path(lives_painter_t *cr)
Definition: widget-helper.c:478
lives_subtitle_t::end_time
double end_time
Definition: pangotext.h:31
MIN_MSGBAR_HEIGHT
#define MIN_MSGBAR_HEIGHT
Definition: mainwindow.h:135
subst
char * subst(const char *string, const char *from, const char *to)
Definition: utils.c:5484
lives_strdup_printf
#define lives_strdup_printf(fmt,...)
Definition: support.c:27
LIVES_TEXT_MODE_BACKGROUND_ONLY
@ LIVES_TEXT_MODE_BACKGROUND_ONLY
Definition: pangotext.h:52
convert_layer_palette
boolean convert_layer_palette(weed_layer_t *layer, int outpl, int op_clamping)
Definition: colourspace.c:11945
render_text_to_layer
weed_plant_t * render_text_to_layer(weed_layer_t *layer, const char *text, const char *fontname, double size, lives_text_mode_t mode, lives_colRGBA64_t *fg_col, lives_colRGBA64_t *bg_col, boolean center, boolean rising, double top)
Definition: pangotext.c:570
lives_create_buffered
int lives_create_buffered(const char *pathname, int mode)
Definition: utils.c:698
consider_swapping
boolean consider_swapping(int *inpal, int *outpal)
look for shortcuts in palette conversions instead of converting e.g RGB -> BGRA, we may be able to pr...
Definition: colourspace.c:13427
SRT_DEF_CHARSET
#define SRT_DEF_CHARSET
subtitles
Definition: pangotext.h:75
weed_layer_t
weed_plant_t weed_layer_t
Definition: colourspace.h:71
lives_clip_t::author
char author[1024]
Definition: main.h:919
weed_leaf_set_flagbits
WEED_GLOBAL_INLINE uint32_t weed_leaf_set_flagbits(weed_plant_t *plant, const char *leaf, uint32_t flagbits)
value ORed with flags
Definition: weed-effects-utils.c:49
lives_clip_t::comment
char comment[1024]
Definition: main.h:919
LIVES_TEXT_MODE_FOREGROUND_AND_BACKGROUND
@ LIVES_TEXT_MODE_FOREGROUND_AND_BACKGROUND
Definition: pangotext.h:51
WEED_LEAF_LIVES_MESSAGE_STRING
#define WEED_LEAF_LIVES_MESSAGE_STRING
Definition: machinestate.h:199
weed_layer_copy
weed_layer_t * weed_layer_copy(weed_layer_t *dlayer, weed_layer_t *slayer)
copy source layer slayer to dest layer dlayer
Definition: colourspace.c:13739
layer_to_lives_painter
lives_painter_t * layer_to_lives_painter(weed_layer_t *layer)
convert a weed layer to lives_painter (a.k.a cairo)
Definition: colourspace.c:13473
save_srt_subtitles
boolean save_srt_subtitles(lives_clip_t *sfile, double start_time, double end_time, double offset_time, const char *filename)
Definition: pangotext.c:1039
lives_memcpy
#define lives_memcpy
Definition: machinestate.h:55
lives_strlen
LIVES_GLOBAL_INLINE size_t lives_strlen(const char *s)
Definition: machinestate.c:1468
lives_subtitles_t::offset
int offset
offset in frames (default 0)
Definition: pangotext.h:45
save_sub_subtitles
boolean save_sub_subtitles(lives_clip_t *sfile, double start_time, double end_time, double offset_time, const char *filename)
Definition: pangotext.c:1101
main.h
lives_colRGBA64_t
Definition: main.h:322
weed_layer_set_gamma
LIVES_GLOBAL_INLINE weed_layer_t * weed_layer_set_gamma(weed_layer_t *layer, int gamma_type)
Definition: colourspace.c:9784
mainw
mainwindow * mainw
Definition: main.c:103
lives_subtitles_t::type
lives_subtitle_type_t type
Definition: pangotext.h:39
lives_text_mode_t
lives_text_mode_t
Definition: pangotext.h:49
lives_buffered_write_printf
ssize_t lives_buffered_write_printf(int fd, boolean allow_fail, const char *fmt,...)
Definition: utils.c:1316
lives_parse_font_string
boolean lives_parse_font_string(const char *string, char **font, int *size, char **stretch, char **style, char **weight)
Definition: pangotext.c:1182
lives_write_buffered
ssize_t lives_write_buffered(int fd, const char *buf, ssize_t count, boolean allow_fail)
Definition: utils.c:1226
lives_painter_to_layer
boolean lives_painter_to_layer(lives_painter_t *cr, weed_layer_t *layer)
convert a lives_painter_t (a.k.a) cairo_t to a weed layer
Definition: colourspace.c:13549
layout_to_lives_painter
void layout_to_lives_painter(LingoLayout *layout, lives_painter_t *cr, lives_text_mode_t mode, lives_colRGBA64_t *fg, lives_colRGBA64_t *bg, int dwidth, int dheight, double x_bg, double y_bg, double x_text, double y_text)
Definition: pangotext.c:165
weed_layer_free
LIVES_GLOBAL_INLINE weed_layer_t * weed_layer_free(weed_layer_t *layer)
frees pixel_data for a layer, then the layer itself
Definition: colourspace.c:13883
lives_subtitles_t::current
lives_subtitle_t * current
pointer to current entry in index
Definition: pangotext.h:42
lives_subtitles_t::text
char * text
Definition: pangotext.h:41
subtitles_free
void subtitles_free(lives_clip_t *sfile)
Definition: pangotext.c:976
lives_clip_t::subt
lives_subtitles_t * subt
Definition: main.h:1076
lives_clip_t
corresponds to one clip in the GUI
Definition: main.h:877
lives_subtitle_t
Definition: pangotext.h:29
lives_colRGBA64_t::red
uint16_t red
Definition: main.h:323
lives_rgba_col_new
WIDGET_HELPER_GLOBAL_INLINE lives_colRGBA64_t lives_rgba_col_new(int red, int green, int blue, int alpha)
Definition: widget-helper.c:12574
lives_subtitle_t::start_time
double start_time
Definition: pangotext.h:30
effects-weed.h
weed_layer_get_rowstride
LIVES_GLOBAL_INLINE int weed_layer_get_rowstride(weed_layer_t *layer)
for packed palettes
Definition: colourspace.c:13935
render_text_overlay
LIVES_GLOBAL_INLINE weed_plant_t * render_text_overlay(weed_layer_t *layer, const char *text)
Definition: pangotext.c:543
pangotext.h
lives_subtitles_t::last
lives_subtitle_t * last
Definition: pangotext.h:44
lives_painter_fill
WIDGET_HELPER_GLOBAL_INLINE boolean lives_painter_fill(lives_painter_t *cr)
Definition: widget-helper.c:367
lives_widget_object_unref
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_object_unref(livespointer object)
decrease refcount by one: if refcount==0, object is destroyed
Definition: widget-helper.c:815
lives_painter_set_source_rgba
WIDGET_HELPER_GLOBAL_INLINE boolean lives_painter_set_source_rgba(lives_painter_t *cr, double red, double green, double blue, double alpha)
Definition: widget-helper.c:619
subtitles_init
boolean subtitles_init(lives_clip_t *sfile, char *fname, lives_subtitle_type_t subtype)
Definition: pangotext.c:996
DEF_FILE_PERMS
#define DEF_FILE_PERMS
non-executable, is modified by the umask
Definition: main.h:209
lives_subtitles_t
Definition: pangotext.h:38
FALSE
#define FALSE
Definition: videoplugin.h:60
lives_painter_rectangle
WIDGET_HELPER_GLOBAL_INLINE boolean lives_painter_rectangle(lives_painter_t *cr, double x, double y, double width, double height)
Definition: widget-helper.c:555
capability::byte_order
int byte_order
Definition: main.h:577
lives_rm
int lives_rm(const char *file)
Definition: utils.c:4395
LIVES_TEXT_MODE_PRECALCULATE
@ LIVES_TEXT_MODE_PRECALCULATE
Definition: pangotext.h:53
lives_subtitle_t::next
xlives_subtitle_t * next
Definition: pangotext.h:35
SUBTITLE_TYPE_SRT
@ SUBTITLE_TYPE_SRT
Definition: pangotext.h:17
WEED_LEAF_PREVIOUS
#define WEED_LEAF_PREVIOUS
Definition: events.h:63