Codebase list libass / 6b3d5d7
New upstream version 0.15.0 Sebastian Ramacher 3 years ago
65 changed file(s) with 6999 addition(s) and 5353 deletion(s). Raw diff Collapse all Expand all
0 libass (0.15.0)
1 * Fix backwards/VSFilter-incompatible font sizes with FreeType 2.10+
2 * Improve speed via better caching
3 * Require HarfBuzz unconditionally to ensure good shaping for complex scripts
4 * ass_set_use_margins(true) now simply places text on the whole
5 screen instead of attempting to tie it to video positioning
6 (set by the margin values) and failing in various ways when
7 margins are used to implement pan & scan in a video player
8 * Add ass_track_set_feature() & catch-all ASS_FEATURE_INCOMPATIBLE_EXTENSIONS
9 * Add ASS_FEATURE_BIDI_BRACKETS to enable Unicode 6.3+ bracket matching
10 when applying the Unicode Bidirectional Algorithm
11 (incompatible with VSFilter; requires libass built with FriBidi 1.0+)
12 * Fix stack overflow on deeply nested \t tags
13 * Fix positioning of events with leading line breaks
14 * Fix small but nonzero \bord becoming \bord0 (regression in 0.14.0)
15 * Measure BorderStyle=4 box padding from glyph border, not from glyph fill
16 * Scale everything from script resolution if storage size is not set
17 (including borders and shadows regardless of ScaledBorderAndShadow)
18 * Fix the default aspect ratio calculation when neither
19 ass_set_pixel_aspect() nor ass_set_aspect_ratio() is called
20 * Multiple fixes for karaoke override tags
21 * Handle memory allocation failures better:
22 avoid some crashes and produce images closer to truth
23 * Avoid some integer overflows
24 * Add internal infrastructure for regression testing
25 * Improve VSFilter compatibility:
26 * Treat invalid nested \t tags like VSFilter
27 * Make \t(T,T,...) at time exactly T use the post-transition values
28 * Make lines stack more like they do in VSFilter
29 * Default ScaledBorderAndShadow to 0 (like VSFilter),
30 except for subtitles that were produced by old FFmpeg/Libav
31 * Make shadow positioning with 3D transforms match VSFilter's
32 * Cut out glyphs from border & shadow in all the same cases as VSFilter
33 * Match VSFilter on animated color tags with negative acceleration
34 * Fix parsing of some files that VSFilter accepts but libass didn't
35 by ignoring leading whitespace in each line of an ASS file or CodecPrivate
36 * Improve font selection with CoreText
37 * Stop faux-bolding fonts that are too bold to get faux bold in VSFilter
38 * Ignore leading/trailing spaces when calculating height of nonblank lines
39 * Match VSFilter on \fade with large alpha value arguments
40 * Stop splitting bitmaps on font substitution
41 * Multiple fixes for Banner and Scroll effects
42 * Multiple fixes for karaoke override tags
43
044 libass (0.14.0)
145 * Brand new, faster and better outline stroker (replaces FreeType stroker)
246 * Remove option to use the FreeType rasterizer
44 pkgconfigdir = $(libdir)/pkgconfig
55 pkgconfig_DATA = libass.pc
66
7 if HAVE_LIBPNG
7 if ENABLE_TEST
88 test = test
9 endif
10
11 if ENABLE_COMPARE
12 compare = compare
913 endif
1014
1115 if ENABLE_PROFILE
1216 profile = profile
1317 endif
1418
15 SUBDIRS = libass $(test) $(profile)
19 SUBDIRS = libass $(test) $(compare) $(profile)
1620
0 # Makefile.in generated by automake 1.15 from Makefile.am.
0 # Makefile.in generated by automake 1.16.1 from Makefile.am.
11 # @configure_input@
22
3 # Copyright (C) 1994-2014 Free Software Foundation, Inc.
3 # Copyright (C) 1994-2018 Free Software Foundation, Inc.
44
55 # This Makefile.in is free software; the Free Software Foundation
66 # gives unlimited permission to copy and/or distribute it,
166166 $(RECURSIVE_CLEAN_TARGETS) \
167167 $(am__extra_recursive_targets)
168168 AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
169 cscope distdir dist dist-all distcheck
169 cscope distdir distdir-am dist dist-all distcheck
170170 am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \
171171 $(LISP)config.h.in
172172 # Read a list of newline-separated strings from the standard input,
188188 ETAGS = etags
189189 CTAGS = ctags
190190 CSCOPE = cscope
191 DIST_SUBDIRS = libass test profile
191 DIST_SUBDIRS = libass test compare profile
192192 am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/config.h.in \
193193 $(srcdir)/libass.pc.in COPYING compile config.guess config.sub \
194 install-sh ltmain.sh missing
194 depcomp install-sh ltmain.sh missing
195195 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
196196 distdir = $(PACKAGE)-$(VERSION)
197197 top_distdir = $(distdir)
377377 EXTRA_DIST = libass.pc.in Changelog
378378 pkgconfigdir = $(libdir)/pkgconfig
379379 pkgconfig_DATA = libass.pc
380 @HAVE_LIBPNG_TRUE@test = test
380 @ENABLE_TEST_TRUE@test = test
381 @ENABLE_COMPARE_TRUE@compare = compare
381382 @ENABLE_PROFILE_TRUE@profile = profile
382 SUBDIRS = libass $(test) $(profile)
383 SUBDIRS = libass $(test) $(compare) $(profile)
383384 all: config.h
384385 $(MAKE) $(AM_MAKEFLAGS) all-recursive
385386
405406 echo ' $(SHELL) ./config.status'; \
406407 $(SHELL) ./config.status;; \
407408 *) \
408 echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \
409 cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \
409 echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles)'; \
410 cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles);; \
410411 esac;
411412
412413 $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
571572 -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
572573 -rm -f cscope.out cscope.in.out cscope.po.out cscope.files
573574
574 distdir: $(DISTFILES)
575 distdir: $(BUILT_SOURCES)
576 $(MAKE) $(AM_MAKEFLAGS) distdir-am
577
578 distdir-am: $(DISTFILES)
575579 $(am__remove_distdir)
576580 test -d "$(distdir)" || mkdir "$(distdir)"
577581 @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
636640 ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \
637641 || chmod -R a+r "$(distdir)"
638642 dist-gzip: distdir
639 tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
643 tardir=$(distdir) && $(am__tar) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).tar.gz
640644 $(am__post_remove_distdir)
641645
642646 dist-bzip2: distdir
662666 @echo WARNING: "Support for shar distribution archives is" \
663667 "deprecated." >&2
664668 @echo WARNING: "It will be removed altogether in Automake 2.0" >&2
665 shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz
669 shar $(distdir) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).shar.gz
666670 $(am__post_remove_distdir)
667671
668672 dist-zip: distdir
680684 distcheck: dist
681685 case '$(DIST_ARCHIVES)' in \
682686 *.tar.gz*) \
683 GZIP=$(GZIP_ENV) gzip -dc $(distdir).tar.gz | $(am__untar) ;;\
687 eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).tar.gz | $(am__untar) ;;\
684688 *.tar.bz2*) \
685689 bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\
686690 *.tar.lz*) \
690694 *.tar.Z*) \
691695 uncompress -c $(distdir).tar.Z | $(am__untar) ;;\
692696 *.shar.gz*) \
693 GZIP=$(GZIP_ENV) gzip -dc $(distdir).shar.gz | unshar ;;\
697 eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).shar.gz | unshar ;;\
694698 *.zip*) \
695699 unzip $(distdir).zip ;;\
696700 esac
0 # generated automatically by aclocal 1.15 -*- Autoconf -*-
1
2 # Copyright (C) 1996-2014 Free Software Foundation, Inc.
0 # generated automatically by aclocal 1.16.1 -*- Autoconf -*-
1
2 # Copyright (C) 1996-2018 Free Software Foundation, Inc.
33
44 # This file is free software; the Free Software Foundation
55 # gives unlimited permission to copy and/or distribute it,
295295 AS_VAR_IF([$1], [""], [$5], [$4])dnl
296296 ])dnl PKG_CHECK_VAR
297297
298 # Copyright (C) 2002-2014 Free Software Foundation, Inc.
298 # Copyright (C) 2002-2018 Free Software Foundation, Inc.
299299 #
300300 # This file is free software; the Free Software Foundation
301301 # gives unlimited permission to copy and/or distribute it,
307307 # generated from the m4 files accompanying Automake X.Y.
308308 # (This private macro should not be called outside this file.)
309309 AC_DEFUN([AM_AUTOMAKE_VERSION],
310 [am__api_version='1.15'
310 [am__api_version='1.16'
311311 dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
312312 dnl require some minimum version. Point them to the right macro.
313 m4_if([$1], [1.15], [],
313 m4_if([$1], [1.16.1], [],
314314 [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
315315 ])
316316
326326 # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
327327 # This function is AC_REQUIREd by AM_INIT_AUTOMAKE.
328328 AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
329 [AM_AUTOMAKE_VERSION([1.15])dnl
329 [AM_AUTOMAKE_VERSION([1.16.1])dnl
330330 m4_ifndef([AC_AUTOCONF_VERSION],
331331 [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
332332 _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
333333
334334 # Figure out how to run the assembler. -*- Autoconf -*-
335335
336 # Copyright (C) 2001-2014 Free Software Foundation, Inc.
336 # Copyright (C) 2001-2018 Free Software Foundation, Inc.
337337 #
338338 # This file is free software; the Free Software Foundation
339339 # gives unlimited permission to copy and/or distribute it,
353353
354354 # AM_AUX_DIR_EXPAND -*- Autoconf -*-
355355
356 # Copyright (C) 2001-2014 Free Software Foundation, Inc.
356 # Copyright (C) 2001-2018 Free Software Foundation, Inc.
357357 #
358358 # This file is free software; the Free Software Foundation
359359 # gives unlimited permission to copy and/or distribute it,
405405
406406 # AM_COND_IF -*- Autoconf -*-
407407
408 # Copyright (C) 2008-2014 Free Software Foundation, Inc.
408 # Copyright (C) 2008-2018 Free Software Foundation, Inc.
409409 #
410410 # This file is free software; the Free Software Foundation
411411 # gives unlimited permission to copy and/or distribute it,
442442
443443 # AM_CONDITIONAL -*- Autoconf -*-
444444
445 # Copyright (C) 1997-2014 Free Software Foundation, Inc.
445 # Copyright (C) 1997-2018 Free Software Foundation, Inc.
446446 #
447447 # This file is free software; the Free Software Foundation
448448 # gives unlimited permission to copy and/or distribute it,
473473 Usually this means the macro was only invoked conditionally.]])
474474 fi])])
475475
476 # Copyright (C) 1999-2014 Free Software Foundation, Inc.
476 # Copyright (C) 1999-2018 Free Software Foundation, Inc.
477477 #
478478 # This file is free software; the Free Software Foundation
479479 # gives unlimited permission to copy and/or distribute it,
664664
665665 # Generate code to set up dependency tracking. -*- Autoconf -*-
666666
667 # Copyright (C) 1999-2014 Free Software Foundation, Inc.
668 #
669 # This file is free software; the Free Software Foundation
670 # gives unlimited permission to copy and/or distribute it,
671 # with or without modifications, as long as this notice is preserved.
672
667 # Copyright (C) 1999-2018 Free Software Foundation, Inc.
668 #
669 # This file is free software; the Free Software Foundation
670 # gives unlimited permission to copy and/or distribute it,
671 # with or without modifications, as long as this notice is preserved.
673672
674673 # _AM_OUTPUT_DEPENDENCY_COMMANDS
675674 # ------------------------------
678677 # Older Autoconf quotes --file arguments for eval, but not when files
679678 # are listed without --file. Let's play safe and only enable the eval
680679 # if we detect the quoting.
681 case $CONFIG_FILES in
682 *\'*) eval set x "$CONFIG_FILES" ;;
683 *) set x $CONFIG_FILES ;;
684 esac
680 # TODO: see whether this extra hack can be removed once we start
681 # requiring Autoconf 2.70 or later.
682 AS_CASE([$CONFIG_FILES],
683 [*\'*], [eval set x "$CONFIG_FILES"],
684 [*], [set x $CONFIG_FILES])
685685 shift
686 for mf
686 # Used to flag and report bootstrapping failures.
687 am_rc=0
688 for am_mf
687689 do
688690 # Strip MF so we end up with the name of the file.
689 mf=`echo "$mf" | sed -e 's/:.*$//'`
690 # Check whether this is an Automake generated Makefile or not.
691 # We used to match only the files named 'Makefile.in', but
692 # some people rename them; so instead we look at the file content.
693 # Grep'ing the first line is not enough: some people post-process
694 # each Makefile.in and add a new line on top of each file to say so.
695 # Grep'ing the whole file is not good either: AIX grep has a line
691 am_mf=`AS_ECHO(["$am_mf"]) | sed -e 's/:.*$//'`
692 # Check whether this is an Automake generated Makefile which includes
693 # dependency-tracking related rules and includes.
694 # Grep'ing the whole file directly is not great: AIX grep has a line
696695 # limit of 2048, but all sed's we know have understand at least 4000.
697 if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then
698 dirpart=`AS_DIRNAME("$mf")`
699 else
700 continue
701 fi
702 # Extract the definition of DEPDIR, am__include, and am__quote
703 # from the Makefile without running 'make'.
704 DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
705 test -z "$DEPDIR" && continue
706 am__include=`sed -n 's/^am__include = //p' < "$mf"`
707 test -z "$am__include" && continue
708 am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
709 # Find all dependency output files, they are included files with
710 # $(DEPDIR) in their names. We invoke sed twice because it is the
711 # simplest approach to changing $(DEPDIR) to its actual value in the
712 # expansion.
713 for file in `sed -n "
714 s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
715 sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do
716 # Make sure the directory exists.
717 test -f "$dirpart/$file" && continue
718 fdir=`AS_DIRNAME(["$file"])`
719 AS_MKDIR_P([$dirpart/$fdir])
720 # echo "creating $dirpart/$file"
721 echo '# dummy' > "$dirpart/$file"
722 done
696 sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \
697 || continue
698 am_dirpart=`AS_DIRNAME(["$am_mf"])`
699 am_filepart=`AS_BASENAME(["$am_mf"])`
700 AM_RUN_LOG([cd "$am_dirpart" \
701 && sed -e '/# am--include-marker/d' "$am_filepart" \
702 | $MAKE -f - am--depfiles]) || am_rc=$?
723703 done
704 if test $am_rc -ne 0; then
705 AC_MSG_FAILURE([Something went wrong bootstrapping makefile fragments
706 for automatic dependency tracking. Try re-running configure with the
707 '--disable-dependency-tracking' option to at least be able to build
708 the package (albeit without support for automatic dependency tracking).])
709 fi
710 AS_UNSET([am_dirpart])
711 AS_UNSET([am_filepart])
712 AS_UNSET([am_mf])
713 AS_UNSET([am_rc])
714 rm -f conftest-deps.mk
724715 }
725716 ])# _AM_OUTPUT_DEPENDENCY_COMMANDS
726717
729720 # -----------------------------
730721 # This macro should only be invoked once -- use via AC_REQUIRE.
731722 #
732 # This code is only required when automatic dependency tracking
733 # is enabled. FIXME. This creates each '.P' file that we will
734 # need in order to bootstrap the dependency handling code.
723 # This code is only required when automatic dependency tracking is enabled.
724 # This creates each '.Po' and '.Plo' makefile fragment that we'll need in
725 # order to bootstrap the dependency handling code.
735726 AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
736727 [AC_CONFIG_COMMANDS([depfiles],
737728 [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS],
738 [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"])
739 ])
729 [AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}"])])
740730
741731 # Do all the work for Automake. -*- Autoconf -*-
742732
743 # Copyright (C) 1996-2014 Free Software Foundation, Inc.
733 # Copyright (C) 1996-2018 Free Software Foundation, Inc.
744734 #
745735 # This file is free software; the Free Software Foundation
746736 # gives unlimited permission to copy and/or distribute it,
827817 AC_REQUIRE([AC_PROG_MKDIR_P])dnl
828818 # For better backward compatibility. To be removed once Automake 1.9.x
829819 # dies out for good. For more background, see:
830 # <http://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
831 # <http://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
820 # <https://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
821 # <https://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
832822 AC_SUBST([mkdir_p], ['$(MKDIR_P)'])
833823 # We need awk for the "check" target (and possibly the TAP driver). The
834824 # system "awk" is bad on some platforms.
895885 Aborting the configuration process, to ensure you take notice of the issue.
896886
897887 You can download and install GNU coreutils to get an 'rm' implementation
898 that behaves properly: <http://www.gnu.org/software/coreutils/>.
888 that behaves properly: <https://www.gnu.org/software/coreutils/>.
899889
900890 If you want to complete the configuration process using your problematic
901891 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM
937927 done
938928 echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count])
939929
940 # Copyright (C) 2001-2014 Free Software Foundation, Inc.
930 # Copyright (C) 2001-2018 Free Software Foundation, Inc.
941931 #
942932 # This file is free software; the Free Software Foundation
943933 # gives unlimited permission to copy and/or distribute it,
958948 fi
959949 AC_SUBST([install_sh])])
960950
961 # Copyright (C) 2003-2014 Free Software Foundation, Inc.
951 # Copyright (C) 2003-2018 Free Software Foundation, Inc.
962952 #
963953 # This file is free software; the Free Software Foundation
964954 # gives unlimited permission to copy and/or distribute it,
979969
980970 # Check to see how 'make' treats includes. -*- Autoconf -*-
981971
982 # Copyright (C) 2001-2014 Free Software Foundation, Inc.
972 # Copyright (C) 2001-2018 Free Software Foundation, Inc.
983973 #
984974 # This file is free software; the Free Software Foundation
985975 # gives unlimited permission to copy and/or distribute it,
987977
988978 # AM_MAKE_INCLUDE()
989979 # -----------------
990 # Check to see how make treats includes.
980 # Check whether make has an 'include' directive that can support all
981 # the idioms we need for our automatic dependency tracking code.
991982 AC_DEFUN([AM_MAKE_INCLUDE],
992 [am_make=${MAKE-make}
993 cat > confinc << 'END'
983 [AC_MSG_CHECKING([whether ${MAKE-make} supports the include directive])
984 cat > confinc.mk << 'END'
994985 am__doit:
995 @echo this is the am__doit target
986 @echo this is the am__doit target >confinc.out
996987 .PHONY: am__doit
997988 END
998 # If we don't find an include directive, just comment out the code.
999 AC_MSG_CHECKING([for style of include used by $am_make])
1000989 am__include="#"
1001990 am__quote=
1002 _am_result=none
1003 # First try GNU make style include.
1004 echo "include confinc" > confmf
1005 # Ignore all kinds of additional output from 'make'.
1006 case `$am_make -s -f confmf 2> /dev/null` in #(
1007 *the\ am__doit\ target*)
1008 am__include=include
1009 am__quote=
1010 _am_result=GNU
1011 ;;
1012 esac
1013 # Now try BSD make style include.
1014 if test "$am__include" = "#"; then
1015 echo '.include "confinc"' > confmf
1016 case `$am_make -s -f confmf 2> /dev/null` in #(
1017 *the\ am__doit\ target*)
1018 am__include=.include
1019 am__quote="\""
1020 _am_result=BSD
1021 ;;
1022 esac
1023 fi
1024 AC_SUBST([am__include])
1025 AC_SUBST([am__quote])
1026 AC_MSG_RESULT([$_am_result])
1027 rm -f confinc confmf
1028 ])
991 # BSD make does it like this.
992 echo '.include "confinc.mk" # ignored' > confmf.BSD
993 # Other make implementations (GNU, Solaris 10, AIX) do it like this.
994 echo 'include confinc.mk # ignored' > confmf.GNU
995 _am_result=no
996 for s in GNU BSD; do
997 AM_RUN_LOG([${MAKE-make} -f confmf.$s && cat confinc.out])
998 AS_CASE([$?:`cat confinc.out 2>/dev/null`],
999 ['0:this is the am__doit target'],
1000 [AS_CASE([$s],
1001 [BSD], [am__include='.include' am__quote='"'],
1002 [am__include='include' am__quote=''])])
1003 if test "$am__include" != "#"; then
1004 _am_result="yes ($s style)"
1005 break
1006 fi
1007 done
1008 rm -f confinc.* confmf.*
1009 AC_MSG_RESULT([${_am_result}])
1010 AC_SUBST([am__include])])
1011 AC_SUBST([am__quote])])
10291012
10301013 # Fake the existence of programs that GNU maintainers use. -*- Autoconf -*-
10311014
1032 # Copyright (C) 1997-2014 Free Software Foundation, Inc.
1015 # Copyright (C) 1997-2018 Free Software Foundation, Inc.
10331016 #
10341017 # This file is free software; the Free Software Foundation
10351018 # gives unlimited permission to copy and/or distribute it,
10681051
10691052 # Helper functions for option handling. -*- Autoconf -*-
10701053
1071 # Copyright (C) 2001-2014 Free Software Foundation, Inc.
1054 # Copyright (C) 2001-2018 Free Software Foundation, Inc.
10721055 #
10731056 # This file is free software; the Free Software Foundation
10741057 # gives unlimited permission to copy and/or distribute it,
10971080 AC_DEFUN([_AM_IF_OPTION],
10981081 [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
10991082
1100 # Copyright (C) 1999-2014 Free Software Foundation, Inc.
1083 # Copyright (C) 1999-2018 Free Software Foundation, Inc.
11011084 #
11021085 # This file is free software; the Free Software Foundation
11031086 # gives unlimited permission to copy and/or distribute it,
11441127 # For backward compatibility.
11451128 AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])])
11461129
1147 # Copyright (C) 2001-2014 Free Software Foundation, Inc.
1130 # Copyright (C) 2001-2018 Free Software Foundation, Inc.
11481131 #
11491132 # This file is free software; the Free Software Foundation
11501133 # gives unlimited permission to copy and/or distribute it,
11631146
11641147 # Check to make sure that the build environment is sane. -*- Autoconf -*-
11651148
1166 # Copyright (C) 1996-2014 Free Software Foundation, Inc.
1149 # Copyright (C) 1996-2018 Free Software Foundation, Inc.
11671150 #
11681151 # This file is free software; the Free Software Foundation
11691152 # gives unlimited permission to copy and/or distribute it,
12441227 rm -f conftest.file
12451228 ])
12461229
1247 # Copyright (C) 2009-2014 Free Software Foundation, Inc.
1230 # Copyright (C) 2009-2018 Free Software Foundation, Inc.
12481231 #
12491232 # This file is free software; the Free Software Foundation
12501233 # gives unlimited permission to copy and/or distribute it,
13041287 _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl
13051288 ])
13061289
1307 # Copyright (C) 2001-2014 Free Software Foundation, Inc.
1290 # Copyright (C) 2001-2018 Free Software Foundation, Inc.
13081291 #
13091292 # This file is free software; the Free Software Foundation
13101293 # gives unlimited permission to copy and/or distribute it,
13321315 INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
13331316 AC_SUBST([INSTALL_STRIP_PROGRAM])])
13341317
1335 # Copyright (C) 2006-2014 Free Software Foundation, Inc.
1318 # Copyright (C) 2006-2018 Free Software Foundation, Inc.
13361319 #
13371320 # This file is free software; the Free Software Foundation
13381321 # gives unlimited permission to copy and/or distribute it,
13511334
13521335 # Check how to create a tarball. -*- Autoconf -*-
13531336
1354 # Copyright (C) 2004-2014 Free Software Foundation, Inc.
1337 # Copyright (C) 2004-2018 Free Software Foundation, Inc.
13551338 #
13561339 # This file is free software; the Free Software Foundation
13571340 # gives unlimited permission to copy and/or distribute it,
0 AM_CFLAGS = -Wall
1
2 noinst_PROGRAMS = compare
3 compare_SOURCES = image.h image.c compare.c
4 compare_CPPFLAGS = -I$(top_srcdir)/libass
5 compare_LDADD = $(top_builddir)/libass/.libs/libass.a
6 compare_LDFLAGS = $(AM_LDFLAGS) $(LIBPNG_LIBS) -static
0 # Makefile.in generated by automake 1.16.1 from Makefile.am.
1 # @configure_input@
2
3 # Copyright (C) 1994-2018 Free Software Foundation, Inc.
4
5 # This Makefile.in is free software; the Free Software Foundation
6 # gives unlimited permission to copy and/or distribute it,
7 # with or without modifications, as long as this notice is preserved.
8
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY, to the extent permitted by law; without
11 # even the implied warranty of MERCHANTABILITY or FITNESS FOR A
12 # PARTICULAR PURPOSE.
13
14 @SET_MAKE@
15
16 VPATH = @srcdir@
17 am__is_gnu_make = { \
18 if test -z '$(MAKELEVEL)'; then \
19 false; \
20 elif test -n '$(MAKE_HOST)'; then \
21 true; \
22 elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
23 true; \
24 else \
25 false; \
26 fi; \
27 }
28 am__make_running_with_option = \
29 case $${target_option-} in \
30 ?) ;; \
31 *) echo "am__make_running_with_option: internal error: invalid" \
32 "target option '$${target_option-}' specified" >&2; \
33 exit 1;; \
34 esac; \
35 has_opt=no; \
36 sane_makeflags=$$MAKEFLAGS; \
37 if $(am__is_gnu_make); then \
38 sane_makeflags=$$MFLAGS; \
39 else \
40 case $$MAKEFLAGS in \
41 *\\[\ \ ]*) \
42 bs=\\; \
43 sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
44 | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
45 esac; \
46 fi; \
47 skip_next=no; \
48 strip_trailopt () \
49 { \
50 flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
51 }; \
52 for flg in $$sane_makeflags; do \
53 test $$skip_next = yes && { skip_next=no; continue; }; \
54 case $$flg in \
55 *=*|--*) continue;; \
56 -*I) strip_trailopt 'I'; skip_next=yes;; \
57 -*I?*) strip_trailopt 'I';; \
58 -*O) strip_trailopt 'O'; skip_next=yes;; \
59 -*O?*) strip_trailopt 'O';; \
60 -*l) strip_trailopt 'l'; skip_next=yes;; \
61 -*l?*) strip_trailopt 'l';; \
62 -[dEDm]) skip_next=yes;; \
63 -[JT]) skip_next=yes;; \
64 esac; \
65 case $$flg in \
66 *$$target_option*) has_opt=yes; break;; \
67 esac; \
68 done; \
69 test $$has_opt = yes
70 am__make_dryrun = (target_option=n; $(am__make_running_with_option))
71 am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
72 pkgdatadir = $(datadir)/@PACKAGE@
73 pkgincludedir = $(includedir)/@PACKAGE@
74 pkglibdir = $(libdir)/@PACKAGE@
75 pkglibexecdir = $(libexecdir)/@PACKAGE@
76 am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
77 install_sh_DATA = $(install_sh) -c -m 644
78 install_sh_PROGRAM = $(install_sh) -c
79 install_sh_SCRIPT = $(install_sh) -c
80 INSTALL_HEADER = $(INSTALL_DATA)
81 transform = $(program_transform_name)
82 NORMAL_INSTALL = :
83 PRE_INSTALL = :
84 POST_INSTALL = :
85 NORMAL_UNINSTALL = :
86 PRE_UNINSTALL = :
87 POST_UNINSTALL = :
88 build_triplet = @build@
89 host_triplet = @host@
90 noinst_PROGRAMS = compare$(EXEEXT)
91 subdir = compare
92 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
93 am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
94 $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
95 $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
96 $(top_srcdir)/configure.ac
97 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
98 $(ACLOCAL_M4)
99 DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
100 mkinstalldirs = $(install_sh) -d
101 CONFIG_HEADER = $(top_builddir)/config.h
102 CONFIG_CLEAN_FILES =
103 CONFIG_CLEAN_VPATH_FILES =
104 PROGRAMS = $(noinst_PROGRAMS)
105 am_compare_OBJECTS = compare-image.$(OBJEXT) compare-compare.$(OBJEXT)
106 compare_OBJECTS = $(am_compare_OBJECTS)
107 compare_DEPENDENCIES = $(top_builddir)/libass/.libs/libass.a
108 AM_V_lt = $(am__v_lt_@AM_V@)
109 am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
110 am__v_lt_0 = --silent
111 am__v_lt_1 =
112 compare_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
113 $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
114 $(compare_LDFLAGS) $(LDFLAGS) -o $@
115 AM_V_P = $(am__v_P_@AM_V@)
116 am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
117 am__v_P_0 = false
118 am__v_P_1 = :
119 AM_V_GEN = $(am__v_GEN_@AM_V@)
120 am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
121 am__v_GEN_0 = @echo " GEN " $@;
122 am__v_GEN_1 =
123 AM_V_at = $(am__v_at_@AM_V@)
124 am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
125 am__v_at_0 = @
126 am__v_at_1 =
127 DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
128 depcomp = $(SHELL) $(top_srcdir)/depcomp
129 am__maybe_remake_depfiles = depfiles
130 am__depfiles_remade = ./$(DEPDIR)/compare-compare.Po \
131 ./$(DEPDIR)/compare-image.Po
132 am__mv = mv -f
133 COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
134 $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
135 LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
136 $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
137 $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
138 $(AM_CFLAGS) $(CFLAGS)
139 AM_V_CC = $(am__v_CC_@AM_V@)
140 am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
141 am__v_CC_0 = @echo " CC " $@;
142 am__v_CC_1 =
143 CCLD = $(CC)
144 LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
145 $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
146 $(AM_LDFLAGS) $(LDFLAGS) -o $@
147 AM_V_CCLD = $(am__v_CCLD_@AM_V@)
148 am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
149 am__v_CCLD_0 = @echo " CCLD " $@;
150 am__v_CCLD_1 =
151 SOURCES = $(compare_SOURCES)
152 DIST_SOURCES = $(compare_SOURCES)
153 am__can_run_installinfo = \
154 case $$AM_UPDATE_INFO_DIR in \
155 n|no|NO) false;; \
156 *) (install-info --version) >/dev/null 2>&1;; \
157 esac
158 am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
159 # Read a list of newline-separated strings from the standard input,
160 # and print each of them once, without duplicates. Input order is
161 # *not* preserved.
162 am__uniquify_input = $(AWK) '\
163 BEGIN { nonempty = 0; } \
164 { items[$$0] = 1; nonempty = 1; } \
165 END { if (nonempty) { for (i in items) print i; }; } \
166 '
167 # Make sure the list of sources is unique. This is necessary because,
168 # e.g., the same source file might be shared among _SOURCES variables
169 # for different programs/libraries.
170 am__define_uniq_tagged_files = \
171 list='$(am__tagged_files)'; \
172 unique=`for i in $$list; do \
173 if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
174 done | $(am__uniquify_input)`
175 ETAGS = etags
176 CTAGS = ctags
177 am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
178 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
179 ACLOCAL = @ACLOCAL@
180 AMTAR = @AMTAR@
181 AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
182 AR = @AR@
183 AS = @AS@
184 ASFLAGS = @ASFLAGS@
185 AUTOCONF = @AUTOCONF@
186 AUTOHEADER = @AUTOHEADER@
187 AUTOMAKE = @AUTOMAKE@
188 AWK = @AWK@
189 CC = @CC@
190 CCAS = @CCAS@
191 CCASDEPMODE = @CCASDEPMODE@
192 CCASFLAGS = @CCASFLAGS@
193 CCDEPMODE = @CCDEPMODE@
194 CFLAGS = @CFLAGS@
195 CPP = @CPP@
196 CPPFLAGS = @CPPFLAGS@
197 CYGPATH_W = @CYGPATH_W@
198 DEFS = @DEFS@
199 DEPDIR = @DEPDIR@
200 DLLTOOL = @DLLTOOL@
201 DSYMUTIL = @DSYMUTIL@
202 DUMPBIN = @DUMPBIN@
203 ECHO_C = @ECHO_C@
204 ECHO_N = @ECHO_N@
205 ECHO_T = @ECHO_T@
206 EGREP = @EGREP@
207 EXEEXT = @EXEEXT@
208 FGREP = @FGREP@
209 FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
210 FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
211 FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
212 FREETYPE_LIBS = @FREETYPE_LIBS@
213 FRIBIDI_CFLAGS = @FRIBIDI_CFLAGS@
214 FRIBIDI_LIBS = @FRIBIDI_LIBS@
215 GREP = @GREP@
216 HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
217 HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
218 INSTALL = @INSTALL@
219 INSTALL_DATA = @INSTALL_DATA@
220 INSTALL_PROGRAM = @INSTALL_PROGRAM@
221 INSTALL_SCRIPT = @INSTALL_SCRIPT@
222 INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
223 LD = @LD@
224 LDFLAGS = @LDFLAGS@
225 LIBOBJS = @LIBOBJS@
226 LIBPNG_CFLAGS = @LIBPNG_CFLAGS@
227 LIBPNG_LIBS = @LIBPNG_LIBS@
228 LIBS = @LIBS@
229 LIBTOOL = @LIBTOOL@
230 LIPO = @LIPO@
231 LN_S = @LN_S@
232 LTLIBOBJS = @LTLIBOBJS@
233 LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
234 MAKEINFO = @MAKEINFO@
235 MANIFEST_TOOL = @MANIFEST_TOOL@
236 MKDIR_P = @MKDIR_P@
237 NM = @NM@
238 NMEDIT = @NMEDIT@
239 OBJDUMP = @OBJDUMP@
240 OBJEXT = @OBJEXT@
241 OTOOL = @OTOOL@
242 OTOOL64 = @OTOOL64@
243 PACKAGE = @PACKAGE@
244 PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
245 PACKAGE_NAME = @PACKAGE_NAME@
246 PACKAGE_STRING = @PACKAGE_STRING@
247 PACKAGE_TARNAME = @PACKAGE_TARNAME@
248 PACKAGE_URL = @PACKAGE_URL@
249 PACKAGE_VERSION = @PACKAGE_VERSION@
250 PATH_SEPARATOR = @PATH_SEPARATOR@
251 PKG_CONFIG = @PKG_CONFIG@
252 PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
253 PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
254 PKG_LIBS_DEFAULT = @PKG_LIBS_DEFAULT@
255 PKG_LIBS_PRIVATE = @PKG_LIBS_PRIVATE@
256 PKG_REQUIRES_DEFAULT = @PKG_REQUIRES_DEFAULT@
257 PKG_REQUIRES_PRIVATE = @PKG_REQUIRES_PRIVATE@
258 RANLIB = @RANLIB@
259 SED = @SED@
260 SET_MAKE = @SET_MAKE@
261 SHELL = @SHELL@
262 STRIP = @STRIP@
263 VERSION = @VERSION@
264 abs_builddir = @abs_builddir@
265 abs_srcdir = @abs_srcdir@
266 abs_top_builddir = @abs_top_builddir@
267 abs_top_srcdir = @abs_top_srcdir@
268 ac_ct_AR = @ac_ct_AR@
269 ac_ct_CC = @ac_ct_CC@
270 ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
271 am__include = @am__include@
272 am__leading_dot = @am__leading_dot@
273 am__quote = @am__quote@
274 am__tar = @am__tar@
275 am__untar = @am__untar@
276 bindir = @bindir@
277 build = @build@
278 build_alias = @build_alias@
279 build_cpu = @build_cpu@
280 build_os = @build_os@
281 build_vendor = @build_vendor@
282 builddir = @builddir@
283 datadir = @datadir@
284 datarootdir = @datarootdir@
285 docdir = @docdir@
286 dvidir = @dvidir@
287 exec_prefix = @exec_prefix@
288 host = @host@
289 host_alias = @host_alias@
290 host_cpu = @host_cpu@
291 host_os = @host_os@
292 host_vendor = @host_vendor@
293 htmldir = @htmldir@
294 includedir = @includedir@
295 infodir = @infodir@
296 install_sh = @install_sh@
297 libdir = @libdir@
298 libexecdir = @libexecdir@
299 localedir = @localedir@
300 localstatedir = @localstatedir@
301 mandir = @mandir@
302 mkdir_p = @mkdir_p@
303 nasm_check = @nasm_check@
304 oldincludedir = @oldincludedir@
305 pdfdir = @pdfdir@
306 prefix = @prefix@
307 program_transform_name = @program_transform_name@
308 psdir = @psdir@
309 sbindir = @sbindir@
310 sharedstatedir = @sharedstatedir@
311 srcdir = @srcdir@
312 sysconfdir = @sysconfdir@
313 target_alias = @target_alias@
314 top_build_prefix = @top_build_prefix@
315 top_builddir = @top_builddir@
316 top_srcdir = @top_srcdir@
317 AM_CFLAGS = -Wall
318 compare_SOURCES = image.h image.c compare.c
319 compare_CPPFLAGS = -I$(top_srcdir)/libass
320 compare_LDADD = $(top_builddir)/libass/.libs/libass.a
321 compare_LDFLAGS = $(AM_LDFLAGS) $(LIBPNG_LIBS) -static
322 all: all-am
323
324 .SUFFIXES:
325 .SUFFIXES: .c .lo .o .obj
326 $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
327 @for dep in $?; do \
328 case '$(am__configure_deps)' in \
329 *$$dep*) \
330 ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
331 && { if test -f $@; then exit 0; else break; fi; }; \
332 exit 1;; \
333 esac; \
334 done; \
335 echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign compare/Makefile'; \
336 $(am__cd) $(top_srcdir) && \
337 $(AUTOMAKE) --foreign compare/Makefile
338 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
339 @case '$?' in \
340 *config.status*) \
341 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
342 *) \
343 echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
344 cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
345 esac;
346
347 $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
348 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
349
350 $(top_srcdir)/configure: $(am__configure_deps)
351 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
352 $(ACLOCAL_M4): $(am__aclocal_m4_deps)
353 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
354 $(am__aclocal_m4_deps):
355
356 clean-noinstPROGRAMS:
357 @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \
358 echo " rm -f" $$list; \
359 rm -f $$list || exit $$?; \
360 test -n "$(EXEEXT)" || exit 0; \
361 list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
362 echo " rm -f" $$list; \
363 rm -f $$list
364
365 compare$(EXEEXT): $(compare_OBJECTS) $(compare_DEPENDENCIES) $(EXTRA_compare_DEPENDENCIES)
366 @rm -f compare$(EXEEXT)
367 $(AM_V_CCLD)$(compare_LINK) $(compare_OBJECTS) $(compare_LDADD) $(LIBS)
368
369 mostlyclean-compile:
370 -rm -f *.$(OBJEXT)
371
372 distclean-compile:
373 -rm -f *.tab.c
374
375 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/compare-compare.Po@am__quote@ # am--include-marker
376 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/compare-image.Po@am__quote@ # am--include-marker
377
378 $(am__depfiles_remade):
379 @$(MKDIR_P) $(@D)
380 @echo '# dummy' >$@-t && $(am__mv) $@-t $@
381
382 am--depfiles: $(am__depfiles_remade)
383
384 .c.o:
385 @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
386 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
387 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
388 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
389 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
390
391 .c.obj:
392 @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
393 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
394 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
395 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
396 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
397
398 .c.lo:
399 @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
400 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
401 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
402 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
403 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
404
405 compare-image.o: image.c
406 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(compare_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT compare-image.o -MD -MP -MF $(DEPDIR)/compare-image.Tpo -c -o compare-image.o `test -f 'image.c' || echo '$(srcdir)/'`image.c
407 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/compare-image.Tpo $(DEPDIR)/compare-image.Po
408 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='image.c' object='compare-image.o' libtool=no @AMDEPBACKSLASH@
409 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
410 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(compare_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o compare-image.o `test -f 'image.c' || echo '$(srcdir)/'`image.c
411
412 compare-image.obj: image.c
413 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(compare_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT compare-image.obj -MD -MP -MF $(DEPDIR)/compare-image.Tpo -c -o compare-image.obj `if test -f 'image.c'; then $(CYGPATH_W) 'image.c'; else $(CYGPATH_W) '$(srcdir)/image.c'; fi`
414 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/compare-image.Tpo $(DEPDIR)/compare-image.Po
415 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='image.c' object='compare-image.obj' libtool=no @AMDEPBACKSLASH@
416 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
417 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(compare_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o compare-image.obj `if test -f 'image.c'; then $(CYGPATH_W) 'image.c'; else $(CYGPATH_W) '$(srcdir)/image.c'; fi`
418
419 compare-compare.o: compare.c
420 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(compare_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT compare-compare.o -MD -MP -MF $(DEPDIR)/compare-compare.Tpo -c -o compare-compare.o `test -f 'compare.c' || echo '$(srcdir)/'`compare.c
421 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/compare-compare.Tpo $(DEPDIR)/compare-compare.Po
422 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='compare.c' object='compare-compare.o' libtool=no @AMDEPBACKSLASH@
423 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
424 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(compare_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o compare-compare.o `test -f 'compare.c' || echo '$(srcdir)/'`compare.c
425
426 compare-compare.obj: compare.c
427 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(compare_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT compare-compare.obj -MD -MP -MF $(DEPDIR)/compare-compare.Tpo -c -o compare-compare.obj `if test -f 'compare.c'; then $(CYGPATH_W) 'compare.c'; else $(CYGPATH_W) '$(srcdir)/compare.c'; fi`
428 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/compare-compare.Tpo $(DEPDIR)/compare-compare.Po
429 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='compare.c' object='compare-compare.obj' libtool=no @AMDEPBACKSLASH@
430 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
431 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(compare_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o compare-compare.obj `if test -f 'compare.c'; then $(CYGPATH_W) 'compare.c'; else $(CYGPATH_W) '$(srcdir)/compare.c'; fi`
432
433 mostlyclean-libtool:
434 -rm -f *.lo
435
436 clean-libtool:
437 -rm -rf .libs _libs
438
439 ID: $(am__tagged_files)
440 $(am__define_uniq_tagged_files); mkid -fID $$unique
441 tags: tags-am
442 TAGS: tags
443
444 tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
445 set x; \
446 here=`pwd`; \
447 $(am__define_uniq_tagged_files); \
448 shift; \
449 if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
450 test -n "$$unique" || unique=$$empty_fix; \
451 if test $$# -gt 0; then \
452 $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
453 "$$@" $$unique; \
454 else \
455 $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
456 $$unique; \
457 fi; \
458 fi
459 ctags: ctags-am
460
461 CTAGS: ctags
462 ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
463 $(am__define_uniq_tagged_files); \
464 test -z "$(CTAGS_ARGS)$$unique" \
465 || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
466 $$unique
467
468 GTAGS:
469 here=`$(am__cd) $(top_builddir) && pwd` \
470 && $(am__cd) $(top_srcdir) \
471 && gtags -i $(GTAGS_ARGS) "$$here"
472 cscopelist: cscopelist-am
473
474 cscopelist-am: $(am__tagged_files)
475 list='$(am__tagged_files)'; \
476 case "$(srcdir)" in \
477 [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
478 *) sdir=$(subdir)/$(srcdir) ;; \
479 esac; \
480 for i in $$list; do \
481 if test -f "$$i"; then \
482 echo "$(subdir)/$$i"; \
483 else \
484 echo "$$sdir/$$i"; \
485 fi; \
486 done >> $(top_builddir)/cscope.files
487
488 distclean-tags:
489 -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
490
491 distdir: $(BUILT_SOURCES)
492 $(MAKE) $(AM_MAKEFLAGS) distdir-am
493
494 distdir-am: $(DISTFILES)
495 @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
496 topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
497 list='$(DISTFILES)'; \
498 dist_files=`for file in $$list; do echo $$file; done | \
499 sed -e "s|^$$srcdirstrip/||;t" \
500 -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
501 case $$dist_files in \
502 */*) $(MKDIR_P) `echo "$$dist_files" | \
503 sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
504 sort -u` ;; \
505 esac; \
506 for file in $$dist_files; do \
507 if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
508 if test -d $$d/$$file; then \
509 dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
510 if test -d "$(distdir)/$$file"; then \
511 find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
512 fi; \
513 if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
514 cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
515 find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
516 fi; \
517 cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
518 else \
519 test -f "$(distdir)/$$file" \
520 || cp -p $$d/$$file "$(distdir)/$$file" \
521 || exit 1; \
522 fi; \
523 done
524 check-am: all-am
525 check: check-am
526 all-am: Makefile $(PROGRAMS)
527 installdirs:
528 install: install-am
529 install-exec: install-exec-am
530 install-data: install-data-am
531 uninstall: uninstall-am
532
533 install-am: all-am
534 @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
535
536 installcheck: installcheck-am
537 install-strip:
538 if test -z '$(STRIP)'; then \
539 $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
540 install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
541 install; \
542 else \
543 $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
544 install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
545 "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
546 fi
547 mostlyclean-generic:
548
549 clean-generic:
550
551 distclean-generic:
552 -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
553 -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
554
555 maintainer-clean-generic:
556 @echo "This command is intended for maintainers to use"
557 @echo "it deletes files that may require special tools to rebuild."
558 clean: clean-am
559
560 clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \
561 mostlyclean-am
562
563 distclean: distclean-am
564 -rm -f ./$(DEPDIR)/compare-compare.Po
565 -rm -f ./$(DEPDIR)/compare-image.Po
566 -rm -f Makefile
567 distclean-am: clean-am distclean-compile distclean-generic \
568 distclean-tags
569
570 dvi: dvi-am
571
572 dvi-am:
573
574 html: html-am
575
576 html-am:
577
578 info: info-am
579
580 info-am:
581
582 install-data-am:
583
584 install-dvi: install-dvi-am
585
586 install-dvi-am:
587
588 install-exec-am:
589
590 install-html: install-html-am
591
592 install-html-am:
593
594 install-info: install-info-am
595
596 install-info-am:
597
598 install-man:
599
600 install-pdf: install-pdf-am
601
602 install-pdf-am:
603
604 install-ps: install-ps-am
605
606 install-ps-am:
607
608 installcheck-am:
609
610 maintainer-clean: maintainer-clean-am
611 -rm -f ./$(DEPDIR)/compare-compare.Po
612 -rm -f ./$(DEPDIR)/compare-image.Po
613 -rm -f Makefile
614 maintainer-clean-am: distclean-am maintainer-clean-generic
615
616 mostlyclean: mostlyclean-am
617
618 mostlyclean-am: mostlyclean-compile mostlyclean-generic \
619 mostlyclean-libtool
620
621 pdf: pdf-am
622
623 pdf-am:
624
625 ps: ps-am
626
627 ps-am:
628
629 uninstall-am:
630
631 .MAKE: install-am install-strip
632
633 .PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
634 clean-generic clean-libtool clean-noinstPROGRAMS cscopelist-am \
635 ctags ctags-am distclean distclean-compile distclean-generic \
636 distclean-libtool distclean-tags distdir dvi dvi-am html \
637 html-am info info-am install install-am install-data \
638 install-data-am install-dvi install-dvi-am install-exec \
639 install-exec-am install-html install-html-am install-info \
640 install-info-am install-man install-pdf install-pdf-am \
641 install-ps install-ps-am install-strip installcheck \
642 installcheck-am installdirs maintainer-clean \
643 maintainer-clean-generic mostlyclean mostlyclean-compile \
644 mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
645 tags tags-am uninstall uninstall-am
646
647 .PRECIOUS: Makefile
648
649
650 # Tell versions [3.59,3.63) of GNU make to not export all variables.
651 # Otherwise a system limit (for SysV at least) may be exceeded.
652 .NOEXPORT:
0 /*
1 * Copyright (C) 2017 Vabishchevich Nikolay <vabnick@gmail.com>
2 *
3 * This file is part of libass.
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include "image.h"
19 #include "../libass/ass.h"
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <dirent.h>
23 #include <string.h>
24
25
26 #define FFMAX(a,b) ((a) > (b) ? (a) : (b))
27 #define FFMIN(a,b) ((a) > (b) ? (b) : (a))
28
29 static void blend_image(Image8 *frame, int32_t x0, int32_t y0,
30 const ASS_Image *img)
31 {
32 int32_t x1 = img->dst_x, x_min = FFMAX(x0, x1);
33 int32_t y1 = img->dst_y, y_min = FFMAX(y0, y1);
34 x0 = x_min - x0; x1 = x_min - x1;
35 y0 = y_min - y0; y1 = y_min - y1;
36
37 int32_t w = FFMIN(x0 + frame->width, x1 + img->w);
38 int32_t h = FFMIN(y0 + frame->height, y1 + img->h);
39 if (w <= 0 || h <= 0)
40 return;
41
42 uint8_t r = img->color >> 24;
43 uint8_t g = img->color >> 16;
44 uint8_t b = img->color >> 8;
45 uint8_t a = img->color >> 0;
46
47 int32_t mul = 129 * (255 - a);
48 const int32_t offs = (int32_t) 1 << 22;
49
50 int32_t stride = 4 * frame->width;
51 uint8_t *dst = frame->buffer + y0 * stride + 4 * x0;
52 const uint8_t *src = img->bitmap + y1 * img->stride + x1;
53 for (int32_t y = 0; y < h; y++) {
54 for (int32_t x = 0; x < w; x++) {
55 int32_t k = src[x] * mul;
56 dst[4 * x + 0] -= ((dst[4 * x + 0] - r) * k + offs) >> 23;
57 dst[4 * x + 1] -= ((dst[4 * x + 1] - g) * k + offs) >> 23;
58 dst[4 * x + 2] -= ((dst[4 * x + 2] - b) * k + offs) >> 23;
59 dst[4 * x + 3] -= ((dst[4 * x + 3] - 0) * k + offs) >> 23;
60 }
61 dst += stride;
62 src += img->stride;
63 }
64 }
65
66 static void blend_all(Image8 *frame, int32_t x0, int32_t y0,
67 const ASS_Image *img)
68 {
69 uint8_t *dst = frame->buffer;
70 size_t size = (size_t) frame->width * frame->height;
71 for (size_t i = 0; i < size; i++) {
72 dst[0] = dst[1] = dst[2] = 0;
73 dst[3] = 255;
74 dst += 4;
75 }
76 for (; img; img = img->next)
77 blend_image(frame, x0, y0, img);
78 }
79
80 inline static uint16_t abs_diff(uint16_t a, uint16_t b)
81 {
82 return a > b ? a - b : b - a;
83 }
84
85 inline static uint16_t abs_diff4(const uint16_t a[4], const uint16_t b[4])
86 {
87 uint16_t res = 0;
88 for (int k = 0; k < 4; k++) {
89 uint16_t diff = abs_diff(a[k], b[k]);
90 res = FFMAX(res, diff);
91 }
92 return res;
93 }
94
95 // Calculate error visibility scale according to formula:
96 // max_pixel_value / 255 + max(max_side_gradient / 4, max_diagonal_gradient / 8).
97 static void calc_grad(const Image16 *target, uint16_t *grad)
98 {
99 const int base = 257;
100 const int border = base + 65535 / 4;
101
102 int32_t w = target->width;
103 int32_t h = target->height;
104 int32_t stride = 4 * target->width;
105
106 for (int32_t x = 0; x < w; x++)
107 *grad++ = border;
108 const uint16_t *tg = target->buffer + stride + 4;
109 for (int32_t y = 1; y < h - 1; y++) {
110 *grad++ = border;
111 for (int32_t x = 1; x < w - 1; x++) {
112 uint16_t g[8];
113 g[0] = abs_diff4(tg, tg - 4) / 4;
114 g[1] = abs_diff4(tg, tg + 4) / 4;
115 g[2] = abs_diff4(tg, tg - stride) / 4;
116 g[3] = abs_diff4(tg, tg + stride) / 4;
117 g[4] = abs_diff4(tg, tg - stride - 4) / 8;
118 g[5] = abs_diff4(tg, tg - stride + 4) / 8;
119 g[6] = abs_diff4(tg, tg + stride - 4) / 8;
120 g[7] = abs_diff4(tg, tg + stride + 4) / 8;
121 uint16_t gg = g[0];
122 for (int k = 1; k < 8; k++)
123 gg = FFMAX(gg, g[k]);
124 *grad++ = base + gg;
125 tg += 4;
126 }
127 *grad++ = border;
128 tg += 8;
129 }
130 for (int32_t x = 0; x < w; x++)
131 *grad++ = border;
132 }
133
134 static int compare1(const Image16 *target, const uint16_t *grad,
135 const ASS_Image *img, const char *path, double *result)
136 {
137 Image8 frame;
138 frame.width = target->width;
139 frame.height = target->height;
140 size_t size = (size_t) frame.width * frame.height;
141 frame.buffer = malloc(4 * size);
142 if (!frame.buffer)
143 return 0;
144
145 blend_all(&frame, 0, 0, img);
146
147 double max_err = 0;
148 const uint8_t *ptr = frame.buffer;
149 const uint16_t *tg = target->buffer;
150 for (size_t i = 0; i < size; i++) {
151 uint16_t cmp[4];
152 for (int k = 0; k < 4; k++)
153 cmp[k] = 257u * ptr[k];
154 double err = (double) abs_diff4(cmp, tg) / *grad++;
155 if (max_err < err)
156 max_err = err;
157 ptr += 4;
158 tg += 4;
159 }
160 int flag = path && !write_png8(path, &frame) ? -1 : 1;
161 free(frame.buffer);
162 *result = max_err;
163 return flag;
164 }
165
166 static int compare(const Image16 *target, const uint16_t *grad,
167 const ASS_Image *img, const char *path,
168 double *result, int scale)
169 {
170 if (scale == 1)
171 return compare1(target, grad, img, path, result);
172 int scale2 = scale * scale;
173
174 Image16 frame;
175 frame.width = target->width;
176 frame.height = target->height;
177 size_t size = (size_t) frame.width * frame.height;
178 frame.buffer = malloc(8 * size);
179 if (!frame.buffer)
180 return 0;
181
182 Image8 temp;
183 temp.width = scale * target->width;
184 temp.height = scale * target->height;
185 temp.buffer = malloc(4 * scale2 * size);
186 if (!temp.buffer) {
187 free(frame.buffer);
188 return 0;
189 }
190 blend_all(&temp, 0, 0, img);
191
192 uint16_t *dst = frame.buffer;
193 const uint8_t *src = temp.buffer;
194 int32_t stride = 4 * temp.width;
195 const uint32_t offs = ((uint32_t) 1 << 18) - 1;
196 uint32_t mul = ((uint32_t) 257 << 19) / scale2;
197 for (int32_t y = 0; y < frame.height; y++) {
198 for (int32_t x = 0; x < frame.width; x++) {
199 uint16_t res[4] = {0};
200 const uint8_t *ptr = src;
201 for (int i = 0; i < scale; i++) {
202 for (int j = 0; j < scale; j++)
203 for (int k = 0; k < 4; k++)
204 res[k] += ptr[4 * j + k];
205 ptr += stride;
206 }
207 for (int k = 0; k < 4; k++)
208 // equivalent to (257 * res[k] + (scale2 - 1) / 2) / scale2;
209 *dst++ = (res[k] * (uint64_t) mul + offs) >> 19;
210 src += 4 * scale;
211 }
212 src += (scale - 1) * stride;
213 }
214
215 free(temp.buffer);
216
217 double max_err = 0;
218 const uint16_t *ptr = frame.buffer;
219 const uint16_t *tg = target->buffer;
220 for (size_t i = 0; i < size; i++) {
221 double err = (double) abs_diff4(ptr, tg) / *grad++;
222 if (max_err < err)
223 max_err = err;
224 ptr += 4;
225 tg += 4;
226 }
227 int flag = path && !write_png16(path, &frame) ? -1 : 1;
228 free(frame.buffer);
229 *result = max_err;
230 return flag;
231 }
232
233
234 static bool load_font(ASS_Library *lib, const char *dir, const char *file)
235 {
236 char path[4096];
237 snprintf(path, sizeof(path), "%s/%s", dir, file);
238 FILE *fp = fopen(path, "rb");
239 if (!fp)
240 return false;
241
242 if (fseek(fp, 0, SEEK_END) == -1) {
243 fclose(fp);
244 return false;
245 }
246
247 long size = ftell(fp);
248 if (size <= 0 || size > (1l << 30)) {
249 fclose(fp);
250 return false;
251 }
252 rewind(fp);
253
254 char *buf = malloc(size);
255 if (!buf) {
256 fclose(fp);
257 return false;
258 }
259
260 long pos = 0;
261 while (pos < size) {
262 size_t n = fread(buf + pos, 1, size - pos, fp);
263 if (!n) {
264 free(buf);
265 fclose(fp);
266 return false;
267 }
268 pos += n;
269 }
270 fclose(fp);
271
272 printf("Loading font '%s'.\n", file);
273 ass_add_font(lib, (char *) file, buf, size);
274 free(buf);
275 return true;
276 }
277
278 static ASS_Track *load_track(ASS_Library *lib,
279 const char *dir, const char *file)
280 {
281 char path[4096];
282 snprintf(path, sizeof(path), "%s/%s.ass", dir, file);
283 ASS_Track *track = ass_read_file(lib, path, NULL);
284 if (!track) {
285 printf("Cannot load subtitle file '%s.ass'!\n", file);
286 return NULL;
287 }
288 printf("Processing '%s.ass':\n", file);
289 return track;
290 }
291
292 static bool out_of_memory()
293 {
294 printf("Not enough memory!\n");
295 return false;
296 }
297
298 static bool process_image(ASS_Renderer *renderer, ASS_Track *track,
299 const char *input, const char *output,
300 const char *file, int64_t time, int scale)
301 {
302 uint64_t tm = time;
303 unsigned msec = tm % 1000; tm /= 1000;
304 unsigned sec = tm % 60; tm /= 60;
305 unsigned min = tm % 60; tm /= 60;
306 printf(" Time %u:%02u:%02u.%03u - ", (unsigned) tm, min, sec, msec);
307
308 char path[4096];
309 snprintf(path, sizeof(path), "%s/%s", input, file);
310
311 Image16 target;
312 if (!read_png(path, &target)) {
313 printf("PNG reading failed!\n");
314 return false;
315 }
316
317 uint16_t *grad = malloc(2 * target.width * target.height);
318 if (!grad) {
319 free(target.buffer);
320 return out_of_memory();
321 }
322 calc_grad(&target, grad);
323
324 ass_set_storage_size(renderer, target.width, target.height);
325 ass_set_frame_size(renderer, scale * target.width, scale * target.height);
326 ASS_Image *img = ass_render_frame(renderer, track, time, NULL);
327
328 const char *out_file = NULL;
329 if (output) {
330 snprintf(path, sizeof(path), "%s/%s", output, file);
331 out_file = path;
332 }
333 double max_err;
334 int res = compare(&target, grad, img, out_file, &max_err, scale);
335 free(target.buffer);
336 free(grad);
337 if (!res)
338 return out_of_memory();
339 bool flag = max_err < 4;
340 printf("%.3f %s\n", max_err, flag ? (max_err < 2 ? "OK" : "BAD") : "FAIL");
341 if (res < 0)
342 printf("Cannot write PNG to file '%s'!\n", path);
343 return flag;
344 }
345
346
347 typedef struct {
348 char *name;
349 int64_t time;
350 } Item;
351
352 typedef struct {
353 size_t n_items, max_items;
354 Item *items;
355 } ItemList;
356
357 static bool init_items(ItemList *list)
358 {
359 int n = 256;
360 list->n_items = list->max_items = 0;
361 list->items = malloc(n * sizeof(Item));
362 if (!list->items)
363 return out_of_memory();
364 list->max_items = n;
365 return true;
366 }
367
368 static bool add_item(ItemList *list)
369 {
370 if (list->n_items < list->max_items)
371 return true;
372
373 int n = 2 * list->max_items;
374 Item *next = realloc(list->items, n * sizeof(Item));
375 if (!next)
376 return out_of_memory();
377 list->max_items = n;
378 list->items = next;
379 return true;
380 }
381
382 static void delete_items(ItemList *list)
383 {
384 for (size_t i = 0; i < list->n_items; i++)
385 free(list->items[i].name);
386 free(list->items);
387 }
388
389 static int item_compare(const void *ptr1, const void *ptr2)
390 {
391 const Item *e1 = ptr1, *e2 = ptr2;
392 int cmp = strcmp(e1->name, e2->name);
393 if (cmp)
394 return cmp;
395 if (e1->time > e2->time)
396 return +1;
397 if (e1->time < e2->time)
398 return -1;
399 return 0;
400 }
401
402
403 static bool add_sub_item(ItemList *list, const char *file, int len)
404 {
405 if (!add_item(list))
406 return false;
407
408 Item *item = &list->items[list->n_items];
409 item->name = strndup(file, len);
410 if (!item->name)
411 return out_of_memory();
412 item->time = -1;
413 list->n_items++;
414 return true;
415 }
416
417 static bool add_img_item(ItemList *list, const char *file, int len)
418 {
419 // Parse image name:
420 // <subtitle_name>-<time_in_msec>.png
421
422 int pos = len, first = len;
423 while (true) {
424 if (!pos--)
425 return true;
426 if (file[pos] == '-')
427 break;
428 if (file[pos] < '0' || file[pos] > '9')
429 return true;
430 if (file[pos] != '0')
431 first = pos;
432 }
433 if (pos + 1 == len || first + 15 < len)
434 return true;
435
436 if (!add_item(list))
437 return false;
438
439 Item *item = &list->items[list->n_items];
440 item->name = strdup(file);
441 if (!item->name)
442 return out_of_memory();
443 item->name[pos] = '\0';
444 item->time = 0;
445 for (int i = first; i < len; i++)
446 item->time = 10 * item->time + (file[i] - '0');
447 list->n_items++;
448 return true;
449 }
450
451
452 static int print_usage(const char *program)
453 {
454 const char *fmt =
455 "Usage: %s [-i] <input-dir> [-o <output-dir>] [-s <scale:1-8>]\n";
456 printf(fmt, program);
457 return 1;
458 }
459
460 void msg_callback(int level, const char *fmt, va_list va, void *data)
461 {
462 if (level > 3)
463 return;
464 printf("libass: ");
465 vprintf(fmt, va);
466 printf("\n");
467 }
468
469 int main(int argc, char *argv[])
470 {
471 enum {
472 INPUT, OUTPUT, SCALE
473 };
474 int pos[3] = {0};
475 for (int i = 1; i < argc; i++) {
476 if (argv[i][0] != '-') {
477 if (pos[INPUT])
478 return print_usage(argv[0]);
479 pos[INPUT] = i;
480 continue;
481 }
482 int index;
483 switch (argv[i][1]) {
484 case 'i': index = INPUT; break;
485 case 'o': index = OUTPUT; break;
486 case 's': index = SCALE; break;
487 default: return print_usage(argv[0]);
488 }
489 if (argv[i][2] || ++i >= argc || pos[index])
490 return print_usage(argv[0]);
491 pos[index] = i;
492 }
493 if (!pos[INPUT])
494 return print_usage(argv[0]);
495
496 int scale = 1;
497 if (pos[SCALE]) {
498 const char *arg = argv[pos[SCALE]];
499 if (arg[0] < '1' || arg[0] > '8' || arg[1]) {
500 printf("Invalid scale value, should be 1-8!\n");
501 return 1;
502 }
503 scale = arg[0] - '0';
504 }
505
506 const char *input = argv[pos[INPUT]];
507 DIR *dir = opendir(input);
508 if (!dir) {
509 printf("Cannot open input directory '%s'!\n", input);
510 return 1;
511 }
512
513 const char *output = NULL;
514 if (pos[OUTPUT]) {
515 output = argv[pos[OUTPUT]];
516 struct stat st;
517 if (stat(output, &st)) {
518 if (mkdir(output, 0755)) {
519 printf("Cannot create output directory '%s'!\n", output);
520 closedir(dir);
521 return 1;
522 }
523 } else if (!(st.st_mode & S_IFDIR)) {
524 printf("Invalid output directory '%s'!\n", output);
525 closedir(dir);
526 return 1;
527 }
528 }
529
530 ASS_Library *lib = ass_library_init();
531 if (!lib) {
532 printf("ass_library_init failed!\n");
533 closedir(dir);
534 return 1;
535 }
536 ass_set_message_cb(lib, msg_callback, NULL);
537
538 ItemList list;
539 if (!init_items(&list)) {
540 ass_library_done(lib);
541 closedir(dir);
542 return 1;
543 }
544
545 while (true) {
546 struct dirent *file = readdir(dir);
547 if (!file)
548 break;
549 const char *name = file->d_name;
550 if (name[0] == '.')
551 continue;
552 const char *ext = strrchr(name + 1, '.');
553 if (!ext)
554 continue;
555
556 if (!strcmp(ext, ".png")) {
557 if (add_img_item(&list, name, ext - name))
558 continue;
559 } else if (!strcmp(ext, ".ass")) {
560 if (add_sub_item(&list, name, ext - name))
561 continue;
562 } else if (!strcmp(ext, ".ttf") || !strcmp(ext, ".otf") || !strcmp(ext, ".pfb")) {
563 if (load_font(lib, input, name))
564 continue;
565 printf("Cannot load font '%s'!\n", name);
566 } else {
567 continue;
568 }
569 delete_items(&list);
570 ass_library_done(lib);
571 closedir(dir);
572 return 1;
573 }
574 closedir(dir);
575
576 ASS_Renderer *renderer = ass_renderer_init(lib);
577 if (!renderer) {
578 printf("ass_renderer_init failed!\n");
579 delete_items(&list);
580 ass_library_done(lib);
581 return 1;
582 }
583 ass_set_fonts(renderer, NULL, NULL, ASS_FONTPROVIDER_NONE, NULL, 0);
584
585 int prefix;
586 const char *prev = "";
587 ASS_Track *track = NULL;
588 unsigned total = 0, good = 0;
589 qsort(list.items, list.n_items, sizeof(Item), item_compare);
590 for (size_t i = 0; i < list.n_items; i++) {
591 if (strcmp(prev, list.items[i].name)) {
592 if (track)
593 ass_free_track(track);
594 prev = list.items[i].name;
595 prefix = strlen(prev);
596 if (list.items[i].time < 0)
597 track = load_track(lib, input, prev);
598 else {
599 printf("Missing subtitle file '%s.ass'!\n", prev);
600 track = NULL;
601 total++;
602 }
603 continue;
604 }
605
606 total++;
607 if (!track)
608 continue;
609 char *name = list.items[i].name;
610 name[prefix] = '-'; // restore initial filename
611 if (process_image(renderer, track, input, output,
612 name, list.items[i].time, scale))
613 good++;
614 }
615 if (track)
616 ass_free_track(track);
617 delete_items(&list);
618 ass_renderer_done(renderer);
619 ass_library_done(lib);
620
621 if (good < total) {
622 printf("Only %u of %u images have passed test\n", good, total);
623 return 1;
624 }
625 printf("All %u images have passed test\n", total);
626 return 0;
627 }
0 /*
1 * Copyright (C) 2017 Vabishchevich Nikolay <vabnick@gmail.com>
2 *
3 * This file is part of libass.
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include "image.h"
19 #include <png.h>
20
21
22 bool read_png(const char *path, Image16 *img)
23 {
24 FILE *fp = fopen(path, "rb");
25 if (!fp)
26 return false;
27
28 png_structp png =
29 png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
30 if (!png) {
31 fclose(fp);
32 return false;
33 }
34
35 png_infop info = png_create_info_struct(png);
36 if (!info) {
37 png_destroy_read_struct(&png, NULL, NULL);
38 fclose(fp);
39 return false;
40 }
41
42 png_byte *volatile buf = NULL;
43 png_byte **volatile rows = NULL;
44 if (setjmp(png_jmpbuf(png))) {
45 free(buf);
46 free(rows);
47 png_destroy_read_struct(&png, &info, NULL);
48 fclose(fp);
49 return false;
50 }
51
52 png_init_io(png, fp);
53 png_read_info(png, info);
54
55 uint32_t w = png_get_image_width(png, info);
56 uint32_t h = png_get_image_height(png, info);
57 int type = png_get_color_type(png, info);
58 int depth = png_get_bit_depth(png, info);
59
60 if (w > 0xFFFF || h > 0xFFFF || type != PNG_COLOR_TYPE_RGBA) {
61 png_destroy_read_struct(&png, &info, NULL);
62 fclose(fp);
63 return false;
64 }
65
66 ptrdiff_t stride = 8 * w;
67 buf = malloc(stride * h);
68 rows = malloc(h * sizeof(png_byte *));
69 if (!buf || !rows) {
70 free(buf);
71 free(rows);
72 png_destroy_read_struct(&png, &info, NULL);
73 fclose(fp);
74 return false;
75 }
76
77 png_byte *ptr = buf;
78 ptrdiff_t half = 4 * w;
79 if (depth == 8)
80 ptr += half;
81 else
82 png_set_swap(png);
83
84 for (uint32_t i = 0; i < h; i++) {
85 rows[i] = ptr;
86 ptr += stride;
87 }
88
89 png_read_image(png, rows);
90 png_read_end(png, NULL);
91
92 free(rows);
93 png_destroy_read_struct(&png, &info, NULL);
94 fclose(fp);
95
96 // convert to premultiplied with inverted alpha
97 if (depth == 8) {
98 uint8_t *ptr = (uint8_t *) buf;
99 for (uint32_t y = 0; y < h; y++) {
100 for (uint32_t x = 0; x < w; x++) {
101 uint8_t r = ptr[half + 4 * x + 0];
102 uint8_t g = ptr[half + 4 * x + 1];
103 uint8_t b = ptr[half + 4 * x + 2];
104 uint8_t a = ptr[half + 4 * x + 3];
105 uint16_t ra = (uint16_t) r * a;
106 uint16_t ga = (uint16_t) g * a;
107 uint16_t ba = (uint16_t) b * a;
108 ptr[8 * x + 0] = ptr[8 * x + 1] = (ra + (ra >> 8) + 128) >> 8;
109 ptr[8 * x + 2] = ptr[8 * x + 3] = (ga + (ga >> 8) + 128) >> 8;
110 ptr[8 * x + 4] = ptr[8 * x + 5] = (ba + (ba >> 8) + 128) >> 8;
111 ptr[8 * x + 6] = ptr[8 * x + 7] = ~a;
112 }
113 ptr += stride;
114 }
115 } else {
116 uint16_t *ptr = (uint16_t *) buf;
117 for (uint32_t y = 0; y < h; y++) {
118 for (uint32_t x = 0; x < w; x++) {
119 uint16_t r = ptr[4 * x + 0];
120 uint16_t g = ptr[4 * x + 1];
121 uint16_t b = ptr[4 * x + 2];
122 uint16_t a = ptr[4 * x + 3];
123 uint32_t ra = (uint32_t) r * a;
124 uint32_t ga = (uint32_t) g * a;
125 uint32_t ba = (uint32_t) b * a;
126 ptr[4 * x + 0] = (ra + (ra >> 16) + (1 << 15)) >> 16;
127 ptr[4 * x + 1] = (ga + (ga >> 16) + (1 << 15)) >> 16;
128 ptr[4 * x + 2] = (ba + (ba >> 16) + (1 << 15)) >> 16;
129 ptr[4 * x + 3] = ~a;
130 }
131 ptr += half;
132 }
133 }
134
135 img->width = w;
136 img->height = h;
137 img->buffer = (uint16_t *) buf;
138 return true;
139 }
140
141 static bool write_png(const char *path, uint32_t width, uint32_t height,
142 ptrdiff_t stride, const void *buffer, int depth)
143 {
144 FILE *fp = fopen(path, "wb");
145 if (!fp)
146 return false;
147
148 png_structp png =
149 png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
150 if (!png) {
151 fclose(fp);
152 return false;
153 }
154
155 png_infop info = png_create_info_struct(png);
156 if (!info) {
157 png_destroy_write_struct(&png, NULL);
158 fclose(fp);
159 return false;
160 }
161
162 png_byte **rows = malloc(height * sizeof(png_byte *));
163 if (!rows) {
164 png_destroy_write_struct(&png, &info);
165 fclose(fp);
166 return false;
167 }
168
169 png_byte *ptr = (png_byte *) buffer;
170 for (uint32_t i = 0; i < height; i++) {
171 rows[i] = (png_byte *) ptr;
172 ptr += stride;
173 }
174
175 if (setjmp(png_jmpbuf(png))) {
176 free(rows);
177 png_destroy_write_struct(&png, &info);
178 fclose(fp);
179 return false;
180 }
181
182 png_init_io(png, fp);
183 png_set_IHDR(png, info, width, height, depth,
184 PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE,
185 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
186 png_write_info(png, info);
187
188 if (depth > 8)
189 png_set_swap(png);
190 png_write_image(png, rows);
191 png_write_end(png, NULL);
192
193 free(rows);
194 png_destroy_write_struct(&png, &info);
195 fclose(fp);
196 return true;
197 }
198
199 bool write_png8(const char *path, Image8 *img)
200 {
201 uint8_t *ptr = img->buffer;
202 size_t size = (size_t) img->width * img->height;
203 for (size_t i = 0; i < size; i++) {
204 uint8_t alpha = ~ptr[3];
205 if (alpha) {
206 const uint32_t offs = (uint32_t) 1 << 15;
207 uint32_t inv = ((uint32_t) 255 << 16) / alpha + 1;
208 // equivalent to (255 * ptr[k] + alpha / 2) / alpha
209 ptr[0] = (ptr[0] * inv + offs) >> 16;
210 ptr[1] = (ptr[1] * inv + offs) >> 16;
211 ptr[2] = (ptr[2] * inv + offs) >> 16;
212 }
213 ptr[3] = alpha;
214 ptr += 4;
215 }
216 return write_png(path, img->width, img->height,
217 4 * img->width, img->buffer, 8);
218 }
219
220 bool write_png16(const char *path, Image16 *img)
221 {
222 uint16_t *ptr = img->buffer;
223 size_t size = (size_t) img->width * img->height;
224 for (size_t i = 0; i < size; i++) {
225 uint16_t alpha = ~ptr[3];
226 if (alpha) {
227 const uint64_t offs = (uint64_t) 1 << 32;
228 uint64_t inv = ((uint64_t) 65535 << 33) / alpha + 1;
229 // equivalent to (65535 * ptr[k] + alpha / 2) / alpha
230 ptr[0] = (ptr[0] * inv + offs) >> 33;
231 ptr[1] = (ptr[1] * inv + offs) >> 33;
232 ptr[2] = (ptr[2] * inv + offs) >> 33;
233 }
234 ptr[3] = alpha;
235 ptr += 4;
236 }
237 return write_png(path, img->width, img->height,
238 8 * img->width, img->buffer, 16);
239 }
0 /*
1 * Copyright (C) 2017 Vabishchevich Nikolay <vabnick@gmail.com>
2 *
3 * This file is part of libass.
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #ifndef COMPARE_IMAGE_H
19 #define COMPARE_IMAGE_H
20
21 #include <stddef.h>
22 #include <stdlib.h>
23 #include <stdbool.h>
24 #include <stdint.h>
25
26 typedef struct {
27 int32_t width, height;
28 uint8_t *buffer;
29 } Image8;
30
31 typedef struct {
32 int32_t width, height;
33 uint16_t *buffer;
34 } Image16;
35
36 bool read_png(const char *path, Image16 *img);
37 bool write_png8(const char *path, Image8 *img);
38 bool write_png16(const char *path, Image16 *img);
39
40 #endif /* COMPARE_IMAGE_H */
00 #! /bin/sh
11 # Wrapper for compilers which do not understand '-c -o'.
22
3 scriptversion=2012-10-14.11; # UTC
4
5 # Copyright (C) 1999-2014 Free Software Foundation, Inc.
3 scriptversion=2018-03-07.03; # UTC
4
5 # Copyright (C) 1999-2018 Free Software Foundation, Inc.
66 # Written by Tom Tromey <tromey@cygnus.com>.
77 #
88 # This program is free software; you can redistribute it and/or modify
1616 # GNU General Public License for more details.
1717 #
1818 # You should have received a copy of the GNU General Public License
19 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 # along with this program. If not, see <https://www.gnu.org/licenses/>.
2020
2121 # As a special exception to the GNU General Public License, if you
2222 # distribute this file as part of a program that contains a
254254 echo "compile $scriptversion"
255255 exit $?
256256 ;;
257 cl | *[/\\]cl | cl.exe | *[/\\]cl.exe )
257 cl | *[/\\]cl | cl.exe | *[/\\]cl.exe | \
258 icl | *[/\\]icl | icl.exe | *[/\\]icl.exe )
258259 func_cl_wrapper "$@" # Doesn't return...
259260 ;;
260261 esac
338339 # Local Variables:
339340 # mode: shell-script
340341 # sh-indentation: 2
341 # eval: (add-hook 'write-file-hooks 'time-stamp)
342 # eval: (add-hook 'before-save-hook 'time-stamp)
342343 # time-stamp-start: "scriptversion="
343344 # time-stamp-format: "%:y-%02m-%02d.%02H"
344 # time-stamp-time-zone: "UTC"
345 # time-stamp-time-zone: "UTC0"
345346 # time-stamp-end: "; # UTC"
346347 # End:
00 #! /bin/sh
11 # Attempt to guess a canonical system name.
2 # Copyright 1992-2014 Free Software Foundation, Inc.
3
4 timestamp='2014-11-04'
2 # Copyright 1992-2018 Free Software Foundation, Inc.
3
4 timestamp='2018-03-08'
55
66 # This file is free software; you can redistribute it and/or modify it
77 # under the terms of the GNU General Public License as published by
1414 # General Public License for more details.
1515 #
1616 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, see <http://www.gnu.org/licenses/>.
17 # along with this program; if not, see <https://www.gnu.org/licenses/>.
1818 #
1919 # As a special exception to the GNU General Public License, if you
2020 # distribute this file as part of a program that contains a
2626 # Originally written by Per Bothner; maintained since 2000 by Ben Elliston.
2727 #
2828 # You can get the latest version of this script from:
29 # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
29 # https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess
3030 #
3131 # Please send patches to <config-patches@gnu.org>.
3232
3838
3939 Output the configuration name of the system \`$me' is run on.
4040
41 Operation modes:
41 Options:
4242 -h, --help print this help, then exit
4343 -t, --time-stamp print date of last modification, then exit
4444 -v, --version print version number, then exit
4949 GNU config.guess ($timestamp)
5050
5151 Originally written by Per Bothner.
52 Copyright 1992-2014 Free Software Foundation, Inc.
52 Copyright 1992-2018 Free Software Foundation, Inc.
5353
5454 This is free software; see the source for copying conditions. There is NO
5555 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
106106 dummy=$tmp/dummy ;
107107 tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
108108 case $CC_FOR_BUILD,$HOST_CC,$CC in
109 ,,) echo "int x;" > $dummy.c ;
109 ,,) echo "int x;" > "$dummy.c" ;
110110 for c in cc gcc c89 c99 ; do
111 if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
111 if ($c -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then
112112 CC_FOR_BUILD="$c"; break ;
113113 fi ;
114114 done ;
131131 UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
132132 UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
133133
134 case "${UNAME_SYSTEM}" in
134 case "$UNAME_SYSTEM" in
135135 Linux|GNU|GNU/*)
136136 # If the system lacks a compiler, then just pick glibc.
137137 # We could probably try harder.
138138 LIBC=gnu
139139
140 eval $set_cc_for_build
141 cat <<-EOF > $dummy.c
140 eval "$set_cc_for_build"
141 cat <<-EOF > "$dummy.c"
142142 #include <features.h>
143143 #if defined(__UCLIBC__)
144144 LIBC=uclibc
148148 LIBC=gnu
149149 #endif
150150 EOF
151 eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`
151 eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`"
152
153 # If ldd exists, use it to detect musl libc.
154 if command -v ldd >/dev/null && \
155 ldd --version 2>&1 | grep -q ^musl
156 then
157 LIBC=musl
158 fi
152159 ;;
153160 esac
154161
155162 # Note: order is significant - the case branches are not exclusive.
156163
157 case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
164 case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in
158165 *:NetBSD:*:*)
159166 # NetBSD (nbsd) targets should (where applicable) match one or
160167 # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*,
167174 # Note: NetBSD doesn't particularly care about the vendor
168175 # portion of the name. We always set it to "unknown".
169176 sysctl="sysctl -n hw.machine_arch"
170 UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
171 /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
172 case "${UNAME_MACHINE_ARCH}" in
177 UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \
178 "/sbin/$sysctl" 2>/dev/null || \
179 "/usr/sbin/$sysctl" 2>/dev/null || \
180 echo unknown)`
181 case "$UNAME_MACHINE_ARCH" in
173182 armeb) machine=armeb-unknown ;;
174183 arm*) machine=arm-unknown ;;
175184 sh3el) machine=shl-unknown ;;
176185 sh3eb) machine=sh-unknown ;;
177186 sh5el) machine=sh5le-unknown ;;
178 *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
187 earmv*)
188 arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'`
189 endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'`
190 machine="${arch}${endian}"-unknown
191 ;;
192 *) machine="$UNAME_MACHINE_ARCH"-unknown ;;
179193 esac
180194 # The Operating System including object format, if it has switched
181 # to ELF recently, or will in the future.
182 case "${UNAME_MACHINE_ARCH}" in
195 # to ELF recently (or will in the future) and ABI.
196 case "$UNAME_MACHINE_ARCH" in
197 earm*)
198 os=netbsdelf
199 ;;
183200 arm*|i386|m68k|ns32k|sh3*|sparc|vax)
184 eval $set_cc_for_build
201 eval "$set_cc_for_build"
185202 if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
186203 | grep -q __ELF__
187204 then
196213 os=netbsd
197214 ;;
198215 esac
216 # Determine ABI tags.
217 case "$UNAME_MACHINE_ARCH" in
218 earm*)
219 expr='s/^earmv[0-9]/-eabi/;s/eb$//'
220 abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"`
221 ;;
222 esac
199223 # The OS release
200224 # Debian GNU/NetBSD machines have a different userland, and
201225 # thus, need a distinct triplet. However, they do not need
202226 # kernel version information, so it can be replaced with a
203227 # suitable tag, in the style of linux-gnu.
204 case "${UNAME_VERSION}" in
228 case "$UNAME_VERSION" in
205229 Debian*)
206230 release='-gnu'
207231 ;;
208232 *)
209 release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
233 release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2`
210234 ;;
211235 esac
212236 # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
213237 # contains redundant information, the shorter form:
214238 # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
215 echo "${machine}-${os}${release}"
239 echo "$machine-${os}${release}${abi}"
216240 exit ;;
217241 *:Bitrig:*:*)
218242 UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`
219 echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE}
243 echo "$UNAME_MACHINE_ARCH"-unknown-bitrig"$UNAME_RELEASE"
220244 exit ;;
221245 *:OpenBSD:*:*)
222246 UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
223 echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE}
247 echo "$UNAME_MACHINE_ARCH"-unknown-openbsd"$UNAME_RELEASE"
248 exit ;;
249 *:LibertyBSD:*:*)
250 UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'`
251 echo "$UNAME_MACHINE_ARCH"-unknown-libertybsd"$UNAME_RELEASE"
252 exit ;;
253 *:MidnightBSD:*:*)
254 echo "$UNAME_MACHINE"-unknown-midnightbsd"$UNAME_RELEASE"
224255 exit ;;
225256 *:ekkoBSD:*:*)
226 echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}
257 echo "$UNAME_MACHINE"-unknown-ekkobsd"$UNAME_RELEASE"
227258 exit ;;
228259 *:SolidBSD:*:*)
229 echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE}
260 echo "$UNAME_MACHINE"-unknown-solidbsd"$UNAME_RELEASE"
230261 exit ;;
231262 macppc:MirBSD:*:*)
232 echo powerpc-unknown-mirbsd${UNAME_RELEASE}
263 echo powerpc-unknown-mirbsd"$UNAME_RELEASE"
233264 exit ;;
234265 *:MirBSD:*:*)
235 echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
236 exit ;;
266 echo "$UNAME_MACHINE"-unknown-mirbsd"$UNAME_RELEASE"
267 exit ;;
268 *:Sortix:*:*)
269 echo "$UNAME_MACHINE"-unknown-sortix
270 exit ;;
271 *:Redox:*:*)
272 echo "$UNAME_MACHINE"-unknown-redox
273 exit ;;
274 mips:OSF1:*.*)
275 echo mips-dec-osf1
276 exit ;;
237277 alpha:OSF1:*:*)
238278 case $UNAME_RELEASE in
239279 *4.0)
250290 ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1`
251291 case "$ALPHA_CPU_TYPE" in
252292 "EV4 (21064)")
253 UNAME_MACHINE="alpha" ;;
293 UNAME_MACHINE=alpha ;;
254294 "EV4.5 (21064)")
255 UNAME_MACHINE="alpha" ;;
295 UNAME_MACHINE=alpha ;;
256296 "LCA4 (21066/21068)")
257 UNAME_MACHINE="alpha" ;;
297 UNAME_MACHINE=alpha ;;
258298 "EV5 (21164)")
259 UNAME_MACHINE="alphaev5" ;;
299 UNAME_MACHINE=alphaev5 ;;
260300 "EV5.6 (21164A)")
261 UNAME_MACHINE="alphaev56" ;;
301 UNAME_MACHINE=alphaev56 ;;
262302 "EV5.6 (21164PC)")
263 UNAME_MACHINE="alphapca56" ;;
303 UNAME_MACHINE=alphapca56 ;;
264304 "EV5.7 (21164PC)")
265 UNAME_MACHINE="alphapca57" ;;
305 UNAME_MACHINE=alphapca57 ;;
266306 "EV6 (21264)")
267 UNAME_MACHINE="alphaev6" ;;
307 UNAME_MACHINE=alphaev6 ;;
268308 "EV6.7 (21264A)")
269 UNAME_MACHINE="alphaev67" ;;
309 UNAME_MACHINE=alphaev67 ;;
270310 "EV6.8CB (21264C)")
271 UNAME_MACHINE="alphaev68" ;;
311 UNAME_MACHINE=alphaev68 ;;
272312 "EV6.8AL (21264B)")
273 UNAME_MACHINE="alphaev68" ;;
313 UNAME_MACHINE=alphaev68 ;;
274314 "EV6.8CX (21264D)")
275 UNAME_MACHINE="alphaev68" ;;
315 UNAME_MACHINE=alphaev68 ;;
276316 "EV6.9A (21264/EV69A)")
277 UNAME_MACHINE="alphaev69" ;;
317 UNAME_MACHINE=alphaev69 ;;
278318 "EV7 (21364)")
279 UNAME_MACHINE="alphaev7" ;;
319 UNAME_MACHINE=alphaev7 ;;
280320 "EV7.9 (21364A)")
281 UNAME_MACHINE="alphaev79" ;;
321 UNAME_MACHINE=alphaev79 ;;
282322 esac
283323 # A Pn.n version is a patched version.
284324 # A Vn.n version is a released version.
285325 # A Tn.n version is a released field test version.
286326 # A Xn.n version is an unreleased experimental baselevel.
287327 # 1.2 uses "1.2" for uname -r.
288 echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
328 echo "$UNAME_MACHINE"-dec-osf"`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`"
289329 # Reset EXIT trap before exiting to avoid spurious non-zero exit code.
290330 exitcode=$?
291331 trap '' 0
292332 exit $exitcode ;;
293 Alpha\ *:Windows_NT*:*)
294 # How do we know it's Interix rather than the generic POSIX subsystem?
295 # Should we change UNAME_MACHINE based on the output of uname instead
296 # of the specific Alpha model?
297 echo alpha-pc-interix
298 exit ;;
299 21064:Windows_NT:50:3)
300 echo alpha-dec-winnt3.5
301 exit ;;
302333 Amiga*:UNIX_System_V:4.0:*)
303334 echo m68k-unknown-sysv4
304335 exit ;;
305336 *:[Aa]miga[Oo][Ss]:*:*)
306 echo ${UNAME_MACHINE}-unknown-amigaos
337 echo "$UNAME_MACHINE"-unknown-amigaos
307338 exit ;;
308339 *:[Mm]orph[Oo][Ss]:*:*)
309 echo ${UNAME_MACHINE}-unknown-morphos
340 echo "$UNAME_MACHINE"-unknown-morphos
310341 exit ;;
311342 *:OS/390:*:*)
312343 echo i370-ibm-openedition
318349 echo powerpc-ibm-os400
319350 exit ;;
320351 arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
321 echo arm-acorn-riscix${UNAME_RELEASE}
352 echo arm-acorn-riscix"$UNAME_RELEASE"
322353 exit ;;
323354 arm*:riscos:*:*|arm*:RISCOS:*:*)
324355 echo arm-unknown-riscos
345376 sparc) echo sparc-icl-nx7; exit ;;
346377 esac ;;
347378 s390x:SunOS:*:*)
348 echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
379 echo "$UNAME_MACHINE"-ibm-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`"
349380 exit ;;
350381 sun4H:SunOS:5.*:*)
351 echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
382 echo sparc-hal-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
352383 exit ;;
353384 sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
354 echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
385 echo sparc-sun-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`"
355386 exit ;;
356387 i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
357 echo i386-pc-auroraux${UNAME_RELEASE}
388 echo i386-pc-auroraux"$UNAME_RELEASE"
358389 exit ;;
359390 i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
360 eval $set_cc_for_build
361 SUN_ARCH="i386"
391 eval "$set_cc_for_build"
392 SUN_ARCH=i386
362393 # If there is a compiler, see if it is configured for 64-bit objects.
363394 # Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
364395 # This test works for both compilers.
365 if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
396 if [ "$CC_FOR_BUILD" != no_compiler_found ]; then
366397 if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
367 (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
398 (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
368399 grep IS_64BIT_ARCH >/dev/null
369400 then
370 SUN_ARCH="x86_64"
401 SUN_ARCH=x86_64
371402 fi
372403 fi
373 echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
404 echo "$SUN_ARCH"-pc-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
374405 exit ;;
375406 sun4*:SunOS:6*:*)
376407 # According to config.sub, this is the proper way to canonicalize
377408 # SunOS6. Hard to guess exactly what SunOS6 will be like, but
378409 # it's likely to be more like Solaris than SunOS4.
379 echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
410 echo sparc-sun-solaris3"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
380411 exit ;;
381412 sun4*:SunOS:*:*)
382413 case "`/usr/bin/arch -k`" in
385416 ;;
386417 esac
387418 # Japanese Language versions have a version number like `4.1.3-JL'.
388 echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
419 echo sparc-sun-sunos"`echo "$UNAME_RELEASE"|sed -e 's/-/_/'`"
389420 exit ;;
390421 sun3*:SunOS:*:*)
391 echo m68k-sun-sunos${UNAME_RELEASE}
422 echo m68k-sun-sunos"$UNAME_RELEASE"
392423 exit ;;
393424 sun*:*:4.2BSD:*)
394425 UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
395 test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
426 test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3
396427 case "`/bin/arch`" in
397428 sun3)
398 echo m68k-sun-sunos${UNAME_RELEASE}
429 echo m68k-sun-sunos"$UNAME_RELEASE"
399430 ;;
400431 sun4)
401 echo sparc-sun-sunos${UNAME_RELEASE}
432 echo sparc-sun-sunos"$UNAME_RELEASE"
402433 ;;
403434 esac
404435 exit ;;
405436 aushp:SunOS:*:*)
406 echo sparc-auspex-sunos${UNAME_RELEASE}
437 echo sparc-auspex-sunos"$UNAME_RELEASE"
407438 exit ;;
408439 # The situation for MiNT is a little confusing. The machine name
409440 # can be virtually everything (everything which is not
414445 # MiNT. But MiNT is downward compatible to TOS, so this should
415446 # be no problem.
416447 atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
417 echo m68k-atari-mint${UNAME_RELEASE}
448 echo m68k-atari-mint"$UNAME_RELEASE"
418449 exit ;;
419450 atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
420 echo m68k-atari-mint${UNAME_RELEASE}
451 echo m68k-atari-mint"$UNAME_RELEASE"
421452 exit ;;
422453 *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
423 echo m68k-atari-mint${UNAME_RELEASE}
454 echo m68k-atari-mint"$UNAME_RELEASE"
424455 exit ;;
425456 milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
426 echo m68k-milan-mint${UNAME_RELEASE}
457 echo m68k-milan-mint"$UNAME_RELEASE"
427458 exit ;;
428459 hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
429 echo m68k-hades-mint${UNAME_RELEASE}
460 echo m68k-hades-mint"$UNAME_RELEASE"
430461 exit ;;
431462 *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
432 echo m68k-unknown-mint${UNAME_RELEASE}
463 echo m68k-unknown-mint"$UNAME_RELEASE"
433464 exit ;;
434465 m68k:machten:*:*)
435 echo m68k-apple-machten${UNAME_RELEASE}
466 echo m68k-apple-machten"$UNAME_RELEASE"
436467 exit ;;
437468 powerpc:machten:*:*)
438 echo powerpc-apple-machten${UNAME_RELEASE}
469 echo powerpc-apple-machten"$UNAME_RELEASE"
439470 exit ;;
440471 RISC*:Mach:*:*)
441472 echo mips-dec-mach_bsd4.3
442473 exit ;;
443474 RISC*:ULTRIX:*:*)
444 echo mips-dec-ultrix${UNAME_RELEASE}
475 echo mips-dec-ultrix"$UNAME_RELEASE"
445476 exit ;;
446477 VAX*:ULTRIX*:*:*)
447 echo vax-dec-ultrix${UNAME_RELEASE}
478 echo vax-dec-ultrix"$UNAME_RELEASE"
448479 exit ;;
449480 2020:CLIX:*:* | 2430:CLIX:*:*)
450 echo clipper-intergraph-clix${UNAME_RELEASE}
481 echo clipper-intergraph-clix"$UNAME_RELEASE"
451482 exit ;;
452483 mips:*:*:UMIPS | mips:*:*:RISCos)
453 eval $set_cc_for_build
454 sed 's/^ //' << EOF >$dummy.c
484 eval "$set_cc_for_build"
485 sed 's/^ //' << EOF > "$dummy.c"
455486 #ifdef __cplusplus
456487 #include <stdio.h> /* for printf() prototype */
457488 int main (int argc, char *argv[]) {
460491 #endif
461492 #if defined (host_mips) && defined (MIPSEB)
462493 #if defined (SYSTYPE_SYSV)
463 printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
494 printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0);
464495 #endif
465496 #if defined (SYSTYPE_SVR4)
466 printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
497 printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0);
467498 #endif
468499 #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
469 printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
500 printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0);
470501 #endif
471502 #endif
472503 exit (-1);
473504 }
474505 EOF
475 $CC_FOR_BUILD -o $dummy $dummy.c &&
476 dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` &&
477 SYSTEM_NAME=`$dummy $dummyarg` &&
506 $CC_FOR_BUILD -o "$dummy" "$dummy.c" &&
507 dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` &&
508 SYSTEM_NAME=`"$dummy" "$dummyarg"` &&
478509 { echo "$SYSTEM_NAME"; exit; }
479 echo mips-mips-riscos${UNAME_RELEASE}
510 echo mips-mips-riscos"$UNAME_RELEASE"
480511 exit ;;
481512 Motorola:PowerMAX_OS:*:*)
482513 echo powerpc-motorola-powermax
502533 AViiON:dgux:*:*)
503534 # DG/UX returns AViiON for all architectures
504535 UNAME_PROCESSOR=`/usr/bin/uname -p`
505 if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
536 if [ "$UNAME_PROCESSOR" = mc88100 ] || [ "$UNAME_PROCESSOR" = mc88110 ]
506537 then
507 if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
508 [ ${TARGET_BINARY_INTERFACE}x = x ]
538 if [ "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx ] || \
539 [ "$TARGET_BINARY_INTERFACE"x = x ]
509540 then
510 echo m88k-dg-dgux${UNAME_RELEASE}
541 echo m88k-dg-dgux"$UNAME_RELEASE"
511542 else
512 echo m88k-dg-dguxbcs${UNAME_RELEASE}
543 echo m88k-dg-dguxbcs"$UNAME_RELEASE"
513544 fi
514545 else
515 echo i586-dg-dgux${UNAME_RELEASE}
546 echo i586-dg-dgux"$UNAME_RELEASE"
516547 fi
517548 exit ;;
518549 M88*:DolphinOS:*:*) # DolphinOS (SVR3)
529560 echo m68k-tektronix-bsd
530561 exit ;;
531562 *:IRIX*:*:*)
532 echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
563 echo mips-sgi-irix"`echo "$UNAME_RELEASE"|sed -e 's/-/_/g'`"
533564 exit ;;
534565 ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
535566 echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id
541572 if [ -x /usr/bin/oslevel ] ; then
542573 IBM_REV=`/usr/bin/oslevel`
543574 else
544 IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
545 fi
546 echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
575 IBM_REV="$UNAME_VERSION.$UNAME_RELEASE"
576 fi
577 echo "$UNAME_MACHINE"-ibm-aix"$IBM_REV"
547578 exit ;;
548579 *:AIX:2:3)
549580 if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
550 eval $set_cc_for_build
551 sed 's/^ //' << EOF >$dummy.c
581 eval "$set_cc_for_build"
582 sed 's/^ //' << EOF > "$dummy.c"
552583 #include <sys/systemcfg.h>
553584
554585 main()
559590 exit(0);
560591 }
561592 EOF
562 if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy`
593 if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"`
563594 then
564595 echo "$SYSTEM_NAME"
565596 else
573604 exit ;;
574605 *:AIX:*:[4567])
575606 IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
576 if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
607 if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then
577608 IBM_ARCH=rs6000
578609 else
579610 IBM_ARCH=powerpc
582613 IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc |
583614 awk -F: '{ print $3 }' | sed s/[0-9]*$/0/`
584615 else
585 IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
586 fi
587 echo ${IBM_ARCH}-ibm-aix${IBM_REV}
616 IBM_REV="$UNAME_VERSION.$UNAME_RELEASE"
617 fi
618 echo "$IBM_ARCH"-ibm-aix"$IBM_REV"
588619 exit ;;
589620 *:AIX:*:*)
590621 echo rs6000-ibm-aix
591622 exit ;;
592 ibmrt:4.4BSD:*|romp-ibm:BSD:*)
623 ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*)
593624 echo romp-ibm-bsd4.4
594625 exit ;;
595626 ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and
596 echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to
627 echo romp-ibm-bsd"$UNAME_RELEASE" # 4.3 with uname added to
597628 exit ;; # report: romp-ibm BSD 4.3
598629 *:BOSX:*:*)
599630 echo rs6000-bull-bosx
608639 echo m68k-hp-bsd4.4
609640 exit ;;
610641 9000/[34678]??:HP-UX:*:*)
611 HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
612 case "${UNAME_MACHINE}" in
613 9000/31? ) HP_ARCH=m68000 ;;
614 9000/[34]?? ) HP_ARCH=m68k ;;
642 HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'`
643 case "$UNAME_MACHINE" in
644 9000/31?) HP_ARCH=m68000 ;;
645 9000/[34]??) HP_ARCH=m68k ;;
615646 9000/[678][0-9][0-9])
616647 if [ -x /usr/bin/getconf ]; then
617648 sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
618649 sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
619 case "${sc_cpu_version}" in
620 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
621 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
650 case "$sc_cpu_version" in
651 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0
652 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1
622653 532) # CPU_PA_RISC2_0
623 case "${sc_kernel_bits}" in
624 32) HP_ARCH="hppa2.0n" ;;
625 64) HP_ARCH="hppa2.0w" ;;
626 '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20
654 case "$sc_kernel_bits" in
655 32) HP_ARCH=hppa2.0n ;;
656 64) HP_ARCH=hppa2.0w ;;
657 '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20
627658 esac ;;
628659 esac
629660 fi
630 if [ "${HP_ARCH}" = "" ]; then
631 eval $set_cc_for_build
632 sed 's/^ //' << EOF >$dummy.c
661 if [ "$HP_ARCH" = "" ]; then
662 eval "$set_cc_for_build"
663 sed 's/^ //' << EOF > "$dummy.c"
633664
634665 #define _HPUX_SOURCE
635666 #include <stdlib.h>
662693 exit (0);
663694 }
664695 EOF
665 (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
696 (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"`
666697 test -z "$HP_ARCH" && HP_ARCH=hppa
667698 fi ;;
668699 esac
669 if [ ${HP_ARCH} = "hppa2.0w" ]
700 if [ "$HP_ARCH" = hppa2.0w ]
670701 then
671 eval $set_cc_for_build
702 eval "$set_cc_for_build"
672703
673704 # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
674705 # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler
679710 # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
680711 # => hppa64-hp-hpux11.23
681712
682 if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) |
713 if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) |
683714 grep -q __LP64__
684715 then
685 HP_ARCH="hppa2.0w"
716 HP_ARCH=hppa2.0w
686717 else
687 HP_ARCH="hppa64"
718 HP_ARCH=hppa64
688719 fi
689720 fi
690 echo ${HP_ARCH}-hp-hpux${HPUX_REV}
721 echo "$HP_ARCH"-hp-hpux"$HPUX_REV"
691722 exit ;;
692723 ia64:HP-UX:*:*)
693 HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
694 echo ia64-hp-hpux${HPUX_REV}
724 HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'`
725 echo ia64-hp-hpux"$HPUX_REV"
695726 exit ;;
696727 3050*:HI-UX:*:*)
697 eval $set_cc_for_build
698 sed 's/^ //' << EOF >$dummy.c
728 eval "$set_cc_for_build"
729 sed 's/^ //' << EOF > "$dummy.c"
699730 #include <unistd.h>
700731 int
701732 main ()
720751 exit (0);
721752 }
722753 EOF
723 $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` &&
754 $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` &&
724755 { echo "$SYSTEM_NAME"; exit; }
725756 echo unknown-hitachi-hiuxwe2
726757 exit ;;
727 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
758 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*)
728759 echo hppa1.1-hp-bsd
729760 exit ;;
730761 9000/8??:4.3bsd:*:*)
733764 *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
734765 echo hppa1.0-hp-mpeix
735766 exit ;;
736 hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
767 hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*)
737768 echo hppa1.1-hp-osf
738769 exit ;;
739770 hp8??:OSF1:*:*)
741772 exit ;;
742773 i*86:OSF1:*:*)
743774 if [ -x /usr/sbin/sysversion ] ; then
744 echo ${UNAME_MACHINE}-unknown-osf1mk
775 echo "$UNAME_MACHINE"-unknown-osf1mk
745776 else
746 echo ${UNAME_MACHINE}-unknown-osf1
777 echo "$UNAME_MACHINE"-unknown-osf1
747778 fi
748779 exit ;;
749780 parisc*:Lites*:*:*)
768799 echo c4-convex-bsd
769800 exit ;;
770801 CRAY*Y-MP:*:*:*)
771 echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
802 echo ymp-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
772803 exit ;;
773804 CRAY*[A-Z]90:*:*:*)
774 echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
805 echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \
775806 | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
776807 -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
777808 -e 's/\.[^.]*$/.X/'
778809 exit ;;
779810 CRAY*TS:*:*:*)
780 echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
811 echo t90-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
781812 exit ;;
782813 CRAY*T3E:*:*:*)
783 echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
814 echo alphaev5-cray-unicosmk"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
784815 exit ;;
785816 CRAY*SV1:*:*:*)
786 echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
817 echo sv1-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
787818 exit ;;
788819 *:UNICOS/mp:*:*)
789 echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
820 echo craynv-cray-unicosmp"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
790821 exit ;;
791822 F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
792 FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
793 FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
794 FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
823 FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`
824 FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'`
825 FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'`
795826 echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
796827 exit ;;
797828 5000:UNIX_System_V:4.*:*)
798 FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
799 FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
829 FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'`
830 FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'`
800831 echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
801832 exit ;;
802833 i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
803 echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
834 echo "$UNAME_MACHINE"-pc-bsdi"$UNAME_RELEASE"
804835 exit ;;
805836 sparc*:BSD/OS:*:*)
806 echo sparc-unknown-bsdi${UNAME_RELEASE}
837 echo sparc-unknown-bsdi"$UNAME_RELEASE"
807838 exit ;;
808839 *:BSD/OS:*:*)
809 echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
840 echo "$UNAME_MACHINE"-unknown-bsdi"$UNAME_RELEASE"
810841 exit ;;
811842 *:FreeBSD:*:*)
812843 UNAME_PROCESSOR=`/usr/bin/uname -p`
813 case ${UNAME_PROCESSOR} in
844 case "$UNAME_PROCESSOR" in
814845 amd64)
815 echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
816 *)
817 echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
846 UNAME_PROCESSOR=x86_64 ;;
847 i386)
848 UNAME_PROCESSOR=i586 ;;
818849 esac
850 echo "$UNAME_PROCESSOR"-unknown-freebsd"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`"
819851 exit ;;
820852 i*:CYGWIN*:*)
821 echo ${UNAME_MACHINE}-pc-cygwin
853 echo "$UNAME_MACHINE"-pc-cygwin
822854 exit ;;
823855 *:MINGW64*:*)
824 echo ${UNAME_MACHINE}-pc-mingw64
856 echo "$UNAME_MACHINE"-pc-mingw64
825857 exit ;;
826858 *:MINGW*:*)
827 echo ${UNAME_MACHINE}-pc-mingw32
859 echo "$UNAME_MACHINE"-pc-mingw32
828860 exit ;;
829861 *:MSYS*:*)
830 echo ${UNAME_MACHINE}-pc-msys
831 exit ;;
832 i*:windows32*:*)
833 # uname -m includes "-pc" on this system.
834 echo ${UNAME_MACHINE}-mingw32
862 echo "$UNAME_MACHINE"-pc-msys
835863 exit ;;
836864 i*:PW*:*)
837 echo ${UNAME_MACHINE}-pc-pw32
865 echo "$UNAME_MACHINE"-pc-pw32
838866 exit ;;
839867 *:Interix*:*)
840 case ${UNAME_MACHINE} in
868 case "$UNAME_MACHINE" in
841869 x86)
842 echo i586-pc-interix${UNAME_RELEASE}
870 echo i586-pc-interix"$UNAME_RELEASE"
843871 exit ;;
844872 authenticamd | genuineintel | EM64T)
845 echo x86_64-unknown-interix${UNAME_RELEASE}
873 echo x86_64-unknown-interix"$UNAME_RELEASE"
846874 exit ;;
847875 IA64)
848 echo ia64-unknown-interix${UNAME_RELEASE}
876 echo ia64-unknown-interix"$UNAME_RELEASE"
849877 exit ;;
850878 esac ;;
851 [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
852 echo i${UNAME_MACHINE}-pc-mks
853 exit ;;
854 8664:Windows_NT:*)
855 echo x86_64-pc-mks
856 exit ;;
857 i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
858 # How do we know it's Interix rather than the generic POSIX subsystem?
859 # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
860 # UNAME_MACHINE based on the output of uname instead of i386?
861 echo i586-pc-interix
862 exit ;;
863879 i*:UWIN*:*)
864 echo ${UNAME_MACHINE}-pc-uwin
880 echo "$UNAME_MACHINE"-pc-uwin
865881 exit ;;
866882 amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
867883 echo x86_64-unknown-cygwin
868884 exit ;;
869 p*:CYGWIN*:*)
870 echo powerpcle-unknown-cygwin
871 exit ;;
872885 prep*:SunOS:5.*:*)
873 echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
886 echo powerpcle-unknown-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
874887 exit ;;
875888 *:GNU:*:*)
876889 # the GNU system
877 echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
890 echo "`echo "$UNAME_MACHINE"|sed -e 's,[-/].*$,,'`-unknown-$LIBC`echo "$UNAME_RELEASE"|sed -e 's,/.*$,,'`"
878891 exit ;;
879892 *:GNU/*:*:*)
880893 # other systems with GNU libc and userland
881 echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC}
894 echo "$UNAME_MACHINE-unknown-`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"``echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`-$LIBC"
882895 exit ;;
883896 i*86:Minix:*:*)
884 echo ${UNAME_MACHINE}-pc-minix
897 echo "$UNAME_MACHINE"-pc-minix
885898 exit ;;
886899 aarch64:Linux:*:*)
887 echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
900 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
888901 exit ;;
889902 aarch64_be:Linux:*:*)
890903 UNAME_MACHINE=aarch64_be
891 echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
904 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
892905 exit ;;
893906 alpha:Linux:*:*)
894907 case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
901914 EV68*) UNAME_MACHINE=alphaev68 ;;
902915 esac
903916 objdump --private-headers /bin/sh | grep -q ld.so.1
904 if test "$?" = 0 ; then LIBC="gnulibc1" ; fi
905 echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
917 if test "$?" = 0 ; then LIBC=gnulibc1 ; fi
918 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
906919 exit ;;
907920 arc:Linux:*:* | arceb:Linux:*:*)
908 echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
921 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
909922 exit ;;
910923 arm*:Linux:*:*)
911 eval $set_cc_for_build
924 eval "$set_cc_for_build"
912925 if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
913926 | grep -q __ARM_EABI__
914927 then
915 echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
928 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
916929 else
917930 if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
918931 | grep -q __ARM_PCS_VFP
919932 then
920 echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi
933 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabi
921934 else
922 echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf
935 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabihf
923936 fi
924937 fi
925938 exit ;;
926939 avr32*:Linux:*:*)
927 echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
940 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
928941 exit ;;
929942 cris:Linux:*:*)
930 echo ${UNAME_MACHINE}-axis-linux-${LIBC}
943 echo "$UNAME_MACHINE"-axis-linux-"$LIBC"
931944 exit ;;
932945 crisv32:Linux:*:*)
933 echo ${UNAME_MACHINE}-axis-linux-${LIBC}
946 echo "$UNAME_MACHINE"-axis-linux-"$LIBC"
947 exit ;;
948 e2k:Linux:*:*)
949 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
934950 exit ;;
935951 frv:Linux:*:*)
936 echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
952 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
937953 exit ;;
938954 hexagon:Linux:*:*)
939 echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
955 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
940956 exit ;;
941957 i*86:Linux:*:*)
942 echo ${UNAME_MACHINE}-pc-linux-${LIBC}
958 echo "$UNAME_MACHINE"-pc-linux-"$LIBC"
943959 exit ;;
944960 ia64:Linux:*:*)
945 echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
961 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
962 exit ;;
963 k1om:Linux:*:*)
964 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
946965 exit ;;
947966 m32r*:Linux:*:*)
948 echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
967 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
949968 exit ;;
950969 m68*:Linux:*:*)
951 echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
970 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
952971 exit ;;
953972 mips:Linux:*:* | mips64:Linux:*:*)
954 eval $set_cc_for_build
955 sed 's/^ //' << EOF >$dummy.c
973 eval "$set_cc_for_build"
974 sed 's/^ //' << EOF > "$dummy.c"
956975 #undef CPU
957976 #undef ${UNAME_MACHINE}
958977 #undef ${UNAME_MACHINE}el
966985 #endif
967986 #endif
968987 EOF
969 eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'`
970 test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; }
988 eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU'`"
989 test "x$CPU" != x && { echo "$CPU-unknown-linux-$LIBC"; exit; }
971990 ;;
991 mips64el:Linux:*:*)
992 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
993 exit ;;
972994 openrisc*:Linux:*:*)
973 echo or1k-unknown-linux-${LIBC}
995 echo or1k-unknown-linux-"$LIBC"
974996 exit ;;
975997 or32:Linux:*:* | or1k*:Linux:*:*)
976 echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
998 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
977999 exit ;;
9781000 padre:Linux:*:*)
979 echo sparc-unknown-linux-${LIBC}
1001 echo sparc-unknown-linux-"$LIBC"
9801002 exit ;;
9811003 parisc64:Linux:*:* | hppa64:Linux:*:*)
982 echo hppa64-unknown-linux-${LIBC}
1004 echo hppa64-unknown-linux-"$LIBC"
9831005 exit ;;
9841006 parisc:Linux:*:* | hppa:Linux:*:*)
9851007 # Look for CPU level
9861008 case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
987 PA7*) echo hppa1.1-unknown-linux-${LIBC} ;;
988 PA8*) echo hppa2.0-unknown-linux-${LIBC} ;;
989 *) echo hppa-unknown-linux-${LIBC} ;;
1009 PA7*) echo hppa1.1-unknown-linux-"$LIBC" ;;
1010 PA8*) echo hppa2.0-unknown-linux-"$LIBC" ;;
1011 *) echo hppa-unknown-linux-"$LIBC" ;;
9901012 esac
9911013 exit ;;
9921014 ppc64:Linux:*:*)
993 echo powerpc64-unknown-linux-${LIBC}
1015 echo powerpc64-unknown-linux-"$LIBC"
9941016 exit ;;
9951017 ppc:Linux:*:*)
996 echo powerpc-unknown-linux-${LIBC}
1018 echo powerpc-unknown-linux-"$LIBC"
9971019 exit ;;
9981020 ppc64le:Linux:*:*)
999 echo powerpc64le-unknown-linux-${LIBC}
1021 echo powerpc64le-unknown-linux-"$LIBC"
10001022 exit ;;
10011023 ppcle:Linux:*:*)
1002 echo powerpcle-unknown-linux-${LIBC}
1024 echo powerpcle-unknown-linux-"$LIBC"
1025 exit ;;
1026 riscv32:Linux:*:* | riscv64:Linux:*:*)
1027 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
10031028 exit ;;
10041029 s390:Linux:*:* | s390x:Linux:*:*)
1005 echo ${UNAME_MACHINE}-ibm-linux-${LIBC}
1030 echo "$UNAME_MACHINE"-ibm-linux-"$LIBC"
10061031 exit ;;
10071032 sh64*:Linux:*:*)
1008 echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
1033 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
10091034 exit ;;
10101035 sh*:Linux:*:*)
1011 echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
1036 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
10121037 exit ;;
10131038 sparc:Linux:*:* | sparc64:Linux:*:*)
1014 echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
1039 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
10151040 exit ;;
10161041 tile*:Linux:*:*)
1017 echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
1042 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
10181043 exit ;;
10191044 vax:Linux:*:*)
1020 echo ${UNAME_MACHINE}-dec-linux-${LIBC}
1045 echo "$UNAME_MACHINE"-dec-linux-"$LIBC"
10211046 exit ;;
10221047 x86_64:Linux:*:*)
1023 echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
1048 echo "$UNAME_MACHINE"-pc-linux-"$LIBC"
10241049 exit ;;
10251050 xtensa*:Linux:*:*)
1026 echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
1051 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
10271052 exit ;;
10281053 i*86:DYNIX/ptx:4*:*)
10291054 # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
10371062 # I am not positive that other SVR4 systems won't match this,
10381063 # I just have to hope. -- rms.
10391064 # Use sysv4.2uw... so that sysv4* matches it.
1040 echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
1065 echo "$UNAME_MACHINE"-pc-sysv4.2uw"$UNAME_VERSION"
10411066 exit ;;
10421067 i*86:OS/2:*:*)
10431068 # If we were able to find `uname', then EMX Unix compatibility
10441069 # is probably installed.
1045 echo ${UNAME_MACHINE}-pc-os2-emx
1070 echo "$UNAME_MACHINE"-pc-os2-emx
10461071 exit ;;
10471072 i*86:XTS-300:*:STOP)
1048 echo ${UNAME_MACHINE}-unknown-stop
1073 echo "$UNAME_MACHINE"-unknown-stop
10491074 exit ;;
10501075 i*86:atheos:*:*)
1051 echo ${UNAME_MACHINE}-unknown-atheos
1076 echo "$UNAME_MACHINE"-unknown-atheos
10521077 exit ;;
10531078 i*86:syllable:*:*)
1054 echo ${UNAME_MACHINE}-pc-syllable
1079 echo "$UNAME_MACHINE"-pc-syllable
10551080 exit ;;
10561081 i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*)
1057 echo i386-unknown-lynxos${UNAME_RELEASE}
1082 echo i386-unknown-lynxos"$UNAME_RELEASE"
10581083 exit ;;
10591084 i*86:*DOS:*:*)
1060 echo ${UNAME_MACHINE}-pc-msdosdjgpp
1061 exit ;;
1062 i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
1063 UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
1085 echo "$UNAME_MACHINE"-pc-msdosdjgpp
1086 exit ;;
1087 i*86:*:4.*:*)
1088 UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'`
10641089 if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
1065 echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
1090 echo "$UNAME_MACHINE"-univel-sysv"$UNAME_REL"
10661091 else
1067 echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
1092 echo "$UNAME_MACHINE"-pc-sysv"$UNAME_REL"
10681093 fi
10691094 exit ;;
10701095 i*86:*:5:[678]*)
10741099 *Pentium) UNAME_MACHINE=i586 ;;
10751100 *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
10761101 esac
1077 echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
1102 echo "$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}{$UNAME_VERSION}"
10781103 exit ;;
10791104 i*86:*:3.2:*)
10801105 if test -f /usr/options/cb.name; then
10811106 UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
1082 echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
1107 echo "$UNAME_MACHINE"-pc-isc"$UNAME_REL"
10831108 elif /bin/uname -X 2>/dev/null >/dev/null ; then
10841109 UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
10851110 (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
10891114 && UNAME_MACHINE=i686
10901115 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
10911116 && UNAME_MACHINE=i686
1092 echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
1117 echo "$UNAME_MACHINE"-pc-sco"$UNAME_REL"
10931118 else
1094 echo ${UNAME_MACHINE}-pc-sysv32
1119 echo "$UNAME_MACHINE"-pc-sysv32
10951120 fi
10961121 exit ;;
10971122 pc:*:*:*)
10991124 # uname -m prints for DJGPP always 'pc', but it prints nothing about
11001125 # the processor, so we play safe by assuming i586.
11011126 # Note: whatever this is, it MUST be the same as what config.sub
1102 # prints for the "djgpp" host, or else GDB configury will decide that
1127 # prints for the "djgpp" host, or else GDB configure will decide that
11031128 # this is a cross-build.
11041129 echo i586-pc-msdosdjgpp
11051130 exit ;;
11111136 exit ;;
11121137 i860:*:4.*:*) # i860-SVR4
11131138 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
1114 echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
1139 echo i860-stardent-sysv"$UNAME_RELEASE" # Stardent Vistra i860-SVR4
11151140 else # Add other i860-SVR4 vendors below as they are discovered.
1116 echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4
1141 echo i860-unknown-sysv"$UNAME_RELEASE" # Unknown i860-SVR4
11171142 fi
11181143 exit ;;
11191144 mini*:CTIX:SYS*5:*)
11331158 test -r /etc/.relid \
11341159 && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
11351160 /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
1136 && { echo i486-ncr-sysv4.3${OS_REL}; exit; }
1161 && { echo i486-ncr-sysv4.3"$OS_REL"; exit; }
11371162 /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
1138 && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
1163 && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;;
11391164 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
11401165 /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
11411166 && { echo i486-ncr-sysv4; exit; } ;;
11441169 test -r /etc/.relid \
11451170 && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
11461171 /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
1147 && { echo i486-ncr-sysv4.3${OS_REL}; exit; }
1172 && { echo i486-ncr-sysv4.3"$OS_REL"; exit; }
11481173 /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
1149 && { echo i586-ncr-sysv4.3${OS_REL}; exit; }
1174 && { echo i586-ncr-sysv4.3"$OS_REL"; exit; }
11501175 /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \
1151 && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
1176 && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;;
11521177 m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
1153 echo m68k-unknown-lynxos${UNAME_RELEASE}
1178 echo m68k-unknown-lynxos"$UNAME_RELEASE"
11541179 exit ;;
11551180 mc68030:UNIX_System_V:4.*:*)
11561181 echo m68k-atari-sysv4
11571182 exit ;;
11581183 TSUNAMI:LynxOS:2.*:*)
1159 echo sparc-unknown-lynxos${UNAME_RELEASE}
1184 echo sparc-unknown-lynxos"$UNAME_RELEASE"
11601185 exit ;;
11611186 rs6000:LynxOS:2.*:*)
1162 echo rs6000-unknown-lynxos${UNAME_RELEASE}
1187 echo rs6000-unknown-lynxos"$UNAME_RELEASE"
11631188 exit ;;
11641189 PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*)
1165 echo powerpc-unknown-lynxos${UNAME_RELEASE}
1190 echo powerpc-unknown-lynxos"$UNAME_RELEASE"
11661191 exit ;;
11671192 SM[BE]S:UNIX_SV:*:*)
1168 echo mips-dde-sysv${UNAME_RELEASE}
1193 echo mips-dde-sysv"$UNAME_RELEASE"
11691194 exit ;;
11701195 RM*:ReliantUNIX-*:*:*)
11711196 echo mips-sni-sysv4
11761201 *:SINIX-*:*:*)
11771202 if uname -p 2>/dev/null >/dev/null ; then
11781203 UNAME_MACHINE=`(uname -p) 2>/dev/null`
1179 echo ${UNAME_MACHINE}-sni-sysv4
1204 echo "$UNAME_MACHINE"-sni-sysv4
11801205 else
11811206 echo ns32k-sni-sysv
11821207 fi
11961221 exit ;;
11971222 i*86:VOS:*:*)
11981223 # From Paul.Green@stratus.com.
1199 echo ${UNAME_MACHINE}-stratus-vos
1224 echo "$UNAME_MACHINE"-stratus-vos
12001225 exit ;;
12011226 *:VOS:*:*)
12021227 # From Paul.Green@stratus.com.
12031228 echo hppa1.1-stratus-vos
12041229 exit ;;
12051230 mc68*:A/UX:*:*)
1206 echo m68k-apple-aux${UNAME_RELEASE}
1231 echo m68k-apple-aux"$UNAME_RELEASE"
12071232 exit ;;
12081233 news*:NEWS-OS:6*:*)
12091234 echo mips-sony-newsos6
12101235 exit ;;
12111236 R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
12121237 if [ -d /usr/nec ]; then
1213 echo mips-nec-sysv${UNAME_RELEASE}
1238 echo mips-nec-sysv"$UNAME_RELEASE"
12141239 else
1215 echo mips-unknown-sysv${UNAME_RELEASE}
1240 echo mips-unknown-sysv"$UNAME_RELEASE"
12161241 fi
12171242 exit ;;
12181243 BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
12311256 echo x86_64-unknown-haiku
12321257 exit ;;
12331258 SX-4:SUPER-UX:*:*)
1234 echo sx4-nec-superux${UNAME_RELEASE}
1259 echo sx4-nec-superux"$UNAME_RELEASE"
12351260 exit ;;
12361261 SX-5:SUPER-UX:*:*)
1237 echo sx5-nec-superux${UNAME_RELEASE}
1262 echo sx5-nec-superux"$UNAME_RELEASE"
12381263 exit ;;
12391264 SX-6:SUPER-UX:*:*)
1240 echo sx6-nec-superux${UNAME_RELEASE}
1265 echo sx6-nec-superux"$UNAME_RELEASE"
12411266 exit ;;
12421267 SX-7:SUPER-UX:*:*)
1243 echo sx7-nec-superux${UNAME_RELEASE}
1268 echo sx7-nec-superux"$UNAME_RELEASE"
12441269 exit ;;
12451270 SX-8:SUPER-UX:*:*)
1246 echo sx8-nec-superux${UNAME_RELEASE}
1271 echo sx8-nec-superux"$UNAME_RELEASE"
12471272 exit ;;
12481273 SX-8R:SUPER-UX:*:*)
1249 echo sx8r-nec-superux${UNAME_RELEASE}
1274 echo sx8r-nec-superux"$UNAME_RELEASE"
1275 exit ;;
1276 SX-ACE:SUPER-UX:*:*)
1277 echo sxace-nec-superux"$UNAME_RELEASE"
12501278 exit ;;
12511279 Power*:Rhapsody:*:*)
1252 echo powerpc-apple-rhapsody${UNAME_RELEASE}
1280 echo powerpc-apple-rhapsody"$UNAME_RELEASE"
12531281 exit ;;
12541282 *:Rhapsody:*:*)
1255 echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
1283 echo "$UNAME_MACHINE"-apple-rhapsody"$UNAME_RELEASE"
12561284 exit ;;
12571285 *:Darwin:*:*)
12581286 UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
1259 eval $set_cc_for_build
1287 eval "$set_cc_for_build"
12601288 if test "$UNAME_PROCESSOR" = unknown ; then
12611289 UNAME_PROCESSOR=powerpc
12621290 fi
1263 if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then
1264 if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
1291 if test "`echo "$UNAME_RELEASE" | sed -e 's/\..*//'`" -le 10 ; then
1292 if [ "$CC_FOR_BUILD" != no_compiler_found ]; then
12651293 if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
1266 (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
1267 grep IS_64BIT_ARCH >/dev/null
1294 (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
1295 grep IS_64BIT_ARCH >/dev/null
12681296 then
12691297 case $UNAME_PROCESSOR in
12701298 i386) UNAME_PROCESSOR=x86_64 ;;
12711299 powerpc) UNAME_PROCESSOR=powerpc64 ;;
12721300 esac
1301 fi
1302 # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc
1303 if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \
1304 (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
1305 grep IS_PPC >/dev/null
1306 then
1307 UNAME_PROCESSOR=powerpc
12731308 fi
12741309 fi
12751310 elif test "$UNAME_PROCESSOR" = i386 ; then
12811316 # that Apple uses in portable devices.
12821317 UNAME_PROCESSOR=x86_64
12831318 fi
1284 echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
1319 echo "$UNAME_PROCESSOR"-apple-darwin"$UNAME_RELEASE"
12851320 exit ;;
12861321 *:procnto*:*:* | *:QNX:[0123456789]*:*)
12871322 UNAME_PROCESSOR=`uname -p`
1288 if test "$UNAME_PROCESSOR" = "x86"; then
1323 if test "$UNAME_PROCESSOR" = x86; then
12891324 UNAME_PROCESSOR=i386
12901325 UNAME_MACHINE=pc
12911326 fi
1292 echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
1327 echo "$UNAME_PROCESSOR"-"$UNAME_MACHINE"-nto-qnx"$UNAME_RELEASE"
12931328 exit ;;
12941329 *:QNX:*:4*)
12951330 echo i386-pc-qnx
12961331 exit ;;
1297 NEO-?:NONSTOP_KERNEL:*:*)
1298 echo neo-tandem-nsk${UNAME_RELEASE}
1332 NEO-*:NONSTOP_KERNEL:*:*)
1333 echo neo-tandem-nsk"$UNAME_RELEASE"
12991334 exit ;;
13001335 NSE-*:NONSTOP_KERNEL:*:*)
1301 echo nse-tandem-nsk${UNAME_RELEASE}
1302 exit ;;
1303 NSR-?:NONSTOP_KERNEL:*:*)
1304 echo nsr-tandem-nsk${UNAME_RELEASE}
1336 echo nse-tandem-nsk"$UNAME_RELEASE"
1337 exit ;;
1338 NSR-*:NONSTOP_KERNEL:*:*)
1339 echo nsr-tandem-nsk"$UNAME_RELEASE"
1340 exit ;;
1341 NSV-*:NONSTOP_KERNEL:*:*)
1342 echo nsv-tandem-nsk"$UNAME_RELEASE"
1343 exit ;;
1344 NSX-*:NONSTOP_KERNEL:*:*)
1345 echo nsx-tandem-nsk"$UNAME_RELEASE"
13051346 exit ;;
13061347 *:NonStop-UX:*:*)
13071348 echo mips-compaq-nonstopux
13101351 echo bs2000-siemens-sysv
13111352 exit ;;
13121353 DS/*:UNIX_System_V:*:*)
1313 echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
1354 echo "$UNAME_MACHINE"-"$UNAME_SYSTEM"-"$UNAME_RELEASE"
13141355 exit ;;
13151356 *:Plan9:*:*)
13161357 # "uname -m" is not consistent, so use $cputype instead. 386
13171358 # is converted to i386 for consistency with other x86
13181359 # operating systems.
1319 if test "$cputype" = "386"; then
1360 if test "$cputype" = 386; then
13201361 UNAME_MACHINE=i386
13211362 else
13221363 UNAME_MACHINE="$cputype"
13231364 fi
1324 echo ${UNAME_MACHINE}-unknown-plan9
1365 echo "$UNAME_MACHINE"-unknown-plan9
13251366 exit ;;
13261367 *:TOPS-10:*:*)
13271368 echo pdp10-unknown-tops10
13421383 echo pdp10-unknown-its
13431384 exit ;;
13441385 SEI:*:*:SEIUX)
1345 echo mips-sei-seiux${UNAME_RELEASE}
1386 echo mips-sei-seiux"$UNAME_RELEASE"
13461387 exit ;;
13471388 *:DragonFly:*:*)
1348 echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
1389 echo "$UNAME_MACHINE"-unknown-dragonfly"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`"
13491390 exit ;;
13501391 *:*VMS:*:*)
13511392 UNAME_MACHINE=`(uname -p) 2>/dev/null`
1352 case "${UNAME_MACHINE}" in
1393 case "$UNAME_MACHINE" in
13531394 A*) echo alpha-dec-vms ; exit ;;
13541395 I*) echo ia64-dec-vms ; exit ;;
13551396 V*) echo vax-dec-vms ; exit ;;
13581399 echo i386-pc-xenix
13591400 exit ;;
13601401 i*86:skyos:*:*)
1361 echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//'
1402 echo "$UNAME_MACHINE"-pc-skyos"`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'`"
13621403 exit ;;
13631404 i*86:rdos:*:*)
1364 echo ${UNAME_MACHINE}-pc-rdos
1405 echo "$UNAME_MACHINE"-pc-rdos
13651406 exit ;;
13661407 i*86:AROS:*:*)
1367 echo ${UNAME_MACHINE}-pc-aros
1408 echo "$UNAME_MACHINE"-pc-aros
13681409 exit ;;
13691410 x86_64:VMkernel:*:*)
1370 echo ${UNAME_MACHINE}-unknown-esx
1411 echo "$UNAME_MACHINE"-unknown-esx
1412 exit ;;
1413 amd64:Isilon\ OneFS:*:*)
1414 echo x86_64-unknown-onefs
13711415 exit ;;
13721416 esac
13731417
1418 echo "$0: unable to guess system type" >&2
1419
1420 case "$UNAME_MACHINE:$UNAME_SYSTEM" in
1421 mips:Linux | mips64:Linux)
1422 # If we got here on MIPS GNU/Linux, output extra information.
1423 cat >&2 <<EOF
1424
1425 NOTE: MIPS GNU/Linux systems require a C compiler to fully recognize
1426 the system type. Please install a C compiler and try again.
1427 EOF
1428 ;;
1429 esac
1430
13741431 cat >&2 <<EOF
1375 $0: unable to guess system type
1376
1377 This script, last modified $timestamp, has failed to recognize
1378 the operating system you are using. It is advised that you
1379 download the most up to date version of the config scripts from
1380
1381 http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
1432
1433 This script (version $timestamp), has failed to recognize the
1434 operating system you are using. If your script is old, overwrite *all*
1435 copies of config.guess and config.sub with the latest versions from:
1436
1437 https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess
13821438 and
1383 http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
1384
1385 If the version you run ($0) is already up to date, please
1386 send the following data and any information you think might be
1387 pertinent to <config-patches@gnu.org> in order to provide the needed
1388 information to handle your system.
1439 https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub
1440
1441 If $0 has already been updated, send the following data and any
1442 information you think might be pertinent to config-patches@gnu.org to
1443 provide the necessary information to handle your system.
13891444
13901445 config.guess timestamp = $timestamp
13911446
14041459 /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null`
14051460 /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
14061461
1407 UNAME_MACHINE = ${UNAME_MACHINE}
1408 UNAME_RELEASE = ${UNAME_RELEASE}
1409 UNAME_SYSTEM = ${UNAME_SYSTEM}
1410 UNAME_VERSION = ${UNAME_VERSION}
1462 UNAME_MACHINE = "$UNAME_MACHINE"
1463 UNAME_RELEASE = "$UNAME_RELEASE"
1464 UNAME_SYSTEM = "$UNAME_SYSTEM"
1465 UNAME_VERSION = "$UNAME_VERSION"
14111466 EOF
14121467
14131468 exit 1
14141469
14151470 # Local variables:
1416 # eval: (add-hook 'write-file-hooks 'time-stamp)
1471 # eval: (add-hook 'before-save-hook 'time-stamp)
14171472 # time-stamp-start: "timestamp='"
14181473 # time-stamp-format: "%:y-%02m-%02d"
14191474 # time-stamp-end: "'"
1717 /* found fribidi via pkg-config */
1818 #undef CONFIG_FRIBIDI
1919
20 /* found harfbuzz-ng via pkg-config */
20 /* found harfbuzz via pkg-config */
2121 #undef CONFIG_HARFBUZZ
2222
2323 /* use iconv */
00 #! /bin/sh
11 # Configuration validation subroutine script.
2 # Copyright 1992-2014 Free Software Foundation, Inc.
3
4 timestamp='2014-12-03'
2 # Copyright 1992-2018 Free Software Foundation, Inc.
3
4 timestamp='2018-03-08'
55
66 # This file is free software; you can redistribute it and/or modify it
77 # under the terms of the GNU General Public License as published by
1414 # General Public License for more details.
1515 #
1616 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, see <http://www.gnu.org/licenses/>.
17 # along with this program; if not, see <https://www.gnu.org/licenses/>.
1818 #
1919 # As a special exception to the GNU General Public License, if you
2020 # distribute this file as part of a program that contains a
3232 # Otherwise, we print the canonical config type on stdout and succeed.
3333
3434 # You can get the latest version of this script from:
35 # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
35 # https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub
3636
3737 # This file is supposed to be the same for all GNU packages
3838 # and recognize all the CPU types, system types and aliases
5252 me=`echo "$0" | sed -e 's,.*/,,'`
5353
5454 usage="\
55 Usage: $0 [OPTION] CPU-MFR-OPSYS
56 $0 [OPTION] ALIAS
55 Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS
5756
5857 Canonicalize a configuration name.
5958
60 Operation modes:
59 Options:
6160 -h, --help print this help, then exit
6261 -t, --time-stamp print date of last modification, then exit
6362 -v, --version print version number, then exit
6766 version="\
6867 GNU config.sub ($timestamp)
6968
70 Copyright 1992-2014 Free Software Foundation, Inc.
69 Copyright 1992-2018 Free Software Foundation, Inc.
7170
7271 This is free software; see the source for copying conditions. There is NO
7372 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
9493
9594 *local*)
9695 # First pass through any local machine types.
97 echo $1
96 echo "$1"
9897 exit ;;
9998
10099 * )
112111
113112 # Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
114113 # Here we must recognize all the valid KERNEL-OS combinations.
115 maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
114 maybe_os=`echo "$1" | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
116115 case $maybe_os in
117116 nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \
118117 linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \
119 knetbsd*-gnu* | netbsd*-gnu* | \
120 kopensolaris*-gnu* | \
118 knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \
119 kopensolaris*-gnu* | cloudabi*-eabi* | \
121120 storm-chaos* | os2-emx* | rtmk-nova*)
122121 os=-$maybe_os
123 basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
122 basic_machine=`echo "$1" | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
124123 ;;
125124 android-linux)
126125 os=-linux-android
127 basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown
126 basic_machine=`echo "$1" | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown
128127 ;;
129128 *)
130 basic_machine=`echo $1 | sed 's/-[^-]*$//'`
131 if [ $basic_machine != $1 ]
132 then os=`echo $1 | sed 's/.*-/-/'`
129 basic_machine=`echo "$1" | sed 's/-[^-]*$//'`
130 if [ "$basic_machine" != "$1" ]
131 then os=`echo "$1" | sed 's/.*-/-/'`
133132 else os=; fi
134133 ;;
135134 esac
178177 ;;
179178 -sco6)
180179 os=-sco5v6
181 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
180 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
182181 ;;
183182 -sco5)
184183 os=-sco3.2v5
185 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
184 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
186185 ;;
187186 -sco4)
188187 os=-sco3.2v4
189 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
188 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
190189 ;;
191190 -sco3.2.[4-9]*)
192191 os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
193 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
192 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
194193 ;;
195194 -sco3.2v[4-9]*)
196195 # Don't forget version if it is 3.2v4 or newer.
197 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
196 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
198197 ;;
199198 -sco5v6*)
200199 # Don't forget version if it is 3.2v4 or newer.
201 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
200 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
202201 ;;
203202 -sco*)
204203 os=-sco3.2v2
205 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
204 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
206205 ;;
207206 -udk*)
208 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
207 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
209208 ;;
210209 -isc)
211210 os=-isc2.2
212 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
211 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
213212 ;;
214213 -clix*)
215214 basic_machine=clipper-intergraph
216215 ;;
217216 -isc*)
218 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
217 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
219218 ;;
220219 -lynx*178)
221220 os=-lynxos178
227226 os=-lynxos
228227 ;;
229228 -ptx*)
230 basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
231 ;;
232 -windowsnt*)
233 os=`echo $os | sed -e 's/windowsnt/winnt/'`
229 basic_machine=`echo "$1" | sed -e 's/86-.*/86-sequent/'`
234230 ;;
235231 -psos*)
236232 os=-psos
254250 | arc | arceb \
255251 | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \
256252 | avr | avr32 \
253 | ba \
257254 | be32 | be64 \
258255 | bfin \
259256 | c4x | c8051 | clipper \
260257 | d10v | d30v | dlx | dsp16xx \
261 | epiphany \
262 | fido | fr30 | frv \
258 | e2k | epiphany \
259 | fido | fr30 | frv | ft32 \
263260 | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
264261 | hexagon \
265 | i370 | i860 | i960 | ia64 \
262 | i370 | i860 | i960 | ia16 | ia64 \
266263 | ip2k | iq2000 \
267264 | k1om \
268265 | le32 | le64 \
298295 | nios | nios2 | nios2eb | nios2el \
299296 | ns16k | ns32k \
300297 | open8 | or1k | or1knd | or32 \
301 | pdp10 | pdp11 | pj | pjl \
298 | pdp10 | pj | pjl \
302299 | powerpc | powerpc64 | powerpc64le | powerpcle \
300 | pru \
303301 | pyramid \
304302 | riscv32 | riscv64 \
305303 | rl78 | rx \
306304 | score \
307 | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
305 | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[234]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
308306 | sh64 | sh64le \
309307 | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \
310308 | sparcv8 | sparcv9 | sparcv9b | sparcv9v \
313311 | ubicom32 \
314312 | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \
315313 | visium \
316 | we32k \
314 | wasm32 \
317315 | x86 | xc16x | xstormy16 | xtensa \
318316 | z8k | z80)
319317 basic_machine=$basic_machine-unknown
334332 basic_machine=$basic_machine-unknown
335333 os=-none
336334 ;;
337 m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
335 m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65)
338336 ;;
339337 ms1)
340338 basic_machine=mt-unknown
363361 ;;
364362 # Object if more than one company name word.
365363 *-*-*)
366 echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
364 echo Invalid configuration \`"$1"\': machine \`"$basic_machine"\' not recognized 1>&2
367365 exit 1
368366 ;;
369367 # Recognize the basic CPU types with company name.
375373 | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \
376374 | arm-* | armbe-* | armle-* | armeb-* | armv*-* \
377375 | avr-* | avr32-* \
376 | ba-* \
378377 | be32-* | be64-* \
379378 | bfin-* | bs2000-* \
380379 | c[123]* | c30-* | [cjt]90-* | c4x-* \
381380 | c8051-* | clipper-* | craynv-* | cydra-* \
382381 | d10v-* | d30v-* | dlx-* \
383 | elxsi-* \
382 | e2k-* | elxsi-* \
384383 | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
385384 | h8300-* | h8500-* \
386385 | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
387386 | hexagon-* \
388 | i*86-* | i860-* | i960-* | ia64-* \
387 | i*86-* | i860-* | i960-* | ia16-* | ia64-* \
389388 | ip2k-* | iq2000-* \
390389 | k1om-* \
391390 | le32-* | le64-* \
426425 | orion-* \
427426 | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
428427 | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \
428 | pru-* \
429429 | pyramid-* \
430 | riscv32-* | riscv64-* \
430431 | rl78-* | romp-* | rs6000-* | rx-* \
431432 | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
432433 | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
433434 | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \
434435 | sparclite-* \
435 | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \
436 | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx*-* \
436437 | tahoe-* \
437438 | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
438439 | tile*-* \
441442 | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \
442443 | vax-* \
443444 | visium-* \
445 | wasm32-* \
444446 | we32k-* \
445447 | x86-* | x86_64-* | xc16x-* | xps100-* \
446448 | xstormy16-* | xtensa*-* \
454456 # Recognize the various machine names and aliases which stand
455457 # for a CPU type and a company and sometimes even an OS.
456458 386bsd)
457 basic_machine=i386-unknown
459 basic_machine=i386-pc
458460 os=-bsd
459461 ;;
460462 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
488490 basic_machine=x86_64-pc
489491 ;;
490492 amd64-*)
491 basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'`
493 basic_machine=x86_64-`echo "$basic_machine" | sed 's/^[^-]*-//'`
492494 ;;
493495 amdahl)
494496 basic_machine=580-amdahl
517519 basic_machine=i386-pc
518520 os=-aros
519521 ;;
522 asmjs)
523 basic_machine=asmjs-unknown
524 ;;
520525 aux)
521526 basic_machine=m68k-apple
522527 os=-aux
530535 os=-linux
531536 ;;
532537 blackfin-*)
533 basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'`
538 basic_machine=bfin-`echo "$basic_machine" | sed 's/^[^-]*-//'`
534539 os=-linux
535540 ;;
536541 bluegene*)
538543 os=-cnk
539544 ;;
540545 c54x-*)
541 basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'`
546 basic_machine=tic54x-`echo "$basic_machine" | sed 's/^[^-]*-//'`
542547 ;;
543548 c55x-*)
544 basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'`
549 basic_machine=tic55x-`echo "$basic_machine" | sed 's/^[^-]*-//'`
545550 ;;
546551 c6x-*)
547 basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'`
552 basic_machine=tic6x-`echo "$basic_machine" | sed 's/^[^-]*-//'`
548553 ;;
549554 c90)
550555 basic_machine=c90-cray
633638 basic_machine=rs6000-bull
634639 os=-bosx
635640 ;;
636 dpx2* | dpx2*-bull)
641 dpx2*)
637642 basic_machine=m68k-bull
638643 os=-sysv3
644 ;;
645 e500v[12])
646 basic_machine=powerpc-unknown
647 os=$os"spe"
648 ;;
649 e500v[12]-*)
650 basic_machine=powerpc-`echo "$basic_machine" | sed 's/^[^-]*-//'`
651 os=$os"spe"
639652 ;;
640653 ebmon29k)
641654 basic_machine=a29k-amd
726739 hp9k8[0-9][0-9] | hp8[0-9][0-9])
727740 basic_machine=hppa1.0-hp
728741 ;;
729 hppa-next)
730 os=-nextstep3
731 ;;
732742 hppaosf)
733743 basic_machine=hppa1.1-hp
734744 os=-osf
741751 basic_machine=i370-ibm
742752 ;;
743753 i*86v32)
744 basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
754 basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'`
745755 os=-sysv32
746756 ;;
747757 i*86v4*)
748 basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
758 basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'`
749759 os=-sysv4
750760 ;;
751761 i*86v)
752 basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
762 basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'`
753763 os=-sysv
754764 ;;
755765 i*86sol2)
756 basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
766 basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'`
757767 os=-solaris2
758768 ;;
759769 i386mach)
760770 basic_machine=i386-mach
761771 os=-mach
762772 ;;
763 i386-vsta | vsta)
773 vsta)
764774 basic_machine=i386-unknown
765775 os=-vsta
766776 ;;
779789 os=-sysv
780790 ;;
781791 leon-*|leon[3-9]-*)
782 basic_machine=sparc-`echo $basic_machine | sed 's/-.*//'`
792 basic_machine=sparc-`echo "$basic_machine" | sed 's/-.*//'`
783793 ;;
784794 m68knommu)
785795 basic_machine=m68k-unknown
786796 os=-linux
787797 ;;
788798 m68knommu-*)
789 basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'`
799 basic_machine=m68k-`echo "$basic_machine" | sed 's/^[^-]*-//'`
790800 os=-linux
791 ;;
792 m88k-omron*)
793 basic_machine=m88k-omron
794801 ;;
795802 magnum | m3230)
796803 basic_machine=mips-mips
823830 os=-mint
824831 ;;
825832 mips3*-*)
826 basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
833 basic_machine=`echo "$basic_machine" | sed -e 's/mips3/mips64/'`
827834 ;;
828835 mips3*)
829 basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
836 basic_machine=`echo "$basic_machine" | sed -e 's/mips3/mips64/'`-unknown
830837 ;;
831838 monitor)
832839 basic_machine=m68k-rom68k
845852 os=-msdos
846853 ;;
847854 ms1-*)
848 basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`
855 basic_machine=`echo "$basic_machine" | sed -e 's/ms1-/mt-/'`
849856 ;;
850857 msys)
851858 basic_machine=i686-pc
887894 basic_machine=v70-nec
888895 os=-sysv
889896 ;;
890 next | m*-next )
897 next | m*-next)
891898 basic_machine=m68k-next
892899 case $os in
893900 -nextstep* )
932939 nsr-tandem)
933940 basic_machine=nsr-tandem
934941 ;;
942 nsv-tandem)
943 basic_machine=nsv-tandem
944 ;;
945 nsx-tandem)
946 basic_machine=nsx-tandem
947 ;;
935948 op50n-* | op60c-*)
936949 basic_machine=hppa1.1-oki
937950 os=-proelf
964977 os=-linux
965978 ;;
966979 parisc-*)
967 basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'`
980 basic_machine=hppa-`echo "$basic_machine" | sed 's/^[^-]*-//'`
968981 os=-linux
969982 ;;
970983 pbd)
980993 basic_machine=i386-pc
981994 ;;
982995 pc98-*)
983 basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'`
996 basic_machine=i386-`echo "$basic_machine" | sed 's/^[^-]*-//'`
984997 ;;
985998 pentium | p5 | k5 | k6 | nexgen | viac3)
986999 basic_machine=i586-pc
9951008 basic_machine=i786-pc
9961009 ;;
9971010 pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
998 basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
1011 basic_machine=i586-`echo "$basic_machine" | sed 's/^[^-]*-//'`
9991012 ;;
10001013 pentiumpro-* | p6-* | 6x86-* | athlon-*)
1001 basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
1014 basic_machine=i686-`echo "$basic_machine" | sed 's/^[^-]*-//'`
10021015 ;;
10031016 pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
1004 basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
1017 basic_machine=i686-`echo "$basic_machine" | sed 's/^[^-]*-//'`
10051018 ;;
10061019 pentium4-*)
1007 basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'`
1020 basic_machine=i786-`echo "$basic_machine" | sed 's/^[^-]*-//'`
10081021 ;;
10091022 pn)
10101023 basic_machine=pn-gould
10141027 ppc | ppcbe) basic_machine=powerpc-unknown
10151028 ;;
10161029 ppc-* | ppcbe-*)
1017 basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
1018 ;;
1019 ppcle | powerpclittle | ppc-le | powerpc-little)
1030 basic_machine=powerpc-`echo "$basic_machine" | sed 's/^[^-]*-//'`
1031 ;;
1032 ppcle | powerpclittle)
10201033 basic_machine=powerpcle-unknown
10211034 ;;
10221035 ppcle-* | powerpclittle-*)
1023 basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
1036 basic_machine=powerpcle-`echo "$basic_machine" | sed 's/^[^-]*-//'`
10241037 ;;
10251038 ppc64) basic_machine=powerpc64-unknown
10261039 ;;
1027 ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
1028 ;;
1029 ppc64le | powerpc64little | ppc64-le | powerpc64-little)
1040 ppc64-*) basic_machine=powerpc64-`echo "$basic_machine" | sed 's/^[^-]*-//'`
1041 ;;
1042 ppc64le | powerpc64little)
10301043 basic_machine=powerpc64le-unknown
10311044 ;;
10321045 ppc64le-* | powerpc64little-*)
1033 basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
1046 basic_machine=powerpc64le-`echo "$basic_machine" | sed 's/^[^-]*-//'`
10341047 ;;
10351048 ps2)
10361049 basic_machine=i386-ibm
10841097 sequent)
10851098 basic_machine=i386-sequent
10861099 ;;
1087 sh)
1088 basic_machine=sh-hitachi
1089 os=-hms
1090 ;;
10911100 sh5el)
10921101 basic_machine=sh5le-unknown
10931102 ;;
1094 sh64)
1095 basic_machine=sh64-unknown
1096 ;;
1097 sparclite-wrs | simso-wrs)
1103 simso-wrs)
10981104 basic_machine=sparclite-wrs
10991105 os=-vxworks
11001106 ;;
11131119 os=-sysv4
11141120 ;;
11151121 strongarm-* | thumb-*)
1116 basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'`
1122 basic_machine=arm-`echo "$basic_machine" | sed 's/^[^-]*-//'`
11171123 ;;
11181124 sun2)
11191125 basic_machine=m68000-sun
12351241 basic_machine=hppa1.1-winbond
12361242 os=-proelf
12371243 ;;
1244 x64)
1245 basic_machine=x86_64-pc
1246 ;;
12381247 xbox)
12391248 basic_machine=i686-pc
12401249 os=-mingw32
12431252 basic_machine=xps100-honeywell
12441253 ;;
12451254 xscale-* | xscalee[bl]-*)
1246 basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'`
1255 basic_machine=`echo "$basic_machine" | sed 's/^xscale/arm/'`
12471256 ;;
12481257 ymp)
12491258 basic_machine=ymp-cray
12501259 os=-unicos
1251 ;;
1252 z8k-*-coff)
1253 basic_machine=z8k-unknown
1254 os=-sim
1255 ;;
1256 z80-*-coff)
1257 basic_machine=z80-unknown
1258 os=-sim
12591260 ;;
12601261 none)
12611262 basic_machine=none-none
12851286 vax)
12861287 basic_machine=vax-dec
12871288 ;;
1288 pdp10)
1289 # there are many clones, so DEC is not a safe bet
1290 basic_machine=pdp10-unknown
1291 ;;
12921289 pdp11)
12931290 basic_machine=pdp11-dec
12941291 ;;
12981295 sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele)
12991296 basic_machine=sh-unknown
13001297 ;;
1301 sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v)
1302 basic_machine=sparc-sun
1303 ;;
13041298 cydra)
13051299 basic_machine=cydra-cydrome
13061300 ;;
13201314 # Make sure to match an already-canonicalized machine name.
13211315 ;;
13221316 *)
1323 echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
1317 echo Invalid configuration \`"$1"\': machine \`"$basic_machine"\' not recognized 1>&2
13241318 exit 1
13251319 ;;
13261320 esac
13281322 # Here we canonicalize certain aliases for manufacturers.
13291323 case $basic_machine in
13301324 *-digital*)
1331 basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
1325 basic_machine=`echo "$basic_machine" | sed 's/digital.*/dec/'`
13321326 ;;
13331327 *-commodore*)
1334 basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
1328 basic_machine=`echo "$basic_machine" | sed 's/commodore.*/cbm/'`
13351329 ;;
13361330 *)
13371331 ;;
13421336 if [ x"$os" != x"" ]
13431337 then
13441338 case $os in
1345 # First match some system type aliases
1346 # that might get confused with valid system types.
1339 # First match some system type aliases that might get confused
1340 # with valid system types.
13471341 # -solaris* is a basic system type, with this one exception.
13481342 -auroraux)
13491343 os=-auroraux
13541348 -solaris)
13551349 os=-solaris2
13561350 ;;
1357 -svr4*)
1358 os=-sysv4
1359 ;;
13601351 -unixware*)
13611352 os=-sysv4.2uw
13621353 ;;
13631354 -gnu/linux*)
13641355 os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
13651356 ;;
1366 # First accept the basic system types.
1357 # es1800 is here to avoid being matched by es* (a different OS)
1358 -es1800*)
1359 os=-ose
1360 ;;
1361 # Now accept the basic system types.
13671362 # The portable systems comes first.
1368 # Each alternative MUST END IN A *, to match a version number.
1363 # Each alternative MUST end in a * to match a version number.
13691364 # -sysv* is not here because it comes later, after sysvr4.
13701365 -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
13711366 | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\
13721367 | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \
13731368 | -sym* | -kopensolaris* | -plan9* \
13741369 | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
1375 | -aos* | -aros* \
1370 | -aos* | -aros* | -cloudabi* | -sortix* \
13761371 | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
13771372 | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
1378 | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
1379 | -bitrig* | -openbsd* | -solidbsd* \
1373 | -hiux* | -knetbsd* | -mirbsd* | -netbsd* \
1374 | -bitrig* | -openbsd* | -solidbsd* | -libertybsd* \
13801375 | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
13811376 | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
13821377 | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
1383 | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
1384 | -chorusos* | -chorusrdb* | -cegcc* \
1378 | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* | -hcos* \
1379 | -chorusos* | -chorusrdb* | -cegcc* | -glidix* \
13851380 | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
1386 | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \
1381 | -midipix* | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \
13871382 | -linux-newlib* | -linux-musl* | -linux-uclibc* \
13881383 | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \
1389 | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
1384 | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* \
13901385 | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
13911386 | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
13921387 | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
1393 | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
1388 | -morphos* | -superux* | -rtmk* | -windiss* \
13941389 | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
1395 | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* | -tirtos*)
1390 | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* \
1391 | -onefs* | -tirtos* | -phoenix* | -fuchsia* | -redox* | -bme* \
1392 | -midnightbsd*)
13961393 # Remember, each alternative MUST END IN *, to match a version number.
13971394 ;;
13981395 -qnx*)
14091406 -nto*)
14101407 os=`echo $os | sed -e 's|nto|nto-qnx|'`
14111408 ;;
1412 -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
1413 | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \
1409 -sim | -xray | -os68k* | -v88r* \
1410 | -windows* | -osx | -abug | -netware* | -os9* \
14141411 | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
14151412 ;;
14161413 -mac*)
1417 os=`echo $os | sed -e 's|mac|macos|'`
1414 os=`echo "$os" | sed -e 's|mac|macos|'`
14181415 ;;
14191416 -linux-dietlibc)
14201417 os=-linux-dietlibc
14231420 os=`echo $os | sed -e 's|linux|linux-gnu|'`
14241421 ;;
14251422 -sunos5*)
1426 os=`echo $os | sed -e 's|sunos5|solaris2|'`
1423 os=`echo "$os" | sed -e 's|sunos5|solaris2|'`
14271424 ;;
14281425 -sunos6*)
1429 os=`echo $os | sed -e 's|sunos6|solaris3|'`
1426 os=`echo "$os" | sed -e 's|sunos6|solaris3|'`
14301427 ;;
14311428 -opened*)
14321429 os=-openedition
14371434 -wince*)
14381435 os=-wince
14391436 ;;
1440 -osfrose*)
1441 os=-osfrose
1442 ;;
1443 -osf*)
1444 os=-osf
1445 ;;
14461437 -utek*)
14471438 os=-bsd
14481439 ;;
14671458 -nova*)
14681459 os=-rtmk-nova
14691460 ;;
1470 -ns2 )
1461 -ns2)
14711462 os=-nextstep2
14721463 ;;
14731464 -nsk*)
14891480 -oss*)
14901481 os=-sysv3
14911482 ;;
1492 -svr4)
1483 -svr4*)
14931484 os=-sysv4
14941485 ;;
14951486 -svr3)
15041495 -ose*)
15051496 os=-ose
15061497 ;;
1507 -es1800*)
1508 os=-ose
1509 ;;
1510 -xenix)
1511 os=-xenix
1512 ;;
15131498 -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
15141499 os=-mint
15151500 ;;
1516 -aros*)
1517 os=-aros
1518 ;;
15191501 -zvmoe)
15201502 os=-zvmoe
15211503 ;;
15221504 -dicos*)
15231505 os=-dicos
15241506 ;;
1507 -pikeos*)
1508 # Until real need of OS specific support for
1509 # particular features comes up, bare metal
1510 # configurations are quite functional.
1511 case $basic_machine in
1512 arm*)
1513 os=-eabi
1514 ;;
1515 *)
1516 os=-elf
1517 ;;
1518 esac
1519 ;;
15251520 -nacl*)
1521 ;;
1522 -ios)
15261523 ;;
15271524 -none)
15281525 ;;
15291526 *)
15301527 # Get rid of the `-' at the beginning of $os.
15311528 os=`echo $os | sed 's/[^-]*-//'`
1532 echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
1529 echo Invalid configuration \`"$1"\': system \`"$os"\' not recognized 1>&2
15331530 exit 1
15341531 ;;
15351532 esac
16191616 sparc-* | *-sun)
16201617 os=-sunos4.1.1
16211618 ;;
1619 pru-*)
1620 os=-elf
1621 ;;
16221622 *-be)
16231623 os=-beos
16241624 ;;
1625 *-haiku)
1626 os=-haiku
1627 ;;
16281625 *-ibm)
16291626 os=-aix
16301627 ;;
16641661 m88k-omron*)
16651662 os=-luna
16661663 ;;
1667 *-next )
1664 *-next)
16681665 os=-nextstep
16691666 ;;
16701667 *-sequent)
16781675 ;;
16791676 i370-*)
16801677 os=-mvs
1681 ;;
1682 *-next)
1683 os=-nextstep3
16841678 ;;
16851679 *-gould)
16861680 os=-sysv
17911785 vendor=stratus
17921786 ;;
17931787 esac
1794 basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
1788 basic_machine=`echo "$basic_machine" | sed "s/unknown/$vendor/"`
17951789 ;;
17961790 esac
17971791
1798 echo $basic_machine$os
1792 echo "$basic_machine$os"
17991793 exit
18001794
18011795 # Local variables:
1802 # eval: (add-hook 'write-file-hooks 'time-stamp)
1796 # eval: (add-hook 'before-save-hook 'time-stamp)
18031797 # time-stamp-start: "timestamp='"
18041798 # time-stamp-format: "%:y-%02m-%02d"
18051799 # time-stamp-end: "'"
00 #! /bin/sh
11 # Guess values for system-dependent variables and create Makefiles.
2 # Generated by GNU Autoconf 2.69 for libass 0.14.0.
2 # Generated by GNU Autoconf 2.69 for libass 0.15.0.
33 #
44 #
55 # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
586586 # Identity of this package.
587587 PACKAGE_NAME='libass'
588588 PACKAGE_TARNAME='libass'
589 PACKAGE_VERSION='0.14.0'
590 PACKAGE_STRING='libass 0.14.0'
589 PACKAGE_VERSION='0.15.0'
590 PACKAGE_STRING='libass 0.15.0'
591591 PACKAGE_BUGREPORT=''
592592 PACKAGE_URL=''
593593
638638 PKG_LIBS_DEFAULT
639639 ENABLE_PROFILE_FALSE
640640 ENABLE_PROFILE_TRUE
641 HAVE_LIBPNG_FALSE
642 HAVE_LIBPNG_TRUE
641 ENABLE_COMPARE_FALSE
642 ENABLE_COMPARE_TRUE
643 ENABLE_TEST_FALSE
644 ENABLE_TEST_TRUE
643645 LIBPNG_LIBS
644646 LIBPNG_CFLAGS
645647 HARFBUZZ_LIBS
706708 AMDEPBACKSLASH
707709 AMDEP_FALSE
708710 AMDEP_TRUE
709 am__quote
710711 am__include
711712 DEPDIR
712713 OBJEXT
789790 PACKAGE_TARNAME
790791 PACKAGE_NAME
791792 PATH_SEPARATOR
792 SHELL'
793 SHELL
794 am__quote'
793795 ac_subst_files=''
794796 ac_user_opts='
795797 enable_option_checking
804806 with_sysroot
805807 enable_libtool_lock
806808 enable_test
809 enable_compare
807810 enable_profile
808811 enable_fontconfig
809812 enable_directwrite
810813 enable_coretext
811814 enable_require_system_font_provider
812 enable_harfbuzz
813815 enable_asm
814816 enable_large_tiles
815817 '
13781380 # Omit some internal or obsolete options to make the list less imposing.
13791381 # This message is too long to be a string in the A/UX 3.1 sh.
13801382 cat <<_ACEOF
1381 \`configure' configures libass 0.14.0 to adapt to many kinds of systems.
1383 \`configure' configures libass 0.15.0 to adapt to many kinds of systems.
13821384
13831385 Usage: $0 [OPTION]... [VAR=VALUE]...
13841386
14481450
14491451 if test -n "$ac_init_help"; then
14501452 case $ac_init_help in
1451 short | recursive ) echo "Configuration of libass 0.14.0:";;
1453 short | recursive ) echo "Configuration of libass 0.15.0:";;
14521454 esac
14531455 cat <<\_ACEOF
14541456
14681470 speeds up one-time build
14691471 --disable-libtool-lock avoid locking (might break parallel builds)
14701472 --enable-test enable test program (requires libpng) [default=no]
1473 --enable-compare enable compare program (requires libpng)
1474 [default=no]
14711475 --enable-profile enable profiling program [default=no]
14721476 --disable-fontconfig disable fontconfig support [default=enabled]
14731477 --disable-directwrite disable DirectWrite support (win32 only)
14761480 --disable-require-system-font-provider
14771481 allow compilation even if no system font provider
14781482 was found [default=enabled:>@
1479 --disable-harfbuzz disable HarfBuzz support [default=check]
14801483 --disable-asm disable compiling with ASM [default=check]
14811484 --enable-large-tiles use larger tiles in the rasterizer (better
14821485 performance, slightly worse quality)
15981601 test -n "$ac_init_help" && exit $ac_status
15991602 if $ac_init_version; then
16001603 cat <<\_ACEOF
1601 libass configure 0.14.0
1604 libass configure 0.15.0
16021605 generated by GNU Autoconf 2.69
16031606
16041607 Copyright (C) 2012 Free Software Foundation, Inc.
20172020 This file contains any messages produced by compilers while
20182021 running configure, to aid debugging if configure makes a mistake.
20192022
2020 It was created by libass $as_me 0.14.0, which was
2023 It was created by libass $as_me 0.15.0, which was
20212024 generated by GNU Autoconf 2.69. Invocation command line was
20222025
20232026 $ $0 $@
23652368 ac_compiler_gnu=$ac_cv_c_compiler_gnu
23662369
23672370
2368 am__api_version='1.15'
2371 am__api_version='1.16'
23692372
23702373 ac_aux_dir=
23712374 for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do
28802883
28812884 # Define the identity of the package.
28822885 PACKAGE='libass'
2883 VERSION='0.14.0'
2886 VERSION='0.15.0'
28842887
28852888
28862889 cat >>confdefs.h <<_ACEOF
29102913
29112914 # For better backward compatibility. To be removed once Automake 1.9.x
29122915 # dies out for good. For more background, see:
2913 # <http://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
2914 # <http://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
2916 # <https://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
2917 # <https://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
29152918 mkdir_p='$(MKDIR_P)'
29162919
29172920 # We need awk for the "check" target (and possibly the TAP driver). The
29622965 Aborting the configuration process, to ensure you take notice of the issue.
29632966
29642967 You can download and install GNU coreutils to get an 'rm' implementation
2965 that behaves properly: <http://www.gnu.org/software/coreutils/>.
2968 that behaves properly: <https://www.gnu.org/software/coreutils/>.
29662969
29672970 If you want to complete the configuration process using your problematic
29682971 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM
31453148
31463149 ac_config_commands="$ac_config_commands depfiles"
31473150
3148
3149 am_make=${MAKE-make}
3150 cat > confinc << 'END'
3151 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} supports the include directive" >&5
3152 $as_echo_n "checking whether ${MAKE-make} supports the include directive... " >&6; }
3153 cat > confinc.mk << 'END'
31513154 am__doit:
3152 @echo this is the am__doit target
3155 @echo this is the am__doit target >confinc.out
31533156 .PHONY: am__doit
31543157 END
3155 # If we don't find an include directive, just comment out the code.
3156 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5
3157 $as_echo_n "checking for style of include used by $am_make... " >&6; }
31583158 am__include="#"
31593159 am__quote=
3160 _am_result=none
3161 # First try GNU make style include.
3162 echo "include confinc" > confmf
3163 # Ignore all kinds of additional output from 'make'.
3164 case `$am_make -s -f confmf 2> /dev/null` in #(
3165 *the\ am__doit\ target*)
3166 am__include=include
3167 am__quote=
3168 _am_result=GNU
3169 ;;
3160 # BSD make does it like this.
3161 echo '.include "confinc.mk" # ignored' > confmf.BSD
3162 # Other make implementations (GNU, Solaris 10, AIX) do it like this.
3163 echo 'include confinc.mk # ignored' > confmf.GNU
3164 _am_result=no
3165 for s in GNU BSD; do
3166 { echo "$as_me:$LINENO: ${MAKE-make} -f confmf.$s && cat confinc.out" >&5
3167 (${MAKE-make} -f confmf.$s && cat confinc.out) >&5 2>&5
3168 ac_status=$?
3169 echo "$as_me:$LINENO: \$? = $ac_status" >&5
3170 (exit $ac_status); }
3171 case $?:`cat confinc.out 2>/dev/null` in #(
3172 '0:this is the am__doit target') :
3173 case $s in #(
3174 BSD) :
3175 am__include='.include' am__quote='"' ;; #(
3176 *) :
3177 am__include='include' am__quote='' ;;
3178 esac ;; #(
3179 *) :
3180 ;;
31703181 esac
3171 # Now try BSD make style include.
3172 if test "$am__include" = "#"; then
3173 echo '.include "confinc"' > confmf
3174 case `$am_make -s -f confmf 2> /dev/null` in #(
3175 *the\ am__doit\ target*)
3176 am__include=.include
3177 am__quote="\""
3178 _am_result=BSD
3179 ;;
3180 esac
3181 fi
3182
3183
3184 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5
3185 $as_echo "$_am_result" >&6; }
3186 rm -f confinc confmf
3182 if test "$am__include" != "#"; then
3183 _am_result="yes ($s style)"
3184 break
3185 fi
3186 done
3187 rm -f confinc.* confmf.*
3188 { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${_am_result}" >&5
3189 $as_echo "${_am_result}" >&6; }
31873190
31883191 # Check whether --enable-dependency-tracking was given.
31893192 if test "${enable_dependency_tracking+set}" = set; then :
74397442 _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;;
74407443 darwin1.*)
74417444 _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;;
7442 darwin*) # darwin 5.x on
7443 # if running on 10.5 or later, the deployment target defaults
7444 # to the OS version, if on x86, and 10.4, the deployment
7445 # target defaults to 10.4. Don't you love it?
7446 case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in
7447 10.0,*86*-darwin8*|10.0,*-darwin[91]*)
7448 _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;;
7449 10.[012][,.]*)
7445 darwin*)
7446 case ${MACOSX_DEPLOYMENT_TARGET},$host in
7447 10.[012],*|,*powerpc*)
74507448 _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;;
7451 10.*)
7449 *)
74527450 _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;;
74537451 esac
74547452 ;;
1349213490 enableval=$enable_test;
1349313491 fi
1349413492
13493 # Check whether --enable-compare was given.
13494 if test "${enable_compare+set}" = set; then :
13495 enableval=$enable_compare;
13496 fi
13497
1349513498 # Check whether --enable-profile was given.
1349613499 if test "${enable_profile+set}" = set; then :
1349713500 enableval=$enable_profile;
1351513518 # Check whether --enable-require-system-font-provider was given.
1351613519 if test "${enable_require_system_font_provider+set}" = set; then :
1351713520 enableval=$enable_require_system_font_provider;
13518 fi
13519
13520 # Check whether --enable-harfbuzz was given.
13521 if test "${enable_harfbuzz+set}" = set; then :
13522 enableval=$enable_harfbuzz;
1352313521 fi
1352413522
1352513523 # Check whether --enable-asm was given.
1425914257 fi
1426014258
1426114259
14262 if test x$enable_harfbuzz != xno; then
1426314260
1426414261 pkg_failed=no
14265 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for harfbuzz >= 0.9.5" >&5
14266 $as_echo_n "checking for harfbuzz >= 0.9.5... " >&6; }
14262 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for harfbuzz >= 1.2.3" >&5
14263 $as_echo_n "checking for harfbuzz >= 1.2.3... " >&6; }
1426714264
1426814265 if test -n "$HARFBUZZ_CFLAGS"; then
1426914266 pkg_cv_HARFBUZZ_CFLAGS="$HARFBUZZ_CFLAGS"
1427014267 elif test -n "$PKG_CONFIG"; then
1427114268 if test -n "$PKG_CONFIG" && \
14272 { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"harfbuzz >= 0.9.5\""; } >&5
14273 ($PKG_CONFIG --exists --print-errors "harfbuzz >= 0.9.5") 2>&5
14269 { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"harfbuzz >= 1.2.3\""; } >&5
14270 ($PKG_CONFIG --exists --print-errors "harfbuzz >= 1.2.3") 2>&5
1427414271 ac_status=$?
1427514272 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
1427614273 test $ac_status = 0; }; then
14277 pkg_cv_HARFBUZZ_CFLAGS=`$PKG_CONFIG --cflags "harfbuzz >= 0.9.5" 2>/dev/null`
14274 pkg_cv_HARFBUZZ_CFLAGS=`$PKG_CONFIG --cflags "harfbuzz >= 1.2.3" 2>/dev/null`
1427814275 test "x$?" != "x0" && pkg_failed=yes
1427914276 else
1428014277 pkg_failed=yes
1428614283 pkg_cv_HARFBUZZ_LIBS="$HARFBUZZ_LIBS"
1428714284 elif test -n "$PKG_CONFIG"; then
1428814285 if test -n "$PKG_CONFIG" && \
14289 { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"harfbuzz >= 0.9.5\""; } >&5
14290 ($PKG_CONFIG --exists --print-errors "harfbuzz >= 0.9.5") 2>&5
14286 { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"harfbuzz >= 1.2.3\""; } >&5
14287 ($PKG_CONFIG --exists --print-errors "harfbuzz >= 1.2.3") 2>&5
1429114288 ac_status=$?
1429214289 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
1429314290 test $ac_status = 0; }; then
14294 pkg_cv_HARFBUZZ_LIBS=`$PKG_CONFIG --libs "harfbuzz >= 0.9.5" 2>/dev/null`
14291 pkg_cv_HARFBUZZ_LIBS=`$PKG_CONFIG --libs "harfbuzz >= 1.2.3" 2>/dev/null`
1429514292 test "x$?" != "x0" && pkg_failed=yes
1429614293 else
1429714294 pkg_failed=yes
1431214309 _pkg_short_errors_supported=no
1431314310 fi
1431414311 if test $_pkg_short_errors_supported = yes; then
14315 HARFBUZZ_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "harfbuzz >= 0.9.5" 2>&1`
14312 HARFBUZZ_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "harfbuzz >= 1.2.3" 2>&1`
1431614313 else
14317 HARFBUZZ_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "harfbuzz >= 0.9.5" 2>&1`
14314 HARFBUZZ_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "harfbuzz >= 1.2.3" 2>&1`
1431814315 fi
1431914316 # Put the nasty error message in config.log where it belongs
1432014317 echo "$HARFBUZZ_PKG_ERRORS" >&5
1432114318
14322 harfbuzz=false
14319 as_fn_error $? "Package requirements (harfbuzz >= 1.2.3) were not met:
14320
14321 $HARFBUZZ_PKG_ERRORS
14322
14323 Consider adjusting the PKG_CONFIG_PATH environment variable if you
14324 installed software in a non-standard prefix.
14325
14326 Alternatively, you may set the environment variables HARFBUZZ_CFLAGS
14327 and HARFBUZZ_LIBS to avoid the need to call pkg-config.
14328 See the pkg-config man page for more details." "$LINENO" 5
1432314329 elif test $pkg_failed = untried; then
1432414330 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
1432514331 $as_echo "no" >&6; }
14326 harfbuzz=false
14332 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
14333 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
14334 as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it
14335 is in your PATH or set the PKG_CONFIG environment variable to the full
14336 path to pkg-config.
14337
14338 Alternatively, you may set the environment variables HARFBUZZ_CFLAGS
14339 and HARFBUZZ_LIBS to avoid the need to call pkg-config.
14340 See the pkg-config man page for more details.
14341
14342 To get pkg-config, see <http://pkg-config.freedesktop.org/>.
14343 See \`config.log' for more details" "$LINENO" 5; }
1432714344 else
1432814345 HARFBUZZ_CFLAGS=$pkg_cv_HARFBUZZ_CFLAGS
1432914346 HARFBUZZ_LIBS=$pkg_cv_HARFBUZZ_LIBS
1433514352
1433614353 $as_echo "#define CONFIG_HARFBUZZ 1" >>confdefs.h
1433714354
14338 harfbuzz=true
14339
14340 fi
14355
1434114356 fi
1434214357
1434314358 libpng=false
14344 if test x$enable_test = xyes; then
14359 if test x$enable_test = xyes || test x$enable_compare = xyes; then
1434514360
1434614361 pkg_failed=no
1434714362 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libpng >= 1.2.0" >&5
1444014455 fi
1444114456 fi
1444214457
14443 if test x$libpng = xtrue; then
14444 HAVE_LIBPNG_TRUE=
14445 HAVE_LIBPNG_FALSE='#'
14446 else
14447 HAVE_LIBPNG_TRUE='#'
14448 HAVE_LIBPNG_FALSE=
14458 if test x$enable_test = xyes && test x$libpng = xtrue; then
14459 ENABLE_TEST_TRUE=
14460 ENABLE_TEST_FALSE='#'
14461 else
14462 ENABLE_TEST_TRUE='#'
14463 ENABLE_TEST_FALSE=
14464 fi
14465
14466 if test x$enable_compare = xyes && test x$libpng = xtrue; then
14467 ENABLE_COMPARE_TRUE=
14468 ENABLE_COMPARE_FALSE='#'
14469 else
14470 ENABLE_COMPARE_TRUE='#'
14471 ENABLE_COMPARE_FALSE=
1444914472 fi
1445014473
1445114474
1447014493 fi
1447114494 pkg_requires="freetype2 >= 9.10.3"
1447214495 pkg_requires="fribidi >= 0.19.0, ${pkg_requires}"
14496 pkg_requires="harfbuzz >= 1.2.3, ${pkg_requires}"
1447314497 if test x$fontconfig = xtrue; then
1447414498 pkg_requires="fontconfig >= 2.10.92, ${pkg_requires}"
14475 fi
14476 if test x$harfbuzz = xtrue; then
14477 pkg_requires="harfbuzz >= 0.9.5, ${pkg_requires}"
1447814499 fi
1447914500
1448014501 if test x$enable_require_system_font_provider != xno &&
1453814559 AM_BACKSLASH='\'
1453914560
1454014561
14541 ac_config_files="$ac_config_files Makefile libass/Makefile test/Makefile profile/Makefile libass.pc"
14562 ac_config_files="$ac_config_files Makefile libass/Makefile test/Makefile compare/Makefile profile/Makefile libass.pc"
1454214563
1454314564 cat >confcache <<\_ACEOF
1454414565 # This file is a shell script that caches the results of configure
1471314734 as_fn_error $? "conditional \"DIRECTWRITE\" was never defined.
1471414735 Usually this means the macro was only invoked conditionally." "$LINENO" 5
1471514736 fi
14716 if test -z "${HAVE_LIBPNG_TRUE}" && test -z "${HAVE_LIBPNG_FALSE}"; then
14717 as_fn_error $? "conditional \"HAVE_LIBPNG\" was never defined.
14737 if test -z "${ENABLE_TEST_TRUE}" && test -z "${ENABLE_TEST_FALSE}"; then
14738 as_fn_error $? "conditional \"ENABLE_TEST\" was never defined.
14739 Usually this means the macro was only invoked conditionally." "$LINENO" 5
14740 fi
14741 if test -z "${ENABLE_COMPARE_TRUE}" && test -z "${ENABLE_COMPARE_FALSE}"; then
14742 as_fn_error $? "conditional \"ENABLE_COMPARE\" was never defined.
1471814743 Usually this means the macro was only invoked conditionally." "$LINENO" 5
1471914744 fi
1472014745 if test -z "${ENABLE_PROFILE_TRUE}" && test -z "${ENABLE_PROFILE_FALSE}"; then
1511815143 # report actual input values of CONFIG_FILES etc. instead of their
1511915144 # values after options handling.
1512015145 ac_log="
15121 This file was extended by libass $as_me 0.14.0, which was
15146 This file was extended by libass $as_me 0.15.0, which was
1512215147 generated by GNU Autoconf 2.69. Invocation command line was
1512315148
1512415149 CONFIG_FILES = $CONFIG_FILES
1518415209 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
1518515210 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
1518615211 ac_cs_version="\\
15187 libass config.status 0.14.0
15212 libass config.status 0.15.0
1518815213 configured by $0, generated by GNU Autoconf 2.69,
1518915214 with options \\"\$ac_cs_config\\"
1519015215
1530315328 #
1530415329 # INIT-COMMANDS
1530515330 #
15306 AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"
15331 AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}"
1530715332
1530815333
1530915334 # The HP-UX ksh and POSIX shell print the target directory to stdout
1560215627 "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
1560315628 "libass/Makefile") CONFIG_FILES="$CONFIG_FILES libass/Makefile" ;;
1560415629 "test/Makefile") CONFIG_FILES="$CONFIG_FILES test/Makefile" ;;
15630 "compare/Makefile") CONFIG_FILES="$CONFIG_FILES compare/Makefile" ;;
1560515631 "profile/Makefile") CONFIG_FILES="$CONFIG_FILES profile/Makefile" ;;
1560615632 "libass.pc") CONFIG_FILES="$CONFIG_FILES libass.pc" ;;
1560715633
1620316229 # Older Autoconf quotes --file arguments for eval, but not when files
1620416230 # are listed without --file. Let's play safe and only enable the eval
1620516231 # if we detect the quoting.
16206 case $CONFIG_FILES in
16207 *\'*) eval set x "$CONFIG_FILES" ;;
16208 *) set x $CONFIG_FILES ;;
16209 esac
16232 # TODO: see whether this extra hack can be removed once we start
16233 # requiring Autoconf 2.70 or later.
16234 case $CONFIG_FILES in #(
16235 *\'*) :
16236 eval set x "$CONFIG_FILES" ;; #(
16237 *) :
16238 set x $CONFIG_FILES ;; #(
16239 *) :
16240 ;;
16241 esac
1621016242 shift
16211 for mf
16243 # Used to flag and report bootstrapping failures.
16244 am_rc=0
16245 for am_mf
1621216246 do
1621316247 # Strip MF so we end up with the name of the file.
16214 mf=`echo "$mf" | sed -e 's/:.*$//'`
16215 # Check whether this is an Automake generated Makefile or not.
16216 # We used to match only the files named 'Makefile.in', but
16217 # some people rename them; so instead we look at the file content.
16218 # Grep'ing the first line is not enough: some people post-process
16219 # each Makefile.in and add a new line on top of each file to say so.
16220 # Grep'ing the whole file is not good either: AIX grep has a line
16248 am_mf=`$as_echo "$am_mf" | sed -e 's/:.*$//'`
16249 # Check whether this is an Automake generated Makefile which includes
16250 # dependency-tracking related rules and includes.
16251 # Grep'ing the whole file directly is not great: AIX grep has a line
1622116252 # limit of 2048, but all sed's we know have understand at least 4000.
16222 if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then
16223 dirpart=`$as_dirname -- "$mf" ||
16224 $as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
16225 X"$mf" : 'X\(//\)[^/]' \| \
16226 X"$mf" : 'X\(//\)$' \| \
16227 X"$mf" : 'X\(/\)' \| . 2>/dev/null ||
16228 $as_echo X"$mf" |
16253 sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \
16254 || continue
16255 am_dirpart=`$as_dirname -- "$am_mf" ||
16256 $as_expr X"$am_mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
16257 X"$am_mf" : 'X\(//\)[^/]' \| \
16258 X"$am_mf" : 'X\(//\)$' \| \
16259 X"$am_mf" : 'X\(/\)' \| . 2>/dev/null ||
16260 $as_echo X"$am_mf" |
1622916261 sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
1623016262 s//\1/
1623116263 q
1624316275 q
1624416276 }
1624516277 s/.*/./; q'`
16246 else
16247 continue
16248 fi
16249 # Extract the definition of DEPDIR, am__include, and am__quote
16250 # from the Makefile without running 'make'.
16251 DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
16252 test -z "$DEPDIR" && continue
16253 am__include=`sed -n 's/^am__include = //p' < "$mf"`
16254 test -z "$am__include" && continue
16255 am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
16256 # Find all dependency output files, they are included files with
16257 # $(DEPDIR) in their names. We invoke sed twice because it is the
16258 # simplest approach to changing $(DEPDIR) to its actual value in the
16259 # expansion.
16260 for file in `sed -n "
16261 s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
16262 sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do
16263 # Make sure the directory exists.
16264 test -f "$dirpart/$file" && continue
16265 fdir=`$as_dirname -- "$file" ||
16266 $as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
16267 X"$file" : 'X\(//\)[^/]' \| \
16268 X"$file" : 'X\(//\)$' \| \
16269 X"$file" : 'X\(/\)' \| . 2>/dev/null ||
16270 $as_echo X"$file" |
16271 sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
16278 am_filepart=`$as_basename -- "$am_mf" ||
16279 $as_expr X/"$am_mf" : '.*/\([^/][^/]*\)/*$' \| \
16280 X"$am_mf" : 'X\(//\)$' \| \
16281 X"$am_mf" : 'X\(/\)' \| . 2>/dev/null ||
16282 $as_echo X/"$am_mf" |
16283 sed '/^.*\/\([^/][^/]*\)\/*$/{
1627216284 s//\1/
1627316285 q
1627416286 }
16275 /^X\(\/\/\)[^/].*/{
16287 /^X\/\(\/\/\)$/{
1627616288 s//\1/
1627716289 q
1627816290 }
16279 /^X\(\/\/\)$/{
16280 s//\1/
16281 q
16282 }
16283 /^X\(\/\).*/{
16291 /^X\/\(\/\).*/{
1628416292 s//\1/
1628516293 q
1628616294 }
1628716295 s/.*/./; q'`
16288 as_dir=$dirpart/$fdir; as_fn_mkdir_p
16289 # echo "creating $dirpart/$file"
16290 echo '# dummy' > "$dirpart/$file"
16291 done
16296 { echo "$as_me:$LINENO: cd "$am_dirpart" \
16297 && sed -e '/# am--include-marker/d' "$am_filepart" \
16298 | $MAKE -f - am--depfiles" >&5
16299 (cd "$am_dirpart" \
16300 && sed -e '/# am--include-marker/d' "$am_filepart" \
16301 | $MAKE -f - am--depfiles) >&5 2>&5
16302 ac_status=$?
16303 echo "$as_me:$LINENO: \$? = $ac_status" >&5
16304 (exit $ac_status); } || am_rc=$?
1629216305 done
16306 if test $am_rc -ne 0; then
16307 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
16308 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
16309 as_fn_error $? "Something went wrong bootstrapping makefile fragments
16310 for automatic dependency tracking. Try re-running configure with the
16311 '--disable-dependency-tracking' option to at least be able to build
16312 the package (albeit without support for automatic dependency tracking).
16313 See \`config.log' for more details" "$LINENO" 5; }
16314 fi
16315 { am_dirpart=; unset am_dirpart;}
16316 { am_filepart=; unset am_filepart;}
16317 { am_mf=; unset am_mf;}
16318 { am_rc=; unset am_rc;}
16319 rm -f conftest-deps.mk
1629316320 }
1629416321 ;;
1629516322 "libtool":C)
0 AC_INIT(libass, 0.14.0)
0 AC_INIT(libass, 0.15.0)
11 AM_INIT_AUTOMAKE
22 AC_CONFIG_MACRO_DIR([m4])
33 # Disable Fortran checks
3434 # Check for libraries via pkg-config
3535 AC_ARG_ENABLE([test], AS_HELP_STRING([--enable-test],
3636 [enable test program (requires libpng) @<:@default=no@:>@]))
37 AC_ARG_ENABLE([compare], AS_HELP_STRING([--enable-compare],
38 [enable compare program (requires libpng) @<:@default=no@:>@]))
3739 AC_ARG_ENABLE([profile], AS_HELP_STRING([--enable-profile],
3840 [enable profiling program @<:@default=no@:>@]))
3941 AC_ARG_ENABLE([fontconfig], AS_HELP_STRING([--disable-fontconfig],
4446 [disable CoreText support (OSX only) @<:@default=check@:>@]))
4547 AC_ARG_ENABLE([require-system-font-provider], AS_HELP_STRING([--disable-require-system-font-provider],
4648 [allow compilation even if no system font provider was found @<:@default=enabled:>@]))
47 AC_ARG_ENABLE([harfbuzz], AS_HELP_STRING([--disable-harfbuzz],
48 [disable HarfBuzz support @<:@default=check@:>@]))
4949 AC_ARG_ENABLE([asm], AS_HELP_STRING([--disable-asm],
5050 [disable compiling with ASM @<:@default=check@:>@]))
5151 AC_ARG_ENABLE([large-tiles], AS_HELP_STRING([--enable-large-tiles],
207207 fi
208208 AM_CONDITIONAL([DIRECTWRITE], [test x$directwrite = xtrue])
209209
210 if test x$enable_harfbuzz != xno; then
211 PKG_CHECK_MODULES([HARFBUZZ], harfbuzz >= 0.9.5, [
210 PKG_CHECK_MODULES([HARFBUZZ], harfbuzz >= 1.2.3, [
212211 CFLAGS="$CFLAGS $HARFBUZZ_CFLAGS"
213212 LIBS="$LIBS $HARFBUZZ_LIBS"
214 AC_DEFINE(CONFIG_HARFBUZZ, 1, [found harfbuzz-ng via pkg-config])
215 harfbuzz=true
216 ], [harfbuzz=false])
217 fi
213 AC_DEFINE(CONFIG_HARFBUZZ, 1, [found harfbuzz via pkg-config])
214 ])
218215
219216 libpng=false
220 if test x$enable_test = xyes; then
217 if test x$enable_test = xyes || test x$enable_compare = xyes; then
221218 PKG_CHECK_MODULES([LIBPNG], libpng >= 1.2.0, [
222219 CFLAGS="$CFLAGS $LIBPNG_CFLAGS"
223220 AC_DEFINE(CONFIG_LIBPNG, 1, [found libpng via pkg-config])
224221 libpng=true])
225222 fi
226223
227 AM_CONDITIONAL([HAVE_LIBPNG], [test x$libpng = xtrue])
224 AM_CONDITIONAL([ENABLE_TEST], [test x$enable_test = xyes && test x$libpng = xtrue])
225 AM_CONDITIONAL([ENABLE_COMPARE], [test x$enable_compare = xyes && test x$libpng = xtrue])
228226
229227 AM_CONDITIONAL([ENABLE_PROFILE], [test x$enable_profile = xyes])
230228
238236 fi
239237 pkg_requires="freetype2 >= 9.10.3"
240238 pkg_requires="fribidi >= 0.19.0, ${pkg_requires}"
239 pkg_requires="harfbuzz >= 1.2.3, ${pkg_requires}"
241240 if test x$fontconfig = xtrue; then
242241 pkg_requires="fontconfig >= 2.10.92, ${pkg_requires}"
243 fi
244 if test x$harfbuzz = xtrue; then
245 pkg_requires="harfbuzz >= 0.9.5, ${pkg_requires}"
246242 fi
247243
248244 if test x$enable_require_system_font_provider != xno &&
264260 # Setup output beautifier.
265261 m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
266262
267 AC_CONFIG_FILES([Makefile libass/Makefile test/Makefile profile/Makefile libass.pc])
263 AC_CONFIG_FILES([Makefile libass/Makefile test/Makefile compare/Makefile profile/Makefile libass.pc])
268264 AC_OUTPUT
00 #! /bin/sh
11 # depcomp - compile a program generating dependencies as side-effects
22
3 scriptversion=2013-05-30.07; # UTC
4
5 # Copyright (C) 1999-2014 Free Software Foundation, Inc.
3 scriptversion=2018-03-07.03; # UTC
4
5 # Copyright (C) 1999-2018 Free Software Foundation, Inc.
66
77 # This program is free software; you can redistribute it and/or modify
88 # it under the terms of the GNU General Public License as published by
1515 # GNU General Public License for more details.
1616
1717 # You should have received a copy of the GNU General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18 # along with this program. If not, see <https://www.gnu.org/licenses/>.
1919
2020 # As a special exception to the GNU General Public License, if you
2121 # distribute this file as part of a program that contains a
782782 # Local Variables:
783783 # mode: shell-script
784784 # sh-indentation: 2
785 # eval: (add-hook 'write-file-hooks 'time-stamp)
785 # eval: (add-hook 'before-save-hook 'time-stamp)
786786 # time-stamp-start: "scriptversion="
787787 # time-stamp-format: "%:y-%02m-%02d.%02H"
788 # time-stamp-time-zone: "UTC"
788 # time-stamp-time-zone: "UTC0"
789789 # time-stamp-end: "; # UTC"
790790 # End:
00 #!/bin/sh
11 # install - install a program, script, or datafile
22
3 scriptversion=2013-12-25.23; # UTC
3 scriptversion=2018-03-11.20; # UTC
44
55 # This originates from X11R5 (mit/util/scripts/install.sh), which was
66 # later released in X11R6 (xc/config/util/install.sh) with the
270270 fi
271271 dst=$dst_arg
272272
273 # If destination is a directory, append the input filename; won't work
274 # if double slashes aren't ignored.
273 # If destination is a directory, append the input filename.
275274 if test -d "$dst"; then
276275 if test "$is_target_a_directory" = never; then
277276 echo "$0: $dst_arg: Is a directory" >&2
278277 exit 1
279278 fi
280279 dstdir=$dst
281 dst=$dstdir/`basename "$src"`
280 dstbase=`basename "$src"`
281 case $dst in
282 */) dst=$dst$dstbase;;
283 *) dst=$dst/$dstbase;;
284 esac
282285 dstdir_status=0
283286 else
284287 dstdir=`dirname "$dst"`
286289 dstdir_status=$?
287290 fi
288291 fi
292
293 case $dstdir in
294 */) dstdirslash=$dstdir;;
295 *) dstdirslash=$dstdir/;;
296 esac
289297
290298 obsolete_mkdir_used=false
291299
323331 # is incompatible with FreeBSD 'install' when (umask & 300) != 0.
324332 ;;
325333 *)
334 # Note that $RANDOM variable is not portable (e.g. dash); Use it
335 # here however when possible just to lower collision chance.
326336 tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
327 trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
328
337
338 trap 'ret=$?; rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null; exit $ret' 0
339
340 # Because "mkdir -p" follows existing symlinks and we likely work
341 # directly in world-writeable /tmp, make sure that the '$tmpdir'
342 # directory is successfully created first before we actually test
343 # 'mkdir -p' feature.
329344 if (umask $mkdir_umask &&
330 exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
345 $mkdirprog $mkdir_mode "$tmpdir" &&
346 exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1
331347 then
332348 if test -z "$dir_arg" || {
333349 # Check for POSIX incompatibilities with -m.
334350 # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
335351 # other-writable bit of parent directory when it shouldn't.
336352 # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
337 ls_ld_tmpdir=`ls -ld "$tmpdir"`
353 test_tmpdir="$tmpdir/a"
354 ls_ld_tmpdir=`ls -ld "$test_tmpdir"`
338355 case $ls_ld_tmpdir in
339356 d????-?r-*) different_mode=700;;
340357 d????-?--*) different_mode=755;;
341358 *) false;;
342359 esac &&
343 $mkdirprog -m$different_mode -p -- "$tmpdir" && {
344 ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
360 $mkdirprog -m$different_mode -p -- "$test_tmpdir" && {
361 ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"`
345362 test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
346363 }
347364 }
348365 then posix_mkdir=:
349366 fi
350 rmdir "$tmpdir/d" "$tmpdir"
367 rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir"
351368 else
352369 # Remove any dirs left behind by ancient mkdir implementations.
353 rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
370 rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null
354371 fi
355372 trap '' 0;;
356373 esac;;
426443 else
427444
428445 # Make a couple of temp file names in the proper directory.
429 dsttmp=$dstdir/_inst.$$_
430 rmtmp=$dstdir/_rm.$$_
446 dsttmp=${dstdirslash}_inst.$$_
447 rmtmp=${dstdirslash}_rm.$$_
431448
432449 # Trap to clean up those temp files at exit.
433450 trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
492509 done
493510
494511 # Local variables:
495 # eval: (add-hook 'write-file-hooks 'time-stamp)
512 # eval: (add-hook 'before-save-hook 'time-stamp)
496513 # time-stamp-start: "scriptversion="
497514 # time-stamp-format: "%:y-%02m-%02d.%02H"
498 # time-stamp-time-zone: "UTC"
515 # time-stamp-time-zone: "UTC0"
499516 # time-stamp-end: "; # UTC"
500517 # End:
22 -Wpointer-arith -Wredundant-decls -Wno-missing-field-initializers\
33 -D_GNU_SOURCE
44
5 LIBASS_LT_CURRENT = 9
6 LIBASS_LT_REVISION = 2
7 LIBASS_LT_AGE = 0
5 LIBASS_LT_CURRENT = 10
6 LIBASS_LT_REVISION = 1
7 LIBASS_LT_AGE = 1
88
99 nasm_verbose = $(nasm_verbose_$(V))
1010 nasm_verbose_ = $(nasm_verbose_$(AM_DEFAULT_VERBOSITY))
2727 ass_library.h ass_library.c ass_cache.h ass_cache.c ass_cache_template.h \
2828 ass_font.h ass_font.c ass_fontselect.h ass_fontselect.c \
2929 ass_render.h ass_render.c ass_render_api.c \
30 ass_parse.h ass_parse.c ass_shaper.h ass_shaper.c \
30 ass_parse.h ass_parse.c ass_priv.h ass_shaper.h ass_shaper.c \
3131 ass_outline.h ass_outline.c ass_drawing.h ass_drawing.c \
3232 ass_rasterizer.h ass_rasterizer.c ass_rasterizer_c.c \
3333 ass_bitmap.h ass_bitmap.c ass_blur.c ass_func_template.h
0 # Makefile.in generated by automake 1.15 from Makefile.am.
0 # Makefile.in generated by automake 1.16.1 from Makefile.am.
11 # @configure_input@
22
3 # Copyright (C) 1994-2014 Free Software Foundation, Inc.
3 # Copyright (C) 1994-2018 Free Software Foundation, Inc.
44
55 # This Makefile.in is free software; the Free Software Foundation
66 # gives unlimited permission to copy and/or distribute it,
142142 ass_strtod.c ass_library.h ass_library.c ass_cache.h \
143143 ass_cache.c ass_cache_template.h ass_font.h ass_font.c \
144144 ass_fontselect.h ass_fontselect.c ass_render.h ass_render.c \
145 ass_render_api.c ass_parse.h ass_parse.c ass_shaper.h \
146 ass_shaper.c ass_outline.h ass_outline.c ass_drawing.h \
147 ass_drawing.c ass_rasterizer.h ass_rasterizer.c \
145 ass_render_api.c ass_parse.h ass_parse.c ass_priv.h \
146 ass_shaper.h ass_shaper.c ass_outline.h ass_outline.c \
147 ass_drawing.h ass_drawing.c ass_rasterizer.h ass_rasterizer.c \
148148 ass_rasterizer_c.c ass_bitmap.h ass_bitmap.c ass_blur.c \
149149 ass_func_template.h ass_fontconfig.c ass_fontconfig.h \
150150 ass_directwrite.c ass_directwrite.h dwrite_c.h ass_coretext.c \
191191 am__v_at_1 =
192192 DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
193193 depcomp = $(SHELL) $(top_srcdir)/depcomp
194 am__depfiles_maybe = depfiles
194 am__maybe_remake_depfiles = depfiles
195 am__depfiles_remade = ./$(DEPDIR)/ass.Plo ./$(DEPDIR)/ass_bitmap.Plo \
196 ./$(DEPDIR)/ass_blur.Plo ./$(DEPDIR)/ass_cache.Plo \
197 ./$(DEPDIR)/ass_coretext.Plo ./$(DEPDIR)/ass_directwrite.Plo \
198 ./$(DEPDIR)/ass_drawing.Plo ./$(DEPDIR)/ass_font.Plo \
199 ./$(DEPDIR)/ass_fontconfig.Plo ./$(DEPDIR)/ass_fontselect.Plo \
200 ./$(DEPDIR)/ass_library.Plo ./$(DEPDIR)/ass_outline.Plo \
201 ./$(DEPDIR)/ass_parse.Plo ./$(DEPDIR)/ass_rasterizer.Plo \
202 ./$(DEPDIR)/ass_rasterizer_c.Plo ./$(DEPDIR)/ass_render.Plo \
203 ./$(DEPDIR)/ass_render_api.Plo ./$(DEPDIR)/ass_shaper.Plo \
204 ./$(DEPDIR)/ass_string.Plo ./$(DEPDIR)/ass_strtod.Plo \
205 ./$(DEPDIR)/ass_utils.Plo
195206 am__mv = mv -f
196207 COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
197208 $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
383394 -Wpointer-arith -Wredundant-decls -Wno-missing-field-initializers\
384395 -D_GNU_SOURCE
385396
386 LIBASS_LT_CURRENT = 9
387 LIBASS_LT_REVISION = 2
388 LIBASS_LT_AGE = 0
397 LIBASS_LT_CURRENT = 10
398 LIBASS_LT_REVISION = 1
399 LIBASS_LT_AGE = 1
389400 nasm_verbose = $(nasm_verbose_$(V))
390401 nasm_verbose_ = $(nasm_verbose_$(AM_DEFAULT_VERBOSITY))
391402 nasm_verbose_0 = @echo " NASM " $@;
402413 ass_library.h ass_library.c ass_cache.h ass_cache.c \
403414 ass_cache_template.h ass_font.h ass_font.c ass_fontselect.h \
404415 ass_fontselect.c ass_render.h ass_render.c ass_render_api.c \
405 ass_parse.h ass_parse.c ass_shaper.h ass_shaper.c \
416 ass_parse.h ass_parse.c ass_priv.h ass_shaper.h ass_shaper.c \
406417 ass_outline.h ass_outline.c ass_drawing.h ass_drawing.c \
407418 ass_rasterizer.h ass_rasterizer.c ass_rasterizer_c.c \
408419 ass_bitmap.h ass_bitmap.c ass_blur.c ass_func_template.h \
427438 exit 1;; \
428439 esac; \
429440 done; \
430 echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu libass/Makefile'; \
441 echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign libass/Makefile'; \
431442 $(am__cd) $(top_srcdir) && \
432 $(AUTOMAKE) --gnu libass/Makefile
443 $(AUTOMAKE) --foreign libass/Makefile
433444 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
434445 @case '$?' in \
435446 *config.status*) \
436447 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
437448 *) \
438 echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
439 cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
449 echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
450 cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
440451 esac;
441452
442453 $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
506517 distclean-compile:
507518 -rm -f *.tab.c
508519
509 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass.Plo@am__quote@
510 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_bitmap.Plo@am__quote@
511 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_blur.Plo@am__quote@
512 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_cache.Plo@am__quote@
513 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_coretext.Plo@am__quote@
514 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_directwrite.Plo@am__quote@
515 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_drawing.Plo@am__quote@
516 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_font.Plo@am__quote@
517 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_fontconfig.Plo@am__quote@
518 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_fontselect.Plo@am__quote@
519 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_library.Plo@am__quote@
520 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_outline.Plo@am__quote@
521 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_parse.Plo@am__quote@
522 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_rasterizer.Plo@am__quote@
523 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_rasterizer_c.Plo@am__quote@
524 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_render.Plo@am__quote@
525 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_render_api.Plo@am__quote@
526 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_shaper.Plo@am__quote@
527 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_string.Plo@am__quote@
528 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_strtod.Plo@am__quote@
529 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_utils.Plo@am__quote@
520 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass.Plo@am__quote@ # am--include-marker
521 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_bitmap.Plo@am__quote@ # am--include-marker
522 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_blur.Plo@am__quote@ # am--include-marker
523 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_cache.Plo@am__quote@ # am--include-marker
524 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_coretext.Plo@am__quote@ # am--include-marker
525 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_directwrite.Plo@am__quote@ # am--include-marker
526 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_drawing.Plo@am__quote@ # am--include-marker
527 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_font.Plo@am__quote@ # am--include-marker
528 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_fontconfig.Plo@am__quote@ # am--include-marker
529 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_fontselect.Plo@am__quote@ # am--include-marker
530 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_library.Plo@am__quote@ # am--include-marker
531 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_outline.Plo@am__quote@ # am--include-marker
532 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_parse.Plo@am__quote@ # am--include-marker
533 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_rasterizer.Plo@am__quote@ # am--include-marker
534 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_rasterizer_c.Plo@am__quote@ # am--include-marker
535 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_render.Plo@am__quote@ # am--include-marker
536 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_render_api.Plo@am__quote@ # am--include-marker
537 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_shaper.Plo@am__quote@ # am--include-marker
538 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_string.Plo@am__quote@ # am--include-marker
539 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_strtod.Plo@am__quote@ # am--include-marker
540 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ass_utils.Plo@am__quote@ # am--include-marker
541
542 $(am__depfiles_remade):
543 @$(MKDIR_P) $(@D)
544 @echo '# dummy' >$@-t && $(am__mv) $@-t $@
545
546 am--depfiles: $(am__depfiles_remade)
530547
531548 .c.o:
532549 @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
629646 distclean-tags:
630647 -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
631648
632 distdir: $(DISTFILES)
649 distdir: $(BUILT_SOURCES)
650 $(MAKE) $(AM_MAKEFLAGS) distdir-am
651
652 distdir-am: $(DISTFILES)
633653 @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
634654 topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
635655 list='$(DISTFILES)'; \
704724 mostlyclean-am
705725
706726 distclean: distclean-am
707 -rm -rf ./$(DEPDIR)
727 -rm -f ./$(DEPDIR)/ass.Plo
728 -rm -f ./$(DEPDIR)/ass_bitmap.Plo
729 -rm -f ./$(DEPDIR)/ass_blur.Plo
730 -rm -f ./$(DEPDIR)/ass_cache.Plo
731 -rm -f ./$(DEPDIR)/ass_coretext.Plo
732 -rm -f ./$(DEPDIR)/ass_directwrite.Plo
733 -rm -f ./$(DEPDIR)/ass_drawing.Plo
734 -rm -f ./$(DEPDIR)/ass_font.Plo
735 -rm -f ./$(DEPDIR)/ass_fontconfig.Plo
736 -rm -f ./$(DEPDIR)/ass_fontselect.Plo
737 -rm -f ./$(DEPDIR)/ass_library.Plo
738 -rm -f ./$(DEPDIR)/ass_outline.Plo
739 -rm -f ./$(DEPDIR)/ass_parse.Plo
740 -rm -f ./$(DEPDIR)/ass_rasterizer.Plo
741 -rm -f ./$(DEPDIR)/ass_rasterizer_c.Plo
742 -rm -f ./$(DEPDIR)/ass_render.Plo
743 -rm -f ./$(DEPDIR)/ass_render_api.Plo
744 -rm -f ./$(DEPDIR)/ass_shaper.Plo
745 -rm -f ./$(DEPDIR)/ass_string.Plo
746 -rm -f ./$(DEPDIR)/ass_strtod.Plo
747 -rm -f ./$(DEPDIR)/ass_utils.Plo
708748 -rm -f Makefile
709749 distclean-am: clean-am distclean-compile distclean-generic \
710750 distclean-tags
750790 installcheck-am:
751791
752792 maintainer-clean: maintainer-clean-am
753 -rm -rf ./$(DEPDIR)
793 -rm -f ./$(DEPDIR)/ass.Plo
794 -rm -f ./$(DEPDIR)/ass_bitmap.Plo
795 -rm -f ./$(DEPDIR)/ass_blur.Plo
796 -rm -f ./$(DEPDIR)/ass_cache.Plo
797 -rm -f ./$(DEPDIR)/ass_coretext.Plo
798 -rm -f ./$(DEPDIR)/ass_directwrite.Plo
799 -rm -f ./$(DEPDIR)/ass_drawing.Plo
800 -rm -f ./$(DEPDIR)/ass_font.Plo
801 -rm -f ./$(DEPDIR)/ass_fontconfig.Plo
802 -rm -f ./$(DEPDIR)/ass_fontselect.Plo
803 -rm -f ./$(DEPDIR)/ass_library.Plo
804 -rm -f ./$(DEPDIR)/ass_outline.Plo
805 -rm -f ./$(DEPDIR)/ass_parse.Plo
806 -rm -f ./$(DEPDIR)/ass_rasterizer.Plo
807 -rm -f ./$(DEPDIR)/ass_rasterizer_c.Plo
808 -rm -f ./$(DEPDIR)/ass_render.Plo
809 -rm -f ./$(DEPDIR)/ass_render_api.Plo
810 -rm -f ./$(DEPDIR)/ass_shaper.Plo
811 -rm -f ./$(DEPDIR)/ass_string.Plo
812 -rm -f ./$(DEPDIR)/ass_strtod.Plo
813 -rm -f ./$(DEPDIR)/ass_utils.Plo
754814 -rm -f Makefile
755815 maintainer-clean-am: distclean-am maintainer-clean-generic
756816
772832
773833 .MAKE: install-am install-strip
774834
775 .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
776 clean-libLTLIBRARIES clean-libtool cscopelist-am ctags \
777 ctags-am distclean distclean-compile distclean-generic \
835 .PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
836 clean-generic clean-libLTLIBRARIES clean-libtool cscopelist-am \
837 ctags ctags-am distclean distclean-compile distclean-generic \
778838 distclean-libtool distclean-tags distdir dvi dvi-am html \
779839 html-am info info-am install install-am install-data \
780840 install-data-am install-dist_assheadersHEADERS install-dvi \
3434 #include "ass.h"
3535 #include "ass_utils.h"
3636 #include "ass_library.h"
37 #include "ass_priv.h"
38 #include "ass_shaper.h"
3739 #include "ass_string.h"
3840
3941 #define ass_atof(STR) (ass_strtod((STR),NULL))
4042
41 typedef enum {
42 PST_UNKNOWN = 0,
43 PST_INFO,
44 PST_STYLES,
45 PST_EVENTS,
46 PST_FONTS
47 } ParserState;
48
49 struct parser_priv {
50 ParserState state;
51 char *fontname;
52 char *fontdata;
53 int fontdata_size;
54 int fontdata_used;
55
56 // contains bitmap of ReadOrder IDs of all read events
57 uint32_t *read_order_bitmap;
58 int read_order_elems; // size in uint32_t units of read_order_bitmap
59 int check_readorder;
60 };
43 static const char *const ass_style_format =
44 "Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, "
45 "OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, "
46 "ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, "
47 "Alignment, MarginL, MarginR, MarginV, Encoding";
48 static const char *const ass_event_format =
49 "Layer, Start, End, Style, Name, "
50 "MarginL, MarginR, MarginV, Effect, Text";
51 static const char *const ssa_style_format =
52 "Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, "
53 "TertiaryColour, BackColour, Bold, Italic, BorderStyle, Outline, "
54 "Shadow, Alignment, MarginL, MarginR, MarginV, AlphaLevel, Encoding";
55 static const char *const ssa_event_format =
56 "Marked, Start, End, Style, Name, "
57 "MarginL, MarginR, MarginV, Effect, Text";
6158
6259 #define ASS_STYLES_ALLOC 20
6360
6966 void ass_free_track(ASS_Track *track)
7067 {
7168 int i;
69
70 if (!track)
71 return;
7272
7373 if (track->parser_priv) {
7474 free(track->parser_priv->read_order_bitmap);
9595
9696 /// \brief Allocate a new style struct
9797 /// \param track track
98 /// \return style id
98 /// \return style id or negative value on failure
9999 int ass_alloc_style(ASS_Track *track)
100100 {
101101 int sid;
103103 assert(track->n_styles <= track->max_styles);
104104
105105 if (track->n_styles == track->max_styles) {
106 track->max_styles += ASS_STYLES_ALLOC;
107 track->styles =
108 (ASS_Style *) realloc(track->styles,
109 sizeof(ASS_Style) *
110 track->max_styles);
106 if (track->max_styles >= FFMIN(SIZE_MAX, INT_MAX) - ASS_STYLES_ALLOC)
107 return -1;
108 int new_max = track->max_styles + ASS_STYLES_ALLOC;
109 if (!ASS_REALLOC_ARRAY(track->styles, new_max))
110 return -1;
111 track->max_styles = new_max;
111112 }
112113
113114 sid = track->n_styles++;
117118
118119 /// \brief Allocate a new event struct
119120 /// \param track track
120 /// \return event id
121 /// \return event id or negative value on failure
121122 int ass_alloc_event(ASS_Track *track)
122123 {
123124 int eid;
125126 assert(track->n_events <= track->max_events);
126127
127128 if (track->n_events == track->max_events) {
128 track->max_events = track->max_events * 2 + 1;
129 track->events =
130 (ASS_Event *) realloc(track->events,
131 sizeof(ASS_Event) *
132 track->max_events);
129 if (track->max_events >= FFMIN(SIZE_MAX, INT_MAX) / 2)
130 return -1;
131 int new_max = track->max_events * 2 + 1;
132 if (!ASS_REALLOC_ARRAY(track->events, new_max))
133 return -1;
134 track->max_events = new_max;
133135 }
134136
135137 eid = track->n_events++;
160162 // Don't allow malicious files to OOM us easily. Also avoids int overflows.
161163 if (max_id < 0 || max_id >= 10 * 1024 * 1024 * 8)
162164 goto fail;
165 assert(track->parser_priv->read_order_bitmap || !track->parser_priv->read_order_elems);
163166 if (max_id >= track->parser_priv->read_order_elems * 32) {
164167 int oldelems = track->parser_priv->read_order_elems;
165168 int elems = ((max_id + 31) / 32 + 1) * 2;
187190 if (resize_read_order_bitmap(track, id) < 0)
188191 return -1;
189192 int index = id / 32;
190 int bit = 1 << (id % 32);
193 uint32_t bit = 1u << (id % 32);
191194 if (track->parser_priv->read_order_bitmap[index] & bit)
192195 return 1;
193196 track->parser_priv->read_order_bitmap[index] |= bit;
236239 }
237240
238241 #define NEXT(str,token) \
239 token = next_token(&str); \
240 if (!token) break;
242 token = next_token(&str); \
243 if (!token) break;
241244
242245
243246 #define ALIAS(alias,name) \
257260 #define PARSE_END }
258261
259262 #define ANYVAL(name,func) \
260 } else if (ass_strcasecmp(tname, #name) == 0) { \
261 target->name = func(token);
263 } else if (ass_strcasecmp(tname, #name) == 0) { \
264 target->name = func(token);
262265
263266 #define STRVAL(name) \
264 } else if (ass_strcasecmp(tname, #name) == 0) { \
265 if (target->name != NULL) free(target->name); \
266 target->name = strdup(token);
267 } else if (ass_strcasecmp(tname, #name) == 0) { \
268 char *new_str = strdup(token); \
269 if (new_str) { \
270 free(target->name); \
271 target->name = new_str; \
272 }
267273
268274 #define STARREDSTRVAL(name) \
269275 } else if (ass_strcasecmp(tname, #name) == 0) { \
270 if (target->name != NULL) free(target->name); \
271276 while (*token == '*') ++token; \
272 target->name = strdup(token);
277 char *new_str = strdup(token); \
278 if (new_str) { \
279 free(target->name); \
280 target->name = new_str; \
281 }
273282
274283 #define COLORVAL(name) ANYVAL(name,parse_color_header)
275284 #define INTVAL(name) ANYVAL(name,atoi)
276285 #define FPVAL(name) ANYVAL(name,ass_atof)
277286 #define TIMEVAL(name) \
278 } else if (ass_strcasecmp(tname, #name) == 0) { \
279 target->name = string2timecode(track->library, token);
287 } else if (ass_strcasecmp(tname, #name) == 0) { \
288 target->name = string2timecode(track->library, token);
280289
281290 #define STYLEVAL(name) \
282 } else if (ass_strcasecmp(tname, #name) == 0) { \
283 target->name = lookup_style(track, token);
291 } else if (ass_strcasecmp(tname, #name) == 0) { \
292 target->name = lookup_style(track, token);
293
294 // skip spaces in str beforehand, or trim leading spaces afterwards
295 static inline void advance_token_pos(const char **const str,
296 const char **const start,
297 const char **const end)
298 {
299 *start = *str;
300 *end = *start;
301 while (**end != '\0' && **end != ',') ++*end;
302 *str = *end + (**end == ',');
303 rskip_spaces((char**)end, (char*)*start);
304 }
284305
285306 static char *next_token(char **str)
286307 {
287 char *p = *str;
308 char *p;
288309 char *start;
289 skip_spaces(&p);
290 if (*p == '\0') {
291 *str = p;
310 skip_spaces(str);
311 if (**str == '\0') {
292312 return 0;
293313 }
294 start = p; // start of the token
295 for (; (*p != '\0') && (*p != ','); ++p) {
296 }
297 if (*p == '\0') {
298 *str = p; // eos found, str will point to '\0' at exit
299 } else {
300 *p = '\0';
301 *str = p + 1; // ',' found, str will point to the next char (beginning of the next token)
302 }
303 rskip_spaces(&p, start); // end of current token: the first space character, or '\0'
314
315 advance_token_pos((const char**)str,
316 (const char**)&start,
317 (const char**)&p);
318
304319 *p = '\0';
305320 return start;
306321 }
322337 ASS_Event *target = event;
323338
324339 char *format = strdup(track->event_format);
340 if (!format)
341 return -1;
325342 char *q = format; // format scanning pointer
326
327 if (track->n_styles == 0) {
328 // add "Default" style to the end
329 // will be used if track does not contain a default style (or even does not contain styles at all)
330 int sid = ass_alloc_style(track);
331 set_default_style(&track->styles[sid]);
332 track->default_style = sid;
333 }
334343
335344 for (i = 0; i < n_ignored; ++i) {
336345 NEXT(q, tname);
341350 if (ass_strcasecmp(tname, "Text") == 0) {
342351 char *last;
343352 event->Text = strdup(p);
344 if (*event->Text != 0) {
353 if (event->Text && *event->Text != 0) {
345354 last = event->Text + strlen(event->Text) - 1;
346355 if (last >= event->Text && *last == '\r')
347356 *last = 0;
348357 }
349358 event->Duration -= event->Start;
350359 free(format);
351 return 0; // "Text" is always the last
360 return event->Text ? 0 : -1; // "Text" is always the last
352361 }
353362 NEXT(p, token);
354363
475484 // no style format header
476485 // probably an ancient script version
477486 if (track->track_type == TRACK_TYPE_SSA)
478 track->style_format =
479 strdup
480 ("Name, Fontname, Fontsize, PrimaryColour, SecondaryColour,"
481 "TertiaryColour, BackColour, Bold, Italic, BorderStyle, Outline,"
482 "Shadow, Alignment, MarginL, MarginR, MarginV, AlphaLevel, Encoding");
487 track->style_format = strdup(ssa_style_format);
483488 else
484 track->style_format =
485 strdup
486 ("Name, Fontname, Fontsize, PrimaryColour, SecondaryColour,"
487 "OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut,"
488 "ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow,"
489 "Alignment, MarginL, MarginR, MarginV, Encoding");
489 track->style_format = strdup(ass_style_format);
490 if (!track->style_format)
491 return -1;
490492 }
491493
492494 q = format = strdup(track->style_format);
493
494 // Add default style first
495 if (track->n_styles == 0) {
496 // will be used if track does not contain a default style (or even does not contain styles at all)
497 int sid = ass_alloc_style(track);
498 set_default_style(&track->styles[sid]);
499 track->default_style = sid;
500 }
495 if (!q)
496 return -1;
501497
502498 ass_msg(track->library, MSGL_V, "[%p] Style: %s", track, str);
503499
504500 sid = ass_alloc_style(track);
501 if (sid < 0) {
502 free(format);
503 return -1;
504 }
505505
506506 style = track->styles + sid;
507507 target = style;
516516
517517 PARSE_START
518518 STARREDSTRVAL(Name)
519 if (strcmp(target->Name, "Default") == 0)
520 track->default_style = sid;
521519 STRVAL(FontName)
522520 COLORVAL(PrimaryColour)
523521 COLORVAL(SecondaryColour)
553551 FPVAL(Shadow)
554552 PARSE_END
555553 }
554 free(format);
556555 style->ScaleX = FFMAX(style->ScaleX, 0.) / 100.;
557556 style->ScaleY = FFMAX(style->ScaleY, 0.) / 100.;
558557 style->Spacing = FFMAX(style->Spacing, 0.);
566565 style->Name = strdup("Default");
567566 if (!style->FontName)
568567 style->FontName = strdup("Arial");
569 free(format);
568 if (!style->Name || !style->FontName) {
569 ass_free_style(track, sid);
570 track->n_styles--;
571 return -1;
572 }
573 if (strcmp(target->Name, "Default") == 0)
574 track->default_style = sid;
570575 return 0;
571576
572577 }
573578
579 static bool format_line_compare(const char *fmt1, const char *fmt2)
580 {
581 while (true) {
582 const char *tk1_start, *tk2_start;
583 const char *tk1_end, *tk2_end;
584
585 skip_spaces((char**)&fmt1);
586 skip_spaces((char**)&fmt2);
587 if (!*fmt1 || !*fmt2)
588 break;
589
590 advance_token_pos(&fmt1, &tk1_start, &tk1_end);
591 advance_token_pos(&fmt2, &tk2_start, &tk2_end);
592
593 if ((tk1_end-tk1_start) != (tk2_end-tk2_start))
594 return false;
595 if (ass_strncasecmp(tk1_start, tk2_start, tk1_end-tk1_start))
596 return false;
597 }
598 return *fmt1 == *fmt2;
599 }
600
601
602 /**
603 * \brief Set SBAS=1 if not set explicitly in case of custom format line
604 * \param track track
605 * \param fmt format line of file
606 * \param std standard format line
607 *
608 * As of writing libass is the only renderer accepting custom format lines.
609 * For years libass defaultet SBAS to yes instead of no.
610 * To avoid breaking released scripts with custom format lines,
611 * keep SBAS=1 default for custom format files.
612 */
613 static void custom_format_line_compatibility(ASS_Track *const track,
614 const char *const fmt,
615 const char *const std)
616 {
617 if (!(track->parser_priv->header_flags & SINFO_SCALEDBORDER)
618 && !format_line_compare(fmt, std)) {
619 ass_msg(track->library, MSGL_INFO,
620 "Track has custom format line(s). "
621 "'ScaledBorderAndShadow' will default to 'yes'.");
622 track->ScaledBorderAndShadow = 1;
623 }
624 }
625
574626 static int process_styles_line(ASS_Track *track, char *str)
575627 {
628 int ret = 0;
576629 if (!strncmp(str, "Format:", 7)) {
577630 char *p = str + 7;
578631 skip_spaces(&p);
579632 free(track->style_format);
580633 track->style_format = strdup(p);
634 if (!track->style_format)
635 return -1;
581636 ass_msg(track->library, MSGL_DBG2, "Style format: %s",
582637 track->style_format);
638 if (track->track_type == TRACK_TYPE_ASS)
639 custom_format_line_compatibility(track, p, ass_style_format);
640 else
641 custom_format_line_compatibility(track, p, ssa_style_format);
583642 } else if (!strncmp(str, "Style:", 6)) {
584643 char *p = str + 6;
585644 skip_spaces(&p);
586 process_style(track, p);
587 }
588 return 0;
645 ret = process_style(track, p);
646 }
647 return ret;
648 }
649
650 static inline void check_duplicate_info_line(const ASS_Track *const track,
651 const ScriptInfo si,
652 const char *const name)
653 {
654 if (track->parser_priv->header_flags & si)
655 ass_msg(track->library, MSGL_WARN,
656 "Duplicate Script Info Header '%s'. Previous value overwritten!",
657 name);
658 else
659 track->parser_priv->header_flags |= si;
589660 }
590661
591662 static int process_info_line(ASS_Track *track, char *str)
592663 {
593664 if (!strncmp(str, "PlayResX:", 9)) {
665 check_duplicate_info_line(track, SINFO_PLAYRESX, "PlayResX");
594666 track->PlayResX = atoi(str + 9);
595667 } else if (!strncmp(str, "PlayResY:", 9)) {
668 check_duplicate_info_line(track, SINFO_PLAYRESY, "PlayResY");
596669 track->PlayResY = atoi(str + 9);
597670 } else if (!strncmp(str, "Timer:", 6)) {
671 check_duplicate_info_line(track, SINFO_TIMER, "Timer");
598672 track->Timer = ass_atof(str + 6);
599673 } else if (!strncmp(str, "WrapStyle:", 10)) {
674 check_duplicate_info_line(track, SINFO_WRAPSTYLE, "WrapStyle");
600675 track->WrapStyle = atoi(str + 10);
601676 } else if (!strncmp(str, "ScaledBorderAndShadow:", 22)) {
677 check_duplicate_info_line(track, SINFO_SCALEDBORDER,
678 "ScaledBorderAndShadow");
602679 track->ScaledBorderAndShadow = parse_bool(str + 22);
603680 } else if (!strncmp(str, "Kerning:", 8)) {
681 check_duplicate_info_line(track, SINFO_KERNING, "Kerning");
604682 track->Kerning = parse_bool(str + 8);
605683 } else if (!strncmp(str, "YCbCr Matrix:", 13)) {
684 check_duplicate_info_line(track, SINFO_COLOURMATRIX, "YCbCr Matrix");
606685 track->YCbCrMatrix = parse_ycbcr_matrix(str + 13);
607686 } else if (!strncmp(str, "Language:", 9)) {
687 check_duplicate_info_line(track, SINFO_LANGUAGE, "Language");
608688 char *p = str + 9;
609689 while (*p && ass_isspace(*p)) p++;
610690 free(track->Language);
611691 track->Language = strndup(p, 2);
692 } else if (!strncmp(str, "; Script generated by ", 22)) {
693 if (!strncmp(str + 22,"FFmpeg/Lavc", 11))
694 track->parser_priv->header_flags |= GENBY_FFMPEG;
612695 }
613696 return 0;
614697 }
617700 {
618701 track->parser_priv->state = PST_EVENTS;
619702 if (track->track_type == TRACK_TYPE_SSA)
620 track->event_format = strdup("Marked, Start, End, Style, "
621 "Name, MarginL, MarginR, MarginV, Effect, Text");
703 track->event_format = strdup(ssa_event_format);
622704 else
623 track->event_format = strdup("Layer, Start, End, Style, "
624 "Actor, MarginL, MarginR, MarginV, Effect, Text");
705 track->event_format = strdup(ass_event_format);
625706 ass_msg(track->library, MSGL_V,
626707 "No event format found, using fallback");
627708 }
709
710 /**
711 * \brief Return if track is post-signature and pre-SBAS ffmpeg track
712 * \param track track
713 */
714 static bool detect_legacy_conv_subs(ASS_Track *track)
715 {
716 /*
717 * FFmpeg and libav convert srt subtitles to ass.
718 * In legacy versions, they did not set the 'ScaledBorderAndShadow' header,
719 * but expected it to default to yes (which libass did).
720 * To avoid breaking them, we try to detect these
721 * converted subs by common properties of ffmpeg/libav's converted subs.
722 * Since files with custom format lines (-2014.10.11) default to SBAS=1
723 * regardless of being ffmpeg generated or not, we are only concerned with
724 * post-signature and pre-SBAS ffmpeg-files (2014.10.11-2020.04.17).
725 * We want to avoid matching modified ffmpeg files though.
726 *
727 * Relevant ffmpeg commits are:
728 * 2c77c90684e24ef16f7e7c4462e011434cee6a98 2010.12.29
729 * Initial conversion format.
730 * Style "Format:" line is mix of SSA and ASS
731 * Event "Format:" line
732 * "Format: Layer, Start, End, Text\r\n"
733 * Only Header in ScriptInfo is "ScriptType: v4.00+"
734 * 0e7782c08ec77739edb0b98ba5d896b45e98235f 2012.06.15
735 * Adds 'Style' to Event "Format:" line
736 * 5039aadf68deb9ad6dd0737ea11259fe53d3727b 2014.06.18
737 * Adds PlayerRes(X|Y) (384x288)
738 * (moved below ScriptType: a few minutes later)
739 * 40b9f28641b696c6bb73ce49dc97c2ce2700cbdb 2014.10.11 14:31:23 +0200
740 * Regular full ASS Event and Style "Format:" lines
741 * 52b0a0ecaa02e17f7e01bead8c3f215f1cfd48dc 2014.10.11 18:37:43 +0200 <==
742 * Signature comment
743 * 56bc0a6736cdc7edab837ff8f304661fd16de0e4 2015.02.08
744 * Allow custom PlayRes(X|Y)
745 * a8ba2a2c1294a330a0e79ae7f0d3a203a7599166 2020.04.17
746 * Set 'ScaledBorderAndShadow: yes'
747 *
748 * libav outputs initial ffmpeg format. (no longer maintained)
749 */
750
751 // GENBY_FFMPEG and exact ffmpeg headers required
752 // Note: If there's SINFO_SCRIPTTYPE in the future this needs to be updated
753 if (track->parser_priv->header_flags
754 ^ (SINFO_PLAYRESX | SINFO_PLAYRESY | GENBY_FFMPEG))
755 return false;
756
757 // Legacy ffmpeg only ever has one style
758 // Check 2 not 1 because libass also adds a def style
759 if (track->n_styles != 2
760 || strncmp(track->styles[1].Name, "Default", 7))
761 return false;
762
763 return true;
764 }
765
628766
629767 static int process_events_line(ASS_Track *track, char *str)
630768 {
633771 skip_spaces(&p);
634772 free(track->event_format);
635773 track->event_format = strdup(p);
774 if (!track->event_format)
775 return -1;
636776 ass_msg(track->library, MSGL_DBG2, "Event format: %s", track->event_format);
777 if (track->track_type == TRACK_TYPE_ASS)
778 custom_format_line_compatibility(track, p, ass_event_format);
779 else
780 custom_format_line_compatibility(track, p, ssa_event_format);
781
782 // Guess if we are dealing with legacy ffmpeg subs and change accordingly
783 // If file has no event format it was probably not created by ffmpeg/libav
784 if (detect_legacy_conv_subs(track)) {
785 track->ScaledBorderAndShadow = 1;
786 ass_msg(track->library, MSGL_INFO,
787 "Track treated as legacy ffmpeg sub.");
788 }
637789 } else if (!strncmp(str, "Dialogue:", 9)) {
638790 // This should never be reached for embedded subtitles.
639791 // They have slightly different format and are parsed in ass_process_chunk,
641793 int eid;
642794 ASS_Event *event;
643795
796 // We can't parse events without event_format
797 if (!track->event_format) {
798 event_format_fallback(track);
799 if (!track->event_format)
800 return -1;
801 }
802
644803 str += 9;
645804 skip_spaces(&str);
646805
647806 eid = ass_alloc_event(track);
807 if (eid < 0)
808 return -1;
648809 event = track->events + eid;
649810
650 // We can't parse events with event_format
651 if (!track->event_format)
652 event_format_fallback(track);
653
654 process_event_tail(track, event, str, 0);
811 return process_event_tail(track, event, str, 0);
655812 } else {
656813 ass_msg(track->library, MSGL_V, "Not understood: '%.30s'", str);
657814 }
659816 }
660817
661818 static unsigned char *decode_chars(const unsigned char *src,
662 unsigned char *dst, int cnt_in)
819 unsigned char *dst, size_t cnt_in)
663820 {
664821 uint32_t value = 0;
665822 for (int i = 0; i < cnt_in; i++)
673830 return dst;
674831 }
675832
833 static void reset_embedded_font_parsing(ASS_ParserPriv *parser_priv)
834 {
835 free(parser_priv->fontname);
836 free(parser_priv->fontdata);
837 parser_priv->fontname = NULL;
838 parser_priv->fontdata = NULL;
839 parser_priv->fontdata_size = 0;
840 parser_priv->fontdata_used = 0;
841 }
842
676843 static int decode_font(ASS_Track *track)
677844 {
678845 unsigned char *p;
679846 unsigned char *q;
680 int i;
681 int size; // original size
682 int dsize; // decoded size
847 size_t i;
848 size_t size; // original size
849 size_t dsize; // decoded size
683850 unsigned char *buf = 0;
684851
685852 ass_msg(track->library, MSGL_V, "Font: %d bytes encoded data",
712879
713880 error_decode_font:
714881 free(buf);
715 free(track->parser_priv->fontname);
716 free(track->parser_priv->fontdata);
717 track->parser_priv->fontname = 0;
718 track->parser_priv->fontdata = 0;
719 track->parser_priv->fontdata_size = 0;
720 track->parser_priv->fontdata_used = 0;
882 reset_embedded_font_parsing(track->parser_priv);
721883 return 0;
722884 }
723885
724886 static int process_fonts_line(ASS_Track *track, char *str)
725887 {
726 int len;
888 size_t len;
727889
728890 if (!strncmp(str, "fontname:", 9)) {
729891 char *p = str + 9;
732894 decode_font(track);
733895 }
734896 track->parser_priv->fontname = strdup(p);
897 if (!track->parser_priv->fontname)
898 return -1;
735899 ass_msg(track->library, MSGL_V, "Fontname: %s",
736 track->parser_priv->fontname);
900 track->parser_priv->fontname);
737901 return 0;
738902 }
739903
740904 if (!track->parser_priv->fontname) {
741905 ass_msg(track->library, MSGL_V, "Not understood: '%s'", str);
742 return 0;
906 return 1;
743907 }
744908
745909 len = strlen(str);
746 if (track->parser_priv->fontdata_used + len >
747 track->parser_priv->fontdata_size) {
748 track->parser_priv->fontdata_size += FFMAX(len, 100 * 1024);
749 track->parser_priv->fontdata =
750 realloc(track->parser_priv->fontdata,
751 track->parser_priv->fontdata_size);
910 if (track->parser_priv->fontdata_used >=
911 SIZE_MAX - FFMAX(len, 100 * 1024)) {
912 goto mem_fail;
913 } else if (track->parser_priv->fontdata_used + len >
914 track->parser_priv->fontdata_size) {
915 size_t new_size =
916 track->parser_priv->fontdata_size + FFMAX(len, 100 * 1024);
917 if (!ASS_REALLOC_ARRAY(track->parser_priv->fontdata, new_size))
918 goto mem_fail;
919 track->parser_priv->fontdata_size = new_size;
752920 }
753921 memcpy(track->parser_priv->fontdata + track->parser_priv->fontdata_used,
754922 str, len);
755923 track->parser_priv->fontdata_used += len;
756924
757925 return 0;
926
927 mem_fail:
928 reset_embedded_font_parsing(track->parser_priv);
929 return -1;
758930 }
759931
760932 /**
764936 */
765937 static int process_line(ASS_Track *track, char *str)
766938 {
939 skip_spaces(&str);
767940 if (!ass_strncasecmp(str, "[Script Info]", 13)) {
768941 track->parser_priv->state = PST_INFO;
769942 } else if (!ass_strncasecmp(str, "[V4 Styles]", 11)) {
8931066 void ass_process_chunk(ASS_Track *track, char *data, int size,
8941067 long long timecode, long long duration)
8951068 {
896 char *str;
1069 char *str = NULL;
8971070 int eid;
8981071 char *p;
8991072 char *token;
9091082
9101083 if (!track->event_format) {
9111084 ass_msg(track->library, MSGL_WARN, "Event format header missing");
912 return;
1085 goto cleanup;
9131086 }
9141087
9151088 str = malloc(size + 1);
9161089 if (!str)
917 return;
1090 goto cleanup;
9181091 memcpy(str, data, size);
9191092 str[size] = '\0';
9201093 ass_msg(track->library, MSGL_V, "Event at %" PRId64 ", +%" PRId64 ": %s",
9211094 (int64_t) timecode, (int64_t) duration, str);
9221095
9231096 eid = ass_alloc_event(track);
1097 if (eid < 0)
1098 goto cleanup;
9241099 event = track->events + eid;
9251100
9261101 p = str;
9391114 event->Start = timecode;
9401115 event->Duration = duration;
9411116
942 free(str);
943 return;
1117 goto cleanup;
9441118 // dump_events(tid);
9451119 } while (0);
9461120 // some error
9471121 ass_free_event(track, eid);
9481122 track->n_events--;
1123
1124 cleanup:
9491125 free(str);
9501126 }
9511127
11121288 int i;
11131289
11141290 track = ass_new_track(library);
1291 if (!track)
1292 return NULL;
11151293
11161294 // process header
11171295 process_text(track, buf);
13121490
13131491 ASS_Track *ass_new_track(ASS_Library *library)
13141492 {
1493 int def_sid = -1;
13151494 ASS_Track *track = calloc(1, sizeof(ASS_Track));
13161495 if (!track)
1317 return NULL;
1496 goto fail;
13181497 track->library = library;
1319 track->ScaledBorderAndShadow = 1;
1498 track->ScaledBorderAndShadow = 0;
13201499 track->parser_priv = calloc(1, sizeof(ASS_ParserPriv));
1321 if (!track->parser_priv) {
1322 free(track);
1323 return NULL;
1324 }
1500 if (!track->parser_priv)
1501 goto fail;
1502 def_sid = ass_alloc_style(track);
1503 if (def_sid < 0)
1504 goto fail;
1505 set_default_style(track->styles + def_sid);
1506 track->default_style = def_sid;
1507 if (!track->styles[def_sid].Name || !track->styles[def_sid].FontName)
1508 goto fail;
13251509 track->parser_priv->check_readorder = 1;
13261510 return track;
1511
1512 fail:
1513 if (track) {
1514 if (def_sid >= 0)
1515 ass_free_style(track, def_sid);
1516 free(track->parser_priv);
1517 free(track);
1518 }
1519 return NULL;
1520 }
1521
1522 int ass_track_set_feature(ASS_Track *track, ASS_Feature feature, int enable)
1523 {
1524 switch (feature) {
1525 case ASS_FEATURE_INCOMPATIBLE_EXTENSIONS:
1526 //-fallthrough
1527 #ifdef USE_FRIBIDI_EX_API
1528 case ASS_FEATURE_BIDI_BRACKETS:
1529 track->parser_priv->bidi_brackets = !!enable;
1530 #endif
1531 return 0;
1532 default:
1533 return -1;
1534 }
13271535 }
13281536
13291537 /**
13441552 ass_msg(lib, MSGL_WARN,
13451553 "PlayResY undefined, setting to %d", track->PlayResY);
13461554 } else if (track->PlayResY <= 0) {
1347 track->PlayResY = FFMAX(1, track->PlayResX * 3 / 4);
1555 track->PlayResY = FFMAX(1, track->PlayResX * 3LL / 4);
13481556 ass_msg(lib, MSGL_WARN,
13491557 "PlayResY undefined, setting to %d", track->PlayResY);
13501558 } else if (track->PlayResX <= 0 && track->PlayResY == 1024) {
13521560 ass_msg(lib, MSGL_WARN,
13531561 "PlayResX undefined, setting to %d", track->PlayResX);
13541562 } else if (track->PlayResX <= 0) {
1355 track->PlayResX = FFMAX(1, track->PlayResY * 4 / 3);
1563 track->PlayResX = FFMAX(1, track->PlayResY * 4LL / 3);
13561564 ass_msg(lib, MSGL_WARN,
13571565 "PlayResX undefined, setting to %d", track->PlayResX);
13581566 }
2323 #include <stdarg.h>
2424 #include "ass_types.h"
2525
26 #define LIBASS_VERSION 0x01400000
26 #define LIBASS_VERSION 0x01500000
2727
2828 #ifdef __cplusplus
2929 extern "C" {
3030 #endif
31
32 #if (defined(__GNUC__) && ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))) || defined(__clang__)
33 #define ASS_DEPRECATED(msg) __attribute__((deprecated(msg)))
34 #if __GNUC__ > 5 || defined(__clang__)
35 #define ASS_DEPRECATED_ENUM(msg) __attribute__((deprecated(msg)))
36 #else
37 #define ASS_DEPRECATED_ENUM(msg)
38 #endif
39 #elif defined(_MSC_VER)
40 #define ASS_DEPRECATED(msg) __declspec(deprecated(msg))
41 #define ASS_DEPRECATED_ENUM(msg)
42 #else
43 #define ASS_DEPRECATED(msg)
44 #define ASS_DEPRECATED_ENUM(msg)
45 #endif
46
3147
3248 /*
3349 * A linked list of images produced by an ass renderer.
5571 IMAGE_TYPE_SHADOW
5672 } type;
5773
74 // New fields can be added here in new ABI-compatible library releases.
5875 } ASS_Image;
5976
6077 /*
127144 /**
128145 * Old alias for ASS_OVERRIDE_BIT_SELECTIVE_FONT_SCALE. Deprecated. Do not use.
129146 */
130 ASS_OVERRIDE_BIT_FONT_SIZE = 1 << 1,
147 ASS_OVERRIDE_BIT_FONT_SIZE ASS_DEPRECATED_ENUM("replaced by ASS_OVERRIDE_BIT_SELECTIVE_FONT_SCALE") = 1 << 1,
131148 /**
132149 * On dialogue events override: FontSize, Spacing, Blur, ScaleX, ScaleY
133150 */
169186 * On dialogue events override: Justify
170187 */
171188 ASS_OVERRIDE_BIT_JUSTIFY = 1 << 10,
189 // New enum values can be added here in new ABI-compatible library releases.
172190 } ASS_OverrideBits;
173191
174192 /**
195213 ASS_FONTPROVIDER_FONTCONFIG,
196214 ASS_FONTPROVIDER_DIRECTWRITE,
197215 } ASS_DefaultFontProvider;
216
217 typedef enum {
218 /**
219 * Enable libass extensions that would display ASS subtitles incorrectly.
220 * These may be useful for applications, which use libass as renderer for
221 * subtitles converted from another format, or which use libass for other
222 * purposes that do not involve actual ASS subtitles authored for
223 * distribution.
224 */
225 ASS_FEATURE_INCOMPATIBLE_EXTENSIONS,
226
227 /**
228 * Match bracket pairs in bidirectional text according to the revised
229 * Unicode Bidirectional Algorithm introduced in Unicode 6.3.
230 * This is incompatible with VSFilter and disabled by default.
231 *
232 * (Directional isolates, also introduced in Unicode 6.3,
233 * are unconditionally processed when FriBidi is new enough.)
234 *
235 * This feature may be unavailable at runtime (ass_track_set_feature
236 * may return -1) if libass was compiled against old FriBidi.
237 */
238 ASS_FEATURE_BIDI_BRACKETS,
239
240 // New enum values can be added here in new ABI-compatible library releases.
241 } ASS_Feature;
198242
199243 /**
200244 * \brief Initialize the library.
373417 * \param dar display aspect ratio (DAR), prescaled for output PAR
374418 * \param sar storage aspect ratio (SAR)
375419 */
376 void ass_set_aspect_ratio(ASS_Renderer *priv, double dar, double sar);
420 ASS_DEPRECATED("use 'ass_set_pixel_aspect' instead") void ass_set_aspect_ratio(ASS_Renderer *priv, double dar, double sar);
377421
378422 /**
379423 * \brief Set a fixed font scaling factor.
472516 * \param priv renderer handle
473517 * \return success
474518 */
475 int ass_fonts_update(ASS_Renderer *priv);
519 ASS_DEPRECATED("it does nothing") int ass_fonts_update(ASS_Renderer *priv);
476520
477521 /**
478522 * \brief Set hard cache limits. Do not set, or set to zero, for reasonable
505549 /**
506550 * \brief Allocate a new empty track object.
507551 * \param library handle
508 * \return pointer to empty track
552 * \return pointer to empty track or NULL on failure
509553 */
510554 ASS_Track *ass_new_track(ASS_Library *);
511555
512556 /**
557 * \brief Enable or disable certain features
558 * This manages flags that control the behavior of the renderer and how certain
559 * tags etc. within the track are interpreted. The defaults on a newly created
560 * ASS_Track are such that rendering is compatible with traditional renderers
561 * like VSFilter, and/or old versions of libass. Calling ass_process_data() or
562 * ass_process_codec_private() may change some of these flags according to file
563 * headers. (ass_process_chunk() will not change any of the flags.)
564 * Additions to ASS_Feature are backward compatible to old libass releases (ABI
565 * compatibility).
566 * \param track track
567 * \param feature the specific feature to enable or disable
568 * \param enable 0 for disable, any non-0 value for enable
569 * \return 0 if feature set, -1 if feature is unknown
570 */
571 int ass_track_set_feature(ASS_Track *track, ASS_Feature feature, int enable);
572
573 /**
513574 * \brief Deallocate track and all its child objects (styles and events).
514 * \param track track to deallocate
575 * \param track track to deallocate or NULL
515576 */
516577 void ass_free_track(ASS_Track *track);
517578
518579 /**
519580 * \brief Allocate new style.
520581 * \param track track
521 * \return newly allocated style id
582 * \return newly allocated style id >= 0, or a value < 0 on failure
522583 */
523584 int ass_alloc_style(ASS_Track *track);
524585
525586 /**
526587 * \brief Allocate new event.
527588 * \param track track
528 * \return newly allocated event id
589 * \return newly allocated event id >= 0, or a value < 0 on failure
529590 */
530591 int ass_alloc_event(ASS_Track *track);
531592
601662 * \param library library handle
602663 * \param fname file name
603664 * \param codepage encoding (iconv format)
604 * \return newly allocated track
665 * \return newly allocated track or NULL on failure
605666 */
606667 ASS_Track *ass_read_file(ASS_Library *library, char *fname,
607668 char *codepage);
612673 * \param buf pointer to subtitles text
613674 * \param bufsize size of buffer
614675 * \param codepage encoding (iconv format)
615 * \return newly allocated track
676 * \return newly allocated track or NULL on failure
616677 */
617678 ASS_Track *ass_read_memory(ASS_Library *library, char *buf,
618679 size_t bufsize, char *codepage);
5858 #endif
5959
6060
61 void ass_synth_blur(const BitmapEngine *engine, int opaque_box, int be,
62 double blur_radius, Bitmap *bm_g, Bitmap *bm_o)
63 {
64 bool blur_g = !bm_o || opaque_box;
65 if (blur_g && !bm_g)
61 void ass_synth_blur(const BitmapEngine *engine, Bitmap *bm,
62 int be, double blur_r2)
63 {
64 if (!bm->buffer)
6665 return;
6766
6867 // Apply gaussian blur
69 double r2 = blur_radius * blur_radius / log(256);
70 if (r2 > 0.001) {
71 if (bm_o)
72 ass_gaussian_blur(engine, bm_o, r2);
73 if (blur_g)
74 ass_gaussian_blur(engine, bm_g, r2);
75 }
68 if (blur_r2 > 0.001)
69 ass_gaussian_blur(engine, bm, blur_r2);
70
71 if (!be)
72 return;
7673
7774 // Apply box blur (multiple passes, if requested)
78 if (be) {
79 size_t size_o = 0, size_g = 0;
80 if (bm_o)
81 size_o = sizeof(uint16_t) * bm_o->stride * 2;
82 if (blur_g)
83 size_g = sizeof(uint16_t) * bm_g->stride * 2;
84 size_t size = FFMAX(size_o, size_g);
85 uint16_t *tmp = size ? ass_aligned_alloc(32, size, false) : NULL;
86 if (!tmp)
87 return;
88 if (bm_o) {
89 unsigned passes = be;
90 unsigned w = bm_o->w;
91 unsigned h = bm_o->h;
92 unsigned stride = bm_o->stride;
93 unsigned char *buf = bm_o->buffer;
94 if(w && h){
95 if(passes > 1){
96 be_blur_pre(buf, w, h, stride);
97 while(--passes){
98 memset(tmp, 0, stride * 2);
99 engine->be_blur(buf, w, h, stride, tmp);
100 }
101 be_blur_post(buf, w, h, stride);
102 }
103 memset(tmp, 0, stride * 2);
104 engine->be_blur(buf, w, h, stride, tmp);
105 }
106 }
107 if (blur_g) {
108 unsigned passes = be;
109 unsigned w = bm_g->w;
110 unsigned h = bm_g->h;
111 unsigned stride = bm_g->stride;
112 unsigned char *buf = bm_g->buffer;
113 if(w && h){
114 if(passes > 1){
115 be_blur_pre(buf, w, h, stride);
116 while(--passes){
117 memset(tmp, 0, stride * 2);
118 engine->be_blur(buf, w, h, stride, tmp);
119 }
120 be_blur_post(buf, w, h, stride);
121 }
122 memset(tmp, 0, stride * 2);
123 engine->be_blur(buf, w, h, stride, tmp);
124 }
125 }
126 ass_aligned_free(tmp);
127 }
128 }
129
130 static bool alloc_bitmap_buffer(const BitmapEngine *engine, Bitmap *bm, int w, int h,
131 bool zero)
75 size_t size = sizeof(uint16_t) * bm->stride * 2;
76 uint16_t *tmp = ass_aligned_alloc(32, size, false);
77 if (!tmp)
78 return;
79
80 int32_t w = bm->w;
81 int32_t h = bm->h;
82 ptrdiff_t stride = bm->stride;
83 uint8_t *buf = bm->buffer;
84 if (--be) {
85 be_blur_pre(buf, w, h, stride);
86 do {
87 memset(tmp, 0, stride * 2);
88 engine->be_blur(buf, w, h, stride, tmp);
89 } while (--be);
90 be_blur_post(buf, w, h, stride);
91 }
92 memset(tmp, 0, stride * 2);
93 engine->be_blur(buf, w, h, stride, tmp);
94 ass_aligned_free(tmp);
95 }
96
97 bool alloc_bitmap(const BitmapEngine *engine, Bitmap *bm,
98 int32_t w, int32_t h, bool zero)
13299 {
133100 unsigned align = 1 << engine->align_order;
134101 size_t s = ass_align(align, w);
145112 return true;
146113 }
147114
148 Bitmap *alloc_bitmap(const BitmapEngine *engine, int w, int h, bool zero)
149 {
150 Bitmap *bm = malloc(sizeof(Bitmap));
151 if (!bm)
152 return NULL;
153 if (!alloc_bitmap_buffer(engine, bm, w, h, zero)) {
154 free(bm);
155 return NULL;
156 }
157 bm->left = bm->top = 0;
158 return bm;
159 }
160
161 bool realloc_bitmap(const BitmapEngine *engine, Bitmap *bm, int w, int h)
115 bool realloc_bitmap(const BitmapEngine *engine, Bitmap *bm, int32_t w, int32_t h)
162116 {
163117 uint8_t *old = bm->buffer;
164 if (!alloc_bitmap_buffer(engine, bm, w, h, false))
118 if (!alloc_bitmap(engine, bm, w, h, false))
165119 return false;
166120 ass_aligned_free(old);
167121 return true;
169123
170124 void ass_free_bitmap(Bitmap *bm)
171125 {
172 if (bm)
173 ass_aligned_free(bm->buffer);
174 free(bm);
175 }
176
177 Bitmap *copy_bitmap(const BitmapEngine *engine, const Bitmap *src)
178 {
179 Bitmap *dst = alloc_bitmap(engine, src->w, src->h, false);
180 if (!dst)
181 return NULL;
126 ass_aligned_free(bm->buffer);
127 }
128
129 bool copy_bitmap(const BitmapEngine *engine, Bitmap *dst, const Bitmap *src)
130 {
131 if (!src->buffer) {
132 memset(dst, 0, sizeof(*dst));
133 return true;
134 }
135 if (!alloc_bitmap(engine, dst, src->w, src->h, false))
136 return false;
182137 dst->left = src->left;
183 dst->top = src->top;
138 dst->top = src->top;
184139 memcpy(dst->buffer, src->buffer, src->stride * src->h);
185 return dst;
186 }
187
188 Bitmap *outline_to_bitmap(ASS_Renderer *render_priv,
189 ASS_Outline *outline1, ASS_Outline *outline2,
190 int bord)
140 return true;
141 }
142
143 bool outline_to_bitmap(ASS_Renderer *render_priv, Bitmap *bm,
144 ASS_Outline *outline1, ASS_Outline *outline2)
191145 {
192146 RasterizerData *rst = &render_priv->rasterizer;
193147 if (outline1 && !rasterizer_set_outline(rst, outline1, false)) {
194148 ass_msg(render_priv->library, MSGL_WARN, "Failed to process glyph outline!\n");
195 return NULL;
149 return false;
196150 }
197151 if (outline2 && !rasterizer_set_outline(rst, outline2, !!outline1)) {
198152 ass_msg(render_priv->library, MSGL_WARN, "Failed to process glyph outline!\n");
199 return NULL;
200 }
201
202 if (bord < 0 || bord > INT_MAX / 2)
203 return NULL;
204 if (rst->bbox.x_max > INT_MAX - 63 || rst->bbox.y_max > INT_MAX - 63)
205 return NULL;
206
207 int x_min = rst->bbox.x_min >> 6;
208 int y_min = rst->bbox.y_min >> 6;
209 int x_max = (rst->bbox.x_max + 63) >> 6;
210 int y_max = (rst->bbox.y_max + 63) >> 6;
211 int w = x_max - x_min;
212 int h = y_max - y_min;
153 return false;
154 }
155 if (rst->bbox.x_min > rst->bbox.x_max || rst->bbox.y_min > rst->bbox.y_max)
156 return false;
157
158 // enlarge by 1/64th of pixel to bypass slow rasterizer path, add 1 pixel for shift_bitmap
159 int32_t x_min = (rst->bbox.x_min - 1) >> 6;
160 int32_t y_min = (rst->bbox.y_min - 1) >> 6;
161 int32_t x_max = (rst->bbox.x_max + 127) >> 6;
162 int32_t y_max = (rst->bbox.y_max + 127) >> 6;
163 int32_t w = x_max - x_min;
164 int32_t h = y_max - y_min;
213165
214166 int mask = (1 << render_priv->engine->tile_order) - 1;
215167
216 if (w < 0 || h < 0 ||
217 w > INT_MAX - (2 * bord + mask) || h > INT_MAX - (2 * bord + mask)) {
218 ass_msg(render_priv->library, MSGL_WARN, "Glyph bounding box too large: %dx%dpx",
219 w, h);
220 return NULL;
221 }
222
223 int tile_w = (w + 2 * bord + mask) & ~mask;
224 int tile_h = (h + 2 * bord + mask) & ~mask;
225 Bitmap *bm = alloc_bitmap(render_priv->engine, tile_w, tile_h, false);
226 if (!bm)
227 return NULL;
228 bm->left = x_min - bord;
229 bm->top = y_min - bord;
168 // XXX: is that possible to trigger at all?
169 if (w < 0 || h < 0 || w > INT_MAX - mask || h > INT_MAX - mask) {
170 ass_msg(render_priv->library, MSGL_WARN,
171 "Glyph bounding box too large: %dx%dpx", w, h);
172 return false;
173 }
174
175 int32_t tile_w = (w + mask) & ~mask;
176 int32_t tile_h = (h + mask) & ~mask;
177 if (!alloc_bitmap(render_priv->engine, bm, tile_w, tile_h, false))
178 return false;
179 bm->left = x_min;
180 bm->top = y_min;
230181
231182 if (!rasterizer_fill(render_priv->engine, rst, bm->buffer,
232 x_min - bord, y_min - bord,
233 bm->stride, tile_h, bm->stride)) {
183 x_min, y_min, bm->stride, tile_h, bm->stride)) {
234184 ass_msg(render_priv->library, MSGL_WARN, "Failed to rasterize glyph!\n");
235185 ass_free_bitmap(bm);
236 return NULL;
237 }
238
239 return bm;
186 return false;
187 }
188
189 return true;
240190 }
241191
242192 /**
247197 */
248198 void fix_outline(Bitmap *bm_g, Bitmap *bm_o)
249199 {
250 int x, y;
251 const int l = bm_o->left > bm_g->left ? bm_o->left : bm_g->left;
252 const int t = bm_o->top > bm_g->top ? bm_o->top : bm_g->top;
253 const int r =
254 bm_o->left + bm_o->stride <
255 bm_g->left + bm_g->stride ? bm_o->left + bm_o->stride : bm_g->left + bm_g->stride;
256 const int b =
257 bm_o->top + bm_o->h <
258 bm_g->top + bm_g->h ? bm_o->top + bm_o->h : bm_g->top + bm_g->h;
259
260 unsigned char *g =
261 bm_g->buffer + (t - bm_g->top) * bm_g->stride + (l - bm_g->left);
262 unsigned char *o =
263 bm_o->buffer + (t - bm_o->top) * bm_o->stride + (l - bm_o->left);
264
265 for (y = 0; y < b - t; ++y) {
266 for (x = 0; x < r - l; ++x) {
267 unsigned char c_g, c_o;
268 c_g = g[x];
269 c_o = o[x];
270 o[x] = (c_o > c_g) ? c_o - (c_g / 2) : 0;
271 }
200 if (!bm_g->buffer || !bm_o->buffer)
201 return;
202
203 int32_t l = FFMAX(bm_o->left, bm_g->left);
204 int32_t t = FFMAX(bm_o->top, bm_g->top);
205 int32_t r = FFMIN(bm_o->left + bm_o->stride, bm_g->left + bm_g->stride);
206 int32_t b = FFMIN(bm_o->top + bm_o->h, bm_g->top + bm_g->h);
207
208 uint8_t *g = bm_g->buffer + (t - bm_g->top) * bm_g->stride + (l - bm_g->left);
209 uint8_t *o = bm_o->buffer + (t - bm_o->top) * bm_o->stride + (l - bm_o->left);
210
211 for (int32_t y = 0; y < b - t; y++) {
212 for (int32_t x = 0; x < r - l; x++)
213 o[x] = (o[x] > g[x]) ? o[x] - (g[x] / 2) : 0;
272214 g += bm_g->stride;
273215 o += bm_o->stride;
274216 }
280222 */
281223 void shift_bitmap(Bitmap *bm, int shift_x, int shift_y)
282224 {
283 int x, y, b;
284 int w = bm->w;
285 int h = bm->h;
286 int s = bm->stride;
287 unsigned char *buf = bm->buffer;
288
289225 assert((shift_x & ~63) == 0 && (shift_y & ~63) == 0);
290226
227 if (!bm->buffer)
228 return;
229
230 int32_t w = bm->w, h = bm->h;
231 ptrdiff_t s = bm->stride;
232 uint8_t *buf = bm->buffer;
233
291234 // Shift in x direction
292 for (y = 0; y < h; y++) {
293 for (x = w - 1; x > 0; x--) {
294 b = (buf[x + y * s - 1] * shift_x) >> 6;
295 buf[x + y * s - 1] -= b;
296 buf[x + y * s] += b;
297 }
298 }
235 if (shift_x)
236 for (int32_t y = 0; y < h; y++) {
237 for (int32_t x = w - 1; x > 0; x--) {
238 uint8_t b = buf[x + y * s - 1] * shift_x >> 6;
239 buf[x + y * s - 1] -= b;
240 buf[x + y * s] += b;
241 }
242 }
299243
300244 // Shift in y direction
301 for (x = 0; x < w; x++) {
302 for (y = h - 1; y > 0; y--) {
303 b = (buf[x + (y - 1) * s] * shift_y) >> 6;
304 buf[x + (y - 1) * s] -= b;
305 buf[x + y * s] += b;
306 }
307 }
245 if (shift_y)
246 for (int32_t x = 0; x < w; x++) {
247 for (int32_t y = h - 1; y > 0; y--) {
248 uint8_t b = buf[x + y * s - s] * shift_y >> 6;
249 buf[x + y * s - s] -= b;
250 buf[x + y * s] += b;
251 }
252 }
308253 }
309254
310255 /**
433378 return FFMAX(128 - be, 0);
434379 }
435380
436 bool outline_to_bitmap2(ASS_Renderer *render_priv, ASS_Outline *outline,
437 ASS_Outline *border1, ASS_Outline *border2,
438 Bitmap **bm_g, Bitmap **bm_o)
439 {
440 assert(bm_g && bm_o);
441 *bm_g = *bm_o = NULL;
442
443 if (outline && !outline->n_points)
444 outline = NULL;
445 if (border1 && !border1->n_points)
446 border1 = NULL;
447 if (border2 && !border2->n_points)
448 border2 = NULL;
449
450 if (outline) {
451 *bm_g = outline_to_bitmap(render_priv, outline, NULL, 1);
452 if (!*bm_g)
453 return false;
454 }
455
456 if (border1 || border2) {
457 *bm_o = outline_to_bitmap(render_priv, border1, border2, 1);
458 if (!*bm_o) {
459 return false;
460 }
461 }
462
463 return true;
464 }
465
466381 /**
467382 * \brief Add two bitmaps together at a given position
468383 * Uses additive blending, clipped to [0,255]. Pure C implementation.
7979 Convert16to8Func stripe_pack;
8080 FilterFunc shrink_horz, shrink_vert;
8181 FilterFunc expand_horz, expand_vert;
82 FilterFunc pre_blur_horz[3], pre_blur_vert[3];
83 ParamFilterFunc main_blur_horz[3], main_blur_vert[3];
82 ParamFilterFunc blur_horz[5], blur_vert[5];
8483 } BitmapEngine;
8584
8685 extern const BitmapEngine ass_bitmap_engine_c;
8988
9089
9190 typedef struct {
92 int left, top;
93 int w, h; // width, height
94 int stride;
95 unsigned char *buffer; // h * stride buffer
91 int32_t left, top;
92 int32_t w, h; // width, height
93 ptrdiff_t stride;
94 uint8_t *buffer; // h * stride buffer
9695 } Bitmap;
9796
98 Bitmap *alloc_bitmap(const BitmapEngine *engine, int w, int h, bool zero);
99 bool realloc_bitmap(const BitmapEngine *engine, Bitmap *bm, int w, int h);
100 Bitmap *copy_bitmap(const BitmapEngine *engine, const Bitmap *src);
97 bool alloc_bitmap(const BitmapEngine *engine, Bitmap *bm, int32_t w, int32_t h, bool zero);
98 bool realloc_bitmap(const BitmapEngine *engine, Bitmap *bm, int32_t w, int32_t h);
99 bool copy_bitmap(const BitmapEngine *engine, Bitmap *dst, const Bitmap *src);
101100 void ass_free_bitmap(Bitmap *bm);
102101
103 Bitmap *outline_to_bitmap(ASS_Renderer *render_priv,
104 ASS_Outline *outline1, ASS_Outline *outline2,
105 int bord);
102 bool outline_to_bitmap(ASS_Renderer *render_priv, Bitmap *bm,
103 ASS_Outline *outline1, ASS_Outline *outline2);
106104
107 void ass_synth_blur(const BitmapEngine *engine, int opaque_box, int be,
108 double blur_radius, Bitmap *bm_g, Bitmap *bm_o);
109
110 /**
111 * \brief perform glyph rendering
112 * \param outline original glyph
113 * \param border1 inside "border" outline, produced by stroker
114 * \param border2 outside "border" outline, produced by stroker
115 * \param bm_g out: pointer to the bitmap of original glyph is returned here
116 * \param bm_o out: pointer to the bitmap of border glyph is returned here
117 */
118 bool outline_to_bitmap2(ASS_Renderer *render_priv, ASS_Outline *outline,
119 ASS_Outline *border1, ASS_Outline *border2,
120 Bitmap **bm_g, Bitmap **bm_o);
105 void ass_synth_blur(const BitmapEngine *engine, Bitmap *bm,
106 int be, double blur_r2);
121107
122108 int be_padding(int be);
123109 void be_blur_pre(uint8_t *buf, intptr_t w,
2828 /*
2929 * Cascade Blur Algorithm
3030 *
31 * The main idea is simple: to approximate gaussian blur with large radius
32 * you can downscale, then apply filter with small pattern, then upscale back.
33 *
34 * To achieve desired precision down/upscaling should be done with sufficiently smooth kernel.
35 * Experiment shows that downscaling of factor 2 with kernel [1, 5, 10, 10, 5, 1] and
31 * The main idea is simple: to approximate a gaussian blur with large radius,
32 * you can scale down, apply a filter with a relatively small pattern, then scale back up.
33 *
34 * To achieve the desired precision, scaling should be done with sufficiently smooth kernel.
35 * Experiments show that downscaling of factor 2 with kernel [1, 5, 10, 10, 5, 1] and
3636 * corresponding upscaling are enough for 8-bit precision.
3737 *
38 * For central filter here is used generic 9-tap filter with one of 3 different patterns
39 * combined with one of optional prefilters with fixed kernels. Kernel coefficients
40 * of the main filter are obtained from solution of least squares problem
41 * for Fourier transform of resulting kernel.
38 * Here we use generic filters with 5 different kernel widths (9 to 17-tap).
39 * Kernel coefficients of that filter are obtained from the solution of the least-squares problem
40 * for the Fourier transform of the resulting kernel.
4241 */
4342
4443
6261
6362 inline static void copy_line(int16_t *buf, const int16_t *ptr, uintptr_t offs, uintptr_t size)
6463 {
65 ptr = get_line(ptr, offs, size);
66 for (int k = 0; k < STRIPE_WIDTH; ++k)
67 buf[k] = ptr[k];
64 memcpy(buf, get_line(ptr, offs, size), STRIPE_WIDTH * sizeof(buf[0]));
6865 }
6966
7067 /*
7875 void ass_stripe_unpack_c(int16_t *dst, const uint8_t *src, ptrdiff_t src_stride,
7976 uintptr_t width, uintptr_t height)
8077 {
81 for (uintptr_t y = 0; y < height; ++y) {
78 for (uintptr_t y = 0; y < height; y++) {
8279 int16_t *ptr = dst;
8380 for (uintptr_t x = 0; x < width; x += STRIPE_WIDTH) {
84 for (int k = 0; k < STRIPE_WIDTH; ++k)
81 for (int k = 0; k < STRIPE_WIDTH; k++)
8582 ptr[k] = (uint16_t) (((src[x + k] << 7) | (src[x + k] >> 1)) + 1) >> 1;
8683 //ptr[k] = (0x4000 * src[x + k] + 127) / 255;
8784 ptr += STRIPE_WIDTH * height;
9693 {
9794 for (uintptr_t x = 0; x < width; x += STRIPE_WIDTH) {
9895 uint8_t *ptr = dst;
99 for (uintptr_t y = 0; y < height; ++y) {
96 for (uintptr_t y = 0; y < height; y++) {
10097 const int16_t *dither = dither_line + (y & 1) * STRIPE_WIDTH;
101 for (int k = 0; k < STRIPE_WIDTH; ++k)
98 for (int k = 0; k < STRIPE_WIDTH; k++)
10299 ptr[k] = (uint16_t) (src[k] - (src[k] >> 8) + dither[k]) >> 6;
103100 //ptr[k] = (255 * src[k] + 0x1FFF) / 0x4000;
104101 ptr += dst_stride;
107104 dst += STRIPE_WIDTH;
108105 }
109106 uintptr_t left = dst_stride - ((width + STRIPE_MASK) & ~STRIPE_MASK);
110 for (uintptr_t y = 0; y < height; ++y) {
111 for (uintptr_t x = 0; x < left; ++x)
107 for (uintptr_t y = 0; y < height; y++) {
108 for (uintptr_t x = 0; x < left; x++)
112109 dst[x] = 0;
113110 dst += dst_stride;
114111 }
144141 int16_t buf[3 * STRIPE_WIDTH];
145142 int16_t *ptr = buf + STRIPE_WIDTH;
146143 for (uintptr_t x = 0; x < dst_width; x += STRIPE_WIDTH) {
147 for (uintptr_t y = 0; y < src_height; ++y) {
144 for (uintptr_t y = 0; y < src_height; y++) {
148145 copy_line(ptr - 1 * STRIPE_WIDTH, src, offs - 1 * step, size);
149146 copy_line(ptr + 0 * STRIPE_WIDTH, src, offs + 0 * step, size);
150147 copy_line(ptr + 1 * STRIPE_WIDTH, src, offs + 1 * step, size);
151 for (int k = 0; k < STRIPE_WIDTH; ++k)
148 for (int k = 0; k < STRIPE_WIDTH; k++)
152149 dst[k] = shrink_func(ptr[2 * k - 4], ptr[2 * k - 3],
153150 ptr[2 * k - 2], ptr[2 * k - 1],
154151 ptr[2 * k + 0], ptr[2 * k + 1]);
155 dst += STRIPE_WIDTH;
152 dst += STRIPE_WIDTH;
156153 offs += STRIPE_WIDTH;
157154 }
158155 offs += step;
167164
168165 for (uintptr_t x = 0; x < src_width; x += STRIPE_WIDTH) {
169166 uintptr_t offs = 0;
170 for (uintptr_t y = 0; y < dst_height; ++y) {
167 for (uintptr_t y = 0; y < dst_height; y++) {
171168 const int16_t *p1p = get_line(src, offs - 4 * STRIPE_WIDTH, step);
172169 const int16_t *p1n = get_line(src, offs - 3 * STRIPE_WIDTH, step);
173170 const int16_t *z0p = get_line(src, offs - 2 * STRIPE_WIDTH, step);
174171 const int16_t *z0n = get_line(src, offs - 1 * STRIPE_WIDTH, step);
175172 const int16_t *n1p = get_line(src, offs - 0 * STRIPE_WIDTH, step);
176173 const int16_t *n1n = get_line(src, offs + 1 * STRIPE_WIDTH, step);
177 for (int k = 0; k < STRIPE_WIDTH; ++k)
174 for (int k = 0; k < STRIPE_WIDTH; k++)
178175 dst[k] = shrink_func(p1p[k], p1n[k], z0p[k], z0n[k], n1p[k], n1n[k]);
179 dst += STRIPE_WIDTH;
176 dst += 1 * STRIPE_WIDTH;
180177 offs += 2 * STRIPE_WIDTH;
181178 }
182179 src += step;
212209 int16_t buf[2 * STRIPE_WIDTH];
213210 int16_t *ptr = buf + STRIPE_WIDTH;
214211 for (uintptr_t x = STRIPE_WIDTH; x < dst_width; x += 2 * STRIPE_WIDTH) {
215 for (uintptr_t y = 0; y < src_height; ++y) {
212 for (uintptr_t y = 0; y < src_height; y++) {
216213 copy_line(ptr - 1 * STRIPE_WIDTH, src, offs - 1 * step, size);
217214 copy_line(ptr - 0 * STRIPE_WIDTH, src, offs - 0 * step, size);
218 for (int k = 0; k < STRIPE_WIDTH / 2; ++k)
215 for (int k = 0; k < STRIPE_WIDTH / 2; k++)
219216 expand_func(&dst[2 * k], &dst[2 * k + 1],
220217 ptr[k - 2], ptr[k - 1], ptr[k]);
221218 int16_t *next = dst + step - STRIPE_WIDTH;
222 for (int k = STRIPE_WIDTH / 2; k < STRIPE_WIDTH; ++k)
219 for (int k = STRIPE_WIDTH / 2; k < STRIPE_WIDTH; k++)
223220 expand_func(&next[2 * k], &next[2 * k + 1],
224221 ptr[k - 2], ptr[k - 1], ptr[k]);
225 dst += STRIPE_WIDTH;
222 dst += STRIPE_WIDTH;
226223 offs += STRIPE_WIDTH;
227224 }
228225 dst += step;
230227 if ((dst_width - 1) & STRIPE_WIDTH)
231228 return;
232229
233 for (uintptr_t y = 0; y < src_height; ++y) {
230 for (uintptr_t y = 0; y < src_height; y++) {
234231 copy_line(ptr - 1 * STRIPE_WIDTH, src, offs - 1 * step, size);
235232 copy_line(ptr - 0 * STRIPE_WIDTH, src, offs - 0 * step, size);
236 for (int k = 0; k < STRIPE_WIDTH / 2; ++k)
233 for (int k = 0; k < STRIPE_WIDTH / 2; k++)
237234 expand_func(&dst[2 * k], &dst[2 * k + 1],
238235 ptr[k - 2], ptr[k - 1], ptr[k]);
239 dst += STRIPE_WIDTH;
236 dst += STRIPE_WIDTH;
240237 offs += STRIPE_WIDTH;
241238 }
242239 }
253250 const int16_t *p1 = get_line(src, offs - 2 * STRIPE_WIDTH, step);
254251 const int16_t *z0 = get_line(src, offs - 1 * STRIPE_WIDTH, step);
255252 const int16_t *n1 = get_line(src, offs - 0 * STRIPE_WIDTH, step);
256 for (int k = 0; k < STRIPE_WIDTH; ++k)
253 for (int k = 0; k < STRIPE_WIDTH; k++)
257254 expand_func(&dst[k], &dst[k + STRIPE_WIDTH],
258255 p1[k], z0[k], n1[k]);
259 dst += 2 * STRIPE_WIDTH;
260 offs += STRIPE_WIDTH;
256 dst += 2 * STRIPE_WIDTH;
257 offs += 1 * STRIPE_WIDTH;
261258 }
262259 src += step;
263260 }
264261 }
265262
266263 /*
267 * First Supplementary Filters
268 *
269 * Perform 1D convolution with kernel [1, 2, 1].
270 */
271
272 static inline int16_t pre_blur1_func(int16_t p1, int16_t z0, int16_t n1)
273 {
274 /*
275 return (1 * p1 + 2 * z0 + 1 * n1 + 2) >> 2;
276 */
277 return (uint16_t) (((uint16_t) (p1 + n1) >> 1) + z0 + 1) >> 1;
278 }
279
280 void ass_pre_blur1_horz_c(int16_t *dst, const int16_t *src,
281 uintptr_t src_width, uintptr_t src_height)
282 {
283 uintptr_t dst_width = src_width + 2;
264 * Main Parametric Filters
265 *
266 * Perform 1D convolution with kernel [..., c2, c1, c0, d, c0, c1, c2, ...],
267 * cN = param[N], d = 1 - 2 * (c0 + c1 + c2 + ...),
268 * number of parameters is part of the function name.
269 */
270
271 static inline void blur_horz(int16_t *dst, const int16_t *src,
272 uintptr_t src_width, uintptr_t src_height,
273 const int16_t *param, const int n)
274 {
275 uintptr_t dst_width = src_width + 2 * n;
284276 uintptr_t size = ((src_width + STRIPE_MASK) & ~STRIPE_MASK) * src_height;
285277 uintptr_t step = STRIPE_WIDTH * src_height;
286278
287279 uintptr_t offs = 0;
288 int16_t buf[2 * STRIPE_WIDTH];
289 int16_t *ptr = buf + STRIPE_WIDTH;
280 int16_t buf[3 * STRIPE_WIDTH];
281 int16_t *ptr = buf + 2 * STRIPE_WIDTH;
290282 for (uintptr_t x = 0; x < dst_width; x += STRIPE_WIDTH) {
291 for (uintptr_t y = 0; y < src_height; ++y) {
292 copy_line(ptr - 1 * STRIPE_WIDTH, src, offs - 1 * step, size);
293 copy_line(ptr - 0 * STRIPE_WIDTH, src, offs - 0 * step, size);
294 for (int k = 0; k < STRIPE_WIDTH; ++k)
295 dst[k] = pre_blur1_func(ptr[k - 2], ptr[k - 1], ptr[k]);
296 dst += STRIPE_WIDTH;
283 for (uintptr_t y = 0; y < src_height; y++) {
284 for (int i = -((2 * n + STRIPE_WIDTH - 1u) / STRIPE_WIDTH); i <= 0; i++)
285 copy_line(ptr + i * STRIPE_WIDTH, src, offs + i * step, size);
286 int32_t acc[STRIPE_WIDTH];
287 for (int k = 0; k < STRIPE_WIDTH; k++)
288 acc[k] = 0x8000;
289 for (int i = n; i > 0; i--)
290 for (int k = 0; k < STRIPE_WIDTH; k++)
291 acc[k] += (int16_t) (ptr[k - n - i] - ptr[k - n]) * param[i - 1] +
292 (int16_t) (ptr[k - n + i] - ptr[k - n]) * param[i - 1];
293 for (int k = 0; k < STRIPE_WIDTH; k++)
294 dst[k] = ptr[k - n] + (acc[k] >> 16);
295
296 dst += STRIPE_WIDTH;
297297 offs += STRIPE_WIDTH;
298298 }
299299 }
300300 }
301301
302 void ass_pre_blur1_vert_c(int16_t *dst, const int16_t *src,
303 uintptr_t src_width, uintptr_t src_height)
304 {
305 uintptr_t dst_height = src_height + 2;
302 static inline void blur_vert(int16_t *dst, const int16_t *src,
303 uintptr_t src_width, uintptr_t src_height,
304 const int16_t *param, const int n)
305 {
306 uintptr_t dst_height = src_height + 2 * n;
306307 uintptr_t step = STRIPE_WIDTH * src_height;
307308
308309 for (uintptr_t x = 0; x < src_width; x += STRIPE_WIDTH) {
309310 uintptr_t offs = 0;
310 for (uintptr_t y = 0; y < dst_height; ++y) {
311 const int16_t *p1 = get_line(src, offs - 2 * STRIPE_WIDTH, step);
312 const int16_t *z0 = get_line(src, offs - 1 * STRIPE_WIDTH, step);
313 const int16_t *n1 = get_line(src, offs - 0 * STRIPE_WIDTH, step);
314 for (int k = 0; k < STRIPE_WIDTH; ++k)
315 dst[k] = pre_blur1_func(p1[k], z0[k], n1[k]);
316 dst += STRIPE_WIDTH;
311 for (uintptr_t y = 0; y < dst_height; y++) {
312 int32_t acc[STRIPE_WIDTH];
313 for (int k = 0; k < STRIPE_WIDTH; k++)
314 acc[k] = 0x8000;
315 const int16_t *center = get_line(src, offs - n * STRIPE_WIDTH, step);
316 for (int i = n; i > 0; i--) {
317 const int16_t *line1 = get_line(src, offs - (n + i) * STRIPE_WIDTH, step);
318 const int16_t *line2 = get_line(src, offs - (n - i) * STRIPE_WIDTH, step);
319 for (int k = 0; k < STRIPE_WIDTH; k++)
320 acc[k] += (int16_t) (line1[k] - center[k]) * param[i - 1] +
321 (int16_t) (line2[k] - center[k]) * param[i - 1];
322 }
323 for (int k = 0; k < STRIPE_WIDTH; k++)
324 dst[k] = center[k] + (acc[k] >> 16);
325
326 dst += STRIPE_WIDTH;
317327 offs += STRIPE_WIDTH;
318328 }
319329 src += step;
320330 }
321331 }
322332
323 /*
324 * Second Supplementary Filters
325 *
326 * Perform 1D convolution with kernel [1, 4, 6, 4, 1].
327 */
328
329 static inline int16_t pre_blur2_func(int16_t p2, int16_t p1, int16_t z0,
330 int16_t n1, int16_t n2)
331 {
332 /*
333 return (1 * p2 + 4 * p1 + 6 * z0 + 4 * n1 + 1 * n2 + 8) >> 4;
334 */
335 uint16_t r1 = ((uint16_t) (((uint16_t) (p2 + n2) >> 1) + z0) >> 1) + z0;
336 uint16_t r2 = p1 + n1;
337 uint16_t r = ((uint16_t) (r1 + r2) >> 1) | (0x8000 & r1 & r2);
338 return (uint16_t) (r + 1) >> 1;
339 }
340
341 void ass_pre_blur2_horz_c(int16_t *dst, const int16_t *src,
342 uintptr_t src_width, uintptr_t src_height)
343 {
344 uintptr_t dst_width = src_width + 4;
345 uintptr_t size = ((src_width + STRIPE_MASK) & ~STRIPE_MASK) * src_height;
346 uintptr_t step = STRIPE_WIDTH * src_height;
347
348 uintptr_t offs = 0;
349 int16_t buf[2 * STRIPE_WIDTH];
350 int16_t *ptr = buf + STRIPE_WIDTH;
351 for (uintptr_t x = 0; x < dst_width; x += STRIPE_WIDTH) {
352 for (uintptr_t y = 0; y < src_height; ++y) {
353 copy_line(ptr - 1 * STRIPE_WIDTH, src, offs - 1 * step, size);
354 copy_line(ptr - 0 * STRIPE_WIDTH, src, offs - 0 * step, size);
355 for (int k = 0; k < STRIPE_WIDTH; ++k)
356 dst[k] = pre_blur2_func(ptr[k - 4], ptr[k - 3], ptr[k - 2], ptr[k - 1], ptr[k]);
357 dst += STRIPE_WIDTH;
358 offs += STRIPE_WIDTH;
359 }
360 }
361 }
362
363 void ass_pre_blur2_vert_c(int16_t *dst, const int16_t *src,
364 uintptr_t src_width, uintptr_t src_height)
365 {
366 uintptr_t dst_height = src_height + 4;
367 uintptr_t step = STRIPE_WIDTH * src_height;
368
369 for (uintptr_t x = 0; x < src_width; x += STRIPE_WIDTH) {
370 uintptr_t offs = 0;
371 for (uintptr_t y = 0; y < dst_height; ++y) {
372 const int16_t *p2 = get_line(src, offs - 4 * STRIPE_WIDTH, step);
373 const int16_t *p1 = get_line(src, offs - 3 * STRIPE_WIDTH, step);
374 const int16_t *z0 = get_line(src, offs - 2 * STRIPE_WIDTH, step);
375 const int16_t *n1 = get_line(src, offs - 1 * STRIPE_WIDTH, step);
376 const int16_t *n2 = get_line(src, offs - 0 * STRIPE_WIDTH, step);
377 for (int k = 0; k < STRIPE_WIDTH; ++k)
378 dst[k] = pre_blur2_func(p2[k], p1[k], z0[k], n1[k], n2[k]);
379 dst += STRIPE_WIDTH;
380 offs += STRIPE_WIDTH;
381 }
382 src += step;
383 }
384 }
385
386 /*
387 * Third Supplementary Filters
388 *
389 * Perform 1D convolution with kernel [1, 6, 15, 20, 15, 6, 1].
390 */
391
392 static inline int16_t pre_blur3_func(int16_t p3, int16_t p2, int16_t p1, int16_t z0,
393 int16_t n1, int16_t n2, int16_t n3)
394 {
395 /*
396 return (1 * p3 + 6 * p2 + 15 * p1 + 20 * z0 + 15 * n1 + 6 * n2 + 1 * n3 + 32) >> 6;
397 */
398 return (20 * (uint16_t) z0 +
399 15 * (uint16_t) (p1 + n1) +
400 6 * (uint16_t) (p2 + n2) +
401 1 * (uint16_t) (p3 + n3) + 32) >> 6;
402 }
403
404 void ass_pre_blur3_horz_c(int16_t *dst, const int16_t *src,
405 uintptr_t src_width, uintptr_t src_height)
406 {
407 uintptr_t dst_width = src_width + 6;
408 uintptr_t size = ((src_width + STRIPE_MASK) & ~STRIPE_MASK) * src_height;
409 uintptr_t step = STRIPE_WIDTH * src_height;
410
411 uintptr_t offs = 0;
412 int16_t buf[2 * STRIPE_WIDTH];
413 int16_t *ptr = buf + STRIPE_WIDTH;
414 for (uintptr_t x = 0; x < dst_width; x += STRIPE_WIDTH) {
415 for (uintptr_t y = 0; y < src_height; ++y) {
416 copy_line(ptr - 1 * STRIPE_WIDTH, src, offs - 1 * step, size);
417 copy_line(ptr - 0 * STRIPE_WIDTH, src, offs - 0 * step, size);
418 for (int k = 0; k < STRIPE_WIDTH; ++k)
419 dst[k] = pre_blur3_func(ptr[k - 6], ptr[k - 5], ptr[k - 4], ptr[k - 3],
420 ptr[k - 2], ptr[k - 1], ptr[k]);
421 dst += STRIPE_WIDTH;
422 offs += STRIPE_WIDTH;
423 }
424 }
425 }
426
427 void ass_pre_blur3_vert_c(int16_t *dst, const int16_t *src,
428 uintptr_t src_width, uintptr_t src_height)
429 {
430 uintptr_t dst_height = src_height + 6;
431 uintptr_t step = STRIPE_WIDTH * src_height;
432
433 for (uintptr_t x = 0; x < src_width; x += STRIPE_WIDTH) {
434 uintptr_t offs = 0;
435 for (uintptr_t y = 0; y < dst_height; ++y) {
436 const int16_t *p3 = get_line(src, offs - 6 * STRIPE_WIDTH, step);
437 const int16_t *p2 = get_line(src, offs - 5 * STRIPE_WIDTH, step);
438 const int16_t *p1 = get_line(src, offs - 4 * STRIPE_WIDTH, step);
439 const int16_t *z0 = get_line(src, offs - 3 * STRIPE_WIDTH, step);
440 const int16_t *n1 = get_line(src, offs - 2 * STRIPE_WIDTH, step);
441 const int16_t *n2 = get_line(src, offs - 1 * STRIPE_WIDTH, step);
442 const int16_t *n3 = get_line(src, offs - 0 * STRIPE_WIDTH, step);
443 for (int k = 0; k < STRIPE_WIDTH; ++k)
444 dst[k] = pre_blur3_func(p3[k], p2[k], p1[k], z0[k], n1[k], n2[k], n3[k]);
445 dst += STRIPE_WIDTH;
446 offs += STRIPE_WIDTH;
447 }
448 src += step;
449 }
450 }
451
452 /*
453 * Main 9-tap Parametric Filters
454 *
455 * Perform 1D convolution with kernel
456 * [c3, c2, c1, c0, d, c0, c1, c2, c3] or
457 * [c3, 0, c2, c1, c0, d, c0, c1, c2, 0, c3] or
458 * [c3, 0, c2, 0, c1, c0, d, c0, c1, 0, c2, 0, c3] accordingly.
459 *
460 * cN = param[N], d = 1 - 2 * (c0 + c1 + c2 + c3).
461 */
462
463 static inline int16_t blur_func(int16_t p4, int16_t p3, int16_t p2, int16_t p1, int16_t z0,
464 int16_t n1, int16_t n2, int16_t n3, int16_t n4, const int16_t c[])
465 {
466 p1 -= z0;
467 p2 -= z0;
468 p3 -= z0;
469 p4 -= z0;
470 n1 -= z0;
471 n2 -= z0;
472 n3 -= z0;
473 n4 -= z0;
474 return (((p1 + n1) * c[0] +
475 (p2 + n2) * c[1] +
476 (p3 + n3) * c[2] +
477 (p4 + n4) * c[3] +
478 0x8000) >> 16) + z0;
479 }
480
481 void ass_blur1234_horz_c(int16_t *dst, const int16_t *src,
482 uintptr_t src_width, uintptr_t src_height,
483 const int16_t *param)
484 {
485 uintptr_t dst_width = src_width + 8;
486 uintptr_t size = ((src_width + STRIPE_MASK) & ~STRIPE_MASK) * src_height;
487 uintptr_t step = STRIPE_WIDTH * src_height;
488
489 uintptr_t offs = 0;
490 int16_t buf[2 * STRIPE_WIDTH];
491 int16_t *ptr = buf + STRIPE_WIDTH;
492 for (uintptr_t x = 0; x < dst_width; x += STRIPE_WIDTH) {
493 for (uintptr_t y = 0; y < src_height; ++y) {
494 copy_line(ptr - 1 * STRIPE_WIDTH, src, offs - 1 * step, size);
495 copy_line(ptr - 0 * STRIPE_WIDTH, src, offs - 0 * step, size);
496 for (int k = 0; k < STRIPE_WIDTH; ++k)
497 dst[k] = blur_func(ptr[k - 8], ptr[k - 7], ptr[k - 6], ptr[k - 5], ptr[k - 4],
498 ptr[k - 3], ptr[k - 2], ptr[k - 1], ptr[k - 0], param);
499 dst += STRIPE_WIDTH;
500 offs += STRIPE_WIDTH;
501 }
502 }
503 }
504
505 void ass_blur1234_vert_c(int16_t *dst, const int16_t *src,
506 uintptr_t src_width, uintptr_t src_height,
507 const int16_t *param)
508 {
509 uintptr_t dst_height = src_height + 8;
510 uintptr_t step = STRIPE_WIDTH * src_height;
511
512 for (uintptr_t x = 0; x < src_width; x += STRIPE_WIDTH) {
513 uintptr_t offs = 0;
514 for (uintptr_t y = 0; y < dst_height; ++y) {
515 const int16_t *p4 = get_line(src, offs - 8 * STRIPE_WIDTH, step);
516 const int16_t *p3 = get_line(src, offs - 7 * STRIPE_WIDTH, step);
517 const int16_t *p2 = get_line(src, offs - 6 * STRIPE_WIDTH, step);
518 const int16_t *p1 = get_line(src, offs - 5 * STRIPE_WIDTH, step);
519 const int16_t *z0 = get_line(src, offs - 4 * STRIPE_WIDTH, step);
520 const int16_t *n1 = get_line(src, offs - 3 * STRIPE_WIDTH, step);
521 const int16_t *n2 = get_line(src, offs - 2 * STRIPE_WIDTH, step);
522 const int16_t *n3 = get_line(src, offs - 1 * STRIPE_WIDTH, step);
523 const int16_t *n4 = get_line(src, offs - 0 * STRIPE_WIDTH, step);
524 for (int k = 0; k < STRIPE_WIDTH; ++k)
525 dst[k] = blur_func(p4[k], p3[k], p2[k], p1[k], z0[k],
526 n1[k], n2[k], n3[k], n4[k], param);
527 dst += STRIPE_WIDTH;
528 offs += STRIPE_WIDTH;
529 }
530 src += step;
531 }
532 }
533
534 void ass_blur1235_horz_c(int16_t *dst, const int16_t *src,
535 uintptr_t src_width, uintptr_t src_height,
536 const int16_t *param)
537 {
538 uintptr_t dst_width = src_width + 10;
539 uintptr_t size = ((src_width + STRIPE_MASK) & ~STRIPE_MASK) * src_height;
540 uintptr_t step = STRIPE_WIDTH * src_height;
541
542 uintptr_t offs = 0;
543 #if STRIPE_WIDTH < 10
544 int16_t buf[3 * STRIPE_WIDTH];
545 int16_t *ptr = buf + 2 * STRIPE_WIDTH;
546 #else
547 int16_t buf[2 * STRIPE_WIDTH];
548 int16_t *ptr = buf + STRIPE_WIDTH;
549 #endif
550 for (uintptr_t x = 0; x < dst_width; x += STRIPE_WIDTH) {
551 for (uintptr_t y = 0; y < src_height; ++y) {
552 #if STRIPE_WIDTH < 10
553 copy_line(ptr - 2 * STRIPE_WIDTH, src, offs - 2 * step, size);
554 #endif
555 copy_line(ptr - 1 * STRIPE_WIDTH, src, offs - 1 * step, size);
556 copy_line(ptr - 0 * STRIPE_WIDTH, src, offs - 0 * step, size);
557 for (int k = 0; k < STRIPE_WIDTH; ++k)
558 dst[k] = blur_func(ptr[k - 10], ptr[k - 8], ptr[k - 7], ptr[k - 6], ptr[k - 5],
559 ptr[k - 4], ptr[k - 3], ptr[k - 2], ptr[k - 0], param);
560 dst += STRIPE_WIDTH;
561 offs += STRIPE_WIDTH;
562 }
563 }
564 }
565
566 void ass_blur1235_vert_c(int16_t *dst, const int16_t *src,
567 uintptr_t src_width, uintptr_t src_height,
568 const int16_t *param)
569 {
570 uintptr_t dst_height = src_height + 10;
571 uintptr_t step = STRIPE_WIDTH * src_height;
572
573 for (uintptr_t x = 0; x < src_width; x += STRIPE_WIDTH) {
574 uintptr_t offs = 0;
575 for (uintptr_t y = 0; y < dst_height; ++y) {
576 const int16_t *p4 = get_line(src, offs - 10 * STRIPE_WIDTH, step);
577 const int16_t *p3 = get_line(src, offs - 8 * STRIPE_WIDTH, step);
578 const int16_t *p2 = get_line(src, offs - 7 * STRIPE_WIDTH, step);
579 const int16_t *p1 = get_line(src, offs - 6 * STRIPE_WIDTH, step);
580 const int16_t *z0 = get_line(src, offs - 5 * STRIPE_WIDTH, step);
581 const int16_t *n1 = get_line(src, offs - 4 * STRIPE_WIDTH, step);
582 const int16_t *n2 = get_line(src, offs - 3 * STRIPE_WIDTH, step);
583 const int16_t *n3 = get_line(src, offs - 2 * STRIPE_WIDTH, step);
584 const int16_t *n4 = get_line(src, offs - 0 * STRIPE_WIDTH, step);
585 for (int k = 0; k < STRIPE_WIDTH; ++k)
586 dst[k] = blur_func(p4[k], p3[k], p2[k], p1[k], z0[k],
587 n1[k], n2[k], n3[k], n4[k], param);
588 dst += STRIPE_WIDTH;
589 offs += STRIPE_WIDTH;
590 }
591 src += step;
592 }
593 }
594
595 void ass_blur1246_horz_c(int16_t *dst, const int16_t *src,
596 uintptr_t src_width, uintptr_t src_height,
597 const int16_t *param)
598 {
599 uintptr_t dst_width = src_width + 12;
600 uintptr_t size = ((src_width + STRIPE_MASK) & ~STRIPE_MASK) * src_height;
601 uintptr_t step = STRIPE_WIDTH * src_height;
602
603 uintptr_t offs = 0;
604 #if STRIPE_WIDTH < 12
605 int16_t buf[3 * STRIPE_WIDTH];
606 int16_t *ptr = buf + 2 * STRIPE_WIDTH;
607 #else
608 int16_t buf[2 * STRIPE_WIDTH];
609 int16_t *ptr = buf + STRIPE_WIDTH;
610 #endif
611 for (uintptr_t x = 0; x < dst_width; x += STRIPE_WIDTH) {
612 for (uintptr_t y = 0; y < src_height; ++y) {
613 #if STRIPE_WIDTH < 12
614 copy_line(ptr - 2 * STRIPE_WIDTH, src, offs - 2 * step, size);
615 #endif
616 copy_line(ptr - 1 * STRIPE_WIDTH, src, offs - 1 * step, size);
617 copy_line(ptr - 0 * STRIPE_WIDTH, src, offs - 0 * step, size);
618 for (int k = 0; k < STRIPE_WIDTH; ++k)
619 dst[k] = blur_func(ptr[k - 12], ptr[k - 10], ptr[k - 8], ptr[k - 7], ptr[k - 6],
620 ptr[k - 5], ptr[k - 4], ptr[k - 2], ptr[k - 0], param);
621 dst += STRIPE_WIDTH;
622 offs += STRIPE_WIDTH;
623 }
624 }
625 }
626
627 void ass_blur1246_vert_c(int16_t *dst, const int16_t *src,
628 uintptr_t src_width, uintptr_t src_height,
629 const int16_t *param)
630 {
631 uintptr_t dst_height = src_height + 12;
632 uintptr_t step = STRIPE_WIDTH * src_height;
633
634 for (uintptr_t x = 0; x < src_width; x += STRIPE_WIDTH) {
635 uintptr_t offs = 0;
636 for (uintptr_t y = 0; y < dst_height; ++y) {
637 const int16_t *p4 = get_line(src, offs - 12 * STRIPE_WIDTH, step);
638 const int16_t *p3 = get_line(src, offs - 10 * STRIPE_WIDTH, step);
639 const int16_t *p2 = get_line(src, offs - 8 * STRIPE_WIDTH, step);
640 const int16_t *p1 = get_line(src, offs - 7 * STRIPE_WIDTH, step);
641 const int16_t *z0 = get_line(src, offs - 6 * STRIPE_WIDTH, step);
642 const int16_t *n1 = get_line(src, offs - 5 * STRIPE_WIDTH, step);
643 const int16_t *n2 = get_line(src, offs - 4 * STRIPE_WIDTH, step);
644 const int16_t *n3 = get_line(src, offs - 2 * STRIPE_WIDTH, step);
645 const int16_t *n4 = get_line(src, offs - 0 * STRIPE_WIDTH, step);
646 for (int k = 0; k < STRIPE_WIDTH; ++k)
647 dst[k] = blur_func(p4[k], p3[k], p2[k], p1[k], z0[k],
648 n1[k], n2[k], n3[k], n4[k], param);
649 dst += STRIPE_WIDTH;
650 offs += STRIPE_WIDTH;
651 }
652 src += step;
653 }
333 void ass_blur4_horz_c(int16_t *dst, const int16_t *src,
334 uintptr_t src_width, uintptr_t src_height,
335 const int16_t *param)
336 {
337 blur_horz(dst, src, src_width, src_height, param, 4);
338 }
339
340 void ass_blur4_vert_c(int16_t *dst, const int16_t *src,
341 uintptr_t src_width, uintptr_t src_height,
342 const int16_t *param)
343 {
344 blur_vert(dst, src, src_width, src_height, param, 4);
345 }
346
347 void ass_blur5_horz_c(int16_t *dst, const int16_t *src,
348 uintptr_t src_width, uintptr_t src_height,
349 const int16_t *param)
350 {
351 blur_horz(dst, src, src_width, src_height, param, 5);
352 }
353
354 void ass_blur5_vert_c(int16_t *dst, const int16_t *src,
355 uintptr_t src_width, uintptr_t src_height,
356 const int16_t *param)
357 {
358 blur_vert(dst, src, src_width, src_height, param, 5);
359 }
360
361 void ass_blur6_horz_c(int16_t *dst, const int16_t *src,
362 uintptr_t src_width, uintptr_t src_height,
363 const int16_t *param)
364 {
365 blur_horz(dst, src, src_width, src_height, param, 6);
366 }
367
368 void ass_blur6_vert_c(int16_t *dst, const int16_t *src,
369 uintptr_t src_width, uintptr_t src_height,
370 const int16_t *param)
371 {
372 blur_vert(dst, src, src_width, src_height, param, 6);
373 }
374
375 void ass_blur7_horz_c(int16_t *dst, const int16_t *src,
376 uintptr_t src_width, uintptr_t src_height,
377 const int16_t *param)
378 {
379 blur_horz(dst, src, src_width, src_height, param, 7);
380 }
381
382 void ass_blur7_vert_c(int16_t *dst, const int16_t *src,
383 uintptr_t src_width, uintptr_t src_height,
384 const int16_t *param)
385 {
386 blur_vert(dst, src, src_width, src_height, param, 7);
387 }
388
389 void ass_blur8_horz_c(int16_t *dst, const int16_t *src,
390 uintptr_t src_width, uintptr_t src_height,
391 const int16_t *param)
392 {
393 blur_horz(dst, src, src_width, src_height, param, 8);
394 }
395
396 void ass_blur8_vert_c(int16_t *dst, const int16_t *src,
397 uintptr_t src_width, uintptr_t src_height,
398 const int16_t *param)
399 {
400 blur_vert(dst, src, src_width, src_height, param, 8);
654401 }
655402
656403
664411 res[0] = cur;
665412 cur *= mul;
666413 res[1] = cur;
667 for (int i = 2; i <= n; ++i) {
414 for (int i = 2; i < n; i++) {
668415 mul *= mul2;
669416 cur *= mul;
670417 res[i] = cur;
671418 }
672419 }
673420
674 static void coeff_blur121(double *coeff, int n)
675 {
676 double prev = coeff[1];
677 for (int i = 0; i <= n; ++i) {
678 double res = (prev + 2 * coeff[i] + coeff[i + 1]) / 4;
679 prev = coeff[i];
680 coeff[i] = res;
681 }
682 }
683
684421 static void coeff_filter(double *coeff, int n, const double kernel[4])
685422 {
686423 double prev1 = coeff[1], prev2 = coeff[2], prev3 = coeff[3];
687 for (int i = 0; i <= n; ++i) {
424 for (int i = 0; i < n; i++) {
688425 double res = coeff[i + 0] * kernel[0] +
689426 (prev1 + coeff[i + 1]) * kernel[1] +
690427 (prev2 + coeff[i + 2]) * kernel[2] +
696433 }
697434 }
698435
699 static void calc_matrix(double mat[4][4], const double *mat_freq, const int *index)
700 {
701 for (int i = 0; i < 4; ++i) {
702 mat[i][i] = mat_freq[2 * index[i]] + 3 * mat_freq[0] - 4 * mat_freq[index[i]];
703 for (int j = i + 1; j < 4; ++j)
704 mat[i][j] = mat[j][i] =
705 mat_freq[index[i] + index[j]] + mat_freq[index[j] - index[i]] +
706 2 * (mat_freq[0] - mat_freq[index[i]] - mat_freq[index[j]]);
436 static void calc_matrix(double mat[][8], const double *mat_freq, int n)
437 {
438 for (int i = 0; i < n; i++) {
439 mat[i][i] = mat_freq[2 * i + 2] + 3 * mat_freq[0] - 4 * mat_freq[i + 1];
440 for (int j = i + 1; j < n; j++)
441 mat[i][j] = mat[j][i] = mat_freq[i + j + 2] + mat_freq[j - i] +
442 2 * (mat_freq[0] - mat_freq[i + 1] - mat_freq[j + 1]);
707443 }
708444
709445 // invert transpose
710 for (int k = 0; k < 4; ++k) {
711 int ip = k, jp = k; // pivot
712 double z = 1 / mat[ip][jp];
713 mat[ip][jp] = 1;
714 for (int i = 0; i < 4; ++i) {
715 if (i == ip)
446 for (int k = 0; k < n; k++) {
447 double z = 1 / mat[k][k];
448 mat[k][k] = 1;
449 for (int i = 0; i < n; i++) {
450 if (i == k)
716451 continue;
717452
718 double mul = mat[i][jp] * z;
719 mat[i][jp] = 0;
720 for (int j = 0; j < 4; ++j)
721 mat[i][j] -= mat[ip][j] * mul;
722 }
723 for (int j = 0; j < 4; ++j)
724 mat[ip][j] *= z;
453 double mul = mat[i][k] * z;
454 mat[i][k] = 0;
455 for (int j = 0; j < n; j++)
456 mat[i][j] -= mat[k][j] * mul;
457 }
458 for (int j = 0; j < n; j++)
459 mat[k][j] *= z;
725460 }
726461 }
727462
728463 /**
729464 * \brief Solve least squares problem for kernel of the main filter
730465 * \param mu out: output coefficients
731 * \param index in: filter tap positions
732 * \param prefilter in: supplementary filter type
466 * \param n in: filter kernel radius
733467 * \param r2 in: desired standard deviation squared
734468 * \param mul in: scale multiplier
735469 */
736 static void calc_coeff(double mu[4], const int index[4], int prefilter, double r2, double mul)
737 {
738 double mul2 = mul * mul, mul3 = mul2 * mul;
470 static void calc_coeff(double mu[], int n, double r2, double mul)
471 {
472 assert(n > 0 && n <= 8);
473
474 const double w = 12096;
739475 double kernel[] = {
740 (5204 + 2520 * mul + 1092 * mul2 + 3280 * mul3) / 12096,
741 (2943 - 210 * mul - 273 * mul2 - 2460 * mul3) / 12096,
742 ( 486 - 924 * mul - 546 * mul2 + 984 * mul3) / 12096,
743 ( 17 - 126 * mul + 273 * mul2 - 164 * mul3) / 12096,
476 ((( + 3280 / w) * mul + 1092 / w) * mul + 2520 / w) * mul + 5204 / w,
477 ((( - 2460 / w) * mul - 273 / w) * mul - 210 / w) * mul + 2943 / w,
478 ((( + 984 / w) * mul - 546 / w) * mul - 924 / w) * mul + 486 / w,
479 ((( - 164 / w) * mul + 273 / w) * mul - 126 / w) * mul + 17 / w,
744480 };
745481
746 double mat_freq[14];
747 memcpy(mat_freq, kernel, sizeof(kernel));
748 memset(mat_freq + 4, 0, sizeof(mat_freq) - sizeof(kernel));
749 int n = 6;
750 coeff_filter(mat_freq, n, kernel);
751 for (int k = 0; k < 2 * prefilter; ++k)
752 coeff_blur121(mat_freq, ++n);
753
754 double vec_freq[13];
755 n = index[3] + prefilter + 3;
756 calc_gauss(vec_freq, n, r2);
757 memset(vec_freq + n + 1, 0, sizeof(vec_freq) - (n + 1) * sizeof(vec_freq[0]));
758 n -= 3;
759 coeff_filter(vec_freq, n, kernel);
760 for (int k = 0; k < prefilter; ++k)
761 coeff_blur121(vec_freq, --n);
762
763 double mat[4][4];
764 calc_matrix(mat, mat_freq, index);
765
766 double vec[4];
767 for (int i = 0; i < 4; ++i)
768 vec[i] = mat_freq[0] - mat_freq[index[i]] - vec_freq[0] + vec_freq[index[i]];
769
770 for (int i = 0; i < 4; ++i) {
482 double mat_freq[17] = { kernel[0], kernel[1], kernel[2], kernel[3] };
483 coeff_filter(mat_freq, 7, kernel);
484
485 double vec_freq[12];
486 calc_gauss(vec_freq, n + 4, r2 * mul);
487 coeff_filter(vec_freq, n + 1, kernel);
488
489 double mat[8][8];
490 calc_matrix(mat, mat_freq, n);
491
492 double vec[8];
493 for (int i = 0; i < n; i++)
494 vec[i] = mat_freq[0] - mat_freq[i + 1] - vec_freq[0] + vec_freq[i + 1];
495
496 for (int i = 0; i < n; i++) {
771497 double res = 0;
772 for (int j = 0; j < 4; ++j)
498 for (int j = 0; j < n; j++)
773499 res += mat[i][j] * vec[j];
774500 mu[i] = FFMAX(0, res);
775501 }
776502 }
777503
778504 typedef struct {
779 int level, prefilter, filter;
780 int16_t coeff[4];
505 int level, radius;
506 int16_t coeff[8];
781507 } BlurMethod;
782508
783509 static void find_best_method(BlurMethod *blur, double r2)
784510 {
785 static const int index[][4] = {
786 { 1, 2, 3, 4 },
787 { 1, 2, 3, 5 },
788 { 1, 2, 4, 6 },
789 };
790
791 double mu[5];
792 if (r2 < 1.9) {
793 blur->level = blur->prefilter = blur->filter = 0;
794
795 if (r2 < 0.5) {
796 mu[2] = 0.085 * r2 * r2 * r2;
797 mu[1] = 0.5 * r2 - 4 * mu[2];
798 mu[3] = mu[4] = 0;
799 } else {
800 calc_gauss(mu, 4, r2);
801 }
511 double mu[8];
512 if (r2 < 0.5) {
513 blur->level = 0;
514 blur->radius = 4;
515 mu[1] = 0.085 * r2 * r2 * r2;
516 mu[0] = 0.5 * r2 - 4 * mu[1];
517 mu[2] = mu[3] = 0;
802518 } else {
803 double mul = 1;
804 if (r2 < 6.693) {
805 blur->level = 0;
806
807 if (r2 < 2.8)
808 blur->prefilter = 1;
809 else if (r2 < 4.4)
810 blur->prefilter = 2;
811 else
812 blur->prefilter = 3;
813
814 blur->filter = blur->prefilter - 1;
815 } else {
816 frexp((r2 + 0.7) / 26.5, &blur->level);
817 blur->level = (blur->level + 3) >> 1;
818 mul = pow(0.25, blur->level);
819 r2 *= mul;
820
821 if (r2 < 3.15 - 1.5 * mul)
822 blur->prefilter = 0;
823 else if (r2 < 5.3 - 5.2 * mul)
824 blur->prefilter = 1;
825 else
826 blur->prefilter = 2;
827
828 blur->filter = blur->prefilter;
829 }
830 calc_coeff(mu + 1, index[blur->filter], blur->prefilter, r2, mul);
831 }
832
833 for (int i = 1; i <= 4; ++i)
834 blur->coeff[i - 1] = (int) (0x10000 * mu[i] + 0.5);
519 double frac = frexp(sqrt(0.11569 * r2 + 0.20591047), &blur->level);
520 double mul = pow(0.25, blur->level);
521 blur->radius = 8 - (int) ((10.1525 + 0.8335 * mul) * (1 - frac));
522 blur->radius = FFMAX(blur->radius, 4);
523 calc_coeff(mu, blur->radius, r2, mul);
524 }
525 for (int i = 0; i < blur->radius; i++)
526 blur->coeff[i] = (int) (0x10000 * mu[i] + 0.5);
835527 }
836528
837529 /**
843535 BlurMethod blur;
844536 find_best_method(&blur, r2);
845537
846 int w = bm->w, h = bm->h;
847 int offset = ((2 * (blur.prefilter + blur.filter) + 17) << blur.level) - 5;
848 int end_w = ((w + offset) & ~((1 << blur.level) - 1)) - 4;
849 int end_h = ((h + offset) & ~((1 << blur.level) - 1)) - 4;
538 uint32_t w = bm->w, h = bm->h;
539 int offset = ((2 * blur.radius + 9) << blur.level) - 5;
540 uint32_t end_w = ((w + offset) & ~((1 << blur.level) - 1)) - 4;
541 uint32_t end_h = ((h + offset) & ~((1 << blur.level) - 1)) - 4;
850542
851543 const int stripe_width = 1 << (engine->align_order - 1);
852 int size = end_h * ((end_w + stripe_width - 1) & ~(stripe_width - 1));
544 uint64_t size = (((uint64_t) end_w + stripe_width - 1) & ~(stripe_width - 1)) * end_h;
545 if (size > INT_MAX / 4)
546 return false;
547
853548 int16_t *tmp = ass_aligned_alloc(2 * stripe_width, 4 * size, false);
854549 if (!tmp)
855550 return false;
858553 int16_t *buf[2] = {tmp, tmp + size};
859554 int index = 0;
860555
861 for (int i = 0; i < blur.level; ++i) {
556 for (int i = 0; i < blur.level; i++) {
862557 engine->shrink_vert(buf[index ^ 1], buf[index], w, h);
863558 h = (h + 5) >> 1;
864559 index ^= 1;
865560 }
866 for (int i = 0; i < blur.level; ++i) {
561 for (int i = 0; i < blur.level; i++) {
867562 engine->shrink_horz(buf[index ^ 1], buf[index], w, h);
868563 w = (w + 5) >> 1;
869564 index ^= 1;
870565 }
871 if (blur.prefilter) {
872 engine->pre_blur_horz[blur.prefilter - 1](buf[index ^ 1], buf[index], w, h);
873 w += 2 * blur.prefilter;
874 index ^= 1;
875 }
876 engine->main_blur_horz[blur.filter](buf[index ^ 1], buf[index], w, h, blur.coeff);
877 w += 2 * blur.filter + 8;
566 assert(blur.radius >= 4 && blur.radius <= 8);
567 engine->blur_horz[blur.radius - 4](buf[index ^ 1], buf[index], w, h, blur.coeff);
568 w += 2 * blur.radius;
878569 index ^= 1;
879 for (int i = 0; i < blur.level; ++i) {
570 engine->blur_vert[blur.radius - 4](buf[index ^ 1], buf[index], w, h, blur.coeff);
571 h += 2 * blur.radius;
572 index ^= 1;
573 for (int i = 0; i < blur.level; i++) {
880574 engine->expand_horz(buf[index ^ 1], buf[index], w, h);
881575 w = 2 * w + 4;
882576 index ^= 1;
883577 }
884 if (blur.prefilter) {
885 engine->pre_blur_vert[blur.prefilter - 1](buf[index ^ 1], buf[index], w, h);
886 h += 2 * blur.prefilter;
887 index ^= 1;
888 }
889 engine->main_blur_vert[blur.filter](buf[index ^ 1], buf[index], w, h, blur.coeff);
890 h += 2 * blur.filter + 8;
891 index ^= 1;
892 for (int i = 0; i < blur.level; ++i) {
578 for (int i = 0; i < blur.level; i++) {
893579 engine->expand_vert(buf[index ^ 1], buf[index], w, h);
894580 h = 2 * h + 4;
895581 index ^= 1;
900586 ass_aligned_free(tmp);
901587 return false;
902588 }
903 offset = ((blur.prefilter + blur.filter + 8) << blur.level) - 4;
589 offset = ((blur.radius + 4) << blur.level) - 4;
904590 bm->left -= offset;
905591 bm->top -= offset;
906592
3737 #include "ass_cache_template.h"
3838
3939 // font cache
40 static unsigned font_hash(void *buf, size_t len)
40 static uint32_t font_hash(void *buf, uint32_t hval)
4141 {
4242 ASS_FontDesc *desc = buf;
43 unsigned hval;
44 hval = fnv_32a_str(desc->family, FNV1_32A_INIT);
43 hval = fnv_32a_str(desc->family, hval);
4544 hval = fnv_32a_buf(&desc->bold, sizeof(desc->bold), hval);
4645 hval = fnv_32a_buf(&desc->italic, sizeof(desc->italic), hval);
4746 hval = fnv_32a_buf(&desc->vertical, sizeof(desc->vertical), hval);
4847 return hval;
4948 }
5049
51 static unsigned font_compare(void *key1, void *key2, size_t key_size)
50 static bool font_compare(void *key1, void *key2)
5251 {
5352 ASS_FontDesc *a = key1;
5453 ASS_FontDesc *b = key2;
5554 if (strcmp(a->family, b->family) != 0)
56 return 0;
55 return false;
5756 if (a->bold != b->bold)
58 return 0;
57 return false;
5958 if (a->italic != b->italic)
60 return 0;
59 return false;
6160 if (a->vertical != b->vertical)
62 return 0;
63 return 1;
64 }
65
66 static bool font_key_move(void *dst, void *src, size_t key_size)
61 return false;
62 return true;
63 }
64
65 static bool font_key_move(void *dst, void *src)
6766 {
6867 ASS_FontDesc *k = src;
6968 if (dst)
70 memcpy(dst, src, key_size);
69 memcpy(dst, src, sizeof(ASS_FontDesc));
7170 else
7271 free(k->family);
7372 return true;
7776 {
7877 ass_font_clear(value);
7978 }
79
80 size_t ass_font_construct(void *key, void *value, void *priv);
8081
8182 const CacheDesc font_cache_desc = {
8283 .hash_func = font_hash,
8384 .compare_func = font_compare,
8485 .key_move_func = font_key_move,
86 .construct_func = ass_font_construct,
8587 .destruct_func = font_destruct,
8688 .key_size = sizeof(ASS_FontDesc),
8789 .value_size = sizeof(ASS_Font)
8991
9092
9193 // bitmap cache
92 static unsigned bitmap_hash(void *key, size_t key_size)
94 static bool bitmap_key_move(void *dst, void *src)
95 {
96 BitmapHashKey *k = src;
97 if (dst)
98 memcpy(dst, src, sizeof(BitmapHashKey));
99 else
100 ass_cache_dec_ref(k->outline);
101 return true;
102 }
103
104 static void bitmap_destruct(void *key, void *value)
93105 {
94106 BitmapHashKey *k = key;
95 switch (k->type) {
96 case BITMAP_OUTLINE: return outline_bitmap_hash(&k->u, key_size);
97 case BITMAP_CLIP: return clip_bitmap_hash(&k->u, key_size);
98 default: return 0;
99 }
100 }
101
102 static unsigned bitmap_compare(void *a, void *b, size_t key_size)
103 {
104 BitmapHashKey *ak = a;
105 BitmapHashKey *bk = b;
106 if (ak->type != bk->type) return 0;
107 switch (ak->type) {
108 case BITMAP_OUTLINE: return outline_bitmap_compare(&ak->u, &bk->u, key_size);
109 case BITMAP_CLIP: return clip_bitmap_compare(&ak->u, &bk->u, key_size);
110 default: return 0;
111 }
112 }
113
114 static bool bitmap_key_move(void *dst, void *src, size_t key_size)
115 {
116 BitmapHashKey *d = dst, *s = src;
117 if (!dst) {
118 if (s->type == BITMAP_OUTLINE)
119 ass_cache_dec_ref(s->u.outline.outline);
120 return true;
121 }
122 memcpy(dst, src, key_size);
123 if (s->type != BITMAP_CLIP)
124 return true;
125 d->u.clip.text = strdup(s->u.clip.text);
126 return d->u.clip.text;
127 }
128
129 static void bitmap_destruct(void *key, void *value)
130 {
131 BitmapHashValue *v = value;
132 BitmapHashKey *k = key;
133 if (v->bm)
134 ass_free_bitmap(v->bm);
135 if (v->bm_o)
136 ass_free_bitmap(v->bm_o);
137 switch (k->type) {
138 case BITMAP_OUTLINE: ass_cache_dec_ref(k->u.outline.outline); break;
139 case BITMAP_CLIP: free(k->u.clip.text); break;
140 }
141 }
107 ass_free_bitmap(value);
108 ass_cache_dec_ref(k->outline);
109 }
110
111 size_t ass_bitmap_construct(void *key, void *value, void *priv);
142112
143113 const CacheDesc bitmap_cache_desc = {
144114 .hash_func = bitmap_hash,
145115 .compare_func = bitmap_compare,
146116 .key_move_func = bitmap_key_move,
117 .construct_func = ass_bitmap_construct,
147118 .destruct_func = bitmap_destruct,
148119 .key_size = sizeof(BitmapHashKey),
149 .value_size = sizeof(BitmapHashValue)
120 .value_size = sizeof(Bitmap)
150121 };
151122
152123
153124 // composite cache
154 static unsigned composite_hash(void *key, size_t key_size)
125 static uint32_t composite_hash(void *key, uint32_t hval)
155126 {
156127 CompositeHashKey *k = key;
157 unsigned hval = filter_hash(&k->filter, key_size);
158 for (size_t i = 0; i < k->bitmap_count; i++) {
159 hval = fnv_32a_buf(&k->bitmaps[i].image, sizeof(k->bitmaps[i].image), hval);
160 hval = fnv_32a_buf(&k->bitmaps[i].x, sizeof(k->bitmaps[i].x), hval);
161 hval = fnv_32a_buf(&k->bitmaps[i].y, sizeof(k->bitmaps[i].y), hval);
162 }
128 hval = filter_hash(&k->filter, hval);
129 for (size_t i = 0; i < k->bitmap_count; i++)
130 hval = bitmap_ref_hash(&k->bitmaps[i], hval);
163131 return hval;
164132 }
165133
166 static unsigned composite_compare(void *a, void *b, size_t key_size)
134 static bool composite_compare(void *a, void *b)
167135 {
168136 CompositeHashKey *ak = a;
169137 CompositeHashKey *bk = b;
138 if (!filter_compare(&ak->filter, &bk->filter))
139 return false;
170140 if (ak->bitmap_count != bk->bitmap_count)
171 return 0;
172 for (size_t i = 0; i < ak->bitmap_count; i++) {
173 if (ak->bitmaps[i].image != bk->bitmaps[i].image ||
174 ak->bitmaps[i].x != bk->bitmaps[i].x ||
175 ak->bitmaps[i].y != bk->bitmaps[i].y)
176 return 0;
177 }
178 return filter_compare(&ak->filter, &bk->filter, key_size);
179 }
180
181 static bool composite_key_move(void *dst, void *src, size_t key_size)
141 return false;
142 for (size_t i = 0; i < ak->bitmap_count; i++)
143 if (!bitmap_ref_compare(&ak->bitmaps[i], &bk->bitmaps[i]))
144 return false;
145 return true;
146 }
147
148 static bool composite_key_move(void *dst, void *src)
182149 {
183150 if (dst) {
184 memcpy(dst, src, key_size);
151 memcpy(dst, src, sizeof(CompositeHashKey));
185152 return true;
186153 }
187154 CompositeHashKey *k = src;
188 for (size_t i = 0; i < k->bitmap_count; i++)
189 ass_cache_dec_ref(k->bitmaps[i].image);
155 for (size_t i = 0; i < k->bitmap_count; i++) {
156 ass_cache_dec_ref(k->bitmaps[i].bm);
157 ass_cache_dec_ref(k->bitmaps[i].bm_o);
158 }
190159 free(k->bitmaps);
191160 return true;
192161 }
195164 {
196165 CompositeHashValue *v = value;
197166 CompositeHashKey *k = key;
198 if (v->bm)
199 ass_free_bitmap(v->bm);
200 if (v->bm_o)
201 ass_free_bitmap(v->bm_o);
202 if (v->bm_s)
203 ass_free_bitmap(v->bm_s);
204 for (size_t i = 0; i < k->bitmap_count; i++)
205 ass_cache_dec_ref(k->bitmaps[i].image);
167 ass_free_bitmap(&v->bm);
168 ass_free_bitmap(&v->bm_o);
169 ass_free_bitmap(&v->bm_s);
170 for (size_t i = 0; i < k->bitmap_count; i++) {
171 ass_cache_dec_ref(k->bitmaps[i].bm);
172 ass_cache_dec_ref(k->bitmaps[i].bm_o);
173 }
206174 free(k->bitmaps);
207175 }
176
177 size_t ass_composite_construct(void *key, void *value, void *priv);
208178
209179 const CacheDesc composite_cache_desc = {
210180 .hash_func = composite_hash,
211181 .compare_func = composite_compare,
212182 .key_move_func = composite_key_move,
183 .construct_func = ass_composite_construct,
213184 .destruct_func = composite_destruct,
214185 .key_size = sizeof(CompositeHashKey),
215186 .value_size = sizeof(CompositeHashValue)
217188
218189
219190 // outline cache
220 static unsigned outline_hash(void *key, size_t key_size)
191 static uint32_t outline_hash(void *key, uint32_t hval)
221192 {
222193 OutlineHashKey *k = key;
223194 switch (k->type) {
224 case OUTLINE_GLYPH: return glyph_hash(&k->u, key_size);
225 case OUTLINE_DRAWING: return drawing_hash(&k->u, key_size);
226 default: return 0;
227 }
228 }
229
230 static unsigned outline_compare(void *a, void *b, size_t key_size)
195 case OUTLINE_GLYPH:
196 return glyph_hash(&k->u, hval);
197 case OUTLINE_DRAWING:
198 return drawing_hash(&k->u, hval);
199 case OUTLINE_BORDER:
200 return border_hash(&k->u, hval);
201 default: // OUTLINE_BOX
202 return hval;
203 }
204 }
205
206 static bool outline_compare(void *a, void *b)
231207 {
232208 OutlineHashKey *ak = a;
233209 OutlineHashKey *bk = b;
234 if (ak->type != bk->type) return 0;
210 if (ak->type != bk->type)
211 return false;
235212 switch (ak->type) {
236 case OUTLINE_GLYPH: return glyph_compare(&ak->u, &bk->u, key_size);
237 case OUTLINE_DRAWING: return drawing_compare(&ak->u, &bk->u, key_size);
238 default: return 0;
239 }
240 }
241
242 static bool outline_key_move(void *dst, void *src, size_t key_size)
213 case OUTLINE_GLYPH:
214 return glyph_compare(&ak->u, &bk->u);
215 case OUTLINE_DRAWING:
216 return drawing_compare(&ak->u, &bk->u);
217 case OUTLINE_BORDER:
218 return border_compare(&ak->u, &bk->u);
219 default: // OUTLINE_BOX
220 return true;
221 }
222 }
223
224 static bool outline_key_move(void *dst, void *src)
243225 {
244226 OutlineHashKey *d = dst, *s = src;
245227 if (!dst) {
247229 ass_cache_dec_ref(s->u.glyph.font);
248230 return true;
249231 }
250 memcpy(dst, src, key_size);
251 if (s->type != OUTLINE_DRAWING)
252 return true;
253 d->u.drawing.text = strdup(s->u.drawing.text);
254 return d->u.drawing.text;
232 memcpy(dst, src, sizeof(OutlineHashKey));
233 if (s->type == OUTLINE_DRAWING) {
234 d->u.drawing.text = strdup(s->u.drawing.text);
235 return d->u.drawing.text;
236 }
237 if (s->type == OUTLINE_BORDER)
238 ass_cache_inc_ref(s->u.border.outline);
239 return true;
255240 }
256241
257242 static void outline_destruct(void *key, void *value)
258243 {
259244 OutlineHashValue *v = value;
260245 OutlineHashKey *k = key;
261 outline_free(&v->outline);
262 outline_free(&v->border[0]);
263 outline_free(&v->border[1]);
246 outline_free(&v->outline[0]);
247 outline_free(&v->outline[1]);
264248 switch (k->type) {
265 case OUTLINE_GLYPH: ass_cache_dec_ref(k->u.glyph.font); break;
266 case OUTLINE_DRAWING: free(k->u.drawing.text); break;
267 }
268 }
249 case OUTLINE_GLYPH:
250 ass_cache_dec_ref(k->u.glyph.font);
251 break;
252 case OUTLINE_DRAWING:
253 free(k->u.drawing.text);
254 break;
255 case OUTLINE_BORDER:
256 ass_cache_dec_ref(k->u.border.outline);
257 break;
258 default: // OUTLINE_BOX
259 break;
260 }
261 }
262
263 size_t ass_outline_construct(void *key, void *value, void *priv);
269264
270265 const CacheDesc outline_cache_desc = {
271266 .hash_func = outline_hash,
272267 .compare_func = outline_compare,
273268 .key_move_func = outline_key_move,
269 .construct_func = ass_outline_construct,
274270 .destruct_func = outline_destruct,
275271 .key_size = sizeof(OutlineHashKey),
276272 .value_size = sizeof(OutlineHashValue)
278274
279275
280276 // glyph metric cache
281 static bool glyph_metrics_key_move(void *dst, void *src, size_t key_size)
277 static bool glyph_metrics_key_move(void *dst, void *src)
282278 {
283279 if (!dst)
284280 return true;
285 memcpy(dst, src, key_size);
281 memcpy(dst, src, sizeof(GlyphMetricsHashKey));
286282 GlyphMetricsHashKey *k = src;
287283 ass_cache_inc_ref(k->font);
288284 return true;
293289 GlyphMetricsHashKey *k = key;
294290 ass_cache_dec_ref(k->font);
295291 }
292
293 size_t ass_glyph_metrics_construct(void *key, void *value, void *priv);
296294
297295 const CacheDesc glyph_metrics_cache_desc = {
298296 .hash_func = glyph_metrics_hash,
299297 .compare_func = glyph_metrics_compare,
300298 .key_move_func = glyph_metrics_key_move,
299 .construct_func = ass_glyph_metrics_construct,
301300 .destruct_func = glyph_metrics_destruct,
302301 .key_size = sizeof(GlyphMetricsHashKey),
303 .value_size = sizeof(GlyphMetricsHashValue)
302 .value_size = sizeof(FT_Glyph_Metrics)
304303 };
305304
306305
359358 return cache;
360359 }
361360
362 bool ass_cache_get(Cache *cache, void *key, void *value_ptr)
363 {
364 char **value = (char **) value_ptr;
361 void *ass_cache_get(Cache *cache, void *key, void *priv)
362 {
365363 const CacheDesc *desc = cache->desc;
366364 size_t key_offs = CACHE_ITEM_SIZE + align_cache(desc->value_size);
367 unsigned bucket = desc->hash_func(key, desc->key_size) % cache->buckets;
365 unsigned bucket = desc->hash_func(key, FNV1_32A_INIT) % cache->buckets;
368366 CacheItem *item = cache->map[bucket];
369367 while (item) {
370 if (desc->compare_func(key, (char *) item + key_offs, desc->key_size)) {
368 if (desc->compare_func(key, (char *) item + key_offs)) {
371369 assert(item->size);
372370 if (!item->queue_prev || item->queue_next) {
373371 if (item->queue_prev) {
381379 item->queue_next = NULL;
382380 }
383381 cache->hits++;
384 desc->key_move_func(NULL, key, desc->key_size);
385 *value = (char *) item + CACHE_ITEM_SIZE;
382 desc->key_move_func(NULL, key);
386383 item->ref_count++;
387 return true;
384 return (char *) item + CACHE_ITEM_SIZE;
388385 }
389386 item = item->next;
390387 }
392389
393390 item = malloc(key_offs + desc->key_size);
394391 if (!item) {
395 desc->key_move_func(NULL, key, desc->key_size);
396 *value = NULL;
397 return false;
398 }
399 item->size = 0;
392 desc->key_move_func(NULL, key);
393 return NULL;
394 }
400395 item->cache = cache;
401396 item->desc = desc;
402 if (!desc->key_move_func((char *) item + key_offs, key, desc->key_size)) {
397 void *new_key = (char *) item + key_offs;
398 if (!desc->key_move_func(new_key, key)) {
403399 free(item);
404 *value = NULL;
405 return false;
406 }
407 *value = (char *) item + CACHE_ITEM_SIZE;
400 return NULL;
401 }
402 void *value = (char *) item + CACHE_ITEM_SIZE;
403 item->size = desc->construct_func(new_key, value, priv);
404 assert(item->size);
408405
409406 CacheItem **bucketptr = &cache->map[bucket];
410407 if (*bucketptr)
413410 item->next = *bucketptr;
414411 *bucketptr = item;
415412
416 item->queue_prev = NULL;
417 item->queue_next = NULL;
418 item->ref_count = 1;
419 return false;
420 }
421
422 void *ass_cache_key(void *value)
423 {
424 CacheItem *item = value_to_item(value);
425 return (char *) value + align_cache(item->desc->value_size);
426 }
427
428 void ass_cache_commit(void *value, size_t item_size)
429 {
430 CacheItem *item = value_to_item(value);
431 assert(!item->size && item_size);
432 item->size = item_size;
433 Cache *cache = item->cache;
434 cache->cache_size += item_size;
435 cache->items++;
436
437413 *cache->queue_last = item;
438414 item->queue_prev = cache->queue_last;
439415 cache->queue_last = &item->queue_next;
440 item->ref_count++;
416 item->queue_next = NULL;
417 item->ref_count = 2;
418
419 cache->cache_size += item->size;
420 cache->items++;
421 return value;
422 }
423
424 void *ass_cache_key(void *value)
425 {
426 CacheItem *item = value_to_item(value);
427 return (char *) value + align_cache(item->desc->value_size);
441428 }
442429
443430 static inline void destroy_item(const CacheDesc *desc, CacheItem *item)
2929 // cache values
3030
3131 typedef struct {
32 bool valid;
33 Bitmap *bm; // the actual bitmaps
34 Bitmap *bm_o;
35 } BitmapHashValue;
36
37 typedef struct {
38 Bitmap *bm;
39 Bitmap *bm_o;
40 Bitmap *bm_s;
32 Bitmap bm, bm_o, bm_s;
4133 } CompositeHashValue;
4234
4335 typedef struct {
4436 bool valid;
45 ASS_Outline outline;
46 ASS_Outline border[2];
47 ASS_Rect bbox_scaled; // bbox after scaling, but before rotation
48 ASS_Vector advance; // 26.6, advance distance to the next outline in line
49 int asc, desc; // ascender/descender
37 ASS_Outline outline[2];
38 ASS_Rect cbox; // bounding box of all control points
39 int advance; // 26.6, advance distance to the next outline in line
40 int asc, desc; // ascender/descender
5041 } OutlineHashValue;
51
52 typedef struct {
53 FT_Glyph_Metrics metrics;
54 } GlyphMetricsHashValue;
5542
5643 // Create definitions for bitmap, outline and composite hash keys
5744 #define CREATE_STRUCT_DEFINITIONS
5845 #include "ass_cache_template.h"
5946
6047 // Type-specific function pointers
61 typedef unsigned(*HashFunction)(void *key, size_t key_size);
62 typedef unsigned(*HashCompare)(void *a, void *b, size_t key_size);
63 typedef bool(*CacheKeyMove)(void *dst, void *src, size_t key_size);
64 typedef void(*CacheItemDestructor)(void *key, void *value);
48 typedef uint32_t (*HashFunction)(void *key, uint32_t hval);
49 typedef bool (*HashCompare)(void *a, void *b);
50 typedef bool (*CacheKeyMove)(void *dst, void *src);
51 typedef size_t (*CacheValueConstructor)(void *key, void *value, void *priv);
52 typedef void (*CacheItemDestructor)(void *key, void *value);
6553
6654 // cache hash keys
6755
6957 enum {
7058 OUTLINE_GLYPH,
7159 OUTLINE_DRAWING,
60 OUTLINE_BORDER,
61 OUTLINE_BOX,
7262 } type;
7363 union {
7464 GlyphHashKey glyph;
7565 DrawingHashKey drawing;
66 BorderHashKey border;
7667 } u;
7768 } OutlineHashKey;
7869
79 typedef struct bitmap_hash_key {
80 enum {
81 BITMAP_OUTLINE,
82 BITMAP_CLIP,
83 } type;
84 union {
85 OutlineBitmapHashKey outline;
86 ClipMaskHashKey clip;
87 } u;
88 } BitmapHashKey;
89
90 typedef struct {
91 BitmapHashValue *image;
92 int x, y;
93 } BitmapRef;
94
9570 enum {
96 FILTER_BORDER_STYLE_3 = 1,
97 FILTER_NONZERO_BORDER = 2,
98 FILTER_NONZERO_SHADOW = 4,
99 FILTER_DRAW_SHADOW = 8, // VSFilter compatibility
71 FILTER_BORDER_STYLE_3 = 0x01,
72 FILTER_NONZERO_BORDER = 0x02,
73 FILTER_NONZERO_SHADOW = 0x04,
74 FILTER_FILL_IN_SHADOW = 0x08,
75 FILTER_FILL_IN_BORDER = 0x10,
10076 };
10177
10278 typedef struct {
11086 HashFunction hash_func;
11187 HashCompare compare_func;
11288 CacheKeyMove key_move_func;
89 CacheValueConstructor construct_func;
11390 CacheItemDestructor destruct_func;
11491 size_t key_size;
11592 size_t value_size;
11693 } CacheDesc;
11794
11895 Cache *ass_cache_create(const CacheDesc *desc);
119 bool ass_cache_get(Cache *cache, void *key, void *value_ptr);
96 void *ass_cache_get(Cache *cache, void *key, void *priv);
12097 void *ass_cache_key(void *value);
121 void ass_cache_commit(void *value, size_t item_size);
12298 void ass_cache_inc_ref(void *value);
12399 void ass_cache_dec_ref(void *value);
124100 void ass_cache_cut(Cache *cache, size_t max_size);
77 char *member;
88 #define VECTOR(member) \
99 ASS_Vector member;
10 #define BITMAPHASHKEY(member) \
11 BitmapHashKey member;
1210 #define END(typedefnamename) \
1311 } typedefnamename;
1412
1513 #elif defined(CREATE_COMPARISON_FUNCTIONS)
1614 #undef CREATE_COMPARISON_FUNCTIONS
1715 #define START(funcname, structname) \
18 static unsigned funcname##_compare(void *key1, void *key2, size_t key_size) \
16 static bool funcname##_compare(void *key1, void *key2) \
1917 { \
2018 struct structname *a = key1; \
2119 struct structname *b = key2; \
2624 strcmp(a->member, b->member) == 0 &&
2725 #define VECTOR(member) \
2826 a->member.x == b->member.x && a->member.y == b->member.y &&
29 #define BITMAPHASHKEY(member) \
30 bitmap_compare(&a->member, &b->member, sizeof(a->member)) &&
3127 #define END(typedefname) \
32 1; \
28 true; \
3329 }
3430
3531 #elif defined(CREATE_HASH_FUNCTIONS)
3632 #undef CREATE_HASH_FUNCTIONS
3733 #define START(funcname, structname) \
38 static unsigned funcname##_hash(void *buf, size_t len) \
34 static uint32_t funcname##_hash(void *buf, uint32_t hval) \
3935 { \
40 struct structname *p = buf; \
41 unsigned hval = FNV1_32A_INIT;
36 struct structname *p = buf;
4237 #define GENERIC(type, member) \
4338 hval = fnv_32a_buf(&p->member, sizeof(p->member), hval);
4439 #define STRING(member) \
4540 hval = fnv_32a_str(p->member, hval);
4641 #define VECTOR(member) GENERIC(, member.x); GENERIC(, member.y);
47 #define BITMAPHASHKEY(member) { \
48 unsigned temp = bitmap_hash(&p->member, sizeof(p->member)); \
49 hval = fnv_32a_buf(&temp, sizeof(temp), hval); \
50 }
5142 #define END(typedefname) \
5243 return hval; \
5344 }
5950
6051
6152 // describes an outline bitmap
62 START(outline_bitmap, outline_bitmap_hash_key)
53 START(bitmap, bitmap_hash_key)
6354 GENERIC(OutlineHashValue *, outline)
64 GENERIC(int, frx) // signed 10.22
65 GENERIC(int, fry) // signed 10.22
66 GENERIC(int, frz) // signed 10.22
67 GENERIC(int, fax) // signed 16.16
68 GENERIC(int, fay) // signed 16.16
69 // shift vector that was added to glyph before applying rotation
70 // = 0, if frx = fry = frx = 0
71 // = (glyph base point) - (rotation origin), otherwise
72 GENERIC(int, shift_x)
73 GENERIC(int, shift_y)
74 VECTOR(advance) // subpixel shift vector
75 END(OutlineBitmapHashKey)
55 // quantized transform matrix
56 VECTOR(offset)
57 VECTOR(matrix_x)
58 VECTOR(matrix_y)
59 VECTOR(matrix_z)
60 END(BitmapHashKey)
7661
77 // describe a clip mask bitmap
78 START(clip_bitmap, clip_bitmap_hash_key)
79 STRING(text)
80 END(ClipMaskHashKey)
62 START(glyph_metrics, glyph_metrics_hash_key)
63 GENERIC(ASS_Font *, font)
64 GENERIC(double, size)
65 GENERIC(int, face_index)
66 GENERIC(int, glyph_index)
67 END(GlyphMetricsHashKey)
8168
8269 // describes an outline glyph
8370 START(glyph, glyph_hash_key)
8774 GENERIC(int, glyph_index)
8875 GENERIC(int, bold)
8976 GENERIC(int, italic)
90 GENERIC(unsigned, scale_x) // 16.16
91 GENERIC(unsigned, scale_y) // 16.16
92 VECTOR(outline) // border width, 26.6
93 GENERIC(unsigned, flags) // glyph decoration flags
94 GENERIC(unsigned, border_style)
95 GENERIC(int, hspacing) // 16.16
77 GENERIC(unsigned, flags) // glyph decoration flags
9678 END(GlyphHashKey)
97
98 START(glyph_metrics, glyph_metrics_hash_key)
99 GENERIC(ASS_Font *, font)
100 GENERIC(double, size)
101 GENERIC(int, face_index)
102 GENERIC(int, glyph_index)
103 GENERIC(unsigned, scale_x)
104 GENERIC(unsigned, scale_y)
105 END(GlyphMetricsHashKey)
10679
10780 // describes an outline drawing
10881 START(drawing, drawing_hash_key)
109 GENERIC(unsigned, scale_x)
110 GENERIC(unsigned, scale_y)
111 GENERIC(int, pbo)
112 VECTOR(outline)
113 GENERIC(unsigned, border_style)
114 GENERIC(int, hspacing)
115 GENERIC(int, scale)
116 GENERIC(unsigned, hash)
11782 STRING(text)
11883 END(DrawingHashKey)
84
85 // describes an offset outline
86 START(border, border_hash_key)
87 GENERIC(OutlineHashValue *, outline)
88 // outline is scaled by 2^scale_ord_x|y before stroking
89 // to keep stoker error in allowable range
90 GENERIC(int, scale_ord_x)
91 GENERIC(int, scale_ord_y)
92 VECTOR(border) // border size in STROKER_ACCURACY units
93 END(BorderHashKey)
11994
12095 // describes post-combining effects
12196 START(filter, filter_desc)
12297 GENERIC(int, flags)
12398 GENERIC(int, be)
124 GENERIC(double, blur)
99 GENERIC(int, blur)
125100 VECTOR(shadow)
126101 END(FilterDesc)
102
103 // describes glyph bitmap reference
104 START(bitmap_ref, bitmap_ref_key)
105 GENERIC(Bitmap *, bm)
106 GENERIC(Bitmap *, bm_o)
107 VECTOR(pos)
108 VECTOR(pos_o)
109 END(BitmapRef)
127110
128111 #undef START
129112 #undef GENERIC
130113 #undef STRING
131114 #undef VECTOR
132 #undef BITMAPHASHKEY
133115 #undef END
2828
2929 #include "ass_coretext.h"
3030
31 #define SAFE_CFRelease(x) do { if (x) CFRelease(x); } while(0)
31 #define SAFE_CFRelease(x) do { if (x) CFRelease(x); } while (0)
3232
3333 static const ASS_FontMapping font_substitutions[] = {
3434 {"sans-serif", "Helvetica"},
5959 SAFE_CFRelease(fontd);
6060 }
6161
62 static bool is_postscript_font_format(CFNumberRef cfformat)
63 {
64 bool ret = false;
65 int format;
66 if (CFNumberGetValue(cfformat, kCFNumberIntType, &format)) {
67 ret = format == kCTFontFormatOpenTypePostScript ||
68 format == kCTFontFormatPostScript;
69 }
70 return ret;
71 }
72
6273 static bool check_postscript(void *priv)
6374 {
6475 CTFontDescriptorRef fontd = priv;
6576 CFNumberRef cfformat =
6677 CTFontDescriptorCopyAttribute(fontd, kCTFontFormatAttribute);
67 int format;
68
69 if (!CFNumberGetValue(cfformat, kCFNumberIntType, &format))
70 return false;
71
78 bool ret = is_postscript_font_format(cfformat);
7279 SAFE_CFRelease(cfformat);
73
74 return format == kCTFontFormatOpenTypePostScript ||
75 format == kCTFontFormatPostScript;
80 return ret;
7681 }
7782
7883 static bool check_glyph(void *priv, uint32_t code)
95100 static char *get_font_file(CTFontDescriptorRef fontd)
96101 {
97102 CFURLRef url = CTFontDescriptorCopyAttribute(fontd, kCTFontURLAttribute);
103 if (!url)
104 return NULL;
98105 CFStringRef path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
106 if (!path) {
107 SAFE_CFRelease(url);
108 return NULL;
109 }
99110 char *buffer = cfstr2buf(path);
100111 SAFE_CFRelease(path);
101112 SAFE_CFRelease(url);
102113 return buffer;
103114 }
104115
105 static void get_name(CTFontDescriptorRef fontd, CFStringRef attr,
106 char **array, int *idx)
107 {
108
116 static char *get_name(CTFontDescriptorRef fontd, CFStringRef attr)
117 {
118 char *ret = NULL;
109119 CFStringRef name = CTFontDescriptorCopyAttribute(fontd, attr);
110120 if (name) {
111 array[*idx] = cfstr2buf(name);
121 ret = cfstr2buf(name);
112122 SAFE_CFRelease(name);
113 *idx += 1;
114 }
115 }
116
117 static void get_trait(CFDictionaryRef traits, CFStringRef attribute,
118 float *trait)
119 {
120 CFNumberRef cftrait = CFDictionaryGetValue(traits, attribute);
121 if (!CFNumberGetValue(cftrait, kCFNumberFloatType, trait))
122 *trait = 0.0;
123 }
124
125 static void get_font_traits(CTFontDescriptorRef fontd,
126 ASS_FontProviderMetaData *meta)
127 {
128 float weight, slant, width;
129
130 CFDictionaryRef traits =
131 CTFontDescriptorCopyAttribute(fontd, kCTFontTraitsAttribute);
132
133 get_trait(traits, kCTFontWeightTrait, &weight);
134 get_trait(traits, kCTFontSlantTrait, &slant);
135 get_trait(traits, kCTFontWidthTrait, &width);
136
137 SAFE_CFRelease(traits);
138
139 // Printed all of my system fonts (see if'deffed code below). Here is how
140 // CoreText 'normalized' weights maps to CSS/libass:
141
142 // opentype: 0 100 200 300 400 500 600 700 800 900
143 // css: LIGHT REG MED SBOLD BOLD BLACK EXTRABL
144 // libass: LIGHT MEDIUM BOLD
145 // coretext: -0.4 0.0 0.23 0.3 0.4 0.62
146
147 if (weight >= 0.62)
148 meta->weight = 800;
149 else if (weight >= 0.4)
150 meta->weight = 700;
151 else if (weight >= 0.3)
152 meta->weight = 600;
153 else if (weight >= 0.23)
154 meta->weight = 500;
155 else if (weight >= -0.4)
156 meta->weight = 400;
157 else
158 meta->weight = 200;
159
160 if (slant > 0.03)
161 meta->slant = FONT_SLANT_ITALIC;
162 else
163 meta->slant = FONT_SLANT_NONE;
164
165 if (width <= -0.2)
166 meta->width = FONT_WIDTH_CONDENSED;
167 else if (width >= 0.2)
168 meta->width = FONT_WIDTH_EXPANDED;
169 else
170 meta->width = FONT_WIDTH_NORMAL;
171
172 #if 0
173 char *name[1];
174 int idx = 0;
175 get_name(fontd, kCTFontDisplayNameAttribute, name, &idx);
176 char *file = get_font_file(fontd);
177 printf(
178 "Font traits for: %-40s [%-50s] "
179 "<slant: %f, %03d>, <weight: (%f, %03d)>, <width: %f, %03d>\n",
180 name[0], file,
181 slant, meta->slant, weight, meta->weight, width, meta->width);
182 free(name[0]);
183 free(file);
184 #endif
185 }
186
187 static void process_descriptors(ASS_FontProvider *provider, CFArrayRef fontsd)
188 {
189 ASS_FontProviderMetaData meta;
190 char *families[1];
191 char *identifiers[1];
192 char *fullnames[1];
193
123 }
124 return ret;
125 }
126
127 static void process_descriptors(ASS_Library *lib, ASS_FontProvider *provider,
128 CFArrayRef fontsd)
129 {
194130 if (!fontsd)
195131 return;
196132
133 FT_Library ftlib;
134 if (FT_Init_FreeType(&ftlib)) {
135 ass_msg(lib, MSGL_WARN, "Failed to create FT lib");
136 return;
137 }
138
197139 for (int i = 0; i < CFArrayGetCount(fontsd); i++) {
140 ASS_FontProviderMetaData meta = {0};
198141 CTFontDescriptorRef fontd = CFArrayGetValueAtIndex(fontsd, i);
199142 int index = -1;
200143
201144 char *path = get_font_file(fontd);
202 if (strcmp("", path) == 0) {
145 if (!path || strcmp("", path) == 0) {
203146 // skip the font if the URL field in the font descriptor is empty
204147 free(path);
205148 continue;
206149 }
207150
208 memset(&meta, 0, sizeof(meta));
209 get_font_traits(fontd, &meta);
210
211 get_name(fontd, kCTFontFamilyNameAttribute, families, &meta.n_family);
212 meta.families = families;
213
214 get_name(fontd, kCTFontDisplayNameAttribute, fullnames, &meta.n_fullname);
215 meta.fullnames = fullnames;
216
217 int zero = 0;
218 get_name(fontd, kCTFontNameAttribute, identifiers, &zero);
219 meta.postscript_name = identifiers[0];
220
221 CFRetain(fontd);
222 ass_font_provider_add_font(provider, &meta, path, index, (void*)fontd);
151 char *ps_name = get_name(fontd, kCTFontNameAttribute);
152
153 if (ass_get_font_info(lib, ftlib, path, ps_name, -1, &meta)) {
154 CFRetain(fontd);
155 ass_font_provider_add_font(provider, &meta, path, index, (void*)fontd);
156 }
223157
224158 for (int j = 0; j < meta.n_family; j++)
225159 free(meta.families[j]);
227161 for (int j = 0; j < meta.n_fullname; j++)
228162 free(meta.fullnames[j]);
229163
164 free(meta.families);
165 free(meta.fullnames);
166
230167 free(meta.postscript_name);
231168
169 free(ps_name);
232170 free(path);
233171 }
172
173 FT_Done_FreeType(ftlib);
234174 }
235175
236176 static void match_fonts(ASS_Library *lib, ASS_FontProvider *provider,
263203 CFArrayRef fontsd =
264204 CTFontCollectionCreateMatchingFontDescriptors(ctcoll);
265205
266 process_descriptors(provider, fontsd);
206 process_descriptors(lib, provider, fontsd);
267207
268208 SAFE_CFRelease(fontsd);
269209 SAFE_CFRelease(ctcoll);
287227 0, (UInt8*)&codepointle, sizeof(codepointle),
288228 kCFStringEncodingUTF32LE, false);
289229 CTFontRef fb = CTFontCreateForString(font, r, CFRangeMake(0, 1));
290 CFStringRef cffamily = CTFontCopyFamilyName(fb);
291 char *res_family = cfstr2buf(cffamily);
230 CFNumberRef cfformat = CTFontCopyAttribute(fb, kCTFontFormatAttribute);
231 CFStringRef cfname = is_postscript_font_format(cfformat) ?
232 CTFontCopyPostScriptName(fb) : CTFontCopyFullName(fb);
233 char *res_name = cfstr2buf(cfname);
292234
293235 SAFE_CFRelease(name);
294236 SAFE_CFRelease(font);
295237 SAFE_CFRelease(r);
296238 SAFE_CFRelease(fb);
297 SAFE_CFRelease(cffamily);
298
299 return res_family;
239 SAFE_CFRelease(cfformat);
240 SAFE_CFRelease(cfname);
241
242 return res_name;
300243 }
301244
302245 static void get_substitutions(void *priv, const char *name,
203203 static void init_FallbackLogTextRenderer(FallbackLogTextRenderer *r,
204204 IDWriteFactory *factory)
205205 {
206 *r = (FallbackLogTextRenderer){
206 *r = (FallbackLogTextRenderer) {
207207 .iface = {
208208 .lpVtbl = &r->vtbl,
209209 },
2828 #include "ass_drawing.h"
2929 #include "ass_font.h"
3030
31 #define GLYPH_INITIAL_POINTS 100
32 #define GLYPH_INITIAL_SEGMENTS 100
33
34 /*
35 * \brief Prepare drawing for parsing. This just sets a few parameters.
36 */
37 static void drawing_prepare(ASS_Drawing *drawing)
38 {
39 // Scaling parameters
40 drawing->point_scale_x = drawing->scale_x / (1 << (drawing->scale - 1));
41 drawing->point_scale_y = drawing->scale_y / (1 << (drawing->scale - 1));
42 }
43
44 /*
45 * \brief Finish a drawing. This only sets the horizontal advance according
46 * to the outline's bbox at the moment.
47 */
48 static void drawing_finish(ASS_Drawing *drawing, bool raw_mode)
49 {
50 ASS_Rect bbox = drawing->cbox;
51 ASS_Outline *ol = &drawing->outline;
52
53 if (drawing->library)
54 ass_msg(drawing->library, MSGL_V,
55 "Parsed drawing with %d points and %d segments",
56 ol->n_points, ol->n_segments);
57
58 if (raw_mode)
59 return;
60
61 drawing->advance.x = bbox.x_max - bbox.x_min;
62
63 double pbo = drawing->pbo / (1 << (drawing->scale - 1));
64 drawing->desc = double_to_d6(pbo * drawing->scale_y);
65 drawing->asc = bbox.y_max - bbox.y_min - drawing->desc;
66
67 // Place it onto the baseline
68 for (size_t i = 0; i < ol->n_points; i++)
69 ol->points[i].y -= drawing->asc;
70 }
31 #define DRAWING_INITIAL_POINTS 100
32 #define DRAWING_INITIAL_SEGMENTS 100
7133
7234 /*
7335 * \brief Check whether a number of items on the list is available
7436 */
75 static int token_check_values(ASS_DrawingToken *token, int i, int type)
37 static bool token_check_values(ASS_DrawingToken *token, int i, int type)
7638 {
7739 for (int j = 0; j < i; j++) {
78 if (!token || token->type != type) return 0;
40 if (!token || token->type != type) return false;
7941 token = token->next;
8042 }
8143
82 return 1;
44 return true;
8345 }
8446
8547 /*
8648 * \brief Tokenize a drawing string into a list of ASS_DrawingToken
8749 * This also expands points for closing b-splines
8850 */
89 static ASS_DrawingToken *drawing_tokenize(char *str)
90 {
91 char *p = str;
51 static ASS_DrawingToken *drawing_tokenize(const char *str)
52 {
53 char *p = (char *) str;
9254 int type = -1, is_set = 0;
9355 double val;
9456 ASS_Vector point = {0, 0};
173135 }
174136
175137 /*
176 * \brief Translate and scale a point coordinate according to baseline
177 * offset and scale.
178 */
179 static inline void translate_point(ASS_Drawing *drawing, ASS_Vector *point)
180 {
181 point->x = lrint(drawing->point_scale_x * point->x);
182 point->y = lrint(drawing->point_scale_y * point->y);
183
184 rectangle_update(&drawing->cbox, point->x, point->y, point->x, point->y);
185 }
186
187 /*
188138 * \brief Add curve to drawing
189139 */
190 static bool drawing_add_curve(ASS_Drawing *drawing, ASS_DrawingToken *token,
191 bool spline, int started)
140 static bool drawing_add_curve(ASS_Outline *outline, ASS_Rect *cbox,
141 ASS_DrawingToken *token, bool spline, int started)
192142 {
193143 ASS_Vector p[4];
194144 for (int i = 0; i < 4; ++i) {
195145 p[i] = token->point;
196 translate_point(drawing, &p[i]);
146 rectangle_update(cbox, p[i].x, p[i].y, p[i].x, p[i].y);
197147 token = token->next;
198148 }
199149
216166 }
217167
218168 return (started ||
219 outline_add_point(&drawing->outline, p[0], 0)) &&
220 outline_add_point(&drawing->outline, p[1], 0) &&
221 outline_add_point(&drawing->outline, p[2], 0) &&
222 outline_add_point(&drawing->outline, p[3], OUTLINE_CUBIC_SPLINE);
223 }
224
225 /*
226 * \brief Create and initialize a new drawing and return it
227 */
228 ASS_Drawing *ass_drawing_new(ASS_Library *lib)
229 {
230 ASS_Drawing *drawing = calloc(1, sizeof(*drawing));
231 if (!drawing)
232 return NULL;
233 rectangle_reset(&drawing->cbox);
234 drawing->library = lib;
235 drawing->scale_x = 1.;
236 drawing->scale_y = 1.;
237
238 if (!outline_alloc(&drawing->outline, GLYPH_INITIAL_POINTS, GLYPH_INITIAL_SEGMENTS)) {
239 free(drawing);
240 return NULL;
241 }
242 return drawing;
243 }
244
245 /*
246 * \brief Free a drawing
247 */
248 void ass_drawing_free(ASS_Drawing *drawing)
249 {
250 if (drawing) {
251 free(drawing->text);
252 outline_free(&drawing->outline);
253 }
254 free(drawing);
255 }
256
257 /*
258 * \brief Copy an ASCII string to the drawing text buffer
259 */
260 void ass_drawing_set_text(ASS_Drawing *drawing, char *str, size_t len)
261 {
262 free(drawing->text);
263 drawing->text = strndup(str, len);
264 }
265
266 /*
267 * \brief Create a hashcode for the drawing
268 * XXX: To avoid collisions a better hash algorithm might be useful.
269 */
270 void ass_drawing_hash(ASS_Drawing *drawing)
271 {
272 if (!drawing->text)
273 return;
274 drawing->hash = fnv_32a_str(drawing->text, FNV1_32A_INIT);
169 outline_add_point(outline, p[0], 0)) &&
170 outline_add_point(outline, p[1], 0) &&
171 outline_add_point(outline, p[2], 0) &&
172 outline_add_point(outline, p[3], OUTLINE_CUBIC_SPLINE);
275173 }
276174
277175 /*
278176 * \brief Convert token list to outline. Calls the line and curve evaluators.
279177 */
280 ASS_Outline *ass_drawing_parse(ASS_Drawing *drawing, bool raw_mode)
281 {
178 bool ass_drawing_parse(ASS_Outline *outline, ASS_Rect *cbox,
179 const char *text, ASS_Library *lib)
180 {
181 if (!outline_alloc(outline, DRAWING_INITIAL_POINTS, DRAWING_INITIAL_SEGMENTS))
182 return false;
183 rectangle_reset(cbox);
184
185 ASS_DrawingToken *tokens = drawing_tokenize(text);
186
282187 bool started = false;
283 ASS_DrawingToken *token;
284188 ASS_Vector pen = {0, 0};
285
286 drawing->tokens = drawing_tokenize(drawing->text);
287 drawing_prepare(drawing);
288
289 token = drawing->tokens;
189 ASS_DrawingToken *token = tokens;
290190 while (token) {
291191 // Draw something according to current command
292192 switch (token->type) {
293193 case TOKEN_MOVE_NC:
294194 pen = token->point;
295 translate_point(drawing, &pen);
195 rectangle_update(cbox, pen.x, pen.y, pen.x, pen.y);
296196 token = token->next;
297197 break;
298198 case TOKEN_MOVE:
299199 pen = token->point;
300 translate_point(drawing, &pen);
200 rectangle_update(cbox, pen.x, pen.y, pen.x, pen.y);
301201 if (started) {
302 if (!outline_add_segment(&drawing->outline, OUTLINE_LINE_SEGMENT))
303 goto error;
304 if (!outline_close_contour(&drawing->outline))
202 if (!outline_add_segment(outline, OUTLINE_LINE_SEGMENT))
203 goto error;
204 if (!outline_close_contour(outline))
305205 goto error;
306206 started = false;
307207 }
309209 break;
310210 case TOKEN_LINE: {
311211 ASS_Vector to = token->point;
312 translate_point(drawing, &to);
313 if (!started && !outline_add_point(&drawing->outline, pen, 0))
212 rectangle_update(cbox, to.x, to.y, to.x, to.y);
213 if (!started && !outline_add_point(outline, pen, 0))
314214 goto error;
315 if (!outline_add_point(&drawing->outline, to, OUTLINE_LINE_SEGMENT))
215 if (!outline_add_point(outline, to, OUTLINE_LINE_SEGMENT))
316216 goto error;
317217 started = true;
318218 token = token->next;
321221 case TOKEN_CUBIC_BEZIER:
322222 if (token_check_values(token, 3, TOKEN_CUBIC_BEZIER) &&
323223 token->prev) {
324 if (!drawing_add_curve(drawing, token->prev, false, started))
224 if (!drawing_add_curve(outline, cbox, token->prev, false, started))
325225 goto error;
326226 token = token->next;
327227 token = token->next;
333233 case TOKEN_B_SPLINE:
334234 if (token_check_values(token, 3, TOKEN_B_SPLINE) &&
335235 token->prev) {
336 if (!drawing_add_curve(drawing, token->prev, true, started))
236 if (!drawing_add_curve(outline, cbox, token->prev, true, started))
337237 goto error;
338238 token = token->next;
339239 started = true;
348248
349249 // Close the last contour
350250 if (started) {
351 if (!outline_add_segment(&drawing->outline, OUTLINE_LINE_SEGMENT))
251 if (!outline_add_segment(outline, OUTLINE_LINE_SEGMENT))
352252 goto error;
353 if (!outline_close_contour(&drawing->outline))
253 if (!outline_close_contour(outline))
354254 goto error;
355255 }
356256
357 drawing_finish(drawing, raw_mode);
358 drawing_free_tokens(drawing->tokens);
359 return &drawing->outline;
257 if (lib)
258 ass_msg(lib, MSGL_V,
259 "Parsed drawing with %d points and %d segments",
260 outline->n_points, outline->n_segments);
261
262 drawing_free_tokens(tokens);
263 return true;
360264
361265 error:
362 drawing_free_tokens(drawing->tokens);
363 return NULL;
364 }
266 drawing_free_tokens(tokens);
267 outline_free(outline);
268 return false;
269 }
4040 struct ass_drawing_token *prev;
4141 } ASS_DrawingToken;
4242
43 typedef struct {
44 char *text; // drawing string
45 int scale; // scale (1-64) for subpixel accuracy
46 double pbo; // drawing will be shifted in y direction by this amount
47 double scale_x; // FontScaleX
48 double scale_y; // FontScaleY
49 int asc; // ascender
50 int desc; // descender
51 ASS_Outline outline; // target outline
52 ASS_Vector advance; // advance (from cbox)
53 int hash; // hash value (for caching)
54
55 // private
56 ASS_Library *library;
57 ASS_DrawingToken *tokens; // tokenized drawing
58 double point_scale_x;
59 double point_scale_y;
60 ASS_Rect cbox; // bounding box, or let's say... VSFilter's idea of it
61 } ASS_Drawing;
62
63 ASS_Drawing *ass_drawing_new(ASS_Library *lib);
64 void ass_drawing_free(ASS_Drawing *drawing);
65 void ass_drawing_set_text(ASS_Drawing *drawing, char *str, size_t n);
66 void ass_drawing_hash(ASS_Drawing *drawing);
67 ASS_Outline *ass_drawing_parse(ASS_Drawing *drawing, bool raw_mode);
43 bool ass_drawing_parse(ASS_Outline *outline, ASS_Rect *cbox,
44 const char *text, ASS_Library *lib);
6845
6946 #endif /* LIBASS_DRAWING_H */
8686 if (!face->charmap)
8787 return symbol;
8888
89 switch(face->charmap->encoding){
89 switch (face->charmap->encoding) {
9090 case FT_ENCODING_MS_SYMBOL:
9191 return 0xF000 | symbol;
9292 default:
9494 }
9595 }
9696
97 static void buggy_font_workaround(FT_Face face)
98 {
99 // Some fonts have zero Ascender/Descender fields in 'hhea' table.
100 // In this case, get the information from 'os2' table or, as
101 // a last resort, from face.bbox.
102 if (face->ascender + face->descender == 0 || face->height == 0) {
103 TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2);
104 if (os2) {
97 static void set_font_metrics(FT_Face face)
98 {
99 // Mimicking GDI's behavior for asc/desc/height.
100 // These fields are (apparently) sometimes used for signed values,
101 // despite being unsigned in the spec.
102 TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2);
103 if (os2 && ((short)os2->usWinAscent + (short)os2->usWinDescent != 0)) {
104 face->ascender = (short)os2->usWinAscent;
105 face->descender = -(short)os2->usWinDescent;
106 face->height = face->ascender - face->descender;
107 }
108
109 // If we didn't have usable Win values in the OS/2 table,
110 // then the values from FreeType will still be in these fields.
111 // It'll use either the OS/2 typo metrics or the hhea ones.
112 // If the font has typo metrics but FreeType didn't use them
113 // (either old FT or USE_TYPO_METRICS not set), we'll try those.
114 // In the case of a very broken font that has none of those options,
115 // we fall back on using face.bbox.
116 // Anything without valid OS/2 Win values isn't supported by VSFilter,
117 // so at this point compatibility's out the window and we're just
118 // trying to render _something_ readable.
119 if (face->ascender - face->descender == 0 || face->height == 0) {
120 if (os2 && (os2->sTypoAscender - os2->sTypoDescender) != 0) {
105121 face->ascender = os2->sTypoAscender;
106122 face->descender = os2->sTypoDescender;
107123 face->height = face->ascender - face->descender;
212228 }
213229
214230 charmap_magic(font->library, face);
215 buggy_font_workaround(face);
231 set_font_metrics(face);
216232
217233 font->faces[font->n_faces] = face;
218234 font->faces_uid[font->n_faces++] = uid;
223239 /**
224240 * \brief Create a new ASS_Font according to "desc" argument
225241 */
226 ASS_Font *ass_font_new(Cache *font_cache, ASS_Library *library,
227 FT_Library ftlibrary, ASS_FontSelector *fontsel,
228 ASS_FontDesc *desc)
229 {
230 ASS_Font *font;
231 if (ass_cache_get(font_cache, desc, &font)) {
232 if (font->desc.family)
233 return font;
234 ass_cache_dec_ref(font);
235 return NULL;
236 }
242 ASS_Font *ass_font_new(ASS_Renderer *render_priv, ASS_FontDesc *desc)
243 {
244 ASS_Font *font = ass_cache_get(render_priv->cache.font_cache, desc, render_priv);
237245 if (!font)
238246 return NULL;
239
240 font->library = library;
241 font->ftlibrary = ftlibrary;
247 if (font->library)
248 return font;
249 ass_cache_dec_ref(font);
250 return NULL;
251 }
252
253 size_t ass_font_construct(void *key, void *value, void *priv)
254 {
255 ASS_Renderer *render_priv = priv;
256 ASS_FontDesc *desc = key;
257 ASS_Font *font = value;
258
259 font->library = render_priv->library;
260 font->ftlibrary = render_priv->ftlibrary;
242261 font->shaper_priv = NULL;
243262 font->n_faces = 0;
244 ASS_FontDesc *new_desc = ass_cache_key(font);
245 font->desc.family = new_desc->family;
263 font->desc.family = desc->family;
246264 font->desc.bold = desc->bold;
247265 font->desc.italic = desc->italic;
248266 font->desc.vertical = desc->vertical;
249267
250 font->scale_x = font->scale_y = 1.;
251 font->v.x = font->v.y = 0;
252268 font->size = 0.;
253269
254 int error = add_face(fontsel, font, 0);
255 if (error == -1) {
256 font->desc.family = NULL;
257 ass_cache_commit(font, 1);
258 ass_cache_dec_ref(font);
259 return NULL;
260 }
261 ass_cache_commit(font, 1);
262 return font;
263 }
264
265 /**
266 * \brief Set font transformation matrix and shift vector
267 **/
268 void ass_font_set_transform(ASS_Font *font, double scale_x,
269 double scale_y, FT_Vector *v)
270 {
271 font->scale_x = scale_x;
272 font->scale_y = scale_y;
273 if (v) {
274 font->v.x = v->x;
275 font->v.y = v->y;
276 }
270 int error = add_face(render_priv->fontselect, font, 0);
271 if (error == -1)
272 font->library = NULL;
273 return 1;
277274 }
278275
279276 void ass_face_set_size(FT_Face face, double size)
280277 {
281 TT_HoriHeader *hori = FT_Get_Sfnt_Table(face, ft_sfnt_hhea);
282 TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2);
283 double mscale = 1.;
284278 FT_Size_RequestRec rq;
285 FT_Size_Metrics *m = &face->size->metrics;
286 // VSFilter uses metrics from TrueType OS/2 table
287 // The idea was borrowed from asa (http://asa.diac24.net)
288 if (os2) {
289 int ft_height = 0;
290 if (hori)
291 ft_height = hori->Ascender - hori->Descender;
292 if (!ft_height)
293 ft_height = os2->sTypoAscender - os2->sTypoDescender;
294 /* sometimes used for signed values despite unsigned in spec */
295 int os2_height = (short)os2->usWinAscent + (short)os2->usWinDescent;
296 if (ft_height && os2_height)
297 mscale = (double) ft_height / os2_height;
298 }
299279 memset(&rq, 0, sizeof(rq));
300280 rq.type = FT_SIZE_REQUEST_TYPE_REAL_DIM;
301281 rq.width = 0;
302 rq.height = double_to_d6(size * mscale);
282 rq.height = double_to_d6(size);
303283 rq.horiResolution = rq.vertResolution = 0;
304284 FT_Request_Size(face, &rq);
305 m->ascender /= mscale;
306 m->descender /= mscale;
307 m->height /= mscale;
308285 }
309286
310287 /**
321298 }
322299
323300 /**
301 * \brief Get face weight
302 **/
303 int ass_face_get_weight(FT_Face face)
304 {
305 #if FREETYPE_MAJOR > 2 || (FREETYPE_MAJOR == 2 && FREETYPE_MINOR >= 6)
306 TT_OS2 *os2 = FT_Get_Sfnt_Table(face, FT_SFNT_OS2);
307 #else
308 // This old name is still included (as a macro), but deprecated as of 2.6, so avoid using it if we can
309 TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2);
310 #endif
311 if (os2 && os2->version != 0xffff && os2->usWeightClass)
312 return os2->usWeightClass;
313 else
314 return 300 * !!(face->style_flags & FT_STYLE_FLAG_BOLD) + 400;
315 }
316
317 /**
324318 * \brief Get maximal font ascender and descender.
325 * \param ch character code
326 * The values are extracted from the font face that provides glyphs for the given character
327319 **/
328 void ass_font_get_asc_desc(ASS_Font *font, uint32_t ch, int *asc,
329 int *desc)
330 {
331 int i;
332 for (i = 0; i < font->n_faces; ++i) {
333 FT_Face face = font->faces[i];
334 TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2);
335 if (FT_Get_Char_Index(face, ass_font_index_magic(face, ch))) {
336 int y_scale = face->size->metrics.y_scale;
337 if (os2) {
338 *asc = FT_MulFix((short)os2->usWinAscent, y_scale);
339 *desc = FT_MulFix((short)os2->usWinDescent, y_scale);
340 } else {
341 *asc = FT_MulFix(face->ascender, y_scale);
342 *desc = FT_MulFix(-face->descender, y_scale);
343 }
344 return;
345 }
346 }
347
348 *asc = *desc = 0;
320 void ass_font_get_asc_desc(ASS_Font *font, int face_index,
321 int *asc, int *desc)
322 {
323 FT_Face face = font->faces[face_index];
324 int y_scale = face->size->metrics.y_scale;
325 *asc = FT_MulFix(face->ascender, y_scale);
326 *desc = FT_MulFix(-face->descender, y_scale);
349327 }
350328
351329 static void add_line(FT_Outline *ol, int bear, int advance, int dir, int pos, int size) {
530508 * \brief Get a glyph
531509 * \param ch character code
532510 **/
533 FT_Glyph ass_font_get_glyph(ASS_Font *font, uint32_t ch, int face_index,
534 int index, ASS_Hinting hinting, int deco)
511 FT_Glyph ass_font_get_glyph(ASS_Font *font, int face_index, int index,
512 ASS_Hinting hinting, int deco)
535513 {
536514 int error;
537515 FT_Glyph glyph;
538516 FT_Face face = font->faces[face_index];
539517 int flags = 0;
540 int vertical = font->desc.vertical;
541518
542519 flags = FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH
543520 | FT_LOAD_IGNORE_TRANSFORM;
566543 FT_GlyphSlot_Oblique(face->glyph);
567544 }
568545
569 if (!(face->style_flags & FT_STYLE_FLAG_BOLD) &&
570 (font->desc.bold > 400)) {
546 if (font->desc.bold > ass_face_get_weight(face) + 150) {
571547 ass_glyph_embolden(face->glyph);
572548 }
573549 error = FT_Get_Glyph(face->glyph, &glyph);
578554 }
579555
580556 // Rotate glyph, if needed
581 if (vertical && ch >= VERTICAL_LOWER_BOUND) {
557 if (deco & DECO_ROTATE) {
582558 FT_Matrix m = { 0, double_to_d16(-1.0), double_to_d16(1.0), 0 };
583559 TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2);
584560 int desc = 0;
595571
596572 ass_strike_outline_glyph(face, font, glyph, deco & DECO_UNDERLINE,
597573 deco & DECO_STRIKETHROUGH);
598
599 // Apply scaling and shift
600 FT_Matrix scale = { double_to_d16(font->scale_x), 0, 0,
601 double_to_d16(font->scale_y) };
602 FT_Outline *outl = &((FT_OutlineGlyph) glyph)->outline;
603 FT_Outline_Transform(outl, &scale);
604 FT_Outline_Translate(outl, font->v.x, font->v.y);
605 glyph->advance.x *= font->scale_x;
606574
607575 return glyph;
608576 }
3434 #define VERTICAL_LOWER_BOUND 0x02f1
3535
3636 #define ASS_FONT_MAX_FACES 10
37 #define DECO_UNDERLINE 1
37 #define DECO_UNDERLINE 1
3838 #define DECO_STRIKETHROUGH 2
39 #define DECO_ROTATE 4
3940
4041 struct ass_font_desc {
4142 char *family;
5253 FT_Face faces[ASS_FONT_MAX_FACES];
5354 ASS_ShaperFontData *shaper_priv;
5455 int n_faces;
55 double scale_x, scale_y; // current transform
56 FT_Vector v; // current shift
5756 double size;
5857 };
5958
6059 void charmap_magic(ASS_Library *library, FT_Face face);
61 ASS_Font *ass_font_new(Cache *font_cache, ASS_Library *library,
62 FT_Library ftlibrary, ASS_FontSelector *fontsel,
63 ASS_FontDesc *desc);
64 void ass_font_set_transform(ASS_Font *font, double scale_x,
65 double scale_y, FT_Vector *v);
60 ASS_Font *ass_font_new(ASS_Renderer *render_priv, ASS_FontDesc *desc);
6661 void ass_face_set_size(FT_Face face, double size);
6762 void ass_font_set_size(ASS_Font *font, double size);
68 void ass_font_get_asc_desc(ASS_Font *font, uint32_t ch, int *asc,
69 int *desc);
63 int ass_face_get_weight(FT_Face face);
64 void ass_font_get_asc_desc(ASS_Font *font, int face_index,
65 int *asc, int *desc);
7066 int ass_font_get_index(ASS_FontSelector *fontsel, ASS_Font *font,
7167 uint32_t symbol, int *face_index, int *glyph_index);
7268 uint32_t ass_font_index_magic(FT_Face face, uint32_t symbol);
73 FT_Glyph ass_font_get_glyph(ASS_Font *font,
74 uint32_t ch, int face_index, int index,
69 FT_Glyph ass_font_get_glyph(ASS_Font *font, int face_index, int index,
7570 ASS_Hinting hinting, int deco);
7671 void ass_font_clear(ASS_Font *font);
7772
9595 for (i = 0; i < fonts->nfont; i++) {
9696 FcPattern *pat = fonts->fonts[i];
9797 FcBool outline;
98 int index, weight;
98 int index;
99 double weight;
99100 char *path;
100101 char *fullnames[MAX_NAME];
101102 char *families[MAX_NAME];
108109 // simple types
109110 result = FcPatternGetInteger(pat, FC_SLANT, 0, &meta.slant);
110111 result |= FcPatternGetInteger(pat, FC_WIDTH, 0, &meta.width);
111 result |= FcPatternGetInteger(pat, FC_WEIGHT, 0, &weight);
112 result |= FcPatternGetDouble(pat, FC_WEIGHT, 0, &weight);
112113 result |= FcPatternGetInteger(pat, FC_INDEX, 0, &index);
113114 if (result != FcResultMatch)
114115 continue;
115116
116117 // fontconfig uses its own weight scale, apparently derived
117118 // from typographical weight. we're using truetype weights, so
118 // convert appropriately
119 if (weight <= FC_WEIGHT_LIGHT)
120 meta.weight = FONT_WEIGHT_LIGHT;
121 else if (weight <= FC_WEIGHT_MEDIUM)
122 meta.weight = FONT_WEIGHT_MEDIUM;
119 // convert appropriately.
120 #if FC_VERSION >= 21292
121 meta.weight = FcWeightToOpenTypeDouble(weight) + 0.5;
122 #elif FC_VERSION >= 21191
123 // Versions prior to 2.12.92 only had integer precision.
124 meta.weight = FcWeightToOpenType(weight + 0.5) + 0.5;
125 #else
126 // On older fontconfig, FcWeightToOpenType is unavailable, and its inverse was
127 // implemented more simply, using an if/else ladder instead of linear interpolation.
128 // We implement an inverse of that ladder here.
129 // We don't expect actual FC caches from these versions to have intermediate
130 // values, so the average checks are only for completeness.
131 #define AVG(x, y) (((double)x + y) / 2)
132 #ifndef FC_WEIGHT_SEMILIGHT
133 #define FC_WEIGHT_SEMILIGHT 55
134 #endif
135 if (weight < AVG(FC_WEIGHT_THIN, FC_WEIGHT_EXTRALIGHT))
136 meta.weight = 100;
137 else if (weight < AVG(FC_WEIGHT_EXTRALIGHT, FC_WEIGHT_LIGHT))
138 meta.weight = 200;
139 else if (weight < AVG(FC_WEIGHT_LIGHT, FC_WEIGHT_SEMILIGHT))
140 meta.weight = 300;
141 else if (weight < AVG(FC_WEIGHT_SEMILIGHT, FC_WEIGHT_BOOK))
142 meta.weight = 350;
143 else if (weight < AVG(FC_WEIGHT_BOOK, FC_WEIGHT_REGULAR))
144 meta.weight = 380;
145 else if (weight < AVG(FC_WEIGHT_REGULAR, FC_WEIGHT_MEDIUM))
146 meta.weight = 400;
147 else if (weight < AVG(FC_WEIGHT_MEDIUM, FC_WEIGHT_SEMIBOLD))
148 meta.weight = 500;
149 else if (weight < AVG(FC_WEIGHT_SEMIBOLD, FC_WEIGHT_BOLD))
150 meta.weight = 600;
151 else if (weight < AVG(FC_WEIGHT_BOLD, FC_WEIGHT_EXTRABOLD))
152 meta.weight = 700;
153 else if (weight < AVG(FC_WEIGHT_EXTRABOLD, FC_WEIGHT_BLACK))
154 meta.weight = 800;
155 else if (weight < AVG(FC_WEIGHT_BLACK, FC_WEIGHT_EXTRABLACK))
156 meta.weight = 900;
123157 else
124 meta.weight = FONT_WEIGHT_BOLD;
158 meta.weight = 1000;
159 #endif
125160
126161 // path
127162 result = FcPatternGetString(pat, FC_FILE, 0, (FcChar8 **)&path);
778778 ass_utf16be_to_utf8(buf, sizeof(buf), (uint8_t *)name.string,
779779 name.string_len);
780780
781 if (name.name_id == TT_NAME_ID_FULL_NAME) {
781 if (name.name_id == TT_NAME_ID_FULL_NAME && num_fullname < MAX_FULLNAME) {
782782 fullnames[num_fullname] = strdup(buf);
783783 if (fullnames[num_fullname] == NULL)
784784 goto error;
785785 num_fullname++;
786786 }
787787
788 if (name.name_id == TT_NAME_ID_FONT_FAMILY) {
788 if (name.name_id == TT_NAME_ID_FONT_FAMILY && num_family < MAX_FULLNAME) {
789789 families[num_family] = strdup(buf);
790790 if (families[num_family] == NULL)
791791 goto error;
809809
810810 // calculate sensible slant and weight from style attributes
811811 slant = 110 * !!(face->style_flags & FT_STYLE_FLAG_ITALIC);
812 weight = 300 * !!(face->style_flags & FT_STYLE_FLAG_BOLD) + 400;
812 weight = ass_face_get_weight(face);
813813
814814 // fill our struct
815815 info->slant = slant;
844844 free(info->families);
845845 free(info->fullnames);
846846
847 info->families = info->fullnames = NULL;
848 info->n_family = info->n_fullname = 0;
849
847850 return false;
851 }
852
853 bool ass_get_font_info(ASS_Library *lib, FT_Library ftlib, const char *path,
854 const char *postscript_name, int index,
855 ASS_FontProviderMetaData *info)
856 {
857 bool ret = false;
858 FT_Face face = NULL;
859 int error = FT_New_Face(ftlib, path, index, &face);
860 if (error) {
861 ass_msg(lib, MSGL_WARN, "Error opening font: '%s', %d", path, index);
862 return false;
863 }
864
865 if (postscript_name && index < 0 && face->num_faces > 0) {
866 // The font provider gave us a postscript name and is not sure
867 // about the face index.. so use the postscript name to find the
868 // correct face_index in the collection!
869 for (int i = 0; i < face->num_faces; i++) {
870 FT_Done_Face(face);
871 error = FT_New_Face(ftlib, path, i, &face);
872 if (error) {
873 ass_msg(lib, MSGL_WARN, "Error opening font: '%s', %d", path, i);
874 return false;
875 }
876
877 const char *face_psname = FT_Get_Postscript_Name(face);
878 if (face_psname != NULL &&
879 strcmp(face_psname, postscript_name) == 0)
880 break;
881 }
882 }
883
884 if (face) {
885 ret = get_font_info(ftlib, face, info);
886 if (ret)
887 info->postscript_name = strdup(info->postscript_name);
888 FT_Done_Face(face);
889 }
890
891 return ret;
848892 }
849893
850894 /**
925969 if (!ass_font_provider_add_font(priv, &info, NULL, face_index, ft)) {
926970 ass_msg(library, MSGL_WARN, "Failed to add embedded font '%s'",
927971 name);
972 free(ft);
928973 }
929974
930975 free_font_info(&info);
4444 * font path (i.e. the path was set to NULL).
4545 *
4646 * \param font_priv font private data
47 * \param output buffer; set to NULL to query stream size
47 * \param data output buffer; set to NULL to query stream size
4848 * \param offset stream offset
4949 * \param len bytes to read into output buffer from stream
5050 * \return actual number of bytes read, or stream size if data == NULL
6464 * Check if a glyph is supported by a font.
6565 *
6666 * \param font_priv font private data
67 * \param codepont Unicode codepoint (UTF-32)
67 * \param codepoint Unicode codepoint (UTF-32)
6868 * \return true if codepoint is supported by the font
6969 */
7070 typedef bool (*CheckGlyphFunc)(void *font_priv, uint32_t codepoint);
242242 * provide additional fonts to libass.
243243 * \param priv parent renderer
244244 * \param funcs callback functions
245 * \param private data for provider callbacks
245 * \param data private data for provider callbacks
246246 *
247247 */
248248 ASS_FontProvider *
266266 int index, void *data);
267267
268268 /**
269 * \brief Read a font's parameters
270 * \param lib a FT_Library to use (need not be the global one)
271 * \param path the path to the font file to read
272 * \param postscript_name the PS name of the specific face to read (set either this or index)
273 * \param index the face index to read, or -1 if not applicable
274 * \param info the struct to store results into
275 * \return success
276 *
277 */
278 bool ass_get_font_info(ASS_Library *lib, FT_Library ftlib, const char *path,
279 const char *postscript_name, int index,
280 ASS_FontProviderMetaData *info);
281
282 /**
269283 * \brief Free font provider and associated fonts.
270284 * \param provider the font provider
271285 *
5656 uintptr_t src_width, uintptr_t src_height);
5757 void DECORATE(expand_vert)(int16_t *dst, const int16_t *src,
5858 uintptr_t src_width, uintptr_t src_height);
59 void DECORATE(pre_blur1_horz)(int16_t *dst, const int16_t *src,
60 uintptr_t src_width, uintptr_t src_height);
61 void DECORATE(pre_blur1_vert)(int16_t *dst, const int16_t *src,
62 uintptr_t src_width, uintptr_t src_height);
63 void DECORATE(pre_blur2_horz)(int16_t *dst, const int16_t *src,
64 uintptr_t src_width, uintptr_t src_height);
65 void DECORATE(pre_blur2_vert)(int16_t *dst, const int16_t *src,
66 uintptr_t src_width, uintptr_t src_height);
67 void DECORATE(pre_blur3_horz)(int16_t *dst, const int16_t *src,
68 uintptr_t src_width, uintptr_t src_height);
69 void DECORATE(pre_blur3_vert)(int16_t *dst, const int16_t *src,
70 uintptr_t src_width, uintptr_t src_height);
71 void DECORATE(blur1234_horz)(int16_t *dst, const int16_t *src,
72 uintptr_t src_width, uintptr_t src_height,
73 const int16_t *param);
74 void DECORATE(blur1234_vert)(int16_t *dst, const int16_t *src,
75 uintptr_t src_width, uintptr_t src_height,
76 const int16_t *param);
77 void DECORATE(blur1235_horz)(int16_t *dst, const int16_t *src,
78 uintptr_t src_width, uintptr_t src_height,
79 const int16_t *param);
80 void DECORATE(blur1235_vert)(int16_t *dst, const int16_t *src,
81 uintptr_t src_width, uintptr_t src_height,
82 const int16_t *param);
83 void DECORATE(blur1246_horz)(int16_t *dst, const int16_t *src,
84 uintptr_t src_width, uintptr_t src_height,
85 const int16_t *param);
86 void DECORATE(blur1246_vert)(int16_t *dst, const int16_t *src,
87 uintptr_t src_width, uintptr_t src_height,
88 const int16_t *param);
59 void DECORATE(blur4_horz)(int16_t *dst, const int16_t *src,
60 uintptr_t src_width, uintptr_t src_height,
61 const int16_t *param);
62 void DECORATE(blur4_vert)(int16_t *dst, const int16_t *src,
63 uintptr_t src_width, uintptr_t src_height,
64 const int16_t *param);
65 void DECORATE(blur5_horz)(int16_t *dst, const int16_t *src,
66 uintptr_t src_width, uintptr_t src_height,
67 const int16_t *param);
68 void DECORATE(blur5_vert)(int16_t *dst, const int16_t *src,
69 uintptr_t src_width, uintptr_t src_height,
70 const int16_t *param);
71 void DECORATE(blur6_horz)(int16_t *dst, const int16_t *src,
72 uintptr_t src_width, uintptr_t src_height,
73 const int16_t *param);
74 void DECORATE(blur6_vert)(int16_t *dst, const int16_t *src,
75 uintptr_t src_width, uintptr_t src_height,
76 const int16_t *param);
77 void DECORATE(blur7_horz)(int16_t *dst, const int16_t *src,
78 uintptr_t src_width, uintptr_t src_height,
79 const int16_t *param);
80 void DECORATE(blur7_vert)(int16_t *dst, const int16_t *src,
81 uintptr_t src_width, uintptr_t src_height,
82 const int16_t *param);
83 void DECORATE(blur8_horz)(int16_t *dst, const int16_t *src,
84 uintptr_t src_width, uintptr_t src_height,
85 const int16_t *param);
86 void DECORATE(blur8_vert)(int16_t *dst, const int16_t *src,
87 uintptr_t src_width, uintptr_t src_height,
88 const int16_t *param);
8989
9090
9191 const BitmapEngine DECORATE(bitmap_engine) = {
124124 .shrink_vert = DECORATE(shrink_vert),
125125 .expand_horz = DECORATE(expand_horz),
126126 .expand_vert = DECORATE(expand_vert),
127 .pre_blur_horz = { DECORATE(pre_blur1_horz), DECORATE(pre_blur2_horz), DECORATE(pre_blur3_horz) },
128 .pre_blur_vert = { DECORATE(pre_blur1_vert), DECORATE(pre_blur2_vert), DECORATE(pre_blur3_vert) },
129 .main_blur_horz = { DECORATE(blur1234_horz), DECORATE(blur1235_horz), DECORATE(blur1246_horz) },
130 .main_blur_vert = { DECORATE(blur1234_vert), DECORATE(blur1235_vert), DECORATE(blur1246_vert) },
127 .blur_horz = { DECORATE(blur4_horz), DECORATE(blur5_horz), DECORATE(blur6_horz), DECORATE(blur7_horz), DECORATE(blur8_horz) },
128 .blur_vert = { DECORATE(blur4_vert), DECORATE(blur5_vert), DECORATE(blur6_vert), DECORATE(blur7_vert), DECORATE(blur8_vert) },
131129 };
3434
3535 outline->max_points = n_points;
3636 outline->max_segments = n_segments;
37 outline->n_points = outline->n_segments = 0;
3738 return true;
3839 }
3940
4445
4546 outline->n_points = outline->max_points = 0;
4647 outline->n_segments = outline->max_segments = 0;
48 }
49
50 static bool valid_point(const FT_Vector *pt)
51 {
52 return labs(pt->x) <= OUTLINE_MAX && labs(pt->y) <= OUTLINE_MAX;
4753 }
4854
4955 bool outline_convert(ASS_Outline *outline, const FT_Outline *source)
6066 S_ON, S_Q, S_C1, S_C2
6167 };
6268
63 outline->n_points = outline->n_segments = 0;
6469 for (size_t i = 0, j = 0; i < source->n_contours; i++) {
6570 ASS_Vector pt;
6671 bool skip_last = false;
7681 continue;
7782 }
7883
84 if (!valid_point(source->points + j))
85 goto fail;
7986 switch (FT_CURVE_TAG(source->tags[j])) {
8087 case FT_CURVE_TAG_ON:
8188 st = S_ON;
8289 break;
8390
8491 case FT_CURVE_TAG_CONIC:
92 if (!valid_point(source->points + last))
93 goto fail;
8594 pt.x = source->points[last].x;
8695 pt.y = -source->points[last].y;
8796 switch (FT_CURVE_TAG(source->tags[last])) {
109118 outline->points[outline->n_points++] = pt;
110119
111120 for (j++; j <= last; j++) {
121 if (!valid_point(source->points + j))
122 goto fail;
112123 switch (FT_CURVE_TAG(source->tags[j])) {
113124 case FT_CURVE_TAG_ON:
114125 switch (st) {
200211 return false;
201212 }
202213
203 bool outline_copy(ASS_Outline *outline, const ASS_Outline *source)
214 bool outline_scale_pow2(ASS_Outline *outline, const ASS_Outline *source,
215 int scale_ord_x, int scale_ord_y)
204216 {
205217 if (!source || !source->n_points) {
206218 outline_clear(outline);
207219 return true;
208220 }
209221
222 int32_t lim_x = OUTLINE_MAX;
223 if (scale_ord_x > 0)
224 lim_x = scale_ord_x < 32 ? lim_x >> scale_ord_x : 0;
225 else
226 scale_ord_x = FFMAX(scale_ord_x, -32);
227
228 int32_t lim_y = OUTLINE_MAX;
229 if (scale_ord_y > 0)
230 lim_y = scale_ord_y < 32 ? lim_y >> scale_ord_y : 0;
231 else
232 scale_ord_y = FFMAX(scale_ord_y, -32);
233
234 if (!lim_x || !lim_y) {
235 outline_clear(outline);
236 return false;
237 }
238
210239 if (!outline_alloc(outline, source->n_points, source->n_segments))
211240 return false;
212241
213 memcpy(outline->points, source->points, sizeof(ASS_Vector) * source->n_points);
242 int sx = scale_ord_x + 32;
243 int sy = scale_ord_y + 32;
244 const ASS_Vector *pt = source->points;
245 for (size_t i = 0; i < source->n_points; i++) {
246 if (abs(pt[i].x) > lim_x || abs(pt[i].y) > lim_y) {
247 outline_free(outline);
248 return false;
249 }
250 // that's equivalent to pt[i].x << scale_ord_x,
251 // but works even for negative coordinate and/or shift amount
252 outline->points[i].x = pt[i].x * ((int64_t) 1 << sx) >> 32;
253 outline->points[i].y = pt[i].y * ((int64_t) 1 << sy) >> 32;
254 }
214255 memcpy(outline->segments, source->segments, source->n_segments);
215256 outline->n_points = source->n_points;
216257 outline->n_segments = source->n_segments;
217258 return true;
218259 }
219260
261 bool outline_transform_2d(ASS_Outline *outline, const ASS_Outline *source,
262 const double m[2][3])
263 {
264 if (!source || !source->n_points) {
265 outline_clear(outline);
266 return true;
267 }
268
269 if (!outline_alloc(outline, source->n_points, source->n_segments))
270 return false;
271
272 const ASS_Vector *pt = source->points;
273 for (size_t i = 0; i < source->n_points; i++) {
274 double v[2];
275 for (int k = 0; k < 2; k++)
276 v[k] = m[k][0] * pt[i].x + m[k][1] * pt[i].y + m[k][2];
277
278 if (!(fabs(v[0]) < OUTLINE_MAX && fabs(v[1]) < OUTLINE_MAX)) {
279 outline_free(outline);
280 return false;
281 }
282 outline->points[i].x = lrint(v[0]);
283 outline->points[i].y = lrint(v[1]);
284 }
285 memcpy(outline->segments, source->segments, source->n_segments);
286 outline->n_points = source->n_points;
287 outline->n_segments = source->n_segments;
288 return true;
289 }
290
291 bool outline_transform_3d(ASS_Outline *outline, const ASS_Outline *source,
292 const double m[3][3])
293 {
294 if (!source || !source->n_points) {
295 outline_clear(outline);
296 return true;
297 }
298
299 if (!outline_alloc(outline, source->n_points, source->n_segments))
300 return false;
301
302 const ASS_Vector *pt = source->points;
303 for (size_t i = 0; i < source->n_points; i++) {
304 double v[3];
305 for (int k = 0; k < 3; k++)
306 v[k] = m[k][0] * pt[i].x + m[k][1] * pt[i].y + m[k][2];
307
308 double w = 1 / FFMAX(v[2], 0.1);
309 v[0] *= w;
310 v[1] *= w;
311
312 if (!(fabs(v[0]) < OUTLINE_MAX && fabs(v[1]) < OUTLINE_MAX)) {
313 outline_free(outline);
314 return false;
315 }
316 outline->points[i].x = lrint(v[0]);
317 outline->points[i].y = lrint(v[1]);
318 }
319 memcpy(outline->segments, source->segments, source->n_segments);
320 outline->n_points = source->n_points;
321 outline->n_segments = source->n_segments;
322 return true;
323 }
324
325 void outline_update_min_transformed_x(const ASS_Outline *outline,
326 const double m[3][3],
327 int32_t *min_x) {
328 const ASS_Vector *pt = outline->points;
329 for (size_t i = 0; i < outline->n_points; i++) {
330 double z = m[2][0] * pt[i].x + m[2][1] * pt[i].y + m[2][2];
331 double x = (m[0][0] * pt[i].x + m[0][1] * pt[i].y + m[0][2]) / FFMAX(z, 0.1);
332 if (isnan(x))
333 continue;
334 int32_t ix = lrint(FFMINMAX(x, -OUTLINE_MAX, OUTLINE_MAX));
335 *min_x = FFMIN(*min_x, ix);
336 }
337 }
338
339
220340 void outline_free(ASS_Outline *outline)
221341 {
222342 if (!outline)
235355 */
236356 bool outline_add_point(ASS_Outline *outline, ASS_Vector pt, char segment)
237357 {
358 if (abs(pt.x) > OUTLINE_MAX || abs(pt.y) > OUTLINE_MAX)
359 return false;
360
238361 if (outline->n_points >= outline->max_points) {
239362 size_t new_size = 2 * outline->max_points;
240363 if (!ASS_REALLOC_ARRAY(outline->points, new_size))
275398 }
276399
277400
278 void outline_translate(const ASS_Outline *outline, int32_t dx, int32_t dy)
279 {
280 for (size_t i = 0; i < outline->n_points; i++) {
281 outline->points[i].x += dx;
282 outline->points[i].y += dy;
283 }
284 }
285
286 void outline_adjust(const ASS_Outline *outline, double scale_x, int32_t dx, int32_t dy)
287 {
288 int32_t mul = lrint(scale_x * 0x10000);
289 if (mul == 0x10000) {
290 outline_translate(outline, dx, dy);
291 return;
292 }
293 for (size_t i = 0; i < outline->n_points; i++) {
294 int32_t x = (int64_t) outline->points[i].x * mul >> 16;
295 outline->points[i].x = x + dx;
296 outline->points[i].y += dy;
297 }
298 }
299
300 void outline_get_cbox(const ASS_Outline *outline, ASS_Rect *cbox)
301 {
302 if (!outline->n_points) {
303 cbox->x_min = cbox->x_max = 0;
304 cbox->y_min = cbox->y_max = 0;
305 return;
306 }
307 cbox->x_min = cbox->x_max = outline->points[0].x;
308 cbox->y_min = cbox->y_max = outline->points[0].y;
309 for (size_t i = 1; i < outline->n_points; i++) {
310 cbox->x_min = FFMIN(cbox->x_min, outline->points[i].x);
311 cbox->x_max = FFMAX(cbox->x_max, outline->points[i].x);
312 cbox->y_min = FFMIN(cbox->y_min, outline->points[i].y);
313 cbox->y_max = FFMAX(cbox->y_max, outline->points[i].y);
314 }
401 /*
402 * \brief Update bounding box of control points.
403 */
404 void outline_update_cbox(const ASS_Outline *outline, ASS_Rect *cbox)
405 {
406 for (size_t i = 0; i < outline->n_points; i++)
407 rectangle_update(cbox,
408 outline->points[i].x, outline->points[i].y,
409 outline->points[i].x, outline->points[i].y);
315410 }
316411
317412
383478 int first_skip, last_skip;
384479 // normal at first and last point
385480 ASS_DVector first_normal, last_normal;
386 // first point of current contour
387 ASS_Vector first_point;
481 // first and last points of current contour
482 ASS_Vector first_point, last_point;
388483
389484 // cosinus of maximal angle that do not require cap
390485 double merge_cos;
516611 static bool draw_arc(StrokerState *str, ASS_Vector pt,
517612 ASS_DVector normal0, ASS_DVector normal1, double c, int dir)
518613 {
519 const int max_subdiv = 15;
614 enum { max_subdiv = 15 };
520615 double mul[max_subdiv + 1];
521616
522617 ASS_DVector center;
552647 */
553648 static bool draw_circle(StrokerState *str, ASS_Vector pt, int dir)
554649 {
555 const int max_subdiv = 15;
650 enum { max_subdiv = 15 };
556651 double mul[max_subdiv + 1], c = 0;
557652
558653 int pos = max_subdiv;
647742 /**
648743 * \brief Process source line segment
649744 * \param str stroker state
650 * \param pt0 start point of the line segment
651745 * \param pt1 end point of the line segment
652746 * \param dir destination outline flags
653747 * \return false on allocation failure
654748 */
655 static bool add_line(StrokerState *str, ASS_Vector pt0, ASS_Vector pt1, int dir)
656 {
657 int32_t dx = pt1.x - pt0.x;
658 int32_t dy = pt1.y - pt0.y;
749 static bool add_line(StrokerState *str, ASS_Vector pt1, int dir)
750 {
751 int32_t dx = pt1.x - str->last_point.x;
752 int32_t dy = pt1.y - str->last_point.y;
659753 if (dx > -str->eps && dx < str->eps && dy > -str->eps && dy < str->eps)
660754 return true;
661755
662756 ASS_DVector deriv = { dy * str->yscale, -dx * str->xscale };
663757 double scale = 1 / vec_len(deriv);
664758 ASS_DVector normal = { deriv.x * scale, deriv.y * scale };
665 if (!start_segment(str, pt0, normal, dir))
759 if (!start_segment(str, str->last_point, normal, dir))
666760 return false;
667 if (!emit_first_point(str, pt0, OUTLINE_LINE_SEGMENT, dir))
761 if (!emit_first_point(str, str->last_point, OUTLINE_LINE_SEGMENT, dir))
668762 return false;
669763 str->last_normal = normal;
764 str->last_point = pt1;
670765 return true;
671766 }
672767
809904 /**
810905 * \brief Process source quadratic spline
811906 * \param str stroker state
812 * \param pt array of 3 source spline points
907 * \param pt1 middle control point
908 * \param pt2 final spline point
813909 * \param dir destination outline flags
814910 * \return false on allocation failure
815911 */
816 static bool add_quadratic(StrokerState *str, const ASS_Vector *pt, int dir)
817 {
818 int32_t dx0 = pt[1].x - pt[0].x;
819 int32_t dy0 = pt[1].y - pt[0].y;
912 static bool add_quadratic(StrokerState *str, ASS_Vector pt1, ASS_Vector pt2, int dir)
913 {
914 int32_t dx0 = pt1.x - str->last_point.x;
915 int32_t dy0 = pt1.y - str->last_point.y;
820916 if (dx0 > -str->eps && dx0 < str->eps && dy0 > -str->eps && dy0 < str->eps)
821 return add_line(str, pt[0], pt[2], dir);
822
823 int32_t dx1 = pt[2].x - pt[1].x;
824 int32_t dy1 = pt[2].y - pt[1].y;
917 return add_line(str, pt2, dir);
918
919 int32_t dx1 = pt2.x - pt1.x;
920 int32_t dy1 = pt2.y - pt1.y;
825921 if (dx1 > -str->eps && dx1 < str->eps && dy1 > -str->eps && dy1 < str->eps)
826 return add_line(str, pt[0], pt[2], dir);
922 return add_line(str, pt2, dir);
923
924 ASS_Vector pt[3] = { str->last_point, pt1, pt2 };
925 str->last_point = pt2;
827926
828927 ASS_DVector deriv[2] = {
829928 { dy0 * str->yscale, -dx0 * str->xscale },
11971296 /**
11981297 * \brief Process source cubic spline
11991298 * \param str stroker state
1200 * \param pt array of 4 source spline points
1299 * \param pt1 first middle control point
1300 * \param pt2 second middle control point
1301 * \param pt3 final spline point
12011302 * \param dir destination outline flags
12021303 * \return false on allocation failure
12031304 */
1204 static bool add_cubic(StrokerState *str, const ASS_Vector *pt, int dir)
1305 static bool add_cubic(StrokerState *str, ASS_Vector pt1, ASS_Vector pt2, ASS_Vector pt3, int dir)
12051306 {
12061307 int flags = 9;
12071308
1208 int32_t dx0 = pt[1].x - pt[0].x;
1209 int32_t dy0 = pt[1].y - pt[0].y;
1309 int32_t dx0 = pt1.x - str->last_point.x;
1310 int32_t dy0 = pt1.y - str->last_point.y;
12101311 if (dx0 > -str->eps && dx0 < str->eps && dy0 > -str->eps && dy0 < str->eps) {
1211 dx0 = pt[2].x - pt[0].x;
1212 dy0 = pt[2].y - pt[0].y;
1312 dx0 = pt2.x - str->last_point.x;
1313 dy0 = pt2.y - str->last_point.y;
12131314 if (dx0 > -str->eps && dx0 < str->eps && dy0 > -str->eps && dy0 < str->eps)
1214 return add_line(str, pt[0], pt[3], dir);
1315 return add_line(str, pt3, dir);
12151316 flags ^= 1;
12161317 }
12171318
1218 int32_t dx2 = pt[3].x - pt[2].x;
1219 int32_t dy2 = pt[3].y - pt[2].y;
1319 int32_t dx2 = pt3.x - pt2.x;
1320 int32_t dy2 = pt3.y - pt2.y;
12201321 if (dx2 > -str->eps && dx2 < str->eps && dy2 > -str->eps && dy2 < str->eps) {
1221 dx2 = pt[3].x - pt[1].x;
1222 dy2 = pt[3].y - pt[1].y;
1322 dx2 = pt3.x - pt1.x;
1323 dy2 = pt3.y - pt1.y;
12231324 if (dx2 > -str->eps && dx2 < str->eps && dy2 > -str->eps && dy2 < str->eps)
1224 return add_line(str, pt[0], pt[3], dir);
1325 return add_line(str, pt3, dir);
12251326 flags ^= 4;
12261327 }
12271328
12281329 if (flags == 12)
1229 return add_line(str, pt[0], pt[3], dir);
1330 return add_line(str, pt3, dir);
1331
1332 ASS_Vector pt[4] = { str->last_point, pt1, pt2, pt3 };
1333 str->last_point = pt3;
12301334
12311335 int32_t dx1 = pt[flags >> 2].x - pt[flags & 3].x;
12321336 int32_t dy1 = pt[flags >> 2].y - pt[flags & 3].y;
12521356 /**
12531357 * \brief Process contour closing
12541358 * \param str stroker state
1255 * \param last_point last contour point
12561359 * \param dir destination outline flags
12571360 * \return false on allocation failure
12581361 */
1259 static bool close_contour(StrokerState *str, ASS_Vector last_point, int dir)
1362 static bool close_contour(StrokerState *str, int dir)
12601363 {
12611364 if (str->contour_start) {
12621365 if ((dir & 3) == 3)
12631366 dir = 1;
1264 if (!draw_circle(str, last_point, dir))
1367 if (!draw_circle(str, str->last_point, dir))
12651368 return false;
12661369 } else {
1267 if (!add_line(str, last_point, str->first_point, dir))
1370 if (!add_line(str, str->first_point, dir))
12681371 return false;
12691372 if (!start_segment(str, str->first_point, str->first_normal, dir))
12701373 return false;
13001403 bool outline_stroke(ASS_Outline *result, ASS_Outline *result1,
13011404 const ASS_Outline *path, int xbord, int ybord, int eps)
13021405 {
1406 outline_alloc(result, 2 * path->n_points, 2 * path->n_segments);
1407 outline_alloc(result1, 2 * path->n_points, 2 * path->n_segments);
1408 if (!result->max_points || !result1->max_points)
1409 return false;
1410
13031411 const int dir = 3;
13041412 int rad = FFMAX(xbord, ybord);
1305 assert(rad >= eps);
1306
1307 result->n_points = result->n_segments = 0;
1308 result1->n_points = result1->n_segments = 0;
1413 assert(rad >= eps && rad <= OUTLINE_MAX);
13091414
13101415 StrokerState str;
13111416 str.result[0] = result;
13281433 str.err_c = 390 * rel_err * rel_err;
13291434 str.err_a = e;
13301435
1331 for (size_t i = 0; i < path->n_points; i++) {
1332 if (path->points[i].x < OUTLINE_MIN || path->points[i].x > OUTLINE_MAX)
1333 return false;
1334 if (path->points[i].y < OUTLINE_MIN || path->points[i].y > OUTLINE_MAX)
1335 return false;
1336 }
1436 #ifndef NDEBUG
1437 for (size_t i = 0; i < path->n_points; i++)
1438 assert(abs(path->points[i].x) <= OUTLINE_MAX && abs(path->points[i].y) <= OUTLINE_MAX);
1439 #endif
13371440
13381441 ASS_Vector *start = path->points, *cur = start;
13391442 for (size_t i = 0; i < path->n_segments; i++) {
1443 if (start == cur)
1444 str.last_point = *start;
1445
13401446 int n = path->segments[i] & OUTLINE_COUNT_MASK;
13411447 cur += n;
13421448
1343 ASS_Vector *end = cur, p[4];
1449 ASS_Vector *end = cur;
13441450 if (path->segments[i] & OUTLINE_CONTOUR_END) {
13451451 end = start;
13461452 start = cur;
13481454
13491455 switch (n) {
13501456 case OUTLINE_LINE_SEGMENT:
1351 if (!add_line(&str, cur[-1], *end, dir))
1457 if (!add_line(&str, *end, dir))
13521458 return false;
13531459 break;
13541460
13551461 case OUTLINE_QUADRATIC_SPLINE:
1356 p[0] = cur[-2];
1357 p[1] = cur[-1];
1358 p[2] = *end;
1359 if (!add_quadratic(&str, p, dir))
1462 if (!add_quadratic(&str, cur[-1], *end, dir))
13601463 return false;
13611464 break;
13621465
13631466 case OUTLINE_CUBIC_SPLINE:
1364 p[0] = cur[-3];
1365 p[1] = cur[-2];
1366 p[2] = cur[-1];
1367 p[3] = *end;
1368 if (!add_cubic(&str, p, dir))
1467 if (!add_cubic(&str, cur[-2], cur[-1], *end, dir))
13691468 return false;
13701469 break;
13711470
13731472 return false;
13741473 }
13751474
1376 if (start == cur && !close_contour(&str, *end, dir))
1475 if (start == cur && !close_contour(&str, dir))
13771476 return false;
13781477 }
13791478 assert(start == cur && cur == path->points + path->n_points);
8282 char *segments;
8383 } ASS_Outline;
8484
85 #define OUTLINE_MIN (-((int32_t) 1 << 28))
85 // ouline point coordinates should always be in [-OUTLINE_MAX, +OUTLINE_MAX] range
8686 #define OUTLINE_MAX (((int32_t) 1 << 28) - 1)
87 // cubic spline splitting requires 8 * OUTLINE_MAX + 4 <= INT32_MAX
8788
8889 bool outline_alloc(ASS_Outline *outline, size_t n_points, size_t n_segments);
8990 bool outline_convert(ASS_Outline *outline, const FT_Outline *source);
90 bool outline_copy(ASS_Outline *outline, const ASS_Outline *source);
91 bool outline_scale_pow2(ASS_Outline *outline, const ASS_Outline *source,
92 int scale_ord_x, int scale_ord_y);
93 bool outline_transform_2d(ASS_Outline *outline, const ASS_Outline *source,
94 const double m[2][3]);
95 bool outline_transform_3d(ASS_Outline *outline, const ASS_Outline *source,
96 const double m[3][3]);
97 void outline_update_min_transformed_x(const ASS_Outline *outline,
98 const double m[3][3],
99 int32_t *min_x);
91100 void outline_free(ASS_Outline *outline);
92101
93102 bool outline_add_point(ASS_Outline *outline, ASS_Vector pt, char segment);
94103 bool outline_add_segment(ASS_Outline *outline, char segment);
95104 bool outline_close_contour(ASS_Outline *outline);
96105
97 void outline_translate(const ASS_Outline *outline, int32_t dx, int32_t dy);
98 void outline_adjust(const ASS_Outline *outline, double scale_x, int32_t dx, int32_t dy);
99 void outline_get_cbox(const ASS_Outline *outline, ASS_Rect *cbox);
106 void outline_update_cbox(const ASS_Outline *outline, ASS_Rect *cbox);
100107
101108 bool outline_stroke(ASS_Outline *result, ASS_Outline *result1,
102109 const ASS_Outline *path, int xbord, int ybord, int eps);
2323 #include <string.h>
2424 #include <math.h>
2525
26 #include "ass_library.h"
2627 #include "ass_render.h"
2728 #include "ass_parse.h"
2829
4142 return value;
4243 }
4344
44 static inline long long argtoll(struct arg arg)
45 {
46 long long value;
47 mystrtoll(&arg.start, &value);
45 static inline int32_t argtoi32(struct arg arg)
46 {
47 int32_t value;
48 mystrtoi32(&arg.start, 10, &value);
4849 return value;
4950 }
5051
7273 */
7374 static inline int mystrcmp(char **p, const char *sample)
7475 {
75 int len = strlen(sample);
76 if (strncmp(*p, sample, len) == 0) {
77 (*p) += len;
76 char *p2;
77 for (p2 = *p; *sample != 0 && *p2 == *sample; p2++, sample++)
78 ;
79 if (*sample == 0) {
80 *p = p2;
7881 return 1;
79 } else
80 return 0;
82 }
83 return 0;
8184 }
8285
8386 double ensure_font_size(ASS_Renderer *priv, double size)
9093 return size;
9194 }
9295
93 static void change_font_size(ASS_Renderer *render_priv, double sz)
94 {
95 render_priv->state.font_size = sz;
96 }
97
9896 /**
9997 * \brief Change current font, using setting from render_priv->state.
10098 */
103101 unsigned val;
104102 ASS_FontDesc desc;
105103
104 if (!render_priv->state.family)
105 return;
106106 if (render_priv->state.family[0] == '@') {
107107 desc.vertical = 1;
108108 desc.family = strdup(render_priv->state.family + 1);
110110 desc.vertical = 0;
111111 desc.family = strdup(render_priv->state.family);
112112 }
113 if (!desc.family)
114 return;
113115
114116 val = render_priv->state.bold;
115117 // 0 = normal, 1 = bold, >1 = exact weight
127129 desc.italic = val;
128130
129131 ass_cache_dec_ref(render_priv->state.font);
130 render_priv->state.font =
131 ass_font_new(render_priv->cache.font_cache, render_priv->library,
132 render_priv->ftlibrary, render_priv->fontselect,
133 &desc);
134
135 if (render_priv->state.font)
136 change_font_size(render_priv, render_priv->state.font_size);
132 render_priv->state.font = ass_font_new(render_priv, &desc);
133 }
134
135 /**
136 * \brief Convert double to int32_t without UB
137 * on out-of-range values; match x86 behavior
138 */
139 static inline int32_t dtoi32(double val)
140 {
141 if (isnan(val) || val <= INT32_MIN || val >= INT32_MAX + 1LL)
142 return INT32_MIN;
143 return val;
144 }
145
146 static double calc_anim(double new, double old, double pwr)
147 {
148 return (1 - pwr) * old + new * pwr;
149 }
150
151 static int32_t calc_anim_int32(uint32_t new, uint32_t old, double pwr)
152 {
153 return dtoi32(calc_anim(new, old, pwr));
137154 }
138155
139156 /**
142159 */
143160 static void change_color(uint32_t *var, uint32_t new, double pwr)
144161 {
145 (*var) = ((uint32_t) (_r(*var) * (1 - pwr) + _r(new) * pwr) << 24) |
146 ((uint32_t) (_g(*var) * (1 - pwr) + _g(new) * pwr) << 16) |
147 ((uint32_t) (_b(*var) * (1 - pwr) + _b(new) * pwr) << 8) | _a(*var);
162 uint32_t co = ass_bswap32(*var);
163 uint32_t cn = ass_bswap32(new);
164
165 uint32_t cc = (calc_anim_int32(cn & 0xff0000, co & 0xff0000, pwr) & 0xff0000) |
166 (calc_anim_int32(cn & 0x00ff00, co & 0x00ff00, pwr) & 0x00ff00) |
167 (calc_anim_int32(cn & 0x0000ff, co & 0x0000ff, pwr) & 0x0000ff);
168
169 (*var) = (ass_bswap32(cc & 0xffffff)) | _a(*var);
148170 }
149171
150172 // like change_color, but for alpha component only
151173 inline void change_alpha(uint32_t *var, int32_t new, double pwr)
152174 {
153 *var = (*var & 0xFFFFFF00) | (uint8_t) (_a(*var) * (1 - pwr) + new * pwr);
175 *var = (*var & 0xFFFFFF00) | (uint8_t)calc_anim_int32(_a(new), _a(*var), pwr);
154176 }
155177
156178 /**
158180 * \param a first value
159181 * \param b second value
160182 * \return result of multiplication
161 * Parameters and result are limited by 0xFF.
183 * At least one of the parameters must be less than or equal to 0xFF.
184 * The result is less than or equal to max(a, b, 0xFF).
162185 */
163186 inline uint32_t mult_alpha(uint32_t a, uint32_t b)
164187 {
165 return 0xFF - (0xFF - a) * (0xFF - b) / 0xFF;
188 return a - ((uint64_t) a * b + 0x7F) / 0xFF + b;
166189 }
167190
168191 /**
170193 * Used for \fad, \fade implementation.
171194 */
172195 static int
173 interpolate_alpha(long long now, long long t1, long long t2, long long t3,
174 long long t4, int a1, int a2, int a3)
196 interpolate_alpha(long long now, int32_t t1, int32_t t2, int32_t t3,
197 int32_t t4, int a1, int a2, int a3)
175198 {
176199 int a;
177200 double cf;
179202 if (now < t1) {
180203 a = a1;
181204 } else if (now < t2) {
182 cf = ((double) (now - t1)) / (t2 - t1);
205 cf = ((double) (int32_t) ((uint32_t) now - t1)) /
206 (int32_t) ((uint32_t) t2 - t1);
183207 a = a1 * (1 - cf) + a2 * cf;
184208 } else if (now < t3) {
185209 a = a2;
186210 } else if (now < t4) {
187 cf = ((double) (now - t3)) / (t4 - t3);
211 cf = ((double) (int32_t) ((uint32_t) now - t3)) /
212 (int32_t) ((uint32_t) t4 - t3);
188213 a = a2 * (1 - cf) + a3 * cf;
189214 } else { // now >= t4
190215 a = a3;
197222 * Parse a vector clip into an outline, using the proper scaling
198223 * parameters. Translate it to correct for screen borders, if needed.
199224 */
200 static int parse_vector_clip(ASS_Renderer *render_priv,
201 struct arg *args, int nargs)
202 {
225 static bool parse_vector_clip(ASS_Renderer *render_priv,
226 struct arg *args, int nargs)
227 {
228 if (nargs != 1 && nargs != 2)
229 return false;
230
203231 int scale = 1;
204 ASS_Drawing *drawing = render_priv->state.clip_drawing;
205
206 if (nargs != 1 && nargs != 2)
207 return 0;
208232 if (nargs == 2)
209233 scale = argtoi(args[0]);
234
210235 struct arg text = args[nargs - 1];
211
212 ass_drawing_free(drawing);
213 render_priv->state.clip_drawing = ass_drawing_new(render_priv->library);
214 drawing = render_priv->state.clip_drawing;
215 if (drawing) {
216 drawing->scale = scale;
217 drawing->scale_x = render_priv->font_scale_x * render_priv->font_scale;
218 drawing->scale_y = render_priv->font_scale;
219 ass_drawing_set_text(drawing, text.start, text.end - text.start);
220 }
221
222 return 1;
236 render_priv->state.clip_drawing_text = strndup(text.start, text.end - text.start);
237 render_priv->state.clip_drawing_scale = scale;
238 return true;
223239 }
224240
225241 /**
226 * \brief Parse style override tag.
242 * \brief Parse style override tags.
227243 * \param p string to parse
228244 * \param end end of string to parse, which must be '}', ')', or the first
229245 * of a number of spaces immediately preceding '}' or ')'
230246 * \param pwr multiplier for some tag effects (comes from \t tags)
231247 */
232 char *parse_tag(ASS_Renderer *render_priv, char *p, char *end, double pwr)
233 {
234 while (*p != '\\' && p != end)
248 char *parse_tags(ASS_Renderer *render_priv, char *p, char *end, double pwr,
249 bool nested)
250 {
251 for (char *q; p < end; p = q) {
252 while (*p != '\\' && p != end)
253 ++p;
254 if (*p != '\\')
255 break;
235256 ++p;
236 if (*p != '\\')
237 return p;
238 ++p;
239 if (p != end)
240 skip_spaces(&p);
241
242 char *q = p;
243 while (*q != '(' && *q != '\\' && q != end)
244 ++q;
245 if (q == p)
246 return q;
247
248 char *name_end = q;
249
250 // Store one extra element to be able to detect excess arguments
251 struct arg args[MAX_VALID_NARGS + 1];
252 int nargs = 0;
253 for (int i = 0; i <= MAX_VALID_NARGS; ++i)
254 args[i].start = args[i].end = "";
255
256 // Split parenthesized arguments. Do this for all tags and before
257 // any non-parenthesized argument because that's what VSFilter does.
258 if (*q == '(') {
259 ++q;
260 while (1) {
261 if (q != end)
262 skip_spaces(&q);
263
264 // Split on commas. If there is a backslash, ignore any
265 // commas following it and lump everything starting from
266 // the last comma, through the backslash and all the way
267 // to the end of the argument string into a single argument.
268
269 char *r = q;
270 while (*r != ',' && *r != '\\' && *r != ')' && r != end)
271 ++r;
272
273 if (*r == ',') {
274 push_arg(args, &nargs, q, r);
275 q = r + 1;
276 } else {
277 // Swallow the rest of the parenthesized string. This could
278 // be either a backslash-argument or simply the last argument.
279 while (*r != ')' && r != end)
257 if (p != end)
258 skip_spaces(&p);
259
260 q = p;
261 while (*q != '(' && *q != '\\' && q != end)
262 ++q;
263 if (q == p)
264 continue;
265
266 char *name_end = q;
267
268 // Store one extra element to be able to detect excess arguments
269 struct arg args[MAX_VALID_NARGS + 1];
270 int nargs = 0;
271 bool has_backslash_arg = false;
272 for (int i = 0; i <= MAX_VALID_NARGS; ++i)
273 args[i].start = args[i].end = "";
274
275 // Split parenthesized arguments. Do this for all tags and before
276 // any non-parenthesized argument because that's what VSFilter does.
277 if (*q == '(') {
278 ++q;
279 while (1) {
280 if (q != end)
281 skip_spaces(&q);
282
283 // Split on commas. If there is a backslash, ignore any
284 // commas following it and lump everything starting from
285 // the last comma, through the backslash and all the way
286 // to the end of the argument string into a single argument.
287
288 char *r = q;
289 while (*r != ',' && *r != '\\' && *r != ')' && r != end)
280290 ++r;
281 push_arg(args, &nargs, q, r);
282 q = r;
283 // The closing parenthesis could be missing.
284 if (q != end)
285 ++q;
286 break;
291
292 if (*r == ',') {
293 push_arg(args, &nargs, q, r);
294 q = r + 1;
295 } else {
296 // Swallow the rest of the parenthesized string. This could
297 // be either a backslash-argument or simply the last argument.
298 if (*r == '\\') {
299 has_backslash_arg = true;
300 while (*r != ')' && r != end)
301 ++r;
302 }
303 push_arg(args, &nargs, q, r);
304 q = r;
305 // The closing parenthesis could be missing.
306 if (q != end)
307 ++q;
308 break;
309 }
287310 }
288311 }
289 }
290312
291313 #define tag(name) (mystrcmp(&p, (name)) && (push_arg(args, &nargs, p, name_end), 1))
292314 #define complex_tag(name) mystrcmp(&p, (name))
293315
294 // New tags introduced in vsfilter 2.39
295 if (tag("xbord")) {
296 double val;
297 if (nargs) {
298 val = argtod(*args);
299 val = render_priv->state.border_x * (1 - pwr) + val * pwr;
316 // New tags introduced in vsfilter 2.39
317 if (tag("xbord")) {
318 double val;
319 if (nargs) {
320 val = argtod(*args);
321 val = render_priv->state.border_x * (1 - pwr) + val * pwr;
322 val = (val < 0) ? 0 : val;
323 } else
324 val = render_priv->state.style->Outline;
325 render_priv->state.border_x = val;
326 } else if (tag("ybord")) {
327 double val;
328 if (nargs) {
329 val = argtod(*args);
330 val = render_priv->state.border_y * (1 - pwr) + val * pwr;
331 val = (val < 0) ? 0 : val;
332 } else
333 val = render_priv->state.style->Outline;
334 render_priv->state.border_y = val;
335 } else if (tag("xshad")) {
336 double val;
337 if (nargs) {
338 val = argtod(*args);
339 val = render_priv->state.shadow_x * (1 - pwr) + val * pwr;
340 } else
341 val = render_priv->state.style->Shadow;
342 render_priv->state.shadow_x = val;
343 } else if (tag("yshad")) {
344 double val;
345 if (nargs) {
346 val = argtod(*args);
347 val = render_priv->state.shadow_y * (1 - pwr) + val * pwr;
348 } else
349 val = render_priv->state.style->Shadow;
350 render_priv->state.shadow_y = val;
351 } else if (tag("fax")) {
352 double val;
353 if (nargs) {
354 val = argtod(*args);
355 render_priv->state.fax =
356 val * pwr + render_priv->state.fax * (1 - pwr);
357 } else
358 render_priv->state.fax = 0.;
359 } else if (tag("fay")) {
360 double val;
361 if (nargs) {
362 val = argtod(*args);
363 render_priv->state.fay =
364 val * pwr + render_priv->state.fay * (1 - pwr);
365 } else
366 render_priv->state.fay = 0.;
367 } else if (complex_tag("iclip")) {
368 if (nargs == 4) {
369 int x0, y0, x1, y1;
370 x0 = argtoi(args[0]);
371 y0 = argtoi(args[1]);
372 x1 = argtoi(args[2]);
373 y1 = argtoi(args[3]);
374 render_priv->state.clip_x0 =
375 render_priv->state.clip_x0 * (1 - pwr) + x0 * pwr;
376 render_priv->state.clip_x1 =
377 render_priv->state.clip_x1 * (1 - pwr) + x1 * pwr;
378 render_priv->state.clip_y0 =
379 render_priv->state.clip_y0 * (1 - pwr) + y0 * pwr;
380 render_priv->state.clip_y1 =
381 render_priv->state.clip_y1 * (1 - pwr) + y1 * pwr;
382 render_priv->state.clip_mode = 1;
383 } else if (!render_priv->state.clip_drawing_text) {
384 if (parse_vector_clip(render_priv, args, nargs))
385 render_priv->state.clip_drawing_mode = 1;
386 }
387 } else if (tag("blur")) {
388 double val;
389 if (nargs) {
390 val = argtod(*args);
391 val = render_priv->state.blur * (1 - pwr) + val * pwr;
392 val = (val < 0) ? 0 : val;
393 val = (val > BLUR_MAX_RADIUS) ? BLUR_MAX_RADIUS : val;
394 render_priv->state.blur = val;
395 } else
396 render_priv->state.blur = 0.0;
397 // ASS standard tags
398 } else if (tag("fscx")) {
399 double val;
400 if (nargs) {
401 val = argtod(*args) / 100;
402 val = render_priv->state.scale_x * (1 - pwr) + val * pwr;
403 val = (val < 0) ? 0 : val;
404 } else
405 val = render_priv->state.style->ScaleX;
406 render_priv->state.scale_x = val;
407 } else if (tag("fscy")) {
408 double val;
409 if (nargs) {
410 val = argtod(*args) / 100;
411 val = render_priv->state.scale_y * (1 - pwr) + val * pwr;
412 val = (val < 0) ? 0 : val;
413 } else
414 val = render_priv->state.style->ScaleY;
415 render_priv->state.scale_y = val;
416 } else if (tag("fsc")) {
417 render_priv->state.scale_x = render_priv->state.style->ScaleX;
418 render_priv->state.scale_y = render_priv->state.style->ScaleY;
419 } else if (tag("fsp")) {
420 double val;
421 if (nargs) {
422 val = argtod(*args);
423 render_priv->state.hspacing =
424 render_priv->state.hspacing * (1 - pwr) + val * pwr;
425 } else
426 render_priv->state.hspacing = render_priv->state.style->Spacing;
427 } else if (tag("fs")) {
428 double val = 0;
429 if (nargs) {
430 val = argtod(*args);
431 if (*args->start == '+' || *args->start == '-')
432 val = render_priv->state.font_size * (1 + pwr * val / 10);
433 else
434 val = render_priv->state.font_size * (1 - pwr) + val * pwr;
435 }
436 if (val <= 0)
437 val = render_priv->state.style->FontSize;
438 render_priv->state.font_size = val;
439 } else if (tag("bord")) {
440 double val, xval, yval;
441 if (nargs) {
442 val = argtod(*args);
443 xval = render_priv->state.border_x * (1 - pwr) + val * pwr;
444 yval = render_priv->state.border_y * (1 - pwr) + val * pwr;
445 xval = (xval < 0) ? 0 : xval;
446 yval = (yval < 0) ? 0 : yval;
447 } else
448 xval = yval = render_priv->state.style->Outline;
449 render_priv->state.border_x = xval;
450 render_priv->state.border_y = yval;
451 } else if (complex_tag("move")) {
452 double x1, x2, y1, y2;
453 int32_t t1, t2, delta_t, t;
454 double x, y;
455 double k;
456 if (nargs == 4 || nargs == 6) {
457 x1 = argtod(args[0]);
458 y1 = argtod(args[1]);
459 x2 = argtod(args[2]);
460 y2 = argtod(args[3]);
461 t1 = t2 = 0;
462 if (nargs == 6) {
463 t1 = argtoi32(args[4]);
464 t2 = argtoi32(args[5]);
465 if (t1 > t2) {
466 long long tmp = t2;
467 t2 = t1;
468 t1 = tmp;
469 }
470 }
471 } else
472 continue;
473 if (t1 <= 0 && t2 <= 0) {
474 t1 = 0;
475 t2 = render_priv->state.event->Duration;
476 }
477 delta_t = (uint32_t) t2 - t1;
478 t = render_priv->time - render_priv->state.event->Start;
479 if (t <= t1)
480 k = 0.;
481 else if (t >= t2)
482 k = 1.;
483 else
484 k = ((double) (int32_t) ((uint32_t) t - t1)) / delta_t;
485 x = k * (x2 - x1) + x1;
486 y = k * (y2 - y1) + y1;
487 if (!(render_priv->state.evt_type & EVENT_POSITIONED)) {
488 render_priv->state.pos_x = x;
489 render_priv->state.pos_y = y;
490 render_priv->state.detect_collisions = 0;
491 render_priv->state.evt_type |= EVENT_POSITIONED;
492 }
493 } else if (tag("frx")) {
494 double val;
495 if (nargs) {
496 val = argtod(*args);
497 render_priv->state.frx =
498 val * pwr + render_priv->state.frx * (1 - pwr);
499 } else
500 render_priv->state.frx = 0.;
501 } else if (tag("fry")) {
502 double val;
503 if (nargs) {
504 val = argtod(*args);
505 render_priv->state.fry =
506 val * pwr + render_priv->state.fry * (1 - pwr);
507 } else
508 render_priv->state.fry = 0.;
509 } else if (tag("frz") || tag("fr")) {
510 double val;
511 if (nargs) {
512 val = argtod(*args);
513 render_priv->state.frz =
514 val * pwr + render_priv->state.frz * (1 - pwr);
515 } else
516 render_priv->state.frz =
517 render_priv->state.style->Angle;
518 } else if (tag("fn")) {
519 char *family;
520 char *start = args->start;
521 if (nargs && strncmp(start, "0", args->end - start)) {
522 skip_spaces(&start);
523 family = strndup(start, args->end - start);
524 } else {
525 family = strdup(render_priv->state.style->FontName);
526 }
527 if (family) {
528 free(render_priv->state.family);
529 render_priv->state.family = family;
530 update_font(render_priv);
531 }
532 } else if (tag("alpha")) {
533 int i;
534 if (nargs) {
535 int32_t a = parse_alpha_tag(args->start);
536 for (i = 0; i < 4; ++i)
537 change_alpha(&render_priv->state.c[i], a, pwr);
538 } else {
539 change_alpha(&render_priv->state.c[0],
540 _a(render_priv->state.style->PrimaryColour), 1);
541 change_alpha(&render_priv->state.c[1],
542 _a(render_priv->state.style->SecondaryColour), 1);
543 change_alpha(&render_priv->state.c[2],
544 _a(render_priv->state.style->OutlineColour), 1);
545 change_alpha(&render_priv->state.c[3],
546 _a(render_priv->state.style->BackColour), 1);
547 }
548 // FIXME: simplify
549 } else if (tag("an")) {
550 int val = argtoi(*args);
551 if ((render_priv->state.parsed_tags & PARSED_A) == 0) {
552 if (val >= 1 && val <= 9)
553 render_priv->state.alignment = numpad2align(val);
554 else
555 render_priv->state.alignment =
556 render_priv->state.style->Alignment;
557 render_priv->state.parsed_tags |= PARSED_A;
558 }
559 } else if (tag("a")) {
560 int val = argtoi(*args);
561 if ((render_priv->state.parsed_tags & PARSED_A) == 0) {
562 if (val >= 1 && val <= 11)
563 // take care of a vsfilter quirk:
564 // handle illegal \a8 and \a4 like \a5
565 render_priv->state.alignment = ((val & 3) == 0) ? 5 : val;
566 else
567 render_priv->state.alignment =
568 render_priv->state.style->Alignment;
569 render_priv->state.parsed_tags |= PARSED_A;
570 }
571 } else if (complex_tag("pos")) {
572 double v1, v2;
573 if (nargs == 2) {
574 v1 = argtod(args[0]);
575 v2 = argtod(args[1]);
576 } else
577 continue;
578 if (render_priv->state.evt_type & EVENT_POSITIONED) {
579 ass_msg(render_priv->library, MSGL_V, "Subtitle has a new \\pos "
580 "after \\move or \\pos, ignoring");
581 } else {
582 render_priv->state.evt_type |= EVENT_POSITIONED;
583 render_priv->state.detect_collisions = 0;
584 render_priv->state.pos_x = v1;
585 render_priv->state.pos_y = v2;
586 }
587 } else if (complex_tag("fade") || complex_tag("fad")) {
588 int a1, a2, a3;
589 int32_t t1, t2, t3, t4;
590 if (nargs == 2) {
591 // 2-argument version (\fad, according to specs)
592 a1 = 0xFF;
593 a2 = 0;
594 a3 = 0xFF;
595 t1 = -1;
596 t2 = argtoi32(args[0]);
597 t3 = argtoi32(args[1]);
598 t4 = -1;
599 } else if (nargs == 7) {
600 // 7-argument version (\fade)
601 a1 = argtoi(args[0]);
602 a2 = argtoi(args[1]);
603 a3 = argtoi(args[2]);
604 t1 = argtoi32(args[3]);
605 t2 = argtoi32(args[4]);
606 t3 = argtoi32(args[5]);
607 t4 = argtoi32(args[6]);
608 } else
609 continue;
610 if (t1 == -1 && t4 == -1) {
611 t1 = 0;
612 t4 = render_priv->state.event->Duration;
613 t3 = (uint32_t) t4 - t3;
614 }
615 if ((render_priv->state.parsed_tags & PARSED_FADE) == 0) {
616 render_priv->state.fade =
617 interpolate_alpha(render_priv->time -
618 render_priv->state.event->Start, t1, t2,
619 t3, t4, a1, a2, a3);
620 render_priv->state.parsed_tags |= PARSED_FADE;
621 }
622 } else if (complex_tag("org")) {
623 double v1, v2;
624 if (nargs == 2) {
625 v1 = argtod(args[0]);
626 v2 = argtod(args[1]);
627 } else
628 continue;
629 if (!render_priv->state.have_origin) {
630 render_priv->state.org_x = v1;
631 render_priv->state.org_y = v2;
632 render_priv->state.have_origin = 1;
633 render_priv->state.detect_collisions = 0;
634 }
635 } else if (complex_tag("t")) {
636 double accel;
637 int cnt = nargs - 1;
638 int32_t t1, t2, t, delta_t;
639 double k;
640 // VSFilter compatibility (because we can): parse the
641 // timestamps differently depending on argument count.
642 if (cnt == 3) {
643 t1 = argtoi32(args[0]);
644 t2 = argtoi32(args[1]);
645 accel = argtod(args[2]);
646 } else if (cnt == 2) {
647 t1 = dtoi32(argtod(args[0]));
648 t2 = dtoi32(argtod(args[1]));
649 accel = 1.;
650 } else if (cnt == 1) {
651 t1 = 0;
652 t2 = 0;
653 accel = argtod(args[0]);
654 } else {
655 t1 = 0;
656 t2 = 0;
657 accel = 1.;
658 }
659 render_priv->state.detect_collisions = 0;
660 if (t2 == 0)
661 t2 = render_priv->state.event->Duration;
662 delta_t = (uint32_t) t2 - t1;
663 t = render_priv->time - render_priv->state.event->Start; // FIXME: move to render_context
664 if (t < t1)
665 k = 0.;
666 else if (t >= t2)
667 k = 1.;
668 else {
669 assert(delta_t != 0.);
670 k = pow((double) (int32_t) ((uint32_t) t - t1) / delta_t, accel);
671 }
672 if (nested)
673 pwr = k;
674 if (cnt < 0 || cnt > 3)
675 continue;
676 // If there's no backslash in the arguments, there are no
677 // override tags, so it's pointless to try to parse them.
678 if (!has_backslash_arg)
679 continue;
680 p = args[cnt].start;
681 if (args[cnt].end < end) {
682 assert(!nested);
683 p = parse_tags(render_priv, p, args[cnt].end, k, true);
684 } else {
685 assert(q == end);
686 // No other tags can possibly follow this \t tag,
687 // so we don't need to restore pwr after parsing \t.
688 // The recursive call is now essentially a tail call,
689 // so optimize it away.
690 pwr = k;
691 nested = true;
692 q = p;
693 }
694 } else if (complex_tag("clip")) {
695 if (nargs == 4) {
696 int x0, y0, x1, y1;
697 x0 = argtoi(args[0]);
698 y0 = argtoi(args[1]);
699 x1 = argtoi(args[2]);
700 y1 = argtoi(args[3]);
701 render_priv->state.clip_x0 =
702 render_priv->state.clip_x0 * (1 - pwr) + x0 * pwr;
703 render_priv->state.clip_x1 =
704 render_priv->state.clip_x1 * (1 - pwr) + x1 * pwr;
705 render_priv->state.clip_y0 =
706 render_priv->state.clip_y0 * (1 - pwr) + y0 * pwr;
707 render_priv->state.clip_y1 =
708 render_priv->state.clip_y1 * (1 - pwr) + y1 * pwr;
709 render_priv->state.clip_mode = 0;
710 } else if (!render_priv->state.clip_drawing_text) {
711 if (parse_vector_clip(render_priv, args, nargs))
712 render_priv->state.clip_drawing_mode = 0;
713 }
714 } else if (tag("c") || tag("1c")) {
715 if (nargs) {
716 uint32_t val = parse_color_tag(args->start);
717 change_color(&render_priv->state.c[0], val, pwr);
718 } else
719 change_color(&render_priv->state.c[0],
720 render_priv->state.style->PrimaryColour, 1);
721 } else if (tag("2c")) {
722 if (nargs) {
723 uint32_t val = parse_color_tag(args->start);
724 change_color(&render_priv->state.c[1], val, pwr);
725 } else
726 change_color(&render_priv->state.c[1],
727 render_priv->state.style->SecondaryColour, 1);
728 } else if (tag("3c")) {
729 if (nargs) {
730 uint32_t val = parse_color_tag(args->start);
731 change_color(&render_priv->state.c[2], val, pwr);
732 } else
733 change_color(&render_priv->state.c[2],
734 render_priv->state.style->OutlineColour, 1);
735 } else if (tag("4c")) {
736 if (nargs) {
737 uint32_t val = parse_color_tag(args->start);
738 change_color(&render_priv->state.c[3], val, pwr);
739 } else
740 change_color(&render_priv->state.c[3],
741 render_priv->state.style->BackColour, 1);
742 } else if (tag("1a")) {
743 if (nargs) {
744 uint32_t val = parse_alpha_tag(args->start);
745 change_alpha(&render_priv->state.c[0], val, pwr);
746 } else
747 change_alpha(&render_priv->state.c[0],
748 _a(render_priv->state.style->PrimaryColour), 1);
749 } else if (tag("2a")) {
750 if (nargs) {
751 uint32_t val = parse_alpha_tag(args->start);
752 change_alpha(&render_priv->state.c[1], val, pwr);
753 } else
754 change_alpha(&render_priv->state.c[1],
755 _a(render_priv->state.style->SecondaryColour), 1);
756 } else if (tag("3a")) {
757 if (nargs) {
758 uint32_t val = parse_alpha_tag(args->start);
759 change_alpha(&render_priv->state.c[2], val, pwr);
760 } else
761 change_alpha(&render_priv->state.c[2],
762 _a(render_priv->state.style->OutlineColour), 1);
763 } else if (tag("4a")) {
764 if (nargs) {
765 uint32_t val = parse_alpha_tag(args->start);
766 change_alpha(&render_priv->state.c[3], val, pwr);
767 } else
768 change_alpha(&render_priv->state.c[3],
769 _a(render_priv->state.style->BackColour), 1);
770 } else if (tag("r")) {
771 if (nargs) {
772 int len = args->end - args->start;
773 reset_render_context(render_priv,
774 lookup_style_strict(render_priv->track, args->start, len));
775 } else
776 reset_render_context(render_priv, NULL);
777 } else if (tag("be")) {
778 double dval;
779 if (nargs) {
780 int val;
781 dval = argtod(*args);
782 // VSFilter always adds +0.5, even if the value is negative
783 val = (int) (render_priv->state.be * (1 - pwr) + dval * pwr + 0.5);
784 // Clamp to a safe upper limit, since high values need excessive CPU
785 val = (val < 0) ? 0 : val;
786 val = (val > MAX_BE) ? MAX_BE : val;
787 render_priv->state.be = val;
788 } else
789 render_priv->state.be = 0;
790 } else if (tag("b")) {
791 int val = argtoi(*args);
792 if (!nargs || !(val == 0 || val == 1 || val >= 100))
793 val = render_priv->state.style->Bold;
794 render_priv->state.bold = val;
795 update_font(render_priv);
796 } else if (tag("i")) {
797 int val = argtoi(*args);
798 if (!nargs || !(val == 0 || val == 1))
799 val = render_priv->state.style->Italic;
800 render_priv->state.italic = val;
801 update_font(render_priv);
802 } else if (tag("kf") || tag("K")) {
803 double val = 100;
804 if (nargs)
805 val = argtod(*args);
806 render_priv->state.effect_type = EF_KARAOKE_KF;
807 if (render_priv->state.effect_timing)
808 render_priv->state.effect_skip_timing +=
809 render_priv->state.effect_timing;
810 render_priv->state.effect_timing = val * 10;
811 } else if (tag("ko")) {
812 double val = 100;
813 if (nargs)
814 val = argtod(*args);
815 render_priv->state.effect_type = EF_KARAOKE_KO;
816 if (render_priv->state.effect_timing)
817 render_priv->state.effect_skip_timing +=
818 render_priv->state.effect_timing;
819 render_priv->state.effect_timing = val * 10;
820 } else if (tag("k")) {
821 double val = 100;
822 if (nargs)
823 val = argtod(*args);
824 render_priv->state.effect_type = EF_KARAOKE;
825 if (render_priv->state.effect_timing)
826 render_priv->state.effect_skip_timing +=
827 render_priv->state.effect_timing;
828 render_priv->state.effect_timing = val * 10;
829 } else if (tag("shad")) {
830 double val, xval, yval;
831 if (nargs) {
832 val = argtod(*args);
833 xval = render_priv->state.shadow_x * (1 - pwr) + val * pwr;
834 yval = render_priv->state.shadow_y * (1 - pwr) + val * pwr;
835 // VSFilter compatibility: clip for \shad but not for \[xy]shad
836 xval = (xval < 0) ? 0 : xval;
837 yval = (yval < 0) ? 0 : yval;
838 } else
839 xval = yval = render_priv->state.style->Shadow;
840 render_priv->state.shadow_x = xval;
841 render_priv->state.shadow_y = yval;
842 } else if (tag("s")) {
843 int val = argtoi(*args);
844 if (!nargs || !(val == 0 || val == 1))
845 val = render_priv->state.style->StrikeOut;
846 if (val)
847 render_priv->state.flags |= DECO_STRIKETHROUGH;
848 else
849 render_priv->state.flags &= ~DECO_STRIKETHROUGH;
850 } else if (tag("u")) {
851 int val = argtoi(*args);
852 if (!nargs || !(val == 0 || val == 1))
853 val = render_priv->state.style->Underline;
854 if (val)
855 render_priv->state.flags |= DECO_UNDERLINE;
856 else
857 render_priv->state.flags &= ~DECO_UNDERLINE;
858 } else if (tag("pbo")) {
859 double val = argtod(*args);
860 render_priv->state.pbo = val;
861 } else if (tag("p")) {
862 int val = argtoi(*args);
300863 val = (val < 0) ? 0 : val;
301 } else
302 val = render_priv->state.style->Outline;
303 render_priv->state.border_x = val;
304 } else if (tag("ybord")) {
305 double val;
306 if (nargs) {
307 val = argtod(*args);
308 val = render_priv->state.border_y * (1 - pwr) + val * pwr;
309 val = (val < 0) ? 0 : val;
310 } else
311 val = render_priv->state.style->Outline;
312 render_priv->state.border_y = val;
313 } else if (tag("xshad")) {
314 double val;
315 if (nargs) {
316 val = argtod(*args);
317 val = render_priv->state.shadow_x * (1 - pwr) + val * pwr;
318 } else
319 val = render_priv->state.style->Shadow;
320 render_priv->state.shadow_x = val;
321 } else if (tag("yshad")) {
322 double val;
323 if (nargs) {
324 val = argtod(*args);
325 val = render_priv->state.shadow_y * (1 - pwr) + val * pwr;
326 } else
327 val = render_priv->state.style->Shadow;
328 render_priv->state.shadow_y = val;
329 } else if (tag("fax")) {
330 double val;
331 if (nargs) {
332 val = argtod(*args);
333 render_priv->state.fax =
334 val * pwr + render_priv->state.fax * (1 - pwr);
335 } else
336 render_priv->state.fax = 0.;
337 } else if (tag("fay")) {
338 double val;
339 if (nargs) {
340 val = argtod(*args);
341 render_priv->state.fay =
342 val * pwr + render_priv->state.fay * (1 - pwr);
343 } else
344 render_priv->state.fay = 0.;
345 } else if (complex_tag("iclip")) {
346 if (nargs == 4) {
347 int x0, y0, x1, y1;
348 x0 = argtoi(args[0]);
349 y0 = argtoi(args[1]);
350 x1 = argtoi(args[2]);
351 y1 = argtoi(args[3]);
352 render_priv->state.clip_x0 =
353 render_priv->state.clip_x0 * (1 - pwr) + x0 * pwr;
354 render_priv->state.clip_x1 =
355 render_priv->state.clip_x1 * (1 - pwr) + x1 * pwr;
356 render_priv->state.clip_y0 =
357 render_priv->state.clip_y0 * (1 - pwr) + y0 * pwr;
358 render_priv->state.clip_y1 =
359 render_priv->state.clip_y1 * (1 - pwr) + y1 * pwr;
360 render_priv->state.clip_mode = 1;
361 } else if (!render_priv->state.clip_drawing) {
362 if (parse_vector_clip(render_priv, args, nargs))
363 render_priv->state.clip_drawing_mode = 1;
864 render_priv->state.drawing_scale = val;
865 } else if (tag("q")) {
866 int val = argtoi(*args);
867 if (!nargs || !(val >= 0 && val <= 3))
868 val = render_priv->track->WrapStyle;
869 render_priv->state.wrap_style = val;
870 } else if (tag("fe")) {
871 int val;
872 if (nargs)
873 val = argtoi(*args);
874 else
875 val = render_priv->state.style->Encoding;
876 render_priv->state.font_encoding = val;
364877 }
365 } else if (tag("blur")) {
366 double val;
367 if (nargs) {
368 val = argtod(*args);
369 val = render_priv->state.blur * (1 - pwr) + val * pwr;
370 val = (val < 0) ? 0 : val;
371 val = (val > BLUR_MAX_RADIUS) ? BLUR_MAX_RADIUS : val;
372 render_priv->state.blur = val;
373 } else
374 render_priv->state.blur = 0.0;
375 // ASS standard tags
376 } else if (tag("fscx")) {
377 double val;
378 if (nargs) {
379 val = argtod(*args) / 100;
380 val = render_priv->state.scale_x * (1 - pwr) + val * pwr;
381 val = (val < 0) ? 0 : val;
382 } else
383 val = render_priv->state.style->ScaleX;
384 render_priv->state.scale_x = val;
385 } else if (tag("fscy")) {
386 double val;
387 if (nargs) {
388 val = argtod(*args) / 100;
389 val = render_priv->state.scale_y * (1 - pwr) + val * pwr;
390 val = (val < 0) ? 0 : val;
391 } else
392 val = render_priv->state.style->ScaleY;
393 render_priv->state.scale_y = val;
394 } else if (tag("fsc")) {
395 render_priv->state.scale_x = render_priv->state.style->ScaleX;
396 render_priv->state.scale_y = render_priv->state.style->ScaleY;
397 } else if (tag("fsp")) {
398 double val;
399 if (nargs) {
400 val = argtod(*args);
401 render_priv->state.hspacing =
402 render_priv->state.hspacing * (1 - pwr) + val * pwr;
403 } else
404 render_priv->state.hspacing = render_priv->state.style->Spacing;
405 } else if (tag("fs")) {
406 double val = 0;
407 if (nargs) {
408 val = argtod(*args);
409 if (*args->start == '+' || *args->start == '-')
410 val = render_priv->state.font_size * (1 + pwr * val / 10);
411 else
412 val = render_priv->state.font_size * (1 - pwr) + val * pwr;
413 }
414 if (val <= 0)
415 val = render_priv->state.style->FontSize;
416 if (render_priv->state.font)
417 change_font_size(render_priv, val);
418 } else if (tag("bord")) {
419 double val, xval, yval;
420 if (nargs) {
421 val = argtod(*args);
422 xval = render_priv->state.border_x * (1 - pwr) + val * pwr;
423 yval = render_priv->state.border_y * (1 - pwr) + val * pwr;
424 xval = (xval < 0) ? 0 : xval;
425 yval = (yval < 0) ? 0 : yval;
426 } else
427 xval = yval = render_priv->state.style->Outline;
428 render_priv->state.border_x = xval;
429 render_priv->state.border_y = yval;
430 } else if (complex_tag("move")) {
431 double x1, x2, y1, y2;
432 long long t1, t2, delta_t, t;
433 double x, y;
434 double k;
435 if (nargs == 4 || nargs == 6) {
436 x1 = argtod(args[0]);
437 y1 = argtod(args[1]);
438 x2 = argtod(args[2]);
439 y2 = argtod(args[3]);
440 t1 = t2 = 0;
441 if (nargs == 6) {
442 t1 = argtoll(args[4]);
443 t2 = argtoll(args[5]);
444 if (t1 > t2) {
445 long long tmp = t2;
446 t2 = t1;
447 t1 = tmp;
448 }
449 }
450 } else
451 return q;
452 if (t1 <= 0 && t2 <= 0) {
453 t1 = 0;
454 t2 = render_priv->state.event->Duration;
455 }
456 delta_t = t2 - t1;
457 t = render_priv->time - render_priv->state.event->Start;
458 if (t <= t1)
459 k = 0.;
460 else if (t >= t2)
461 k = 1.;
462 else
463 k = ((double) (t - t1)) / delta_t;
464 x = k * (x2 - x1) + x1;
465 y = k * (y2 - y1) + y1;
466 if (render_priv->state.evt_type != EVENT_POSITIONED) {
467 render_priv->state.pos_x = x;
468 render_priv->state.pos_y = y;
469 render_priv->state.detect_collisions = 0;
470 render_priv->state.evt_type = EVENT_POSITIONED;
471 }
472 } else if (tag("frx")) {
473 double val;
474 if (nargs) {
475 val = argtod(*args);
476 val *= M_PI / 180;
477 render_priv->state.frx =
478 val * pwr + render_priv->state.frx * (1 - pwr);
479 } else
480 render_priv->state.frx = 0.;
481 } else if (tag("fry")) {
482 double val;
483 if (nargs) {
484 val = argtod(*args);
485 val *= M_PI / 180;
486 render_priv->state.fry =
487 val * pwr + render_priv->state.fry * (1 - pwr);
488 } else
489 render_priv->state.fry = 0.;
490 } else if (tag("frz") || tag("fr")) {
491 double val;
492 if (nargs) {
493 val = argtod(*args);
494 val *= M_PI / 180;
495 render_priv->state.frz =
496 val * pwr + render_priv->state.frz * (1 - pwr);
497 } else
498 render_priv->state.frz =
499 M_PI * render_priv->state.style->Angle / 180.;
500 } else if (tag("fn")) {
501 char *family;
502 char *start = args->start;
503 end = args->end;
504 if (nargs && strncmp(start, "0", end - start)) {
505 skip_spaces(&start);
506 family = strndup(start, end - start);
507 } else
508 family = strdup(render_priv->state.style->FontName);
509 free(render_priv->state.family);
510 render_priv->state.family = family;
511 update_font(render_priv);
512 } else if (tag("alpha")) {
513 int i;
514 if (nargs) {
515 int32_t a = parse_alpha_tag(args->start);
516 for (i = 0; i < 4; ++i)
517 change_alpha(&render_priv->state.c[i], a, pwr);
518 } else {
519 change_alpha(&render_priv->state.c[0],
520 _a(render_priv->state.style->PrimaryColour), 1);
521 change_alpha(&render_priv->state.c[1],
522 _a(render_priv->state.style->SecondaryColour), 1);
523 change_alpha(&render_priv->state.c[2],
524 _a(render_priv->state.style->OutlineColour), 1);
525 change_alpha(&render_priv->state.c[3],
526 _a(render_priv->state.style->BackColour), 1);
527 }
528 // FIXME: simplify
529 } else if (tag("an")) {
530 int val = argtoi(*args);
531 if ((render_priv->state.parsed_tags & PARSED_A) == 0) {
532 if (val >= 1 && val <= 9)
533 render_priv->state.alignment = numpad2align(val);
534 else
535 render_priv->state.alignment =
536 render_priv->state.style->Alignment;
537 render_priv->state.parsed_tags |= PARSED_A;
538 }
539 } else if (tag("a")) {
540 int val = argtoi(*args);
541 if ((render_priv->state.parsed_tags & PARSED_A) == 0) {
542 if (val >= 1 && val <= 11)
543 // take care of a vsfilter quirk:
544 // handle illegal \a8 and \a4 like \a5
545 render_priv->state.alignment = ((val & 3) == 0) ? 5 : val;
546 else
547 render_priv->state.alignment =
548 render_priv->state.style->Alignment;
549 render_priv->state.parsed_tags |= PARSED_A;
550 }
551 } else if (complex_tag("pos")) {
552 double v1, v2;
553 if (nargs == 2) {
554 v1 = argtod(args[0]);
555 v2 = argtod(args[1]);
556 } else
557 return q;
558 if (render_priv->state.evt_type == EVENT_POSITIONED) {
559 ass_msg(render_priv->library, MSGL_V, "Subtitle has a new \\pos "
560 "after \\move or \\pos, ignoring");
561 } else {
562 render_priv->state.evt_type = EVENT_POSITIONED;
563 render_priv->state.detect_collisions = 0;
564 render_priv->state.pos_x = v1;
565 render_priv->state.pos_y = v2;
566 }
567 } else if (complex_tag("fade") || complex_tag("fad")) {
568 int a1, a2, a3;
569 long long t1, t2, t3, t4;
570 if (nargs == 2) {
571 // 2-argument version (\fad, according to specs)
572 a1 = 0xFF;
573 a2 = 0;
574 a3 = 0xFF;
575 t1 = -1;
576 t2 = argtoll(args[0]);
577 t3 = argtoll(args[1]);
578 t4 = -1;
579 } else if (nargs == 7) {
580 // 7-argument version (\fade)
581 a1 = argtoi(args[0]);
582 a2 = argtoi(args[1]);
583 a3 = argtoi(args[2]);
584 t1 = argtoll(args[3]);
585 t2 = argtoll(args[4]);
586 t3 = argtoll(args[5]);
587 t4 = argtoll(args[6]);
588 } else
589 return q;
590 if (t1 == -1 && t4 == -1) {
591 t1 = 0;
592 t4 = render_priv->state.event->Duration;
593 t3 = t4 - t3;
594 }
595 if ((render_priv->state.parsed_tags & PARSED_FADE) == 0) {
596 render_priv->state.fade =
597 interpolate_alpha(render_priv->time -
598 render_priv->state.event->Start, t1, t2,
599 t3, t4, a1, a2, a3);
600 render_priv->state.parsed_tags |= PARSED_FADE;
601 }
602 } else if (complex_tag("org")) {
603 double v1, v2;
604 if (nargs == 2) {
605 v1 = argtod(args[0]);
606 v2 = argtod(args[1]);
607 } else
608 return q;
609 if (!render_priv->state.have_origin) {
610 render_priv->state.org_x = v1;
611 render_priv->state.org_y = v2;
612 render_priv->state.have_origin = 1;
613 render_priv->state.detect_collisions = 0;
614 }
615 } else if (complex_tag("t")) {
616 double accel;
617 int cnt = nargs - 1;
618 long long t1, t2, t, delta_t;
619 double k;
620 if (cnt == 3) {
621 t1 = argtoll(args[0]);
622 t2 = argtoll(args[1]);
623 accel = argtod(args[2]);
624 } else if (cnt == 2) {
625 t1 = argtoll(args[0]);
626 t2 = argtoll(args[1]);
627 accel = 1.;
628 } else if (cnt == 1) {
629 t1 = 0;
630 t2 = 0;
631 accel = argtod(args[0]);
632 } else if (cnt == 0) {
633 t1 = 0;
634 t2 = 0;
635 accel = 1.;
636 } else
637 return q;
638 render_priv->state.detect_collisions = 0;
639 if (t2 == 0)
640 t2 = render_priv->state.event->Duration;
641 delta_t = t2 - t1;
642 t = render_priv->time - render_priv->state.event->Start; // FIXME: move to render_context
643 if (t <= t1)
644 k = 0.;
645 else if (t >= t2)
646 k = 1.;
647 else {
648 assert(delta_t != 0.);
649 k = pow(((double) (t - t1)) / delta_t, accel);
650 }
651 p = args[cnt].start;
652 while (p < args[cnt].end)
653 p = parse_tag(render_priv, p, args[cnt].end, k); // maybe k*pwr ? no, specs forbid nested \t's
654 } else if (complex_tag("clip")) {
655 if (nargs == 4) {
656 int x0, y0, x1, y1;
657 x0 = argtoi(args[0]);
658 y0 = argtoi(args[1]);
659 x1 = argtoi(args[2]);
660 y1 = argtoi(args[3]);
661 render_priv->state.clip_x0 =
662 render_priv->state.clip_x0 * (1 - pwr) + x0 * pwr;
663 render_priv->state.clip_x1 =
664 render_priv->state.clip_x1 * (1 - pwr) + x1 * pwr;
665 render_priv->state.clip_y0 =
666 render_priv->state.clip_y0 * (1 - pwr) + y0 * pwr;
667 render_priv->state.clip_y1 =
668 render_priv->state.clip_y1 * (1 - pwr) + y1 * pwr;
669 render_priv->state.clip_mode = 0;
670 } else if (!render_priv->state.clip_drawing) {
671 if (parse_vector_clip(render_priv, args, nargs))
672 render_priv->state.clip_drawing_mode = 0;
673 }
674 } else if (tag("c") || tag("1c")) {
675 if (nargs) {
676 uint32_t val = parse_color_tag(args->start);
677 change_color(&render_priv->state.c[0], val, pwr);
678 } else
679 change_color(&render_priv->state.c[0],
680 render_priv->state.style->PrimaryColour, 1);
681 } else if (tag("2c")) {
682 if (nargs) {
683 uint32_t val = parse_color_tag(args->start);
684 change_color(&render_priv->state.c[1], val, pwr);
685 } else
686 change_color(&render_priv->state.c[1],
687 render_priv->state.style->SecondaryColour, 1);
688 } else if (tag("3c")) {
689 if (nargs) {
690 uint32_t val = parse_color_tag(args->start);
691 change_color(&render_priv->state.c[2], val, pwr);
692 } else
693 change_color(&render_priv->state.c[2],
694 render_priv->state.style->OutlineColour, 1);
695 } else if (tag("4c")) {
696 if (nargs) {
697 uint32_t val = parse_color_tag(args->start);
698 change_color(&render_priv->state.c[3], val, pwr);
699 } else
700 change_color(&render_priv->state.c[3],
701 render_priv->state.style->BackColour, 1);
702 } else if (tag("1a")) {
703 if (nargs) {
704 uint32_t val = parse_alpha_tag(args->start);
705 change_alpha(&render_priv->state.c[0], val, pwr);
706 } else
707 change_alpha(&render_priv->state.c[0],
708 _a(render_priv->state.style->PrimaryColour), 1);
709 } else if (tag("2a")) {
710 if (nargs) {
711 uint32_t val = parse_alpha_tag(args->start);
712 change_alpha(&render_priv->state.c[1], val, pwr);
713 } else
714 change_alpha(&render_priv->state.c[1],
715 _a(render_priv->state.style->SecondaryColour), 1);
716 } else if (tag("3a")) {
717 if (nargs) {
718 uint32_t val = parse_alpha_tag(args->start);
719 change_alpha(&render_priv->state.c[2], val, pwr);
720 } else
721 change_alpha(&render_priv->state.c[2],
722 _a(render_priv->state.style->OutlineColour), 1);
723 } else if (tag("4a")) {
724 if (nargs) {
725 uint32_t val = parse_alpha_tag(args->start);
726 change_alpha(&render_priv->state.c[3], val, pwr);
727 } else
728 change_alpha(&render_priv->state.c[3],
729 _a(render_priv->state.style->BackColour), 1);
730 } else if (tag("r")) {
731 if (nargs) {
732 int len = args->end - args->start;
733 reset_render_context(render_priv,
734 lookup_style_strict(render_priv->track, args->start, len));
735 } else
736 reset_render_context(render_priv, NULL);
737 } else if (tag("be")) {
738 double dval;
739 if (nargs) {
740 int val;
741 dval = argtod(*args);
742 // VSFilter always adds +0.5, even if the value is negative
743 val = (int) (render_priv->state.be * (1 - pwr) + dval * pwr + 0.5);
744 // Clamp to a safe upper limit, since high values need excessive CPU
745 val = (val < 0) ? 0 : val;
746 val = (val > MAX_BE) ? MAX_BE : val;
747 render_priv->state.be = val;
748 } else
749 render_priv->state.be = 0;
750 } else if (tag("b")) {
751 int val = argtoi(*args);
752 if (!nargs || !(val == 0 || val == 1 || val >= 100))
753 val = render_priv->state.style->Bold;
754 render_priv->state.bold = val;
755 update_font(render_priv);
756 } else if (tag("i")) {
757 int val = argtoi(*args);
758 if (!nargs || !(val == 0 || val == 1))
759 val = render_priv->state.style->Italic;
760 render_priv->state.italic = val;
761 update_font(render_priv);
762 } else if (tag("kf") || tag("K")) {
763 double val = 100;
764 if (nargs)
765 val = argtod(*args);
766 render_priv->state.effect_type = EF_KARAOKE_KF;
767 if (render_priv->state.effect_timing)
768 render_priv->state.effect_skip_timing +=
769 render_priv->state.effect_timing;
770 render_priv->state.effect_timing = val * 10;
771 } else if (tag("ko")) {
772 double val = 100;
773 if (nargs)
774 val = argtod(*args);
775 render_priv->state.effect_type = EF_KARAOKE_KO;
776 if (render_priv->state.effect_timing)
777 render_priv->state.effect_skip_timing +=
778 render_priv->state.effect_timing;
779 render_priv->state.effect_timing = val * 10;
780 } else if (tag("k")) {
781 double val = 100;
782 if (nargs)
783 val = argtod(*args);
784 render_priv->state.effect_type = EF_KARAOKE;
785 if (render_priv->state.effect_timing)
786 render_priv->state.effect_skip_timing +=
787 render_priv->state.effect_timing;
788 render_priv->state.effect_timing = val * 10;
789 } else if (tag("shad")) {
790 double val, xval, yval;
791 if (nargs) {
792 val = argtod(*args);
793 xval = render_priv->state.shadow_x * (1 - pwr) + val * pwr;
794 yval = render_priv->state.shadow_y * (1 - pwr) + val * pwr;
795 // VSFilter compatibility: clip for \shad but not for \[xy]shad
796 xval = (xval < 0) ? 0 : xval;
797 yval = (yval < 0) ? 0 : yval;
798 } else
799 xval = yval = render_priv->state.style->Shadow;
800 render_priv->state.shadow_x = xval;
801 render_priv->state.shadow_y = yval;
802 } else if (tag("s")) {
803 int val = argtoi(*args);
804 if (!nargs || !(val == 0 || val == 1))
805 val = render_priv->state.style->StrikeOut;
806 if (val)
807 render_priv->state.flags |= DECO_STRIKETHROUGH;
808 else
809 render_priv->state.flags &= ~DECO_STRIKETHROUGH;
810 } else if (tag("u")) {
811 int val = argtoi(*args);
812 if (!nargs || !(val == 0 || val == 1))
813 val = render_priv->state.style->Underline;
814 if (val)
815 render_priv->state.flags |= DECO_UNDERLINE;
816 else
817 render_priv->state.flags &= ~DECO_UNDERLINE;
818 } else if (tag("pbo")) {
819 double val = argtod(*args);
820 render_priv->state.pbo = val;
821 } else if (tag("p")) {
822 int val = argtoi(*args);
823 val = (val < 0) ? 0 : val;
824 render_priv->state.drawing_scale = val;
825 } else if (tag("q")) {
826 int val = argtoi(*args);
827 if (!nargs || !(val >= 0 && val <= 3))
828 val = render_priv->track->WrapStyle;
829 render_priv->state.wrap_style = val;
830 } else if (tag("fe")) {
831 int val;
832 if (nargs)
833 val = argtoi(*args);
834 else
835 val = render_priv->state.style->Encoding;
836 render_priv->state.font_encoding = val;
837 }
838
839 return q;
878 }
879
880 return p;
840881 }
841882
842883 void apply_transition_effects(ASS_Renderer *render_priv, ASS_Event *event)
860901 "Error parsing effect: '%s'", event->Effect);
861902 return;
862903 }
863 if (cnt >= 2 && v[1] == 0) // right-to-left
904 if (cnt >= 2 && v[1]) // left-to-right
905 render_priv->state.scroll_direction = SCROLL_LR;
906 else // right-to-left
864907 render_priv->state.scroll_direction = SCROLL_RL;
865 else // left-to-right
866 render_priv->state.scroll_direction = SCROLL_LR;
867908
868909 delay = v[0];
869910 if (delay == 0)
870911 delay = 1; // ?
871912 render_priv->state.scroll_shift =
872913 (render_priv->time - render_priv->state.event->Start) / delay;
873 render_priv->state.evt_type = EVENT_HSCROLL;
914 render_priv->state.evt_type |= EVENT_HSCROLL;
915 render_priv->state.detect_collisions = 0;
916 render_priv->state.wrap_style = 2;
874917 return;
875918 }
876919
904947 y0 = v[1];
905948 y1 = v[0];
906949 }
907 if (y1 == 0)
908 y1 = render_priv->track->PlayResY; // y0=y1=0 means fullscreen scrolling
909 render_priv->state.clip_y0 = y0;
910 render_priv->state.clip_y1 = y1;
911 render_priv->state.evt_type = EVENT_VSCROLL;
950 render_priv->state.scroll_y0 = y0;
951 render_priv->state.scroll_y1 = y1;
952 render_priv->state.evt_type |= EVENT_VSCROLL;
912953 render_priv->state.detect_collisions = 0;
913954 }
914955
927968 */
928969 void process_karaoke_effects(ASS_Renderer *render_priv)
929970 {
930 GlyphInfo *cur, *cur2;
931 GlyphInfo *s1, *e1; // start and end of the current word
932 GlyphInfo *s2; // start of the next word
933 int i;
934 int timing; // current timing
935 int tm_start, tm_end; // timings at start and end of the current word
936 int tm_current;
937 double dt;
938 int x;
939 int x_start, x_end;
940
941 tm_current = render_priv->time - render_priv->state.event->Start;
942 timing = 0;
943 s1 = s2 = 0;
944 for (i = 0; i <= render_priv->text_info.length; ++i) {
945 cur = render_priv->text_info.glyphs + i;
946 if ((i == render_priv->text_info.length)
947 || (cur->effect_type != EF_NONE)) {
948 s1 = s2;
949 s2 = cur;
950 if (s1) {
951 e1 = s2 - 1;
952 tm_start = timing + s1->effect_skip_timing;
953 tm_end = tm_start + s1->effect_timing;
954 timing = tm_end;
955 x_start = 1000000;
956 x_end = -1000000;
957 for (cur2 = s1; cur2 <= e1; ++cur2) {
958 x_start = FFMIN(x_start, d6_to_int(cur2->bbox.x_min + cur2->pos.x));
959 x_end = FFMAX(x_end, d6_to_int(cur2->bbox.x_max + cur2->pos.x));
971 long long tm_current = render_priv->time - render_priv->state.event->Start;
972
973 int timing = 0, skip_timing = 0;
974 Effect effect_type = EF_NONE;
975 GlyphInfo *last_boundary = NULL;
976 for (int i = 0; i <= render_priv->text_info.length; i++) {
977 if (i < render_priv->text_info.length &&
978 !render_priv->text_info.glyphs[i].starts_new_run) {
979 // VSFilter compatibility: if we have \k12345\k0 without a run
980 // break, subsequent text is still part of the same karaoke word,
981 // the current word's starting and ending time stay unchanged,
982 // but the starting time of the next karaoke word is advanced.
983 skip_timing += render_priv->text_info.glyphs[i].effect_skip_timing;
984 continue;
985 }
986
987 GlyphInfo *start = last_boundary;
988 GlyphInfo *end = render_priv->text_info.glyphs + i;
989 last_boundary = end;
990 if (!start)
991 continue;
992
993 if (start->effect_type != EF_NONE)
994 effect_type = start->effect_type;
995 if (effect_type == EF_NONE)
996 continue;
997
998 long long tm_start = timing + start->effect_skip_timing;
999 long long tm_end = tm_start + start->effect_timing;
1000 timing = tm_end + skip_timing;
1001 skip_timing = 0;
1002
1003 if (effect_type != EF_KARAOKE_KF)
1004 tm_end = tm_start;
1005
1006 int x;
1007 if (tm_current < tm_start)
1008 x = -100000000;
1009 else if (tm_current >= tm_end)
1010 x = 100000000;
1011 else {
1012 GlyphInfo *first_visible = start, *last_visible = end - 1;
1013 while (first_visible < last_visible && first_visible->skip)
1014 ++first_visible;
1015 while (first_visible < last_visible && last_visible->skip)
1016 --last_visible;
1017
1018 int x_start = first_visible->pos.x;
1019 int x_end = last_visible->pos.x + last_visible->advance.x;
1020 double dt = (double) (tm_current - tm_start) / (tm_end - tm_start);
1021 double frz = fmod(start->frz, 360);
1022 if (frz > 90 && frz < 270) {
1023 // Fill from right to left
1024 dt = 1 - dt;
1025 for (GlyphInfo *info = start; info < end; info++) {
1026 uint32_t tmp = info->c[0];
1027 info->c[0] = info->c[1];
1028 info->c[1] = tmp;
9601029 }
961
962 dt = (tm_current - tm_start);
963 if ((s1->effect_type == EF_KARAOKE)
964 || (s1->effect_type == EF_KARAOKE_KO)) {
965 if (dt >= 0)
966 x = x_end + 1;
967 else
968 x = x_start;
969 } else if (s1->effect_type == EF_KARAOKE_KF) {
970 dt /= (tm_end - tm_start);
971 x = x_start + (x_end - x_start) * dt;
972 } else {
973 ass_msg(render_priv->library, MSGL_ERR,
974 "Unknown effect type");
975 continue;
976 }
977
978 for (cur2 = s1; cur2 <= e1; ++cur2) {
979 cur2->effect_type = s1->effect_type;
980 cur2->effect_timing = x - d6_to_int(cur2->pos.x);
981 }
982 s1->effect = 1;
983 }
1030 }
1031 x = x_start + lrint((x_end - x_start) * dt);
1032 }
1033
1034 for (GlyphInfo *info = start; info < end; info++) {
1035 info->effect_type = effect_type;
1036 info->effect_timing = x - info->pos.x;
9841037 }
9851038 }
9861039 }
3030 void apply_transition_effects(ASS_Renderer *render_priv, ASS_Event *event);
3131 void process_karaoke_effects(ASS_Renderer *render_priv);
3232 unsigned get_next_char(ASS_Renderer *render_priv, char **str);
33 char *parse_tag(ASS_Renderer *render_priv, char *p, char *end, double pwr);
33 char *parse_tags(ASS_Renderer *render_priv, char *p, char *end, double pwr,
34 bool nested);
3435 int event_has_hard_overrides(char *str);
3536 extern void change_alpha(uint32_t *var, int32_t new, double pwr);
3637 extern uint32_t mult_alpha(uint32_t a, uint32_t b);
0 /*
1 * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
2 *
3 * This file is part of libass.
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #ifndef LIBASS_PRIV_H
19 #define LIBASS_PRIV_H
20
21 #include <stdbool.h>
22 #include <stdint.h>
23
24 #include "ass_shaper.h"
25
26 typedef enum {
27 PST_UNKNOWN = 0,
28 PST_INFO,
29 PST_STYLES,
30 PST_EVENTS,
31 PST_FONTS
32 } ParserState;
33
34 typedef enum {
35 SINFO_LANGUAGE = 1 << 0,
36 SINFO_PLAYRESX = 1 << 1,
37 SINFO_PLAYRESY = 1 << 2,
38 SINFO_TIMER = 1 << 3,
39 SINFO_WRAPSTYLE = 1 << 4,
40 SINFO_SCALEDBORDER = 1 << 5,
41 SINFO_COLOURMATRIX = 1 << 6,
42 SINFO_KERNING = 1 << 7,
43 // for legacy detection
44 GENBY_FFMPEG = 1 << 8
45 // max 32 enumerators
46 } ScriptInfo;
47
48 struct parser_priv {
49 ParserState state;
50 char *fontname;
51 char *fontdata;
52 size_t fontdata_size;
53 size_t fontdata_used;
54
55 // contains bitmap of ReadOrder IDs of all read events
56 uint32_t *read_order_bitmap;
57 int read_order_elems; // size in uint32_t units of read_order_bitmap
58 int check_readorder;
59
60 // tracks [Script Info] headers set by the script
61 uint32_t header_flags;
62
63 #ifdef USE_FRIBIDI_EX_API
64 bool bidi_brackets;
65 #endif
66 };
67
68 #endif /* LIBASS_PRIV_H */
268268 }
269269 rst->size[0] = rst->n_first;
270270
271 for (size_t i = 0; i < path->n_points; i++) {
272 if (path->points[i].x < OUTLINE_MIN || path->points[i].x > OUTLINE_MAX)
273 return false;
274 if (path->points[i].y < OUTLINE_MIN || path->points[i].y > OUTLINE_MAX)
275 return false;
276 }
271 #ifndef NDEBUG
272 for (size_t i = 0; i < path->n_points; i++)
273 assert(abs(path->points[i].x) <= OUTLINE_MAX && abs(path->points[i].y) <= OUTLINE_MAX);
274 #endif
277275
278276 ASS_Vector *start = path->points, *cur = start;
279277 for (size_t i = 0; i < path->n_segments; i++) {
2626 #include "ass_outline.h"
2727 #include "ass_render.h"
2828 #include "ass_parse.h"
29 #include "ass_priv.h"
2930 #include "ass_shaper.h"
3031
3132 #define MAX_GLYPHS_INITIAL 1024
3334 #define MAX_BITMAPS_INITIAL 16
3435 #define MAX_SUB_BITMAPS_INITIAL 64
3536 #define SUBPIXEL_MASK 63
36 #define SUBPIXEL_ACCURACY 7
37 #define STROKER_PRECISION 16 // stroker error in integer units, unrelated to final accuracy
38 #define RASTERIZER_PRECISION 16 // rasterizer spline approximation error in 1/64 pixel units
39 #define POSITION_PRECISION 8.0 // rough estimate of transform error in 1/64 pixel units
40 #define MAX_PERSP_SCALE 16.0
41 #define SUBPIXEL_ORDER 3 // ~ log2(64 / POSITION_PRECISION)
42 #define BLUR_PRECISION (1.0 / 256) // blur error as fraction of full input range
3743
3844
3945 ASS_Renderer *ass_renderer_init(ASS_Library *library)
4652 error = FT_Init_FreeType(&ft);
4753 if (error) {
4854 ass_msg(library, MSGL_FATAL, "%s failed", "FT_Init_FreeType");
49 goto ass_init_exit;
55 goto fail;
5056 }
5157
5258 FT_Library_Version(ft, &vmajor, &vminor, &vpatch);
5662 priv = calloc(1, sizeof(ASS_Renderer));
5763 if (!priv) {
5864 FT_Done_FreeType(ft);
59 goto ass_init_exit;
65 goto fail;
6066 }
6167
6268 priv->library = library;
7480 priv->engine = &ass_bitmap_engine_c;
7581 #endif
7682
77 if (!rasterizer_init(&priv->rasterizer, priv->engine->tile_order, 16)) {
78 FT_Done_FreeType(ft);
79 goto ass_init_exit;
80 }
83 if (!rasterizer_init(&priv->rasterizer, priv->engine->tile_order,
84 RASTERIZER_PRECISION))
85 goto fail;
8186
8287 priv->cache.font_cache = ass_font_cache_create();
8388 priv->cache.bitmap_cache = ass_bitmap_cache_create();
8489 priv->cache.composite_cache = ass_composite_cache_create();
8590 priv->cache.outline_cache = ass_outline_cache_create();
91 if (!priv->cache.font_cache || !priv->cache.bitmap_cache || !priv->cache.composite_cache || !priv->cache.outline_cache)
92 goto fail;
93
8694 priv->cache.glyph_max = GLYPH_CACHE_MAX;
8795 priv->cache.bitmap_max_size = BITMAP_CACHE_MAX_SIZE;
8896 priv->cache.composite_max_size = COMPOSITE_CACHE_MAX_SIZE;
94102 priv->text_info.combined_bitmaps = calloc(MAX_BITMAPS_INITIAL, sizeof(CombinedBitmapInfo));
95103 priv->text_info.glyphs = calloc(MAX_GLYPHS_INITIAL, sizeof(GlyphInfo));
96104 priv->text_info.lines = calloc(MAX_LINES_INITIAL, sizeof(LineInfo));
105 if (!priv->text_info.combined_bitmaps || !priv->text_info.glyphs || !priv->text_info.lines)
106 goto fail;
97107
98108 priv->settings.font_size_coeff = 1.;
99109 priv->settings.selective_style_overrides = ASS_OVERRIDE_BIT_SELECTIVE_FONT_SCALE;
100110
101 priv->shaper = ass_shaper_new(0);
111 if (!(priv->shaper = ass_shaper_new()))
112 goto fail;
113
102114 ass_shaper_info(library);
103 #ifdef CONFIG_HARFBUZZ
104115 priv->settings.shaper = ASS_SHAPING_COMPLEX;
105 #else
106 priv->settings.shaper = ASS_SHAPING_SIMPLE;
107 #endif
108
109 ass_init_exit:
110 if (priv)
111 ass_msg(library, MSGL_V, "Initialized");
112 else
113 ass_msg(library, MSGL_ERR, "Initialization failed");
116
117 ass_msg(library, MSGL_V, "Initialized");
114118
115119 return priv;
120
121 fail:
122 ass_msg(library, MSGL_ERR, "Initialization failed");
123 ass_renderer_done(priv);
124
125 return NULL;
116126 }
117127
118128 void ass_renderer_done(ASS_Renderer *render_priv)
119129 {
130 if (!render_priv)
131 return;
132
120133 ass_frame_unref(render_priv->images_root);
121134 ass_frame_unref(render_priv->prev_images_root);
122135
172185
173186 img->source = source;
174187 ass_cache_inc_ref(source);
188 img->buffer = source ? NULL : bitmap;
175189 img->ref_count = 0;
176190
177191 return &img->result;
185199 return x * render_priv->orig_width / render_priv->font_scale_x / render_priv->track->PlayResX +
186200 render_priv->settings.left_margin;
187201 }
188 static double x2scr(ASS_Renderer *render_priv, double x)
189 {
190 if (render_priv->state.explicit)
202 static double x2scr_left(ASS_Renderer *render_priv, double x)
203 {
204 if (render_priv->state.explicit || !render_priv->settings.use_margins)
191205 return x2scr_pos(render_priv, x);
192 return x * render_priv->orig_width_nocrop / render_priv->font_scale_x /
206 return x * render_priv->fit_width / render_priv->font_scale_x /
207 render_priv->track->PlayResX;
208 }
209 static double x2scr_right(ASS_Renderer *render_priv, double x)
210 {
211 if (render_priv->state.explicit || !render_priv->settings.use_margins)
212 return x2scr_pos(render_priv, x);
213 return x * render_priv->fit_width / render_priv->font_scale_x /
193214 render_priv->track->PlayResX +
194 FFMAX(render_priv->settings.left_margin, 0);
215 (render_priv->width - render_priv->fit_width);
195216 }
196217 static double x2scr_pos_scaled(ASS_Renderer *render_priv, double x)
197218 {
198219 return x * render_priv->orig_width / render_priv->track->PlayResX +
199220 render_priv->settings.left_margin;
200 }
201 static double x2scr_scaled(ASS_Renderer *render_priv, double x)
202 {
203 if (render_priv->state.explicit)
204 return x2scr_pos_scaled(render_priv, x);
205 return x * render_priv->orig_width_nocrop /
206 render_priv->track->PlayResX +
207 FFMAX(render_priv->settings.left_margin, 0);
208221 }
209222 /**
210223 * \brief Mapping between script and screen coordinates
216229 }
217230 static double y2scr(ASS_Renderer *render_priv, double y)
218231 {
219 if (render_priv->state.explicit)
232 if (render_priv->state.explicit || !render_priv->settings.use_margins)
220233 return y2scr_pos(render_priv, y);
221 return y * render_priv->orig_height_nocrop /
234 return y * render_priv->fit_height /
222235 render_priv->track->PlayResY +
223 FFMAX(render_priv->settings.top_margin, 0);
236 (render_priv->height - render_priv->fit_height) * 0.5;
224237 }
225238
226239 // the same for toptitles
227240 static double y2scr_top(ASS_Renderer *render_priv, double y)
228241 {
229 if (render_priv->state.explicit)
242 if (render_priv->state.explicit || !render_priv->settings.use_margins)
230243 return y2scr_pos(render_priv, y);
231 if (render_priv->settings.use_margins)
232 return y * render_priv->orig_height_nocrop /
233 render_priv->track->PlayResY;
234 else
235 return y * render_priv->orig_height_nocrop /
236 render_priv->track->PlayResY +
237 FFMAX(render_priv->settings.top_margin, 0);
244 return y * render_priv->fit_height /
245 render_priv->track->PlayResY;
238246 }
239247 // the same for subtitles
240248 static double y2scr_sub(ASS_Renderer *render_priv, double y)
241249 {
242 if (render_priv->state.explicit)
250 if (render_priv->state.explicit || !render_priv->settings.use_margins)
243251 return y2scr_pos(render_priv, y);
244 if (render_priv->settings.use_margins)
245 return y * render_priv->orig_height_nocrop /
246 render_priv->track->PlayResY +
247 FFMAX(render_priv->settings.top_margin, 0)
248 + FFMAX(render_priv->settings.bottom_margin, 0);
249 else
250 return y * render_priv->orig_height_nocrop /
251 render_priv->track->PlayResY +
252 FFMAX(render_priv->settings.top_margin, 0);
252 return y * render_priv->fit_height /
253 render_priv->track->PlayResY +
254 (render_priv->height - render_priv->fit_height);
253255 }
254256
255257 /*
277279
278280 dst_x += bm->left;
279281 dst_y += bm->top;
282 brk -= dst_x;
280283
281284 // we still need to clip against screen boundaries
282285 zx = x2scr_pos_scaled(render_priv, 0);
378381 return render_glyph_i(render_priv, bm, dst_x, dst_y, color, color2,
379382 brk, tail, type, source);
380383
381 // brk is relative to dst_x
384 // brk is absolute
382385 // color = color left of brk
383386 // color2 = color right of brk
384387 int b_x0, b_y0, b_x1, b_y1; // visible part of the bitmap
388391
389392 dst_x += bm->left;
390393 dst_y += bm->top;
391 brk -= bm->left;
394 brk -= dst_x;
392395
393396 // clipping
394397 clip_x0 = FFMINMAX(render_priv->state.clip_x0, 0, render_priv->width);
441444 return tail;
442445 }
443446
447 static bool quantize_transform(double m[3][3], ASS_Vector *pos,
448 ASS_DVector *offset, bool first,
449 BitmapHashKey *key)
450 {
451 // Full transform:
452 // x_out = (m_xx * x + m_xy * y + m_xz) / z,
453 // y_out = (m_yx * x + m_yy * y + m_yz) / z,
454 // z = m_zx * x + m_zy * y + m_zz.
455
456 const double max_val = 1000000;
457
458 const ASS_Rect *bbox = &key->outline->cbox;
459 double x0 = (bbox->x_min + bbox->x_max) / 2.0;
460 double y0 = (bbox->y_min + bbox->y_max) / 2.0;
461 double dx = (bbox->x_max - bbox->x_min) / 2.0 + 64;
462 double dy = (bbox->y_max - bbox->y_min) / 2.0 + 64;
463
464 // Change input coordinates' origin to (x0, y0),
465 // after that transformation x:[-dx, dx], y:[-dy, dy],
466 // max|x| = dx and max|y| = dy.
467 for (int i = 0; i < 3; i++)
468 m[i][2] += m[i][0] * x0 + m[i][1] * y0;
469
470 if (m[2][2] <= 0)
471 return false;
472
473 double w = 1 / m[2][2];
474 // Transformed center of bounding box
475 double center[2] = { m[0][2] * w, m[1][2] * w };
476 // Change output coordinates' origin to center,
477 // m_xz and m_yz is skipped as it becomes 0 and no longer needed.
478 for (int i = 0; i < 2; i++)
479 for (int j = 0; j < 2; j++)
480 m[i][j] -= m[2][j] * center[i];
481
482 double delta[2] = {0};
483 if (!first) {
484 delta[0] = offset->x;
485 delta[1] = offset->y;
486 }
487
488 int32_t qr[2]; // quantized center position
489 for (int i = 0; i < 2; i++) {
490 center[i] /= 64 >> SUBPIXEL_ORDER;
491 center[i] -= delta[i];
492 if (!(fabs(center[i]) < max_val))
493 return false;
494 qr[i] = lrint(center[i]);
495 }
496
497 // Minimal bounding box z coordinate
498 double z0 = m[2][2] - fabs(m[2][0]) * dx - fabs(m[2][1]) * dy;
499 // z0 clamped to z_center / MAX_PERSP_SCALE to mitigate problems with small z
500 w = 1.0 / POSITION_PRECISION / FFMAX(z0, m[2][2] / MAX_PERSP_SCALE);
501 double mul[2] = { dx * w, dy * w }; // 1 / q_x, 1 / q_y
502
503 // z0 = m_zz - |m_zx| * dx - |m_zy| * dy,
504 // m_zz = z0 + |m_zx| * dx + |m_zy| * dy,
505 // z = m_zx * x + m_zy * y + m_zz
506 // = m_zx * (x + sign(m_zx) * dx) + m_zy * (y + sign(m_zy) * dy) + z0.
507
508 // D(f)--absolute value of error in quantity f
509 // as function of error in matrix coefficients, i. e. D(m_??).
510 // Error in constant is zero, i. e. D(dx) = D(dy) = D(z0) = 0.
511 // In the following calculation errors are considered small
512 // and second- and higher-order terms are dropped.
513 // That approximation is valid as long as glyph dimensions are larger than couple of pixels.
514 // Therefore standard relations for derivatives can be used for D(?):
515 // D(A * B) <= D(A) * max|B| + max|A| * D(B),
516 // D(1 / C) <= D(C) * max|1 / C^2|.
517
518 // D(x_out) = D((m_xx * x + m_xy * y) / z)
519 // <= D(m_xx * x + m_xy * y) * max|1 / z| + max|m_xx * x + m_xy * y| * D(1 / z)
520 // <= (D(m_xx) * dx + D(m_xy) * dy) / z0 + (|m_xx| * dx + |m_xy| * dy) * D(z) / z0^2,
521 // D(y_out) = D((m_yx * x + m_yy * y) / z)
522 // <= D(m_yx * x + m_yy * y) * max|1 / z| + max|m_yx * x + m_yy * y| * D(1 / z)
523 // <= (D(m_yx) * dx + D(m_yy) * dy) / z0 + (|m_yx| * dx + |m_yy| * dy) * D(z) / z0^2,
524 // |m_xx| * dx + |m_xy| * dy = x_lim,
525 // |m_yx| * dx + |m_yy| * dy = y_lim,
526 // D(z) <= 2 * (D(m_zx) * dx + D(m_zy) * dy),
527 // D(x_out) <= (D(m_xx) * dx + D(m_xy) * dy) / z0
528 // + 2 * (D(m_zx) * dx + D(m_zy) * dy) * x_lim / z0^2,
529 // D(y_out) <= (D(m_yx) * dx + D(m_yy) * dy) / z0
530 // + 2 * (D(m_zx) * dx + D(m_zy) * dy) * y_lim / z0^2.
531
532 // To estimate acceptable error in matrix coefficient
533 // set error in all other coefficients to zero and solve system
534 // D(x_out) <= ACCURACY & D(y_out) <= ACCURACY for desired D(m_??).
535 // ACCURACY here is some part of total error, i. e. ACCURACY ~ POSITION_PRECISION.
536 // Note that POSITION_PRECISION isn't total error, it's convenient constant.
537 // True error can be up to several POSITION_PRECISION.
538
539 // Quantization steps (ACCURACY ~ POSITION_PRECISION):
540 // D(m_xx), D(m_yx) ~ q_x = POSITION_PRECISION * z0 / dx,
541 // D(m_xy), D(m_yy) ~ q_y = POSITION_PRECISION * z0 / dy,
542 // qm_xx = round(m_xx / q_x), qm_xy = round(m_xy / q_y),
543 // qm_yx = round(m_yx / q_x), qm_yy = round(m_yy / q_y).
544
545 int32_t qm[3][2];
546 for (int i = 0; i < 2; i++)
547 for (int j = 0; j < 2; j++) {
548 double val = m[i][j] * mul[j];
549 if (!(fabs(val) < max_val))
550 return false;
551 qm[i][j] = lrint(val);
552 }
553
554 // x_lim = |m_xx| * dx + |m_xy| * dy
555 // ~= |qm_xx| * q_x * dx + |qm_xy| * q_y * dy
556 // = (|qm_xx| + |qm_xy|) * POSITION_PRECISION * z0,
557 // y_lim = |m_yx| * dx + |m_yy| * dy
558 // ~= |qm_yx| * q_x * dx + |qm_yy| * q_y * dy
559 // = (|qm_yx| + |qm_yy|) * POSITION_PRECISION * z0,
560 // max(x_lim, y_lim) / z0 ~= w
561 // = max(|qm_xx| + |qm_xy|, |qm_yx| + |qm_yy|) * POSITION_PRECISION.
562
563 // Quantization steps (ACCURACY ~ 2 * POSITION_PRECISION):
564 // D(m_zx) ~ POSITION_PRECISION * z0^2 / max(x_lim, y_lim) / dx ~= q_zx = q_x / w,
565 // D(m_zy) ~ POSITION_PRECISION * z0^2 / max(x_lim, y_lim) / dy ~= q_zy = q_y / w,
566 // qm_zx = round(m_zx / q_zx), qm_zy = round(m_zy / q_zy).
567
568 int32_t qmx = abs(qm[0][0]) + abs(qm[0][1]);
569 int32_t qmy = abs(qm[1][0]) + abs(qm[1][1]);
570 w = POSITION_PRECISION * FFMAX(qmx, qmy);
571 mul[0] *= w;
572 mul[1] *= w;
573
574 for (int j = 0; j < 2; j++) {
575 double val = m[2][j] * mul[j];
576 if (!(fabs(val) < max_val))
577 return false;
578 qm[2][j] = lrint(val);
579 }
580
581 if (first && offset) {
582 offset->x = center[0] - qr[0];
583 offset->y = center[1] - qr[1];
584 }
585 pos->x = qr[0] >> SUBPIXEL_ORDER;
586 pos->y = qr[1] >> SUBPIXEL_ORDER;
587 key->offset.x = qr[0] & ((1 << SUBPIXEL_ORDER) - 1);
588 key->offset.y = qr[1] & ((1 << SUBPIXEL_ORDER) - 1);
589 key->matrix_x.x = qm[0][0]; key->matrix_x.y = qm[0][1];
590 key->matrix_y.x = qm[1][0]; key->matrix_y.y = qm[1][1];
591 key->matrix_z.x = qm[2][0]; key->matrix_z.y = qm[2][1];
592 return true;
593 }
594
595 static void restore_transform(double m[3][3], const BitmapHashKey *key)
596 {
597 const ASS_Rect *bbox = &key->outline->cbox;
598 double x0 = (bbox->x_min + bbox->x_max) / 2.0;
599 double y0 = (bbox->y_min + bbox->y_max) / 2.0;
600 double dx = (bbox->x_max - bbox->x_min) / 2.0 + 64;
601 double dy = (bbox->y_max - bbox->y_min) / 2.0 + 64;
602
603 // Arbitrary scale has chosen so that z0 = 1
604 double q_x = POSITION_PRECISION / dx;
605 double q_y = POSITION_PRECISION / dy;
606 m[0][0] = key->matrix_x.x * q_x;
607 m[0][1] = key->matrix_x.y * q_y;
608 m[1][0] = key->matrix_y.x * q_x;
609 m[1][1] = key->matrix_y.y * q_y;
610
611 int32_t qmx = abs(key->matrix_x.x) + abs(key->matrix_x.y);
612 int32_t qmy = abs(key->matrix_y.x) + abs(key->matrix_y.y);
613 double scale_z = 1.0 / POSITION_PRECISION / FFMAX(qmx, qmy);
614 m[2][0] = key->matrix_z.x * q_x * scale_z; // qm_zx * q_zx
615 m[2][1] = key->matrix_z.y * q_y * scale_z; // qm_zy * q_zy
616
617 m[0][2] = m[1][2] = 0;
618 m[2][2] = 1 + fabs(m[2][0]) * dx + fabs(m[2][1]) * dy;
619 m[2][2] = FFMIN(m[2][2], MAX_PERSP_SCALE);
620
621 double center[2] = {
622 key->offset.x * (64 >> SUBPIXEL_ORDER),
623 key->offset.y * (64 >> SUBPIXEL_ORDER),
624 };
625 for (int i = 0; i < 2; i++)
626 for (int j = 0; j < 3; j++)
627 m[i][j] += m[2][j] * center[i];
628
629 for (int i = 0; i < 3; i++)
630 m[i][2] -= m[i][0] * x0 + m[i][1] * y0;
631 }
632
444633 // Calculate bitmap memory footprint
445 static inline size_t bitmap_size(Bitmap *bm)
446 {
447 return bm ? sizeof(Bitmap) + bm->stride * bm->h : 0;
634 static inline size_t bitmap_size(const Bitmap *bm)
635 {
636 return bm->stride * bm->h;
448637 }
449638
450639 /**
452641 * applicable. The blended bitmaps are added to a free list which is freed
453642 * at the start of a new frame.
454643 */
455 static void blend_vector_clip(ASS_Renderer *render_priv,
456 ASS_Image *head)
457 {
458 ASS_Drawing *drawing = render_priv->state.clip_drawing;
459 if (!drawing)
644 static void blend_vector_clip(ASS_Renderer *render_priv, ASS_Image *head)
645 {
646 if (!render_priv->state.clip_drawing_text)
460647 return;
461648
462 // Try to get mask from cache
649 OutlineHashKey ol_key;
650 ol_key.type = OUTLINE_DRAWING;
651 ol_key.u.drawing.text = render_priv->state.clip_drawing_text;
652
653 double m[3][3] = {{0}};
654 double w = render_priv->font_scale / (1 << (render_priv->state.clip_drawing_scale - 1));
655 m[0][0] = render_priv->font_scale_x * w;
656 m[1][1] = w;
657 m[2][2] = 1;
658
659 m[0][2] = int_to_d6(render_priv->settings.left_margin);
660 m[1][2] = int_to_d6(render_priv->settings.top_margin);
661
662 ASS_Vector pos;
463663 BitmapHashKey key;
464 memset(&key, 0, sizeof(key));
465 key.type = BITMAP_CLIP;
466 key.u.clip.text = drawing->text;
467
468 BitmapHashValue *val;
469 if (!ass_cache_get(render_priv->cache.bitmap_cache, &key, &val)) {
470 if (!val)
471 return;
472 val->bm = val->bm_o = NULL;
473
474 // Not found in cache, parse and rasterize it
475 ASS_Outline *outline = ass_drawing_parse(drawing, true);
476 if (!outline) {
477 ass_msg(render_priv->library, MSGL_WARN,
478 "Clip vector parsing failed. Skipping.");
479 ass_cache_commit(val, sizeof(BitmapHashKey) + sizeof(BitmapHashValue));
480 ass_cache_dec_ref(val);
481 return;
482 }
483
484 // We need to translate the clip according to screen borders
485 if (render_priv->settings.left_margin != 0 ||
486 render_priv->settings.top_margin != 0) {
487 ASS_Vector trans = {
488 .x = int_to_d6(render_priv->settings.left_margin),
489 .y = int_to_d6(render_priv->settings.top_margin),
490 };
491 outline_translate(outline, trans.x, trans.y);
492 }
493
494 val->bm = outline_to_bitmap(render_priv, outline, NULL, 1);
495 ass_cache_commit(val, bitmap_size(val->bm) +
496 sizeof(BitmapHashKey) + sizeof(BitmapHashValue));
497 }
498
499 Bitmap *clip_bm = val->bm;
500 if (!clip_bm) {
501 ass_cache_dec_ref(val);
664 key.outline = ass_cache_get(render_priv->cache.outline_cache, &ol_key, render_priv);
665 if (!key.outline || !key.outline->valid ||
666 !quantize_transform(m, &pos, NULL, true, &key)) {
667 ass_cache_dec_ref(key.outline);
668 return;
669 }
670 Bitmap *clip_bm = ass_cache_get(render_priv->cache.bitmap_cache, &key, render_priv);
671 if (!clip_bm || !clip_bm->buffer) {
672 ass_cache_dec_ref(clip_bm);
502673 return;
503674 }
504675
517688 aw = cur->w;
518689 ah = cur->h;
519690 as = cur->stride;
520 bx = clip_bm->left;
521 by = clip_bm->top;
691 bx = pos.x + clip_bm->left;
692 by = pos.y + clip_bm->top;
522693 bw = clip_bm->w;
523694 bh = clip_bm->h;
524695 bs = clip_bm->stride;
579750 cur->stride = ns;
580751 }
581752
582 cur->bitmap = nbuffer;
583753 ASS_ImagePriv *priv = (ASS_ImagePriv *) cur;
754 priv->buffer = cur->bitmap = nbuffer;
584755 ass_cache_dec_ref(priv->source);
585756 priv->source = NULL;
586757 }
587758
588 ass_cache_dec_ref(val);
759 ass_cache_dec_ref(clip_bm);
589760 }
590761
591762 /**
615786 continue;
616787
617788 if ((info->effect_type == EF_KARAOKE_KO)
618 && (info->effect_timing <= info->first_pos_x)) {
789 && (info->effect_timing <= 0)) {
619790 // do nothing
620791 } else {
621792 tail =
631802
632803 if ((info->effect_type == EF_KARAOKE)
633804 || (info->effect_type == EF_KARAOKE_KO)) {
634 if (info->effect_timing > info->first_pos_x)
805 if (info->effect_timing > 0)
635806 tail =
636807 render_glyph(render_priv, info->bm, info->x, info->y,
637808 info->c[0], 0, 1000000, tail,
690861 // The user style was set with ass_set_selective_style_override().
691862 ASS_Style *user = &render_priv->user_override_style;
692863 ASS_Style *new = &render_priv->state.override_style_temp_storage;
693 int explicit = event_has_hard_overrides(render_priv->state.event->Text) ||
694 render_priv->state.evt_type != EVENT_NORMAL;
864 int explicit = render_priv->state.explicit;
695865 int requested = render_priv->settings.selective_style_overrides;
696866 double scale;
697867
705875 // user_style (the user's override style). Copy only fields from the
706876 // script's style that are deemed necessary.
707877 *new = *rstyle;
708
709 render_priv->state.explicit = explicit;
710878
711879 render_priv->state.apply_font_scale =
712880 !explicit || !(requested & ASS_OVERRIDE_BIT_SELECTIVE_FONT_SCALE);
788956 {
789957 ASS_Settings *settings_priv = &render_priv->settings;
790958
791 render_priv->font_scale = ((double) render_priv->orig_height) /
792 render_priv->track->PlayResY;
959 double font_scr_h = render_priv->orig_height;
960 if (!render_priv->state.explicit && render_priv->settings.use_margins)
961 font_scr_h = render_priv->fit_height;
962
963 render_priv->font_scale = font_scr_h / render_priv->track->PlayResY;
793964 if (settings_priv->storage_height)
794 render_priv->blur_scale = ((double) render_priv->orig_height) /
795 settings_priv->storage_height;
965 render_priv->blur_scale = font_scr_h / settings_priv->storage_height;
796966 else
797 render_priv->blur_scale = 1.;
967 render_priv->blur_scale = font_scr_h / render_priv->track->PlayResY;
798968 if (render_priv->track->ScaledBorderAndShadow)
799969 render_priv->border_scale =
800 ((double) render_priv->orig_height) /
801 render_priv->track->PlayResY;
970 font_scr_h / render_priv->track->PlayResY;
802971 else
803972 render_priv->border_scale = render_priv->blur_scale;
804 if (!settings_priv->storage_height)
805 render_priv->blur_scale = render_priv->border_scale;
806973
807974 if (render_priv->state.apply_font_scale) {
808975 render_priv->font_scale *= settings_priv->font_size_coeff;
830997 (style->StrikeOut ? DECO_STRIKETHROUGH : 0);
831998 render_priv->state.font_size = style->FontSize;
832999
833 free(render_priv->state.family);
834 render_priv->state.family = NULL;
835 render_priv->state.family = strdup(style->FontName);
836 render_priv->state.treat_family_as_pattern =
837 style->treat_fontname_as_pattern;
1000 char* new_family = strdup(style->FontName);
1001 if (new_family) {
1002 free(render_priv->state.family);
1003 render_priv->state.family = new_family;
1004 render_priv->state.treat_family_as_pattern =
1005 style->treat_fontname_as_pattern;
1006 }
8381007 render_priv->state.bold = style->Bold;
8391008 render_priv->state.italic = style->Italic;
8401009 update_font(render_priv);
8501019 render_priv->state.shadow_x = style->Shadow;
8511020 render_priv->state.shadow_y = style->Shadow;
8521021 render_priv->state.frx = render_priv->state.fry = 0.;
853 render_priv->state.frz = M_PI * style->Angle / 180.;
1022 render_priv->state.frz = style->Angle;
8541023 render_priv->state.fax = render_priv->state.fay = 0.;
8551024 render_priv->state.font_encoding = style->Encoding;
8561025 }
8651034 render_priv->state.parsed_tags = 0;
8661035 render_priv->state.evt_type = EVENT_NORMAL;
8671036
868 reset_render_context(render_priv, NULL);
8691037 render_priv->state.wrap_style = render_priv->track->WrapStyle;
8701038
871 render_priv->state.alignment = render_priv->state.style->Alignment;
872 render_priv->state.justify = render_priv->state.style->Justify;
8731039 render_priv->state.pos_x = 0;
8741040 render_priv->state.pos_y = 0;
8751041 render_priv->state.org_x = 0;
8891055 render_priv->state.effect_skip_timing = 0;
8901056
8911057 apply_transition_effects(render_priv, event);
1058 render_priv->state.explicit = render_priv->state.evt_type != EVENT_NORMAL ||
1059 event_has_hard_overrides(event->Text);
1060
1061 reset_render_context(render_priv, NULL);
1062 render_priv->state.alignment = render_priv->state.style->Alignment;
1063 render_priv->state.justify = render_priv->state.style->Justify;
8921064 }
8931065
8941066 static void free_render_context(ASS_Renderer *render_priv)
8951067 {
8961068 ass_cache_dec_ref(render_priv->state.font);
8971069 free(render_priv->state.family);
898 ass_drawing_free(render_priv->state.clip_drawing);
1070 free(render_priv->state.clip_drawing_text);
8991071
9001072 render_priv->state.font = NULL;
9011073 render_priv->state.family = NULL;
902 render_priv->state.clip_drawing = NULL;
1074 render_priv->state.clip_drawing_text = NULL;
9031075
9041076 TextInfo *text_info = &render_priv->text_info;
9051077 for (int n = 0; n < text_info->length; n++)
906 ass_drawing_free(text_info->glyphs[n].drawing);
1078 free(text_info->glyphs[n].drawing_text);
9071079 text_info->length = 0;
908 }
909
910 /*
911 * Replace the outline of a glyph by a contour which makes up a simple
912 * opaque rectangle.
913 */
914 static void draw_opaque_box(ASS_Renderer *render_priv, GlyphInfo *info,
915 int asc, int desc, ASS_Outline *ol,
916 ASS_Vector advance, int sx, int sy)
917 {
918 int adv = advance.x;
919 double scale_y = info->orig_scale_y;
920 double scale_x = info->orig_scale_x;
921
922 // to avoid gaps
923 sx = FFMAX(64, sx);
924 sy = FFMAX(64, sy);
925
926 // Emulate the WTFish behavior of VSFilter, i.e. double-scale
927 // the sizes of the opaque box.
928 adv += double_to_d6(info->hspacing * render_priv->font_scale * scale_x);
929 adv *= scale_x;
930 sx *= scale_x;
931 sy *= scale_y;
932 desc *= scale_y;
933 desc += asc * (scale_y - 1.0);
934
935 ASS_Vector points[4] = {
936 { .x = -sx, .y = -asc - sy },
937 { .x = adv + sx, .y = -asc - sy },
938 { .x = adv + sx, .y = desc + sy },
939 { .x = -sx, .y = desc + sy },
940 };
941
942 const char segments[4] = {
943 OUTLINE_LINE_SEGMENT,
944 OUTLINE_LINE_SEGMENT,
945 OUTLINE_LINE_SEGMENT,
946 OUTLINE_LINE_SEGMENT | OUTLINE_CONTOUR_END
947 };
948
949 ol->n_points = ol->n_segments = 0;
950 if (!outline_alloc(ol, 4, 4))
951 return;
952 for (int i = 0; i < 4; i++) {
953 ol->points[ol->n_points++] = points[i];
954 ol->segments[ol->n_segments++] = segments[i];
955 }
956 }
957
958 /**
959 * \brief Prepare glyph hash
960 */
961 static void
962 fill_glyph_hash(ASS_Renderer *priv, OutlineHashKey *outline_key,
963 GlyphInfo *info)
964 {
965 if (info->drawing) {
966 DrawingHashKey *key = &outline_key->u.drawing;
967 outline_key->type = OUTLINE_DRAWING;
968 key->scale_x = double_to_d16(info->scale_x);
969 key->scale_y = double_to_d16(info->scale_y);
970 key->outline.x = double_to_d6(info->border_x * priv->border_scale);
971 key->outline.y = double_to_d6(info->border_y * priv->border_scale);
972 key->border_style = info->border_style;
973 // hpacing only matters for opaque box borders (see draw_opaque_box),
974 // so for normal borders, maximize cache utility by ignoring it
975 key->hspacing =
976 info->border_style == 3 ? double_to_d16(info->hspacing) : 0;
977 key->hash = info->drawing->hash;
978 key->text = info->drawing->text;
979 key->pbo = info->drawing->pbo;
980 key->scale = info->drawing->scale;
981 } else {
982 GlyphHashKey *key = &outline_key->u.glyph;
983 outline_key->type = OUTLINE_GLYPH;
984 key->font = info->font;
985 key->size = info->font_size;
986 key->face_index = info->face_index;
987 key->glyph_index = info->glyph_index;
988 key->bold = info->bold;
989 key->italic = info->italic;
990 key->scale_x = double_to_d16(info->scale_x);
991 key->scale_y = double_to_d16(info->scale_y);
992 key->outline.x = double_to_d6(info->border_x * priv->border_scale);
993 key->outline.y = double_to_d6(info->border_y * priv->border_scale);
994 key->flags = info->flags;
995 key->border_style = info->border_style;
996 key->hspacing =
997 info->border_style == 3 ? double_to_d16(info->hspacing) : 0;
998 }
999 }
1000
1001 /**
1002 * \brief Prepare combined-bitmap hash
1003 */
1004 static void fill_composite_hash(CompositeHashKey *hk, CombinedBitmapInfo *info)
1005 {
1006 hk->filter = info->filter;
1007 hk->bitmap_count = info->bitmap_count;
1008 hk->bitmaps = info->bitmaps;
10091080 }
10101081
10111082 /**
10141085 * Tries to get both glyphs from cache.
10151086 * If they can't be found, gets a glyph from font face, generates outline,
10161087 * and add them to cache.
1017 * The glyphs are returned in info->glyph and info->outline_glyph
10181088 */
10191089 static void
10201090 get_outline_glyph(ASS_Renderer *priv, GlyphInfo *info)
10211091 {
1022 memset(&info->hash_key, 0, sizeof(info->hash_key));
1023
1092 OutlineHashValue *val;
1093 ASS_DVector scale, offset = {0};
1094
1095 int32_t asc, desc;
10241096 OutlineHashKey key;
1025 OutlineHashValue *val;
1026 fill_glyph_hash(priv, &key, info);
1027 if (!ass_cache_get(priv->cache.outline_cache, &key, &val)) {
1028 if (!val)
1097 if (info->drawing_text) {
1098 key.type = OUTLINE_DRAWING;
1099 key.u.drawing.text = info->drawing_text;
1100 val = ass_cache_get(priv->cache.outline_cache, &key, priv);
1101 if (!val || !val->valid) {
1102 ass_cache_dec_ref(val);
10291103 return;
1030 memset(val, 0, sizeof(*val));
1031
1032 if (info->drawing) {
1033 ASS_Drawing *drawing = info->drawing;
1034 ass_drawing_hash(drawing);
1035 if(!ass_drawing_parse(drawing, false) ||
1036 !outline_copy(&val->outline, &drawing->outline)) {
1037 ass_cache_commit(val, 1);
1038 ass_cache_dec_ref(val);
1039 return;
1040 }
1041 val->advance.x = drawing->advance.x;
1042 val->advance.y = drawing->advance.y;
1043 val->asc = drawing->asc;
1044 val->desc = drawing->desc;
1045 } else {
1046 ass_face_set_size(info->font->faces[info->face_index],
1047 info->font_size);
1048 ass_font_set_transform(info->font, info->scale_x,
1049 info->scale_y, NULL);
1104 }
1105
1106 double w = priv->font_scale / (1 << (info->drawing_scale - 1));
1107 scale.x = info->scale_x * w;
1108 scale.y = info->scale_y * w;
1109 desc = 64 * info->drawing_pbo;
1110 asc = val->asc - desc;
1111
1112 offset.y = -asc * scale.y;
1113 } else {
1114 key.type = OUTLINE_GLYPH;
1115 GlyphHashKey *k = &key.u.glyph;
1116 k->font = info->font;
1117 k->size = info->font_size;
1118 k->face_index = info->face_index;
1119 k->glyph_index = info->glyph_index;
1120 k->bold = info->bold;
1121 k->italic = info->italic;
1122 k->flags = info->flags;
1123
1124 val = ass_cache_get(priv->cache.outline_cache, &key, priv);
1125 if (!val || !val->valid) {
1126 ass_cache_dec_ref(val);
1127 return;
1128 }
1129
1130 scale.x = info->scale_x;
1131 scale.y = info->scale_y;
1132 asc = val->asc;
1133 desc = val->desc;
1134 }
1135
1136 info->outline = val;
1137 info->transform.scale = scale;
1138 info->transform.offset = offset;
1139
1140 info->bbox.x_min = lrint(val->cbox.x_min * scale.x + offset.x);
1141 info->bbox.y_min = lrint(val->cbox.y_min * scale.y + offset.y);
1142 info->bbox.x_max = lrint(val->cbox.x_max * scale.x + offset.x);
1143 info->bbox.y_max = lrint(val->cbox.y_max * scale.y + offset.y);
1144
1145 if (info->drawing_text || priv->settings.shaper == ASS_SHAPING_SIMPLE) {
1146 info->cluster_advance.x = info->advance.x = lrint(val->advance * scale.x);
1147 info->cluster_advance.y = info->advance.y = 0;
1148 }
1149 info->asc = lrint(asc * scale.y);
1150 info->desc = lrint(desc * scale.y);
1151 }
1152
1153 size_t ass_outline_construct(void *key, void *value, void *priv)
1154 {
1155 ASS_Renderer *render_priv = priv;
1156 OutlineHashKey *outline_key = key;
1157 OutlineHashValue *v = value;
1158 memset(v, 0, sizeof(*v));
1159
1160 switch (outline_key->type) {
1161 case OUTLINE_GLYPH:
1162 {
1163 GlyphHashKey *k = &outline_key->u.glyph;
1164 ass_face_set_size(k->font->faces[k->face_index], k->size);
10501165 FT_Glyph glyph =
1051 ass_font_get_glyph(info->font,
1052 info->symbol, info->face_index, info->glyph_index,
1053 priv->settings.hinting, info->flags);
1166 ass_font_get_glyph(k->font, k->face_index, k->glyph_index,
1167 render_priv->settings.hinting, k->flags);
10541168 if (glyph != NULL) {
10551169 FT_Outline *src = &((FT_OutlineGlyph) glyph)->outline;
1056 if (!outline_convert(&val->outline, src)) {
1057 ass_cache_commit(val, 1);
1058 ass_cache_dec_ref(val);
1059 return;
1060 }
1061 if (priv->settings.shaper == ASS_SHAPING_SIMPLE) {
1062 val->advance.x = d16_to_d6(glyph->advance.x);
1063 val->advance.y = d16_to_d6(glyph->advance.y);
1064 }
1170 if (!outline_convert(&v->outline[0], src))
1171 return 1;
1172 v->advance = d16_to_d6(glyph->advance.x);
10651173 FT_Done_Glyph(glyph);
1066 ass_font_get_asc_desc(info->font, info->symbol,
1067 &val->asc, &val->desc);
1068 val->asc *= info->scale_y;
1069 val->desc *= info->scale_y;
1174 ass_font_get_asc_desc(k->font, k->face_index,
1175 &v->asc, &v->desc);
10701176 }
1071 }
1072 val->valid = true;
1073
1074 outline_get_cbox(&val->outline, &val->bbox_scaled);
1075
1076 if (info->border_style == 3) {
1077 ASS_Vector advance;
1078 if (priv->settings.shaper == ASS_SHAPING_SIMPLE || info->drawing)
1079 advance = val->advance;
1080 else
1081 advance = info->advance;
1082
1083 draw_opaque_box(priv, info, val->asc, val->desc, &val->border[0], advance,
1084 double_to_d6(info->border_x * priv->border_scale),
1085 double_to_d6(info->border_y * priv->border_scale));
1086
1087 } else if (val->outline.n_points && (info->border_x > 0 || info->border_y > 0)
1088 && double_to_d6(info->scale_x) && double_to_d6(info->scale_y)) {
1089 const int eps = 16;
1090 int xbord = double_to_d6(info->border_x * priv->border_scale);
1091 int ybord = double_to_d6(info->border_y * priv->border_scale);
1092 if(xbord >= eps || ybord >= eps) {
1093 outline_alloc(&val->border[0], 2 * val->outline.n_points, 2 * val->outline.n_segments);
1094 outline_alloc(&val->border[1], 2 * val->outline.n_points, 2 * val->outline.n_segments);
1095 if (!val->border[0].max_points || !val->border[1].max_points ||
1096 !outline_stroke(&val->border[0], &val->border[1],
1097 &val->outline, xbord, ybord, eps)) {
1098 ass_msg(priv->library, MSGL_WARN, "Cannot stoke outline");
1099 outline_free(&val->border[0]);
1100 outline_free(&val->border[1]);
1101 }
1177 break;
1178 }
1179 case OUTLINE_DRAWING:
1180 {
1181 ASS_Rect bbox;
1182 const char *text = outline_key->u.drawing.text;
1183 if (!ass_drawing_parse(&v->outline[0], &bbox, text, render_priv->library))
1184 return 1;
1185
1186 v->advance = bbox.x_max - bbox.x_min;
1187 v->asc = bbox.y_max - bbox.y_min;
1188 v->desc = 0;
1189 break;
1190 }
1191 case OUTLINE_BORDER:
1192 {
1193 BorderHashKey *k = &outline_key->u.border;
1194 if (!k->border.x && !k->border.y)
1195 break;
1196 if (!k->outline->outline[0].n_points)
1197 break;
1198
1199 ASS_Outline src;
1200 if (!outline_scale_pow2(&src, &k->outline->outline[0],
1201 k->scale_ord_x, k->scale_ord_y))
1202 return 1;
1203 if (!outline_stroke(&v->outline[0], &v->outline[1], &src,
1204 k->border.x * STROKER_PRECISION,
1205 k->border.y * STROKER_PRECISION,
1206 STROKER_PRECISION)) {
1207 ass_msg(render_priv->library, MSGL_WARN, "Cannot stroke outline");
1208 outline_free(&v->outline[0]);
1209 outline_free(&v->outline[1]);
1210 outline_free(&src);
1211 return 1;
11021212 }
1103 }
1104
1105 ass_cache_commit(val, 1);
1106 } else if (!val->valid) {
1107 ass_cache_dec_ref(val);
1108 return;
1109 }
1110
1111 info->hash_key.u.outline.outline = val;
1112 info->outline = &val->outline;
1113 info->border[0] = &val->border[0];
1114 info->border[1] = &val->border[1];
1115 info->bbox = val->bbox_scaled;
1116 if (info->drawing || priv->settings.shaper == ASS_SHAPING_SIMPLE) {
1117 info->cluster_advance.x = info->advance.x = val->advance.x;
1118 info->cluster_advance.y = info->advance.y = val->advance.y;
1119 }
1120 info->asc = val->asc;
1121 info->desc = val->desc;
1213 outline_free(&src);
1214 break;
1215 }
1216 case OUTLINE_BOX:
1217 {
1218 ASS_Outline *ol = &v->outline[0];
1219 if (!outline_alloc(ol, 4, 4))
1220 return 1;
1221 ol->points[0].x = ol->points[3].x = 0;
1222 ol->points[1].x = ol->points[2].x = 64;
1223 ol->points[0].y = ol->points[1].y = 0;
1224 ol->points[2].y = ol->points[3].y = 64;
1225 ol->segments[0] = OUTLINE_LINE_SEGMENT;
1226 ol->segments[1] = OUTLINE_LINE_SEGMENT;
1227 ol->segments[2] = OUTLINE_LINE_SEGMENT;
1228 ol->segments[3] = OUTLINE_LINE_SEGMENT | OUTLINE_CONTOUR_END;
1229 ol->n_points = ol->n_segments = 4;
1230 break;
1231 }
1232 default:
1233 return 1;
1234 }
1235
1236 rectangle_reset(&v->cbox);
1237 outline_update_cbox(&v->outline[0], &v->cbox);
1238 outline_update_cbox(&v->outline[1], &v->cbox);
1239 if (v->cbox.x_min > v->cbox.x_max || v->cbox.y_min > v->cbox.y_max)
1240 v->cbox.x_min = v->cbox.y_min = v->cbox.x_max = v->cbox.y_max = 0;
1241 v->valid = true;
1242 return 1;
11221243 }
11231244
11241245 /**
1125 * \brief Calculate transform matrix for transform_3d()
1246 * \brief Calculate outline transformation matrix
11261247 */
1127 static void
1128 calc_transform_matrix(ASS_Vector shift,
1129 double frx, double fry, double frz,
1130 double fax, double fay, double scale,
1131 int yshift, double m[3][3])
1132 {
1248 static void calc_transform_matrix(ASS_Renderer *render_priv,
1249 GlyphInfo *info, double m[3][3])
1250 {
1251 double frx = M_PI / 180 * info->frx;
1252 double fry = M_PI / 180 * info->fry;
1253 double frz = M_PI / 180 * info->frz;
1254
11331255 double sx = -sin(frx), cx = cos(frx);
11341256 double sy = sin(fry), cy = cos(fry);
11351257 double sz = -sin(frz), cz = cos(frz);
11361258
1137 double x1[3] = { 1, fax, shift.x + fax * yshift };
1138 double y1[3] = { fay, 1, shift.y };
1259 double fax = info->fax * info->scale_x / info->scale_y;
1260 double fay = info->fay * info->scale_y / info->scale_x;
1261 double x1[3] = { 1, fax, info->shift.x + info->asc * fax };
1262 double y1[3] = { fay, 1, info->shift.y };
11391263
11401264 double x2[3], y2[3];
11411265 for (int i = 0; i < 3; i++) {
11551279 z4[i] = x2[i] * sy + z3[i] * cy;
11561280 }
11571281
1158 double dist = 20000 * scale;
1282 double dist = 20000 * render_priv->blur_scale;
1283 z4[2] += dist;
1284
1285 double scale_x = dist * render_priv->font_scale_x;
1286 double offs_x = info->pos.x - info->shift.x * render_priv->font_scale_x;
1287 double offs_y = info->pos.y - info->shift.y;
11591288 for (int i = 0; i < 3; i++) {
1160 m[0][i] = x4[i] * dist;
1161 m[1][i] = y3[i] * dist;
1289 m[0][i] = z4[i] * offs_x + x4[i] * scale_x;
1290 m[1][i] = z4[i] * offs_y + y3[i] * dist;
11621291 m[2][i] = z4[i];
1163 }
1164 m[2][2] += dist;
1165 }
1166
1167 /**
1168 * \brief Apply 3d transformation to several objects
1169 * \param shift FreeType vector
1170 * \param glyph FreeType glyph
1171 * \param glyph2 FreeType glyph
1172 * \param frx x-axis rotation angle
1173 * \param fry y-axis rotation angle
1174 * \param frz z-axis rotation angle
1175 * Rotates both glyphs by frx, fry and frz. Shift vector is added before rotation and subtracted after it.
1176 */
1177 static void
1178 transform_3d(ASS_Vector shift, ASS_Outline *outline, int n_outlines,
1179 double frx, double fry, double frz, double fax, double fay,
1180 double scale, int yshift)
1181 {
1182 if (frx == 0 && fry == 0 && frz == 0 && fax == 0 && fay == 0)
1183 return;
1184
1185 double m[3][3];
1186 calc_transform_matrix(shift, frx, fry, frz, fax, fay, scale, yshift, m);
1187
1188 for (int i = 0; i < n_outlines; i++) {
1189 ASS_Vector *p = outline[i].points;
1190 for (size_t j = 0; j < outline[i].n_points; ++j) {
1191 double v[3];
1192 for (int k = 0; k < 3; k++)
1193 v[k] = m[k][0] * p[j].x + m[k][1] * p[j].y + m[k][2];
1194
1195 double w = 1 / FFMAX(v[2], 1000);
1196 p[j].x = lrint(v[0] * w) - shift.x;
1197 p[j].y = lrint(v[1] * w) - shift.y;
1198 }
11991292 }
12001293 }
12011294
12051298 * Tries to get glyph bitmaps from bitmap cache.
12061299 * If they can't be found, they are generated by rotating and rendering the glyph.
12071300 * After that, bitmaps are added to the cache.
1208 * They are returned in info->bm (glyph), info->bm_o (outline) and info->bm_s (shadow).
1301 * They are returned in info->bm (glyph), info->bm_o (outline).
12091302 */
12101303 static void
1211 get_bitmap_glyph(ASS_Renderer *render_priv, GlyphInfo *info)
1212 {
1213 if (!info->outline || info->symbol == '\n' || info->symbol == 0 || info->skip)
1304 get_bitmap_glyph(ASS_Renderer *render_priv, GlyphInfo *info,
1305 int32_t *leftmost_x,
1306 ASS_Vector *pos, ASS_Vector *pos_o,
1307 ASS_DVector *offset, bool first, int flags)
1308 {
1309 if (!info->outline || info->symbol == '\n' || info->symbol == 0 || info->skip) {
1310 ass_cache_dec_ref(info->outline);
12141311 return;
1215
1216 BitmapHashValue *val;
1217 OutlineBitmapHashKey *key = &info->hash_key.u.outline;
1218 if (ass_cache_get(render_priv->cache.bitmap_cache, &info->hash_key, &val)) {
1219 info->image = val;
1220 if (!val->valid)
1221 info->symbol = 0;
1312 }
1313
1314 double m1[3][3], m2[3][3], m[3][3];
1315 const ASS_Transform *tr = &info->transform;
1316 calc_transform_matrix(render_priv, info, m1);
1317 for (int i = 0; i < 3; i++) {
1318 m2[i][0] = m1[i][0] * tr->scale.x;
1319 m2[i][1] = m1[i][1] * tr->scale.y;
1320 m2[i][2] = m1[i][0] * tr->offset.x + m1[i][1] * tr->offset.y + m1[i][2];
1321 }
1322 memcpy(m, m2, sizeof(m));
1323
1324 if (info->effect_type == EF_KARAOKE_KF)
1325 outline_update_min_transformed_x(&info->outline->outline[0], m, leftmost_x);
1326
1327 BitmapHashKey key;
1328 key.outline = info->outline;
1329 if (!quantize_transform(m, pos, offset, first, &key)) {
1330 ass_cache_dec_ref(info->outline);
12221331 return;
12231332 }
1224 if (!val) {
1225 info->symbol = 0;
1333 info->bm = ass_cache_get(render_priv->cache.bitmap_cache, &key, render_priv);
1334 if (!info->bm || !info->bm->buffer) {
1335 ass_cache_dec_ref(info->bm);
1336 info->bm = NULL;
1337 }
1338 *pos_o = *pos;
1339
1340 OutlineHashKey ol_key;
1341 if (flags & FILTER_BORDER_STYLE_3) {
1342 if (!(flags & (FILTER_NONZERO_BORDER | FILTER_NONZERO_SHADOW)))
1343 return;
1344
1345 ol_key.type = OUTLINE_BOX;
1346
1347 double w = 64 * render_priv->border_scale;
1348 ASS_DVector bord = { info->border_x * w, info->border_y * w };
1349 double width = info->hspacing_scaled + info->advance.x;
1350 double height = info->asc + info->desc;
1351
1352 ASS_DVector orig_scale;
1353 orig_scale.x = info->scale_x * info->scale_fix;
1354 orig_scale.y = info->scale_y * info->scale_fix;
1355
1356 // Emulate the WTFish behavior of VSFilter, i.e. double-scale
1357 // the sizes of the opaque box.
1358 bord.x *= orig_scale.x;
1359 bord.y *= orig_scale.y;
1360 width *= orig_scale.x;
1361 height *= orig_scale.y;
1362
1363 // to avoid gaps
1364 bord.x = FFMAX(64, bord.x);
1365 bord.y = FFMAX(64, bord.y);
1366
1367 ASS_DVector scale = {
1368 (width + 2 * bord.x) / 64,
1369 (height + 2 * bord.y) / 64,
1370 };
1371 ASS_DVector offset = { -bord.x, -bord.y - info->asc };
1372 for (int i = 0; i < 3; i++) {
1373 m[i][0] = m1[i][0] * scale.x;
1374 m[i][1] = m1[i][1] * scale.y;
1375 m[i][2] = m1[i][0] * offset.x + m1[i][1] * offset.y + m1[i][2];
1376 }
1377 } else {
1378 if (!(flags & FILTER_NONZERO_BORDER))
1379 return;
1380
1381 ol_key.type = OUTLINE_BORDER;
1382 BorderHashKey *k = &ol_key.u.border;
1383 k->outline = info->outline;
1384
1385 double w = 64 * render_priv->border_scale;
1386 double bord_x = w * info->border_x / tr->scale.x;
1387 double bord_y = w * info->border_y / tr->scale.y;
1388
1389 const ASS_Rect *bbox = &info->outline->cbox;
1390 // Estimate bounding box half size after stroking
1391 double dx = (bbox->x_max - bbox->x_min) / 2.0 + (bord_x + 64);
1392 double dy = (bbox->y_max - bbox->y_min) / 2.0 + (bord_y + 64);
1393
1394 // Matrix after quantize_transform() has
1395 // input and output origin at bounding box center.
1396 double mxx = fabs(m[0][0]), mxy = fabs(m[0][1]);
1397 double myx = fabs(m[1][0]), myy = fabs(m[1][1]);
1398 double mzx = fabs(m[2][0]), mzy = fabs(m[2][1]);
1399
1400 double z0 = m[2][2] - mzx * dx - mzy * dy;
1401 w = 1 / FFMAX(z0, m[2][2] / MAX_PERSP_SCALE);
1402
1403 // Notation from quantize_transform().
1404 // Note that goal here is to estimate acceptable error for stroking, i. e. D(x) and D(y).
1405 // Matrix coefficients are constants now, so D(m_??) = 0.
1406
1407 // D(z) <= |m_zx| * D(x) + |m_zy| * D(y),
1408 // D(x_out) = D((m_xx * x + m_xy * y) / z)
1409 // <= (|m_xx| * D(x) + |m_xy| * D(y)) / z0 + x_lim * D(z) / z0^2
1410 // <= (|m_xx| / z0 + |m_zx| * x_lim / z0^2) * D(x)
1411 // + (|m_xy| / z0 + |m_zy| * x_lim / z0^2) * D(y),
1412 // D(y_out) = D((m_yx * x + m_yy * y) / z)
1413 // <= (|m_yx| * D(x) + |m_yy| * D(y)) / z0 + y_lim * D(z) / z0^2
1414 // <= (|m_yx| / z0 + |m_zx| * y_lim / z0^2) * D(x)
1415 // + (|m_yy| / z0 + |m_zy| * y_lim / z0^2) * D(y).
1416
1417 // Quantization steps (ACCURACY ~ POSITION_PRECISION):
1418 // STROKER_PRECISION / 2^scale_ord_x ~ D(x) ~ POSITION_PRECISION /
1419 // (max(|m_xx|, |m_yx|) / z0 + |m_zx| * max(x_lim, y_lim) / z0^2),
1420 // STROKER_PRECISION / 2^scale_ord_y ~ D(y) ~ POSITION_PRECISION /
1421 // (max(|m_xy|, |m_yy|) / z0 + |m_zy| * max(x_lim, y_lim) / z0^2).
1422
1423 double x_lim = mxx * dx + mxy * dy;
1424 double y_lim = myx * dx + myy * dy;
1425 double rz = FFMAX(x_lim, y_lim) * w;
1426
1427 w *= STROKER_PRECISION / POSITION_PRECISION;
1428 frexp(w * (FFMAX(mxx, myx) + mzx * rz), &k->scale_ord_x);
1429 frexp(w * (FFMAX(mxy, myy) + mzy * rz), &k->scale_ord_y);
1430 bord_x = ldexp(bord_x, k->scale_ord_x);
1431 bord_y = ldexp(bord_y, k->scale_ord_y);
1432 if (!(bord_x < OUTLINE_MAX && bord_y < OUTLINE_MAX))
1433 return;
1434 k->border.x = lrint(bord_x / STROKER_PRECISION);
1435 k->border.y = lrint(bord_y / STROKER_PRECISION);
1436 if (!k->border.x && !k->border.y) {
1437 ass_cache_inc_ref(info->bm);
1438 info->bm_o = info->bm;
1439 return;
1440 }
1441
1442 for (int i = 0; i < 3; i++) {
1443 m[i][0] = ldexp(m2[i][0], -k->scale_ord_x);
1444 m[i][1] = ldexp(m2[i][1], -k->scale_ord_y);
1445 m[i][2] = m2[i][2];
1446 }
1447 }
1448
1449 key.outline = ass_cache_get(render_priv->cache.outline_cache, &ol_key, render_priv);
1450 if (!key.outline || !key.outline->valid ||
1451 !quantize_transform(m, pos_o, offset, false, &key)) {
1452 ass_cache_dec_ref(key.outline);
12261453 return;
12271454 }
1228 if (!info->outline) {
1229 memset(val, 0, sizeof(*val));
1230 ass_cache_commit(val, sizeof(BitmapHashKey) + sizeof(BitmapHashValue));
1231 info->image = val;
1232 info->symbol = 0;
1233 return;
1234 }
1235
1236 const int n_outlines = 3;
1237 ASS_Outline outline[n_outlines];
1238 outline_copy(&outline[0], info->outline);
1239 outline_copy(&outline[1], info->border[0]);
1240 outline_copy(&outline[2], info->border[1]);
1241
1242 // calculating rotation shift vector (from rotation origin to the glyph basepoint)
1243 ASS_Vector shift = { key->shift_x, key->shift_y };
1244 double scale_x = render_priv->font_scale_x;
1245 double fax_scaled = info->fax / info->scale_y * info->scale_x;
1246 double fay_scaled = info->fay / info->scale_x * info->scale_y;
1247
1248 // apply rotation
1249 // use blur_scale because, like blurs, VSFilter forgets to scale this
1250 transform_3d(shift, outline, n_outlines,
1251 info->frx, info->fry, info->frz, fax_scaled,
1252 fay_scaled, render_priv->blur_scale, info->asc);
1253
1254 // PAR correction scaling + subpixel shift
1255 for (int i = 0; i < n_outlines; i++)
1256 outline_adjust(&outline[i], scale_x, key->advance.x, key->advance.y);
1257
1258 // render glyph
1259 val->valid = outline_to_bitmap2(render_priv,
1260 &outline[0], &outline[1], &outline[2],
1261 &val->bm, &val->bm_o);
1262 if (!val->valid)
1263 info->symbol = 0;
1264
1265 ass_cache_commit(val, bitmap_size(val->bm) + bitmap_size(val->bm_o) +
1266 sizeof(BitmapHashKey) + sizeof(BitmapHashValue));
1267 info->image = val;
1268
1269 for (int i = 0; i < n_outlines; i++)
1270 outline_free(&outline[i]);
1271 }
1455 info->bm_o = ass_cache_get(render_priv->cache.bitmap_cache, &key, render_priv);
1456 if (!info->bm_o || !info->bm_o->buffer) {
1457 ass_cache_dec_ref(info->bm_o);
1458 info->bm_o = NULL;
1459 *pos_o = *pos;
1460 } else if (!info->bm)
1461 *pos = *pos_o;
1462 }
1463
1464 size_t ass_bitmap_construct(void *key, void *value, void *priv)
1465 {
1466 ASS_Renderer *render_priv = priv;
1467 BitmapHashKey *k = key;
1468 Bitmap *bm = value;
1469
1470 double m[3][3];
1471 restore_transform(m, k);
1472
1473 ASS_Outline outline[2];
1474 if (k->matrix_z.x || k->matrix_z.y) {
1475 outline_transform_3d(&outline[0], &k->outline->outline[0], m);
1476 outline_transform_3d(&outline[1], &k->outline->outline[1], m);
1477 } else {
1478 outline_transform_2d(&outline[0], &k->outline->outline[0], m);
1479 outline_transform_2d(&outline[1], &k->outline->outline[1], m);
1480 }
1481
1482 if (!outline_to_bitmap(render_priv, bm, &outline[0], &outline[1]))
1483 memset(bm, 0, sizeof(*bm));
1484 outline_free(&outline[0]);
1485 outline_free(&outline[1]);
1486
1487 return sizeof(BitmapHashKey) + sizeof(Bitmap) + bitmap_size(bm);
1488 }
1489
1490 static void measure_text_on_eol(ASS_Renderer *render_priv, double scale, int cur_line,
1491 int max_asc, int max_desc,
1492 double max_border_x, double max_border_y)
1493 {
1494 render_priv->text_info.lines[cur_line].asc = scale * max_asc;
1495 render_priv->text_info.lines[cur_line].desc = scale * max_desc;
1496 render_priv->text_info.height += scale * max_asc + scale * max_desc;
1497 // For *VSFilter compatibility do biased rounding on max_border*
1498 // https://github.com/Cyberbeing/xy-VSFilter/blob/xy_sub_filter_rc4@%7B2020-05-17%7D/src/subtitles/RTS.cpp#L1465
1499 render_priv->text_info.border_bottom = (int) (render_priv->border_scale * max_border_y + 0.5);
1500 if (cur_line == 0)
1501 render_priv->text_info.border_top = render_priv->text_info.border_bottom;
1502 // VSFilter takes max \bordx into account for collision, even if far from edge
1503 render_priv->text_info.border_x = FFMAX(render_priv->text_info.border_x,
1504 (int) (render_priv->border_scale * max_border_x + 0.5));
1505 }
1506
12721507
12731508 /**
12741509 * This function goes through text_info and calculates text parameters.
12751510 * The following text_info fields are filled:
12761511 * height
1277 * lines[].height
1512 * border_top
1513 * border_bottom
1514 * border_x
12781515 * lines[].asc
12791516 * lines[].desc
12801517 */
12811518 static void measure_text(ASS_Renderer *render_priv)
12821519 {
12831520 TextInfo *text_info = &render_priv->text_info;
1521 text_info->height = 0;
1522
12841523 int cur_line = 0;
1285 double max_asc = 0., max_desc = 0.;
1286 GlyphInfo *last = NULL;
1287 int i;
1288 int empty_line = 1;
1289 text_info->height = 0.;
1290 for (i = 0; i < text_info->length + 1; ++i) {
1291 if ((i == text_info->length) || text_info->glyphs[i].linebreak) {
1292 if (empty_line && cur_line > 0 && last) {
1293 max_asc = d6_to_double(last->asc) / 2.0;
1294 max_desc = d6_to_double(last->desc) / 2.0;
1295 }
1296 text_info->lines[cur_line].asc = max_asc;
1297 text_info->lines[cur_line].desc = max_desc;
1298 text_info->height += max_asc + max_desc;
1524 double scale = 0.5 / 64;
1525 int max_asc = 0, max_desc = 0;
1526 double max_border_y = 0, max_border_x = 0;
1527 bool empty_trimmed_line = true;
1528 for (int i = 0; i < text_info->length; i++) {
1529 if (text_info->glyphs[i].linebreak) {
1530 measure_text_on_eol(render_priv, scale, cur_line,
1531 max_asc, max_desc, max_border_x, max_border_y);
1532 empty_trimmed_line = true;
1533 max_asc = max_desc = 0;
1534 max_border_y = max_border_x = 0;
1535 scale = 0.5 / 64;
12991536 cur_line++;
1300 max_asc = max_desc = 0.;
1301 empty_line = 1;
1302 }
1303 if (i < text_info->length) {
1304 GlyphInfo *cur = text_info->glyphs + i;
1305 if (d6_to_double(cur->asc) > max_asc)
1306 max_asc = d6_to_double(cur->asc);
1307 if (d6_to_double(cur->desc) > max_desc)
1308 max_desc = d6_to_double(cur->desc);
1309 if (cur->symbol != '\n' && cur->symbol != 0) {
1310 empty_line = 0;
1311 last = cur;
1312 }
1313 }
1314 }
1315 text_info->height +=
1316 (text_info->n_lines -
1317 1) * render_priv->settings.line_spacing;
1537 }
1538 GlyphInfo *cur = text_info->glyphs + i;
1539 // VSFilter ignores metrics of line-leading/trailing (trimmed)
1540 // whitespace, except when the line becomes empty after trimming
1541 if (empty_trimmed_line && !cur->is_trimmed_whitespace) {
1542 empty_trimmed_line = false;
1543 // Forget metrics of line-leading whitespace
1544 max_asc = max_desc = 0;
1545 max_border_y = max_border_x = 0;
1546 } else if (!empty_trimmed_line && cur->is_trimmed_whitespace) {
1547 // Ignore metrics of line-trailing whitespace
1548 continue;
1549 }
1550 max_asc = FFMAX(max_asc, cur->asc);
1551 max_desc = FFMAX(max_desc, cur->desc);
1552 max_border_y = FFMAX(max_border_y, cur->border_y);
1553 max_border_x = FFMAX(max_border_x, cur->border_x);
1554 if (cur->symbol != '\n')
1555 scale = 1.0 / 64;
1556 }
1557 assert(cur_line == text_info->n_lines - 1);
1558 measure_text_on_eol(render_priv, scale, cur_line,
1559 max_asc, max_desc, max_border_x, max_border_y);
1560 text_info->height += cur_line * render_priv->settings.line_spacing;
13181561 }
13191562
13201563 /**
13321575 i = ti->length - 1;
13331576 cur = ti->glyphs + i;
13341577 while (i && IS_WHITESPACE(cur)) {
1335 cur->skip++;
1578 cur->skip = true;
1579 cur->is_trimmed_whitespace = true;
13361580 cur = ti->glyphs + --i;
13371581 }
13381582
13401584 i = 0;
13411585 cur = ti->glyphs;
13421586 while (i < ti->length && IS_WHITESPACE(cur)) {
1343 cur->skip++;
1587 cur->skip = true;
1588 cur->is_trimmed_whitespace = true;
13441589 cur = ti->glyphs + ++i;
13451590 }
1591 if (i < ti->length)
1592 cur->starts_new_run = true;
13461593
13471594 // Mark all extraneous whitespace inbetween
13481595 for (i = 0; i < ti->length; ++i) {
13521599 j = i - 1;
13531600 cur = ti->glyphs + j;
13541601 while (j && IS_WHITESPACE(cur)) {
1355 cur->skip++;
1602 cur->skip = true;
1603 cur->is_trimmed_whitespace = true;
13561604 cur = ti->glyphs + --j;
13571605 }
13581606 // A break itself can contain a whitespace, too
13591607 cur = ti->glyphs + i;
13601608 if (cur->symbol == ' ' || cur->symbol == '\n') {
1361 cur->skip++;
1609 cur->skip = true;
1610 cur->is_trimmed_whitespace = true;
13621611 // Mark whitespace after
13631612 j = i + 1;
13641613 cur = ti->glyphs + j;
13651614 while (j < ti->length && IS_WHITESPACE(cur)) {
1366 cur->skip++;
1615 cur->skip = true;
1616 cur->is_trimmed_whitespace = true;
13671617 cur = ti->glyphs + ++j;
13681618 }
13691619 i = j - 1;
13701620 }
1621 if (cur < ti->glyphs + ti->length)
1622 cur->starts_new_run = true;
13711623 }
13721624 }
13731625 }
15011753 assert(text_info->n_lines >= 1);
15021754 #undef DIFF
15031755
1756 trim_whitespace(render_priv);
15041757 measure_text(render_priv);
1505 trim_whitespace(render_priv);
15061758
15071759 cur_line = 1;
15081760
15801832 }
15811833
15821834 /**
1583 * Prepare bitmap hash key of a glyph
1584 */
1585 static void
1586 fill_bitmap_hash(ASS_Renderer *priv, GlyphInfo *info,
1587 OutlineBitmapHashKey *hash_key)
1588 {
1589 hash_key->frx = rot_key(info->frx);
1590 hash_key->fry = rot_key(info->fry);
1591 hash_key->frz = rot_key(info->frz);
1592 hash_key->fax = double_to_d16(info->fax);
1593 hash_key->fay = double_to_d16(info->fay);
1594 }
1595
1596 /**
15971835 * \brief Adjust the glyph's font size and scale factors to ensure smooth
15981836 * scaling and handle pathological font sizes. The main problem here is
15991837 * freetype's grid fitting, which destroys animations by font size, or will
16151853 // to freetype. Normalize scale_y to 1.0.
16161854 ft_size = glyph->scale_y * glyph->font_size;
16171855 }
1618 glyph->scale_x = glyph->scale_x * glyph->font_size / ft_size;
1619 glyph->scale_y = glyph->scale_y * glyph->font_size / ft_size;
1856 double mul = glyph->font_size / ft_size;
1857 glyph->scale_fix = 1 / mul;
1858 glyph->scale_x *= mul;
1859 glyph->scale_y *= mul;
16201860 glyph->font_size = ft_size;
16211861 }
16221862
1623 /**
1624 * \brief Checks whether a glyph should start a new bitmap run
1625 * \param info Pointer to new GlyphInfo to check
1626 * \param current_info Pointer to CombinedBitmapInfo for current run (may be NULL)
1627 * \return 1 if a new run should be started
1628 */
1629 static int is_new_bm_run(GlyphInfo *info, GlyphInfo *last)
1630 {
1631 // FIXME: Don't break on glyph substitutions
1632 return !last || info->effect || info->drawing || last->drawing ||
1633 strcmp(last->font->desc.family, info->font->desc.family) ||
1634 last->font->desc.vertical != info->font->desc.vertical ||
1635 last->face_index != info->face_index ||
1636 last->font_size != info->font_size ||
1637 last->c[0] != info->c[0] ||
1638 last->c[1] != info->c[1] ||
1639 last->c[2] != info->c[2] ||
1640 last->c[3] != info->c[3] ||
1641 last->be != info->be ||
1642 last->blur != info->blur ||
1643 last->shadow_x != info->shadow_x ||
1644 last->shadow_y != info->shadow_y ||
1645 last->frx != info->frx ||
1646 last->fry != info->fry ||
1647 last->frz != info->frz ||
1648 last->fax != info->fax ||
1649 last->fay != info->fay ||
1650 last->scale_x != info->scale_x ||
1651 last->scale_y != info->scale_y ||
1652 last->border_style != info->border_style ||
1653 last->border_x != info->border_x ||
1654 last->border_y != info->border_y ||
1655 last->hspacing != info->hspacing ||
1656 last->italic != info->italic ||
1657 last->bold != info->bold ||
1658 last->flags != info->flags;
1659 }
1660
1661 static void make_shadow_bitmap(CombinedBitmapInfo *info, ASS_Renderer *render_priv)
1662 {
1663 if (!(info->filter.flags & FILTER_NONZERO_SHADOW)) {
1664 if (info->bm && info->bm_o && !(info->filter.flags & FILTER_BORDER_STYLE_3)) {
1665 fix_outline(info->bm, info->bm_o);
1666 } else if (info->bm_o && !(info->filter.flags & FILTER_NONZERO_BORDER)) {
1667 ass_free_bitmap(info->bm_o);
1668 info->bm_o = 0;
1669 }
1670 return;
1671 }
1672
1673 // Create shadow and fix outline as needed
1674 if (info->bm && info->bm_o && !(info->filter.flags & FILTER_BORDER_STYLE_3)) {
1675 info->bm_s = copy_bitmap(render_priv->engine, info->bm_o);
1676 fix_outline(info->bm, info->bm_o);
1677 } else if (info->bm_o && (info->filter.flags & FILTER_NONZERO_BORDER)) {
1678 info->bm_s = copy_bitmap(render_priv->engine, info->bm_o);
1679 } else if (info->bm_o) {
1680 info->bm_s = info->bm_o;
1681 info->bm_o = 0;
1682 } else if (info->bm)
1683 info->bm_s = copy_bitmap(render_priv->engine, info->bm);
1684
1685 if (!info->bm_s)
1686 return;
1687
1688 // Works right even for negative offsets
1689 // '>>' rounds toward negative infinity, '&' returns correct remainder
1690 info->bm_s->left += info->filter.shadow.x >> 6;
1691 info->bm_s->top += info->filter.shadow.y >> 6;
1692 shift_bitmap(info->bm_s, info->filter.shadow.x & SUBPIXEL_MASK, info->filter.shadow.y & SUBPIXEL_MASK);
1863 // Initial run splitting based purely on the characters' styles
1864 static void split_style_runs(ASS_Renderer *render_priv)
1865 {
1866 Effect last_effect_type = render_priv->text_info.glyphs[0].effect_type;
1867 render_priv->text_info.glyphs[0].starts_new_run = true;
1868 for (int i = 1; i < render_priv->text_info.length; i++) {
1869 GlyphInfo *info = render_priv->text_info.glyphs + i;
1870 GlyphInfo *last = render_priv->text_info.glyphs + (i - 1);
1871 Effect effect_type = info->effect_type;
1872 info->starts_new_run =
1873 info->effect_timing || // but ignore effect_skip_timing
1874 (effect_type != EF_NONE && effect_type != last_effect_type) ||
1875 info->drawing_text ||
1876 last->drawing_text ||
1877 strcmp(last->font->desc.family, info->font->desc.family) ||
1878 last->font->desc.vertical != info->font->desc.vertical ||
1879 last->font_size != info->font_size ||
1880 last->c[0] != info->c[0] ||
1881 last->c[1] != info->c[1] ||
1882 last->c[2] != info->c[2] ||
1883 last->c[3] != info->c[3] ||
1884 last->be != info->be ||
1885 last->blur != info->blur ||
1886 last->shadow_x != info->shadow_x ||
1887 last->shadow_y != info->shadow_y ||
1888 last->frx != info->frx ||
1889 last->fry != info->fry ||
1890 last->frz != info->frz ||
1891 last->fax != info->fax ||
1892 last->fay != info->fay ||
1893 last->scale_x != info->scale_x ||
1894 last->scale_y != info->scale_y ||
1895 last->border_style != info->border_style ||
1896 last->border_x != info->border_x ||
1897 last->border_y != info->border_y ||
1898 last->hspacing != info->hspacing ||
1899 last->italic != info->italic ||
1900 last->bold != info->bold ||
1901 ((last->flags ^ info->flags) & ~DECO_ROTATE);
1902 if (effect_type != EF_NONE)
1903 last_effect_type = effect_type;
1904 }
16931905 }
16941906
16951907 // Parse event text.
16961908 // Fill render_priv->text_info.
1697 static int parse_events(ASS_Renderer *render_priv, ASS_Event *event)
1909 static bool parse_events(ASS_Renderer *render_priv, ASS_Event *event)
16981910 {
16991911 TextInfo *text_info = &render_priv->text_info;
1700 ASS_Drawing *drawing = NULL;
1701 unsigned code;
1702 char *p, *q;
1703 int i;
1704
1705 p = event->Text;
1912
1913 char *p = event->Text, *q;
1914 char *drawing_text;
17061915
17071916 // Event parsing.
1708 while (1) {
1917 while (true) {
1918 drawing_text = NULL;
1919
17091920 // get next char, executing style override
17101921 // this affects render_context
1711 code = 0;
1922 unsigned code = 0;
17121923 while (*p) {
17131924 if ((*p == '{') && (q = strchr(p, '}'))) {
1714 while (p < q)
1715 p = parse_tag(render_priv, p, q, 1.);
1925 p = parse_tags(render_priv, p, q, 1., false);
17161926 assert(*p == '}');
17171927 p++;
17181928 } else if (render_priv->state.drawing_scale) {
17211931 q++;
17221932 while ((*q != '{') && (*q != 0))
17231933 q++;
1724 if (!drawing) {
1725 drawing = ass_drawing_new(render_priv->library);
1726 if (!drawing)
1727 return 1;
1728 }
1729 ass_drawing_set_text(drawing, p, q - p);
1934 drawing_text = strndup(p, q - p);
17301935 code = 0xfffc; // object replacement character
17311936 p = q;
17321937 break;
17401945 break;
17411946
17421947 // face could have been changed in get_next_char
1743 if (!render_priv->state.font) {
1744 free_render_context(render_priv);
1745 ass_drawing_free(drawing);
1746 return 1;
1747 }
1948 if (!render_priv->state.font)
1949 goto fail;
17481950
17491951 if (text_info->length >= text_info->max_glyphs) {
17501952 // Raise maximum number of glyphs
1751 text_info->max_glyphs *= 2;
1752 text_info->glyphs =
1753 realloc(text_info->glyphs,
1754 sizeof(GlyphInfo) * text_info->max_glyphs);
1953 int new_max = 2 * FFMIN(text_info->max_glyphs, INT_MAX / 2);
1954 if (text_info->length >= new_max)
1955 goto fail;
1956 if (!ASS_REALLOC_ARRAY(text_info->glyphs, new_max))
1957 goto fail;
1958 text_info->max_glyphs = new_max;
17551959 }
17561960
17571961 GlyphInfo *info = &text_info->glyphs[text_info->length];
17601964 memset(info, 0, sizeof(GlyphInfo));
17611965
17621966 // Parse drawing
1763 if (drawing && drawing->text) {
1764 drawing->scale_x = render_priv->state.scale_x *
1765 render_priv->font_scale;
1766 drawing->scale_y = render_priv->state.scale_y *
1767 render_priv->font_scale;
1768 drawing->scale = render_priv->state.drawing_scale;
1769 drawing->pbo = render_priv->state.pbo;
1770 info->drawing = drawing;
1771 drawing = NULL;
1967 if (drawing_text) {
1968 info->drawing_text = drawing_text;
1969 info->drawing_scale = render_priv->state.drawing_scale;
1970 info->drawing_pbo = render_priv->state.pbo;
17721971 }
17731972
17741973 // Fill glyph information
17751974 info->symbol = code;
17761975 info->font = render_priv->state.font;
1777 if (!info->drawing)
1976 if (!drawing_text)
17781977 ass_cache_inc_ref(info->font);
1779 for (i = 0; i < 4; ++i) {
1978 for (int i = 0; i < 4; i++) {
17801979 uint32_t clr = render_priv->state.c[i];
17811980 // VSFilter compatibility: apply fade only when it's positive
1981 info->a_pre_fade[i] = _a(clr);
17821982 if (render_priv->state.fade > 0)
17831983 change_alpha(&clr,
17841984 mult_alpha(_a(clr), render_priv->state.fade), 1.);
17941994 info->blur = render_priv->state.blur;
17951995 info->shadow_x = render_priv->state.shadow_x;
17961996 info->shadow_y = render_priv->state.shadow_y;
1797 info->scale_x = info->orig_scale_x = render_priv->state.scale_x;
1798 info->scale_y = info->orig_scale_y = render_priv->state.scale_y;
1997 info->scale_x = render_priv->state.scale_x;
1998 info->scale_y = render_priv->state.scale_y;
17991999 info->border_style = render_priv->state.border_style;
1800 info->border_x= render_priv->state.border_x;
2000 info->border_x = render_priv->state.border_x;
18012001 info->border_y = render_priv->state.border_y;
18022002 info->hspacing = render_priv->state.hspacing;
18032003 info->bold = render_priv->state.bold;
18042004 info->italic = render_priv->state.italic;
18052005 info->flags = render_priv->state.flags;
2006 if (info->font->desc.vertical && code >= VERTICAL_LOWER_BOUND)
2007 info->flags |= DECO_ROTATE;
18062008 info->frx = render_priv->state.frx;
18072009 info->fry = render_priv->state.fry;
18082010 info->frz = render_priv->state.frz;
18092011 info->fax = render_priv->state.fax;
18102012 info->fay = render_priv->state.fay;
18112013
1812 if (!info->drawing)
2014 info->hspacing_scaled = double_to_d6(info->hspacing *
2015 render_priv->font_scale * info->scale_x);
2016 info->scale_fix = 1;
2017
2018 if (!drawing_text)
18132019 fix_glyph_scaling(render_priv, info);
18142020
18152021 text_info->length++;
18192025 render_priv->state.effect_skip_timing = 0;
18202026 }
18212027
1822 ass_drawing_free(drawing);
1823
1824 return 0;
2028 return true;
2029
2030 fail:
2031 free_render_context(render_priv);
2032 free(drawing_text);
2033 return false;
18252034 }
18262035
18272036 // Process render_priv->text_info and load glyph outlines.
18322041
18332042 for (i = 0; i < render_priv->text_info.length; i++) {
18342043 GlyphInfo *info = glyphs + i;
1835 while (info) {
2044 do {
18362045 get_outline_glyph(render_priv, info);
18372046 info = info->next;
1838 }
2047 } while (info);
18392048 info = glyphs + i;
18402049
18412050 // Add additional space after italic to non-italic style changes
18502059 }
18512060
18522061 // add horizontal letter spacing
1853 info->cluster_advance.x += double_to_d6(info->hspacing *
1854 render_priv->font_scale * info->orig_scale_x);
2062 info->cluster_advance.x += info->hspacing_scaled;
18552063
18562064 // add displacement for vertical shearing
18572065 info->cluster_advance.y += (info->fay / info->scale_x * info->scale_y) * info->cluster_advance.x;
18652073 for (int i = 0; i < render_priv->text_info.length; i++) {
18662074 GlyphInfo *info = render_priv->text_info.glyphs + i;
18672075 ASS_Vector cluster_pen = pen;
1868 while (info) {
2076 do {
18692077 info->pos.x = cluster_pen.x;
18702078 info->pos.y = cluster_pen.y;
18712079
18722080 cluster_pen.x += info->advance.x;
18732081 cluster_pen.y += info->advance.y;
18742082
1875 // fill bitmap hash
1876 info->hash_key.type = BITMAP_OUTLINE;
1877 fill_bitmap_hash(render_priv, info, &info->hash_key.u.outline);
1878
18792083 info = info->next;
1880 }
2084 } while (info);
18812085 info = render_priv->text_info.glyphs + i;
18822086 pen.x += info->cluster_advance.x;
18832087 pen.y += info->cluster_advance.y;
19422146 int justify = render_priv->state.justify;
19432147 double max_width = 0;
19442148
1945 if (render_priv->state.evt_type == EVENT_HSCROLL)
1946 return;
2149 if (render_priv->state.evt_type & EVENT_HSCROLL) {
2150 justify = halign;
2151 halign = HALIGN_LEFT;
2152 }
19472153
19482154 for (i = 0; i <= text_info->length; ++i) { // (text_info->length + 1) is the end of the last line
19492155 if ((i == text_info->length) || glyphs[i].linebreak) {
20052211 {
20062212 ASS_DVector center;
20072213 if (render_priv->state.have_origin) {
2008 center.x = x2scr(render_priv, render_priv->state.org_x);
2009 center.y = y2scr(render_priv, render_priv->state.org_y);
2214 center.x = x2scr_pos(render_priv, render_priv->state.org_x);
2215 center.y = y2scr_pos(render_priv, render_priv->state.org_y);
20102216 } else {
20112217 double bx = 0., by = 0.;
20122218 get_base_point(bbox, render_priv->state.alignment, &bx, &by);
20182224 for (int i = 0; i < text_info->length; i++) {
20192225 GlyphInfo *info = text_info->glyphs + i;
20202226 while (info) {
2021 OutlineBitmapHashKey *key = &info->hash_key.u.outline;
2022
2023 if (key->frx || key->fry || key->frz || key->fax || key->fay) {
2024 key->shift_x = info->pos.x + double_to_d6(device_x - center.x);
2025 key->shift_y = info->pos.y + double_to_d6(device_y - center.y);
2026 } else {
2027 key->shift_x = 0;
2028 key->shift_y = 0;
2029 }
2227 info->shift.x = info->pos.x + double_to_d6(device_x - center.x +
2228 info->shadow_x * render_priv->border_scale /
2229 render_priv->font_scale_x);
2230 info->shift.y = info->pos.y + double_to_d6(device_y - center.y +
2231 info->shadow_y * render_priv->border_scale);
20302232 info = info->next;
20312233 }
20322234 }
20332235 }
20342236
20352237
2036 static inline void rectangle_combine(ASS_Rect *rect, const Bitmap *bm, int x, int y)
2037 {
2038 x += bm->left;
2039 y += bm->top;
2040 rectangle_update(rect, x, y, x + bm->w, y + bm->h);
2238 static int quantize_blur(double radius, int32_t *shadow_mask)
2239 {
2240 // Gaussian filter kernel (1D):
2241 // G(x, r2) = exp(-x^2 / (2 * r2)) / sqrt(2 * pi * r2),
2242 // position unit is 1/64th of pixel, r = 64 * radius, r2 = r^2.
2243
2244 // Difference between kernels with different but near r2:
2245 // G(x, r2 + dr2) - G(x, r2) ~= dr2 * G(x, r2) * (x^2 - r2) / (2 * r2^2).
2246 // Maximal possible error relative to full pixel value is half of
2247 // integral (from -inf to +inf) of absolute value of that difference.
2248 // E_max ~= dr2 / 2 * integral(G(x, r2) * |x^2 - r2| / (2 * r2^2), x)
2249 // = dr2 / (4 * r2) * integral(G(y, 1) * |y^2 - 1|, y)
2250 // = dr2 / (4 * r2) * 4 / sqrt(2 * pi * e)
2251 // ~ dr2 / (4 * r2) ~= dr / (2 * r).
2252 // E_max ~ BLUR_PRECISION / 2 as we have 2 dimensions.
2253
2254 // To get discretized blur radius solve the following
2255 // differential equation (n--quantization index):
2256 // dr(n) / dn = BLUR_PRECISION * r + POSITION_PRECISION, r(0) = 0,
2257 // r(n) = (exp(BLUR_PRECISION * n) - 1) * POSITION_PRECISION / BLUR_PRECISION,
2258 // n = log(1 + r * BLUR_PRECISION / POSITION_PRECISION) / BLUR_PRECISION.
2259
2260 // To get shadow offset quantization estimate difference of
2261 // G(x + dx, r2) - G(x, r2) ~= dx * G(x, r2) * (-x / r2).
2262 // E_max ~= dx / 2 * integral(G(x, r2) * |x| / r2, x)
2263 // = dx / sqrt(2 * pi * r2) ~ dx / (2 * r).
2264 // 2^ord ~ dx ~ BLUR_PRECISION * r + POSITION_PRECISION.
2265
2266 const double scale = 64 * BLUR_PRECISION / POSITION_PRECISION;
2267 radius *= scale;
2268
2269 int ord;
2270 // ord = floor(log2(BLUR_PRECISION * r + POSITION_PRECISION))
2271 // = floor(log2(64 * radius * BLUR_PRECISION + POSITION_PRECISION))
2272 // = floor(log2((radius * scale + 1) * POSITION_PRECISION)),
2273 // floor(log2(x)) = frexp(x) - 1 = frexp(x / 2).
2274 frexp((1 + radius) * (POSITION_PRECISION / 2), &ord);
2275 *shadow_mask = ((uint32_t) 1 << ord) - 1;
2276 return lrint(log1p(radius) / BLUR_PRECISION);
2277 }
2278
2279 static double restore_blur(int qblur)
2280 {
2281 const double scale = 64 * BLUR_PRECISION / POSITION_PRECISION;
2282 double sigma = expm1(BLUR_PRECISION * qblur) / scale;
2283 return sigma * sigma;
20412284 }
20422285
20432286 // Convert glyphs to bitmaps, combine them, apply blur, generate shadows.
20482291 int left = render_priv->settings.left_margin;
20492292 device_x = (device_x - left) * render_priv->font_scale_x + left;
20502293 unsigned nb_bitmaps = 0;
2051 char linebreak = 0;
2294 bool new_run = true;
20522295 CombinedBitmapInfo *combined_info = text_info->combined_bitmaps;
20532296 CombinedBitmapInfo *current_info = NULL;
2054 GlyphInfo *last_info = NULL;
2297 ASS_DVector offset;
20552298 for (int i = 0; i < text_info->length; i++) {
20562299 GlyphInfo *info = text_info->glyphs + i;
2057 if (info->linebreak) linebreak = 1;
2300 if (info->starts_new_run) new_run = true;
20582301 if (info->skip) {
20592302 for (; info; info = info->next)
2060 ass_cache_dec_ref(info->hash_key.u.outline.outline);
2303 ass_cache_dec_ref(info->outline);
20612304 continue;
20622305 }
20632306 for (; info; info = info->next) {
2064 OutlineBitmapHashKey *key = &info->hash_key.u.outline;
2065
2066 info->pos.x = double_to_d6(device_x + d6_to_double(info->pos.x) * render_priv->font_scale_x);
2067 info->pos.y = double_to_d6(device_y) + info->pos.y;
2068 key->advance.x = info->pos.x & (SUBPIXEL_MASK & ~SUBPIXEL_ACCURACY);
2069 key->advance.y = info->pos.y & (SUBPIXEL_MASK & ~SUBPIXEL_ACCURACY);
2070 int x = info->pos.x >> 6, y = info->pos.y >> 6;
2071 get_bitmap_glyph(render_priv, info);
2072
2073 if(linebreak || is_new_bm_run(info, last_info)) {
2074 linebreak = 0;
2075 last_info = NULL;
2307 int flags = 0;
2308 if (info->border_style == 3)
2309 flags |= FILTER_BORDER_STYLE_3;
2310 if (info->border_x || info->border_y)
2311 flags |= FILTER_NONZERO_BORDER;
2312 if (info->shadow_x || info->shadow_y)
2313 flags |= FILTER_NONZERO_SHADOW;
2314 if (flags & FILTER_NONZERO_SHADOW &&
2315 (info->effect_type == EF_KARAOKE_KF ||
2316 info->effect_type == EF_KARAOKE_KO ||
2317 (info->a_pre_fade[0]) != 0xFF ||
2318 info->border_style == 3))
2319 flags |= FILTER_FILL_IN_SHADOW;
2320 if (!(flags & FILTER_NONZERO_BORDER) &&
2321 !(flags & FILTER_FILL_IN_SHADOW))
2322 flags &= ~FILTER_NONZERO_SHADOW;
2323 if ((flags & FILTER_NONZERO_BORDER &&
2324 info->a_pre_fade[0] == 0 &&
2325 info->a_pre_fade[1] == 0 &&
2326 _a(info->c[2]) == 0) ||
2327 info->border_style == 3)
2328 flags |= FILTER_FILL_IN_BORDER;
2329
2330 if (new_run) {
20762331 if (nb_bitmaps >= text_info->max_bitmaps) {
20772332 size_t new_size = 2 * text_info->max_bitmaps;
20782333 if (!ASS_REALLOC_ARRAY(text_info->combined_bitmaps, new_size)) {
2079 ass_cache_dec_ref(info->image);
2334 ass_cache_dec_ref(info->outline);
20802335 continue;
20812336 }
20822337 text_info->max_bitmaps = new_size;
20872342 memcpy(&current_info->c, &info->c, sizeof(info->c));
20882343 current_info->effect_type = info->effect_type;
20892344 current_info->effect_timing = info->effect_timing;
2090 current_info->first_pos_x = info->bbox.x_max >> 6;
2091
2092 current_info->filter.flags = 0;
2093 if (info->border_style == 3)
2094 current_info->filter.flags |= FILTER_BORDER_STYLE_3;
2095 if (info->border_x || info->border_y)
2096 current_info->filter.flags |= FILTER_NONZERO_BORDER;
2097 if (info->shadow_x || info->shadow_y)
2098 current_info->filter.flags |= FILTER_NONZERO_SHADOW;
2099 // VSFilter compatibility: invisible fill and no border?
2100 // In this case no shadow is supposed to be rendered.
2101 if (info->border[0] || info->border[1] || (info->c[0] & 0xFF) != 0xFF)
2102 current_info->filter.flags |= FILTER_DRAW_SHADOW;
2103
2104 current_info->filter.be = info->be;
2105 current_info->filter.blur = 2 * info->blur * render_priv->blur_scale;
2106 current_info->filter.shadow.x = double_to_d6(info->shadow_x * render_priv->border_scale);
2107 current_info->filter.shadow.y = double_to_d6(info->shadow_y * render_priv->border_scale);
2345 current_info->leftmost_x = OUTLINE_MAX;
2346
2347 FilterDesc *filter = &current_info->filter;
2348 filter->flags = flags;
2349 filter->be = info->be;
2350
2351 int32_t shadow_mask;
2352 double blur_scale = render_priv->blur_scale * (2 / sqrt(log(256)));
2353 filter->blur = quantize_blur(info->blur * blur_scale, &shadow_mask);
2354 if (flags & FILTER_NONZERO_SHADOW) {
2355 int32_t x = double_to_d6(info->shadow_x * render_priv->border_scale);
2356 int32_t y = double_to_d6(info->shadow_y * render_priv->border_scale);
2357 filter->shadow.x = (x + (shadow_mask >> 1)) & ~shadow_mask;
2358 filter->shadow.y = (y + (shadow_mask >> 1)) & ~shadow_mask;
2359 } else
2360 filter->shadow.x = filter->shadow.y = 0;
21082361
21092362 current_info->x = current_info->y = INT_MAX;
2110 rectangle_reset(&current_info->rect);
2111 rectangle_reset(&current_info->rect_o);
2112 current_info->n_bm = current_info->n_bm_o = 0;
21132363 current_info->bm = current_info->bm_o = current_info->bm_s = NULL;
21142364 current_info->image = NULL;
21152365
21162366 current_info->bitmap_count = current_info->max_bitmap_count = 0;
21172367 current_info->bitmaps = malloc(MAX_SUB_BITMAPS_INITIAL * sizeof(BitmapRef));
21182368 if (!current_info->bitmaps) {
2119 ass_cache_dec_ref(info->image);
2369 ass_cache_dec_ref(info->outline);
21202370 continue;
21212371 }
21222372 current_info->max_bitmap_count = MAX_SUB_BITMAPS_INITIAL;
21232373
21242374 nb_bitmaps++;
2375 new_run = false;
21252376 }
2126 last_info = info;
2127
2128 if (!info->image || !current_info) {
2129 ass_cache_dec_ref(info->image);
2377 assert(current_info);
2378
2379 ASS_Vector pos, pos_o;
2380 info->pos.x = double_to_d6(device_x + d6_to_double(info->pos.x) * render_priv->font_scale_x);
2381 info->pos.y = double_to_d6(device_y) + info->pos.y;
2382 get_bitmap_glyph(render_priv, info, &current_info->leftmost_x, &pos, &pos_o,
2383 &offset, !current_info->bitmap_count, flags);
2384
2385 if (!info->bm && !info->bm_o) {
2386 ass_cache_dec_ref(info->bm);
2387 ass_cache_dec_ref(info->bm_o);
21302388 continue;
21312389 }
21322390
21332391 if (current_info->bitmap_count >= current_info->max_bitmap_count) {
21342392 size_t new_size = 2 * current_info->max_bitmap_count;
21352393 if (!ASS_REALLOC_ARRAY(current_info->bitmaps, new_size)) {
2136 ass_cache_dec_ref(info->image);
2394 ass_cache_dec_ref(info->bm);
2395 ass_cache_dec_ref(info->bm_o);
21372396 continue;
21382397 }
21392398 current_info->max_bitmap_count = new_size;
21402399 }
2141 current_info->bitmaps[current_info->bitmap_count].image = info->image;
2142 current_info->bitmaps[current_info->bitmap_count].x = x;
2143 current_info->bitmaps[current_info->bitmap_count].y = y;
2400 current_info->bitmaps[current_info->bitmap_count].bm = info->bm;
2401 current_info->bitmaps[current_info->bitmap_count].bm_o = info->bm_o;
2402 current_info->bitmaps[current_info->bitmap_count].pos = pos;
2403 current_info->bitmaps[current_info->bitmap_count].pos_o = pos_o;
21442404 current_info->bitmap_count++;
21452405
2146 current_info->x = FFMIN(current_info->x, x);
2147 current_info->y = FFMIN(current_info->y, y);
2148 if (info->image->bm) {
2149 rectangle_combine(&current_info->rect, info->image->bm, x, y);
2150 current_info->n_bm++;
2151 }
2152 if (info->image->bm_o) {
2153 rectangle_combine(&current_info->rect_o, info->image->bm_o, x, y);
2154 current_info->n_bm_o++;
2155 }
2406 current_info->x = FFMIN(current_info->x, pos.x);
2407 current_info->y = FFMIN(current_info->y, pos.y);
21562408 }
21572409 }
21582410
21592411 for (int i = 0; i < nb_bitmaps; i++) {
21602412 CombinedBitmapInfo *info = &combined_info[i];
2413 if (!info->bitmap_count) {
2414 free(info->bitmaps);
2415 continue;
2416 }
2417
2418 if (info->effect_type == EF_KARAOKE_KF)
2419 info->effect_timing = lround(d6_to_double(info->leftmost_x) +
2420 d6_to_double(info->effect_timing) * render_priv->font_scale_x);
2421
21612422 for (int j = 0; j < info->bitmap_count; j++) {
2162 info->bitmaps[j].x -= info->x;
2163 info->bitmaps[j].y -= info->y;
2164 }
2165
2166 CompositeHashKey hk;
2167 CompositeHashValue *hv;
2168 fill_composite_hash(&hk, info);
2169 if (ass_cache_get(render_priv->cache.composite_cache, &hk, &hv)) {
2170 info->bm = hv->bm;
2171 info->bm_o = hv->bm_o;
2172 info->bm_s = hv->bm_s;
2173 info->image = hv;
2423 info->bitmaps[j].pos.x -= info->x;
2424 info->bitmaps[j].pos.y -= info->y;
2425 info->bitmaps[j].pos_o.x -= info->x;
2426 info->bitmaps[j].pos_o.y -= info->y;
2427 }
2428
2429 CompositeHashKey key;
2430 key.filter = info->filter;
2431 key.bitmap_count = info->bitmap_count;
2432 key.bitmaps = info->bitmaps;
2433 CompositeHashValue *val = ass_cache_get(render_priv->cache.composite_cache, &key, render_priv);
2434 if (!val)
21742435 continue;
2175 }
2176 if (!hv)
2177 continue;
2178
2179 int bord = be_padding(info->filter.be);
2180 if (!bord && info->n_bm == 1) {
2181 for (int j = 0; j < info->bitmap_count; j++) {
2182 if (!info->bitmaps[j].image->bm)
2183 continue;
2184 info->bm = copy_bitmap(render_priv->engine, info->bitmaps[j].image->bm);
2185 if (info->bm) {
2186 info->bm->left += info->bitmaps[j].x;
2187 info->bm->top += info->bitmaps[j].y;
2188 }
2189 break;
2190 }
2191 } else if (info->n_bm) {
2192 info->bm = alloc_bitmap(render_priv->engine,
2193 info->rect.x_max - info->rect.x_min + 2 * bord,
2194 info->rect.y_max - info->rect.y_min + 2 * bord, true);
2195 Bitmap *dst = info->bm;
2196 if (dst) {
2197 dst->left = info->rect.x_min - info->x - bord;
2198 dst->top = info->rect.y_min - info->y - bord;
2199 for (int j = 0; j < info->bitmap_count; j++) {
2200 Bitmap *src = info->bitmaps[j].image->bm;
2201 if (!src)
2202 continue;
2203 int x = info->bitmaps[j].x + src->left - dst->left;
2204 int y = info->bitmaps[j].y + src->top - dst->top;
2205 assert(x >= 0 && x + src->w <= dst->w);
2206 assert(y >= 0 && y + src->h <= dst->h);
2207 unsigned char *buf = dst->buffer + y * dst->stride + x;
2208 render_priv->engine->add_bitmaps(buf, dst->stride,
2209 src->buffer, src->stride,
2210 src->h, src->w);
2211 }
2212 }
2213 }
2214 if (!bord && info->n_bm_o == 1) {
2215 for (int j = 0; j < info->bitmap_count; j++) {
2216 if (!info->bitmaps[j].image->bm_o)
2217 continue;
2218 info->bm_o = copy_bitmap(render_priv->engine, info->bitmaps[j].image->bm_o);
2219 if (info->bm_o) {
2220 info->bm_o->left += info->bitmaps[j].x;
2221 info->bm_o->top += info->bitmaps[j].y;
2222 }
2223 break;
2224 }
2225 } else if (info->n_bm_o) {
2226 info->bm_o = alloc_bitmap(render_priv->engine,
2227 info->rect_o.x_max - info->rect_o.x_min + 2 * bord,
2228 info->rect_o.y_max - info->rect_o.y_min + 2 * bord,
2229 true);
2230 Bitmap *dst = info->bm_o;
2231 if (dst) {
2232 dst->left = info->rect_o.x_min - info->x - bord;
2233 dst->top = info->rect_o.y_min - info->y - bord;
2234 for (int j = 0; j < info->bitmap_count; j++) {
2235 Bitmap *src = info->bitmaps[j].image->bm_o;
2236 if (!src)
2237 continue;
2238 int x = info->bitmaps[j].x + src->left - dst->left;
2239 int y = info->bitmaps[j].y + src->top - dst->top;
2240 assert(x >= 0 && x + src->w <= dst->w);
2241 assert(y >= 0 && y + src->h <= dst->h);
2242 unsigned char *buf = dst->buffer + y * dst->stride + x;
2243 render_priv->engine->add_bitmaps(buf, dst->stride,
2244 src->buffer, src->stride,
2245 src->h, src->w);
2246 }
2247 }
2248 }
2249
2250 if (info->bm || info->bm_o) {
2251 ass_synth_blur(render_priv->engine, info->filter.flags & FILTER_BORDER_STYLE_3,
2252 info->filter.be, info->filter.blur, info->bm, info->bm_o);
2253 if (info->filter.flags & FILTER_DRAW_SHADOW)
2254 make_shadow_bitmap(info, render_priv);
2255 }
2256
2257 hv->bm = info->bm;
2258 hv->bm_o = info->bm_o;
2259 hv->bm_s = info->bm_s;
2260 ass_cache_commit(hv, bitmap_size(hv->bm) +
2261 bitmap_size(hv->bm_o) + bitmap_size(hv->bm_s) +
2262 sizeof(CompositeHashKey) + sizeof(CompositeHashValue));
2263 info->image = hv;
2436
2437 if (val->bm.buffer)
2438 info->bm = &val->bm;
2439 if (val->bm_o.buffer)
2440 info->bm_o = &val->bm_o;
2441 if (val->bm_s.buffer)
2442 info->bm_s = &val->bm_s;
2443 info->image = val;
2444 continue;
22642445 }
22652446
22662447 text_info->n_bitmaps = nb_bitmaps;
2448 }
2449
2450 static inline void rectangle_combine(ASS_Rect *rect, const Bitmap *bm, ASS_Vector pos)
2451 {
2452 pos.x += bm->left;
2453 pos.y += bm->top;
2454 rectangle_update(rect, pos.x, pos.y, pos.x + bm->w, pos.y + bm->h);
2455 }
2456
2457 size_t ass_composite_construct(void *key, void *value, void *priv)
2458 {
2459 ASS_Renderer *render_priv = priv;
2460 CompositeHashKey *k = key;
2461 CompositeHashValue *v = value;
2462 memset(v, 0, sizeof(*v));
2463
2464 ASS_Rect rect, rect_o;
2465 rectangle_reset(&rect);
2466 rectangle_reset(&rect_o);
2467
2468 size_t n_bm = 0, n_bm_o = 0;
2469 BitmapRef *last = NULL, *last_o = NULL;
2470 for (int i = 0; i < k->bitmap_count; i++) {
2471 BitmapRef *ref = &k->bitmaps[i];
2472 if (ref->bm) {
2473 rectangle_combine(&rect, ref->bm, ref->pos);
2474 last = ref;
2475 n_bm++;
2476 }
2477 if (ref->bm_o) {
2478 rectangle_combine(&rect_o, ref->bm_o, ref->pos_o);
2479 last_o = ref;
2480 n_bm_o++;
2481 }
2482 }
2483
2484 int bord = be_padding(k->filter.be);
2485 if (!bord && n_bm == 1) {
2486 copy_bitmap(render_priv->engine, &v->bm, last->bm);
2487 v->bm.left += last->pos.x;
2488 v->bm.top += last->pos.y;
2489 } else if (n_bm && alloc_bitmap(render_priv->engine, &v->bm,
2490 rect.x_max - rect.x_min + 2 * bord,
2491 rect.y_max - rect.y_min + 2 * bord,
2492 true)) {
2493 Bitmap *dst = &v->bm;
2494 dst->left = rect.x_min - bord;
2495 dst->top = rect.y_min - bord;
2496 for (int i = 0; i < k->bitmap_count; i++) {
2497 Bitmap *src = k->bitmaps[i].bm;
2498 if (!src)
2499 continue;
2500 int x = k->bitmaps[i].pos.x + src->left - dst->left;
2501 int y = k->bitmaps[i].pos.y + src->top - dst->top;
2502 assert(x >= 0 && x + src->w <= dst->w);
2503 assert(y >= 0 && y + src->h <= dst->h);
2504 unsigned char *buf = dst->buffer + y * dst->stride + x;
2505 render_priv->engine->add_bitmaps(buf, dst->stride,
2506 src->buffer, src->stride,
2507 src->h, src->w);
2508 }
2509 }
2510 if (!bord && n_bm_o == 1) {
2511 copy_bitmap(render_priv->engine, &v->bm_o, last_o->bm_o);
2512 v->bm_o.left += last_o->pos_o.x;
2513 v->bm_o.top += last_o->pos_o.y;
2514 } else if (n_bm_o && alloc_bitmap(render_priv->engine, &v->bm_o,
2515 rect_o.x_max - rect_o.x_min + 2 * bord,
2516 rect_o.y_max - rect_o.y_min + 2 * bord,
2517 true)) {
2518 Bitmap *dst = &v->bm_o;
2519 dst->left = rect_o.x_min - bord;
2520 dst->top = rect_o.y_min - bord;
2521 for (int i = 0; i < k->bitmap_count; i++) {
2522 Bitmap *src = k->bitmaps[i].bm_o;
2523 if (!src)
2524 continue;
2525 int x = k->bitmaps[i].pos_o.x + src->left - dst->left;
2526 int y = k->bitmaps[i].pos_o.y + src->top - dst->top;
2527 assert(x >= 0 && x + src->w <= dst->w);
2528 assert(y >= 0 && y + src->h <= dst->h);
2529 unsigned char *buf = dst->buffer + y * dst->stride + x;
2530 render_priv->engine->add_bitmaps(buf, dst->stride,
2531 src->buffer, src->stride,
2532 src->h, src->w);
2533 }
2534 }
2535
2536 int flags = k->filter.flags;
2537 double r2 = restore_blur(k->filter.blur);
2538 if (!(flags & FILTER_NONZERO_BORDER) || (flags & FILTER_BORDER_STYLE_3))
2539 ass_synth_blur(render_priv->engine, &v->bm, k->filter.be, r2);
2540 ass_synth_blur(render_priv->engine, &v->bm_o, k->filter.be, r2);
2541
2542 if (!(flags & FILTER_FILL_IN_BORDER) && !(flags & FILTER_FILL_IN_SHADOW))
2543 fix_outline(&v->bm, &v->bm_o);
2544
2545 if (flags & FILTER_NONZERO_SHADOW) {
2546 if (flags & FILTER_NONZERO_BORDER) {
2547 copy_bitmap(render_priv->engine, &v->bm_s, &v->bm_o);
2548 if ((flags & FILTER_FILL_IN_BORDER) && !(flags & FILTER_FILL_IN_SHADOW))
2549 fix_outline(&v->bm, &v->bm_s);
2550 } else if (flags & FILTER_BORDER_STYLE_3) {
2551 v->bm_s = v->bm_o;
2552 memset(&v->bm_o, 0, sizeof(v->bm_o));
2553 } else {
2554 copy_bitmap(render_priv->engine, &v->bm_s, &v->bm);
2555 }
2556
2557 // Works right even for negative offsets
2558 // '>>' rounds toward negative infinity, '&' returns correct remainder
2559 v->bm_s.left += k->filter.shadow.x >> 6;
2560 v->bm_s.top += k->filter.shadow.y >> 6;
2561 shift_bitmap(&v->bm_s, k->filter.shadow.x & SUBPIXEL_MASK, k->filter.shadow.y & SUBPIXEL_MASK);
2562 }
2563
2564 if ((flags & FILTER_FILL_IN_SHADOW) && !(flags & FILTER_FILL_IN_BORDER))
2565 fix_outline(&v->bm, &v->bm_o);
2566
2567 return sizeof(CompositeHashKey) + sizeof(CompositeHashValue) +
2568 bitmap_size(&v->bm) + bitmap_size(&v->bm_o) + bitmap_size(&v->bm_s);
22672569 }
22682570
22692571 static void add_background(ASS_Renderer *render_priv, EventImages *event_images)
23022604 * \param event_images struct containing resulting images, will also be initialized
23032605 * Process event, appending resulting ASS_Image's to images_root.
23042606 */
2305 static int
2607 static bool
23062608 ass_render_event(ASS_Renderer *render_priv, ASS_Event *event,
23072609 EventImages *event_images)
23082610 {
23092611 if (event->Style >= render_priv->track->n_styles) {
23102612 ass_msg(render_priv->library, MSGL_WARN, "No style found");
2311 return 1;
2613 return false;
23122614 }
23132615 if (!event->Text) {
23142616 ass_msg(render_priv->library, MSGL_WARN, "Empty event");
2315 return 1;
2617 return false;
23162618 }
23172619
23182620 free_render_context(render_priv);
23192621 init_render_context(render_priv, event);
23202622
2321 if (parse_events(render_priv, event))
2322 return 1;
2623 if (!parse_events(render_priv, event))
2624 return false;
23232625
23242626 TextInfo *text_info = &render_priv->text_info;
23252627 if (text_info->length == 0) {
23262628 // no valid symbols in the event; this can be smth like {comment}
23272629 free_render_context(render_priv);
2328 return 1;
2329 }
2630 return false;
2631 }
2632
2633 split_style_runs(render_priv);
23302634
23312635 // Find shape runs and shape text
23322636 ass_shaper_set_base_direction(render_priv->shaper,
23332637 resolve_base_direction(render_priv->state.font_encoding));
23342638 ass_shaper_find_runs(render_priv->shaper, render_priv, text_info->glyphs,
23352639 text_info->length);
2336 if (ass_shaper_shape(render_priv->shaper, text_info) < 0) {
2640 if (!ass_shaper_shape(render_priv->shaper, text_info)) {
23372641 ass_msg(render_priv->library, MSGL_ERR, "Failed to shape text");
23382642 free_render_context(render_priv);
2339 return 1;
2643 return false;
23402644 }
23412645
23422646 retrieve_glyphs(render_priv);
23432647
23442648 preliminary_layout(render_priv);
2345
2346 // depends on glyph x coordinates being monotonous, so it should be done before line wrap
2347 process_karaoke_effects(render_priv);
23482649
23492650 int valign = render_priv->state.alignment & 12;
23502651
23572658
23582659 // calculate max length of a line
23592660 double max_text_width =
2360 x2scr(render_priv, render_priv->track->PlayResX - MarginR) -
2361 x2scr(render_priv, MarginL);
2661 x2scr_right(render_priv, render_priv->track->PlayResX - MarginR) -
2662 x2scr_left(render_priv, MarginL);
23622663
23632664 // wrap lines
2364 if (render_priv->state.evt_type != EVENT_HSCROLL) {
2365 // rearrange text in several lines
2366 wrap_lines_smart(render_priv, max_text_width);
2367 } else {
2368 // no breaking or wrapping, everything in a single line
2369 text_info->lines[0].offset = 0;
2370 text_info->lines[0].len = text_info->length;
2371 text_info->n_lines = 1;
2372 measure_text(render_priv);
2373 }
2665 wrap_lines_smart(render_priv, max_text_width);
2666
2667 // depends on glyph x coordinates being monotonous within runs, so it should be done before reorder
2668 process_karaoke_effects(render_priv);
23742669
23752670 reorder_text(render_priv);
23762671
23812676 compute_string_bbox(text_info, &bbox);
23822677
23832678 // determine device coordinates for text
2384
2385 // x coordinate for everything except positioned events
23862679 double device_x = 0;
2387 if (render_priv->state.evt_type == EVENT_NORMAL ||
2388 render_priv->state.evt_type == EVENT_VSCROLL) {
2389 device_x = x2scr(render_priv, MarginL);
2390 } else if (render_priv->state.evt_type == EVENT_HSCROLL) {
2680 double device_y = 0;
2681
2682 // handle positioned events first: an event can be both positioned and
2683 // scrolling, and the scrolling effect overrides the position on one axis
2684 if (render_priv->state.evt_type & EVENT_POSITIONED) {
2685 double base_x = 0;
2686 double base_y = 0;
2687 get_base_point(&bbox, render_priv->state.alignment, &base_x, &base_y);
2688 device_x =
2689 x2scr_pos(render_priv, render_priv->state.pos_x) - base_x;
2690 device_y =
2691 y2scr_pos(render_priv, render_priv->state.pos_y) - base_y;
2692 }
2693
2694 // x coordinate
2695 if (render_priv->state.evt_type & EVENT_HSCROLL) {
23912696 if (render_priv->state.scroll_direction == SCROLL_RL)
23922697 device_x =
2393 x2scr(render_priv,
2698 x2scr_pos(render_priv,
23942699 render_priv->track->PlayResX -
23952700 render_priv->state.scroll_shift);
23962701 else if (render_priv->state.scroll_direction == SCROLL_LR)
23972702 device_x =
2398 x2scr(render_priv, render_priv->state.scroll_shift) -
2703 x2scr_pos(render_priv, render_priv->state.scroll_shift) -
23992704 (bbox.x_max - bbox.x_min);
2400 }
2401
2402 // y coordinate for everything except positioned events
2403 double device_y = 0;
2404 if (render_priv->state.evt_type == EVENT_NORMAL ||
2405 render_priv->state.evt_type == EVENT_HSCROLL) {
2705 } else if (!(render_priv->state.evt_type & EVENT_POSITIONED)) {
2706 device_x = x2scr_left(render_priv, MarginL);
2707 }
2708
2709 // y coordinate
2710 if (render_priv->state.evt_type & EVENT_VSCROLL) {
2711 if (render_priv->state.scroll_direction == SCROLL_TB)
2712 device_y =
2713 y2scr(render_priv,
2714 render_priv->state.scroll_y0 +
2715 render_priv->state.scroll_shift) -
2716 bbox.y_max;
2717 else if (render_priv->state.scroll_direction == SCROLL_BT)
2718 device_y =
2719 y2scr(render_priv,
2720 render_priv->state.scroll_y1 -
2721 render_priv->state.scroll_shift) -
2722 bbox.y_min;
2723 } else if (!(render_priv->state.evt_type & EVENT_POSITIONED)) {
24062724 if (valign == VALIGN_TOP) { // toptitle
24072725 device_y =
24082726 y2scr_top(render_priv,
24332751 device_y = scr_y0;
24342752 }
24352753 }
2436 } else if (render_priv->state.evt_type == EVENT_VSCROLL) {
2437 if (render_priv->state.scroll_direction == SCROLL_TB)
2438 device_y =
2439 y2scr(render_priv,
2440 render_priv->state.clip_y0 +
2441 render_priv->state.scroll_shift) -
2442 (bbox.y_max - bbox.y_min);
2443 else if (render_priv->state.scroll_direction == SCROLL_BT)
2444 device_y =
2445 y2scr(render_priv,
2446 render_priv->state.clip_y1 -
2447 render_priv->state.scroll_shift);
2448 }
2449
2450 // positioned events are totally different
2451 if (render_priv->state.evt_type == EVENT_POSITIONED) {
2452 double base_x = 0;
2453 double base_y = 0;
2454 get_base_point(&bbox, render_priv->state.alignment, &base_x, &base_y);
2455 device_x =
2456 x2scr_pos(render_priv, render_priv->state.pos_x) - base_x;
2457 device_y =
2458 y2scr_pos(render_priv, render_priv->state.pos_y) - base_y;
2459 }
2460
2461 // fix clip coordinates (they depend on alignment)
2462 if (render_priv->state.evt_type == EVENT_NORMAL ||
2463 render_priv->state.evt_type == EVENT_HSCROLL ||
2464 render_priv->state.evt_type == EVENT_VSCROLL) {
2465 render_priv->state.clip_x0 =
2466 x2scr_scaled(render_priv, render_priv->state.clip_x0);
2467 render_priv->state.clip_x1 =
2468 x2scr_scaled(render_priv, render_priv->state.clip_x1);
2469 if (valign == VALIGN_TOP) {
2470 render_priv->state.clip_y0 =
2471 y2scr_top(render_priv, render_priv->state.clip_y0);
2472 render_priv->state.clip_y1 =
2473 y2scr_top(render_priv, render_priv->state.clip_y1);
2474 } else if (valign == VALIGN_CENTER) {
2475 render_priv->state.clip_y0 =
2476 y2scr(render_priv, render_priv->state.clip_y0);
2477 render_priv->state.clip_y1 =
2478 y2scr(render_priv, render_priv->state.clip_y1);
2479 } else if (valign == VALIGN_SUB) {
2480 render_priv->state.clip_y0 =
2481 y2scr_sub(render_priv, render_priv->state.clip_y0);
2482 render_priv->state.clip_y1 =
2483 y2scr_sub(render_priv, render_priv->state.clip_y1);
2484 }
2485 } else if (render_priv->state.evt_type == EVENT_POSITIONED) {
2754 }
2755
2756 // fix clip coordinates
2757 if (render_priv->state.explicit || !render_priv->settings.use_margins) {
24862758 render_priv->state.clip_x0 =
24872759 x2scr_pos_scaled(render_priv, render_priv->state.clip_x0);
24882760 render_priv->state.clip_x1 =
24912763 y2scr_pos(render_priv, render_priv->state.clip_y0);
24922764 render_priv->state.clip_y1 =
24932765 y2scr_pos(render_priv, render_priv->state.clip_y1);
2494 }
2495
2496 if (render_priv->state.explicit) {
2497 // we still need to clip against screen boundaries
2498 double zx = x2scr_pos_scaled(render_priv, 0);
2499 double zy = y2scr_pos(render_priv, 0);
2500 double sx = x2scr_pos_scaled(render_priv, render_priv->track->PlayResX);
2501 double sy = y2scr_pos(render_priv, render_priv->track->PlayResY);
2502
2503 render_priv->state.clip_x0 = render_priv->state.clip_x0 < zx ? zx : render_priv->state.clip_x0;
2504 render_priv->state.clip_y0 = render_priv->state.clip_y0 < zy ? zy : render_priv->state.clip_y0;
2505 render_priv->state.clip_x1 = render_priv->state.clip_x1 > sx ? sx : render_priv->state.clip_x1;
2506 render_priv->state.clip_y1 = render_priv->state.clip_y1 > sy ? sy : render_priv->state.clip_y1;
2766
2767 if (render_priv->state.explicit) {
2768 // we still need to clip against screen boundaries
2769 double zx = x2scr_pos_scaled(render_priv, 0);
2770 double zy = y2scr_pos(render_priv, 0);
2771 double sx = x2scr_pos_scaled(render_priv, render_priv->track->PlayResX);
2772 double sy = y2scr_pos(render_priv, render_priv->track->PlayResY);
2773
2774 render_priv->state.clip_x0 = FFMAX(render_priv->state.clip_x0, zx);
2775 render_priv->state.clip_y0 = FFMAX(render_priv->state.clip_y0, zy);
2776 render_priv->state.clip_x1 = FFMIN(render_priv->state.clip_x1, sx);
2777 render_priv->state.clip_y1 = FFMIN(render_priv->state.clip_y1, sy);
2778 }
2779 } else {
2780 // no \clip (explicit==0) and use_margins => only clip to screen with margins
2781 render_priv->state.clip_x0 = 0;
2782 render_priv->state.clip_y0 = 0;
2783 render_priv->state.clip_x1 = render_priv->settings.frame_width;
2784 render_priv->state.clip_y1 = render_priv->settings.frame_height;
2785 }
2786
2787 if (render_priv->state.evt_type & EVENT_VSCROLL) {
2788 double y0 = y2scr_pos(render_priv, render_priv->state.scroll_y0);
2789 double y1 = y2scr_pos(render_priv, render_priv->state.scroll_y1);
2790
2791 render_priv->state.clip_y0 = FFMAX(render_priv->state.clip_y0, y0);
2792 render_priv->state.clip_y1 = FFMIN(render_priv->state.clip_y1, y1);
25072793 }
25082794
25092795 calculate_rotation_params(render_priv, &bbox, device_x, device_y);
25112797 render_and_combine_glyphs(render_priv, device_x, device_y);
25122798
25132799 memset(event_images, 0, sizeof(*event_images));
2514 event_images->top = device_y - text_info->lines[0].asc;
2515 event_images->height = text_info->height;
2800 // VSFilter does *not* shift lines with a border > margin to be within the
2801 // frame, so negative values for top and left may occur
2802 event_images->top = device_y - text_info->lines[0].asc - text_info->border_top;
2803 event_images->height =
2804 text_info->height + text_info->border_bottom + text_info->border_top;
25162805 event_images->left =
2517 (device_x + bbox.x_min * render_priv->font_scale_x) + 0.5;
2806 (device_x + bbox.x_min) * render_priv->font_scale_x - text_info->border_x + 0.5;
25182807 event_images->width =
2519 (bbox.x_max - bbox.x_min) * render_priv->font_scale_x + 0.5;
2808 (bbox.x_max - bbox.x_min) * render_priv->font_scale_x
2809 + 2 * text_info->border_x + 0.5;
25202810 event_images->detect_collisions = render_priv->state.detect_collisions;
2521 event_images->shift_direction = (valign == VALIGN_TOP) ? 1 : -1;
2811 event_images->shift_direction = (valign == VALIGN_SUB) ? -1 : 1;
25222812 event_images->event = event;
25232813 event_images->imgs = render_text(render_priv);
25242814
25282818 ass_shaper_cleanup(render_priv->shaper, text_info);
25292819 free_render_context(render_priv);
25302820
2531 return 0;
2821 return true;
25322822 }
25332823
25342824 /**
25442834 /**
25452835 * \brief Start a new frame
25462836 */
2547 static int
2837 static bool
25482838 ass_start_frame(ASS_Renderer *render_priv, ASS_Track *track,
25492839 long long now)
25502840 {
25522842
25532843 if (!render_priv->settings.frame_width
25542844 && !render_priv->settings.frame_height)
2555 return 1; // library not initialized
2845 return false; // library not initialized
25562846
25572847 if (!render_priv->fontselect)
2558 return 1;
2848 return false;
25592849
25602850 if (render_priv->library != track->library)
2561 return 1;
2851 return false;
25622852
25632853 if (track->n_events == 0)
2564 return 1; // nothing to do
2854 return false; // nothing to do
25652855
25662856 render_priv->track = track;
25672857 render_priv->time = now;
25712861 ass_shaper_set_kerning(render_priv->shaper, track->Kerning);
25722862 ass_shaper_set_language(render_priv->shaper, track->Language);
25732863 ass_shaper_set_level(render_priv->shaper, render_priv->settings.shaper);
2864 #ifdef USE_FRIBIDI_EX_API
2865 ass_shaper_set_bidi_brackets(render_priv->shaper,
2866 track->parser_priv->bidi_brackets);
2867 #endif
25742868
25752869 // PAR correction
25762870 double par = render_priv->settings.par;
25772871 if (par == 0.) {
2578 if (settings_priv->frame_width && settings_priv->frame_height &&
2872 if (render_priv->orig_width && render_priv->orig_height &&
25792873 settings_priv->storage_width && settings_priv->storage_height) {
2580 double dar = ((double) settings_priv->frame_width) /
2581 settings_priv->frame_height;
2874 double dar = ((double) render_priv->orig_width) /
2875 render_priv->orig_height;
25822876 double sar = ((double) settings_priv->storage_width) /
25832877 settings_priv->storage_height;
2584 par = sar / dar;
2878 par = dar / sar;
25852879 } else
25862880 par = 1.0;
25872881 }
25922886
25932887 check_cache_limits(render_priv, &render_priv->cache);
25942888
2595 return 0;
2889 return true;
25962890 }
25972891
25982892 static int cmp_event_layer(const void *p1, const void *p2)
26262920 return event->render_priv;
26272921 }
26282922
2629 static int overlap(Segment *s1, Segment *s2)
2630 {
2631 if (s1->a >= s2->b || s2->a >= s1->b ||
2632 s1->ha >= s2->hb || s2->ha >= s1->hb)
2923 static int overlap(Rect *s1, Rect *s2)
2924 {
2925 if (s1->y0 >= s2->y1 || s2->y0 >= s1->y1 ||
2926 s1->x0 >= s2->x1 || s2->x0 >= s1->x1)
26332927 return 0;
26342928 return 1;
26352929 }
26362930
2637 static int cmp_segment(const void *p1, const void *p2)
2638 {
2639 return ((Segment *) p1)->a - ((Segment *) p2)->a;
2931 static int cmp_rect_y0(const void *p1, const void *p2)
2932 {
2933 return ((Rect *) p1)->y0 - ((Rect *) p2)->y0;
26402934 }
26412935
26422936 static void
26672961
26682962 // dir: 1 - move down
26692963 // -1 - move up
2670 static int fit_segment(Segment *s, Segment *fixed, int *cnt, int dir)
2964 static int fit_rect(Rect *s, Rect *fixed, int *cnt, int dir)
26712965 {
26722966 int i;
26732967 int shift = 0;
26742968
26752969 if (dir == 1) // move down
26762970 for (i = 0; i < *cnt; ++i) {
2677 if (s->b + shift <= fixed[i].a || s->a + shift >= fixed[i].b ||
2678 s->hb <= fixed[i].ha || s->ha >= fixed[i].hb)
2971 if (s->y1 + shift <= fixed[i].y0 || s->y0 + shift >= fixed[i].y1 ||
2972 s->x1 <= fixed[i].x0 || s->x0 >= fixed[i].x1)
26792973 continue;
2680 shift = fixed[i].b - s->a;
2974 shift = fixed[i].y1 - s->y0;
26812975 } else // dir == -1, move up
26822976 for (i = *cnt - 1; i >= 0; --i) {
2683 if (s->b + shift <= fixed[i].a || s->a + shift >= fixed[i].b ||
2684 s->hb <= fixed[i].ha || s->ha >= fixed[i].hb)
2977 if (s->y1 + shift <= fixed[i].y0 || s->y0 + shift >= fixed[i].y1 ||
2978 s->x1 <= fixed[i].x0 || s->x0 >= fixed[i].x1)
26852979 continue;
2686 shift = fixed[i].a - s->b;
2687 }
2688
2689 fixed[*cnt].a = s->a + shift;
2690 fixed[*cnt].b = s->b + shift;
2691 fixed[*cnt].ha = s->ha;
2692 fixed[*cnt].hb = s->hb;
2980 shift = fixed[i].y0 - s->y1;
2981 }
2982
2983 fixed[*cnt].y0 = s->y0 + shift;
2984 fixed[*cnt].y1 = s->y1 + shift;
2985 fixed[*cnt].x0 = s->x0;
2986 fixed[*cnt].x1 = s->x1;
26932987 (*cnt)++;
2694 qsort(fixed, *cnt, sizeof(Segment), cmp_segment);
2988 qsort(fixed, *cnt, sizeof(*fixed), cmp_rect_y0);
26952989
26962990 return shift;
26972991 }
26992993 static void
27002994 fix_collisions(ASS_Renderer *render_priv, EventImages *imgs, int cnt)
27012995 {
2702 Segment *used = ass_realloc_array(NULL, cnt, sizeof(*used));
2996 Rect *used = ass_realloc_array(NULL, cnt, sizeof(*used));
27032997 int cnt_used = 0;
27042998 int i, j;
27052999
27133007 continue;
27143008 priv = get_render_priv(render_priv, imgs[i].event);
27153009 if (priv && priv->height > 0) { // it's a fixed event
2716 Segment s;
2717 s.a = priv->top;
2718 s.b = priv->top + priv->height;
2719 s.ha = priv->left;
2720 s.hb = priv->left + priv->width;
3010 Rect s;
3011 s.y0 = priv->top;
3012 s.y1 = priv->top + priv->height;
3013 s.x0 = priv->left;
3014 s.x1 = priv->left + priv->width;
27213015 if (priv->height != imgs[i].height) { // no, it's not
27223016 ass_msg(render_priv->library, MSGL_WARN,
27233017 "Event height has changed");
27343028 priv->width = 0;
27353029 }
27363030 if (priv->height > 0) { // still a fixed event
2737 used[cnt_used].a = priv->top;
2738 used[cnt_used].b = priv->top + priv->height;
2739 used[cnt_used].ha = priv->left;
2740 used[cnt_used].hb = priv->left + priv->width;
3031 used[cnt_used].y0 = priv->top;
3032 used[cnt_used].y1 = priv->top + priv->height;
3033 used[cnt_used].x0 = priv->left;
3034 used[cnt_used].x1 = priv->left + priv->width;
27413035 cnt_used++;
27423036 shift_event(render_priv, imgs + i, priv->top - imgs[i].top);
27433037 }
27443038 }
27453039 }
2746 qsort(used, cnt_used, sizeof(Segment), cmp_segment);
3040 qsort(used, cnt_used, sizeof(*used), cmp_rect_y0);
27473041
27483042 // try to fit other events in free spaces
27493043 for (i = 0; i < cnt; ++i) {
27533047 priv = get_render_priv(render_priv, imgs[i].event);
27543048 if (priv && priv->height == 0) { // not a fixed event
27553049 int shift;
2756 Segment s;
2757 s.a = imgs[i].top;
2758 s.b = imgs[i].top + imgs[i].height;
2759 s.ha = imgs[i].left;
2760 s.hb = imgs[i].left + imgs[i].width;
2761 shift = fit_segment(&s, used, &cnt_used, imgs[i].shift_direction);
3050 Rect s;
3051 s.y0 = imgs[i].top;
3052 s.y1 = imgs[i].top + imgs[i].height;
3053 s.x0 = imgs[i].left;
3054 s.x1 = imgs[i].left + imgs[i].width;
3055 shift = fit_rect(&s, used, &cnt_used, imgs[i].shift_direction);
27623056 if (shift)
27633057 shift_event(render_priv, imgs + i, shift);
27643058 // make it fixed
28473141 ASS_Image *ass_render_frame(ASS_Renderer *priv, ASS_Track *track,
28483142 long long now, int *detect_change)
28493143 {
2850 int i, cnt, rc;
2851 EventImages *last;
2852 ASS_Image **tail;
2853
28543144 // init frame
2855 rc = ass_start_frame(priv, track, now);
2856 if (rc != 0) {
2857 if (detect_change) {
3145 if (!ass_start_frame(priv, track, now)) {
3146 if (detect_change)
28583147 *detect_change = 2;
2859 }
28603148 return NULL;
28613149 }
28623150
28633151 // render events separately
2864 cnt = 0;
2865 for (i = 0; i < track->n_events; ++i) {
3152 int cnt = 0;
3153 for (int i = 0; i < track->n_events; i++) {
28663154 ASS_Event *event = track->events + i;
28673155 if ((event->Start <= now)
28683156 && (now < (event->Start + event->Duration))) {
28723160 realloc(priv->eimg,
28733161 priv->eimg_size * sizeof(EventImages));
28743162 }
2875 rc = ass_render_event(priv, event, priv->eimg + cnt);
2876 if (!rc)
2877 ++cnt;
3163 if (ass_render_event(priv, event, priv->eimg + cnt))
3164 cnt++;
28783165 }
28793166 }
28803167
28813168 // sort by layer
2882 qsort(priv->eimg, cnt, sizeof(EventImages), cmp_event_layer);
3169 if (cnt > 0)
3170 qsort(priv->eimg, cnt, sizeof(EventImages), cmp_event_layer);
28833171
28843172 // call fix_collisions for each group of events with the same layer
2885 last = priv->eimg;
2886 for (i = 1; i < cnt; ++i)
3173 EventImages *last = priv->eimg;
3174 for (int i = 1; i < cnt; i++)
28873175 if (last->event->Layer != priv->eimg[i].event->Layer) {
28883176 fix_collisions(priv, last, priv->eimg + i - last);
28893177 last = priv->eimg + i;
28923180 fix_collisions(priv, last, priv->eimg + cnt - last);
28933181
28943182 // concat lists
2895 tail = &priv->images_root;
2896 for (i = 0; i < cnt; ++i) {
3183 ASS_Image **tail = &priv->images_root;
3184 for (int i = 0; i < cnt; i++) {
28973185 ASS_Image *cur = priv->eimg[i].imgs;
28983186 while (cur) {
28993187 *tail = cur;
29353223 do {
29363224 ASS_ImagePriv *priv = (ASS_ImagePriv *) img;
29373225 img = img->next;
2938 if (priv->source)
2939 ass_cache_dec_ref(priv->source);
2940 else
2941 ass_aligned_free(priv->result.bitmap);
3226 ass_cache_dec_ref(priv->source);
3227 ass_aligned_free(priv->buffer);
29423228 free(priv);
29433229 } while (img);
29443230 }
2020 #define LIBASS_RENDER_H
2121
2222 #include <inttypes.h>
23 #include <stdbool.h>
2324 #include <ft2build.h>
2425 #include FT_FREETYPE_H
2526 #include FT_GLYPH_H
2627 #include FT_SYNTHESIS_H
27 #ifdef CONFIG_HARFBUZZ
2828 #include <hb.h>
29 #endif
3029
3130 #include "ass.h"
3231 #include "ass_font.h"
5150 typedef struct {
5251 ASS_Image result;
5352 CompositeHashValue *source;
53 unsigned char *buffer;
5454 size_t ref_count;
5555 } ASS_ImagePriv;
5656
6262 double font_size_coeff; // font size multiplier
6363 double line_spacing; // additional line spacing (in frame pixels)
6464 double line_position; // vertical position for subtitles, 0-100 (0 = no change)
65 int top_margin; // height of top margin. Everything except toptitles is shifted down by top_margin.
65 int top_margin; // height of top margin. Video frame is shifted down by top_margin.
6666 int bottom_margin; // height of bottom margin. (frame_height - top_margin - bottom_margin) is original video height.
6767 int left_margin;
6868 int right_margin;
6969 int use_margins; // 0 - place all subtitles inside original frame
70 // 1 - use margins for placing toptitles and subtitles
70 // 1 - place subtitles (incl. toptitles) in full display frame incl. margins
7171 double par; // user defined pixel aspect ratio (0 = unset)
7272 ASS_Hinting hinting;
7373 ASS_ShapingLevel shaper;
9898 FilterDesc filter;
9999 uint32_t c[4]; // colors
100100 Effect effect_type;
101 int effect_timing; // time duration of current karaoke word
102 // after process_karaoke_effects: distance in pixels from the glyph origin.
101
102 // during render_and_combine_glyphs: distance in subpixels from the karaoke origin.
103 // after render_and_combine_glyphs: screen coordinate in pixels.
103104 // part of the glyph to the left of it is displayed in a different color.
104
105 int first_pos_x;
105 int effect_timing;
106
107 // karaoke origin: screen coordinate of leftmost post-transform control point x in subpixels
108 int32_t leftmost_x;
106109
107110 size_t bitmap_count, max_bitmap_count;
108111 BitmapRef *bitmaps;
109112
110113 int x, y;
111 ASS_Rect rect, rect_o;
112 size_t n_bm, n_bm_o;
113
114114 Bitmap *bm, *bm_o, *bm_s; // glyphs, outline, shadow bitmaps
115115 CompositeHashValue *image;
116116 } CombinedBitmapInfo;
117
118 typedef struct {
119 ASS_DVector scale, offset;
120 } ASS_Transform;
117121
118122 // describes a glyph
119123 // GlyphInfo and TextInfo are used for text centering and word-wrapping operations
120124 typedef struct glyph_info {
121125 unsigned symbol;
122 unsigned skip; // skip glyph when layouting text
126 bool skip; // skip glyph when layouting text
127 bool is_trimmed_whitespace;
123128 ASS_Font *font;
124129 int face_index;
125130 int glyph_index;
126 #ifdef CONFIG_HARFBUZZ
127131 hb_script_t script;
128 #else
129 int script;
130 #endif
131132 double font_size;
132 ASS_Drawing *drawing;
133 ASS_Outline *outline;
134 ASS_Outline *border[2];
133 char *drawing_text;
134 int drawing_scale;
135 int drawing_pbo;
136 OutlineHashValue *outline;
137 ASS_Transform transform;
135138 ASS_Rect bbox;
136139 ASS_Vector pos;
137140 ASS_Vector offset;
138141 char linebreak; // the first (leading) glyph of some line ?
142 bool starts_new_run;
139143 uint32_t c[4]; // colors
144 uint8_t a_pre_fade[4]; // alpha values before applying fades
140145 ASS_Vector advance; // 26.6
141146 ASS_Vector cluster_advance;
142 char effect; // the first (leading) glyph of some effect ?
143147 Effect effect_type;
144148 int effect_timing; // time duration of current karaoke word
145 // after process_karaoke_effects: distance in pixels from the glyph origin.
149 // after process_karaoke_effects: distance in subpixels from the karaoke origin.
146150 // part of the glyph to the left of it is displayed in a different color.
147151 int effect_skip_timing; // delay after the end of last karaoke word
148152 int asc, desc; // font max ascender and descender
153157 double frx, fry, frz; // rotation
154158 double fax, fay; // text shearing
155159 double scale_x, scale_y;
156 double orig_scale_x, orig_scale_y; // scale_x,y before fix_glyph_scaling
160 // amount of scale_x,y change due to fix_glyph_scaling
161 // scale_fix = before / after
162 double scale_fix;
157163 int border_style;
158164 double border_x, border_y;
159165 double hspacing;
166 int hspacing_scaled; // 26.6
160167 unsigned italic;
161168 unsigned bold;
162169 int flags;
163170
164171 int shape_run_id;
165172
166 BitmapHashKey hash_key;
167 BitmapHashValue *image;
173 ASS_Vector shift;
174 Bitmap *bm, *bm_o;
168175
169176 // next glyph in this cluster
170177 struct glyph_info *next;
183190 CombinedBitmapInfo *combined_bitmaps;
184191 unsigned n_bitmaps;
185192 double height;
193 int border_top;
194 int border_bottom;
195 int border_x;
186196 int max_glyphs;
187197 int max_lines;
188198 unsigned max_bitmaps;
193203 typedef struct {
194204 ASS_Event *event;
195205 ASS_Style *style;
196 int parsed_tags;
197206
198207 ASS_Font *font;
199208 double font_size;
209 int parsed_tags;
200210 int flags; // decoration flags (underline/strike-through)
201211
202212 int alignment; // alignment overrides go here; if zero, style value will be used
203213 int justify; // justify instructions
204214 double frx, fry, frz;
205215 double fax, fay; // text shearing
206 enum {
207 EVENT_NORMAL, // "normal" top-, sub- or mid- title
208 EVENT_POSITIONED, // happens after pos(,), margins are ignored
209 EVENT_HSCROLL, // "Banner" transition effect, text_width is unlimited
210 EVENT_VSCROLL // "Scroll up", "Scroll down" transition effects
211 } evt_type;
212216 double pos_x, pos_y; // position
213217 double org_x, org_y; // origin
214 char have_origin; // origin is explicitly defined; if 0, get_base_point() is used
215218 double scale_x, scale_y;
216219 double hspacing; // distance between letters, in pixels
217 int border_style;
218220 double border_x; // outline width
219221 double border_y;
222 enum {
223 EVENT_NORMAL = 0, // "normal" top-, sub- or mid- title
224 EVENT_POSITIONED = 1, // happens after \pos or \move, margins are ignored
225 EVENT_HSCROLL = 2, // "Banner" transition effect, text_width is unlimited
226 EVENT_VSCROLL = 4 // "Scroll up", "Scroll down" transition effects
227 } evt_type;
228 int border_style;
220229 uint32_t c[4]; // colors(Primary, Secondary, so on) in RGBA
221230 int clip_x0, clip_y0, clip_x1, clip_y1;
231 char have_origin; // origin is explicitly defined; if 0, get_base_point() is used
222232 char clip_mode; // 1 = iclip
223233 char detect_collisions;
234 char be; // blur edges
224235 int fade; // alpha from \fad
225 char be; // blur edges
226236 double blur; // gaussian blur
227237 double shadow_x;
228238 double shadow_y;
239 double pbo; // drawing baseline offset
240 char *clip_drawing_text;
241
242 // used to store RenderContext.style when doing selective style overrides
243 ASS_Style override_style_temp_storage;
244
229245 int drawing_scale; // currently reading: regular text if 0, drawing otherwise
230 double pbo; // drawing baseline offset
231 ASS_Drawing *clip_drawing; // clip vector
246 int clip_drawing_scale;
232247 int clip_drawing_mode; // 0 = regular clip, 1 = inverse clip
233248
234249 Effect effect_type;
242257 SCROLL_BT
243258 } scroll_direction; // for EVENT_HSCROLL, EVENT_VSCROLL
244259 int scroll_shift;
260 int scroll_y0, scroll_y1;
245261
246262 // face properties
247263 char *family;
257273 int apply_font_scale;
258274 // whether this is assumed to be explicitly positioned
259275 int explicit;
260
261 // used to store RenderContext.style when doing selective style overrides
262 ASS_Style override_style_temp_storage;
263276 } RenderContext;
264277
265278 typedef struct {
292305 int width, height; // screen dimensions
293306 int orig_height; // frame height ( = screen height - margins )
294307 int orig_width; // frame width ( = screen width - margins )
295 int orig_height_nocrop; // frame height ( = screen height - margins + cropheight)
296 int orig_width_nocrop; // frame width ( = screen width - margins + cropwidth)
308 double fit_height; // frame height without zoom & pan (fit to screen & letterboxed)
309 double fit_width; // frame width without zoom & pan (fit to screen & letterboxed)
297310 ASS_Track *track;
298311 long long time; // frame's timestamp, ms
299312 double font_scale;
323336 int y1;
324337 } Rect;
325338
326 typedef struct {
327 int a, b; // top and height
328 int ha, hb; // left and width
329 } Segment;
330
331339 void reset_render_context(ASS_Renderer *render_priv, ASS_Style *style);
332340 void ass_frame_ref(ASS_Image *img);
333341 void ass_frame_unref(ASS_Image *img);
3636 settings->right_margin;
3737 priv->orig_height = settings->frame_height - settings->top_margin -
3838 settings->bottom_margin;
39 priv->orig_width_nocrop =
40 settings->frame_width - FFMAX(settings->left_margin, 0) -
41 FFMAX(settings->right_margin, 0);
42 priv->orig_height_nocrop =
43 settings->frame_height - FFMAX(settings->top_margin, 0) -
44 FFMAX(settings->bottom_margin, 0);
39 priv->fit_width =
40 (long long) priv->orig_width * priv->height >=
41 (long long) priv->orig_height * priv->width ?
42 priv->width :
43 (double) priv->orig_width * priv->height / priv->orig_height;
44 priv->fit_height =
45 (long long) priv->orig_width * priv->height <=
46 (long long) priv->orig_height * priv->width ?
47 priv->height :
48 (double) priv->orig_height * priv->width / priv->orig_width;
4549 }
4650
4751 void ass_set_frame_size(ASS_Renderer *priv, int w, int h)
6569
6670 void ass_set_shaper(ASS_Renderer *priv, ASS_ShapingLevel level)
6771 {
68 #ifdef CONFIG_HARFBUZZ
6972 // select the complex shaper for illegal values
7073 if (level == ASS_SHAPING_SIMPLE || level == ASS_SHAPING_COMPLEX)
7174 priv->settings.shaper = level;
7275 else
7376 priv->settings.shaper = ASS_SHAPING_COMPLEX;
74 #endif
7577 }
7678
7779 void ass_set_margins(ASS_Renderer *priv, int t, int b, int l, int r)
2626 #include <limits.h>
2727 #include <stdbool.h>
2828
29 #ifdef CONFIG_HARFBUZZ
30 #include <hb-ft.h>
29 #include <ft2build.h>
30 #include FT_FREETYPE_H
31 #include FT_TRUETYPE_TABLES_H
3132 enum {
3233 VERT = 0,
3334 VKNA,
3637 CLIG
3738 };
3839 #define NUM_FEATURES 5
39 #endif
4040
4141 struct ass_shaper {
4242 ASS_ShapingLevel shaping_level;
4949 FriBidiStrIndex *cmap;
5050 FriBidiParType base_direction;
5151
52 #ifdef CONFIG_HARFBUZZ
5352 // OpenType features
5453 int n_features;
5554 hb_feature_t *features;
5756
5857 // Glyph metrics cache, to speed up shaping
5958 Cache *metrics_cache;
59
60 #ifdef USE_FRIBIDI_EX_API
61 FriBidiBracketType *btypes;
62 bool bidi_brackets;
6063 #endif
6164 };
6265
63 #ifdef CONFIG_HARFBUZZ
6466 struct ass_shaper_metrics_data {
6567 Cache *metrics_cache;
6668 GlyphMetricsHashKey hash_key;
7274 hb_font_funcs_t *font_funcs[ASS_FONT_MAX_FACES];
7375 struct ass_shaper_metrics_data *metrics_data[ASS_FONT_MAX_FACES];
7476 };
75 #endif
7677
7778 /**
7879 * \brief Print version information
8182 {
8283 ass_msg(lib, MSGL_INFO, "Shaper: FriBidi "
8384 FRIBIDI_VERSION " (SIMPLE)"
84 #ifdef CONFIG_HARFBUZZ
8585 " HarfBuzz-ng %s (COMPLEX)", hb_version_string()
86 #endif
8786 );
8887 }
8988
9695 if (new_size > shaper->n_glyphs) {
9796 if (!ASS_REALLOC_ARRAY(shaper->event_text, new_size) ||
9897 !ASS_REALLOC_ARRAY(shaper->ctypes, new_size) ||
98 #ifdef USE_FRIBIDI_EX_API
99 (shaper->bidi_brackets && !ASS_REALLOC_ARRAY(shaper->btypes, new_size)) ||
100 #endif
99101 !ASS_REALLOC_ARRAY(shaper->emblevels, new_size) ||
100102 !ASS_REALLOC_ARRAY(shaper->cmap, new_size))
101103 return false;
109111 */
110112 void ass_shaper_free(ASS_Shaper *shaper)
111113 {
112 #ifdef CONFIG_HARFBUZZ
113114 ass_cache_done(shaper->metrics_cache);
114115 free(shaper->features);
115 #endif
116116 free(shaper->event_text);
117117 free(shaper->ctypes);
118 #ifdef USE_FRIBIDI_EX_API
119 free(shaper->btypes);
120 #endif
118121 free(shaper->emblevels);
119122 free(shaper->cmap);
120123 free(shaper);
122125
123126 void ass_shaper_empty_cache(ASS_Shaper *shaper)
124127 {
125 #ifdef CONFIG_HARFBUZZ
126128 ass_cache_empty(shaper->metrics_cache);
127 #endif
128129 }
129130
130131 void ass_shaper_font_data_free(ASS_ShaperFontData *priv)
131132 {
132 #ifdef CONFIG_HARFBUZZ
133133 int i;
134134 for (i = 0; i < ASS_FONT_MAX_FACES; i++)
135135 if (priv->fonts[i]) {
138138 hb_font_funcs_destroy(priv->font_funcs[i]);
139139 }
140140 free(priv);
141 #endif
142 }
143
144 #ifdef CONFIG_HARFBUZZ
141 }
142
145143 /**
146144 * \brief set up the HarfBuzz OpenType feature list with some
147145 * standard features.
209207 *
210208 */
211209
212 GlyphMetricsHashValue *
213 get_cached_metrics(struct ass_shaper_metrics_data *metrics, FT_Face face,
210 FT_Glyph_Metrics *
211 get_cached_metrics(struct ass_shaper_metrics_data *metrics,
214212 hb_codepoint_t unicode, hb_codepoint_t glyph)
215213 {
216 GlyphMetricsHashValue *val;
217 metrics->hash_key.glyph_index = glyph;
218 if (ass_cache_get(metrics->metrics_cache, &metrics->hash_key, &val)) {
219 if (val->metrics.width >= 0)
220 return val;
221 ass_cache_dec_ref(val);
222 return NULL;
223 }
224 if (!val)
225 return NULL;
226
227 int load_flags = FT_LOAD_DEFAULT | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH
228 | FT_LOAD_IGNORE_TRANSFORM;
229
230 if (FT_Load_Glyph(face, glyph, load_flags)) {
231 val->metrics.width = -1;
232 ass_cache_commit(val, 1);
233 ass_cache_dec_ref(val);
234 return NULL;
235 }
236
237 memcpy(&val->metrics, &face->glyph->metrics, sizeof(FT_Glyph_Metrics));
238
214 bool rotate = false;
239215 // if @font rendering is enabled and the glyph should be rotated,
240216 // make cached_h_advance pick up the right advance later
241217 if (metrics->vertical && unicode >= VERTICAL_LOWER_BOUND)
242 val->metrics.horiAdvance = val->metrics.vertAdvance;
243
244 ass_cache_commit(val, 1);
245 return val;
218 rotate = true;
219
220 metrics->hash_key.glyph_index = glyph;
221 FT_Glyph_Metrics *val = ass_cache_get(metrics->metrics_cache, &metrics->hash_key,
222 rotate ? metrics : NULL);
223 if (!val)
224 return NULL;
225 if (val->width >= 0)
226 return val;
227 ass_cache_dec_ref(val);
228 return NULL;
229 }
230
231 size_t ass_glyph_metrics_construct(void *key, void *value, void *priv)
232 {
233 GlyphMetricsHashKey *k = key;
234 FT_Glyph_Metrics *v = value;
235
236 int load_flags = FT_LOAD_DEFAULT | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH
237 | FT_LOAD_IGNORE_TRANSFORM;
238
239 FT_Face face = k->font->faces[k->face_index];
240 if (FT_Load_Glyph(face, k->glyph_index, load_flags)) {
241 v->width = -1;
242 return 1;
243 }
244
245 memcpy(v, &face->glyph->metrics, sizeof(FT_Glyph_Metrics));
246
247 if (priv) // rotate
248 v->horiAdvance = v->vertAdvance;
249
250 return 1;
251 }
252
253 static hb_blob_t*
254 get_reference_table(hb_face_t *hbface, hb_tag_t tag, void *font_data)
255 {
256 FT_Face face = font_data;
257 FT_ULong len = 0;
258
259 if (FT_Load_Sfnt_Table(face, tag, 0, NULL, &len) != FT_Err_Ok)
260 return NULL;
261
262 char *buf = malloc(len);
263 if (!buf)
264 return NULL;
265
266 if (FT_Load_Sfnt_Table(face, tag, 0, (FT_Byte*)buf, &len) != FT_Err_Ok) {
267 free(buf);
268 return NULL;
269 }
270
271 hb_blob_t *blob = hb_blob_create(buf, len, HB_MEMORY_MODE_WRITABLE, buf, free);
272 if (!blob)
273 free(buf);
274
275 return blob;
246276 }
247277
248278 static hb_bool_t
249 get_glyph(hb_font_t *font, void *font_data, hb_codepoint_t unicode,
250 hb_codepoint_t variation, hb_codepoint_t *glyph, void *user_data)
279 get_glyph_nominal(hb_font_t *font, void *font_data, hb_codepoint_t unicode,
280 hb_codepoint_t *glyph, void *user_data)
251281 {
252282 FT_Face face = font_data;
253283 struct ass_shaper_metrics_data *metrics_priv = user_data;
254284
255 if (variation)
256 *glyph = FT_Face_GetCharVariantIndex(face, ass_font_index_magic(face, unicode), variation);
257 else
258 *glyph = FT_Get_Char_Index(face, ass_font_index_magic(face, unicode));
285 *glyph = FT_Get_Char_Index(face, ass_font_index_magic(face, unicode));
259286 if (!*glyph)
260287 return false;
261288
262289 // rotate glyph advances for @fonts while we still know the Unicode codepoints
263 GlyphMetricsHashValue *metrics = get_cached_metrics(metrics_priv, face, unicode, *glyph);
290 FT_Glyph_Metrics *metrics = get_cached_metrics(metrics_priv, unicode, *glyph);
291 ass_cache_dec_ref(metrics);
292 return true;
293 }
294
295 static hb_bool_t
296 get_glyph_variation(hb_font_t *font, void *font_data, hb_codepoint_t unicode,
297 hb_codepoint_t variation, hb_codepoint_t *glyph, void *user_data)
298 {
299 FT_Face face = font_data;
300 struct ass_shaper_metrics_data *metrics_priv = user_data;
301
302 *glyph = FT_Face_GetCharVariantIndex(face, ass_font_index_magic(face, unicode), variation);
303 if (!*glyph)
304 return false;
305
306 // rotate glyph advances for @fonts while we still know the Unicode codepoints
307 FT_Glyph_Metrics *metrics = get_cached_metrics(metrics_priv, unicode, *glyph);
264308 ass_cache_dec_ref(metrics);
265309 return true;
266310 }
269313 cached_h_advance(hb_font_t *font, void *font_data, hb_codepoint_t glyph,
270314 void *user_data)
271315 {
272 FT_Face face = font_data;
273316 struct ass_shaper_metrics_data *metrics_priv = user_data;
274 GlyphMetricsHashValue *metrics = get_cached_metrics(metrics_priv, face, 0, glyph);
317 FT_Glyph_Metrics *metrics = get_cached_metrics(metrics_priv, 0, glyph);
275318 if (!metrics)
276319 return 0;
277320
278 hb_position_t advance = metrics->metrics.horiAdvance;
321 hb_position_t advance = metrics->horiAdvance;
279322 ass_cache_dec_ref(metrics);
280323 return advance;
281324 }
284327 cached_v_advance(hb_font_t *font, void *font_data, hb_codepoint_t glyph,
285328 void *user_data)
286329 {
287 FT_Face face = font_data;
288330 struct ass_shaper_metrics_data *metrics_priv = user_data;
289 GlyphMetricsHashValue *metrics = get_cached_metrics(metrics_priv, face, 0, glyph);
331 FT_Glyph_Metrics *metrics = get_cached_metrics(metrics_priv, 0, glyph);
290332 if (!metrics)
291333 return 0;
292334
293 hb_position_t advance = metrics->metrics.vertAdvance;
335 hb_position_t advance = metrics->vertAdvance;
294336 ass_cache_dec_ref(metrics);
295337 return advance;
296338 }
306348 cached_v_origin(hb_font_t *font, void *font_data, hb_codepoint_t glyph,
307349 hb_position_t *x, hb_position_t *y, void *user_data)
308350 {
309 FT_Face face = font_data;
310351 struct ass_shaper_metrics_data *metrics_priv = user_data;
311 GlyphMetricsHashValue *metrics = get_cached_metrics(metrics_priv, face, 0, glyph);
352 FT_Glyph_Metrics *metrics = get_cached_metrics(metrics_priv, 0, glyph);
312353 if (!metrics)
313354 return false;
314355
315 *x = metrics->metrics.horiBearingX - metrics->metrics.vertBearingX;
316 *y = metrics->metrics.horiBearingY - (-metrics->metrics.vertBearingY);
356 *x = metrics->horiBearingX - metrics->vertBearingX;
357 *y = metrics->horiBearingY + metrics->vertBearingY;
317358 ass_cache_dec_ref(metrics);
318359 return true;
319360 }
342383 cached_extents(hb_font_t *font, void *font_data, hb_codepoint_t glyph,
343384 hb_glyph_extents_t *extents, void *user_data)
344385 {
345 FT_Face face = font_data;
346386 struct ass_shaper_metrics_data *metrics_priv = user_data;
347 GlyphMetricsHashValue *metrics = get_cached_metrics(metrics_priv, face, 0, glyph);
387 FT_Glyph_Metrics *metrics = get_cached_metrics(metrics_priv, 0, glyph);
348388 if (!metrics)
349389 return false;
350390
351 extents->x_bearing = metrics->metrics.horiBearingX;
352 extents->y_bearing = metrics->metrics.horiBearingY;
353 extents->width = metrics->metrics.width;
354 extents->height = -metrics->metrics.height;
391 extents->x_bearing = metrics->horiBearingX;
392 extents->y_bearing = metrics->horiBearingY;
393 extents->width = metrics->width;
394 extents->height = -metrics->height;
355395 ass_cache_dec_ref(metrics);
356396 return true;
357397 }
389429
390430 if (!font->shaper_priv)
391431 font->shaper_priv = calloc(sizeof(ASS_ShaperFontData), 1);
392
432 if (!font->shaper_priv)
433 return NULL;
393434
394435 hb_fonts = font->shaper_priv->fonts;
395436 if (!hb_fonts[info->face_index]) {
396 hb_fonts[info->face_index] =
397 hb_ft_font_create(font->faces[info->face_index], NULL);
437 FT_Face face = font->faces[info->face_index];
438 hb_face_t *hb_face = hb_face_create_for_tables(get_reference_table, face, NULL);
439 if (!hb_face)
440 return NULL;
441 hb_face_set_index(hb_face, face->face_index);
442 hb_face_set_upem(hb_face, face->units_per_EM);
443
444 hb_font_t *hb_font = hb_fonts[info->face_index] = hb_font_create(hb_face);
445 hb_face_destroy(hb_face);
446 if (!hb_font)
447 return NULL;
448
449 hb_font_set_scale(hb_font,
450 (int)(((uint64_t)face->size->metrics.x_scale * face->units_per_EM + (1<<15)) >> 16),
451 (int)(((uint64_t)face->size->metrics.y_scale * face->units_per_EM + (1<<15)) >> 16));
398452
399453 // set up cached metrics access
400 font->shaper_priv->metrics_data[info->face_index] =
401 calloc(sizeof(struct ass_shaper_metrics_data), 1);
402454 struct ass_shaper_metrics_data *metrics =
403 font->shaper_priv->metrics_data[info->face_index];
455 font->shaper_priv->metrics_data[info->face_index] =
456 calloc(sizeof(struct ass_shaper_metrics_data), 1);
457 if (!metrics)
458 return NULL;
404459 metrics->metrics_cache = shaper->metrics_cache;
405460 metrics->vertical = info->font->desc.vertical;
406461
407462 hb_font_funcs_t *funcs = hb_font_funcs_create();
463 if (!funcs)
464 return NULL;
408465 font->shaper_priv->font_funcs[info->face_index] = funcs;
409 hb_font_funcs_set_glyph_func(funcs, get_glyph,
466 hb_font_funcs_set_nominal_glyph_func(funcs, get_glyph_nominal,
467 metrics, NULL);
468 hb_font_funcs_set_variation_glyph_func(funcs, get_glyph_variation,
410469 metrics, NULL);
411470 hb_font_funcs_set_glyph_h_advance_func(funcs, cached_h_advance,
412471 metrics, NULL);
424483 metrics, NULL);
425484 hb_font_funcs_set_glyph_contour_point_func(funcs, get_contour_point,
426485 metrics, NULL);
427 hb_font_set_funcs(hb_fonts[info->face_index], funcs,
428 font->faces[info->face_index], NULL);
486 hb_font_set_funcs(hb_font, funcs, face, NULL);
429487 }
430488
431489 ass_face_set_size(font->faces[info->face_index], info->font_size);
437495 metrics->hash_key.font = info->font;
438496 metrics->hash_key.face_index = info->face_index;
439497 metrics->hash_key.size = info->font_size;
440 metrics->hash_key.scale_x = double_to_d6(info->scale_x);
441 metrics->hash_key.scale_y = double_to_d6(info->scale_y);
442498
443499 return hb_fonts[info->face_index];
444500 }
566622
567623 // if we have more than one glyph per cluster, allocate a new one
568624 // and attach to the root glyph
569 if (info->skip == 0) {
625 if (!info->skip) {
570626 while (info->next)
571627 info = info->next;
572628 info->next = malloc(sizeof(GlyphInfo));
579635 }
580636
581637 // set position and advance
582 info->skip = 0;
638 info->skip = false;
583639 info->glyph_index = glyph_info[j].codepoint;
584640 info->offset.x = pos[j].x_offset * info->scale_x;
585641 info->offset.y = -pos[j].y_offset * info->scale_y;
597653 * \param glyphs glyph clusters
598654 * \param len number of clusters
599655 */
600 static void shape_harfbuzz(ASS_Shaper *shaper, GlyphInfo *glyphs, size_t len)
656 static bool shape_harfbuzz(ASS_Shaper *shaper, GlyphInfo *glyphs, size_t len)
601657 {
602658 int i;
603659 hb_buffer_t *buf = hb_buffer_create();
605661
606662 // Initialize: skip all glyphs, this is undone later as needed
607663 for (i = 0; i < len; i++)
608 glyphs[i].skip = 1;
664 glyphs[i].skip = true;
609665
610666 for (i = 0; i < len; i++) {
667 if (glyphs[i].drawing_text) {
668 glyphs[i].skip = false;
669 continue;
670 }
671
611672 int offset = i;
612673 hb_font_t *font = get_hb_font(shaper, glyphs + offset);
613 int level = glyphs[offset].shape_run_id;
614 int direction = shaper->emblevels[offset] % 2;
674 if (!font)
675 return false;
676 int run_id = glyphs[offset].shape_run_id;
677 int level = shaper->emblevels[offset];
615678
616679 // advance in text until end of run
617 while (i < (len - 1) && level == glyphs[i+1].shape_run_id)
680 while (i < (len - 1) && run_id == glyphs[i + 1].shape_run_id &&
681 level == shaper->emblevels[i + 1])
618682 i++;
619683
620684 hb_buffer_pre_allocate(buf, i - offset + 1);
621685 hb_buffer_add_utf32(buf, shaper->event_text + offset, i - offset + 1,
622686 0, i - offset + 1);
623687
624 props.direction = direction ? HB_DIRECTION_RTL : HB_DIRECTION_LTR;
688 props.direction = FRIBIDI_LEVEL_IS_RTL(level) ?
689 HB_DIRECTION_RTL : HB_DIRECTION_LTR;
625690 props.script = glyphs[offset].script;
626691 props.language = hb_shaper_get_run_language(shaper, props.script);
627692 hb_buffer_set_segment_properties(buf, &props);
634699 }
635700
636701 hb_buffer_destroy(buf);
702
703 return true;
637704 }
638705
639706 /**
685752 }
686753 }
687754 }
688 #endif
689755
690756 /**
691757 * \brief Shape event text with FriBidi. Does mirroring and simple
719785 * \param shaper shaper instance
720786 * \param kern toggle kerning
721787 */
722 void ass_shaper_set_kerning(ASS_Shaper *shaper, int kern)
723 {
724 #ifdef CONFIG_HARFBUZZ
725 shaper->features[KERN].value = !!kern;
726 #endif
788 void ass_shaper_set_kerning(ASS_Shaper *shaper, bool kern)
789 {
790 shaper->features[KERN].value = kern;
727791 }
728792
729793 /**
735799 int i;
736800 int shape_run = 0;
737801
738 #ifdef CONFIG_HARFBUZZ
739802 ass_shaper_determine_script(shaper, glyphs, len);
740 #endif
741803
742804 // find appropriate fonts for the shape runs
743805 for (i = 0; i < len; i++) {
744 GlyphInfo *last = glyphs + i - 1;
745806 GlyphInfo *info = glyphs + i;
746 // skip drawings
747 if (info->symbol == 0xfffc)
748 continue;
749 // set size and get glyph index
750 ass_font_get_index(render_priv->fontselect, info->font,
751 info->symbol, &info->face_index, &info->glyph_index);
752 // shape runs break on: xbord, ybord, xshad, yshad,
753 // all four colors, all four alphas, be, blur, fn, fs,
754 // fscx, fscy, fsp, bold, italic, underline, strikeout,
755 // frx, fry, frz, fax, fay, karaoke start, karaoke type,
756 // and on every line break
757 if (i > 0 && (last->font != info->font ||
807 if (!info->drawing_text) {
808 // set size and get glyph index
809 ass_font_get_index(render_priv->fontselect, info->font,
810 info->symbol, &info->face_index, &info->glyph_index);
811 }
812 if (i > 0) {
813 GlyphInfo *last = glyphs + i - 1;
814 if ((last->font != info->font ||
758815 last->face_index != info->face_index ||
759816 last->script != info->script ||
760 last->font_size != info->font_size ||
761 last->c[0] != info->c[0] ||
762 last->c[1] != info->c[1] ||
763 last->c[2] != info->c[2] ||
764 last->c[3] != info->c[3] ||
765 last->be != info->be ||
766 last->blur != info->blur ||
767 last->shadow_x != info->shadow_x ||
768 last->shadow_y != info->shadow_y ||
769 last->frx != info->frx ||
770 last->fry != info->fry ||
771 last->frz != info->frz ||
772 last->fax != info->fax ||
773 last->fay != info->fay ||
774 last->scale_x != info->scale_x ||
775 last->scale_y != info->scale_y ||
776 last->border_style != info->border_style ||
777 last->border_x != info->border_x ||
778 last->border_y != info->border_y ||
779 last->hspacing != info->hspacing ||
780 last->italic != info->italic ||
781 last->bold != info->bold ||
817 info->starts_new_run ||
782818 last->flags != info->flags))
783 shape_run++;
819 shape_run++;
820 }
784821 info->shape_run_id = shape_run;
785822 }
786823 }
801838 */
802839 void ass_shaper_set_language(ASS_Shaper *shaper, const char *code)
803840 {
804 #ifdef CONFIG_HARFBUZZ
805841 hb_language_t lang;
806842
807843 if (code)
810846 lang = HB_LANGUAGE_INVALID;
811847
812848 shaper->language = lang;
849 }
850
851 /**
852 * Set shaping level. Essentially switches between FriBidi and HarfBuzz.
853 */
854 void ass_shaper_set_level(ASS_Shaper *shaper, ASS_ShapingLevel level)
855 {
856 shaper->shaping_level = level;
857 }
858
859 #ifdef USE_FRIBIDI_EX_API
860 void ass_shaper_set_bidi_brackets(ASS_Shaper *shaper, bool match_brackets)
861 {
862 shaper->bidi_brackets = match_brackets;
863 }
813864 #endif
814 }
815
816 /**
817 * Set shaping level. Essentially switches between FriBidi and HarfBuzz.
818 */
819 void ass_shaper_set_level(ASS_Shaper *shaper, ASS_ShapingLevel level)
820 {
821 shaper->shaping_level = level;
822 }
823865
824866 /**
825867 * \brief Remove all zero-width invisible characters from the text.
834876 // Skip direction override control characters
835877 if ((glyphs[i].symbol <= 0x202e && glyphs[i].symbol >= 0x202a)
836878 || (glyphs[i].symbol <= 0x200f && glyphs[i].symbol >= 0x200b)
837 || (glyphs[i].symbol <= 0x2063 && glyphs[i].symbol >= 0x2060)
879 || (glyphs[i].symbol <= 0x206f && glyphs[i].symbol >= 0x2060)
880 || (glyphs[i].symbol <= 0xfe0f && glyphs[i].symbol >= 0xfe00)
881 || (glyphs[i].symbol <= 0xe01ef && glyphs[i].symbol >= 0xe0100)
882 || (glyphs[i].symbol <= 0x180f && glyphs[i].symbol >= 0x180b)
883 || glyphs[i].symbol == 0x061c
838884 || glyphs[i].symbol == 0xfeff
839885 || glyphs[i].symbol == 0x00ad
840886 || glyphs[i].symbol == 0x034f) {
841887 glyphs[i].symbol = 0;
842 glyphs[i].skip++;
888 glyphs[i].skip = true;
843889 }
844890 }
845891 }
849895 * \param text_info event's text
850896 * \return success, when 0
851897 */
852 int ass_shaper_shape(ASS_Shaper *shaper, TextInfo *text_info)
898 bool ass_shaper_shape(ASS_Shaper *shaper, TextInfo *text_info)
853899 {
854900 int i, ret, last_break;
855901 FriBidiParType dir;
856902 GlyphInfo *glyphs = text_info->glyphs;
857903
858904 if (!check_allocations(shaper, text_info->length))
859 return -1;
905 return false;
860906
861907 // Get bidi character types and embedding levels
862908 last_break = 0;
867913 dir = shaper->base_direction;
868914 fribidi_get_bidi_types(shaper->event_text + last_break,
869915 i - last_break + 1, shaper->ctypes + last_break);
916 #ifdef USE_FRIBIDI_EX_API
917 FriBidiBracketType *btypes = NULL;
918 if (shaper->bidi_brackets) {
919 btypes = shaper->btypes + last_break;
920 fribidi_get_bracket_types(shaper->event_text + last_break,
921 i - last_break + 1, shaper->ctypes + last_break,
922 btypes);
923 }
924 ret = fribidi_get_par_embedding_levels_ex(
925 shaper->ctypes + last_break, btypes,
926 i - last_break + 1, &dir, shaper->emblevels + last_break);
927 #else
870928 ret = fribidi_get_par_embedding_levels(shaper->ctypes + last_break,
871929 i - last_break + 1, &dir, shaper->emblevels + last_break);
930 #endif
872931 if (ret == 0)
873 return -1;
932 return false;
874933 last_break = i + 1;
875934 }
876935 }
877936
878 // add embedding levels to shape runs for final runs
879 for (i = 0; i < text_info->length; i++) {
880 glyphs[i].shape_run_id += shaper->emblevels[i];
881 }
882
883 #ifdef CONFIG_HARFBUZZ
884937 switch (shaper->shaping_level) {
885938 case ASS_SHAPING_SIMPLE:
886939 shape_fribidi(shaper, glyphs, text_info->length);
887940 ass_shaper_skip_characters(text_info);
888 break;
941 return true;
889942 case ASS_SHAPING_COMPLEX:
890 shape_harfbuzz(shaper, glyphs, text_info->length);
891 break;
892 }
893 #else
894 shape_fribidi(shaper, glyphs, text_info->length);
895 ass_shaper_skip_characters(text_info);
896 #endif
897
898 return 0;
899 }
900
901 /**
902 * \brief Create a new shaper instance and preallocate data structures
903 * \param prealloc preallocation size
904 */
905 ASS_Shaper *ass_shaper_new(size_t prealloc)
943 default:
944 return shape_harfbuzz(shaper, glyphs, text_info->length);
945 }
946 }
947
948 /**
949 * \brief Create a new shaper instance
950 */
951 ASS_Shaper *ass_shaper_new(void)
906952 {
907953 ASS_Shaper *shaper = calloc(sizeof(*shaper), 1);
908954 if (!shaper)
909955 return NULL;
910956
911957 shaper->base_direction = FRIBIDI_PAR_ON;
912 if (!check_allocations(shaper, prealloc))
913 goto error;
914
915 #ifdef CONFIG_HARFBUZZ
958
916959 if (!init_features(shaper))
917960 goto error;
918961 shaper->metrics_cache = ass_glyph_metrics_cache_create();
919962 if (!shaper->metrics_cache)
920963 goto error;
921 #endif
922964
923965 return shaper;
924966
2121 typedef struct ass_shaper ASS_Shaper;
2222
2323 #include <fribidi.h>
24 #include <stdbool.h>
2425 #include "ass_render.h"
2526
27 #if FRIBIDI_MAJOR_VERSION >= 1
28 #define USE_FRIBIDI_EX_API
29 #endif
30
2631 void ass_shaper_info(ASS_Library *lib);
27 ASS_Shaper *ass_shaper_new(size_t prealloc);
32 ASS_Shaper *ass_shaper_new(void);
2833 void ass_shaper_free(ASS_Shaper *shaper);
2934 void ass_shaper_empty_cache(ASS_Shaper *shaper);
30 void ass_shaper_set_kerning(ASS_Shaper *shaper, int kern);
35 void ass_shaper_set_kerning(ASS_Shaper *shaper, bool kern);
3136 void ass_shaper_find_runs(ASS_Shaper *shaper, ASS_Renderer *render_priv,
3237 GlyphInfo *glyphs, size_t len);
3338 void ass_shaper_set_base_direction(ASS_Shaper *shaper, FriBidiParType dir);
3439 void ass_shaper_set_language(ASS_Shaper *shaper, const char *code);
3540 void ass_shaper_set_level(ASS_Shaper *shaper, ASS_ShapingLevel level);
36 int ass_shaper_shape(ASS_Shaper *shaper, TextInfo *text_info);
41 #ifdef USE_FRIBIDI_EX_API
42 void ass_shaper_set_bidi_brackets(ASS_Shaper *shaper, bool match_brackets);
43 #endif
44 bool ass_shaper_shape(ASS_Shaper *shaper, TextInfo *text_info);
3745 void ass_shaper_cleanup(ASS_Shaper *shaper, TextInfo *text_info);
3846 FriBidiStrIndex *ass_shaper_reorder(ASS_Shaper *shaper, TextInfo *text_info);
3947 FriBidiParType resolve_base_direction(int font_encoding);
258258 } else if (exp > ((size_t) -1 - (*p - '0')) / 10) {
259259 expWraparound = 1;
260260 }
261 exp = exp * 10 + (*p - '0');
261 exp = exp * 10u + (*p - '0');
262262 p += 1;
263263 }
264264 if (expSign == fracExpSign) {
165165 YCBCR_SMPTE240M_PC,
166166 YCBCR_FCC_TV,
167167 YCBCR_FCC_PC
168 // New enum values can be added here in new ABI-compatible library releases.
168169 } ASS_YCbCrMatrix;
169170
170171 /*
204205
205206 ASS_Library *library;
206207 ASS_ParserPriv *parser_priv;
208
209 // New fields can be added here in new ABI-compatible library releases.
207210 } ASS_Track;
208211
209212 #endif /* LIBASS_TYPES_H */
4545 {
4646 uint32_t eax = 1, ebx, ecx, edx;
4747 ass_get_cpuid(&eax, &ebx, &ecx, &edx);
48 if(!(ecx & (1 << 27))) // not OSXSAVE
48 if (!(ecx & (1 << 27))) // not OSXSAVE
4949 return 0;
5050 uint32_t misc = ecx;
5151 ass_get_xgetbv(0, &eax, &edx);
52 if((eax & 0x6) != 0x6)
52 if ((eax & 0x6) != 0x6)
5353 return 0;
5454 eax = 0;
5555 ass_get_cpuid(&eax, &ebx, &ecx, &edx);
153153 *str = p;
154154 }
155155
156 int mystrtoi(char **p, int *res)
157 {
158 char *start = *p;
159 double temp_res = ass_strtod(*p, p);
160 *res = (int) (temp_res + (temp_res > 0 ? 0.5 : -0.5));
161 return *p != start;
162 }
163
164 int mystrtoll(char **p, long long *res)
165 {
166 char *start = *p;
167 double temp_res = ass_strtod(*p, p);
168 *res = (long long) (temp_res + (temp_res > 0 ? 0.5 : -0.5));
169 return *p != start;
170 }
171
172 int mystrtod(char **p, double *res)
173 {
174 char *start = *p;
175 *res = ass_strtod(*p, p);
176 return *p != start;
177 }
178
179 int mystrtoi32(char **p, int base, int32_t *res)
180 {
181 char *start = *p;
182 long long temp_res = strtoll(*p, p, base);
183 *res = FFMINMAX(temp_res, INT32_MIN, INT32_MAX);
184 return *p != start;
185 }
186
187 static int read_digits(char **str, int base, uint32_t *res)
156 static int read_digits(char **str, unsigned base, uint32_t *res)
188157 {
189158 char *p = *str;
190159 char *start = p;
191160 uint32_t val = 0;
192161
193162 while (1) {
194 int digit;
163 unsigned digit;
195164 if (*p >= '0' && *p < FFMIN(base, 10) + '0')
196165 digit = *p - '0';
197166 else if (*p >= 'a' && *p < base - 10 + 'a')
214183 * Follows the rules for strtoul but reduces the number modulo 2**32
215184 * instead of saturating it to 2**32 - 1.
216185 */
217 static int mystrtou32_modulo(char **p, int base, uint32_t *res)
186 static int mystrtou32_modulo(char **p, unsigned base, uint32_t *res)
218187 {
219188 // This emulates scanf with %d or %x format as it works on
220189 // Windows, because that's what is used by VSFilter. In practice,
270239 uint32_t parse_color_header(char *str)
271240 {
272241 uint32_t color = 0;
273 int base;
242 unsigned base;
274243
275244 if (!ass_strncasecmp(str, "&h", 2) || !ass_strncasecmp(str, "0x", 2)) {
276245 str += 2;
7777
7878 void skip_spaces(char **str);
7979 void rskip_spaces(char **str, char *limit);
80 int mystrtoi(char **p, int *res);
81 int mystrtoll(char **p, long long *res);
82 int mystrtod(char **p, double *res);
83 int mystrtoi32(char **p, int base, int32_t *res);
8480 int32_t parse_alpha_tag(char *str);
8581 uint32_t parse_color_tag(char *str);
8682 uint32_t parse_color_header(char *str);
163159 return (int) (x * 0x400000);
164160 }
165161
166 // Calculate cache key for a rotational angle in radians
167 static inline int rot_key(double a)
168 {
169 return double_to_d22(remainder(a, 2 * M_PI));
170 }
171
172162 #define FNV1_32A_INIT 0x811c9dc5U
173163 #define FNV1_32A_PRIME 16777619U
174164
175 static inline unsigned fnv_32a_buf(void *buf, size_t len, unsigned hval)
176 {
177 unsigned char *bp = (unsigned char*)buf;
165 static inline uint32_t fnv_32a_buf(void *buf, size_t len, uint32_t hval)
166 {
167 unsigned char *bp = (unsigned char *) buf;
178168 size_t n = (len + 3) / 4;
179169
180170 switch (len % 4) {
181 case 0: do { hval ^= (unsigned) *bp++; hval *= FNV1_32A_PRIME;
182 case 3: hval ^= (unsigned) *bp++; hval *= FNV1_32A_PRIME;
183 case 2: hval ^= (unsigned) *bp++; hval *= FNV1_32A_PRIME;
184 case 1: hval ^= (unsigned) *bp++; hval *= FNV1_32A_PRIME;
171 case 0: do { hval ^= *bp++; hval *= FNV1_32A_PRIME; //-fallthrough
172 case 3: hval ^= *bp++; hval *= FNV1_32A_PRIME; //-fallthrough
173 case 2: hval ^= *bp++; hval *= FNV1_32A_PRIME; //-fallthrough
174 case 1: hval ^= *bp++; hval *= FNV1_32A_PRIME;
185175 } while (--n > 0);
186176 }
187177
188178 return hval;
189179 }
190 static inline unsigned fnv_32a_str(char *str, unsigned hval)
180 static inline uint32_t fnv_32a_str(const char *str, uint32_t hval)
191181 {
192182 unsigned char *s = (unsigned char *) str;
193183 while (*s) {
194 hval ^= (unsigned) *s++;
184 hval ^= *s++;
195185 hval *= FNV1_32A_PRIME;
196186 }
197187 return hval;
198188 }
199189
190 static inline int mystrtoi(char **p, int *res)
191 {
192 char *start = *p;
193 double temp_res = ass_strtod(*p, p);
194 *res = (int) (temp_res + (temp_res > 0 ? 0.5 : -0.5));
195 return *p != start;
196 }
197
198 static inline int mystrtoll(char **p, long long *res)
199 {
200 char *start = *p;
201 double temp_res = ass_strtod(*p, p);
202 *res = (long long) (temp_res + (temp_res > 0 ? 0.5 : -0.5));
203 return *p != start;
204 }
205
206 static inline int mystrtod(char **p, double *res)
207 {
208 char *start = *p;
209 *res = ass_strtod(*p, p);
210 return *p != start;
211 }
212
213 static inline int mystrtoi32(char **p, int base, int32_t *res)
214 {
215 char *start = *p;
216 long long temp_res = strtoll(*p, p, base);
217 *res = FFMINMAX(temp_res, INT32_MIN, INT32_MAX);
218 return *p != start;
219 }
220
200221 #endif /* LIBASS_UTILS_H */
5555
5656 typedef enum DWRITE_FACTORY_TYPE {
5757 DWRITE_FACTORY_TYPE_SHARED = 0,
58 DWRITE_FACTORY_TYPE_ISOLATED
58 DWRITE_FACTORY_TYPE_ISOLATED
5959 } DWRITE_FACTORY_TYPE;
6060
6161 typedef enum DWRITE_FONT_FACE_TYPE {
7272 typedef enum DWRITE_FONT_SIMULATIONS {
7373 DWRITE_FONT_SIMULATIONS_NONE = 0x0000,
7474 DWRITE_FONT_SIMULATIONS_BOLD = 0x0001,
75 DWRITE_FONT_SIMULATIONS_OBLIQUE = 0x0002
75 DWRITE_FONT_SIMULATIONS_OBLIQUE = 0x0002
7676 } DWRITE_FONT_SIMULATIONS;
7777
7878 typedef enum DWRITE_FONT_STRETCH {
8686 DWRITE_FONT_STRETCH_SEMI_EXPANDED = 6,
8787 DWRITE_FONT_STRETCH_EXPANDED = 7,
8888 DWRITE_FONT_STRETCH_EXTRA_EXPANDED = 8,
89 DWRITE_FONT_STRETCH_ULTRA_EXPANDED = 9
89 DWRITE_FONT_STRETCH_ULTRA_EXPANDED = 9
9090 } DWRITE_FONT_STRETCH;
9191
9292 typedef enum DWRITE_FONT_STYLE {
9393 DWRITE_FONT_STYLE_NORMAL = 0,
9494 DWRITE_FONT_STYLE_OBLIQUE,
95 DWRITE_FONT_STYLE_ITALIC
95 DWRITE_FONT_STYLE_ITALIC
9696 } DWRITE_FONT_STYLE;
9797
9898 typedef enum DWRITE_FONT_WEIGHT {
4242 ass_set_selective_style_override_enabled
4343 ass_set_selective_style_override
4444 ass_set_check_readorder
45 ass_track_set_feature
00 ;******************************************************************************
11 ;* be_blur.asm: SSE2 \be blur
22 ;******************************************************************************
3 ;* Copyright (C) 2013 Rodger Combs <rcombs@rcombs.me>
3 ;* Copyright (C) 2013 rcombs <rcombs@rcombs.me>
44 ;*
55 ;* This file is part of libass.
66 ;*
00 ;******************************************************************************
11 ;* add_bitmaps.asm: SSE2 and x86 add_bitmaps
22 ;******************************************************************************
3 ;* Copyright (C) 2013 Rodger Combs <rcombs@rcombs.me>
3 ;* Copyright (C) 2013 rcombs <rcombs@rcombs.me>
44 ;*
55 ;* This file is part of libass.
66 ;*
202202 lea %6, [%5]
203203 cmp %6, %3
204204 cmovae %6, %4
205 %if (mmsize != 32) || (%0 < 7)
205 %if mmsize != 32 || %0 < 7
206206 mova m%1, [%2 + %6]
207207 %elifidn %7, left
208208 mova xm%1, [%2 + %6]
218218 sub %5, %2
219219 cmp %4, %3
220220 cmovb %5, %4
221 %if (mmsize != 32) || (%0 < 6)
221 %if mmsize != 32 || %0 < 6
222222 mova m%1, [%2 + %5]
223223 %elifidn %6, left
224224 mova xm%1, [%2 + %5]
285285 mova m3, m0
286286 mova m4, m1
287287 %endif
288 psrldq m3, 10
289 psrldq m4, 10
290 pslldq m6, m1, 6
291 por m3, m6
292 pslldq m6, m2, 6
293 por m4, m6
288 PALIGNR m3,m1,m3, m6, 10
289 PALIGNR m4,m2,m4, m6, 10
294290 paddw m3, m1
295291 paddw m4, m2
296292 pand m3, m7
309305 %if mmsize == 32
310306 vperm2i128 m0, m0, m1, 0x20
311307 %endif
312 psrldq m0, 8
313 pslldq m6, m1, 8
314 por m0, m6
315 paddd m5, m0, m1
308 PALIGNR m5,m1,m0, m6, 8
309 paddd m5, m1
316310 psrld m5, 1
317 psrldq m0, 4
318 pslldq m6, m1, 4
319 por m0, m6
311 PALIGNR m0,m1,m0, m6, 12
320312 paddd m5, m0
321313 psrld m5, 1
322314 paddd m5, m3
326318 %if mmsize == 32
327319 vperm2i128 m1, m1, m2, 0x21
328320 %endif
329 psrldq m1, 8
330 pslldq m6, m2, 8
331 por m1, m6
332 paddd m5, m1, m2
321 PALIGNR m5,m2,m1, m6, 8
322 paddd m5, m2
333323 psrld m5, 1
334 psrldq m1, 4
335 pslldq m6, m2, 4
336 por m1, m6
324 PALIGNR m1,m2,m1, m6, 12
337325 paddd m5, m1
338326 psrld m5, 1
339327 paddd m5, m4
500488 %endif
501489 .main_loop:
502490 %if ARCH_X86_64
503 LOAD_LINE 0, r1,r2,r7, r4 + 0 * r3, r6, right
491 LOAD_LINE 2, r1,r2,r7, r4 + 0 * r3, r6, right
504492 LOAD_LINE 1, r1,r2,r7, r4 + 1 * r3, r6
505493 %else
506 LOAD_LINE_COMPACT 0, r1,r2,r4, r6, right
494 LOAD_LINE_COMPACT 2, r1,r2,r4, r6, right
507495 add r4, r3
508496 LOAD_LINE_COMPACT 1, r1,r2,r4, r6
509497 sub r4, r3
510498 %endif
511499
512500 %if mmsize == 32
513 vperm2i128 m0, m0, m1, 0x20
514 %endif
515 psrldq m0, 12
516 pslldq m3, m1, 4
517 por m0, m3
518 psrldq m2, m0, 2
519 pslldq m3, m1, 2
520 por m2, m3
521
501 vperm2i128 m2, m2, m1, 0x20
502 %endif
503 PALIGNR m0,m1,m2, m3, 12
504 PALIGNR m2,m1,m2, m3, 14
522505 paddw m3, m0, m1
523506 psrlw m3, 1
524507 paddw m3, m2
563546
564547 .odd_stripe:
565548 %if ARCH_X86_64
566 LOAD_LINE 0, r1,r2,r7, r4 + 0 * r3, r6, right
549 LOAD_LINE 2, r1,r2,r7, r4 + 0 * r3, r6, right
567550 LOAD_LINE 1, r1,r2,r7, r4 + 1 * r3, r6, left
568551 %else
569 LOAD_LINE_COMPACT 0, r1,r2,r4, r6, right
552 LOAD_LINE_COMPACT 2, r1,r2,r4, r6, right
570553 add r4, r3
571554 LOAD_LINE_COMPACT 1, r1,r2,r4, r6, left
572555 sub r4, r3
573556 %endif
574557
575 psrldq xm0, 12
576 pslldq xm3, xm1, 4
577 por xm0, xm3
578 psrldq xm2, xm0, 2
579 pslldq xm3, xm1, 2
580 por xm2, xm3
581
558 PALIGNR xm0,xm1,xm2, xm3, 12
559 PALIGNR xm2,xm1,xm2, xm3, 14
582560 paddw xm3, xm0, xm1
583561 psrlw xm3, 1
584562 paddw xm3, xm2
673651 EXPAND_VERT
674652
675653 ;------------------------------------------------------------------------------
676 ; PRE_BLUR1_HORZ
677 ; void pre_blur1_horz(int16_t *dst, const int16_t *src,
678 ; uintptr_t src_width, uintptr_t src_height);
679 ;------------------------------------------------------------------------------
680
681 %macro PRE_BLUR1_HORZ 0
682 %if ARCH_X86_64
683 cglobal pre_blur1_horz, 4,8,4
684 %else
685 cglobal pre_blur1_horz, 4,7,4
686 %endif
687 lea r5, [2 * r2 + mmsize + 3]
654 ; LOAD_MULTIPLIER 1:n, 2:m_mul, 3:src, 4:tmp
655 ; Load blur parameters into xmm/ymm registers
656 ;------------------------------------------------------------------------------
657
658 %macro LOAD_MULTIPLIER 4
659 %if ARCH_X86_64
660 %assign %%t %2 + (%1 - 1) / 2
661 %else
662 %assign %%t %2
663 %endif
664 movu xm %+ %%t, [%3]
665 %if %1 % 2
666 pextrw %4d, xm %+ %%t, 0
667 pslldq xm %+ %%t, 2
668 pinsrw xm %+ %%t, %4d, 0
669 %endif
670 %if mmsize == 32
671 vpermq m %+ %%t, m %+ %%t, q1010
672 %endif
673 %if ARCH_X86_64
674 %assign %%i 0
675 %rep (%1 + 1) / 2
676 %assign %%c %2 + %%i
677 pshufd m %+ %%c, m %+ %%t, q1111 * %%i
678 %assign %%i %%i + 1
679 %endrep
680 %endif
681 %endmacro
682
683 ;------------------------------------------------------------------------------
684 ; FILTER_PAIR 1-2:m_acc[2], 3-4:m_line[2], 5:m_tmp, 6:m_mul, 7:pos
685 ; Calculate acc += line[0] * mul[odd] + line[1] * mul[even]
686 ;------------------------------------------------------------------------------
687
688 %macro FILTER_PAIR 7
689 punpcklwd m%5, m%4, m%3
690 punpckhwd m%4, m%3
691 %assign %%p ((%7) - 1) / 2
692 %if ARCH_X86_64
693 %assign %%p %6 + %%p
694 %else
695 pshufd m%3, m%6, q1111 * %%p
696 %assign %%p %3
697 %endif
698 pmaddwd m%5, m %+ %%p
699 pmaddwd m%4, m %+ %%p
700 %ifidn %1, %5
701 paddd m%1, m%2
702 %else
703 paddd m%1, m%5
704 %endif
705 paddd m%2, m%4
706 %endmacro
707
708 ;------------------------------------------------------------------------------
709 ; NEXT_DIFF 1:m_res, 2:m_side, 3:m_center, 4:position, 5:left/right
710 ; Calculate difference between next offset line and center line
711 ;------------------------------------------------------------------------------
712
713 %macro NEXT_DIFF 5
714 %ifidn %5, left
715
716 %if cpuflag(ssse3)
717 palignr m%1, m%3, m%2, 16 - (%4)
718 %else
719 psrldq m%2, 2
720 pslldq m%1, m%3, %4
721 por m%1, m%2
722 %endif
723
724 %elifidn %5, right
725
726 %if cpuflag(ssse3)
727 palignr m%1, m%2, m%3, %4
728 %else
729 pslldq m%2, 2
730 psrldq m%1, m%3, %4
731 por m%1, m%2
732 %endif
733
734 %else
735 %error "left/right expected"
736 %endif
737 psubw m%1, m%3
738 %endmacro
739
740 ;------------------------------------------------------------------------------
741 ; BLUR_HORZ 1:radius
742 ; void blurN_horz(int16_t *dst, const int16_t *src,
743 ; uintptr_t src_width, uintptr_t src_height,
744 ; const int16_t *param);
745 ;------------------------------------------------------------------------------
746
747 %macro BLUR_HORZ 1
748 %if ARCH_X86_64
749 %assign %%narg 9 + (%1 + 1) / 2
750 cglobal blur%1_horz, 5,8,%%narg
751 %else
752 cglobal blur%1_horz, 5,7,8
753 SWAP 7, 9
754 %endif
755 LOAD_MULTIPLIER %1, 9, r4, r5
756 lea r5, [2 * r2 + mmsize + 4 * %1 - 1]
688757 lea r2, [2 * r2 + mmsize - 1]
689758 and r5, ~(mmsize - 1)
690759 and r2, ~(mmsize - 1)
693762 add r5, r0
694763 xor r4, r4
695764 MUL r3, mmsize
765 %if mmsize != 32 && %1 > 4
696766 sub r4, r3
697 mova m3, [words_one]
698 %if ARCH_X86_64
767 %endif
768 sub r4, r3
769 %if ARCH_X86_64
770 mova m7, [dwords_round]
699771 lea r7, [words_zero]
700772 sub r7, r1
701773 %endif
702774
703775 .main_loop:
704776 %if ARCH_X86_64
705 LOAD_LINE 0, r1,r2,r7, r4 + 0 * r3, r6, right
706 LOAD_LINE 1, r1,r2,r7, r4 + 1 * r3, r6
707 %else
708 LOAD_LINE_COMPACT 0, r1,r2,r4, r6, right
777 %if %1 > 4
778 LOAD_LINE 1, r1,r2,r7, r4 + 0 * r3, r6
779 %else
780 LOAD_LINE 1, r1,r2,r7, r4 + 0 * r3, r6, right
781 %endif
782 LOAD_LINE 2, r1,r2,r7, r4 + 1 * r3, r6
783 %if mmsize != 32 && %1 > 4
784 LOAD_LINE 0, r1,r2,r7, r4 + 2 * r3, r6
785 SWAP 0, 2
786 %endif
787 %else
788 %if %1 > 4
789 LOAD_LINE_COMPACT 1, r1,r2,r4, r6
790 %else
791 LOAD_LINE_COMPACT 1, r1,r2,r4, r6, right
792 %endif
709793 add r4, r3
710 LOAD_LINE_COMPACT 1, r1,r2,r4, r6
794 LOAD_LINE_COMPACT 2, r1,r2,r4, r6
795 %if mmsize != 32 && %1 > 4
796 add r4, r3
797 LOAD_LINE_COMPACT 0, r1,r2,r4, r6
798 SWAP 0, 2
711799 sub r4, r3
712800 %endif
713
714 %if mmsize == 32
715 vperm2i128 m0, m0, m1, 0x20
716 %endif
717 psrldq m0, 12
718 pslldq m2, m1, 4
719 por m0, m2
720 psrldq m2, m0, 2
721 paddw m0, m1
722 pslldq m1, 2
723 psrlw m0, 1
724 por m1, m2
725 paddw m0, m1
726 paddw m0, m3
727 psrlw m0, 1
728
801 sub r4, r3
802 %endif
803
804 %if %1 > 4
805 %if mmsize == 32
806 vperm2i128 m0, m1, m2, 0x21
807 %endif
808 %if cpuflag(ssse3)
809 PALIGNR m1,m0,m1, m3, 16 - 2 * %1
810 %else
811 PALIGNR m1,m0,m1, m3, 32 - 4 * %1
812 %endif
813 PALIGNR m0,m2,m0, m3, 16 - 2 * %1
814 %else
815 %if mmsize == 32
816 vperm2i128 m1, m1, m2, 0x20
817 %endif
818 %if cpuflag(ssse3)
819 palignr m0, m2, m1, 8
820 pslldq m1, 8
821 %else
822 shufpd m0, m1, m2, 5
823 %endif
824 %endif
825
826 %if ARCH_X86_64
827 mova m6, m7
828 %else
829 mova m6, [dwords_round]
830 mova [r0], m1
831 SWAP 1, 8
832 %endif
833
834 %assign %%i %1
835 psubw m3, m2, m0
836 %if cpuflag(ssse3) && %1 < 8
837 psrldq m2, 16 - 2 * %1
838 %endif
839 NEXT_DIFF 4,2,0, 2 * %%i - 2, right
840 FILTER_PAIR 5,6, 3,4, 5, 9,%%i
841 %rep %1 / 2 - 1
842 %assign %%i %%i - 2
843 NEXT_DIFF 3,2,0, 2 * %%i, right
844 NEXT_DIFF 4,2,0, 2 * %%i - 2, right
845 FILTER_PAIR 5,6, 3,4, 8, 9,%%i
846 %endrep
847
848 %if ARCH_X86_64 == 0
849 SWAP 1, 8
850 mova m1, [r0]
851 %if %1 % 2
852 mova [r0], m2
853 %endif
854 SWAP 2, 8
855 %endif
856
857 %assign %%i %1
858 %if cpuflag(ssse3) && %1 < 8
859 NEXT_DIFF 3,1,0, 2 * %%i, left
860 %else
861 psubw m3, m1, m0
862 %endif
863 NEXT_DIFF 4,1,0, 2 * %%i - 2, left
864 FILTER_PAIR 5,6, 3,4, 8, 9,%%i
865 %rep %1 / 2 - 1
866 %assign %%i %%i - 2
867 NEXT_DIFF 3,1,0, 2 * %%i, left
868 NEXT_DIFF 4,1,0, 2 * %%i - 2, left
869 FILTER_PAIR 5,6, 3,4, 8, 9,%%i
870 %endrep
871
872 %if %%i > 2
873 %assign %%i %%i - 2
874 %if ARCH_X86_64 == 0
875 SWAP 2, 8
876 mova m2, [r0]
877 %endif
878 NEXT_DIFF 3,1,0, 2 * %%i, left
879 NEXT_DIFF 4,2,0, 2 * %%i, right
880 FILTER_PAIR 5,6, 3,4, 1, 9,%%i
881 %endif
882
883 psrad m5, 16
884 psrad m6, 16
885 packssdw m5, m6
886 paddw m0, m5
729887 mova [r0], m0
730888 add r0, mmsize
731889 add r4, mmsize
735893 %endmacro
736894
737895 INIT_XMM sse2
738 PRE_BLUR1_HORZ
896 BLUR_HORZ 4
897 BLUR_HORZ 5
898 BLUR_HORZ 6
899 BLUR_HORZ 7
900 BLUR_HORZ 8
739901 INIT_YMM avx2
740 PRE_BLUR1_HORZ
741
742 ;------------------------------------------------------------------------------
743 ; PRE_BLUR1_VERT
744 ; void pre_blur1_vert(int16_t *dst, const int16_t *src,
745 ; uintptr_t src_width, uintptr_t src_height);
746 ;------------------------------------------------------------------------------
747
748 %macro PRE_BLUR1_VERT 0
749 cglobal pre_blur1_vert, 4,7,4
902 BLUR_HORZ 4
903 BLUR_HORZ 5
904 BLUR_HORZ 6
905 BLUR_HORZ 7
906 BLUR_HORZ 8
907
908 ;------------------------------------------------------------------------------
909 ; BLUR_VERT 1:radius
910 ; void blurN_vert(int16_t *dst, const int16_t *src,
911 ; uintptr_t src_width, uintptr_t src_height,
912 ; const int16_t *param);
913 ;------------------------------------------------------------------------------
914
915 %macro BLUR_VERT 1
916 %if ARCH_X86_64
917 %assign %%narg 7 + (%1 + 1) / 2
918 cglobal blur%1_vert, 5,7,%%narg
919 %else
920 cglobal blur%1_vert, 5,7,8
921 %endif
922 LOAD_MULTIPLIER %1, 7, r4, r5
750923 lea r2, [2 * r2 + mmsize - 1]
751 lea r5, [r3 + 2]
924 lea r5, [r3 + 2 * %1]
752925 and r2, ~(mmsize - 1)
753926 imul r2, r5
754927 MUL r3, mmsize
755928 add r2, r0
756 mova m3, [words_one]
929 mova m4, [dwords_round]
757930 lea r6, [words_zero]
758931 sub r6, r1
759932
760933 .col_loop:
761 mov r4, -2 * mmsize
762 pxor m0, m0
763 pxor m1, m1
934 mov r4, -2 * %1 * mmsize
764935 .row_loop:
765 LOAD_LINE 2, r1,r3,r6, r4 + 2 * mmsize, r5
766
767 paddw m0, m2
768 psrlw m0, 1
769 paddw m0, m1
770 paddw m0, m3
771 psrlw m0, 1
772
773 mova [r0], m0
774 add r4, mmsize
775 add r0, mmsize
776 mova m0, m1
777 mova m1, m2
778 cmp r4, r3
779 jl .row_loop
780 add r1, r3
781 sub r6, r3
782 cmp r0, r2
783 jb .col_loop
784 RET
785 %endmacro
786
787 INIT_XMM sse2
788 PRE_BLUR1_VERT
789 INIT_YMM avx2
790 PRE_BLUR1_VERT
791
792 ;------------------------------------------------------------------------------
793 ; PRE_BLUR2_HORZ
794 ; void pre_blur2_horz(int16_t *dst, const int16_t *src,
795 ; uintptr_t src_width, uintptr_t src_height);
796 ;------------------------------------------------------------------------------
797
798 %macro PRE_BLUR2_HORZ 0
799 %if ARCH_X86_64
800 cglobal pre_blur2_horz, 4,8,7
801 %else
802 cglobal pre_blur2_horz, 4,7,7
803 %endif
804 lea r5, [2 * r2 + mmsize + 7]
805 lea r2, [2 * r2 + mmsize - 1]
806 and r5, ~(mmsize - 1)
807 and r2, ~(mmsize - 1)
808 imul r5, r3
809 imul r2, r3
810 add r5, r0
811 xor r4, r4
812 MUL r3, mmsize
813 sub r4, r3
814 mova m5, [words_one]
815 mova m6, [words_sign]
816 %if ARCH_X86_64
817 lea r7, [words_zero]
818 sub r7, r1
819 %endif
820
821 .main_loop:
822 %if ARCH_X86_64
823 LOAD_LINE 0, r1,r2,r7, r4 + 0 * r3, r6, right
824 LOAD_LINE 1, r1,r2,r7, r4 + 1 * r3, r6
825 %else
826 LOAD_LINE_COMPACT 0, r1,r2,r4, r6, right
827 add r4, r3
828 LOAD_LINE_COMPACT 1, r1,r2,r4, r6
829 sub r4, r3
830 %endif
831
832 %if mmsize == 32
833 vperm2i128 m0, m0, m1, 0x20
834 %endif
835 psrldq m0, 8
836 pslldq m2, m1, 8
837 por m2, m0
838 paddw m2, m1
839 psrlw m2, 1
840 psrldq m0, 2
841 pslldq m3, m1, 6
842 por m3, m0
843 psrldq m0, 2
844 pslldq m4, m1, 4
845 por m4, m0
846 paddw m2, m4
847 psrlw m2, 1
848 paddw m2, m4
849 psrldq m0, 2
850 pslldq m1, 2
851 por m0, m1
852 paddw m0, m3
853 mova m1, m6
854 pand m1, m0
855 pand m1, m2
856 paddw m0, m2
857 psrlw m0, 1
858 por m0, m1
859 paddw m0, m5
860 psrlw m0, 1
861
862 mova [r0], m0
863 add r0, mmsize
864 add r4, mmsize
865 cmp r0, r5
866 jb .main_loop
867 RET
868 %endmacro
869
870 INIT_XMM sse2
871 PRE_BLUR2_HORZ
872 INIT_YMM avx2
873 PRE_BLUR2_HORZ
874
875 ;------------------------------------------------------------------------------
876 ; PRE_BLUR2_VERT
877 ; void pre_blur2_vert(int16_t *dst, const int16_t *src,
878 ; uintptr_t src_width, uintptr_t src_height);
879 ;------------------------------------------------------------------------------
880
881 %macro PRE_BLUR2_VERT 0
882 %if ARCH_X86_64
883 cglobal pre_blur2_vert, 4,7,9
884 %else
885 cglobal pre_blur2_vert, 4,7,8
886 %endif
887 lea r2, [2 * r2 + mmsize - 1]
888 lea r5, [r3 + 4]
889 and r2, ~(mmsize - 1)
890 imul r2, r5
891 MUL r3, mmsize
892 add r2, r0
893 mova m7, [words_one]
894 %if ARCH_X86_64
895 mova m8, [words_sign]
896 %endif
897 lea r6, [words_zero]
898 sub r6, r1
899
900 .col_loop:
901 mov r4, -4 * mmsize
902 pxor m0, m0
903 pxor m1, m1
904 pxor m2, m2
905 pxor m3, m3
906 .row_loop:
907 LOAD_LINE 4, r1,r3,r6, r4 + 4 * mmsize, r5
908
909 %if ARCH_X86_64
910 mova m6, m8
911 %else
912 psllw m6, m7, 15
913 %endif
914 paddw m0, m4
915 psrlw m0, 1
916 paddw m0, m2
917 psrlw m0, 1
918 paddw m0, m2
919 paddw m5, m1, m3
920 pand m6, m0
921 pand m6, m5
922 paddw m0, m5
923 psrlw m0, 1
924 por m0, m6
925 paddw m0, m7
926 psrlw m0, 1
927
928 mova [r0], m0
929 add r4, mmsize
930 add r0, mmsize
931 mova m0, m1
932 mova m1, m2
933 mova m2, m3
934 mova m3, m4
935 cmp r4, r3
936 jl .row_loop
937 add r1, r3
938 sub r6, r3
939 cmp r0, r2
940 jb .col_loop
941 RET
942 %endmacro
943
944 INIT_XMM sse2
945 PRE_BLUR2_VERT
946 INIT_YMM avx2
947 PRE_BLUR2_VERT
948
949 ;------------------------------------------------------------------------------
950 ; ADD_LINE 1:m_acc1, 2:m_acc2, 3:m_line, 4-5:m_tmp
951 ; Calculate acc += line
952 ;------------------------------------------------------------------------------
953
954 %macro ADD_LINE 5
955 psraw m%4, m%3, 15
956 punpcklwd m%5, m%3, m%4
957 punpckhwd m%3, m%4
958 %ifidn %1, %5
959 paddd m%1, m%2
960 %else
961 paddd m%1, m%5
962 %endif
963 paddd m%2, m%3
964 %endmacro
965
966 ;------------------------------------------------------------------------------
967 ; FILTER_PAIR 1:m_acc1, 2:m_acc2, 3:m_line1, 4:m_line2,
968 ; 5:m_tmp, 6:m_mul64, [7:m_mul32, 8:swizzle]
969 ; Calculate acc += line1 * mul[odd] + line2 * mul[even]
970 ;------------------------------------------------------------------------------
971
972 %macro FILTER_PAIR 6-8
973 punpcklwd m%5, m%4, m%3
974 punpckhwd m%4, m%3
975 %if ARCH_X86_64 || (%0 < 8)
976 pmaddwd m%5, m%6
977 pmaddwd m%4, m%6
978 %else
979 pshufd m%3, m%7, %8
980 pmaddwd m%5, m%3
981 pmaddwd m%4, m%3
982 %endif
983 %ifidn %1, %5
984 paddd m%1, m%2
985 %else
986 paddd m%1, m%5
987 %endif
988 paddd m%2, m%4
989 %endmacro
990
991 ;------------------------------------------------------------------------------
992 ; PRE_BLUR3_HORZ
993 ; void pre_blur3_horz(int16_t *dst, const int16_t *src,
994 ; uintptr_t src_width, uintptr_t src_height);
995 ;------------------------------------------------------------------------------
996
997 %macro PRE_BLUR3_HORZ 0
998 %if ARCH_X86_64
999 cglobal pre_blur3_horz, 4,8,9
1000 %else
1001 cglobal pre_blur3_horz, 4,7,8
1002 %endif
1003 lea r5, [2 * r2 + mmsize + 11]
1004 lea r2, [2 * r2 + mmsize - 1]
1005 and r5, ~(mmsize - 1)
1006 and r2, ~(mmsize - 1)
1007 imul r5, r3
1008 imul r2, r3
1009 add r5, r0
1010 xor r4, r4
1011 MUL r3, mmsize
1012 sub r4, r3
1013 mova m5, [words_15_6]
1014 %if ARCH_X86_64
1015 mova m8, [dwords_32]
1016 lea r7, [words_zero]
1017 sub r7, r1
1018 %endif
1019
1020 .main_loop:
1021 %if ARCH_X86_64
1022 LOAD_LINE 0, r1,r2,r7, r4 + 0 * r3, r6, right
1023 LOAD_LINE 1, r1,r2,r7, r4 + 1 * r3, r6
1024 %else
1025 LOAD_LINE_COMPACT 0, r1,r2,r4, r6, right
1026 add r4, r3
1027 LOAD_LINE_COMPACT 1, r1,r2,r4, r6
1028 sub r4, r3
1029 %endif
1030
1031 %if ARCH_X86_64
1032 mova m7, m8
1033 %else
1034 mova m7, [dwords_32]
1035 %endif
1036 %if mmsize == 32
1037 vperm2i128 m0, m0, m1, 0x20
1038 %endif
1039 psrldq m2, m0, 10
1040 pslldq m3, m1, 6
1041 por m2, m3
1042
1043 psrldq m0, 4
1044 pslldq m3, m2, 6
1045 por m3, m0
1046 psubw m3, m2
1047 ADD_LINE 6,7, 3,4, 6
1048
1049 psrldq m0, 2
1050 pslldq m3, m2, 4
1051 por m3, m0
1052 psubw m3, m2
1053 psrldq m0, 2
1054 pslldq m4, m2, 2
1055 por m4, m0
1056 psubw m4, m2
1057 FILTER_PAIR 6,7, 3,4, 0, 5
1058
1059 psubw m3, m1, m2
1060 ADD_LINE 6,7, 3,4, 0
1061
1062 pslldq m1, 2
1063 psrldq m3, m2, 4
1064 por m3, m1
1065 psubw m3, m2
1066 pslldq m1, 2
1067 psrldq m4, m2, 2
1068 por m4, m1
1069 psubw m4, m2
1070 FILTER_PAIR 6,7, 3,4, 0, 5
1071
1072 psrad m6, 6
1073 psrad m7, 6
1074 packssdw m6, m7
1075 paddw m2, m6
1076 mova [r0], m2
1077 add r0, mmsize
1078 add r4, mmsize
1079 cmp r0, r5
1080 jb .main_loop
1081 RET
1082 %endmacro
1083
1084 INIT_XMM sse2
1085 PRE_BLUR3_HORZ
1086 INIT_YMM avx2
1087 PRE_BLUR3_HORZ
1088
1089 ;------------------------------------------------------------------------------
1090 ; PRE_BLUR3_VERT
1091 ; void pre_blur3_vert(int16_t *dst, const int16_t *src,
1092 ; uintptr_t src_width, uintptr_t src_height);
1093 ;------------------------------------------------------------------------------
1094
1095 %macro PRE_BLUR3_VERT 0
1096 %if ARCH_X86_64
1097 cglobal pre_blur3_vert, 4,7,8
1098 %else
1099 cglobal pre_blur3_vert, 4,7,8
1100 %endif
1101 lea r2, [2 * r2 + mmsize - 1]
1102 lea r5, [r3 + 6]
1103 and r2, ~(mmsize - 1)
1104 imul r2, r5
1105 MUL r3, mmsize
1106 add r2, r0
1107 mova m4, [dwords_32]
1108 mova m5, [words_15_6]
1109 lea r6, [words_zero]
1110 sub r6, r1
1111
1112 .col_loop:
1113 mov r4, -6 * mmsize
1114 .row_loop:
936 mova m5, m4
1115937 mova m6, m4
1116 mova m7, m4
1117 LOAD_LINE 0, r1,r3,r6, r4 + 3 * mmsize, r5
1118
1119 LOAD_LINE 1, r1,r3,r6, r4 + 0 * mmsize, r5
1120 psubw m1, m0
1121 ADD_LINE 6,7, 1,2, 3
1122
1123 LOAD_LINE 1, r1,r3,r6, r4 + 1 * mmsize, r5
1124 LOAD_LINE 2, r1,r3,r6, r4 + 2 * mmsize, r5
938 LOAD_LINE 0, r1,r3,r6, r4 + %1 * mmsize, r5
939
940 %assign %%i %1
941 %rep %1 / 2
942
943 LOAD_LINE 1, r1,r3,r6, r4 + (%1 - %%i) * mmsize, r5
944 LOAD_LINE 2, r1,r3,r6, r4 + (%1 - %%i + 1) * mmsize, r5
1125945 psubw m1, m0
1126946 psubw m2, m0
1127 FILTER_PAIR 6,7, 1,2, 3, 5
1128
1129 LOAD_LINE 1, r1,r3,r6, r4 + 6 * mmsize, r5
1130 psubw m1, m0
1131 ADD_LINE 6,7, 1,2, 3
1132
1133 LOAD_LINE 1, r1,r3,r6, r4 + 5 * mmsize, r5
1134 LOAD_LINE 2, r1,r3,r6, r4 + 4 * mmsize, r5
947 FILTER_PAIR 5,6, 1,2, 3, 7,%%i
948
949 LOAD_LINE 1, r1,r3,r6, r4 + (%1 + %%i) * mmsize, r5
950 LOAD_LINE 2, r1,r3,r6, r4 + (%1 + %%i - 1) * mmsize, r5
1135951 psubw m1, m0
1136952 psubw m2, m0
1137 FILTER_PAIR 6,7, 1,2, 3, 5
1138
1139 psrad m6, 6
1140 psrad m7, 6
1141 packssdw m6, m7
1142 paddw m0, m6
953 FILTER_PAIR 5,6, 1,2, 3, 7,%%i
954
955 %assign %%i %%i - 2
956 %endrep
957
958 %if %%i > 0
959 LOAD_LINE 1, r1,r3,r6, r4 + (%1 - %%i) * mmsize, r5
960 LOAD_LINE 2, r1,r3,r6, r4 + (%1 + %%i) * mmsize, r5
961 psubw m1, m0
962 psubw m2, m0
963 FILTER_PAIR 5,6, 1,2, 3, 7,%%i
964 %endif
965
966 psrad m5, 16
967 psrad m6, 16
968 packssdw m5, m6
969 paddw m0, m5
1143970 mova [r0], m0
1144971 add r4, mmsize
1145972 add r0, mmsize
1153980 %endmacro
1154981
1155982 INIT_XMM sse2
1156 PRE_BLUR3_VERT
983 BLUR_VERT 4
984 BLUR_VERT 5
985 BLUR_VERT 6
986 BLUR_VERT 7
987 BLUR_VERT 8
1157988 INIT_YMM avx2
1158 PRE_BLUR3_VERT
1159
1160 ;------------------------------------------------------------------------------
1161 ; LOAD_MULTIPLIER 1:m_mul1, 2:m_mul2, 3:src, 4:tmp
1162 ; Load blur parameters into xmm/ymm registers
1163 ;------------------------------------------------------------------------------
1164
1165 %macro LOAD_MULTIPLIER 4
1166 mov %4, [%3]
1167 movd xm%1, %4d
1168 %if ARCH_X86_64
1169 shr %4, 32
1170 %else
1171 mov %4, [%3 + 4]
1172 %endif
1173 movd xm%2, %4d
1174 %if ARCH_X86_64 == 0
1175 punpckldq xm%1, xm%2
1176 %if mmsize == 32
1177 vpbroadcastq m%1, xm%1
1178 %endif
1179 %elif mmsize == 32
1180 vpbroadcastd m%1, xm%1
1181 vpbroadcastd m%2, xm%2
1182 %else
1183 pshufd m%1, m%1, q0000
1184 pshufd m%2, m%2, q0000
1185 %endif
1186 %endmacro
1187
1188 ;------------------------------------------------------------------------------
1189 ; BLUR_HORZ 1:pattern
1190 ; void blurNNNN_horz(int16_t *dst, const int16_t *src,
1191 ; uintptr_t src_width, uintptr_t src_height,
1192 ; const int16_t *param);
1193 ;------------------------------------------------------------------------------
1194
1195 %macro BLUR_HORZ 1
1196 %assign %%i1 %1 / 1000 % 10
1197 %assign %%i2 %1 / 100 % 10
1198 %assign %%i3 %1 / 10 % 10
1199 %assign %%i4 %1 / 1 % 10
1200 %if ARCH_X86_64
1201 cglobal blur%1_horz, 5,8,10
1202 %else
1203 cglobal blur%1_horz, 5,7,8
1204 %endif
1205 %if ARCH_X86_64
1206 LOAD_MULTIPLIER 8,9, r4, r5
1207 %else
1208 LOAD_MULTIPLIER 5,0, r4, r5
1209 %endif
1210 lea r5, [2 * r2 + mmsize + 4 * %%i4 - 1]
1211 lea r2, [2 * r2 + mmsize - 1]
1212 and r5, ~(mmsize - 1)
1213 and r2, ~(mmsize - 1)
1214 imul r5, r3
1215 imul r2, r3
1216 add r5, r0
1217 xor r4, r4
1218 MUL r3, mmsize
1219 %if (mmsize != 32) && (%%i4 > 4)
1220 sub r4, r3
1221 %endif
1222 sub r4, r3
1223 %if ARCH_X86_64
1224 mova m5, [dwords_round]
1225 lea r7, [words_zero]
1226 sub r7, r1
1227 %endif
1228
1229 .main_loop:
1230 %if ARCH_X86_64
1231 %if %%i4 > 4
1232 LOAD_LINE 0, r1,r2,r7, r4 + 0 * r3, r6
1233 %else
1234 LOAD_LINE 0, r1,r2,r7, r4 + 0 * r3, r6, right
1235 %endif
1236 LOAD_LINE 1, r1,r2,r7, r4 + 1 * r3, r6
1237 %if (mmsize != 32) && (%%i4 > 4)
1238 LOAD_LINE 2, r1,r2,r7, r4 + 2 * r3, r6
1239 SWAP 1, 2
1240 %endif
1241 %else
1242 %if %%i4 > 4
1243 LOAD_LINE_COMPACT 0, r1,r2,r4, r6
1244 %else
1245 LOAD_LINE_COMPACT 0, r1,r2,r4, r6, right
1246 %endif
1247 add r4, r3
1248 LOAD_LINE_COMPACT 1, r1,r2,r4, r6
1249 %if (mmsize != 32) && (%%i4 > 4)
1250 add r4, r3
1251 LOAD_LINE_COMPACT 2, r1,r2,r4, r6
1252 SWAP 1, 2
1253 sub r4, r3
1254 %endif
1255 sub r4, r3
1256 %endif
1257
1258 %if ARCH_X86_64
1259 mova m7, m5
1260 %else
1261 mova m7, [dwords_round]
1262 %endif
1263 %if %%i4 > 4
1264 %if mmsize == 32
1265 vperm2i128 m2, m0, m1, 0x21
1266 %endif
1267 psrldq m0, 32 - 4 * %%i4
1268 pslldq m3, m2, 4 * %%i4 - 16
1269 por m0, m3
1270 psrldq m2, 16 - 2 * %%i4
1271 %else
1272 %if mmsize == 32
1273 vperm2i128 m0, m0, m1, 0x20
1274 %endif
1275 psrldq m2, m0, 16 - 2 * %%i4
1276 %endif
1277 pslldq m3, m1, 2 * %%i4
1278 por m2, m3
1279
1280 psubw m3, m1, m2
1281 pslldq m1, 2 * (%%i4 - %%i3)
1282 psrldq m4, m2, 2 * %%i3
1283 por m4, m1
1284 psubw m4, m2
1285 FILTER_PAIR 6,7, 3,4, 6, 9,5,q1111
1286
1287 pslldq m1, 2 * (%%i3 - %%i2)
1288 psrldq m3, m2, 2 * %%i2
1289 por m3, m1
1290 psubw m3, m2
1291 pslldq m1, 2 * (%%i2 - %%i1)
1292 psrldq m4, m2, 2 * %%i1
1293 por m4, m1
1294 psubw m4, m2
1295 FILTER_PAIR 6,7, 3,4, 1, 8,5,q0000
1296
1297 psubw m3, m0, m2
1298 psrldq m0, 2 * (%%i4 - %%i3)
1299 pslldq m4, m2, 2 * %%i3
1300 por m4, m0
1301 psubw m4, m2
1302 FILTER_PAIR 6,7, 3,4, 1, 9,5,q1111
1303
1304 psrldq m0, 2 * (%%i3 - %%i2)
1305 pslldq m3, m2, 2 * %%i2
1306 por m3, m0
1307 psubw m3, m2
1308 psrldq m0, 2 * (%%i2 - %%i1)
1309 pslldq m4, m2, 2 * %%i1
1310 por m4, m0
1311 psubw m4, m2
1312 FILTER_PAIR 6,7, 3,4, 1, 8,5,q0000
1313
1314 psrad m6, 16
1315 psrad m7, 16
1316 packssdw m6, m7
1317 paddw m2, m6
1318 mova [r0], m2
1319 add r0, mmsize
1320 add r4, mmsize
1321 cmp r0, r5
1322 jb .main_loop
1323 RET
1324 %endmacro
1325
1326 INIT_XMM sse2
1327 BLUR_HORZ 1234
1328 BLUR_HORZ 1235
1329 BLUR_HORZ 1246
1330 INIT_YMM avx2
1331 BLUR_HORZ 1234
1332 BLUR_HORZ 1235
1333 BLUR_HORZ 1246
1334
1335 ;------------------------------------------------------------------------------
1336 ; BLUR_VERT 1:pattern
1337 ; void blurNNNN_vert(int16_t *dst, const int16_t *src,
1338 ; uintptr_t src_width, uintptr_t src_height,
1339 ; const int16_t *param);
1340 ;------------------------------------------------------------------------------
1341
1342 %macro BLUR_VERT 1
1343 %assign %%i1 %1 / 1000 % 10
1344 %assign %%i2 %1 / 100 % 10
1345 %assign %%i3 %1 / 10 % 10
1346 %assign %%i4 %1 / 1 % 10
1347 %if ARCH_X86_64
1348 cglobal blur%1_vert, 5,7,9
1349 %else
1350 cglobal blur%1_vert, 5,7,8
1351 %endif
1352 %if ARCH_X86_64
1353 LOAD_MULTIPLIER 4,5, r4, r5
1354 %else
1355 LOAD_MULTIPLIER 5,0, r4, r5
1356 SWAP 4, 8
1357 %endif
1358 lea r2, [2 * r2 + mmsize - 1]
1359 lea r5, [r3 + 2 * %%i4]
1360 and r2, ~(mmsize - 1)
1361 imul r2, r5
1362 MUL r3, mmsize
1363 add r2, r0
1364 mova m8, [dwords_round]
1365 lea r6, [words_zero]
1366 sub r6, r1
1367
1368 .col_loop:
1369 mov r4, -2 * %%i4 * mmsize
1370 .row_loop:
1371 mova m6, m8
1372 mova m7, m8
1373 LOAD_LINE 0, r1,r3,r6, r4 + %%i4 * mmsize, r5
1374
1375 LOAD_LINE 1, r1,r3,r6, r4 + (%%i4 - %%i4) * mmsize, r5
1376 LOAD_LINE 2, r1,r3,r6, r4 + (%%i4 - %%i3) * mmsize, r5
1377 psubw m1, m0
1378 psubw m2, m0
1379 FILTER_PAIR 6,7, 1,2, 3, 5,5,q1111
1380
1381 LOAD_LINE 1, r1,r3,r6, r4 + (%%i4 - %%i2) * mmsize, r5
1382 LOAD_LINE 2, r1,r3,r6, r4 + (%%i4 - %%i1) * mmsize, r5
1383 psubw m1, m0
1384 psubw m2, m0
1385 FILTER_PAIR 6,7, 1,2, 3, 4,5,q0000
1386
1387 LOAD_LINE 1, r1,r3,r6, r4 + (%%i4 + %%i4) * mmsize, r5
1388 LOAD_LINE 2, r1,r3,r6, r4 + (%%i4 + %%i3) * mmsize, r5
1389 psubw m1, m0
1390 psubw m2, m0
1391 FILTER_PAIR 6,7, 1,2, 3, 5,5,q1111
1392
1393 LOAD_LINE 1, r1,r3,r6, r4 + (%%i4 + %%i2) * mmsize, r5
1394 LOAD_LINE 2, r1,r3,r6, r4 + (%%i4 + %%i1) * mmsize, r5
1395 psubw m1, m0
1396 psubw m2, m0
1397 FILTER_PAIR 6,7, 1,2, 3, 4,5,q0000
1398
1399 psrad m6, 16
1400 psrad m7, 16
1401 packssdw m6, m7
1402 paddw m0, m6
1403 mova [r0], m0
1404 add r4, mmsize
1405 add r0, mmsize
1406 cmp r4, r3
1407 jl .row_loop
1408 add r1, r3
1409 sub r6, r3
1410 cmp r0, r2
1411 jb .col_loop
1412 RET
1413 %endmacro
1414
1415 INIT_XMM sse2
1416 BLUR_VERT 1234
1417 BLUR_VERT 1235
1418 BLUR_VERT 1246
1419 INIT_YMM avx2
1420 BLUR_VERT 1234
1421 BLUR_VERT 1235
1422 BLUR_VERT 1246
989 BLUR_VERT 4
990 BLUR_VERT 5
991 BLUR_VERT 6
992 BLUR_VERT 7
993 BLUR_VERT 8
00 ;******************************************************************************
11 ;* add_bitmaps.asm: SSE2 and x86 add_bitmaps
22 ;******************************************************************************
3 ;* Copyright (C) 2013 Rodger Combs <rcombs@rcombs.me>
3 ;* Copyright (C) 2013 rcombs <rcombs@rcombs.me>
44 ;*
55 ;* This file is part of libass.
66 ;*
00 /*
1 * Copyright (C) 2013 Rodger Combs <rcombs@rcombs.me>
1 * Copyright (C) 2013 rcombs <rcombs@rcombs.me>
22 *
33 * This file is part of libass.
44 *
1818 #ifndef INTEL_CPUID_H
1919 #define INTEL_CPUID_H
2020
21 #include <stdint.h>
22
2123 void ass_get_cpuid( uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx);
2224 void ass_get_xgetbv( uint32_t op, uint32_t *eax, uint32_t *edx );
2325
8282 pmaxsw m%1, m%2
8383 %endif
8484 %endmacro
85
86 ;------------------------------------------------------------------------------
87 ; PALIGNR 1:m_dst, 2:m_src1, 3:m_src2, 4:m_tmp, 5:amount
88 ;------------------------------------------------------------------------------
89
90 %macro PALIGNR 5
91 %if (%5) == 0
92 %ifnidn %1, %3
93 mova %1, %3
94 %endif
95 %elif mmsize == 32
96 palignr %1, %2, %3, %5
97 %elif cpuflag(ssse3)
98
99 %ifnidn %1, %3
100 palignr %1, %2, %3, %5
101 %elifidn %2, %4
102 palignr %2, %3, %5
103 mova %1, %2
104 %else
105 mova %4, %3
106 palignr %1, %2, %4, %5
107 %endif
108
109 %elif (%5) == 8
110
111 %ifnidn %1, %2
112 shufpd %1, %3, %2, 5
113 %elifidn %3, %4
114 shufpd %3, %2, 5
115 mova %1, %3
116 %else
117 mova %4, %2
118 shufpd %1, %3, %4, 5
119 %endif
120
121 %else
122
123 %assign %%flip 0
124 %ifidn %1, %3
125 %assign %%flip 1
126 %endif
127 %ifidn %2, %4
128 %assign %%flip 1
129 %endif
130 %if %%flip
131 pslldq %4, %2, 16 - (%5)
132 psrldq %1, %3, %5
133 %else
134 pslldq %1, %2, 16 - (%5)
135 psrldq %4, %3, %5
136 %endif
137 por %1, %4
138
139 %endif
140 %endmacro
10661066 _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;;
10671067 darwin1.*)
10681068 _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;;
1069 darwin*) # darwin 5.x on
1070 # if running on 10.5 or later, the deployment target defaults
1071 # to the OS version, if on x86, and 10.4, the deployment
1072 # target defaults to 10.4. Don't you love it?
1073 case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in
1074 10.0,*86*-darwin8*|10.0,*-darwin[[91]]*)
1075 _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;;
1076 10.[[012]][[,.]]*)
1069 darwin*)
1070 case ${MACOSX_DEPLOYMENT_TARGET},$host in
1071 10.[[012]],*|,*powerpc*)
10771072 _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;;
1078 10.*)
1073 *)
10791074 _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;;
10801075 esac
10811076 ;;
00 #! /bin/sh
11 # Common wrapper for a few potentially missing GNU programs.
22
3 scriptversion=2013-10-28.13; # UTC
4
5 # Copyright (C) 1996-2014 Free Software Foundation, Inc.
3 scriptversion=2018-03-07.03; # UTC
4
5 # Copyright (C) 1996-2018 Free Software Foundation, Inc.
66 # Originally written by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
77
88 # This program is free software; you can redistribute it and/or modify
1616 # GNU General Public License for more details.
1717
1818 # You should have received a copy of the GNU General Public License
19 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 # along with this program. If not, see <https://www.gnu.org/licenses/>.
2020
2121 # As a special exception to the GNU General Public License, if you
2222 # distribute this file as part of a program that contains a
100100 exit $st
101101 fi
102102
103 perl_URL=http://www.perl.org/
104 flex_URL=http://flex.sourceforge.net/
105 gnu_software_URL=http://www.gnu.org/software
103 perl_URL=https://www.perl.org/
104 flex_URL=https://github.com/westes/flex
105 gnu_software_URL=https://www.gnu.org/software
106106
107107 program_details ()
108108 {
206206 exit $st
207207
208208 # Local variables:
209 # eval: (add-hook 'write-file-hooks 'time-stamp)
209 # eval: (add-hook 'before-save-hook 'time-stamp)
210210 # time-stamp-start: "scriptversion="
211211 # time-stamp-format: "%:y-%02m-%02d.%02H"
212 # time-stamp-time-zone: "UTC"
212 # time-stamp-time-zone: "UTC0"
213213 # time-stamp-end: "; # UTC"
214214 # End:
0 # Makefile.in generated by automake 1.15 from Makefile.am.
0 # Makefile.in generated by automake 1.16.1 from Makefile.am.
11 # @configure_input@
22
3 # Copyright (C) 1994-2014 Free Software Foundation, Inc.
3 # Copyright (C) 1994-2018 Free Software Foundation, Inc.
44
55 # This Makefile.in is free software; the Free Software Foundation
66 # gives unlimited permission to copy and/or distribute it,
126126 am__v_at_1 =
127127 DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
128128 depcomp = $(SHELL) $(top_srcdir)/depcomp
129 am__depfiles_maybe = depfiles
129 am__maybe_remake_depfiles = depfiles
130 am__depfiles_remade = ./$(DEPDIR)/profile-profile.Po
130131 am__mv = mv -f
131132 COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
132133 $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
330331 exit 1;; \
331332 esac; \
332333 done; \
333 echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu profile/Makefile'; \
334 echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign profile/Makefile'; \
334335 $(am__cd) $(top_srcdir) && \
335 $(AUTOMAKE) --gnu profile/Makefile
336 $(AUTOMAKE) --foreign profile/Makefile
336337 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
337338 @case '$?' in \
338339 *config.status*) \
339340 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
340341 *) \
341 echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
342 cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
342 echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
343 cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
343344 esac;
344345
345346 $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
370371 distclean-compile:
371372 -rm -f *.tab.c
372373
373 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/profile-profile.Po@am__quote@
374 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/profile-profile.Po@am__quote@ # am--include-marker
375
376 $(am__depfiles_remade):
377 @$(MKDIR_P) $(@D)
378 @echo '# dummy' >$@-t && $(am__mv) $@-t $@
379
380 am--depfiles: $(am__depfiles_remade)
374381
375382 .c.o:
376383 @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
465472 distclean-tags:
466473 -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
467474
468 distdir: $(DISTFILES)
475 distdir: $(BUILT_SOURCES)
476 $(MAKE) $(AM_MAKEFLAGS) distdir-am
477
478 distdir-am: $(DISTFILES)
469479 @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
470480 topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
471481 list='$(DISTFILES)'; \
535545 mostlyclean-am
536546
537547 distclean: distclean-am
538 -rm -rf ./$(DEPDIR)
548 -rm -f ./$(DEPDIR)/profile-profile.Po
539549 -rm -f Makefile
540550 distclean-am: clean-am distclean-compile distclean-generic \
541551 distclean-tags
581591 installcheck-am:
582592
583593 maintainer-clean: maintainer-clean-am
584 -rm -rf ./$(DEPDIR)
594 -rm -f ./$(DEPDIR)/profile-profile.Po
585595 -rm -f Makefile
586596 maintainer-clean-am: distclean-am maintainer-clean-generic
587597
602612
603613 .MAKE: install-am install-strip
604614
605 .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
606 clean-libtool clean-noinstPROGRAMS cscopelist-am ctags \
607 ctags-am distclean distclean-compile distclean-generic \
615 .PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
616 clean-generic clean-libtool clean-noinstPROGRAMS cscopelist-am \
617 ctags ctags-am distclean distclean-compile distclean-generic \
608618 distclean-libtool distclean-tags distdir dvi dvi-am html \
609619 html-am info info-am install install-am install-data \
610620 install-data-am install-dvi install-dvi-am install-exec \
00 /*
11 * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
22 * Copyright (C) 2009 Grigori Goronzy <greg@geekmind.org>
3 * Copyright (C) 2013 Rodger Combs <rcombs@rcombs.me>
3 * Copyright (C) 2013 rcombs <rcombs@rcombs.me>
44 *
55 * This file is part of libass.
66 *
6565 const int frame_h = 720;
6666
6767 if (argc < 5) {
68 printf("usage: %s <subtitle file> <start time> <fps> <time to render>\n", argv[0]);
68 printf("usage: %s <subtitle file> <start time> <fps> <end time>\n", argv[0]);
6969 exit(1);
7070 }
7171 char *subfile = argv[1];
7272 double tm = strtod(argv[2], 0);
7373 double fps = strtod(argv[3], 0);
74 double time = strtod(argv[4], 0);
75
76 if(fps == 0){
74 double end_time = strtod(argv[4], 0);
75
76 if (fps == 0) {
7777 printf("fps cannot equal 0\n");
7878 exit(1);
7979 }
8585 exit(1);
8686 }
8787
88 while(tm < time){
88 while (tm < end_time) {
8989 ass_render_frame(ass_renderer, track, (int) (tm * 1000), NULL);
9090 tm += 1 / fps;
9191 }
0 # Makefile.in generated by automake 1.15 from Makefile.am.
0 # Makefile.in generated by automake 1.16.1 from Makefile.am.
11 # @configure_input@
22
3 # Copyright (C) 1994-2014 Free Software Foundation, Inc.
3 # Copyright (C) 1994-2018 Free Software Foundation, Inc.
44
55 # This Makefile.in is free software; the Free Software Foundation
66 # gives unlimited permission to copy and/or distribute it,
126126 am__v_at_1 =
127127 DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
128128 depcomp = $(SHELL) $(top_srcdir)/depcomp
129 am__depfiles_maybe = depfiles
129 am__maybe_remake_depfiles = depfiles
130 am__depfiles_remade = ./$(DEPDIR)/test-test.Po
130131 am__mv = mv -f
131132 COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
132133 $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
330331 exit 1;; \
331332 esac; \
332333 done; \
333 echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu test/Makefile'; \
334 echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign test/Makefile'; \
334335 $(am__cd) $(top_srcdir) && \
335 $(AUTOMAKE) --gnu test/Makefile
336 $(AUTOMAKE) --foreign test/Makefile
336337 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
337338 @case '$?' in \
338339 *config.status*) \
339340 cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
340341 *) \
341 echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
342 cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
342 echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
343 cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
343344 esac;
344345
345346 $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
370371 distclean-compile:
371372 -rm -f *.tab.c
372373
373 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-test.Po@am__quote@
374 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-test.Po@am__quote@ # am--include-marker
375
376 $(am__depfiles_remade):
377 @$(MKDIR_P) $(@D)
378 @echo '# dummy' >$@-t && $(am__mv) $@-t $@
379
380 am--depfiles: $(am__depfiles_remade)
374381
375382 .c.o:
376383 @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
465472 distclean-tags:
466473 -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
467474
468 distdir: $(DISTFILES)
475 distdir: $(BUILT_SOURCES)
476 $(MAKE) $(AM_MAKEFLAGS) distdir-am
477
478 distdir-am: $(DISTFILES)
469479 @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
470480 topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
471481 list='$(DISTFILES)'; \
535545 mostlyclean-am
536546
537547 distclean: distclean-am
538 -rm -rf ./$(DEPDIR)
548 -rm -f ./$(DEPDIR)/test-test.Po
539549 -rm -f Makefile
540550 distclean-am: clean-am distclean-compile distclean-generic \
541551 distclean-tags
581591 installcheck-am:
582592
583593 maintainer-clean: maintainer-clean-am
584 -rm -rf ./$(DEPDIR)
594 -rm -f ./$(DEPDIR)/test-test.Po
585595 -rm -f Makefile
586596 maintainer-clean-am: distclean-am maintainer-clean-generic
587597
602612
603613 .MAKE: install-am install-strip
604614
605 .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
606 clean-libtool clean-noinstPROGRAMS cscopelist-am ctags \
607 ctags-am distclean distclean-compile distclean-generic \
615 .PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
616 clean-generic clean-libtool clean-noinstPROGRAMS cscopelist-am \
617 ctags ctags-am distclean distclean-compile distclean-generic \
608618 distclean-libtool distclean-tags distdir dvi dvi-am html \
609619 html-am info info-am install install-am install-data \
610620 install-data-am install-dvi install-dvi-am install-exec \